diff --git a/oracle/Cargo.toml b/oracle/Cargo.toml index c05903493..043fb29d8 100644 --- a/oracle/Cargo.toml +++ b/oracle/Cargo.toml @@ -18,6 +18,7 @@ sp-application-crypto = { workspace = true } sp-io = { workspace = true } sp-runtime = { workspace = true } sp-std = { workspace = true } +frame-benchmarking = { workspace = true, optional = true } orml-traits = { path = "../traits", version = "0.8.0", default-features = false } orml-utilities = { path = "../utilities", version = "0.8.0", default-features = false } @@ -28,6 +29,7 @@ sp-core = { workspace = true } [features] default = [ "std" ] std = [ + "frame-benchmarking?/std", "frame-support/std", "frame-system/std", "orml-traits/std", @@ -45,3 +47,9 @@ try-runtime = [ "frame-system/try-runtime", "sp-runtime/try-runtime", ] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] diff --git a/oracle/src/benchmarking.rs b/oracle/src/benchmarking.rs new file mode 100644 index 000000000..910bf229c --- /dev/null +++ b/oracle/src/benchmarking.rs @@ -0,0 +1,55 @@ +use super::*; +use crate::Pallet as Oracle; + +use frame_benchmarking::v2::*; + +use frame_support::assert_ok; +use frame_system::{Pallet as System, RawOrigin}; +use sp_std::vec; + +#[instance_benchmarks] +mod benchmarks { + use super::*; + + #[benchmark] + fn feed_values(x: Linear<1, { T::BenchmarkHelper::get_currency_id_value_pairs().len() as u32 }>) { + // Register the caller + let caller: T::AccountId = whitelisted_caller(); + T::Members::add(&caller); + + let values = T::BenchmarkHelper::get_currency_id_value_pairs()[..x as usize] + .to_vec() + .try_into() + .expect("Must succeed since at worst the length remained the same."); + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone()), values); + + assert!(HasDispatched::::get().contains(&caller)); + } + + #[benchmark] + fn on_finalize() { + // Register the caller + let caller: T::AccountId = whitelisted_caller(); + T::Members::add(&caller); + + // Feed some values before running `on_finalize` hook + System::::set_block_number(1u32.into()); + let values = T::BenchmarkHelper::get_currency_id_value_pairs(); + assert_ok!(Oracle::::feed_values(RawOrigin::Signed(caller).into(), values)); + + #[block] + { + Oracle::::on_finalize(System::::block_number()); + } + + assert!(!HasDispatched::::exists()); + } + + impl_benchmark_test_suite! { + Oracle, + crate::mock::new_test_ext(), + crate::mock::Test, + } +} diff --git a/oracle/src/lib.rs b/oracle/src/lib.rs index abaa545b9..ede378a44 100644 --- a/oracle/src/lib.rs +++ b/oracle/src/lib.rs @@ -41,6 +41,9 @@ use sp_std::{prelude::*, vec}; pub use crate::default_combine_data::DefaultCombineData; +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; + mod default_combine_data; mod mock; mod tests; @@ -49,6 +52,15 @@ mod weights; pub use module::*; pub use weights::WeightInfo; +#[cfg(feature = "runtime-benchmarks")] +/// Helper trait for benchmarking. +pub trait BenchmarkHelper> { + /// Returns a list of `(oracle_key, oracle_value)` pairs to be used for benchmarking. + /// + /// NOTE: User should ensure to at least submit two values, otherwise the benchmark linear analysis might fail. + fn get_currency_id_value_pairs() -> BoundedVec<(OracleKey, OracleValue), L>; +} + #[frame_support::pallet] pub mod module { use super::*; @@ -100,6 +112,9 @@ pub mod module { /// Maximum size the vector used for feed values #[pallet::constant] type MaxFeedValues: Get; + + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper: BenchmarkHelper; } #[pallet::error] diff --git a/oracle/src/mock.rs b/oracle/src/mock.rs index 502f33805..4907aa0c0 100644 --- a/oracle/src/mock.rs +++ b/oracle/src/mock.rs @@ -27,6 +27,7 @@ impl frame_system::Config for Test { thread_local! { static TIME: RefCell = RefCell::new(0); + static MEMBERS: RefCell> = RefCell::new(vec![1, 2, 3]); } pub struct Timestamp; @@ -46,14 +47,28 @@ impl Timestamp { parameter_types! { pub const RootOperatorAccountId: AccountId = 4; - pub static OracleMembers: Vec = vec![1, 2, 3]; + pub const MaxFeedValues: u32 = 5; } pub struct Members; impl SortedMembers for Members { fn sorted_members() -> Vec { - OracleMembers::get() + MEMBERS.with(|v| v.borrow().clone()) + } + + #[cfg(feature = "runtime-benchmarks")] + fn add(who: &AccountId) { + MEMBERS.with(|v| v.borrow_mut().push(*who)); + } +} + +#[cfg(feature = "runtime-benchmarks")] +pub struct BenchmarkHelper; +#[cfg(feature = "runtime-benchmarks")] +impl crate::BenchmarkHelper for BenchmarkHelper { + fn get_currency_id_value_pairs() -> BoundedVec<(Key, Value), MaxFeedValues> { + vec![(1, 1), (2, 2), (3, 3)].try_into().unwrap() } } @@ -68,7 +83,9 @@ impl Config for Test { type Members = Members; type WeightInfo = (); type MaxHasDispatchedSize = ConstU32<100>; - type MaxFeedValues = ConstU32<5>; + type MaxFeedValues = MaxFeedValues; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = BenchmarkHelper; } type Block = frame_system::mocking::MockBlock; @@ -80,6 +97,10 @@ construct_runtime!( } ); +pub fn set_members(members: Vec) { + MEMBERS.with(|v| *v.borrow_mut() = members); +} + // This function basically just builds a genesis storage key/value store // according to our desired mockup. pub fn new_test_ext() -> sp_io::TestExternalities { diff --git a/oracle/src/tests.rs b/oracle/src/tests.rs index 201117bf2..b116c25c5 100644 --- a/oracle/src/tests.rs +++ b/oracle/src/tests.rs @@ -290,7 +290,7 @@ fn get_all_values_should_work() { #[test] fn change_member_should_work() { new_test_ext().execute_with(|| { - OracleMembers::set(vec![2, 3, 4]); + set_members(vec![2, 3, 4]); >::change_members_sorted(&[4], &[1], &[2, 3, 4]); assert_noop!( ModuleOracle::feed_values(RuntimeOrigin::signed(1), vec![(50, 1000)].try_into().unwrap()),