casper_storage/system/
auction.rs

1mod auction_native;
2/// Auction business logic.
3pub mod detail;
4/// System logic providers.
5pub mod providers;
6
7use std::collections::BTreeMap;
8
9use itertools::Itertools;
10use num_rational::Ratio;
11use num_traits::{CheckedMul, CheckedSub};
12use tracing::{debug, error, warn};
13
14use crate::system::auction::detail::{
15    process_with_vesting_schedule, read_delegator_bid, read_delegator_bids, read_validator_bid,
16    seigniorage_recipients,
17};
18use casper_types::{
19    account::AccountHash,
20    system::auction::{
21        BidAddr, BidKind, Bridge, DelegationRate, DelegatorKind, EraInfo, EraValidators, Error,
22        Reservation, SeigniorageRecipient, SeigniorageRecipientsSnapshot, SeigniorageRecipientsV2,
23        UnbondEra, UnbondKind, ValidatorBid, ValidatorCredit, ValidatorWeights,
24        DELEGATION_RATE_DENOMINATOR,
25    },
26    AccessRights, ApiError, EraId, Key, PublicKey, URef, U512,
27};
28
29use self::providers::{AccountProvider, MintProvider, RuntimeProvider, StorageProvider};
30
31/// Bonding auction contract interface
32pub trait Auction:
33    StorageProvider + RuntimeProvider + MintProvider + AccountProvider + Sized
34{
35    /// Returns active validators and auction winners for a number of future eras determined by the
36    /// configured auction_delay.
37    fn get_era_validators(&mut self) -> Result<EraValidators, Error> {
38        let snapshot = detail::get_seigniorage_recipients_snapshot(self)?;
39        let era_validators = detail::era_validators_from_snapshot(snapshot);
40        Ok(era_validators)
41    }
42
43    /// Returns validators in era_validators, mapped to their bids or founding stakes, delegation
44    /// rates and lists of delegators together with their delegated quantities from delegators.
45    /// This function is publicly accessible, but intended for system use by the Handle Payment
46    /// contract, because this data is necessary for distributing seigniorage.
47    fn read_seigniorage_recipients(&mut self) -> Result<SeigniorageRecipientsV2, Error> {
48        // `era_validators` are assumed to be computed already by calling "run_auction" entrypoint.
49        let era_index = detail::get_era_id(self)?;
50        let mut seigniorage_recipients_snapshot =
51            detail::get_seigniorage_recipients_snapshot(self)?;
52        let seigniorage_recipients = seigniorage_recipients_snapshot
53            .remove(&era_index)
54            .ok_or(Error::MissingSeigniorageRecipients)?;
55        Ok(seigniorage_recipients)
56    }
57
58    /// This entry point adds or modifies an entry in the `Key::Bid` section of the global state and
59    /// creates (or tops off) a bid purse. Post genesis, any new call on this entry point causes a
60    /// non-founding validator in the system to exist.
61    ///
62    /// The logic works for both founding and non-founding validators, making it possible to adjust
63    /// their delegation rate and increase their stakes.
64    ///
65    /// A validator with its bid inactive due to slashing can activate its bid again by increasing
66    /// its stake.
67    ///
68    /// Validators cannot create a bid with 0 amount, and the delegation rate can't exceed
69    /// [`DELEGATION_RATE_DENOMINATOR`].
70    ///
71    /// Returns a [`U512`] value indicating total amount of tokens staked for given `public_key`.
72    #[allow(clippy::too_many_arguments)]
73    fn add_bid(
74        &mut self,
75        public_key: PublicKey,
76        delegation_rate: DelegationRate,
77        amount: U512,
78        minimum_delegation_amount: u64,
79        maximum_delegation_amount: u64,
80        minimum_bid_amount: u64,
81        max_delegators_per_validator: u32,
82        reserved_slots: u32,
83    ) -> Result<U512, ApiError> {
84        if !self.allow_auction_bids() {
85            // The validator set may be closed on some side chains,
86            // which is configured by disabling bids.
87            return Err(Error::AuctionBidsDisabled.into());
88        }
89
90        if amount == U512::zero() {
91            return Err(Error::BondTooSmall.into());
92        }
93
94        if delegation_rate > DELEGATION_RATE_DENOMINATOR {
95            return Err(Error::DelegationRateTooLarge.into());
96        }
97
98        if reserved_slots > max_delegators_per_validator {
99            return Err(Error::ExceededReservationSlotsLimit.into());
100        }
101
102        let provided_account_hash = AccountHash::from(&public_key);
103
104        if !self.is_allowed_session_caller(&provided_account_hash) {
105            return Err(Error::InvalidContext.into());
106        }
107
108        let validator_bid_key = BidAddr::from(public_key.clone()).into();
109        let (target, validator_bid) = if let Some(BidKind::Validator(mut validator_bid)) =
110            self.read_bid(&validator_bid_key)?
111        {
112            let updated_stake = validator_bid.increase_stake(amount)?;
113            if updated_stake < U512::from(minimum_bid_amount) {
114                return Err(Error::BondTooSmall.into());
115            }
116            // update an existing validator bid
117            if validator_bid.inactive() {
118                validator_bid.activate();
119            }
120            validator_bid.with_delegation_rate(delegation_rate);
121            validator_bid.set_delegation_amount_boundaries(
122                minimum_delegation_amount,
123                maximum_delegation_amount,
124            );
125            // perform validation if number of reserved slots has changed
126            if reserved_slots != validator_bid.reserved_slots() {
127                let validator_bid_addr = BidAddr::from(public_key.clone());
128                // cannot reserve fewer slots than there are reservations
129                let reservation_count = self.reservation_count(&validator_bid_addr)?;
130                if reserved_slots < reservation_count as u32 {
131                    return Err(Error::ReservationSlotsCountTooSmall.into());
132                }
133
134                // cannot reserve more slots than there are free delegator slots
135                let used_reservation_count = self.used_reservation_count(&validator_bid_addr)?;
136                let delegator_count = self.delegator_count(&validator_bid_addr)?;
137                let normal_delegators = delegator_count - used_reservation_count;
138                let max_reserved_slots = max_delegators_per_validator - normal_delegators as u32;
139                if reserved_slots > max_reserved_slots {
140                    return Err(Error::ExceededReservationSlotsLimit.into());
141                }
142                validator_bid.with_reserved_slots(reserved_slots);
143            }
144
145            (*validator_bid.bonding_purse(), validator_bid)
146        } else {
147            if amount < U512::from(minimum_bid_amount) {
148                return Err(Error::BondTooSmall.into());
149            }
150            // create new validator bid
151            let bonding_purse = self.create_purse()?;
152            let validator_bid = ValidatorBid::unlocked(
153                public_key,
154                bonding_purse,
155                amount,
156                delegation_rate,
157                minimum_delegation_amount,
158                maximum_delegation_amount,
159                reserved_slots,
160            );
161            (bonding_purse, Box::new(validator_bid))
162        };
163
164        let source = self.get_main_purse()?;
165        self.mint_transfer_direct(
166            Some(PublicKey::System.to_account_hash()),
167            source,
168            target,
169            amount,
170            None,
171        )
172        .map_err(|_| Error::TransferToBidPurse)?
173        .map_err(|mint_error| {
174            // Propagate mint contract's error that occurred during execution of transfer
175            // entrypoint. This will improve UX in case of (for example)
176            // unapproved spending limit error.
177            ApiError::from(mint_error)
178        })?;
179
180        let updated_amount = validator_bid.staked_amount();
181        self.write_bid(validator_bid_key, BidKind::Validator(validator_bid))?;
182        Ok(updated_amount)
183    }
184
185    /// Unbonds aka reduces stake by specified amount, adding an entry to the unbonding queue.
186    /// For a genesis validator, this is subject to vesting if applicable to a given network.
187    ///
188    /// If this bid stake is reduced to 0, any delegators to this bid will be undelegated, with
189    /// entries made to the unbonding queue for each of them for their full delegated amount.
190    /// Additionally, this bid record will be pruned away from the next calculated root hash.
191    ///
192    /// An attempt to reduce stake by more than is staked will instead 0 the stake.
193    ///
194    /// The function returns the remaining staked amount (we allow partial unbonding).
195    fn withdraw_bid(
196        &mut self,
197        public_key: PublicKey,
198        amount: U512,
199        minimum_bid_amount: u64,
200    ) -> Result<U512, Error> {
201        let provided_account_hash = AccountHash::from(&public_key);
202
203        if !self.is_allowed_session_caller(&provided_account_hash) {
204            return Err(Error::InvalidContext);
205        }
206
207        let validator_bid_addr = BidAddr::from(public_key.clone());
208        let validator_bid_key = validator_bid_addr.into();
209        let mut validator_bid = read_validator_bid(self, &validator_bid_key)?;
210        let staked_amount = validator_bid.staked_amount();
211
212        // An attempt to unbond more than is staked results in unbonding the staked amount.
213        let unbonding_amount = U512::min(amount, validator_bid.staked_amount());
214
215        let era_end_timestamp_millis = detail::get_era_end_timestamp_millis(self)?;
216        let updated_stake =
217            validator_bid.decrease_stake(unbonding_amount, era_end_timestamp_millis)?;
218
219        debug!(
220            "withdrawing bid for {validator_bid_addr} reducing {staked_amount} by {unbonding_amount} to {updated_stake}",
221        );
222        // if validator stake is less than minimum_bid_amount, unbond fully and prune validator bid
223        if updated_stake < U512::from(minimum_bid_amount) {
224            // create unbonding purse for full validator stake
225            detail::create_unbonding_purse(
226                self,
227                public_key.clone(),
228                UnbondKind::Validator(public_key.clone()), // validator is the unbonder
229                *validator_bid.bonding_purse(),
230                staked_amount,
231                None,
232            )?;
233            // Unbond all delegators and zero them out
234            let delegators = read_delegator_bids(self, &public_key)?;
235            for mut delegator in delegators {
236                let unbond_kind = delegator.unbond_kind();
237                detail::create_unbonding_purse(
238                    self,
239                    public_key.clone(),
240                    unbond_kind,
241                    *delegator.bonding_purse(),
242                    delegator.staked_amount(),
243                    None,
244                )?;
245                delegator.decrease_stake(delegator.staked_amount(), era_end_timestamp_millis)?;
246
247                let delegator_bid_addr = delegator.bid_addr();
248                debug!("pruning delegator bid {}", delegator_bid_addr);
249                self.prune_bid(delegator_bid_addr)
250            }
251            debug!("pruning validator bid {}", validator_bid_addr);
252            self.prune_bid(validator_bid_addr);
253        } else {
254            // create unbonding purse for the unbonding amount
255            detail::create_unbonding_purse(
256                self,
257                public_key.clone(),
258                UnbondKind::Validator(public_key.clone()), // validator is the unbonder
259                *validator_bid.bonding_purse(),
260                unbonding_amount,
261                None,
262            )?;
263            self.write_bid(validator_bid_key, BidKind::Validator(validator_bid))?;
264        }
265
266        Ok(updated_stake)
267    }
268
269    /// Adds a new delegator to delegators or increases its current stake. If the target validator
270    /// is missing, the function call returns an error and does nothing.
271    ///
272    /// The function transfers motes from the source purse to the delegator's bonding purse.
273    ///
274    /// This entry point returns the number of tokens currently delegated to a given validator.
275    fn delegate(
276        &mut self,
277        delegator_kind: DelegatorKind,
278        validator_public_key: PublicKey,
279        amount: U512,
280        max_delegators_per_validator: u32,
281    ) -> Result<U512, ApiError> {
282        if !self.allow_auction_bids() {
283            // Validation set rotation might be disabled on some private chains and we should not
284            // allow new bids to come in.
285            return Err(Error::AuctionBidsDisabled.into());
286        }
287
288        let source = match &delegator_kind {
289            DelegatorKind::PublicKey(pk) => {
290                let account_hash = pk.to_account_hash();
291                if !self.is_allowed_session_caller(&account_hash) {
292                    return Err(Error::InvalidContext.into());
293                }
294                self.get_main_purse()?
295            }
296            DelegatorKind::Purse(addr) => {
297                let uref = URef::new(*addr, AccessRights::WRITE);
298                if !self.is_valid_uref(uref) {
299                    return Err(Error::InvalidContext.into());
300                }
301                uref
302            }
303        };
304
305        detail::handle_delegation(
306            self,
307            delegator_kind,
308            validator_public_key,
309            source,
310            amount,
311            max_delegators_per_validator,
312        )
313    }
314
315    /// Unbonds aka reduces stake by specified amount, adding an entry to the unbonding queue
316    ///
317    /// The arguments are the delegator's key, the validator's key, and the amount.
318    ///
319    /// Returns the remaining staked amount (we allow partial unbonding).
320    fn undelegate(
321        &mut self,
322        delegator_kind: DelegatorKind,
323        validator_public_key: PublicKey,
324        amount: U512,
325    ) -> Result<U512, Error> {
326        match &delegator_kind {
327            DelegatorKind::PublicKey(pk) => {
328                let account_hash = pk.to_account_hash();
329                if !self.is_allowed_session_caller(&account_hash) {
330                    return Err(Error::InvalidContext);
331                }
332            }
333            DelegatorKind::Purse(addr) => {
334                let uref = URef::new(*addr, AccessRights::WRITE);
335                if !self.is_valid_uref(uref) {
336                    return Err(Error::InvalidContext);
337                }
338            }
339        }
340
341        let validator_bid_key = BidAddr::from(validator_public_key.clone()).into();
342        let _ = read_validator_bid(self, &validator_bid_key)?;
343
344        let delegator_bid_addr =
345            BidAddr::new_delegator_kind(&validator_public_key, &delegator_kind);
346        let mut delegator_bid = read_delegator_bid(self, &delegator_bid_addr.into())?;
347
348        // An attempt to unbond more than is staked results in unbonding the full staked amount.
349        let unbonding_amount = U512::min(amount, delegator_bid.staked_amount());
350
351        let era_end_timestamp_millis = detail::get_era_end_timestamp_millis(self)?;
352        let updated_stake =
353            delegator_bid.decrease_stake(unbonding_amount, era_end_timestamp_millis)?;
354
355        let unbond_kind = delegator_kind.into();
356
357        detail::create_unbonding_purse(
358            self,
359            validator_public_key,
360            unbond_kind,
361            *delegator_bid.bonding_purse(),
362            unbonding_amount,
363            None,
364        )?;
365
366        debug!(
367            "undelegation for {} reducing {} by {} to {}",
368            delegator_bid_addr,
369            delegator_bid.staked_amount(),
370            unbonding_amount,
371            updated_stake
372        );
373
374        if updated_stake.is_zero() {
375            debug!("pruning delegator bid {}", delegator_bid_addr);
376            self.prune_bid(delegator_bid_addr);
377        } else {
378            self.write_bid(delegator_bid_addr.into(), BidKind::Delegator(delegator_bid))?;
379        }
380
381        Ok(updated_stake)
382    }
383
384    /// Unbonds aka reduces stake by specified amount, adding an entry to the unbonding queue,
385    /// which when processed will attempt to re-delegate the stake to the specified new validator.
386    /// If this is not possible at that future point in time, the unbonded stake will instead
387    /// downgrade to a standard undelegate operation automatically (the unbonded stake is
388    /// returned to the associated purse).
389    ///
390    /// This is a quality of life / convenience method, allowing a delegator to indicate they
391    /// would like some or all of their stake moved away from a validator to a different validator
392    /// with a single transaction, instead of requiring them to send an unbonding transaction
393    /// to unbond from the first validator and then wait a number of eras equal to the unbonding
394    /// delay and then send a second transaction to bond to the second validator.
395    ///
396    /// The arguments are the delegator's key, the existing validator's key, the amount,
397    /// and the new validator's key.
398    ///
399    /// Returns the remaining staked amount (we allow partial unbonding).
400    fn redelegate(
401        &mut self,
402        delegator_kind: DelegatorKind,
403        validator_public_key: PublicKey,
404        amount: U512,
405        new_validator: PublicKey,
406    ) -> Result<U512, Error> {
407        match &delegator_kind {
408            DelegatorKind::PublicKey(pk) => {
409                let delegator_account_hash = pk.to_account_hash();
410                if !self.is_allowed_session_caller(&delegator_account_hash) {
411                    return Err(Error::InvalidContext);
412                }
413            }
414            DelegatorKind::Purse(addr) => {
415                if !self.is_valid_uref(URef::new(*addr, AccessRights::WRITE)) {
416                    return Err(Error::InvalidContext);
417                }
418            }
419        }
420
421        // does the validator being moved away from exist?
422        let validator_addr = BidAddr::from(validator_public_key.clone());
423        let validator_bid = read_validator_bid(self, &validator_addr.into())?;
424        if amount < U512::from(validator_bid.minimum_delegation_amount()) {
425            return Err(Error::DelegationAmountTooSmall);
426        }
427        if amount > U512::from(validator_bid.maximum_delegation_amount()) {
428            return Err(Error::DelegationAmountTooLarge);
429        }
430
431        let delegator_bid_addr =
432            BidAddr::new_delegator_kind(&validator_public_key, &delegator_kind);
433
434        let mut delegator_bid = read_delegator_bid(self, &delegator_bid_addr.into())?;
435
436        // An attempt to unbond more than is staked results in unbonding the staked amount.
437        let unbonding_amount = U512::min(amount, delegator_bid.staked_amount());
438
439        let era_end_timestamp_millis = detail::get_era_end_timestamp_millis(self)?;
440        let updated_stake =
441            delegator_bid.decrease_stake(unbonding_amount, era_end_timestamp_millis)?;
442
443        detail::create_unbonding_purse(
444            self,
445            validator_public_key,
446            delegator_kind.into(),
447            *delegator_bid.bonding_purse(),
448            unbonding_amount,
449            Some(new_validator),
450        )?;
451
452        debug!(
453            "redelegation for {} reducing {} by {} to {}",
454            delegator_bid_addr,
455            delegator_bid.staked_amount(),
456            unbonding_amount,
457            updated_stake
458        );
459
460        if updated_stake.is_zero() {
461            debug!("pruning redelegator bid {}", delegator_bid_addr);
462            self.prune_bid(delegator_bid_addr);
463        } else {
464            self.write_bid(delegator_bid_addr.into(), BidKind::Delegator(delegator_bid))?;
465        }
466
467        Ok(updated_stake)
468    }
469
470    /// Unbond delegator bids which fall outside validator-configured delegation limits.
471    fn forced_undelegate(&mut self) -> Result<(), Error> {
472        if self.get_caller() != PublicKey::System.to_account_hash() {
473            return Err(Error::InvalidCaller);
474        }
475
476        let era_end_timestamp_millis = detail::get_era_end_timestamp_millis(self)?;
477        let era_id = detail::get_era_id(self)?;
478        let bids_detail = detail::get_validator_bids(self, era_id)?;
479
480        // Forcibly undelegate bids outside a validator's delegation limits
481        for (validator_public_key, validator_bid) in bids_detail.validator_bids().iter() {
482            let minimum_delegation_amount = U512::from(validator_bid.minimum_delegation_amount());
483            let maximum_delegation_amount = U512::from(validator_bid.maximum_delegation_amount());
484
485            let delegators = read_delegator_bids(self, validator_public_key)?;
486            for mut delegator in delegators {
487                let staked_amount = delegator.staked_amount();
488                let delegator_bid_addr = delegator.bid_addr();
489                if staked_amount.is_zero() {
490                    // If the stake is zero, we don't need to unbond anything - we can just prune
491                    // the bid outright. Also, we don't want to keep zero bids even if the minimum
492                    // allowed amount is zero, as they only use up space for no reason.
493                    debug!("pruning delegator bid {}", delegator_bid_addr);
494                    self.prune_bid(delegator_bid_addr);
495                } else if staked_amount < minimum_delegation_amount
496                    || staked_amount > maximum_delegation_amount
497                {
498                    let amount = if staked_amount < minimum_delegation_amount {
499                        staked_amount
500                    } else {
501                        staked_amount - maximum_delegation_amount
502                    };
503                    let unbond_kind = delegator.unbond_kind();
504                    detail::create_unbonding_purse(
505                        self,
506                        validator_public_key.clone(),
507                        unbond_kind,
508                        *delegator.bonding_purse(),
509                        amount,
510                        None,
511                    )?;
512                    let updated_stake =
513                        match delegator.decrease_stake(amount, era_end_timestamp_millis) {
514                            Ok(updated_stake) => updated_stake,
515                            // Work around the case when the locked amounts table has yet to be
516                            // initialized (likely pre-90 day mark).
517                            Err(Error::DelegatorFundsLocked) => continue,
518                            Err(err) => return Err(err),
519                        };
520
521                    if updated_stake.is_zero() {
522                        debug!("pruning delegator bid {}", delegator_bid_addr);
523                        self.prune_bid(delegator_bid_addr);
524                    } else {
525                        debug!(
526                            "forced undelegation for {} reducing {} by {} to {}",
527                            delegator_bid_addr, staked_amount, amount, updated_stake
528                        );
529                        self.write_bid(
530                            delegator_bid_addr.into(),
531                            BidKind::Delegator(Box::new(delegator)),
532                        )?;
533                    }
534                }
535            }
536        }
537        Ok(())
538    }
539
540    /// Adds new reservations for a given validator with specified delegator public keys
541    /// and delegation rates. If during adding reservations configured number of reserved
542    /// delegator slots is exceeded it returns an error.
543    ///
544    /// If given reservation exists already and the delegation rate was changed it's updated.
545    fn add_reservations(&mut self, reservations: Vec<Reservation>) -> Result<(), Error> {
546        if !self.allow_auction_bids() {
547            // Validation set rotation might be disabled on some private chains and we should not
548            // allow new bids to come in.
549            return Err(Error::AuctionBidsDisabled);
550        }
551
552        for reservation in reservations {
553            if !self
554                .is_allowed_session_caller(&AccountHash::from(reservation.validator_public_key()))
555            {
556                return Err(Error::InvalidContext);
557            }
558
559            detail::handle_add_reservation(self, reservation)?;
560        }
561        Ok(())
562    }
563
564    /// Removes reservations for given delegator public keys. If a reservation for one of the keys
565    /// does not exist it returns an error.
566    fn cancel_reservations(
567        &mut self,
568        validator: PublicKey,
569        delegators: Vec<DelegatorKind>,
570        max_delegators_per_validator: u32,
571    ) -> Result<(), Error> {
572        if !self.is_allowed_session_caller(&AccountHash::from(&validator)) {
573            return Err(Error::InvalidContext);
574        }
575
576        for delegator in delegators {
577            detail::handle_cancel_reservation(
578                self,
579                validator.clone(),
580                delegator.clone(),
581                max_delegators_per_validator,
582            )?;
583        }
584        Ok(())
585    }
586
587    /// Slashes each validator.
588    ///
589    /// This can be only invoked through a system call.
590    fn slash(&mut self, validator_public_keys: Vec<PublicKey>) -> Result<(), Error> {
591        fn slash_unbonds(unbond_eras: Vec<UnbondEra>) -> U512 {
592            let mut burned_amount = U512::zero();
593            for unbond_era in unbond_eras {
594                burned_amount += *unbond_era.amount();
595            }
596            burned_amount
597        }
598
599        if self.get_caller() != PublicKey::System.to_account_hash() {
600            return Err(Error::InvalidCaller);
601        }
602
603        let mut burned_amount: U512 = U512::zero();
604
605        for validator_public_key in validator_public_keys {
606            let validator_bid_addr = BidAddr::from(validator_public_key.clone());
607            // Burn stake, deactivate
608            if let Some(BidKind::Validator(validator_bid)) =
609                self.read_bid(&validator_bid_addr.into())?
610            {
611                burned_amount += validator_bid.staked_amount();
612                self.prune_bid(validator_bid_addr);
613
614                // Also slash delegator stakes when deactivating validator bid.
615                let delegator_keys = {
616                    let mut ret =
617                        self.get_keys_by_prefix(&validator_bid_addr.delegated_account_prefix()?)?;
618                    ret.extend(
619                        self.get_keys_by_prefix(&validator_bid_addr.delegated_purse_prefix()?)?,
620                    );
621                    ret
622                };
623
624                for delegator_key in delegator_keys {
625                    if let Some(BidKind::Delegator(delegator_bid)) =
626                        self.read_bid(&delegator_key)?
627                    {
628                        burned_amount += delegator_bid.staked_amount();
629                        let delegator_bid_addr = delegator_bid.bid_addr();
630                        self.prune_bid(delegator_bid_addr);
631
632                        // Also slash delegator unbonds.
633                        let delegator_unbond_addr = match delegator_bid.delegator_kind() {
634                            DelegatorKind::PublicKey(pk) => BidAddr::UnbondAccount {
635                                validator: validator_public_key.to_account_hash(),
636                                unbonder: pk.to_account_hash(),
637                            },
638                            DelegatorKind::Purse(addr) => BidAddr::UnbondPurse {
639                                validator: validator_public_key.to_account_hash(),
640                                unbonder: *addr,
641                            },
642                        };
643
644                        match self.read_unbond(delegator_unbond_addr)? {
645                            Some(unbond) => {
646                                let burned = slash_unbonds(unbond.take_eras());
647
648                                burned_amount += burned;
649                                self.write_unbond(delegator_unbond_addr, None)?;
650                            }
651                            None => {
652                                continue;
653                            }
654                        }
655                    }
656                }
657            }
658
659            // get rid of any staked token in the unbonding queue
660            let validator_unbond_addr = BidAddr::UnbondAccount {
661                validator: validator_public_key.to_account_hash(),
662                unbonder: validator_public_key.to_account_hash(),
663            };
664            match self.read_unbond(validator_unbond_addr)? {
665                Some(unbond) => {
666                    let burned = slash_unbonds(unbond.take_eras());
667                    burned_amount += burned;
668                    self.write_unbond(validator_unbond_addr, None)?;
669                }
670                None => {
671                    continue;
672                }
673            }
674        }
675
676        self.reduce_total_supply(burned_amount)?;
677
678        Ok(())
679    }
680
681    /// Takes active_bids and delegators to construct a list of validators' total bids (their own
682    /// added to their delegators') ordered by size from largest to smallest, then takes the top N
683    /// (number of auction slots) bidders and replaces era_validators with these.
684    ///
685    /// Accessed by: node
686    fn run_auction(
687        &mut self,
688        era_end_timestamp_millis: u64,
689        evicted_validators: Vec<PublicKey>,
690        max_delegators_per_validator: u32,
691        include_credits: bool,
692        credit_cap: Ratio<U512>,
693        minimum_bid_amount: u64,
694    ) -> Result<(), ApiError> {
695        debug!("run_auction called");
696
697        if self.get_caller() != PublicKey::System.to_account_hash() {
698            return Err(Error::InvalidCaller.into());
699        }
700
701        let vesting_schedule_period_millis = self.vesting_schedule_period_millis();
702        let validator_slots = detail::get_validator_slots(self)?;
703        let auction_delay = detail::get_auction_delay(self)?;
704        // We have to store auction_delay future eras, one current era and one past era (for
705        // rewards calculations).
706        let snapshot_size = auction_delay as usize + 2;
707        let mut era_id: EraId = detail::get_era_id(self)?;
708
709        // Process unbond requests
710        debug!("processing unbond requests");
711        detail::process_unbond_requests(self, max_delegators_per_validator)?;
712        debug!("processing unbond request successful");
713
714        let mut validator_bids_detail = detail::get_validator_bids(self, era_id)?;
715
716        // Process bids
717        let mut bids_modified = false;
718        for (validator_public_key, validator_bid) in
719            validator_bids_detail.validator_bids_mut().iter_mut()
720        {
721            if process_with_vesting_schedule(
722                self,
723                validator_bid,
724                era_end_timestamp_millis,
725                self.vesting_schedule_period_millis(),
726            )? {
727                bids_modified = true;
728            }
729
730            if evicted_validators.contains(validator_public_key) {
731                bids_modified = validator_bid.deactivate();
732            }
733        }
734
735        let winners = validator_bids_detail.pick_winners(
736            era_id,
737            validator_slots,
738            minimum_bid_amount,
739            include_credits,
740            credit_cap,
741            era_end_timestamp_millis,
742            vesting_schedule_period_millis,
743        )?;
744
745        let (validator_bids, validator_credits, delegator_bids, reservations) =
746            validator_bids_detail.destructure();
747
748        // call prune BEFORE incrementing the era
749        detail::prune_validator_credits(self, era_id, &validator_credits);
750
751        // Increment era
752        era_id = era_id.checked_add(1).ok_or(Error::ArithmeticOverflow)?;
753
754        let delayed_era = era_id
755            .checked_add(auction_delay)
756            .ok_or(Error::ArithmeticOverflow)?;
757
758        // Update seigniorage recipients for current era
759        {
760            let mut snapshot = detail::get_seigniorage_recipients_snapshot(self)?;
761            let recipients =
762                seigniorage_recipients(&winners, &validator_bids, &delegator_bids, &reservations)?;
763            let previous_recipients = snapshot.insert(delayed_era, recipients);
764            assert!(previous_recipients.is_none());
765
766            let snapshot = snapshot.into_iter().rev().take(snapshot_size).collect();
767            detail::set_seigniorage_recipients_snapshot(self, snapshot)?;
768        }
769
770        detail::set_era_id(self, era_id)?;
771        detail::set_era_end_timestamp_millis(self, era_end_timestamp_millis)?;
772
773        if bids_modified {
774            detail::set_validator_bids(self, validator_bids)?;
775        }
776
777        debug!("run_auction successful");
778
779        Ok(())
780    }
781
782    /// Mint and distribute seigniorage rewards to validators and their delegators,
783    /// according to `reward_factors` returned by the consensus component.
784    // TODO: rework EraInfo and other related structs, methods, etc. to report correct era-end
785    // totals of per-block rewards
786    fn distribute(&mut self, rewards: BTreeMap<PublicKey, Vec<U512>>) -> Result<(), Error> {
787        if self.get_caller() != PublicKey::System.to_account_hash() {
788            error!("invalid caller to auction distribute");
789            return Err(Error::InvalidCaller);
790        }
791
792        debug!("reading seigniorage recipients snapshot");
793        let seigniorage_recipients_snapshot = detail::get_seigniorage_recipients_snapshot(self)?;
794        let current_era_id = detail::get_era_id(self)?;
795
796        let mut era_info = EraInfo::new();
797        let seigniorage_allocations = era_info.seigniorage_allocations_mut();
798
799        debug!(rewards_set_size = rewards.len(), "processing rewards");
800        for item in rewards
801            .into_iter()
802            .filter(|(key, _amounts)| key != &PublicKey::System)
803            .map(|(proposer, amounts)| {
804                rewards_per_validator(
805                    &proposer,
806                    current_era_id,
807                    &amounts,
808                    &SeigniorageRecipientsSnapshot::V2(seigniorage_recipients_snapshot.clone()),
809                )
810                .map(|infos| infos.into_iter().map(move |info| (proposer.clone(), info)))
811            })
812            .flatten_ok()
813        {
814            let (proposer, reward_info) = item?;
815
816            // fetch most recent validator public key if public key was changed
817            // or the validator withdrew their bid completely
818            let validator_public_key =
819                match detail::get_most_recent_validator_public_key(self, proposer.clone()) {
820                    Ok(pubkey) => pubkey,
821                    Err(Error::BridgeRecordChainTooLong) => {
822                        // Validator bid's public key has been changed too many times,
823                        // and we were unable to find the current public key.
824                        // In this case we are unable to distribute rewards for this validator.
825                        continue;
826                    }
827                    Err(err) => return Err(err),
828                };
829
830            debug!(?validator_public_key, "delegator payout for validator");
831            let delegator_payouts = detail::distribute_delegator_rewards(
832                self,
833                seigniorage_allocations,
834                validator_public_key.clone(),
835                reward_info.delegator_rewards,
836            )?;
837            debug!(
838                ?validator_public_key,
839                delegator_set_size = delegator_payouts.len(),
840                "delegator payout finished"
841            );
842
843            let validator_bonding_purse = detail::distribute_validator_rewards(
844                self,
845                seigniorage_allocations,
846                validator_public_key.clone(),
847                reward_info.validator_reward,
848            )?;
849            debug!(?validator_public_key, "validator payout finished");
850
851            // mint new token and put it to the recipients' purses
852            self.mint_into_existing_purse(reward_info.validator_reward, validator_bonding_purse)?;
853
854            for (_delegator_account_hash, delegator_payout, bonding_purse) in delegator_payouts {
855                self.mint_into_existing_purse(delegator_payout, bonding_purse)?;
856            }
857            debug!("rewards minted into recipient purses");
858        }
859
860        // record allocations for this era for reporting purposes.
861        self.record_era_info(era_info)?;
862
863        Ok(())
864    }
865
866    /// Reads current era id.
867    fn read_era_id(&mut self) -> Result<EraId, Error> {
868        detail::get_era_id(self)
869    }
870
871    /// Activates a given validator's bid.  To be used when a validator has been marked as inactive
872    /// by consensus (aka "evicted").
873    fn activate_bid(&mut self, validator: PublicKey, minimum_bid: u64) -> Result<(), Error> {
874        let provided_account_hash = AccountHash::from(&validator);
875
876        if !self.is_allowed_session_caller(&provided_account_hash) {
877            return Err(Error::InvalidContext);
878        }
879
880        let key = BidAddr::from(validator).into();
881        if let Some(BidKind::Validator(mut validator_bid)) = self.read_bid(&key)? {
882            if validator_bid.staked_amount() >= minimum_bid.into() {
883                validator_bid.activate();
884                self.write_bid(key, BidKind::Validator(validator_bid))?;
885                Ok(())
886            } else {
887                Err(Error::BondTooSmall)
888            }
889        } else {
890            Err(Error::ValidatorNotFound)
891        }
892    }
893
894    /// Updates a `ValidatorBid` and all related delegator bids to use a new public key.
895    ///
896    /// This in effect "transfers" a validator bid along with its stake and all delegators
897    /// from one public key to another.
898    /// This method can only be called by the account associated with the current `ValidatorBid`.
899    ///
900    /// The arguments are the existing bid's 'validator_public_key' and the new public key.
901    fn change_bid_public_key(
902        &mut self,
903        public_key: PublicKey,
904        new_public_key: PublicKey,
905    ) -> Result<(), Error> {
906        let validator_account_hash = AccountHash::from(&public_key);
907
908        // check that the caller is the current bid's owner
909        if !self.is_allowed_session_caller(&validator_account_hash) {
910            return Err(Error::InvalidContext);
911        }
912
913        // verify that a bid for given public key exists
914        let validator_bid_addr = BidAddr::from(public_key.clone());
915        let mut validator_bid = read_validator_bid(self, &validator_bid_addr.into())?;
916
917        // verify that a bid for the new key does not exist yet
918        let new_validator_bid_addr = BidAddr::from(new_public_key.clone());
919        if self.read_bid(&new_validator_bid_addr.into())?.is_some() {
920            return Err(Error::ValidatorBidExistsAlready);
921        }
922
923        debug!("changing validator bid {validator_bid_addr} public key from {public_key} to {new_public_key}");
924
925        // store new validator bid
926        validator_bid.with_validator_public_key(new_public_key.clone());
927        self.write_bid(
928            new_validator_bid_addr.into(),
929            BidKind::Validator(validator_bid),
930        )?;
931
932        // store bridge record in place of old validator bid
933        let bridge = Bridge::new(
934            public_key.clone(),
935            new_public_key.clone(),
936            self.read_era_id()?,
937        );
938        self.write_bid(validator_bid_addr.into(), BidKind::Bridge(Box::new(bridge)))?;
939
940        debug!("transferring delegator bids from validator bid {validator_bid_addr} to {new_validator_bid_addr}");
941        let delegators = read_delegator_bids(self, &public_key)?;
942        for mut delegator in delegators {
943            let delegator_bid_addr =
944                BidAddr::new_delegator_kind(&public_key, delegator.delegator_kind());
945
946            delegator.with_validator_public_key(new_public_key.clone());
947            let new_delegator_bid_addr =
948                BidAddr::new_delegator_kind(&new_public_key, delegator.delegator_kind());
949
950            self.write_bid(
951                new_delegator_bid_addr.into(),
952                BidKind::Delegator(Box::from(delegator)),
953            )?;
954
955            debug!("pruning delegator bid {delegator_bid_addr}");
956            self.prune_bid(delegator_bid_addr);
957        }
958
959        Ok(())
960    }
961
962    /// Writes a validator credit record.
963    fn write_validator_credit(
964        &mut self,
965        validator: PublicKey,
966        era_id: EraId,
967        amount: U512,
968    ) -> Result<Option<BidAddr>, Error> {
969        // only the system may use this method
970        if self.get_caller() != PublicKey::System.to_account_hash() {
971            error!("invalid caller to auction validator_credit");
972            return Err(Error::InvalidCaller);
973        }
974
975        // is imputed public key associated with a validator bid record?
976        let bid_addr = BidAddr::new_from_public_keys(&validator, None);
977        let key = Key::BidAddr(bid_addr);
978        let _ = match self.read_bid(&key)? {
979            Some(bid_kind) => bid_kind,
980            None => {
981                warn!(
982                    ?key,
983                    ?era_id,
984                    ?amount,
985                    "attempt to add a validator credit to a non-existent validator"
986                );
987                return Ok(None);
988            }
989        };
990
991        // if amount is zero, noop
992        if amount.is_zero() {
993            return Ok(None);
994        }
995
996        // write credit record
997        let credit_addr = BidAddr::new_credit(&validator, era_id);
998        let credit_key = Key::BidAddr(credit_addr);
999        let credit_bid = match self.read_bid(&credit_key)? {
1000            Some(BidKind::Credit(mut existing_credit)) => {
1001                existing_credit.increase(amount);
1002                existing_credit
1003            }
1004            Some(_) => return Err(Error::UnexpectedBidVariant),
1005            None => Box::new(ValidatorCredit::new(validator, era_id, amount)),
1006        };
1007
1008        self.write_bid(credit_key, BidKind::Credit(credit_bid))
1009            .map(|_| Some(credit_addr))
1010    }
1011}
1012
1013/// Retrieves the total reward for a given validator or delegator in a given era.
1014pub fn reward(
1015    validator: &PublicKey,
1016    delegator: Option<&DelegatorKind>,
1017    era_id: EraId,
1018    rewards: &[U512],
1019    seigniorage_recipients_snapshot: &SeigniorageRecipientsSnapshot,
1020) -> Result<Option<U512>, Error> {
1021    let validator_rewards =
1022        match rewards_per_validator(validator, era_id, rewards, seigniorage_recipients_snapshot) {
1023            Ok(rewards) => rewards,
1024            Err(Error::ValidatorNotFound) => return Ok(None),
1025            Err(Error::MissingSeigniorageRecipients) => return Ok(None),
1026            Err(err) => return Err(err),
1027        };
1028
1029    let reward = validator_rewards
1030        .into_iter()
1031        .map(|reward_info| {
1032            if let Some(delegator) = delegator {
1033                reward_info
1034                    .delegator_rewards
1035                    .get(delegator)
1036                    .copied()
1037                    .unwrap_or_default()
1038            } else {
1039                reward_info.validator_reward
1040            }
1041        })
1042        .sum();
1043
1044    Ok(Some(reward))
1045}
1046
1047fn rewards_per_validator(
1048    validator: &PublicKey,
1049    era_id: EraId,
1050    rewards: &[U512],
1051    seigniorage_recipients_snapshot: &SeigniorageRecipientsSnapshot,
1052) -> Result<Vec<RewardsPerValidator>, Error> {
1053    let mut results = Vec::with_capacity(rewards.len());
1054
1055    for (reward_amount, eras_back) in rewards
1056        .iter()
1057        .enumerate()
1058        .map(move |(i, &amount)| (amount, i as u64))
1059        // do not process zero amounts, unless they are for the current era (we still want to
1060        // record zero allocations for the current validators in EraInfo)
1061        .filter(|(amount, eras_back)| !amount.is_zero() || *eras_back == 0)
1062    {
1063        let total_reward = Ratio::from(reward_amount);
1064        let rewarded_era = era_id
1065            .checked_sub(eras_back)
1066            .ok_or(Error::MissingSeigniorageRecipients)?;
1067
1068        // try to find validator in seigniorage snapshot
1069        let maybe_seigniorage_recipient = match seigniorage_recipients_snapshot {
1070            SeigniorageRecipientsSnapshot::V1(snapshot) => snapshot
1071                .get(&rewarded_era)
1072                .ok_or(Error::MissingSeigniorageRecipients)?
1073                .get(validator)
1074                .cloned()
1075                .map(SeigniorageRecipient::V1),
1076            SeigniorageRecipientsSnapshot::V2(snapshot) => snapshot
1077                .get(&rewarded_era)
1078                .ok_or(Error::MissingSeigniorageRecipients)?
1079                .get(validator)
1080                .cloned()
1081                .map(SeigniorageRecipient::V2),
1082        };
1083
1084        let Some(recipient) = maybe_seigniorage_recipient else {
1085            // We couldn't find the validator. If the reward amount is zero, we don't care -
1086            // the validator wasn't supposed to be rewarded in this era, anyway. Otherwise,
1087            // return an error.
1088            if reward_amount.is_zero() {
1089                continue;
1090            } else {
1091                return Err(Error::ValidatorNotFound);
1092            }
1093        };
1094
1095        let total_stake = recipient.total_stake().ok_or(Error::ArithmeticOverflow)?;
1096
1097        if total_stake.is_zero() {
1098            // The validator has completely unbonded. We can't compute the delegators' part (as
1099            // their stakes are also zero), so we just give the whole reward to the validator.
1100            // When used from `distribute`, we will mint the reward into their bonding purse
1101            // and increase their unbond request by the corresponding amount.
1102
1103            results.push(RewardsPerValidator {
1104                validator_reward: reward_amount,
1105                delegator_rewards: BTreeMap::new(),
1106            });
1107            continue;
1108        }
1109
1110        let delegator_total_stake: U512 = recipient
1111            .delegator_total_stake()
1112            .ok_or(Error::ArithmeticOverflow)?;
1113
1114        // calculate part of reward to be distributed to delegators before commission
1115        let base_delegators_part: Ratio<U512> = {
1116            let reward_multiplier: Ratio<U512> = Ratio::new(delegator_total_stake, total_stake);
1117            total_reward
1118                .checked_mul(&reward_multiplier)
1119                .ok_or(Error::ArithmeticOverflow)?
1120        };
1121
1122        let default = BTreeMap::new();
1123        let reservation_delegation_rates =
1124            recipient.reservation_delegation_rates().unwrap_or(&default);
1125        // calculate commission and final reward for each delegator
1126        let mut delegator_rewards: BTreeMap<DelegatorKind, U512> = BTreeMap::new();
1127        for (delegator_kind, delegator_stake) in recipient.delegator_stake().iter() {
1128            let reward_multiplier = Ratio::new(*delegator_stake, delegator_total_stake);
1129            let base_reward = base_delegators_part * reward_multiplier;
1130            let delegation_rate = *reservation_delegation_rates
1131                .get(delegator_kind)
1132                .unwrap_or(recipient.delegation_rate());
1133            let commission_rate = Ratio::new(
1134                U512::from(delegation_rate),
1135                U512::from(DELEGATION_RATE_DENOMINATOR),
1136            );
1137            let commission: Ratio<U512> = base_reward
1138                .checked_mul(&commission_rate)
1139                .ok_or(Error::ArithmeticOverflow)?;
1140            let reward = base_reward
1141                .checked_sub(&commission)
1142                .ok_or(Error::ArithmeticOverflow)?;
1143            delegator_rewards.insert(delegator_kind.clone(), reward.to_integer());
1144        }
1145
1146        let total_delegator_payout: U512 =
1147            delegator_rewards.iter().map(|(_, &amount)| amount).sum();
1148
1149        let validator_reward = reward_amount - total_delegator_payout;
1150
1151        results.push(RewardsPerValidator {
1152            validator_reward,
1153            delegator_rewards,
1154        });
1155    }
1156    Ok(results)
1157}
1158
1159/// Aggregated rewards data for a validator.
1160#[derive(Debug, Default)]
1161pub struct RewardsPerValidator {
1162    validator_reward: U512,
1163    delegator_rewards: BTreeMap<DelegatorKind, U512>,
1164}