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:
2025-12-16 09:57:23 +03:00
parent eea003e14d
commit 3139ffa25e
3022 changed files with 42157 additions and 23579 deletions
@@ -0,0 +1,182 @@
// 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/>.
//! Adapter that allows using `pezpallet-bridge-relayers` as a signed extension in the
//! bridge with remote GRANDPA chain.
use crate::{
extension::verify_messages_call_succeeded, Config as BridgeRelayersConfig, LOG_TARGET,
};
use bp_relayers::{BatchCallUnpacker, ExtensionCallData, ExtensionCallInfo, ExtensionConfig};
use pezbp_runtime::{Chain, StaticStrProvider};
use core::marker::PhantomData;
use pezframe_support::dispatch::{DispatchInfo, PostDispatchInfo};
use pezframe_system::Config as SystemConfig;
use pezpallet_bridge_grandpa::{
CallSubType as BridgeGrandpaCallSubtype, Config as BridgeGrandpaConfig,
SubmitFinalityProofHelper,
};
use pezpallet_bridge_messages::{
CallSubType as BridgeMessagesCallSubType, Config as BridgeMessagesConfig, LaneIdOf,
};
use pezsp_runtime::{
traits::{Dispatchable, Get},
transaction_validity::{TransactionPriority, TransactionValidityError},
Saturating,
};
/// Adapter to be used in signed extension configuration, when bridging with remote
/// chains that are using GRANDPA finality.
pub struct WithGrandpaChainExtensionConfig<
// signed extension identifier
IdProvider,
// runtime that implements `BridgeMessagesConfig<BridgeMessagesPalletInstance>`, which
// uses `BridgeGrandpaConfig<BridgeGrandpaPalletInstance>` to receive messages and
// confirmations from the remote chain.
Runtime,
// batch call unpacker
BatchCallUnpacker,
// instance of the `pezpallet-bridge-grandpa`, tracked by this extension
BridgeGrandpaPalletInstance,
// instance of BridgedChain `pezpallet-bridge-messages`, tracked by this extension
BridgeMessagesPalletInstance,
// instance of `pezpallet-bridge-relayers`, tracked by this extension
BridgeRelayersPalletInstance,
// message delivery transaction priority boost for every additional message
PriorityBoostPerMessage,
>(
PhantomData<(
IdProvider,
Runtime,
BatchCallUnpacker,
BridgeGrandpaPalletInstance,
BridgeMessagesPalletInstance,
BridgeRelayersPalletInstance,
PriorityBoostPerMessage,
)>,
);
impl<ID, R, BCU, GI, MI, RI, P> ExtensionConfig
for WithGrandpaChainExtensionConfig<ID, R, BCU, GI, MI, RI, P>
where
ID: StaticStrProvider,
R: BridgeRelayersConfig<RI>
+ BridgeMessagesConfig<MI, BridgedChain = pezpallet_bridge_grandpa::BridgedChain<R, GI>>
+ BridgeGrandpaConfig<GI>,
BCU: BatchCallUnpacker<R>,
GI: 'static,
MI: 'static,
RI: 'static,
P: Get<TransactionPriority>,
R::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>
+ BridgeGrandpaCallSubtype<R, GI>
+ BridgeMessagesCallSubType<R, MI>,
{
type IdProvider = ID;
type Runtime = R;
type BridgeMessagesPalletInstance = MI;
type BridgeRelayersPalletInstance = RI;
type PriorityBoostPerMessage = P;
type RemoteGrandpaChainBlockNumber = pezpallet_bridge_grandpa::BridgedBlockNumber<R, GI>;
type LaneId = LaneIdOf<R, Self::BridgeMessagesPalletInstance>;
fn parse_and_check_for_obsolete_call(
call: &R::RuntimeCall,
) -> Result<
Option<ExtensionCallInfo<Self::RemoteGrandpaChainBlockNumber, Self::LaneId>>,
TransactionValidityError,
> {
let calls = BCU::unpack(call, 2);
let total_calls = calls.len();
let mut calls = calls.into_iter().map(Self::check_obsolete_parsed_call).rev();
let msgs_call = calls.next().transpose()?.and_then(|c| c.call_info());
let relay_finality_call =
calls.next().transpose()?.and_then(|c| c.submit_finality_proof_info());
Ok(match (total_calls, relay_finality_call, msgs_call) {
(2, Some(relay_finality_call), Some(msgs_call)) =>
Some(ExtensionCallInfo::RelayFinalityAndMsgs(relay_finality_call, msgs_call)),
(1, None, Some(msgs_call)) => Some(ExtensionCallInfo::Msgs(msgs_call)),
_ => None,
})
}
fn check_obsolete_parsed_call(
call: &R::RuntimeCall,
) -> Result<&R::RuntimeCall, TransactionValidityError> {
call.check_obsolete_submit_finality_proof()?;
call.check_obsolete_call()?;
Ok(call)
}
fn check_call_result(
call_info: &ExtensionCallInfo<Self::RemoteGrandpaChainBlockNumber, Self::LaneId>,
call_data: &mut ExtensionCallData,
relayer: &R::AccountId,
) -> bool {
verify_submit_finality_proof_succeeded::<Self, GI>(call_info, call_data, relayer) &&
verify_messages_call_succeeded::<Self>(call_info, call_data, relayer)
}
}
/// If the batch call contains the GRANDPA chain state update call, verify that it
/// has been successful.
///
/// Only returns false when GRANDPA chain state update call has failed.
pub(crate) fn verify_submit_finality_proof_succeeded<C, GI>(
call_info: &ExtensionCallInfo<C::RemoteGrandpaChainBlockNumber, C::LaneId>,
call_data: &mut ExtensionCallData,
relayer: &<C::Runtime as SystemConfig>::AccountId,
) -> bool
where
C: ExtensionConfig,
GI: 'static,
C::Runtime: BridgeGrandpaConfig<GI>,
<C::Runtime as BridgeGrandpaConfig<GI>>::BridgedChain:
Chain<BlockNumber = C::RemoteGrandpaChainBlockNumber>,
{
let Some(finality_proof_info) = call_info.submit_finality_proof_info() else { return true };
if !SubmitFinalityProofHelper::<C::Runtime, GI>::was_successful(
finality_proof_info.block_number,
) {
// we only refund relayer if all calls have updated chain state
tracing::trace!(
target: LOG_TARGET,
id_provider=%C::IdProvider::STR,
lane_id=?call_info.messages_call_info().lane_id(),
?relayer,
"Relayer has submitted invalid GRANDPA chain finality proof"
);
return false;
}
// there's a conflict between how bridge GRANDPA pezpallet works and a `utility.batchAll`
// transaction. If relay chain header is mandatory, the GRANDPA pezpallet returns
// `Pays::No`, because such transaction is mandatory for operating the bridge. But
// `utility.batchAll` transaction always requires payment. But in both cases we'll
// refund relayer - either explicitly here, or using `Pays::No` if he's choosing
// to submit dedicated transaction.
// submitter has means to include extra weight/bytes in the `submit_finality_proof`
// call, so let's subtract extra weight/size to avoid refunding for this extra stuff
call_data.extra_weight.saturating_accrue(finality_proof_info.extra_weight);
call_data.extra_size.saturating_accrue(finality_proof_info.extra_size);
true
}
@@ -0,0 +1,99 @@
// 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/>.
//! Adapter that allows using `pezpallet-bridge-relayers` as a signed extension in the
//! bridge with any remote chain. This adapter does not refund any finality transactions.
use crate::{extension::verify_messages_call_succeeded, Config as BridgeRelayersConfig};
use bp_relayers::{ExtensionCallData, ExtensionCallInfo, ExtensionConfig};
use pezbp_runtime::StaticStrProvider;
use core::marker::PhantomData;
use pezframe_support::dispatch::{DispatchInfo, PostDispatchInfo};
use pezpallet_bridge_messages::{
CallSubType as BridgeMessagesCallSubType, Config as BridgeMessagesConfig, LaneIdOf,
};
use pezsp_runtime::{
traits::{Dispatchable, Get},
transaction_validity::{TransactionPriority, TransactionValidityError},
};
/// Transaction extension that refunds a relayer for standalone messages delivery and confirmation
/// transactions. Finality transactions are not refunded.
pub struct WithMessagesExtensionConfig<
IdProvider,
Runtime,
BridgeMessagesPalletInstance,
BridgeRelayersPalletInstance,
PriorityBoostPerMessage,
>(
PhantomData<(
// signed extension identifier
IdProvider,
// runtime with `pezpallet-bridge-messages` pezpallet deployed
Runtime,
// instance of BridgedChain `pezpallet-bridge-messages`, tracked by this extension
BridgeMessagesPalletInstance,
// instance of `pezpallet-bridge-relayers`, tracked by this extension
BridgeRelayersPalletInstance,
// message delivery transaction priority boost for every additional message
PriorityBoostPerMessage,
)>,
);
impl<ID, R, MI, RI, P> ExtensionConfig for WithMessagesExtensionConfig<ID, R, MI, RI, P>
where
ID: StaticStrProvider,
R: BridgeRelayersConfig<RI> + BridgeMessagesConfig<MI>,
MI: 'static,
RI: 'static,
P: Get<TransactionPriority>,
R::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>
+ BridgeMessagesCallSubType<R, MI>,
{
type IdProvider = ID;
type Runtime = R;
type BridgeMessagesPalletInstance = MI;
type BridgeRelayersPalletInstance = RI;
type PriorityBoostPerMessage = P;
type RemoteGrandpaChainBlockNumber = ();
type LaneId = LaneIdOf<R, Self::BridgeMessagesPalletInstance>;
fn parse_and_check_for_obsolete_call(
call: &R::RuntimeCall,
) -> Result<
Option<ExtensionCallInfo<Self::RemoteGrandpaChainBlockNumber, Self::LaneId>>,
TransactionValidityError,
> {
let call = Self::check_obsolete_parsed_call(call)?;
Ok(call.call_info().map(ExtensionCallInfo::Msgs))
}
fn check_obsolete_parsed_call(
call: &R::RuntimeCall,
) -> Result<&R::RuntimeCall, TransactionValidityError> {
call.check_obsolete_call()?;
Ok(call)
}
fn check_call_result(
call_info: &ExtensionCallInfo<Self::RemoteGrandpaChainBlockNumber, Self::LaneId>,
call_data: &mut ExtensionCallData,
relayer: &R::AccountId,
) -> bool {
verify_messages_call_succeeded::<Self>(call_info, call_data, relayer)
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,431 @@
// 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/>.
//! Bridge transaction priority calculator.
//!
//! We want to prioritize message delivery transactions with more messages over
//! transactions with less messages. That's because we reject delivery transactions
//! if it contains already delivered message. And if some transaction delivers
//! single message with nonce `N`, then the transaction with nonces `N..=N+100` will
//! be rejected. This can lower bridge throughput down to one message per block.
use pezframe_support::traits::Get;
use pezsp_runtime::transaction_validity::TransactionPriority;
// reexport everything from `integrity_tests` module
#[allow(unused_imports)]
pub use integrity_tests::*;
/// We'll deal with different bridge items here - messages, headers, ...
/// To avoid being too verbose with generic code, let's just define a separate alias.
pub type ItemCount = u64;
/// Compute priority boost for transaction that brings given number of bridge
/// items (messages, headers, ...), when every additional item adds `PriorityBoostPerItem`
/// to transaction priority.
pub fn compute_priority_boost<PriorityBoostPerItem>(n_items: ItemCount) -> TransactionPriority
where
PriorityBoostPerItem: Get<TransactionPriority>,
{
// we don't want any boost for transaction with single (additional) item => minus one
PriorityBoostPerItem::get().saturating_mul(n_items.saturating_sub(1))
}
#[cfg(not(feature = "integrity-test"))]
mod integrity_tests {}
#[cfg(feature = "integrity-test")]
mod integrity_tests {
use super::{compute_priority_boost, ItemCount};
use bp_messages::MessageNonce;
use pezbp_runtime::PreComputedSize;
use pezframe_support::{
dispatch::{DispatchClass, DispatchInfo, Pays, PostDispatchInfo},
traits::Get,
};
use pezpallet_transaction_payment::OnChargeTransaction;
use pezsp_runtime::{
traits::{Dispatchable, UniqueSaturatedInto, Zero},
transaction_validity::TransactionPriority,
FixedPointOperand, SaturatedConversion, Saturating,
};
type BalanceOf<T> =
<<T as pezpallet_transaction_payment::Config>::OnChargeTransaction as OnChargeTransaction<
T,
>>::Balance;
/// Ensures that the value of `PriorityBoostPerItem` matches the value of
/// `tip_boost_per_item`.
///
/// We want two transactions, `TX1` with `N` items and `TX2` with `N+1` items, have almost
/// the same priority if we'll add `tip_boost_per_item` tip to the `TX1`. We want to be sure
/// that if we add plain `PriorityBoostPerItem` priority to `TX1`, the priority will be close
/// to `TX2` as well.
fn ensure_priority_boost_is_sane<PriorityBoostPerItem, Balance>(
param_name: &str,
max_items: ItemCount,
tip_boost_per_item: Balance,
estimate_priority: impl Fn(ItemCount, Balance) -> TransactionPriority,
) where
PriorityBoostPerItem: Get<TransactionPriority>,
ItemCount: UniqueSaturatedInto<Balance>,
Balance: FixedPointOperand + Zero,
{
let priority_boost_per_item = PriorityBoostPerItem::get();
for n_items in 1..=max_items {
let base_priority = estimate_priority(n_items, Zero::zero());
let priority_boost = compute_priority_boost::<PriorityBoostPerItem>(n_items);
let priority_with_boost = base_priority
.checked_add(priority_boost)
.expect("priority overflow: try lowering `max_items` or `tip_boost_per_item`?");
let tip = tip_boost_per_item.saturating_mul((n_items - 1).unique_saturated_into());
let priority_with_tip = estimate_priority(1, tip);
const ERROR_MARGIN: TransactionPriority = 5; // 5%
if priority_with_boost.abs_diff(priority_with_tip).saturating_mul(100) /
priority_with_tip >
ERROR_MARGIN
{
panic!(
"The {param_name} value ({}) must be fixed to: {}",
priority_boost_per_item,
compute_priority_boost_per_item(
max_items,
tip_boost_per_item,
estimate_priority
),
);
}
}
}
/// Compute priority boost that we give to bridge transaction for every
/// additional bridge item.
#[cfg(feature = "integrity-test")]
fn compute_priority_boost_per_item<Balance>(
max_items: ItemCount,
tip_boost_per_item: Balance,
estimate_priority: impl Fn(ItemCount, Balance) -> TransactionPriority,
) -> TransactionPriority
where
ItemCount: UniqueSaturatedInto<Balance>,
Balance: FixedPointOperand + Zero,
{
// estimate priority of transaction that delivers one item and has large tip
let small_with_tip_priority =
estimate_priority(1, tip_boost_per_item.saturating_mul(max_items.saturated_into()));
// estimate priority of transaction that delivers maximal number of items, but has no tip
let large_without_tip_priority = estimate_priority(max_items, Zero::zero());
small_with_tip_priority
.saturating_sub(large_without_tip_priority)
.saturating_div(max_items - 1)
}
/// Computations, specific to bridge relay chains transactions.
pub mod per_relay_header {
use super::*;
use bp_header_pez_chain::{
max_expected_submit_finality_proof_arguments_size, ChainWithGrandpa,
};
use pezpallet_bridge_grandpa::WeightInfoExt;
/// Ensures that the value of `PriorityBoostPerHeader` matches the value of
/// `tip_boost_per_header`.
///
/// We want two transactions, `TX1` with `N` headers and `TX2` with `N+1` headers, have
/// almost the same priority if we'll add `tip_boost_per_header` tip to the `TX1`. We want
/// to be sure that if we add plain `PriorityBoostPerHeader` priority to `TX1`, the priority
/// will be close to `TX2` as well.
pub fn ensure_priority_boost_is_sane<Runtime, GrandpaInstance, PriorityBoostPerHeader>(
tip_boost_per_header: BalanceOf<Runtime>,
) where
Runtime:
pezpallet_transaction_payment::Config + pezpallet_bridge_grandpa::Config<GrandpaInstance>,
GrandpaInstance: 'static,
PriorityBoostPerHeader: Get<TransactionPriority>,
Runtime::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
BalanceOf<Runtime>: Send + Sync + FixedPointOperand,
{
// the meaning of `max_items` here is different when comparing with message
// transactions - with messages we have a strict limit on maximal number of
// messages we can fit into a single transaction. With headers, current best
// header may be improved by any "number of items". But this number is only
// used to verify priority boost, so it should be fine to select this arbitrary
// value - it SHALL NOT affect any value, it just adds more tests for the value.
let maximal_improved_by = 4_096;
super::ensure_priority_boost_is_sane::<PriorityBoostPerHeader, BalanceOf<Runtime>>(
"PriorityBoostPerRelayHeader",
maximal_improved_by,
tip_boost_per_header,
|_n_headers, tip| {
estimate_relay_header_submit_transaction_priority::<Runtime, GrandpaInstance>(
tip,
)
},
);
}
/// Estimate relay header delivery transaction priority.
#[cfg(feature = "integrity-test")]
fn estimate_relay_header_submit_transaction_priority<Runtime, GrandpaInstance>(
tip: BalanceOf<Runtime>,
) -> TransactionPriority
where
Runtime:
pezpallet_transaction_payment::Config + pezpallet_bridge_grandpa::Config<GrandpaInstance>,
GrandpaInstance: 'static,
Runtime::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
BalanceOf<Runtime>: Send + Sync + FixedPointOperand,
{
// just an estimation of extra transaction bytes that are added to every transaction
// (including signature, signed extensions extra and etc + in our case it includes
// all call arguments except the proof itself)
let base_tx_size = 512;
// let's say we are relaying largest relay chain headers
let tx_call_size = max_expected_submit_finality_proof_arguments_size::<
Runtime::BridgedChain,
>(true, Runtime::BridgedChain::MAX_AUTHORITIES_COUNT * 2 / 3 + 1);
// finally we are able to estimate transaction size and weight
let transaction_size = base_tx_size.saturating_add(tx_call_size);
let transaction_weight = <Runtime as ::pezpallet_bridge_grandpa::Config<
GrandpaInstance,
>>::WeightInfo::submit_finality_proof_weight(
Runtime::BridgedChain::MAX_AUTHORITIES_COUNT * 2 / 3 + 1,
Runtime::BridgedChain::REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY,
);
pezpallet_transaction_payment::ChargeTransactionPayment::<Runtime>::get_priority(
&DispatchInfo {
call_weight: transaction_weight,
extension_weight: Default::default(),
class: DispatchClass::Normal,
pays_fee: Pays::Yes,
},
transaction_size as _,
tip,
Zero::zero(),
)
}
}
/// Computations, specific to bridge teyrchains transactions.
pub mod per_teyrchain_header {
use super::*;
use pezbp_runtime::Teyrchain;
use pezpallet_bridge_teyrchains::WeightInfoExt;
/// Ensures that the value of `PriorityBoostPerHeader` matches the value of
/// `tip_boost_per_header`.
///
/// We want two transactions, `TX1` with `N` headers and `TX2` with `N+1` headers, have
/// almost the same priority if we'll add `tip_boost_per_header` tip to the `TX1`. We want
/// to be sure that if we add plain `PriorityBoostPerHeader` priority to `TX1`, the priority
/// will be close to `TX2` as well.
pub fn ensure_priority_boost_is_sane<
Runtime,
TeyrchainsInstance,
Para,
PriorityBoostPerHeader,
>(
tip_boost_per_header: BalanceOf<Runtime>,
) where
Runtime: pezpallet_transaction_payment::Config
+ pezpallet_bridge_teyrchains::Config<TeyrchainsInstance>,
TeyrchainsInstance: 'static,
Para: Teyrchain,
PriorityBoostPerHeader: Get<TransactionPriority>,
Runtime::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
BalanceOf<Runtime>: Send + Sync + FixedPointOperand,
{
// the meaning of `max_items` here is different when comparing with message
// transactions - with messages we have a strict limit on maximal number of
// messages we can fit into a single transaction. With headers, current best
// header may be improved by any "number of items". But this number is only
// used to verify priority boost, so it should be fine to select this arbitrary
// value - it SHALL NOT affect any value, it just adds more tests for the value.
let maximal_improved_by = 4_096;
super::ensure_priority_boost_is_sane::<PriorityBoostPerHeader, BalanceOf<Runtime>>(
"PriorityBoostPerTeyrchainHeader",
maximal_improved_by,
tip_boost_per_header,
|_n_headers, tip| {
estimate_teyrchain_header_submit_transaction_priority::<
Runtime,
TeyrchainsInstance,
Para,
>(tip)
},
);
}
/// Estimate teyrchain header delivery transaction priority.
#[cfg(feature = "integrity-test")]
fn estimate_teyrchain_header_submit_transaction_priority<
Runtime,
TeyrchainsInstance,
Para,
>(
tip: BalanceOf<Runtime>,
) -> TransactionPriority
where
Runtime: pezpallet_transaction_payment::Config
+ pezpallet_bridge_teyrchains::Config<TeyrchainsInstance>,
TeyrchainsInstance: 'static,
Para: Teyrchain,
Runtime::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
BalanceOf<Runtime>: Send + Sync + FixedPointOperand,
{
// just an estimation of extra transaction bytes that are added to every transaction
// (including signature, signed extensions extra and etc + in our case it includes
// all call arguments except the proof itself)
let base_tx_size = 512;
// let's say we are relaying largest teyrchain headers and proof takes some more bytes
let tx_call_size = <Runtime as pezpallet_bridge_teyrchains::Config<
TeyrchainsInstance,
>>::WeightInfo::expected_extra_storage_proof_size()
.saturating_add(Para::MAX_HEADER_SIZE);
// finally we are able to estimate transaction size and weight
let transaction_size = base_tx_size.saturating_add(tx_call_size);
let transaction_weight = <Runtime as pezpallet_bridge_teyrchains::Config<
TeyrchainsInstance,
>>::WeightInfo::submit_teyrchain_heads_weight(
Runtime::DbWeight::get(),
&PreComputedSize(transaction_size as _),
// just one teyrchain - all other submissions won't receive any boost
1,
);
pezpallet_transaction_payment::ChargeTransactionPayment::<Runtime>::get_priority(
&DispatchInfo {
call_weight: transaction_weight,
extension_weight: Default::default(),
class: DispatchClass::Normal,
pays_fee: Pays::Yes,
},
transaction_size as _,
tip,
Zero::zero(),
)
}
}
/// Computations, specific to bridge messages transactions.
pub mod per_message {
use super::*;
use bp_messages::ChainWithMessages;
use pezpallet_bridge_messages::WeightInfoExt;
/// Ensures that the value of `PriorityBoostPerMessage` matches the value of
/// `tip_boost_per_message`.
///
/// We want two transactions, `TX1` with `N` messages and `TX2` with `N+1` messages, have
/// almost the same priority if we'll add `tip_boost_per_message` tip to the `TX1`. We want
/// to be sure that if we add plain `PriorityBoostPerMessage` priority to `TX1`, the
/// priority will be close to `TX2` as well.
pub fn ensure_priority_boost_is_sane<Runtime, MessagesInstance, PriorityBoostPerMessage>(
tip_boost_per_message: BalanceOf<Runtime>,
) where
Runtime: pezpallet_transaction_payment::Config
+ pezpallet_bridge_messages::Config<MessagesInstance>,
MessagesInstance: 'static,
PriorityBoostPerMessage: Get<TransactionPriority>,
Runtime::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
BalanceOf<Runtime>: Send + Sync + FixedPointOperand,
{
let maximal_messages_in_delivery_transaction =
Runtime::BridgedChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX;
super::ensure_priority_boost_is_sane::<PriorityBoostPerMessage, BalanceOf<Runtime>>(
"PriorityBoostPerMessage",
maximal_messages_in_delivery_transaction,
tip_boost_per_message,
|n_messages, tip| {
estimate_message_delivery_transaction_priority::<Runtime, MessagesInstance>(
n_messages, tip,
)
},
);
}
/// Estimate message delivery transaction priority.
#[cfg(feature = "integrity-test")]
fn estimate_message_delivery_transaction_priority<Runtime, MessagesInstance>(
messages: MessageNonce,
tip: BalanceOf<Runtime>,
) -> TransactionPriority
where
Runtime: pezpallet_transaction_payment::Config
+ pezpallet_bridge_messages::Config<MessagesInstance>,
MessagesInstance: 'static,
Runtime::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
BalanceOf<Runtime>: Send + Sync + FixedPointOperand,
{
// just an estimation of extra transaction bytes that are added to every transaction
// (including signature, signed extensions extra and etc + in our case it includes
// all call arguments except the proof itself)
let base_tx_size = 512;
// let's say we are relaying similar small messages and for every message we add more
// trie nodes to the proof (x0.5 because we expect some nodes to be reused)
let estimated_message_size = 512;
// let's say all our messages have the same dispatch weight
let estimated_message_dispatch_weight = <Runtime as pezpallet_bridge_messages::Config<
MessagesInstance,
>>::WeightInfo::message_dispatch_weight(
estimated_message_size
);
// messages proof argument size is (for every message) messages size + some additional
// trie nodes. Some of them are reused by different messages, so let's take 2/3 of
// default "overhead" constant
let messages_proof_size = <Runtime as pezpallet_bridge_messages::Config<
MessagesInstance,
>>::WeightInfo::expected_extra_storage_proof_size()
.saturating_mul(2)
.saturating_div(3)
.saturating_add(estimated_message_size)
.saturating_mul(messages as _);
// finally we are able to estimate transaction size and weight
let transaction_size = base_tx_size.saturating_add(messages_proof_size);
let transaction_weight = <Runtime as pezpallet_bridge_messages::Config<
MessagesInstance,
>>::WeightInfo::receive_messages_proof_weight(
&PreComputedSize(transaction_size as _),
messages as _,
estimated_message_dispatch_weight.saturating_mul(messages),
);
pezpallet_transaction_payment::ChargeTransactionPayment::<Runtime>::get_priority(
&DispatchInfo {
call_weight: transaction_weight,
extension_weight: Default::default(),
class: DispatchClass::Normal,
pays_fee: Pays::Yes,
},
transaction_size as _,
tip,
Zero::zero(),
)
}
}
}
@@ -0,0 +1,188 @@
// 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/>.
//! Adapter that allows using `pezpallet-bridge-relayers` as a signed extension in the
//! bridge with remote teyrchain.
use crate::{
extension::{
grandpa_adapter::verify_submit_finality_proof_succeeded, verify_messages_call_succeeded,
},
Config as BridgeRelayersConfig, LOG_TARGET,
};
use bp_relayers::{BatchCallUnpacker, ExtensionCallData, ExtensionCallInfo, ExtensionConfig};
use pezbp_runtime::{StaticStrProvider, Teyrchain};
use core::marker::PhantomData;
use pezframe_support::dispatch::{DispatchInfo, PostDispatchInfo};
use pezframe_system::Config as SystemConfig;
use pezpallet_bridge_grandpa::{
CallSubType as BridgeGrandpaCallSubtype, Config as BridgeGrandpaConfig,
};
use pezpallet_bridge_messages::{
CallSubType as BridgeMessagesCallSubType, Config as BridgeMessagesConfig, LaneIdOf,
};
use pezpallet_bridge_teyrchains::{
CallSubType as BridgeTeyrchainsCallSubtype, Config as BridgeTeyrchainsConfig,
SubmitTeyrchainHeadsHelper,
};
use pezsp_runtime::{
traits::{Dispatchable, Get},
transaction_validity::{TransactionPriority, TransactionValidityError},
};
/// Adapter to be used in signed extension configuration, when bridging with remote teyrchains.
pub struct WithTeyrchainExtensionConfig<
// signed extension identifier
IdProvider,
// runtime that implements `BridgeMessagesConfig<BridgeMessagesPalletInstance>`, which
// uses `BridgeTeyrchainsConfig<BridgeTeyrchainsPalletInstance>` to receive messages and
// confirmations from the remote chain.
Runtime,
// batch call unpacker
BatchCallUnpacker,
// instance of the `pezpallet-bridge-teyrchains`, tracked by this extension
BridgeTeyrchainsPalletInstance,
// instance of BridgedChain `pezpallet-bridge-messages`, tracked by this extension
BridgeMessagesPalletInstance,
// instance of `pezpallet-bridge-relayers`, tracked by this extension
BridgeRelayersPalletInstance,
// message delivery transaction priority boost for every additional message
PriorityBoostPerMessage,
>(
PhantomData<(
IdProvider,
Runtime,
BatchCallUnpacker,
BridgeTeyrchainsPalletInstance,
BridgeMessagesPalletInstance,
BridgeRelayersPalletInstance,
PriorityBoostPerMessage,
)>,
);
impl<ID, R, BCU, PI, MI, RI, P> ExtensionConfig
for WithTeyrchainExtensionConfig<ID, R, BCU, PI, MI, RI, P>
where
ID: StaticStrProvider,
R: BridgeRelayersConfig<RI>
+ BridgeMessagesConfig<MI>
+ BridgeTeyrchainsConfig<PI>
+ BridgeGrandpaConfig<R::BridgesGrandpaPalletInstance>,
BCU: BatchCallUnpacker<R>,
PI: 'static,
MI: 'static,
RI: 'static,
P: Get<TransactionPriority>,
R::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>
+ BridgeGrandpaCallSubtype<R, R::BridgesGrandpaPalletInstance>
+ BridgeTeyrchainsCallSubtype<R, PI>
+ BridgeMessagesCallSubType<R, MI>,
<R as BridgeMessagesConfig<MI>>::BridgedChain: Teyrchain,
{
type IdProvider = ID;
type Runtime = R;
type BridgeMessagesPalletInstance = MI;
type BridgeRelayersPalletInstance = RI;
type PriorityBoostPerMessage = P;
type RemoteGrandpaChainBlockNumber =
pezpallet_bridge_grandpa::BridgedBlockNumber<R, R::BridgesGrandpaPalletInstance>;
type LaneId = LaneIdOf<R, Self::BridgeMessagesPalletInstance>;
fn parse_and_check_for_obsolete_call(
call: &R::RuntimeCall,
) -> Result<
Option<ExtensionCallInfo<Self::RemoteGrandpaChainBlockNumber, Self::LaneId>>,
TransactionValidityError,
> {
let calls = BCU::unpack(call, 3);
let total_calls = calls.len();
let mut calls = calls.into_iter().map(Self::check_obsolete_parsed_call).rev();
let msgs_call = calls.next().transpose()?.and_then(|c| c.call_info());
let para_finality_call = calls.next().transpose()?.and_then(|c| {
let r = c.submit_teyrchain_heads_info_for(
<R as BridgeMessagesConfig<Self::BridgeMessagesPalletInstance>>::BridgedChain::TEYRCHAIN_ID,
);
r
});
let relay_finality_call =
calls.next().transpose()?.and_then(|c| c.submit_finality_proof_info());
Ok(match (total_calls, relay_finality_call, para_finality_call, msgs_call) {
(3, Some(relay_finality_call), Some(para_finality_call), Some(msgs_call)) =>
Some(ExtensionCallInfo::AllFinalityAndMsgs(
relay_finality_call,
para_finality_call,
msgs_call,
)),
(2, None, Some(para_finality_call), Some(msgs_call)) =>
Some(ExtensionCallInfo::TeyrchainFinalityAndMsgs(para_finality_call, msgs_call)),
(1, None, None, Some(msgs_call)) => Some(ExtensionCallInfo::Msgs(msgs_call)),
_ => None,
})
}
fn check_obsolete_parsed_call(
call: &R::RuntimeCall,
) -> Result<&R::RuntimeCall, TransactionValidityError> {
call.check_obsolete_submit_finality_proof()?;
call.check_obsolete_submit_teyrchain_heads()?;
call.check_obsolete_call()?;
Ok(call)
}
fn check_call_result(
call_info: &ExtensionCallInfo<Self::RemoteGrandpaChainBlockNumber, Self::LaneId>,
call_data: &mut ExtensionCallData,
relayer: &R::AccountId,
) -> bool {
verify_submit_finality_proof_succeeded::<Self, R::BridgesGrandpaPalletInstance>(
call_info, call_data, relayer,
) && verify_submit_teyrchain_head_succeeded::<Self, PI>(call_info, call_data, relayer) &&
verify_messages_call_succeeded::<Self>(call_info, call_data, relayer)
}
}
/// If the batch call contains the teyrchain state update call, verify that it
/// has been successful.
///
/// Only returns false when teyrchain state update call has failed.
pub(crate) fn verify_submit_teyrchain_head_succeeded<C, PI>(
call_info: &ExtensionCallInfo<C::RemoteGrandpaChainBlockNumber, C::LaneId>,
_call_data: &mut ExtensionCallData,
relayer: &<C::Runtime as SystemConfig>::AccountId,
) -> bool
where
C: ExtensionConfig,
PI: 'static,
C::Runtime: BridgeTeyrchainsConfig<PI>,
{
let Some(para_proof_info) = call_info.submit_teyrchain_heads_info() else { return true };
if !SubmitTeyrchainHeadsHelper::<C::Runtime, PI>::was_successful(para_proof_info) {
// we only refund relayer if all calls have updated chain state
tracing::trace!(
target: LOG_TARGET,
id_provider=%C::IdProvider::STR,
lane_id=?call_info.messages_call_info().lane_id(),
?relayer,
"Relayer has submitted invalid teyrchain finality proof"
);
return false;
}
true
}