From 11191abe385c9e74a6f179fc1df0e37c5163e9fe Mon Sep 17 00:00:00 2001 From: brettkolodny <brettkolodny@gmail.com> Date: Thu, 20 May 2021 10:31:59 -0400 Subject: [PATCH] Add BoundedVec to gradually-update (#498) * Converted Vec storage to BoundedVec * Test for exceeding max gradually updates * Converted storage key and value to BoundedVec --- gradually-update/src/lib.rs | 66 ++++++---- gradually-update/src/mock.rs | 6 + gradually-update/src/tests.rs | 226 ++++++++++++++++++++++------------ 3 files changed, 198 insertions(+), 100 deletions(-) diff --git a/gradually-update/src/lib.rs b/gradually-update/src/lib.rs index 9479e76..f7eaafc 100644 --- a/gradually-update/src/lib.rs +++ b/gradually-update/src/lib.rs @@ -25,11 +25,12 @@ use frame_support::{ ensure, pallet_prelude::*, storage, - traits::{EnsureOrigin, Get}, + traits::{EnsureOrigin, Get, MaxEncodedLen}, + BoundedVec, }; use frame_system::pallet_prelude::*; use sp_runtime::{traits::SaturatedConversion, DispatchResult, RuntimeDebug}; -use sp_std::prelude::Vec; +use sp_std::convert::TryInto; mod default_weight; mod mock; @@ -38,14 +39,11 @@ mod tests; /// Gradually update a value stored at `key` to `target_value`, /// change `per_block` * `T::UpdateFrequency` per `T::UpdateFrequency` /// blocks. -#[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebug)] -pub struct GraduallyUpdate { - /// The storage key of the value to update - pub key: StorageKeyBytes, - /// The target value - pub target_value: StorageValueBytes, - /// The amount of the value to update per one block - pub per_block: StorageValueBytes, +#[derive(Encode, Decode, Clone, Eq, PartialEq, MaxEncodedLen, RuntimeDebug)] +pub struct GraduallyUpdate<Key, Value> { + pub key: Key, + pub target_value: Value, + pub per_block: Value, } pub use module::*; @@ -60,8 +58,10 @@ pub mod module { fn on_finalize(u: u32) -> Weight; } - pub(crate) type StorageKeyBytes = Vec<u8>; - pub(crate) type StorageValueBytes = Vec<u8>; + pub(crate) type StorageKeyBytes<T> = BoundedVec<u8, <T as Config>::MaxStorageKeyBytes>; + pub(crate) type StorageValueBytes<T> = BoundedVec<u8, <T as Config>::MaxStorageValueBytes>; + + type GraduallyUpdateOf<T> = GraduallyUpdate<StorageKeyBytes<T>, StorageValueBytes<T>>; #[pallet::config] pub trait Config: frame_system::Config { @@ -76,6 +76,15 @@ pub mod module { /// Weight information for extrinsics in this module. type WeightInfo: WeightInfo; + + /// Maximum active gradual updates + type MaxGraduallyUpdate: Get<u32>; + + /// Maximum size of storage key + type MaxStorageKeyBytes: Get<u32>; + + /// Maximum size of storage value + type MaxStorageValueBytes: Get<u32>; } #[pallet::error] @@ -88,23 +97,30 @@ pub mod module { GraduallyUpdateHasExisted, /// No update exists to cancel. GraduallyUpdateNotFound, + /// Maximum updates exceeded + MaxGraduallyUpdateExceeded, + /// Maximum key size exceeded + MaxStorageKeyBytesExceeded, + /// Maximum value size exceeded + MaxStorageValueBytesExceeded, } #[pallet::event] #[pallet::generate_deposit(pub(crate) fn deposit_event)] pub enum Event<T: Config> { /// Gradually update added. [key, per_block, target_value] - GraduallyUpdateAdded(StorageKeyBytes, StorageValueBytes, StorageValueBytes), + GraduallyUpdateAdded(StorageKeyBytes<T>, StorageValueBytes<T>, StorageValueBytes<T>), /// Gradually update cancelled. [key] - GraduallyUpdateCancelled(StorageKeyBytes), + GraduallyUpdateCancelled(StorageKeyBytes<T>), /// Gradually update applied. [block_number, key, target_value] - Updated(T::BlockNumber, StorageKeyBytes, StorageValueBytes), + Updated(T::BlockNumber, StorageKeyBytes<T>, StorageValueBytes<T>), } /// All the on-going updates #[pallet::storage] #[pallet::getter(fn gradually_updates)] - pub(crate) type GraduallyUpdates<T: Config> = StorageValue<_, Vec<GraduallyUpdate>, ValueQuery>; + pub(crate) type GraduallyUpdates<T: Config> = + StorageValue<_, BoundedVec<GraduallyUpdateOf<T>, T::MaxGraduallyUpdate>, ValueQuery>; /// The last updated block number #[pallet::storage] @@ -135,7 +151,7 @@ pub mod module { impl<T: Config> Pallet<T> { /// Add gradually_update to adjust numeric parameter. #[pallet::weight(T::WeightInfo::gradually_update())] - pub fn gradually_update(origin: OriginFor<T>, update: GraduallyUpdate) -> DispatchResultWithPostInfo { + pub fn gradually_update(origin: OriginFor<T>, update: GraduallyUpdateOf<T>) -> DispatchResultWithPostInfo { T::DispatchOrigin::try_origin(origin).map(|_| ()).or_else(ensure_root)?; // Support max value is u128, ensure per_block and target_value <= 16 bytes. @@ -145,7 +161,7 @@ pub mod module { ); if storage::unhashed::exists(&update.key) { - let current_value = storage::unhashed::get::<StorageValueBytes>(&update.key).unwrap(); + let current_value = storage::unhashed::get::<StorageValueBytes<T>>(&update.key).unwrap(); ensure!( current_value.len() == update.target_value.len(), Error::<T>::InvalidTargetValue @@ -158,7 +174,9 @@ pub mod module { Error::<T>::GraduallyUpdateHasExisted ); - gradually_updates.push(update.clone()); + gradually_updates + .try_push(update.clone()) + .map_err(|_| Error::<T>::MaxGraduallyUpdateExceeded)?; Ok(()) })?; @@ -173,7 +191,7 @@ pub mod module { /// Cancel gradually_update to adjust numeric parameter. #[pallet::weight(T::WeightInfo::cancel_gradually_update())] - pub fn cancel_gradually_update(origin: OriginFor<T>, key: StorageKeyBytes) -> DispatchResultWithPostInfo { + pub fn cancel_gradually_update(origin: OriginFor<T>, key: StorageKeyBytes<T>) -> DispatchResultWithPostInfo { T::DispatchOrigin::try_origin(origin).map(|_| ()).or_else(ensure_root)?; GraduallyUpdates::<T>::try_mutate(|gradually_updates| -> DispatchResult { @@ -206,7 +224,7 @@ impl<T: Config> Pallet<T> { gradually_updates.retain(|update| { let mut keep = true; - let current_value = storage::unhashed::get::<StorageValueBytes>(&update.key).unwrap_or_default(); + let current_value = storage::unhashed::get::<StorageValueBytes<T>>(&update.key).unwrap_or_default(); let current_value_u128 = u128::from_le_bytes(Self::convert_vec_to_u8(¤t_value)); let frequency_u128: u128 = T::UpdateFrequency::get().saturated_into(); @@ -232,7 +250,9 @@ impl<T: Config> Pallet<T> { storage::unhashed::put(&update.key, &value); - Self::deposit_event(Event::Updated(now, update.key.clone(), value)); + let bounded_value: StorageValueBytes<T> = value.to_vec().try_into().unwrap(); + + Self::deposit_event(Event::Updated(now, update.key.clone(), bounded_value)); keep }); @@ -246,7 +266,7 @@ impl<T: Config> Pallet<T> { } #[allow(clippy::ptr_arg)] - fn convert_vec_to_u8(input: &StorageValueBytes) -> [u8; 16] { + fn convert_vec_to_u8(input: &StorageValueBytes<T>) -> [u8; 16] { let mut array: [u8; 16] = [0; 16]; for (i, v) in input.iter().enumerate() { array[i] = *v; diff --git a/gradually-update/src/mock.rs b/gradually-update/src/mock.rs index 410cb93..2224255 100644 --- a/gradually-update/src/mock.rs +++ b/gradually-update/src/mock.rs @@ -44,6 +44,9 @@ impl frame_system::Config for Runtime { parameter_types! { pub const UpdateFrequency: BlockNumber = 10; + pub MaxGraduallyUpdate: u32 = 3; + pub MaxStorageKeyBytes: u32 = 100_000; + pub MaxStorageValueBytes: u32 = 100_000; } impl Config for Runtime { @@ -51,6 +54,9 @@ impl Config for Runtime { type UpdateFrequency = UpdateFrequency; type DispatchOrigin = frame_system::EnsureRoot<AccountId>; type WeightInfo = (); + type MaxGraduallyUpdate = MaxGraduallyUpdate; + type MaxStorageKeyBytes = MaxStorageKeyBytes; + type MaxStorageValueBytes = MaxStorageValueBytes; } type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Runtime>; diff --git a/gradually-update/src/tests.rs b/gradually-update/src/tests.rs index fbbec8c..345b82b 100644 --- a/gradually-update/src/tests.rs +++ b/gradually-update/src/tests.rs @@ -7,13 +7,17 @@ use codec::Encode; use frame_support::{assert_noop, assert_ok}; use mock::{Event, *}; use sp_runtime::{FixedPointNumber, FixedU128, Permill}; +use sp_std::convert::TryInto; fn storage_set(key: &Vec<u8>, value: &Vec<u8>) { + // let bounded_key: StorageValueBytes<Runtime> = + // key.to_vec().try_into().unwrap(); let bounded_value: + // StorageValueBytes<Runtime> = key.to_vec().try_into().unwrap(); frame_support::storage::unhashed::put(key, value); } -fn storage_get(key: &Vec<u8>) -> Vec<u8> { - frame_support::storage::unhashed::get::<StorageValueBytes>(key).unwrap_or_default() +fn storage_get(key: &Vec<u8>) -> StorageValueBytes<Runtime> { + frame_support::storage::unhashed::get::<StorageValueBytes<Runtime>>(key).unwrap_or_default() } #[test] @@ -21,10 +25,10 @@ fn gradually_update_should_work() { ExtBuilder::default().build().execute_with(|| { System::set_block_number(1); - let update = GraduallyUpdate { - key: vec![1], - target_value: vec![9], - per_block: vec![1], + let update: GraduallyUpdate<StorageKeyBytes<Runtime>, StorageValueBytes<Runtime>> = GraduallyUpdate { + key: vec![1].try_into().unwrap(), + target_value: vec![9].try_into().unwrap(), + per_block: vec![1].try_into().unwrap(), }; assert_ok!(GraduallyUpdateModule::gradually_update(Origin::root(), update.clone())); @@ -42,29 +46,29 @@ fn gradually_update_should_work() { #[test] fn gradually_update_should_fail() { ExtBuilder::default().build().execute_with(|| { - let update = GraduallyUpdate { - key: vec![1], - target_value: 9u32.encode(), - per_block: 1u64.encode(), + let update: GraduallyUpdate<StorageKeyBytes<Runtime>, StorageValueBytes<Runtime>> = GraduallyUpdate { + key: vec![1].try_into().unwrap(), + target_value: 9u32.encode().try_into().unwrap(), + per_block: 1u64.encode().try_into().unwrap(), }; assert_noop!( GraduallyUpdateModule::gradually_update(Origin::root(), update.clone()), Error::<Runtime>::InvalidPerBlockOrTargetValue ); - let update = GraduallyUpdate { - key: vec![1], - target_value: 90u32.encode(), - per_block: 1u32.encode(), + let update: GraduallyUpdate<StorageKeyBytes<Runtime>, StorageValueBytes<Runtime>> = GraduallyUpdate { + key: vec![1].try_into().unwrap(), + target_value: 90u32.encode().try_into().unwrap(), + per_block: 1u32.encode().try_into().unwrap(), }; assert_ok!(GraduallyUpdateModule::gradually_update(Origin::root(), update.clone())); GraduallyUpdateModule::on_finalize(20); let new_update = GraduallyUpdate { - key: vec![1], - target_value: 9u64.encode(), - per_block: 1u64.encode(), + key: vec![1].try_into().unwrap(), + target_value: 9u64.encode().try_into().unwrap(), + per_block: 1u64.encode().try_into().unwrap(), }; assert_noop!( GraduallyUpdateModule::gradually_update(Origin::root(), new_update.clone()), @@ -83,10 +87,10 @@ fn cancel_gradually_update_should_work() { ExtBuilder::default().build().execute_with(|| { System::set_block_number(1); - let update = GraduallyUpdate { - key: vec![1], - target_value: vec![9], - per_block: vec![1], + let update: GraduallyUpdate<StorageKeyBytes<Runtime>, StorageValueBytes<Runtime>> = GraduallyUpdate { + key: vec![1].try_into().unwrap(), + target_value: vec![9].try_into().unwrap(), + per_block: vec![1].try_into().unwrap(), }; assert_ok!(GraduallyUpdateModule::gradually_update(Origin::root(), update.clone())); let gradually_update_event = Event::gradually_update(crate::Event::GraduallyUpdateAdded( @@ -102,7 +106,8 @@ fn cancel_gradually_update_should_work() { Origin::root(), update.key.clone() )); - let cancel_gradually_update_event = Event::gradually_update(crate::Event::GraduallyUpdateCancelled(update.key)); + let cancel_gradually_update_event = + Event::gradually_update(crate::Event::GraduallyUpdateCancelled(update.key.clone())); assert!(System::events() .iter() .any(|record| record.event == cancel_gradually_update_event)); @@ -112,10 +117,10 @@ fn cancel_gradually_update_should_work() { #[test] fn cancel_gradually_update_should_fail() { ExtBuilder::default().build().execute_with(|| { - let update = GraduallyUpdate { - key: vec![1], - target_value: 9u32.encode(), - per_block: 1u32.encode(), + let update: GraduallyUpdate<StorageKeyBytes<Runtime>, StorageValueBytes<Runtime>> = GraduallyUpdate { + key: vec![1].try_into().unwrap(), + target_value: 9u32.encode().try_into().unwrap(), + per_block: 1u32.encode().try_into().unwrap(), }; assert_noop!( GraduallyUpdateModule::cancel_gradually_update(Origin::root(), update.key.clone()), @@ -136,21 +141,26 @@ fn add_on_finalize_should_work() { ExtBuilder::default().build().execute_with(|| { System::set_block_number(1); - let update = GraduallyUpdate { - key: vec![1], - target_value: vec![30], - per_block: vec![1], + let update: GraduallyUpdate<StorageKeyBytes<Runtime>, StorageValueBytes<Runtime>> = GraduallyUpdate { + key: vec![1].try_into().unwrap(), + target_value: vec![30].try_into().unwrap(), + per_block: vec![1].try_into().unwrap(), }; assert_ok!(GraduallyUpdateModule::gradually_update(Origin::root(), update.clone())); assert_eq!(storage_get(&update.key), Vec::<u8>::new()); GraduallyUpdateModule::on_finalize(10); assert_eq!(storage_get(&update.key), vec![10]); - let gradually_update_blocknumber_event = - Event::gradually_update(crate::Event::Updated(10, update.key.clone(), vec![10])); - assert!(System::events() - .iter() - .any(|record| record.event == gradually_update_blocknumber_event)); + let gradually_update_blocknumber_event = Event::gradually_update(crate::Event::Updated( + 10, + update.key.clone(), + vec![10].try_into().unwrap(), + )); + println!("Length {}", System::events().len()); + assert!(System::events().iter().any(|record| { + println!("{:?}", record.event); + record.event == gradually_update_blocknumber_event + })); assert_eq!(System::events().len(), 2); GraduallyUpdateModule::on_finalize(15); @@ -159,8 +169,11 @@ fn add_on_finalize_should_work() { GraduallyUpdateModule::on_finalize(20); assert_eq!(storage_get(&update.key), vec![20]); - let gradually_update_blocknumber_event = - Event::gradually_update(crate::Event::Updated(20, update.key.clone(), vec![20])); + let gradually_update_blocknumber_event = Event::gradually_update(crate::Event::Updated( + 20, + update.key.clone(), + vec![20].try_into().unwrap(), + )); assert!(System::events() .iter() .any(|record| record.event == gradually_update_blocknumber_event)); @@ -168,8 +181,11 @@ fn add_on_finalize_should_work() { GraduallyUpdateModule::on_finalize(40); assert_eq!(storage_get(&update.key), vec![30]); - let gradually_update_blocknumber_event = - Event::gradually_update(crate::Event::Updated(40, update.key.clone(), vec![30])); + let gradually_update_blocknumber_event = Event::gradually_update(crate::Event::Updated( + 40, + update.key.clone(), + vec![30].try_into().unwrap(), + )); assert!(System::events() .iter() .any(|record| record.event == gradually_update_blocknumber_event)); @@ -181,10 +197,10 @@ fn sub_on_finalize_should_work() { ExtBuilder::default().build().execute_with(|| { System::set_block_number(1); - let update = GraduallyUpdate { - key: vec![1], - target_value: vec![5], - per_block: vec![1], + let update: GraduallyUpdate<StorageKeyBytes<Runtime>, StorageValueBytes<Runtime>> = GraduallyUpdate { + key: vec![1].try_into().unwrap(), + target_value: vec![5].try_into().unwrap(), + per_block: vec![1].try_into().unwrap(), }; storage_set(&update.key, &vec![30]); @@ -193,8 +209,11 @@ fn sub_on_finalize_should_work() { GraduallyUpdateModule::on_finalize(10); assert_eq!(storage_get(&update.key), vec![20]); - let gradually_update_blocknumber_event = - Event::gradually_update(crate::Event::Updated(10, update.key.clone(), vec![20])); + let gradually_update_blocknumber_event = Event::gradually_update(crate::Event::Updated( + 10, + update.key.clone(), + vec![20].try_into().unwrap(), + )); assert!(System::events() .iter() .any(|record| record.event == gradually_update_blocknumber_event)); @@ -206,8 +225,11 @@ fn sub_on_finalize_should_work() { GraduallyUpdateModule::on_finalize(20); assert_eq!(storage_get(&update.key), vec![10]); - let gradually_update_blocknumber_event = - Event::gradually_update(crate::Event::Updated(20, update.key.clone(), vec![10])); + let gradually_update_blocknumber_event = Event::gradually_update(crate::Event::Updated( + 20, + update.key.clone(), + vec![10].try_into().unwrap(), + )); assert!(System::events() .iter() .any(|record| record.event == gradually_update_blocknumber_event)); @@ -215,8 +237,11 @@ fn sub_on_finalize_should_work() { GraduallyUpdateModule::on_finalize(40); assert_eq!(storage_get(&update.key), vec![5]); - let gradually_update_blocknumber_event = - Event::gradually_update(crate::Event::Updated(40, update.key.clone(), vec![5])); + let gradually_update_blocknumber_event = Event::gradually_update(crate::Event::Updated( + 40, + update.key.clone(), + vec![5].try_into().unwrap(), + )); assert!(System::events() .iter() .any(|record| record.event == gradually_update_blocknumber_event)); @@ -226,10 +251,10 @@ fn sub_on_finalize_should_work() { #[test] fn u32_should_work() { ExtBuilder::default().build().execute_with(|| { - let update = GraduallyUpdate { - key: vec![1], - target_value: 30u32.encode(), - per_block: 1u32.encode(), + let update: GraduallyUpdate<StorageKeyBytes<Runtime>, StorageValueBytes<Runtime>> = GraduallyUpdate { + key: vec![1].try_into().unwrap(), + target_value: 30u32.encode().try_into().unwrap(), + per_block: 1u32.encode().try_into().unwrap(), }; assert_ok!(GraduallyUpdateModule::gradually_update(Origin::root(), update.clone())); assert_eq!(storage_get(&update.key), Vec::<u8>::new()); @@ -247,10 +272,10 @@ fn u32_should_work() { #[test] fn u128_should_work() { ExtBuilder::default().build().execute_with(|| { - let update = GraduallyUpdate { - key: vec![1], - target_value: 30u128.encode(), - per_block: 1u128.encode(), + let update: GraduallyUpdate<StorageKeyBytes<Runtime>, StorageValueBytes<Runtime>> = GraduallyUpdate { + key: vec![1].try_into().unwrap(), + target_value: 30u128.encode().try_into().unwrap(), + per_block: 1u128.encode().try_into().unwrap(), }; assert_ok!(GraduallyUpdateModule::gradually_update(Origin::root(), update.clone())); assert_eq!(storage_get(&update.key), Vec::<u8>::new()); @@ -280,10 +305,10 @@ fn u128_should_work() { #[test] fn permill_should_work() { ExtBuilder::default().build().execute_with(|| { - let update = GraduallyUpdate { - key: vec![1], - target_value: Permill::from_percent(30).encode(), - per_block: Permill::from_percent(1).encode(), + let update: GraduallyUpdate<StorageKeyBytes<Runtime>, StorageValueBytes<Runtime>> = GraduallyUpdate { + key: vec![1].try_into().unwrap(), + target_value: Permill::from_percent(30).encode().try_into().unwrap(), + per_block: Permill::from_percent(1).encode().try_into().unwrap(), }; assert_ok!(GraduallyUpdateModule::gradually_update(Origin::root(), update.clone())); assert_eq!(storage_get(&update.key), Vec::<u8>::new()); @@ -301,10 +326,10 @@ fn permill_should_work() { #[test] fn fixedu128_should_work() { ExtBuilder::default().build().execute_with(|| { - let update = GraduallyUpdate { - key: vec![1], - target_value: FixedU128::saturating_from_rational(30, 1).encode(), - per_block: FixedU128::saturating_from_rational(1, 1).encode(), + let update: GraduallyUpdate<StorageKeyBytes<Runtime>, StorageValueBytes<Runtime>> = GraduallyUpdate { + key: vec![1].try_into().unwrap(), + target_value: FixedU128::saturating_from_rational(30, 1).encode().try_into().unwrap(), + per_block: FixedU128::saturating_from_rational(1, 1).encode().try_into().unwrap(), }; assert_ok!(GraduallyUpdateModule::gradually_update(Origin::root(), update.clone())); assert_eq!(storage_get(&update.key), Vec::<u8>::new()); @@ -336,20 +361,20 @@ fn finish_multiple_on_finalize_should_work() { ExtBuilder::default().build().execute_with(|| { System::set_block_number(1); - let update = GraduallyUpdate { - key: vec![10], - target_value: vec![30], - per_block: vec![1], + let update: GraduallyUpdate<StorageKeyBytes<Runtime>, StorageValueBytes<Runtime>> = GraduallyUpdate { + key: vec![10].try_into().unwrap(), + target_value: vec![30].try_into().unwrap(), + per_block: vec![1].try_into().unwrap(), }; - let update2 = GraduallyUpdate { - key: vec![20], - target_value: vec![60], - per_block: vec![2], + let update2: GraduallyUpdate<StorageKeyBytes<Runtime>, StorageValueBytes<Runtime>> = GraduallyUpdate { + key: vec![20].try_into().unwrap(), + target_value: vec![60].try_into().unwrap(), + per_block: vec![2].try_into().unwrap(), }; - let update3 = GraduallyUpdate { - key: vec![30], - target_value: vec![100], - per_block: vec![3], + let update3: GraduallyUpdate<StorageKeyBytes<Runtime>, StorageValueBytes<Runtime>> = GraduallyUpdate { + key: vec![30].try_into().unwrap(), + target_value: vec![100].try_into().unwrap(), + per_block: vec![3].try_into().unwrap(), }; assert_ok!(GraduallyUpdateModule::gradually_update(Origin::root(), update.clone())); assert_ok!(GraduallyUpdateModule::gradually_update(Origin::root(), update2.clone())); @@ -381,3 +406,50 @@ fn finish_multiple_on_finalize_should_work() { assert_eq!(storage_get(&update3.key), vec![100]); }); } + +#[test] +fn exceeding_max_gradually_updates_should_fail() { + ExtBuilder::default().build().execute_with(|| { + System::set_block_number(1); + + let update: GraduallyUpdate<StorageKeyBytes<Runtime>, StorageValueBytes<Runtime>> = GraduallyUpdate { + key: vec![10].try_into().unwrap(), + target_value: vec![30].try_into().unwrap(), + per_block: vec![1].try_into().unwrap(), + }; + let update2: GraduallyUpdate<StorageKeyBytes<Runtime>, StorageValueBytes<Runtime>> = GraduallyUpdate { + key: vec![20].try_into().unwrap(), + target_value: vec![60].try_into().unwrap(), + per_block: vec![2].try_into().unwrap(), + }; + let update3: GraduallyUpdate<StorageKeyBytes<Runtime>, StorageValueBytes<Runtime>> = GraduallyUpdate { + key: vec![30].try_into().unwrap(), + target_value: vec![100].try_into().unwrap(), + per_block: vec![3].try_into().unwrap(), + }; + let update4: GraduallyUpdate<StorageKeyBytes<Runtime>, StorageValueBytes<Runtime>> = GraduallyUpdate { + key: vec![40].try_into().unwrap(), + target_value: vec![120].try_into().unwrap(), + per_block: vec![4].try_into().unwrap(), + }; + assert_ok!(GraduallyUpdateModule::gradually_update(Origin::root(), update.clone())); + assert_ok!(GraduallyUpdateModule::gradually_update(Origin::root(), update2.clone())); + assert_ok!(GraduallyUpdateModule::gradually_update(Origin::root(), update3.clone())); + assert_noop!( + GraduallyUpdateModule::gradually_update(Origin::root(), update4.clone()), + Error::<Runtime>::MaxGraduallyUpdateExceeded + ); + + GraduallyUpdateModule::on_finalize(10); + GraduallyUpdateModule::on_finalize(20); + GraduallyUpdateModule::on_finalize(30); + assert_ok!(GraduallyUpdateModule::gradually_update(Origin::root(), update4.clone())); + GraduallyUpdateModule::on_finalize(40); + GraduallyUpdateModule::on_finalize(50); + GraduallyUpdateModule::on_finalize(60); + assert_eq!(storage_get(&update.key), vec![30]); + assert_eq!(storage_get(&update2.key), vec![60]); + assert_eq!(storage_get(&update3.key), vec![100]); + assert_eq!(storage_get(&update4.key), vec![120]); + }); +} -- GitLab