Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

stake-contract: events #2294

Merged
merged 2 commits into from
Sep 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 19 additions & 45 deletions contracts/stake/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand All @@ -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,
},
);
}
Expand Down Expand Up @@ -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
Expand All @@ -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)
Expand Down
47 changes: 35 additions & 12 deletions contracts/stake/tests/common/assert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};

Expand All @@ -27,17 +27,7 @@ pub fn assert_event<S>(
.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::<StakeWithReceiverEvent>(
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::<Vec<Reward>>(&event.data)
.expect("Reward event data should deserialize correctly");

Expand All @@ -55,3 +45,36 @@ pub fn assert_event<S>(
assert_eq!(staking_event_data.account.to_bytes(), should_pk.to_bytes());
}
}

pub fn assert_slash_event<S, E: Into<Option<u64>>>(
events: &Vec<Event>,
topic: S,
should_pk: &BlsPublicKey,
should_amount: u64,
should_eligibility: E,
) where
S: AsRef<str>,
{
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::<SlashEvent>(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<u64> = 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");
}
}
42 changes: 33 additions & 9 deletions contracts/stake/tests/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

pub mod common;

use common::assert::assert_slash_event;
use rand::rngs::StdRng;
use rand::SeedableRng;

Expand Down Expand Up @@ -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::<_, ()>(
Expand All @@ -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,
Expand All @@ -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(())
}
Expand Down Expand Up @@ -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%)
Expand All @@ -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)
Expand All @@ -196,7 +208,13 @@ fn stake_hard_slash() -> Result<(), PiecrustError> {
&(stake_pk, Some(hard_slash_amount), None::<u8>),
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 {
Expand All @@ -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(())
}
18 changes: 8 additions & 10 deletions execution-core/src/stake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use crate::{
PublicKey as BlsPublicKey, SecretKey as BlsSecretKey,
Signature as BlsSignature,
},
transfer::withdraw::{Withdraw as TransferWithdraw, WithdrawReceiver},
transfer::withdraw::Withdraw as TransferWithdraw,
ContractId,
};

Expand Down Expand Up @@ -196,22 +196,20 @@ impl Withdraw {
pub struct StakeEvent {
/// Account associated to the event.
pub account: BlsPublicKey,
/// Value of the relevant operation, be it `stake`, `reward` or `slash`.
///
/// In case of `suspended` the amount refers to the next eligibility
/// Value of the relevant operation, be it `stake`, `unstake`,`withdraw`
pub value: u64,
}

/// Event emitted after a stake contract operation is performed.
/// Event emitted after a slash operation is performed.
#[derive(Debug, Clone, Archive, Deserialize, Serialize)]
#[archive_attr(derive(CheckBytes))]
pub struct StakeWithReceiverEvent {
/// Account associated to the event.
pub struct SlashEvent {
/// Account slashed.
pub account: BlsPublicKey,
/// Value of the relevant operation, be it `unstake` or `withdraw`.
/// Slashed amount
pub value: u64,
/// The receiver of the action
pub receiver: Option<WithdrawReceiver>,
/// New eligibility for the slashed account
pub next_eligibility: u64,
}

/// The minimum amount of Dusk one can stake.
Expand Down
Loading