Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
brentstone committed Dec 20, 2022
1 parent d43bf76 commit 7bb827b
Show file tree
Hide file tree
Showing 2 changed files with 141 additions and 50 deletions.
174 changes: 130 additions & 44 deletions proof_of_stake/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,15 @@ use namada_core::types::storage::Epoch;
use namada_core::types::token;
use parameters::PosParams;
use rust_decimal::Decimal;
use rust_decimal_macros::dec;
use storage::{
num_active_validators_key, params_key, validator_address_raw_hash_key,
validator_max_commission_rate_change_key, validator_state_key,
};
use thiserror::Error;
use types::{
ActiveValidator, ActiveValidatorSetNew, ActiveValidatorSetsNew,
AllSlashesNew, Bonds, BondsNew, CommissionRates, CommissionRatesNew,
ActiveValidator, ActiveValidatorSetNew, ActiveValidatorSetsNew, Bonds,
BondsNew, CommissionRates, CommissionRatesNew, EpochedSlashes,
GenesisValidator, InactiveValidatorSetNew, InactiveValidatorSetsNew,
Position, Slash, SlashNew, SlashType, Slashes, SlashesNew, TotalDeltas,
TotalDeltasNew, Unbond, UnbondNew, Unbonds, ValidatorConsensusKeys,
Expand Down Expand Up @@ -2864,11 +2865,10 @@ where
break;
}
for slash in validator_slashes.iter(storage)? {
let Slash {
epoch,
let SlashNew {
infraction_epoch: epoch,
block_height: _,
r#type: slash_type,
rate: _,
} = slash?;
if epoch > start_epoch && epoch < end_epoch {
let slash_rate =
Expand Down Expand Up @@ -2951,7 +2951,8 @@ where
commission_handle.set(storage, new_rate, current_epoch, params.pipeline_len)
}

/// NEW: apply a slash and write it to storage
/// NEW: apply and record a slash for a misbehavior that has been received from
/// Tendermint
pub fn slash_new<S>(
storage: &mut S,
params: &PosParams,
Expand All @@ -2964,47 +2965,131 @@ pub fn slash_new<S>(
where
S: for<'iter> StorageRead<'iter> + StorageWrite,
{
let rate = slash_type.get_slash_rate(params);
// Upon slash detection, write the slash to the validator storage, write it
// to EpochedSlashes at the processing epoch, jail the validator, and
// immediately remove it from the validator set

// Write the slash data to storage
let slash = SlashNew {
infraction_epoch: evidence_epoch,
block_height: evidence_block_height.into(),
r#type: slash_type,
};

let current_stake =
read_validator_stake(storage, params, validator, current_epoch)?;
let slashed_amount = decimal_mult_u64(rate, u64::from(current_stake));
let token_change = -token::Change::from(slashed_amount);

// Update validator sets and deltas at the pipeline length
update_validator_set_new(
storage,
params,
validator,
token_change,
&active_validator_set_handle(),
&inactive_validator_set_handle(),
current_epoch,
)?;
update_validator_deltas(
let processing_epoch = evidence_epoch + params.unbonding_len;

validator_slashes_handle(validator).push(storage, slash.clone())?;
slashes_handle()
.at(&processing_epoch)
.at(validator)
.push(storage, slash)?;

// Jail the validator and remove it from the validator set immediately
let prev_state = validator_state_handle(validator)
.get(storage, current_epoch, params)?
.expect("Expected to find a valid validator.");
match prev_state {
ValidatorState::Inactive => {
// TODO: maybe I want to just leave it in here and do nothing, but
// if so, need to make sure in other functions that when we promote
// an inactive validator to the active set, then the validator is
// not jailed
}
ValidatorState::Candidate => {
let amount_pre = validator_deltas_handle(validator)
.get_sum(storage, current_epoch, params)?
.unwrap_or_default();
let val_position = validator_set_positions_handle()
.at(&current_epoch)
.get(storage, validator)?
.expect("Could not find validator's position in storage.");
let _ = active_validator_set_handle()
.at(&current_epoch)
.at(&token::Amount::from_change(amount_pre))
.remove(storage, &val_position)?;

// TODO: turn this num_active_validators thing into an epoched
// perhaps so I can properly update it here
let num = read_num_active_validators(storage)?;
write_num_active_validators(storage, num - 1)?;

// Promote the next max inactive validator to the active validator
// set at the pipeline offset TODO: confirm that this is
// what we will want to do
let pipeline_epoch = current_epoch + params.pipeline_len;
let inactive_handle =
inactive_validator_set_handle().at(&pipeline_epoch);
let max_inactive_amount =
get_max_inactive_validator_amount(&inactive_handle, storage)?;
let position_to_promote = find_lowest_position(
&inactive_handle.at(&max_inactive_amount.into()),
storage,
)?
.expect("Should return a position.");
let removed_validator = inactive_handle
.at(&max_inactive_amount.into())
.remove(storage, &position_to_promote)?
.expect("Should have returned a removed validator.");
insert_validator_into_set(
&active_validator_set_handle()
.at(&pipeline_epoch)
.at(&max_inactive_amount),
storage,
&pipeline_epoch,
&removed_validator,
)?;
}
_ => {
// TODO: get rid of this eventually
println!(
"Already jailed or in 'Pending' state which will prob be \
removed"
);
}
}
validator_state_handle(validator).set(
storage,
params,
validator,
token_change,
ValidatorState::Jailed,
current_epoch,
0,
)?;
update_total_deltas(storage, params, token_change, current_epoch)?;

// Write the validator slash to storage
validator_slashes_handle(validator).push(storage, slash)?;
// No other actions are performed here until the epoch in which the slash is
// processed.

// -------------------------------------------

// let current_stake =
// read_validator_stake(storage, params, validator, current_epoch)?;
// let slashed_amount = decimal_mult_u64(rate, u64::from(current_stake));
// let token_change = -token::Change::from(slashed_amount);

// Update validator sets and deltas at the pipeline length
// update_validator_set_new(
// storage,
// params,
// validator,
// token_change,
// &active_validator_set_handle(),
// &inactive_validator_set_handle(),
// current_epoch,
// )?;
// update_validator_deltas(
// storage,
// params,
// validator,
// token_change,
// current_epoch,
// )?;
// update_total_deltas(storage, params, token_change, current_epoch)?;

// Transfer the slashed tokens from PoS account to Slash Fund address
transfer_tokens(
storage,
&staking_token_address(),
token::Amount::from(slashed_amount),
&ADDRESS,
&SLASH_POOL_ADDRESS,
);
// transfer_tokens(
// storage,
// &staking_token_address(),
// token::Amount::from(slashed_amount),
// &ADDRESS,
// &SLASH_POOL_ADDRESS,
// );

Ok(())
}
Expand Down Expand Up @@ -3087,14 +3172,14 @@ pub fn credit_tokens_new<S>(
// Some cubic slashing stuff - may want to move into its own file

/// Get the storage handle to a PoS validator's deltas
pub fn slashes_handle() -> AllSlashesNew {
pub fn slashes_handle() -> EpochedSlashes {
let key = storage::all_slashes_key();
AllSlashesNew::open(key)
EpochedSlashes::open(key)
}

/// Calculate cubic slashing rate
pub fn get_cubic_slash_rate<S>(
storage: &mut S,
storage: &S,
params: &PosParams,
infraction_epoch: Epoch,
current_slash_type: SlashType,
Expand All @@ -3109,18 +3194,19 @@ where
let slashes = slashes_handle().at(&epoch);
let infracting_stake =
slashes.iter(storage)?.fold(Decimal::ZERO, |sum, res| {
let (key, _slash) = res?;
let (key, _slash) = res.unwrap();
match key {
NestedSubKey::Data {
key,
nested_sub_key: _,
} => {
let validator_stake =
read_validator_stake(storage, params, &key, epoch)?;
read_validator_stake(storage, params, &key, epoch)
.unwrap();
sum + Decimal::from(validator_stake)
// TODO: does something more complex need to be done
// here in the event some of these slashes correspond to
// the same validator
// the same validator?
}
}
});
Expand All @@ -3134,7 +3220,7 @@ where
Decimal::ONE,
cmp::max(
current_slash_type.get_slash_rate(params),
9 * sum_vp_fraction * sum_vp_fraction,
dec!(9) * sum_vp_fraction * sum_vp_fraction,
),
);
Ok(rate)
Expand Down
17 changes: 11 additions & 6 deletions proof_of_stake/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,12 @@ pub type BondsNew = crate::epoched_new::EpochedDelta<
>;

/// Slashes indexed by validator address
pub type ValidatorSlashesNew = NestedMap<Address, LazyVec<SlashNew>>;
pub type ValidatorSlashes = NestedMap<Address, SlashesNew>;

/// Epoched slashes
pub type AllSlashesNew = crate::epoched_new::NestedEpoched<
ValidatorSlashesNew,
/// Epoched slashes, where the outer epoch key is the epoch in which the slash
/// is processed
pub type EpochedSlashes = crate::epoched_new::NestedEpoched<
ValidatorSlashes,
crate::epoched_new::OffsetUnbondingLen,
U64_MAX,
>;
Expand Down Expand Up @@ -318,7 +319,9 @@ pub enum ValidatorState {
/// A `Candidate` validator may participate in the consensus. It is either
/// in the active or inactive validator set.
Candidate,
// TODO consider adding `Jailed`
/// A `Jailed` validator has been prohibited from participating in
/// consensus due to a misbehavior
Jailed,
}

/// A bond is either a validator's self-bond or a delegation from a regular
Expand Down Expand Up @@ -377,6 +380,8 @@ pub type Slashes = Vec<Slash>;
/// their staked tokens at and before the epoch of the slash.
#[derive(Debug, Clone, BorshDeserialize, BorshSerialize, BorshSchema)]
pub struct SlashNew {
/// Epoch at which the slashable event occurred.
pub infraction_epoch: Epoch,
/// Block height at which the slashable event occurred.
pub block_height: u64,
/// A type of slashsable event.
Expand All @@ -385,7 +390,7 @@ pub struct SlashNew {

/// Slashes applied to validator, to punish byzantine behavior by removing
/// their staked tokens at and before the epoch of the slash.
pub type SlashesNew = LazyVec<Slash>;
pub type SlashesNew = LazyVec<SlashNew>;

/// A type of slashable event.
#[derive(Debug, Clone, BorshDeserialize, BorshSerialize, BorshSchema)]
Expand Down

0 comments on commit 7bb827b

Please sign in to comment.