Parachains pallet benchmarks (#1436)

* added parachains pallet benchmarks

* deduplicate insertion of bridged header

* pruning weight

* fixes

* fix compilation
This commit is contained in:
Svyatoslav Nikolsky
2022-06-07 10:50:55 +03:00
committed by Bastian Köcher
parent 690a929cf6
commit b870fe74f8
20 changed files with 565 additions and 74 deletions
+1
View File
@@ -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",
+29 -3
View File
@@ -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<WitRialtoParachainsInstance> for Runtime {
impl pallet_bridge_parachains::Config<WithRialtoParachainsInstance> for Runtime {
type WeightInfo = pallet_bridge_parachains::weights::MillauWeight<Runtime>;
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::<BenchmarkList>::new();
list_benchmark!(list, extra, pallet_bridge_messages, MessagesBench::<Runtime, WithRialtoMessagesInstance>);
list_benchmark!(list, extra, pallet_bridge_grandpa, BridgeRialtoGrandpa);
list_benchmark!(list, extra, pallet_bridge_parachains, ParachainsBench::<Runtime, WithRialtoMessagesInstance>);
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<WithRialtoMessagesInstance> for Runtime {
@@ -991,6 +998,19 @@ impl_runtime_apis! {
}
}
impl ParachainsConfig<WithRialtoParachainsInstance> 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::<Runtime, WithRialtoParachainsInstance>(
parachains,
parachain_head_size,
proof_size,
)
}
}
add_benchmark!(
params,
@@ -999,6 +1019,12 @@ impl_runtime_apis! {
MessagesBench::<Runtime, WithRialtoMessagesInstance>
);
add_benchmark!(params, batches, pallet_bridge_grandpa, BridgeRialtoGrandpa);
add_benchmark!(
params,
batches,
pallet_bridge_parachains,
ParachainsBench::<Runtime, WithRialtoParachainsInstance>
);
Ok(batches)
}
@@ -252,7 +252,7 @@ impl TargetHeaderChain<ToRialtoParachainMessagePayload, bp_rialto_parachain::Acc
WithRialtoParachainMessageBridge,
bp_rialto_parachain::Header,
Runtime,
crate::WitRialtoParachainsInstance,
crate::WithRialtoParachainsInstance,
>(ParaId(bp_rialto_parachain::RIALTO_PARACHAIN_ID), proof)
}
}
@@ -274,7 +274,7 @@ impl SourceHeaderChain<bp_rialto_parachain::Balance> for RialtoParachain {
WithRialtoParachainMessageBridge,
bp_rialto_parachain::Header,
Runtime,
crate::WitRialtoParachainsInstance,
crate::WithRialtoParachainsInstance,
>(ParaId(bp_rialto_parachain::RIALTO_PARACHAIN_ID), proof, messages_count)
}
}
+2
View File
@@ -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",
+1
View File
@@ -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;
@@ -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<AccountId = AccountIdOf<ThisChain<B>>>
+ pallet_balances::Config<BI, Balance = BalanceOf<ThisChain<B>>>
+ pallet_bridge_grandpa::Config<FI>,
R::BridgedChain: bp_runtime::Chain<Header = BH>,
R::BridgedChain: bp_runtime::Chain<Hash = HashOf<BridgedChain<B>>, Header = BH>,
B: MessageBridge,
BI: 'static,
FI: 'static,
@@ -76,14 +77,14 @@ where
+ IdentifyAccount<AccountId = AccountIdOf<ThisChain<B>>>,
{
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::<B, BHH>(&params, message_payload);
let bridged_header_hash = insert_bridged_chain_header::<R, FI, B, BH>(state_root);
let bridged_header_hash = insert_header_to_grandpa_pallet::<R, FI>(state_root);
(
FromBridgedChainMessagesProof {
@@ -103,7 +104,7 @@ pub fn prepare_message_delivery_proof<R, FI, B, BH, BHH>(
) -> FromBridgedChainMessagesDeliveryProof<HashOf<BridgedChain<B>>>
where
R: pallet_bridge_grandpa::Config<FI>,
R::BridgedChain: bp_runtime::Chain<Header = BH>,
R::BridgedChain: bp_runtime::Chain<Hash = HashOf<BridgedChain<B>>, Header = BH>,
FI: 'static,
B: MessageBridge,
BH: Header<Hash = HashOf<BridgedChain<B>>>,
@@ -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::<R, FI, B, BH>(root);
let bridged_header_hash = insert_header_to_grandpa_pallet::<R, FI>(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<R, FI, B, BH>(
state_root: HashOf<BridgedChain<B>>,
) -> HashOf<BridgedChain<B>>
/// Insert header to the bridge GRANDPA pallet.
pub(crate) fn insert_header_to_grandpa_pallet<R, GI>(
state_root: bp_runtime::HashOf<R::BridgedChain>,
) -> bp_runtime::HashOf<R::BridgedChain>
where
R: pallet_bridge_grandpa::Config<FI>,
R::BridgedChain: bp_runtime::Chain<Header = BH>,
FI: 'static,
B: MessageBridge,
BH: Header<Hash = HashOf<BridgedChain<B>>>,
HashOf<BridgedChain<B>>: Default,
R: pallet_bridge_grandpa::Config<GI>,
GI: 'static,
R::BridgedChain: bp_runtime::Chain,
{
let bridged_header = BH::new(
let bridged_header = bp_runtime::HeaderOf::<R::BridgedChain>::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::<R, FI>(bridged_header);
pallet_bridge_grandpa::initialize_for_benchmarks::<R, GI>(bridged_header);
bridged_header_hash
}
/// Populate trie with dummy keys+values until trie has at least given size.
fn grow_trie<H: Hasher>(mut root: H::Out, mdb: &mut MemoryDB<H>, trie_size: ProofSize) -> H::Out {
pub fn grow_trie<H: Hasher>(
mut root: H::Out,
mdb: &mut MemoryDB<H>,
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;
@@ -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 <http://www.gnu.org/licenses/>.
//! 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<R, PI>(
parachains: &[ParaId],
parachain_head_size: u32,
size: StorageProofSize,
) -> (RelayBlockHash, ParaHeadsProof)
where
R: pallet_bridge_parachains::Config<PI>
+ pallet_bridge_grandpa::Config<R::BridgesGrandpaPalletInstance>,
PI: 'static,
<R as pallet_bridge_grandpa::Config<R::BridgesGrandpaPalletInstance>>::BridgedChain:
bp_runtime::Chain<Hash = RelayBlockHash>,
{
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::<RelayBlockHasher>::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, &parachain_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::<RelayBlockHash>::new();
record_all_keys::<LayoutV1<RelayBlockHasher>, _>(&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::<R, R::BridgesGrandpaPalletInstance>(state_root);
(relay_block_hash, ParaHeadsProof(proof))
}
+12 -26
View File
@@ -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<T: Config<I>, I: 'static>(crate::Pallet<T, I>);
/// 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<ThisAccountId> {
@@ -70,7 +56,7 @@ pub struct MessageProofParams {
/// If `Some`, the proof needs to include this outbound lane data.
pub outbound_lane_data: Option<OutboundLaneData>,
/// 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<ThisChainAccountId> {
/// The proof needs to include this inbound lane data.
pub inbound_lane_data: InboundLaneData<ThisChainAccountId>,
/// 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 {
+4
View File
@@ -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",
]
@@ -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 <http://www.gnu.org/licenses/>.
//! 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<T: Config<I>, I: 'static>(crate::Pallet<T, I>);
/// Trait that must be implemented by runtime to benchmark the parachains finality pallet.
pub trait Config<I: 'static>: crate::Config<I> {
/// 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
<T as pallet_bridge_grandpa::Config<T::BridgesGrandpaPalletInstance>>::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::<Vec<_>>();
let (relay_block_hash, parachain_heads_proof) = T::prepare_parachain_heads_proof(
&parachains,
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::<T, I>::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(
&parachains,
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::<T, I>::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(
&parachains,
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::<T, I>::best_parachain_head(parachain).is_some());
}
}
}
+77 -16
View File
@@ -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<T, I> = <T as Config<I>>::WeightInfo;
#[pallet::error]
pub enum Error<T, I = ()> {
/// Relay chain block is unknown to us.
@@ -81,6 +101,9 @@ pub mod pallet {
pub trait Config<I: 'static = ()>:
pallet_bridge_grandpa::Config<Self::BridgesGrandpaPalletInstance>
{
/// 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::<T, I>::submit_parachain_heads_weight(
T::DbWeight::get(),
parachain_heads_proof,
parachains.len() as _,
))]
pub fn submit_parachain_heads(
_origin: OriginFor<T>,
relay_block_hash: RelayBlockHash,
parachains: Vec<ParaId>,
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::<T, I>::submit_parachain_heads_weight(
T::DbWeight::get(),
&parachain_heads_proof,
parachains.len() as _,
);
pallet_bridge_grandpa::Pallet::<T, T::BridgesGrandpaPalletInstance>::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::<T, I>::try_mutate(parachain, |stored_best_head| {
*stored_best_head = Some(Pallet::<T, I>::update_parachain_head(
let prune_happened: Result<_, ()> = BestParaHeads::<T, I>::try_mutate(parachain, |stored_best_head| {
let artifacts = Pallet::<T, I>::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::<T, I>::parachain_head_pruning_weight(T::DbWeight::get()));
}
}
},
)
.map_err(|_| Error::<T, I>::InvalidStorageProof)?;
Ok(())
Ok(PostDispatchInfo { actual_weight: Some(actual_weight), pays_fee: Pays::Yes })
}
}
@@ -269,7 +307,7 @@ pub mod pallet {
stored_best_head: Option<BestParaHead>,
updated_at_relay_block_number: RelayBlockNumber,
updated_head: ParaHead,
) -> Result<BestParaHead, ()> {
) -> Result<UpdateParachainHeadArtifacts, ()> {
// 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::<T, I>::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::<TestRuntime>::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 = <TestRuntime as frame_system::Config>::DbWeight::get();
WeightInfoOf::<TestRuntime, ()>::submit_parachain_heads_weight(db_weight, proof, 1)
.saturating_sub(if prune_expected {
0
} else {
WeightInfoOf::<TestRuntime, ()>::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!(
+1
View File
@@ -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<GetTenFirstParachains>;
+98
View File
@@ -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 <http://www.gnu.org/licenses/>.
//! 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<T>(PhantomData<T>);
impl<T: frame_system::Config> WeightInfo for MillauWeight<T> {
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))
}
}
@@ -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 <http://www.gnu.org/licenses/>.
//! 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<T: frame_system::Config> WeightInfoExt for MillauWeight<T> {
fn expected_extra_storage_proof_size() -> u32 {
EXTRA_STORAGE_PROOF_SIZE
}
}
@@ -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<Vec<u8>>;
#[derive(Clone, Decode, Encode, Eq, PartialEq, RuntimeDebug, TypeInfo)]
pub struct ParaHeadsProof(pub Vec<Vec<u8>>);
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)
}
}
+3 -1
View File
@@ -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;
@@ -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.
@@ -49,5 +49,5 @@ pub type RialtoParachainsToMillauSubmitParachainHeadsCallBuilder =
DirectSubmitParachainHeadsCallBuilder<
RialtoParachainsToMillau,
millau_runtime::Runtime,
millau_runtime::WitRialtoParachainsInstance,
millau_runtime::WithRialtoParachainsInstance,
>;
@@ -161,6 +161,6 @@ where
.iter_nodes()
.collect();
Ok(parachain_heads_proof)
Ok(ParaHeadsProof(parachain_heads_proof))
}
}
@@ -664,7 +664,7 @@ mod tests {
.ok_or(TestError::MissingParachainHeadProof)?,
);
}
Ok(proofs)
Ok(ParaHeadsProof(proofs))
}
}