mod bid;
mod constants;
mod detail;
mod providers;
mod seigniorage_recipient;
mod types;
mod unbonding_purse;
use alloc::{collections::BTreeMap, vec::Vec};
use num_rational::Ratio;
use crate::{
account::AccountHash,
system_contract_errors::auction::{Error, Result},
Key, PublicKey, URef, U512,
};
pub use bid::Bid;
pub use constants::*;
pub use providers::{MintProvider, RuntimeProvider, StorageProvider, SystemProvider};
pub use seigniorage_recipient::SeigniorageRecipient;
pub use types::*;
pub use unbonding_purse::UnbondingPurse;
pub trait Auction:
StorageProvider + SystemProvider + RuntimeProvider + MintProvider + Sized
{
fn get_era_validators(&mut self) -> Result<EraValidators> {
let era_validators = detail::get_era_validators(self)?;
Ok(era_validators)
}
fn read_seigniorage_recipients(&mut self) -> Result<SeigniorageRecipients> {
let era_index = detail::get_era_id(self)?;
let mut seigniorage_recipients_snapshot =
detail::get_seigniorage_recipients_snapshot(self)?;
let seigniorage_recipients = seigniorage_recipients_snapshot
.remove(&era_index)
.unwrap_or_else(|| panic!("No seigniorage_recipients for era {}", era_index));
Ok(seigniorage_recipients)
}
fn add_bid(
&mut self,
public_key: PublicKey,
source: URef,
delegation_rate: DelegationRate,
amount: U512,
) -> Result<U512> {
let account_hash = AccountHash::from_public_key(public_key, |x| self.blake2b(x));
if self.get_caller() != account_hash {
return Err(Error::InvalidPublicKey);
}
let (bonding_purse, _total_amount) = detail::bond(self, public_key, source, amount)?;
let mut validators = detail::get_bids(self)?;
let bid = validators
.entry(public_key)
.and_modify(|bid| {
bid.bonding_purse = bonding_purse;
bid.delegation_rate = delegation_rate;
bid.staked_amount += amount;
})
.or_insert_with(|| {
Bid {
bonding_purse,
staked_amount: amount,
delegation_rate,
funds_locked: None,
}
});
let new_amount = bid.staked_amount;
detail::set_bids(self, validators)?;
Ok(new_amount)
}
fn withdraw_bid(
&mut self,
public_key: PublicKey,
amount: U512,
unbond_purse: URef,
) -> Result<U512> {
let account_hash = AccountHash::from_public_key(public_key, |x| self.blake2b(x));
if self.get_caller() != account_hash {
return Err(Error::InvalidPublicKey);
}
let mut bids = detail::get_bids(self)?;
let bid = bids.get_mut(&public_key).ok_or(Error::ValidatorNotFound)?;
let new_amount = if bid.can_withdraw_funds() {
let new_amount = bid
.staked_amount
.checked_sub(amount)
.ok_or(Error::InvalidAmount)?;
bid.staked_amount = new_amount;
new_amount
} else {
return Err(Error::ValidatorFundsLocked);
};
if new_amount.is_zero() {
bids.remove(&public_key).unwrap();
}
detail::set_bids(self, bids)?;
let _total_amount = detail::unbond(self, public_key, amount, unbond_purse)?;
Ok(new_amount)
}
fn delegate(
&mut self,
delegator_public_key: PublicKey,
source: URef,
validator_public_key: PublicKey,
amount: U512,
) -> Result<U512> {
let account_hash = AccountHash::from_public_key(delegator_public_key, |x| self.blake2b(x));
if self.get_caller() != account_hash {
return Err(Error::InvalidPublicKey);
}
let bids = detail::get_bids(self)?;
if !bids.contains_key(&validator_public_key) {
return Err(Error::ValidatorNotFound);
}
let (_bonding_purse, _total_amount) =
detail::bond(self, delegator_public_key, source, amount)?;
let new_delegation_amount =
detail::update_delegators(self, validator_public_key, delegator_public_key, amount)?;
{
let mut delegator_reward_map = detail::get_delegator_reward_map(self)?;
delegator_reward_map
.entry(validator_public_key)
.or_default()
.entry(delegator_public_key)
.or_insert_with(U512::zero);
detail::set_delegator_reward_map(self, delegator_reward_map)?;
}
Ok(new_delegation_amount)
}
fn undelegate(
&mut self,
delegator_public_key: PublicKey,
validator_public_key: PublicKey,
amount: U512,
unbonding_purse: URef,
) -> Result<U512> {
let account_hash = AccountHash::from_public_key(delegator_public_key, |x| self.blake2b(x));
if self.get_caller() != account_hash {
return Err(Error::InvalidPublicKey);
}
let bids = detail::get_bids(self)?;
if !bids.contains_key(&validator_public_key) {
return Err(Error::ValidatorNotFound);
}
let _unbonding_purse_balance =
detail::unbond(self, delegator_public_key, amount, unbonding_purse)?;
let mut delegators = detail::get_delegators(self)?;
let delegators_map = delegators
.get_mut(&validator_public_key)
.ok_or(Error::ValidatorNotFound)?;
let new_amount = {
let delegators_amount = delegators_map
.get_mut(&delegator_public_key)
.ok_or(Error::DelegatorNotFound)?;
let new_amount = delegators_amount
.checked_sub(amount)
.ok_or(Error::InvalidAmount)?;
*delegators_amount = new_amount;
new_amount
};
debug_assert!(_unbonding_purse_balance > new_amount);
if new_amount.is_zero() {
let _value = delegators_map
.remove(&delegator_public_key)
.ok_or(Error::DelegatorNotFound)?;
debug_assert!(_value.is_zero());
let mut outer = detail::get_delegator_reward_map(self)?;
let mut inner = outer
.remove(&validator_public_key)
.ok_or(Error::ValidatorNotFound)?;
inner
.remove(&delegator_public_key)
.ok_or(Error::DelegatorNotFound)?;
if !inner.is_empty() {
outer.insert(validator_public_key, inner);
};
detail::set_delegator_reward_map(self, outer)?;
}
detail::set_delegators(self, delegators)?;
Ok(new_amount)
}
fn slash(&mut self, validator_public_keys: Vec<PublicKey>) -> Result<()> {
if self.get_caller() != SYSTEM_ACCOUNT {
return Err(Error::InvalidCaller);
}
detail::quash_bid(self, &validator_public_keys)?;
let bid_purses_uref = self
.get_key(BID_PURSES_KEY)
.and_then(Key::into_uref)
.ok_or(Error::MissingKey)?;
let mut bid_purses: BidPurses = self.read(bid_purses_uref)?.ok_or(Error::Storage)?;
let unbonding_purses_uref = self
.get_key(UNBONDING_PURSES_KEY)
.and_then(Key::into_uref)
.ok_or(Error::MissingKey)?;
let mut unbonding_purses: UnbondingPurses =
self.read(unbonding_purses_uref)?.ok_or(Error::Storage)?;
let mut bid_purses_modified = false;
let mut unbonding_purses_modified = false;
for validator_account_hash in validator_public_keys {
if let Some(_bid_purse) = bid_purses.remove(&validator_account_hash) {
bid_purses_modified = true;
}
if let Some(unbonding_list) = unbonding_purses.get_mut(&validator_account_hash) {
let size_before = unbonding_list.len();
unbonding_list.retain(|element| element.origin != validator_account_hash);
unbonding_purses_modified = size_before != unbonding_list.len();
}
}
if bid_purses_modified {
self.write(bid_purses_uref, bid_purses)?;
}
if unbonding_purses_modified {
self.write(unbonding_purses_uref, unbonding_purses)?;
}
Ok(())
}
fn run_auction(&mut self) -> Result<()> {
if self.get_caller() != SYSTEM_ACCOUNT {
return Err(Error::InvalidCaller);
}
detail::process_unbond_requests(self)?;
let validator_slots = detail::get_validator_slots(self)?;
let auction_delay = detail::get_auction_delay(self)?;
let snapshot_size = auction_delay as usize + 1;
let mut era_id = detail::get_era_id(self)?;
let mut bids = detail::get_bids(self)?;
let mut bids_modified = false;
for bid in bids.values_mut() {
if let Some(locked_until) = bid.funds_locked {
if era_id >= locked_until {
bid.funds_locked = None;
bids_modified = true;
}
}
}
let mut bid_weights: ValidatorWeights = {
bids.iter()
.filter(|(_validator_account_hash, founding_validator)| {
founding_validator.funds_locked.is_some()
})
.map(|(validator_account_hash, amount)| {
(*validator_account_hash, amount.staked_amount)
})
.collect()
};
let bid_scores = bids
.iter()
.filter(|(_validator_account_hash, founding_validator)| {
founding_validator.funds_locked.is_none()
})
.map(|(validator_account_hash, amount)| {
(*validator_account_hash, amount.staked_amount)
});
let mut scores = BTreeMap::new();
for (account_hash, score) in bid_scores {
scores
.entry(account_hash)
.and_modify(|acc| *acc += score)
.or_insert_with(|| score);
}
let mut scores: Vec<_> = scores.into_iter().collect();
scores.sort_by(|(_, lhs), (_, rhs)| rhs.cmp(lhs));
let remaining_auction_slots = validator_slots.saturating_sub(bid_weights.len());
bid_weights.extend(scores.into_iter().take(remaining_auction_slots));
let mut era_validators = detail::get_era_validators(self)?;
era_id += 1;
let next_era_id = era_id + auction_delay;
let mut delegators = detail::get_delegators(self)?;
let mut seigniorage_recipients_snapshot =
detail::get_seigniorage_recipients_snapshot(self)?;
let mut seigniorage_recipients = SeigniorageRecipients::new();
for era_validator in bid_weights.keys() {
let mut seigniorage_recipient = SeigniorageRecipient::default();
if let Some(founding_validator) = bids.get(era_validator) {
seigniorage_recipient.stake = founding_validator.staked_amount;
seigniorage_recipient.delegation_rate = founding_validator.delegation_rate;
}
if let Some(delegator_map) = delegators.remove(era_validator) {
seigniorage_recipient.delegators = delegator_map;
}
seigniorage_recipients.insert(*era_validator, seigniorage_recipient);
}
let previous_seigniorage_recipients =
seigniorage_recipients_snapshot.insert(next_era_id, seigniorage_recipients);
assert!(previous_seigniorage_recipients.is_none());
let seigniorage_recipients_snapshot = seigniorage_recipients_snapshot
.into_iter()
.rev()
.take(snapshot_size)
.collect();
detail::set_seigniorage_recipients_snapshot(self, seigniorage_recipients_snapshot)?;
let previous_era_validators = era_validators.insert(era_id + auction_delay, bid_weights);
assert!(previous_era_validators.is_none());
detail::set_era_id(self, era_id)?;
let era_validators = era_validators
.into_iter()
.rev()
.take(snapshot_size)
.collect();
detail::set_era_validators(self, era_validators)?;
if bids_modified {
detail::set_bids(self, bids)?;
}
Ok(())
}
fn distribute(&mut self, reward_factors: BTreeMap<PublicKey, u64>) -> Result<()> {
if self.get_caller() != SYSTEM_ACCOUNT {
return Err(Error::InvalidCaller);
}
let seigniorage_recipients = self.read_seigniorage_recipients()?;
let base_round_reward = self.read_base_round_reward()?;
if reward_factors.keys().ne(seigniorage_recipients.keys()) {
return Err(Error::MismatchedEraValidators);
}
for (public_key, reward_factor) in reward_factors {
let recipient = seigniorage_recipients
.get(&public_key)
.ok_or(Error::ValidatorNotFound)?;
let total_stake = recipient.total_stake();
if total_stake.is_zero() {
continue;
}
let total_reward: Ratio<U512> = {
let reward_rate = Ratio::new(U512::from(reward_factor), U512::from(BLOCK_REWARD));
reward_rate * base_round_reward
};
let delegator_total_stake: U512 = recipient.delegator_total_stake();
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 * reward_multiplier;
let commission: Ratio<U512> = delegator_reward * commission_rate;
delegator_reward - commission
};
let delegator_rewards =
recipient
.delegators
.iter()
.map(|(delegator_key, delegator_stake)| {
let reward_multiplier = Ratio::new(*delegator_stake, delegator_total_stake);
let reward = delegators_part * reward_multiplier;
(*delegator_key, reward)
});
let total_delegator_payout: U512 =
detail::update_delegator_rewards(self, public_key, delegator_rewards)?;
let validators_part: Ratio<U512> = total_reward - Ratio::from(total_delegator_payout);
let validator_reward = validators_part.to_integer();
detail::update_validator_reward(self, public_key, validator_reward)?;
let validator_reward_purse = self
.get_key(VALIDATOR_REWARD_PURSE_KEY)
.ok_or(Error::MissingKey)?
.into_uref()
.ok_or(Error::InvalidKeyVariant)?;
let tmp_validator_reward_purse =
self.mint(validator_reward).map_err(|_| Error::MintReward)?;
self.transfer_purse_to_purse(
tmp_validator_reward_purse,
validator_reward_purse,
validator_reward,
)
.map_err(|_| Error::Transfer)?;
let delegator_reward_purse = self
.get_key(DELEGATOR_REWARD_PURSE_KEY)
.ok_or(Error::MissingKey)?
.into_uref()
.ok_or(Error::InvalidKeyVariant)?;
let tmp_delegator_reward_purse = self
.mint(total_delegator_payout)
.map_err(|_| Error::MintReward)?;
self.transfer_purse_to_purse(
tmp_delegator_reward_purse,
delegator_reward_purse,
total_delegator_payout,
)
.map_err(|_| Error::Transfer)?;
}
Ok(())
}
fn withdraw_delegator_reward(
&mut self,
validator_public_key: PublicKey,
delegator_public_key: PublicKey,
target_purse: URef,
) -> Result<U512> {
let account_hash = AccountHash::from_public_key(delegator_public_key, |x| self.blake2b(x));
if self.get_caller() != account_hash {
return Err(Error::InvalidPublicKey);
}
let mut outer: DelegatorRewardMap = detail::get_delegator_reward_map(self)?;
let mut inner = outer
.remove(&validator_public_key)
.ok_or(Error::ValidatorNotFound)?;
let reward_amount: &mut U512 = inner
.get_mut(&delegator_public_key)
.ok_or(Error::DelegatorNotFound)?;
let ret = *reward_amount;
if !ret.is_zero() {
let source_purse = self
.get_key(DELEGATOR_REWARD_PURSE_KEY)
.ok_or(Error::MissingKey)?
.into_uref()
.ok_or(Error::InvalidKeyVariant)?;
self.transfer_purse_to_purse(source_purse, target_purse, *reward_amount)
.map_err(|_| Error::Transfer)?;
*reward_amount = U512::zero();
}
outer.insert(validator_public_key, inner);
detail::set_delegator_reward_map(self, outer)?;
Ok(ret)
}
fn withdraw_validator_reward(
&mut self,
validator_public_key: PublicKey,
target_purse: URef,
) -> Result<U512> {
let account_hash = AccountHash::from_public_key(validator_public_key, |x| self.blake2b(x));
if self.get_caller() != account_hash {
return Err(Error::InvalidPublicKey);
}
let mut validator_reward_map = detail::get_validator_reward_map(self)?;
let reward_amount: &mut U512 = validator_reward_map
.get_mut(&validator_public_key)
.ok_or(Error::ValidatorNotFound)?;
let ret = *reward_amount;
if !ret.is_zero() {
let source_purse = self
.get_key(VALIDATOR_REWARD_PURSE_KEY)
.ok_or(Error::MissingKey)?
.into_uref()
.ok_or(Error::InvalidKeyVariant)?;
self.transfer_purse_to_purse(source_purse, target_purse, *reward_amount)
.map_err(|_| Error::Transfer)?;
*reward_amount = U512::zero();
}
detail::set_validator_reward_map(self, validator_reward_map)?;
Ok(ret)
}
fn read_era_id(&mut self) -> Result<EraId> {
detail::get_era_id(self)
}
}