mod bid;
mod constants;
mod delegator;
mod detail;
mod era_info;
mod error;
mod providers;
mod seigniorage_recipient;
mod unbonding_purse;
use alloc::{collections::BTreeMap, vec::Vec};
use num_rational::Ratio;
use crate::{account::AccountHash, PublicKey, U512};
pub use bid::Bid;
pub use constants::*;
pub use delegator::Delegator;
pub use era_info::*;
pub use error::Error;
pub use providers::{
AccountProvider, MintProvider, RuntimeProvider, StorageProvider, SystemProvider,
};
pub use seigniorage_recipient::SeigniorageRecipient;
pub use unbonding_purse::UnbondingPurse;
pub type DelegationRate = u8;
pub type Bids = BTreeMap<PublicKey, Bid>;
pub type ValidatorWeights = BTreeMap<PublicKey, U512>;
pub type EraId = u64;
pub type EraValidators = BTreeMap<EraId, ValidatorWeights>;
pub type SeigniorageRecipients = BTreeMap<PublicKey, SeigniorageRecipient>;
pub type SeigniorageRecipientsSnapshot = BTreeMap<EraId, SeigniorageRecipients>;
pub type UnbondingPurses = BTreeMap<AccountHash, Vec<UnbondingPurse>>;
pub trait Auction:
StorageProvider + SystemProvider + RuntimeProvider + MintProvider + AccountProvider + Sized
{
fn get_era_validators(&mut self) -> Result<EraValidators, Error> {
let snapshot = detail::get_seigniorage_recipients_snapshot(self)?;
let era_validators = snapshot
.into_iter()
.map(|(era_id, recipients)| {
let validator_weights = recipients
.into_iter()
.map(|(public_key, bid)| (public_key, bid.total_stake()))
.collect::<ValidatorWeights>();
(era_id, validator_weights)
})
.collect::<BTreeMap<EraId, ValidatorWeights>>();
Ok(era_validators)
}
fn read_seigniorage_recipients(&mut self) -> Result<SeigniorageRecipients, Error> {
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)
.ok_or(Error::MissingSeigniorageRecipients)?;
Ok(seigniorage_recipients)
}
fn add_bid(
&mut self,
public_key: PublicKey,
delegation_rate: DelegationRate,
amount: U512,
) -> Result<U512, Error> {
let account_hash = AccountHash::from_public_key(&public_key, |x| self.blake2b(x));
if self.get_caller() != account_hash {
return Err(Error::InvalidPublicKey);
}
if amount.is_zero() {
return Err(Error::BondTooSmall);
}
if delegation_rate > DELEGATION_RATE_DENOMINATOR {
return Err(Error::DelegationRateTooLarge);
}
let source = self.get_main_purse()?;
let account_hash = AccountHash::from(&public_key);
let updated_amount = match self.read_bid(&account_hash)? {
Some(mut bid) => {
if bid.inactive() {
bid.activate();
}
self.transfer_purse_to_purse(source, *bid.bonding_purse(), amount)
.map_err(|_| Error::TransferToBidPurse)?;
let updated_amount = bid
.with_delegation_rate(delegation_rate)
.increase_stake(amount)?;
self.write_bid(account_hash, bid)?;
updated_amount
}
None => {
let bonding_purse = self.create_purse()?;
self.transfer_purse_to_purse(source, bonding_purse, amount)
.map_err(|_| Error::TransferToBidPurse)?;
let bid = Bid::unlocked(public_key, bonding_purse, amount, delegation_rate);
self.write_bid(account_hash, bid)?;
amount
}
};
Ok(updated_amount)
}
fn withdraw_bid(&mut self, public_key: PublicKey, amount: U512) -> Result<U512, Error> {
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 bid = self
.read_bid(&account_hash)?
.ok_or(Error::ValidatorNotFound)?;
let era_end_timestamp_millis = detail::get_era_end_timestamp_millis(self)?;
let updated_stake = bid.decrease_stake(amount, era_end_timestamp_millis)?;
detail::create_unbonding_purse(
self,
public_key,
public_key,
*bid.bonding_purse(),
amount,
)?;
if updated_stake.is_zero() {
for (delegator_public_key, delegator) in bid.delegators() {
detail::create_unbonding_purse(
self,
public_key,
*delegator_public_key,
*delegator.bonding_purse(),
*delegator.staked_amount(),
)?;
}
*bid.delegators_mut() = BTreeMap::new();
bid.deactivate();
}
self.write_bid(account_hash, bid)?;
Ok(updated_stake)
}
fn delegate(
&mut self,
delegator_public_key: PublicKey,
validator_public_key: PublicKey,
amount: U512,
) -> Result<U512, Error> {
let account_hash = AccountHash::from_public_key(&delegator_public_key, |x| self.blake2b(x));
if self.get_caller() != account_hash {
return Err(Error::InvalidPublicKey);
}
if amount.is_zero() {
return Err(Error::BondTooSmall);
}
let source = self.get_main_purse()?;
let validator_account_hash = AccountHash::from(&validator_public_key);
let mut bid = match self.read_bid(&validator_account_hash)? {
Some(bid) => bid,
None => {
return Err(Error::ValidatorNotFound);
}
};
let delegators = bid.delegators_mut();
let new_delegation_amount = match delegators.get_mut(&delegator_public_key) {
Some(delegator) => {
self.transfer_purse_to_purse(source, *delegator.bonding_purse(), amount)
.map_err(|_| Error::TransferToDelegatorPurse)?;
delegator.increase_stake(amount)?;
*delegator.staked_amount()
}
None => {
let bonding_purse = self.create_purse()?;
self.transfer_purse_to_purse(source, bonding_purse, amount)
.map_err(|_| Error::TransferToDelegatorPurse)?;
let delegator = Delegator::unlocked(
delegator_public_key,
amount,
bonding_purse,
validator_public_key,
);
delegators.insert(delegator_public_key, delegator);
amount
}
};
self.write_bid(validator_account_hash, bid)?;
Ok(new_delegation_amount)
}
fn undelegate(
&mut self,
delegator_public_key: PublicKey,
validator_public_key: PublicKey,
amount: U512,
) -> Result<U512, Error> {
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 validator_account_hash = AccountHash::from(&validator_public_key);
let mut bid = match self.read_bid(&validator_account_hash)? {
Some(bid) => bid,
None => return Err(Error::ValidatorNotFound),
};
let delegators = bid.delegators_mut();
let new_amount = match delegators.get_mut(&delegator_public_key) {
Some(delegator) => {
detail::create_unbonding_purse(
self,
validator_public_key,
delegator_public_key,
*delegator.bonding_purse(),
amount,
)?;
let era_end_timestamp_millis = detail::get_era_end_timestamp_millis(self)?;
let updated_stake = delegator.decrease_stake(amount, era_end_timestamp_millis)?;
if updated_stake == U512::zero() {
delegators.remove(&delegator_public_key);
};
updated_stake
}
None => return Err(Error::DelegatorNotFound),
};
self.write_bid(validator_account_hash, bid)?;
Ok(new_amount)
}
fn slash(&mut self, validator_public_keys: Vec<PublicKey>) -> Result<(), Error> {
if self.get_caller() != PublicKey::System.to_account_hash() {
return Err(Error::InvalidCaller);
}
let mut burned_amount: U512 = U512::zero();
for validator_public_key in validator_public_keys {
let validator_account_hash = AccountHash::from(&validator_public_key);
if let Some(mut bid) = self.read_bid(&validator_account_hash)? {
burned_amount += *bid.staked_amount();
*bid.staked_amount_mut() = U512::zero();
bid.deactivate();
for delegator in bid.delegators_mut().values_mut() {
*delegator.staked_amount_mut() = U512::zero();
}
self.write_bid(validator_account_hash, bid)?;
};
let validator_account_hash = AccountHash::from(&validator_public_key);
let unbonding_purses = self.read_withdraw(&validator_account_hash)?;
if !unbonding_purses.is_empty() {
burned_amount += unbonding_purses
.into_iter()
.map(|unbonding_purse| *unbonding_purse.amount())
.sum();
self.write_withdraw(validator_account_hash, Vec::new())?;
}
}
self.reduce_total_supply(burned_amount)?;
Ok(())
}
fn run_auction(
&mut self,
era_end_timestamp_millis: u64,
evicted_validators: Vec<PublicKey>,
) -> Result<(), Error> {
if self.get_caller() != PublicKey::System.to_account_hash() {
return Err(Error::InvalidCaller);
}
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)?;
detail::process_unbond_requests(self)?;
let mut bids_modified = false;
for (validator_public_key, bid) in bids.iter_mut() {
if bid.process(era_end_timestamp_millis) {
bids_modified = true;
}
if evicted_validators.contains(validator_public_key) {
bids_modified = bid.deactivate()
}
}
let winners: ValidatorWeights = {
let founder_weights: ValidatorWeights = bids
.iter()
.filter(|(_public_key, bid)| bid.vesting_schedule().is_some() && !bid.inactive())
.map(|(public_key, bid)| {
let total_staked_amount = bid.total_staked_amount()?;
Ok((*public_key, total_staked_amount))
})
.collect::<Result<ValidatorWeights, Error>>()?;
let mut non_founder_weights: Vec<(PublicKey, U512)> = bids
.iter()
.filter(|(_public_key, bid)| bid.vesting_schedule().is_none() && !bid.inactive())
.map(|(public_key, bid)| {
let total_staked_amount = bid.total_staked_amount()?;
Ok((*public_key, total_staked_amount))
})
.collect::<Result<Vec<(PublicKey, U512)>, Error>>()?;
non_founder_weights.sort_by(|(_, lhs), (_, rhs)| rhs.cmp(lhs));
let remaining_auction_slots = validator_slots.saturating_sub(founder_weights.len());
founder_weights
.into_iter()
.chain(
non_founder_weights
.into_iter()
.take(remaining_auction_slots),
)
.collect()
};
era_id += 1;
let delayed_era = era_id + auction_delay;
{
let mut snapshot = detail::get_seigniorage_recipients_snapshot(self)?;
let mut recipients = SeigniorageRecipients::new();
for era_validator in winners.keys() {
let seigniorage_recipient = match bids.get(era_validator) {
Some(bid) => bid.into(),
None => return Err(Error::BidNotFound),
};
recipients.insert(*era_validator, seigniorage_recipient);
}
let previous_recipients = snapshot.insert(delayed_era, recipients);
assert!(previous_recipients.is_none());
let snapshot = snapshot.into_iter().rev().take(snapshot_size).collect();
detail::set_seigniorage_recipients_snapshot(self, snapshot)?;
}
detail::set_era_id(self, era_id)?;
detail::set_era_end_timestamp_millis(self, era_end_timestamp_millis)?;
if bids_modified {
detail::set_bids(self, bids)?;
}
Ok(())
}
fn distribute(&mut self, reward_factors: BTreeMap<PublicKey, u64>) -> Result<(), Error> {
if self.get_caller() != PublicKey::System.to_account_hash() {
return Err(Error::InvalidCaller);
}
let seigniorage_recipients = self.read_seigniorage_recipients()?;
let base_round_reward = self.read_base_round_reward()?;
let era_id = detail::get_era_id(self)?;
if reward_factors.keys().ne(seigniorage_recipients.keys()) {
return Err(Error::MismatchedEraValidators);
}
let mut era_info = EraInfo::new();
let mut seigniorage_allocations = era_info.seigniorage_allocations_mut();
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
.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, reward)
});
let delegator_payouts = detail::reinvest_delegator_rewards(
self,
&mut seigniorage_allocations,
public_key,
delegator_rewards,
)?;
let total_delegator_payout = delegator_payouts
.iter()
.map(|(amount, _bonding_purse)| *amount)
.sum();
let validators_part: Ratio<U512> = total_reward - Ratio::from(total_delegator_payout);
let validator_reward = validators_part.to_integer();
let validator_bonding_purse = detail::reinvest_validator_reward(
self,
&mut seigniorage_allocations,
public_key,
validator_reward,
)?;
let tmp_validator_reward_purse =
self.mint(validator_reward).map_err(|_| Error::MintReward)?;
self.transfer_purse_to_purse(
tmp_validator_reward_purse,
validator_bonding_purse,
validator_reward,
)
.map_err(|_| Error::ValidatorRewardTransfer)?;
let tmp_delegator_reward_purse = self
.mint(total_delegator_payout)
.map_err(|_| Error::MintReward)?;
for (delegator_payout, bonding_purse) in delegator_payouts {
self.transfer_purse_to_purse(
tmp_delegator_reward_purse,
bonding_purse,
delegator_payout,
)
.map_err(|_| Error::DelegatorRewardTransfer)?;
}
}
self.record_era_info(era_id, era_info)?;
Ok(())
}
fn read_era_id(&mut self) -> Result<EraId, Error> {
detail::get_era_id(self)
}
fn activate_bid(&mut self, validator_public_key: PublicKey) -> Result<(), Error> {
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 bid = match self.read_bid(&account_hash)? {
Some(bid) => bid,
None => return Err(Error::ValidatorNotFound),
};
bid.activate();
self.write_bid(account_hash, bid)?;
Ok(())
}
}