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,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
|
||||
}
|
||||
Reference in New Issue
Block a user