Skip to content

Commit

Permalink
WIP endOfEpoch and slash processing
Browse files Browse the repository at this point in the history
  • Loading branch information
brentstone committed Jul 17, 2023
1 parent 326a03c commit 1ae11c8
Showing 1 changed file with 173 additions and 6 deletions.
179 changes: 173 additions & 6 deletions proof_of_stake/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,12 @@ use storage::{
is_unbond_key, is_validator_slashes_key, last_block_proposer_key,
params_key, slashes_prefix, unbonds_for_source_prefix, unbonds_prefix,
validator_address_raw_hash_key, validator_last_slash_key,
validator_max_commission_rate_change_key, BondDetails,
BondsAndUnbondsDetail, BondsAndUnbondsDetails, DelegatorRedelegatedBonded,
DelegatorRedelegatedUnbonded, EpochedSlashes, IncomingRedelegations,
OutgoingRedelegations, ReverseOrdTokenAmount, RewardsAccumulator,
SlashedAmount, TotalRedelegatedUnbonded, UnbondDetails, ValidatorAddresses,
ValidatorUnbondRecords,
validator_max_commission_rate_change_key, validator_total_unbonded_key,
BondDetails, BondsAndUnbondsDetail, BondsAndUnbondsDetails,
DelegatorRedelegatedBonded, DelegatorRedelegatedUnbonded, EpochedSlashes,
IncomingRedelegations, OutgoingRedelegations, ReverseOrdTokenAmount,
RewardsAccumulator, SlashedAmount, TotalRedelegatedUnbonded, UnbondDetails,
ValidatorAddresses, ValidatorUnbondRecords,
};
use thiserror::Error;
use types::{
Expand Down Expand Up @@ -4510,6 +4510,173 @@ where
Ok(())
}

pub fn process_slashes_new<S>(
storage: &mut S,
current_epoch: EPoch,
) -> storage_api::Result<()>
where
S: StorageRead + StorageWrite,
{
let params = read_pos_params(storage)?;

if current_epoch.0 < params.slash_processing_epoch_offset() {
return Ok(());
}
let infraction_epoch =
current_epoch - params.slash_processing_epoch_offset();

// Slashes to be processed in the current epoch
let enqueued_slashes = enqueued_slashes_handle().at(&current_epoch);
if enqueued_slashes.is_empty(storage)? {
return Ok(());
}
tracing::debug!(
"Processing slashes at the beginning of epoch {} (committed in epoch \
{})",
current_epoch,
infraction_epoch
);

// Compute the cubic slash rate
let cubic_slash_rate =
compute_cubic_slash_rate(storage, &params, infraction_epoch)?;

let validator_slash_rates = enqueued_slashes.iter(storage)?.fold(
HashMap::<Address, Dec>::new(),
|mut acc, s| {
let (
NestedSubKey::Data {
key: validator,
nested_sub_key,
},
slash,
) = s.unwrap();
let rate = acc.entry(validator).or_default();
*rate += slash.rate;
acc
},
);

// Collect the enqueued slashes and update their rates
let mut eager_validator_slashes: HashMap<Address, Vec<Slash>> =
HashMap::new();
let mut eager_validator_slash_rates: HashMap<Address, Dec> = HashMap::new();

for enqueued_slash in enqueued_slashes.iter(storage)? {
let (
NestedSubKey::Data {
key: validator,
nested_sub_key: _,
},
enqueued_slash,
) = enqueued_slash?;
debug_assert_eq!(enqueued_slash.epoch, infraction_epoch);

let slash_rate = cmp::min(
Dec::one(),
cmp::max(
enqueued_slash.r#type.get_slash_rate(&params),
cubic_slash_rate,
),
);
let updated_slash = Slash {
epoch: enqueued_slash.epoch,
block_height: enqueued_slash.block_height,
r#type: enqueued_slash.r#type,
rate: slash_rate,
};
tracing::debug!(
"Slash for validator {} committed in epoch {} has rate {}",
&validator,
enqueued_slash.epoch,
slash_rate
);

let cur_slashes = eager_validator_slashes.entry(validator).or_default();
cur_slashes.push(updated_slash);
let cur_rate =
eager_validator_slash_rates.entry(validator).or_default();
*cur_rate = cmp::min(Dec::one(), *cur_rate + slash_rate);
}

let mut map_validator_slash: HashMap<
Address,
HashMap<Epoch, token::Change>,
> = HashMap::new();
for (validator, slash_rate) in eager_validator_slash_rates {
process_validator_slash()
}

Ok(())
}

/// Quint `processSlash` and `slashValidator`
fn process_validator_slash<S>(
storage: &mut S,
params: &PosParams,
validator: &Address,
slash_rate: Dec,
current_epoch: Epoch,
slashed_amount_map: &mut HashMap<Address, HashMap<Epoch, token::Change>>,
) -> storage_api::Result<()>
where
S: StorageRead + StorageWrite,
{
let infraction_epoch =
current_epoch - params.slash_processing_epoch_offset();

let current_stake = validator_deltas_handle(validator)
.get_sum(storage, current_epoch, params)?
.unwrap_or_default();

let slashes = validator_slashes_handle(validator);
let total_unbonded = unbond_records_handle(validator);
let total_redelegated_unbonded =
validator_total_redelegated_unbonds_handle(validator);
let total_bonded = total_bonded_handle(validator);

let slashed_amount = if let Some(sa) = slashed_amount_map.get(validator) {
sa
} else {
// TODO: turn into a real error
panic!("slashed_amount_map does not have an entry for validator")
};

let mut init_total_unbonded = token::Change::default();
let mut init_bond_balance = token::Change::default();
for epoch in
Epoch::iter_bounds_inclusive(infraction_epoch.next(), current_epoch)
{
init_total_unbonded += compute_total_unbonded(
storage,
validator,
infraction_epoch,
&slashes,
&total_unbonded.at(&epoch),
&total_redelegated_unbonded.at(&epoch),
)?;
}

Ok(())
}

fn compute_total_unbonded<S>(
storage: &mut S,
validator: &Address,
infraction_epoch: Epoch,
validator_slashes: &Slashes,
total_unbonded: &LazyMap<Epoch, token::Amount>,
total_redelegated_unbonded: &NestedMap<
Epoch,
NestedMap<Address, LazyMap<Epoch, token::Amount>>,
>,
) -> storage_api::Result<token::Change>
where
S: StorageRead,
{
todo!()
}

/// Process slashes that have been queued up after discovery. Calculate the
/// cubic slashing rate, store the finalized slashes, update the deltas, then
/// transfer slashed tokens from PoS to the Slash Pool. This function is called
Expand Down

0 comments on commit 1ae11c8

Please sign in to comment.