Skip to content

Commit

Permalink
Merge #4321
Browse files Browse the repository at this point in the history
4321: Merge feat 2.0 r=Boiethios a=Boiethios

Merge the feature branch with rewarded signatures with 2.0
To make that possible, the block header is now versioned.

Co-authored-by: Félix <felix@casperlabs.io>
Co-authored-by: Bartłomiej Kamiński <bart@casperlabs.io>
Co-authored-by: Félix / Felix <Boiethios@users.noreply.github.com>
Co-authored-by: Alexander Limonov <51098682+AlexanderLimonov@users.noreply.github.com>
Co-authored-by: Luca B <93586856+bradjohnl@users.noreply.github.com>
  • Loading branch information
6 people committed Oct 16, 2023
2 parents d27d8dc + e5620ed commit a054bd5
Show file tree
Hide file tree
Showing 88 changed files with 5,562 additions and 1,992 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ lint-smart-contracts:

.PHONY: audit-rs
audit-rs:
$(CARGO) audit --ignore RUSTSEC-2022-0093 --ignore RUSTSEC-2023-0044
$(CARGO) audit

.PHONY: audit-as
audit-as:
Expand Down
106 changes: 57 additions & 49 deletions execution_engine/src/engine_state/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ use casper_types::{
system::{
auction::{
BidAddr, BidKind, EraValidators, UnbondingPurse, ValidatorBid, WithdrawPurse,
ARG_ERA_END_TIMESTAMP_MILLIS, ARG_EVICTED_VALIDATORS, ARG_VALIDATOR,
ARG_ERA_END_TIMESTAMP_MILLIS, ARG_EVICTED_VALIDATORS, ARG_REWARDS_MAP,
ARG_VALIDATOR_PUBLIC_KEYS, AUCTION_DELAY_KEY, ERA_ID_KEY, LOCKED_FUNDS_PERIOD_KEY,
SEIGNIORAGE_RECIPIENTS_SNAPSHOT_KEY, UNBONDING_DELAY_KEY, VALIDATOR_SLOTS_KEY,
},
Expand Down Expand Up @@ -2295,7 +2295,7 @@ where
&self,
pre_state_hash: Digest,
protocol_version: ProtocolVersion,
proposer: PublicKey,
rewards: &BTreeMap<PublicKey, U512>,
next_block_height: u64,
time: u64,
) -> Result<Digest, StepError> {
Expand All @@ -2305,16 +2305,15 @@ where
Ok(Some(tracking_copy)) => Rc::new(RefCell::new(tracking_copy)),
};

let mut runtime_args = RuntimeArgs::new();
runtime_args.insert(ARG_VALIDATOR, proposer)?;

let executor = Executor::new(self.config().clone());

let system_account_addr = PublicKey::System.to_account_hash();
let virtual_system_contract_by_account = {
let system_account_addr = PublicKey::System.to_account_hash();

let virtual_system_contract_by_account = tracking_copy
.borrow_mut()
.get_addressable_entity_by_account_hash(protocol_version, system_account_addr)?;
tracking_copy
.borrow_mut()
.get_addressable_entity_by_account_hash(protocol_version, system_account_addr)?
};

let authorization_keys = {
let mut ret = BTreeSet::new();
Expand All @@ -2331,52 +2330,61 @@ where
DeployHash::new(Digest::hash(&bytes))
};

let distribute_accumulated_fees_stack = self.get_new_system_call_stack();
let system_account_hash = PublicKey::System.to_account_hash();
let (_, execution_result): (Option<()>, ExecutionResult) = executor.call_system_contract(
DirectSystemContractCall::DistributeAccumulatedFees,
RuntimeArgs::default(),
&virtual_system_contract_by_account,
ContractPackageKind::Account(system_account_hash),
authorization_keys.clone(),
system_account_hash,
BlockTime::default(),
deploy_hash,
gas_limit,
protocol_version,
Rc::clone(&tracking_copy),
Phase::Session,
distribute_accumulated_fees_stack,
// There should be no tokens transferred during rewards distribution.
U512::zero(),
);

if let Some(exec_error) = execution_result.take_error() {
return Err(StepError::DistributeAccumulatedFeesError(exec_error));
{
let distribute_accumulated_fees_stack = self.get_new_system_call_stack();
let (_, execution_result): (Option<()>, ExecutionResult) = executor
.call_system_contract(
DirectSystemContractCall::DistributeAccumulatedFees,
RuntimeArgs::default(),
&virtual_system_contract_by_account,
ContractPackageKind::Account(system_account_hash),
authorization_keys.clone(),
system_account_hash,
BlockTime::default(),
deploy_hash,
gas_limit,
protocol_version,
Rc::clone(&tracking_copy),
Phase::Session,
distribute_accumulated_fees_stack,
// There should be no tokens transferred during rewards distribution.
U512::zero(),
);

if let Some(exec_error) = execution_result.take_error() {
return Err(StepError::DistributeAccumulatedFeesError(exec_error));
}
}

let distribute_rewards_stack = self.get_new_system_call_stack();
{
let mut runtime_args = RuntimeArgs::new();
runtime_args.insert(ARG_REWARDS_MAP, rewards)?;
let distribute_rewards_stack = self.get_new_system_call_stack();

let (_, execution_result): (Option<()>, ExecutionResult) = executor.call_system_contract(
DirectSystemContractCall::DistributeRewards,
runtime_args,
&virtual_system_contract_by_account,
ContractPackageKind::Account(system_account_hash),
authorization_keys,
system_account_hash,
BlockTime::default(),
deploy_hash,
gas_limit,
protocol_version,
Rc::clone(&tracking_copy),
Phase::Session,
distribute_rewards_stack,
// There should be no tokens transferred during rewards distribution.
U512::zero(),
);
let (_, execution_result): (Option<()>, ExecutionResult) = executor
.call_system_contract(
DirectSystemContractCall::DistributeRewards,
runtime_args,
&virtual_system_contract_by_account,
ContractPackageKind::Account(system_account_hash),
authorization_keys,
system_account_hash,
BlockTime::default(),
deploy_hash,
gas_limit,
protocol_version,
Rc::clone(&tracking_copy),
Phase::Session,
distribute_rewards_stack,
// There should be no tokens transferred during rewards distribution.
U512::zero(),
);

if let Some(exec_error) = execution_result.take_error() {
return Err(StepError::DistributeError(exec_error));
if let Some(exec_error) = execution_result.take_error() {
return Err(StepError::DistributeError(exec_error));
}
}

let effects = tracking_copy.borrow().effects();
Expand Down
4 changes: 2 additions & 2 deletions execution_engine/src/runtime/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -925,8 +925,8 @@ where
runtime
.context
.charge_gas(auction_costs.distribute.into())?;
let proposer = Self::get_named_argument(runtime_args, auction::ARG_VALIDATOR)?;
runtime.distribute(proposer).map_err(Self::reverter)?;
let rewards = Self::get_named_argument(runtime_args, auction::ARG_REWARDS_MAP)?;
runtime.distribute(rewards).map_err(Self::reverter)?;
CLValue::from_t(()).map_err(Self::reverter)
})(),

Expand Down
147 changes: 68 additions & 79 deletions execution_engine/src/system/auction.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
pub(crate) mod detail;
pub(crate) mod providers;

use std::collections::BTreeMap;

use num_rational::Ratio;
use num_traits::{CheckedMul, CheckedSub};
use tracing::debug;
Expand Down Expand Up @@ -142,6 +144,7 @@ pub trait Auction:
let validator_bid_addr = BidAddr::from(public_key.clone());
let validator_bid_key = validator_bid_addr.into();
let mut validator_bid = read_validator_bid(self, &validator_bid_key)?;
let initial_amount = validator_bid.staked_amount();

// An attempt to unbond more than is staked results in unbonding the staked amount.
let unbonding_amount = U512::min(amount, validator_bid.staked_amount());
Expand All @@ -161,10 +164,7 @@ pub trait Auction:

debug!(
"withdrawing bid for {} reducing {} by {} to {}",
validator_bid_addr,
validator_bid.staked_amount(),
unbonding_amount,
updated_stake
validator_bid_addr, initial_amount, unbonding_amount, updated_stake
);
if updated_stake.is_zero() {
// Unbond all delegators and zero them out
Expand Down Expand Up @@ -587,95 +587,84 @@ pub trait Auction:
/// according to `reward_factors` returned by the consensus component.
// TODO: rework EraInfo and other related structs, methods, etc. to report correct era-end
// totals of per-block rewards
fn distribute(&mut self, proposer: PublicKey) -> Result<(), Error> {
fn distribute(&mut self, rewards: BTreeMap<PublicKey, U512>) -> Result<(), Error> {
if self.get_caller() != PublicKey::System.to_account_hash() {
return Err(Error::InvalidCaller);
}
if proposer == PublicKey::System {
// systemically generated blocks (such as immediate switch blocks at genesis and
// upgrade boundaries) do not produce rewards, therefore there is nothing to distribute
// and a call to this function is effectively a noop.
return Ok(());
}

let seigniorage_recipients = self.read_seigniorage_recipients()?;

let mut era_info = EraInfo::new();
let seigniorage_allocations = era_info.seigniorage_allocations_mut();

let recipient = seigniorage_recipients
.get(&proposer)
.ok_or(Error::ValidatorNotFound)?;

let total_stake = recipient.total_stake().ok_or(Error::ArithmeticOverflow)?;

let total_reward = if self.should_compute_rewards() {
let base_round_reward = self.read_base_round_reward()?;

Ratio::from(base_round_reward)
} else {
Ratio::from(U512::zero())
};

let delegator_total_stake: U512 = recipient
.delegator_total_stake()
.ok_or(Error::ArithmeticOverflow)?;

let delegators_part: Ratio<U512> = {
let commission_rate = Ratio::new(
U512::from(*recipient.delegation_rate()),
U512::from(DELEGATION_RATE_DENOMINATOR),
);
let reward_multiplier: Ratio<U512> = Ratio::new(delegator_total_stake, total_stake);
let delegator_reward: Ratio<U512> = total_reward
.checked_mul(&reward_multiplier)
.ok_or(Error::ArithmeticOverflow)?;
let commission: Ratio<U512> = delegator_reward
.checked_mul(&commission_rate)
for (proposer, reward_amount) in rewards
.into_iter()
.filter(|(key, _amount)| key != &PublicKey::System)
{
let total_reward = Ratio::from(reward_amount);
let recipient = seigniorage_recipients
.get(&proposer)
.ok_or(Error::ValidatorNotFound)?;

let total_stake = recipient.total_stake().ok_or(Error::ArithmeticOverflow)?;
let delegator_total_stake: U512 = recipient
.delegator_total_stake()
.ok_or(Error::ArithmeticOverflow)?;
delegator_reward
.checked_sub(&commission)
.ok_or(Error::ArithmeticOverflow)?
};

let delegator_rewards =
recipient
.delegator_stake()
.iter()
.map(|(delegator_key, delegator_stake)| {
let reward_multiplier = Ratio::new(*delegator_stake, delegator_total_stake);
let reward = delegators_part * reward_multiplier;
(delegator_key.clone(), reward)
});

let delegator_payouts = detail::distribute_delegator_rewards(
self,
seigniorage_allocations,
proposer.clone(),
delegator_rewards,
)?;

let total_delegator_payout: U512 = delegator_payouts
.iter()
.map(|(_delegator_hash, amount, _bonding_purse)| *amount)
.sum();
let delegators_part: Ratio<U512> = {
let commission_rate = Ratio::new(
U512::from(*recipient.delegation_rate()),
U512::from(DELEGATION_RATE_DENOMINATOR),
);
let reward_multiplier: Ratio<U512> = Ratio::new(delegator_total_stake, total_stake);
let delegator_reward: Ratio<U512> = total_reward
.checked_mul(&reward_multiplier)
.ok_or(Error::ArithmeticOverflow)?;
let commission: Ratio<U512> = delegator_reward
.checked_mul(&commission_rate)
.ok_or(Error::ArithmeticOverflow)?;
delegator_reward
.checked_sub(&commission)
.ok_or(Error::ArithmeticOverflow)?
};

let delegator_rewards =
recipient
.delegator_stake()
.iter()
.map(|(delegator_key, delegator_stake)| {
let reward_multiplier = Ratio::new(*delegator_stake, delegator_total_stake);
let reward = delegators_part * reward_multiplier;
(delegator_key.clone(), reward)
});

let delegator_payouts = detail::distribute_delegator_rewards(
self,
seigniorage_allocations,
proposer.clone(),
delegator_rewards,
)?;

let validators_part: Ratio<U512> = total_reward - Ratio::from(total_delegator_payout);
let validator_reward = validators_part.to_integer();
let validator_bonding_purse = detail::distribute_validator_rewards(
self,
seigniorage_allocations,
proposer.clone(),
validator_reward,
)?;
let total_delegator_payout: U512 = delegator_payouts
.iter()
.map(|(_delegator_hash, amount, _bonding_purse)| *amount)
.sum();

// mint new token and put it to the recipients' purses
self.mint_into_existing_purse(validator_reward, validator_bonding_purse)
.map_err(Error::from)?;
let validator_reward = reward_amount - total_delegator_payout;
let validator_bonding_purse = detail::distribute_validator_rewards(
self,
seigniorage_allocations,
proposer.clone(),
validator_reward,
)?;

for (_delegator_account_hash, delegator_payout, bonding_purse) in delegator_payouts {
self.mint_into_existing_purse(delegator_payout, bonding_purse)
// mint new token and put it to the recipients' purses
self.mint_into_existing_purse(validator_reward, validator_bonding_purse)
.map_err(Error::from)?;

for (_delegator_account_hash, delegator_payout, bonding_purse) in delegator_payouts {
self.mint_into_existing_purse(delegator_payout, bonding_purse)
.map_err(Error::from)?;
}
}

// record allocations for this era for reporting purposes.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -861,15 +861,15 @@ where
&mut self,
pre_state_hash: Option<Digest>,
protocol_version: ProtocolVersion,
proposer: PublicKey,
rewards: &BTreeMap<PublicKey, U512>,
next_block_height: u64,
time: u64,
) -> Result<Digest, StepError> {
let pre_state_hash = pre_state_hash.or(self.post_state_hash).unwrap();
let post_state_hash = self.engine_state.distribute_block_rewards(
pre_state_hash,
protocol_version,
proposer,
rewards,
next_block_height,
time,
)?;
Expand All @@ -880,6 +880,7 @@ where
}

/// Expects a successful run
#[track_caller]
pub fn expect_success(&mut self) -> &mut Self {
// Check first result, as only first result is interesting for a simple test
let exec_results = self
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ fn should_distribute_accumulated_fees_to_admins() {
.distribute(
None,
*DEFAULT_PROTOCOL_VERSION,
VALIDATOR_1_PUBLIC_KEY.clone(),
&IntoIterator::into_iter([(VALIDATOR_1_PUBLIC_KEY.clone(), U512::from(0))]).collect(),
1,
DEFAULT_BLOCK_TIME,
)
Expand Down
Loading

0 comments on commit a054bd5

Please sign in to comment.