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,278 @@
// Copyright 2019-2021 Parity Technologies (UK) Ltd.
// This file is part of Parity Bridges Common.
// Parity Bridges Common is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity Bridges Common is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
//! The code that allows to use the pezpallet (`pezpallet-xcm-bridge-hub`) as inbound
//! bridge messages dispatcher. Internally, it just forwards inbound blob to the
//! XCM-level blob dispatcher, which pushes message to some other queue (e.g.
//! to HRMP queue with the sibling target chain).
//!
//! This code is executed at the target bridge hub.
use crate::{Config, Pezpallet, LOG_TARGET};
use bp_messages::target_chain::{DispatchMessage, MessageDispatch};
use pezbp_runtime::messages::MessageDispatchResult;
use bp_xcm_bridge_hub::{LocalXcmChannelManager, XcmAsPlainPayload};
use codec::{Decode, DecodeWithMemTracking, Encode};
use pezframe_support::{weights::Weight, CloneNoBound, EqNoBound, PartialEqNoBound};
use pezpallet_bridge_messages::{Config as BridgeMessagesConfig, WeightInfoExt};
use scale_info::TypeInfo;
use pezsp_runtime::SaturatedConversion;
use xcm::prelude::*;
use xcm_builder::{DispatchBlob, DispatchBlobError};
/// Message dispatch result type for single message.
#[derive(
CloneNoBound,
EqNoBound,
PartialEqNoBound,
Encode,
Decode,
DecodeWithMemTracking,
Debug,
TypeInfo,
)]
pub enum XcmBlobMessageDispatchResult {
/// We've been unable to decode message payload.
InvalidPayload,
/// Message has been dispatched.
Dispatched,
/// Message has **NOT** been dispatched because of given error.
NotDispatched(#[codec(skip)] Option<DispatchBlobError>),
}
/// An easy way to access associated messages pezpallet weights.
type MessagesPalletWeights<T, I> =
<T as BridgeMessagesConfig<<T as Config<I>>::BridgeMessagesPalletInstance>>::WeightInfo;
impl<T: Config<I>, I: 'static> MessageDispatch for Pezpallet<T, I>
where
T: BridgeMessagesConfig<T::BridgeMessagesPalletInstance, InboundPayload = XcmAsPlainPayload>,
{
type DispatchPayload = XcmAsPlainPayload;
type DispatchLevelResult = XcmBlobMessageDispatchResult;
type LaneId = T::LaneId;
fn is_active(lane: Self::LaneId) -> bool {
Pezpallet::<T, I>::bridge_by_lane_id(&lane)
.and_then(|(_, bridge)| (*bridge.bridge_origin_relative_location).try_into().ok())
.map(|recipient: Location| !T::LocalXcmChannelManager::is_congested(&recipient))
.unwrap_or(false)
}
fn dispatch_weight(
message: &mut DispatchMessage<Self::DispatchPayload, Self::LaneId>,
) -> Weight {
match message.data.payload {
Ok(ref payload) => {
let payload_size = payload.encoded_size().saturated_into();
MessagesPalletWeights::<T, I>::message_dispatch_weight(payload_size)
},
Err(_) => Weight::zero(),
}
}
fn dispatch(
message: DispatchMessage<Self::DispatchPayload, Self::LaneId>,
) -> MessageDispatchResult<Self::DispatchLevelResult> {
let payload = match message.data.payload {
Ok(payload) => payload,
Err(e) => {
tracing::error!(
target: LOG_TARGET,
error=?e,
lane_id=?message.key.lane_id,
message_nonce=?message.key.nonce,
"dispatch - payload error"
);
return MessageDispatchResult {
unspent_weight: Weight::zero(),
dispatch_level_result: XcmBlobMessageDispatchResult::InvalidPayload,
};
},
};
let dispatch_level_result = match T::BlobDispatcher::dispatch_blob(payload) {
Ok(_) => {
tracing::debug!(
target: LOG_TARGET,
lane_id=?message.key.lane_id,
message_nonce=?message.key.nonce,
"dispatch - `DispatchBlob::dispatch_blob` was ok"
);
XcmBlobMessageDispatchResult::Dispatched
},
Err(e) => {
tracing::error!(
target: LOG_TARGET,
error=?e,
lane_id=?message.key.lane_id,
message_nonce=?message.key.nonce,
"dispatch - `DispatchBlob::dispatch_blob` failed"
);
XcmBlobMessageDispatchResult::NotDispatched(Some(e))
},
};
MessageDispatchResult { unspent_weight: Weight::zero(), dispatch_level_result }
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{mock::*, Bridges, LaneToBridge, LanesManagerOf};
use bp_messages::{target_chain::DispatchMessageData, LaneIdType, MessageKey};
use bp_xcm_bridge_hub::{Bridge, BridgeLocations, BridgeState};
use pezframe_support::assert_ok;
use pezpallet_bridge_messages::InboundLaneStorage;
use xcm_executor::traits::ConvertLocation;
fn bridge() -> (Box<BridgeLocations>, TestLaneIdType) {
let origin = OpenBridgeOrigin::sibling_teyrchain_origin();
let with = bridged_asset_hub_universal_location();
let locations =
XcmOverBridge::bridge_locations_from_origin(origin, Box::new(with.into())).unwrap();
let lane_id = locations.calculate_lane_id(xcm::latest::VERSION).unwrap();
(locations, lane_id)
}
fn run_test_with_opened_bridge(test: impl FnOnce()) {
run_test(|| {
let (bridge, lane_id) = bridge();
if !Bridges::<TestRuntime, ()>::contains_key(bridge.bridge_id()) {
// insert bridge
Bridges::<TestRuntime, ()>::insert(
bridge.bridge_id(),
Bridge {
bridge_origin_relative_location: Box::new(
bridge.bridge_origin_relative_location().clone().into(),
),
bridge_origin_universal_location: Box::new(
bridge.bridge_origin_universal_location().clone().into(),
),
bridge_destination_universal_location: Box::new(
bridge.bridge_destination_universal_location().clone().into(),
),
state: BridgeState::Opened,
bridge_owner_account: LocationToAccountId::convert_location(
bridge.bridge_origin_relative_location(),
)
.expect("valid accountId"),
deposit: 0,
lane_id,
},
);
LaneToBridge::<TestRuntime, ()>::insert(lane_id, bridge.bridge_id());
// create lanes
let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
if lanes_manager.create_inbound_lane(lane_id).is_ok() {
assert_eq!(
0,
lanes_manager
.active_inbound_lane(lane_id)
.unwrap()
.storage()
.data()
.last_confirmed_nonce
);
}
if lanes_manager.create_outbound_lane(lane_id).is_ok() {
assert!(lanes_manager
.active_outbound_lane(lane_id)
.unwrap()
.queued_messages()
.is_empty());
}
}
assert_ok!(XcmOverBridge::do_try_state());
test();
});
}
fn invalid_message() -> DispatchMessage<Vec<u8>, TestLaneIdType> {
DispatchMessage {
key: MessageKey { lane_id: TestLaneIdType::try_new(1, 2).unwrap(), nonce: 1 },
data: DispatchMessageData { payload: Err(codec::Error::from("test")) },
}
}
fn valid_message() -> DispatchMessage<Vec<u8>, TestLaneIdType> {
DispatchMessage {
key: MessageKey { lane_id: TestLaneIdType::try_new(1, 2).unwrap(), nonce: 1 },
data: DispatchMessageData { payload: Ok(vec![42]) },
}
}
#[test]
fn dispatcher_is_inactive_when_channel_with_target_chain_is_congested() {
run_test_with_opened_bridge(|| {
TestLocalXcmChannelManager::make_congested();
assert!(!XcmOverBridge::is_active(bridge().1));
});
}
#[test]
fn dispatcher_is_active_when_channel_with_target_chain_is_not_congested() {
run_test_with_opened_bridge(|| {
assert!(XcmOverBridge::is_active(bridge().1));
});
}
#[test]
fn dispatch_weight_is_zero_if_we_have_failed_to_decode_message() {
run_test(|| {
assert_eq!(XcmOverBridge::dispatch_weight(&mut invalid_message()), Weight::zero());
});
}
#[test]
fn dispatch_weight_is_non_zero_if_we_have_decoded_message() {
run_test(|| {
assert_ne!(XcmOverBridge::dispatch_weight(&mut valid_message()), Weight::zero());
});
}
#[test]
fn message_is_not_dispatched_when_we_have_failed_to_decode_message() {
run_test(|| {
assert_eq!(
XcmOverBridge::dispatch(invalid_message()),
MessageDispatchResult {
unspent_weight: Weight::zero(),
dispatch_level_result: XcmBlobMessageDispatchResult::InvalidPayload,
},
);
assert!(!TestBlobDispatcher::is_dispatched());
});
}
#[test]
fn message_is_dispatched_when_we_have_decoded_message() {
run_test(|| {
assert_eq!(
XcmOverBridge::dispatch(valid_message()),
MessageDispatchResult {
unspent_weight: Weight::zero(),
dispatch_level_result: XcmBlobMessageDispatchResult::Dispatched,
},
);
assert!(TestBlobDispatcher::is_dispatched());
});
}
}
@@ -0,0 +1,873 @@
// Copyright 2019-2021 Parity Technologies (UK) Ltd.
// This file is part of Parity Bridges Common.
// Parity Bridges Common is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity Bridges Common is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
//! The code that allows to use the pezpallet (`pezpallet-xcm-bridge-hub`) as XCM message
//! exporter at the sending bridge hub. Internally, it just enqueues outbound blob
//! in the messages pezpallet queue.
//!
//! This code is executed at the source bridge hub.
use crate::{Config, Pezpallet, LOG_TARGET};
use crate::{BridgeOf, Bridges};
use bp_messages::{
source_chain::{MessagesBridge, OnMessagesDelivered},
MessageNonce,
};
use bp_xcm_bridge_hub::{BridgeId, BridgeState, LocalXcmChannelManager, XcmAsPlainPayload};
use pezframe_support::{ensure, traits::Get};
use pezpallet_bridge_messages::{
Config as BridgeMessagesConfig, Error, Pezpallet as BridgeMessagesPallet,
};
use xcm::prelude::*;
use xcm_builder::{HaulBlob, HaulBlobError, HaulBlobExporter};
use xcm_executor::traits::ExportXcm;
/// Maximal number of messages in the outbound bridge queue. Once we reach this limit, we
/// suspend a bridge.
const OUTBOUND_LANE_CONGESTED_THRESHOLD: MessageNonce = 8_192;
/// After we have suspended the bridge, we wait until number of messages in the outbound bridge
/// queue drops to this count, before sending resuming the bridge.
const OUTBOUND_LANE_UNCONGESTED_THRESHOLD: MessageNonce = 1_024;
/// An easy way to access `HaulBlobExporter`.
pub type PalletAsHaulBlobExporter<T, I> = HaulBlobExporter<
DummyHaulBlob,
<T as Config<I>>::BridgedNetwork,
<T as Config<I>>::DestinationVersion,
<T as Config<I>>::MessageExportPrice,
>;
/// An easy way to access associated messages pezpallet.
type MessagesPallet<T, I> = BridgeMessagesPallet<T, <T as Config<I>>::BridgeMessagesPalletInstance>;
impl<T: Config<I>, I: 'static> ExportXcm for Pezpallet<T, I>
where
T: BridgeMessagesConfig<T::BridgeMessagesPalletInstance, OutboundPayload = XcmAsPlainPayload>,
{
type Ticket = (
BridgeId,
BridgeOf<T, I>,
<MessagesPallet<T, I> as MessagesBridge<T::OutboundPayload, T::LaneId>>::SendMessageArgs,
XcmHash,
);
fn validate(
network: NetworkId,
channel: u32,
universal_source: &mut Option<InteriorLocation>,
destination: &mut Option<InteriorLocation>,
message: &mut Option<Xcm<()>>,
) -> Result<(Self::Ticket, Assets), SendError> {
tracing::trace!(
target: LOG_TARGET,
?network,
?channel,
?universal_source,
?destination,
"Validate for network"
);
// `HaulBlobExporter` may consume the `universal_source` and `destination` arguments, so
// let's save them before
let bridge_origin_universal_location =
universal_source.clone().ok_or(SendError::MissingArgument)?;
// Note: watch out this is `ExportMessage::destination`, which is relative to the `network`,
// which means it does not contain `GlobalConsensus`, We need to find `BridgeId` with
// `Self::bridge_locations` which requires **universal** location for destination.
let bridge_destination_universal_location = {
let dest = destination.clone().ok_or(SendError::MissingArgument)?;
match dest.global_consensus() {
Ok(dest_network) => {
tracing::trace!(
target: LOG_TARGET,
?dest,
?dest_network,
?network,
"Destination is already universal, checking if matches: {:?}",
dest_network == network
);
ensure!(dest_network == network, SendError::NotApplicable);
// ok, `dest` looks like a universal location, so let's use it
dest
},
Err(_) => {
// `dest` is not a universal location, so we need to prepend it with
// `GlobalConsensus`.
dest.pushed_front_with(GlobalConsensus(network)).map_err(|error_data| {
tracing::error!(
target: LOG_TARGET, error=?error_data,
"Destination is not a universal and prepending failed!"
);
SendError::NotApplicable
})?
},
}
};
// prepare the origin relative location
let bridge_origin_relative_location =
bridge_origin_universal_location.relative_to(&T::UniversalLocation::get());
// then we are able to compute the `BridgeId` and find `LaneId` used to send messages
let locations = Self::bridge_locations(
bridge_origin_relative_location,
bridge_destination_universal_location.into(),
)
.map_err(|e| {
tracing::error!(
target: LOG_TARGET, error=?e,
"Validate `bridge_locations` with error"
);
SendError::NotApplicable
})?;
let bridge = Self::bridge(locations.bridge_id()).ok_or_else(|| {
tracing::error!(
target: LOG_TARGET,
bridge_origin_relative_location=?locations.bridge_origin_relative_location(),
bridge_destination_universal_location=?locations.bridge_destination_universal_location(),
"No opened bridge for requested"
);
SendError::NotApplicable
})?;
// check if we are able to route the message. We use existing `HaulBlobExporter` for that.
// It will make all required changes and will encode message properly, so that the
// `DispatchBlob` at the bridged bridge hub will be able to decode it
let ((blob, id), price) = PalletAsHaulBlobExporter::<T, I>::validate(
network,
channel,
universal_source,
destination,
message,
)?;
let bridge_message = MessagesPallet::<T, I>::validate_message(bridge.lane_id, &blob)
.map_err(|e| {
match e {
Error::LanesManager(ref ei) => {
tracing::error!(target: LOG_TARGET, error=?ei, "LanesManager")
},
Error::MessageRejectedByPallet(ref ei) => {
tracing::error!(target: LOG_TARGET, error=?ei, "MessageRejectedByPallet")
},
Error::ReceptionConfirmation(ref ei) => {
tracing::error!(target: LOG_TARGET, error=?ei, "ReceptionConfirmation")
},
_ => (),
};
tracing::error!(
target: LOG_TARGET,
error=?e,
topic_id=?id,
bridge_id=?locations,
lane_id=?bridge.lane_id,
"XCM message cannot be exported"
);
SendError::Transport("BridgeValidateError")
})?;
Ok(((*locations.bridge_id(), bridge, bridge_message, id), price))
}
fn deliver(
(bridge_id, bridge, bridge_message, id): Self::Ticket,
) -> Result<XcmHash, SendError> {
let artifacts = MessagesPallet::<T, I>::send_message(bridge_message);
tracing::info!(
target: LOG_TARGET,
topic_id=?id,
bridge_id=?bridge_id,
lane_id=?bridge.lane_id,
nonce=%artifacts.nonce,
"XCM message has been enqueued"
);
// maybe we need switch to congested state
Self::on_bridge_message_enqueued(bridge_id, bridge, artifacts.enqueued_messages);
Ok(id)
}
}
impl<T: Config<I>, I: 'static> OnMessagesDelivered<T::LaneId> for Pezpallet<T, I> {
fn on_messages_delivered(lane_id: T::LaneId, enqueued_messages: MessageNonce) {
Self::on_bridge_messages_delivered(lane_id, enqueued_messages);
}
}
impl<T: Config<I>, I: 'static> Pezpallet<T, I> {
/// Called when new message is pushed onto outbound bridge queue.
fn on_bridge_message_enqueued(
bridge_id: BridgeId,
bridge: BridgeOf<T, I>,
enqueued_messages: MessageNonce,
) {
// if the bridge queue is not congested, we don't want to do anything
let is_congested = enqueued_messages > OUTBOUND_LANE_CONGESTED_THRESHOLD;
if !is_congested {
return;
}
// TODO: https://github.com/pezkuwichain/kurdistan-sdk/issues/83 we either need fishermens
// to watch this rule violation (suspended, but keep sending new messages), or we need a
// hard limit for that like other XCM queues have
// check if the lane is already suspended. If it is, do nothing. We still accept new
// messages to the suspended bridge, hoping that it'll be actually resumed soon
if bridge.state == BridgeState::Suspended {
return;
}
// else - suspend the bridge
let result_bridge_origin_relative_location =
(*bridge.bridge_origin_relative_location).clone().try_into();
let bridge_origin_relative_location = match &result_bridge_origin_relative_location {
Ok(bridge_origin_relative_location) => bridge_origin_relative_location,
Err(_) => {
tracing::debug!(
target: LOG_TARGET,
?bridge_id,
origin_location=?bridge.bridge_origin_relative_location,
"Failed to convert"
);
return;
},
};
let suspend_result =
T::LocalXcmChannelManager::suspend_bridge(bridge_origin_relative_location, bridge_id);
match suspend_result {
Ok(_) => {
tracing::debug!(
target: LOG_TARGET,
?bridge_id,
originated_by=?bridge.bridge_origin_relative_location,
"Suspended"
);
},
Err(e) => {
tracing::debug!(
target: LOG_TARGET,
error=?e,
?bridge_id,
originated_by=?bridge.bridge_origin_relative_location,
"Failed to suspended"
);
return;
},
}
// and remember that we have suspended the bridge
Bridges::<T, I>::mutate_extant(bridge_id, |bridge| {
bridge.state = BridgeState::Suspended;
});
}
/// Must be called whenever we receive a message delivery confirmation.
fn on_bridge_messages_delivered(lane_id: T::LaneId, enqueued_messages: MessageNonce) {
// if the bridge queue is still congested, we don't want to do anything
let is_congested = enqueued_messages > OUTBOUND_LANE_UNCONGESTED_THRESHOLD;
if is_congested {
return;
}
// if we have not suspended the bridge before (or it is closed), we don't want to do
// anything
let (bridge_id, bridge) = match Self::bridge_by_lane_id(&lane_id) {
Some(bridge) if bridge.1.state == BridgeState::Suspended => bridge,
_ => {
// if there is no bridge or it has been closed, then we don't need to send resume
// signal to the local origin - it has closed bridge itself, so it should have
// alrady pruned everything else
return;
},
};
// else - resume the bridge
let bridge_origin_relative_location = (*bridge.bridge_origin_relative_location).try_into();
let bridge_origin_relative_location = match bridge_origin_relative_location {
Ok(bridge_origin_relative_location) => bridge_origin_relative_location,
Err(e) => {
tracing::debug!(
target: LOG_TARGET,
error=?e,
?bridge_id,
?lane_id,
"Failed to convert",
);
return;
},
};
let resume_result =
T::LocalXcmChannelManager::resume_bridge(&bridge_origin_relative_location, bridge_id);
match resume_result {
Ok(_) => {
tracing::debug!(
target: LOG_TARGET,
?bridge_id,
?lane_id,
originated_by=?bridge_origin_relative_location,
"Resumed",
);
},
Err(e) => {
tracing::debug!(
target: LOG_TARGET,
error=?e,
?bridge_id,
?lane_id,
originated_by=?bridge_origin_relative_location,
"Failed to resume"
);
return;
},
}
// and forget that we have previously suspended the bridge
Bridges::<T, I>::mutate_extant(bridge_id, |bridge| {
bridge.state = BridgeState::Opened;
});
}
}
/// Dummy implementation of the `HaulBlob` trait that is never called.
///
/// We are using `HaulBlobExporter`, which requires `HaulBlob` implementation. It assumes that
/// there's a single channel between two bridge hubs - `HaulBlob` only accepts the blob and nothing
/// else. But bridge messages pezpallet may have a dedicated channel (lane) for every pair of bridged
/// chains. So we are using our own `ExportXcm` implementation, but to utilize `HaulBlobExporter` we
/// still need this `DummyHaulBlob`.
pub struct DummyHaulBlob;
impl HaulBlob for DummyHaulBlob {
fn haul_blob(_blob: XcmAsPlainPayload) -> Result<(), HaulBlobError> {
Err(HaulBlobError::Transport("DummyHaulBlob"))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{mock::*, Bridges, LaneToBridge, LanesManagerOf};
use pezbp_runtime::RangeInclusiveExt;
use bp_xcm_bridge_hub::{Bridge, BridgeLocations, BridgeState};
use pezframe_support::{assert_ok, traits::EnsureOrigin};
use pezpallet_bridge_messages::InboundLaneStorage;
use xcm_builder::{NetworkExportTable, UnpaidRemoteExporter};
use xcm_executor::traits::{export_xcm, ConvertLocation};
fn universal_source() -> InteriorLocation {
SiblingUniversalLocation::get()
}
fn bridged_relative_destination() -> InteriorLocation {
BridgedRelativeDestination::get()
}
fn bridged_universal_destination() -> InteriorLocation {
BridgedUniversalDestination::get()
}
fn open_lane(origin: RuntimeOrigin) -> (BridgeLocations, TestLaneIdType) {
// open expected outbound lane
let with = bridged_asset_hub_universal_location();
let locations =
XcmOverBridge::bridge_locations_from_origin(origin, Box::new(with.into())).unwrap();
let lane_id = locations.calculate_lane_id(xcm::latest::VERSION).unwrap();
if !Bridges::<TestRuntime, ()>::contains_key(locations.bridge_id()) {
// insert bridge
Bridges::<TestRuntime, ()>::insert(
locations.bridge_id(),
Bridge {
bridge_origin_relative_location: Box::new(SiblingLocation::get().into()),
bridge_origin_universal_location: Box::new(
locations.bridge_origin_universal_location().clone().into(),
),
bridge_destination_universal_location: Box::new(
locations.bridge_destination_universal_location().clone().into(),
),
state: BridgeState::Opened,
bridge_owner_account: LocationToAccountId::convert_location(
locations.bridge_origin_relative_location(),
)
.expect("valid accountId"),
deposit: 0,
lane_id,
},
);
LaneToBridge::<TestRuntime, ()>::insert(lane_id, locations.bridge_id());
// create lanes
let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
if lanes_manager.create_inbound_lane(lane_id).is_ok() {
assert_eq!(
0,
lanes_manager
.active_inbound_lane(lane_id)
.unwrap()
.storage()
.data()
.last_confirmed_nonce
);
}
if lanes_manager.create_outbound_lane(lane_id).is_ok() {
assert!(lanes_manager
.active_outbound_lane(lane_id)
.unwrap()
.queued_messages()
.is_empty());
}
}
assert_ok!(XcmOverBridge::do_try_state());
(*locations, lane_id)
}
fn open_lane_and_send_regular_message() -> (BridgeId, TestLaneIdType) {
let (locations, lane_id) = open_lane(OpenBridgeOrigin::sibling_teyrchain_origin());
// now let's try to enqueue message using our `ExportXcm` implementation
export_xcm::<XcmOverBridge>(
BridgedRelayNetwork::get(),
0,
locations.bridge_origin_universal_location().clone(),
locations.bridge_destination_universal_location().clone(),
vec![Instruction::ClearOrigin].into(),
)
.unwrap();
(*locations.bridge_id(), lane_id)
}
#[test]
fn exporter_works() {
run_test(|| {
let (_, lane_id) = open_lane_and_send_regular_message();
// double check that the message has been pushed to the expected lane
// (it should already been checked during `send_message` call)
assert!(!LanesManagerOf::<TestRuntime, ()>::new()
.active_outbound_lane(lane_id)
.unwrap()
.queued_messages()
.is_empty());
});
}
#[test]
fn exporter_does_not_suspend_the_bridge_if_outbound_bridge_queue_is_not_congested() {
run_test(|| {
let (bridge_id, _) = open_lane_and_send_regular_message();
assert!(!TestLocalXcmChannelManager::is_bridge_suspended(&bridge_id));
assert_eq!(XcmOverBridge::bridge(&bridge_id).unwrap().state, BridgeState::Opened);
});
}
#[test]
fn exporter_does_not_suspend_the_bridge_if_it_is_already_suspended() {
run_test(|| {
let (bridge_id, _) = open_lane_and_send_regular_message();
Bridges::<TestRuntime, ()>::mutate_extant(bridge_id, |bridge| {
bridge.state = BridgeState::Suspended;
});
for _ in 1..OUTBOUND_LANE_CONGESTED_THRESHOLD {
open_lane_and_send_regular_message();
}
open_lane_and_send_regular_message();
assert!(!TestLocalXcmChannelManager::is_bridge_suspended(&bridge_id));
});
}
#[test]
fn exporter_suspends_the_bridge_if_outbound_bridge_queue_is_congested() {
run_test(|| {
let (bridge_id, _) = open_lane_and_send_regular_message();
for _ in 1..OUTBOUND_LANE_CONGESTED_THRESHOLD {
open_lane_and_send_regular_message();
}
assert!(!TestLocalXcmChannelManager::is_bridge_suspended(&bridge_id));
assert_eq!(XcmOverBridge::bridge(&bridge_id).unwrap().state, BridgeState::Opened);
open_lane_and_send_regular_message();
assert!(TestLocalXcmChannelManager::is_bridge_suspended(&bridge_id));
assert_eq!(XcmOverBridge::bridge(&bridge_id).unwrap().state, BridgeState::Suspended);
});
}
#[test]
fn bridge_is_not_resumed_if_outbound_bridge_queue_is_still_congested() {
run_test(|| {
let (bridge_id, lane_id) = open_lane_and_send_regular_message();
Bridges::<TestRuntime, ()>::mutate_extant(bridge_id, |bridge| {
bridge.state = BridgeState::Suspended;
});
XcmOverBridge::on_bridge_messages_delivered(
lane_id,
OUTBOUND_LANE_UNCONGESTED_THRESHOLD + 1,
);
assert!(!TestLocalXcmChannelManager::is_bridge_resumed(&bridge_id));
assert_eq!(XcmOverBridge::bridge(&bridge_id).unwrap().state, BridgeState::Suspended);
});
}
#[test]
fn bridge_is_not_resumed_if_it_was_not_suspended_before() {
run_test(|| {
let (bridge_id, lane_id) = open_lane_and_send_regular_message();
XcmOverBridge::on_bridge_messages_delivered(
lane_id,
OUTBOUND_LANE_UNCONGESTED_THRESHOLD,
);
assert!(!TestLocalXcmChannelManager::is_bridge_resumed(&bridge_id));
assert_eq!(XcmOverBridge::bridge(&bridge_id).unwrap().state, BridgeState::Opened);
});
}
#[test]
fn bridge_is_resumed_when_enough_messages_are_delivered() {
run_test(|| {
let (bridge_id, lane_id) = open_lane_and_send_regular_message();
Bridges::<TestRuntime, ()>::mutate_extant(bridge_id, |bridge| {
bridge.state = BridgeState::Suspended;
});
XcmOverBridge::on_bridge_messages_delivered(
lane_id,
OUTBOUND_LANE_UNCONGESTED_THRESHOLD,
);
assert!(TestLocalXcmChannelManager::is_bridge_resumed(&bridge_id));
assert_eq!(XcmOverBridge::bridge(&bridge_id).unwrap().state, BridgeState::Opened);
});
}
#[test]
fn export_fails_if_argument_is_missing() {
run_test(|| {
assert_eq!(
XcmOverBridge::validate(
BridgedRelayNetwork::get(),
0,
&mut None,
&mut Some(bridged_relative_destination()),
&mut Some(Vec::new().into()),
),
Err(SendError::MissingArgument),
);
assert_eq!(
XcmOverBridge::validate(
BridgedRelayNetwork::get(),
0,
&mut Some(universal_source()),
&mut None,
&mut Some(Vec::new().into()),
),
Err(SendError::MissingArgument),
);
})
}
#[test]
fn exporter_computes_correct_lane_id() {
run_test(|| {
assert_ne!(bridged_universal_destination(), bridged_relative_destination());
let locations = BridgeLocations::bridge_locations(
UniversalLocation::get(),
SiblingLocation::get(),
bridged_universal_destination(),
BridgedRelayNetwork::get(),
)
.unwrap();
let expected_bridge_id = locations.bridge_id();
let expected_lane_id = locations.calculate_lane_id(xcm::latest::VERSION).unwrap();
if LanesManagerOf::<TestRuntime, ()>::new()
.create_outbound_lane(expected_lane_id)
.is_ok()
{
Bridges::<TestRuntime, ()>::insert(
expected_bridge_id,
Bridge {
bridge_origin_relative_location: Box::new(
locations.bridge_origin_relative_location().clone().into(),
),
bridge_origin_universal_location: Box::new(
locations.bridge_origin_universal_location().clone().into(),
),
bridge_destination_universal_location: Box::new(
locations.bridge_destination_universal_location().clone().into(),
),
state: BridgeState::Opened,
bridge_owner_account: [0u8; 32].into(),
deposit: 0,
lane_id: expected_lane_id,
},
);
}
let ticket = XcmOverBridge::validate(
BridgedRelayNetwork::get(),
0,
&mut Some(universal_source()),
// Note: The `ExportMessage` expects relative `InteriorLocation` in the
// `BridgedRelayNetwork`.
&mut Some(bridged_relative_destination()),
&mut Some(Vec::new().into()),
)
.unwrap()
.0;
assert_eq!(&ticket.0, expected_bridge_id);
assert_eq!(ticket.1.lane_id, expected_lane_id);
});
}
#[test]
fn exporter_is_compatible_with_pallet_xcm_bridge_hub_router() {
run_test(|| {
// valid routable destination
let dest = Location::new(2, BridgedUniversalDestination::get());
// open bridge
let origin = OpenBridgeOrigin::sibling_teyrchain_origin();
let origin_as_location =
OpenBridgeOriginOf::<TestRuntime, ()>::try_origin(origin.clone()).unwrap();
let (_, expected_lane_id) = open_lane(origin);
// check before - no messages
assert_eq!(
pezpallet_bridge_messages::Pezpallet::<TestRuntime, ()>::outbound_lane_data(
expected_lane_id
)
.unwrap()
.queued_messages()
.saturating_len(),
0
);
// send `ExportMessage(message)` by `UnpaidRemoteExporter`.
ExecuteXcmOverSendXcm::set_origin_for_execute(origin_as_location);
assert_ok!(send_xcm::<
UnpaidRemoteExporter<
NetworkExportTable<BridgeTable>,
ExecuteXcmOverSendXcm,
UniversalLocation,
>,
>(dest.clone(), Xcm::<()>::default()));
// we need to set `UniversalLocation` for `sibling_teyrchain_origin` for
// `XcmOverBridgeWrappedWithExportMessageRouterInstance`.
ExportMessageOriginUniversalLocation::set(Some(SiblingUniversalLocation::get()));
// send `ExportMessage(message)` by `pezpallet_xcm_bridge_hub_router`.
ExecuteXcmOverSendXcm::set_origin_for_execute(SiblingLocation::get());
assert_ok!(send_xcm::<XcmOverBridgeWrappedWithExportMessageRouter>(
dest.clone(),
Xcm::<()>::default()
));
// check after - a message ready to be relayed
assert_eq!(
pezpallet_bridge_messages::Pezpallet::<TestRuntime, ()>::outbound_lane_data(
expected_lane_id
)
.unwrap()
.queued_messages()
.saturating_len(),
2
);
})
}
#[test]
fn validate_works() {
run_test(|| {
let xcm: Xcm<()> = vec![ClearOrigin].into();
// check that router does not consume when `NotApplicable`
let mut xcm_wrapper = Some(xcm.clone());
let mut universal_source_wrapper = Some(universal_source());
// wrong `NetworkId`
let mut dest_wrapper = Some(bridged_relative_destination());
assert_eq!(
XcmOverBridge::validate(
NetworkId::ByGenesis([0; 32]),
0,
&mut universal_source_wrapper,
&mut dest_wrapper,
&mut xcm_wrapper,
),
Err(SendError::NotApplicable),
);
// dest and xcm is NOT consumed and untouched
assert_eq!(&Some(xcm.clone()), &xcm_wrapper);
assert_eq!(&Some(universal_source()), &universal_source_wrapper);
assert_eq!(&Some(bridged_relative_destination()), &dest_wrapper);
// dest starts with wrong `NetworkId`
let mut invalid_dest_wrapper = Some(
[GlobalConsensus(NetworkId::ByGenesis([0; 32])), Teyrchain(BRIDGED_ASSET_HUB_ID)]
.into(),
);
assert_eq!(
XcmOverBridge::validate(
BridgedRelayNetwork::get(),
0,
&mut Some(universal_source()),
&mut invalid_dest_wrapper,
&mut xcm_wrapper,
),
Err(SendError::NotApplicable),
);
// dest and xcm is NOT consumed and untouched
assert_eq!(&Some(xcm.clone()), &xcm_wrapper);
assert_eq!(&Some(universal_source()), &universal_source_wrapper);
assert_eq!(
&Some(
[
GlobalConsensus(NetworkId::ByGenesis([0; 32]),),
Teyrchain(BRIDGED_ASSET_HUB_ID)
]
.into()
),
&invalid_dest_wrapper
);
// no opened lane for dest
let mut dest_without_lane_wrapper =
Some([GlobalConsensus(BridgedRelayNetwork::get()), Teyrchain(5679)].into());
assert_eq!(
XcmOverBridge::validate(
BridgedRelayNetwork::get(),
0,
&mut Some(universal_source()),
&mut dest_without_lane_wrapper,
&mut xcm_wrapper,
),
Err(SendError::NotApplicable),
);
// dest and xcm is NOT consumed and untouched
assert_eq!(&Some(xcm.clone()), &xcm_wrapper);
assert_eq!(&Some(universal_source()), &universal_source_wrapper);
assert_eq!(
&Some([GlobalConsensus(BridgedRelayNetwork::get(),), Teyrchain(5679)].into()),
&dest_without_lane_wrapper
);
// ok
let _ = open_lane(OpenBridgeOrigin::sibling_teyrchain_origin());
let mut dest_wrapper = Some(bridged_relative_destination());
assert_ok!(XcmOverBridge::validate(
BridgedRelayNetwork::get(),
0,
&mut Some(universal_source()),
&mut dest_wrapper,
&mut xcm_wrapper,
));
// dest and xcm IS consumed
assert_eq!(None, xcm_wrapper);
assert_eq!(&Some(universal_source()), &universal_source_wrapper);
assert_eq!(None, dest_wrapper);
});
}
#[test]
fn congestion_with_pallet_xcm_bridge_hub_router_works() {
run_test(|| {
// valid routable destination
let dest = Location::new(2, BridgedUniversalDestination::get());
fn router_bridge_state() -> pezpallet_xcm_bridge_hub_router::BridgeState {
pezpallet_xcm_bridge_hub_router::Bridge::<
TestRuntime,
XcmOverBridgeWrappedWithExportMessageRouterInstance,
>::get()
}
// open two bridges
let origin = OpenBridgeOrigin::sibling_teyrchain_origin();
let origin_as_location =
OpenBridgeOriginOf::<TestRuntime, ()>::try_origin(origin.clone()).unwrap();
let (bridge_1, expected_lane_id_1) = open_lane(origin);
// we need to set `UniversalLocation` for `sibling_teyrchain_origin` for
// `XcmOverBridgeWrappedWithExportMessageRouterInstance`.
ExportMessageOriginUniversalLocation::set(Some(SiblingUniversalLocation::get()));
// check before
// bridges are opened
assert_eq!(
XcmOverBridge::bridge(bridge_1.bridge_id()).unwrap().state,
BridgeState::Opened
);
// the router is uncongested
assert!(!router_bridge_state().is_congested);
assert!(!TestLocalXcmChannelManager::is_bridge_suspended(bridge_1.bridge_id()));
assert!(!TestLocalXcmChannelManager::is_bridge_resumed(bridge_1.bridge_id()));
// make bridges congested with sending too much messages
for _ in 1..(OUTBOUND_LANE_CONGESTED_THRESHOLD + 2) {
// send `ExportMessage(message)` by `pezpallet_xcm_bridge_hub_router`.
ExecuteXcmOverSendXcm::set_origin_for_execute(origin_as_location.clone());
assert_ok!(send_xcm::<XcmOverBridgeWrappedWithExportMessageRouter>(
dest.clone(),
Xcm::<()>::default()
));
}
// checks after
// bridges are suspended
assert_eq!(
XcmOverBridge::bridge(bridge_1.bridge_id()).unwrap().state,
BridgeState::Suspended,
);
// the router is congested
assert!(router_bridge_state().is_congested);
assert!(TestLocalXcmChannelManager::is_bridge_suspended(bridge_1.bridge_id()));
assert!(!TestLocalXcmChannelManager::is_bridge_resumed(bridge_1.bridge_id()));
// make bridges uncongested to trigger resume signal
XcmOverBridge::on_bridge_messages_delivered(
expected_lane_id_1,
OUTBOUND_LANE_UNCONGESTED_THRESHOLD,
);
// bridge is again opened
assert_eq!(
XcmOverBridge::bridge(bridge_1.bridge_id()).unwrap().state,
BridgeState::Opened
);
// the router is uncongested
assert!(!router_bridge_state().is_congested);
assert!(TestLocalXcmChannelManager::is_bridge_resumed(bridge_1.bridge_id()));
})
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,154 @@
// 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/>.
//! A module that is responsible for migration of storage.
use crate::{Config, Pezpallet, LOG_TARGET};
use pezframe_support::{
traits::{Get, OnRuntimeUpgrade, StorageVersion},
weights::Weight,
};
use xcm::prelude::{InteriorLocation, Location};
/// The in-code storage version.
pub const STORAGE_VERSION: StorageVersion = StorageVersion::new(0);
/// This migration does not modify storage but can be used to open a bridge and link it to the
/// specified LaneId. This is useful when we want to open a bridge and use a custom LaneId instead
/// of the pre-calculated one provided by the `fn open_bridge extrinsic`.
/// Or perhaps if you want to ensure that your runtime (e.g., for testing) always has an open
/// bridge.
pub struct OpenBridgeForLane<
T,
I,
Lane,
CreateLane,
SourceRelativeLocation,
BridgedUniversalLocation,
>(
core::marker::PhantomData<(
T,
I,
Lane,
CreateLane,
SourceRelativeLocation,
BridgedUniversalLocation,
)>,
);
impl<
T: Config<I>,
I: 'static,
Lane: Get<T::LaneId>,
CreateLane: Get<bool>,
SourceRelativeLocation: Get<Location>,
BridgedUniversalLocation: Get<InteriorLocation>,
> OnRuntimeUpgrade
for OpenBridgeForLane<T, I, Lane, CreateLane, SourceRelativeLocation, BridgedUniversalLocation>
{
fn on_runtime_upgrade() -> Weight {
let bridge_origin_relative_location = SourceRelativeLocation::get();
let bridge_destination_universal_location = BridgedUniversalLocation::get();
let lane_id = Lane::get();
let create_lane = CreateLane::get();
tracing::info!(
target: LOG_TARGET,
?lane_id,
?create_lane,
?bridge_origin_relative_location,
?bridge_destination_universal_location,
"OpenBridgeForLane - going to open bridge"
);
let locations = match Pezpallet::<T, I>::bridge_locations(
bridge_origin_relative_location.clone(),
bridge_destination_universal_location.clone(),
) {
Ok(locations) => locations,
Err(e) => {
tracing::error!(
target: LOG_TARGET,
error=?e,
"OpenBridgeForLane - on_runtime_upgrade failed to construct bridge_locations"
);
return T::DbWeight::get().reads(0);
},
};
// check if already exists
if let Some((bridge_id, bridge)) = Pezpallet::<T, I>::bridge_by_lane_id(&lane_id) {
tracing::info!(
target: LOG_TARGET,
?bridge,
?bridge_id,
?lane_id,
"OpenBridgeForLane - already exist!"
);
if &bridge_id != locations.bridge_id() {
tracing::warn!(
target: LOG_TARGET,
?bridge,
?bridge_id,
?lane_id,
?bridge_origin_relative_location,
?bridge_destination_universal_location,
"OpenBridgeForLane - check you parameters, because a different bridge exist for requested!"
);
}
return T::DbWeight::get().reads(2);
}
if let Err(e) = Pezpallet::<T, I>::do_open_bridge(locations, lane_id, create_lane) {
tracing::error!(target: LOG_TARGET, error=?e, "OpenBridgeForLane - do_open_bridge failed");
T::DbWeight::get().reads(6)
} else {
tracing::info!(target: LOG_TARGET, "OpenBridgeForLane - do_open_bridge passed!");
T::DbWeight::get().reads_writes(6, 4)
}
}
#[cfg(feature = "try-runtime")]
fn post_upgrade(_state: pezsp_std::vec::Vec<u8>) -> Result<(), pezsp_runtime::DispatchError> {
let bridge_origin_relative_location = SourceRelativeLocation::get();
let bridge_destination_universal_location = BridgedUniversalLocation::get();
let lane_id = Lane::get();
// check that requested bridge is stored
let Ok(locations) = Pezpallet::<T, I>::bridge_locations(
bridge_origin_relative_location.clone(),
bridge_destination_universal_location.clone(),
) else {
return Err(pezsp_runtime::DispatchError::Other("Invalid locations!"));
};
let Some((bridge_id, _)) = Pezpallet::<T, I>::bridge_by_lane_id(&lane_id) else {
return Err(pezsp_runtime::DispatchError::Other("Missing bridge!"));
};
pezframe_support::ensure!(
locations.bridge_id() == &bridge_id,
"Bridge is not stored correctly!"
);
tracing::info!(
target: LOG_TARGET,
?lane_id,
?bridge_origin_relative_location,
?bridge_destination_universal_location,
"OpenBridgeForLane - post_upgrade found opened bridge"
);
Ok(())
}
}
@@ -0,0 +1,669 @@
// Copyright 2019-2021 Parity Technologies (UK) Ltd.
// This file is part of Parity Bridges Common.
// Parity Bridges Common is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity Bridges Common is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
#![cfg(test)]
use crate as pezpallet_xcm_bridge_hub;
use bp_messages::{
target_chain::{DispatchMessage, MessageDispatch},
ChainWithMessages, HashedLaneId, MessageNonce,
};
use pezbp_runtime::{messages::MessageDispatchResult, Chain, ChainId, HashOf};
use bp_xcm_bridge_hub::{BridgeId, LocalXcmChannelManager};
use codec::{Decode, Encode};
use pezframe_support::{
assert_ok, derive_impl, parameter_types,
traits::{EnsureOrigin, Equals, Everything, Get, OriginTrait},
weights::RuntimeDbWeight,
};
use pezkuwi_teyrchain_primitives::primitives::Sibling;
use pezsp_core::H256;
use pezsp_runtime::{
testing::Header as BizinikiwiHeader,
traits::{BlakeTwo256, ConstU128, ConstU32, IdentityLookup},
AccountId32, BuildStorage, StateVersion,
};
use pezsp_std::cell::RefCell;
use xcm::{latest::PEZKUWICHAIN_GENESIS_HASH, prelude::*};
use xcm_builder::{
AllowUnpaidExecutionFrom, DispatchBlob, DispatchBlobError, FixedWeightBounds,
InspectMessageQueues, NetworkExportTable, NetworkExportTableItem, ParentIsPreset,
SiblingTeyrchainConvertsVia,
};
use xcm_executor::{traits::ConvertOrigin, XcmExecutor};
pub type AccountId = AccountId32;
pub type Balance = u64;
type Block = pezframe_system::mocking::MockBlock<TestRuntime>;
/// Lane identifier type used for tests.
pub type TestLaneIdType = HashedLaneId;
pub const SIBLING_ASSET_HUB_ID: u32 = 2001;
pub const THIS_BRIDGE_HUB_ID: u32 = 2002;
pub const BRIDGED_ASSET_HUB_ID: u32 = 1001;
pezframe_support::construct_runtime! {
pub enum TestRuntime {
System: pezframe_system::{Pezpallet, Call, Config<T>, Storage, Event<T>},
Balances: pezpallet_balances::{Pezpallet, Event<T>},
Messages: pezpallet_bridge_messages::{Pezpallet, Call, Event<T>},
XcmOverBridge: pezpallet_xcm_bridge_hub::{Pezpallet, Call, HoldReason, Event<T>},
XcmOverBridgeWrappedWithExportMessageRouter: pezpallet_xcm_bridge_hub_router = 57,
}
}
parameter_types! {
pub const DbWeight: RuntimeDbWeight = RuntimeDbWeight { read: 1, write: 2 };
pub const ExistentialDeposit: Balance = 1;
}
#[derive_impl(pezframe_system::config_preludes::TestDefaultConfig)]
impl pezframe_system::Config for TestRuntime {
type AccountId = AccountId;
type AccountData = pezpallet_balances::AccountData<Balance>;
type Block = Block;
type Lookup = IdentityLookup<Self::AccountId>;
}
#[derive_impl(pezpallet_balances::config_preludes::TestDefaultConfig)]
impl pezpallet_balances::Config for TestRuntime {
type AccountStore = System;
}
impl pezpallet_bridge_messages::Config for TestRuntime {
type RuntimeEvent = RuntimeEvent;
type WeightInfo = TestMessagesWeights;
type ThisChain = ThisUnderlyingChain;
type BridgedChain = BridgedUnderlyingChain;
type BridgedHeaderChain = BridgedHeaderChain;
type OutboundPayload = Vec<u8>;
type InboundPayload = Vec<u8>;
type LaneId = TestLaneIdType;
type DeliveryPayments = ();
type DeliveryConfirmationPayments = ();
type OnMessagesDelivered = ();
type MessageDispatch = TestMessageDispatch;
}
pub struct TestMessagesWeights;
impl pezpallet_bridge_messages::WeightInfo for TestMessagesWeights {
fn receive_single_message_proof() -> Weight {
Weight::zero()
}
fn receive_n_messages_proof(_: u32) -> Weight {
Weight::zero()
}
fn receive_single_message_proof_with_outbound_lane_state() -> Weight {
Weight::zero()
}
fn receive_single_n_bytes_message_proof(_: u32) -> Weight {
Weight::zero()
}
fn receive_delivery_proof_for_single_message() -> Weight {
Weight::zero()
}
fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight {
Weight::zero()
}
fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight {
Weight::zero()
}
fn receive_single_n_bytes_message_proof_with_dispatch(_n: u32) -> Weight {
Weight::from_parts(1, 0)
}
}
impl pezpallet_bridge_messages::WeightInfoExt for TestMessagesWeights {
fn expected_extra_storage_proof_size() -> u32 {
0
}
fn receive_messages_proof_overhead_from_runtime() -> Weight {
Weight::zero()
}
fn receive_messages_delivery_proof_overhead_from_runtime() -> Weight {
Weight::zero()
}
}
parameter_types! {
pub const RelayNetwork: NetworkId = NetworkId::Kusama;
pub UniversalLocation: InteriorLocation = [
GlobalConsensus(RelayNetwork::get()),
Teyrchain(THIS_BRIDGE_HUB_ID),
].into();
pub SiblingLocation: Location = Location::new(1, [Teyrchain(SIBLING_ASSET_HUB_ID)]);
pub SiblingUniversalLocation: InteriorLocation = [GlobalConsensus(RelayNetwork::get()), Teyrchain(SIBLING_ASSET_HUB_ID)].into();
pub const BridgedRelayNetwork: NetworkId = NetworkId::ByGenesis([1; 32]);
pub BridgedRelayNetworkLocation: Location = (Parent, GlobalConsensus(BridgedRelayNetwork::get())).into();
pub BridgedRelativeDestination: InteriorLocation = [Teyrchain(BRIDGED_ASSET_HUB_ID)].into();
pub BridgedUniversalDestination: InteriorLocation = [GlobalConsensus(BridgedRelayNetwork::get()), Teyrchain(BRIDGED_ASSET_HUB_ID)].into();
pub const NonBridgedRelayNetwork: NetworkId = NetworkId::ByGenesis(PEZKUWICHAIN_GENESIS_HASH);
pub const BridgeDeposit: Balance = 100_000;
// configuration for pezpallet_xcm_bridge_hub_router
pub BridgeHubLocation: Location = Here.into();
pub BridgeFeeAsset: AssetId = Location::here().into();
pub BridgeTable: Vec<NetworkExportTableItem>
= vec![
NetworkExportTableItem::new(
BridgedRelayNetwork::get(),
None,
BridgeHubLocation::get(),
None
)
];
pub UnitWeightCost: Weight = Weight::from_parts(10, 10);
}
/// **Universal** `InteriorLocation` of bridged asset hub.
pub fn bridged_asset_hub_universal_location() -> InteriorLocation {
BridgedUniversalDestination::get()
}
impl pezpallet_xcm_bridge_hub::Config for TestRuntime {
type RuntimeEvent = RuntimeEvent;
type UniversalLocation = UniversalLocation;
type BridgedNetwork = BridgedRelayNetworkLocation;
type BridgeMessagesPalletInstance = ();
type MessageExportPrice = ();
type DestinationVersion = AlwaysLatest;
type ForceOrigin = pezframe_system::EnsureNever<()>;
type OpenBridgeOrigin = OpenBridgeOrigin;
type BridgeOriginAccountIdConverter = LocationToAccountId;
type BridgeDeposit = BridgeDeposit;
type Currency = Balances;
type RuntimeHoldReason = RuntimeHoldReason;
type AllowWithoutBridgeDeposit = Equals<ParentRelayChainLocation>;
type LocalXcmChannelManager = TestLocalXcmChannelManager;
type BlobDispatcher = TestBlobDispatcher;
}
/// A router instance simulates a scenario where the router is deployed on a different chain than
/// the `MessageExporter`. This means that the router sends an `ExportMessage`.
pub type XcmOverBridgeWrappedWithExportMessageRouterInstance = ();
impl pezpallet_xcm_bridge_hub_router::Config<XcmOverBridgeWrappedWithExportMessageRouterInstance>
for TestRuntime
{
type RuntimeEvent = RuntimeEvent;
type WeightInfo = ();
type UniversalLocation = ExportMessageOriginUniversalLocation;
type SiblingBridgeHubLocation = BridgeHubLocation;
type BridgedNetworkId = BridgedRelayNetwork;
type Bridges = NetworkExportTable<BridgeTable>;
type DestinationVersion = AlwaysLatest;
// We convert to root `here` location with `BridgeHubLocationXcmOriginAsRoot`
type BridgeHubOrigin = pezframe_system::EnsureRoot<AccountId>;
// **Note**: The crucial part is that `ExportMessage` is processed by `XcmExecutor`, which
// calls the `ExportXcm` implementation of `pezpallet_xcm_bridge_hub` as the
// `MessageExporter`.
type ToBridgeHubSender = ExecuteXcmOverSendXcm;
type LocalXcmChannelManager = TestLocalXcmChannelManager;
type ByteFee = ConstU128<0>;
type FeeAsset = BridgeFeeAsset;
}
pub struct XcmConfig;
impl xcm_executor::Config for XcmConfig {
type RuntimeCall = RuntimeCall;
type XcmSender = ();
type XcmEventEmitter = ();
type AssetTransactor = ();
type OriginConverter = BridgeHubLocationXcmOriginAsRoot<RuntimeOrigin>;
type IsReserve = ();
type IsTeleporter = ();
type UniversalLocation = UniversalLocation;
type Barrier = AllowUnpaidExecutionFrom<Everything>;
type Weigher = FixedWeightBounds<UnitWeightCost, RuntimeCall, ConstU32<100>>;
type Trader = ();
type ResponseHandler = ();
type AssetTrap = ();
type AssetClaims = ();
type SubscriptionService = ();
type PalletInstancesInfo = ();
type MaxAssetsIntoHolding = ();
type AssetLocker = ();
type AssetExchanger = ();
type FeeManager = ();
// We just set `MessageExporter` as our `pezpallet_xcm_bridge_hub` instance.
type MessageExporter = (XcmOverBridge,);
type UniversalAliases = ();
type CallDispatcher = RuntimeCall;
type SafeCallFilter = Everything;
type Aliasers = ();
type TransactionalProcessor = ();
type HrmpNewChannelOpenRequestHandler = ();
type HrmpChannelAcceptedHandler = ();
type HrmpChannelClosingHandler = ();
type XcmRecorder = ();
}
thread_local! {
pub static EXECUTE_XCM_ORIGIN: RefCell<Option<Location>> = RefCell::new(None);
}
/// The `SendXcm` implementation directly executes XCM using `XcmExecutor`.
///
/// We ensure that the `ExportMessage` produced by `pezpallet_xcm_bridge_hub_router` is compatible with
/// the `ExportXcm` implementation of `pezpallet_xcm_bridge_hub`.
///
/// Note: The crucial part is that `ExportMessage` is processed by `XcmExecutor`, which calls the
/// `ExportXcm` implementation of `pezpallet_xcm_bridge_hub` as `MessageExporter`.
pub struct ExecuteXcmOverSendXcm;
impl SendXcm for ExecuteXcmOverSendXcm {
type Ticket = Xcm<()>;
fn validate(
_: &mut Option<Location>,
message: &mut Option<Xcm<()>>,
) -> SendResult<Self::Ticket> {
Ok((message.take().unwrap(), Assets::new()))
}
fn deliver(ticket: Self::Ticket) -> Result<XcmHash, SendError> {
let xcm: Xcm<RuntimeCall> = ticket.into();
let origin = EXECUTE_XCM_ORIGIN.with(|o| o.borrow().clone().unwrap());
let mut hash = xcm.using_encoded(pezsp_io::hashing::blake2_256);
let outcome = XcmExecutor::<XcmConfig>::prepare_and_execute(
origin,
xcm,
&mut hash,
Weight::MAX,
Weight::zero(),
);
assert_ok!(outcome.ensure_complete());
Ok(hash)
}
}
impl InspectMessageQueues for ExecuteXcmOverSendXcm {
fn clear_messages() {
todo!()
}
fn get_messages() -> Vec<(VersionedLocation, Vec<VersionedXcm<()>>)> {
todo!()
}
}
impl ExecuteXcmOverSendXcm {
pub fn set_origin_for_execute(origin: Location) {
EXECUTE_XCM_ORIGIN.with(|o| *o.borrow_mut() = Some(origin));
}
}
/// A dynamic way to set different universal location for the origin which sends `ExportMessage`.
pub struct ExportMessageOriginUniversalLocation;
impl ExportMessageOriginUniversalLocation {
pub(crate) fn set(universal_location: Option<InteriorLocation>) {
EXPORT_MESSAGE_ORIGIN_UNIVERSAL_LOCATION.with(|o| *o.borrow_mut() = universal_location);
}
}
impl Get<InteriorLocation> for ExportMessageOriginUniversalLocation {
fn get() -> InteriorLocation {
EXPORT_MESSAGE_ORIGIN_UNIVERSAL_LOCATION.with(|o| {
o.borrow()
.clone()
.expect("`EXPORT_MESSAGE_ORIGIN_UNIVERSAL_LOCATION` is not set!")
})
}
}
thread_local! {
pub static EXPORT_MESSAGE_ORIGIN_UNIVERSAL_LOCATION: RefCell<Option<InteriorLocation>> = RefCell::new(None);
}
pub struct BridgeHubLocationXcmOriginAsRoot<RuntimeOrigin>(
pezsp_std::marker::PhantomData<RuntimeOrigin>,
);
impl<RuntimeOrigin: OriginTrait> ConvertOrigin<RuntimeOrigin>
for BridgeHubLocationXcmOriginAsRoot<RuntimeOrigin>
{
fn convert_origin(
origin: impl Into<Location>,
kind: OriginKind,
) -> Result<RuntimeOrigin, Location> {
let origin = origin.into();
if kind == OriginKind::Xcm && origin.eq(&BridgeHubLocation::get()) {
Ok(RuntimeOrigin::root())
} else {
Err(origin)
}
}
}
/// Type for specifying how a `Location` can be converted into an `AccountId`. This is used
/// when determining ownership of accounts for asset transacting and when attempting to use XCM
/// `Transact` in order to determine the dispatch Origin.
pub type LocationToAccountId = (
// The parent (Relay-chain) origin converts to the parent `AccountId`.
ParentIsPreset<AccountId>,
// Sibling teyrchain origins convert to AccountId via the `ParaId::into`.
SiblingTeyrchainConvertsVia<Sibling, AccountId>,
);
parameter_types! {
pub ParentRelayChainLocation: Location = Location { parents: 1, interior: Here };
}
pub struct OpenBridgeOrigin;
impl OpenBridgeOrigin {
pub fn parent_relay_chain_origin() -> RuntimeOrigin {
RuntimeOrigin::signed([0u8; 32].into())
}
pub fn parent_relay_chain_universal_origin() -> RuntimeOrigin {
RuntimeOrigin::signed([1u8; 32].into())
}
pub fn sibling_teyrchain_origin() -> RuntimeOrigin {
let mut account = [0u8; 32];
account[..4].copy_from_slice(&SIBLING_ASSET_HUB_ID.encode()[..4]);
RuntimeOrigin::signed(account.into())
}
pub fn sibling_teyrchain_universal_origin() -> RuntimeOrigin {
RuntimeOrigin::signed([2u8; 32].into())
}
pub fn origin_without_sovereign_account() -> RuntimeOrigin {
RuntimeOrigin::signed([3u8; 32].into())
}
pub fn disallowed_origin() -> RuntimeOrigin {
RuntimeOrigin::signed([42u8; 32].into())
}
}
impl EnsureOrigin<RuntimeOrigin> for OpenBridgeOrigin {
type Success = Location;
fn try_origin(o: RuntimeOrigin) -> Result<Self::Success, RuntimeOrigin> {
let signer = o.clone().into_signer();
if signer == Self::parent_relay_chain_origin().into_signer() {
return Ok(ParentRelayChainLocation::get());
} else if signer == Self::parent_relay_chain_universal_origin().into_signer() {
return Ok(Location {
parents: 2,
interior: GlobalConsensus(RelayNetwork::get()).into(),
});
} else if signer == Self::sibling_teyrchain_universal_origin().into_signer() {
return Ok(Location {
parents: 2,
interior: [GlobalConsensus(RelayNetwork::get()), Teyrchain(SIBLING_ASSET_HUB_ID)]
.into(),
});
} else if signer == Self::origin_without_sovereign_account().into_signer() {
return Ok(Location {
parents: 1,
interior: [Teyrchain(SIBLING_ASSET_HUB_ID), OnlyChild].into(),
});
}
let mut sibling_account = [0u8; 32];
sibling_account[..4].copy_from_slice(&SIBLING_ASSET_HUB_ID.encode()[..4]);
if signer == Some(sibling_account.into()) {
return Ok(Location { parents: 1, interior: Teyrchain(SIBLING_ASSET_HUB_ID).into() });
}
Err(o)
}
#[cfg(feature = "runtime-benchmarks")]
fn try_successful_origin() -> Result<RuntimeOrigin, ()> {
Ok(Self::parent_relay_chain_origin())
}
}
pub(crate) type OpenBridgeOriginOf<T, I> =
<T as pezpallet_xcm_bridge_hub::Config<I>>::OpenBridgeOrigin;
pub struct TestLocalXcmChannelManager;
impl TestLocalXcmChannelManager {
pub fn make_congested() {
pezframe_support::storage::unhashed::put(b"TestLocalXcmChannelManager.Congested", &true);
}
fn suspended_key(bridge: &BridgeId) -> Vec<u8> {
[b"TestLocalXcmChannelManager.Suspended", bridge.encode().as_slice()].concat()
}
fn resumed_key(bridge: &BridgeId) -> Vec<u8> {
[b"TestLocalXcmChannelManager.Resumed", bridge.encode().as_slice()].concat()
}
pub fn is_bridge_suspended(bridge: &BridgeId) -> bool {
pezframe_support::storage::unhashed::get_or_default(&Self::suspended_key(bridge))
}
pub fn is_bridge_resumed(bridge: &BridgeId) -> bool {
pezframe_support::storage::unhashed::get_or_default(&Self::resumed_key(bridge))
}
fn build_congestion_message(bridge: &BridgeId, is_congested: bool) -> Vec<Instruction<()>> {
use bp_xcm_bridge_hub_router::XcmBridgeHubRouterCall;
#[allow(clippy::large_enum_variant)]
#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, scale_info::TypeInfo)]
enum Call {
#[codec(index = 57)]
XcmOverBridgeWrappedWithExportMessageRouter(XcmBridgeHubRouterCall),
}
pezsp_std::vec![
UnpaidExecution { weight_limit: Unlimited, check_origin: None },
Transact {
origin_kind: OriginKind::Xcm,
fallback_max_weight: None,
call: Call::XcmOverBridgeWrappedWithExportMessageRouter(
XcmBridgeHubRouterCall::report_bridge_status {
bridge_id: bridge.inner(),
is_congested,
}
)
.encode()
.into(),
},
ExpectTransactStatus(MaybeErrorCode::Success),
]
}
fn report_bridge_status(
local_origin: &Location,
bridge: &BridgeId,
is_congested: bool,
key: Vec<u8>,
) -> Result<(), SendError> {
// send as BridgeHub would send to sibling chain
ExecuteXcmOverSendXcm::set_origin_for_execute(BridgeHubLocation::get());
let result = send_xcm::<ExecuteXcmOverSendXcm>(
local_origin.clone(),
Self::build_congestion_message(&bridge, is_congested).into(),
);
if result.is_ok() {
pezframe_support::storage::unhashed::put(&key, &true);
}
result.map(|_| ())
}
}
impl LocalXcmChannelManager for TestLocalXcmChannelManager {
type Error = SendError;
fn is_congested(_with: &Location) -> bool {
pezframe_support::storage::unhashed::get_or_default(b"TestLocalXcmChannelManager.Congested")
}
fn suspend_bridge(local_origin: &Location, bridge: BridgeId) -> Result<(), Self::Error> {
Self::report_bridge_status(local_origin, &bridge, true, Self::suspended_key(&bridge))
}
fn resume_bridge(local_origin: &Location, bridge: BridgeId) -> Result<(), Self::Error> {
Self::report_bridge_status(local_origin, &bridge, false, Self::resumed_key(&bridge))
}
}
impl pezpallet_xcm_bridge_hub_router::XcmChannelStatusProvider for TestLocalXcmChannelManager {
fn is_congested(with: &Location) -> bool {
<Self as LocalXcmChannelManager>::is_congested(with)
}
}
pub struct TestBlobDispatcher;
impl TestBlobDispatcher {
pub fn is_dispatched() -> bool {
pezframe_support::storage::unhashed::get_or_default(b"TestBlobDispatcher.Dispatched")
}
}
impl DispatchBlob for TestBlobDispatcher {
fn dispatch_blob(_blob: Vec<u8>) -> Result<(), DispatchBlobError> {
pezframe_support::storage::unhashed::put(b"TestBlobDispatcher.Dispatched", &true);
Ok(())
}
}
pub struct ThisUnderlyingChain;
impl Chain for ThisUnderlyingChain {
const ID: ChainId = *b"tuch";
type BlockNumber = u64;
type Hash = H256;
type Hasher = BlakeTwo256;
type Header = BizinikiwiHeader;
type AccountId = AccountId;
type Balance = Balance;
type Nonce = u64;
type Signature = pezsp_runtime::MultiSignature;
const STATE_VERSION: StateVersion = StateVersion::V1;
fn max_extrinsic_size() -> u32 {
u32::MAX
}
fn max_extrinsic_weight() -> Weight {
Weight::MAX
}
}
impl ChainWithMessages for ThisUnderlyingChain {
const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = "WithThisChainBridgeMessages";
const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 16;
const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 128;
}
pub type BridgedHeaderHash = H256;
pub type BridgedChainHeader = BizinikiwiHeader;
pub struct BridgedUnderlyingChain;
impl Chain for BridgedUnderlyingChain {
const ID: ChainId = *b"bgdc";
type BlockNumber = u64;
type Hash = BridgedHeaderHash;
type Hasher = BlakeTwo256;
type Header = BridgedChainHeader;
type AccountId = AccountId;
type Balance = Balance;
type Nonce = u64;
type Signature = pezsp_runtime::MultiSignature;
const STATE_VERSION: StateVersion = StateVersion::V1;
fn max_extrinsic_size() -> u32 {
4096
}
fn max_extrinsic_weight() -> Weight {
Weight::MAX
}
}
impl ChainWithMessages for BridgedUnderlyingChain {
const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = "WithBridgedChainBridgeMessages";
const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 16;
const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 128;
}
pub struct BridgedHeaderChain;
impl bp_header_pez_chain::HeaderChain<BridgedUnderlyingChain> for BridgedHeaderChain {
fn finalized_header_state_root(
_hash: HashOf<BridgedUnderlyingChain>,
) -> Option<HashOf<BridgedUnderlyingChain>> {
unreachable!()
}
}
/// Test message dispatcher.
pub struct TestMessageDispatch;
impl TestMessageDispatch {
pub fn deactivate(lane: TestLaneIdType) {
pezframe_support::storage::unhashed::put(&(b"inactive", lane).encode()[..], &false);
}
}
impl MessageDispatch for TestMessageDispatch {
type DispatchPayload = Vec<u8>;
type DispatchLevelResult = ();
type LaneId = TestLaneIdType;
fn is_active(lane: Self::LaneId) -> bool {
pezframe_support::storage::unhashed::take::<bool>(&(b"inactive", lane).encode()[..]) !=
Some(false)
}
fn dispatch_weight(
_message: &mut DispatchMessage<Self::DispatchPayload, Self::LaneId>,
) -> Weight {
Weight::zero()
}
fn dispatch(
_: DispatchMessage<Self::DispatchPayload, Self::LaneId>,
) -> MessageDispatchResult<Self::DispatchLevelResult> {
MessageDispatchResult { unspent_weight: Weight::zero(), dispatch_level_result: () }
}
}
/// Run pezpallet test.
pub fn run_test<T>(test: impl FnOnce() -> T) -> T {
pezsp_io::TestExternalities::new(
pezframe_system::GenesisConfig::<TestRuntime>::default().build_storage().unwrap(),
)
.execute_with(test)
}