make networks pallet to be indexed storage map; migrations included

Signed-off-by: Uncle Stinky <uncle.stinky@ghostchain.io>
This commit is contained in:
Uncle Stinky 2026-02-20 17:15:53 +03:00
parent 5b5e53e6fd
commit cce2910cf8
Signed by: st1nky
GPG Key ID: 016064BD97603B40
6 changed files with 167 additions and 1 deletions

View File

@ -11,6 +11,7 @@ repository.workspace = true
scale-info = { workspace = true, features = ["derive"] } scale-info = { workspace = true, features = ["derive"] }
codec = { workspace = true, features = ["max-encoded-len"] } codec = { workspace = true, features = ["max-encoded-len"] }
num-traits = { workspace = true } num-traits = { workspace = true }
log = { workspace = true }
frame-benchmarking = { workspace = true, optional = true } frame-benchmarking = { workspace = true, optional = true }
frame-support = { workspace = true } frame-support = { workspace = true }
@ -31,6 +32,7 @@ default = ["std"]
std = [ std = [
"scale-info/std", "scale-info/std",
"codec/std", "codec/std",
"log/std",
"num-traits/std", "num-traits/std",
"frame-support/std", "frame-support/std",
"frame-system/std", "frame-system/std",

View File

@ -22,6 +22,7 @@ pub use ghost_traits::networks::{
}; };
mod math; mod math;
pub mod migrations;
mod weights; mod weights;
pub use crate::weights::WeightInfo; pub use crate::weights::WeightInfo;
@ -35,6 +36,8 @@ mod mock;
#[cfg(all(feature = "std", test))] #[cfg(all(feature = "std", test))]
mod tests; mod tests;
const LOG_TARGET: &str = "runtime::ghost-networks";
pub type BalanceOf<T> = pub type BalanceOf<T> =
<<T as Config>::Currency as Inspect<<T as frame_system::Config>::AccountId>>::Balance; <<T as Config>::Currency as Inspect<<T as frame_system::Config>::AccountId>>::Balance;
@ -123,6 +126,8 @@ where
pub mod module { pub mod module {
use super::*; use super::*;
const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
#[pallet::config] #[pallet::config]
pub trait Config: frame_system::Config { pub trait Config: frame_system::Config {
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>; type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
@ -149,6 +154,9 @@ pub mod module {
/// The origin required to remove network. /// The origin required to remove network.
type RemoveOrigin: EnsureOrigin<Self::RuntimeOrigin>; type RemoveOrigin: EnsureOrigin<Self::RuntimeOrigin>;
#[pallet::constant]
type MaxNetworks: Get<u32>;
/// Weight information for extrinsics in this module. /// Weight information for extrinsics in this module.
type WeightInfo: WeightInfo; type WeightInfo: WeightInfo;
} }
@ -163,6 +171,8 @@ pub mod module {
WrongGatekeeperAddress, WrongGatekeeperAddress,
/// Topic name length not 66 or prefix `0x` missed. /// Topic name length not 66 or prefix `0x` missed.
WrongTopicName, WrongTopicName,
/// Could not store networks into bounded vector.
TooManyNetworks,
} }
#[pallet::event] #[pallet::event]
@ -243,6 +253,11 @@ pub mod module {
#[pallet::getter(fn accumulated_commission)] #[pallet::getter(fn accumulated_commission)]
pub type AccumulatedCommission<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>; pub type AccumulatedCommission<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>;
#[pallet::storage]
#[pallet::getter(fn network_indexes)]
pub type NetworkIndexes<T: Config> =
StorageValue<_, BoundedVec<T::NetworkId, T::MaxNetworks>, ValueQuery>;
#[pallet::storage] #[pallet::storage]
#[pallet::getter(fn networks)] #[pallet::getter(fn networks)]
pub type Networks<T: Config> = pub type Networks<T: Config> =
@ -282,6 +297,7 @@ pub mod module {
} }
#[pallet::pallet] #[pallet::pallet]
#[pallet::storage_version(STORAGE_VERSION)]
#[pallet::without_storage_info] #[pallet::without_storage_info]
pub struct Pallet<T>(PhantomData<T>); pub struct Pallet<T>(PhantomData<T>);
@ -470,12 +486,19 @@ impl<T: Config> Pallet<T> {
Ok(()) Ok(())
})?; })?;
NetworkIndexes::<T>::try_mutate(|ids| -> DispatchResult {
ids.try_push(chain_id)
.map_err(|_| Error::<T>::TooManyNetworks)?;
Ok(())
})?;
Self::deposit_event(Event::<T>::NetworkRegistered { chain_id, network }); Self::deposit_event(Event::<T>::NetworkRegistered { chain_id, network });
Ok(()) Ok(())
} }
/// Remove existent network. /// Remove existent network.
pub fn do_remove_network(chain_id: T::NetworkId) -> DispatchResult { pub fn do_remove_network(chain_id: T::NetworkId) -> DispatchResult {
NetworkIndexes::<T>::mutate(|ids| ids.retain(|id| id != &chain_id));
Networks::<T>::try_mutate(&chain_id, |maybe_network| -> DispatchResult { Networks::<T>::try_mutate(&chain_id, |maybe_network| -> DispatchResult {
ensure!(maybe_network.is_some(), Error::<T>::NetworkDoesNotExist); ensure!(maybe_network.is_some(), Error::<T>::NetworkDoesNotExist);
*maybe_network = None; *maybe_network = None;
@ -723,10 +746,29 @@ impl<T: Config> NetworkDataBasicHandler for Pallet<T> {
} }
impl<T: Config> NetworkDataInspectHandler<NetworkData> for Pallet<T> { impl<T: Config> NetworkDataInspectHandler<NetworkData> for Pallet<T> {
fn count() -> u32 {
NetworkIndexes::<T>::get().len() as u32
}
fn get(n: &Self::NetworkId) -> Option<NetworkData> { fn get(n: &Self::NetworkId) -> Option<NetworkData> {
Networks::<T>::get(n) Networks::<T>::get(n)
} }
fn next_network_for_block(
block_number: impl Into<usize>,
) -> Option<(Self::NetworkId, NetworkData)> {
let network_indexes = NetworkIndexes::<T>::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)> { fn iter() -> PrefixIterator<(Self::NetworkId, NetworkData)> {
Networks::<T>::iter() Networks::<T>::iter()
} }

View File

@ -0,0 +1,9 @@
pub mod v1;
pub type MigrateV0ToV1<T> = frame_support::migrations::VersionedMigration<
0,
1,
v1::StoreNetworkIdsIntoBoundedVec<T>,
crate::Pallet<T>,
<T as frame_system::Config>::DbWeight,
>;

View File

@ -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<T>(PhantomData<T>);
impl<T: Config> UncheckedOnRuntimeUpgrade for StoreNetworkIdsIntoBoundedVec<T> {
fn on_runtime_upgrade() -> Weight {
let mut weight = T::DbWeight::get().reads(1);
let network_ids: Vec<T::NetworkId> = Networks::<T>::iter_keys().collect();
let networks_count = network_ids.len();
weight = weight.saturating_add(T::DbWeight::get().reads(networks_count as u64));
let writes = BoundedVec::<T::NetworkId, T::MaxNetworks>::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::<T>::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))
}
}

View File

@ -98,6 +98,7 @@ impl ghost_networks::Config for Test {
type RegisterOrigin = EnsureSignedBy<RegistererAccount, AccountId>; type RegisterOrigin = EnsureSignedBy<RegistererAccount, AccountId>;
type UpdateOrigin = EnsureSignedBy<UpdaterAccount, AccountId>; type UpdateOrigin = EnsureSignedBy<UpdaterAccount, AccountId>;
type RemoveOrigin = EnsureSignedBy<RemoverAccount, AccountId>; type RemoveOrigin = EnsureSignedBy<RemoverAccount, AccountId>;
type MaxNetworks = ConstU32<3>;
type WeightInfo = (); type WeightInfo = ();
} }

View File

@ -36,7 +36,8 @@ fn register_and_check_network(chain_id: u32, network: NetworkData) {
chain_id, chain_id,
network.clone() network.clone()
)); ));
assert_eq!(Networks::<Test>::get(chain_id), Some(network.clone())); assert_eq!(Networks::<Test>::get(chain_id), Some(network));
assert_eq!(NetworkIndexes::<Test>::get(), vec![chain_id]);
} }
#[test] #[test]
@ -44,6 +45,7 @@ fn could_add_network_from_authority() {
ExtBuilder::build().execute_with(|| { ExtBuilder::build().execute_with(|| {
let (chain_id, network) = prepare_network_data(); let (chain_id, network) = prepare_network_data();
assert_eq!(Networks::<Test>::get(chain_id), None); assert_eq!(Networks::<Test>::get(chain_id), None);
assert_eq!(NetworkIndexes::<Test>::get(), vec![]);
assert_ok!(GhostNetworks::register_network( assert_ok!(GhostNetworks::register_network(
RuntimeOrigin::signed(RegistererAccount::get()), RuntimeOrigin::signed(RegistererAccount::get()),
chain_id, chain_id,
@ -56,6 +58,7 @@ fn could_add_network_from_authority() {
}, },
)); ));
assert_eq!(Networks::<Test>::get(chain_id), Some(network)); assert_eq!(Networks::<Test>::get(chain_id), Some(network));
assert_eq!(NetworkIndexes::<Test>::get(), vec![chain_id]);
}); });
} }
@ -64,6 +67,7 @@ fn could_not_add_network_from_random_account() {
ExtBuilder::build().execute_with(|| { ExtBuilder::build().execute_with(|| {
let (chain_id, network) = prepare_network_data(); let (chain_id, network) = prepare_network_data();
assert_eq!(Networks::<Test>::get(chain_id), None); assert_eq!(Networks::<Test>::get(chain_id), None);
assert_eq!(NetworkIndexes::<Test>::get(), vec![]);
assert_err!( assert_err!(
GhostNetworks::register_network( GhostNetworks::register_network(
RuntimeOrigin::signed(RandomAccount::get()), RuntimeOrigin::signed(RandomAccount::get()),
@ -89,6 +93,7 @@ fn could_not_add_network_from_random_account() {
DispatchError::BadOrigin DispatchError::BadOrigin
); );
assert_eq!(Networks::<Test>::get(chain_id), None); assert_eq!(Networks::<Test>::get(chain_id), None);
assert_eq!(NetworkIndexes::<Test>::get(), vec![]);
}); });
} }
@ -1055,6 +1060,7 @@ fn could_remove_network_from_authority_account() {
chain_id, chain_id,
)); ));
assert_eq!(Networks::<Test>::get(chain_id), None); assert_eq!(Networks::<Test>::get(chain_id), None);
assert_eq!(NetworkIndexes::<Test>::get(), vec![]);
}); });
} }
@ -1079,6 +1085,7 @@ fn could_not_remove_network_from_random_account() {
DispatchError::BadOrigin DispatchError::BadOrigin
); );
assert_eq!(Networks::<Test>::get(chain_id), Some(network)); assert_eq!(Networks::<Test>::get(chain_id), Some(network));
assert_eq!(NetworkIndexes::<Test>::get(), vec![chain_id]);
}); });
} }
@ -1092,6 +1099,7 @@ fn could_not_remove_non_existent_network() {
crate::Error::<Test>::NetworkDoesNotExist crate::Error::<Test>::NetworkDoesNotExist
); );
assert_eq!(Networks::<Test>::get(chain_id), None); assert_eq!(Networks::<Test>::get(chain_id), None);
assert_eq!(NetworkIndexes::<Test>::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::<Test>::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::<Test>::kill();
assert_eq!(Networks::<Test>::get(chain_id), Some(network.clone()));
assert_eq!(Networks::<Test>::get(other_chain_id), Some(network));
assert_eq!(NetworkIndexes::<Test>::get(), vec![]);
type Migrate = crate::migrations::MigrateV0ToV1<Test>;
<Migrate as frame_support::traits::OnRuntimeUpgrade>::on_runtime_upgrade();
assert_eq!(
NetworkIndexes::<Test>::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::<GhostNetworks>();
let (chain_id, network) = prepare_network_data();
let other_chain_id = chain_id.saturating_add(1);
assert_eq!(Networks::<Test>::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::<Test>::kill();
assert_eq!(Networks::<Test>::get(chain_id), Some(network.clone()));
assert_eq!(Networks::<Test>::get(other_chain_id), Some(network));
assert_eq!(NetworkIndexes::<Test>::get(), vec![]);
type Migrate = crate::migrations::MigrateV0ToV1<Test>;
<Migrate as frame_support::traits::OnRuntimeUpgrade>::on_runtime_upgrade();
assert_eq!(NetworkIndexes::<Test>::get(), vec![]);
});
}