casper_storage/system/
protocol_upgrade.rs

1//! Support for applying upgrades on the execution engine.
2use num_rational::Ratio;
3use std::{
4    cell::RefCell,
5    collections::{BTreeMap, BTreeSet},
6    rc::Rc,
7};
8
9use thiserror::Error;
10use tracing::{debug, error, info, warn};
11
12use casper_types::{
13    addressable_entity::{
14        ActionThresholds, AssociatedKeys, EntityKind, NamedKeyAddr, NamedKeyValue, Weight,
15    },
16    bytesrepr::{self, ToBytes},
17    contracts::{ContractHash, ContractPackageStatus, NamedKeys},
18    system::{
19        auction::{
20            BidAddr, BidAddrTag, BidKind, DelegatorBid, DelegatorKind,
21            SeigniorageRecipientsSnapshotV1, SeigniorageRecipientsSnapshotV2,
22            SeigniorageRecipientsV2, Unbond, ValidatorBid, AUCTION_DELAY_KEY,
23            DEFAULT_SEIGNIORAGE_RECIPIENTS_SNAPSHOT_VERSION, LOCKED_FUNDS_PERIOD_KEY,
24            SEIGNIORAGE_RECIPIENTS_SNAPSHOT_KEY, SEIGNIORAGE_RECIPIENTS_SNAPSHOT_VERSION_KEY,
25            UNBONDING_DELAY_KEY, VALIDATOR_SLOTS_KEY,
26        },
27        handle_payment::{ACCUMULATION_PURSE_KEY, PAYMENT_PURSE_KEY},
28        mint::{
29            MINT_GAS_HOLD_HANDLING_KEY, MINT_GAS_HOLD_INTERVAL_KEY, ROUND_SEIGNIORAGE_RATE_KEY,
30            TOTAL_SUPPLY_KEY,
31        },
32        SystemEntityType, AUCTION, HANDLE_PAYMENT, MINT,
33    },
34    AccessRights, AddressableEntity, AddressableEntityHash, ByteCode, ByteCodeAddr, ByteCodeHash,
35    ByteCodeKind, CLValue, CLValueError, Contract, Digest, EntityAddr, EntityVersionKey,
36    EntityVersions, EntryPointAddr, EntryPointValue, EntryPoints, FeeHandling, Groups, HashAddr,
37    Key, KeyTag, Motes, Package, PackageHash, PackageStatus, Phase, ProtocolUpgradeConfig,
38    ProtocolVersion, PublicKey, StoredValue, SystemHashRegistry, URef, U512,
39};
40
41use crate::{
42    global_state::state::StateProvider,
43    tracking_copy::{TrackingCopy, TrackingCopyEntityExt, TrackingCopyExt},
44    AddressGenerator,
45};
46
47const NO_CARRY_FORWARD: bool = false;
48const CARRY_FORWARD: bool = true;
49
50/// Represents outcomes of a failed protocol upgrade.
51#[derive(Clone, Error, Debug)]
52pub enum ProtocolUpgradeError {
53    /// Protocol version used in the deploy is invalid.
54    #[error("Invalid protocol version: {0}")]
55    InvalidProtocolVersion(ProtocolVersion),
56    /// Error validating a protocol upgrade config.
57    #[error("Invalid upgrade config")]
58    InvalidUpgradeConfig,
59    /// Unable to retrieve a system contract.
60    #[error("Unable to retrieve system contract: {0}")]
61    UnableToRetrieveSystemContract(String),
62    /// Unable to retrieve a system contract package.
63    #[error("Unable to retrieve system contract package: {0}")]
64    UnableToRetrieveSystemContractPackage(String),
65    /// Unable to disable previous version of a system contract.
66    #[error("Failed to disable previous version of system contract: {0}")]
67    FailedToDisablePreviousVersion(String),
68    /// (De)serialization error.
69    #[error("Bytesrepr error: {0}")]
70    Bytesrepr(String),
71    /// Failed to create system entity registry.
72    #[error("Failed to insert system entity registry")]
73    FailedToCreateSystemRegistry,
74    /// Found unexpected variant of a key.
75    #[error("Unexpected key variant")]
76    UnexpectedKeyVariant,
77    /// Found unexpected variant of a stored value.
78    #[error("Unexpected stored value variant")]
79    UnexpectedStoredValueVariant,
80    /// Failed to convert into a CLValue.
81    #[error("{0}")]
82    CLValue(String),
83    /// Missing system contract hash.
84    #[error("Missing system contract hash: {0}")]
85    MissingSystemEntityHash(String),
86    /// Tracking copy error.
87    #[error("{0}")]
88    TrackingCopy(crate::tracking_copy::TrackingCopyError),
89}
90
91impl From<CLValueError> for ProtocolUpgradeError {
92    fn from(v: CLValueError) -> Self {
93        Self::CLValue(v.to_string())
94    }
95}
96
97impl From<crate::tracking_copy::TrackingCopyError> for ProtocolUpgradeError {
98    fn from(err: crate::tracking_copy::TrackingCopyError) -> Self {
99        ProtocolUpgradeError::TrackingCopy(err)
100    }
101}
102
103impl From<bytesrepr::Error> for ProtocolUpgradeError {
104    fn from(error: bytesrepr::Error) -> Self {
105        ProtocolUpgradeError::Bytesrepr(error.to_string())
106    }
107}
108
109/// Addresses for system entities.
110pub struct SystemHashAddresses {
111    mint: HashAddr,
112    auction: HashAddr,
113    handle_payment: HashAddr,
114}
115
116impl SystemHashAddresses {
117    /// Creates a new instance of system entity addresses.
118    pub fn new(mint: HashAddr, auction: HashAddr, handle_payment: HashAddr) -> Self {
119        SystemHashAddresses {
120            mint,
121            auction,
122            handle_payment,
123        }
124    }
125
126    /// Mint address.
127    pub fn mint(&self) -> HashAddr {
128        self.mint
129    }
130
131    /// Auction address.
132    pub fn auction(&self) -> HashAddr {
133        self.auction
134    }
135
136    /// Handle payment address.
137    pub fn handle_payment(&self) -> HashAddr {
138        self.handle_payment
139    }
140}
141
142/// The system upgrader deals with conducting an actual protocol upgrade.
143pub struct ProtocolUpgrader<S>
144where
145    S: StateProvider,
146{
147    config: ProtocolUpgradeConfig,
148    address_generator: Rc<RefCell<AddressGenerator>>,
149    tracking_copy: TrackingCopy<<S as StateProvider>::Reader>,
150}
151
152impl<S> ProtocolUpgrader<S>
153where
154    S: StateProvider,
155{
156    /// Creates new system upgrader instance.
157    pub fn new(
158        config: ProtocolUpgradeConfig,
159        protocol_upgrade_config_hash: Digest,
160        tracking_copy: TrackingCopy<<S as StateProvider>::Reader>,
161    ) -> Self {
162        let phase = Phase::System;
163        let protocol_upgrade_config_hash_bytes = protocol_upgrade_config_hash.as_ref();
164
165        let address_generator = {
166            let generator = AddressGenerator::new(protocol_upgrade_config_hash_bytes, phase);
167            Rc::new(RefCell::new(generator))
168        };
169        ProtocolUpgrader {
170            config,
171            address_generator,
172            tracking_copy,
173        }
174    }
175
176    /// Apply a protocol upgrade.
177    pub fn upgrade(
178        mut self,
179        pre_state_hash: Digest,
180    ) -> Result<TrackingCopy<<S as StateProvider>::Reader>, ProtocolUpgradeError> {
181        self.check_next_protocol_version_validity()?;
182        self.handle_global_state_updates();
183        let system_entity_addresses = self.handle_system_hashes()?;
184
185        if self.config.enable_addressable_entity() {
186            self.migrate_system_account(pre_state_hash)?;
187            self.create_accumulation_purse_if_required(
188                &system_entity_addresses.handle_payment(),
189                self.config.fee_handling(),
190            )?;
191            self.migrate_or_refresh_system_entities(&system_entity_addresses)?;
192        } else {
193            self.create_accumulation_purse_if_required_by_contract(
194                &system_entity_addresses.handle_payment(),
195                self.config.fee_handling(),
196            )?;
197            self.refresh_system_contracts(&system_entity_addresses)?;
198        }
199
200        self.handle_payment_purse_check(
201            system_entity_addresses.handle_payment(),
202            system_entity_addresses.mint(),
203        )?;
204        self.handle_new_gas_hold_config(system_entity_addresses.mint())?;
205        self.handle_new_validator_slots(system_entity_addresses.auction())?;
206        self.handle_new_auction_delay(system_entity_addresses.auction())?;
207        self.handle_new_locked_funds_period_millis(system_entity_addresses.auction())?;
208        self.handle_new_unbonding_delay(system_entity_addresses.auction())?;
209        self.handle_new_round_seigniorage_rate(system_entity_addresses.mint())?;
210        self.handle_unbonds_migration()?;
211        self.handle_bids_migration(
212            self.config.validator_minimum_bid_amount(),
213            self.config.minimum_delegation_amount(),
214            self.config.maximum_delegation_amount(),
215        )?;
216        self.handle_era_info_migration()?;
217        self.handle_seignorage_snapshot_migration(system_entity_addresses.auction())?;
218
219        Ok(self.tracking_copy)
220    }
221
222    /// Determine if the next protocol version is a legitimate semver progression.
223    pub fn check_next_protocol_version_validity(&self) -> Result<(), ProtocolUpgradeError> {
224        debug!("check next protocol version validity");
225        let current_protocol_version = self.config.current_protocol_version();
226        let new_protocol_version = self.config.new_protocol_version();
227
228        let upgrade_check_result =
229            current_protocol_version.check_next_version(&new_protocol_version);
230
231        if upgrade_check_result.is_invalid() {
232            Err(ProtocolUpgradeError::InvalidProtocolVersion(
233                new_protocol_version,
234            ))
235        } else {
236            Ok(())
237        }
238    }
239
240    fn system_hash_registry(&self) -> Result<SystemHashRegistry, ProtocolUpgradeError> {
241        debug!("system entity registry");
242        let registry = if let Ok(registry) = self.tracking_copy.get_system_entity_registry() {
243            registry
244        } else {
245            // Check the upgrade config for the registry
246            let upgrade_registry = self
247                .config
248                .global_state_update()
249                .get(&Key::SystemEntityRegistry)
250                .ok_or_else(|| {
251                    error!("Registry is absent in upgrade config");
252                    ProtocolUpgradeError::FailedToCreateSystemRegistry
253                })?
254                .to_owned();
255            if let StoredValue::CLValue(cl_registry) = upgrade_registry {
256                CLValue::into_t::<SystemHashRegistry>(cl_registry).map_err(|error| {
257                    let error_msg = format!("Conversion to system registry failed: {:?}", error);
258                    error!("{}", error_msg);
259                    ProtocolUpgradeError::Bytesrepr(error_msg)
260                })?
261            } else {
262                error!("Failed to create registry as StoreValue in upgrade config is not CLValue");
263                return Err(ProtocolUpgradeError::FailedToCreateSystemRegistry);
264            }
265        };
266        Ok(registry)
267    }
268
269    /// Handle system entities.
270    pub fn handle_system_hashes(&mut self) -> Result<SystemHashAddresses, ProtocolUpgradeError> {
271        debug!("handle system entities");
272        let mut registry = self.system_hash_registry()?;
273
274        let mint = *registry.get(MINT).ok_or_else(|| {
275            error!("Missing system mint entity hash");
276            ProtocolUpgradeError::MissingSystemEntityHash(MINT.to_string())
277        })?;
278        let auction = *registry.get(AUCTION).ok_or_else(|| {
279            error!("Missing system auction entity hash");
280            ProtocolUpgradeError::MissingSystemEntityHash(AUCTION.to_string())
281        })?;
282        let handle_payment = *registry.get(HANDLE_PAYMENT).ok_or_else(|| {
283            error!("Missing system handle payment entity hash");
284            ProtocolUpgradeError::MissingSystemEntityHash(HANDLE_PAYMENT.to_string())
285        })?;
286        if let Some(standard_payment_hash) = registry.remove_standard_payment() {
287            // Write the chainspec registry to global state
288            let cl_value_chainspec_registry = CLValue::from_t(registry)
289                .map_err(|error| ProtocolUpgradeError::Bytesrepr(error.to_string()))?;
290
291            self.tracking_copy.write(
292                Key::SystemEntityRegistry,
293                StoredValue::CLValue(cl_value_chainspec_registry),
294            );
295
296            // Prune away standard payment from global state.
297            self.tracking_copy.prune(Key::Hash(standard_payment_hash));
298        };
299
300        // Write the chainspec registry to global state
301        let cl_value_chainspec_registry = CLValue::from_t(self.config.chainspec_registry().clone())
302            .map_err(|error| ProtocolUpgradeError::Bytesrepr(error.to_string()))?;
303
304        self.tracking_copy.write(
305            Key::ChainspecRegistry,
306            StoredValue::CLValue(cl_value_chainspec_registry),
307        );
308
309        let system_hash_addresses = SystemHashAddresses::new(mint, auction, handle_payment);
310
311        Ok(system_hash_addresses)
312    }
313
314    /// Bump major version and/or update the entry points for system contracts.
315    pub fn migrate_or_refresh_system_entities(
316        &mut self,
317        system_entity_addresses: &SystemHashAddresses,
318    ) -> Result<(), ProtocolUpgradeError> {
319        debug!("refresh system contracts");
320        self.migrate_or_refresh_system_entity_entry_points(
321            system_entity_addresses.mint(),
322            SystemEntityType::Mint,
323        )?;
324        self.migrate_or_refresh_system_entity_entry_points(
325            system_entity_addresses.auction(),
326            SystemEntityType::Auction,
327        )?;
328        self.migrate_or_refresh_system_entity_entry_points(
329            system_entity_addresses.handle_payment(),
330            SystemEntityType::HandlePayment,
331        )?;
332
333        Ok(())
334    }
335
336    /// Bump major version and/or update the entry points for system contracts.
337    pub fn refresh_system_contracts(
338        &mut self,
339        system_entity_addresses: &SystemHashAddresses,
340    ) -> Result<(), ProtocolUpgradeError> {
341        self.refresh_system_contract_entry_points(
342            system_entity_addresses.mint(),
343            SystemEntityType::Mint,
344        )?;
345        self.refresh_system_contract_entry_points(
346            system_entity_addresses.auction(),
347            SystemEntityType::Auction,
348        )?;
349        self.refresh_system_contract_entry_points(
350            system_entity_addresses.handle_payment(),
351            SystemEntityType::HandlePayment,
352        )?;
353
354        Ok(())
355    }
356
357    /// Refresh the system contracts with an updated set of entry points,
358    /// and bump the contract version at a major version upgrade.
359    fn migrate_or_refresh_system_entity_entry_points(
360        &mut self,
361        hash_addr: HashAddr,
362        system_entity_type: SystemEntityType,
363    ) -> Result<(), ProtocolUpgradeError> {
364        debug!(%system_entity_type, "refresh system contract entry points");
365        let entity_name = system_entity_type.entity_name();
366
367        let (mut entity, maybe_named_keys, must_carry_forward) =
368            match self.retrieve_system_entity(hash_addr, system_entity_type) {
369                Ok(ret) => ret,
370                Err(err) => {
371                    error!("{:?}", err);
372                    return Err(err);
373                }
374            };
375
376        let mut package =
377            self.retrieve_system_package(entity.package_hash(), system_entity_type)?;
378
379        let entity_hash = AddressableEntityHash::new(hash_addr);
380        let entity_addr = EntityAddr::new_system(entity_hash.value());
381        package.disable_entity_version(entity_addr).map_err(|_| {
382            ProtocolUpgradeError::FailedToDisablePreviousVersion(entity_name.to_string())
383        })?;
384
385        entity.set_protocol_version(self.config.new_protocol_version());
386
387        let new_entity = AddressableEntity::new(
388            entity.package_hash(),
389            ByteCodeHash::default(),
390            self.config.new_protocol_version(),
391            URef::default(),
392            AssociatedKeys::default(),
393            ActionThresholds::default(),
394            EntityKind::System(system_entity_type),
395        );
396
397        let byte_code_key = Key::byte_code_key(ByteCodeAddr::Empty);
398        let byte_code = ByteCode::new(ByteCodeKind::Empty, vec![]);
399
400        self.tracking_copy
401            .write(byte_code_key, StoredValue::ByteCode(byte_code));
402
403        let entity_key = new_entity.entity_key(entity_hash);
404
405        self.tracking_copy
406            .write(entity_key, StoredValue::AddressableEntity(new_entity));
407
408        if let Some(named_keys) = maybe_named_keys {
409            for (string, key) in named_keys.into_inner().into_iter() {
410                let entry_addr = NamedKeyAddr::new_from_string(entity_addr, string.clone())
411                    .map_err(|err| ProtocolUpgradeError::Bytesrepr(err.to_string()))?;
412
413                let entry_key = Key::NamedKey(entry_addr);
414
415                let named_key_value = NamedKeyValue::from_concrete_values(key, string)
416                    .map_err(|error| ProtocolUpgradeError::CLValue(error.to_string()))?;
417
418                self.tracking_copy
419                    .write(entry_key, StoredValue::NamedKey(named_key_value));
420            }
421        }
422
423        let entry_points = system_entity_type.entry_points();
424
425        for entry_point in entry_points.take_entry_points() {
426            let entry_point_addr =
427                EntryPointAddr::new_v1_entry_point_addr(entity_addr, entry_point.name())
428                    .map_err(|error| ProtocolUpgradeError::Bytesrepr(error.to_string()))?;
429            self.tracking_copy.write(
430                Key::EntryPoint(entry_point_addr),
431                StoredValue::EntryPoint(EntryPointValue::V1CasperVm(entry_point)),
432            );
433        }
434
435        package.insert_entity_version(
436            self.config.new_protocol_version().value().major,
437            entity_addr,
438        );
439
440        self.tracking_copy.write(
441            Key::SmartContract(entity.package_hash().value()),
442            StoredValue::SmartContract(package),
443        );
444
445        if must_carry_forward {
446            // carry forward
447            let package_key = Key::SmartContract(entity.package_hash().value());
448            let uref = URef::default();
449            let indirection = CLValue::from_t((package_key, uref))
450                .map_err(|cl_error| ProtocolUpgradeError::CLValue(cl_error.to_string()))?;
451
452            self.tracking_copy.write(
453                Key::Hash(entity.package_hash().value()),
454                StoredValue::CLValue(indirection),
455            );
456
457            let contract_wasm_key = Key::Hash(entity.byte_code_hash().value());
458            let contract_wasm_indirection = CLValue::from_t(Key::ByteCode(ByteCodeAddr::Empty))
459                .map_err(|cl_error| ProtocolUpgradeError::CLValue(cl_error.to_string()))?;
460            self.tracking_copy.write(
461                contract_wasm_key,
462                StoredValue::CLValue(contract_wasm_indirection),
463            );
464
465            let contract_indirection = CLValue::from_t(Key::AddressableEntity(entity_addr))
466                .map_err(|cl_error| ProtocolUpgradeError::CLValue(cl_error.to_string()))?;
467
468            self.tracking_copy.write(
469                Key::Hash(entity_addr.value()),
470                StoredValue::CLValue(contract_indirection),
471            )
472        }
473
474        Ok(())
475    }
476
477    fn retrieve_system_package(
478        &mut self,
479        package_hash: PackageHash,
480        system_contract_type: SystemEntityType,
481    ) -> Result<Package, ProtocolUpgradeError> {
482        debug!(%system_contract_type, "retrieve system package");
483        if let Some(StoredValue::SmartContract(system_entity)) = self
484            .tracking_copy
485            .read(&Key::SmartContract(package_hash.value()))
486            .map_err(|_| {
487                ProtocolUpgradeError::UnableToRetrieveSystemContractPackage(
488                    system_contract_type.to_string(),
489                )
490            })?
491        {
492            return Ok(system_entity);
493        }
494
495        if let Some(StoredValue::ContractPackage(contract_package)) = self
496            .tracking_copy
497            .read(&Key::Hash(package_hash.value()))
498            .map_err(|_| {
499                ProtocolUpgradeError::UnableToRetrieveSystemContractPackage(
500                    system_contract_type.to_string(),
501                )
502            })?
503        {
504            let versions: BTreeMap<EntityVersionKey, EntityAddr> = contract_package
505                .versions()
506                .iter()
507                .map(|(version, contract_hash)| {
508                    let entity_version = EntityVersionKey::new(2, version.contract_version());
509                    let entity_hash = EntityAddr::System(contract_hash.value());
510                    (entity_version, entity_hash)
511                })
512                .collect();
513
514            let disabled_versions = contract_package
515                .disabled_versions()
516                .iter()
517                .map(|contract_versions| {
518                    EntityVersionKey::new(
519                        contract_versions.protocol_version_major(),
520                        contract_versions.contract_version(),
521                    )
522                })
523                .collect();
524
525            let lock_status = if contract_package.lock_status() == ContractPackageStatus::Locked {
526                PackageStatus::Locked
527            } else {
528                PackageStatus::Unlocked
529            };
530
531            let groups = contract_package.take_groups();
532            return Ok(Package::new(
533                versions.into(),
534                disabled_versions,
535                groups,
536                lock_status,
537            ));
538        }
539
540        Err(ProtocolUpgradeError::UnableToRetrieveSystemContractPackage(
541            system_contract_type.to_string(),
542        ))
543    }
544
545    fn retrieve_system_entity(
546        &mut self,
547        hash_addr: HashAddr,
548        system_contract_type: SystemEntityType,
549    ) -> Result<(AddressableEntity, Option<NamedKeys>, bool), ProtocolUpgradeError> {
550        debug!(%system_contract_type, "retrieve system entity");
551        if let Some(StoredValue::Contract(system_contract)) = self
552            .tracking_copy
553            .read(&Key::Hash(hash_addr))
554            .map_err(|_| {
555                ProtocolUpgradeError::UnableToRetrieveSystemContract(
556                    system_contract_type.to_string(),
557                )
558            })?
559        {
560            let named_keys = system_contract.named_keys().clone();
561            return Ok((system_contract.into(), Some(named_keys), CARRY_FORWARD));
562        }
563
564        if let Some(StoredValue::AddressableEntity(system_entity)) = self
565            .tracking_copy
566            .read(&Key::AddressableEntity(EntityAddr::new_system(hash_addr)))
567            .map_err(|_| {
568                ProtocolUpgradeError::UnableToRetrieveSystemContract(
569                    system_contract_type.to_string(),
570                )
571            })?
572        {
573            return Ok((system_entity, None, NO_CARRY_FORWARD));
574        }
575
576        Err(ProtocolUpgradeError::UnableToRetrieveSystemContract(
577            system_contract_type.to_string(),
578        ))
579    }
580
581    /// Refresh the system contracts with an updated set of entry points,
582    /// and bump the contract version at a major version upgrade.
583    fn refresh_system_contract_entry_points(
584        &mut self,
585        contract_hash: HashAddr,
586        system_entity_type: SystemEntityType,
587    ) -> Result<(), ProtocolUpgradeError> {
588        let contract_name = system_entity_type.entity_name();
589        let entry_points = system_entity_type.entry_points();
590
591        let mut contract = if let StoredValue::Contract(contract) = self
592            .tracking_copy
593            .read(&Key::Hash(contract_hash))
594            .map_err(|_| {
595                ProtocolUpgradeError::UnableToRetrieveSystemContract(contract_name.to_string())
596            })?
597            .ok_or_else(|| {
598                ProtocolUpgradeError::UnableToRetrieveSystemContract(contract_name.to_string())
599            })? {
600            contract
601        } else {
602            return Err(ProtocolUpgradeError::UnableToRetrieveSystemContract(
603                contract_name,
604            ));
605        };
606
607        let is_major_bump = self
608            .config
609            .current_protocol_version()
610            .check_next_version(&self.config.new_protocol_version())
611            .is_major_version();
612
613        let contract_entry_points: EntryPoints = contract.entry_points().clone().into();
614        let entry_points_unchanged = contract_entry_points == entry_points;
615        if entry_points_unchanged && !is_major_bump {
616            // We don't need to do anything if entry points are unchanged, or there's no major
617            // version bump.
618            return Ok(());
619        }
620
621        let contract_package_key = Key::Hash(contract.contract_package_hash().value());
622
623        let mut contract_package = if let StoredValue::ContractPackage(contract_package) = self
624            .tracking_copy
625            .read(&contract_package_key)
626            .map_err(|_| {
627                ProtocolUpgradeError::UnableToRetrieveSystemContractPackage(
628                    contract_name.to_string(),
629                )
630            })?
631            .ok_or_else(|| {
632                ProtocolUpgradeError::UnableToRetrieveSystemContractPackage(
633                    contract_name.to_string(),
634                )
635            })? {
636            contract_package
637        } else {
638            return Err(ProtocolUpgradeError::UnableToRetrieveSystemContractPackage(
639                contract_name,
640            ));
641        };
642
643        contract.set_protocol_version(self.config.new_protocol_version());
644
645        let new_contract = Contract::new(
646            contract.contract_package_hash(),
647            contract.contract_wasm_hash(),
648            contract.named_keys().clone(),
649            entry_points.into(),
650            self.config.new_protocol_version(),
651        );
652        self.tracking_copy.write(
653            Key::Hash(contract_hash),
654            StoredValue::Contract(new_contract),
655        );
656
657        contract_package.insert_contract_version(
658            self.config.new_protocol_version().value().major,
659            ContractHash::new(contract_hash),
660        );
661
662        self.tracking_copy.write(
663            contract_package_key,
664            StoredValue::ContractPackage(contract_package),
665        );
666
667        Ok(())
668    }
669
670    /// Migrate the system account to addressable entity if necessary.
671    pub fn migrate_system_account(
672        &mut self,
673        pre_state_hash: Digest,
674    ) -> Result<(), ProtocolUpgradeError> {
675        debug!("migrate system account");
676        let mut address_generator = AddressGenerator::new(pre_state_hash.as_ref(), Phase::System);
677
678        let account_hash = PublicKey::System.to_account_hash();
679
680        let main_purse = {
681            let purse_addr = address_generator.new_hash_address();
682            let balance_cl_value = CLValue::from_t(U512::zero())
683                .map_err(|error| ProtocolUpgradeError::CLValue(error.to_string()))?;
684
685            self.tracking_copy.write(
686                Key::Balance(purse_addr),
687                StoredValue::CLValue(balance_cl_value),
688            );
689
690            let purse_cl_value = CLValue::unit();
691            let purse_uref = URef::new(purse_addr, AccessRights::READ_ADD_WRITE);
692
693            self.tracking_copy
694                .write(Key::URef(purse_uref), StoredValue::CLValue(purse_cl_value));
695            purse_uref
696        };
697
698        let associated_keys = AssociatedKeys::new(account_hash, Weight::new(1));
699        let byte_code_hash = ByteCodeHash::default();
700        let entity_hash = AddressableEntityHash::new(PublicKey::System.to_account_hash().value());
701        let package_hash = PackageHash::new(address_generator.new_hash_address());
702
703        let byte_code = ByteCode::new(ByteCodeKind::Empty, vec![]);
704
705        let system_account_entity = AddressableEntity::new(
706            package_hash,
707            byte_code_hash,
708            self.config.new_protocol_version(),
709            main_purse,
710            associated_keys,
711            ActionThresholds::default(),
712            EntityKind::Account(account_hash),
713        );
714
715        let package = {
716            let mut package = Package::new(
717                EntityVersions::default(),
718                BTreeSet::default(),
719                Groups::default(),
720                PackageStatus::default(),
721            );
722            package.insert_entity_version(
723                self.config.new_protocol_version().value().major,
724                EntityAddr::Account(entity_hash.value()),
725            );
726            package
727        };
728
729        let byte_code_key = Key::ByteCode(ByteCodeAddr::Empty);
730        self.tracking_copy
731            .write(byte_code_key, StoredValue::ByteCode(byte_code));
732
733        let entity_key = system_account_entity.entity_key(entity_hash);
734
735        self.tracking_copy.write(
736            entity_key,
737            StoredValue::AddressableEntity(system_account_entity),
738        );
739
740        self.tracking_copy
741            .write(package_hash.into(), StoredValue::SmartContract(package));
742
743        let contract_by_account = CLValue::from_t(entity_key)
744            .map_err(|error| ProtocolUpgradeError::CLValue(error.to_string()))?;
745
746        self.tracking_copy.write(
747            Key::Account(account_hash),
748            StoredValue::CLValue(contract_by_account),
749        );
750
751        Ok(())
752    }
753
754    /// Creates an accumulation purse in the handle payment system contract if its not present.
755    ///
756    /// This can happen on older networks that did not have support for [`FeeHandling::Accumulate`]
757    /// at the genesis. In such cases we have to check the state of handle payment contract and
758    /// create an accumulation purse.
759    pub fn create_accumulation_purse_if_required(
760        &mut self,
761        handle_payment_hash: &HashAddr,
762        fee_handling: FeeHandling,
763    ) -> Result<(), ProtocolUpgradeError> {
764        debug!(?fee_handling, "create accumulation purse if required");
765        match fee_handling {
766            FeeHandling::PayToProposer | FeeHandling::Burn => return Ok(()),
767            FeeHandling::Accumulate | FeeHandling::NoFee => {}
768        }
769        let mut address_generator = {
770            let seed_bytes = (
771                self.config.current_protocol_version(),
772                self.config.new_protocol_version(),
773            )
774                .to_bytes()?;
775            let phase = Phase::System;
776            AddressGenerator::new(&seed_bytes, phase)
777        };
778        let system_contract = SystemEntityType::HandlePayment;
779
780        let (addressable_entity, maybe_named_keys, _) =
781            self.retrieve_system_entity(*handle_payment_hash, system_contract)?;
782
783        let entity_addr = EntityAddr::new_system(*handle_payment_hash);
784
785        if let Some(named_keys) = maybe_named_keys {
786            for (string, key) in named_keys.into_inner().into_iter() {
787                let entry_addr = NamedKeyAddr::new_from_string(entity_addr, string.clone())
788                    .map_err(|err| ProtocolUpgradeError::Bytesrepr(err.to_string()))?;
789
790                let named_key_value = NamedKeyValue::from_concrete_values(key, string)
791                    .map_err(|error| ProtocolUpgradeError::CLValue(error.to_string()))?;
792
793                let entry_key = Key::NamedKey(entry_addr);
794
795                self.tracking_copy
796                    .write(entry_key, StoredValue::NamedKey(named_key_value));
797            }
798        }
799
800        let named_key_addr =
801            NamedKeyAddr::new_from_string(entity_addr, ACCUMULATION_PURSE_KEY.to_string())
802                .map_err(|err| ProtocolUpgradeError::Bytesrepr(err.to_string()))?;
803
804        let requries_accumulation_purse = self
805            .tracking_copy
806            .read(&Key::NamedKey(named_key_addr))
807            .map_err(|_| ProtocolUpgradeError::UnexpectedStoredValueVariant)?
808            .is_none();
809
810        if requries_accumulation_purse {
811            let purse_uref = address_generator.new_uref(AccessRights::READ_ADD_WRITE);
812            let balance_clvalue = CLValue::from_t(U512::zero())?;
813            self.tracking_copy.write(
814                Key::Balance(purse_uref.addr()),
815                StoredValue::CLValue(balance_clvalue),
816            );
817
818            let purse_key = Key::URef(purse_uref);
819
820            self.tracking_copy
821                .write(purse_key, StoredValue::CLValue(CLValue::unit()));
822
823            let purse =
824                NamedKeyValue::from_concrete_values(purse_key, ACCUMULATION_PURSE_KEY.to_string())
825                    .map_err(|cl_error| ProtocolUpgradeError::CLValue(cl_error.to_string()))?;
826
827            self.tracking_copy
828                .write(Key::NamedKey(named_key_addr), StoredValue::NamedKey(purse));
829
830            let entity_key = Key::AddressableEntity(EntityAddr::System(*handle_payment_hash));
831
832            self.tracking_copy.write(
833                entity_key,
834                StoredValue::AddressableEntity(addressable_entity),
835            );
836        }
837
838        Ok(())
839    }
840
841    /// Creates an accumulation purse in the handle payment system contract if its not present.
842    ///
843    /// This can happen on older networks that did not have support for [`FeeHandling::Accumulate`]
844    /// at the genesis. In such cases we have to check the state of handle payment contract and
845    /// create an accumulation purse.
846    pub fn create_accumulation_purse_if_required_by_contract(
847        &mut self,
848        handle_payment_hash: &HashAddr,
849        fee_handling: FeeHandling,
850    ) -> Result<(), ProtocolUpgradeError> {
851        match fee_handling {
852            FeeHandling::PayToProposer | FeeHandling::Burn => return Ok(()),
853            FeeHandling::Accumulate | FeeHandling::NoFee => {}
854        }
855
856        let mut address_generator = {
857            let seed_bytes = (
858                self.config.current_protocol_version(),
859                self.config.new_protocol_version(),
860            )
861                .to_bytes()?;
862
863            let phase = Phase::System;
864
865            AddressGenerator::new(&seed_bytes, phase)
866        };
867
868        let system_contract = SystemEntityType::HandlePayment;
869        let contract_name = system_contract.entity_name();
870        let mut contract = if let StoredValue::Contract(contract) = self
871            .tracking_copy
872            .read(&Key::Hash(*handle_payment_hash))
873            .map_err(|_| {
874                ProtocolUpgradeError::UnableToRetrieveSystemContract(contract_name.to_string())
875            })?
876            .ok_or_else(|| {
877                ProtocolUpgradeError::UnableToRetrieveSystemContract(contract_name.to_string())
878            })? {
879            contract
880        } else {
881            return Err(ProtocolUpgradeError::UnableToRetrieveSystemContract(
882                contract_name,
883            ));
884        };
885
886        if !contract.named_keys().contains(ACCUMULATION_PURSE_KEY) {
887            let purse_uref = address_generator.new_uref(AccessRights::READ_ADD_WRITE);
888            let balance_clvalue = CLValue::from_t(U512::zero())?;
889            self.tracking_copy.write(
890                Key::Balance(purse_uref.addr()),
891                StoredValue::CLValue(balance_clvalue),
892            );
893            self.tracking_copy
894                .write(Key::URef(purse_uref), StoredValue::CLValue(CLValue::unit()));
895
896            let mut new_named_keys = NamedKeys::new();
897            new_named_keys.insert(ACCUMULATION_PURSE_KEY.into(), Key::from(purse_uref));
898            contract.named_keys_append(new_named_keys);
899
900            self.tracking_copy.write(
901                Key::Hash(*handle_payment_hash),
902                StoredValue::Contract(contract),
903            );
904        }
905
906        Ok(())
907    }
908
909    fn get_named_keys(
910        &mut self,
911        contract_hash: HashAddr,
912    ) -> Result<NamedKeys, ProtocolUpgradeError> {
913        if self.config.enable_addressable_entity() {
914            let named_keys = self
915                .tracking_copy
916                .get_named_keys(EntityAddr::System(contract_hash))?;
917            Ok(named_keys)
918        } else {
919            let named_keys = self
920                .tracking_copy
921                .read(&Key::Hash(contract_hash))?
922                .ok_or_else(|| {
923                    ProtocolUpgradeError::UnableToRetrieveSystemContract(format!(
924                        "{:?}",
925                        contract_hash
926                    ))
927                })?
928                .as_contract()
929                .map(|contract| contract.named_keys().clone())
930                .ok_or(ProtocolUpgradeError::UnexpectedStoredValueVariant)?;
931
932            Ok(named_keys)
933        }
934    }
935
936    /// Check payment purse balance.
937    pub fn handle_payment_purse_check(
938        &mut self,
939        handle_payment: HashAddr,
940        mint: HashAddr,
941    ) -> Result<(), ProtocolUpgradeError> {
942        let payment_named_keys = self.get_named_keys(handle_payment)?;
943        let payment_purse_key = payment_named_keys
944            .get(PAYMENT_PURSE_KEY)
945            .expect("payment purse key must exist in handle payment contract's named keys");
946        let balance = self
947            .tracking_copy
948            .get_total_balance(*payment_purse_key)
949            .expect("must be able to get payment purse balance");
950        if balance <= Motes::zero() {
951            return Ok(());
952        }
953        warn!("payment purse had remaining balance at upgrade {}", balance);
954        let balance_key = {
955            let uref_addr = payment_purse_key
956                .as_uref()
957                .expect("payment purse key must be uref.")
958                .addr();
959            Key::Balance(uref_addr)
960        };
961
962        let mint_named_keys = self.get_named_keys(mint)?;
963        let total_supply_key = mint_named_keys
964            .get(TOTAL_SUPPLY_KEY)
965            .expect("total supply key must exist in mint contract's named keys");
966
967        let stored_value = self
968            .tracking_copy
969            .read(total_supply_key)
970            .expect("must be able to read total supply")
971            .expect("total supply must have a value");
972
973        // by convention, we only store CLValues under Key::URef
974        if let StoredValue::CLValue(value) = stored_value {
975            // Only CLTyped instances should be stored as a CLValue.
976            let total_supply: U512 =
977                CLValue::into_t(value).expect("total supply must have expected type.");
978
979            let new_total_supply = total_supply.saturating_sub(balance.value());
980            info!(
981                "adjusting total supply from {} to {}",
982                total_supply, new_total_supply
983            );
984            let cl_value = CLValue::from_t(new_total_supply)
985                .expect("new total supply must convert to CLValue.");
986            self.tracking_copy
987                .write(*total_supply_key, StoredValue::CLValue(cl_value));
988            info!(
989                "adjusting payment purse balance from {} to {}",
990                balance.value(),
991                U512::zero()
992            );
993            let cl_value = CLValue::from_t(U512::zero()).expect("zero must convert to CLValue.");
994            self.tracking_copy
995                .write(balance_key, StoredValue::CLValue(cl_value));
996            Ok(())
997        } else {
998            Err(ProtocolUpgradeError::CLValue(
999                "failure to retrieve total supply".to_string(),
1000            ))
1001        }
1002    }
1003
1004    /// Upsert gas hold interval to mint named keys.
1005    pub fn handle_new_gas_hold_config(
1006        &mut self,
1007        mint: HashAddr,
1008    ) -> Result<(), ProtocolUpgradeError> {
1009        if self.config.new_gas_hold_handling().is_none()
1010            && self.config.new_gas_hold_interval().is_none()
1011        {
1012            return Ok(());
1013        }
1014
1015        let mint_addr = EntityAddr::System(mint);
1016        let named_keys = self.get_named_keys(mint)?;
1017
1018        if let Some(new_gas_hold_handling) = self.config.new_gas_hold_handling() {
1019            debug!(%new_gas_hold_handling, "handle new gas hold handling");
1020            let stored_value =
1021                StoredValue::CLValue(CLValue::from_t(new_gas_hold_handling.tag()).map_err(
1022                    |_| ProtocolUpgradeError::Bytesrepr("new_gas_hold_handling".to_string()),
1023                )?);
1024
1025            self.system_uref(
1026                mint_addr,
1027                MINT_GAS_HOLD_HANDLING_KEY,
1028                &named_keys,
1029                stored_value,
1030            )?;
1031        }
1032
1033        if let Some(new_gas_hold_interval) = self.config.new_gas_hold_interval() {
1034            debug!(%new_gas_hold_interval, "handle new gas hold interval");
1035            let stored_value =
1036                StoredValue::CLValue(CLValue::from_t(new_gas_hold_interval).map_err(|_| {
1037                    ProtocolUpgradeError::Bytesrepr("new_gas_hold_interval".to_string())
1038                })?);
1039
1040            self.system_uref(
1041                mint_addr,
1042                MINT_GAS_HOLD_INTERVAL_KEY,
1043                &named_keys,
1044                stored_value,
1045            )?;
1046        }
1047        Ok(())
1048    }
1049
1050    fn system_uref(
1051        &mut self,
1052        entity_addr: EntityAddr,
1053        name: &str,
1054        named_keys: &NamedKeys,
1055        stored_value: StoredValue,
1056    ) -> Result<(), ProtocolUpgradeError> {
1057        let uref = {
1058            match named_keys.get(name) {
1059                Some(key) => match key.as_uref() {
1060                    Some(uref) => *uref,
1061                    None => {
1062                        return Err(ProtocolUpgradeError::UnexpectedKeyVariant);
1063                    }
1064                },
1065                None => self
1066                    .address_generator
1067                    .borrow_mut()
1068                    .new_uref(AccessRights::READ_ADD_WRITE),
1069            }
1070        };
1071        self.tracking_copy
1072            .upsert_uref_to_named_keys(entity_addr, name, named_keys, uref, stored_value)
1073            .map_err(ProtocolUpgradeError::TrackingCopy)
1074    }
1075
1076    /// Handle new validator slots.
1077    pub fn handle_new_validator_slots(
1078        &mut self,
1079        auction: HashAddr,
1080    ) -> Result<(), ProtocolUpgradeError> {
1081        if let Some(new_validator_slots) = self.config.new_validator_slots() {
1082            debug!(%new_validator_slots, "handle new validator slots");
1083            // if new total validator slots is provided, update auction contract state
1084            let auction_named_keys = self.get_named_keys(auction)?;
1085
1086            let validator_slots_key = auction_named_keys
1087                .get(VALIDATOR_SLOTS_KEY)
1088                .expect("validator_slots key must exist in auction contract's named keys");
1089            let value =
1090                StoredValue::CLValue(CLValue::from_t(new_validator_slots).map_err(|_| {
1091                    ProtocolUpgradeError::Bytesrepr("new_validator_slots".to_string())
1092                })?);
1093            self.tracking_copy.write(*validator_slots_key, value);
1094        }
1095        Ok(())
1096    }
1097
1098    /// Applies the necessary changes if a new auction delay is part of the upgrade.
1099    pub fn handle_new_auction_delay(
1100        &mut self,
1101        auction: HashAddr,
1102    ) -> Result<(), ProtocolUpgradeError> {
1103        if let Some(new_auction_delay) = self.config.new_auction_delay() {
1104            debug!(%new_auction_delay, "handle new auction delay");
1105            let auction_named_keys = self.get_named_keys(auction)?;
1106
1107            let auction_delay_key = auction_named_keys
1108                .get(AUCTION_DELAY_KEY)
1109                .expect("auction_delay key must exist in auction contract's named keys");
1110            let value =
1111                StoredValue::CLValue(CLValue::from_t(new_auction_delay).map_err(|_| {
1112                    ProtocolUpgradeError::Bytesrepr("new_auction_delay".to_string())
1113                })?);
1114            self.tracking_copy.write(*auction_delay_key, value);
1115        }
1116        Ok(())
1117    }
1118
1119    /// Applies the necessary changes if a new locked funds period is part of the upgrade.
1120    pub fn handle_new_locked_funds_period_millis(
1121        &mut self,
1122        auction: HashAddr,
1123    ) -> Result<(), ProtocolUpgradeError> {
1124        if let Some(new_locked_funds_period) = self.config.new_locked_funds_period_millis() {
1125            debug!(%new_locked_funds_period,"handle new locked funds period millis");
1126
1127            let auction_named_keys = self.get_named_keys(auction)?;
1128
1129            let locked_funds_period_key = auction_named_keys
1130                .get(LOCKED_FUNDS_PERIOD_KEY)
1131                .expect("locked_funds_period key must exist in auction contract's named keys");
1132            let value =
1133                StoredValue::CLValue(CLValue::from_t(new_locked_funds_period).map_err(|_| {
1134                    ProtocolUpgradeError::Bytesrepr("new_locked_funds_period".to_string())
1135                })?);
1136            self.tracking_copy.write(*locked_funds_period_key, value);
1137        }
1138        Ok(())
1139    }
1140
1141    /// Applies the necessary changes if a new unbonding delay is part of the upgrade.
1142    pub fn handle_new_unbonding_delay(
1143        &mut self,
1144        auction: HashAddr,
1145    ) -> Result<(), ProtocolUpgradeError> {
1146        // We insert the new unbonding delay once the purses to be paid out have been transformed
1147        // based on the previous unbonding delay.
1148        if let Some(new_unbonding_delay) = self.config.new_unbonding_delay() {
1149            debug!(%new_unbonding_delay,"handle new unbonding delay");
1150
1151            let auction_named_keys = self.get_named_keys(auction)?;
1152
1153            let unbonding_delay_key = auction_named_keys
1154                .get(UNBONDING_DELAY_KEY)
1155                .expect("unbonding_delay key must exist in auction contract's named keys");
1156            let value =
1157                StoredValue::CLValue(CLValue::from_t(new_unbonding_delay).map_err(|_| {
1158                    ProtocolUpgradeError::Bytesrepr("new_unbonding_delay".to_string())
1159                })?);
1160            self.tracking_copy.write(*unbonding_delay_key, value);
1161        }
1162        Ok(())
1163    }
1164
1165    /// Applies the necessary changes if a new round seigniorage rate is part of the upgrade.
1166    pub fn handle_new_round_seigniorage_rate(
1167        &mut self,
1168        mint: HashAddr,
1169    ) -> Result<(), ProtocolUpgradeError> {
1170        if let Some(new_round_seigniorage_rate) = self.config.new_round_seigniorage_rate() {
1171            debug!(%new_round_seigniorage_rate,"handle new round seigniorage rate");
1172            let new_round_seigniorage_rate: Ratio<U512> = {
1173                let (numer, denom) = new_round_seigniorage_rate.into();
1174                Ratio::new(numer.into(), denom.into())
1175            };
1176
1177            let mint_named_keys = self.get_named_keys(mint)?;
1178
1179            let locked_funds_period_key = mint_named_keys
1180                .get(ROUND_SEIGNIORAGE_RATE_KEY)
1181                .expect("round_seigniorage_rate key must exist in mint contract's named keys");
1182            let value = StoredValue::CLValue(CLValue::from_t(new_round_seigniorage_rate).map_err(
1183                |_| ProtocolUpgradeError::Bytesrepr("new_round_seigniorage_rate".to_string()),
1184            )?);
1185            self.tracking_copy.write(*locked_funds_period_key, value);
1186        }
1187        Ok(())
1188    }
1189
1190    /// Handle unbonds migration.
1191    pub fn handle_unbonds_migration(&mut self) -> Result<(), ProtocolUpgradeError> {
1192        debug!("handle unbonds migration");
1193        let tc = &mut self.tracking_copy;
1194        let existing_keys = match tc.get_keys(&KeyTag::Unbond) {
1195            Ok(keys) => keys,
1196            Err(err) => return Err(ProtocolUpgradeError::TrackingCopy(err)),
1197        };
1198        for key in existing_keys {
1199            if let Some(StoredValue::Unbonding(unbonding_purses)) =
1200                tc.get(&key).map_err(Into::<ProtocolUpgradeError>::into)?
1201            {
1202                // prune away the original record, we don't need it anymore
1203                tc.prune(key);
1204
1205                // re-write records under Key::BidAddr , StoredValue::BidKind
1206                for unbonding_purse in unbonding_purses {
1207                    let validator = unbonding_purse.validator_public_key();
1208                    let unbonder = unbonding_purse.unbonder_public_key();
1209                    let new_key = Key::BidAddr(BidAddr::UnbondAccount {
1210                        validator: validator.to_account_hash(),
1211                        unbonder: unbonder.to_account_hash(),
1212                    });
1213                    let unbond = Box::new(Unbond::from(unbonding_purse));
1214                    let unbond_bid_kind = BidKind::Unbond(unbond.clone());
1215                    if !unbond.eras().is_empty() {
1216                        tc.write(new_key, StoredValue::BidKind(unbond_bid_kind));
1217                    }
1218                }
1219            }
1220        }
1221
1222        Ok(())
1223    }
1224
1225    /// Handle bids migration.
1226    pub fn handle_bids_migration(
1227        &mut self,
1228        validator_minimum: u64,
1229        delegation_minimum: u64,
1230        delegation_maximum: u64,
1231    ) -> Result<(), ProtocolUpgradeError> {
1232        if delegation_maximum < delegation_minimum {
1233            return Err(ProtocolUpgradeError::InvalidUpgradeConfig);
1234        }
1235        debug!("handle bids migration");
1236        let tc = &mut self.tracking_copy;
1237        let existing_bid_keys = match tc.get_keys(&KeyTag::Bid) {
1238            Ok(keys) => keys,
1239            Err(err) => return Err(ProtocolUpgradeError::TrackingCopy(err)),
1240        };
1241        for key in existing_bid_keys {
1242            if let Some(StoredValue::Bid(existing_bid)) =
1243                tc.get(&key).map_err(Into::<ProtocolUpgradeError>::into)?
1244            {
1245                // prune away the original record, we don't need it anymore
1246                tc.prune(key);
1247
1248                if existing_bid.staked_amount().is_zero() {
1249                    // the previous logic enforces unbonding all delegators of
1250                    // a validator that reduced their personal stake to 0 (and we have
1251                    // various existent tests that prove this), thus there is no need
1252                    // to handle the complicated hypothetical case of one or more
1253                    // delegator stakes being > 0 if the validator stake is 0.
1254                    //
1255                    // tl;dr this is a "zombie" bid and we don't need to continue
1256                    // carrying it forward at tip.
1257                    continue;
1258                }
1259
1260                let validator_public_key = existing_bid.validator_public_key();
1261                let validator_bid_addr = BidAddr::from(validator_public_key.clone());
1262                let validator_bid = {
1263                    let validator_bid = ValidatorBid::from(*existing_bid.clone());
1264                    let inactive = validator_bid.staked_amount() < U512::from(validator_minimum);
1265                    validator_bid
1266                        .with_inactive(inactive)
1267                        .with_min_max_delegation_amount(delegation_maximum, delegation_minimum)
1268                };
1269                tc.write(
1270                    validator_bid_addr.into(),
1271                    StoredValue::BidKind(BidKind::Validator(Box::new(validator_bid))),
1272                );
1273
1274                let delegators = existing_bid.delegators().clone();
1275                for (_, delegator) in delegators {
1276                    let delegator_bid_addr = BidAddr::new_delegator_kind(
1277                        validator_public_key,
1278                        &DelegatorKind::PublicKey(delegator.delegator_public_key().clone()),
1279                    );
1280                    // the previous code was removing a delegator bid from the embedded
1281                    // collection within their validator's bid when the delegator fully
1282                    // unstaked, so technically we don't need to check for 0 balance here.
1283                    // However, since it is low effort to check, doing it just to be sure.
1284                    if !delegator.staked_amount().is_zero() {
1285                        tc.write(
1286                            delegator_bid_addr.into(),
1287                            StoredValue::BidKind(BidKind::Delegator(Box::new(DelegatorBid::from(
1288                                delegator,
1289                            )))),
1290                        );
1291                    }
1292                }
1293            }
1294        }
1295
1296        let validator_bid_keys = tc
1297            .get_by_byte_prefix(&[KeyTag::BidAddr as u8, BidAddrTag::Validator as u8])
1298            .map_err(|_| ProtocolUpgradeError::UnexpectedKeyVariant)?;
1299        for validator_bid_key in validator_bid_keys {
1300            if let Some(StoredValue::BidKind(BidKind::Validator(validator_bid))) = tc
1301                .get(&validator_bid_key)
1302                .map_err(Into::<ProtocolUpgradeError>::into)?
1303            {
1304                let is_bid_inactive = validator_bid.inactive();
1305                let has_less_than_validator_minimum =
1306                    validator_bid.staked_amount() < U512::from(validator_minimum);
1307                if !is_bid_inactive && has_less_than_validator_minimum {
1308                    let inactive_bid = validator_bid.with_inactive(true);
1309                    info!("marking bid inactive {validator_bid_key}");
1310                    tc.write(
1311                        validator_bid_key,
1312                        StoredValue::BidKind(BidKind::Validator(Box::new(inactive_bid))),
1313                    );
1314                }
1315            }
1316        }
1317
1318        Ok(())
1319    }
1320
1321    /// Handle era info migration.
1322    pub fn handle_era_info_migration(&mut self) -> Result<(), ProtocolUpgradeError> {
1323        // EraInfo migration
1324        if let Some(activation_point) = self.config.activation_point() {
1325            // The highest stored era is the immediate predecessor of the activation point.
1326            let highest_era_info_id = activation_point.saturating_sub(1);
1327            let highest_era_info_key = Key::EraInfo(highest_era_info_id);
1328
1329            let get_result = self
1330                .tracking_copy
1331                .get(&highest_era_info_key)
1332                .map_err(ProtocolUpgradeError::TrackingCopy)?;
1333
1334            match get_result {
1335                Some(stored_value @ StoredValue::EraInfo(_)) => {
1336                    self.tracking_copy.write(Key::EraSummary, stored_value);
1337                }
1338                Some(other_stored_value) => {
1339                    // This should not happen as we only write EraInfo variants.
1340                    error!(stored_value_type_name=%other_stored_value.type_name(),
1341                        "EraInfo key contains unexpected StoredValue variant");
1342                    return Err(ProtocolUpgradeError::UnexpectedStoredValueVariant);
1343                }
1344                None => {
1345                    // Can't find key
1346                    // Most likely this chain did not yet run an auction, or recently completed a
1347                    // prune
1348                }
1349            };
1350        }
1351        Ok(())
1352    }
1353
1354    /// Handle seignorage snapshot migration to new version.
1355    pub fn handle_seignorage_snapshot_migration(
1356        &mut self,
1357        auction: HashAddr,
1358    ) -> Result<(), ProtocolUpgradeError> {
1359        let auction_named_keys = self.get_named_keys(auction)?;
1360        let maybe_snapshot_version_key =
1361            auction_named_keys.get(SEIGNIORAGE_RECIPIENTS_SNAPSHOT_VERSION_KEY);
1362        let snapshot_key = auction_named_keys
1363            .get(SEIGNIORAGE_RECIPIENTS_SNAPSHOT_KEY)
1364            .expect("snapshot key should already exist");
1365
1366        // if version flag does not exist yet, set it and migrate snapshot
1367        if maybe_snapshot_version_key.is_none() {
1368            let auction_addr = EntityAddr::new_system(auction);
1369
1370            // add new snapshot version named key
1371            let stored_value = StoredValue::CLValue(CLValue::from_t(
1372                DEFAULT_SEIGNIORAGE_RECIPIENTS_SNAPSHOT_VERSION,
1373            )?);
1374            self.system_uref(
1375                auction_addr,
1376                SEIGNIORAGE_RECIPIENTS_SNAPSHOT_VERSION_KEY,
1377                &auction_named_keys,
1378                stored_value,
1379            )?;
1380
1381            // read legacy snapshot
1382            if let Some(snapshot_stored_value) = self.tracking_copy.read(snapshot_key)? {
1383                let snapshot_cl_value = match snapshot_stored_value.into_cl_value() {
1384                    Some(cl_value) => cl_value,
1385                    None => {
1386                        error!("seigniorage recipients snapshot is not a CLValue");
1387                        return Err(ProtocolUpgradeError::CLValue(
1388                            "seigniorage recipients snapshot is not a CLValue".to_string(),
1389                        ));
1390                    }
1391                };
1392
1393                let legacy_snapshot: SeigniorageRecipientsSnapshotV1 =
1394                    snapshot_cl_value.into_t()?;
1395
1396                let mut new_snapshot = SeigniorageRecipientsSnapshotV2::default();
1397                for (era_id, recipients) in legacy_snapshot.into_iter() {
1398                    let mut new_recipients = SeigniorageRecipientsV2::default();
1399                    for (pubkey, recipient) in recipients {
1400                        new_recipients.insert(pubkey, recipient.into());
1401                    }
1402                    new_snapshot.insert(era_id, new_recipients);
1403                }
1404
1405                // store new snapshot
1406                self.tracking_copy.write(
1407                    *snapshot_key,
1408                    StoredValue::CLValue(CLValue::from_t(new_snapshot)?),
1409                );
1410            };
1411        }
1412
1413        Ok(())
1414    }
1415
1416    /// Handle global state updates.
1417    pub fn handle_global_state_updates(&mut self) {
1418        debug!("handle global state updates");
1419        for (key, value) in self.config.global_state_update() {
1420            self.tracking_copy.write(*key, value.clone());
1421        }
1422    }
1423}