diff --git a/bridges/bin/millau/runtime/Cargo.toml b/bridges/bin/millau/runtime/Cargo.toml index 9e8f50a38e..a98fc2f6c3 100644 --- a/bridges/bin/millau/runtime/Cargo.toml +++ b/bridges/bin/millau/runtime/Cargo.toml @@ -145,6 +145,7 @@ runtime-benchmarks = [ "frame-system/runtime-benchmarks", "libsecp256k1", "pallet-bridge-messages/runtime-benchmarks", + "pallet-bridge-parachains/runtime-benchmarks", "pallet-xcm/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "xcm-builder/runtime-benchmarks", diff --git a/bridges/bin/millau/runtime/src/lib.rs b/bridges/bin/millau/runtime/src/lib.rs index 3b4b06fbc1..fbc3f8e374 100644 --- a/bridges/bin/millau/runtime/src/lib.rs +++ b/bridges/bin/millau/runtime/src/lib.rs @@ -515,9 +515,10 @@ parameter_types! { } /// Instance of the with-Rialto parachains token swap pallet. -pub type WitRialtoParachainsInstance = (); +pub type WithRialtoParachainsInstance = (); -impl pallet_bridge_parachains::Config for Runtime { +impl pallet_bridge_parachains::Config for Runtime { + type WeightInfo = pallet_bridge_parachains::weights::MillauWeight; type BridgesGrandpaPalletInstance = RialtoGrandpaInstance; type ParasPalletName = RialtoParasPalletName; type TrackedParachains = frame_support::traits::Everything; @@ -807,7 +808,7 @@ impl_runtime_apis! { use bp_rialto_parachain::RIALTO_PARACHAIN_ID; let best_rialto_parachain_head = pallet_bridge_parachains::Pallet::< Runtime, - WitRialtoParachainsInstance, + WithRialtoParachainsInstance, >::best_parachain_head(RIALTO_PARACHAIN_ID.into()) .and_then(|encoded_header| bp_rialto_parachain::Header::decode(&mut &encoded_header.0[..]).ok()); match best_rialto_parachain_head { @@ -901,11 +902,13 @@ impl_runtime_apis! { use frame_support::traits::StorageInfoTrait; use pallet_bridge_messages::benchmarking::Pallet as MessagesBench; + use pallet_bridge_parachains::benchmarking::Pallet as ParachainsBench; let mut list = Vec::::new(); list_benchmark!(list, extra, pallet_bridge_messages, MessagesBench::); list_benchmark!(list, extra, pallet_bridge_grandpa, BridgeRialtoGrandpa); + list_benchmark!(list, extra, pallet_bridge_parachains, ParachainsBench::); let storage_info = AllPalletsWithSystem::storage_info(); @@ -942,6 +945,10 @@ impl_runtime_apis! { MessageParams, MessageProofParams, }; + use pallet_bridge_parachains::benchmarking::{ + Pallet as ParachainsBench, + Config as ParachainsConfig, + }; use rialto_messages::WithRialtoMessageBridge; impl MessagesConfig for Runtime { @@ -991,6 +998,19 @@ impl_runtime_apis! { } } + impl ParachainsConfig for Runtime { + fn prepare_parachain_heads_proof( + parachains: &[bp_polkadot_core::parachains::ParaId], + parachain_head_size: u32, + proof_size: bp_runtime::StorageProofSize, + ) -> (pallet_bridge_parachains::RelayBlockHash, bp_polkadot_core::parachains::ParaHeadsProof) { + bridge_runtime_common::parachains_benchmarking::prepare_parachain_heads_proof::( + parachains, + parachain_head_size, + proof_size, + ) + } + } add_benchmark!( params, @@ -999,6 +1019,12 @@ impl_runtime_apis! { MessagesBench:: ); add_benchmark!(params, batches, pallet_bridge_grandpa, BridgeRialtoGrandpa); + add_benchmark!( + params, + batches, + pallet_bridge_parachains, + ParachainsBench:: + ); Ok(batches) } diff --git a/bridges/bin/millau/runtime/src/rialto_parachain_messages.rs b/bridges/bin/millau/runtime/src/rialto_parachain_messages.rs index 3ac2690888..26b2b5d4c4 100644 --- a/bridges/bin/millau/runtime/src/rialto_parachain_messages.rs +++ b/bridges/bin/millau/runtime/src/rialto_parachain_messages.rs @@ -252,7 +252,7 @@ impl TargetHeaderChain(ParaId(bp_rialto_parachain::RIALTO_PARACHAIN_ID), proof) } } @@ -274,7 +274,7 @@ impl SourceHeaderChain for RialtoParachain { WithRialtoParachainMessageBridge, bp_rialto_parachain::Header, Runtime, - crate::WitRialtoParachainsInstance, + crate::WithRialtoParachainsInstance, >(ParaId(bp_rialto_parachain::RIALTO_PARACHAIN_ID), proof, messages_count) } } diff --git a/bridges/bin/runtime-common/Cargo.toml b/bridges/bin/runtime-common/Cargo.toml index 61251847bb..198df7f163 100644 --- a/bridges/bin/runtime-common/Cargo.toml +++ b/bridges/bin/runtime-common/Cargo.toml @@ -18,6 +18,7 @@ static_assertions = { version = "1.1", optional = true } # Bridge dependencies bp-messages = { path = "../../primitives/messages", default-features = false } +bp-parachains = { path = "../../primitives/parachains", default-features = false } bp-polkadot-core = { path = "../../primitives/polkadot-core", default-features = false } bp-runtime = { path = "../../primitives/runtime", default-features = false } pallet-bridge-grandpa = { path = "../../modules/grandpa", default-features = false } @@ -49,6 +50,7 @@ xcm-executor = { git = "https://github.com/paritytech/polkadot", branch = "gav-x default = ["std"] std = [ "bp-messages/std", + "bp-parachains/std", "bp-polkadot-core/std", "bp-runtime/std", "codec/std", diff --git a/bridges/bin/runtime-common/src/lib.rs b/bridges/bin/runtime-common/src/lib.rs index c7fb98aba7..616a55d436 100644 --- a/bridges/bin/runtime-common/src/lib.rs +++ b/bridges/bin/runtime-common/src/lib.rs @@ -21,6 +21,7 @@ pub mod messages; pub mod messages_api; pub mod messages_benchmarking; +pub mod parachains_benchmarking; #[cfg(feature = "integrity-test")] pub mod integrity; diff --git a/bridges/bin/runtime-common/src/messages_benchmarking.rs b/bridges/bin/runtime-common/src/messages_benchmarking.rs index f789bee9f7..880cb8fd55 100644 --- a/bridges/bin/runtime-common/src/messages_benchmarking.rs +++ b/bridges/bin/runtime-common/src/messages_benchmarking.rs @@ -27,10 +27,11 @@ use crate::messages::{ }; use bp_messages::{storage_keys, MessageData, MessageKey, MessagePayload}; +use bp_runtime::StorageProofSize; use codec::Encode; use frame_support::weights::{GetDispatchInfo, Weight}; use pallet_bridge_messages::benchmarking::{ - MessageDeliveryProofParams, MessageParams, MessageProofParams, ProofSize, + MessageDeliveryProofParams, MessageParams, MessageProofParams, }; use sp_core::Hasher; use sp_runtime::traits::{Header, IdentifyAccount, MaybeSerializeDeserialize, Zero}; @@ -59,7 +60,7 @@ where R: frame_system::Config>> + pallet_balances::Config>> + pallet_bridge_grandpa::Config, - R::BridgedChain: bp_runtime::Chain
, + R::BridgedChain: bp_runtime::Chain>, Header = BH>, B: MessageBridge, BI: 'static, FI: 'static, @@ -76,14 +77,14 @@ where + IdentifyAccount>>, { let message_payload = match params.size { - ProofSize::Minimal(ref size) => vec![0u8; *size as _], + StorageProofSize::Minimal(ref size) => vec![0u8; *size as _], _ => vec![], }; // finally - prepare storage proof and update environment let (state_root, storage_proof) = prepare_messages_storage_proof::(¶ms, message_payload); - let bridged_header_hash = insert_bridged_chain_header::(state_root); + let bridged_header_hash = insert_header_to_grandpa_pallet::(state_root); ( FromBridgedChainMessagesProof { @@ -103,7 +104,7 @@ pub fn prepare_message_delivery_proof( ) -> FromBridgedChainMessagesDeliveryProof>> where R: pallet_bridge_grandpa::Config, - R::BridgedChain: bp_runtime::Chain
, + R::BridgedChain: bp_runtime::Chain>, Header = BH>, FI: 'static, B: MessageBridge, BH: Header>>, @@ -131,7 +132,7 @@ where let storage_proof = proof_recorder.drain().into_iter().map(|n| n.data.to_vec()).collect(); // finally insert header with given state root to our storage - let bridged_header_hash = insert_bridged_chain_header::(root); + let bridged_header_hash = insert_header_to_grandpa_pallet::(root); FromBridgedChainMessagesDeliveryProof { bridged_header_hash: bridged_header_hash.into(), @@ -203,19 +204,16 @@ where (root, storage_proof) } -/// Insert Bridged chain header with given state root into storage of GRANDPA pallet at This chain. -fn insert_bridged_chain_header( - state_root: HashOf>, -) -> HashOf> +/// Insert header to the bridge GRANDPA pallet. +pub(crate) fn insert_header_to_grandpa_pallet( + state_root: bp_runtime::HashOf, +) -> bp_runtime::HashOf where - R: pallet_bridge_grandpa::Config, - R::BridgedChain: bp_runtime::Chain
, - FI: 'static, - B: MessageBridge, - BH: Header>>, - HashOf>: Default, + R: pallet_bridge_grandpa::Config, + GI: 'static, + R::BridgedChain: bp_runtime::Chain, { - let bridged_header = BH::new( + let bridged_header = bp_runtime::HeaderOf::::new( Zero::zero(), Default::default(), state_root, @@ -223,16 +221,20 @@ where Default::default(), ); let bridged_header_hash = bridged_header.hash(); - pallet_bridge_grandpa::initialize_for_benchmarks::(bridged_header); + pallet_bridge_grandpa::initialize_for_benchmarks::(bridged_header); bridged_header_hash } /// Populate trie with dummy keys+values until trie has at least given size. -fn grow_trie(mut root: H::Out, mdb: &mut MemoryDB, trie_size: ProofSize) -> H::Out { +pub fn grow_trie( + mut root: H::Out, + mdb: &mut MemoryDB, + trie_size: StorageProofSize, +) -> H::Out { let (iterations, leaf_size, minimal_trie_size) = match trie_size { - ProofSize::Minimal(_) => return root, - ProofSize::HasLargeLeaf(size) => (1, size, size), - ProofSize::HasExtraNodes(size) => (8, 1, size), + StorageProofSize::Minimal(_) => return root, + StorageProofSize::HasLargeLeaf(size) => (1, size, size), + StorageProofSize::HasExtraNodes(size) => (8, 1, size), }; let mut key_index = 0; diff --git a/bridges/bin/runtime-common/src/parachains_benchmarking.rs b/bridges/bin/runtime-common/src/parachains_benchmarking.rs new file mode 100644 index 0000000000..e2635eb254 --- /dev/null +++ b/bridges/bin/runtime-common/src/parachains_benchmarking.rs @@ -0,0 +1,80 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +//! Everything required to run benchmarks of parachains finality module. + +#![cfg(feature = "runtime-benchmarks")] + +use crate::messages_benchmarking::{grow_trie, insert_header_to_grandpa_pallet}; + +use bp_parachains::parachain_head_storage_key_at_source; +use bp_polkadot_core::parachains::{ParaHead, ParaHeadsProof, ParaId}; +use bp_runtime::StorageProofSize; +use codec::Encode; +use frame_support::traits::Get; +use pallet_bridge_parachains::{RelayBlockHash, RelayBlockHasher}; +use sp_std::prelude::*; +use sp_trie::{record_all_keys, trie_types::TrieDBMutV1, LayoutV1, MemoryDB, Recorder, TrieMut}; + +/// Prepare proof of messages for the `receive_messages_proof` call. +/// +/// In addition to returning valid messages proof, environment is prepared to verify this message +/// proof. +pub fn prepare_parachain_heads_proof( + parachains: &[ParaId], + parachain_head_size: u32, + size: StorageProofSize, +) -> (RelayBlockHash, ParaHeadsProof) +where + R: pallet_bridge_parachains::Config + + pallet_bridge_grandpa::Config, + PI: 'static, + >::BridgedChain: + bp_runtime::Chain, +{ + let parachain_head = ParaHead(vec![0u8; parachain_head_size as usize]); + + // insert all heads to the trie + let mut storage_keys = Vec::with_capacity(parachains.len()); + let mut state_root = Default::default(); + let mut mdb = MemoryDB::default(); + { + let mut trie = TrieDBMutV1::::new(&mut mdb, &mut state_root); + + // insert parachain heads + for parachain in parachains { + let storage_key = + parachain_head_storage_key_at_source(R::ParasPalletName::get(), *parachain); + trie.insert(&storage_key.0, ¶chain_head.encode()) + .map_err(|_| "TrieMut::insert has failed") + .expect("TrieMut::insert should not fail in benchmarks"); + storage_keys.push(storage_key); + } + } + state_root = grow_trie(state_root, &mut mdb, size); + + // generate heads storage proof + let mut proof_recorder = Recorder::::new(); + record_all_keys::, _>(&mdb, &state_root, &mut proof_recorder) + .map_err(|_| "record_all_keys has failed") + .expect("record_all_keys should not fail in benchmarks"); + let proof = proof_recorder.drain().into_iter().map(|n| n.data.to_vec()).collect(); + + let relay_block_hash = + insert_header_to_grandpa_pallet::(state_root); + + (relay_block_hash, ParaHeadsProof(proof)) +} diff --git a/bridges/modules/messages/src/benchmarking.rs b/bridges/modules/messages/src/benchmarking.rs index 46a8150d03..66aa70312c 100644 --- a/bridges/modules/messages/src/benchmarking.rs +++ b/bridges/modules/messages/src/benchmarking.rs @@ -26,7 +26,7 @@ use bp_messages::{ InboundLaneData, LaneId, MessageData, MessageNonce, OutboundLaneData, UnrewardedRelayer, UnrewardedRelayersState, }; -use bp_runtime::messages::DispatchFeePayment; +use bp_runtime::{messages::DispatchFeePayment, StorageProofSize}; use frame_benchmarking::{account, benchmarks_instance_pallet}; use frame_support::{traits::Get, weights::Weight}; use frame_system::RawOrigin; @@ -37,20 +37,6 @@ const SEED: u32 = 0; /// Pallet we're benchmarking here. pub struct Pallet, I: 'static>(crate::Pallet); -/// Proof size requirements. -#[derive(Clone, Copy, Debug)] -pub enum ProofSize { - /// The proof is expected to be minimal. If value size may be changed, then it is expected to - /// have given size. - Minimal(u32), - /// The proof is expected to have at least given size and grow by increasing number of trie - /// nodes included in the proof. - HasExtraNodes(u32), - /// The proof is expected to have at least given size and grow by increasing value that is - /// stored in the trie. - HasLargeLeaf(u32), -} - /// Benchmark-specific message parameters. #[derive(Debug)] pub struct MessageParams { @@ -70,7 +56,7 @@ pub struct MessageProofParams { /// If `Some`, the proof needs to include this outbound lane data. pub outbound_lane_data: Option, /// Proof size requirements. - pub size: ProofSize, + pub size: StorageProofSize, /// Where the fee for dispatching message is paid? pub dispatch_fee_payment: DispatchFeePayment, } @@ -83,7 +69,7 @@ pub struct MessageDeliveryProofParams { /// The proof needs to include this inbound lane data. pub inbound_lane_data: InboundLaneData, /// Proof size requirements. - pub size: ProofSize, + pub size: StorageProofSize, } /// Trait that must be implemented by runtime. @@ -302,7 +288,7 @@ benchmarks_instance_pallet! { lane: T::bench_lane_id(), message_nonces: 21..=21, outbound_lane_data: None, - size: ProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH), + size: StorageProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH), dispatch_fee_payment: DispatchFeePayment::AtTargetChain, }); }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight) @@ -337,7 +323,7 @@ benchmarks_instance_pallet! { lane: T::bench_lane_id(), message_nonces: 21..=22, outbound_lane_data: None, - size: ProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH), + size: StorageProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH), dispatch_fee_payment: DispatchFeePayment::AtTargetChain, }); }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 2, dispatch_weight) @@ -376,7 +362,7 @@ benchmarks_instance_pallet! { latest_received_nonce: 20, latest_generated_nonce: 21, }), - size: ProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH), + size: StorageProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH), dispatch_fee_payment: DispatchFeePayment::AtTargetChain, }); }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight) @@ -408,7 +394,7 @@ benchmarks_instance_pallet! { lane: T::bench_lane_id(), message_nonces: 21..=21, outbound_lane_data: None, - size: ProofSize::HasExtraNodes(1024), + size: StorageProofSize::HasExtraNodes(1024), dispatch_fee_payment: DispatchFeePayment::AtTargetChain, }); }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight) @@ -443,7 +429,7 @@ benchmarks_instance_pallet! { lane: T::bench_lane_id(), message_nonces: 21..=21, outbound_lane_data: None, - size: ProofSize::HasExtraNodes(16 * 1024), + size: StorageProofSize::HasExtraNodes(16 * 1024), dispatch_fee_payment: DispatchFeePayment::AtTargetChain, }); }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight) @@ -477,7 +463,7 @@ benchmarks_instance_pallet! { lane: T::bench_lane_id(), message_nonces: 21..=21, outbound_lane_data: None, - size: ProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH), + size: StorageProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH), dispatch_fee_payment: DispatchFeePayment::AtSourceChain, }); }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight) @@ -517,7 +503,7 @@ benchmarks_instance_pallet! { }].into_iter().collect(), last_confirmed_nonce: 0, }, - size: ProofSize::Minimal(0), + size: StorageProofSize::Minimal(0), }); }: receive_messages_delivery_proof(RawOrigin::Signed(relayer_id.clone()), proof, relayers_state) verify { @@ -560,7 +546,7 @@ benchmarks_instance_pallet! { }].into_iter().collect(), last_confirmed_nonce: 0, }, - size: ProofSize::Minimal(0), + size: StorageProofSize::Minimal(0), }); }: receive_messages_delivery_proof(RawOrigin::Signed(relayer_id.clone()), proof, relayers_state) verify { @@ -606,7 +592,7 @@ benchmarks_instance_pallet! { ].into_iter().collect(), last_confirmed_nonce: 0, }, - size: ProofSize::Minimal(0), + size: StorageProofSize::Minimal(0), }); }: receive_messages_delivery_proof(RawOrigin::Signed(relayer1_id.clone()), proof, relayers_state) verify { diff --git a/bridges/modules/parachains/Cargo.toml b/bridges/modules/parachains/Cargo.toml index e6942bf7ce..8636c7a40b 100644 --- a/bridges/modules/parachains/Cargo.toml +++ b/bridges/modules/parachains/Cargo.toml @@ -20,6 +20,7 @@ pallet-bridge-grandpa = { path = "../grandpa", default-features = false } # Substrate Dependencies +frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true } frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } @@ -50,3 +51,6 @@ std = [ "sp-std/std", "sp-trie/std", ] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", +] diff --git a/bridges/modules/parachains/src/benchmarking.rs b/bridges/modules/parachains/src/benchmarking.rs new file mode 100644 index 0000000000..2734dfafd6 --- /dev/null +++ b/bridges/modules/parachains/src/benchmarking.rs @@ -0,0 +1,105 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +//! Parachains finality pallet benchmarking. + +use crate::{ + weights_ext::DEFAULT_PARACHAIN_HEAD_SIZE, Call, RelayBlockHash, RelayBlockHasher, + RelayBlockNumber, +}; + +use bp_polkadot_core::parachains::{ParaHeadsProof, ParaId}; +use bp_runtime::StorageProofSize; +use frame_benchmarking::{account, benchmarks_instance_pallet}; +use frame_system::RawOrigin; +use sp_std::prelude::*; + +/// Pallet we're benchmarking here. +pub struct Pallet, I: 'static>(crate::Pallet); + +/// Trait that must be implemented by runtime to benchmark the parachains finality pallet. +pub trait Config: crate::Config { + /// Generate parachain heads proof and prepare environment for verifying this proof. + fn prepare_parachain_heads_proof( + parachains: &[ParaId], + parachain_head_size: u32, + proof_size: StorageProofSize, + ) -> (RelayBlockHash, ParaHeadsProof); +} + +benchmarks_instance_pallet! { + where_clause { + where + >::BridgedChain: + bp_runtime::Chain< + BlockNumber = RelayBlockNumber, + Hash = RelayBlockHash, + Hasher = RelayBlockHasher, + >, + } + + // Benchmark `submit_parachain_heads` extrinsic with different number of parachains. + submit_parachain_heads_with_n_parachains { + let p in 1..1024; + + let sender = account("sender", 0, 0); + let parachains = (1..=p).map(ParaId).collect::>(); + let (relay_block_hash, parachain_heads_proof) = T::prepare_parachain_heads_proof( + ¶chains, + DEFAULT_PARACHAIN_HEAD_SIZE, + StorageProofSize::Minimal(0), + ); + }: submit_parachain_heads(RawOrigin::Signed(sender), relay_block_hash, parachains.clone(), parachain_heads_proof) + verify { + for parachain in parachains { + assert!(crate::Pallet::::best_parachain_head(parachain).is_some()); + } + } + + // Benchmark `submit_parachain_heads` extrinsic with 1kb proof size. + submit_parachain_heads_with_1kb_proof { + let sender = account("sender", 0, 0); + let parachains = vec![ParaId(1)]; + let (relay_block_hash, parachain_heads_proof) = T::prepare_parachain_heads_proof( + ¶chains, + DEFAULT_PARACHAIN_HEAD_SIZE, + StorageProofSize::HasExtraNodes(1024), + ); + + }: submit_parachain_heads(RawOrigin::Signed(sender), relay_block_hash, parachains.clone(), parachain_heads_proof) + verify { + for parachain in parachains { + assert!(crate::Pallet::::best_parachain_head(parachain).is_some()); + } + } + + // Benchmark `submit_parachain_heads` extrinsic with 16kb proof size. + submit_parachain_heads_with_16kb_proof { + let sender = account("sender", 0, 0); + let parachains = vec![ParaId(1)]; + let (relay_block_hash, parachain_heads_proof) = T::prepare_parachain_heads_proof( + ¶chains, + DEFAULT_PARACHAIN_HEAD_SIZE, + StorageProofSize::HasExtraNodes(16 * 1024), + ); + + }: submit_parachain_heads(RawOrigin::Signed(sender), relay_block_hash, parachains.clone(), parachain_heads_proof) + verify { + for parachain in parachains { + assert!(crate::Pallet::::best_parachain_head(parachain).is_some()); + } + } +} diff --git a/bridges/modules/parachains/src/lib.rs b/bridges/modules/parachains/src/lib.rs index cafd715f8c..4cedffc300 100644 --- a/bridges/modules/parachains/src/lib.rs +++ b/bridges/modules/parachains/src/lib.rs @@ -23,11 +23,14 @@ #![cfg_attr(not(feature = "std"), no_std)] +pub use weights::WeightInfo; +pub use weights_ext::WeightInfoExt; + use bp_parachains::parachain_head_storage_key_at_source; use bp_polkadot_core::parachains::{ParaHash, ParaHasher, ParaHead, ParaHeadsProof, ParaId}; use bp_runtime::StorageProofError; use codec::{Decode, Encode}; -use frame_support::{traits::Contains, RuntimeDebug}; +use frame_support::{traits::Contains, weights::PostDispatchInfo, RuntimeDebug}; use scale_info::TypeInfo; use sp_runtime::traits::Header as HeaderT; use sp_std::vec::Vec; @@ -35,6 +38,12 @@ use sp_std::vec::Vec; // Re-export in crate namespace for `construct_runtime!`. pub use pallet::*; +pub mod weights; +pub mod weights_ext; + +#[cfg(feature = "runtime-benchmarks")] +pub mod benchmarking; + #[cfg(test)] mod mock; @@ -56,12 +65,23 @@ pub struct BestParaHead { pub next_imported_hash_position: u32, } +/// Artifacts of the parachains head update. +struct UpdateParachainHeadArtifacts { + /// New best head of the parachain. + pub best_head: BestParaHead, + /// If `true`, some old parachain head has been pruned during update. + pub prune_happened: bool, +} + #[frame_support::pallet] pub mod pallet { use super::*; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; + /// Weight info of the given parachains pallet. + pub type WeightInfoOf = >::WeightInfo; + #[pallet::error] pub enum Error { /// Relay chain block is unknown to us. @@ -81,6 +101,9 @@ pub mod pallet { pub trait Config: pallet_bridge_grandpa::Config { + /// Benchmarks results from runtime we're plugged into. + type WeightInfo: WeightInfoExt; + /// Instance of bridges GRANDPA pallet (within this runtime) that this pallet is linked to. /// /// The GRANDPA pallet instance must be configured to import headers of relay chain that @@ -145,13 +168,17 @@ pub mod pallet { /// `polkadot-runtime-parachains::paras` pallet instance, deployed at the bridged chain. /// The proof is supposed to be crafted at the `relay_header_hash` that must already be /// imported by corresponding GRANDPA pallet at this chain. - #[pallet::weight(0)] // TODO: https://github.com/paritytech/parity-bridges-common/issues/1391 + #[pallet::weight(WeightInfoOf::::submit_parachain_heads_weight( + T::DbWeight::get(), + parachain_heads_proof, + parachains.len() as _, + ))] pub fn submit_parachain_heads( _origin: OriginFor, relay_block_hash: RelayBlockHash, parachains: Vec, parachain_heads_proof: ParaHeadsProof, - ) -> DispatchResult { + ) -> DispatchResultWithPostInfo { // we'll need relay chain header to verify that parachains heads are always increasing. let relay_block = pallet_bridge_grandpa::ImportedHeaders::< T, @@ -161,9 +188,14 @@ pub mod pallet { let relay_block_number = *relay_block.number(); // now parse storage proof and read parachain heads + let mut actual_weight = WeightInfoOf::::submit_parachain_heads_weight( + T::DbWeight::get(), + ¶chain_heads_proof, + parachains.len() as _, + ); pallet_bridge_grandpa::Pallet::::parse_finalized_storage_proof( relay_block_hash, - sp_trie::StorageProof::new(parachain_heads_proof), + sp_trie::StorageProof::new(parachain_heads_proof.0), move |storage| { for parachain in parachains { // if we're not tracking this parachain, we'll just ignore its head proof here @@ -202,21 +234,27 @@ pub mod pallet { }, }; - let _: Result<_, ()> = BestParaHeads::::try_mutate(parachain, |stored_best_head| { - *stored_best_head = Some(Pallet::::update_parachain_head( + let prune_happened: Result<_, ()> = BestParaHeads::::try_mutate(parachain, |stored_best_head| { + let artifacts = Pallet::::update_parachain_head( parachain, stored_best_head.take(), relay_block_number, parachain_head, - )?); - Ok(()) + )?; + *stored_best_head = Some(artifacts.best_head); + Ok(artifacts.prune_happened) }); + + if matches!(prune_happened, Err(_) | Ok(false)) { + actual_weight = actual_weight + .saturating_sub(WeightInfoOf::::parachain_head_pruning_weight(T::DbWeight::get())); + } } }, ) .map_err(|_| Error::::InvalidStorageProof)?; - Ok(()) + Ok(PostDispatchInfo { actual_weight: Some(actual_weight), pays_fee: Pays::Yes }) } } @@ -269,7 +307,7 @@ pub mod pallet { stored_best_head: Option, updated_at_relay_block_number: RelayBlockNumber, updated_head: ParaHead, - ) -> Result { + ) -> Result { // check if head has been already updated at better relay chain block. Without this // check, we may import heads in random order let updated_head_hash = updated_head.hash(); @@ -332,6 +370,7 @@ pub mod pallet { ); // remove old head + let prune_happened = head_hash_to_prune.is_ok(); if let Ok(head_hash_to_prune) = head_hash_to_prune { log::trace!( target: "runtime::bridge-parachains", @@ -342,7 +381,7 @@ pub mod pallet { ImportedParaHeads::::remove(parachain, head_hash_to_prune); } - Ok(updated_best_para_head) + Ok(UpdateParachainHeadArtifacts { best_head: updated_best_para_head, prune_happened }) } } } @@ -355,7 +394,12 @@ mod tests { }; use bp_test_utils::{authority_list, make_default_justification}; - use frame_support::{assert_noop, assert_ok, traits::OnInitialize}; + use frame_support::{ + assert_noop, assert_ok, + dispatch::DispatchResultWithPostInfo, + traits::{Get, OnInitialize}, + weights::Weight, + }; use sp_trie::{ record_all_keys, trie_types::TrieDBMutV1, LayoutV1, MemoryDB, Recorder, TrieMut, }; @@ -414,7 +458,7 @@ mod tests { .expect("record_all_keys should not fail in benchmarks"); let storage_proof = proof_recorder.drain().into_iter().map(|n| n.data.to_vec()).collect(); - (root, storage_proof) + (root, ParaHeadsProof(storage_proof)) } fn initial_best_head(parachain: u32) -> BestParaHead { @@ -437,7 +481,7 @@ mod tests { relay_chain_block: RelayBlockNumber, relay_state_root: RelayBlockHash, proof: ParaHeadsProof, - ) -> sp_runtime::DispatchResult { + ) -> DispatchResultWithPostInfo { Pallet::::submit_parachain_heads( Origin::signed(1), test_relay_header(relay_chain_block, relay_state_root).hash(), @@ -446,6 +490,16 @@ mod tests { ) } + fn weight_of_import_parachain_1_head(proof: &ParaHeadsProof, prune_expected: bool) -> Weight { + let db_weight = ::DbWeight::get(); + WeightInfoOf::::submit_parachain_heads_weight(db_weight, proof, 1) + .saturating_sub(if prune_expected { + 0 + } else { + WeightInfoOf::::parachain_head_pruning_weight(db_weight) + }) + } + #[test] fn imports_initial_parachain_heads() { let (state_root, proof) = @@ -636,7 +690,11 @@ mod tests { } else { proceed(i, state_root); } - assert_ok!(import_parachain_1_head(i, state_root, proof)); + + let expected_weight = weight_of_import_parachain_1_head(&proof, false); + let result = import_parachain_1_head(i, state_root, proof); + assert_ok!(result); + assert_eq!(result.expect("checked above").actual_weight, Some(expected_weight)); } // nothing is pruned yet @@ -649,7 +707,10 @@ mod tests { let (state_root, proof) = prepare_parachain_heads_proof(vec![(1, head_data(1, heads_to_keep))]); proceed(heads_to_keep, state_root); - assert_ok!(import_parachain_1_head(heads_to_keep, state_root, proof)); + let expected_weight = weight_of_import_parachain_1_head(&proof, true); + let result = import_parachain_1_head(heads_to_keep, state_root, proof); + assert_ok!(result); + assert_eq!(result.expect("checked above").actual_weight, Some(expected_weight)); // and the head#0 is pruned assert!( diff --git a/bridges/modules/parachains/src/mock.rs b/bridges/modules/parachains/src/mock.rs index c314fe7ab1..e07b0a9101 100644 --- a/bridges/modules/parachains/src/mock.rs +++ b/bridges/modules/parachains/src/mock.rs @@ -112,6 +112,7 @@ parameter_types! { } impl pallet_bridge_parachains::Config for TestRuntime { + type WeightInfo = (); type BridgesGrandpaPalletInstance = pallet_bridge_grandpa::Instance1; type ParasPalletName = ParasPalletName; type TrackedParachains = IsInVec; diff --git a/bridges/modules/parachains/src/weights.rs b/bridges/modules/parachains/src/weights.rs new file mode 100644 index 0000000000..e203e3ef52 --- /dev/null +++ b/bridges/modules/parachains/src/weights.rs @@ -0,0 +1,98 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +//! Autogenerated weights for `pallet_bridge_parachains` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2022-06-06, STEPS: 50, REPEAT: 20 +//! LOW RANGE: [], HIGH RANGE: [] +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled +//! CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/millau-bridge-node +// benchmark +// pallet +// --chain=dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_bridge_parachains +// --extrinsic=* +// --execution=wasm +// --wasm-execution=Compiled +// --heap-pages=4096 +// --output=./modules/parachains/src/weights.rs +// --template=./.maintain/millau-weight-template.hbs + +#![allow(clippy::all)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{ + traits::Get, + weights::{constants::RocksDbWeight, Weight}, +}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for `pallet_bridge_parachains`. +pub trait WeightInfo { + fn submit_parachain_heads_with_n_parachains(p: u32) -> Weight; + fn submit_parachain_heads_with_1kb_proof() -> Weight; + fn submit_parachain_heads_with_16kb_proof() -> Weight; +} + +/// Weights for `pallet_bridge_parachains` using the Millau node and recommended hardware. +pub struct MillauWeight(PhantomData); +impl WeightInfo for MillauWeight { + fn submit_parachain_heads_with_n_parachains(p: u32) -> Weight { + (0 as Weight) + .saturating_add((18_729_000 as Weight).saturating_mul(p as Weight)) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().reads((2 as Weight).saturating_mul(p as Weight))) + .saturating_add(T::DbWeight::get().writes((3 as Weight).saturating_mul(p as Weight))) + } + fn submit_parachain_heads_with_1kb_proof() -> Weight { + (25_355_000 as Weight) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) + } + fn submit_parachain_heads_with_16kb_proof() -> Weight { + (74_570_000 as Weight) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + fn submit_parachain_heads_with_n_parachains(p: u32) -> Weight { + (0 as Weight) + .saturating_add((18_729_000 as Weight).saturating_mul(p as Weight)) + .saturating_add(RocksDbWeight::get().reads(1 as Weight)) + .saturating_add(RocksDbWeight::get().reads((2 as Weight).saturating_mul(p as Weight))) + .saturating_add(RocksDbWeight::get().writes((3 as Weight).saturating_mul(p as Weight))) + } + fn submit_parachain_heads_with_1kb_proof() -> Weight { + (25_355_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(3 as Weight)) + .saturating_add(RocksDbWeight::get().writes(3 as Weight)) + } + fn submit_parachain_heads_with_16kb_proof() -> Weight { + (74_570_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(3 as Weight)) + .saturating_add(RocksDbWeight::get().writes(3 as Weight)) + } +} diff --git a/bridges/modules/parachains/src/weights_ext.rs b/bridges/modules/parachains/src/weights_ext.rs new file mode 100644 index 0000000000..3f3815eed9 --- /dev/null +++ b/bridges/modules/parachains/src/weights_ext.rs @@ -0,0 +1,97 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +//! Weight-related utilities. + +use crate::weights::{MillauWeight, WeightInfo}; + +use bp_runtime::Size; +use frame_support::weights::{RuntimeDbWeight, Weight}; + +/// Size of the regular parachain head. +/// +/// It's not that we are expecting all parachain heads to share the same size or that we would +/// reject all heads that have larger/lesser size. It is about head size that we use in benchmarks. +/// Relayer would need to pay additional fee for extra bytes. +/// +/// 384 is a bit larger (1.3 times) than the size of the randomly chosen Polkadot block. +pub const DEFAULT_PARACHAIN_HEAD_SIZE: u32 = 384; + +/// Number of extra bytes (excluding size of storage value itself) of storage proof, built at +/// the Rialto chain. +pub const EXTRA_STORAGE_PROOF_SIZE: u32 = 1024; + +/// Extended weight info. +pub trait WeightInfoExt: WeightInfo { + /// Storage proof overhead, that is included in every storage proof. + /// + /// The relayer would pay some extra fee for additional proof bytes, since they mean + /// more hashing operations. + fn expected_extra_storage_proof_size() -> u32; + + /// Weight of the parachain heads delivery extrinsic. + fn submit_parachain_heads_weight( + db_weight: RuntimeDbWeight, + proof: &impl Size, + parachains_count: u32, + ) -> Weight { + // weight of the `submit_parachain_heads` with exactly `parachains_count` parachain + // heads of the default size (`DEFAULT_PARACHAIN_HEAD_SIZE`) + let base_weight = Self::submit_parachain_heads_with_n_parachains(parachains_count); + + // overhead because of extra storage proof bytes + let expected_proof_size = parachains_count + .saturating_mul(DEFAULT_PARACHAIN_HEAD_SIZE) + .saturating_add(Self::expected_extra_storage_proof_size()); + let actual_proof_size = proof.size_hint(); + let proof_size_overhead = Self::storage_proof_size_overhead( + actual_proof_size.saturating_sub(expected_proof_size), + ); + + // potential pruning weight (refunded if hasn't happened) + let pruning_weight = (parachains_count as Weight) + .saturating_mul(Self::parachain_head_pruning_weight(db_weight)); + + base_weight.saturating_add(proof_size_overhead).saturating_add(pruning_weight) + } + + /// Returns weight of single parachain head pruning. + fn parachain_head_pruning_weight(db_weight: RuntimeDbWeight) -> Weight { + // it's just one write operation, we don't want any benchmarks for that + db_weight.writes(1) + } + + /// Returns weight that needs to be accounted when storage proof of given size is received. + fn storage_proof_size_overhead(extra_proof_bytes: u32) -> Weight { + let extra_proof_bytes_in_bytes = extra_proof_bytes as Weight; + let extra_byte_weight = (Self::submit_parachain_heads_with_16kb_proof() - + Self::submit_parachain_heads_with_1kb_proof()) / + (15 * 1024); + extra_proof_bytes_in_bytes.saturating_mul(extra_byte_weight) + } +} + +impl WeightInfoExt for () { + fn expected_extra_storage_proof_size() -> u32 { + EXTRA_STORAGE_PROOF_SIZE + } +} + +impl WeightInfoExt for MillauWeight { + fn expected_extra_storage_proof_size() -> u32 { + EXTRA_STORAGE_PROOF_SIZE + } +} diff --git a/bridges/primitives/polkadot-core/src/parachains.rs b/bridges/primitives/polkadot-core/src/parachains.rs index 8980c46c6e..af05e7e985 100644 --- a/bridges/primitives/polkadot-core/src/parachains.rs +++ b/bridges/primitives/polkadot-core/src/parachains.rs @@ -22,6 +22,7 @@ //! chains. Having pallets that are referencing polkadot, would mean that there may //! be two versions of polkadot crates included in the runtime. Which is bad. +use bp_runtime::Size; use frame_support::RuntimeDebug; use parity_scale_codec::{CompactAs, Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; @@ -88,4 +89,12 @@ pub type ParaHash = crate::Hash; pub type ParaHasher = crate::Hasher; /// Raw storage proof of parachain heads, stored in polkadot-like chain runtime. -pub type ParaHeadsProof = Vec>; +#[derive(Clone, Decode, Encode, Eq, PartialEq, RuntimeDebug, TypeInfo)] +pub struct ParaHeadsProof(pub Vec>); + +impl Size for ParaHeadsProof { + fn size_hint(&self) -> u32 { + u32::try_from(self.0.iter().fold(0usize, |sum, node| sum.saturating_add(node.len()))) + .unwrap_or(u32::MAX) + } +} diff --git a/bridges/primitives/runtime/src/lib.rs b/bridges/primitives/runtime/src/lib.rs index cd3ea448e1..bd09a651c7 100644 --- a/bridges/primitives/runtime/src/lib.rs +++ b/bridges/primitives/runtime/src/lib.rs @@ -29,7 +29,9 @@ pub use chain::{ HasherOf, HeaderOf, IndexOf, SignatureOf, TransactionEraOf, }; pub use frame_support::storage::storage_prefix as storage_value_final_key; -pub use storage_proof::{Error as StorageProofError, StorageProofChecker}; +pub use storage_proof::{ + Error as StorageProofError, ProofSize as StorageProofSize, StorageProofChecker, +}; #[cfg(feature = "std")] pub use storage_proof::craft_valid_storage_proof; diff --git a/bridges/primitives/runtime/src/storage_proof.rs b/bridges/primitives/runtime/src/storage_proof.rs index 24b7dac97e..8236c0f223 100644 --- a/bridges/primitives/runtime/src/storage_proof.rs +++ b/bridges/primitives/runtime/src/storage_proof.rs @@ -22,6 +22,22 @@ use sp_runtime::RuntimeDebug; use sp_std::vec::Vec; use sp_trie::{read_trie_value, LayoutV1, MemoryDB, StorageProof}; +/// Storage proof size requirements. +/// +/// This is currently used by benchmarks when generating storage proofs. +#[derive(Clone, Copy, Debug)] +pub enum ProofSize { + /// The proof is expected to be minimal. If value size may be changed, then it is expected to + /// have given size. + Minimal(u32), + /// The proof is expected to have at least given size and grow by increasing number of trie + /// nodes included in the proof. + HasExtraNodes(u32), + /// The proof is expected to have at least given size and grow by increasing value that is + /// stored in the trie. + HasLargeLeaf(u32), +} + /// This struct is used to read storage values from a subset of a Merklized database. The "proof" /// is a subset of the nodes in the Merkle structure of the database, so that it provides /// authentication against a known Merkle root as well as the values in the database themselves. diff --git a/bridges/relays/bin-substrate/src/chains/rialto_parachains_to_millau.rs b/bridges/relays/bin-substrate/src/chains/rialto_parachains_to_millau.rs index 078c24a3e3..986c335e1e 100644 --- a/bridges/relays/bin-substrate/src/chains/rialto_parachains_to_millau.rs +++ b/bridges/relays/bin-substrate/src/chains/rialto_parachains_to_millau.rs @@ -49,5 +49,5 @@ pub type RialtoParachainsToMillauSubmitParachainHeadsCallBuilder = DirectSubmitParachainHeadsCallBuilder< RialtoParachainsToMillau, millau_runtime::Runtime, - millau_runtime::WitRialtoParachainsInstance, + millau_runtime::WithRialtoParachainsInstance, >; diff --git a/bridges/relays/lib-substrate-relay/src/parachains/source.rs b/bridges/relays/lib-substrate-relay/src/parachains/source.rs index ea30143e4b..dd23936671 100644 --- a/bridges/relays/lib-substrate-relay/src/parachains/source.rs +++ b/bridges/relays/lib-substrate-relay/src/parachains/source.rs @@ -161,6 +161,6 @@ where .iter_nodes() .collect(); - Ok(parachain_heads_proof) + Ok(ParaHeadsProof(parachain_heads_proof)) } } diff --git a/bridges/relays/parachains/src/parachains_loop.rs b/bridges/relays/parachains/src/parachains_loop.rs index 6e9cc5f1da..b1dd741462 100644 --- a/bridges/relays/parachains/src/parachains_loop.rs +++ b/bridges/relays/parachains/src/parachains_loop.rs @@ -664,7 +664,7 @@ mod tests { .ok_or(TestError::MissingParachainHeadProof)?, ); } - Ok(proofs) + Ok(ParaHeadsProof(proofs)) } }