From e37427aa2a42238329c657d7c7d86fa633ca8deb Mon Sep 17 00:00:00 2001 From: Herr Seppia Date: Wed, 26 Jun 2024 12:44:29 +0200 Subject: [PATCH 01/10] stake-contract: check if new block --- contracts/stake/src/lib.rs | 2 +- contracts/stake/src/state.rs | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/contracts/stake/src/lib.rs b/contracts/stake/src/lib.rs index 3064023652..1a488fa0c1 100644 --- a/contracts/stake/src/lib.rs +++ b/contracts/stake/src/lib.rs @@ -89,7 +89,7 @@ unsafe fn prev_state_changes(arg_len: u32) -> u32 { unsafe fn before_state_transition(arg_len: u32) -> u32 { rusk_abi::wrap_call(arg_len, |_: ()| { assert_external_caller(); - STATE.before_state_transition() + STATE.on_new_block() }) } diff --git a/contracts/stake/src/state.rs b/contracts/stake/src/state.rs index c75ba55a09..eb783dde14 100644 --- a/contracts/stake/src/state.rs +++ b/contracts/stake/src/state.rs @@ -54,20 +54,20 @@ impl StakeState { } } - pub fn before_state_transition(&mut self) { + pub fn on_new_block(&mut self) { self.previous_block_state.clear() } - fn clear_prev_if_needed(&mut self) { + fn check_new_block(&mut self) { let current_height = rusk_abi::block_height(); if current_height != self.previous_block_height { self.previous_block_height = current_height; - self.before_state_transition(); + self.on_new_block(); } } pub fn stake(&mut self, stake: Stake) { - self.clear_prev_if_needed(); + self.check_new_block(); if stake.value < MINIMUM_STAKE { panic!("The staked value is lower than the minimum amount!"); @@ -108,7 +108,7 @@ impl StakeState { } pub fn unstake(&mut self, unstake: Unstake) { - self.clear_prev_if_needed(); + self.check_new_block(); // remove the stake from a key and increment the signature counter let loaded_stake = self @@ -255,7 +255,7 @@ impl StakeState { /// Rewards a `stake_pk` with the given `value`. If a stake does not exist /// in the map for the key one will be created. pub fn reward(&mut self, stake_pk: &StakePublicKey, value: u64) { - self.clear_prev_if_needed(); + self.check_new_block(); let stake = self.load_or_create_stake_mut(stake_pk); stake.increase_reward(value); @@ -284,7 +284,7 @@ impl StakeState { /// depleted and the provisioner eligibility is shifted to the /// next epoch as well pub fn slash(&mut self, stake_pk: &StakePublicKey, to_slash: u64) { - self.clear_prev_if_needed(); + self.check_new_block(); let stake = self .get_stake_mut(stake_pk) @@ -335,7 +335,7 @@ impl StakeState { /// If the stake is less than the `to_slash` amount, then the stake is /// depleted pub fn hard_slash(&mut self, stake_pk: &StakePublicKey, to_slash: u64) { - self.clear_prev_if_needed(); + self.check_new_block(); let stake_info = self .get_stake_mut(stake_pk) From b70d4c0a8f0731b67299d6711f372db4b0f935bf Mon Sep 17 00:00:00 2001 From: Herr Seppia Date: Fri, 28 Jun 2024 15:46:52 +0200 Subject: [PATCH 02/10] execution-core: add stake faults --- execution-core/src/stake.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/execution-core/src/stake.rs b/execution-core/src/stake.rs index 7b6d80202c..d217a00226 100644 --- a/execution-core/src/stake.rs +++ b/execution-core/src/stake.rs @@ -19,6 +19,9 @@ use crate::{ /// Epoch used for stake operations pub const EPOCH: u64 = 2160; +/// Number of warnings before being penalized +pub const STAKE_WARNINGS: u8 = 1; + /// Calculate the block height at which the next epoch takes effect. #[must_use] pub const fn next_epoch(block_height: BlockHeight) -> u64 { @@ -161,6 +164,8 @@ pub struct StakeData { pub reward: u64, /// The signature counter to prevent replay. pub counter: u64, + /// Faults + pub faults: u8, } impl StakeData { @@ -193,6 +198,7 @@ impl StakeData { amount, reward, counter: 0, + faults: 0, } } From a18bff687ad41da06d99fd99620bf9c5daee5026 Mon Sep 17 00:00:00 2001 From: Herr Seppia Date: Fri, 28 Jun 2024 15:53:26 +0200 Subject: [PATCH 03/10] rusk-recovery: add stake faults --- rusk-recovery/src/state.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/rusk-recovery/src/state.rs b/rusk-recovery/src/state.rs index 92694f4641..6be2f5469d 100644 --- a/rusk-recovery/src/state.rs +++ b/rusk-recovery/src/state.rs @@ -100,6 +100,7 @@ fn generate_stake_state( amount, reward: staker.reward.unwrap_or_default(), counter: 0, + faults: 0, }; session .call::<_, ()>( From 1b92bb31e5930073f95a2fa2e7c3c90d761ef85c Mon Sep 17 00:00:00 2001 From: Herr Seppia Date: Fri, 28 Jun 2024 16:01:38 +0200 Subject: [PATCH 04/10] stake-contract: add deduct_module_balance --- contracts/stake/src/state.rs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/contracts/stake/src/state.rs b/contracts/stake/src/state.rs index eb783dde14..a82754e9f6 100644 --- a/contracts/stake/src/state.rs +++ b/contracts/stake/src/state.rs @@ -359,14 +359,7 @@ impl StakeState { // Update the staked amount stake.0 -= to_slash; - // Update the contract balance to reflect the change in the amount - // withdrawable from the contract - let _: bool = rusk_abi::call( - TRANSFER_CONTRACT, - "sub_contract_balance", - &(STAKE_CONTRACT, to_slash), - ) - .expect("Subtracting balance should succeed"); + Self::deduct_contract_balance(to_slash); // Update the total slashed amount self.slashed_amount += to_slash; @@ -396,6 +389,17 @@ impl StakeState { } } + fn deduct_contract_balance(amount: u64) { + // Update the module balance to reflect the change in the amount + // withdrawable from the contract + let _: () = rusk_abi::call( + TRANSFER_CONTRACT, + "sub_contract_balance", + &(STAKE_CONTRACT, amount), + ) + .expect("Subtracting balance should succeed"); + } + /// Feeds the host with previous state of the changed provisioners. pub fn prev_state_changes(&self) { for (stake_data, stake_pk) in self.previous_block_state.values() { From e618a4f545b558e9ce409d278de562307ef15dc7 Mon Sep 17 00:00:00 2001 From: Herr Seppia Date: Fri, 28 Jun 2024 16:02:45 +0200 Subject: [PATCH 05/10] stake-contract: change `slash` mechanism See also #1059 --- contracts/stake/src/state.rs | 41 ++++++++++++++++----- contracts/stake/tests/common/utils.rs | 4 +-- contracts/stake/tests/events.rs | 52 ++++++++++++++++++++++++++- 3 files changed, 85 insertions(+), 12 deletions(-) diff --git a/contracts/stake/src/state.rs b/contracts/stake/src/state.rs index a82754e9f6..be7ee950ce 100644 --- a/contracts/stake/src/state.rs +++ b/contracts/stake/src/state.rs @@ -12,6 +12,7 @@ use dusk_bytes::Serializable; use execution_core::{ stake::{ next_epoch, Stake, StakeData, StakingEvent, Unstake, Withdraw, EPOCH, + STAKE_WARNINGS, }, transfer::Mint, StakePublicKey, @@ -258,6 +259,8 @@ impl StakeState { self.check_new_block(); let stake = self.load_or_create_stake_mut(stake_pk); + // Reset faults counter + stake.faults = 0; stake.increase_reward(value); rusk_abi::emit( "reward", @@ -283,19 +286,39 @@ impl StakeState { /// If the reward is less than the `to_slash` amount, then the reward is /// depleted and the provisioner eligibility is shifted to the /// next epoch as well - pub fn slash(&mut self, stake_pk: &StakePublicKey, to_slash: u64) { + pub fn slash(&mut self, stake_pk: &StakePublicKey, to_slash: Option) { self.check_new_block(); let stake = self .get_stake_mut(stake_pk) .expect("The stake to slash should exist"); + if stake.amount().is_none() { + // stake.amount can be None if the provisioner unstake in the same + // block + return; + } + let prev_value = Some(stake.clone()); - let to_slash = min(to_slash, stake.reward); + stake.faults = stake.faults.saturating_add(1); + let effective_faults = + stake.faults.saturating_sub(STAKE_WARNINGS) as u64; + + let (stake_amount, _) = stake.amount.as_mut().expect("stake_to_exists"); + + // Slash the provided amount or calculate the percentage according to + // effective faults + let to_slash = + to_slash.unwrap_or(*stake_amount / 100 * effective_faults * 10); + let to_slash = min(to_slash, *stake_amount); if to_slash > 0 { - stake.reward -= to_slash; + // Move the slash amount from stake to reward and deduct contract + // balance + *stake_amount -= to_slash; + stake.increase_reward(to_slash); + Self::deduct_contract_balance(to_slash); rusk_abi::emit( "slash", @@ -306,11 +329,16 @@ impl StakeState { ); } - if stake.reward == 0 { + // Shift eligibility (aka stake suspension) only if warnings are + // saturated + if effective_faults > 0 { // stake.amount can be None if the provisioner unstake in the same // block if let Some((_, eligibility)) = stake.amount.as_mut() { - *eligibility = next_epoch(rusk_abi::block_height()) + EPOCH; + // The stake is suspended for the rest of the current epoch plus + // effective_faults epochs + let to_shift = effective_faults * EPOCH; + *eligibility = next_epoch(rusk_abi::block_height()) + to_shift; rusk_abi::emit( "shifted", StakingEvent { @@ -321,9 +349,6 @@ impl StakeState { } } - // Update the total slashed amount - self.slashed_amount += to_slash; - let key = stake_pk.to_bytes(); self.previous_block_state .entry(key) diff --git a/contracts/stake/tests/common/utils.rs b/contracts/stake/tests/common/utils.rs index 8179d6ab5c..f51d873382 100644 --- a/contracts/stake/tests/common/utils.rs +++ b/contracts/stake/tests/common/utils.rs @@ -21,9 +21,7 @@ use execution_core::{ value_commitment, JubJubScalar, Note, PublicKey, SchnorrSecretKey, SecretKey, Sender, TxSkeleton, ViewKey, }; -use rusk_abi::{ - CallReceipt, ContractError, ContractId, Error, Session, TRANSFER_CONTRACT, -}; +use rusk_abi::{CallReceipt, ContractError, Error, Session, TRANSFER_CONTRACT}; const POINT_LIMIT: u64 = 0x100000000; diff --git a/contracts/stake/tests/events.rs b/contracts/stake/tests/events.rs index 2b492b1382..389505cbb6 100644 --- a/contracts/stake/tests/events.rs +++ b/contracts/stake/tests/events.rs @@ -36,8 +36,30 @@ fn reward_slash() -> Result<(), Error> { let mut session = instantiate(rng, vm, &pk, GENESIS_VALUE); let reward_amount = dusk(10.0); + let stake_amount = dusk(100.0); let slash_amount = dusk(5.0); + let stake_data = StakeData { + reward: 0, + amount: Some((stake_amount, 0)), + counter: 0, + faults: 0, + }; + + session.call::<_, ()>( + TRANSFER_CONTRACT, + "add_contract_balance", + &(STAKE_CONTRACT, stake_amount), + u64::MAX, + )?; + + session.call::<_, ()>( + STAKE_CONTRACT, + "insert_stake", + &(stake_pk, stake_data), + u64::MAX, + )?; + let receipt = session.call::<_, ()>( STAKE_CONTRACT, "reward", @@ -49,10 +71,37 @@ fn reward_slash() -> Result<(), Error> { let receipt = session.call::<_, ()>( STAKE_CONTRACT, "slash", - &(stake_pk, slash_amount), + &(stake_pk, Some(slash_amount)), u64::MAX, )?; + assert!(receipt.events.len() == 1, "No shift at first warn"); assert_event(&receipt.events, "slash", &stake_pk, slash_amount); + let stake_amount = stake_amount - slash_amount; + + let receipt = session.call::<_, ()>( + STAKE_CONTRACT, + "slash", + &(stake_pk, None::), + u64::MAX, + )?; + // 10% of current amount + let slash_amount = stake_amount / 10; + assert_event(&receipt.events, "slash", &stake_pk, slash_amount); + assert_event(&receipt.events, "shifted", &stake_pk, 4320); + + let receipt = session.call::<_, ()>( + STAKE_CONTRACT, + "slash", + &(stake_pk, None::), + u64::MAX, + )?; + let stake_amount = stake_amount - slash_amount; + + // 20% of current amount + let slash_amount = stake_amount / 100 * 20; + assert_event(&receipt.events, "slash", &stake_pk, slash_amount); + assert_event(&receipt.events, "shifted", &stake_pk, 6480); + Ok(()) } @@ -79,6 +128,7 @@ fn stake_hard_slash() -> Result<(), Error> { reward: 0, amount: Some((balance, block_height)), counter: 0, + faults: 0, }; session.call::<_, ()>( From 9c92ff7b948008f058edda57a397612781674428 Mon Sep 17 00:00:00 2001 From: Herr Seppia Date: Fri, 28 Jun 2024 16:05:24 +0200 Subject: [PATCH 06/10] stake-contract: rename `slashed` to `burnt` amount --- contracts/stake/src/lib.rs | 10 +++++----- contracts/stake/src/state.rs | 20 ++++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/contracts/stake/src/lib.rs b/contracts/stake/src/lib.rs index 1a488fa0c1..4f6c65ca0e 100644 --- a/contracts/stake/src/lib.rs +++ b/contracts/stake/src/lib.rs @@ -62,8 +62,8 @@ unsafe fn get_stake(arg_len: u32) -> u32 { } #[no_mangle] -unsafe fn slashed_amount(arg_len: u32) -> u32 { - rusk_abi::wrap_call(arg_len, |_: ()| STATE.slashed_amount()) +unsafe fn burnt_amount(arg_len: u32) -> u32 { + rusk_abi::wrap_call(arg_len, |_: ()| STATE.burnt_amount()) } #[no_mangle] @@ -126,10 +126,10 @@ unsafe fn hard_slash(arg_len: u32) -> u32 { } #[no_mangle] -unsafe fn set_slashed_amount(arg_len: u32) -> u32 { - rusk_abi::wrap_call(arg_len, |slashed_amount| { +unsafe fn set_burnt_amount(arg_len: u32) -> u32 { + rusk_abi::wrap_call(arg_len, |burnt_amount| { assert_external_caller(); - STATE.set_slashed_amount(slashed_amount) + STATE.set_burnt_amount(burnt_amount) }) } diff --git a/contracts/stake/src/state.rs b/contracts/stake/src/state.rs index be7ee950ce..bccf692498 100644 --- a/contracts/stake/src/state.rs +++ b/contracts/stake/src/state.rs @@ -32,7 +32,7 @@ use crate::*; #[derive(Debug, Default, Clone)] pub struct StakeState { stakes: BTreeMap<[u8; StakePublicKey::SIZE], (StakeData, StakePublicKey)>, - slashed_amount: u64, + burnt_amount: u64, previous_block_state: BTreeMap< [u8; StakePublicKey::SIZE], (Option, StakePublicKey), @@ -49,7 +49,7 @@ impl StakeState { pub const fn new() -> Self { Self { stakes: BTreeMap::new(), - slashed_amount: 0u64, + burnt_amount: 0u64, previous_block_state: BTreeMap::new(), previous_block_height: 0, } @@ -271,9 +271,9 @@ impl StakeState { ); } - /// Total amount slashed from the genesis - pub fn slashed_amount(&self) -> u64 { - self.slashed_amount + /// Total amount burned since the genesis + pub fn burnt_amount(&self) -> u64 { + self.burnt_amount } /// Version of the stake contract @@ -386,8 +386,8 @@ impl StakeState { Self::deduct_contract_balance(to_slash); - // Update the total slashed amount - self.slashed_amount += to_slash; + // Update the total burnt amount + self.burnt_amount += to_slash; rusk_abi::emit( "hard_slash", @@ -402,9 +402,9 @@ impl StakeState { .or_insert((prev_value, *stake_pk)); } - /// Sets the slashed amount - pub fn set_slashed_amount(&mut self, slashed_amount: u64) { - self.slashed_amount = slashed_amount; + /// Sets the burnt amount + pub fn set_burnt_amount(&mut self, burnt_amount: u64) { + self.burnt_amount = burnt_amount; } /// Feeds the host with the stakes. From 3abdc1ca06df7ebc57bd650f3f0a058259f1da96 Mon Sep 17 00:00:00 2001 From: Herr Seppia Date: Mon, 1 Jul 2024 10:57:22 +0200 Subject: [PATCH 07/10] rusk: remove explicit slash amount --- rusk/src/lib/chain/rusk.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/rusk/src/lib/chain/rusk.rs b/rusk/src/lib/chain/rusk.rs index a43f995436..80e3f43f88 100644 --- a/rusk/src/lib/chain/rusk.rs +++ b/rusk/src/lib/chain/rusk.rs @@ -29,7 +29,7 @@ use rusk_abi::{ use rusk_profile::to_rusk_state_id_path; use tokio::sync::broadcast; -use super::{coinbase_value, emission_amount, Rusk, RuskTip}; +use super::{coinbase_value, Rusk, RuskTip}; use crate::http::RuesEvent; use crate::{Error, Result}; @@ -550,13 +550,11 @@ fn reward_slash_and_update_root( )?; events.extend(r.events); - let slash_amount = emission_amount(block_height); - for to_slash in slashing { let r = session.call::<_, ()>( STAKE_CONTRACT, "slash", - &(*to_slash, slash_amount), + &(*to_slash, None::), u64::MAX, )?; events.extend(r.events); From 06f97b1215e1bbccaf599840a2cdeaa995157cf9 Mon Sep 17 00:00:00 2001 From: Herr Seppia Date: Mon, 1 Jul 2024 12:15:56 +0200 Subject: [PATCH 08/10] rusk: change slash tests to new mechanism --- rusk/tests/services/stake.rs | 60 +++++++++++++++++++++++++++--------- 1 file changed, 45 insertions(+), 15 deletions(-) diff --git a/rusk/tests/services/stake.rs b/rusk/tests/services/stake.rs index 81492dc8dc..5791710999 100644 --- a/rusk/tests/services/stake.rs +++ b/rusk/tests/services/stake.rs @@ -291,6 +291,15 @@ pub async fn slash() -> Result<()> { assert_eq!(stake.reward, dusk(3.0)); assert_eq!(stake.amount, Some((dusk(20.0), 0))); + generator_procedure( + &rusk, + &[], + BLOCK_HEIGHT, + BLOCK_GAS_LIMIT, + vec![to_slash], + None, + ) + .expect("to work"); generator_procedure( &rusk, &[], @@ -307,11 +316,19 @@ pub async fn slash() -> Result<()> { assert_eq!(prev.reward, dusk(3.0)); assert_eq!(prev.amount, Some((dusk(20.0), 0))); + let (prev_stake, _) = prev.amount.unwrap(); + let slashed_amount = prev_stake / 10; + let after_slash = wallet.get_stake(0).unwrap(); - assert_eq!(after_slash.reward, 0); - assert_eq!(after_slash.amount, Some((dusk(20.0), 4320))); + assert_eq!(after_slash.reward, dusk(5.0)); + assert_eq!(after_slash.reward, prev.reward + slashed_amount); + assert_eq!( + after_slash.amount, + Some((prev_stake - slashed_amount, 4320)) + ); + assert_eq!(after_slash.amount, Some((dusk(18.0), 4320))); let new_balance = rusk.contract_balance(STAKE_CONTRACT).unwrap(); - assert_eq!(new_balance, contract_balance); + assert_eq!(new_balance, contract_balance - slashed_amount); let contract_balance = new_balance; generator_procedure( @@ -327,14 +344,24 @@ pub async fn slash() -> Result<()> { let last_changes = rusk.last_provisioners_change(None).unwrap(); let (_, prev) = last_changes.first().expect("Something changed").clone(); let prev = prev.expect("to have something"); - assert_eq!(prev.reward, 0); - assert_eq!(prev.amount, Some((dusk(20.0), 4320))); + assert_eq!(prev.reward, dusk(5.0)); + assert_eq!(prev.amount, Some((dusk(18.0), 4320))); + + let (prev_stake, _) = prev.amount.unwrap(); + // 20% slash + let slashed_amount = prev_stake / 10 * 2; let after_slash = wallet.get_stake(0).unwrap(); - assert_eq!(after_slash.reward, 0); - assert_eq!(after_slash.amount, Some((dusk(20.0), 4320))); + assert_eq!(after_slash.reward, dusk(8.6)); + assert_eq!(after_slash.reward, prev.reward + slashed_amount); + assert_eq!( + after_slash.amount, + Some((prev_stake - slashed_amount, 6480)) + ); + assert_eq!(after_slash.amount, Some((dusk(14.4), 6480))); + let new_balance = rusk.contract_balance(STAKE_CONTRACT).unwrap(); - assert_eq!(new_balance, contract_balance); + assert_eq!(new_balance, contract_balance - slashed_amount); let contract_balance = new_balance; generator_procedure( @@ -350,14 +377,17 @@ pub async fn slash() -> Result<()> { let last_changes = rusk.last_provisioners_change(None).unwrap(); let (_, prev) = last_changes.first().expect("Something changed").clone(); let prev = prev.expect("to have something"); - assert_eq!(prev.reward, 0); - assert_eq!(prev.amount, Some((dusk(20.0), 4320))); + assert_eq!(prev.reward, dusk(8.6)); + assert_eq!(prev.amount, Some((dusk(14.4), 6480))); + let (prev_stake, _) = prev.amount.unwrap(); + // 30% slash + let slashed_amount = prev_stake / 10 * 3; let after_slash = wallet.get_stake(0).unwrap(); - assert_eq!(after_slash.reward, 0); - assert_eq!(after_slash.amount, Some((dusk(20.0), 12960))); + assert_eq!(after_slash.reward, dusk(12.92)); + assert_eq!(after_slash.amount, Some((dusk(10.08), 17280))); let new_balance = rusk.contract_balance(STAKE_CONTRACT).unwrap(); - assert_eq!(new_balance, contract_balance); + assert_eq!(new_balance, contract_balance - slashed_amount); generator_procedure( &rusk, @@ -373,8 +403,8 @@ pub async fn slash() -> Result<()> { let last_changes = rusk.last_provisioners_change(None).unwrap(); let (_, prev) = last_changes.first().expect("Something changed").clone(); let prev = prev.expect("to have something"); - assert_eq!(prev.reward, 0); - assert_eq!(prev.amount, Some((dusk(20.0), 4320))); + assert_eq!(prev.reward, dusk(8.6)); + assert_eq!(prev.amount, Some((dusk(14.4), 6480))); generator_procedure(&rusk, &[], 9001, BLOCK_GAS_LIMIT, vec![], None) .expect("To work properly"); From 6c3ef0e30f2e1ff48e16f393e0034975c6b6bc9e Mon Sep 17 00:00:00 2001 From: Herr Seppia Date: Tue, 2 Jul 2024 17:37:02 +0200 Subject: [PATCH 09/10] stake-contract: remove duplicate checks Change the code to first perform suspension and then slash. This is required to satisfy the borrow checker --- contracts/stake/src/state.rs | 42 ++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/contracts/stake/src/state.rs b/contracts/stake/src/state.rs index bccf692498..d34475e48c 100644 --- a/contracts/stake/src/state.rs +++ b/contracts/stake/src/state.rs @@ -293,9 +293,8 @@ impl StakeState { .get_stake_mut(stake_pk) .expect("The stake to slash should exist"); + // Stake can have no amount if provisioner unstake in the same block if stake.amount().is_none() { - // stake.amount can be None if the provisioner unstake in the same - // block return; } @@ -305,7 +304,24 @@ impl StakeState { let effective_faults = stake.faults.saturating_sub(STAKE_WARNINGS) as u64; - let (stake_amount, _) = stake.amount.as_mut().expect("stake_to_exists"); + let (stake_amount, eligibility) = + stake.amount.as_mut().expect("stake_to_exists"); + + // Shift eligibility (aka stake suspension) only if warnings are + // saturated + if effective_faults > 0 { + // The stake is suspended for the rest of the current epoch plus + // effective_faults epochs + let to_shift = effective_faults * EPOCH; + *eligibility = next_epoch(rusk_abi::block_height()) + to_shift; + rusk_abi::emit( + "shifted", + StakingEvent { + public_key: *stake_pk, + value: *eligibility, + }, + ); + } // Slash the provided amount or calculate the percentage according to // effective faults @@ -329,26 +345,6 @@ impl StakeState { ); } - // Shift eligibility (aka stake suspension) only if warnings are - // saturated - if effective_faults > 0 { - // stake.amount can be None if the provisioner unstake in the same - // block - if let Some((_, eligibility)) = stake.amount.as_mut() { - // The stake is suspended for the rest of the current epoch plus - // effective_faults epochs - let to_shift = effective_faults * EPOCH; - *eligibility = next_epoch(rusk_abi::block_height()) + to_shift; - rusk_abi::emit( - "shifted", - StakingEvent { - public_key: *stake_pk, - value: *eligibility, - }, - ); - } - } - let key = stake_pk.to_bytes(); self.previous_block_state .entry(key) From f5d43a684d462059584b59ca3aff69af95cfe0d2 Mon Sep 17 00:00:00 2001 From: Herr Seppia Date: Tue, 2 Jul 2024 17:37:23 +0200 Subject: [PATCH 10/10] stake-contract: rename `shifted` event to `suspended` --- contracts/stake/src/state.rs | 2 +- contracts/stake/tests/events.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/contracts/stake/src/state.rs b/contracts/stake/src/state.rs index d34475e48c..92f47b1e69 100644 --- a/contracts/stake/src/state.rs +++ b/contracts/stake/src/state.rs @@ -315,7 +315,7 @@ impl StakeState { let to_shift = effective_faults * EPOCH; *eligibility = next_epoch(rusk_abi::block_height()) + to_shift; rusk_abi::emit( - "shifted", + "suspended", StakingEvent { public_key: *stake_pk, value: *eligibility, diff --git a/contracts/stake/tests/events.rs b/contracts/stake/tests/events.rs index 389505cbb6..da544b45fb 100644 --- a/contracts/stake/tests/events.rs +++ b/contracts/stake/tests/events.rs @@ -87,7 +87,7 @@ fn reward_slash() -> Result<(), Error> { // 10% of current amount let slash_amount = stake_amount / 10; assert_event(&receipt.events, "slash", &stake_pk, slash_amount); - assert_event(&receipt.events, "shifted", &stake_pk, 4320); + assert_event(&receipt.events, "suspended", &stake_pk, 4320); let receipt = session.call::<_, ()>( STAKE_CONTRACT, @@ -100,7 +100,7 @@ fn reward_slash() -> Result<(), Error> { // 20% of current amount let slash_amount = stake_amount / 100 * 20; assert_event(&receipt.events, "slash", &stake_pk, slash_amount); - assert_event(&receipt.events, "shifted", &stake_pk, 6480); + assert_event(&receipt.events, "suspended", &stake_pk, 6480); Ok(()) }