diff --git a/Cargo.lock b/Cargo.lock index ac344ea6f..ae6333870 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7732,9 +7732,11 @@ version = "0.1.1" name = "ismp-grandpa" version = "1.15.3" dependencies = [ + "anyhow", "ckb-merkle-mountain-range", "cumulus-primitives-core", "finality-grandpa", + "frame-benchmarking", "frame-support 37.0.0", "frame-system", "grandpa-verifier", @@ -7748,6 +7750,7 @@ dependencies = [ "sp-core 34.0.0", "sp-io 38.0.0", "sp-runtime 39.0.0", + "sp-std 14.0.0", "sp-trie 37.0.0", "substrate-state-machine", ] @@ -7758,6 +7761,7 @@ version = "1.15.3" dependencies = [ "cumulus-pallet-parachain-system", "cumulus-primitives-core", + "frame-benchmarking", "frame-support 37.0.0", "frame-system", "hex-literal 0.4.1", diff --git a/modules/ismp/clients/grandpa/Cargo.toml b/modules/ismp/clients/grandpa/Cargo.toml index 39cf8ec41..3a33ef767 100644 --- a/modules/ismp/clients/grandpa/Cargo.toml +++ b/modules/ismp/clients/grandpa/Cargo.toml @@ -12,15 +12,18 @@ keywords = ["substrate", "polkadot-sdk", "ISMP", "interoperability", "GRANDPA"] readme = "./README.md" [dependencies] +anyhow = { workspace = true } codec = { workspace = true, features = ["derive"] } primitive-types = { workspace = true } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } merkle-mountain-range = { workspace = true } finality-grandpa = { version = "0.16.0", features = ["derive-codec"], default-features = false } +frame-benchmarking = { workspace = true, optional = true } +sp-std = { workspace = true } # polytope labs ismp = { workspace = true } -grandpa-verifier-primitives = { workspace = true } +grandpa-verifier-primitives = { workspace = true } grandpa-verifier = { workspace = true } pallet-ismp = { workspace = true } @@ -40,6 +43,7 @@ substrate-state-machine = { workspace = true } [features] default = ["std"] std = [ + "sp-std/std", "codec/std", "frame-support/std", "frame-system/std", @@ -60,3 +64,10 @@ std = [ "finality-grandpa/std", ] try-runtime = [] + +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-ismp/runtime-benchmarks", +] \ No newline at end of file diff --git a/modules/ismp/clients/grandpa/src/benchmarking.rs b/modules/ismp/clients/grandpa/src/benchmarking.rs new file mode 100644 index 000000000..4c9765f3a --- /dev/null +++ b/modules/ismp/clients/grandpa/src/benchmarking.rs @@ -0,0 +1,83 @@ +#![cfg(feature = "runtime-benchmarks")] +use super::*; +use frame_benchmarking::v2::*; +use frame_support::pallet_prelude::Weight; +use frame_system::RawOrigin; +use sp_std::prelude::*; + +/// Benchmarks for the ISMP GRANDPA pallet operations +#[benchmarks] +mod benchmarks { + use super::*; + + /// Benchmark for add_state_machines extrinsic + /// The benchmark creates n state machines and measures the time to add them + /// to the whitelist. + /// + /// Parameters: + /// - `n`: Number of state machines to add in a single call + #[benchmark] + fn add_state_machines(n: Linear<1, 100>) -> Result<(), BenchmarkError> { + let caller: T::AccountId = whitelisted_caller(); + + let state_machines: Vec = (0..n) + .map(|i| { + let id = [i as u8, 0, 0, 0]; // Create unique 4-byte identifier + AddStateMachine { + state_machine: StateMachine::Substrate(id), + slot_duration: 6000u64, + } + }) + .collect(); + + #[extrinsic_call] + _(RawOrigin::Root, state_machines); + + // Verify operation was successful + assert!(SupportedStateMachines::::iter().count() == n as usize); + Ok(()) + } + + /// Benchmark for remove_state_machines extrinsic + /// The benchmark first adds n state machines, then measures the time to remove them + /// from the whitelist. + /// + /// Parameters: + /// - `n`: Number of state machines to remove in a single call + #[benchmark] + fn remove_state_machines(n: Linear<1, 100>) -> Result<(), BenchmarkError> { + let caller: T::AccountId = whitelisted_caller(); + + // Setup: First add state machines that we'll remove + let setup_machines: Vec = (0..n) + .map(|i| { + let id = [i as u8, 0, 0, 0]; // Create unique 4-byte identifier + AddStateMachine { + state_machine: StateMachine::Substrate(id), + slot_duration: 6000u64, + } + }) + .collect(); + + // Add the machines using root origin + Pallet::::add_state_machines( + RawOrigin::Root.into(), + setup_machines.clone(), + )?; + + // Create removal list + let remove_machines: Vec = + setup_machines.into_iter().map(|m| m.state_machine).collect(); + + // Verify initial state + assert!(SupportedStateMachines::::iter().count() == n as usize); + + #[extrinsic_call] + _(RawOrigin::Root, remove_machines); + + // Verify all machines were removed + assert!(SupportedStateMachines::::iter().count() == 0); + Ok(()) + } +} + diff --git a/modules/ismp/clients/grandpa/src/lib.rs b/modules/ismp/clients/grandpa/src/lib.rs index b84335c30..a2c1cca9e 100644 --- a/modules/ismp/clients/grandpa/src/lib.rs +++ b/modules/ismp/clients/grandpa/src/lib.rs @@ -15,13 +15,27 @@ #![cfg_attr(not(feature = "std"), no_std)] extern crate alloc; +#[cfg(feature = "runtime-benchmarks")] +pub mod benchmarking; + pub mod consensus; pub mod messages; use alloc::vec::Vec; +use frame_support::pallet_prelude::Weight; use ismp::host::StateMachine; pub use pallet::*; +pub trait WeightInfo { + /// Weight for adding state machines, scaled by the number of machines + /// * n: The number of machines being added + fn add_state_machines(n: u32) -> Weight; + + /// Weight for removing state machines, scaled by the number of machines + /// * n: The number of machines being removed + fn remove_state_machines(n: u32) -> Weight; +} +pub mod weights; #[frame_support::pallet] pub mod pallet { use super::*; @@ -41,6 +55,7 @@ pub mod pallet { /// IsmpHost implementation type IsmpHost: IsmpHost + Default; + type WeightInfo: WeightInfo; } /// Events emitted by this pallet @@ -69,7 +84,7 @@ pub mod pallet { impl Pallet { /// Add some a state machine to the list of supported state machines #[pallet::call_index(0)] - #[pallet::weight(T::DbWeight::get().writes(new_state_machines.len() as u64))] + #[pallet::weight(T::WeightInfo::add_state_machines(new_state_machines.len() as u32))] pub fn add_state_machines( origin: OriginFor, new_state_machines: Vec, @@ -89,7 +104,7 @@ pub mod pallet { /// Remove a state machine from the list of supported state machines #[pallet::call_index(1)] - #[pallet::weight(T::DbWeight::get().writes(state_machines.len() as u64))] + #[pallet::weight(T::WeightInfo::remove_state_machines(state_machines.len() as u32))] pub fn remove_state_machines( origin: OriginFor, state_machines: Vec, diff --git a/modules/ismp/clients/grandpa/src/weights.rs b/modules/ismp/clients/grandpa/src/weights.rs new file mode 100644 index 000000000..c5a7564f6 --- /dev/null +++ b/modules/ismp/clients/grandpa/src/weights.rs @@ -0,0 +1,49 @@ +// This file is part of Hyperbridge. + +// Copyright (C) Polytope Labs Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weights for ismp_grandpa +pub struct WeightInfo(PhantomData); + +/// Weight functions for ismp-parachain pallet extrinsics. +impl crate::WeightInfo for WeightInfo { + /// Weight for adding state machines, scaled by the number of machines. + /// Values based on measured benchmarks: + /// - Base Weight: 5.525 µs + /// - Additional Weight per item: 1.458 µs + /// - DB Weight: n writes + fn add_state_machines(n: u32) -> Weight { + Weight::from_parts(5_525, 0) + .saturating_add(Weight::from_parts(1_458, 0).saturating_mul(n as u64)) + .saturating_add(T::DbWeight::get().writes(n as u64)) + } + + /// Weight for removing state machines, scaled by the number of machines. + /// Values based on measured benchmarks: + /// - Base Weight: 4.914 µs + /// - Additional Weight per item: 1.419 µs + /// - DB Weight: n writes + fn remove_state_machines(n: u32) -> Weight { + Weight::from_parts(4_914, 0) + .saturating_add(Weight::from_parts(1_419, 0).saturating_mul(n as u64)) + .saturating_add(T::DbWeight::get().writes(n as u64)) + } +} \ No newline at end of file diff --git a/modules/ismp/clients/parachain/client/Cargo.toml b/modules/ismp/clients/parachain/client/Cargo.toml index 0c6f8a654..8546a70d9 100644 --- a/modules/ismp/clients/parachain/client/Cargo.toml +++ b/modules/ismp/clients/parachain/client/Cargo.toml @@ -28,6 +28,7 @@ sp-inherents = { workspace = true } sp-io = { workspace = true } sp-runtime = { workspace = true } sp-consensus-aura = { workspace = true } +frame-benchmarking = { workspace = true, optional = true } # cumulus cumulus-pallet-parachain-system = { workspace = true, default-features = false } @@ -63,3 +64,10 @@ try-runtime = [ "frame-system/try-runtime", "sp-runtime/try-runtime", ] + +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-ismp/runtime-benchmarks", +] diff --git a/parachain/runtimes/gargantua/Cargo.toml b/parachain/runtimes/gargantua/Cargo.toml index 98c57f72a..8439515f5 100644 --- a/parachain/runtimes/gargantua/Cargo.toml +++ b/parachain/runtimes/gargantua/Cargo.toml @@ -214,7 +214,8 @@ runtime-benchmarks = [ "pallet-message-queue/runtime-benchmarks", "pallet-assets/runtime-benchmarks", "pallet-sudo/runtime-benchmarks", - "parachains-common/runtime-benchmarks" + "parachains-common/runtime-benchmarks", + "ismp-grandpa/runtime-benchmarks", ] try-runtime = [ diff --git a/parachain/runtimes/gargantua/src/ismp.rs b/parachain/runtimes/gargantua/src/ismp.rs index 2d84441e3..ffe1a49c5 100644 --- a/parachain/runtimes/gargantua/src/ismp.rs +++ b/parachain/runtimes/gargantua/src/ismp.rs @@ -74,6 +74,7 @@ impl pallet_state_coprocessor::Config for Runtime { type Mmr = Mmr; } + pub struct Coprocessor; impl Get> for Coprocessor { @@ -107,8 +108,9 @@ impl pallet_ismp::Config for Runtime { } impl ismp_grandpa::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type IsmpHost = Ismp; + type RuntimeEvent = RuntimeEvent; + type IsmpHost = pallet_ismp::Pallet; + type WeightInfo = ismp_grandpa::weights::WeightInfo; } impl pallet_token_governor::Config for Runtime { diff --git a/parachain/runtimes/gargantua/src/lib.rs b/parachain/runtimes/gargantua/src/lib.rs index b32467853..70a534dd5 100644 --- a/parachain/runtimes/gargantua/src/lib.rs +++ b/parachain/runtimes/gargantua/src/lib.rs @@ -758,6 +758,8 @@ mod benches { [pallet_collective, TechnicalCollective] [cumulus_pallet_parachain_system, ParachainSystem] [pallet_session, SessionBench::] + [ismp_grandpa, IsmpGrandpa] + ); } diff --git a/parachain/runtimes/nexus/src/ismp.rs b/parachain/runtimes/nexus/src/ismp.rs index e3624f23f..1ff0e6e21 100644 --- a/parachain/runtimes/nexus/src/ismp.rs +++ b/parachain/runtimes/nexus/src/ismp.rs @@ -84,6 +84,12 @@ impl Get> for Coprocessor { } } +impl ismp_grandpa::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type IsmpHost = pallet_ismp::Pallet; + type WeightInfo = ismp_grandpa::weights::WeightInfo; +} + impl pallet_ismp::Config for Runtime { type RuntimeEvent = RuntimeEvent; type AdminOrigin = EnsureRoot; @@ -108,10 +114,7 @@ impl pallet_ismp::Config for Runtime { type WeightProvider = (); } -impl ismp_grandpa::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type IsmpHost = Ismp; -} + impl pallet_ismp_relayer::Config for Runtime { type RuntimeEvent = RuntimeEvent;