From cce2910cf860c6537bedaa54661aaba15d43f203 Mon Sep 17 00:00:00 2001 From: Uncle Stinky Date: Fri, 20 Feb 2026 17:15:53 +0300 Subject: [PATCH] make networks pallet to be indexed storage map; migrations included Signed-off-by: Uncle Stinky --- pallets/networks/Cargo.toml | 2 + pallets/networks/src/lib.rs | 42 +++++++++++++++ pallets/networks/src/migrations/mod.rs | 9 ++++ pallets/networks/src/migrations/v1.rs | 41 +++++++++++++++ pallets/networks/src/mock.rs | 1 + pallets/networks/src/tests.rs | 73 +++++++++++++++++++++++++- 6 files changed, 167 insertions(+), 1 deletion(-) create mode 100644 pallets/networks/src/migrations/mod.rs create mode 100644 pallets/networks/src/migrations/v1.rs diff --git a/pallets/networks/Cargo.toml b/pallets/networks/Cargo.toml index 64f6d90..ca9a189 100644 --- a/pallets/networks/Cargo.toml +++ b/pallets/networks/Cargo.toml @@ -11,6 +11,7 @@ repository.workspace = true scale-info = { workspace = true, features = ["derive"] } codec = { workspace = true, features = ["max-encoded-len"] } num-traits = { workspace = true } +log = { workspace = true } frame-benchmarking = { workspace = true, optional = true } frame-support = { workspace = true } @@ -31,6 +32,7 @@ default = ["std"] std = [ "scale-info/std", "codec/std", + "log/std", "num-traits/std", "frame-support/std", "frame-system/std", diff --git a/pallets/networks/src/lib.rs b/pallets/networks/src/lib.rs index 344df73..5cf532b 100644 --- a/pallets/networks/src/lib.rs +++ b/pallets/networks/src/lib.rs @@ -22,6 +22,7 @@ pub use ghost_traits::networks::{ }; mod math; +pub mod migrations; mod weights; pub use crate::weights::WeightInfo; @@ -35,6 +36,8 @@ mod mock; #[cfg(all(feature = "std", test))] mod tests; +const LOG_TARGET: &str = "runtime::ghost-networks"; + pub type BalanceOf = <::Currency as Inspect<::AccountId>>::Balance; @@ -123,6 +126,8 @@ where pub mod module { use super::*; + const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); + #[pallet::config] pub trait Config: frame_system::Config { type RuntimeEvent: From> + IsType<::RuntimeEvent>; @@ -149,6 +154,9 @@ pub mod module { /// The origin required to remove network. type RemoveOrigin: EnsureOrigin; + #[pallet::constant] + type MaxNetworks: Get; + /// Weight information for extrinsics in this module. type WeightInfo: WeightInfo; } @@ -163,6 +171,8 @@ pub mod module { WrongGatekeeperAddress, /// Topic name length not 66 or prefix `0x` missed. WrongTopicName, + /// Could not store networks into bounded vector. + TooManyNetworks, } #[pallet::event] @@ -243,6 +253,11 @@ pub mod module { #[pallet::getter(fn accumulated_commission)] pub type AccumulatedCommission = StorageValue<_, BalanceOf, ValueQuery>; + #[pallet::storage] + #[pallet::getter(fn network_indexes)] + pub type NetworkIndexes = + StorageValue<_, BoundedVec, ValueQuery>; + #[pallet::storage] #[pallet::getter(fn networks)] pub type Networks = @@ -282,6 +297,7 @@ pub mod module { } #[pallet::pallet] + #[pallet::storage_version(STORAGE_VERSION)] #[pallet::without_storage_info] pub struct Pallet(PhantomData); @@ -470,12 +486,19 @@ impl Pallet { Ok(()) })?; + NetworkIndexes::::try_mutate(|ids| -> DispatchResult { + ids.try_push(chain_id) + .map_err(|_| Error::::TooManyNetworks)?; + Ok(()) + })?; + Self::deposit_event(Event::::NetworkRegistered { chain_id, network }); Ok(()) } /// Remove existent network. pub fn do_remove_network(chain_id: T::NetworkId) -> DispatchResult { + NetworkIndexes::::mutate(|ids| ids.retain(|id| id != &chain_id)); Networks::::try_mutate(&chain_id, |maybe_network| -> DispatchResult { ensure!(maybe_network.is_some(), Error::::NetworkDoesNotExist); *maybe_network = None; @@ -723,10 +746,29 @@ impl NetworkDataBasicHandler for Pallet { } impl NetworkDataInspectHandler for Pallet { + fn count() -> u32 { + NetworkIndexes::::get().len() as u32 + } + fn get(n: &Self::NetworkId) -> Option { Networks::::get(n) } + fn next_network_for_block( + block_number: impl Into, + ) -> Option<(Self::NetworkId, NetworkData)> { + let network_indexes = NetworkIndexes::::get(); + block_number + .into() + .checked_rem(network_indexes.len()) + .map(|id| { + network_indexes.get(id).copied().and_then(|network_id| { + Self::get(&network_id).map(|network_data| (network_id, network_data)) + }) + }) + .flatten() + } + fn iter() -> PrefixIterator<(Self::NetworkId, NetworkData)> { Networks::::iter() } diff --git a/pallets/networks/src/migrations/mod.rs b/pallets/networks/src/migrations/mod.rs new file mode 100644 index 0000000..c39ae32 --- /dev/null +++ b/pallets/networks/src/migrations/mod.rs @@ -0,0 +1,9 @@ +pub mod v1; + +pub type MigrateV0ToV1 = frame_support::migrations::VersionedMigration< + 0, + 1, + v1::StoreNetworkIdsIntoBoundedVec, + crate::Pallet, + ::DbWeight, +>; diff --git a/pallets/networks/src/migrations/v1.rs b/pallets/networks/src/migrations/v1.rs new file mode 100644 index 0000000..baa35a2 --- /dev/null +++ b/pallets/networks/src/migrations/v1.rs @@ -0,0 +1,41 @@ +use frame_support::{ + traits::{Get, UncheckedOnRuntimeUpgrade}, + weights::Weight, +}; +use sp_std::marker::PhantomData; + +use crate::{BoundedVec, Config, NetworkIndexes, Networks, Vec, LOG_TARGET}; + +pub struct StoreNetworkIdsIntoBoundedVec(PhantomData); +impl UncheckedOnRuntimeUpgrade for StoreNetworkIdsIntoBoundedVec { + fn on_runtime_upgrade() -> Weight { + let mut weight = T::DbWeight::get().reads(1); + + let network_ids: Vec = Networks::::iter_keys().collect(); + let networks_count = network_ids.len(); + + weight = weight.saturating_add(T::DbWeight::get().reads(networks_count as u64)); + + let writes = BoundedVec::::try_from(network_ids) + .inspect_err(|err| { + log::error!( + target: LOG_TARGET, + "⛓️ Network ids to bounded_vec migration failed: {:?}", + err, + ) + }) + .ok() + .map(|bounded_networks| { + NetworkIndexes::::put(bounded_networks); + log::info!( + target: LOG_TARGET, + "⛓️ Network ids to bounded_vec migration success: {} networks moved", + networks_count, + ); + 1u64 + }) + .unwrap_or_default(); + + weight.saturating_add(T::DbWeight::get().writes(writes)) + } +} diff --git a/pallets/networks/src/mock.rs b/pallets/networks/src/mock.rs index d4e106e..951d286 100644 --- a/pallets/networks/src/mock.rs +++ b/pallets/networks/src/mock.rs @@ -98,6 +98,7 @@ impl ghost_networks::Config for Test { type RegisterOrigin = EnsureSignedBy; type UpdateOrigin = EnsureSignedBy; type RemoveOrigin = EnsureSignedBy; + type MaxNetworks = ConstU32<3>; type WeightInfo = (); } diff --git a/pallets/networks/src/tests.rs b/pallets/networks/src/tests.rs index cb52bde..c4c86ab 100644 --- a/pallets/networks/src/tests.rs +++ b/pallets/networks/src/tests.rs @@ -36,7 +36,8 @@ fn register_and_check_network(chain_id: u32, network: NetworkData) { chain_id, network.clone() )); - assert_eq!(Networks::::get(chain_id), Some(network.clone())); + assert_eq!(Networks::::get(chain_id), Some(network)); + assert_eq!(NetworkIndexes::::get(), vec![chain_id]); } #[test] @@ -44,6 +45,7 @@ fn could_add_network_from_authority() { ExtBuilder::build().execute_with(|| { let (chain_id, network) = prepare_network_data(); assert_eq!(Networks::::get(chain_id), None); + assert_eq!(NetworkIndexes::::get(), vec![]); assert_ok!(GhostNetworks::register_network( RuntimeOrigin::signed(RegistererAccount::get()), chain_id, @@ -56,6 +58,7 @@ fn could_add_network_from_authority() { }, )); assert_eq!(Networks::::get(chain_id), Some(network)); + assert_eq!(NetworkIndexes::::get(), vec![chain_id]); }); } @@ -64,6 +67,7 @@ fn could_not_add_network_from_random_account() { ExtBuilder::build().execute_with(|| { let (chain_id, network) = prepare_network_data(); assert_eq!(Networks::::get(chain_id), None); + assert_eq!(NetworkIndexes::::get(), vec![]); assert_err!( GhostNetworks::register_network( RuntimeOrigin::signed(RandomAccount::get()), @@ -89,6 +93,7 @@ fn could_not_add_network_from_random_account() { DispatchError::BadOrigin ); assert_eq!(Networks::::get(chain_id), None); + assert_eq!(NetworkIndexes::::get(), vec![]); }); } @@ -1055,6 +1060,7 @@ fn could_remove_network_from_authority_account() { chain_id, )); assert_eq!(Networks::::get(chain_id), None); + assert_eq!(NetworkIndexes::::get(), vec![]); }); } @@ -1079,6 +1085,7 @@ fn could_not_remove_network_from_random_account() { DispatchError::BadOrigin ); assert_eq!(Networks::::get(chain_id), Some(network)); + assert_eq!(NetworkIndexes::::get(), vec![chain_id]); }); } @@ -1092,6 +1099,7 @@ fn could_not_remove_non_existent_network() { crate::Error::::NetworkDoesNotExist ); assert_eq!(Networks::::get(chain_id), None); + assert_eq!(NetworkIndexes::::get(), vec![]); }); } @@ -1898,3 +1906,66 @@ fn check_bridged_inflation_curve_for_big_commissions() { } }); } + +#[test] +fn migration_from_v0_to_v1_works() { + ExtBuilder::build().execute_with(|| { + let (chain_id, network) = prepare_network_data(); + let other_chain_id = chain_id.saturating_add(1); + + assert_eq!(Networks::::get(chain_id), None); + assert_ok!(GhostNetworks::register_network( + RuntimeOrigin::signed(RegistererAccount::get()), + chain_id, + network.clone(), + )); + assert_ok!(GhostNetworks::register_network( + RuntimeOrigin::signed(RegistererAccount::get()), + other_chain_id, + network.clone(), + )); + NetworkIndexes::::kill(); + + assert_eq!(Networks::::get(chain_id), Some(network.clone())); + assert_eq!(Networks::::get(other_chain_id), Some(network)); + assert_eq!(NetworkIndexes::::get(), vec![]); + + type Migrate = crate::migrations::MigrateV0ToV1; + ::on_runtime_upgrade(); + assert_eq!( + NetworkIndexes::::get(), + vec![chain_id, other_chain_id] + ); + }); +} + +#[test] +fn migration_from_v0_to_v1_does_not_run_twice() { + ExtBuilder::build().execute_with(|| { + StorageVersion::new(1).put::(); + + let (chain_id, network) = prepare_network_data(); + let other_chain_id = chain_id.saturating_add(1); + + assert_eq!(Networks::::get(chain_id), None); + assert_ok!(GhostNetworks::register_network( + RuntimeOrigin::signed(RegistererAccount::get()), + chain_id, + network.clone(), + )); + assert_ok!(GhostNetworks::register_network( + RuntimeOrigin::signed(RegistererAccount::get()), + other_chain_id, + network.clone(), + )); + NetworkIndexes::::kill(); + + assert_eq!(Networks::::get(chain_id), Some(network.clone())); + assert_eq!(Networks::::get(other_chain_id), Some(network)); + assert_eq!(NetworkIndexes::::get(), vec![]); + + type Migrate = crate::migrations::MigrateV0ToV1; + ::on_runtime_upgrade(); + assert_eq!(NetworkIndexes::::get(), vec![]); + }); +}