mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 15:11:02 +00:00
Weights for pallet-bridge-grandpa (#815)
* Add benchmarking skeleton * Allow runtime to indicate concrete header type for benches * Set up skeleton for benchmark tests * Play around with mutating bench header * Create a working test for benchmarking * Add benches related to enacting authority set changes * Add bench for checking effect of prec-commits/vote ancestries * Use new `no_std` test utils in benchmarks * Support pallet instances in benchmarking * Use correct benchmarking instance macro * Add instance to runtime benchmark helper impl * Start using new justification creation API * Allow mock header's number to be specified * Set up benches with correct fork/depth parameters * Use new pallet name during runtime bench setup * Use correct `set_id` in tests * Limit number of forks as workaround to get tests passing * Use number of authorities which matches number of forks * Make sure test post-conditions are checked properly * Only read `CurrentAuthoritySet` from storage once * Add combined benchmark for `submit_finality_proof` * Add bench test * Introduce config bounds related to justification verification * Use config consts from pallet in benchmarking * Return data relevant to benchmarks from helper functions * Annotate `submit_finality_proof` with autogenerated weights * Return actual weight after call execution * Ignore Clippy warnings in bench template * Update benchmark template * Use `test-utils` to create test headers * Clarify that helper is only for messages benches * Add more documentation to benches * Update TODOs * Clarify return types in comment * Fix pallet name post-merge * Update NOTE to a TODO item * Indicate that Config params are max values, not actual values * Change Config validator count type to be `u32` * Return decoded justification instead of fields * Add missing trait bounds for tests * Correctly issue weight refund Thanks for spotting this Tomek! * Update comment * Add note about SESSION_LENGTH * Fix benchmarking code
This commit is contained in:
committed by
Bastian Köcher
parent
67cdca8aa4
commit
025a9cad59
@@ -311,18 +311,34 @@ parameter_types! {
|
||||
// Note that once this is hit the pallet will essentially throttle incoming requests down to one
|
||||
// call per block.
|
||||
pub const MaxRequests: u32 = 50;
|
||||
pub const WestendSessionLength: bp_westend::BlockNumber = bp_westend::SESSION_LENGTH;
|
||||
pub const RialtoSessionLength: bp_rialto::BlockNumber = bp_rialto::SESSION_LENGTH;
|
||||
|
||||
// TODO [#846]: Right now this will break benchmarking if it is greater than `u8::MAX`
|
||||
pub const RialtoValidatorCount: u32 = 255;
|
||||
pub const WestendValidatorCount: u32 = 255;
|
||||
}
|
||||
|
||||
pub type RialtoGrandpaInstance = ();
|
||||
impl pallet_bridge_grandpa::Config for Runtime {
|
||||
type BridgedChain = bp_rialto::Rialto;
|
||||
type MaxRequests = MaxRequests;
|
||||
type MaxBridgedSessionLength = RialtoSessionLength;
|
||||
type MaxBridgedValidatorCount = RialtoValidatorCount;
|
||||
|
||||
// TODO [#391]: Use weights generated for the Millau runtime instead of Rialto ones.
|
||||
type WeightInfo = pallet_bridge_grandpa::weights::RialtoWeight<Runtime>;
|
||||
}
|
||||
|
||||
pub type WestendGrandpaInstance = pallet_bridge_grandpa::Instance1;
|
||||
impl pallet_bridge_grandpa::Config<WestendGrandpaInstance> for Runtime {
|
||||
type BridgedChain = bp_westend::Westend;
|
||||
type MaxRequests = MaxRequests;
|
||||
type MaxBridgedSessionLength = WestendSessionLength;
|
||||
type MaxBridgedValidatorCount = WestendValidatorCount;
|
||||
|
||||
// TODO [#391]: Use weights generated for the Millau runtime instead of Rialto ones.
|
||||
type WeightInfo = pallet_bridge_grandpa::weights::RialtoWeight<Runtime>;
|
||||
}
|
||||
|
||||
impl pallet_shift_session_manager::Config for Runtime {}
|
||||
|
||||
@@ -417,11 +417,18 @@ parameter_types! {
|
||||
// Note that once this is hit the pallet will essentially throttle incoming requests down to one
|
||||
// call per block.
|
||||
pub const MaxRequests: u32 = 50;
|
||||
pub const MillauSessionLength: bp_millau::BlockNumber = bp_millau::SESSION_LENGTH;
|
||||
|
||||
// TODO [#846]: Right now this will break benchmarking if it is greater than `u8::MAX`
|
||||
pub const MillauValidatorCount: u32 = 255;
|
||||
}
|
||||
|
||||
impl pallet_bridge_grandpa::Config for Runtime {
|
||||
type BridgedChain = bp_millau::Millau;
|
||||
type MaxRequests = MaxRequests;
|
||||
type MaxBridgedSessionLength = MillauSessionLength;
|
||||
type MaxBridgedValidatorCount = MillauValidatorCount;
|
||||
type WeightInfo = pallet_bridge_grandpa::weights::RialtoWeight<Runtime>;
|
||||
}
|
||||
|
||||
impl pallet_shift_session_manager::Config for Runtime {}
|
||||
@@ -799,6 +806,7 @@ impl_runtime_apis! {
|
||||
config: frame_benchmarking::BenchmarkConfig,
|
||||
) -> Result<Vec<frame_benchmarking::BenchmarkBatch>, sp_runtime::RuntimeString> {
|
||||
use frame_benchmarking::{Benchmarking, BenchmarkBatch, TrackedStorageKey, add_benchmark};
|
||||
|
||||
let whitelist: Vec<TrackedStorageKey> = vec![
|
||||
// Block Number
|
||||
hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef702a5c1b19ab7a04f536c519aca4983ac").to_vec().into(),
|
||||
@@ -945,6 +953,7 @@ impl_runtime_apis! {
|
||||
>(
|
||||
&lane_id,
|
||||
).0;
|
||||
|
||||
let make_millau_header = |state_root| bp_millau::Header::new(
|
||||
0,
|
||||
Default::default(),
|
||||
@@ -1005,7 +1014,6 @@ impl_runtime_apis! {
|
||||
}
|
||||
}
|
||||
|
||||
add_benchmark!(params, batches, pallet_bridge_eth_poa, BridgeKovan);
|
||||
add_benchmark!(
|
||||
params,
|
||||
batches,
|
||||
@@ -1018,6 +1026,7 @@ impl_runtime_apis! {
|
||||
pallet_bridge_messages,
|
||||
MessagesBench::<Runtime, WithMillauMessagesInstance>
|
||||
);
|
||||
add_benchmark!(params, batches, pallet_bridge_grandpa, BridgeMillauGrandpa);
|
||||
|
||||
if batches.is_empty() { return Err("Benchmark not found for this pallet.".into()) }
|
||||
Ok(batches)
|
||||
|
||||
@@ -11,6 +11,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
|
||||
codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false }
|
||||
finality-grandpa = { version = "0.14.0", default-features = false }
|
||||
log = { version = "0.4.14", default-features = false }
|
||||
num-traits = { version = "0.2", default-features = false }
|
||||
serde = { version = "1.0", optional = true }
|
||||
|
||||
# Bridge Dependencies
|
||||
@@ -27,25 +28,33 @@ sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master
|
||||
sp-std = { git = "https://github.com/paritytech/substrate", branch = "master" , default-features = false }
|
||||
sp-trie = { git = "https://github.com/paritytech/substrate", branch = "master" , default-features = false }
|
||||
|
||||
# Optional Benchmarking Dependencies
|
||||
bp-test-utils = { path = "../../primitives/test-utils", default-features = false, optional = true }
|
||||
frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "master" , default-features = false, optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
bp-test-utils = {path = "../../primitives/test-utils" }
|
||||
pallet-substrate-bridge = { path = "../../modules/substrate" }
|
||||
sp-io = { git = "https://github.com/paritytech/substrate", branch = "master" }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = [
|
||||
"bp-runtime/std",
|
||||
"bp-header-chain/std",
|
||||
"bp-runtime/std",
|
||||
"bp-test-utils/std",
|
||||
"codec/std",
|
||||
"finality-grandpa/std",
|
||||
"frame-support/std",
|
||||
"frame-system/std",
|
||||
"log/std",
|
||||
"num-traits/std",
|
||||
"serde",
|
||||
"sp-finality-grandpa/std",
|
||||
"sp-runtime/std",
|
||||
"sp-std/std",
|
||||
"sp-trie/std",
|
||||
]
|
||||
runtime-benchmarks = []
|
||||
runtime-benchmarks = [
|
||||
"bp-test-utils",
|
||||
"frame-benchmarking",
|
||||
]
|
||||
|
||||
@@ -0,0 +1,264 @@
|
||||
// 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/>.
|
||||
|
||||
//! Benchmarks for the GRANDPA Pallet.
|
||||
//!
|
||||
//! The main dispatchable for the GRANDPA pallet is `submit_finality_proof`, so these benchmarks are
|
||||
//! based around that. There are to main factors which affect finality proof verification:
|
||||
//!
|
||||
//! 1. The number of `votes-ancestries` in the justification
|
||||
//! 2. The number of `pre-commits` in the justification
|
||||
//!
|
||||
//! Vote ancestries are the headers between (`finality_target`, `head_of_chain`], where
|
||||
//! `header_of_chain` is a decendant of `finality_target`.
|
||||
//!
|
||||
//! Pre-commits are messages which are signed by validators at the head of the chain they think is
|
||||
//! the best.
|
||||
//!
|
||||
//! Consider the following:
|
||||
//!
|
||||
//! / [B'] <- [C']
|
||||
//! [A] <- [B] <- [C]
|
||||
//!
|
||||
//! The common ancestor of both forks is block A, so this is what GRANDPA will finalize. In order to
|
||||
//! verify this we will have vote ancestries of [B, C, B', C'] and pre-commits [C, C'].
|
||||
//!
|
||||
//! Note that the worst case scenario here would be a justification where each validator has it's
|
||||
//! own fork which is `SESSION_LENGTH` blocks long.
|
||||
//!
|
||||
//! As far as benchmarking results go, the only benchmark that should be used in
|
||||
//! `pallet-bridge-grandpa` to annotate weights is the `submit_finality_proof` one. The others are
|
||||
//! looking at the effects of specific code paths and do not actually reflect the overall worst case
|
||||
//! scenario.
|
||||
|
||||
use crate::*;
|
||||
|
||||
use bp_test_utils::{
|
||||
accounts, authority_list, make_justification_for_header, test_keyring, JustificationGeneratorParams, ALICE,
|
||||
TEST_GRANDPA_ROUND, TEST_GRANDPA_SET_ID,
|
||||
};
|
||||
use frame_benchmarking::{benchmarks_instance_pallet, whitelisted_caller};
|
||||
use frame_support::traits::Get;
|
||||
use frame_system::RawOrigin;
|
||||
use num_traits::cast::AsPrimitive;
|
||||
use sp_finality_grandpa::AuthorityId;
|
||||
use sp_runtime::traits::{One, Zero};
|
||||
use sp_std::{vec, vec::Vec};
|
||||
|
||||
benchmarks_instance_pallet! {
|
||||
// This is the "gold standard" benchmark for this extrinsic, and it's what should be used to
|
||||
// annotate the weight in the pallet.
|
||||
//
|
||||
// The other benchmarks related to `submit_finality_proof` are looking at the effect of specific
|
||||
// parameters and are there mostly for seeing how specific codepaths behave.
|
||||
submit_finality_proof {
|
||||
let s in 1..T::MaxBridgedSessionLength::get().as_() as u32;
|
||||
let p in 1..T::MaxBridgedValidatorCount::get();
|
||||
|
||||
let caller: T::AccountId = whitelisted_caller();
|
||||
|
||||
let authority_list = accounts(p as u8)
|
||||
.iter()
|
||||
.map(|id| (AuthorityId::from(*id), 1))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let init_data = InitializationData {
|
||||
header: bp_test_utils::test_header(Zero::zero()),
|
||||
authority_list,
|
||||
set_id: TEST_GRANDPA_SET_ID,
|
||||
is_halted: false,
|
||||
};
|
||||
|
||||
initialize_bridge::<T, I>(init_data);
|
||||
let header: BridgedHeader<T, I> = bp_test_utils::test_header(One::one());
|
||||
|
||||
let params = JustificationGeneratorParams {
|
||||
header: header.clone(),
|
||||
round: TEST_GRANDPA_ROUND,
|
||||
set_id: TEST_GRANDPA_SET_ID,
|
||||
authorities: accounts(p as u8).iter().map(|k| (*k, 1)).collect::<Vec<_>>(),
|
||||
depth: s,
|
||||
forks: p,
|
||||
};
|
||||
|
||||
let justification = make_justification_for_header(params);
|
||||
|
||||
}: _(RawOrigin::Signed(caller), header, justification)
|
||||
verify {
|
||||
let header: BridgedHeader<T, I> = bp_test_utils::test_header(One::one());
|
||||
let expected_hash = header.hash();
|
||||
|
||||
assert_eq!(<BestFinalized<T, I>>::get(), expected_hash);
|
||||
assert!(<ImportedHeaders<T, I>>::contains_key(expected_hash));
|
||||
}
|
||||
|
||||
// What we want to check here is the effect of vote ancestries on justification verification
|
||||
// do this by varying the number of headers between `finality_target` and `header_of_chain`.
|
||||
submit_finality_proof_on_single_fork {
|
||||
let s in 1..T::MaxBridgedSessionLength::get().as_() as u32;
|
||||
|
||||
let caller: T::AccountId = whitelisted_caller();
|
||||
|
||||
let init_data = InitializationData {
|
||||
header: bp_test_utils::test_header(Zero::zero()),
|
||||
authority_list: authority_list(),
|
||||
set_id: TEST_GRANDPA_SET_ID,
|
||||
is_halted: false,
|
||||
};
|
||||
|
||||
initialize_bridge::<T, I>(init_data);
|
||||
let header: BridgedHeader<T, I> = bp_test_utils::test_header(One::one());
|
||||
|
||||
let params = JustificationGeneratorParams {
|
||||
header: header.clone(),
|
||||
round: TEST_GRANDPA_ROUND,
|
||||
set_id: TEST_GRANDPA_SET_ID,
|
||||
authorities: test_keyring(),
|
||||
depth: s,
|
||||
forks: 1,
|
||||
};
|
||||
|
||||
let justification = make_justification_for_header(params);
|
||||
|
||||
}: submit_finality_proof(RawOrigin::Signed(caller), header, justification)
|
||||
verify {
|
||||
let header: BridgedHeader<T, I> = bp_test_utils::test_header(One::one());
|
||||
let expected_hash = header.hash();
|
||||
|
||||
assert_eq!(<BestFinalized<T, I>>::get(), expected_hash);
|
||||
assert!(<ImportedHeaders<T, I>>::contains_key(expected_hash));
|
||||
}
|
||||
|
||||
// What we want to check here is the effect of many pre-commits on justification verification.
|
||||
// We do this by creating many forks, whose head will be used as a signed pre-commit in the
|
||||
// final justification.
|
||||
submit_finality_proof_on_many_forks {
|
||||
let p in 1..T::MaxBridgedValidatorCount::get();
|
||||
|
||||
let caller: T::AccountId = whitelisted_caller();
|
||||
|
||||
let authority_list = accounts(p as u8)
|
||||
.iter()
|
||||
.map(|id| (AuthorityId::from(*id), 1))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let init_data = InitializationData {
|
||||
header: bp_test_utils::test_header(Zero::zero()),
|
||||
authority_list,
|
||||
set_id: TEST_GRANDPA_SET_ID,
|
||||
is_halted: false,
|
||||
};
|
||||
|
||||
initialize_bridge::<T, I>(init_data);
|
||||
let header: BridgedHeader<T, I> = bp_test_utils::test_header(One::one());
|
||||
|
||||
let params = JustificationGeneratorParams {
|
||||
header: header.clone(),
|
||||
round: TEST_GRANDPA_ROUND,
|
||||
set_id: TEST_GRANDPA_SET_ID,
|
||||
authorities: accounts(p as u8).iter().map(|k| (*k, 1)).collect::<Vec<_>>(),
|
||||
depth: 2,
|
||||
forks: p,
|
||||
};
|
||||
|
||||
let justification = make_justification_for_header(params);
|
||||
|
||||
}: submit_finality_proof(RawOrigin::Signed(caller), header, justification)
|
||||
verify {
|
||||
let header: BridgedHeader<T, I> = bp_test_utils::test_header(One::one());
|
||||
let expected_hash = header.hash();
|
||||
|
||||
assert_eq!(<BestFinalized<T, I>>::get(), expected_hash);
|
||||
assert!(<ImportedHeaders<T, I>>::contains_key(expected_hash));
|
||||
}
|
||||
|
||||
// Here we want to find out the overheaded of looking through consensus digests found in a
|
||||
// header. As the number of logs in a header grows, how much more work do we require to look
|
||||
// through them?
|
||||
//
|
||||
// Note that this should be the same for looking through scheduled changes and forces changes,
|
||||
// which is why we only have one benchmark for this.
|
||||
find_scheduled_change {
|
||||
// Not really sure what a good bound for this is.
|
||||
let n in 1..1000;
|
||||
|
||||
let mut logs = vec![];
|
||||
for i in 0..n {
|
||||
// We chose a non-consensus log on purpose since that way we have to look through all
|
||||
// the logs in the header
|
||||
logs.push(sp_runtime::DigestItem::Other(vec![]));
|
||||
}
|
||||
|
||||
let mut header: BridgedHeader<T, I> = bp_test_utils::test_header(Zero::zero());
|
||||
let digest = header.digest_mut();
|
||||
*digest = sp_runtime::Digest {
|
||||
logs,
|
||||
};
|
||||
|
||||
}: {
|
||||
crate::find_scheduled_change(&header)
|
||||
}
|
||||
|
||||
// What we want to check here is how long it takes to read and write the authority set tracked
|
||||
// by the pallet as the number of authorities grows.
|
||||
read_write_authority_sets {
|
||||
// The current max target number of validators on Polkadot/Kusama
|
||||
let n in 1..1000;
|
||||
|
||||
let mut authorities = vec![];
|
||||
for i in 0..n {
|
||||
authorities.push((ALICE, 1));
|
||||
}
|
||||
|
||||
let authority_set = bp_header_chain::AuthoritySet {
|
||||
authorities: authorities.iter().map(|(id, w)| (AuthorityId::from(*id), *w)).collect(),
|
||||
set_id: 0
|
||||
};
|
||||
|
||||
<CurrentAuthoritySet<T, I>>::put(&authority_set);
|
||||
|
||||
}: {
|
||||
let authority_set = <CurrentAuthoritySet<T, I>>::get();
|
||||
<CurrentAuthoritySet<T, I>>::put(&authority_set);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use frame_support::assert_ok;
|
||||
|
||||
#[test]
|
||||
fn finality_proof_is_valid() {
|
||||
mock::run_test(|| {
|
||||
assert_ok!(test_benchmark_submit_finality_proof::<mock::TestRuntime>());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn single_fork_finality_proof_is_valid() {
|
||||
mock::run_test(|| {
|
||||
assert_ok!(test_benchmark_submit_finality_proof_on_single_fork::<mock::TestRuntime>());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multi_fork_finality_proof_is_valid() {
|
||||
mock::run_test(|| {
|
||||
assert_ok!(test_benchmark_submit_finality_proof_on_many_forks::<mock::TestRuntime>());
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -36,12 +36,15 @@
|
||||
// Runtime-generated enums
|
||||
#![allow(clippy::large_enum_variant)]
|
||||
|
||||
use crate::weights::WeightInfo;
|
||||
|
||||
use bp_header_chain::justification::GrandpaJustification;
|
||||
use bp_runtime::{BlockNumberOf, Chain, HashOf, HasherOf, HeaderOf};
|
||||
use codec::{Decode, Encode};
|
||||
use finality_grandpa::voter_set::VoterSet;
|
||||
use frame_support::ensure;
|
||||
use frame_system::{ensure_signed, RawOrigin};
|
||||
use num_traits::cast::AsPrimitive;
|
||||
#[cfg(feature = "std")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sp_finality_grandpa::{ConsensusLog, GRANDPA_ENGINE_ID};
|
||||
@@ -51,6 +54,12 @@ use sp_runtime::RuntimeDebug;
|
||||
#[cfg(test)]
|
||||
mod mock;
|
||||
|
||||
/// Module containing weights for this pallet.
|
||||
pub mod weights;
|
||||
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
pub mod benchmarking;
|
||||
|
||||
// Re-export in crate namespace for `construct_runtime!`
|
||||
pub use pallet::*;
|
||||
|
||||
@@ -82,6 +91,30 @@ pub mod pallet {
|
||||
/// until the request count has decreased.
|
||||
#[pallet::constant]
|
||||
type MaxRequests: Get<u32>;
|
||||
|
||||
/// The maximum length of a session on the bridged chain.
|
||||
///
|
||||
/// The pallet uses this to bound justification verification since justifications contain
|
||||
/// ancestry proofs whose size is capped at `MaxBridgedSessionLength`.
|
||||
#[pallet::constant]
|
||||
type MaxBridgedSessionLength: Get<BridgedBlockNumber<Self, I>>;
|
||||
|
||||
/// The number of validators on the bridged chain.
|
||||
///
|
||||
/// The pallet uses this to bound justification verification since justifications may
|
||||
/// contain up to `MaxBridgedValidatorCount` number of signed `pre-commit` messages which
|
||||
/// need to be verified.
|
||||
///
|
||||
/// Note that `MaxBridgedValidatorCount` should *not* match the exact number of validators
|
||||
/// on the bridged chain. Instead it should be a number which is greater than the actual
|
||||
/// number of validators in order to provide some buffer room should the validator set
|
||||
/// increase in size. If this number ends up being lower than the actual number of
|
||||
/// validators on the bridged chain you risk stalling the bridge.
|
||||
#[pallet::constant]
|
||||
type MaxBridgedValidatorCount: Get<u32>;
|
||||
|
||||
/// Weights gathered through benchmarking.
|
||||
type WeightInfo: WeightInfo;
|
||||
}
|
||||
|
||||
#[pallet::pallet]
|
||||
@@ -107,7 +140,10 @@ pub mod pallet {
|
||||
///
|
||||
/// If successful in verification, it will write the target header to the underlying storage
|
||||
/// pallet.
|
||||
#[pallet::weight(0)]
|
||||
#[pallet::weight(T::WeightInfo::submit_finality_proof(
|
||||
T::MaxBridgedSessionLength::get().as_() as u32,
|
||||
T::MaxBridgedValidatorCount::get(),
|
||||
))]
|
||||
pub fn submit_finality_proof(
|
||||
origin: OriginFor<T>,
|
||||
finality_target: BridgedHeader<T, I>,
|
||||
@@ -134,16 +170,26 @@ pub mod pallet {
|
||||
// "travelling back in time" (which could be indicative of something bad, e.g a hard-fork).
|
||||
ensure!(best_finalized.number() < number, <Error<T, I>>::OldHeader);
|
||||
|
||||
verify_justification::<T, I>(&justification, hash, *number)?;
|
||||
let authority_set = <CurrentAuthoritySet<T, I>>::get();
|
||||
let set_id = authority_set.set_id;
|
||||
verify_justification::<T, I>(&justification, hash, *number, authority_set)?;
|
||||
|
||||
try_enact_authority_change::<T, I>(&finality_target)?;
|
||||
let _enacted = try_enact_authority_change::<T, I>(&finality_target, set_id)?;
|
||||
<BestFinalized<T, I>>::put(hash);
|
||||
<ImportedHeaders<T, I>>::insert(hash, finality_target);
|
||||
<RequestCount<T, I>>::mutate(|count| *count += 1);
|
||||
|
||||
log::info!(target: "runtime::bridge-grandpa", "Succesfully imported finalized header with hash {:?}!", hash);
|
||||
|
||||
Ok(().into())
|
||||
// Note that the number of precommits is indicitive of the number of GRANDPA forks being
|
||||
// voted on.
|
||||
let precommits = justification.commit.precommits.len();
|
||||
|
||||
// This represents the average number of votes in a single fork since the weight formula
|
||||
// uses that metric instead of the aggregated number of vote ancestries.
|
||||
let votes = (justification.votes_ancestries.len() + precommits - 1) / precommits;
|
||||
|
||||
Ok(Some(T::WeightInfo::submit_finality_proof(votes as u32, precommits as u32)).into())
|
||||
}
|
||||
|
||||
/// Bootstrap the bridge pallet with an initial header and authority set from which to sync.
|
||||
@@ -319,9 +365,14 @@ pub mod pallet {
|
||||
///
|
||||
/// This function does not support forced changes, or scheduled changes with delays
|
||||
/// since these types of changes are indicitive of abnormal behaviour from GRANDPA.
|
||||
///
|
||||
/// Returned value will indicate if a change was enacted or not.
|
||||
pub(crate) fn try_enact_authority_change<T: Config<I>, I: 'static>(
|
||||
header: &BridgedHeader<T, I>,
|
||||
) -> Result<(), sp_runtime::DispatchError> {
|
||||
current_set_id: sp_finality_grandpa::SetId,
|
||||
) -> Result<bool, sp_runtime::DispatchError> {
|
||||
let mut change_enacted = false;
|
||||
|
||||
// We don't support forced changes - at that point governance intervention is required.
|
||||
ensure!(
|
||||
super::find_forced_change(header).is_none(),
|
||||
@@ -332,7 +383,6 @@ pub mod pallet {
|
||||
// GRANDPA only includes a `delay` for forced changes, so this isn't valid.
|
||||
ensure!(change.delay == Zero::zero(), <Error<T, I>>::UnsupportedScheduledChange);
|
||||
|
||||
let current_set_id = <CurrentAuthoritySet<T, I>>::get().set_id;
|
||||
// TODO [#788]: Stop manually increasing the `set_id` here.
|
||||
let next_authorities = bp_header_chain::AuthoritySet {
|
||||
authorities: change.next_authorities,
|
||||
@@ -342,6 +392,7 @@ pub mod pallet {
|
||||
// Since our header schedules a change and we know the delay is 0, it must also enact
|
||||
// the change.
|
||||
<CurrentAuthoritySet<T, I>>::put(&next_authorities);
|
||||
change_enacted = true;
|
||||
|
||||
log::info!(
|
||||
target: "runtime::bridge-grandpa",
|
||||
@@ -352,20 +403,23 @@ pub mod pallet {
|
||||
);
|
||||
};
|
||||
|
||||
Ok(())
|
||||
Ok(change_enacted)
|
||||
}
|
||||
|
||||
/// Verify a GRANDPA justification (finality proof) for a given header.
|
||||
///
|
||||
/// Will use the GRANDPA current authorities known to the pallet.
|
||||
///
|
||||
/// If succesful it returns the decoded GRANDPA justification so we can refund any weight which
|
||||
/// was overcharged in the initial call.
|
||||
pub(crate) fn verify_justification<T: Config<I>, I: 'static>(
|
||||
justification: &GrandpaJustification<BridgedHeader<T, I>>,
|
||||
hash: BridgedBlockHash<T, I>,
|
||||
number: BridgedBlockNumber<T, I>,
|
||||
authority_set: bp_header_chain::AuthoritySet,
|
||||
) -> Result<(), sp_runtime::DispatchError> {
|
||||
use bp_header_chain::justification::verify_justification;
|
||||
|
||||
let authority_set = <CurrentAuthoritySet<T, I>>::get();
|
||||
let voter_set = VoterSet::new(authority_set.authorities).ok_or(<Error<T, I>>::InvalidAuthoritySet)?;
|
||||
let set_id = authority_set.set_id;
|
||||
|
||||
@@ -510,7 +564,7 @@ pub(crate) fn find_forced_change<H: HeaderT>(
|
||||
header.digest().convert_first(|l| l.try_to(id).and_then(filter_log))
|
||||
}
|
||||
|
||||
/// (Re)initialize bridge with given header for using it in external benchmarks.
|
||||
/// (Re)initialize bridge with given header for using it in `pallet-bridge-messages` benchmarks.
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
pub fn initialize_for_benchmarks<T: Config<I>, I: 'static>(header: BridgedHeader<T, I>) {
|
||||
initialize_bridge::<T, I>(InitializationData {
|
||||
|
||||
@@ -80,11 +80,16 @@ impl frame_system::Config for TestRuntime {
|
||||
|
||||
parameter_types! {
|
||||
pub const MaxRequests: u32 = 2;
|
||||
pub const SessionLength: u64 = 5;
|
||||
pub const NumValidators: u32 = 5;
|
||||
}
|
||||
|
||||
impl grandpa::Config for TestRuntime {
|
||||
type BridgedChain = TestBridgedChain;
|
||||
type MaxRequests = MaxRequests;
|
||||
type MaxBridgedSessionLength = SessionLength;
|
||||
type MaxBridgedValidatorCount = NumValidators;
|
||||
type WeightInfo = ();
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
// Copyright 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_grandpa
|
||||
//!
|
||||
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 3.0.0
|
||||
//! DATE: 2021-03-26, STEPS: [50, ], REPEAT: 20
|
||||
//! LOW RANGE: [], HIGH RANGE: []
|
||||
//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled
|
||||
//! CHAIN: Some("dev"), DB CACHE: 128
|
||||
|
||||
// Executed Command:
|
||||
// ./target/release/rialto-bridge-node
|
||||
// benchmark
|
||||
// --chain
|
||||
// dev
|
||||
// --execution
|
||||
// wasm
|
||||
// --extrinsic
|
||||
// *
|
||||
// --pallet
|
||||
// pallet_bridge_grandpa
|
||||
// --wasm-execution
|
||||
// compiled
|
||||
// --steps
|
||||
// 50
|
||||
// --repeat
|
||||
// 20
|
||||
// --template
|
||||
// ./.maintain/rialto-weight-template.hbs
|
||||
// --output
|
||||
// ./grandpa-template.txt
|
||||
// --raw
|
||||
|
||||
#![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_grandpa.
|
||||
pub trait WeightInfo {
|
||||
fn submit_finality_proof(s: u32, p: u32) -> Weight;
|
||||
fn submit_finality_proof_on_single_fork(s: u32) -> Weight;
|
||||
fn submit_finality_proof_on_many_forks(p: u32) -> Weight;
|
||||
fn find_scheduled_change(n: u32) -> Weight;
|
||||
fn read_write_authority_sets(n: u32) -> Weight;
|
||||
fn write_authority_sets(n: u32) -> Weight;
|
||||
}
|
||||
|
||||
/// Weights for pallet_bridge_grandpa using the Rialto node and recommended hardware.
|
||||
pub struct RialtoWeight<T>(PhantomData<T>);
|
||||
impl<T: frame_system::Config> WeightInfo for RialtoWeight<T> {
|
||||
fn submit_finality_proof(s: u32, p: u32) -> Weight {
|
||||
(0 as Weight)
|
||||
.saturating_add((3_248_661_000 as Weight).saturating_mul(s as Weight))
|
||||
.saturating_add((776_552_000 as Weight).saturating_mul(p as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(5 as Weight))
|
||||
.saturating_add(T::DbWeight::get().writes(3 as Weight))
|
||||
}
|
||||
fn submit_finality_proof_on_single_fork(s: u32) -> Weight {
|
||||
(189_213_000 as Weight)
|
||||
.saturating_add((12_937_000 as Weight).saturating_mul(s as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(5 as Weight))
|
||||
.saturating_add(T::DbWeight::get().writes(3 as Weight))
|
||||
}
|
||||
fn submit_finality_proof_on_many_forks(p: u32) -> Weight {
|
||||
(0 as Weight)
|
||||
.saturating_add((138_751_000 as Weight).saturating_mul(p as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(5 as Weight))
|
||||
.saturating_add(T::DbWeight::get().writes(3 as Weight))
|
||||
}
|
||||
fn find_scheduled_change(n: u32) -> Weight {
|
||||
(301_000 as Weight).saturating_add((10_000 as Weight).saturating_mul(n as Weight))
|
||||
}
|
||||
fn read_write_authority_sets(n: u32) -> Weight {
|
||||
(6_787_000 as Weight)
|
||||
.saturating_add((247_000 as Weight).saturating_mul(n as Weight))
|
||||
.saturating_add(T::DbWeight::get().reads(1 as Weight))
|
||||
.saturating_add(T::DbWeight::get().writes(1 as Weight))
|
||||
}
|
||||
fn write_authority_sets(n: u32) -> Weight {
|
||||
(3_383_000 as Weight)
|
||||
.saturating_add((99_000 as Weight).saturating_mul(n as Weight))
|
||||
.saturating_add(T::DbWeight::get().writes(1 as Weight))
|
||||
}
|
||||
}
|
||||
|
||||
// For backwards compatibility and tests
|
||||
impl WeightInfo for () {
|
||||
fn submit_finality_proof(s: u32, p: u32) -> Weight {
|
||||
(0 as Weight)
|
||||
.saturating_add((3_248_661_000 as Weight).saturating_mul(s as Weight))
|
||||
.saturating_add((776_552_000 as Weight).saturating_mul(p as Weight))
|
||||
.saturating_add(RocksDbWeight::get().reads(5 as Weight))
|
||||
.saturating_add(RocksDbWeight::get().writes(3 as Weight))
|
||||
}
|
||||
fn submit_finality_proof_on_single_fork(s: u32) -> Weight {
|
||||
(189_213_000 as Weight)
|
||||
.saturating_add((12_937_000 as Weight).saturating_mul(s as Weight))
|
||||
.saturating_add(RocksDbWeight::get().reads(5 as Weight))
|
||||
.saturating_add(RocksDbWeight::get().writes(3 as Weight))
|
||||
}
|
||||
fn submit_finality_proof_on_many_forks(p: u32) -> Weight {
|
||||
(0 as Weight)
|
||||
.saturating_add((138_751_000 as Weight).saturating_mul(p as Weight))
|
||||
.saturating_add(RocksDbWeight::get().reads(5 as Weight))
|
||||
.saturating_add(RocksDbWeight::get().writes(3 as Weight))
|
||||
}
|
||||
fn find_scheduled_change(n: u32) -> Weight {
|
||||
(301_000 as Weight).saturating_add((10_000 as Weight).saturating_mul(n as Weight))
|
||||
}
|
||||
fn read_write_authority_sets(n: u32) -> Weight {
|
||||
(6_787_000 as Weight)
|
||||
.saturating_add((247_000 as Weight).saturating_mul(n as Weight))
|
||||
.saturating_add(RocksDbWeight::get().reads(1 as Weight))
|
||||
.saturating_add(RocksDbWeight::get().writes(1 as Weight))
|
||||
}
|
||||
fn write_authority_sets(n: u32) -> Weight {
|
||||
(3_383_000 as Weight)
|
||||
.saturating_add((99_000 as Weight).saturating_mul(n as Weight))
|
||||
.saturating_add(RocksDbWeight::get().writes(1 as Weight))
|
||||
}
|
||||
}
|
||||
@@ -95,7 +95,11 @@ pub const ADDITIONAL_MESSAGE_BYTE_DELIVERY_WEIGHT: Weight = 25_000;
|
||||
/// runtime upgrades.
|
||||
pub const MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT: Weight = 2_000_000_000;
|
||||
|
||||
/// The length of a session (how often authorities change) on Millau measured in of number of blocks.
|
||||
/// The target length of a session (how often authorities change) on Millau measured in of number of
|
||||
/// blocks.
|
||||
///
|
||||
/// Note that since this is a target sessions may change before/after this time depending on network
|
||||
/// conditions.
|
||||
pub const SESSION_LENGTH: BlockNumber = 5 * time_units::MINUTES;
|
||||
|
||||
/// Re-export `time_units` to make usage easier.
|
||||
|
||||
@@ -86,7 +86,11 @@ pub const ADDITIONAL_MESSAGE_BYTE_DELIVERY_WEIGHT: Weight = 25_000;
|
||||
/// runtime upgrades.
|
||||
pub const MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT: Weight = 2_000_000_000;
|
||||
|
||||
/// The length of a session (how often authorities change) on Rialto measured in of number of blocks.
|
||||
/// The target length of a session (how often authorities change) on Rialto measured in of number of
|
||||
/// blocks.
|
||||
///
|
||||
/// Note that since this is a target sessions may change before/after this time depending on network
|
||||
/// conditions.
|
||||
pub const SESSION_LENGTH: BlockNumber = 4;
|
||||
|
||||
/// Re-export `time_units` to make usage easier.
|
||||
|
||||
@@ -69,6 +69,13 @@ pub const FROM_WESTEND_LATEST_CONFIRMED_NONCE_METHOD: &str = "FromWestendInbound
|
||||
/// Name of the `FromWestendInboundLaneApi::unrewarded_relayers_state` runtime method.
|
||||
pub const FROM_WESTEND_UNREWARDED_RELAYERS_STATE: &str = "FromWestendInboundLaneApi_unrewarded_relayers_state";
|
||||
|
||||
/// The target length of a session (how often authorities change) on Westend measured in of number of
|
||||
/// blocks.
|
||||
///
|
||||
/// Note that since this is a target sessions may change before/after this time depending on network
|
||||
/// conditions.
|
||||
pub const SESSION_LENGTH: BlockNumber = 10 * time_units::MINUTES;
|
||||
|
||||
sp_api::decl_runtime_apis! {
|
||||
/// API for querying information about the finalized Westend headers.
|
||||
///
|
||||
|
||||
@@ -136,6 +136,21 @@ pub const MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE: MessageNonce = 128;
|
||||
/// Maximal number of unconfirmed messages at inbound lane.
|
||||
pub const MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE: MessageNonce = 8192;
|
||||
|
||||
/// Re-export `time_units` to make usage easier.
|
||||
pub use time_units::*;
|
||||
|
||||
/// Human readable time units defined in terms of number of blocks.
|
||||
pub mod time_units {
|
||||
use super::BlockNumber;
|
||||
|
||||
pub const MILLISECS_PER_BLOCK: u64 = 6000;
|
||||
pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK;
|
||||
|
||||
pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber);
|
||||
pub const HOURS: BlockNumber = MINUTES * 60;
|
||||
pub const DAYS: BlockNumber = HOURS * 24;
|
||||
}
|
||||
|
||||
/// Block number type used in Polkadot-like chains.
|
||||
pub type BlockNumber = u32;
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ use finality_grandpa::voter_set::VoterSet;
|
||||
use sp_application_crypto::Public;
|
||||
use sp_finality_grandpa::{AuthorityId, AuthorityList, AuthorityWeight};
|
||||
use sp_runtime::RuntimeDebug;
|
||||
use sp_std::prelude::*;
|
||||
|
||||
/// Set of test accounts with friendly names.
|
||||
pub const ALICE: Account = Account(0);
|
||||
|
||||
Reference in New Issue
Block a user