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(&current_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