Skip to content

Commit

Permalink
Benchmarking (#39)
Browse files Browse the repository at this point in the history
* benchmarking frameowrk

* add some information

* progress on benchmarks

* benchmark test: not running yet

* add an assertion after benchmarks

* added some comments
  • Loading branch information
Wizdave97 authored May 22, 2023
1 parent fa50fe4 commit dbd8d33
Show file tree
Hide file tree
Showing 5 changed files with 578 additions and 10 deletions.
8 changes: 7 additions & 1 deletion pallet-ismp/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkad
sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40", default-features = false }
sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40", default-features = false }
sp-api = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40", default-features = false }
pallet-timestamp = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40", default-features = false, optional = true }

# polytope labs
ismp-rs = { package = "ismp", git = "ssh://[email protected]/polytope-labs/ismp-rs.git", branch = "main", default-features = false }
Expand Down Expand Up @@ -54,4 +55,9 @@ std = [
"ismp-primitives/std",
]

runtime-benchmarks = ["frame-benchmarking/runtime-benchmarks"]
runtime-benchmarks = [
"frame-benchmarking/runtime-benchmarks",
"pallet-timestamp/runtime-benchmarks",
"frame-system/runtime-benchmarks",
"frame-support/runtime-benchmarks"
]
272 changes: 272 additions & 0 deletions pallet-ismp/src/benchmarking.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,272 @@
// Only enable this module for benchmarking.
#![cfg(feature = "runtime-benchmarks")]

use crate::*;
use frame_benchmarking::v2::*;
use frame_system::RawOrigin;

// Running the benchmarks correctly
// Add the [`BenchmarkClient`] as one of the consensus clients available to pallet-ismp in the
// runtime configuration
// In your module router configuration add the [`BenchmarkIsmpModule`] as one of the ismp modules
// using the pallet id defined here as it's module id.

// Details on using the benchmarks macro can be seen at:
// https://paritytech.github.io/substrate/master/frame_benchmarking/trait.Benchmarking.html#tymethod.benchmarks
#[benchmarks(
where
<T as frame_system::Config>::Hash: From<H256>,
T: pallet_timestamp::Config,
<T as pallet_timestamp::Config>::Moment: From<u64>
)]
pub mod benchmarks {
use super::*;
use crate::router::Receipt;
use frame_support::PalletId;
use frame_system::EventRecord;
use ismp_rs::{
consensus::{ConsensusClient, IntermediateState, StateCommitment, StateMachineHeight},
error::Error as IsmpError,
messaging::{Message, Proof, RequestMessage, ResponseMessage, TimeoutMessage},
module::ISMPModule,
router::{Post, RequestResponse},
util::hash_request,
};

fn assert_last_event<T: Config>(generic_event: <T as Config>::RuntimeEvent) {
let events = frame_system::Pallet::<T>::events();
let system_event: <T as frame_system::Config>::RuntimeEvent = generic_event.into();
let EventRecord { event, .. } = &events[events.len() - 1];
assert_eq!(event, &system_event);
}

#[derive(Default)]
pub struct BenchmarkClient;

pub const BENCHMARK_CONSENSUS_CLIENT_ID: [u8; 4] = [1u8; 4];

impl ConsensusClient for BenchmarkClient {
fn verify_consensus(
&self,
_host: &dyn ISMPHost,
_trusted_consensus_state: Vec<u8>,
_proof: Vec<u8>,
) -> Result<(Vec<u8>, Vec<IntermediateState>), IsmpError> {
Ok(Default::default())
}

fn unbonding_period(&self) -> Duration {
Duration::from_secs(60 * 60 * 60)
}

fn verify_membership(
&self,
_host: &dyn ISMPHost,
_item: RequestResponse,
_root: StateCommitment,
_proof: &Proof,
) -> Result<(), IsmpError> {
Ok(())
}

fn state_trie_key(&self, _request: RequestResponse) -> Vec<Vec<u8>> {
Default::default()
}

fn verify_state_proof(
&self,
_host: &dyn ISMPHost,
_keys: Vec<Vec<u8>>,
_root: StateCommitment,
_proof: &Proof,
) -> Result<Vec<Option<Vec<u8>>>, IsmpError> {
Ok(Default::default())
}

fn is_frozen(&self, _trusted_consensus_state: &[u8]) -> Result<(), IsmpError> {
Ok(())
}
}

/// This module should be added to the module router in runtime for benchmarks to pass
pub struct BenchmarkIsmpModule;
pub const MODULE_ID: PalletId = PalletId(*b"benchmak");
impl ISMPModule for BenchmarkIsmpModule {
fn on_accept(_request: Request) -> Result<(), IsmpError> {
Ok(())
}

fn on_response(_response: Response) -> Result<(), IsmpError> {
Ok(())
}

fn on_timeout(_request: Request) -> Result<(), IsmpError> {
Ok(())
}
}

fn set_timestamp<T: pallet_timestamp::Config>()
where
<T as pallet_timestamp::Config>::Moment: From<u64>,
{
pallet_timestamp::Pallet::<T>::set_timestamp(1000_000_000u64.into());
}

#[benchmark]
fn create_consensus_client() {
set_timestamp::<T>();
let intermediate_state = IntermediateState {
height: StateMachineHeight {
id: StateMachineId {
state_id: StateMachine::Polkadot(1000),
consensus_client: BENCHMARK_CONSENSUS_CLIENT_ID,
},
height: 1,
},

commitment: StateCommitment {
timestamp: 1651280681,
ismp_root: None,
state_root: Default::default(),
},
};

let message = CreateConsensusClient {
consensus_state: Default::default(),
consensus_client_id: BENCHMARK_CONSENSUS_CLIENT_ID,
state_machine_commitments: vec![intermediate_state],
};

#[extrinsic_call]
_(RawOrigin::Root, message);

assert_last_event::<T>(
Event::ConsensusClientCreated { consensus_client_id: BENCHMARK_CONSENSUS_CLIENT_ID }
.into(),
);
}

fn setup_mock_client<H: ISMPHost>(host: &H) -> IntermediateState {
let intermediate_state = IntermediateState {
height: StateMachineHeight {
id: StateMachineId {
state_id: StateMachine::Ethereum,
consensus_client: BENCHMARK_CONSENSUS_CLIENT_ID,
},
height: 1,
},
commitment: StateCommitment {
timestamp: 1000,
ismp_root: None,
state_root: Default::default(),
},
};

host.store_consensus_state(BENCHMARK_CONSENSUS_CLIENT_ID, vec![]).unwrap();
host.store_consensus_update_time(BENCHMARK_CONSENSUS_CLIENT_ID, Duration::from_secs(1000))
.unwrap();
host.store_state_machine_commitment(
intermediate_state.height,
intermediate_state.commitment,
)
.unwrap();

intermediate_state
}

// The Benchmark consensus client should be added to the runtime for these benchmarks to work
#[benchmark]
fn handle_request_message() {
set_timestamp::<T>();
let host = Host::<T>::default();
let intermediate_state = setup_mock_client(&host);
let post = Post {
source_chain: StateMachine::Ethereum,
dest_chain: <T as Config>::StateMachine::get(),
nonce: 0,
from: MODULE_ID.0.to_vec(),
to: MODULE_ID.0.to_vec(),
timeout_timestamp: 5000,
data: vec![],
};

let msg = RequestMessage {
requests: vec![Request::Post(post.clone())],
proof: Proof { height: intermediate_state.height, proof: vec![] },
};
let caller = whitelisted_caller();

#[extrinsic_call]
handle(RawOrigin::Signed(caller), vec![Message::Request(msg)]);

let commitment = hash_request::<Host<T>>(&Request::Post(post));
assert!(RequestAcks::<T>::get(commitment.0.to_vec()).is_some());
}

#[benchmark]
fn handle_response_message() {
set_timestamp::<T>();
let host = Host::<T>::default();
let intermediate_state = setup_mock_client(&host);
let post = Post {
source_chain: <T as Config>::StateMachine::get(),
dest_chain: StateMachine::Ethereum,
nonce: 0,
from: MODULE_ID.0.to_vec(),
to: MODULE_ID.0.to_vec(),
timeout_timestamp: 5000,
data: vec![],
};
let request = Request::Post(post.clone());

let commitment = hash_request::<Host<T>>(&request);
RequestAcks::<T>::insert(commitment.0.to_vec(), Receipt::Ok);

let response = Response::Post { post, response: vec![] };

let msg = ResponseMessage::Post {
responses: vec![response],
proof: Proof { height: intermediate_state.height, proof: vec![] },
};

let caller = whitelisted_caller();

#[extrinsic_call]
handle(RawOrigin::Signed(caller), vec![Message::Response(msg)]);

assert!(RequestAcks::<T>::get(commitment.0.to_vec()).is_none());
}

#[benchmark]
fn handle_timeout_message() {
set_timestamp::<T>();
let host = Host::<T>::default();
let intermediate_state = setup_mock_client(&host);
let post = Post {
source_chain: <T as Config>::StateMachine::get(),
dest_chain: StateMachine::Ethereum,
nonce: 0,
from: MODULE_ID.0.to_vec(),
to: MODULE_ID.0.to_vec(),
timeout_timestamp: 500,
data: vec![],
};
let request = Request::Post(post.clone());

let commitment = hash_request::<Host<T>>(&request);
RequestAcks::<T>::insert(commitment.0.to_vec(), Receipt::Ok);

let msg = TimeoutMessage::Post {
requests: vec![request],
timeout_proof: Proof { height: intermediate_state.height, proof: vec![] },
};
let caller = whitelisted_caller();

#[extrinsic_call]
handle(RawOrigin::Signed(caller), vec![Message::Timeout(msg)]);

assert!(RequestAcks::<T>::get(commitment.0.to_vec()).is_none());
}

impl_benchmark_test_suite!(Pallet, crate::tests::new_test_ext(), crate::mock::Test);
}
23 changes: 16 additions & 7 deletions pallet-ismp/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,18 @@

