fix: Complete snowbridge pezpallet rebrand and critical bug fixes
- snowbridge-pezpallet-* → pezsnowbridge-pezpallet-* (201 refs) - pallet/ directories → pezpallet/ (4 locations) - Fixed pezpallet.rs self-include recursion bug - Fixed sc-chain-spec hardcoded crate name in derive macro - Reverted .pezpallet_by_name() to .pallet_by_name() (subxt API) - Added BizinikiwiConfig type alias for zombienet tests - Deleted obsolete session state files Verified: pezsnowbridge-pezpallet-*, pezpallet-staking, pezpallet-staking-async, pezframe-benchmarking-cli all pass cargo check
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
[package]
|
||||
name = "pezpallet-bridge-grandpa"
|
||||
version = "0.7.0"
|
||||
description = "Module implementing GRANDPA on-chain light client used for bridging consensus of bizinikiwi-based chains."
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
|
||||
repository.workspace = true
|
||||
documentation = "https://docs.rs/pezpallet-bridge-grandpa"
|
||||
homepage = { workspace = true }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
codec = { workspace = true }
|
||||
scale-info = { features = ["derive"], workspace = true }
|
||||
tracing = { workspace = true }
|
||||
|
||||
# Bridge Dependencies
|
||||
bp-header-pez-chain = { workspace = true }
|
||||
pezbp-runtime = { workspace = true }
|
||||
|
||||
# Bizinikiwi Dependencies
|
||||
pezframe-support = { workspace = true }
|
||||
pezframe-system = { workspace = true }
|
||||
pezsp-consensus-grandpa = { features = ["serde"], workspace = true }
|
||||
pezsp-runtime = { features = ["serde"], workspace = true }
|
||||
pezsp-std = { workspace = true }
|
||||
|
||||
# Optional Benchmarking Dependencies
|
||||
bp-test-utils = { optional = true, workspace = true }
|
||||
pezframe-benchmarking = { optional = true, workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
pezbp-runtime = { features = ["test-helpers"], workspace = true }
|
||||
pezsp-core = { workspace = true, default-features = true }
|
||||
pezsp-io = { workspace = true, default-features = true }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = [
|
||||
"bp-header-pez-chain/std",
|
||||
"pezbp-runtime/std",
|
||||
"bp-test-utils/std",
|
||||
"codec/std",
|
||||
"pezframe-benchmarking/std",
|
||||
"pezframe-support/std",
|
||||
"pezframe-system/std",
|
||||
"scale-info/std",
|
||||
"pezsp-consensus-grandpa/std",
|
||||
"pezsp-runtime/std",
|
||||
"pezsp-std/std",
|
||||
"tracing/std",
|
||||
]
|
||||
runtime-benchmarks = [
|
||||
"bp-header-pez-chain/runtime-benchmarks",
|
||||
"pezbp-runtime/runtime-benchmarks",
|
||||
"bp-test-utils",
|
||||
"bp-test-utils?/runtime-benchmarks",
|
||||
"pezframe-benchmarking/runtime-benchmarks",
|
||||
"pezframe-support/runtime-benchmarks",
|
||||
"pezframe-system/runtime-benchmarks",
|
||||
"pezsp-consensus-grandpa/runtime-benchmarks",
|
||||
"pezsp-io/runtime-benchmarks",
|
||||
"pezsp-runtime/runtime-benchmarks",
|
||||
]
|
||||
try-runtime = [
|
||||
"pezframe-support/try-runtime",
|
||||
"pezframe-system/try-runtime",
|
||||
"pezsp-runtime/try-runtime",
|
||||
]
|
||||
@@ -0,0 +1,101 @@
|
||||
# Bridge GRANDPA Pezpallet
|
||||
|
||||
The bridge GRANDPA pezpallet is a light client for the GRANDPA finality gadget, running at the bridged chain.
|
||||
It may import headers and their GRANDPA finality proofs (justifications) of the bridged chain. Imported
|
||||
headers then may be used to verify storage proofs by other pallets. This makes the bridge GRANDPA pezpallet
|
||||
a basic pezpallet of all bridges with Bizinikiwi-based chains. It is used by all bridge types (bridge between
|
||||
standalone chains, between teyrchains and any combination of those) and is used by other bridge pallets.
|
||||
It is used by the teyrchains light client (bridge teyrchains pezpallet) and by messages pezpallet.
|
||||
|
||||
## A Brief Introduction into GRANDPA Finality
|
||||
|
||||
You can find detailed information on GRANDPA, by exploring its [repository](https://github.com/pezkuwichain/finality-grandpa).
|
||||
Here is the minimal required GRANDPA information to understand how pezpallet works.
|
||||
|
||||
Any Bizinikiwi chain may use different block authorship algorithms (like BABE or Aura) to determine block producers and
|
||||
generate blocks. This has nothing common with finality, though - the task of block authorship is to coordinate
|
||||
blocks generation. Any block may be reverted (if there's a fork) if it is not finalized. The finality solution
|
||||
for (standalone) Bizinikiwi-based chains is the GRANDPA finality gadget. If some block is finalized by the gadget, it
|
||||
can't be reverted.
|
||||
|
||||
In GRANDPA, there are validators, identified by their public keys. They select some generated block and produce
|
||||
signatures on this block hash. If there are enough (more than `2 / 3 * N`, where `N` is number of validators)
|
||||
signatures, then the block is considered finalized. The set of signatures for the block is called justification.
|
||||
Anyone who knows the public keys of validators is able to verify GRANDPA justification and that it is generated
|
||||
for provided header.
|
||||
|
||||
There are two main things in GRANDPA that help building light clients:
|
||||
|
||||
- there's no need to import all headers of the bridged chain. Light client may import finalized headers or just
|
||||
some of finalized headers that it consider useful. While the validators set stays the same, the client may
|
||||
import any header that is finalized by this set;
|
||||
|
||||
- when validators set changes, the GRANDPA gadget adds next set to the header. So light client doesn't need to
|
||||
verify storage proofs when this happens - it only needs to look at the header and see if it changes the set.
|
||||
Once set is changed, all following justifications are generated by the new set. Header that is changing the
|
||||
set is called "mandatory" in the pezpallet. As the name says, the light client need to import all such headers
|
||||
to be able to operate properly.
|
||||
|
||||
## Pezpallet Operations
|
||||
|
||||
The main entrypoint of the pezpallet is the `submit_finality_proof_ex` call. It has three arguments - the finalized
|
||||
headers, associated GRANDPA justification and ID of the authority set that has generated this justification. The
|
||||
call simply verifies the justification using current validators set and checks if header is better than the
|
||||
previous best header. If both checks are passed, the header (only its useful fields) is inserted into the runtime
|
||||
storage and may be used by other pallets to verify storage proofs.
|
||||
|
||||
The submitter pays regular fee for submitting all headers, except for the mandatory header. Since it is
|
||||
required for the pezpallet operations, submitting such header is free. So if you're ok with session-length
|
||||
lags (meaning that there's exactly 1 mandatory header per session), the cost of pezpallet calls is zero.
|
||||
|
||||
When the pezpallet sees mandatory header, it updates the validators set with the set from the header. All
|
||||
following justifications (until next mandatory header) must be generated by this new set.
|
||||
|
||||
## Pezpallet Initialization
|
||||
|
||||
As the previous section states, there are two things that are mandatory for pezpallet operations: best finalized
|
||||
header and the current validators set. Without it the pezpallet can't import any headers. But how to provide
|
||||
initial values for these fields? There are two options.
|
||||
|
||||
First option, while it is easier, doesn't work in all cases. It is to start chain with initial header and
|
||||
validators set specified in the chain specification. This won't work, however, if we want to add bridge
|
||||
to already started chain.
|
||||
|
||||
For the latter case we have the `initialize` call. It accepts the initial header and initial validators set.
|
||||
The call may be called by the governance, root or by the pezpallet owner (if it is set).
|
||||
|
||||
## Non-Essential Functionality
|
||||
|
||||
There may be a special account in every runtime where the bridge GRANDPA module is deployed. This
|
||||
account, named 'module owner', is like a module-level sudo account - he's able to halt and
|
||||
resume all module operations without requiring runtime upgrade. Calls that are related to this
|
||||
account are:
|
||||
|
||||
- `fn set_owner()`: current module owner may call it to transfer "ownership" to another account;
|
||||
|
||||
- `fn set_operating_mode()`: the module owner (or sudo account) may call this function to stop all
|
||||
module operations. After this call, all finality proofs will be rejected until further `set_operating_mode` call'.
|
||||
This call may be used when something extraordinary happens with the bridge;
|
||||
|
||||
- `fn initialize()`: module owner may call this function to initialize the bridge.
|
||||
|
||||
If pezpallet owner is not defined, the governance may be used to make those calls.
|
||||
|
||||
## Signed Extension to Reject Obsolete Headers
|
||||
|
||||
It'd be better for anyone (for chain and for submitters) to reject all transactions that are submitting
|
||||
already known headers to the pezpallet. This way, we leave block space to other useful transactions and
|
||||
we don't charge concurrent submitters for their honest actions.
|
||||
|
||||
To deal with that, we have a [signed extension](./src/call_ext.rs) that may be added to the runtime.
|
||||
It does exactly what is required - rejects all transactions with already known headers. The submitter
|
||||
pays nothing for such transactions - they're simply removed from the transaction pool, when the block
|
||||
is built.
|
||||
|
||||
You may also take a look at the [`generate_bridge_reject_obsolete_headers_and_messages`](../../bin/runtime-common/src/lib.rs)
|
||||
macro that bundles several similar signed extensions in a single one.
|
||||
|
||||
## GRANDPA Finality Relay
|
||||
|
||||
We have an offchain actor, who is watching for GRANDPA justifications and submits them to the bridged chain.
|
||||
It is the finality relay - you may look at the [crate level documentation and the code](../../relays/finality/).
|
||||
@@ -0,0 +1,157 @@
|
||||
// Copyright (C) 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 Pezpallet.
|
||||
//!
|
||||
//! The main dispatchable for the GRANDPA pezpallet is `submit_finality_proof_ex`. Our benchmarks
|
||||
//! are based around `submit_finality_proof`, though - from weight PoV they are the same calls.
|
||||
//! 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 descendant 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.
|
||||
|
||||
use crate::*;
|
||||
|
||||
use bp_header_pez_chain::justification::required_justification_precommits;
|
||||
use pezbp_runtime::BasicOperatingMode;
|
||||
use bp_test_utils::{
|
||||
accounts, make_justification_for_header, JustificationGeneratorParams, TEST_GRANDPA_ROUND,
|
||||
TEST_GRANDPA_SET_ID,
|
||||
};
|
||||
use pezframe_benchmarking::{benchmarks_instance_pallet, whitelisted_caller};
|
||||
use pezframe_system::RawOrigin;
|
||||
use pezsp_consensus_grandpa::AuthorityId;
|
||||
use pezsp_runtime::traits::{One, Zero};
|
||||
use pezsp_std::vec::Vec;
|
||||
|
||||
/// The maximum number of vote ancestries to include in a justification.
|
||||
///
|
||||
/// In practice this would be limited by the session length (number of blocks a single authority set
|
||||
/// can produce) of a given chain.
|
||||
const MAX_VOTE_ANCESTRIES: u32 = 1000;
|
||||
|
||||
// `1..MAX_VOTE_ANCESTRIES` is too large && benchmarks are running for almost 40m (steps=50,
|
||||
// repeat=20) on a decent laptop, which is too much. Since we're building linear function here,
|
||||
// let's just select some limited subrange for benchmarking.
|
||||
const MAX_VOTE_ANCESTRIES_RANGE_BEGIN: u32 = MAX_VOTE_ANCESTRIES / 20;
|
||||
const MAX_VOTE_ANCESTRIES_RANGE_END: u32 =
|
||||
MAX_VOTE_ANCESTRIES_RANGE_BEGIN + MAX_VOTE_ANCESTRIES_RANGE_BEGIN;
|
||||
|
||||
// the same with validators - if there are too much validators, let's run benchmarks on subrange
|
||||
fn precommits_range_end<T: Config<I>, I: 'static>() -> u32 {
|
||||
let max_bridged_authorities = T::BridgedChain::MAX_AUTHORITIES_COUNT;
|
||||
let max_bridged_authorities = if max_bridged_authorities > 128 {
|
||||
pezsp_std::cmp::max(128, max_bridged_authorities / 5)
|
||||
} else {
|
||||
max_bridged_authorities
|
||||
};
|
||||
|
||||
required_justification_precommits(max_bridged_authorities)
|
||||
}
|
||||
|
||||
/// Prepare header and its justification to submit using `submit_finality_proof`.
|
||||
fn prepare_benchmark_data<T: Config<I>, I: 'static>(
|
||||
precommits: u32,
|
||||
ancestors: u32,
|
||||
) -> (BridgedHeader<T, I>, GrandpaJustification<BridgedHeader<T, I>>) {
|
||||
// going from precommits to total authorities count
|
||||
let total_authorities_count = (3 * precommits - 1) / 2;
|
||||
|
||||
let authority_list = accounts(total_authorities_count as u16)
|
||||
.iter()
|
||||
.map(|id| (AuthorityId::from(*id), 1))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let genesis_header: BridgedHeader<T, I> = bp_test_utils::test_header(Zero::zero());
|
||||
let genesis_hash = genesis_header.hash();
|
||||
let init_data = InitializationData {
|
||||
header: Box::new(genesis_header),
|
||||
authority_list,
|
||||
set_id: TEST_GRANDPA_SET_ID,
|
||||
operating_mode: BasicOperatingMode::Normal,
|
||||
};
|
||||
|
||||
bootstrap_bridge::<T, I>(init_data);
|
||||
assert!(<ImportedHeaders<T, I>>::contains_key(genesis_hash));
|
||||
|
||||
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(precommits as u16).iter().map(|k| (*k, 1)).collect::<Vec<_>>(),
|
||||
ancestors,
|
||||
forks: 1,
|
||||
};
|
||||
let justification = make_justification_for_header(params);
|
||||
(header, justification)
|
||||
}
|
||||
|
||||
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 pezpallet.
|
||||
submit_finality_proof {
|
||||
let p in 1 .. precommits_range_end::<T, I>();
|
||||
let v in MAX_VOTE_ANCESTRIES_RANGE_BEGIN..MAX_VOTE_ANCESTRIES_RANGE_END;
|
||||
let caller: T::AccountId = whitelisted_caller();
|
||||
let (header, justification) = prepare_benchmark_data::<T, I>(p, v);
|
||||
}: submit_finality_proof(RawOrigin::Signed(caller), Box::new(header), justification)
|
||||
verify {
|
||||
let genesis_header: BridgedHeader<T, I> = bp_test_utils::test_header(Zero::zero());
|
||||
let header: BridgedHeader<T, I> = bp_test_utils::test_header(One::one());
|
||||
let expected_hash = header.hash();
|
||||
|
||||
// check that the header#1 has been inserted
|
||||
assert_eq!(<BestFinalized<T, I>>::get().unwrap().1, expected_hash);
|
||||
assert!(<ImportedHeaders<T, I>>::contains_key(expected_hash));
|
||||
|
||||
// check that the header#0 has been pruned
|
||||
assert!(!<ImportedHeaders<T, I>>::contains_key(genesis_header.hash()));
|
||||
}
|
||||
|
||||
force_set_pallet_state {
|
||||
let set_id = 100;
|
||||
let authorities = accounts(T::BridgedChain::MAX_AUTHORITIES_COUNT as u16)
|
||||
.iter()
|
||||
.map(|id| (AuthorityId::from(*id), 1))
|
||||
.collect::<Vec<_>>();
|
||||
let (header, _) = prepare_benchmark_data::<T, I>(1, 1);
|
||||
let expected_hash = header.hash();
|
||||
}: force_set_pallet_state(RawOrigin::Root, set_id, authorities, Box::new(header))
|
||||
verify {
|
||||
assert_eq!(<BestFinalized<T, I>>::get().unwrap().1, expected_hash);
|
||||
assert_eq!(<CurrentAuthoritySet<T, I>>::get().set_id, set_id);
|
||||
}
|
||||
|
||||
impl_benchmark_test_suite!(Pezpallet, crate::mock::new_test_ext(), crate::mock::TestRuntime)
|
||||
}
|
||||
@@ -0,0 +1,720 @@
|
||||
// Copyright (C) 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/>.
|
||||
|
||||
use crate::{
|
||||
weights::WeightInfo, BestFinalized, BridgedBlockNumber, BridgedHeader, Config,
|
||||
CurrentAuthoritySet, Error, FreeHeadersRemaining, Pezpallet,
|
||||
};
|
||||
use bp_header_pez_chain::{
|
||||
justification::GrandpaJustification, submit_finality_proof_limits_extras,
|
||||
SubmitFinalityProofInfo,
|
||||
};
|
||||
use pezbp_runtime::{BlockNumberOf, Chain, OwnedBridgeModule};
|
||||
use pezframe_support::{
|
||||
dispatch::CallableCallFor,
|
||||
traits::{Get, IsSubType},
|
||||
weights::Weight,
|
||||
};
|
||||
use pezsp_consensus_grandpa::SetId;
|
||||
use pezsp_runtime::{
|
||||
traits::{CheckedSub, Header, Zero},
|
||||
transaction_validity::{InvalidTransaction, TransactionValidityError},
|
||||
RuntimeDebug, SaturatedConversion,
|
||||
};
|
||||
use pezsp_std::fmt::Debug;
|
||||
|
||||
/// Verified `SubmitFinalityProofInfo<N>`.
|
||||
#[derive(Copy, Clone, PartialEq, RuntimeDebug)]
|
||||
pub struct VerifiedSubmitFinalityProofInfo<N: Debug> {
|
||||
/// Base call information.
|
||||
pub base: SubmitFinalityProofInfo<N>,
|
||||
/// A difference between bundled bridged header and best bridged header known to us
|
||||
/// before the call.
|
||||
pub improved_by: N,
|
||||
}
|
||||
|
||||
/// Helper struct that provides methods for working with the `SubmitFinalityProof` call.
|
||||
pub struct SubmitFinalityProofHelper<T: Config<I>, I: 'static> {
|
||||
_phantom_data: pezsp_std::marker::PhantomData<(T, I)>,
|
||||
}
|
||||
|
||||
impl<T: Config<I>, I: 'static> SubmitFinalityProofHelper<T, I> {
|
||||
/// Returns `true` if we may fit more free headers into the current block. If `false` is
|
||||
/// returned, the call will be paid even if `is_free_execution_expected` has been set
|
||||
/// to `true`.
|
||||
pub fn has_free_header_slots() -> bool {
|
||||
// `unwrap_or(u32::MAX)` means that if `FreeHeadersRemaining` is `None`, we may accept
|
||||
// this header for free. That is a small cheat - it is `None` if executed outside of
|
||||
// transaction (e.g. during block initialization). Normal relayer would never submit
|
||||
// such calls, but if he did, that is not our problem. During normal transactions,
|
||||
// the `FreeHeadersRemaining` is always `Some(_)`.
|
||||
let free_headers_remaining = FreeHeadersRemaining::<T, I>::get().unwrap_or(u32::MAX);
|
||||
free_headers_remaining > 0
|
||||
}
|
||||
|
||||
/// Check that the: (1) GRANDPA head provided by the `SubmitFinalityProof` is better than the
|
||||
/// best one we know (2) if `current_set_id` matches the current authority set id, if specified
|
||||
/// and (3) whether transaction MAY be free for the submitter if `is_free_execution_expected`
|
||||
/// is `true`.
|
||||
///
|
||||
/// Returns number of headers between the current best finalized header, known to the pezpallet
|
||||
/// and the bundled header.
|
||||
pub fn check_obsolete_from_extension(
|
||||
call_info: &SubmitFinalityProofInfo<BlockNumberOf<T::BridgedChain>>,
|
||||
) -> Result<BlockNumberOf<T::BridgedChain>, Error<T, I>> {
|
||||
// do basic checks first
|
||||
let improved_by = Self::check_obsolete(call_info.block_number, call_info.current_set_id)?;
|
||||
|
||||
// if submitter has NOT specified that it wants free execution, then we are done
|
||||
if !call_info.is_free_execution_expected {
|
||||
return Ok(improved_by);
|
||||
}
|
||||
|
||||
// else - if we can not accept more free headers, "reject" the transaction
|
||||
if !Self::has_free_header_slots() {
|
||||
tracing::trace!(
|
||||
target: crate::LOG_TARGET,
|
||||
chain_id=?T::BridgedChain::ID,
|
||||
block_number=?call_info.block_number,
|
||||
"Cannot accept free header. No more free slots remaining"
|
||||
);
|
||||
|
||||
return Err(Error::<T, I>::FreeHeadersLimitExceded);
|
||||
}
|
||||
|
||||
// ensure that the `improved_by` is larger than the configured free interval
|
||||
if !call_info.is_mandatory {
|
||||
if let Some(free_headers_interval) = T::FreeHeadersInterval::get() {
|
||||
if improved_by < free_headers_interval.into() {
|
||||
tracing::trace!(
|
||||
target: crate::LOG_TARGET,
|
||||
chain_id=?T::BridgedChain::ID,
|
||||
block_number=?call_info.block_number,
|
||||
?improved_by,
|
||||
%free_headers_interval,
|
||||
"Cannot accept free header. Too small difference between submitted headers"
|
||||
);
|
||||
|
||||
return Err(Error::<T, I>::BelowFreeHeaderInterval);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// let's also check whether the header submission fits the hardcoded limits. A normal
|
||||
// relayer would check that before submitting a transaction (since limits are constants
|
||||
// and do not depend on a volatile runtime state), but the ckeck itself is cheap, so
|
||||
// let's do it here too
|
||||
if !call_info.fits_limits() {
|
||||
return Err(Error::<T, I>::HeaderOverflowLimits);
|
||||
}
|
||||
|
||||
Ok(improved_by)
|
||||
}
|
||||
|
||||
/// Check that the GRANDPA head provided by the `SubmitFinalityProof` is better than the best
|
||||
/// one we know. Additionally, checks if `current_set_id` matches the current authority set
|
||||
/// id, if specified. This method is called by the call code and the transaction extension,
|
||||
/// so it does not check the free execution.
|
||||
///
|
||||
/// Returns number of headers between the current best finalized header, known to the pezpallet
|
||||
/// and the bundled header.
|
||||
pub fn check_obsolete(
|
||||
finality_target: BlockNumberOf<T::BridgedChain>,
|
||||
current_set_id: Option<SetId>,
|
||||
) -> Result<BlockNumberOf<T::BridgedChain>, Error<T, I>> {
|
||||
let best_finalized = BestFinalized::<T, I>::get().ok_or_else(|| {
|
||||
tracing::trace!(
|
||||
target: crate::LOG_TARGET,
|
||||
header=?finality_target,
|
||||
"Cannot finalize header because pezpallet is not yet initialized"
|
||||
);
|
||||
<Error<T, I>>::NotInitialized
|
||||
})?;
|
||||
|
||||
let improved_by = match finality_target.checked_sub(&best_finalized.number()) {
|
||||
Some(improved_by) if improved_by > Zero::zero() => improved_by,
|
||||
_ => {
|
||||
tracing::trace!(
|
||||
target: crate::LOG_TARGET,
|
||||
bundled=?finality_target,
|
||||
best=?best_finalized,
|
||||
"Cannot finalize obsolete header"
|
||||
);
|
||||
|
||||
return Err(Error::<T, I>::OldHeader);
|
||||
},
|
||||
};
|
||||
|
||||
if let Some(current_set_id) = current_set_id {
|
||||
let actual_set_id = <CurrentAuthoritySet<T, I>>::get().set_id;
|
||||
if current_set_id != actual_set_id {
|
||||
tracing::trace!(
|
||||
target: crate::LOG_TARGET,
|
||||
bundled=?current_set_id,
|
||||
best=?actual_set_id,
|
||||
"Cannot finalize header signed by unknown authority set"
|
||||
);
|
||||
|
||||
return Err(Error::<T, I>::InvalidAuthoritySetId);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(improved_by)
|
||||
}
|
||||
|
||||
/// Check if the `SubmitFinalityProof` was successfully executed.
|
||||
pub fn was_successful(finality_target: BlockNumberOf<T::BridgedChain>) -> bool {
|
||||
match BestFinalized::<T, I>::get() {
|
||||
Some(best_finalized) => best_finalized.number() == finality_target,
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait representing a call that is a sub type of this pezpallet's call.
|
||||
pub trait CallSubType<T: Config<I, RuntimeCall = Self>, I: 'static>:
|
||||
IsSubType<CallableCallFor<Pezpallet<T, I>, T>>
|
||||
{
|
||||
/// Extract finality proof info from a runtime call.
|
||||
fn submit_finality_proof_info(
|
||||
&self,
|
||||
) -> Option<SubmitFinalityProofInfo<BridgedBlockNumber<T, I>>> {
|
||||
if let Some(crate::Call::<T, I>::submit_finality_proof { finality_target, justification }) =
|
||||
self.is_sub_type()
|
||||
{
|
||||
return Some(submit_finality_proof_info_from_args::<T, I>(
|
||||
finality_target,
|
||||
justification,
|
||||
None,
|
||||
false,
|
||||
));
|
||||
} else if let Some(crate::Call::<T, I>::submit_finality_proof_ex {
|
||||
finality_target,
|
||||
justification,
|
||||
current_set_id,
|
||||
is_free_execution_expected,
|
||||
}) = self.is_sub_type()
|
||||
{
|
||||
return Some(submit_finality_proof_info_from_args::<T, I>(
|
||||
finality_target,
|
||||
justification,
|
||||
Some(*current_set_id),
|
||||
*is_free_execution_expected,
|
||||
));
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/// Validate Grandpa headers in order to avoid "mining" transactions that provide outdated
|
||||
/// bridged chain headers. Without this validation, even honest relayers may lose their funds
|
||||
/// if there are multiple relays running and submitting the same information.
|
||||
///
|
||||
/// Returns `Ok(None)` if the call is not the `submit_finality_proof` call of our pezpallet.
|
||||
/// Returns `Ok(Some(_))` if the call is the `submit_finality_proof` call of our pezpallet and
|
||||
/// we believe the call brings header that improves the pezpallet state.
|
||||
/// Returns `Err(_)` if the call is the `submit_finality_proof` call of our pezpallet and we
|
||||
/// believe that the call will fail.
|
||||
fn check_obsolete_submit_finality_proof(
|
||||
&self,
|
||||
) -> Result<
|
||||
Option<VerifiedSubmitFinalityProofInfo<BridgedBlockNumber<T, I>>>,
|
||||
TransactionValidityError,
|
||||
>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let call_info = match self.submit_finality_proof_info() {
|
||||
Some(finality_proof) => finality_proof,
|
||||
_ => return Ok(None),
|
||||
};
|
||||
|
||||
if Pezpallet::<T, I>::ensure_not_halted().is_err() {
|
||||
return Err(InvalidTransaction::Call.into());
|
||||
}
|
||||
|
||||
let result = SubmitFinalityProofHelper::<T, I>::check_obsolete_from_extension(&call_info);
|
||||
match result {
|
||||
Ok(improved_by) =>
|
||||
Ok(Some(VerifiedSubmitFinalityProofInfo { base: call_info, improved_by })),
|
||||
Err(Error::<T, I>::OldHeader) => Err(InvalidTransaction::Stale.into()),
|
||||
Err(_) => Err(InvalidTransaction::Call.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config<I>, I: 'static> CallSubType<T, I> for T::RuntimeCall where
|
||||
T::RuntimeCall: IsSubType<CallableCallFor<Pezpallet<T, I>, T>>
|
||||
{
|
||||
}
|
||||
|
||||
/// Extract finality proof info from the submitted header and justification.
|
||||
pub(crate) fn submit_finality_proof_info_from_args<T: Config<I>, I: 'static>(
|
||||
finality_target: &BridgedHeader<T, I>,
|
||||
justification: &GrandpaJustification<BridgedHeader<T, I>>,
|
||||
current_set_id: Option<SetId>,
|
||||
is_free_execution_expected: bool,
|
||||
) -> SubmitFinalityProofInfo<BridgedBlockNumber<T, I>> {
|
||||
// check if call exceeds limits. In other words - whether some size or weight is included
|
||||
// in the call
|
||||
let extras =
|
||||
submit_finality_proof_limits_extras::<T::BridgedChain>(finality_target, justification);
|
||||
|
||||
// We do care about extra weight because of more-than-expected headers in the votes
|
||||
// ancestries. But we have problems computing extra weight for additional headers (weight of
|
||||
// additional header is too small, so that our benchmarks aren't detecting that). So if there
|
||||
// are more than expected headers in votes ancestries, we will treat the whole call weight
|
||||
// as an extra weight.
|
||||
let extra_weight = if extras.is_weight_limit_exceeded {
|
||||
let precommits_len = justification.commit.precommits.len().saturated_into();
|
||||
let votes_ancestries_len = justification.votes_ancestries.len().saturated_into();
|
||||
T::WeightInfo::submit_finality_proof(precommits_len, votes_ancestries_len)
|
||||
} else {
|
||||
Weight::zero()
|
||||
};
|
||||
|
||||
SubmitFinalityProofInfo {
|
||||
block_number: *finality_target.number(),
|
||||
current_set_id,
|
||||
is_mandatory: extras.is_mandatory_finality_target,
|
||||
is_free_execution_expected,
|
||||
extra_weight,
|
||||
extra_size: extras.extra_size,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{
|
||||
call_ext::CallSubType,
|
||||
mock::{
|
||||
run_test, test_header, FreeHeadersInterval, RuntimeCall, TestBridgedChain, TestNumber,
|
||||
TestRuntime,
|
||||
},
|
||||
BestFinalized, Config, CurrentAuthoritySet, FreeHeadersRemaining, PalletOperatingMode,
|
||||
StoredAuthoritySet, WeightInfo,
|
||||
};
|
||||
use bp_header_pez_chain::{ChainWithGrandpa, SubmitFinalityProofInfo};
|
||||
use pezbp_runtime::{BasicOperatingMode, HeaderId};
|
||||
use bp_test_utils::{
|
||||
make_default_justification, make_justification_for_header, JustificationGeneratorParams,
|
||||
TEST_GRANDPA_SET_ID,
|
||||
};
|
||||
use codec::Encode;
|
||||
use pezframe_support::weights::Weight;
|
||||
use pezsp_runtime::{testing::DigestItem, traits::Header as _, SaturatedConversion};
|
||||
|
||||
fn validate_block_submit(num: TestNumber) -> bool {
|
||||
let bridge_grandpa_call = crate::Call::<TestRuntime, ()>::submit_finality_proof_ex {
|
||||
finality_target: Box::new(test_header(num)),
|
||||
justification: make_default_justification(&test_header(num)),
|
||||
// not initialized => zero
|
||||
current_set_id: 0,
|
||||
is_free_execution_expected: false,
|
||||
};
|
||||
RuntimeCall::check_obsolete_submit_finality_proof(&RuntimeCall::Grandpa(
|
||||
bridge_grandpa_call,
|
||||
))
|
||||
.is_ok()
|
||||
}
|
||||
|
||||
fn sync_to_header_10() {
|
||||
let header10_hash = pezsp_core::H256::default();
|
||||
BestFinalized::<TestRuntime, ()>::put(HeaderId(10, header10_hash));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extension_rejects_obsolete_header() {
|
||||
run_test(|| {
|
||||
// when current best finalized is #10 and we're trying to import header#5 => tx is
|
||||
// rejected
|
||||
sync_to_header_10();
|
||||
assert!(!validate_block_submit(5));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extension_rejects_same_header() {
|
||||
run_test(|| {
|
||||
// when current best finalized is #10 and we're trying to import header#10 => tx is
|
||||
// rejected
|
||||
sync_to_header_10();
|
||||
assert!(!validate_block_submit(10));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extension_rejects_new_header_if_pallet_is_halted() {
|
||||
run_test(|| {
|
||||
// when pezpallet is halted => tx is rejected
|
||||
sync_to_header_10();
|
||||
PalletOperatingMode::<TestRuntime, ()>::put(BasicOperatingMode::Halted);
|
||||
|
||||
assert!(!validate_block_submit(15));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extension_rejects_new_header_if_set_id_is_invalid() {
|
||||
run_test(|| {
|
||||
// when set id is different from the passed one => tx is rejected
|
||||
sync_to_header_10();
|
||||
let next_set = StoredAuthoritySet::<TestRuntime, ()>::try_new(vec![], 0x42).unwrap();
|
||||
CurrentAuthoritySet::<TestRuntime, ()>::put(next_set);
|
||||
|
||||
assert!(!validate_block_submit(15));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extension_rejects_new_header_if_free_execution_is_requested_and_free_submissions_are_not_accepted(
|
||||
) {
|
||||
run_test(|| {
|
||||
let bridge_grandpa_call = crate::Call::<TestRuntime, ()>::submit_finality_proof_ex {
|
||||
finality_target: Box::new(test_header(10 + FreeHeadersInterval::get() as u64)),
|
||||
justification: make_default_justification(&test_header(
|
||||
10 + FreeHeadersInterval::get() as u64,
|
||||
)),
|
||||
current_set_id: 0,
|
||||
is_free_execution_expected: true,
|
||||
};
|
||||
sync_to_header_10();
|
||||
|
||||
// when we can accept free headers => Ok
|
||||
FreeHeadersRemaining::<TestRuntime, ()>::put(2);
|
||||
assert!(RuntimeCall::check_obsolete_submit_finality_proof(&RuntimeCall::Grandpa(
|
||||
bridge_grandpa_call.clone(),
|
||||
),)
|
||||
.is_ok());
|
||||
|
||||
// when we can NOT accept free headers => Err
|
||||
FreeHeadersRemaining::<TestRuntime, ()>::put(0);
|
||||
assert!(RuntimeCall::check_obsolete_submit_finality_proof(&RuntimeCall::Grandpa(
|
||||
bridge_grandpa_call.clone(),
|
||||
),)
|
||||
.is_err());
|
||||
|
||||
// when called outside of transaction => Ok
|
||||
FreeHeadersRemaining::<TestRuntime, ()>::kill();
|
||||
assert!(RuntimeCall::check_obsolete_submit_finality_proof(&RuntimeCall::Grandpa(
|
||||
bridge_grandpa_call,
|
||||
),)
|
||||
.is_ok());
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extension_rejects_new_header_if_it_overflow_size_limits() {
|
||||
run_test(|| {
|
||||
let mut large_finality_target = test_header(10 + FreeHeadersInterval::get() as u64);
|
||||
large_finality_target
|
||||
.digest_mut()
|
||||
.push(DigestItem::Other(vec![42u8; 1024 * 1024]));
|
||||
let justification_params = JustificationGeneratorParams {
|
||||
header: large_finality_target.clone(),
|
||||
..Default::default()
|
||||
};
|
||||
let large_justification = make_justification_for_header(justification_params);
|
||||
|
||||
let bridge_grandpa_call = crate::Call::<TestRuntime, ()>::submit_finality_proof_ex {
|
||||
finality_target: Box::new(large_finality_target),
|
||||
justification: large_justification,
|
||||
current_set_id: 0,
|
||||
is_free_execution_expected: true,
|
||||
};
|
||||
sync_to_header_10();
|
||||
|
||||
// if overflow size limits => Err
|
||||
FreeHeadersRemaining::<TestRuntime, ()>::put(2);
|
||||
assert!(RuntimeCall::check_obsolete_submit_finality_proof(&RuntimeCall::Grandpa(
|
||||
bridge_grandpa_call.clone(),
|
||||
),)
|
||||
.is_err());
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extension_rejects_new_header_if_it_overflow_weight_limits() {
|
||||
run_test(|| {
|
||||
let finality_target = test_header(10 + FreeHeadersInterval::get() as u64);
|
||||
let justification_params = JustificationGeneratorParams {
|
||||
header: finality_target.clone(),
|
||||
ancestors: TestBridgedChain::REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY,
|
||||
..Default::default()
|
||||
};
|
||||
let justification = make_justification_for_header(justification_params);
|
||||
|
||||
let bridge_grandpa_call = crate::Call::<TestRuntime, ()>::submit_finality_proof_ex {
|
||||
finality_target: Box::new(finality_target),
|
||||
justification,
|
||||
current_set_id: 0,
|
||||
is_free_execution_expected: true,
|
||||
};
|
||||
sync_to_header_10();
|
||||
|
||||
// if overflow weight limits => Err
|
||||
FreeHeadersRemaining::<TestRuntime, ()>::put(2);
|
||||
assert!(RuntimeCall::check_obsolete_submit_finality_proof(&RuntimeCall::Grandpa(
|
||||
bridge_grandpa_call.clone(),
|
||||
),)
|
||||
.is_err());
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extension_rejects_new_header_if_free_execution_is_requested_and_improved_by_is_below_expected(
|
||||
) {
|
||||
run_test(|| {
|
||||
let bridge_grandpa_call = crate::Call::<TestRuntime, ()>::submit_finality_proof_ex {
|
||||
finality_target: Box::new(test_header(100)),
|
||||
justification: make_default_justification(&test_header(100)),
|
||||
current_set_id: 0,
|
||||
is_free_execution_expected: true,
|
||||
};
|
||||
sync_to_header_10();
|
||||
|
||||
// when `improved_by` is less than the free interval
|
||||
BestFinalized::<TestRuntime, ()>::put(HeaderId(
|
||||
100 - FreeHeadersInterval::get() as u64 + 1,
|
||||
pezsp_core::H256::default(),
|
||||
));
|
||||
assert!(RuntimeCall::check_obsolete_submit_finality_proof(&RuntimeCall::Grandpa(
|
||||
bridge_grandpa_call.clone(),
|
||||
),)
|
||||
.is_err());
|
||||
|
||||
// when `improved_by` is equal to the free interval
|
||||
BestFinalized::<TestRuntime, ()>::put(HeaderId(
|
||||
100 - FreeHeadersInterval::get() as u64,
|
||||
pezsp_core::H256::default(),
|
||||
));
|
||||
assert!(RuntimeCall::check_obsolete_submit_finality_proof(&RuntimeCall::Grandpa(
|
||||
bridge_grandpa_call.clone(),
|
||||
),)
|
||||
.is_ok());
|
||||
|
||||
// when `improved_by` is larger than the free interval
|
||||
BestFinalized::<TestRuntime, ()>::put(HeaderId(
|
||||
100 - FreeHeadersInterval::get() as u64 - 1,
|
||||
pezsp_core::H256::default(),
|
||||
));
|
||||
assert!(RuntimeCall::check_obsolete_submit_finality_proof(&RuntimeCall::Grandpa(
|
||||
bridge_grandpa_call.clone(),
|
||||
),)
|
||||
.is_ok());
|
||||
|
||||
// when `improved_by` is less than the free interval BUT it is a mandatory header
|
||||
let mut mandatory_header = test_header(100);
|
||||
let consensus_log = pezsp_consensus_grandpa::ConsensusLog::<TestNumber>::ScheduledChange(
|
||||
pezsp_consensus_grandpa::ScheduledChange {
|
||||
next_authorities: bp_test_utils::authority_list(),
|
||||
delay: 0,
|
||||
},
|
||||
);
|
||||
mandatory_header.digest = pezsp_runtime::Digest {
|
||||
logs: vec![DigestItem::Consensus(
|
||||
pezsp_consensus_grandpa::GRANDPA_ENGINE_ID,
|
||||
consensus_log.encode(),
|
||||
)],
|
||||
};
|
||||
let justification = make_justification_for_header(JustificationGeneratorParams {
|
||||
header: mandatory_header.clone(),
|
||||
set_id: 1,
|
||||
..Default::default()
|
||||
});
|
||||
let bridge_grandpa_call = crate::Call::<TestRuntime, ()>::submit_finality_proof_ex {
|
||||
finality_target: Box::new(mandatory_header),
|
||||
justification,
|
||||
current_set_id: 0,
|
||||
is_free_execution_expected: true,
|
||||
};
|
||||
BestFinalized::<TestRuntime, ()>::put(HeaderId(
|
||||
100 - FreeHeadersInterval::get() as u64 + 1,
|
||||
pezsp_core::H256::default(),
|
||||
));
|
||||
assert!(RuntimeCall::check_obsolete_submit_finality_proof(&RuntimeCall::Grandpa(
|
||||
bridge_grandpa_call.clone(),
|
||||
),)
|
||||
.is_ok());
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extension_accepts_new_header() {
|
||||
run_test(|| {
|
||||
// when current best finalized is #10 and we're trying to import header#15 => tx is
|
||||
// accepted
|
||||
sync_to_header_10();
|
||||
assert!(validate_block_submit(15));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn submit_finality_proof_info_is_parsed() {
|
||||
// when `submit_finality_proof` is used, `current_set_id` is set to `None`
|
||||
let deprecated_call =
|
||||
RuntimeCall::Grandpa(crate::Call::<TestRuntime, ()>::submit_finality_proof {
|
||||
finality_target: Box::new(test_header(42)),
|
||||
justification: make_default_justification(&test_header(42)),
|
||||
});
|
||||
assert_eq!(
|
||||
deprecated_call.submit_finality_proof_info(),
|
||||
Some(SubmitFinalityProofInfo {
|
||||
block_number: 42,
|
||||
current_set_id: None,
|
||||
extra_weight: Weight::zero(),
|
||||
extra_size: 0,
|
||||
is_mandatory: false,
|
||||
is_free_execution_expected: false,
|
||||
})
|
||||
);
|
||||
|
||||
// when `submit_finality_proof_ex` is used, `current_set_id` is set to `Some`
|
||||
let deprecated_call =
|
||||
RuntimeCall::Grandpa(crate::Call::<TestRuntime, ()>::submit_finality_proof_ex {
|
||||
finality_target: Box::new(test_header(42)),
|
||||
justification: make_default_justification(&test_header(42)),
|
||||
current_set_id: 777,
|
||||
is_free_execution_expected: false,
|
||||
});
|
||||
assert_eq!(
|
||||
deprecated_call.submit_finality_proof_info(),
|
||||
Some(SubmitFinalityProofInfo {
|
||||
block_number: 42,
|
||||
current_set_id: Some(777),
|
||||
extra_weight: Weight::zero(),
|
||||
extra_size: 0,
|
||||
is_mandatory: false,
|
||||
is_free_execution_expected: false,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extension_returns_correct_extra_size_if_call_arguments_are_too_large() {
|
||||
// when call arguments are below our limit => no refund
|
||||
let small_finality_target = test_header(1);
|
||||
let justification_params = JustificationGeneratorParams {
|
||||
header: small_finality_target.clone(),
|
||||
..Default::default()
|
||||
};
|
||||
let small_justification = make_justification_for_header(justification_params);
|
||||
let small_call = RuntimeCall::Grandpa(crate::Call::submit_finality_proof_ex {
|
||||
finality_target: Box::new(small_finality_target),
|
||||
justification: small_justification,
|
||||
current_set_id: TEST_GRANDPA_SET_ID,
|
||||
is_free_execution_expected: false,
|
||||
});
|
||||
assert_eq!(small_call.submit_finality_proof_info().unwrap().extra_size, 0);
|
||||
|
||||
// when call arguments are too large => partial refund
|
||||
let mut large_finality_target = test_header(1);
|
||||
large_finality_target
|
||||
.digest_mut()
|
||||
.push(DigestItem::Other(vec![42u8; 1024 * 1024]));
|
||||
let justification_params = JustificationGeneratorParams {
|
||||
header: large_finality_target.clone(),
|
||||
..Default::default()
|
||||
};
|
||||
let large_justification = make_justification_for_header(justification_params);
|
||||
let large_call = RuntimeCall::Grandpa(crate::Call::submit_finality_proof_ex {
|
||||
finality_target: Box::new(large_finality_target),
|
||||
justification: large_justification,
|
||||
current_set_id: TEST_GRANDPA_SET_ID,
|
||||
is_free_execution_expected: false,
|
||||
});
|
||||
assert_ne!(large_call.submit_finality_proof_info().unwrap().extra_size, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extension_returns_correct_extra_weight_if_there_are_too_many_headers_in_votes_ancestry() {
|
||||
let finality_target = test_header(1);
|
||||
let mut justification_params = JustificationGeneratorParams {
|
||||
header: finality_target.clone(),
|
||||
ancestors: TestBridgedChain::REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
// when there are `REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY` headers => no refund
|
||||
let justification = make_justification_for_header(justification_params.clone());
|
||||
let call = RuntimeCall::Grandpa(crate::Call::submit_finality_proof_ex {
|
||||
finality_target: Box::new(finality_target.clone()),
|
||||
justification,
|
||||
current_set_id: TEST_GRANDPA_SET_ID,
|
||||
is_free_execution_expected: false,
|
||||
});
|
||||
assert_eq!(call.submit_finality_proof_info().unwrap().extra_weight, Weight::zero());
|
||||
|
||||
// when there are `REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY + 1` headers => full refund
|
||||
justification_params.ancestors += 1;
|
||||
let justification = make_justification_for_header(justification_params);
|
||||
let call_weight = <TestRuntime as Config>::WeightInfo::submit_finality_proof(
|
||||
justification.commit.precommits.len().saturated_into(),
|
||||
justification.votes_ancestries.len().saturated_into(),
|
||||
);
|
||||
let call = RuntimeCall::Grandpa(crate::Call::submit_finality_proof_ex {
|
||||
finality_target: Box::new(finality_target),
|
||||
justification,
|
||||
current_set_id: TEST_GRANDPA_SET_ID,
|
||||
is_free_execution_expected: false,
|
||||
});
|
||||
assert_eq!(call.submit_finality_proof_info().unwrap().extra_weight, call_weight);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_obsolete_submit_finality_proof_returns_correct_improved_by() {
|
||||
run_test(|| {
|
||||
fn make_call(number: u64) -> RuntimeCall {
|
||||
RuntimeCall::Grandpa(crate::Call::<TestRuntime, ()>::submit_finality_proof_ex {
|
||||
finality_target: Box::new(test_header(number)),
|
||||
justification: make_default_justification(&test_header(number)),
|
||||
current_set_id: 0,
|
||||
is_free_execution_expected: false,
|
||||
})
|
||||
}
|
||||
|
||||
sync_to_header_10();
|
||||
|
||||
// when the difference between headers is 1
|
||||
assert_eq!(
|
||||
RuntimeCall::check_obsolete_submit_finality_proof(&make_call(11))
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.improved_by,
|
||||
1,
|
||||
);
|
||||
|
||||
// when the difference between headers is 2
|
||||
assert_eq!(
|
||||
RuntimeCall::check_obsolete_submit_finality_proof(&make_call(12))
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.improved_by,
|
||||
2,
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_obsolete_submit_finality_proof_ignores_other_calls() {
|
||||
run_test(|| {
|
||||
let call =
|
||||
RuntimeCall::System(pezframe_system::Call::<TestRuntime>::remark { remark: vec![42] });
|
||||
|
||||
assert_eq!(RuntimeCall::check_obsolete_submit_finality_proof(&call), Ok(None));
|
||||
})
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,117 @@
|
||||
// Copyright (C) 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/>.
|
||||
|
||||
// From construct_runtime macro
|
||||
#![allow(clippy::from_over_into)]
|
||||
|
||||
use bp_header_pez_chain::ChainWithGrandpa;
|
||||
use pezbp_runtime::{Chain, ChainId};
|
||||
use pezframe_support::{
|
||||
construct_runtime, derive_impl, parameter_types, pezsp_runtime::StateVersion, traits::Hooks,
|
||||
weights::Weight,
|
||||
};
|
||||
use pezsp_core::sr25519::Signature;
|
||||
|
||||
pub type AccountId = u64;
|
||||
pub type TestHeader = pezsp_runtime::testing::Header;
|
||||
pub type TestNumber = u64;
|
||||
|
||||
type Block = pezframe_system::mocking::MockBlock<TestRuntime>;
|
||||
|
||||
pub const MAX_BRIDGED_AUTHORITIES: u32 = 5;
|
||||
|
||||
use crate as grandpa;
|
||||
|
||||
construct_runtime! {
|
||||
pub enum TestRuntime
|
||||
{
|
||||
System: pezframe_system::{Pezpallet, Call, Config<T>, Storage, Event<T>},
|
||||
Grandpa: grandpa::{Pezpallet, Call, Event<T>},
|
||||
}
|
||||
}
|
||||
|
||||
#[derive_impl(pezframe_system::config_preludes::TestDefaultConfig)]
|
||||
impl pezframe_system::Config for TestRuntime {
|
||||
type Block = Block;
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub const MaxFreeHeadersPerBlock: u32 = 2;
|
||||
pub const FreeHeadersInterval: u32 = 32;
|
||||
pub const HeadersToKeep: u32 = 5;
|
||||
}
|
||||
|
||||
impl grandpa::Config for TestRuntime {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type BridgedChain = TestBridgedChain;
|
||||
type MaxFreeHeadersPerBlock = MaxFreeHeadersPerBlock;
|
||||
type FreeHeadersInterval = FreeHeadersInterval;
|
||||
type HeadersToKeep = HeadersToKeep;
|
||||
type WeightInfo = ();
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TestBridgedChain;
|
||||
|
||||
impl Chain for TestBridgedChain {
|
||||
const ID: ChainId = *b"tbch";
|
||||
|
||||
type BlockNumber = pezframe_system::pezpallet_prelude::BlockNumberFor<TestRuntime>;
|
||||
type Hash = <TestRuntime as pezframe_system::Config>::Hash;
|
||||
type Hasher = <TestRuntime as pezframe_system::Config>::Hashing;
|
||||
type Header = TestHeader;
|
||||
|
||||
type AccountId = AccountId;
|
||||
type Balance = u64;
|
||||
type Nonce = u64;
|
||||
type Signature = Signature;
|
||||
|
||||
const STATE_VERSION: StateVersion = StateVersion::V1;
|
||||
|
||||
fn max_extrinsic_size() -> u32 {
|
||||
unreachable!()
|
||||
}
|
||||
fn max_extrinsic_weight() -> Weight {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
impl ChainWithGrandpa for TestBridgedChain {
|
||||
const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = "";
|
||||
const MAX_AUTHORITIES_COUNT: u32 = MAX_BRIDGED_AUTHORITIES;
|
||||
const REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY: u32 = 8;
|
||||
const MAX_MANDATORY_HEADER_SIZE: u32 = 256;
|
||||
const AVERAGE_HEADER_SIZE: u32 = 64;
|
||||
}
|
||||
|
||||
/// Return test externalities to use in tests.
|
||||
pub fn new_test_ext() -> pezsp_io::TestExternalities {
|
||||
pezsp_io::TestExternalities::new(Default::default())
|
||||
}
|
||||
|
||||
/// Return test within default test externalities context.
|
||||
pub fn run_test<T>(test: impl FnOnce() -> T) -> T {
|
||||
new_test_ext().execute_with(|| {
|
||||
let _ = Grandpa::on_initialize(0);
|
||||
test()
|
||||
})
|
||||
}
|
||||
|
||||
/// Return test header with given number.
|
||||
pub fn test_header(num: TestNumber) -> TestHeader {
|
||||
// We wrap the call to avoid explicit type annotations in our tests
|
||||
bp_test_utils::test_header(num)
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
// Copyright (C) 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/>.
|
||||
|
||||
//! Wrappers for public types that are implementing `MaxEncodedLen`
|
||||
|
||||
use crate::{Config, Error};
|
||||
|
||||
use bp_header_pez_chain::{AuthoritySet, ChainWithGrandpa};
|
||||
use codec::{Decode, Encode, MaxEncodedLen};
|
||||
use pezframe_support::{traits::Get, BoundedVec, CloneNoBound, RuntimeDebugNoBound};
|
||||
use scale_info::TypeInfo;
|
||||
use pezsp_consensus_grandpa::{AuthorityId, AuthorityList, AuthorityWeight, SetId};
|
||||
use pezsp_std::marker::PhantomData;
|
||||
|
||||
/// A bounded list of Grandpa authorities with associated weights.
|
||||
pub type StoredAuthorityList<MaxBridgedAuthorities> =
|
||||
BoundedVec<(AuthorityId, AuthorityWeight), MaxBridgedAuthorities>;
|
||||
|
||||
/// Adapter for using `T::BridgedChain::MAX_BRIDGED_AUTHORITIES` in `BoundedVec`.
|
||||
pub struct StoredAuthorityListLimit<T, I>(PhantomData<(T, I)>);
|
||||
|
||||
impl<T: Config<I>, I: 'static> Get<u32> for StoredAuthorityListLimit<T, I> {
|
||||
fn get() -> u32 {
|
||||
T::BridgedChain::MAX_AUTHORITIES_COUNT
|
||||
}
|
||||
}
|
||||
|
||||
/// A bounded GRANDPA Authority List and ID.
|
||||
#[derive(CloneNoBound, Decode, Encode, Eq, TypeInfo, MaxEncodedLen, RuntimeDebugNoBound)]
|
||||
#[scale_info(skip_type_params(T, I))]
|
||||
pub struct StoredAuthoritySet<T: Config<I>, I: 'static> {
|
||||
/// List of GRANDPA authorities for the current round.
|
||||
pub authorities: StoredAuthorityList<StoredAuthorityListLimit<T, I>>,
|
||||
/// Monotonic identifier of the current GRANDPA authority set.
|
||||
pub set_id: SetId,
|
||||
}
|
||||
|
||||
impl<T: Config<I>, I: 'static> StoredAuthoritySet<T, I> {
|
||||
/// Try to create a new bounded GRANDPA Authority Set from unbounded list.
|
||||
///
|
||||
/// Returns error if number of authorities in the provided list is too large.
|
||||
pub fn try_new(authorities: AuthorityList, set_id: SetId) -> Result<Self, Error<T, I>> {
|
||||
Ok(Self {
|
||||
authorities: TryFrom::try_from(authorities)
|
||||
.map_err(|_| Error::TooManyAuthoritiesInSet)?,
|
||||
set_id,
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns number of bytes that may be subtracted from the PoV component of
|
||||
/// `submit_finality_proof` call, because the actual authorities set is smaller than the maximal
|
||||
/// configured.
|
||||
///
|
||||
/// Maximal authorities set size is configured by the `MaxBridgedAuthorities` constant from
|
||||
/// the pezpallet configuration. The PoV of the call includes the size of maximal authorities
|
||||
/// count. If the actual size is smaller, we may subtract extra bytes from this component.
|
||||
pub fn unused_proof_size(&self) -> u64 {
|
||||
// we can only safely estimate bytes that are occupied by the authority data itself. We have
|
||||
// no means here to compute PoV bytes, occupied by extra trie nodes or extra bytes in the
|
||||
// whole set encoding
|
||||
let single_authority_max_encoded_len =
|
||||
<(AuthorityId, AuthorityWeight)>::max_encoded_len() as u64;
|
||||
let extra_authorities =
|
||||
T::BridgedChain::MAX_AUTHORITIES_COUNT.saturating_sub(self.authorities.len() as _);
|
||||
single_authority_max_encoded_len.saturating_mul(extra_authorities as u64)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config<I>, I: 'static> PartialEq for StoredAuthoritySet<T, I> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.set_id == other.set_id && self.authorities == other.authorities
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config<I>, I: 'static> Default for StoredAuthoritySet<T, I> {
|
||||
fn default() -> Self {
|
||||
StoredAuthoritySet { authorities: BoundedVec::default(), set_id: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config<I>, I: 'static> From<StoredAuthoritySet<T, I>> for AuthoritySet {
|
||||
fn from(t: StoredAuthoritySet<T, I>) -> Self {
|
||||
AuthoritySet { authorities: t.authorities.into(), set_id: t.set_id }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::mock::{TestRuntime, MAX_BRIDGED_AUTHORITIES};
|
||||
use bp_test_utils::authority_list;
|
||||
|
||||
type StoredAuthoritySet = super::StoredAuthoritySet<TestRuntime, ()>;
|
||||
|
||||
#[test]
|
||||
fn unused_proof_size_works() {
|
||||
let authority_entry = authority_list().pop().unwrap();
|
||||
|
||||
// when we have exactly `MaxBridgedAuthorities` authorities
|
||||
assert_eq!(
|
||||
StoredAuthoritySet::try_new(
|
||||
vec![authority_entry.clone(); MAX_BRIDGED_AUTHORITIES as usize],
|
||||
0,
|
||||
)
|
||||
.unwrap()
|
||||
.unused_proof_size(),
|
||||
0,
|
||||
);
|
||||
|
||||
// when we have less than `MaxBridgedAuthorities` authorities
|
||||
assert_eq!(
|
||||
StoredAuthoritySet::try_new(
|
||||
vec![authority_entry; MAX_BRIDGED_AUTHORITIES as usize - 1],
|
||||
0,
|
||||
)
|
||||
.unwrap()
|
||||
.unused_proof_size(),
|
||||
40,
|
||||
);
|
||||
|
||||
// and we can't have more than `MaxBridgedAuthorities` authorities in the bounded vec, so
|
||||
// no test for this case
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
// Copyright (C) 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 pezpallet_bridge_grandpa
|
||||
//!
|
||||
//! THIS FILE WAS AUTO-GENERATED USING THE BIZINIKIWI BENCHMARK CLI VERSION 4.0.0-dev
|
||||
//! DATE: 2023-03-02, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
|
||||
//! WORST CASE MAP SIZE: `1000000`
|
||||
//! HOSTNAME: `covid`, CPU: `11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz`
|
||||
//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024
|
||||
|
||||
// Executed Command:
|
||||
// target/release/unknown-bridge-node
|
||||
// benchmark
|
||||
// pezpallet
|
||||
// --chain=dev
|
||||
// --steps=50
|
||||
// --repeat=20
|
||||
// --pezpallet=pezpallet_bridge_grandpa
|
||||
// --extrinsic=*
|
||||
// --execution=wasm
|
||||
// --wasm-execution=Compiled
|
||||
// --heap-pages=4096
|
||||
// --output=./modules/grandpa/src/weights.rs
|
||||
// --template=./.maintain/bridge-weight-template.hbs
|
||||
|
||||
#![allow(clippy::all)]
|
||||
#![allow(unused_parens)]
|
||||
#![allow(unused_imports)]
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use pezframe_support::{
|
||||
traits::Get,
|
||||
weights::{constants::RocksDbWeight, Weight},
|
||||
};
|
||||
use pezsp_std::marker::PhantomData;
|
||||
|
||||
/// Weight functions needed for pezpallet_bridge_grandpa.
|
||||
pub trait WeightInfo {
|
||||
fn submit_finality_proof(p: u32, v: u32) -> Weight;
|
||||
fn force_set_pallet_state() -> Weight;
|
||||
}
|
||||
|
||||
/// Weights for `pezpallet_bridge_grandpa` that are generated using one of the Bridge testnets.
|
||||
///
|
||||
/// Those weights are test only and must never be used in production.
|
||||
pub struct BridgeWeight<T>(PhantomData<T>);
|
||||
impl<T: pezframe_system::Config> WeightInfo for BridgeWeight<T> {
|
||||
/// Storage: BridgeUnknownGrandpa PalletOperatingMode (r:1 w:0)
|
||||
///
|
||||
/// Proof: BridgeUnknownGrandpa PalletOperatingMode (max_values: Some(1), max_size: Some(1),
|
||||
/// added: 496, mode: MaxEncodedLen)
|
||||
///
|
||||
/// Storage: BridgeUnknownGrandpa RequestCount (r:1 w:1)
|
||||
///
|
||||
/// Proof: BridgeUnknownGrandpa RequestCount (max_values: Some(1), max_size: Some(4), added:
|
||||
/// 499, mode: MaxEncodedLen)
|
||||
///
|
||||
/// Storage: BridgeUnknownGrandpa BestFinalized (r:1 w:1)
|
||||
///
|
||||
/// Proof: BridgeUnknownGrandpa BestFinalized (max_values: Some(1), max_size: Some(36), added:
|
||||
/// 531, mode: MaxEncodedLen)
|
||||
///
|
||||
/// Storage: BridgeUnknownGrandpa CurrentAuthoritySet (r:1 w:0)
|
||||
///
|
||||
/// Proof: BridgeUnknownGrandpa CurrentAuthoritySet (max_values: Some(1), max_size: Some(209),
|
||||
/// added: 704, mode: MaxEncodedLen)
|
||||
///
|
||||
/// Storage: BridgeUnknownGrandpa ImportedHashesPointer (r:1 w:1)
|
||||
///
|
||||
/// Proof: BridgeUnknownGrandpa ImportedHashesPointer (max_values: Some(1), max_size: Some(4),
|
||||
/// added: 499, mode: MaxEncodedLen)
|
||||
///
|
||||
/// Storage: BridgeUnknownGrandpa ImportedHashes (r:1 w:1)
|
||||
///
|
||||
/// Proof: BridgeUnknownGrandpa ImportedHashes (max_values: Some(14400), max_size: Some(36),
|
||||
/// added: 2016, mode: MaxEncodedLen)
|
||||
///
|
||||
/// Storage: BridgeUnknownGrandpa ImportedHeaders (r:0 w:2)
|
||||
///
|
||||
/// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68),
|
||||
/// added: 2048, mode: MaxEncodedLen)
|
||||
///
|
||||
/// The range of component `p` is `[1, 4]`.
|
||||
///
|
||||
/// The range of component `v` is `[50, 100]`.
|
||||
fn submit_finality_proof(p: u32, v: u32) -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `394 + p * (60 ±0)`
|
||||
// Estimated: `4745`
|
||||
// Minimum execution time: 228_072 nanoseconds.
|
||||
Weight::from_parts(57_853_228, 4745)
|
||||
// Standard Error: 149_421
|
||||
.saturating_add(Weight::from_parts(36_708_702, 0).saturating_mul(p.into()))
|
||||
// Standard Error: 10_625
|
||||
.saturating_add(Weight::from_parts(1_469_032, 0).saturating_mul(v.into()))
|
||||
.saturating_add(T::DbWeight::get().reads(6_u64))
|
||||
.saturating_add(T::DbWeight::get().writes(6_u64))
|
||||
}
|
||||
|
||||
/// Storage: `BridgeZagrosGrandpa::CurrentAuthoritySet` (r:1 w:1)
|
||||
/// Proof: `BridgeZagrosGrandpa::CurrentAuthoritySet` (`max_values`: Some(1), `max_size`:
|
||||
/// Some(50250), added: 50745, mode: `MaxEncodedLen`)
|
||||
/// Storage: `BridgeZagrosGrandpa::ImportedHashesPointer` (r:1 w:1)
|
||||
/// Proof: `BridgeZagrosGrandpa::ImportedHashesPointer` (`max_values`: Some(1), `max_size`:
|
||||
/// Some(4), added: 499, mode: `MaxEncodedLen`) Storage: `BridgeZagrosGrandpa::ImportedHashes`
|
||||
/// (r:1 w:1) Proof: `BridgeZagrosGrandpa::ImportedHashes` (`max_values`: Some(1024),
|
||||
/// `max_size`: Some(36), added: 1521, mode: `MaxEncodedLen`)
|
||||
/// Storage: `BridgeZagrosGrandpa::BestFinalized` (r:0 w:1)
|
||||
/// Proof: `BridgeZagrosGrandpa::BestFinalized` (`max_values`: Some(1), `max_size`: Some(36),
|
||||
/// added: 531, mode: `MaxEncodedLen`) Storage: `BridgeZagrosGrandpa::ImportedHeaders` (r:0
|
||||
/// w:2) Proof: `BridgeZagrosGrandpa::ImportedHeaders` (`max_values`: Some(1024), `max_size`:
|
||||
/// Some(68), added: 1553, mode: `MaxEncodedLen`)
|
||||
fn force_set_pallet_state() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `452`
|
||||
// Estimated: `51735`
|
||||
// Minimum execution time: 62_232_000 picoseconds.
|
||||
Weight::from_parts(78_755_000, 0)
|
||||
.saturating_add(Weight::from_parts(0, 51735))
|
||||
.saturating_add(RocksDbWeight::get().reads(3))
|
||||
.saturating_add(RocksDbWeight::get().writes(6))
|
||||
}
|
||||
}
|
||||
|
||||
// For backwards compatibility and tests
|
||||
impl WeightInfo for () {
|
||||
/// Storage: BridgeUnknownGrandpa PalletOperatingMode (r:1 w:0)
|
||||
///
|
||||
/// Proof: BridgeUnknownGrandpa PalletOperatingMode (max_values: Some(1), max_size: Some(1),
|
||||
/// added: 496, mode: MaxEncodedLen)
|
||||
///
|
||||
/// Storage: BridgeUnknownGrandpa RequestCount (r:1 w:1)
|
||||
///
|
||||
/// Proof: BridgeUnknownGrandpa RequestCount (max_values: Some(1), max_size: Some(4), added:
|
||||
/// 499, mode: MaxEncodedLen)
|
||||
///
|
||||
/// Storage: BridgeUnknownGrandpa BestFinalized (r:1 w:1)
|
||||
///
|
||||
/// Proof: BridgeUnknownGrandpa BestFinalized (max_values: Some(1), max_size: Some(36), added:
|
||||
/// 531, mode: MaxEncodedLen)
|
||||
///
|
||||
/// Storage: BridgeUnknownGrandpa CurrentAuthoritySet (r:1 w:0)
|
||||
///
|
||||
/// Proof: BridgeUnknownGrandpa CurrentAuthoritySet (max_values: Some(1), max_size: Some(209),
|
||||
/// added: 704, mode: MaxEncodedLen)
|
||||
///
|
||||
/// Storage: BridgeUnknownGrandpa ImportedHashesPointer (r:1 w:1)
|
||||
///
|
||||
/// Proof: BridgeUnknownGrandpa ImportedHashesPointer (max_values: Some(1), max_size: Some(4),
|
||||
/// added: 499, mode: MaxEncodedLen)
|
||||
///
|
||||
/// Storage: BridgeUnknownGrandpa ImportedHashes (r:1 w:1)
|
||||
///
|
||||
/// Proof: BridgeUnknownGrandpa ImportedHashes (max_values: Some(14400), max_size: Some(36),
|
||||
/// added: 2016, mode: MaxEncodedLen)
|
||||
///
|
||||
/// Storage: BridgeUnknownGrandpa ImportedHeaders (r:0 w:2)
|
||||
///
|
||||
/// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68),
|
||||
/// added: 2048, mode: MaxEncodedLen)
|
||||
///
|
||||
/// The range of component `p` is `[1, 4]`.
|
||||
///
|
||||
/// The range of component `v` is `[50, 100]`.
|
||||
fn submit_finality_proof(p: u32, v: u32) -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `394 + p * (60 ±0)`
|
||||
// Estimated: `4745`
|
||||
// Minimum execution time: 228_072 nanoseconds.
|
||||
Weight::from_parts(57_853_228, 4745)
|
||||
// Standard Error: 149_421
|
||||
.saturating_add(Weight::from_parts(36_708_702, 0).saturating_mul(p.into()))
|
||||
// Standard Error: 10_625
|
||||
.saturating_add(Weight::from_parts(1_469_032, 0).saturating_mul(v.into()))
|
||||
.saturating_add(RocksDbWeight::get().reads(6_u64))
|
||||
.saturating_add(RocksDbWeight::get().writes(6_u64))
|
||||
}
|
||||
|
||||
/// Storage: `BridgeZagrosGrandpa::CurrentAuthoritySet` (r:1 w:1)
|
||||
/// Proof: `BridgeZagrosGrandpa::CurrentAuthoritySet` (`max_values`: Some(1), `max_size`:
|
||||
/// Some(50250), added: 50745, mode: `MaxEncodedLen`)
|
||||
/// Storage: `BridgeZagrosGrandpa::ImportedHashesPointer` (r:1 w:1)
|
||||
/// Proof: `BridgeZagrosGrandpa::ImportedHashesPointer` (`max_values`: Some(1), `max_size`:
|
||||
/// Some(4), added: 499, mode: `MaxEncodedLen`) Storage: `BridgeZagrosGrandpa::ImportedHashes`
|
||||
/// (r:1 w:1) Proof: `BridgeZagrosGrandpa::ImportedHashes` (`max_values`: Some(1024),
|
||||
/// `max_size`: Some(36), added: 1521, mode: `MaxEncodedLen`)
|
||||
/// Storage: `BridgeZagrosGrandpa::BestFinalized` (r:0 w:1)
|
||||
/// Proof: `BridgeZagrosGrandpa::BestFinalized` (`max_values`: Some(1), `max_size`: Some(36),
|
||||
/// added: 531, mode: `MaxEncodedLen`) Storage: `BridgeZagrosGrandpa::ImportedHeaders` (r:0
|
||||
/// w:2) Proof: `BridgeZagrosGrandpa::ImportedHeaders` (`max_values`: Some(1024), `max_size`:
|
||||
/// Some(68), added: 1553, mode: `MaxEncodedLen`)
|
||||
fn force_set_pallet_state() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `452`
|
||||
// Estimated: `51735`
|
||||
// Minimum execution time: 62_232_000 picoseconds.
|
||||
Weight::from_parts(78_755_000, 0)
|
||||
.saturating_add(Weight::from_parts(0, 51735))
|
||||
.saturating_add(RocksDbWeight::get().reads(3))
|
||||
.saturating_add(RocksDbWeight::get().writes(6))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
// Copyright (C) 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::{BridgeWeight, WeightInfo};
|
||||
|
||||
use pezframe_support::weights::Weight;
|
||||
|
||||
/// Extended weight info.
|
||||
pub trait WeightInfoExt: WeightInfo {
|
||||
// Our configuration assumes that the runtime has special signed extensions used to:
|
||||
//
|
||||
// 1) boost priority of `submit_finality_proof` transactions;
|
||||
//
|
||||
// 2) slash relayer if he submits an invalid transaction.
|
||||
//
|
||||
// We read and update storage values of other pallets (`pezpallet-bridge-relayers` and
|
||||
// balances/assets pezpallet). So we need to add this weight to the weight of our call.
|
||||
// Hence two following methods.
|
||||
|
||||
/// Extra weight that is added to the `submit_finality_proof` call weight by signed extensions
|
||||
/// that are declared at runtime level.
|
||||
fn submit_finality_proof_overhead_from_runtime() -> Weight;
|
||||
|
||||
// Functions that are directly mapped to extrinsics weights.
|
||||
|
||||
/// Weight of message delivery extrinsic.
|
||||
fn submit_finality_proof_weight(precommits_len: u32, votes_ancestries_len: u32) -> Weight {
|
||||
let base_weight = Self::submit_finality_proof(precommits_len, votes_ancestries_len);
|
||||
base_weight.saturating_add(Self::submit_finality_proof_overhead_from_runtime())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: pezframe_system::Config> WeightInfoExt for BridgeWeight<T> {
|
||||
fn submit_finality_proof_overhead_from_runtime() -> Weight {
|
||||
Weight::zero()
|
||||
}
|
||||
}
|
||||
|
||||
impl WeightInfoExt for () {
|
||||
fn submit_finality_proof_overhead_from_runtime() -> Weight {
|
||||
Weight::zero()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user