diff --git a/contracts/stake/src/state.rs b/contracts/stake/src/state.rs index 84b3a5e4b2..4414b5d12b 100644 --- a/contracts/stake/src/state.rs +++ b/contracts/stake/src/state.rs @@ -14,8 +14,8 @@ use dusk_bytes::Serializable; use execution_core::{ signatures::bls::PublicKey as BlsPublicKey, stake::{ - next_epoch, Reward, Stake, StakeAmount, StakeData, StakeEvent, - StakeWithReceiverEvent, Withdraw, EPOCH, MINIMUM_STAKE, STAKE_CONTRACT, + next_epoch, Reward, SlashEvent, Stake, StakeAmount, StakeData, + StakeEvent, Withdraw, EPOCH, MINIMUM_STAKE, STAKE_CONTRACT, STAKE_WARNINGS, }, transfer::TRANSFER_CONTRACT, @@ -165,14 +165,7 @@ impl StakeState { // update the state accordingly loaded_stake.amount = None; - rusk_abi::emit( - "unstake", - StakeWithReceiverEvent { - account, - value: withdrawal_value, - receiver: Some(*transfer_withdraw.receiver()), - }, - ); + rusk_abi::emit("unstake", StakeEvent { account, value }); let key = account.to_bytes(); self.previous_block_state @@ -214,14 +207,7 @@ impl StakeState { // update the state accordingly loaded_stake.reward -= value; - rusk_abi::emit( - "withdraw", - StakeWithReceiverEvent { - account, - value, - receiver: Some(*transfer_withdraw.receiver()), - }, - ); + rusk_abi::emit("withdraw", StakeEvent { account, value }); } /// Gets a reference to a stake. @@ -327,14 +313,6 @@ impl StakeState { stake_amount.eligibility = next_epoch(rusk_abi::block_height()) + to_shift; - - rusk_abi::emit( - "suspended", - StakeEvent { - account: *account, - value: stake_amount.eligibility, - }, - ); } // Slash the provided amount or calculate the percentage according to @@ -345,12 +323,15 @@ impl StakeState { if to_slash > 0 { stake_amount.lock_amount(to_slash); + } + if to_slash > 0 || effective_faults > 0 { rusk_abi::emit( "slash", - StakeEvent { + SlashEvent { account: *account, value: to_slash, + next_eligibility: stake_amount.eligibility, }, ); } @@ -393,16 +374,8 @@ impl StakeState { // The stake is shifted (aka suspended) for the rest of the current // epoch plus hard_faults epochs let to_shift = hard_faults * EPOCH; - stake_amount.eligibility = - next_epoch(rusk_abi::block_height()) + to_shift; - - rusk_abi::emit( - "suspended", - StakeEvent { - account: *account, - value: stake_amount.eligibility, - }, - ); + let next_eligibility = next_epoch(rusk_abi::block_height()) + to_shift; + stake_amount.eligibility = next_eligibility; // Slash the provided amount or calculate the percentage according to // hard faults @@ -417,16 +390,17 @@ impl StakeState { // Update the total burnt amount self.burnt_amount += to_slash; - - rusk_abi::emit( - "hard_slash", - StakeEvent { - account: *account, - value: to_slash, - }, - ); } + rusk_abi::emit( + "hard_slash", + SlashEvent { + account: *account, + value: to_slash, + next_eligibility, + }, + ); + let key = account.to_bytes(); self.previous_block_state .entry(key) diff --git a/contracts/stake/tests/common/assert.rs b/contracts/stake/tests/common/assert.rs index bbbac2580e..b9bd456428 100644 --- a/contracts/stake/tests/common/assert.rs +++ b/contracts/stake/tests/common/assert.rs @@ -9,7 +9,7 @@ use rkyv::{check_archived_root, Deserialize, Infallible}; use execution_core::{ signatures::bls::PublicKey as BlsPublicKey, - stake::{Reward, StakeEvent, StakeWithReceiverEvent}, + stake::{Reward, SlashEvent, StakeEvent}, Event, }; @@ -27,17 +27,7 @@ pub fn assert_event( .find(|e| e.topic == topic) .expect(&format!("event: {topic} should exist in the event list",)); - if topic == "unstake" || topic == "withdraw" { - let staking_event_data = check_archived_root::( - event.data.as_slice(), - ) - .expect("Stake event data should deserialize correctly"); - let staking_event_data: StakeWithReceiverEvent = staking_event_data - .deserialize(&mut Infallible) - .expect("Infallible"); - assert_eq!(staking_event_data.value, should_amount); - assert_eq!(staking_event_data.account.to_bytes(), should_pk.to_bytes()); - } else if topic == "reward" { + if topic == "reward" { let reward_event_data = rkyv::from_bytes::>(&event.data) .expect("Reward event data should deserialize correctly"); @@ -55,3 +45,36 @@ pub fn assert_event( assert_eq!(staking_event_data.account.to_bytes(), should_pk.to_bytes()); } } + +pub fn assert_slash_event>>( + events: &Vec, + topic: S, + should_pk: &BlsPublicKey, + should_amount: u64, + should_eligibility: E, +) where + S: AsRef, +{ + let topic = topic.as_ref(); + let event = events + .iter() + .find(|e| e.topic == topic) + .expect(&format!("event: {topic} should exist in the event list",)); + + if topic == "slash" || topic == "hard_slash" { + let staking_event_data = + check_archived_root::(event.data.as_slice()) + .expect("Stake event data should deserialize correctly"); + let staking_event_data: SlashEvent = staking_event_data + .deserialize(&mut Infallible) + .expect("Infallible"); + assert_eq!(staking_event_data.value, should_amount); + assert_eq!(staking_event_data.account.to_bytes(), should_pk.to_bytes()); + let should_eligibility: Option = should_eligibility.into(); + if let Some(should_eligibility) = should_eligibility { + assert_eq!(staking_event_data.next_eligibility, should_eligibility); + } + } else { + panic!("{topic} topic cannot be verified with assert_slash_event"); + } +} diff --git a/contracts/stake/tests/events.rs b/contracts/stake/tests/events.rs index 0bc0ccaa25..d9aaea48ec 100644 --- a/contracts/stake/tests/events.rs +++ b/contracts/stake/tests/events.rs @@ -6,6 +6,7 @@ pub mod common; +use common::assert::assert_slash_event; use rand::rngs::StdRng; use rand::SeedableRng; @@ -89,7 +90,7 @@ fn reward_slash() -> Result<(), PiecrustError> { u64::MAX, )?; assert!(receipt.events.len() == 1, "No shift at first warn"); - assert_event(&receipt.events, "slash", &stake_pk, slash_amount); + assert_slash_event(&receipt.events, "slash", &stake_pk, slash_amount, None); let stake_amount = stake_amount - slash_amount; let receipt = session.call::<_, ()>( @@ -100,8 +101,7 @@ fn reward_slash() -> Result<(), PiecrustError> { )?; // 10% of current amount let slash_amount = stake_amount / 10; - assert_event(&receipt.events, "slash", &stake_pk, slash_amount); - assert_event(&receipt.events, "suspended", &stake_pk, 4320); + assert_slash_event(&receipt.events, "slash", &stake_pk, slash_amount, 4320); let receipt = session.call::<_, ()>( STAKE_CONTRACT, @@ -113,8 +113,7 @@ fn reward_slash() -> Result<(), PiecrustError> { // 20% of current amount let slash_amount = stake_amount / 100 * 20; - assert_event(&receipt.events, "slash", &stake_pk, slash_amount); - assert_event(&receipt.events, "suspended", &stake_pk, 6480); + assert_slash_event(&receipt.events, "slash", &stake_pk, slash_amount, 6480); Ok(()) } @@ -175,7 +174,14 @@ fn stake_hard_slash() -> Result<(), PiecrustError> { u64::MAX, )?; let expected_slash = stake_amount / 100 * 10; - assert_event(&receipt.events, "hard_slash", &stake_pk, expected_slash); + assert_slash_event( + &receipt.events, + "hard_slash", + &stake_pk, + expected_slash, + None, + ); + println!("f1"); cur_balance -= expected_slash; // Severe hard fault (slash 30%) @@ -186,7 +192,13 @@ fn stake_hard_slash() -> Result<(), PiecrustError> { u64::MAX, )?; let expected_slash = cur_balance / 100 * (1 + severity) * 10; - assert_event(&receipt.events, "hard_slash", &stake_pk, expected_slash); + assert_slash_event( + &receipt.events, + "hard_slash", + &stake_pk, + expected_slash, + None, + ); cur_balance -= expected_slash; // Direct slash (slash hard_slash_amount) @@ -196,7 +208,13 @@ fn stake_hard_slash() -> Result<(), PiecrustError> { &(stake_pk, Some(hard_slash_amount), None::), u64::MAX, )?; - assert_event(&receipt.events, "hard_slash", &stake_pk, hard_slash_amount); + assert_slash_event( + &receipt.events, + "hard_slash", + &stake_pk, + hard_slash_amount, + None, + ); cur_balance -= hard_slash_amount; let rewards = vec![Reward { @@ -218,7 +236,13 @@ fn stake_hard_slash() -> Result<(), PiecrustError> { u64::MAX, )?; let expected_slash = cur_balance / 100 * 10; - assert_event(&receipt.events, "hard_slash", &stake_pk, expected_slash); + assert_slash_event( + &receipt.events, + "hard_slash", + &stake_pk, + expected_slash, + None, + ); Ok(()) }