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,72 @@
[package]
name = "pezpallet-xcm-bridge-hub-router"
description = "Bridge hub interface for sibling/parent chains with dynamic fees support."
version = "0.5.0"
authors.workspace = true
edition.workspace = true
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
repository.workspace = true
documentation = "https://docs.rs/pezpallet-xcm-bridge-hub-router"
homepage = { workspace = true }
[lints]
workspace = true
[dependencies]
codec = { workspace = true }
scale-info = { features = ["bit-vec", "derive", "serde"], workspace = true }
tracing = { workspace = true }
# Bridge dependencies
bp-xcm-bridge-hub-router = { workspace = true }
# Bizinikiwi Dependencies
pezframe-benchmarking = { optional = true, workspace = true }
pezframe-support = { workspace = true }
pezframe-system = { workspace = true }
pezsp-core = { workspace = true }
pezsp-runtime = { workspace = true }
pezsp-std = { workspace = true }
# Pezkuwi Dependencies
pezkuwi-runtime-teyrchains = { workspace = true }
xcm = { workspace = true }
xcm-builder = { workspace = true }
[dev-dependencies]
pezsp-io = { workspace = true, default-features = true }
[features]
default = ["std"]
std = [
"bp-xcm-bridge-hub-router/std",
"codec/std",
"pezframe-benchmarking/std",
"pezframe-support/std",
"pezframe-system/std",
"pezkuwi-runtime-teyrchains/std",
"scale-info/std",
"pezsp-core/std",
"pezsp-runtime/std",
"pezsp-std/std",
"tracing/std",
"xcm-builder/std",
"xcm/std",
]
runtime-benchmarks = [
"bp-xcm-bridge-hub-router/runtime-benchmarks",
"pezframe-benchmarking/runtime-benchmarks",
"pezframe-support/runtime-benchmarks",
"pezframe-system/runtime-benchmarks",
"pezkuwi-runtime-teyrchains/runtime-benchmarks",
"pezsp-io/runtime-benchmarks",
"pezsp-runtime/runtime-benchmarks",
"xcm-builder/runtime-benchmarks",
"xcm/runtime-benchmarks",
]
try-runtime = [
"pezframe-support/try-runtime",
"pezframe-system/try-runtime",
"pezkuwi-runtime-teyrchains/try-runtime",
"pezsp-runtime/try-runtime",
]
@@ -0,0 +1,80 @@
// 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/>.
//! XCM bridge hub router pezpallet benchmarks.
#![cfg(feature = "runtime-benchmarks")]
use crate::{Bridge, BridgeState, Call};
use pezframe_benchmarking::{benchmarks_instance_pallet, BenchmarkError};
use pezframe_support::traits::{EnsureOrigin, Get, Hooks, UnfilteredDispatchable};
use pezkuwi_runtime_teyrchains::FeeTracker;
use pezsp_runtime::{traits::Zero, Saturating};
use xcm::prelude::*;
/// Pezpallet we're benchmarking here.
pub struct Pezpallet<T: Config<I>, I: 'static = ()>(crate::Pezpallet<T, I>);
/// Trait that must be implemented by runtime to be able to benchmark pezpallet properly.
pub trait Config<I: 'static>: crate::Config<I> {
/// Fill up queue so it becomes congested.
fn make_congested();
/// Returns destination which is valid for this router instance.
/// (Needs to pass `T::Bridges`)
/// Make sure that `SendXcm` will pass.
fn ensure_bridged_target_destination() -> Result<Location, BenchmarkError> {
Ok(Location::new(
Self::UniversalLocation::get().len() as u8,
[GlobalConsensus(Self::BridgedNetworkId::get().unwrap())],
))
}
}
benchmarks_instance_pallet! {
on_initialize_when_non_congested {
Bridge::<T, I>::put(BridgeState {
is_congested: false,
delivery_fee_factor: crate::Pezpallet::<T, I>::MIN_FEE_FACTOR.saturating_mul(2.into()),
});
}: {
crate::Pezpallet::<T, I>::on_initialize(Zero::zero())
}
on_initialize_when_congested {
Bridge::<T, I>::put(BridgeState {
is_congested: false,
delivery_fee_factor: crate::Pezpallet::<T, I>::MIN_FEE_FACTOR.saturating_mul(2.into()),
});
let _ = T::ensure_bridged_target_destination()?;
T::make_congested();
}: {
crate::Pezpallet::<T, I>::on_initialize(Zero::zero())
}
report_bridge_status {
Bridge::<T, I>::put(BridgeState::default());
let origin: T::RuntimeOrigin = T::BridgeHubOrigin::try_successful_origin().expect("expected valid BridgeHubOrigin");
let bridge_id = Default::default();
let is_congested = true;
let call = Call::<T, I>::report_bridge_status { bridge_id, is_congested };
}: { call.dispatch_bypass_filter(origin)? }
verify {
assert!(Bridge::<T, I>::get().is_congested);
}
}
@@ -0,0 +1,747 @@
// 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/>.
//! Pezpallet that may be used instead of `SovereignPaidRemoteExporter` in the XCM router
//! configuration. The main thing that the pezpallet offers is the dynamic message fee,
//! that is computed based on the bridge queues state. It starts exponentially increasing
//! if the queue between this chain and the sibling/child bridge hub is congested.
//!
//! All other bridge hub queues offer some backpressure mechanisms. So if at least one
//! of all queues is congested, it will eventually lead to the growth of the queue at
//! this chain.
//!
//! **A note on terminology**: when we mention the bridge hub here, we mean the chain that
//! has the messages pezpallet deployed (`pezpallet-bridge-grandpa`, `pezpallet-bridge-messages`,
//! `pezpallet-xcm-bridge-hub`, ...). It may be the system bridge hub teyrchain or any other
//! chain.
#![cfg_attr(not(feature = "std"), no_std)]
use bp_xcm_bridge_hub_router::MINIMAL_DELIVERY_FEE_FACTOR;
pub use bp_xcm_bridge_hub_router::{BridgeState, XcmChannelStatusProvider};
use codec::Encode;
use pezframe_support::traits::Get;
use pezkuwi_runtime_teyrchains::FeeTracker;
use pezsp_core::H256;
use pezsp_runtime::{FixedPointNumber, FixedU128};
use pezsp_std::vec::Vec;
use xcm::prelude::*;
use xcm_builder::{ExporterFor, InspectMessageQueues, SovereignPaidRemoteExporter};
pub use pezpallet::*;
pub use weights::WeightInfo;
pub mod benchmarking;
pub mod weights;
mod mock;
/// Maximal size of the XCM message that may be sent over bridge.
///
/// This should be less than the maximal size, allowed by the messages pezpallet, because
/// the message itself is wrapped in other structs and is double encoded.
pub const HARD_MESSAGE_SIZE_LIMIT: u32 = 32 * 1024;
/// The target that will be used when publishing logs related to this pezpallet.
///
/// This doesn't match the pattern used by other bridge pallets (`runtime::bridge-*`). But this
/// pezpallet has significant differences with those pallets. The main one is that is intended to
/// be deployed at sending chains. Other bridge pallets are likely to be deployed at the separate
/// bridge hub teyrchain.
pub const LOG_TARGET: &str = "xcm::bridge-hub-router";
#[pezframe_support::pezpallet]
pub mod pezpallet {
use super::*;
use pezframe_support::pezpallet_prelude::*;
use pezframe_system::pezpallet_prelude::*;
#[pezpallet::config]
pub trait Config<I: 'static = ()>: pezframe_system::Config {
/// The overarching event type.
#[allow(deprecated)]
type RuntimeEvent: From<Event<Self, I>>
+ IsType<<Self as pezframe_system::Config>::RuntimeEvent>;
/// Benchmarks results from runtime we're plugged into.
type WeightInfo: WeightInfo;
/// Universal location of this runtime.
type UniversalLocation: Get<InteriorLocation>;
/// Relative location of the supported sibling bridge hub.
type SiblingBridgeHubLocation: Get<Location>;
/// The bridged network that this config is for if specified.
/// Also used for filtering `Bridges` by `BridgedNetworkId`.
/// If not specified, allows all networks pass through.
type BridgedNetworkId: Get<Option<NetworkId>>;
/// Configuration for supported **bridged networks/locations** with **bridge location** and
/// **possible fee**. Allows to externalize better control over allowed **bridged
/// networks/locations**.
type Bridges: ExporterFor;
/// Checks the XCM version for the destination.
type DestinationVersion: GetVersion;
/// Origin of the sibling bridge hub that is allowed to report bridge status.
type BridgeHubOrigin: EnsureOrigin<Self::RuntimeOrigin>;
/// Actual message sender (`HRMP` or `DMP`) to the sibling bridge hub location.
type ToBridgeHubSender: SendXcm;
/// Local XCM channel manager.
type LocalXcmChannelManager: XcmChannelStatusProvider;
/// Additional fee that is paid for every byte of the outbound message.
type ByteFee: Get<u128>;
/// Asset that is used to paid bridge fee.
type FeeAsset: Get<AssetId>;
}
#[pezpallet::pezpallet]
pub struct Pezpallet<T, I = ()>(PhantomData<(T, I)>);
#[pezpallet::hooks]
impl<T: Config<I>, I: 'static> Hooks<BlockNumberFor<T>> for Pezpallet<T, I> {
fn on_initialize(_n: BlockNumberFor<T>) -> Weight {
// if XCM channel is still congested, we don't change anything
if T::LocalXcmChannelManager::is_congested(&T::SiblingBridgeHubLocation::get()) {
return T::WeightInfo::on_initialize_when_congested();
}
// if bridge has reported congestion, we don't change anything
let mut bridge = Self::bridge();
if bridge.is_congested {
return T::WeightInfo::on_initialize_when_congested();
}
let previous_factor = Self::get_fee_factor(());
// if we can't decrease the delivery fee factor anymore, we don't change anything
if !Self::do_decrease_fee_factor(&mut bridge.delivery_fee_factor) {
return T::WeightInfo::on_initialize_when_congested();
}
tracing::info!(
target: LOG_TARGET,
from=%previous_factor,
to=%bridge.delivery_fee_factor,
"Bridge channel is uncongested. Decreased fee factor"
);
Self::deposit_event(Event::DeliveryFeeFactorDecreased {
new_value: bridge.delivery_fee_factor,
});
Bridge::<T, I>::put(bridge);
T::WeightInfo::on_initialize_when_non_congested()
}
}
#[pezpallet::call]
impl<T: Config<I>, I: 'static> Pezpallet<T, I> {
/// Notification about congested bridge queue.
#[pezpallet::call_index(0)]
#[pezpallet::weight(T::WeightInfo::report_bridge_status())]
pub fn report_bridge_status(
origin: OriginFor<T>,
// this argument is not currently used, but to ease future migration, we'll keep it
// here
bridge_id: H256,
is_congested: bool,
) -> DispatchResult {
T::BridgeHubOrigin::ensure_origin(origin)?;
tracing::info!(
target: LOG_TARGET,
from=?bridge_id,
congested=%is_congested,
"Received bridge status"
);
Bridge::<T, I>::mutate(|bridge| {
bridge.is_congested = is_congested;
});
Ok(())
}
}
/// Bridge that we are using.
///
/// **bridges-v1** assumptions: all outbound messages through this router are using single lane
/// and to single remote consensus. If there is some other remote consensus that uses the same
/// bridge hub, the separate pezpallet instance shall be used, In `v2` we'll have all required
/// primitives (lane-id aka bridge-id, derived from XCM locations) to support multiple bridges
/// by the same pezpallet instance.
#[pezpallet::storage]
pub type Bridge<T: Config<I>, I: 'static = ()> = StorageValue<_, BridgeState, ValueQuery>;
impl<T: Config<I>, I: 'static> Pezpallet<T, I> {
/// Bridge that we are using.
pub fn bridge() -> BridgeState {
Bridge::<T, I>::get()
}
/// Called when new message is sent (queued to local outbound XCM queue) over the bridge.
pub(crate) fn on_message_sent_to_bridge(message_size: u32) {
tracing::trace!(
target: LOG_TARGET,
?message_size, "on_message_sent_to_bridge"
);
let _ = Bridge::<T, I>::try_mutate(|bridge| {
let is_channel_with_bridge_hub_congested =
T::LocalXcmChannelManager::is_congested(&T::SiblingBridgeHubLocation::get());
let is_bridge_congested = bridge.is_congested;
// if outbound queue is not congested AND bridge has not reported congestion, do
// nothing
if !is_channel_with_bridge_hub_congested && !is_bridge_congested {
return Err(());
}
let previous_factor = Self::get_fee_factor(());
// ok - we need to increase the fee factor, let's do that
<Self as FeeTracker>::do_increase_fee_factor(
&mut bridge.delivery_fee_factor,
message_size as u128,
);
tracing::info!(
target: LOG_TARGET,
from=%previous_factor,
to=%bridge.delivery_fee_factor,
"Bridge channel is congested. Increased fee factor"
);
Self::deposit_event(Event::DeliveryFeeFactorIncreased {
new_value: bridge.delivery_fee_factor,
});
Ok(())
});
}
}
#[pezpallet::event]
#[pezpallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config<I>, I: 'static = ()> {
/// Delivery fee factor has been decreased.
DeliveryFeeFactorDecreased {
/// New value of the `DeliveryFeeFactor`.
new_value: FixedU128,
},
/// Delivery fee factor has been increased.
DeliveryFeeFactorIncreased {
/// New value of the `DeliveryFeeFactor`.
new_value: FixedU128,
},
}
}
/// We'll be using `SovereignPaidRemoteExporter` to send remote messages over the sibling/child
/// bridge hub.
type ViaBridgeHubExporter<T, I> = SovereignPaidRemoteExporter<
Pezpallet<T, I>,
<T as Config<I>>::ToBridgeHubSender,
<T as Config<I>>::UniversalLocation,
>;
// This pezpallet acts as the `ExporterFor` for the `SovereignPaidRemoteExporter` to compute
// message fee using fee factor.
impl<T: Config<I>, I: 'static> ExporterFor for Pezpallet<T, I> {
fn exporter_for(
network: &NetworkId,
remote_location: &InteriorLocation,
message: &Xcm<()>,
) -> Option<(Location, Option<Asset>)> {
tracing::trace!(
target: LOG_TARGET,
?network, ?remote_location, msg=?message, "exporter_for"
);
// ensure that the message is sent to the expected bridged network (if specified).
if let Some(bridged_network) = T::BridgedNetworkId::get() {
if *network != bridged_network {
tracing::trace!(
target: LOG_TARGET,
bridged_network_id=?bridged_network, ?network, "Router does not support bridging!"
);
return None;
}
}
// ensure that the message is sent to the expected bridged network and location.
let (bridge_hub_location, maybe_payment) =
match T::Bridges::exporter_for(network, remote_location, message) {
Some((bridge_hub_location, maybe_payment))
if bridge_hub_location.eq(&T::SiblingBridgeHubLocation::get()) =>
(bridge_hub_location, maybe_payment),
_ => {
tracing::trace!(
target: LOG_TARGET,
bridged_network_id=?T::BridgedNetworkId::get(),
sibling_bridge_hub_location=?T::SiblingBridgeHubLocation::get(),
?network,
?remote_location,
"Router configured does not support bridging!"
);
return None;
},
};
// take `base_fee` from `T::Brides`, but it has to be the same `T::FeeAsset`
let base_fee = match maybe_payment {
Some(payment) => match payment {
Asset { fun: Fungible(amount), id } if id.eq(&T::FeeAsset::get()) => amount,
invalid_asset => {
tracing::error!(
target: LOG_TARGET,
bridged_network_id=?T::BridgedNetworkId::get(),
fee_asset=?T::FeeAsset::get(),
with=?invalid_asset,
?bridge_hub_location,
?network,
?remote_location,
"Router is configured for `T::FeeAsset` which is not compatible for bridging!"
);
return None;
},
},
None => 0,
};
// compute fee amount. Keep in mind that this is only the bridge fee. The fee for sending
// message from this chain to child/sibling bridge hub is determined by the
// `Config::ToBridgeHubSender`
let message_size = message.encoded_size();
let message_fee = (message_size as u128).saturating_mul(T::ByteFee::get());
let fee_sum = base_fee.saturating_add(message_fee);
let fee_factor = Self::get_fee_factor(());
let fee = fee_factor.saturating_mul_int(fee_sum);
let fee = if fee > 0 { Some((T::FeeAsset::get(), fee).into()) } else { None };
tracing::info!(
target: LOG_TARGET,
to=?(network, remote_location),
bridge_fee=?fee,
%fee_factor,
"Going to send message ({message_size} bytes) over bridge."
);
Some((bridge_hub_location, fee))
}
}
// This pezpallet acts as the `SendXcm` to the sibling/child bridge hub instead of regular
// XCMP/DMP transport. This allows injecting dynamic message fees into XCM programs that
// are going to the bridged network.
impl<T: Config<I>, I: 'static> SendXcm for Pezpallet<T, I> {
type Ticket = (u32, <T::ToBridgeHubSender as SendXcm>::Ticket);
fn validate(
dest: &mut Option<Location>,
xcm: &mut Option<Xcm<()>>,
) -> SendResult<Self::Ticket> {
tracing::trace!(target: LOG_TARGET, msg=?xcm, destination=?dest, "validate");
// In case of success, the `ViaBridgeHubExporter` can modify XCM instructions and consume
// `dest` / `xcm`, so we retain the clone of original message and the destination for later
// `DestinationVersion` validation.
let xcm_to_dest_clone = xcm.clone();
let dest_clone = dest.clone();
// First, use the inner exporter to validate the destination to determine if it is even
// routable. If it is not, return an error. If it is, then the XCM is extended with
// instructions to pay the message fee at the sibling/child bridge hub. The cost will
// include both the cost of (1) delivery to the sibling bridge hub (returned by
// `Config::ToBridgeHubSender`) and (2) delivery to the bridged bridge hub (returned by
// `Self::exporter_for`).
match ViaBridgeHubExporter::<T, I>::validate(dest, xcm) {
Ok((ticket, cost)) => {
// If the ticket is ok, it means we are routing with this router, so we need to
// apply more validations to the cloned `dest` and `xcm`, which are required here.
let xcm_to_dest_clone = xcm_to_dest_clone.ok_or(SendError::MissingArgument)?;
let dest_clone = dest_clone.ok_or(SendError::MissingArgument)?;
// We won't have access to `dest` and `xcm` in the `deliver` method, so we need to
// precompute everything required here. However, `dest` and `xcm` were consumed by
// `ViaBridgeHubExporter`, so we need to use their clones.
let message_size = xcm_to_dest_clone.encoded_size() as _;
// The bridge doesn't support oversized or overweight messages. Therefore, it's
// better to drop such messages here rather than at the bridge hub. Let's check the
// message size."
if message_size > HARD_MESSAGE_SIZE_LIMIT {
return Err(SendError::ExceedsMaxMessageSize);
}
// We need to ensure that the known `dest`'s XCM version can comprehend the current
// `xcm` program. This may seem like an additional, unnecessary check, but it is
// not. A similar check is probably performed by the `ViaBridgeHubExporter`, which
// attempts to send a versioned message to the sibling bridge hub. However, the
// local bridge hub may have a higher XCM version than the remote `dest`. Once
// again, it is better to discard such messages here than at the bridge hub (e.g.,
// to avoid losing funds).
let destination_version = T::DestinationVersion::get_version_for(&dest_clone)
.ok_or(SendError::DestinationUnsupported)?;
VersionedXcm::from(xcm_to_dest_clone)
.into_version(destination_version)
.map_err(|()| SendError::DestinationUnsupported)?;
Ok(((message_size, ticket), cost))
},
Err(e) => {
tracing::trace!(target: LOG_TARGET, error=?e, "validate - ViaBridgeHubExporter");
Err(e)
},
}
}
fn deliver(ticket: Self::Ticket) -> Result<XcmHash, SendError> {
// use router to enqueue message to the sibling/child bridge hub. This also should handle
// payment for passing through this queue.
let (message_size, ticket) = ticket;
let xcm_hash = ViaBridgeHubExporter::<T, I>::deliver(ticket)?;
// increase delivery fee factor if required
Self::on_message_sent_to_bridge(message_size);
tracing::trace!(target: LOG_TARGET, ?xcm_hash, "deliver - message sent");
Ok(xcm_hash)
}
}
impl<T: Config<I>, I: 'static> InspectMessageQueues for Pezpallet<T, I> {
fn clear_messages() {}
/// This router needs to implement `InspectMessageQueues` but doesn't have to
/// return any messages, since it just reuses the `XcmpQueue` router.
fn get_messages() -> Vec<(VersionedLocation, Vec<VersionedXcm<()>>)> {
Vec::new()
}
}
impl<T: Config<I>, I: 'static> FeeTracker for Pezpallet<T, I> {
type Id = ();
const MIN_FEE_FACTOR: FixedU128 = MINIMAL_DELIVERY_FEE_FACTOR;
fn get_fee_factor(_id: Self::Id) -> FixedU128 {
Self::bridge().delivery_fee_factor
}
fn set_fee_factor(_id: Self::Id, val: FixedU128) {
let mut bridge = Self::bridge();
bridge.delivery_fee_factor = val;
Bridge::<T, I>::put(bridge);
}
}
#[cfg(test)]
mod tests {
use super::*;
use pezframe_support::assert_ok;
use mock::*;
use pezframe_support::traits::Hooks;
use pezframe_system::{EventRecord, Phase};
use pezsp_runtime::traits::One;
fn congested_bridge(delivery_fee_factor: FixedU128) -> BridgeState {
BridgeState { is_congested: true, delivery_fee_factor }
}
fn uncongested_bridge(delivery_fee_factor: FixedU128) -> BridgeState {
BridgeState { is_congested: false, delivery_fee_factor }
}
#[test]
fn initial_fee_factor_is_one() {
run_test(|| {
assert_eq!(
Bridge::<TestRuntime, ()>::get(),
uncongested_bridge(Pezpallet::<TestRuntime, ()>::MIN_FEE_FACTOR),
);
})
}
#[test]
fn fee_factor_is_not_decreased_from_on_initialize_when_xcm_channel_is_congested() {
run_test(|| {
Bridge::<TestRuntime, ()>::put(uncongested_bridge(FixedU128::from_rational(125, 100)));
TestLocalXcmChannelManager::make_congested(&SiblingBridgeHubLocation::get());
// it should not decrease, because queue is congested
let old_delivery = XcmBridgeHubRouter::bridge();
XcmBridgeHubRouter::on_initialize(One::one());
assert_eq!(XcmBridgeHubRouter::bridge(), old_delivery);
assert_eq!(System::events(), vec![]);
})
}
#[test]
fn fee_factor_is_not_decreased_from_on_initialize_when_bridge_has_reported_congestion() {
run_test(|| {
Bridge::<TestRuntime, ()>::put(congested_bridge(FixedU128::from_rational(125, 100)));
// it should not decrease, because bridge congested
let old_bridge = XcmBridgeHubRouter::bridge();
XcmBridgeHubRouter::on_initialize(One::one());
assert_eq!(XcmBridgeHubRouter::bridge(), old_bridge);
assert_eq!(System::events(), vec![]);
})
}
#[test]
fn fee_factor_is_decreased_from_on_initialize_when_xcm_channel_is_uncongested() {
run_test(|| {
let initial_fee_factor = FixedU128::from_rational(125, 100);
Bridge::<TestRuntime, ()>::put(uncongested_bridge(initial_fee_factor));
// it should eventually decrease to one
while XcmBridgeHubRouter::bridge().delivery_fee_factor >
Pezpallet::<TestRuntime, ()>::MIN_FEE_FACTOR
{
XcmBridgeHubRouter::on_initialize(One::one());
}
// verify that it doesn't decrease anymore
XcmBridgeHubRouter::on_initialize(One::one());
assert_eq!(
XcmBridgeHubRouter::bridge(),
uncongested_bridge(Pezpallet::<TestRuntime, ()>::MIN_FEE_FACTOR)
);
// check emitted event
let first_system_event = System::events().first().cloned();
assert_eq!(
first_system_event,
Some(EventRecord {
phase: Phase::Initialization,
event: RuntimeEvent::XcmBridgeHubRouter(Event::DeliveryFeeFactorDecreased {
new_value: initial_fee_factor / XcmBridgeHubRouter::EXPONENTIAL_FEE_BASE,
}),
topics: vec![],
})
);
})
}
#[test]
fn not_applicable_if_destination_is_within_other_network() {
run_test(|| {
// unroutable dest
let dest = Location::new(2, [GlobalConsensus(ByGenesis([0; 32])), Teyrchain(1000)]);
let xcm: Xcm<()> = vec![ClearOrigin].into();
// check that router does not consume when `NotApplicable`
let mut xcm_wrapper = Some(xcm.clone());
assert_eq!(
XcmBridgeHubRouter::validate(&mut Some(dest.clone()), &mut xcm_wrapper),
Err(SendError::NotApplicable),
);
// XCM is NOT consumed and untouched
assert_eq!(Some(xcm.clone()), xcm_wrapper);
// check the full `send_xcm`
assert_eq!(send_xcm::<XcmBridgeHubRouter>(dest, xcm,), Err(SendError::NotApplicable),);
});
}
#[test]
fn exceeds_max_message_size_if_size_is_above_hard_limit() {
run_test(|| {
// routable dest with XCM version
let dest =
Location::new(2, [GlobalConsensus(BridgedNetworkId::get()), Teyrchain(1000)]);
// oversized XCM
let xcm: Xcm<()> = vec![ClearOrigin; HARD_MESSAGE_SIZE_LIMIT as usize].into();
// dest is routable with the inner router
assert_ok!(ViaBridgeHubExporter::<TestRuntime, ()>::validate(
&mut Some(dest.clone()),
&mut Some(xcm.clone())
));
// check for oversized message
let mut xcm_wrapper = Some(xcm.clone());
assert_eq!(
XcmBridgeHubRouter::validate(&mut Some(dest.clone()), &mut xcm_wrapper),
Err(SendError::ExceedsMaxMessageSize),
);
// XCM is consumed by the inner router
assert!(xcm_wrapper.is_none());
// check the full `send_xcm`
assert_eq!(
send_xcm::<XcmBridgeHubRouter>(dest, xcm,),
Err(SendError::ExceedsMaxMessageSize),
);
});
}
#[test]
fn destination_unsupported_if_wrap_version_fails() {
run_test(|| {
// routable dest but we don't know XCM version
let dest = UnknownXcmVersionForRoutableLocation::get();
let xcm: Xcm<()> = vec![ClearOrigin].into();
// dest is routable with the inner router
assert_ok!(ViaBridgeHubExporter::<TestRuntime, ()>::validate(
&mut Some(dest.clone()),
&mut Some(xcm.clone())
));
// check that it does not pass XCM version check
let mut xcm_wrapper = Some(xcm.clone());
assert_eq!(
XcmBridgeHubRouter::validate(&mut Some(dest.clone()), &mut xcm_wrapper),
Err(SendError::DestinationUnsupported),
);
// XCM is consumed by the inner router
assert!(xcm_wrapper.is_none());
// check the full `send_xcm`
assert_eq!(
send_xcm::<XcmBridgeHubRouter>(dest, xcm,),
Err(SendError::DestinationUnsupported),
);
});
}
#[test]
fn returns_proper_delivery_price() {
run_test(|| {
let dest = Location::new(2, [GlobalConsensus(BridgedNetworkId::get())]);
let xcm: Xcm<()> = vec![ClearOrigin].into();
let msg_size = xcm.encoded_size();
// initially the base fee is used: `BASE_FEE + BYTE_FEE * msg_size + HRMP_FEE`
let expected_fee = BASE_FEE + BYTE_FEE * (msg_size as u128) + HRMP_FEE;
assert_eq!(
XcmBridgeHubRouter::validate(&mut Some(dest.clone()), &mut Some(xcm.clone()))
.unwrap()
.1
.get(0),
Some(&(BridgeFeeAsset::get(), expected_fee).into()),
);
// but when factor is larger than one, it increases the fee, so it becomes:
// `(BASE_FEE + BYTE_FEE * msg_size) * F + HRMP_FEE`
let factor = FixedU128::from_rational(125, 100);
Bridge::<TestRuntime, ()>::put(uncongested_bridge(factor));
let expected_fee =
(FixedU128::saturating_from_integer(BASE_FEE + BYTE_FEE * (msg_size as u128)) *
factor)
.into_inner() / FixedU128::DIV +
HRMP_FEE;
assert_eq!(
XcmBridgeHubRouter::validate(&mut Some(dest), &mut Some(xcm)).unwrap().1.get(0),
Some(&(BridgeFeeAsset::get(), expected_fee).into()),
);
});
}
#[test]
fn sent_message_doesnt_increase_factor_if_queue_is_uncongested() {
run_test(|| {
let old_bridge = XcmBridgeHubRouter::bridge();
assert_eq!(
send_xcm::<XcmBridgeHubRouter>(
Location::new(2, [GlobalConsensus(BridgedNetworkId::get()), Teyrchain(1000)]),
vec![ClearOrigin].into(),
)
.map(drop),
Ok(()),
);
assert!(TestToBridgeHubSender::is_message_sent());
assert_eq!(old_bridge, XcmBridgeHubRouter::bridge());
assert_eq!(System::events(), vec![]);
});
}
#[test]
fn sent_message_increases_factor_if_xcm_channel_is_congested() {
run_test(|| {
TestLocalXcmChannelManager::make_congested(&SiblingBridgeHubLocation::get());
let old_bridge = XcmBridgeHubRouter::bridge();
assert_ok!(send_xcm::<XcmBridgeHubRouter>(
Location::new(2, [GlobalConsensus(BridgedNetworkId::get()), Teyrchain(1000)]),
vec![ClearOrigin].into(),
)
.map(drop));
assert!(TestToBridgeHubSender::is_message_sent());
assert!(
old_bridge.delivery_fee_factor < XcmBridgeHubRouter::bridge().delivery_fee_factor
);
// check emitted event
let first_system_event = System::events().first().cloned();
assert!(matches!(
first_system_event,
Some(EventRecord {
phase: Phase::Initialization,
event: RuntimeEvent::XcmBridgeHubRouter(
Event::DeliveryFeeFactorIncreased { .. }
),
..
})
));
});
}
#[test]
fn sent_message_increases_factor_if_bridge_has_reported_congestion() {
run_test(|| {
Bridge::<TestRuntime, ()>::put(congested_bridge(
Pezpallet::<TestRuntime, ()>::MIN_FEE_FACTOR,
));
let old_bridge = XcmBridgeHubRouter::bridge();
assert_ok!(send_xcm::<XcmBridgeHubRouter>(
Location::new(2, [GlobalConsensus(BridgedNetworkId::get()), Teyrchain(1000)]),
vec![ClearOrigin].into(),
)
.map(drop));
assert!(TestToBridgeHubSender::is_message_sent());
assert!(
old_bridge.delivery_fee_factor < XcmBridgeHubRouter::bridge().delivery_fee_factor
);
// check emitted event
let first_system_event = System::events().first().cloned();
assert!(matches!(
first_system_event,
Some(EventRecord {
phase: Phase::Initialization,
event: RuntimeEvent::XcmBridgeHubRouter(
Event::DeliveryFeeFactorIncreased { .. }
),
..
})
));
});
}
#[test]
fn get_messages_does_not_return_anything() {
run_test(|| {
assert_ok!(send_xcm::<XcmBridgeHubRouter>(
(Parent, Parent, GlobalConsensus(BridgedNetworkId::get()), Teyrchain(1000)).into(),
vec![ClearOrigin].into()
));
assert_eq!(XcmBridgeHubRouter::get_messages(), vec![]);
});
}
}
@@ -0,0 +1,191 @@
// 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/>.
#![cfg(test)]
use crate as pezpallet_xcm_bridge_hub_router;
use bp_xcm_bridge_hub_router::XcmChannelStatusProvider;
use codec::Encode;
use pezframe_support::{
construct_runtime, derive_impl, parameter_types,
traits::{Contains, Equals},
};
use pezsp_runtime::{traits::ConstU128, BuildStorage};
use pezsp_std::cell::RefCell;
use xcm::prelude::*;
use xcm_builder::{InspectMessageQueues, NetworkExportTable, NetworkExportTableItem};
type Block = pezframe_system::mocking::MockBlock<TestRuntime>;
/// HRMP fee.
pub const HRMP_FEE: u128 = 500;
/// Base bridge fee.
pub const BASE_FEE: u128 = 1_000_000;
/// Byte bridge fee.
pub const BYTE_FEE: u128 = 1_000;
construct_runtime! {
pub enum TestRuntime
{
System: pezframe_system::{Pezpallet, Call, Config<T>, Storage, Event<T>},
XcmBridgeHubRouter: pezpallet_xcm_bridge_hub_router::{Pezpallet, Storage, Event<T>},
}
}
parameter_types! {
pub ThisNetworkId: NetworkId = Pezkuwi;
pub BridgedNetworkId: NetworkId = Kusama;
pub UniversalLocation: InteriorLocation = [GlobalConsensus(ThisNetworkId::get()), Teyrchain(1000)].into();
pub SiblingBridgeHubLocation: Location = ParentThen([Teyrchain(1002)].into()).into();
pub BridgeFeeAsset: AssetId = Location::parent().into();
pub BridgeTable: Vec<NetworkExportTableItem>
= vec![
NetworkExportTableItem::new(
BridgedNetworkId::get(),
None,
SiblingBridgeHubLocation::get(),
Some((BridgeFeeAsset::get(), BASE_FEE).into())
)
];
pub UnknownXcmVersionForRoutableLocation: Location = Location::new(2, [GlobalConsensus(BridgedNetworkId::get()), Teyrchain(9999)]);
}
#[derive_impl(pezframe_system::config_preludes::TestDefaultConfig)]
impl pezframe_system::Config for TestRuntime {
type Block = Block;
}
impl pezpallet_xcm_bridge_hub_router::Config<()> for TestRuntime {
type RuntimeEvent = RuntimeEvent;
type WeightInfo = ();
type UniversalLocation = UniversalLocation;
type SiblingBridgeHubLocation = SiblingBridgeHubLocation;
type BridgedNetworkId = BridgedNetworkId;
type Bridges = NetworkExportTable<BridgeTable>;
type DestinationVersion =
LatestOrNoneForLocationVersionChecker<Equals<UnknownXcmVersionForRoutableLocation>>;
type BridgeHubOrigin = pezframe_system::EnsureRoot<u64>;
type ToBridgeHubSender = TestToBridgeHubSender;
type LocalXcmChannelManager = TestLocalXcmChannelManager;
type ByteFee = ConstU128<BYTE_FEE>;
type FeeAsset = BridgeFeeAsset;
}
pub struct LatestOrNoneForLocationVersionChecker<Location>(pezsp_std::marker::PhantomData<Location>);
impl<LocationValue: Contains<Location>> GetVersion
for LatestOrNoneForLocationVersionChecker<LocationValue>
{
fn get_version_for(dest: &Location) -> Option<XcmVersion> {
if LocationValue::contains(dest) {
return None;
}
Some(XCM_VERSION)
}
}
pub struct TestToBridgeHubSender;
impl TestToBridgeHubSender {
pub fn is_message_sent() -> bool {
!Self::get_messages().is_empty()
}
}
thread_local! {
pub static SENT_XCM: RefCell<Vec<(Location, Xcm<()>)>> = RefCell::new(Vec::new());
}
impl SendXcm for TestToBridgeHubSender {
type Ticket = (Location, Xcm<()>);
fn validate(
destination: &mut Option<Location>,
message: &mut Option<Xcm<()>>,
) -> SendResult<Self::Ticket> {
let pair = (destination.take().unwrap(), message.take().unwrap());
Ok((pair, (BridgeFeeAsset::get(), HRMP_FEE).into()))
}
fn deliver(pair: Self::Ticket) -> Result<XcmHash, SendError> {
let hash = fake_message_hash(&pair.1);
SENT_XCM.with(|q| q.borrow_mut().push(pair));
Ok(hash)
}
}
impl InspectMessageQueues for TestToBridgeHubSender {
fn clear_messages() {
SENT_XCM.with(|q| q.borrow_mut().clear());
}
fn get_messages() -> Vec<(VersionedLocation, Vec<VersionedXcm<()>>)> {
SENT_XCM.with(|q| {
(*q.borrow())
.clone()
.iter()
.map(|(location, message)| {
(
VersionedLocation::from(location.clone()),
vec![VersionedXcm::from(message.clone())],
)
})
.collect()
})
}
}
pub struct TestLocalXcmChannelManager;
impl TestLocalXcmChannelManager {
pub fn make_congested(with: &Location) {
pezframe_support::storage::unhashed::put(
&(b"TestLocalXcmChannelManager.Congested", with).encode()[..],
&true,
);
}
}
impl XcmChannelStatusProvider for TestLocalXcmChannelManager {
fn is_congested(with: &Location) -> bool {
pezframe_support::storage::unhashed::get_or_default(
&(b"TestLocalXcmChannelManager.Congested", with).encode()[..],
)
}
}
/// Return test externalities to use in tests.
pub fn new_test_ext() -> pezsp_io::TestExternalities {
let t = pezframe_system::GenesisConfig::<TestRuntime>::default().build_storage().unwrap();
pezsp_io::TestExternalities::new(t)
}
/// Run pezpallet 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()
})
}
pub(crate) fn fake_message_hash<T>(message: &Xcm<T>) -> XcmHash {
message.using_encoded(pezsp_io::hashing::blake2_256)
}
@@ -0,0 +1,150 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Parity Bridges Common.
// Parity Bridges Common is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity Bridges Common is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
//! Autogenerated weights for pezpallet_xcm_bridge_hub_router
//!
//! THIS FILE WAS AUTO-GENERATED USING THE BIZINIKIWI BENCHMARK CLI VERSION 4.0.0-dev
//! DATE: 2023-08-03, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! WORST CASE MAP SIZE: `1000000`
//! HOSTNAME: `covid`, CPU: `11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz`
//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024
// Executed Command:
// target/release/rip-bridge-node
// benchmark
// pezpallet
// --chain=dev
// --steps=50
// --repeat=20
// --pezpallet=pezpallet_xcm_bridge_hub_router
// --extrinsic=*
// --execution=wasm
// --wasm-execution=Compiled
// --heap-pages=4096
// --output=./modules/xcm-bridge-hub-router/src/weights.rs
// --template=./.maintain/bridge-weight-template.hbs
#![allow(clippy::all)]
#![allow(unused_parens)]
#![allow(unused_imports)]
#![allow(missing_docs)]
use pezframe_support::{
traits::Get,
weights::{constants::RocksDbWeight, Weight},
};
use pezsp_std::marker::PhantomData;
/// Weight functions needed for pezpallet_xcm_bridge_hub_router.
pub trait WeightInfo {
fn on_initialize_when_non_congested() -> Weight;
fn on_initialize_when_congested() -> Weight;
fn report_bridge_status() -> Weight;
}
/// Weights for `pezpallet_xcm_bridge_hub_router` that are generated using one of the Bridge testnets.
///
/// Those weights are test only and must never be used in production.
pub struct BridgeWeight<T>(PhantomData<T>);
impl<T: pezframe_system::Config> WeightInfo for BridgeWeight<T> {
///
/// Storage: `XcmBridgeHubRouter::DeliveryFeeFactor` (r:1 w:1)
///
/// Proof: `XcmBridgeHubRouter::DeliveryFeeFactor` (`max_values`: Some(1), `max_size`: Some(16),
/// added: 511, mode: `MaxEncodedLen`)
fn on_initialize_when_non_congested() -> Weight {
// Proof Size summary in bytes:
// Measured: `52`
// Estimated: `3517`
// Minimum execution time: 11_141 nanoseconds.
Weight::from_parts(11_339_000, 3517)
.saturating_add(T::DbWeight::get().reads(2_u64))
.saturating_add(T::DbWeight::get().writes(1_u64))
}
/// Storage: UNKNOWN KEY `0x456d756c617465645369626c696e6758636d704368616e6e656c2e436f6e6765`
/// (r:1 w:0)
///
/// Proof: UNKNOWN KEY `0x456d756c617465645369626c696e6758636d704368616e6e656c2e436f6e6765` (r:1
/// w:0)
fn on_initialize_when_congested() -> Weight {
// Proof Size summary in bytes:
// Measured: `82`
// Estimated: `3547`
// Minimum execution time: 4_239 nanoseconds.
Weight::from_parts(4_383_000, 3547).saturating_add(T::DbWeight::get().reads(1_u64))
}
/// Storage: `XcmBridgeHubRouter::Bridge` (r:1 w:1)
///
/// Proof: `XcmBridgeHubRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added:
/// 512, mode: `MaxEncodedLen`)
fn report_bridge_status() -> Weight {
// Proof Size summary in bytes:
// Measured: `53`
// Estimated: `1502`
// Minimum execution time: 10_427 nanoseconds.
Weight::from_parts(10_682_000, 1502)
.saturating_add(T::DbWeight::get().reads(1_u64))
.saturating_add(T::DbWeight::get().writes(1_u64))
}
}
// For backwards compatibility and tests
impl WeightInfo for () {
/// Storage: UNKNOWN KEY `0x456d756c617465645369626c696e6758636d704368616e6e656c2e436f6e6765`
/// (r:1 w:0)
///
/// Proof: UNKNOWN KEY `0x456d756c617465645369626c696e6758636d704368616e6e656c2e436f6e6765` (r:1
/// w:0)
///
/// Storage: `XcmBridgeHubRouter::DeliveryFeeFactor` (r:1 w:1)
///
/// Proof: `XcmBridgeHubRouter::DeliveryFeeFactor` (`max_values`: Some(1), `max_size`: Some(16),
/// added: 511, mode: `MaxEncodedLen`)
fn on_initialize_when_non_congested() -> Weight {
// Proof Size summary in bytes:
// Measured: `52`
// Estimated: `3517`
// Minimum execution time: 11_141 nanoseconds.
Weight::from_parts(11_339_000, 3517)
.saturating_add(RocksDbWeight::get().reads(2_u64))
.saturating_add(RocksDbWeight::get().writes(1_u64))
}
/// Storage: UNKNOWN KEY `0x456d756c617465645369626c696e6758636d704368616e6e656c2e436f6e6765`
/// (r:1 w:0)
///
/// Proof: UNKNOWN KEY `0x456d756c617465645369626c696e6758636d704368616e6e656c2e436f6e6765` (r:1
/// w:0)
fn on_initialize_when_congested() -> Weight {
// Proof Size summary in bytes:
// Measured: `82`
// Estimated: `3547`
// Minimum execution time: 4_239 nanoseconds.
Weight::from_parts(4_383_000, 3547).saturating_add(RocksDbWeight::get().reads(1_u64))
}
/// Storage: `XcmBridgeHubRouter::Bridge` (r:1 w:1)
///
/// Proof: `XcmBridgeHubRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added:
/// 512, mode: `MaxEncodedLen`)
fn report_bridge_status() -> Weight {
// Proof Size summary in bytes:
// Measured: `53`
// Estimated: `1502`
// Minimum execution time: 10_427 nanoseconds.
Weight::from_parts(10_682_000, 1502)
.saturating_add(RocksDbWeight::get().reads(1_u64))
.saturating_add(RocksDbWeight::get().writes(1_u64))
}
}