From b4f0e848168474248a9d060532b777c672c3adcd Mon Sep 17 00:00:00 2001 From: Henrique Nogara Date: Wed, 14 Aug 2024 11:30:15 -0300 Subject: [PATCH] Mesh 2188/migrations (#1699) * Add asset migrations * Add checkpoint migrations * Add compliance manager migrations * Add corporate-actions migrations * Addd external-agents migrations * Add nft migrations * Add portfolio migrations * Add pallet-settlement migrations * Add statistics migrations * Add sto migrations * Add identity migrations * Add AssetIdentifiers missing migration * Add missing on_runtime_upgrade * Count migrated items * Add missing cargo.lock --- Cargo.lock | 9 +- pallets/asset/src/checkpoint/migrations.rs | 174 +++++++++ pallets/asset/src/checkpoint/mod.rs | 17 +- pallets/asset/src/lib.rs | 25 +- pallets/asset/src/migrations.rs | 366 ++++++++++++++++++ pallets/compliance-manager/Cargo.toml | 1 + pallets/compliance-manager/src/lib.rs | 18 +- pallets/compliance-manager/src/migrations.rs | 192 +++++++++ pallets/corporate-actions/Cargo.toml | 1 + .../src/ballot/migrations.rs | 90 +++++ pallets/corporate-actions/src/ballot/mod.rs | 17 +- .../src/distribution/migrations.rs | 71 ++++ .../corporate-actions/src/distribution/mod.rs | 16 +- pallets/corporate-actions/src/lib.rs | 21 +- pallets/corporate-actions/src/migrations.rs | 136 +++++++ pallets/external-agents/Cargo.toml | 2 + pallets/external-agents/src/lib.rs | 18 +- pallets/external-agents/src/migrations.rs | 98 +++++ pallets/identity/src/lib.rs | 5 +- pallets/identity/src/ticker_migrations.rs | 276 +++++++++++++ pallets/nft/src/lib.rs | 9 +- pallets/nft/src/migrations.rs | 110 ++++++ pallets/portfolio/Cargo.toml | 2 + pallets/portfolio/src/lib.rs | 19 +- pallets/portfolio/src/migrations.rs | 99 +++++ pallets/settlement/src/lib.rs | 17 +- pallets/settlement/src/migrations.rs | 133 +++++++ pallets/statistics/src/lib.rs | 30 +- pallets/statistics/src/migrations.rs | 143 +++++++ pallets/sto/Cargo.toml | 1 + pallets/sto/src/lib.rs | 18 +- pallets/sto/src/migrations.rs | 69 ++++ primitives/src/asset.rs | 10 +- 33 files changed, 2153 insertions(+), 60 deletions(-) create mode 100644 pallets/asset/src/checkpoint/migrations.rs create mode 100644 pallets/asset/src/migrations.rs create mode 100644 pallets/compliance-manager/src/migrations.rs create mode 100644 pallets/corporate-actions/src/ballot/migrations.rs create mode 100644 pallets/corporate-actions/src/distribution/migrations.rs create mode 100644 pallets/corporate-actions/src/migrations.rs create mode 100644 pallets/external-agents/src/migrations.rs create mode 100644 pallets/identity/src/ticker_migrations.rs create mode 100644 pallets/nft/src/migrations.rs create mode 100644 pallets/portfolio/src/migrations.rs create mode 100644 pallets/settlement/src/migrations.rs create mode 100644 pallets/statistics/src/migrations.rs create mode 100644 pallets/sto/src/migrations.rs diff --git a/Cargo.lock b/Cargo.lock index 64ded24001..3fd88bb9b6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5187,6 +5187,7 @@ dependencies = [ "frame-benchmarking", "frame-support", "frame-system", + "log", "pallet-asset", "pallet-balances 0.1.0", "pallet-base", @@ -5267,6 +5268,7 @@ dependencies = [ "frame-benchmarking", "frame-support", "frame-system", + "log", "pallet-asset", "pallet-balances 0.1.0", "pallet-base", @@ -5298,6 +5300,7 @@ dependencies = [ "frame-benchmarking", "frame-support", "frame-system", + "log", "pallet-base", "pallet-identity", "pallet-permissions", @@ -5308,6 +5311,7 @@ dependencies = [ "scale-info", "serde", "serde_derive", + "sp-runtime", "sp-std 5.0.0", ] @@ -5587,6 +5591,7 @@ dependencies = [ "frame-benchmarking", "frame-support", "frame-system", + "log", "pallet-balances 0.1.0", "pallet-base", "pallet-identity", @@ -5597,6 +5602,7 @@ dependencies = [ "scale-info", "serde", "sp-arithmetic", + "sp-runtime", "sp-std 5.0.0", ] @@ -5876,6 +5882,7 @@ dependencies = [ "frame-benchmarking", "frame-support", "frame-system", + "log", "pallet-asset", "pallet-balances 0.1.0", "pallet-base", @@ -12618,4 +12625,4 @@ checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656" dependencies = [ "cc", "pkg-config", -] +] \ No newline at end of file diff --git a/pallets/asset/src/checkpoint/migrations.rs b/pallets/asset/src/checkpoint/migrations.rs new file mode 100644 index 0000000000..ee597b3a23 --- /dev/null +++ b/pallets/asset/src/checkpoint/migrations.rs @@ -0,0 +1,174 @@ +use sp_runtime::runtime_logger::RuntimeLogger; +use sp_std::collections::btree_map::BTreeMap; + +use super::*; + +mod v1 { + use super::*; + use polymesh_primitives::Ticker; + + decl_storage! { + trait Store for Module as Checkpoint { + // This storage changed the Ticker key to AssetID. + pub TotalSupply get(fn total_supply_at): + double_map hasher(blake2_128_concat) Ticker, hasher(twox_64_concat) CheckpointId => polymesh_primitives::Balance; + + // This storage changed the Ticker key to AssetID. + pub Balance get(fn balance_at_checkpoint): + double_map hasher(blake2_128_concat) (Ticker, CheckpointId), hasher(twox_64_concat) IdentityId => polymesh_primitives::Balance; + + // This storage changed the Ticker key to AssetID. + pub CheckpointIdSequence get(fn checkpoint_id_sequence): + map hasher(blake2_128_concat) Ticker => CheckpointId; + + // This storage changed the Ticker key to AssetID. + pub BalanceUpdates get(fn balance_updates): + double_map hasher(blake2_128_concat) Ticker, hasher(twox_64_concat) IdentityId => Vec; + + // This storage changed the Ticker key to AssetID. + pub Timestamps get(fn timestamps): + double_map hasher(blake2_128_concat) Ticker, hasher(twox_64_concat) CheckpointId => Moment; + + // This storage changed the Ticker key to AssetID. + pub ScheduleIdSequence get(fn schedule_id_sequence): + map hasher(blake2_128_concat) Ticker => ScheduleId; + + // This storage changed the Ticker key to AssetID. + pub CachedNextCheckpoints get(fn cached_next_checkpoints): + map hasher(blake2_128_concat) Ticker => Option; + + // This storage changed the Ticker key to AssetID. + pub ScheduledCheckpoints get(fn scheduled_checkpoints): + double_map hasher(blake2_128_concat) Ticker, hasher(twox_64_concat) ScheduleId => Option; + + // This storage changed the Ticker key to AssetID. + pub ScheduleRefCount get(fn schedule_ref_count): + double_map hasher(blake2_128_concat) Ticker, hasher(twox_64_concat) ScheduleId => u32; + + // This storage changed the Ticker key to AssetID. + pub SchedulePoints get(fn schedule_points): + double_map hasher(blake2_128_concat) Ticker, hasher(twox_64_concat) ScheduleId => Vec; + } + } + + decl_module! { + pub struct Module for enum Call where origin: T::RuntimeOrigin { } + } +} + +pub(crate) fn migrate_to_v2() { + RuntimeLogger::init(); + let mut ticker_to_asset_id = BTreeMap::new(); + + // Removes all elements in the old storage and inserts it in the new storage + + let mut count = 0; + log::info!("Updating types for the TotalSupply storage"); + v1::TotalSupply::drain().for_each(|(ticker, checkpoint_id, balance)| { + count += 1; + let asset_id = ticker_to_asset_id + .entry(ticker) + .or_insert(AssetID::from(ticker)); + TotalSupply::insert(asset_id, checkpoint_id, balance); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Updating types for the Balance storage"); + v1::Balance::drain().for_each(|((ticker, checkpoint_id), did, balance)| { + count += 1; + let asset_id = ticker_to_asset_id + .entry(ticker) + .or_insert(AssetID::from(ticker)); + Balance::insert((asset_id, checkpoint_id), did, balance); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Updating types for the CheckpointIdSequence storage"); + v1::CheckpointIdSequence::drain().for_each(|(ticker, checkpoint_id)| { + count += 1; + let asset_id = ticker_to_asset_id + .entry(ticker) + .or_insert(AssetID::from(ticker)); + CheckpointIdSequence::insert(asset_id, checkpoint_id); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Updating types for the BalanceUpdates storage"); + v1::BalanceUpdates::drain().for_each(|(ticker, did, checkpoint_id)| { + count += 1; + let asset_id = ticker_to_asset_id + .entry(ticker) + .or_insert(AssetID::from(ticker)); + BalanceUpdates::insert(asset_id, did, checkpoint_id); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Updating types for the Timestamps storage"); + v1::Timestamps::drain().for_each(|(ticker, checkpoint_id, when)| { + count += 1; + let asset_id = ticker_to_asset_id + .entry(ticker) + .or_insert(AssetID::from(ticker)); + Timestamps::insert(asset_id, checkpoint_id, when); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Updating types for the ScheduleIdSequence storage"); + v1::ScheduleIdSequence::drain().for_each(|(ticker, schedule_id)| { + count += 1; + let asset_id = ticker_to_asset_id + .entry(ticker) + .or_insert(AssetID::from(ticker)); + ScheduleIdSequence::insert(asset_id, schedule_id); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Updating types for the CachedNextCheckpoints storage"); + v1::CachedNextCheckpoints::drain().for_each(|(ticker, next_checkpoint)| { + count += 1; + let asset_id = ticker_to_asset_id + .entry(ticker) + .or_insert(AssetID::from(ticker)); + CachedNextCheckpoints::insert(asset_id, next_checkpoint); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Updating types for the ScheduledCheckpoints storage"); + v1::ScheduledCheckpoints::drain().for_each(|(ticker, schedule_id, next_checkpoint)| { + count += 1; + let asset_id = ticker_to_asset_id + .entry(ticker) + .or_insert(AssetID::from(ticker)); + ScheduledCheckpoints::insert(asset_id, schedule_id, next_checkpoint); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Updating types for the ScheduleRefCount storage"); + v1::ScheduleRefCount::drain().for_each(|(ticker, schedule_id, ref_count)| { + count += 1; + let asset_id = ticker_to_asset_id + .entry(ticker) + .or_insert(AssetID::from(ticker)); + ScheduleRefCount::insert(asset_id, schedule_id, ref_count); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Updating types for the SchedulePoints storage"); + v1::SchedulePoints::drain().for_each(|(ticker, schedule_id, checkpoint_id)| { + count += 1; + let asset_id = ticker_to_asset_id + .entry(ticker) + .or_insert(AssetID::from(ticker)); + SchedulePoints::insert(asset_id, schedule_id, checkpoint_id); + }); + log::info!("{:?} items migrated", count); +} diff --git a/pallets/asset/src/checkpoint/mod.rs b/pallets/asset/src/checkpoint/mod.rs index 73dd732a8f..846b4315be 100644 --- a/pallets/asset/src/checkpoint/mod.rs +++ b/pallets/asset/src/checkpoint/mod.rs @@ -41,6 +41,7 @@ #[cfg(feature = "runtime-benchmarks")] pub mod benchmarking; +mod migrations; use codec::{Decode, Encode}; use frame_support::{ @@ -48,6 +49,7 @@ use frame_support::{ dispatch::{DispatchError, DispatchResult}, ensure, traits::UnixTime, + weights::Weight, }; use frame_system::ensure_root; use sp_runtime::traits::SaturatedConversion; @@ -64,14 +66,16 @@ use polymesh_common_utilities::{ GC_DID, }; use polymesh_primitives::asset::AssetID; -use polymesh_primitives::{asset::CheckpointId, storage_migration_ver, IdentityId, Moment}; +use polymesh_primitives::{ + asset::CheckpointId, storage_migrate_on, storage_migration_ver, IdentityId, Moment, +}; use crate::Config; type Asset = crate::Module; type ExternalAgents = pallet_external_agents::Module; -storage_migration_ver!(1); +storage_migration_ver!(2); decl_storage! { trait Store for Module as Checkpoint { @@ -155,7 +159,7 @@ decl_storage! { double_map hasher(blake2_128_concat) AssetID, hasher(twox_64_concat) ScheduleId => Vec; /// Storage version. - StorageVersion get(fn storage_version) build(|_| Version::new(1)): Version; + StorageVersion get(fn storage_version) build(|_| Version::new(2)): Version; } } @@ -165,6 +169,13 @@ decl_module! { fn deposit_event() = default; + fn on_runtime_upgrade() -> Weight { + storage_migrate_on!(StorageVersion, 2, { + migrations::migrate_to_v2::(); + }); + Weight::zero() + } + /// Creates a single checkpoint at the current time. /// /// # Arguments diff --git a/pallets/asset/src/lib.rs b/pallets/asset/src/lib.rs index da0ee027ae..5887a9512b 100644 --- a/pallets/asset/src/lib.rs +++ b/pallets/asset/src/lib.rs @@ -79,6 +79,7 @@ pub mod benchmarking; pub mod checkpoint; mod error; +mod migrations; mod types; use codec::{Decode, Encode}; @@ -86,6 +87,7 @@ use core::mem; use currency::*; use frame_support::dispatch::{DispatchError, DispatchResult}; use frame_support::traits::Get; +use frame_support::weights::Weight; use frame_support::BoundedBTreeSet; use frame_support::{decl_module, decl_storage, ensure}; use frame_system::ensure_root; @@ -115,9 +117,9 @@ use polymesh_primitives::asset_metadata::{ }; use polymesh_primitives::settlement::InstructionId; use polymesh_primitives::{ - extract_auth, storage_migration_ver, AssetIdentifier, Balance, Document, DocumentId, - IdentityId, Memo, PortfolioId, PortfolioKind, PortfolioUpdateReason, SecondaryKey, Ticker, - WeightMeter, + extract_auth, storage_migrate_on, storage_migration_ver, AssetIdentifier, Balance, Document, + DocumentId, IdentityId, Memo, PortfolioId, PortfolioKind, PortfolioUpdateReason, SecondaryKey, + Ticker, WeightMeter, }; pub use error::Error; @@ -132,7 +134,7 @@ type Identity = pallet_identity::Module; type Portfolio = pallet_portfolio::Module; type Statistics = pallet_statistics::Module; -storage_migration_ver!(4); +storage_migration_ver!(5); decl_storage! { trait Store for Module as Asset { @@ -249,7 +251,7 @@ decl_storage! { pub AssetNonce: map hasher(identity) T::AccountId => u64; /// Storage version. - StorageVersion get(fn storage_version) build(|_| Version::new(4)): Version; + StorageVersion get(fn storage_version) build(|_| Version::new(5)): Version; } add_extra_genesis { @@ -284,9 +286,6 @@ decl_module! { type Error = Error; - /// initialize the default event for this module - fn deposit_event() = default; - const AssetNameMaxLength: u32 = T::AssetNameMaxLength::get(); const FundingRoundNameMaxLength: u32 = T::FundingRoundNameMaxLength::get(); const AssetMetadataNameMaxLength: u32 = T::AssetMetadataNameMaxLength::get(); @@ -294,6 +293,16 @@ decl_module! { const AssetMetadataTypeDefMaxLength: u32 = T::AssetMetadataTypeDefMaxLength::get(); const MaxAssetMediators: u32 = T::MaxAssetMediators::get(); + /// initialize the default event for this module + fn deposit_event() = default; + + fn on_runtime_upgrade() -> Weight { + storage_migrate_on!(StorageVersion, 5, { + migrations::migrate_to_v5::(); + }); + Weight::zero() + } + /// Registers a unique ticker or extends validity of an existing ticker. /// NB: Ticker validity does not get carry forward when renewing ticker. /// diff --git a/pallets/asset/src/migrations.rs b/pallets/asset/src/migrations.rs new file mode 100644 index 0000000000..17b179783e --- /dev/null +++ b/pallets/asset/src/migrations.rs @@ -0,0 +1,366 @@ +use sp_runtime::runtime_logger::RuntimeLogger; +use sp_std::collections::btree_map::BTreeMap; + +use super::*; + +mod v4 { + use super::*; + + decl_storage! { + trait Store for Module as Asset { + // This storage was renamed to UniqueTickerRegistration. + pub Tickers get(fn ticker_registration): + map hasher(blake2_128_concat) Ticker => Option>; + + // This storage was renamed to SecurityTokens and changed the Ticker key to AssetID. + pub Tokens get(fn tokens): map hasher(blake2_128_concat) Ticker => Option; + + // This storage changed the Ticker key to AssetID. + pub AssetNames get(fn asset_names): + map hasher(blake2_128_concat) Ticker => Option; + + // This storage changed the Ticker key to AssetID. + pub BalanceOf get(fn balance_of): + double_map hasher(blake2_128_concat) Ticker, hasher(identity) IdentityId => Balance; + + // This storage was renamed to AssetIdentifiers and changed the Ticker key to AssetID. + pub Identifiers get(fn identifiers): + map hasher(blake2_128_concat) Ticker => Vec; + + // This storage changed the Ticker key to AssetID. + pub FundingRound get(fn funding_round): + map hasher(blake2_128_concat) Ticker => FundingRoundName; + + // This storage changed the Ticker key to AssetID. + pub IssuedInFundingRound get(fn issued_in_funding_round): + map hasher(blake2_128_concat) (Ticker, FundingRoundName) => Balance; + + // This storage changed the Ticker key to AssetID. + pub Frozen get(fn frozen): map hasher(blake2_128_concat) Ticker => bool; + + // This storage was split into TickersOwnedByUser and SecurityTokensOwnedByUser. + pub AssetOwnershipRelations get(fn asset_ownership_relation): + double_map hasher(identity) IdentityId, hasher(blake2_128_concat) Ticker => AssetOwnershipRelation; + + // This storage changed the Ticker key to AssetID. + pub AssetDocuments get(fn asset_documents): + double_map hasher(blake2_128_concat) Ticker, hasher(twox_64_concat) DocumentId => Option; + + // This storage changed the Ticker key to AssetID. + pub AssetDocumentsIdSequence get(fn asset_documents_id_sequence): + map hasher(blake2_128_concat) Ticker => DocumentId; + + // This storage changed the Ticker key to AssetID. + pub AssetMetadataValues get(fn asset_metadata_values): + double_map hasher(blake2_128_concat) Ticker, hasher(twox_64_concat) AssetMetadataKey => Option; + + // This storage changed the Ticker key to AssetID. + pub AssetMetadataValueDetails get(fn asset_metadata_value_details): + double_map hasher(blake2_128_concat) Ticker, hasher(twox_64_concat) AssetMetadataKey => Option>; + + // This storage changed the Ticker key to AssetID. + pub AssetMetadataLocalNameToKey get(fn asset_metadata_local_name_to_key): + double_map hasher(blake2_128_concat) Ticker, hasher(blake2_128_concat) AssetMetadataName => Option; + + // This storage changed the Ticker key to AssetID. + pub AssetMetadataLocalKeyToName get(fn asset_metadata_local_key_to_name): + double_map hasher(blake2_128_concat) Ticker, hasher(twox_64_concat) AssetMetadataLocalKey => Option; + + // This storage changed the Ticker key to AssetID. + pub AssetMetadataLocalSpecs get(fn asset_metadata_local_specs): + double_map hasher(blake2_128_concat) Ticker, hasher(twox_64_concat) AssetMetadataLocalKey => Option; + + // This storage changed the Ticker key to AssetID. + pub AssetMetadataNextLocalKey get(fn asset_metadata_next_local_key): + map hasher(blake2_128_concat) Ticker => AssetMetadataLocalKey; + + // This storage was renamed to AssetsExemptFromAffirmation and changed the Ticker key to AssetID. + pub TickersExemptFromAffirmation get(fn tickers_exempt_from_affirmation): + map hasher(blake2_128_concat) Ticker => bool; + + // This storage was renamed to PreApprovedAsset and changed the Ticker key to AssetID. + pub PreApprovedTicker get(fn pre_approved_tickers): + double_map hasher(identity) IdentityId, hasher(blake2_128_concat) Ticker => bool; + + // This storage changed the Ticker key to AssetID. + pub MandatoryMediators get(fn mandatory_mediators): + map hasher(blake2_128_concat) Ticker => BoundedBTreeSet; + + // This storage changed the Ticker key to AssetID. + pub CurrentAssetMetadataLocalKey get(fn current_asset_metadata_local_key): + map hasher(blake2_128_concat) Ticker => Option; + } + } + + decl_module! { + pub struct Module for enum Call where origin: T::RuntimeOrigin { } + } +} + +pub(crate) fn migrate_to_v5() { + RuntimeLogger::init(); + let mut ticker_to_asset_id = BTreeMap::new(); + + // Removes all elements in the old storage and inserts it in the new storage + + let mut count = 0; + log::info!("Moving items from Tickers to UniqueTickerRegistration"); + v4::Tickers::::drain().for_each(|(ticker, ticker_registration)| { + count += 1; + let asset_id = AssetID::from(ticker); + ticker_to_asset_id.insert(ticker, asset_id); + UniqueTickerRegistration::::insert(ticker, ticker_registration); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Moving items from Tokens to SecurityTokens"); + v4::Tokens::drain().for_each(|(ticker, security_token)| { + count += 1; + let asset_id = ticker_to_asset_id + .entry(ticker) + .or_insert(AssetID::from(ticker)); + SecurityTokens::insert(asset_id, security_token); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Updating types for the AssetNames storage"); + v4::AssetNames::drain().for_each(|(ticker, asset_name)| { + count += 1; + let asset_id = ticker_to_asset_id + .entry(ticker) + .or_insert(AssetID::from(ticker)); + AssetNames::insert(asset_id, asset_name); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Updating types for the BalanceOf storage"); + v4::BalanceOf::drain().for_each(|(ticker, identity, balance)| { + count += 1; + let asset_id = ticker_to_asset_id + .entry(ticker) + .or_insert(AssetID::from(ticker)); + BalanceOf::insert(asset_id, identity, balance); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Moving items from Identifiers to AssetIdentifiers"); + v4::Identifiers::drain().for_each(|(ticker, identifiers)| { + count += 1; + let asset_id = ticker_to_asset_id + .entry(ticker) + .or_insert(AssetID::from(ticker)); + AssetIdentifiers::insert(asset_id, identifiers); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Updating types for the FundingRound storage"); + v4::FundingRound::drain().for_each(|(ticker, name)| { + count += 1; + let asset_id = ticker_to_asset_id + .entry(ticker) + .or_insert(AssetID::from(ticker)); + FundingRound::insert(asset_id, name); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Updating types for the IssuedInFundingRound storage"); + v4::IssuedInFundingRound::drain().for_each(|((ticker, name), balance)| { + count += 1; + let asset_id = ticker_to_asset_id + .entry(ticker) + .or_insert(AssetID::from(ticker)); + IssuedInFundingRound::insert((asset_id, name), balance); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Updating types for the Frozen storage"); + v4::Frozen::drain().for_each(|(ticker, frozen)| { + count += 1; + let asset_id = ticker_to_asset_id + .entry(ticker) + .or_insert(AssetID::from(ticker)); + Frozen::insert(asset_id, frozen); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Moving items from AssetOwnershipRelations to TickersOwnedByUser and SecurityTokensOwnedByUser"); + v4::AssetOwnershipRelations::drain().for_each(|(owner_did, ticker, ownership_detail)| { + count += 1; + let asset_id = ticker_to_asset_id + .entry(ticker) + .or_insert(AssetID::from(ticker)); + + match ownership_detail { + AssetOwnershipRelation::TickerOwned => { + TickersOwnedByUser::insert(owner_did, ticker, true); + } + AssetOwnershipRelation::AssetOwned => { + TickersOwnedByUser::insert(owner_did, ticker, true); + SecurityTokensOwnedByUser::insert(owner_did, asset_id, true); + } + AssetOwnershipRelation::NotOwned => {} + } + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Updating types for the AssetDocuments storage"); + v4::AssetDocuments::drain().for_each(|(ticker, doc_id, doc)| { + count += 1; + let asset_id = ticker_to_asset_id + .entry(ticker) + .or_insert(AssetID::from(ticker)); + + AssetDocuments::insert(asset_id, doc_id, doc); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Updating types for the AssetDocumentsIdSequence storage"); + v4::AssetDocumentsIdSequence::drain().for_each(|(ticker, seq)| { + count += 1; + let asset_id = ticker_to_asset_id + .entry(ticker) + .or_insert(AssetID::from(ticker)); + + AssetDocumentsIdSequence::insert(asset_id, seq); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Updating types for the AssetMetadataValues storage"); + v4::AssetMetadataValues::drain().for_each(|(ticker, key, value)| { + count += 1; + let asset_id = ticker_to_asset_id + .entry(ticker) + .or_insert(AssetID::from(ticker)); + + AssetMetadataValues::insert(asset_id, key, value); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Updating types for the AssetMetadataValueDetails storage"); + v4::AssetMetadataValueDetails::::drain().for_each(|(ticker, key, value)| { + count += 1; + let asset_id = ticker_to_asset_id + .entry(ticker) + .or_insert(AssetID::from(ticker)); + + AssetMetadataValueDetails::::insert(asset_id, key, value); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Updating types for the AssetMetadataLocalNameToKey storage"); + v4::AssetMetadataLocalNameToKey::drain().for_each(|(ticker, name, local_key)| { + count += 1; + let asset_id = ticker_to_asset_id + .entry(ticker) + .or_insert(AssetID::from(ticker)); + + AssetMetadataLocalNameToKey::insert(asset_id, name, local_key); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Updating types for the AssetMetadataLocalKeyToName storage"); + v4::AssetMetadataLocalKeyToName::drain().for_each(|(ticker, local_key, name)| { + count += 1; + let asset_id = ticker_to_asset_id + .entry(ticker) + .or_insert(AssetID::from(ticker)); + + AssetMetadataLocalKeyToName::insert(asset_id, local_key, name); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Updating types for the AssetMetadataLocalSpecs storage"); + v4::AssetMetadataLocalSpecs::drain().for_each(|(ticker, local_key, spec)| { + count += 1; + let asset_id = ticker_to_asset_id + .entry(ticker) + .or_insert(AssetID::from(ticker)); + + AssetMetadataLocalSpecs::insert(asset_id, local_key, spec); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Updating types for the AssetMetadataNextLocalKey storage"); + v4::AssetMetadataNextLocalKey::drain().for_each(|(ticker, next)| { + count += 1; + let asset_id = ticker_to_asset_id + .entry(ticker) + .or_insert(AssetID::from(ticker)); + + AssetMetadataNextLocalKey::insert(asset_id, next); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Moving items from TickersExemptFromAffirmation to AssetsExemptFromAffirmation"); + v4::TickersExemptFromAffirmation::drain().for_each(|(ticker, exempt)| { + count += 1; + let asset_id = ticker_to_asset_id + .entry(ticker) + .or_insert(AssetID::from(ticker)); + + AssetsExemptFromAffirmation::insert(asset_id, exempt); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Moving items from PreApprovedTicker to PreApprovedAsset"); + v4::PreApprovedTicker::drain().for_each(|(did, ticker, approved)| { + count += 1; + let asset_id = ticker_to_asset_id + .entry(ticker) + .or_insert(AssetID::from(ticker)); + + PreApprovedAsset::insert(did, asset_id, approved); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Updating types for the MandatoryMediators storage"); + v4::MandatoryMediators::::drain().for_each(|(ticker, mediators)| { + count += 1; + let asset_id = ticker_to_asset_id + .entry(ticker) + .or_insert(AssetID::from(ticker)); + + MandatoryMediators::::insert(asset_id, mediators); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Updating types for the CurrentAssetMetadataLocalKey storage"); + v4::CurrentAssetMetadataLocalKey::drain().for_each(|(ticker, current_key)| { + count += 1; + let asset_id = ticker_to_asset_id + .entry(ticker) + .or_insert(AssetID::from(ticker)); + + CurrentAssetMetadataLocalKey::insert(asset_id, current_key); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Adding link from legacy tickers to an asset_id"); + for (ticker, asset_id) in ticker_to_asset_id.into_iter() { + count += 1; + AssetIDTicker::insert(asset_id, ticker); + TickerAssetID::insert(ticker, asset_id); + } + log::info!("{:?} items migrated", count); +} diff --git a/pallets/compliance-manager/Cargo.toml b/pallets/compliance-manager/Cargo.toml index b48c219332..4507b98ff7 100644 --- a/pallets/compliance-manager/Cargo.toml +++ b/pallets/compliance-manager/Cargo.toml @@ -22,6 +22,7 @@ pallet-balances = { path = "../balances", optional = true, default-features = fa serde = { version = "1.0.104", default-features = false } serde_derive = { version = "1.0.104", optional = true, default-features = false} either = { version = "1.6.1", default-features = false } +log = "0.4" # Substrate codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } diff --git a/pallets/compliance-manager/src/lib.rs b/pallets/compliance-manager/src/lib.rs index ae0a2714db..819181dcda 100644 --- a/pallets/compliance-manager/src/lib.rs +++ b/pallets/compliance-manager/src/lib.rs @@ -74,6 +74,7 @@ #[cfg(feature = "runtime-benchmarks")] pub mod benchmarking; +mod migrations; use codec::{Decode, Encode}; use core::result::Result; @@ -94,14 +95,14 @@ use polymesh_primitives::compliance_manager::{ ConditionReport, ConditionResult, RequirementReport, }; use polymesh_primitives::{ - proposition, storage_migration_ver, Claim, Condition, ConditionType, Context, IdentityId, - TargetIdentity, TrustedFor, TrustedIssuer, WeightMeter, + proposition, storage_migrate_on, storage_migration_ver, Claim, Condition, ConditionType, + Context, IdentityId, TargetIdentity, TrustedFor, TrustedIssuer, WeightMeter, }; type ExternalAgents = pallet_external_agents::Module; type Identity = pallet_identity::Module; -storage_migration_ver!(0); +storage_migration_ver!(1); decl_storage! { trait Store for Module as ComplianceManager { @@ -110,7 +111,7 @@ decl_storage! { /// List of trusted claim issuer [`AssetID`] -> Issuer Identity pub TrustedClaimIssuer get(fn trusted_claim_issuer): map hasher(blake2_128_concat) AssetID => Vec; /// Storage version. - StorageVersion get(fn storage_version) build(|_| Version::new(0)): Version; + StorageVersion get(fn storage_version) build(|_| Version::new(1)): Version; } } @@ -138,9 +139,16 @@ decl_module! { pub struct Module for enum Call where origin: T::RuntimeOrigin { type Error = Error; + const MaxConditionComplexity: u32 = T::MaxConditionComplexity::get(); + fn deposit_event() = default; - const MaxConditionComplexity: u32 = T::MaxConditionComplexity::get(); + fn on_runtime_upgrade() -> Weight { + storage_migrate_on!(StorageVersion, 1, { + migrations::migrate_to_v1::(); + }); + Weight::zero() + } /// Adds a compliance requirement to an asset given by `asset_id`. /// If there are duplicate ClaimTypes for a particular trusted issuer, duplicates are removed. diff --git a/pallets/compliance-manager/src/migrations.rs b/pallets/compliance-manager/src/migrations.rs new file mode 100644 index 0000000000..b9202bcf14 --- /dev/null +++ b/pallets/compliance-manager/src/migrations.rs @@ -0,0 +1,192 @@ +use sp_runtime::runtime_logger::RuntimeLogger; +use sp_std::collections::btree_map::BTreeMap; + +use super::*; +use polymesh_primitives::Scope; + +mod v0 { + use codec::{Decode, Encode}; + use scale_info::TypeInfo; + + use super::*; + use polymesh_primitives::{CddId, CountryCode, CustomClaimTypeId, Ticker}; + + #[derive(Encode, Decode, TypeInfo, Default, Clone, PartialEq, Eq)] + pub struct AssetCompliance { + pub paused: bool, + pub requirements: Vec, + } + + #[derive(Encode, Decode, TypeInfo, Default, Clone, PartialEq, Eq, Debug)] + pub struct ComplianceRequirement { + pub sender_conditions: Vec, + pub receiver_conditions: Vec, + pub id: u32, + } + + #[derive(Encode, Decode, TypeInfo, Clone, PartialEq, Eq, Debug, Hash)] + pub struct Condition { + pub condition_type: ConditionType, + pub issuers: Vec, + } + + #[derive(Encode, Decode, TypeInfo, Clone, PartialEq, Eq, Debug, Hash)] + pub enum ConditionType { + IsPresent(Claim), + IsAbsent(Claim), + IsAnyOf(Vec), + IsNoneOf(Vec), + IsIdentity(TargetIdentity), + } + + #[derive(Encode, Decode, TypeInfo, Clone, PartialEq, Eq, Debug, Hash)] + pub enum Claim { + Accredited(Scope), + Affiliate(Scope), + BuyLockup(Scope), + SellLockup(Scope), + CustomerDueDiligence(CddId), + KnowYourCustomer(Scope), + Jurisdiction(CountryCode, Scope), + Exempted(Scope), + Blocked(Scope), + Custom(CustomClaimTypeId, Option), + } + + #[derive(Encode, Decode, TypeInfo)] + #[derive(Clone, PartialEq, Eq, Debug, PartialOrd, Ord, Hash)] + pub enum Scope { + Identity(IdentityId), + Ticker(Ticker), + Custom(Vec), + } + + decl_storage! { + trait Store for Module as ComplianceManager { + // This storage changed the Ticker key to AssetID. + // The Scope type, which is inside the compliance codition, has also been changed. + pub AssetCompliances get(fn asset_compliance): + map hasher(blake2_128_concat) Ticker => AssetCompliance; + + // This storage changed the Ticker key to AssetID. + pub TrustedClaimIssuer get(fn trusted_claim_issuer): + map hasher(blake2_128_concat) Ticker => Vec; + } + } + + decl_module! { + pub struct Module for enum Call where origin: T::RuntimeOrigin { } + } +} + +impl From for Scope { + fn from(v0_scope: v0::Scope) -> Self { + match v0_scope { + v0::Scope::Identity(did) => Scope::Identity(did), + v0::Scope::Ticker(ticker) => Scope::Asset(AssetID::from(ticker)), + v0::Scope::Custom(bytes) => Scope::Custom(bytes), + } + } +} + +impl From for Claim { + fn from(v0_claim: v0::Claim) -> Self { + match v0_claim { + v0::Claim::Accredited(scope) => Claim::Accredited(scope.into()), + v0::Claim::Affiliate(scope) => Claim::Affiliate(scope.into()), + v0::Claim::BuyLockup(scope) => Claim::BuyLockup(scope.into()), + v0::Claim::SellLockup(scope) => Claim::SellLockup(scope.into()), + v0::Claim::CustomerDueDiligence(cdd_id) => Claim::CustomerDueDiligence(cdd_id), + v0::Claim::KnowYourCustomer(scope) => Claim::KnowYourCustomer(scope.into()), + v0::Claim::Jurisdiction(cc, scope) => Claim::Jurisdiction(cc, scope.into()), + v0::Claim::Exempted(scope) => Claim::Exempted(scope.into()), + v0::Claim::Blocked(scope) => Claim::Blocked(scope.into()), + v0::Claim::Custom(id, option_scope) => { + Claim::Custom(id, option_scope.map(|scope| scope.into())) + } + } + } +} + +impl From for ConditionType { + fn from(v0_condition_type: v0::ConditionType) -> Self { + match v0_condition_type { + v0::ConditionType::IsPresent(claim) => ConditionType::IsPresent(claim.into()), + v0::ConditionType::IsAbsent(claim) => ConditionType::IsAbsent(claim.into()), + v0::ConditionType::IsAnyOf(claims) => { + ConditionType::IsAnyOf(claims.into_iter().map(|claim| claim.into()).collect()) + } + v0::ConditionType::IsNoneOf(claims) => { + ConditionType::IsNoneOf(claims.into_iter().map(|claim| claim.into()).collect()) + } + v0::ConditionType::IsIdentity(target_did) => ConditionType::IsIdentity(target_did), + } + } +} + +impl From for Condition { + fn from(v0_condition: v0::Condition) -> Self { + Condition { + condition_type: v0_condition.condition_type.into(), + issuers: v0_condition.issuers, + } + } +} + +impl From for ComplianceRequirement { + fn from(v0_compliance_req: v0::ComplianceRequirement) -> Self { + ComplianceRequirement { + sender_conditions: v0_compliance_req + .sender_conditions + .into_iter() + .map(|condition| condition.into()) + .collect(), + receiver_conditions: v0_compliance_req + .receiver_conditions + .into_iter() + .map(|condition| condition.into()) + .collect(), + id: v0_compliance_req.id, + } + } +} + +impl From for AssetCompliance { + fn from(v0_asset_compliance: v0::AssetCompliance) -> Self { + AssetCompliance { + paused: v0_asset_compliance.paused, + requirements: v0_asset_compliance + .requirements + .into_iter() + .map(|req| req.into()) + .collect(), + } + } +} + +pub(crate) fn migrate_to_v1() { + RuntimeLogger::init(); + let mut ticker_to_asset_id = BTreeMap::new(); + + let mut count = 0; + log::info!("Updating types for the AssetCompliances storage"); + v0::AssetCompliances::drain().for_each(|(ticker, compliance)| { + count += 1; + let asset_id = ticker_to_asset_id + .entry(ticker) + .or_insert(AssetID::from(ticker)); + AssetCompliances::insert(asset_id, AssetCompliance::from(compliance)); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Updating types for the TrustedClaimIssuer storage"); + v0::TrustedClaimIssuer::drain().for_each(|(ticker, trusted_issuers)| { + count += 1; + let asset_id = ticker_to_asset_id + .entry(ticker) + .or_insert(AssetID::from(ticker)); + TrustedClaimIssuer::insert(asset_id, trusted_issuers); + }); + log::info!("{:?} items migrated", count); +} diff --git a/pallets/corporate-actions/Cargo.toml b/pallets/corporate-actions/Cargo.toml index 3180af2633..6f6339da54 100644 --- a/pallets/corporate-actions/Cargo.toml +++ b/pallets/corporate-actions/Cargo.toml @@ -21,6 +21,7 @@ pallet-portfolio = { path = "../portfolio", default-features = false } # Other serde = { version = "1.0.104", default-features = false } serde_derive = { version = "1.0.104", optional = true, default-features = false } +log = "0.4" # Substrate codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } diff --git a/pallets/corporate-actions/src/ballot/migrations.rs b/pallets/corporate-actions/src/ballot/migrations.rs new file mode 100644 index 0000000000..0c5e84443e --- /dev/null +++ b/pallets/corporate-actions/src/ballot/migrations.rs @@ -0,0 +1,90 @@ +use sp_runtime::runtime_logger::RuntimeLogger; + +use super::*; + +mod v0 { + use super::*; + + decl_storage! { + trait Store for Module as CorporateBallot { + // The CAId type has changed. + pub Metas get(fn metas): + map hasher(blake2_128_concat) crate::migrations::v0::CAId => Option; + + // The CAId type has changed. + pub TimeRanges get(fn time_ranges): + map hasher(blake2_128_concat) crate::migrations::v0::CAId => Option; + + // The CAId type has changed. + pub MotionNumChoices get(fn motion_choices): + map hasher(blake2_128_concat) crate::migrations::v0::CAId => Vec; + + // The CAId type has changed. + pub RCV get(fn rcv): map hasher(blake2_128_concat) crate::migrations::v0::CAId => bool; + + // The CAId type has changed. + pub Results get(fn results): map hasher(blake2_128_concat) crate::migrations::v0::CAId => Vec; + + // The CAId type has changed. + pub Votes get(fn votes): + double_map hasher(blake2_128_concat) crate::migrations::v0::CAId, hasher(identity) IdentityId => Vec; + + } + } + + decl_module! { + pub struct Module for enum Call where origin: T::RuntimeOrigin { } + } +} + +pub(crate) fn migrate_to_v1() { + RuntimeLogger::init(); + + let mut count = 0; + log::info!("Updating types for the Metas storage"); + v0::Metas::drain().for_each(|(ca_id, ballot)| { + count += 1; + Metas::insert(CAId::from(ca_id), ballot); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Updating types for the TimeRanges storage"); + v0::TimeRanges::drain().for_each(|(ca_id, range)| { + count += 1; + TimeRanges::insert(CAId::from(ca_id), range); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Updating types for the MotionNumChoices storage"); + v0::MotionNumChoices::drain().for_each(|(ca_id, choices)| { + count += 1; + MotionNumChoices::insert(CAId::from(ca_id), choices); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Updating types for the RCV storage"); + v0::RCV::drain().for_each(|(ca_id, rcv)| { + count += 1; + RCV::insert(CAId::from(ca_id), rcv); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Updating types for the Results storage"); + v0::Results::drain().for_each(|(ca_id, balances)| { + count += 1; + Results::insert(CAId::from(ca_id), balances); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Updating types for the Votes storage"); + v0::Votes::drain().for_each(|(ca_id, did, vote)| { + count += 1; + Votes::insert(CAId::from(ca_id), did, vote); + }); + log::info!("{:?} items migrated", count); +} diff --git a/pallets/corporate-actions/src/ballot/mod.rs b/pallets/corporate-actions/src/ballot/mod.rs index 36c3a1ce9f..b71083a520 100644 --- a/pallets/corporate-actions/src/ballot/mod.rs +++ b/pallets/corporate-actions/src/ballot/mod.rs @@ -76,6 +76,7 @@ #[cfg(feature = "runtime-benchmarks")] pub mod benchmarking; +mod migrations; use crate as ca; use ca::{CAId, CAKind, Config, CorporateAction}; @@ -93,7 +94,9 @@ use pallet_asset::checkpoint; use pallet_base::ensure_string_limited; use pallet_identity as identity; use polymesh_common_utilities::protocol_fee::{ChargeProtocolFee, ProtocolOp}; -use polymesh_primitives::{Balance, EventDid, IdentityId, Moment}; +use polymesh_primitives::{ + storage_migrate_on, storage_migration_ver, Balance, EventDid, IdentityId, Moment, +}; use polymesh_primitives_derive::VecU8StrongTyped; use scale_info::TypeInfo; use sp_runtime::traits::Zero; @@ -245,6 +248,8 @@ pub trait WeightInfo { fn remove_ballot() -> Weight; } +storage_migration_ver!(1); + decl_storage! { trait Store for Module as CorporateBallot { /// Metadata of a corporate ballot. @@ -293,6 +298,9 @@ decl_storage! { pub Votes get(fn votes): double_map hasher(blake2_128_concat) CAId, hasher(identity) IdentityId => Vec; + + /// Storage version. + StorageVersion get(fn storage_version) build(|_| Version::new(1)): Version; } } @@ -302,6 +310,13 @@ decl_module! { fn deposit_event() = default; + fn on_runtime_upgrade() -> Weight { + storage_migrate_on!(StorageVersion, 1, { + migrations::migrate_to_v1::(); + }); + Weight::zero() + } + /// Attach a corporate ballot to the CA identified by `ca_id`. /// /// The ballot will admit votes within `range`. diff --git a/pallets/corporate-actions/src/distribution/migrations.rs b/pallets/corporate-actions/src/distribution/migrations.rs new file mode 100644 index 0000000000..5db9f1bfd5 --- /dev/null +++ b/pallets/corporate-actions/src/distribution/migrations.rs @@ -0,0 +1,71 @@ +use sp_runtime::runtime_logger::RuntimeLogger; + +use super::*; + +mod v0 { + use super::*; + use polymesh_primitives::Ticker; + + #[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode, TypeInfo)] + pub struct Distribution { + pub from: PortfolioId, + pub currency: Ticker, + pub per_share: Balance, + pub amount: Balance, + pub remaining: Balance, + pub reclaimed: bool, + pub payment_at: Moment, + pub expires_at: Option, + } + + decl_storage! { + trait Store for Module as CapitalDistribution { + // CAId and Distribution have changed types. + pub(crate) Distributions get(fn distributions): + map hasher(blake2_128_concat) crate::migrations::v0::CAId => Option; + + // The CAId type has changed. + pub(crate) HolderPaid get(fn holder_paid): + map hasher(blake2_128_concat) (crate::migrations::v0::CAId, IdentityId) => bool; + } + } + + decl_module! { + pub struct Module for enum Call where origin: T::RuntimeOrigin { } + } +} + +impl From for Distribution { + fn from(v0_distribution: v0::Distribution) -> Self { + Self { + from: v0_distribution.from, + currency: v0_distribution.currency.into(), + per_share: v0_distribution.per_share, + amount: v0_distribution.amount, + remaining: v0_distribution.remaining, + reclaimed: v0_distribution.reclaimed, + payment_at: v0_distribution.payment_at, + expires_at: v0_distribution.expires_at, + } + } +} + +pub(crate) fn migrate_to_v1() { + RuntimeLogger::init(); + + let mut count = 0; + log::info!("Updating types for the Distributions storage"); + v0::Distributions::drain().for_each(|(ca_id, distribution)| { + count += 1; + Distributions::insert(CAId::from(ca_id), Distribution::from(distribution)); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Updating types for the HolderPaid storage"); + v0::HolderPaid::drain().for_each(|((ca_id, did), paid)| { + count += 1; + HolderPaid::insert((CAId::from(ca_id), did), paid); + }); + log::info!("{:?} items migrated", count); +} diff --git a/pallets/corporate-actions/src/distribution/mod.rs b/pallets/corporate-actions/src/distribution/mod.rs index 65c5eb8560..39dda9aa96 100644 --- a/pallets/corporate-actions/src/distribution/mod.rs +++ b/pallets/corporate-actions/src/distribution/mod.rs @@ -63,6 +63,7 @@ #[cfg(feature = "runtime-benchmarks")] pub mod benchmarking; +mod migrations; use crate as ca; use ca::{CAId, Config, Tax}; @@ -84,8 +85,8 @@ use polymesh_common_utilities::{ }; use polymesh_primitives::asset::AssetID; use polymesh_primitives::{ - storage_migration_ver, Balance, EventDid, IdentityId, Moment, PortfolioId, PortfolioNumber, - SecondaryKey, WeightMeter, + storage_migrate_on, storage_migration_ver, Balance, EventDid, IdentityId, Moment, PortfolioId, + PortfolioNumber, SecondaryKey, WeightMeter, }; use scale_info::TypeInfo; use sp_runtime::traits::Zero; @@ -155,11 +156,11 @@ decl_storage! { HolderPaid get(fn holder_paid): map hasher(blake2_128_concat) (CAId, IdentityId) => bool; /// Storage version. - StorageVersion get(fn storage_version) build(|_| Version::new(0)): Version; + StorageVersion get(fn storage_version) build(|_| Version::new(1)): Version; } } -storage_migration_ver!(0); +storage_migration_ver!(1); decl_module! { pub struct Module for enum Call where origin: T::RuntimeOrigin { @@ -167,6 +168,13 @@ decl_module! { fn deposit_event() = default; + fn on_runtime_upgrade() -> Weight { + storage_migrate_on!(StorageVersion, 1, { + migrations::migrate_to_v1::(); + }); + Weight::zero() + } + /// Start and attach a capital distribution, to the CA identified by `ca_id`, /// with `amount` funds in `currency` withdrawn from `portfolio` belonging to `origin`'s DID. /// diff --git a/pallets/corporate-actions/src/lib.rs b/pallets/corporate-actions/src/lib.rs index 50df8c127f..6215285079 100644 --- a/pallets/corporate-actions/src/lib.rs +++ b/pallets/corporate-actions/src/lib.rs @@ -90,6 +90,7 @@ pub mod benchmarking; pub mod ballot; pub mod distribution; +mod migrations; use codec::{Decode, Encode}; use distribution::WeightInfo as DistWeightInfoTrait; @@ -110,8 +111,8 @@ use polymesh_common_utilities::{ }; use polymesh_primitives::asset::AssetID; use polymesh_primitives::{ - asset::CheckpointId, impl_checked_inc, storage_migration_ver, Balance, DocumentId, EventDid, - IdentityId, Moment, PortfolioNumber, + asset::CheckpointId, impl_checked_inc, storage_migrate_on, storage_migration_ver, Balance, + DocumentId, EventDid, IdentityId, Moment, PortfolioNumber, }; use polymesh_primitives_derive::VecU8StrongTyped; use scale_info::TypeInfo; @@ -404,22 +405,30 @@ decl_storage! { pub Details get(fn details): map hasher(blake2_128_concat) CAId => CADetails; /// Storage version. - StorageVersion get(fn storage_version) build(|_| Version::new(0)): Version; + StorageVersion get(fn storage_version) build(|_| Version::new(1)): Version; } } -storage_migration_ver!(0); +storage_migration_ver!(1); // Public interface for this runtime module. decl_module! { pub struct Module for enum Call where origin: T::RuntimeOrigin { type Error = Error; + const MaxTargetIds: u32 = T::MaxTargetIds::get(); + const MaxDidWhts: u32 = T::MaxDidWhts::get(); + /// initialize the default event for this module fn deposit_event() = default; - const MaxTargetIds: u32 = T::MaxTargetIds::get(); - const MaxDidWhts: u32 = T::MaxDidWhts::get(); + fn on_runtime_upgrade() -> Weight { + storage_migrate_on!(StorageVersion, 1, { + migrations::migrate_to_v1::(); + }); + Weight::zero() + } + /// Set the max `length` of `details` in terms of bytes. /// May only be called via a PIP. diff --git a/pallets/corporate-actions/src/migrations.rs b/pallets/corporate-actions/src/migrations.rs new file mode 100644 index 0000000000..241e421586 --- /dev/null +++ b/pallets/corporate-actions/src/migrations.rs @@ -0,0 +1,136 @@ +use sp_runtime::runtime_logger::RuntimeLogger; +use sp_std::collections::btree_map::BTreeMap; + +use super::*; + +pub(crate) mod v0 { + use super::*; + use polymesh_primitives::Ticker; + + #[derive(Copy, Clone, PartialEq, Eq, Encode, Decode, TypeInfo, Debug)] + pub struct CAId { + pub ticker: Ticker, + pub local_id: LocalCAId, + } + + decl_storage! { + trait Store for Module as CorporateAction { + // This storage changed the Ticker key to AssetID. + pub DefaultTargetIdentities get(fn default_target_identities): + map hasher(blake2_128_concat) Ticker => TargetIdentities; + + // This storage changed the Ticker key to AssetID. + pub DefaultWithholdingTax get(fn default_withholding_tax): + map hasher(blake2_128_concat) Ticker => Tax; + + // This storage changed the Ticker key to AssetID. + pub DidWithholdingTax get(fn did_withholding_tax): + map hasher(blake2_128_concat) Ticker => Vec<(IdentityId, Tax)>; + + // This storage changed the Ticker key to AssetID. + pub CAIdSequence get(fn ca_id_sequence): + map hasher(blake2_128_concat) Ticker => LocalCAId; + + // This storage changed the Ticker key to AssetID. + pub CorporateActions get(fn corporate_actions): + double_map hasher(blake2_128_concat) Ticker, hasher(twox_64_concat) LocalCAId => Option; + + // The CAId type has been updated. + pub CADocLink get(fn ca_doc_link): + map hasher(blake2_128_concat) CAId => Vec; + + // The CAId type has been updated. + pub Details get(fn details): + map hasher(blake2_128_concat) CAId => CADetails; + } + } + + decl_module! { + pub struct Module for enum Call where origin: T::RuntimeOrigin { } + } +} + +impl From for CAId { + fn from(v0_ca_id: v0::CAId) -> Self { + Self { + asset_id: AssetID::from(v0_ca_id.ticker), + local_id: v0_ca_id.local_id, + } + } +} + +pub(crate) fn migrate_to_v1() { + RuntimeLogger::init(); + let mut ticker_to_asset_id = BTreeMap::new(); + + let mut count = 0; + log::info!("Updating types for the DefaultTargetIdentities storage"); + v0::DefaultTargetIdentities::drain().for_each(|(ticker, target_identities)| { + count += 1; + let asset_id = ticker_to_asset_id + .entry(ticker) + .or_insert(AssetID::from(ticker)); + DefaultTargetIdentities::insert(asset_id, target_identities); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Updating types for the DefaultWithholdingTax storage"); + v0::DefaultWithholdingTax::drain().for_each(|(ticker, tax)| { + count += 1; + let asset_id = ticker_to_asset_id + .entry(ticker) + .or_insert(AssetID::from(ticker)); + DefaultWithholdingTax::insert(asset_id, tax); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Updating types for the DidWithholdingTax storage"); + v0::DidWithholdingTax::drain().for_each(|(ticker, id_tax)| { + count += 1; + let asset_id = ticker_to_asset_id + .entry(ticker) + .or_insert(AssetID::from(ticker)); + DidWithholdingTax::insert(asset_id, id_tax); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Updating types for the CAIdSequence storage"); + v0::CAIdSequence::drain().for_each(|(ticker, id_tax)| { + count += 1; + let asset_id = ticker_to_asset_id + .entry(ticker) + .or_insert(AssetID::from(ticker)); + CAIdSequence::insert(asset_id, id_tax); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Updating types for the CorporateActions storage"); + v0::CorporateActions::drain().for_each(|(ticker, local_id, ca)| { + count += 1; + let asset_id = ticker_to_asset_id + .entry(ticker) + .or_insert(AssetID::from(ticker)); + CorporateActions::insert(asset_id, local_id, ca); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Updating types for the CADocLink storage"); + v0::CADocLink::drain().for_each(|(ca_id, docs)| { + count += 1; + CADocLink::insert(CAId::from(ca_id), docs); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Updating types for the Details storage"); + v0::Details::drain().for_each(|(ca_id, details)| { + count += 1; + Details::insert(CAId::from(ca_id), details); + }); + log::info!("{:?} items migrated", count); +} diff --git a/pallets/external-agents/Cargo.toml b/pallets/external-agents/Cargo.toml index 56885e5e5b..92f66315f0 100644 --- a/pallets/external-agents/Cargo.toml +++ b/pallets/external-agents/Cargo.toml @@ -18,12 +18,14 @@ pallet-permissions = { path = "../permissions", default-features = false } # Other serde = { version = "1.0.104", default-features = false } serde_derive = { version = "1.0.104", optional = true, default-features = false } +log = "0.4" # Substrate codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } frame-system = { version = "4.0.0-dev", default-features = false } frame-support = { version = "4.0.0-dev", default-features = false } scale-info = { version = "2.0", default-features = false, features = ["derive"] } +sp-runtime = { version = "7.0.0", default-features = false } sp-std = { version = "5.0.0", default-features = false } # Only in STD diff --git a/pallets/external-agents/src/lib.rs b/pallets/external-agents/src/lib.rs index 08306ab139..4fbf68e806 100644 --- a/pallets/external-agents/src/lib.rs +++ b/pallets/external-agents/src/lib.rs @@ -51,11 +51,14 @@ #[cfg(feature = "runtime-benchmarks")] pub mod benchmarking; +mod migrations; +use codec::{Decode, Encode}; use frame_support::{ decl_error, decl_module, decl_storage, dispatch::{DispatchError, DispatchResult}, ensure, + weights::Weight, }; use pallet_base::{try_next_post, try_next_pre}; use pallet_identity::PermissionedCallOriginData; @@ -64,14 +67,16 @@ use polymesh_common_utilities::with_transaction; use polymesh_primitives::agent::{AGId, AgentGroup}; use polymesh_primitives::asset::AssetID; use polymesh_primitives::{ - extract_auth, AuthorizationData, EventDid, ExtrinsicPermissions, IdentityId, PalletPermissions, - Signatory, SubsetRestriction, + extract_auth, storage_migrate_on, storage_migration_ver, AuthorizationData, EventDid, + ExtrinsicPermissions, IdentityId, PalletPermissions, Signatory, SubsetRestriction, }; use sp_std::prelude::*; type Identity = pallet_identity::Module; type Permissions = pallet_permissions::Module; +storage_migration_ver!(1); + decl_storage! { trait Store for Module as ExternalAgents { /// The next per-asset AG ID in the sequence. @@ -107,6 +112,8 @@ decl_storage! { hasher(blake2_128_concat) AssetID, hasher(twox_64_concat) AGId => Option; + + StorageVersion get(fn storage_version) build(|_| Version::new(1)): Version; } } @@ -116,6 +123,13 @@ decl_module! { fn deposit_event() = default; + fn on_runtime_upgrade() -> Weight { + storage_migrate_on!(StorageVersion, 1, { + migrations::migrate_to_v1::(); + }); + Weight::zero() + } + /// Creates a custom agent group (AG) for the given `asset_id`. /// /// The AG will have the permissions as given by `perms`. diff --git a/pallets/external-agents/src/migrations.rs b/pallets/external-agents/src/migrations.rs new file mode 100644 index 0000000000..e7383b6903 --- /dev/null +++ b/pallets/external-agents/src/migrations.rs @@ -0,0 +1,98 @@ +use sp_runtime::runtime_logger::RuntimeLogger; +use sp_std::collections::btree_map::BTreeMap; + +use super::*; + +mod v0 { + use super::*; + use polymesh_primitives::Ticker; + + decl_storage! { + trait Store for Module as ExternalAgents { + // This storage changed the Ticker key to AssetID. + pub AGIdSequence get(fn agent_group_id_sequence): + map hasher(blake2_128_concat) Ticker => AGId; + + // This storage changed the Ticker key to AssetID. + pub AgentOf get(fn agent_of): + double_map hasher(blake2_128_concat) IdentityId, hasher(blake2_128_concat) Ticker => (); + + // This storage changed the Ticker key to AssetID. + pub GroupOfAgent get(fn agents): + double_map hasher(blake2_128_concat) Ticker, hasher(twox_64_concat) IdentityId => Option; + + // This storage changed the Ticker key to AssetID. + pub NumFullAgents get(fn num_full_agents): + map hasher(blake2_128_concat) Ticker => u32; + + // This storage changed the Ticker key to AssetID. + pub GroupPermissions get(fn permissions): + double_map hasher(blake2_128_concat) Ticker, hasher(twox_64_concat) AGId => Option; + } + + } + + decl_module! { + pub struct Module for enum Call where origin: T::RuntimeOrigin { } + } +} + +pub(crate) fn migrate_to_v1() { + RuntimeLogger::init(); + let mut ticker_to_asset_id = BTreeMap::new(); + + let mut count = 0; + log::info!("Updating types for the AGIdSequence storage"); + v0::AGIdSequence::drain().for_each(|(ticker, ag_id)| { + count += 1; + let asset_id = ticker_to_asset_id + .entry(ticker) + .or_insert(AssetID::from(ticker)); + AGIdSequence::insert(asset_id, ag_id); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Updating types for the AgentOf storage"); + v0::AgentOf::drain().for_each(|(did, ticker, empty)| { + count += 1; + let asset_id = ticker_to_asset_id + .entry(ticker) + .or_insert(AssetID::from(ticker)); + AgentOf::insert(did, asset_id, empty); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Updating types for the GroupOfAgent storage"); + v0::GroupOfAgent::drain().for_each(|(ticker, did, group)| { + count += 1; + let asset_id = ticker_to_asset_id + .entry(ticker) + .or_insert(AssetID::from(ticker)); + GroupOfAgent::insert(asset_id, did, group); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Updating types for the NumFullAgents storage"); + v0::NumFullAgents::drain().for_each(|(ticker, n)| { + count += 1; + let asset_id = ticker_to_asset_id + .entry(ticker) + .or_insert(AssetID::from(ticker)); + NumFullAgents::insert(asset_id, n); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Updating types for the GroupPermissions storage"); + v0::GroupPermissions::drain().for_each(|(ticker, ag_id, ext_perms)| { + count += 1; + let asset_id = ticker_to_asset_id + .entry(ticker) + .or_insert(AssetID::from(ticker)); + GroupPermissions::insert(asset_id, ag_id, ext_perms); + }); + log::info!("{:?} items migrated", count); +} diff --git a/pallets/identity/src/lib.rs b/pallets/identity/src/lib.rs index 0b50820139..0ceae37b17 100644 --- a/pallets/identity/src/lib.rs +++ b/pallets/identity/src/lib.rs @@ -84,6 +84,7 @@ pub mod benchmarking; mod auth; mod claims; mod keys; +mod ticker_migrations; pub mod types; pub use polymesh_common_utilities::traits::identity::WeightInfo; @@ -113,7 +114,7 @@ use polymesh_primitives::{ pub type Event = polymesh_common_utilities::traits::identity::Event; -storage_migration_ver!(6); +storage_migration_ver!(7); decl_storage! { trait Store for Module as Identity { @@ -167,7 +168,7 @@ decl_storage! { pub CddAuthForPrimaryKeyRotation get(fn cdd_auth_for_primary_key_rotation): bool; /// Storage version. - StorageVersion get(fn storage_version) build(|_| Version::new(6)): Version; + StorageVersion get(fn storage_version) build(|_| Version::new(7)): Version; /// How many "strong" references to the account key. /// diff --git a/pallets/identity/src/ticker_migrations.rs b/pallets/identity/src/ticker_migrations.rs new file mode 100644 index 0000000000..d17d6523a6 --- /dev/null +++ b/pallets/identity/src/ticker_migrations.rs @@ -0,0 +1,276 @@ +use sp_runtime::runtime_logger::RuntimeLogger; + +use super::*; + +mod v6 { + use scale_info::TypeInfo; + use sp_std::collections::btree_set::BTreeSet; + + use super::*; + use polymesh_primitives::{ + agent::AgentGroup, Balance, CountryCode, ExtrinsicPermissions, Moment, PortfolioId, + PortfolioPermissions, Ticker, + }; + + #[derive(Encode, Decode, TypeInfo, Clone, PartialEq, Eq, Debug, PartialOrd, Ord)] + pub struct Claim2ndKey { + pub issuer: IdentityId, + pub scope: Option, + } + + #[derive(Encode, Decode, TypeInfo)] + #[derive(Clone, PartialEq, Eq, Debug, PartialOrd, Ord, Hash)] + pub enum Scope { + Identity(IdentityId), + Ticker(Ticker), + Custom(Vec), + } + + #[derive(Encode, Decode, TypeInfo, Clone, PartialEq, Eq)] + pub struct IdentityClaim { + pub claim_issuer: IdentityId, + pub issuance_date: Moment, + pub last_update_date: Moment, + pub expiry: Option, + pub claim: Claim, + } + + #[derive(Encode, Decode, TypeInfo, Clone, PartialEq, Eq, Debug, Hash)] + pub enum Claim { + Accredited(Scope), + Affiliate(Scope), + BuyLockup(Scope), + SellLockup(Scope), + CustomerDueDiligence(CddId), + KnowYourCustomer(Scope), + Jurisdiction(CountryCode, Scope), + Exempted(Scope), + Blocked(Scope), + Custom(CustomClaimTypeId, Option), + } + + #[derive(Encode, Decode, TypeInfo)] + #[derive(Clone, Debug, PartialEq, Eq)] + pub enum KeyRecord { + PrimaryKey(IdentityId), + SecondaryKey(IdentityId, Permissions), + MultiSigSignerKey(AccountId), + } + + #[derive(Decode, Encode, TypeInfo)] + #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] + pub struct Permissions { + pub asset: AssetPermissions, + pub extrinsic: ExtrinsicPermissions, + pub portfolio: PortfolioPermissions, + } + + pub type AssetPermissions = SubsetRestriction; + + #[derive(Encode, Decode, TypeInfo, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] + pub enum SubsetRestriction { + Whole, + These(BTreeSet), + Except(BTreeSet), + } + + #[derive(Encode, Decode, TypeInfo, Clone, PartialEq, Debug)] + pub struct Authorization { + pub authorization_data: AuthorizationData, + pub authorized_by: IdentityId, + pub expiry: Option, + pub auth_id: u64, + pub count: u32, + } + + #[derive(Encode, Decode, TypeInfo, Clone, PartialEq, Eq, Debug, PartialOrd, Ord)] + pub enum AuthorizationData { + AttestPrimaryKeyRotation(IdentityId), + RotatePrimaryKey, + TransferTicker(Ticker), + AddMultiSigSigner(AccountId), + TransferAssetOwnership(Ticker), + JoinIdentity(Permissions), + PortfolioCustody(PortfolioId), + BecomeAgent(Ticker, AgentGroup), + AddRelayerPayingKey(AccountId, AccountId, Balance), + RotatePrimaryKeyToSecondary(Permissions), + } + + decl_storage! { + trait Store for Module as Identity { + // This storage changed the Ticker key to AssetID. + pub Claims: double_map hasher(twox_64_concat) Claim1stKey, hasher(blake2_128_concat) Claim2ndKey => Option; + + pub KeyRecords get(fn key_records): + map hasher(twox_64_concat) T::AccountId => Option>; + + pub Authorizations get(fn authorizations): + double_map hasher(blake2_128_concat) Signatory, hasher(twox_64_concat) u64 => Option>; + } + } + + decl_module! { + pub struct Module for enum Call where origin: T::RuntimeOrigin { } + } +} + +impl From for Claim2ndKey { + fn from(v6_claim2key: v6::Claim2ndKey) -> Self { + Claim2ndKey { + issuer: v6_claim2key.issuer, + scope: v6_claim2key.scope.map(|v| v.into()), + } + } +} + +impl From for Scope { + fn from(v6_scope: v6::Scope) -> Self { + match v6_scope { + v6::Scope::Identity(did) => Scope::Identity(did), + v6::Scope::Ticker(ticker) => Scope::Asset(ticker.into()), + v6::Scope::Custom(bytes) => Scope::Custom(bytes), + } + } +} + +impl From for Claim { + fn from(v6_claim: v6::Claim) -> Self { + match v6_claim { + v6::Claim::Accredited(scope) => Claim::Accredited(scope.into()), + v6::Claim::Affiliate(scope) => Claim::Affiliate(scope.into()), + v6::Claim::BuyLockup(scope) => Claim::BuyLockup(scope.into()), + v6::Claim::SellLockup(scope) => Claim::SellLockup(scope.into()), + v6::Claim::CustomerDueDiligence(cdd_id) => Claim::CustomerDueDiligence(cdd_id), + v6::Claim::KnowYourCustomer(scope) => Claim::KnowYourCustomer(scope.into()), + v6::Claim::Jurisdiction(cc, scope) => Claim::Jurisdiction(cc, scope.into()), + v6::Claim::Exempted(scope) => Claim::Exempted(scope.into()), + v6::Claim::Blocked(scope) => Claim::Blocked(scope.into()), + v6::Claim::Custom(id, option_scope) => { + Claim::Custom(id, option_scope.map(|scope| scope.into())) + } + } + } +} + +impl From for IdentityClaim { + fn from(v6_id_claim: v6::IdentityClaim) -> Self { + IdentityClaim { + claim_issuer: v6_id_claim.claim_issuer, + issuance_date: v6_id_claim.issuance_date, + last_update_date: v6_id_claim.last_update_date, + expiry: v6_id_claim.expiry, + claim: v6_id_claim.claim.into(), + } + } +} + +use polymesh_primitives::AssetPermissions; + +impl From for AssetPermissions { + fn from(v6_asset_perms: v6::AssetPermissions) -> Self { + match v6_asset_perms { + v6::AssetPermissions::Whole => AssetPermissions::Whole, + v6::AssetPermissions::These(tickers) => { + AssetPermissions::These(tickers.into_iter().map(|t| t.into()).collect()) + } + v6::AssetPermissions::Except(tickers) => { + AssetPermissions::Except(tickers.into_iter().map(|t| t.into()).collect()) + } + } + } +} + +impl From for Permissions { + fn from(v6_perms: v6::Permissions) -> Self { + Permissions { + asset: v6_perms.asset.into(), + extrinsic: v6_perms.extrinsic, + portfolio: v6_perms.portfolio, + } + } +} + +impl From> for KeyRecord { + fn from(v6_key_record: v6::KeyRecord) -> Self { + match v6_key_record { + v6::KeyRecord::PrimaryKey(did) => KeyRecord::PrimaryKey(did), + v6::KeyRecord::SecondaryKey(did, permissions) => { + KeyRecord::SecondaryKey(did, permissions.into()) + } + v6::KeyRecord::MultiSigSignerKey(acc) => KeyRecord::MultiSigSignerKey(acc), + } + } +} + +impl From> for AuthorizationData { + fn from(v6_auth_data: v6::AuthorizationData) -> Self { + match v6_auth_data { + v6::AuthorizationData::AttestPrimaryKeyRotation(did) => { + AuthorizationData::AttestPrimaryKeyRotation(did) + } + v6::AuthorizationData::RotatePrimaryKey => AuthorizationData::RotatePrimaryKey, + v6::AuthorizationData::TransferTicker(ticker) => { + AuthorizationData::TransferTicker(ticker) + } + v6::AuthorizationData::AddMultiSigSigner(acc) => { + AuthorizationData::AddMultiSigSigner(acc) + } + v6::AuthorizationData::TransferAssetOwnership(ticker) => { + AuthorizationData::TransferAssetOwnership(ticker.into()) + } + v6::AuthorizationData::JoinIdentity(perms) => { + AuthorizationData::JoinIdentity(perms.into()) + } + v6::AuthorizationData::PortfolioCustody(portfolio_id) => { + AuthorizationData::PortfolioCustody(portfolio_id) + } + v6::AuthorizationData::BecomeAgent(ticker, ag) => { + AuthorizationData::BecomeAgent(ticker.into(), ag) + } + v6::AuthorizationData::AddRelayerPayingKey(acc1, acc2, balance) => { + AuthorizationData::AddRelayerPayingKey(acc1, acc2, balance) + } + v6::AuthorizationData::RotatePrimaryKeyToSecondary(perms) => { + AuthorizationData::RotatePrimaryKeyToSecondary(perms.into()) + } + } + } +} + +impl From> for Authorization { + fn from(v6_auth: v6::Authorization) -> Self { + Authorization { + authorization_data: v6_auth.authorization_data.into(), + authorized_by: v6_auth.authorized_by, + expiry: v6_auth.expiry, + auth_id: v6_auth.auth_id, + count: v6_auth.count, + } + } +} + +#[allow(dead_code)] +pub(crate) fn migrate_to_v7() { + RuntimeLogger::init(); + + // Removes all elements in the old storage and inserts it in the new storage + log::info!("Updating types for the Claims storage"); + v6::Claims::drain().for_each(|(claim1key, claim2key, id_claim)| { + Claims::insert( + claim1key, + Claim2ndKey::from(claim2key), + IdentityClaim::from(id_claim), + ); + }); + + log::info!("Updating types for the KeyRecords storage"); + v6::KeyRecords::::drain().for_each(|(acc_id, key_record)| { + KeyRecords::::insert(acc_id, KeyRecord::from(key_record)); + }); + + log::info!("Updating types for the Authorizations storage"); + v6::Authorizations::::drain().for_each(|(acc, n, auth)| { + Authorizations::::insert(acc, n, Authorization::from(auth)); + }); +} diff --git a/pallets/nft/src/lib.rs b/pallets/nft/src/lib.rs index 050e84a8e6..72a6b504b0 100644 --- a/pallets/nft/src/lib.rs +++ b/pallets/nft/src/lib.rs @@ -33,8 +33,9 @@ type Portfolio = pallet_portfolio::Module; #[cfg(feature = "runtime-benchmarks")] pub mod benchmarking; +mod migrations; -storage_migration_ver!(3); +storage_migration_ver!(4); decl_storage!( trait Store for Module as NFT { @@ -75,7 +76,7 @@ decl_storage!( pub CurrentCollectionId get(fn current_collection_id): Option; /// Storage version. - StorageVersion get(fn storage_version) build(|_| Version::new(3)): Version; + StorageVersion get(fn storage_version) build(|_| Version::new(4)): Version; } ); @@ -91,8 +92,8 @@ decl_module! { fn deposit_event() = default; fn on_runtime_upgrade() -> Weight { - storage_migrate_on!(StorageVersion, 3, { - migration::migrate_to_v3::(); + storage_migrate_on!(StorageVersion, 4, { + migrations::migrate_to_v4::(); }); Weight::zero() } diff --git a/pallets/nft/src/migrations.rs b/pallets/nft/src/migrations.rs new file mode 100644 index 0000000000..8ba9bd9cb5 --- /dev/null +++ b/pallets/nft/src/migrations.rs @@ -0,0 +1,110 @@ +use sp_runtime::runtime_logger::RuntimeLogger; +use sp_std::collections::btree_map::BTreeMap; + +use super::*; + +mod v3 { + use scale_info::TypeInfo; + + use super::*; + use polymesh_primitives::Ticker; + + #[derive(Clone, Debug, Decode, Default, Encode, PartialEq, TypeInfo)] + pub struct NFTCollection { + pub id: NFTCollectionId, + pub ticker: Ticker, + } + + decl_storage! { + trait Store for Module as NFT { + // This storage changed the Ticker key to AssetID. + pub NumberOfNFTs get(fn balance_of): + double_map hasher(blake2_128_concat) Ticker, hasher(identity) IdentityId => NFTCount; + + // This storage changed the Ticker key to AssetID. + pub CollectionTicker get(fn collection_ticker): + map hasher(blake2_128_concat) Ticker => NFTCollectionId; + + // This storage changed the Ticker key to AssetID. + pub Collection get(fn nft_collection): + map hasher(blake2_128_concat) NFTCollectionId => NFTCollection; + + // This storage changed the Ticker key to AssetID. + pub NFTsInCollection get(fn nfts_in_collection): + map hasher(blake2_128_concat) Ticker => NFTCount; + + // This storage changed the Ticker key to AssetID. + pub NFTOwner get(fn nft_owner): + double_map hasher(blake2_128_concat) Ticker, hasher(blake2_128_concat) NFTId => Option; + } + } + + decl_module! { + pub struct Module for enum Call where origin: T::RuntimeOrigin { } + } +} + +impl From for NFTCollection { + fn from(v3_nft_collection: v3::NFTCollection) -> NFTCollection { + NFTCollection::new(v3_nft_collection.id, v3_nft_collection.ticker.into()) + } +} + +pub(crate) fn migrate_to_v4() { + RuntimeLogger::init(); + let mut ticker_to_asset_id = BTreeMap::new(); + + // Removes all elements in the old storage and inserts it in the new storage + + let mut count = 0; + log::info!("Updating types for the NumberOfNFTs storage"); + v3::NumberOfNFTs::drain().for_each(|(ticker, did, n)| { + count += 1; + let asset_id = ticker_to_asset_id + .entry(ticker) + .or_insert(AssetID::from(ticker)); + NumberOfNFTs::insert(asset_id, did, n); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Updating types for the CollectionTicker storage"); + v3::CollectionTicker::drain().for_each(|(ticker, id)| { + count += 1; + let asset_id = ticker_to_asset_id + .entry(ticker) + .or_insert(AssetID::from(ticker)); + CollectionAsset::insert(asset_id, id); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Updating types for the Collection storage"); + v3::Collection::drain().for_each(|(id, collection)| { + count += 1; + Collection::insert(id, NFTCollection::from(collection)); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Updating types for the NFTsInCollection storage"); + v3::NFTsInCollection::drain().for_each(|(ticker, n)| { + count += 1; + let asset_id = ticker_to_asset_id + .entry(ticker) + .or_insert(AssetID::from(ticker)); + NFTsInCollection::insert(asset_id, n); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Updating types for the NFTOwner storage"); + v3::NFTOwner::drain().for_each(|(ticker, nft_id, portfolio)| { + count += 1; + let asset_id = ticker_to_asset_id + .entry(ticker) + .or_insert(AssetID::from(ticker)); + NFTOwner::insert(asset_id, nft_id, portfolio); + }); + log::info!("{:?} items migrated", count); +} diff --git a/pallets/portfolio/Cargo.toml b/pallets/portfolio/Cargo.toml index e3c63c111e..029aa9edd3 100644 --- a/pallets/portfolio/Cargo.toml +++ b/pallets/portfolio/Cargo.toml @@ -17,12 +17,14 @@ pallet-permissions = { path = "../permissions", default-features = false } # Other serde = { version = "1.0.104", default-features = false } +log = "0.4.8" # Substrate codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } frame-support = { version = "4.0.0-dev", default-features = false } frame-system = { version = "4.0.0-dev", default-features = false } scale-info = { version = "2.0", default-features = false, features = ["derive"] } +sp-runtime = { version = "7.0.0", default-features = false } sp-arithmetic = { version = "6.0.0", default-features = false } sp-std = { version = "5.0.0", default-features = false } diff --git a/pallets/portfolio/src/lib.rs b/pallets/portfolio/src/lib.rs index ccdca4b660..4682855df3 100644 --- a/pallets/portfolio/src/lib.rs +++ b/pallets/portfolio/src/lib.rs @@ -44,10 +44,12 @@ #[cfg(feature = "runtime-benchmarks")] pub mod benchmarking; +mod migrations; use codec::{Decode, Encode}; use core::{iter, mem}; use frame_support::dispatch::{DispatchError, DispatchResult}; +use frame_support::weights::Weight; use frame_support::{decl_error, decl_module, decl_storage, ensure}; use sp_arithmetic::traits::Zero; use sp_std::collections::btree_set::BTreeSet; @@ -60,9 +62,9 @@ use polymesh_common_utilities::traits::nft::NFTTrait; use polymesh_common_utilities::traits::portfolio::PortfolioSubTrait; use polymesh_primitives::asset::AssetID; use polymesh_primitives::{ - extract_auth, identity_id::PortfolioValidityResult, storage_migration_ver, Balance, Fund, - FundDescription, IdentityId, NFTId, PortfolioId, PortfolioKind, PortfolioName, PortfolioNumber, - SecondaryKey, + extract_auth, identity_id::PortfolioValidityResult, storage_migrate_on, storage_migration_ver, + Balance, Fund, FundDescription, IdentityId, NFTId, PortfolioId, PortfolioKind, PortfolioName, + PortfolioNumber, SecondaryKey, }; type Identity = pallet_identity::Module; @@ -126,11 +128,11 @@ decl_storage! { double_map hasher(identity) IdentityId, hasher(identity) IdentityId => bool; /// Storage version. - StorageVersion get(fn storage_version) build(|_| Version::new(2)): Version; + StorageVersion get(fn storage_version) build(|_| Version::new(3)): Version; } } -storage_migration_ver!(2); +storage_migration_ver!(3); decl_error! { pub enum Error for Module { @@ -180,6 +182,13 @@ decl_module! { /// The event logger. fn deposit_event() = default; + fn on_runtime_upgrade() -> Weight { + storage_migrate_on!(StorageVersion, 3, { + migrations::migrate_to_v3::(); + }); + Weight::zero() + } + /// Creates a portfolio with the given `name`. #[weight = ::WeightInfo::create_portfolio()] pub fn create_portfolio(origin, name: PortfolioName) -> DispatchResult { diff --git a/pallets/portfolio/src/migrations.rs b/pallets/portfolio/src/migrations.rs new file mode 100644 index 0000000000..dcbbc39377 --- /dev/null +++ b/pallets/portfolio/src/migrations.rs @@ -0,0 +1,99 @@ +use sp_runtime::runtime_logger::RuntimeLogger; +use sp_std::collections::btree_map::BTreeMap; + +use super::*; + +mod v2 { + use super::*; + use polymesh_primitives::Ticker; + + decl_storage! { + trait Store for Module as Portfolio { + // This storage changed the Ticker key to AssetID. + pub PortfolioAssetBalances get(fn portfolio_asset_balances): + double_map hasher(twox_64_concat) PortfolioId, hasher(blake2_128_concat) Ticker => Balance; + + // This storage changed the Ticker key to AssetID. + pub PortfolioLockedAssets get(fn locked_assets): + double_map hasher(twox_64_concat) PortfolioId, hasher(blake2_128_concat) Ticker => Balance; + + // This storage changed the Ticker key to AssetID. + pub PortfolioNFT get(fn portfolio_nft): + double_map hasher(twox_64_concat) PortfolioId, hasher(blake2_128_concat) (Ticker, NFTId) => bool; + + // This storage changed the Ticker key to AssetID. + pub PortfolioLockedNFT get(fn portfolio_locked_nft): + double_map hasher(twox_64_concat) PortfolioId, hasher(blake2_128_concat) (Ticker, NFTId) => bool; + + // This storage changed the Ticker key to AssetID. + pub PreApprovedPortfolios get(fn pre_approved_portfolios): + double_map hasher(twox_64_concat) PortfolioId, hasher(blake2_128_concat) Ticker => bool; + } + } + + decl_module! { + pub struct Module for enum Call where origin: T::RuntimeOrigin { } + } +} + +pub(crate) fn migrate_to_v3() { + RuntimeLogger::init(); + let mut ticker_to_asset_id = BTreeMap::new(); + + // Removes all elements in the old storage and inserts it in the new storage + + let mut count = 0; + log::info!("Updating types for the PortfolioAssetBalances storage"); + v2::PortfolioAssetBalances::drain().for_each(|(portfolio, ticker, balance)| { + count += 1; + let asset_id = ticker_to_asset_id + .entry(ticker) + .or_insert(AssetID::from(ticker)); + PortfolioAssetBalances::insert(portfolio, asset_id, balance); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Updating types for the PortfolioLockedAssets storage"); + v2::PortfolioLockedAssets::drain().for_each(|(portfolio, ticker, balance)| { + count += 1; + let asset_id = ticker_to_asset_id + .entry(ticker) + .or_insert(AssetID::from(ticker)); + PortfolioLockedAssets::insert(portfolio, asset_id, balance); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Updating types for the PortfolioNFT storage"); + v2::PortfolioNFT::drain().for_each(|(portfolio, (ticker, nft_id), v)| { + count += 1; + let asset_id = ticker_to_asset_id + .entry(ticker) + .or_insert(AssetID::from(ticker)); + PortfolioNFT::insert(portfolio, (asset_id, nft_id), v); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Updating types for the PortfolioLockedNFT storage"); + v2::PortfolioLockedNFT::drain().for_each(|(portfolio, (ticker, nft_id), v)| { + count += 1; + let asset_id = ticker_to_asset_id + .entry(ticker) + .or_insert(AssetID::from(ticker)); + PortfolioLockedNFT::insert(portfolio, (asset_id, nft_id), v); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Updating types for the PreApprovedPortfolios storage"); + v2::PreApprovedPortfolios::drain().for_each(|(portfolio, ticker, v)| { + count += 1; + let asset_id = ticker_to_asset_id + .entry(ticker) + .or_insert(AssetID::from(ticker)); + PreApprovedPortfolios::insert(portfolio, asset_id, v); + }); + log::info!("{:?} items migrated", count); +} diff --git a/pallets/settlement/src/lib.rs b/pallets/settlement/src/lib.rs index fe017d0886..8eda8f1ab5 100644 --- a/pallets/settlement/src/lib.rs +++ b/pallets/settlement/src/lib.rs @@ -48,6 +48,7 @@ #[cfg(feature = "runtime-benchmarks")] pub mod benchmarking; +mod migrations; use codec::{Decode, Encode}; use frame_support::dispatch::{ @@ -84,7 +85,8 @@ use polymesh_primitives::settlement::{ VenueId, VenueType, }; use polymesh_primitives::{ - storage_migration_ver, Balance, IdentityId, Memo, NFTs, PortfolioId, SecondaryKey, WeightMeter, + storage_migrate_on, storage_migration_ver, Balance, IdentityId, Memo, NFTs, PortfolioId, + SecondaryKey, WeightMeter, }; type Identity = pallet_identity::Module; @@ -233,7 +235,7 @@ decl_error! { } } -storage_migration_ver!(2); +storage_migration_ver!(3); decl_storage! { trait Store for Module as Settlement { @@ -287,8 +289,6 @@ decl_storage! { VenueCounter get(fn venue_counter) build(|_| VenueId(1u64)): VenueId; /// Number of instructions in the system (It's one more than the actual number) InstructionCounter get(fn instruction_counter) build(|_| InstructionId(1u64)): InstructionId; - /// Storage version. - StorageVersion get(fn storage_version) build(|_| Version::new(2)): Version; /// Instruction memo pub InstructionMemos get(fn memo): map hasher(twox_64_concat) InstructionId => Option; /// Instruction statuses. instruction_id -> InstructionStatus @@ -305,6 +305,8 @@ decl_storage! { /// The status for the mediators affirmation. pub InstructionMediatorsAffirmations get(fn venue_mediators_affirmations): double_map hasher(twox_64_concat) InstructionId, hasher(identity) IdentityId => MediatorAffirmationStatus; + /// Storage version. + StorageVersion get(fn storage_version) build(|_| Version::new(3)): Version; } } @@ -320,6 +322,13 @@ decl_module! { fn deposit_event() = default; + fn on_runtime_upgrade() -> Weight { + storage_migrate_on!(StorageVersion, 3, { + migrations::migrate_to_v3::(); + }); + Weight::zero() + } + /// Registers a new venue. /// /// * `details` - Extra details about a venue diff --git a/pallets/settlement/src/migrations.rs b/pallets/settlement/src/migrations.rs new file mode 100644 index 0000000000..6840791d5b --- /dev/null +++ b/pallets/settlement/src/migrations.rs @@ -0,0 +1,133 @@ +use sp_runtime::runtime_logger::RuntimeLogger; +use sp_std::collections::btree_map::BTreeMap; + +use super::*; + +mod v2 { + use scale_info::TypeInfo; + + use super::*; + use polymesh_primitives::{NFTId, Ticker}; + + #[derive(Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo)] + pub enum Leg { + Fungible { + sender: PortfolioId, + receiver: PortfolioId, + ticker: Ticker, + amount: Balance, + }, + NonFungible { + sender: PortfolioId, + receiver: PortfolioId, + nfts: NFTs, + }, + OffChain { + sender_identity: IdentityId, + receiver_identity: IdentityId, + ticker: Ticker, + amount: Balance, + }, + } + + #[derive(Clone, Debug, Decode, Default, Encode, Eq, PartialEq, TypeInfo)] + pub struct NFTs { + pub ticker: Ticker, + pub ids: Vec, + } + + decl_storage! { + trait Store for Module as Settlement { + // This storage changed the Ticker key to AssetID. + pub(crate) VenueFiltering get(fn venue_filtering): + map hasher(blake2_128_concat) Ticker => bool; + + // This storage changed the Ticker key to AssetID. + pub(crate) VenueAllowList get(fn venue_allow_list): + double_map hasher(blake2_128_concat) Ticker, hasher(twox_64_concat) VenueId => bool; + + // This storage changed the Leg type. + pub(crate) InstructionLegs get(fn instruction_legs): + double_map hasher(twox_64_concat) InstructionId, hasher(twox_64_concat) LegId => Option; + + } + } + + decl_module! { + pub struct Module for enum Call where origin: T::RuntimeOrigin { } + } +} + +impl From for NFTs { + fn from(v2_nfts: v2::NFTs) -> NFTs { + NFTs::new_unverified(v2_nfts.ticker.into(), v2_nfts.ids) + } +} + +#[rustfmt::skip] +impl From for Leg { + fn from(v2_leg: v2::Leg) -> Leg { + match v2_leg { + v2::Leg::Fungible { sender, receiver, ticker, amount } => { + Leg::Fungible { + sender, + receiver, + asset_id: ticker.into(), + amount, + } + }, + v2::Leg::NonFungible { sender, receiver, nfts } => { + Leg::NonFungible { + sender, + receiver, + nfts: nfts.into(), + } + }, + v2::Leg::OffChain { sender_identity, receiver_identity, ticker, amount } => { + Leg::OffChain { + sender_identity, + receiver_identity, + ticker, + amount, + } + } + } + } +} + +pub(crate) fn migrate_to_v3() { + RuntimeLogger::init(); + let mut ticker_to_asset_id = BTreeMap::new(); + + // Removes all elements in the old storage and inserts it in the new storage + + let mut count = 0; + log::info!("Updating types for the VenueFiltering storage"); + v2::VenueFiltering::drain().for_each(|(ticker, v)| { + count += 1; + let asset_id = ticker_to_asset_id + .entry(ticker) + .or_insert(AssetID::from(ticker)); + VenueFiltering::insert(asset_id, v); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Updating types for the VenueAllowList storage"); + v2::VenueAllowList::drain().for_each(|(ticker, id, v)| { + count += 1; + let asset_id = ticker_to_asset_id + .entry(ticker) + .or_insert(AssetID::from(ticker)); + VenueAllowList::insert(asset_id, id, v); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Updating types for the InstructionLegs storage"); + v2::InstructionLegs::drain().for_each(|(instruction_id, leg_id, leg)| { + count += 1; + InstructionLegs::insert(instruction_id, leg_id, Leg::from(leg)); + }); + log::info!("{:?} items migrated", count); +} diff --git a/pallets/statistics/src/lib.rs b/pallets/statistics/src/lib.rs index 1400f7df06..77496c165c 100644 --- a/pallets/statistics/src/lib.rs +++ b/pallets/statistics/src/lib.rs @@ -17,6 +17,7 @@ #[cfg(feature = "runtime-benchmarks")] pub mod benchmarking; +mod migrations; use codec::{Decode, Encode}; use frame_support::dispatch::{DispatchError, DispatchResult}; @@ -33,12 +34,14 @@ use polymesh_primitives::statistics::{ use polymesh_primitives::transfer_compliance::{ AssetTransferCompliance, TransferCondition, TransferConditionExemptKey, TransferConditionResult, }; -use polymesh_primitives::{storage_migration_ver, Balance, IdentityId, WeightMeter}; +use polymesh_primitives::{ + storage_migrate_on, storage_migration_ver, Balance, IdentityId, WeightMeter, +}; type Identity = pallet_identity::Module; type ExternalAgents = pallet_external_agents::Module; -storage_migration_ver!(1); +storage_migration_ver!(3); decl_storage! { trait Store for Module as Statistics { @@ -48,9 +51,7 @@ decl_storage! { /// Asset stats. pub AssetStats get(fn asset_stats): - double_map - hasher(blake2_128_concat) Stat1stKey, - hasher(blake2_128_concat) Stat2ndKey => u128; + double_map hasher(blake2_128_concat) Stat1stKey, hasher(blake2_128_concat) Stat2ndKey => u128; /// The [`AssetTransferCompliance`] for each [`AssetID`]. pub AssetTransferCompliances get(fn asset_transfer_compliance): @@ -58,14 +59,10 @@ decl_storage! { /// Entities exempt from a Transfer Compliance rule. pub TransferConditionExemptEntities get(fn transfer_condition_exempt_entities): - double_map - hasher(blake2_128_concat) TransferConditionExemptKey, - hasher(blake2_128_concat) IdentityId - => - bool; + double_map hasher(blake2_128_concat) TransferConditionExemptKey, hasher(blake2_128_concat) IdentityId => bool; /// Storage migration version. - StorageVersion get(fn storage_version) build(|_| Version::new(1)): Version; + StorageVersion get(fn storage_version) build(|_| Version::new(3)): Version; } } @@ -73,11 +70,18 @@ decl_module! { pub struct Module for enum Call where origin: T::RuntimeOrigin { type Error = Error; + const MaxStatsPerAsset: u32 = T::MaxStatsPerAsset::get(); + const MaxTransferConditionsPerAsset: u32 = T::MaxTransferConditionsPerAsset::get(); + /// initialize the default event for this module fn deposit_event() = default; - const MaxStatsPerAsset: u32 = T::MaxStatsPerAsset::get(); - const MaxTransferConditionsPerAsset: u32 = T::MaxTransferConditionsPerAsset::get(); + fn on_runtime_upgrade() -> Weight { + storage_migrate_on!(StorageVersion, 3, { + migrations::migrate_to_v3::(); + }); + Weight::zero() + } /// Set the active asset stat_types. /// diff --git a/pallets/statistics/src/migrations.rs b/pallets/statistics/src/migrations.rs new file mode 100644 index 0000000000..d8af9e680e --- /dev/null +++ b/pallets/statistics/src/migrations.rs @@ -0,0 +1,143 @@ +use frame_support::BoundedBTreeSet; +use sp_runtime::runtime_logger::RuntimeLogger; + +use super::*; + +mod v2 { + use scale_info::TypeInfo; + + use super::*; + use polymesh_primitives::{ClaimType, Ticker}; + + #[derive(Decode, Encode, TypeInfo)] + #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] + pub enum AssetScope { + Ticker(Ticker), + } + + #[derive(Decode, Encode, TypeInfo)] + #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] + pub struct StatType { + pub op: StatOpType, + pub claim_issuer: Option<(ClaimType, IdentityId)>, + } + + #[derive(Decode, Encode, TypeInfo)] + #[derive(Copy, Clone, Debug, PartialEq, Eq)] + pub struct Stat1stKey { + pub asset: AssetScope, + pub stat_type: StatType, + } + + #[derive(Decode, Encode, TypeInfo)] + #[derive(Copy, Clone, Debug, PartialEq, Eq)] + pub struct TransferConditionExemptKey { + pub asset: AssetScope, + pub op: StatOpType, + pub claim_type: Option, + } + + decl_storage! { + trait Store for Module as Statistics { + // This storage changed the AssetScope type. + pub ActiveAssetStats get(fn active_asset_stats): + map hasher(blake2_128_concat) AssetScope => BoundedBTreeSet; + + // This storage changed the Stat1stKey type. + pub AssetStats get(fn asset_stats): + double_map hasher(blake2_128_concat) Stat1stKey, hasher(blake2_128_concat) Stat2ndKey => u128; + + // This storage changed the AssetScope type. + pub AssetTransferCompliances get(fn asset_transfer_compliance): + map hasher(blake2_128_concat) AssetScope => AssetTransferCompliance; + + // This storage changed the TransferConditionExemptKey type. + pub TransferConditionExemptEntities get(fn transfer_condition_exempt_entities): + double_map hasher(blake2_128_concat) TransferConditionExemptKey, hasher(blake2_128_concat) IdentityId => bool; + } + } + + decl_module! { + pub struct Module for enum Call where origin: T::RuntimeOrigin { } + } +} + +impl From for AssetID { + fn from(v2_asset_scope: v2::AssetScope) -> AssetID { + match v2_asset_scope { + v2::AssetScope::Ticker(ticker) => ticker.into(), + } + } +} + +impl From for StatType { + fn from(v2_stat_type: v2::StatType) -> StatType { + StatType { + operation_type: v2_stat_type.op, + claim_issuer: v2_stat_type.claim_issuer, + } + } +} + +impl From for Stat1stKey { + fn from(v2_stat1key: v2::Stat1stKey) -> Stat1stKey { + Stat1stKey { + asset_id: v2_stat1key.asset.into(), + stat_type: v2_stat1key.stat_type.into(), + } + } +} + +impl From for TransferConditionExemptKey { + fn from(v2_exempt_key: v2::TransferConditionExemptKey) -> TransferConditionExemptKey { + TransferConditionExemptKey { + asset_id: v2_exempt_key.asset.into(), + op: v2_exempt_key.op, + claim_type: v2_exempt_key.claim_type, + } + } +} + +pub(crate) fn migrate_to_v3() { + RuntimeLogger::init(); + + // Removes all elements in the old storage and inserts it in the new storage + + let mut count = 0; + log::info!("Updating types for the ActiveAssetStats storage"); + v2::ActiveAssetStats::::drain().for_each(|(scope, set)| { + count += 1; + let set: BTreeSet = set.into_iter().map(|v| v.into()).collect(); + let bounded_set = BoundedBTreeSet::try_from(set).unwrap_or_default(); + ActiveAssetStats::::insert(AssetID::from(scope), bounded_set); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Updating types for the AssetStats storage"); + v2::AssetStats::drain().for_each(|(stat1key, stat2key, v)| { + count += 1; + AssetStats::insert(Stat1stKey::from(stat1key), stat2key, v); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Updating types for the AssetTransferCompliances storage"); + v2::AssetTransferCompliances::::drain().for_each(|(scope, compliance)| { + count += 1; + AssetTransferCompliances::::insert(AssetID::from(scope), compliance); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Updating types for the TransferConditionExemptEntities storage"); + v2::TransferConditionExemptEntities::drain().for_each(|(exemption_key, did, exempt)| { + count += 1; + TransferConditionExemptEntities::insert( + TransferConditionExemptKey::from(exemption_key), + did, + exempt, + ); + }); + log::info!("{:?} items migrated", count); +} diff --git a/pallets/sto/Cargo.toml b/pallets/sto/Cargo.toml index 4bcf8b510f..0677995e31 100644 --- a/pallets/sto/Cargo.toml +++ b/pallets/sto/Cargo.toml @@ -20,6 +20,7 @@ polymesh-primitives-derive = { path = "../../primitives_derive", default-feature serde = { version = "1.0.104", default-features = false } serde_derive = { version = "1.0.104", optional = true, default-features = false } +log = "0.4.8" codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } scale-info = { version = "2.0", default-features = false, features = ["derive"] } diff --git a/pallets/sto/src/lib.rs b/pallets/sto/src/lib.rs index 28f908c5fe..2b0b37be22 100644 --- a/pallets/sto/src/lib.rs +++ b/pallets/sto/src/lib.rs @@ -25,6 +25,7 @@ #[cfg(feature = "runtime-benchmarks")] pub mod benchmarking; +mod migrations; use codec::{Decode, Encode}; use frame_support::dispatch::DispatchResult; @@ -44,7 +45,10 @@ use polymesh_common_utilities::with_transaction; use polymesh_primitives::asset::AssetID; use polymesh_primitives::impl_checked_inc; use polymesh_primitives::settlement::{Leg, ReceiptDetails, SettlementType, VenueId, VenueType}; -use polymesh_primitives::{Balance, EventDid, IdentityId, PortfolioId, WeightMeter}; +use polymesh_primitives::{ + storage_migrate_on, storage_migration_ver, Balance, EventDid, IdentityId, PortfolioId, + WeightMeter, +}; use polymesh_primitives_derive::VecU8StrongTyped; pub const MAX_TIERS: usize = 10; @@ -241,6 +245,8 @@ decl_error! { } } +storage_migration_ver!(1); + decl_storage! { trait Store for Module as Sto { /// All fundraisers that are currently running. @@ -263,6 +269,9 @@ decl_storage! { hasher(blake2_128_concat) AssetID, hasher(twox_64_concat) FundraiserId => Option; + + /// Storage migration version. + StorageVersion get(fn storage_version) build(|_| Version::new(1)): Version; } } @@ -272,6 +281,13 @@ decl_module! { fn deposit_event() = default; + fn on_runtime_upgrade() -> Weight { + storage_migrate_on!(StorageVersion, 1, { + migrations::migrate_to_v1::(); + }); + Weight::zero() + } + /// Create a new fundraiser. /// /// * `offering_portfolio` - Portfolio containing the `offering_asset`. diff --git a/pallets/sto/src/migrations.rs b/pallets/sto/src/migrations.rs new file mode 100644 index 0000000000..8987f7182a --- /dev/null +++ b/pallets/sto/src/migrations.rs @@ -0,0 +1,69 @@ +use sp_runtime::runtime_logger::RuntimeLogger; +use sp_std::collections::btree_map::BTreeMap; + +use super::*; + +mod v0 { + use super::*; + use polymesh_primitives::Ticker; + + decl_storage! { + trait Store for Module as Sto { + // This storage changed the Ticker key to AssetID. + pub(crate) Fundraisers get(fn fundraisers): + double_map hasher(blake2_128_concat) Ticker, hasher(twox_64_concat) FundraiserId => Option>; + + // This storage changed the Ticker key to AssetID. + pub(crate) FundraiserCount get(fn fundraiser_count): + map hasher(blake2_128_concat) Ticker => FundraiserId; + + // This storage changed the Ticker key to AssetID. + pub(crate) FundraiserNames get(fn fundraiser_name): + double_map hasher(blake2_128_concat) Ticker, hasher(twox_64_concat) FundraiserId => Option; + } + } + + decl_module! { + pub struct Module for enum Call where origin: T::RuntimeOrigin { } + } +} + +pub(crate) fn migrate_to_v1() { + RuntimeLogger::init(); + let mut ticker_to_asset_id = BTreeMap::new(); + + // Removes all elements in the old storage and inserts it in the new storage + + let mut count = 0; + log::info!("Updating types for the Fundraisers storage"); + v0::Fundraisers::::drain().for_each(|(ticker, id, fundraiser)| { + count += 1; + let asset_id = ticker_to_asset_id + .entry(ticker) + .or_insert(AssetID::from(ticker)); + Fundraisers::::insert(asset_id, id, fundraiser); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Updating types for the FundraiserCount storage"); + v0::FundraiserCount::drain().for_each(|(ticker, id)| { + count += 1; + let asset_id = ticker_to_asset_id + .entry(ticker) + .or_insert(AssetID::from(ticker)); + FundraiserCount::insert(asset_id, id); + }); + log::info!("{:?} items migrated", count); + + let mut count = 0; + log::info!("Updating types for the FundraiserNames storage"); + v0::FundraiserNames::drain().for_each(|(ticker, id, name)| { + count += 1; + let asset_id = ticker_to_asset_id + .entry(ticker) + .or_insert(AssetID::from(ticker)); + FundraiserNames::insert(asset_id, id, name); + }); + log::info!("{:?} items migrated", count); +} diff --git a/primitives/src/asset.rs b/primitives/src/asset.rs index 34150308f0..0ead88378e 100644 --- a/primitives/src/asset.rs +++ b/primitives/src/asset.rs @@ -17,11 +17,13 @@ use sp_runtime::{Deserialize, Serialize}; use codec::{Decode, Encode}; -use polymesh_primitives_derive::VecU8StrongTyped; use scale_info::TypeInfo; +use sp_io::hashing::blake2_128; use sp_std::prelude::Vec; use crate::impl_checked_inc; +use crate::ticker::Ticker; +use polymesh_primitives_derive::VecU8StrongTyped; /// An unique asset identifier. #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] @@ -47,6 +49,12 @@ impl AssetID { } } +impl From for AssetID { + fn from(ticker: Ticker) -> AssetID { + blake2_128(&(b"legacy_ticker", ticker).encode()).into() + } +} + /// A per-asset checkpoint ID. #[derive(Encode, Decode, TypeInfo)] #[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]