extern crate alloc;

pub mod benchmarking;
mod errors;
pub mod events;
pub mod host;
mod mmr;
#[cfg(test)]
mod mock;
pub mod mock;
pub mod primitives;
pub mod router;
#[cfg(test)]
mod tests;
pub mod tests;
pub mod weight_info;

pub use mmr::utils::NodesUtils;

Expand Down Expand Up @@ -64,6 +66,7 @@ pub mod pallet {
errors::HandlingError,
primitives::{ConsensusClientProvider, ISMP_ID},
router::Receipt,
weight_info::{WeightInfo, WeightProvider},
};
use alloc::collections::BTreeSet;
use frame_support::{pallet_prelude::*, traits::UnixTime};
Expand All @@ -77,6 +80,7 @@ pub mod pallet {
router::ISMPRouter,
};
use sp_core::H256;
use weight_info::get_weight;

/// Our pallet's configuration trait. All our types and constants go in here. If the
/// pallet is dependent on specific other pallets, then their configuration traits
Expand Down Expand Up @@ -111,6 +115,10 @@ pub mod pallet {
type IsmpRouter: ISMPRouter + Default;
/// Provides concrete implementations of consensus clients
type ConsensusClientProvider: ConsensusClientProvider;
/// Weight Info
type WeightInfo: WeightInfo;
/// Weight provider for consensus clients and module callbacks
type WeightProvider: WeightProvider;
}

