feat: initialize Kurdistan SDK - independent fork of Polkadot SDK

This commit is contained in:
2025-12-13 15:44:15 +03:00
commit 286de54384
6841 changed files with 1848356 additions and 0 deletions
+73
View File
@@ -0,0 +1,73 @@
[package]
name = "pallet-bridge-teyrchains"
version = "0.7.0"
description = "Module that allows bridged relay chains to exchange information on their teyrchains' heads."
authors.workspace = true
edition.workspace = true
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
repository.workspace = true
[lints]
workspace = true
[dependencies]
codec = { workspace = true }
scale-info = { features = ["derive"], workspace = true }
tracing = { workspace = true }
# Bridge Dependencies
bp-header-chain = { workspace = true }
bp-pezkuwi-core = { workspace = true }
bp-runtime = { workspace = true }
bp-teyrchains = { workspace = true }
pallet-bridge-grandpa = { workspace = true }
# Substrate Dependencies
frame-benchmarking = { optional = true, workspace = true }
frame-support = { workspace = true }
frame-system = { workspace = true }
sp-runtime = { workspace = true }
sp-std = { workspace = true }
[dev-dependencies]
bp-header-chain = { workspace = true, default-features = true }
bp-test-utils = { workspace = true, default-features = true }
sp-core = { workspace = true, default-features = true }
sp-io = { workspace = true, default-features = true }
[features]
default = ["std"]
std = [
"bp-header-chain/std",
"bp-pezkuwi-core/std",
"bp-runtime/std",
"bp-teyrchains/std",
"codec/std",
"frame-benchmarking/std",
"frame-support/std",
"frame-system/std",
"pallet-bridge-grandpa/std",
"scale-info/std",
"sp-runtime/std",
"sp-std/std",
"tracing/std",
]
runtime-benchmarks = [
"bp-header-chain/runtime-benchmarks",
"bp-pezkuwi-core/runtime-benchmarks",
"bp-runtime/runtime-benchmarks",
"bp-test-utils/runtime-benchmarks",
"bp-teyrchains/runtime-benchmarks",
"frame-benchmarking/runtime-benchmarks",
"frame-support/runtime-benchmarks",
"frame-system/runtime-benchmarks",
"pallet-bridge-grandpa/runtime-benchmarks",
"sp-io/runtime-benchmarks",
"sp-runtime/runtime-benchmarks",
]
try-runtime = [
"frame-support/try-runtime",
"frame-system/try-runtime",
"pallet-bridge-grandpa/try-runtime",
"sp-runtime/try-runtime",
]
+90
View File
@@ -0,0 +1,90 @@
# Bridge Teyrchains Pallet
The bridge teyrchains pallet is a light client for one or several teyrchains of the bridged relay chain.
It serves as a source of finalized teyrchain headers and is used when you need to build a bridge with
a teyrchain.
The pallet requires [bridge GRANDPA pallet](../grandpa/) to be deployed at the same chain - it is used
to verify storage proofs, generated at the bridged relay chain.
## A Brief Introduction into Teyrchains Finality
You can find detailed information on teyrchains finality in the
[PezkuwiChain-SDK](https://github.com/pezkuwichain/pezkuwi-sdk) repository. This section gives a brief overview of how the
teyrchain finality works and how to build a light client for a teyrchain.
The main thing there is that the teyrchain generates blocks on its own, but it can't achieve finality without
help of its relay chain. Instead, the teyrchain collators create a block and hand it over to the relay chain
validators. Validators validate the block and register the new teyrchain head in the
[`Heads` map](https://github.com/pezkuwichain/pezkuwi-sdk/blob/bc5005217a8c2e7c95b9011c96d7e619879b1200/polkadot/runtime/parachains/src/paras/mod.rs#L683-L686)
of the [`paras`](https://github.com/paritytech/polkadot-sdk/tree/master/polkadot/runtime/parachains/src/paras) pallet,
deployed at the relay chain. Keep in mind that this pallet, deployed at a relay chain, is **NOT** a bridge pallet,
even though the names are similar.
And what the bridge teyrchains pallet does, is simply verifying storage proofs of teyrchain heads within that
`Heads` map. It does that using relay chain header, that has been previously imported by the
[bridge GRANDPA pallet](../grandpa/). Once the proof is verified, the pallet knows that the given teyrchain
header has been finalized by the relay chain. The teyrchain header fields may then be used to verify storage
proofs, coming from the teyrchain. This allows the pallet to be used e.g. as a source of finality for the messages
pallet.
## Pallet Operations
The main entrypoint of the pallet is the `submit_teyrchain_heads` call. It has three arguments:
- storage proof of teyrchain heads from the `Heads` map;
- teyrchain identifiers and hashes of their heads from the storage proof;
- the relay block, at which the storage proof has been generated.
The pallet may track multiple teyrchains. And the teyrchains may use different primitives - one may use 128-bit block
numbers, other - 32-bit. To avoid extra decode operations, the pallet is using relay chain block number to order
teyrchain headers. Any finalized descendant of finalized relay block `RB`, which has teyrchain block `PB` in
its `Heads` map, is guaranteed to have either `PB`, or its descendant. So teyrchain block number grows with relay
block number.
The pallet may reject teyrchain head if it already knows better (or the same) head. In addition, pallet rejects
heads of untracked teyrchains.
The pallet doesn't track anything behind teyrchain heads. So it requires no initialization - it is ready to accept
headers right after deployment.
## Non-Essential Functionality
There may be a special account in every runtime where the bridge teyrchains 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.
If pallet 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 teyrchain heads to the pallet. 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 heads. The submitter
pays nothing for such transactions - they're simply removed from the transaction pool, when the block
is built.
The signed extension, however, is a bit limited - it only works with transactions that provide single
teyrchain head. So it won't work with multiple teyrchain heads transactions. This fits our needs
for [Kusama <> PezkuwiChain bridge](../../docs/pezkuwi-kusama-bridge-overview.md). If you need to deal
with other transaction formats, you may implement similar extension for your runtime.
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.
## Teyrchains Finality Relay
We have an offchain actor, who is watching for new teyrchain heads and submits them to the bridged chain.
It is the teyrchains relay - you may look at the [crate level documentation and the code](../../relays/teyrchains/).
@@ -0,0 +1,116 @@
// 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/>.
//! Teyrchains finality pallet benchmarking.
use crate::{
weights_ext::DEFAULT_TEYRCHAIN_HEAD_SIZE, Call, RelayBlockHash, RelayBlockHasher,
RelayBlockNumber,
};
use bp_pezkuwi_core::teyrchains::{ParaHash, ParaHeadsProof, ParaId};
use bp_runtime::UnverifiedStorageProofParams;
use frame_benchmarking::{account, benchmarks_instance_pallet};
use frame_system::RawOrigin;
use sp_std::prelude::*;
/// Pallet we're benchmarking here.
pub struct Pallet<T: Config<I>, I: 'static = ()>(crate::Pallet<T, I>);
/// Trait that must be implemented by runtime to benchmark the teyrchains finality pallet.
pub trait Config<I: 'static>: crate::Config<I> {
/// Returns vector of supported teyrchains.
fn teyrchains() -> Vec<ParaId>;
/// Generate teyrchain heads proof and prepare environment for verifying this proof.
fn prepare_teyrchain_heads_proof(
teyrchains: &[ParaId],
teyrchain_head_size: u32,
proof_params: UnverifiedStorageProofParams,
) -> (RelayBlockNumber, RelayBlockHash, ParaHeadsProof, Vec<(ParaId, ParaHash)>);
}
benchmarks_instance_pallet! {
where_clause {
where
<T as pallet_bridge_grandpa::Config<T::BridgesGrandpaPalletInstance>>::BridgedChain:
bp_runtime::Chain<
BlockNumber = RelayBlockNumber,
Hash = RelayBlockHash,
Hasher = RelayBlockHasher,
>,
}
// Benchmark `submit_teyrchain_heads` extrinsic with different number of teyrchains.
submit_teyrchain_heads_with_n_teyrchains {
let p in 1..(T::teyrchains().len() + 1) as u32;
let sender = account("sender", 0, 0);
let mut teyrchains = T::teyrchains();
let _ = if p <= teyrchains.len() as u32 {
teyrchains.split_off(p as usize)
} else {
Default::default()
};
tracing::trace!(target: crate::LOG_TARGET, "=== {:?}", teyrchains.len());
let (relay_block_number, relay_block_hash, teyrchain_heads_proof, teyrchains_heads) = T::prepare_teyrchain_heads_proof(
&teyrchains,
DEFAULT_TEYRCHAIN_HEAD_SIZE,
UnverifiedStorageProofParams::default(),
);
let at_relay_block = (relay_block_number, relay_block_hash);
}: submit_teyrchain_heads(RawOrigin::Signed(sender), at_relay_block, teyrchains_heads, teyrchain_heads_proof)
verify {
for teyrchain in teyrchains {
assert!(crate::Pallet::<T, I>::best_teyrchain_head(teyrchain).is_some());
}
}
// Benchmark `submit_teyrchain_heads` extrinsic with 1kb proof size.
submit_teyrchain_heads_with_1kb_proof {
let sender = account("sender", 0, 0);
let teyrchains = vec![T::teyrchains()[0]];
let (relay_block_number, relay_block_hash, teyrchain_heads_proof, teyrchains_heads) = T::prepare_teyrchain_heads_proof(
&teyrchains,
DEFAULT_TEYRCHAIN_HEAD_SIZE,
UnverifiedStorageProofParams::from_db_size(1024),
);
let at_relay_block = (relay_block_number, relay_block_hash);
}: submit_teyrchain_heads(RawOrigin::Signed(sender), at_relay_block, teyrchains_heads, teyrchain_heads_proof)
verify {
for teyrchain in teyrchains {
assert!(crate::Pallet::<T, I>::best_teyrchain_head(teyrchain).is_some());
}
}
// Benchmark `submit_teyrchain_heads` extrinsic with 16kb proof size.
submit_teyrchain_heads_with_16kb_proof {
let sender = account("sender", 0, 0);
let teyrchains = vec![T::teyrchains()[0]];
let (relay_block_number, relay_block_hash, teyrchain_heads_proof, teyrchains_heads) = T::prepare_teyrchain_heads_proof(
&teyrchains,
DEFAULT_TEYRCHAIN_HEAD_SIZE,
UnverifiedStorageProofParams::from_db_size(16 * 1024),
);
let at_relay_block = (relay_block_number, relay_block_hash);
}: submit_teyrchain_heads(RawOrigin::Signed(sender), at_relay_block, teyrchains_heads, teyrchain_heads_proof)
verify {
for teyrchain in teyrchains {
assert!(crate::Pallet::<T, I>::best_teyrchain_head(teyrchain).is_some());
}
}
impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::TestRuntime)
}
+439
View File
@@ -0,0 +1,439 @@
// 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::{Config, GrandpaPalletOf, Pallet, RelayBlockNumber};
use bp_header_chain::HeaderChain;
use bp_runtime::{HeaderId, OwnedBridgeModule};
use bp_teyrchains::{BestParaHeadHash, SubmitTeyrchainHeadsInfo};
use frame_support::{
dispatch::CallableCallFor,
traits::{Get, IsSubType},
};
use pallet_bridge_grandpa::SubmitFinalityProofHelper;
use sp_runtime::{
traits::Zero,
transaction_validity::{InvalidTransaction, TransactionValidityError},
RuntimeDebug,
};
/// Verified `SubmitTeyrchainHeadsInfo`.
#[derive(PartialEq, RuntimeDebug)]
pub struct VerifiedSubmitTeyrchainHeadsInfo {
/// Base call information.
pub base: SubmitTeyrchainHeadsInfo,
/// A difference between bundled bridged relay chain header and relay chain header number
/// used to prove best bridged teyrchain header, known to us before the call.
pub improved_by: RelayBlockNumber,
}
/// Helper struct that provides methods for working with the `SubmitTeyrchainHeads` call.
pub struct SubmitTeyrchainHeadsHelper<T: Config<I>, I: 'static> {
_phantom_data: sp_std::marker::PhantomData<(T, I)>,
}
impl<T: Config<I>, I: 'static> SubmitTeyrchainHeadsHelper<T, I> {
/// Check that is called from signed extension and takes the `is_free_execution_expected`
/// into account.
pub fn check_obsolete_from_extension(
update: &SubmitTeyrchainHeadsInfo,
) -> Result<RelayBlockNumber, TransactionValidityError> {
// first do all base checks
let improved_by = Self::check_obsolete(update)?;
// if we don't expect free execution - no more checks
if !update.is_free_execution_expected {
return Ok(improved_by);
}
// reject if no more free slots remaining in the block
if !SubmitFinalityProofHelper::<T, T::BridgesGrandpaPalletInstance>::has_free_header_slots()
{
tracing::trace!(
target: crate::LOG_TARGET,
para_id=?update.para_id,
"The free teyrchain head can't be updated: no more free slots left in the block."
);
return Err(InvalidTransaction::Call.into());
}
// if free headers interval is not configured and call is expected to execute
// for free => it is a relayer error, it should've been able to detect that.
let free_headers_interval = match T::FreeHeadersInterval::get() {
Some(free_headers_interval) => free_headers_interval,
None => return Ok(improved_by),
};
// reject if we are importing teyrchain headers too often
if improved_by < free_headers_interval {
tracing::trace!(
target: crate::LOG_TARGET,
para_id=?update.para_id,
%improved_by,
"The free teyrchain head can't be updated: it improves previous
best head while at least {free_headers_interval} is expected."
);
return Err(InvalidTransaction::Stale.into());
}
Ok(improved_by)
}
/// Check if the para head provided by the `SubmitTeyrchainHeads` is better than the best one
/// we know.
pub fn check_obsolete(
update: &SubmitTeyrchainHeadsInfo,
) -> Result<RelayBlockNumber, TransactionValidityError> {
// check if we know better teyrchain head already
let improved_by = match crate::ParasInfo::<T, I>::get(update.para_id) {
Some(stored_best_head) => {
let improved_by = match update
.at_relay_block
.0
.checked_sub(stored_best_head.best_head_hash.at_relay_block_number)
{
Some(improved_by) if improved_by > Zero::zero() => improved_by,
_ => {
tracing::trace!(
target: crate::LOG_TARGET,
para_id=?update.para_id,
"The teyrchain head can't be updated. The teyrchain head \
was already updated at better relay chain block {} >= {}.",
stored_best_head.best_head_hash.at_relay_block_number,
update.at_relay_block.0
);
return Err(InvalidTransaction::Stale.into());
},
};
if stored_best_head.best_head_hash.head_hash == update.para_head_hash {
tracing::trace!(
target: crate::LOG_TARGET,
para_id=?update.para_id,
para_head_hash=%update.para_head_hash,
"The teyrchain head can't be updated. The teyrchain head hash \
was already updated at block {} < {}.",
stored_best_head.best_head_hash.at_relay_block_number,
update.at_relay_block.0
);
return Err(InvalidTransaction::Stale.into());
}
improved_by
},
None => RelayBlockNumber::MAX,
};
// let's check if our chain had no reorgs and we still know the relay chain header
// used to craft the proof
if GrandpaPalletOf::<T, I>::finalized_header_state_root(update.at_relay_block.1).is_none() {
tracing::trace!(
target: crate::LOG_TARGET,
para_id=?update.para_id,
at_relay_block=?update.at_relay_block,
"The teyrchain head can't be updated. Relay chain header used to create \
teyrchain proof is missing from the storage."
);
return Err(InvalidTransaction::Call.into());
}
Ok(improved_by)
}
/// Check if the `SubmitTeyrchainHeads` was successfully executed.
pub fn was_successful(update: &SubmitTeyrchainHeadsInfo) -> bool {
match crate::ParasInfo::<T, I>::get(update.para_id) {
Some(stored_best_head) =>
stored_best_head.best_head_hash ==
BestParaHeadHash {
at_relay_block_number: update.at_relay_block.0,
head_hash: update.para_head_hash,
},
None => false,
}
}
}
/// Trait representing a call that is a sub type of this pallet's call.
pub trait CallSubType<T: Config<I, RuntimeCall = Self>, I: 'static>:
IsSubType<CallableCallFor<Pallet<T, I>, T>>
{
/// Create a new instance of `SubmitTeyrchainHeadsInfo` from a `SubmitTeyrchainHeads` call with
/// one single teyrchain entry.
fn one_entry_submit_teyrchain_heads_info(&self) -> Option<SubmitTeyrchainHeadsInfo> {
match self.is_sub_type() {
Some(crate::Call::<T, I>::submit_teyrchain_heads {
ref at_relay_block,
ref teyrchains,
..
}) => match &teyrchains[..] {
&[(para_id, para_head_hash)] => Some(SubmitTeyrchainHeadsInfo {
at_relay_block: HeaderId(at_relay_block.0, at_relay_block.1),
para_id,
para_head_hash,
is_free_execution_expected: false,
}),
_ => None,
},
Some(crate::Call::<T, I>::submit_teyrchain_heads_ex {
ref at_relay_block,
ref teyrchains,
is_free_execution_expected,
..
}) => match &teyrchains[..] {
&[(para_id, para_head_hash)] => Some(SubmitTeyrchainHeadsInfo {
at_relay_block: HeaderId(at_relay_block.0, at_relay_block.1),
para_id,
para_head_hash,
is_free_execution_expected: *is_free_execution_expected,
}),
_ => None,
},
_ => None,
}
}
/// Create a new instance of `SubmitTeyrchainHeadsInfo` from a `SubmitTeyrchainHeads` call with
/// one single teyrchain entry, if the entry is for the provided teyrchain id.
fn submit_teyrchain_heads_info_for(&self, para_id: u32) -> Option<SubmitTeyrchainHeadsInfo> {
self.one_entry_submit_teyrchain_heads_info()
.filter(|update| update.para_id.0 == para_id)
}
/// Validate teyrchain heads in order to avoid "mining" transactions that provide
/// outdated bridged teyrchain heads. Without this validation, even honest relayers
/// may lose their funds if there are multiple relays running and submitting the
/// same information.
///
/// This validation only works with transactions that are updating single teyrchain
/// head. We can't use unbounded validation - it may take too long and either break
/// block production, or "eat" significant portion of block production time literally
/// for nothing. In addition, the single-teyrchain-head-per-transaction is how the
/// pallet will be used in our environment.
fn check_obsolete_submit_teyrchain_heads(
&self,
) -> Result<Option<VerifiedSubmitTeyrchainHeadsInfo>, TransactionValidityError>
where
Self: Sized,
{
let update = match self.one_entry_submit_teyrchain_heads_info() {
Some(update) => update,
None => return Ok(None),
};
if Pallet::<T, I>::ensure_not_halted().is_err() {
return Err(InvalidTransaction::Call.into());
}
SubmitTeyrchainHeadsHelper::<T, I>::check_obsolete_from_extension(&update)
.map(|improved_by| Some(VerifiedSubmitTeyrchainHeadsInfo { base: update, improved_by }))
}
}
impl<T, I: 'static> CallSubType<T, I> for T::RuntimeCall
where
T: Config<I>,
T::RuntimeCall: IsSubType<CallableCallFor<Pallet<T, I>, T>>,
{
}
#[cfg(test)]
mod tests {
use crate::{
mock::{run_test, FreeHeadersInterval, RuntimeCall, TestRuntime},
CallSubType, PalletOperatingMode, ParaInfo, ParasInfo, RelayBlockHash, RelayBlockNumber,
};
use bp_header_chain::StoredHeaderData;
use bp_pezkuwi_core::teyrchains::{ParaHash, ParaHeadsProof, ParaId};
use bp_runtime::BasicOperatingMode;
use bp_teyrchains::BestParaHeadHash;
fn validate_submit_teyrchain_heads(
num: RelayBlockNumber,
teyrchains: Vec<(ParaId, ParaHash)>,
) -> bool {
RuntimeCall::Teyrchains(crate::Call::<TestRuntime, ()>::submit_teyrchain_heads_ex {
at_relay_block: (num, [num as u8; 32].into()),
teyrchains,
teyrchain_heads_proof: ParaHeadsProof { storage_proof: Default::default() },
is_free_execution_expected: false,
})
.check_obsolete_submit_teyrchain_heads()
.is_ok()
}
fn validate_free_submit_teyrchain_heads(
num: RelayBlockNumber,
teyrchains: Vec<(ParaId, ParaHash)>,
) -> bool {
RuntimeCall::Teyrchains(crate::Call::<TestRuntime, ()>::submit_teyrchain_heads_ex {
at_relay_block: (num, [num as u8; 32].into()),
teyrchains,
teyrchain_heads_proof: ParaHeadsProof { storage_proof: Default::default() },
is_free_execution_expected: true,
})
.check_obsolete_submit_teyrchain_heads()
.is_ok()
}
fn insert_relay_block(num: RelayBlockNumber) {
pallet_bridge_grandpa::ImportedHeaders::<TestRuntime, crate::Instance1>::insert(
RelayBlockHash::from([num as u8; 32]),
StoredHeaderData { number: num, state_root: RelayBlockHash::from([10u8; 32]) },
);
}
fn sync_to_relay_header_10() {
ParasInfo::<TestRuntime, ()>::insert(
ParaId(1),
ParaInfo {
best_head_hash: BestParaHeadHash {
at_relay_block_number: 10,
head_hash: [1u8; 32].into(),
},
next_imported_hash_position: 0,
},
);
}
#[test]
fn extension_rejects_header_from_the_obsolete_relay_block() {
run_test(|| {
// when current best finalized is #10 and we're trying to import header#5 => tx is
// rejected
sync_to_relay_header_10();
assert!(!validate_submit_teyrchain_heads(5, vec![(ParaId(1), [1u8; 32].into())]));
});
}
#[test]
fn extension_rejects_header_from_the_same_relay_block() {
run_test(|| {
// when current best finalized is #10 and we're trying to import header#10 => tx is
// rejected
sync_to_relay_header_10();
assert!(!validate_submit_teyrchain_heads(10, vec![(ParaId(1), [1u8; 32].into())]));
});
}
#[test]
fn extension_rejects_header_from_new_relay_block_with_same_hash() {
run_test(|| {
// when current best finalized is #10 and we're trying to import header#10 => tx is
// rejected
sync_to_relay_header_10();
assert!(!validate_submit_teyrchain_heads(20, vec![(ParaId(1), [1u8; 32].into())]));
});
}
#[test]
fn extension_rejects_header_if_pallet_is_halted() {
run_test(|| {
// when pallet is halted => tx is rejected
sync_to_relay_header_10();
PalletOperatingMode::<TestRuntime, ()>::put(BasicOperatingMode::Halted);
assert!(!validate_submit_teyrchain_heads(15, vec![(ParaId(1), [2u8; 32].into())]));
});
}
#[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_relay_header_10();
insert_relay_block(15);
assert!(validate_submit_teyrchain_heads(15, vec![(ParaId(1), [2u8; 32].into())]));
});
}
#[test]
fn extension_accepts_if_more_than_one_teyrchain_is_submitted() {
run_test(|| {
// when current best finalized is #10 and we're trying to import header#5, but another
// teyrchain head is also supplied => tx is accepted
sync_to_relay_header_10();
assert!(validate_submit_teyrchain_heads(
5,
vec![(ParaId(1), [1u8; 32].into()), (ParaId(2), [1u8; 32].into())]
));
});
}
#[test]
fn extension_rejects_initial_teyrchain_head_if_missing_relay_chain_header() {
run_test(|| {
// when relay chain header is unknown => "obsolete"
assert!(!validate_submit_teyrchain_heads(10, vec![(ParaId(1), [1u8; 32].into())]));
// when relay chain header is unknown => "ok"
insert_relay_block(10);
assert!(validate_submit_teyrchain_heads(10, vec![(ParaId(1), [1u8; 32].into())]));
});
}
#[test]
fn extension_rejects_free_teyrchain_head_if_missing_relay_chain_header() {
run_test(|| {
sync_to_relay_header_10();
// when relay chain header is unknown => "obsolete"
assert!(!validate_submit_teyrchain_heads(15, vec![(ParaId(2), [15u8; 32].into())]));
// when relay chain header is unknown => "ok"
insert_relay_block(15);
assert!(validate_submit_teyrchain_heads(15, vec![(ParaId(2), [15u8; 32].into())]));
});
}
#[test]
fn extension_rejects_free_teyrchain_head_if_no_free_slots_remaining() {
run_test(|| {
// when current best finalized is #10 and we're trying to import header#15 => tx should
// be accepted
sync_to_relay_header_10();
insert_relay_block(15);
// ... but since we have specified `is_free_execution_expected = true`, it'll be
// rejected
assert!(!validate_free_submit_teyrchain_heads(15, vec![(ParaId(1), [2u8; 32].into())]));
// ... if we have specify `is_free_execution_expected = false`, it'll be accepted
assert!(validate_submit_teyrchain_heads(15, vec![(ParaId(1), [2u8; 32].into())]));
});
}
#[test]
fn extension_rejects_free_teyrchain_head_if_improves_by_is_below_expected() {
run_test(|| {
// when current best finalized is #10 and we're trying to import header#15 => tx should
// be accepted
sync_to_relay_header_10();
insert_relay_block(10 + FreeHeadersInterval::get() - 1);
insert_relay_block(10 + FreeHeadersInterval::get());
// try to submit at 10 + FreeHeadersInterval::get() - 1 => failure
let relay_header = 10 + FreeHeadersInterval::get() - 1;
assert!(!validate_free_submit_teyrchain_heads(
relay_header,
vec![(ParaId(1), [2u8; 32].into())]
));
// try to submit at 10 + FreeHeadersInterval::get() => ok
let relay_header = 10 + FreeHeadersInterval::get();
assert!(validate_free_submit_teyrchain_heads(
relay_header,
vec![(ParaId(1), [2u8; 32].into())]
));
});
}
}
File diff suppressed because it is too large Load Diff
+313
View File
@@ -0,0 +1,313 @@
// 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 bp_header_chain::ChainWithGrandpa;
use bp_pezkuwi_core::teyrchains::ParaId;
use bp_runtime::{Chain, ChainId, Teyrchain};
use frame_support::{
construct_runtime, derive_impl, parameter_types, traits::ConstU32, weights::Weight,
};
use sp_runtime::{
testing::H256,
traits::{BlakeTwo256, Header as HeaderT},
MultiSignature, StateVersion,
};
use crate as pallet_bridge_teyrchains;
pub type AccountId = u64;
pub type RelayBlockHeader =
sp_runtime::generic::Header<crate::RelayBlockNumber, crate::RelayBlockHasher>;
type Block = frame_system::mocking::MockBlock<TestRuntime>;
pub const PARAS_PALLET_NAME: &str = "Paras";
pub const UNTRACKED_TEYRCHAIN_ID: u32 = 10;
// use exact expected encoded size: `vec_len_size + header_number_size + state_root_hash_size`
pub const MAXIMAL_TEYRCHAIN_HEAD_DATA_SIZE: u32 = 1 + 8 + 32;
// total teyrchains that we use in tests
pub const TOTAL_TEYRCHAINS: u32 = 4;
pub type RegularTeyrchainHeader = sp_runtime::testing::Header;
pub type RegularTeyrchainHasher = BlakeTwo256;
pub type BigTeyrchainHeader = sp_runtime::generic::Header<u128, BlakeTwo256>;
pub struct Teyrchain1;
impl Chain for Teyrchain1 {
const ID: ChainId = *b"pch1";
type BlockNumber = u64;
type Hash = H256;
type Hasher = RegularTeyrchainHasher;
type Header = RegularTeyrchainHeader;
type AccountId = u64;
type Balance = u64;
type Nonce = u64;
type Signature = MultiSignature;
const STATE_VERSION: StateVersion = StateVersion::V1;
fn max_extrinsic_size() -> u32 {
0
}
fn max_extrinsic_weight() -> Weight {
Weight::zero()
}
}
impl Teyrchain for Teyrchain1 {
const TEYRCHAIN_ID: u32 = 1;
const MAX_HEADER_SIZE: u32 = 1_024;
}
pub struct Teyrchain2;
impl Chain for Teyrchain2 {
const ID: ChainId = *b"pch2";
type BlockNumber = u64;
type Hash = H256;
type Hasher = RegularTeyrchainHasher;
type Header = RegularTeyrchainHeader;
type AccountId = u64;
type Balance = u64;
type Nonce = u64;
type Signature = MultiSignature;
const STATE_VERSION: StateVersion = StateVersion::V1;
fn max_extrinsic_size() -> u32 {
0
}
fn max_extrinsic_weight() -> Weight {
Weight::zero()
}
}
impl Teyrchain for Teyrchain2 {
const TEYRCHAIN_ID: u32 = 2;
const MAX_HEADER_SIZE: u32 = 1_024;
}
pub struct Teyrchain3;
impl Chain for Teyrchain3 {
const ID: ChainId = *b"pch3";
type BlockNumber = u64;
type Hash = H256;
type Hasher = RegularTeyrchainHasher;
type Header = RegularTeyrchainHeader;
type AccountId = u64;
type Balance = u64;
type Nonce = u64;
type Signature = MultiSignature;
const STATE_VERSION: StateVersion = StateVersion::V1;
fn max_extrinsic_size() -> u32 {
0
}
fn max_extrinsic_weight() -> Weight {
Weight::zero()
}
}
impl Teyrchain for Teyrchain3 {
const TEYRCHAIN_ID: u32 = 3;
const MAX_HEADER_SIZE: u32 = 1_024;
}
// this teyrchain is using u128 as block number and stored head data size exceeds limit
pub struct BigTeyrchain;
impl Chain for BigTeyrchain {
const ID: ChainId = *b"bpch";
type BlockNumber = u128;
type Hash = H256;
type Hasher = RegularTeyrchainHasher;
type Header = BigTeyrchainHeader;
type AccountId = u64;
type Balance = u64;
type Nonce = u64;
type Signature = MultiSignature;
const STATE_VERSION: StateVersion = StateVersion::V1;
fn max_extrinsic_size() -> u32 {
0
}
fn max_extrinsic_weight() -> Weight {
Weight::zero()
}
}
impl Teyrchain for BigTeyrchain {
const TEYRCHAIN_ID: u32 = 4;
const MAX_HEADER_SIZE: u32 = 2_048;
}
construct_runtime! {
pub enum TestRuntime
{
System: frame_system::{Pallet, Call, Config<T>, Storage, Event<T>},
Grandpa1: pallet_bridge_grandpa::<Instance1>::{Pallet, Event<T>},
Grandpa2: pallet_bridge_grandpa::<Instance2>::{Pallet, Event<T>},
Teyrchains: pallet_bridge_teyrchains::{Call, Pallet, Event<T>},
}
}
#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
impl frame_system::Config for TestRuntime {
type Block = Block;
}
parameter_types! {
pub const HeadersToKeep: u32 = 5;
pub const FreeHeadersInterval: u32 = 15;
}
impl pallet_bridge_grandpa::Config<pallet_bridge_grandpa::Instance1> for TestRuntime {
type RuntimeEvent = RuntimeEvent;
type BridgedChain = TestBridgedChain;
type MaxFreeHeadersPerBlock = ConstU32<2>;
type FreeHeadersInterval = FreeHeadersInterval;
type HeadersToKeep = HeadersToKeep;
type WeightInfo = ();
}
impl pallet_bridge_grandpa::Config<pallet_bridge_grandpa::Instance2> for TestRuntime {
type RuntimeEvent = RuntimeEvent;
type BridgedChain = TestBridgedChain;
type MaxFreeHeadersPerBlock = ConstU32<2>;
type FreeHeadersInterval = FreeHeadersInterval;
type HeadersToKeep = HeadersToKeep;
type WeightInfo = ();
}
parameter_types! {
pub const HeadsToKeep: u32 = 4;
pub const ParasPalletName: &'static str = PARAS_PALLET_NAME;
pub GetTenFirstTeyrchains: Vec<ParaId> = (0..10).map(ParaId).collect();
}
impl pallet_bridge_teyrchains::Config for TestRuntime {
type RuntimeEvent = RuntimeEvent;
type WeightInfo = ();
type BridgesGrandpaPalletInstance = pallet_bridge_grandpa::Instance1;
type ParasPalletName = ParasPalletName;
type ParaStoredHeaderDataBuilder = (Teyrchain1, Teyrchain2, Teyrchain3, BigTeyrchain);
type HeadsToKeep = HeadsToKeep;
type MaxParaHeadDataSize = ConstU32<MAXIMAL_TEYRCHAIN_HEAD_DATA_SIZE>;
type OnNewHead = ();
}
#[cfg(feature = "runtime-benchmarks")]
impl pallet_bridge_teyrchains::benchmarking::Config<()> for TestRuntime {
fn teyrchains() -> Vec<ParaId> {
vec![
ParaId(Teyrchain1::TEYRCHAIN_ID),
ParaId(Teyrchain2::TEYRCHAIN_ID),
ParaId(Teyrchain3::TEYRCHAIN_ID),
]
}
fn prepare_teyrchain_heads_proof(
teyrchains: &[ParaId],
_teyrchain_head_size: u32,
_proof_params: bp_runtime::UnverifiedStorageProofParams,
) -> (
crate::RelayBlockNumber,
crate::RelayBlockHash,
bp_pezkuwi_core::teyrchains::ParaHeadsProof,
Vec<(ParaId, bp_pezkuwi_core::teyrchains::ParaHash)>,
) {
// in mock run we only care about benchmarks correctness, not the benchmark results
// => ignore size related arguments
let (state_root, proof, teyrchains) =
bp_test_utils::prepare_teyrchain_heads_proof::<RegularTeyrchainHeader>(
teyrchains.iter().map(|p| (p.0, crate::tests::head_data(p.0, 1))).collect(),
);
let relay_genesis_hash = crate::tests::initialize(state_root);
(0, relay_genesis_hash, proof, teyrchains)
}
}
#[derive(Debug)]
pub struct TestBridgedChain;
impl Chain for TestBridgedChain {
const ID: ChainId = *b"tbch";
type BlockNumber = crate::RelayBlockNumber;
type Hash = crate::RelayBlockHash;
type Hasher = crate::RelayBlockHasher;
type Header = RelayBlockHeader;
type AccountId = AccountId;
type Balance = u32;
type Nonce = u32;
type Signature = sp_runtime::testing::TestSignature;
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 = 16;
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() -> sp_io::TestExternalities {
sp_io::TestExternalities::new(Default::default())
}
/// Run pallet test.
pub fn run_test<T>(test: impl FnOnce() -> T) -> T {
new_test_ext().execute_with(|| {
System::set_block_number(1);
System::reset_events();
test()
})
}
/// Return test relay chain header with given number.
pub fn test_relay_header(
num: crate::RelayBlockNumber,
state_root: crate::RelayBlockHash,
) -> RelayBlockHeader {
RelayBlockHeader::new(
num,
Default::default(),
state_root,
Default::default(),
Default::default(),
)
}
+81
View File
@@ -0,0 +1,81 @@
// 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/>.
//! Tools for teyrchain head proof verification.
use crate::{Config, GrandpaPalletOf, RelayBlockHash, RelayBlockHasher};
use bp_header_chain::{HeaderChain, HeaderChainError};
use bp_pezkuwi_core::teyrchains::{ParaHead, ParaId};
use bp_runtime::{RawStorageProof, StorageProofChecker, StorageProofError};
use bp_teyrchains::teyrchain_head_storage_key_at_source;
use codec::Decode;
use frame_support::traits::Get;
/// Abstraction over storage proof manipulation, hiding implementation details of actual storage
/// proofs.
pub trait StorageProofAdapter<T: Config<I>, I: 'static> {
/// Read and decode optional value from the proof.
fn read_and_decode_optional_value<D: Decode>(
&mut self,
key: &impl AsRef<[u8]>,
) -> Result<Option<D>, StorageProofError>;
/// Checks if each key was read.
fn ensure_no_unused_keys(self) -> Result<(), StorageProofError>;
/// Read teyrchain head from storage proof.
fn read_teyrchain_head(
&mut self,
teyrchain: ParaId,
) -> Result<Option<ParaHead>, StorageProofError> {
let teyrchain_head_key =
teyrchain_head_storage_key_at_source(T::ParasPalletName::get(), teyrchain);
self.read_and_decode_optional_value(&teyrchain_head_key)
}
}
/// Actual storage proof adapter for teyrchain proofs.
pub type TeyrchainsStorageProofAdapter<T, I> = RawStorageProofAdapter<T, I>;
/// A `StorageProofAdapter` implementation for raw storage proofs.
pub struct RawStorageProofAdapter<T: Config<I>, I: 'static> {
storage: StorageProofChecker<RelayBlockHasher>,
_dummy: sp_std::marker::PhantomData<(T, I)>,
}
impl<T: Config<I>, I: 'static> RawStorageProofAdapter<T, I> {
/// Try to create a new instance of `RawStorageProofAdapter`.
pub fn try_new_with_verified_storage_proof(
relay_block_hash: RelayBlockHash,
storage_proof: RawStorageProof,
) -> Result<Self, HeaderChainError> {
GrandpaPalletOf::<T, I>::verify_storage_proof(relay_block_hash, storage_proof)
.map(|storage| RawStorageProofAdapter::<T, I> { storage, _dummy: Default::default() })
}
}
impl<T: Config<I>, I: 'static> StorageProofAdapter<T, I> for RawStorageProofAdapter<T, I> {
fn read_and_decode_optional_value<D: Decode>(
&mut self,
key: &impl AsRef<[u8]>,
) -> Result<Option<D>, StorageProofError> {
self.storage.read_and_decode_opt_value(key.as_ref())
}
fn ensure_no_unused_keys(self) -> Result<(), StorageProofError> {
self.storage.ensure_no_unused_nodes()
}
}
+269
View File
@@ -0,0 +1,269 @@
// 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 pallet_bridge_teyrchains
//!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
//! DATE: 2023-06-22, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! WORST CASE MAP SIZE: `1000000`
//! HOSTNAME: `serban-ROG-Zephyrus`, CPU: `12th Gen Intel(R) Core(TM) i7-12700H`
//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024
// Executed Command:
// target/release/unknown-bridge-node
// benchmark
// pallet
// --chain=dev
// --steps=50
// --repeat=20
// --pallet=pallet_bridge_teyrchains
// --extrinsic=*
// --execution=wasm
// --wasm-execution=Compiled
// --heap-pages=4096
// --output=./modules/teyrchains/src/weights.rs
// --template=./.maintain/bridge-weight-template.hbs
#![allow(clippy::all)]
#![allow(unused_parens)]
#![allow(unused_imports)]
#![allow(missing_docs)]
use frame_support::{
traits::Get,
weights::{constants::RocksDbWeight, Weight},
};
use sp_std::marker::PhantomData;
/// Weight functions needed for pallet_bridge_teyrchains.
pub trait WeightInfo {
fn submit_teyrchain_heads_with_n_teyrchains(p: u32) -> Weight;
fn submit_teyrchain_heads_with_1kb_proof() -> Weight;
fn submit_teyrchain_heads_with_16kb_proof() -> Weight;
}
/// Weights for `pallet_bridge_teyrchains` 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: frame_system::Config> WeightInfo for BridgeWeight<T> {
/// Storage: BridgeUnknownTeyrchains PalletOperatingMode (r:1 w:0)
///
/// Proof: BridgeUnknownTeyrchains PalletOperatingMode (max_values: Some(1), max_size: Some(1),
/// added: 496, mode: MaxEncodedLen)
///
/// Storage: BridgeUnknownGrandpa ImportedHeaders (r:1 w:0)
///
/// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68),
/// added: 2048, mode: MaxEncodedLen)
///
/// Storage: BridgeUnknownTeyrchains ParasInfo (r:1 w:1)
///
/// Proof: BridgeUnknownTeyrchains ParasInfo (max_values: Some(1), max_size: Some(60), added:
/// 555, mode: MaxEncodedLen)
///
/// Storage: BridgeUnknownTeyrchains ImportedParaHashes (r:1 w:1)
///
/// Proof: BridgeUnknownTeyrchains ImportedParaHashes (max_values: Some(1024), max_size:
/// Some(64), added: 1549, mode: MaxEncodedLen)
///
/// Storage: BridgeUnknownTeyrchains ImportedParaHeads (r:0 w:1)
///
/// Proof: BridgeUnknownTeyrchains ImportedParaHeads (max_values: Some(1024), max_size:
/// Some(196), added: 1681, mode: MaxEncodedLen)
///
/// The range of component `p` is `[1, 2]`.
fn submit_teyrchain_heads_with_n_teyrchains(_p: u32) -> Weight {
// Proof Size summary in bytes:
// Measured: `302`
// Estimated: `3038`
// Minimum execution time: 30_211 nanoseconds.
Weight::from_parts(32_633_893, 3038)
.saturating_add(T::DbWeight::get().reads(4_u64))
.saturating_add(T::DbWeight::get().writes(3_u64))
}
/// Storage: BridgeUnknownTeyrchains PalletOperatingMode (r:1 w:0)
///
/// Proof: BridgeUnknownTeyrchains PalletOperatingMode (max_values: Some(1), max_size: Some(1),
/// added: 496, mode: MaxEncodedLen)
///
/// Storage: BridgeUnknownGrandpa ImportedHeaders (r:1 w:0)
///
/// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68),
/// added: 2048, mode: MaxEncodedLen)
///
/// Storage: BridgeUnknownTeyrchains ParasInfo (r:1 w:1)
///
/// Proof: BridgeUnknownTeyrchains ParasInfo (max_values: Some(1), max_size: Some(60), added:
/// 555, mode: MaxEncodedLen)
///
/// Storage: BridgeUnknownTeyrchains ImportedParaHashes (r:1 w:1)
///
/// Proof: BridgeUnknownTeyrchains ImportedParaHashes (max_values: Some(1024), max_size:
/// Some(64), added: 1549, mode: MaxEncodedLen)
///
/// Storage: BridgeUnknownTeyrchains ImportedParaHeads (r:0 w:1)
///
/// Proof: BridgeUnknownTeyrchains ImportedParaHeads (max_values: Some(1024), max_size:
/// Some(196), added: 1681, mode: MaxEncodedLen)
fn submit_teyrchain_heads_with_1kb_proof() -> Weight {
// Proof Size summary in bytes:
// Measured: `302`
// Estimated: `3038`
// Minimum execution time: 30_830 nanoseconds.
Weight::from_parts(31_801_000, 3038)
.saturating_add(T::DbWeight::get().reads(4_u64))
.saturating_add(T::DbWeight::get().writes(3_u64))
}
/// Storage: BridgeUnknownTeyrchains PalletOperatingMode (r:1 w:0)
///
/// Proof: BridgeUnknownTeyrchains PalletOperatingMode (max_values: Some(1), max_size: Some(1),
/// added: 496, mode: MaxEncodedLen)
///
/// Storage: BridgeUnknownGrandpa ImportedHeaders (r:1 w:0)
///
/// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68),
/// added: 2048, mode: MaxEncodedLen)
///
/// Storage: BridgeUnknownTeyrchains ParasInfo (r:1 w:1)
///
/// Proof: BridgeUnknownTeyrchains ParasInfo (max_values: Some(1), max_size: Some(60), added:
/// 555, mode: MaxEncodedLen)
///
/// Storage: BridgeUnknownTeyrchains ImportedParaHashes (r:1 w:1)
///
/// Proof: BridgeUnknownTeyrchains ImportedParaHashes (max_values: Some(1024), max_size:
/// Some(64), added: 1549, mode: MaxEncodedLen)
///
/// Storage: BridgeUnknownTeyrchains ImportedParaHeads (r:0 w:1)
///
/// Proof: BridgeUnknownTeyrchains ImportedParaHeads (max_values: Some(1024), max_size:
/// Some(196), added: 1681, mode: MaxEncodedLen)
fn submit_teyrchain_heads_with_16kb_proof() -> Weight {
// Proof Size summary in bytes:
// Measured: `302`
// Estimated: `3038`
// Minimum execution time: 44_736 nanoseconds.
Weight::from_parts(45_296_000, 3038)
.saturating_add(T::DbWeight::get().reads(4_u64))
.saturating_add(T::DbWeight::get().writes(3_u64))
}
}
// For backwards compatibility and tests
impl WeightInfo for () {
/// Storage: BridgeUnknownTeyrchains PalletOperatingMode (r:1 w:0)
///
/// Proof: BridgeUnknownTeyrchains PalletOperatingMode (max_values: Some(1), max_size: Some(1),
/// added: 496, mode: MaxEncodedLen)
///
/// Storage: BridgeUnknownGrandpa ImportedHeaders (r:1 w:0)
///
/// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68),
/// added: 2048, mode: MaxEncodedLen)
///
/// Storage: BridgeUnknownTeyrchains ParasInfo (r:1 w:1)
///
/// Proof: BridgeUnknownTeyrchains ParasInfo (max_values: Some(1), max_size: Some(60), added:
/// 555, mode: MaxEncodedLen)
///
/// Storage: BridgeUnknownTeyrchains ImportedParaHashes (r:1 w:1)
///
/// Proof: BridgeUnknownTeyrchains ImportedParaHashes (max_values: Some(1024), max_size:
/// Some(64), added: 1549, mode: MaxEncodedLen)
///
/// Storage: BridgeUnknownTeyrchains ImportedParaHeads (r:0 w:1)
///
/// Proof: BridgeUnknownTeyrchains ImportedParaHeads (max_values: Some(1024), max_size:
/// Some(196), added: 1681, mode: MaxEncodedLen)
///
/// The range of component `p` is `[1, 2]`.
fn submit_teyrchain_heads_with_n_teyrchains(_p: u32) -> Weight {
// Proof Size summary in bytes:
// Measured: `302`
// Estimated: `3038`
// Minimum execution time: 30_211 nanoseconds.
Weight::from_parts(32_633_893, 3038)
.saturating_add(RocksDbWeight::get().reads(4_u64))
.saturating_add(RocksDbWeight::get().writes(3_u64))
}
/// Storage: BridgeUnknownTeyrchains PalletOperatingMode (r:1 w:0)
///
/// Proof: BridgeUnknownTeyrchains PalletOperatingMode (max_values: Some(1), max_size: Some(1),
/// added: 496, mode: MaxEncodedLen)
///
/// Storage: BridgeUnknownGrandpa ImportedHeaders (r:1 w:0)
///
/// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68),
/// added: 2048, mode: MaxEncodedLen)
///
/// Storage: BridgeUnknownTeyrchains ParasInfo (r:1 w:1)
///
/// Proof: BridgeUnknownTeyrchains ParasInfo (max_values: Some(1), max_size: Some(60), added:
/// 555, mode: MaxEncodedLen)
///
/// Storage: BridgeUnknownTeyrchains ImportedParaHashes (r:1 w:1)
///
/// Proof: BridgeUnknownTeyrchains ImportedParaHashes (max_values: Some(1024), max_size:
/// Some(64), added: 1549, mode: MaxEncodedLen)
///
/// Storage: BridgeUnknownTeyrchains ImportedParaHeads (r:0 w:1)
///
/// Proof: BridgeUnknownTeyrchains ImportedParaHeads (max_values: Some(1024), max_size:
/// Some(196), added: 1681, mode: MaxEncodedLen)
fn submit_teyrchain_heads_with_1kb_proof() -> Weight {
// Proof Size summary in bytes:
// Measured: `302`
// Estimated: `3038`
// Minimum execution time: 30_830 nanoseconds.
Weight::from_parts(31_801_000, 3038)
.saturating_add(RocksDbWeight::get().reads(4_u64))
.saturating_add(RocksDbWeight::get().writes(3_u64))
}
/// Storage: BridgeUnknownTeyrchains PalletOperatingMode (r:1 w:0)
///
/// Proof: BridgeUnknownTeyrchains PalletOperatingMode (max_values: Some(1), max_size: Some(1),
/// added: 496, mode: MaxEncodedLen)
///
/// Storage: BridgeUnknownGrandpa ImportedHeaders (r:1 w:0)
///
/// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68),
/// added: 2048, mode: MaxEncodedLen)
///
/// Storage: BridgeUnknownTeyrchains ParasInfo (r:1 w:1)
///
/// Proof: BridgeUnknownTeyrchains ParasInfo (max_values: Some(1), max_size: Some(60), added:
/// 555, mode: MaxEncodedLen)
///
/// Storage: BridgeUnknownTeyrchains ImportedParaHashes (r:1 w:1)
///
/// Proof: BridgeUnknownTeyrchains ImportedParaHashes (max_values: Some(1024), max_size:
/// Some(64), added: 1549, mode: MaxEncodedLen)
///
/// Storage: BridgeUnknownTeyrchains ImportedParaHeads (r:0 w:1)
///
/// Proof: BridgeUnknownTeyrchains ImportedParaHeads (max_values: Some(1024), max_size:
/// Some(196), added: 1681, mode: MaxEncodedLen)
fn submit_teyrchain_heads_with_16kb_proof() -> Weight {
// Proof Size summary in bytes:
// Measured: `302`
// Estimated: `3038`
// Minimum execution time: 44_736 nanoseconds.
Weight::from_parts(45_296_000, 3038)
.saturating_add(RocksDbWeight::get().reads(4_u64))
.saturating_add(RocksDbWeight::get().writes(3_u64))
}
}
@@ -0,0 +1,132 @@
// 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 bp_runtime::Size;
use frame_support::weights::{RuntimeDbWeight, Weight};
/// Size of the regular teyrchain head.
///
/// It's not that we are expecting all teyrchain heads to share the same size or that we would
/// reject all heads that have larger/lesser size. It is about head size that we use in benchmarks.
/// Relayer would need to pay additional fee for extra bytes.
///
/// 384 is a bit larger (1.3 times) than the size of the randomly chosen Pezkuwi block.
pub const DEFAULT_TEYRCHAIN_HEAD_SIZE: u32 = 384;
/// Number of extra bytes (excluding size of storage value itself) of storage proof, built at
/// some generic chain.
pub const EXTRA_STORAGE_PROOF_SIZE: u32 = 1024;
/// Extended weight info.
pub trait WeightInfoExt: WeightInfo {
// Our configuration assumes that the runtime has special signed extensions used to:
//
// 1) boost priority of `submit_teyrchain_heads` transactions;
//
// 2) slash relayer if he submits an invalid transaction.
//
// We read and update storage values of other pallets (`pallet-bridge-relayers` and
// balances/assets pallet). 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_teyrchain_heads_overhead_from_runtime() -> Weight;
/// Storage proof overhead, that is included in every storage proof.
///
/// The relayer would pay some extra fee for additional proof bytes, since they mean
/// more hashing operations.
fn expected_extra_storage_proof_size() -> u32;
/// Weight of the teyrchain heads delivery extrinsic.
fn submit_teyrchain_heads_weight(
db_weight: RuntimeDbWeight,
proof: &impl Size,
teyrchains_count: u32,
) -> Weight {
// weight of the `submit_teyrchain_heads` with exactly `teyrchains_count` teyrchain
// heads of the default size (`DEFAULT_TEYRCHAIN_HEAD_SIZE`)
let base_weight = Self::submit_teyrchain_heads_with_n_teyrchains(teyrchains_count);
// overhead because of extra storage proof bytes
let expected_proof_size = teyrchains_count
.saturating_mul(DEFAULT_TEYRCHAIN_HEAD_SIZE)
.saturating_add(Self::expected_extra_storage_proof_size());
let actual_proof_size = proof.size();
let proof_size_overhead = Self::storage_proof_size_overhead(
actual_proof_size.saturating_sub(expected_proof_size),
);
// potential pruning weight (refunded if hasn't happened)
let pruning_weight =
Self::teyrchain_head_pruning_weight(db_weight).saturating_mul(teyrchains_count as u64);
base_weight
.saturating_add(proof_size_overhead)
.saturating_add(pruning_weight)
.saturating_add(Self::submit_teyrchain_heads_overhead_from_runtime())
}
/// Returns weight of single teyrchain head storage update.
///
/// This weight only includes db write operations that happens if teyrchain head is actually
/// updated. All extra weights (weight of storage proof validation, additional checks, ...) is
/// not included.
fn teyrchain_head_storage_write_weight(db_weight: RuntimeDbWeight) -> Weight {
// it's just a couple of operations - we need to write the hash (`ImportedParaHashes`) and
// the head itself (`ImportedParaHeads`. Pruning is not included here
db_weight.writes(2)
}
/// Returns weight of single teyrchain head pruning.
fn teyrchain_head_pruning_weight(db_weight: RuntimeDbWeight) -> Weight {
// it's just one write operation, we don't want any benchmarks for that
db_weight.writes(1)
}
/// Returns weight that needs to be accounted when storage proof of given size is received.
fn storage_proof_size_overhead(extra_proof_bytes: u32) -> Weight {
let extra_byte_weight = (Self::submit_teyrchain_heads_with_16kb_proof() -
Self::submit_teyrchain_heads_with_1kb_proof()) /
(15 * 1024);
extra_byte_weight.saturating_mul(extra_proof_bytes as u64)
}
}
impl WeightInfoExt for () {
fn submit_teyrchain_heads_overhead_from_runtime() -> Weight {
Weight::zero()
}
fn expected_extra_storage_proof_size() -> u32 {
EXTRA_STORAGE_PROOF_SIZE
}
}
impl<T: frame_system::Config> WeightInfoExt for BridgeWeight<T> {
fn submit_teyrchain_heads_overhead_from_runtime() -> Weight {
Weight::zero()
}
fn expected_extra_storage_proof_size() -> u32 {
EXTRA_STORAGE_PROOF_SIZE
}
}