1mod auction_native;
2pub mod detail;
4pub 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
31pub trait Auction:
33 StorageProvider + RuntimeProvider + MintProvider + AccountProvider + Sized
34{
35 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 fn read_seigniorage_recipients(&mut self) -> Result<SeigniorageRecipientsV2, Error> {
48 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 #[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 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 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 if reserved_slots != validator_bid.reserved_slots() {
127 let validator_bid_addr = BidAddr::from(public_key.clone());
128 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 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 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 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 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 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 updated_stake < U512::from(minimum_bid_amount) {
224 detail::create_unbonding_purse(
226 self,
227 public_key.clone(),
228 UnbondKind::Validator(public_key.clone()), *validator_bid.bonding_purse(),
230 staked_amount,
231 None,
232 )?;
233 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 detail::create_unbonding_purse(
256 self,
257 public_key.clone(),
258 UnbondKind::Validator(public_key.clone()), *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 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 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 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 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 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 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 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 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 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 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 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 fn add_reservations(&mut self, reservations: Vec<Reservation>) -> Result<(), Error> {
546 if !self.allow_auction_bids() {
547 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 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 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 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 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 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 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 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 let snapshot_size = auction_delay as usize + 2;
707 let mut era_id: EraId = detail::get_era_id(self)?;
708
709 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 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 detail::prune_validator_credits(self, era_id, &validator_credits);
750
751 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 {
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 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 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 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 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 self.record_era_info(era_info)?;
862
863 Ok(())
864 }
865
866 fn read_era_id(&mut self) -> Result<EraId, Error> {
868 detail::get_era_id(self)
869 }
870
871 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 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 if !self.is_allowed_session_caller(&validator_account_hash) {
910 return Err(Error::InvalidContext);
911 }
912
913 let validator_bid_addr = BidAddr::from(public_key.clone());
915 let mut validator_bid = read_validator_bid(self, &validator_bid_addr.into())?;
916
917 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 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 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 fn write_validator_credit(
964 &mut self,
965 validator: PublicKey,
966 era_id: EraId,
967 amount: U512,
968 ) -> Result<Option<BidAddr>, Error> {
969 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 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() {
993 return Ok(None);
994 }
995
996 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
1013pub 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 .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 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 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 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 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 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#[derive(Debug, Default)]
1161pub struct RewardsPerValidator {
1162 validator_reward: U512,
1163 delegator_rewards: BTreeMap<DelegatorKind, U512>,
1164}