Skip to content

Commit

Permalink
Merge pull request #2292 from dusk-network/consolidate-reward
Browse files Browse the repository at this point in the history
Consolidate `reward` calls into one
  • Loading branch information
Eduardo Leegwater Simões authored Sep 6, 2024
2 parents 8d4aaef + a62a59f commit 7e52446
Show file tree
Hide file tree
Showing 8 changed files with 129 additions and 90 deletions.
4 changes: 2 additions & 2 deletions contracts/stake/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,9 @@ unsafe fn insert_stake(arg_len: u32) -> u32 {

#[no_mangle]
unsafe fn reward(arg_len: u32) -> u32 {
rusk_abi::wrap_call(arg_len, |(pk, value)| {
rusk_abi::wrap_call(arg_len, |arg| {
assert_external_caller();
STATE.reward(&pk, value);
STATE.reward(arg);
})
}

Expand Down
24 changes: 15 additions & 9 deletions contracts/stake/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@
// Copyright (c) DUSK NETWORK. All rights reserved.

use alloc::collections::BTreeMap;
use alloc::vec::Vec;

use core::cmp::min;

use dusk_bytes::Serializable;

use execution_core::{
signatures::bls::PublicKey as BlsPublicKey,
stake::{
next_epoch, Stake, StakeAmount, StakeData, StakeEvent,
next_epoch, Reward, Stake, StakeAmount, StakeData, StakeEvent,
StakeWithReceiverEvent, Withdraw, EPOCH, MINIMUM_STAKE, STAKE_CONTRACT,
STAKE_WARNINGS,
},
Expand Down Expand Up @@ -263,19 +265,23 @@ impl StakeState {

/// Rewards a `account` 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, account: &BlsPublicKey, value: u64) {
pub fn reward(&mut self, rewards: Vec<Reward>) {
// since we assure that reward is called at least once per block, this
// call is necessary to ensure that there are no inconsistencies in
// `prev_block_state`.
self.check_new_block();

let stake = self.load_or_create_stake_mut(account);
for reward in &rewards {
let stake = self.load_or_create_stake_mut(&reward.account);

// Reset faults counters
stake.faults = 0;
stake.hard_faults = 0;
// Reset faults counters
stake.faults = 0;
stake.hard_faults = 0;

stake.reward += value;
stake.reward += reward.value;
}

let account = *account;
rusk_abi::emit("reward", StakeEvent { account, value });
rusk_abi::emit("reward", rewards);
}

/// Total amount burned since the genesis
Expand Down
9 changes: 8 additions & 1 deletion 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::{StakeEvent, StakeWithReceiverEvent},
stake::{Reward, StakeEvent, StakeWithReceiverEvent},
Event,
};

Expand Down Expand Up @@ -37,6 +37,13 @@ pub fn assert_event<S>(
.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" {
let reward_event_data = rkyv::from_bytes::<Vec<Reward>>(&event.data)
.expect("Reward event data should deserialize correctly");

assert!(reward_event_data.iter().any(|reward| {
&reward.account == should_pk && reward.value == should_amount
}))
} else {
let staking_event_data =
check_archived_root::<StakeEvent>(event.data.as_slice())
Expand Down
30 changes: 17 additions & 13 deletions contracts/stake/tests/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use rand::SeedableRng;
use execution_core::{
dusk,
signatures::bls::{PublicKey as BlsPublicKey, SecretKey as BlsSecretKey},
stake::{StakeAmount, StakeData, STAKE_CONTRACT},
stake::{Reward, RewardReason, StakeAmount, StakeData, STAKE_CONTRACT},
transfer::{
phoenix::{
PublicKey as PhoenixPublicKey, SecretKey as PhoenixSecretKey,
Expand Down Expand Up @@ -72,12 +72,14 @@ fn reward_slash() -> Result<(), PiecrustError> {
u64::MAX,
)?;

let receipt = session.call::<_, ()>(
STAKE_CONTRACT,
"reward",
&(stake_pk, reward_amount),
u64::MAX,
)?;
let rewards = vec![Reward {
account: stake_pk,
value: reward_amount,
reason: RewardReason::Other,
}];

let receipt =
session.call::<_, ()>(STAKE_CONTRACT, "reward", &rewards, u64::MAX)?;
assert_event(&receipt.events, "reward", &stake_pk, reward_amount);

let receipt = session.call::<_, ()>(
Expand Down Expand Up @@ -197,12 +199,14 @@ fn stake_hard_slash() -> Result<(), PiecrustError> {
assert_event(&receipt.events, "hard_slash", &stake_pk, hard_slash_amount);
cur_balance -= hard_slash_amount;

let receipt = session.call::<_, ()>(
STAKE_CONTRACT,
"reward",
&(stake_pk, reward_amount),
u64::MAX,
)?;
let rewards = vec![Reward {
account: stake_pk,
value: reward_amount,
reason: RewardReason::Other,
}];

let receipt =
session.call::<_, ()>(STAKE_CONTRACT, "reward", &rewards, u64::MAX)?;
assert_event(&receipt.events, "reward", &stake_pk, reward_amount);

// Simple hard fault post-reward (slash 10%)
Expand Down
18 changes: 11 additions & 7 deletions contracts/stake/tests/stake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ use rand::SeedableRng;
use execution_core::{
dusk,
signatures::bls::{PublicKey as BlsPublicKey, SecretKey as BlsSecretKey},
stake::{Stake, StakeData, Withdraw as StakeWithdraw, STAKE_CONTRACT},
stake::{
Reward, RewardReason, Stake, StakeData, Withdraw as StakeWithdraw,
STAKE_CONTRACT,
},
transfer::{
data::ContractCall,
phoenix::{
Expand Down Expand Up @@ -132,13 +135,14 @@ fn stake_withdraw_unstake() {

const REWARD_AMOUNT: u64 = dusk(5.0);

let rewards = vec![Reward {
account: stake_pk,
value: REWARD_AMOUNT,
reason: RewardReason::Other,
}];

let receipt = session
.call::<_, ()>(
STAKE_CONTRACT,
"reward",
&(stake_pk, REWARD_AMOUNT),
POINT_LIMIT,
)
.call::<_, ()>(STAKE_CONTRACT, "reward", &rewards, POINT_LIMIT)
.expect("Rewarding a key should succeed");

assert_event(&receipt.events, "reward", &stake_pk, REWARD_AMOUNT);
Expand Down
2 changes: 2 additions & 0 deletions execution-core/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ stake::{
StakeData;
StakeEvent;
Withdraw;
Reward;
RewardReason;
EPOCH;
STAKE_CONTRACT;
STAKE_WARNINGS;
Expand Down
27 changes: 27 additions & 0 deletions execution-core/src/stake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -457,3 +457,30 @@ impl Serializable<STAKE_AMOUNT_SIZE> for StakeAmount {
buf
}
}

/// Used in a `reward` call to reward a given account with an amount of Dusk,
/// and emitted as an event, once a reward succeeds.
#[derive(Debug, Clone, PartialEq, Eq, Archive, Serialize, Deserialize)]
#[archive_attr(derive(CheckBytes))]
pub struct Reward {
/// The account to be rewarded.
pub account: BlsPublicKey,
/// The amount to reward.
pub value: u64,
/// The reason for the reward.
pub reason: RewardReason,
}

/// The reason that a reward is issued.
#[derive(Debug, Clone, PartialEq, Eq, Archive, Serialize, Deserialize)]
#[archive_attr(derive(CheckBytes))]
pub enum RewardReason {
/// The fixed amount awarded to a generator.
GeneratorFixed,
/// Extra amount awarded to a generator.
GeneratorExtra,
/// Amount awarded to a voter.
Voter,
/// Amount awarded for another reason, such as rewarding Dusk.
Other,
}
105 changes: 47 additions & 58 deletions rusk/src/lib/node/rusk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use sha3::{Digest, Sha3_256};
use tokio::task;
use tracing::{debug, info, warn};

use dusk_bytes::{DeserializableSlice, Serializable};
use dusk_bytes::DeserializableSlice;
use dusk_consensus::config::{
ratification_extra, ratification_quorum, validation_extra,
validation_quorum, RATIFICATION_COMMITTEE_CREDITS,
Expand All @@ -24,7 +24,7 @@ use dusk_consensus::config::{
use dusk_consensus::operations::{CallParams, VerificationOutput, Voter};
use execution_core::{
signatures::bls::PublicKey as BlsPublicKey,
stake::{StakeData, STAKE_CONTRACT},
stake::{Reward, RewardReason, StakeData, STAKE_CONTRACT},
transfer::{
data::{ContractBytecode, ContractDeploy},
moonlight::AccountData,
Expand Down Expand Up @@ -699,12 +699,8 @@ fn reward_slash_and_update_root(
slashing: Vec<Slash>,
voters: Option<&[Voter]>,
) -> Result<Vec<Event>> {
let (
dusk_value,
generator_fixed_reward,
generator_extra_reward,
voters_reward,
) = coinbase_value(block_height, dusk_spent);
let (dusk_value, generator_reward, generator_extra_reward, voters_reward) =
coinbase_value(block_height, dusk_spent);

let credits = voters
.unwrap_or_default()
Expand All @@ -716,40 +712,43 @@ fn reward_slash_and_update_root(
return Err(InvalidCreditsCount(block_height, 0));
}

let r = session.call::<_, ()>(
STAKE_CONTRACT,
"reward",
&(*DUSK_KEY, dusk_value),
u64::MAX,
)?;

let mut events = r.events;
let generator_extra_reward =
calc_generator_extra_reward(generator_extra_reward, credits);

debug!(
event = "Dusk rewarded",
voter = to_bs58(&DUSK_KEY),
reward = dusk_value
);
// We first start with only the generator (fixed) and Dusk
let mut num_rewards = 2;

let generator_curr_extra_reward =
calc_generator_extra_reward(generator_extra_reward, credits);
// If there is an extra reward we add it.
if generator_extra_reward != 0 {
num_rewards += 1;
}

let generator_reward = generator_fixed_reward + generator_curr_extra_reward;
let r = session.call::<_, ()>(
STAKE_CONTRACT,
"reward",
&(*generator, generator_reward),
u64::MAX,
)?;
events.extend(r.events);
// Additionally we also reward the voters.
if let Some(voters) = &voters {
num_rewards += voters.len();
}

debug!(
event = "generator rewarded",
generator = to_bs58(generator),
total_reward = generator_reward,
extra_reward = generator_curr_extra_reward,
credits,
);
let mut rewards = Vec::with_capacity(num_rewards);

rewards.push(Reward {
account: *generator,
value: generator_reward,
reason: RewardReason::GeneratorFixed,
});

rewards.push(Reward {
account: *DUSK_KEY,
value: dusk_value,
reason: RewardReason::Other,
});

if generator_extra_reward != 0 {
rewards.push(Reward {
account: *generator,
value: generator_extra_reward,
reason: RewardReason::GeneratorExtra,
});
}

let credit_reward = voters_reward
/ (VALIDATION_COMMITTEE_CREDITS + RATIFICATION_COMMITTEE_CREDITS)
Expand All @@ -758,22 +757,18 @@ fn reward_slash_and_update_root(
for (to_voter, credits) in voters.unwrap_or_default() {
let voter = to_voter.inner();
let voter_reward = *credits as u64 * credit_reward;
let r = session.call::<_, ()>(
STAKE_CONTRACT,
"reward",
&(*voter, voter_reward),
u64::MAX,
)?;
events.extend(r.events);

debug!(
event = "validator of prev block rewarded",
voter = to_bs58(voter),
credits = *credits,
reward = voter_reward
)
rewards.push(Reward {
account: *voter,
value: voter_reward,
reason: RewardReason::Voter,
});
}

let r =
session.call::<_, ()>(STAKE_CONTRACT, "reward", &rewards, u64::MAX)?;

let mut events = r.events;

events.extend(slash(session, slashing)?);

let r = session.call::<_, ()>(
Expand Down Expand Up @@ -806,12 +801,6 @@ fn calc_generator_extra_reward(
credits.saturating_sub(sum as u64) * reward_per_quota
}

fn to_bs58(pk: &BlsPublicKey) -> String {
let mut pk = bs58::encode(&pk.to_bytes()).into_string();
pk.truncate(16);
pk
}

fn slash(session: &mut Session, slash: Vec<Slash>) -> Result<Vec<Event>> {
let mut events = vec![];
for s in slash {
Expand Down

0 comments on commit 7e52446

Please sign in to comment.