1mod auction_native;
2pub mod detail;
4pub mod providers;
6
7use itertools::Itertools;
8use num_rational::Ratio;
9use std::collections::BTreeMap;
10use tracing::{debug, error, warn};
11
12use self::providers::{AccountProvider, MintProvider, RuntimeProvider, StorageProvider};
13use crate::system::auction::detail::{
14 process_undelegation, process_updated_delegator_reservation_slots,
15 process_updated_delegator_stake_boundaries, process_with_vesting_schedule, read_delegator_bids,
16 read_validator_bid, rewards_per_validator, seigniorage_recipients, DistributeTarget,
17};
18use casper_types::{
19 account::AccountHash,
20 system::auction::{
21 BidAddr, BidKind, Bridge, DelegationRate, DelegatorKind, EraInfo, EraValidators, Error,
22 Reservation, SeigniorageAllocation, SeigniorageRecipientsSnapshot, SeigniorageRecipientsV2,
23 UnbondEra, UnbondKind, ValidatorBid, ValidatorCredit, ValidatorWeights,
24 DELEGATION_RATE_DENOMINATOR,
25 },
26 AccessRights, ApiError, EraId, Key, PublicKey, URef, U512,
27};
28
29pub trait Auction:
31 StorageProvider + RuntimeProvider + MintProvider + AccountProvider + Sized
32{
33 fn get_era_validators(&mut self) -> Result<EraValidators, Error> {
36 let snapshot = detail::get_seigniorage_recipients_snapshot(self)?;
37 let era_validators = detail::era_validators_from_snapshot(snapshot);
38 Ok(era_validators)
39 }
40
41 fn read_seigniorage_recipients(&mut self) -> Result<SeigniorageRecipientsV2, Error> {
46 let era_index = detail::get_era_id(self)?;
48 let mut seigniorage_recipients_snapshot =
49 detail::get_seigniorage_recipients_snapshot(self)?;
50 let seigniorage_recipients = seigniorage_recipients_snapshot
51 .remove(&era_index)
52 .ok_or(Error::MissingSeigniorageRecipients)?;
53 Ok(seigniorage_recipients)
54 }
55
56 #[allow(clippy::too_many_arguments)]
71 fn add_bid(
72 &mut self,
73 public_key: PublicKey,
74 delegation_rate: DelegationRate,
75 amount: U512,
76 minimum_delegation_amount: u64,
77 maximum_delegation_amount: u64,
78 minimum_bid_amount: u64,
79 max_delegators_per_validator: u32,
80 reserved_slots: u32,
81 ) -> Result<U512, ApiError> {
82 if !self.allow_auction_bids() {
83 return Err(Error::AuctionBidsDisabled.into());
86 }
87
88 if amount == U512::zero() {
89 return Err(Error::BondTooSmall.into());
90 }
91
92 if delegation_rate > DELEGATION_RATE_DENOMINATOR {
93 return Err(Error::DelegationRateTooLarge.into());
94 }
95
96 if reserved_slots > max_delegators_per_validator {
97 return Err(Error::ExceededReservationSlotsLimit.into());
98 }
99
100 let provided_account_hash = AccountHash::from(&public_key);
101
102 if !self.is_allowed_session_caller(&provided_account_hash) {
103 return Err(Error::InvalidContext.into());
104 }
105 let validator_bid_key = BidAddr::from(public_key.clone()).into();
106 let (target, validator_bid) = if let Some(BidKind::Validator(mut validator_bid)) =
107 self.read_bid(&validator_bid_key)?
108 {
109 let updated_stake = validator_bid.increase_stake(amount)?;
110 if updated_stake < U512::from(minimum_bid_amount) {
111 return Err(Error::BondTooSmall.into());
112 }
113 validator_bid.activate();
115
116 validator_bid.with_delegation_rate(delegation_rate);
117 process_updated_delegator_stake_boundaries(
118 self,
119 &mut validator_bid,
120 minimum_delegation_amount,
121 maximum_delegation_amount,
122 )?;
123 process_updated_delegator_reservation_slots(
124 self,
125 &mut validator_bid,
126 max_delegators_per_validator,
127 reserved_slots,
128 )?;
129 (*validator_bid.bonding_purse(), validator_bid)
130 } else {
131 if amount < U512::from(minimum_bid_amount) {
132 return Err(Error::BondTooSmall.into());
133 }
134 let bonding_purse = self.create_purse()?;
136 let validator_bid = ValidatorBid::unlocked(
137 public_key,
138 bonding_purse,
139 amount,
140 delegation_rate,
141 minimum_delegation_amount,
142 maximum_delegation_amount,
143 reserved_slots,
144 );
145 (bonding_purse, Box::new(validator_bid))
146 };
147
148 let source = self.get_main_purse()?;
149 self.mint_transfer_direct(
150 Some(PublicKey::System.to_account_hash()),
151 source,
152 target,
153 amount,
154 None,
155 )
156 .map_err(|_| Error::TransferToBidPurse)?
157 .map_err(|mint_error| {
158 ApiError::from(mint_error)
162 })?;
163
164 let updated_amount = validator_bid.staked_amount();
165 self.write_bid(validator_bid_key, BidKind::Validator(validator_bid))?;
166 Ok(updated_amount)
167 }
168
169 fn withdraw_bid(
180 &mut self,
181 public_key: PublicKey,
182 amount: U512,
183 minimum_bid_amount: u64,
184 ) -> Result<U512, Error> {
185 let provided_account_hash = AccountHash::from(&public_key);
186
187 if !self.is_allowed_session_caller(&provided_account_hash) {
188 return Err(Error::InvalidContext);
189 }
190
191 let validator_bid_addr = BidAddr::from(public_key.clone());
192 let validator_bid_key = validator_bid_addr.into();
193 let mut validator_bid = read_validator_bid(self, &validator_bid_key)?;
194 let staked_amount = validator_bid.staked_amount();
195
196 let unbonding_amount = U512::min(amount, validator_bid.staked_amount());
198
199 let era_end_timestamp_millis = detail::get_era_end_timestamp_millis(self)?;
200 let updated_stake =
201 validator_bid.decrease_stake(unbonding_amount, era_end_timestamp_millis)?;
202
203 debug!(
204 "withdrawing bid for {validator_bid_addr} reducing {staked_amount} by {unbonding_amount} to {updated_stake}",
205 );
206 if updated_stake < U512::from(minimum_bid_amount) {
208 detail::create_unbonding_purse(
210 self,
211 public_key.clone(),
212 UnbondKind::Validator(public_key.clone()), *validator_bid.bonding_purse(),
214 staked_amount,
215 None,
216 )?;
217 let delegators = read_delegator_bids(self, &public_key)?;
219 for mut delegator in delegators {
220 let unbond_kind = delegator.unbond_kind();
221 detail::create_unbonding_purse(
222 self,
223 public_key.clone(),
224 unbond_kind,
225 *delegator.bonding_purse(),
226 delegator.staked_amount(),
227 None,
228 )?;
229 delegator.decrease_stake(delegator.staked_amount(), era_end_timestamp_millis)?;
230
231 let delegator_bid_addr = delegator.bid_addr();
232 debug!("pruning delegator bid {}", delegator_bid_addr);
233 self.prune_bid(delegator_bid_addr)
234 }
235 debug!("pruning validator bid {}", validator_bid_addr);
236 self.prune_bid(validator_bid_addr);
237 } else {
238 detail::create_unbonding_purse(
240 self,
241 public_key.clone(),
242 UnbondKind::Validator(public_key.clone()), *validator_bid.bonding_purse(),
244 unbonding_amount,
245 None,
246 )?;
247 self.write_bid(validator_bid_key, BidKind::Validator(validator_bid))?;
248 }
249
250 Ok(updated_stake)
251 }
252
253 fn delegate(
260 &mut self,
261 delegator_kind: DelegatorKind,
262 validator_public_key: PublicKey,
263 amount: U512,
264 max_delegators_per_validator: u32,
265 ) -> Result<U512, ApiError> {
266 if !self.allow_auction_bids() {
267 return Err(Error::AuctionBidsDisabled.into());
269 }
270
271 let source = match &delegator_kind {
272 DelegatorKind::PublicKey(pk) => {
273 let account_hash = pk.to_account_hash();
274 if !self.is_allowed_session_caller(&account_hash) {
275 return Err(Error::InvalidContext.into());
276 }
277 self.get_main_purse()?
278 }
279 DelegatorKind::Purse(addr) => {
280 let uref = URef::new(*addr, AccessRights::WRITE);
281 if !self.is_valid_uref(uref) {
282 return Err(Error::InvalidContext.into());
283 }
284 uref
285 }
286 };
287
288 detail::handle_delegation(
289 self,
290 delegator_kind,
291 validator_public_key,
292 source,
293 amount,
294 max_delegators_per_validator,
295 )
296 }
297
298 fn undelegate(
304 &mut self,
305 delegator_kind: DelegatorKind,
306 validator_public_key: PublicKey,
307 amount: U512,
308 ) -> Result<U512, Error> {
309 let redelegate_target = None;
310 process_undelegation(
311 self,
312 delegator_kind,
313 validator_public_key,
314 amount,
315 redelegate_target,
316 )
317 }
318
319 fn redelegate(
336 &mut self,
337 delegator_kind: DelegatorKind,
338 validator_public_key: PublicKey,
339 amount: U512,
340 new_validator: PublicKey,
341 ) -> Result<U512, Error> {
342 let redelegate_target = Some(new_validator);
343 process_undelegation(
344 self,
345 delegator_kind,
346 validator_public_key,
347 amount,
348 redelegate_target,
349 )
350 }
351
352 fn add_reservations(&mut self, reservations: Vec<Reservation>) -> Result<(), Error> {
358 if !self.allow_auction_bids() {
359 return Err(Error::AuctionBidsDisabled);
361 }
362
363 for reservation in reservations {
364 if !self
365 .is_allowed_session_caller(&AccountHash::from(reservation.validator_public_key()))
366 {
367 return Err(Error::InvalidContext);
368 }
369
370 detail::handle_add_reservation(self, reservation)?;
371 }
372 Ok(())
373 }
374
375 fn cancel_reservations(
378 &mut self,
379 validator: PublicKey,
380 delegators: Vec<DelegatorKind>,
381 max_delegators_per_validator: u32,
382 ) -> Result<(), Error> {
383 if !self.is_allowed_session_caller(&AccountHash::from(&validator)) {
384 return Err(Error::InvalidContext);
385 }
386
387 for delegator in delegators {
388 detail::handle_cancel_reservation(
389 self,
390 validator.clone(),
391 delegator.clone(),
392 max_delegators_per_validator,
393 )?;
394 }
395 Ok(())
396 }
397
398 fn slash(&mut self, validator_public_keys: Vec<PublicKey>) -> Result<(), Error> {
402 fn slash_unbonds(unbond_eras: Vec<UnbondEra>) -> U512 {
403 let mut burned_amount = U512::zero();
404 for unbond_era in unbond_eras {
405 burned_amount += *unbond_era.amount();
406 }
407 burned_amount
408 }
409
410 if self.get_caller() != PublicKey::System.to_account_hash() {
411 return Err(Error::InvalidCaller);
412 }
413
414 let mut burned_amount: U512 = U512::zero();
415
416 for validator_public_key in validator_public_keys {
417 let validator_bid_addr = BidAddr::from(validator_public_key.clone());
418 if let Some(BidKind::Validator(validator_bid)) =
420 self.read_bid(&validator_bid_addr.into())?
421 {
422 burned_amount += validator_bid.staked_amount();
423 self.prune_bid(validator_bid_addr);
424
425 let delegator_keys = {
427 let mut ret =
428 self.get_keys_by_prefix(&validator_bid_addr.delegated_account_prefix()?)?;
429 ret.extend(
430 self.get_keys_by_prefix(&validator_bid_addr.delegated_purse_prefix()?)?,
431 );
432 ret
433 };
434
435 for delegator_key in delegator_keys {
436 if let Some(BidKind::Delegator(delegator_bid)) =
437 self.read_bid(&delegator_key)?
438 {
439 burned_amount += delegator_bid.staked_amount();
440 let delegator_bid_addr = delegator_bid.bid_addr();
441 self.prune_bid(delegator_bid_addr);
442
443 let delegator_unbond_addr = match delegator_bid.delegator_kind() {
445 DelegatorKind::PublicKey(pk) => BidAddr::UnbondAccount {
446 validator: validator_public_key.to_account_hash(),
447 unbonder: pk.to_account_hash(),
448 },
449 DelegatorKind::Purse(addr) => BidAddr::UnbondPurse {
450 validator: validator_public_key.to_account_hash(),
451 unbonder: *addr,
452 },
453 };
454
455 match self.read_unbond(delegator_unbond_addr)? {
456 Some(unbond) => {
457 let burned = slash_unbonds(unbond.take_eras());
458
459 burned_amount += burned;
460 self.write_unbond(delegator_unbond_addr, None)?;
461 }
462 None => {
463 continue;
464 }
465 }
466 }
467 }
468 }
469
470 let validator_unbond_addr = BidAddr::UnbondAccount {
472 validator: validator_public_key.to_account_hash(),
473 unbonder: validator_public_key.to_account_hash(),
474 };
475 match self.read_unbond(validator_unbond_addr)? {
476 Some(unbond) => {
477 let burned = slash_unbonds(unbond.take_eras());
478 burned_amount += burned;
479 self.write_unbond(validator_unbond_addr, None)?;
480 }
481 None => {
482 continue;
483 }
484 }
485 }
486
487 self.reduce_total_supply(burned_amount)?;
488
489 Ok(())
490 }
491
492 fn run_auction(
498 &mut self,
499 era_end_timestamp_millis: u64,
500 evicted_validators: Vec<PublicKey>,
501 max_delegators_per_validator: u32,
502 include_credits: bool,
503 credit_cap: Ratio<U512>,
504 minimum_bid_amount: u64,
505 ) -> Result<(), ApiError> {
506 debug!("run_auction called");
507
508 if self.get_caller() != PublicKey::System.to_account_hash() {
509 return Err(Error::InvalidCaller.into());
510 }
511
512 let vesting_schedule_period_millis = self.vesting_schedule_period_millis();
513 let validator_slots = detail::get_validator_slots(self)?;
514 let auction_delay = detail::get_auction_delay(self)?;
515 let snapshot_size = auction_delay as usize + 2;
518 let mut era_id: EraId = detail::get_era_id(self)?;
519
520 debug!("processing unbond requests");
522 detail::process_unbond_requests(self, max_delegators_per_validator)?;
523 debug!("processing unbond request successful");
524
525 let mut validator_bids_detail = detail::get_validator_bids(self, era_id)?;
526
527 let mut bids_modified = false;
529 for (validator_public_key, validator_bid) in
530 validator_bids_detail.validator_bids_mut().iter_mut()
531 {
532 if process_with_vesting_schedule(
533 self,
534 validator_bid,
535 era_end_timestamp_millis,
536 self.vesting_schedule_period_millis(),
537 )? {
538 bids_modified = true;
539 }
540
541 if evicted_validators.contains(validator_public_key) {
542 validator_bid.deactivate();
543 bids_modified = true;
544 }
545 }
546
547 let winners = validator_bids_detail.pick_winners(
548 era_id,
549 validator_slots,
550 minimum_bid_amount,
551 include_credits,
552 credit_cap,
553 era_end_timestamp_millis,
554 vesting_schedule_period_millis,
555 )?;
556
557 let (validator_bids, validator_credits, delegator_bids, reservations) =
558 validator_bids_detail.destructure();
559
560 detail::prune_validator_credits(self, era_id, &validator_credits);
562
563 era_id = era_id.checked_add(1).ok_or(Error::ArithmeticOverflow)?;
565
566 let delayed_era = era_id
567 .checked_add(auction_delay)
568 .ok_or(Error::ArithmeticOverflow)?;
569
570 {
572 let mut snapshot = detail::get_seigniorage_recipients_snapshot(self)?;
573 let recipients =
574 seigniorage_recipients(&winners, &validator_bids, &delegator_bids, &reservations)?;
575 let previous_recipients = snapshot.insert(delayed_era, recipients);
576 assert!(previous_recipients.is_none());
577
578 let snapshot = snapshot.into_iter().rev().take(snapshot_size).collect();
579 detail::set_seigniorage_recipients_snapshot(self, snapshot)?;
580 }
581
582 detail::set_era_id(self, era_id)?;
583 detail::set_era_end_timestamp_millis(self, era_end_timestamp_millis)?;
584
585 if bids_modified {
586 detail::set_validator_bids(self, validator_bids)?;
587 }
588
589 debug!("run_auction successful");
590
591 Ok(())
592 }
593
594 fn distribute(&mut self, rewards: BTreeMap<PublicKey, Vec<U512>>) -> Result<(), Error> {
599 if self.get_caller() != PublicKey::System.to_account_hash() {
600 error!("invalid caller to auction distribute");
601 return Err(Error::InvalidCaller);
602 }
603
604 debug!("reading seigniorage recipients snapshot");
605 let seigniorage_recipients_snapshot = detail::get_seigniorage_recipients_snapshot(self)?;
606 let current_era_id = detail::get_era_id(self)?;
607
608 let mut era_info = EraInfo::new();
609 let seigniorage_allocations = era_info.seigniorage_allocations_mut();
610
611 debug!(rewards_set_size = rewards.len(), "processing rewards");
612 for item in rewards
613 .into_iter()
614 .filter(|(key, _amounts)| key != &PublicKey::System)
615 .map(|(proposer, amounts)| {
616 rewards_per_validator(
617 &proposer,
618 current_era_id,
619 &amounts,
620 &SeigniorageRecipientsSnapshot::V2(seigniorage_recipients_snapshot.clone()),
621 )
622 .map(|infos| infos.into_iter().map(move |info| (proposer.clone(), info)))
623 })
624 .flatten_ok()
625 {
626 let (validator_public_key, reward_info) = item?;
627
628 let validator_bid_addr = BidAddr::Validator(validator_public_key.to_account_hash());
629 let mut maybe_bridged_validator_addrs: Option<Vec<BidAddr>> = None;
630 let validator_reward_amount = reward_info.validator_reward();
631 let (validator_bonding_purse, min_del, max_del) =
632 match detail::get_distribution_target(self, validator_bid_addr) {
633 Ok(target) => match target {
634 DistributeTarget::Validator(mut validator_bid) => {
635 debug!(?validator_public_key, "validator payout starting ");
636 let validator_bonding_purse = *validator_bid.bonding_purse();
637 validator_bid.increase_stake(validator_reward_amount)?;
638
639 self.write_bid(
640 validator_bid_addr.into(),
641 BidKind::Validator(validator_bid.clone()),
642 )?;
643 (
644 validator_bonding_purse,
645 validator_bid.minimum_delegation_amount().into(),
646 validator_bid.maximum_delegation_amount().into(),
647 )
648 }
649 DistributeTarget::BridgedValidator {
650 requested_validator_bid_addr: _requested_validator_bid_addr,
651 current_validator_bid_addr,
652 bridged_validator_addrs,
653 mut validator_bid,
654 } => {
655 debug!(?validator_public_key, "bridged validator payout starting ");
656 maybe_bridged_validator_addrs = Some(bridged_validator_addrs); let validator_bonding_purse = *validator_bid.bonding_purse();
658 validator_bid.increase_stake(validator_reward_amount)?;
659
660 self.write_bid(
661 current_validator_bid_addr.into(),
662 BidKind::Validator(validator_bid.clone()),
663 )?;
664 (
665 validator_bonding_purse,
666 validator_bid.minimum_delegation_amount().into(),
667 validator_bid.maximum_delegation_amount().into(),
668 )
669 }
670 DistributeTarget::Unbond(unbond) => match unbond.target_unbond_era() {
671 Some(mut unbond_era) => {
672 let account_hash = validator_public_key.to_account_hash();
673 let unbond_addr = BidAddr::UnbondAccount {
674 validator: account_hash,
675 unbonder: account_hash,
676 };
677 let validator_bonding_purse = *unbond_era.bonding_purse();
678 let new_amount =
679 unbond_era.amount().saturating_add(validator_reward_amount);
680 unbond_era.with_amount(new_amount);
681 self.write_unbond(unbond_addr, Some(*unbond.clone()))?;
682 (validator_bonding_purse, U512::MAX, U512::MAX)
683 }
684 None => {
685 warn!(
686 ?validator_public_key,
687 "neither validator bid or unbond found"
688 );
689 continue;
690 }
691 },
692 DistributeTarget::Delegator(_) => {
693 return Err(Error::UnexpectedBidVariant);
694 }
695 },
696 Err(Error::BridgeRecordChainTooLong) => {
697 warn!(?validator_public_key, "bridge record chain too long");
698 continue;
699 }
700 Err(err) => return Err(err),
701 };
702
703 self.mint_into_existing_purse(validator_reward_amount, validator_bonding_purse)?;
704 seigniorage_allocations.push(SeigniorageAllocation::validator(
705 validator_public_key.clone(),
706 validator_reward_amount,
707 ));
708 debug!(?validator_public_key, "validator payout finished");
709
710 debug!(?validator_public_key, "delegator payouts for validator");
711 let mut undelegates = vec![];
712 let mut prunes = vec![];
713 for (delegator_kind, delegator_reward) in reward_info.take_delegator_rewards() {
714 let mut delegator_bid_addrs = Vec::with_capacity(2);
715 if let Some(bridged_validator_addrs) = &maybe_bridged_validator_addrs {
716 for bridged_addr in bridged_validator_addrs {
717 delegator_bid_addrs.push(BidAddr::new_delegator_kind_relaxed(
718 bridged_addr.validator_account_hash(),
719 &delegator_kind,
720 ))
721 }
722 }
723 delegator_bid_addrs.push(BidAddr::new_delegator_kind_relaxed(
724 validator_bid_addr.validator_account_hash(),
725 &delegator_kind,
726 ));
727 let mut maybe_delegator_bonding_purse: Option<URef> = None;
728 for delegator_bid_addr in delegator_bid_addrs {
729 if delegator_reward.is_zero() {
730 maybe_delegator_bonding_purse = None;
731 break; } else {
733 let delegator_bid_key = delegator_bid_addr.into();
734 match detail::get_distribution_target(self, delegator_bid_addr) {
735 Ok(target) => match target {
736 DistributeTarget::Delegator(mut delegator_bid) => {
737 let delegator_bonding_purse = *delegator_bid.bonding_purse();
738 let increased_stake =
739 delegator_bid.increase_stake(delegator_reward)?;
740 if increased_stake < min_del {
741 undelegates.push((
744 delegator_kind.clone(),
745 validator_public_key.clone(),
746 increased_stake,
747 ));
748 prunes.push(delegator_bid_addr);
749 } else if increased_stake > max_del {
750 let unbond_amount = increased_stake.saturating_sub(max_del);
752 if !unbond_amount.is_zero() {
753 undelegates.push((
754 delegator_kind.clone(),
755 validator_public_key.clone(),
756 unbond_amount,
757 ));
758 }
759 }
760 self.write_bid(
761 delegator_bid_key,
762 BidKind::Delegator(delegator_bid),
763 )?;
764 maybe_delegator_bonding_purse = Some(delegator_bonding_purse);
765 break;
766 }
767 DistributeTarget::Unbond(mut unbond) => {
768 match unbond.target_unbond_era_mut() {
769 Some(unbond_era) => {
770 let unbond_addr = BidAddr::new_delegator_unbond_relaxed(
771 delegator_bid_addr.validator_account_hash(),
772 &delegator_kind,
773 );
774 let delegator_bonding_purse =
775 *unbond_era.bonding_purse();
776 let new_amount = unbond_era
777 .amount()
778 .saturating_add(delegator_reward);
779
780 unbond_era.with_amount(new_amount);
781 self.write_unbond(unbond_addr, Some(*unbond.clone()))?;
782 maybe_delegator_bonding_purse =
783 Some(delegator_bonding_purse);
784 break;
785 }
786 None => {
787 debug!(
788 ?delegator_bid_key,
789 "neither delegator bid or unbond found"
790 );
791 }
793 }
794 }
795 DistributeTarget::Validator(_)
796 | DistributeTarget::BridgedValidator { .. } => {
797 return Err(Error::UnexpectedBidVariant)
798 }
799 },
800 Err(Error::DelegatorNotFound) => {
801 debug!(
802 ?validator_public_key,
803 ?delegator_bid_addr,
804 "delegator bid not found"
805 );
806 }
808 Err(err) => return Err(err),
809 }
810 }
811 }
812
813 let allocation = SeigniorageAllocation::delegator_kind(
815 delegator_kind,
816 validator_public_key.clone(),
817 delegator_reward,
818 );
819 seigniorage_allocations.push(allocation);
820 if let Some(delegator_bonding_purse) = maybe_delegator_bonding_purse {
821 self.mint_into_existing_purse(delegator_reward, delegator_bonding_purse)?;
822 }
823 }
824
825 for (kind, pk, unbond_amount) in undelegates {
826 debug!(?kind, ?pk, ?unbond_amount, "unbonding delegator");
827 self.undelegate(kind, pk, unbond_amount)?;
828 }
829
830 for bid_addr in prunes {
831 debug!(?bid_addr, "pruning bid");
832 self.prune_bid(bid_addr);
833 }
834
835 debug!(
836 ?validator_public_key,
837 delegator_set_size = seigniorage_allocations.len(),
838 "delegator payout finished"
839 );
840
841 debug!(
842 ?validator_public_key,
843 "rewards minted into recipient purses"
844 );
845 }
846
847 self.record_era_info(era_info)?;
849
850 Ok(())
851 }
852
853 fn read_era_id(&mut self) -> Result<EraId, Error> {
855 detail::get_era_id(self)
856 }
857
858 fn activate_bid(&mut self, validator: PublicKey, minimum_bid: u64) -> Result<(), Error> {
861 let provided_account_hash = AccountHash::from(&validator);
862
863 if !self.is_allowed_session_caller(&provided_account_hash) {
864 return Err(Error::InvalidContext);
865 }
866
867 let key = BidAddr::from(validator).into();
868 if let Some(BidKind::Validator(mut validator_bid)) = self.read_bid(&key)? {
869 if validator_bid.staked_amount() >= minimum_bid.into() {
870 validator_bid.activate();
871 self.write_bid(key, BidKind::Validator(validator_bid))?;
872 Ok(())
873 } else {
874 Err(Error::BondTooSmall)
875 }
876 } else {
877 Err(Error::ValidatorNotFound)
878 }
879 }
880
881 fn change_bid_public_key(
889 &mut self,
890 public_key: PublicKey,
891 new_public_key: PublicKey,
892 ) -> Result<(), Error> {
893 let validator_account_hash = AccountHash::from(&public_key);
894
895 if !self.is_allowed_session_caller(&validator_account_hash) {
897 return Err(Error::InvalidContext);
898 }
899
900 let validator_bid_addr = BidAddr::from(public_key.clone());
902 let mut validator_bid = read_validator_bid(self, &validator_bid_addr.into())?;
903
904 let new_validator_bid_addr = BidAddr::from(new_public_key.clone());
906 if self.read_bid(&new_validator_bid_addr.into())?.is_some() {
907 return Err(Error::ValidatorBidExistsAlready);
908 }
909
910 debug!("changing validator bid {validator_bid_addr} public key from {public_key} to {new_public_key}");
911
912 validator_bid.with_validator_public_key(new_public_key.clone());
914 self.write_bid(
915 new_validator_bid_addr.into(),
916 BidKind::Validator(validator_bid),
917 )?;
918
919 let bridge = Bridge::new(
921 public_key.clone(),
922 new_public_key.clone(),
923 self.read_era_id()?,
924 );
925 self.write_bid(
928 validator_bid_addr.into(),
929 BidKind::Bridge(Box::new(bridge.clone())),
930 )?;
931 let rev_addr = BidAddr::new_validator_rev_addr_from_public_key(new_public_key.clone());
934 self.write_bid(rev_addr.into(), BidKind::Bridge(Box::new(bridge)))?;
935
936 debug!("transferring delegator bids from validator bid {validator_bid_addr} to {new_validator_bid_addr}");
937 let delegators = read_delegator_bids(self, &public_key)?;
938 for mut delegator in delegators {
939 let delegator_bid_addr =
940 BidAddr::new_delegator_kind(&public_key, delegator.delegator_kind());
941
942 delegator.with_validator_public_key(new_public_key.clone());
943 let new_delegator_bid_addr =
944 BidAddr::new_delegator_kind(&new_public_key, delegator.delegator_kind());
945
946 self.write_bid(
947 new_delegator_bid_addr.into(),
948 BidKind::Delegator(Box::from(delegator)),
949 )?;
950
951 debug!("pruning delegator bid {delegator_bid_addr}");
952 self.prune_bid(delegator_bid_addr);
953 }
954
955 Ok(())
956 }
957
958 fn write_validator_credit(
960 &mut self,
961 validator: PublicKey,
962 era_id: EraId,
963 amount: U512,
964 ) -> Result<Option<BidAddr>, Error> {
965 if self.get_caller() != PublicKey::System.to_account_hash() {
967 error!("invalid caller to auction validator_credit");
968 return Err(Error::InvalidCaller);
969 }
970
971 let bid_addr = BidAddr::new_from_public_keys(&validator, None);
973 let key = Key::BidAddr(bid_addr);
974 let _ = match self.read_bid(&key)? {
975 Some(bid_kind) => bid_kind,
976 None => {
977 warn!(
978 ?key,
979 ?era_id,
980 ?amount,
981 "attempt to add a validator credit to a non-existent validator"
982 );
983 return Ok(None);
984 }
985 };
986
987 if amount.is_zero() {
989 return Ok(None);
990 }
991
992 let credit_addr = BidAddr::new_credit(&validator, era_id);
994 let credit_key = Key::BidAddr(credit_addr);
995 let credit_bid = match self.read_bid(&credit_key)? {
996 Some(BidKind::Credit(mut existing_credit)) => {
997 existing_credit.increase(amount);
998 existing_credit
999 }
1000 Some(_) => return Err(Error::UnexpectedBidVariant),
1001 None => Box::new(ValidatorCredit::new(validator, era_id, amount)),
1002 };
1003
1004 self.write_bid(credit_key, BidKind::Credit(credit_bid))
1005 .map(|_| Some(credit_addr))
1006 }
1007}