// Simple declaration of the `Pallet` type. It is placeholder we use to implement traits and
Expand Down Expand Up @@ -173,7 +181,7 @@ pub mod pallet {

#[pallet::storage]
#[pallet::getter(fn response_acks)]
/// Acknowledgements for receipt of responses
/// Acknowledgements for incoming and outgoing responses
/// The key is the response commitment
pub type ResponseAcks<T: Config> =
StorageMap<_, Blake2_128Concat, Vec<u8>, Receipt, OptionQuery>;
Expand Down Expand Up @@ -240,8 +248,9 @@ pub mod pallet {
<T as frame_system::Config>::Hash: From<H256>,
{
/// Handles ismp messages
#[pallet::weight(0)]
#[pallet::weight(get_weight::<T>(&messages))]
#[pallet::call_index(0)]
#[frame_support::transactional]
pub fn handle(origin: OriginFor<T>, messages: Vec<Message>) -> DispatchResult {
let _ = ensure_signed(origin)?;
// Define a host
Expand Down Expand Up @@ -306,7 +315,7 @@ pub mod pallet {
}

/// Create consensus clients
#[pallet::weight(0)]
#[pallet::weight(<T as Config>::WeightInfo::create_consensus_client())]
#[pallet::call_index(1)]
pub fn create_consensus_client(
origin: OriginFor<T>,
Expand Down Expand Up @@ -343,7 +352,7 @@ pub mod pallet {
},
/// Indicates that a consensus client has been created
ConsensusClientCreated { consensus_client_id: ConsensusClientId },
/// Response was process successfully
/// An Outgoing Response has been deposited
Response {
/// Chain that this response will be routed to
dest_chain: StateMachine,
Expand All @@ -352,7 +361,7 @@ pub mod pallet {
/// Nonce for the request which this response is for
request_nonce: u64,
},
/// Request processed successfully
/// An Outgoing Request has been deposited
Request {
/// Chain that this request will be routed to
dest_chain: StateMachine,
Expand Down
Loading

0 comments on commit dbd8d33

Please sign in to comment.