feat: Rebrand Polkadot/Substrate references to PezkuwiChain

This commit systematically rebrands various references from Parity Technologies'
Polkadot/Substrate ecosystem to PezkuwiChain within the kurdistan-sdk.

Key changes include:
- Updated external repository URLs (zombienet-sdk, parity-db, parity-scale-codec, wasm-instrument) to point to pezkuwichain forks.
- Modified internal documentation and code comments to reflect PezkuwiChain naming and structure.
- Replaced direct references to  with  or specific paths within the  for XCM, Pezkuwi, and other modules.
- Cleaned up deprecated  issue and PR references in various  and  files, particularly in  and  modules.
- Adjusted image and logo URLs in documentation to point to PezkuwiChain assets.
- Removed or rephrased comments related to external Polkadot/Substrate PRs and issues.

This is a significant step towards fully customizing the SDK for the PezkuwiChain ecosystem.
This commit is contained in:
2025-12-14 00:04:10 +03:00
parent 286de54384
commit 1c0e57d984
9084 changed files with 997839 additions and 997557 deletions
@@ -0,0 +1,125 @@
[package]
name = "bridge-hub-test-utils"
version = "0.7.0"
authors.workspace = true
edition.workspace = true
description = "Utils for BridgeHub testing"
license = "Apache-2.0"
homepage.workspace = true
repository.workspace = true
[lints]
workspace = true
[dependencies]
codec = { features = ["derive", "max-encoded-len"], workspace = true }
impl-trait-for-tuples = { workspace = true }
tracing = { workspace = true }
# Bizinikiwi
pezframe-support = { workspace = true }
pezframe-system = { workspace = true }
pezpallet-balances = { workspace = true }
pezpallet-timestamp = { workspace = true }
pezpallet-utility = { workspace = true }
pezsp-core = { workspace = true }
pezsp-io = { workspace = true }
pezsp-keyring = { workspace = true, default-features = true }
pezsp-runtime = { workspace = true }
pezsp-std = { workspace = true }
pezsp-tracing = { workspace = true, default-features = true }
# Pezcumulus
asset-test-utils = { workspace = true, default-features = true }
pezcumulus-pezpallet-teyrchain-system = { workspace = true }
pezcumulus-pezpallet-xcmp-queue = { workspace = true }
teyrchains-common = { workspace = true }
teyrchains-runtimes-test-utils = { workspace = true }
# Pezkuwi
pezpallet-xcm = { workspace = true }
xcm = { workspace = true }
xcm-builder = { workspace = true }
xcm-executor = { workspace = true }
# Bridges
bp-header-chain = { workspace = true }
bp-messages = { workspace = true }
bp-pezkuwi-core = { workspace = true }
bp-relayers = { workspace = true }
bp-runtime = { workspace = true }
bp-test-utils = { workspace = true }
bp-teyrchains = { workspace = true }
pezpallet-bridge-grandpa = { workspace = true }
pezpallet-bridge-messages = { features = ["test-helpers"], workspace = true }
pezpallet-bridge-relayers = { workspace = true }
pezpallet-bridge-teyrchains = { workspace = true }
pezpallet-xcm-bridge-hub = { workspace = true }
[features]
default = ["std"]
std = [
"asset-test-utils/std",
"bp-header-chain/std",
"bp-messages/std",
"bp-pezkuwi-core/std",
"bp-relayers/std",
"bp-runtime/std",
"bp-test-utils/std",
"bp-teyrchains/std",
"codec/std",
"pezcumulus-pezpallet-teyrchain-system/std",
"pezcumulus-pezpallet-xcmp-queue/std",
"pezframe-support/std",
"pezframe-system/std",
"pezpallet-balances/std",
"pezpallet-bridge-grandpa/std",
"pezpallet-bridge-messages/std",
"pezpallet-bridge-relayers/std",
"pezpallet-bridge-teyrchains/std",
"pezpallet-timestamp/std",
"pezpallet-utility/std",
"pezpallet-xcm-bridge-hub/std",
"pezpallet-xcm/std",
"pezsp-core/std",
"pezsp-io/std",
"pezsp-runtime/std",
"pezsp-std/std",
"teyrchains-common/std",
"teyrchains-runtimes-test-utils/std",
"tracing/std",
"xcm-builder/std",
"xcm-executor/std",
"xcm/std",
]
runtime-benchmarks = [
"asset-test-utils/runtime-benchmarks",
"bp-header-chain/runtime-benchmarks",
"bp-messages/runtime-benchmarks",
"bp-pezkuwi-core/runtime-benchmarks",
"bp-relayers/runtime-benchmarks",
"bp-runtime/runtime-benchmarks",
"bp-test-utils/runtime-benchmarks",
"bp-teyrchains/runtime-benchmarks",
"pezcumulus-pezpallet-teyrchain-system/runtime-benchmarks",
"pezcumulus-pezpallet-xcmp-queue/runtime-benchmarks",
"pezframe-support/runtime-benchmarks",
"pezframe-system/runtime-benchmarks",
"pezpallet-balances/runtime-benchmarks",
"pezpallet-bridge-grandpa/runtime-benchmarks",
"pezpallet-bridge-messages/runtime-benchmarks",
"pezpallet-bridge-relayers/runtime-benchmarks",
"pezpallet-bridge-teyrchains/runtime-benchmarks",
"pezpallet-timestamp/runtime-benchmarks",
"pezpallet-utility/runtime-benchmarks",
"pezpallet-xcm-bridge-hub/runtime-benchmarks",
"pezpallet-xcm/runtime-benchmarks",
"pezsp-io/runtime-benchmarks",
"pezsp-keyring/runtime-benchmarks",
"pezsp-runtime/runtime-benchmarks",
"teyrchains-common/runtime-benchmarks",
"teyrchains-runtimes-test-utils/runtime-benchmarks",
"xcm-builder/runtime-benchmarks",
"xcm-executor/runtime-benchmarks",
"xcm/runtime-benchmarks",
]
@@ -0,0 +1,86 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Pezcumulus.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Module contains predefined test-case scenarios for "BridgeHub" `Runtime`s.
pub mod test_cases;
pub mod test_data;
extern crate alloc;
pub use bp_test_utils::test_header;
use pezsp_runtime::Perbill;
pub use test_cases::helpers::for_pallet_xcm_bridge_hub::{
ensure_opened_bridge, open_bridge_with_extrinsic, open_bridge_with_storage,
};
pub use teyrchains_runtimes_test_utils::*;
/// A helper function for comparing the actual value of a fee constant with its estimated value. The
/// estimated value can be overestimated (`overestimate_in_percent`), and if the difference to the
/// actual value is below `margin_overestimate_diff_in_percent_for_lowering`, we should lower the
/// actual value.
pub fn check_sane_fees_values(
const_name: &str,
actual: u128,
calculate_estimated_fee: fn() -> u128,
overestimate_in_percent: Perbill,
margin_overestimate_diff_in_percent_for_lowering: Option<i16>,
label: &str,
) {
let estimated = calculate_estimated_fee();
let estimated_plus_overestimate = estimated + (overestimate_in_percent * estimated);
let diff_to_estimated = diff_as_percent(actual, estimated);
let diff_to_estimated_plus_overestimate = diff_as_percent(actual, estimated_plus_overestimate);
pezsp_tracing::try_init_simple();
tracing::error!(
target: "bridges::estimate",
%label, constant=%const_name, %actual, %estimated,
"{diff_to_estimated:.2?})\n[+] estimated(+33%): {estimated_plus_overestimate} ({diff_to_estimated_plus_overestimate:.2?}"
);
// check if estimated value is sane
assert!(
estimated <= actual,
"estimated: {estimated}, actual: {actual}, please adjust `{const_name}` to the value: {estimated_plus_overestimate}",
);
assert!(
estimated_plus_overestimate <= actual,
"estimated_plus_overestimate: {estimated_plus_overestimate}, actual: {actual}, please adjust `{const_name}` to the value: {estimated_plus_overestimate}",
);
if let Some(margin_overestimate_diff_in_percent_for_lowering) =
margin_overestimate_diff_in_percent_for_lowering
{
assert!(
diff_to_estimated_plus_overestimate > margin_overestimate_diff_in_percent_for_lowering as f64,
"diff_to_estimated_plus_overestimate: {diff_to_estimated_plus_overestimate:.2}, overestimate_diff_in_percent_for_lowering: {margin_overestimate_diff_in_percent_for_lowering}, please adjust `{const_name}` to the value: {estimated_plus_overestimate}",
);
}
}
pub fn diff_as_percent(left: u128, right: u128) -> f64 {
let left = left as f64;
let right = right as f64;
((left - right).abs() / left) * 100f64 * (if left >= right { -1 } else { 1 }) as f64
}
#[test]
fn diff_as_percent_works() {
assert_eq!(-20_f64, diff_as_percent(100, 80));
assert_eq!(25_f64, diff_as_percent(80, 100));
assert_eq!(33_f64, diff_as_percent(13351000000, 17756830000));
}
@@ -0,0 +1,654 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Pezcumulus.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Module contains predefined test-case scenarios for `Runtime` with bridging capabilities
//! with remote GRANDPA chain.
use crate::{
test_cases::{bridges_prelude::*, helpers, run_test},
test_data,
test_data::XcmAsPlainPayload,
};
use alloc::{boxed::Box, vec};
use bp_header_chain::ChainWithGrandpa;
use bp_messages::UnrewardedRelayersState;
use bp_relayers::{RewardsAccountOwner, RewardsAccountParams};
use pezframe_support::traits::{OnFinalize, OnInitialize};
use pezframe_system::pezpallet_prelude::BlockNumberFor;
use pezpallet_bridge_messages::{BridgedChainOf, LaneIdOf, ThisChainOf};
use pezsp_core::Get;
use pezsp_keyring::Sr25519Keyring::*;
use pezsp_runtime::{traits::Header as HeaderT, AccountId32};
use teyrchains_runtimes_test_utils::{
AccountIdOf, BasicTeyrchainRuntime, CollatorSessionKeys, RuntimeCallOf, SlotDurations,
};
use xcm::latest::prelude::*;
/// Helper trait to test bridges with remote GRANDPA chain.
///
/// This is only used to decrease amount of lines, dedicated to bounds.
pub trait WithRemoteGrandpaChainHelper {
/// This chain runtime.
type Runtime: BasicTeyrchainRuntime
+ cumulus_pallet_xcmp_queue::Config
+ BridgeGrandpaConfig<Self::GPI, BridgedChain = BridgedChainOf<Self::Runtime, Self::MPI>>
+ BridgeMessagesConfig<
Self::MPI,
InboundPayload = XcmAsPlainPayload,
OutboundPayload = XcmAsPlainPayload,
> + pezpallet_bridge_relayers::Config<Self::RPI, Reward = Self::RelayerReward>;
/// All pallets of this chain, excluding system pallet.
type AllPalletsWithoutSystem: OnInitialize<BlockNumberFor<Self::Runtime>>
+ OnFinalize<BlockNumberFor<Self::Runtime>>;
/// Instance of the `pezpallet-bridge-grandpa`, used to bridge with remote GRANDPA chain.
type GPI: 'static;
/// Instance of the `pezpallet-bridge-messages`, used to bridge with remote GRANDPA chain.
type MPI: 'static;
/// Instance of the `pezpallet-bridge-relayers`, used to collect rewards from messages `MPI`
/// instance.
type RPI: 'static;
/// Relayer reward type.
type RelayerReward: From<RewardsAccountParams<LaneIdOf<Self::Runtime, Self::MPI>>>;
}
/// Adapter struct that implements [`WithRemoteGrandpaChainHelper`].
pub struct WithRemoteGrandpaChainHelperAdapter<Runtime, AllPalletsWithoutSystem, GPI, MPI, RPI>(
core::marker::PhantomData<(Runtime, AllPalletsWithoutSystem, GPI, MPI, RPI)>,
);
impl<Runtime, AllPalletsWithoutSystem, GPI, MPI, RPI> WithRemoteGrandpaChainHelper
for WithRemoteGrandpaChainHelperAdapter<Runtime, AllPalletsWithoutSystem, GPI, MPI, RPI>
where
Runtime: BasicTeyrchainRuntime
+ cumulus_pallet_xcmp_queue::Config
+ BridgeGrandpaConfig<GPI, BridgedChain = BridgedChainOf<Runtime, MPI>>
+ BridgeMessagesConfig<
MPI,
InboundPayload = XcmAsPlainPayload,
OutboundPayload = XcmAsPlainPayload,
> + pezpallet_bridge_relayers::Config<RPI>,
AllPalletsWithoutSystem:
OnInitialize<BlockNumberFor<Runtime>> + OnFinalize<BlockNumberFor<Runtime>>,
<Runtime as pezpallet_bridge_relayers::Config<RPI>>::Reward:
From<RewardsAccountParams<LaneIdOf<Runtime, MPI>>>,
GPI: 'static,
MPI: 'static,
RPI: 'static,
{
type Runtime = Runtime;
type AllPalletsWithoutSystem = AllPalletsWithoutSystem;
type GPI = GPI;
type MPI = MPI;
type RPI = RPI;
type RelayerReward = Runtime::Reward;
}
/// Test-case makes sure that Runtime can dispatch XCM messages submitted by relayer,
/// with proofs (finality, message) independently submitted.
/// Also verifies relayer transaction signed extensions work as intended.
pub fn relayed_incoming_message_works<RuntimeHelper>(
collator_session_key: CollatorSessionKeys<RuntimeHelper::Runtime>,
slot_durations: SlotDurations,
runtime_para_id: u32,
sibling_teyrchain_id: u32,
local_relay_chain_id: NetworkId,
prepare_configuration: impl Fn() -> LaneIdOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
construct_and_apply_extrinsic: fn(
pezsp_keyring::Sr25519Keyring,
RuntimeCallOf<RuntimeHelper::Runtime>,
) -> pezsp_runtime::DispatchOutcome,
expect_rewards: bool,
) where
RuntimeHelper: WithRemoteGrandpaChainHelper,
AccountIdOf<RuntimeHelper::Runtime>: From<AccountId32>,
RuntimeCallOf<RuntimeHelper::Runtime>: From<BridgeGrandpaCall<RuntimeHelper::Runtime, RuntimeHelper::GPI>>
+ From<BridgeMessagesCall<RuntimeHelper::Runtime, RuntimeHelper::MPI>>,
BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>: ChainWithGrandpa,
{
helpers::relayed_incoming_message_works::<
RuntimeHelper::Runtime,
RuntimeHelper::AllPalletsWithoutSystem,
RuntimeHelper::MPI,
>(
collator_session_key,
slot_durations,
runtime_para_id,
sibling_teyrchain_id,
local_relay_chain_id,
construct_and_apply_extrinsic,
|relayer_id_at_this_chain,
relayer_id_at_bridged_chain,
message_destination,
message_nonce,
xcm,
bridged_chain_id| {
let relay_header_number = 5u32.into();
let lane_id = prepare_configuration();
// start with bridged relay chain block#0
helpers::initialize_bridge_grandpa_pallet::<RuntimeHelper::Runtime, RuntimeHelper::GPI>(
test_data::initialization_data::<RuntimeHelper::Runtime, RuntimeHelper::GPI>(0),
);
// generate bridged relay chain finality, teyrchain heads and message proofs,
// to be submitted by relayer to this chain.
let (relay_chain_header, grandpa_justification, message_proof) =
test_data::from_grandpa_chain::make_complex_relayer_delivery_proofs::<
BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
ThisChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
LaneIdOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
>(
lane_id,
xcm.into(),
message_nonce,
message_destination,
relay_header_number,
false,
);
let relay_chain_header_hash = relay_chain_header.hash();
vec![
(
BridgeGrandpaCall::<RuntimeHelper::Runtime, RuntimeHelper::GPI>::submit_finality_proof {
finality_target: Box::new(relay_chain_header),
justification: grandpa_justification,
}.into(),
helpers::VerifySubmitGrandpaFinalityProofOutcome::<RuntimeHelper::Runtime, RuntimeHelper::GPI>::expect_best_header_hash(
relay_chain_header_hash,
),
),
(
BridgeMessagesCall::<RuntimeHelper::Runtime, RuntimeHelper::MPI>::receive_messages_proof {
relayer_id_at_bridged_chain,
proof: Box::new(message_proof),
messages_count: 1,
dispatch_weight: Weight::from_parts(1000000000, 0),
}.into(),
Box::new((
helpers::VerifySubmitMessagesProofOutcome::<RuntimeHelper::Runtime, RuntimeHelper::MPI>::expect_last_delivered_nonce(
lane_id,
1,
),
if expect_rewards {
helpers::VerifyRelayerRewarded::<RuntimeHelper::Runtime, RuntimeHelper::RPI>::expect_relayer_reward(
relayer_id_at_this_chain,
RewardsAccountParams::new(
lane_id,
bridged_chain_id,
RewardsAccountOwner::ThisChain,
),
)
} else {
Box::new(())
}
)),
),
]
},
);
}
/// Test-case makes sure that Runtime can dispatch XCM messages submitted by relayer,
/// with proofs (finality, message) independently submitted.
/// Finality proof is submitted for free in this test.
/// Also verifies relayer transaction signed extensions work as intended.
pub fn free_relay_extrinsic_works<RuntimeHelper>(
collator_session_key: CollatorSessionKeys<RuntimeHelper::Runtime>,
slot_durations: SlotDurations,
runtime_para_id: u32,
sibling_teyrchain_id: u32,
local_relay_chain_id: NetworkId,
prepare_configuration: impl Fn() -> LaneIdOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
construct_and_apply_extrinsic: fn(
pezsp_keyring::Sr25519Keyring,
RuntimeCallOf<RuntimeHelper::Runtime>,
) -> pezsp_runtime::DispatchOutcome,
expect_rewards: bool,
) where
RuntimeHelper: WithRemoteGrandpaChainHelper,
RuntimeHelper::Runtime: pezpallet_balances::Config,
AccountIdOf<RuntimeHelper::Runtime>: From<AccountId32>,
RuntimeCallOf<RuntimeHelper::Runtime>: From<BridgeGrandpaCall<RuntimeHelper::Runtime, RuntimeHelper::GPI>>
+ From<BridgeMessagesCall<RuntimeHelper::Runtime, RuntimeHelper::MPI>>,
BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>: ChainWithGrandpa,
{
// ensure that the runtime allows free header submissions
let free_headers_interval = <RuntimeHelper::Runtime as BridgeGrandpaConfig<
RuntimeHelper::GPI,
>>::FreeHeadersInterval::get()
.expect("this test requires runtime, configured to accept headers for free; qed");
helpers::relayed_incoming_message_works::<
RuntimeHelper::Runtime,
RuntimeHelper::AllPalletsWithoutSystem,
RuntimeHelper::MPI,
>(
collator_session_key,
slot_durations,
runtime_para_id,
sibling_teyrchain_id,
local_relay_chain_id,
construct_and_apply_extrinsic,
|relayer_id_at_this_chain,
relayer_id_at_bridged_chain,
message_destination,
message_nonce,
xcm,
bridged_chain_id| {
let lane_id = prepare_configuration();
// start with bridged relay chain block#0
let initial_block_number = 0;
helpers::initialize_bridge_grandpa_pallet::<RuntimeHelper::Runtime, RuntimeHelper::GPI>(
test_data::initialization_data::<RuntimeHelper::Runtime, RuntimeHelper::GPI>(
initial_block_number,
),
);
// free relay chain header is `0 + free_headers_interval`
let relay_header_number = initial_block_number + free_headers_interval;
// relayer balance shall not change after relay and para header submissions
let initial_relayer_balance =
pezpallet_balances::Pallet::<RuntimeHelper::Runtime>::free_balance(
relayer_id_at_this_chain.clone(),
);
// initialize the `FreeHeadersRemaining` storage value
pezpallet_bridge_grandpa::Pallet::<RuntimeHelper::Runtime, RuntimeHelper::GPI>::on_initialize(
0u32.into(),
);
// generate bridged relay chain finality, teyrchain heads and message proofs,
// to be submitted by relayer to this chain.
let (relay_chain_header, grandpa_justification, message_proof) =
test_data::from_grandpa_chain::make_complex_relayer_delivery_proofs::<
BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
ThisChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
LaneIdOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
>(
lane_id,
xcm.into(),
message_nonce,
message_destination,
relay_header_number.into(),
true,
);
let relay_chain_header_hash = relay_chain_header.hash();
vec![
(
BridgeGrandpaCall::<RuntimeHelper::Runtime, RuntimeHelper::GPI>::submit_finality_proof {
finality_target: Box::new(relay_chain_header),
justification: grandpa_justification,
}.into(),
Box::new((
helpers::VerifySubmitGrandpaFinalityProofOutcome::<RuntimeHelper::Runtime, RuntimeHelper::GPI>::expect_best_header_hash(
relay_chain_header_hash,
),
helpers::VerifyRelayerBalance::<RuntimeHelper::Runtime>::expect_relayer_balance(
relayer_id_at_this_chain.clone(),
initial_relayer_balance,
),
))
),
(
BridgeMessagesCall::<RuntimeHelper::Runtime, RuntimeHelper::MPI>::receive_messages_proof {
relayer_id_at_bridged_chain,
proof: Box::new(message_proof),
messages_count: 1,
dispatch_weight: Weight::from_parts(1000000000, 0),
}.into(),
Box::new((
helpers::VerifySubmitMessagesProofOutcome::<RuntimeHelper::Runtime, RuntimeHelper::MPI>::expect_last_delivered_nonce(
lane_id,
1,
),
if expect_rewards {
helpers::VerifyRelayerRewarded::<RuntimeHelper::Runtime, RuntimeHelper::RPI>::expect_relayer_reward(
relayer_id_at_this_chain,
RewardsAccountParams::new(
lane_id,
bridged_chain_id,
RewardsAccountOwner::ThisChain,
),
)
} else {
Box::new(())
}
)),
),
]
},
);
}
/// Test-case makes sure that Runtime can dispatch XCM messages submitted by relayer,
/// with proofs (finality, message) batched together in signed extrinsic.
/// Also verifies relayer transaction signed extensions work as intended.
pub fn complex_relay_extrinsic_works<RuntimeHelper>(
collator_session_key: CollatorSessionKeys<RuntimeHelper::Runtime>,
slot_durations: SlotDurations,
runtime_para_id: u32,
sibling_teyrchain_id: u32,
local_relay_chain_id: NetworkId,
prepare_configuration: impl Fn() -> LaneIdOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
construct_and_apply_extrinsic: fn(
pezsp_keyring::Sr25519Keyring,
RuntimeCallOf<RuntimeHelper::Runtime>,
) -> pezsp_runtime::DispatchOutcome,
) where
RuntimeHelper: WithRemoteGrandpaChainHelper,
RuntimeHelper::Runtime:
pezpallet_utility::Config<RuntimeCall = RuntimeCallOf<RuntimeHelper::Runtime>>,
AccountIdOf<RuntimeHelper::Runtime>: From<AccountId32>,
RuntimeCallOf<RuntimeHelper::Runtime>: From<BridgeGrandpaCall<RuntimeHelper::Runtime, RuntimeHelper::GPI>>
+ From<BridgeMessagesCall<RuntimeHelper::Runtime, RuntimeHelper::MPI>>
+ From<pezpallet_utility::Call<RuntimeHelper::Runtime>>,
BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>: ChainWithGrandpa,
{
helpers::relayed_incoming_message_works::<
RuntimeHelper::Runtime,
RuntimeHelper::AllPalletsWithoutSystem,
RuntimeHelper::MPI,
>(
collator_session_key,
slot_durations,
runtime_para_id,
sibling_teyrchain_id,
local_relay_chain_id,
construct_and_apply_extrinsic,
|relayer_id_at_this_chain,
relayer_id_at_bridged_chain,
message_destination,
message_nonce,
xcm,
bridged_chain_id| {
let relay_header_number = 1u32.into();
let lane_id = prepare_configuration();
// start with bridged relay chain block#0
helpers::initialize_bridge_grandpa_pallet::<RuntimeHelper::Runtime, RuntimeHelper::GPI>(
test_data::initialization_data::<RuntimeHelper::Runtime, RuntimeHelper::GPI>(0),
);
// generate bridged relay chain finality, teyrchain heads and message proofs,
// to be submitted by relayer to this chain.
let (relay_chain_header, grandpa_justification, message_proof) =
test_data::from_grandpa_chain::make_complex_relayer_delivery_proofs::<
BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
ThisChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
LaneIdOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
>(
lane_id,
xcm.into(),
message_nonce,
message_destination,
relay_header_number,
false,
);
let relay_chain_header_hash = relay_chain_header.hash();
vec![
(
pezpallet_utility::Call::<RuntimeHelper::Runtime>::batch_all {
calls: vec![
BridgeGrandpaCall::<RuntimeHelper::Runtime, RuntimeHelper::GPI>::submit_finality_proof {
finality_target: Box::new(relay_chain_header),
justification: grandpa_justification,
}.into(),
BridgeMessagesCall::<RuntimeHelper::Runtime, RuntimeHelper::MPI>::receive_messages_proof {
relayer_id_at_bridged_chain,
proof: Box::new(message_proof),
messages_count: 1,
dispatch_weight: Weight::from_parts(1000000000, 0),
}.into(),
],
}
.into(),
Box::new(
(
helpers::VerifySubmitGrandpaFinalityProofOutcome::<
RuntimeHelper::Runtime,
RuntimeHelper::GPI,
>::expect_best_header_hash(relay_chain_header_hash),
helpers::VerifySubmitMessagesProofOutcome::<
RuntimeHelper::Runtime,
RuntimeHelper::MPI,
>::expect_last_delivered_nonce(lane_id, 1),
helpers::VerifyRelayerRewarded::<
RuntimeHelper::Runtime,
RuntimeHelper::RPI,
>::expect_relayer_reward(
relayer_id_at_this_chain,
RewardsAccountParams::new(
lane_id,
bridged_chain_id,
RewardsAccountOwner::ThisChain,
),
),
),
),
),
]
},
);
}
/// Estimates transaction fee for default message delivery transaction (batched with required
/// proofs) from bridged GRANDPA chain.
pub fn can_calculate_fee_for_complex_message_delivery_transaction<RuntimeHelper>(
collator_session_key: CollatorSessionKeys<RuntimeHelper::Runtime>,
compute_extrinsic_fee: fn(pezpallet_utility::Call<RuntimeHelper::Runtime>) -> u128,
) -> u128
where
RuntimeHelper: WithRemoteGrandpaChainHelper,
RuntimeHelper::Runtime:
pezpallet_utility::Config<RuntimeCall = RuntimeCallOf<RuntimeHelper::Runtime>>,
RuntimeCallOf<RuntimeHelper::Runtime>: From<BridgeGrandpaCall<RuntimeHelper::Runtime, RuntimeHelper::GPI>>
+ From<BridgeMessagesCall<RuntimeHelper::Runtime, RuntimeHelper::MPI>>,
BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>: ChainWithGrandpa,
{
run_test::<RuntimeHelper::Runtime, _>(collator_session_key, 1000, vec![], || {
// generate bridged relay chain finality, teyrchain heads and message proofs,
// to be submitted by relayer to this chain.
//
// we don't care about parameter values here, apart from the XCM message size. But we
// do not need to have a large message here, because we're charging for every byte of
// the message additionally
let (relay_chain_header, grandpa_justification, message_proof) =
test_data::from_grandpa_chain::make_complex_relayer_delivery_proofs::<
BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
ThisChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
LaneIdOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
>(
LaneIdOf::<RuntimeHelper::Runtime, RuntimeHelper::MPI>::default(),
vec![Instruction::<()>::ClearOrigin; 1_024].into(),
1,
[GlobalConsensus(Pezkuwi), Teyrchain(1_000)].into(),
1u32.into(),
false,
);
// generate batch call that provides finality for bridged relay and teyrchains + message
// proof
let batch = test_data::from_grandpa_chain::make_complex_relayer_delivery_batch::<
RuntimeHelper::Runtime,
RuntimeHelper::GPI,
RuntimeHelper::MPI,
>(
relay_chain_header,
grandpa_justification,
message_proof,
helpers::relayer_id_at_bridged_chain::<RuntimeHelper::Runtime, RuntimeHelper::MPI>(),
);
compute_extrinsic_fee(batch)
})
}
/// Estimates transaction fee for default message confirmation transaction (batched with required
/// proofs) from bridged GRANDPA chain.
pub fn can_calculate_fee_for_complex_message_confirmation_transaction<RuntimeHelper>(
collator_session_key: CollatorSessionKeys<RuntimeHelper::Runtime>,
compute_extrinsic_fee: fn(pezpallet_utility::Call<RuntimeHelper::Runtime>) -> u128,
) -> u128
where
RuntimeHelper: WithRemoteGrandpaChainHelper,
AccountIdOf<RuntimeHelper::Runtime>: From<AccountId32>,
RuntimeHelper::Runtime:
pezpallet_utility::Config<RuntimeCall = RuntimeCallOf<RuntimeHelper::Runtime>>,
ThisChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>:
bp_runtime::Chain<AccountId = AccountIdOf<RuntimeHelper::Runtime>>,
RuntimeCallOf<RuntimeHelper::Runtime>: From<BridgeGrandpaCall<RuntimeHelper::Runtime, RuntimeHelper::GPI>>
+ From<BridgeMessagesCall<RuntimeHelper::Runtime, RuntimeHelper::MPI>>,
BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>: ChainWithGrandpa,
{
run_test::<RuntimeHelper::Runtime, _>(collator_session_key, 1000, vec![], || {
// generate bridged relay chain finality, teyrchain heads and message proofs,
// to be submitted by relayer to this chain.
let unrewarded_relayers = UnrewardedRelayersState {
unrewarded_relayer_entries: 1,
total_messages: 1,
..Default::default()
};
let (relay_chain_header, grandpa_justification, message_delivery_proof) =
test_data::from_grandpa_chain::make_complex_relayer_confirmation_proofs::<
BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
ThisChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
(),
LaneIdOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
>(
LaneIdOf::<RuntimeHelper::Runtime, RuntimeHelper::MPI>::default(),
1u32.into(),
AccountId32::from(Alice.public()).into(),
unrewarded_relayers.clone(),
);
// generate batch call that provides finality for bridged relay and teyrchains + message
// proof
let batch = test_data::from_grandpa_chain::make_complex_relayer_confirmation_batch::<
RuntimeHelper::Runtime,
RuntimeHelper::GPI,
RuntimeHelper::MPI,
>(
relay_chain_header,
grandpa_justification,
message_delivery_proof,
unrewarded_relayers,
);
compute_extrinsic_fee(batch)
})
}
/// Estimates transaction fee for default message delivery transaction from bridged GRANDPA chain.
pub fn can_calculate_fee_for_standalone_message_delivery_transaction<RuntimeHelper>(
collator_session_key: CollatorSessionKeys<RuntimeHelper::Runtime>,
compute_extrinsic_fee: fn(
<RuntimeHelper::Runtime as pezframe_system::Config>::RuntimeCall,
) -> u128,
) -> u128
where
RuntimeHelper: WithRemoteGrandpaChainHelper,
RuntimeCallOf<RuntimeHelper::Runtime>:
From<BridgeMessagesCall<RuntimeHelper::Runtime, RuntimeHelper::MPI>>,
BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>: ChainWithGrandpa,
{
run_test::<RuntimeHelper::Runtime, _>(collator_session_key, 1000, vec![], || {
// generate bridged relay chain finality, teyrchain heads and message proofs,
// to be submitted by relayer to this chain.
//
// we don't care about parameter values here, apart from the XCM message size. But we
// do not need to have a large message here, because we're charging for every byte of
// the message additionally
let (_, _, message_proof) =
test_data::from_grandpa_chain::make_complex_relayer_delivery_proofs::<
BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
ThisChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
LaneIdOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
>(
LaneIdOf::<RuntimeHelper::Runtime, RuntimeHelper::MPI>::default(),
vec![Instruction::<()>::ClearOrigin; 1_024].into(),
1,
[GlobalConsensus(Pezkuwi), Teyrchain(1_000)].into(),
1u32.into(),
false,
);
let call = test_data::from_grandpa_chain::make_standalone_relayer_delivery_call::<
RuntimeHelper::Runtime,
RuntimeHelper::GPI,
RuntimeHelper::MPI,
>(
message_proof,
helpers::relayer_id_at_bridged_chain::<RuntimeHelper::Runtime, RuntimeHelper::MPI>(),
);
compute_extrinsic_fee(call)
})
}
/// Estimates transaction fee for default message confirmation transaction (batched with required
/// proofs) from bridged teyrchain.
pub fn can_calculate_fee_for_standalone_message_confirmation_transaction<RuntimeHelper>(
collator_session_key: CollatorSessionKeys<RuntimeHelper::Runtime>,
compute_extrinsic_fee: fn(
<RuntimeHelper::Runtime as pezframe_system::Config>::RuntimeCall,
) -> u128,
) -> u128
where
RuntimeHelper: WithRemoteGrandpaChainHelper,
AccountIdOf<RuntimeHelper::Runtime>: From<AccountId32>,
ThisChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>:
bp_runtime::Chain<AccountId = AccountIdOf<RuntimeHelper::Runtime>>,
RuntimeCallOf<RuntimeHelper::Runtime>:
From<BridgeMessagesCall<RuntimeHelper::Runtime, RuntimeHelper::MPI>>,
BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>: ChainWithGrandpa,
{
run_test::<RuntimeHelper::Runtime, _>(collator_session_key, 1000, vec![], || {
// generate bridged relay chain finality, teyrchain heads and message proofs,
// to be submitted by relayer to this chain.
let unrewarded_relayers = UnrewardedRelayersState {
unrewarded_relayer_entries: 1,
total_messages: 1,
..Default::default()
};
let (_, _, message_delivery_proof) =
test_data::from_grandpa_chain::make_complex_relayer_confirmation_proofs::<
BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
ThisChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
(),
LaneIdOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
>(
LaneIdOf::<RuntimeHelper::Runtime, RuntimeHelper::MPI>::default(),
1u32.into(),
AccountId32::from(Alice.public()).into(),
unrewarded_relayers.clone(),
);
let call = test_data::from_grandpa_chain::make_standalone_relayer_confirmation_call::<
RuntimeHelper::Runtime,
RuntimeHelper::GPI,
RuntimeHelper::MPI,
>(message_delivery_proof, unrewarded_relayers);
compute_extrinsic_fee(call)
})
}
@@ -0,0 +1,790 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Pezcumulus.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Module contains predefined test-case scenarios for `Runtime` with bridging capabilities
//! with remote teyrchain.
use crate::{
test_cases::{bridges_prelude::*, helpers, run_test},
test_data,
test_data::XcmAsPlainPayload,
};
use alloc::{boxed::Box, vec};
use bp_header_chain::ChainWithGrandpa;
use bp_messages::UnrewardedRelayersState;
use bp_pezkuwi_core::teyrchains::ParaHash;
use bp_relayers::{RewardsAccountOwner, RewardsAccountParams};
use bp_runtime::{Chain, Teyrchain};
use pezframe_support::traits::{OnFinalize, OnInitialize};
use pezframe_system::pezpallet_prelude::BlockNumberFor;
use pezpallet_bridge_messages::{BridgedChainOf, LaneIdOf, ThisChainOf};
use pezsp_core::Get;
use pezsp_keyring::Sr25519Keyring::*;
use pezsp_runtime::{traits::Header as HeaderT, AccountId32};
use teyrchains_runtimes_test_utils::{
AccountIdOf, BasicTeyrchainRuntime, CollatorSessionKeys, RuntimeCallOf, SlotDurations,
};
use xcm::latest::prelude::*;
/// Helper trait to test bridges with remote teyrchain.
///
/// This is only used to decrease amount of lines, dedicated to bounds.
pub trait WithRemoteTeyrchainHelper {
/// This chain runtime.
type Runtime: BasicTeyrchainRuntime
+ cumulus_pallet_xcmp_queue::Config
+ BridgeGrandpaConfig<Self::GPI>
+ BridgeTeyrchainsConfig<Self::PPI>
+ BridgeMessagesConfig<
Self::MPI,
InboundPayload = XcmAsPlainPayload,
OutboundPayload = XcmAsPlainPayload,
> + pezpallet_bridge_relayers::Config<Self::RPI, Reward = Self::RelayerReward>;
/// All pallets of this chain, excluding system pallet.
type AllPalletsWithoutSystem: OnInitialize<BlockNumberFor<Self::Runtime>>
+ OnFinalize<BlockNumberFor<Self::Runtime>>;
/// Instance of the `pezpallet-bridge-grandpa`, used to bridge with remote relay chain.
type GPI: 'static;
/// Instance of the `pezpallet-bridge-teyrchains`, used to bridge with remote teyrchain.
type PPI: 'static;
/// Instance of the `pezpallet-bridge-messages`, used to bridge with remote teyrchain.
type MPI: 'static;
/// Instance of the `pezpallet-bridge-relayers`, used to collect rewards from messages `MPI`
/// instance.
type RPI: 'static;
/// Relayer reward type.
type RelayerReward: From<RewardsAccountParams<LaneIdOf<Self::Runtime, Self::MPI>>>;
}
/// Adapter struct that implements `WithRemoteTeyrchainHelper`.
pub struct WithRemoteTeyrchainHelperAdapter<Runtime, AllPalletsWithoutSystem, GPI, PPI, MPI, RPI>(
core::marker::PhantomData<(Runtime, AllPalletsWithoutSystem, GPI, PPI, MPI, RPI)>,
);
impl<Runtime, AllPalletsWithoutSystem, GPI, PPI, MPI, RPI> WithRemoteTeyrchainHelper
for WithRemoteTeyrchainHelperAdapter<Runtime, AllPalletsWithoutSystem, GPI, PPI, MPI, RPI>
where
Runtime: BasicTeyrchainRuntime
+ cumulus_pallet_xcmp_queue::Config
+ BridgeGrandpaConfig<GPI>
+ BridgeTeyrchainsConfig<PPI>
+ BridgeMessagesConfig<
MPI,
InboundPayload = XcmAsPlainPayload,
OutboundPayload = XcmAsPlainPayload,
> + pezpallet_bridge_relayers::Config<RPI>,
AllPalletsWithoutSystem:
OnInitialize<BlockNumberFor<Runtime>> + OnFinalize<BlockNumberFor<Runtime>>,
<Runtime as pezpallet_bridge_relayers::Config<RPI>>::Reward:
From<RewardsAccountParams<LaneIdOf<Runtime, MPI>>>,
GPI: 'static,
PPI: 'static,
MPI: 'static,
RPI: 'static,
{
type Runtime = Runtime;
type AllPalletsWithoutSystem = AllPalletsWithoutSystem;
type GPI = GPI;
type PPI = PPI;
type MPI = MPI;
type RPI = RPI;
type RelayerReward = Runtime::Reward;
}
/// Test-case makes sure that Runtime can dispatch XCM messages submitted by relayer,
/// with proofs (finality, para heads, message) independently submitted.
/// Also verifies relayer transaction signed extensions work as intended.
pub fn relayed_incoming_message_works<RuntimeHelper>(
collator_session_key: CollatorSessionKeys<RuntimeHelper::Runtime>,
slot_durations: SlotDurations,
runtime_para_id: u32,
bridged_para_id: u32,
sibling_teyrchain_id: u32,
local_relay_chain_id: NetworkId,
prepare_configuration: impl Fn() -> LaneIdOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
construct_and_apply_extrinsic: fn(
pezsp_keyring::Sr25519Keyring,
<RuntimeHelper::Runtime as pezframe_system::Config>::RuntimeCall,
) -> pezsp_runtime::DispatchOutcome,
expect_rewards: bool,
) where
RuntimeHelper: WithRemoteTeyrchainHelper,
AccountIdOf<RuntimeHelper::Runtime>: From<AccountId32>,
RuntimeCallOf<RuntimeHelper::Runtime>: From<BridgeGrandpaCall<RuntimeHelper::Runtime, RuntimeHelper::GPI>>
+ From<BridgeTeyrchainsCall<RuntimeHelper::Runtime, RuntimeHelper::PPI>>
+ From<BridgeMessagesCall<RuntimeHelper::Runtime, RuntimeHelper::MPI>>,
BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>: Chain<Hash = ParaHash> + Teyrchain,
<RuntimeHelper::Runtime as BridgeGrandpaConfig<RuntimeHelper::GPI>>::BridgedChain:
bp_runtime::Chain<Hash = RelayBlockHash, BlockNumber = RelayBlockNumber> + ChainWithGrandpa,
{
helpers::relayed_incoming_message_works::<
RuntimeHelper::Runtime,
RuntimeHelper::AllPalletsWithoutSystem,
RuntimeHelper::MPI,
>(
collator_session_key,
slot_durations,
runtime_para_id,
sibling_teyrchain_id,
local_relay_chain_id,
construct_and_apply_extrinsic,
|relayer_id_at_this_chain,
relayer_id_at_bridged_chain,
message_destination,
message_nonce,
xcm,
bridged_chain_id| {
let para_header_number = 5;
let relay_header_number = 1;
let lane_id = prepare_configuration();
// start with bridged relay chain block#0
helpers::initialize_bridge_grandpa_pallet::<RuntimeHelper::Runtime, RuntimeHelper::GPI>(
test_data::initialization_data::<RuntimeHelper::Runtime, RuntimeHelper::GPI>(0),
);
// generate bridged relay chain finality, teyrchain heads and message proofs,
// to be submitted by relayer to this chain.
let (
relay_chain_header,
grandpa_justification,
teyrchain_head,
teyrchain_heads,
para_heads_proof,
message_proof,
) = test_data::from_teyrchain::make_complex_relayer_delivery_proofs::<
<RuntimeHelper::Runtime as BridgeGrandpaConfig<RuntimeHelper::GPI>>::BridgedChain,
BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
ThisChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
LaneIdOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
>(
lane_id,
xcm.into(),
message_nonce,
message_destination,
para_header_number,
relay_header_number,
bridged_para_id,
false,
);
let teyrchain_head_hash = teyrchain_head.hash();
let relay_chain_header_hash = relay_chain_header.hash();
let relay_chain_header_number = *relay_chain_header.number();
vec![
(
BridgeGrandpaCall::<RuntimeHelper::Runtime, RuntimeHelper::GPI>::submit_finality_proof {
finality_target: Box::new(relay_chain_header),
justification: grandpa_justification,
}.into(),
helpers::VerifySubmitGrandpaFinalityProofOutcome::<RuntimeHelper::Runtime, RuntimeHelper::GPI>::expect_best_header_hash(
relay_chain_header_hash,
),
),
(
BridgeTeyrchainsCall::<RuntimeHelper::Runtime, RuntimeHelper::PPI>::submit_teyrchain_heads {
at_relay_block: (relay_chain_header_number, relay_chain_header_hash),
teyrchains: teyrchain_heads,
teyrchain_heads_proof: para_heads_proof,
}.into(),
helpers::VerifySubmitTeyrchainHeaderProofOutcome::<RuntimeHelper::Runtime, RuntimeHelper::PPI>::expect_best_header_hash(
bridged_para_id,
teyrchain_head_hash,
),
),
(
BridgeMessagesCall::<RuntimeHelper::Runtime, RuntimeHelper::MPI>::receive_messages_proof {
relayer_id_at_bridged_chain,
proof: Box::new(message_proof),
messages_count: 1,
dispatch_weight: Weight::from_parts(1000000000, 0),
}.into(),
Box::new((
helpers::VerifySubmitMessagesProofOutcome::<RuntimeHelper::Runtime, RuntimeHelper::MPI>::expect_last_delivered_nonce(
lane_id,
1,
),
if expect_rewards {
helpers::VerifyRelayerRewarded::<RuntimeHelper::Runtime, RuntimeHelper::RPI>::expect_relayer_reward(
relayer_id_at_this_chain,
RewardsAccountParams::new(
lane_id,
bridged_chain_id,
RewardsAccountOwner::ThisChain,
),
)
} else {
Box::new(())
}
)),
),
]
},
);
}
/// Test-case makes sure that Runtime can dispatch XCM messages submitted by relayer,
/// with proofs (finality, para heads, message) independently submitted.
/// Finality and para heads are submitted for free in this test.
/// Also verifies relayer transaction signed extensions work as intended.
pub fn free_relay_extrinsic_works<RuntimeHelper>(
collator_session_key: CollatorSessionKeys<RuntimeHelper::Runtime>,
slot_durations: SlotDurations,
runtime_para_id: u32,
bridged_para_id: u32,
sibling_teyrchain_id: u32,
local_relay_chain_id: NetworkId,
prepare_configuration: impl Fn() -> LaneIdOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
construct_and_apply_extrinsic: fn(
pezsp_keyring::Sr25519Keyring,
<RuntimeHelper::Runtime as pezframe_system::Config>::RuntimeCall,
) -> pezsp_runtime::DispatchOutcome,
expect_rewards: bool,
) where
RuntimeHelper: WithRemoteTeyrchainHelper,
RuntimeHelper::Runtime: pezpallet_balances::Config,
AccountIdOf<RuntimeHelper::Runtime>: From<AccountId32>,
RuntimeCallOf<RuntimeHelper::Runtime>: From<BridgeGrandpaCall<RuntimeHelper::Runtime, RuntimeHelper::GPI>>
+ From<BridgeTeyrchainsCall<RuntimeHelper::Runtime, RuntimeHelper::PPI>>
+ From<BridgeMessagesCall<RuntimeHelper::Runtime, RuntimeHelper::MPI>>,
BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>: Chain<Hash = ParaHash> + Teyrchain,
<RuntimeHelper::Runtime as BridgeGrandpaConfig<RuntimeHelper::GPI>>::BridgedChain:
bp_runtime::Chain<Hash = RelayBlockHash, BlockNumber = RelayBlockNumber> + ChainWithGrandpa,
{
// ensure that the runtime allows free header submissions
let free_headers_interval = <RuntimeHelper::Runtime as BridgeGrandpaConfig<
RuntimeHelper::GPI,
>>::FreeHeadersInterval::get()
.expect("this test requires runtime, configured to accept headers for free; qed");
helpers::relayed_incoming_message_works::<
RuntimeHelper::Runtime,
RuntimeHelper::AllPalletsWithoutSystem,
RuntimeHelper::MPI,
>(
collator_session_key,
slot_durations,
runtime_para_id,
sibling_teyrchain_id,
local_relay_chain_id,
construct_and_apply_extrinsic,
|relayer_id_at_this_chain,
relayer_id_at_bridged_chain,
message_destination,
message_nonce,
xcm,
bridged_chain_id| {
let lane_id = prepare_configuration();
// start with bridged relay chain block#0
let initial_block_number = 0;
helpers::initialize_bridge_grandpa_pallet::<RuntimeHelper::Runtime, RuntimeHelper::GPI>(
test_data::initialization_data::<RuntimeHelper::Runtime, RuntimeHelper::GPI>(
initial_block_number,
),
);
// free relay chain header is `0 + free_headers_interval`
let relay_header_number = initial_block_number + free_headers_interval;
// first teyrchain header is always submitted for free
let para_header_number = 1;
// relayer balance shall not change after relay and para header submissions
let initial_relayer_balance =
pezpallet_balances::Pallet::<RuntimeHelper::Runtime>::free_balance(
relayer_id_at_this_chain.clone(),
);
// initialize the `FreeHeadersRemaining` storage value
pezpallet_bridge_grandpa::Pallet::<RuntimeHelper::Runtime, RuntimeHelper::GPI>::on_initialize(
0u32.into(),
);
// generate bridged relay chain finality, teyrchain heads and message proofs,
// to be submitted by relayer to this chain.
let (
relay_chain_header,
grandpa_justification,
teyrchain_head,
teyrchain_heads,
para_heads_proof,
message_proof,
) = test_data::from_teyrchain::make_complex_relayer_delivery_proofs::<
<RuntimeHelper::Runtime as BridgeGrandpaConfig<RuntimeHelper::GPI>>::BridgedChain,
BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
ThisChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
LaneIdOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
>(
lane_id,
xcm.into(),
message_nonce,
message_destination,
para_header_number,
relay_header_number,
bridged_para_id,
true,
);
let teyrchain_head_hash = teyrchain_head.hash();
let relay_chain_header_hash = relay_chain_header.hash();
let relay_chain_header_number = *relay_chain_header.number();
vec![
(
BridgeGrandpaCall::<RuntimeHelper::Runtime, RuntimeHelper::GPI>::submit_finality_proof {
finality_target: Box::new(relay_chain_header),
justification: grandpa_justification,
}.into(),
Box::new((
helpers::VerifySubmitGrandpaFinalityProofOutcome::<RuntimeHelper::Runtime, RuntimeHelper::GPI>::expect_best_header_hash(
relay_chain_header_hash,
),
helpers::VerifyRelayerBalance::<RuntimeHelper::Runtime>::expect_relayer_balance(
relayer_id_at_this_chain.clone(),
initial_relayer_balance,
),
)),
),
(
BridgeTeyrchainsCall::<RuntimeHelper::Runtime, RuntimeHelper::PPI>::submit_teyrchain_heads {
at_relay_block: (relay_chain_header_number, relay_chain_header_hash),
teyrchains: teyrchain_heads,
teyrchain_heads_proof: para_heads_proof,
}.into(),
Box::new((
helpers::VerifySubmitTeyrchainHeaderProofOutcome::<RuntimeHelper::Runtime, RuntimeHelper::PPI>::expect_best_header_hash(
bridged_para_id,
teyrchain_head_hash,
),
helpers::VerifyRelayerBalance::<RuntimeHelper::Runtime>::expect_relayer_balance(
relayer_id_at_this_chain.clone(),
initial_relayer_balance,
),
)),
),
(
BridgeMessagesCall::<RuntimeHelper::Runtime, RuntimeHelper::MPI>::receive_messages_proof {
relayer_id_at_bridged_chain,
proof: Box::new(message_proof),
messages_count: 1,
dispatch_weight: Weight::from_parts(1000000000, 0),
}.into(),
Box::new((
helpers::VerifySubmitMessagesProofOutcome::<RuntimeHelper::Runtime, RuntimeHelper::MPI>::expect_last_delivered_nonce(
lane_id,
1,
),
if expect_rewards {
helpers::VerifyRelayerRewarded::<RuntimeHelper::Runtime, RuntimeHelper::RPI>::expect_relayer_reward(
relayer_id_at_this_chain,
RewardsAccountParams::new(
lane_id,
bridged_chain_id,
RewardsAccountOwner::ThisChain,
),
)
} else {
Box::new(())
}
)),
),
]
},
);
}
/// Test-case makes sure that Runtime can dispatch XCM messages submitted by relayer,
/// with proofs (finality, para heads, message) batched together in signed extrinsic.
/// Also verifies relayer transaction signed extensions work as intended.
pub fn complex_relay_extrinsic_works<RuntimeHelper>(
collator_session_key: CollatorSessionKeys<RuntimeHelper::Runtime>,
slot_durations: SlotDurations,
runtime_para_id: u32,
bridged_para_id: u32,
sibling_teyrchain_id: u32,
local_relay_chain_id: NetworkId,
prepare_configuration: impl Fn() -> LaneIdOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
construct_and_apply_extrinsic: fn(
pezsp_keyring::Sr25519Keyring,
<RuntimeHelper::Runtime as pezframe_system::Config>::RuntimeCall,
) -> pezsp_runtime::DispatchOutcome,
) where
RuntimeHelper: WithRemoteTeyrchainHelper,
RuntimeHelper::Runtime:
pezpallet_utility::Config<RuntimeCall = RuntimeCallOf<RuntimeHelper::Runtime>>,
AccountIdOf<RuntimeHelper::Runtime>: From<AccountId32>,
RuntimeCallOf<RuntimeHelper::Runtime>: From<BridgeGrandpaCall<RuntimeHelper::Runtime, RuntimeHelper::GPI>>
+ From<BridgeTeyrchainsCall<RuntimeHelper::Runtime, RuntimeHelper::PPI>>
+ From<BridgeMessagesCall<RuntimeHelper::Runtime, RuntimeHelper::MPI>>
+ From<pezpallet_utility::Call<RuntimeHelper::Runtime>>,
BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>: Chain<Hash = ParaHash> + Teyrchain,
<RuntimeHelper::Runtime as BridgeGrandpaConfig<RuntimeHelper::GPI>>::BridgedChain:
bp_runtime::Chain<Hash = RelayBlockHash, BlockNumber = RelayBlockNumber> + ChainWithGrandpa,
{
helpers::relayed_incoming_message_works::<
RuntimeHelper::Runtime,
RuntimeHelper::AllPalletsWithoutSystem,
RuntimeHelper::MPI,
>(
collator_session_key,
slot_durations,
runtime_para_id,
sibling_teyrchain_id,
local_relay_chain_id,
construct_and_apply_extrinsic,
|relayer_id_at_this_chain,
relayer_id_at_bridged_chain,
message_destination,
message_nonce,
xcm,
bridged_chain_id| {
let para_header_number = 5;
let relay_header_number = 1;
let lane_id = prepare_configuration();
// start with bridged relay chain block#0
helpers::initialize_bridge_grandpa_pallet::<RuntimeHelper::Runtime, RuntimeHelper::GPI>(
test_data::initialization_data::<RuntimeHelper::Runtime, RuntimeHelper::GPI>(0),
);
// generate bridged relay chain finality, teyrchain heads and message proofs,
// to be submitted by relayer to this chain.
let (
relay_chain_header,
grandpa_justification,
teyrchain_head,
teyrchain_heads,
para_heads_proof,
message_proof,
) = test_data::from_teyrchain::make_complex_relayer_delivery_proofs::<
<RuntimeHelper::Runtime as BridgeGrandpaConfig<RuntimeHelper::GPI>>::BridgedChain,
BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
ThisChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
LaneIdOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
>(
lane_id,
xcm.into(),
message_nonce,
message_destination,
para_header_number,
relay_header_number,
bridged_para_id,
false,
);
let teyrchain_head_hash = teyrchain_head.hash();
let relay_chain_header_hash = relay_chain_header.hash();
let relay_chain_header_number = *relay_chain_header.number();
vec![
(
pezpallet_utility::Call::<RuntimeHelper::Runtime>::batch_all {
calls: vec![
BridgeGrandpaCall::<RuntimeHelper::Runtime, RuntimeHelper::GPI>::submit_finality_proof {
finality_target: Box::new(relay_chain_header),
justification: grandpa_justification,
}.into(),
BridgeTeyrchainsCall::<RuntimeHelper::Runtime, RuntimeHelper::PPI>::submit_teyrchain_heads {
at_relay_block: (relay_chain_header_number, relay_chain_header_hash),
teyrchains: teyrchain_heads,
teyrchain_heads_proof: para_heads_proof,
}.into(),
BridgeMessagesCall::<RuntimeHelper::Runtime, RuntimeHelper::MPI>::receive_messages_proof {
relayer_id_at_bridged_chain,
proof: Box::new(message_proof),
messages_count: 1,
dispatch_weight: Weight::from_parts(1000000000, 0),
}.into(),
],
}
.into(),
Box::new(
(
helpers::VerifySubmitGrandpaFinalityProofOutcome::<
RuntimeHelper::Runtime,
RuntimeHelper::GPI,
>::expect_best_header_hash(relay_chain_header_hash),
helpers::VerifySubmitTeyrchainHeaderProofOutcome::<
RuntimeHelper::Runtime,
RuntimeHelper::PPI,
>::expect_best_header_hash(bridged_para_id, teyrchain_head_hash),
helpers::VerifySubmitMessagesProofOutcome::<
RuntimeHelper::Runtime,
RuntimeHelper::MPI,
>::expect_last_delivered_nonce(lane_id, 1),
helpers::VerifyRelayerRewarded::<
RuntimeHelper::Runtime,
RuntimeHelper::RPI,
>::expect_relayer_reward(
relayer_id_at_this_chain,
RewardsAccountParams::new(
lane_id,
bridged_chain_id,
RewardsAccountOwner::ThisChain,
),
),
),
),
),
]
},
);
}
/// Estimates transaction fee for default message delivery transaction (batched with required
/// proofs) from bridged teyrchain.
pub fn can_calculate_fee_for_complex_message_delivery_transaction<RuntimeHelper>(
collator_session_key: CollatorSessionKeys<RuntimeHelper::Runtime>,
compute_extrinsic_fee: fn(pezpallet_utility::Call<RuntimeHelper::Runtime>) -> u128,
) -> u128
where
RuntimeHelper: WithRemoteTeyrchainHelper,
RuntimeHelper::Runtime:
pezpallet_utility::Config<RuntimeCall = RuntimeCallOf<RuntimeHelper::Runtime>>,
RuntimeCallOf<RuntimeHelper::Runtime>: From<BridgeGrandpaCall<RuntimeHelper::Runtime, RuntimeHelper::GPI>>
+ From<BridgeTeyrchainsCall<RuntimeHelper::Runtime, RuntimeHelper::PPI>>
+ From<BridgeMessagesCall<RuntimeHelper::Runtime, RuntimeHelper::MPI>>,
BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>: Chain<Hash = ParaHash> + Teyrchain,
<RuntimeHelper::Runtime as BridgeGrandpaConfig<RuntimeHelper::GPI>>::BridgedChain:
bp_runtime::Chain<Hash = RelayBlockHash, BlockNumber = RelayBlockNumber> + ChainWithGrandpa,
{
run_test::<RuntimeHelper::Runtime, _>(collator_session_key, 1000, vec![], || {
// generate bridged relay chain finality, teyrchain heads and message proofs,
// to be submitted by relayer to this chain.
//
// we don't care about parameter values here, apart from the XCM message size. But we
// do not need to have a large message here, because we're charging for every byte of
// the message additionally
let (
relay_chain_header,
grandpa_justification,
_,
teyrchain_heads,
para_heads_proof,
message_proof,
) = test_data::from_teyrchain::make_complex_relayer_delivery_proofs::<
<RuntimeHelper::Runtime as pezpallet_bridge_grandpa::Config<RuntimeHelper::GPI>>::BridgedChain,
BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
ThisChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
LaneIdOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>
>(
LaneIdOf::<RuntimeHelper::Runtime, RuntimeHelper::MPI>::default(),
vec![Instruction::<()>::ClearOrigin; 1_024].into(),
1,
[GlobalConsensus(Pezkuwi), Teyrchain(1_000)].into(),
1,
5,
1_000,
false,
);
// generate batch call that provides finality for bridged relay and teyrchains + message
// proof
let batch = test_data::from_teyrchain::make_complex_relayer_delivery_batch::<
RuntimeHelper::Runtime,
RuntimeHelper::GPI,
RuntimeHelper::PPI,
RuntimeHelper::MPI,
>(
relay_chain_header,
grandpa_justification,
teyrchain_heads,
para_heads_proof,
message_proof,
helpers::relayer_id_at_bridged_chain::<RuntimeHelper::Runtime, RuntimeHelper::MPI>(),
);
compute_extrinsic_fee(batch)
})
}
/// Estimates transaction fee for default message confirmation transaction (batched with required
/// proofs) from bridged teyrchain.
pub fn can_calculate_fee_for_complex_message_confirmation_transaction<RuntimeHelper>(
collator_session_key: CollatorSessionKeys<RuntimeHelper::Runtime>,
compute_extrinsic_fee: fn(pezpallet_utility::Call<RuntimeHelper::Runtime>) -> u128,
) -> u128
where
RuntimeHelper: WithRemoteTeyrchainHelper,
AccountIdOf<RuntimeHelper::Runtime>: From<AccountId32>,
RuntimeHelper::Runtime:
pezpallet_utility::Config<RuntimeCall = RuntimeCallOf<RuntimeHelper::Runtime>>,
ThisChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>:
Chain<AccountId = AccountIdOf<RuntimeHelper::Runtime>>,
RuntimeCallOf<RuntimeHelper::Runtime>: From<BridgeGrandpaCall<RuntimeHelper::Runtime, RuntimeHelper::GPI>>
+ From<BridgeTeyrchainsCall<RuntimeHelper::Runtime, RuntimeHelper::PPI>>
+ From<BridgeMessagesCall<RuntimeHelper::Runtime, RuntimeHelper::MPI>>,
BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>: Chain<Hash = ParaHash> + Teyrchain,
<RuntimeHelper::Runtime as BridgeGrandpaConfig<RuntimeHelper::GPI>>::BridgedChain:
bp_runtime::Chain<Hash = RelayBlockHash, BlockNumber = RelayBlockNumber> + ChainWithGrandpa,
{
run_test::<RuntimeHelper::Runtime, _>(collator_session_key, 1000, vec![], || {
// generate bridged relay chain finality, teyrchain heads and message proofs,
// to be submitted by relayer to this chain.
let unrewarded_relayers = UnrewardedRelayersState {
unrewarded_relayer_entries: 1,
total_messages: 1,
..Default::default()
};
let (
relay_chain_header,
grandpa_justification,
_,
teyrchain_heads,
para_heads_proof,
message_delivery_proof,
) = test_data::from_teyrchain::make_complex_relayer_confirmation_proofs::<
<RuntimeHelper::Runtime as BridgeGrandpaConfig<RuntimeHelper::GPI>>::BridgedChain,
BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
ThisChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
LaneIdOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
>(
LaneIdOf::<RuntimeHelper::Runtime, RuntimeHelper::MPI>::default(),
1,
5,
1_000,
AccountId32::from(Alice.public()).into(),
unrewarded_relayers.clone(),
);
// generate batch call that provides finality for bridged relay and teyrchains + message
// proof
let batch = test_data::from_teyrchain::make_complex_relayer_confirmation_batch::<
RuntimeHelper::Runtime,
RuntimeHelper::GPI,
RuntimeHelper::PPI,
RuntimeHelper::MPI,
>(
relay_chain_header,
grandpa_justification,
teyrchain_heads,
para_heads_proof,
message_delivery_proof,
unrewarded_relayers,
);
compute_extrinsic_fee(batch)
})
}
/// Estimates transaction fee for default message delivery transaction from bridged teyrchain.
pub fn can_calculate_fee_for_standalone_message_delivery_transaction<RuntimeHelper>(
collator_session_key: CollatorSessionKeys<RuntimeHelper::Runtime>,
compute_extrinsic_fee: fn(
<RuntimeHelper::Runtime as pezframe_system::Config>::RuntimeCall,
) -> u128,
) -> u128
where
RuntimeHelper: WithRemoteTeyrchainHelper,
RuntimeCallOf<RuntimeHelper::Runtime>:
From<BridgeMessagesCall<RuntimeHelper::Runtime, RuntimeHelper::MPI>>,
BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>: Chain<Hash = ParaHash> + Teyrchain,
<RuntimeHelper::Runtime as BridgeGrandpaConfig<RuntimeHelper::GPI>>::BridgedChain:
bp_runtime::Chain<Hash = RelayBlockHash, BlockNumber = RelayBlockNumber> + ChainWithGrandpa,
{
run_test::<RuntimeHelper::Runtime, _>(collator_session_key, 1000, vec![], || {
// generate bridged relay chain finality, teyrchain heads and message proofs,
// to be submitted by relayer to this chain.
//
// we don't care about parameter values here, apart from the XCM message size. But we
// do not need to have a large message here, because we're charging for every byte of
// the message additionally
let (
_,
_,
_,
_,
_,
message_proof,
) = test_data::from_teyrchain::make_complex_relayer_delivery_proofs::<
<RuntimeHelper::Runtime as pezpallet_bridge_grandpa::Config<RuntimeHelper::GPI>>::BridgedChain,
BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
ThisChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
LaneIdOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
>(
LaneIdOf::<RuntimeHelper::Runtime, RuntimeHelper::MPI>::default(),
vec![Instruction::<()>::ClearOrigin; 1_024].into(),
1,
[GlobalConsensus(Pezkuwi), Teyrchain(1_000)].into(),
1,
5,
1_000,
false,
);
let call = test_data::from_teyrchain::make_standalone_relayer_delivery_call::<
RuntimeHelper::Runtime,
RuntimeHelper::MPI,
>(
message_proof,
helpers::relayer_id_at_bridged_chain::<RuntimeHelper::Runtime, RuntimeHelper::MPI>(),
);
compute_extrinsic_fee(call)
})
}
/// Estimates transaction fee for default message confirmation transaction (batched with required
/// proofs) from bridged teyrchain.
pub fn can_calculate_fee_for_standalone_message_confirmation_transaction<RuntimeHelper>(
collator_session_key: CollatorSessionKeys<RuntimeHelper::Runtime>,
compute_extrinsic_fee: fn(
<RuntimeHelper::Runtime as pezframe_system::Config>::RuntimeCall,
) -> u128,
) -> u128
where
RuntimeHelper: WithRemoteTeyrchainHelper,
AccountIdOf<RuntimeHelper::Runtime>: From<AccountId32>,
ThisChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>:
Chain<AccountId = AccountIdOf<RuntimeHelper::Runtime>>,
RuntimeCallOf<RuntimeHelper::Runtime>:
From<BridgeMessagesCall<RuntimeHelper::Runtime, RuntimeHelper::MPI>>,
BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>: Chain<Hash = ParaHash> + Teyrchain,
<RuntimeHelper::Runtime as BridgeGrandpaConfig<RuntimeHelper::GPI>>::BridgedChain:
bp_runtime::Chain<Hash = RelayBlockHash, BlockNumber = RelayBlockNumber> + ChainWithGrandpa,
{
run_test::<RuntimeHelper::Runtime, _>(collator_session_key, 1000, vec![], || {
// generate bridged relay chain finality, teyrchain heads and message proofs,
// to be submitted by relayer to this chain.
let unrewarded_relayers = UnrewardedRelayersState {
unrewarded_relayer_entries: 1,
total_messages: 1,
..Default::default()
};
let (_, _, _, _, _, message_delivery_proof) =
test_data::from_teyrchain::make_complex_relayer_confirmation_proofs::<
<RuntimeHelper::Runtime as BridgeGrandpaConfig<RuntimeHelper::GPI>>::BridgedChain,
BridgedChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
ThisChainOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
LaneIdOf<RuntimeHelper::Runtime, RuntimeHelper::MPI>,
>(
LaneIdOf::<RuntimeHelper::Runtime, RuntimeHelper::MPI>::default(),
1,
5,
1_000,
AccountId32::from(Alice.public()).into(),
unrewarded_relayers.clone(),
);
let call = test_data::from_teyrchain::make_standalone_relayer_confirmation_call::<
RuntimeHelper::Runtime,
RuntimeHelper::MPI,
>(message_delivery_proof, unrewarded_relayers);
compute_extrinsic_fee(call)
})
}
@@ -0,0 +1,595 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Pezcumulus.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Module contains tests code, that is shared by all types of bridges
use crate::test_cases::{bridges_prelude::*, run_test, RuntimeHelper};
use asset_test_utils::BasicTeyrchainRuntime;
use bp_messages::MessageNonce;
use bp_pezkuwi_core::teyrchains::{ParaHash, ParaId};
use bp_runtime::Chain;
use codec::Decode;
use core::marker::PhantomData;
use pezframe_support::{
assert_ok,
dispatch::GetDispatchInfo,
traits::{fungible::Mutate, Contains, OnFinalize, OnInitialize, PalletInfoAccess},
};
use pezframe_system::pezpallet_prelude::BlockNumberFor;
use pezpallet_bridge_grandpa::{BridgedBlockHash, BridgedHeader};
use pezpallet_bridge_messages::{BridgedChainOf, LaneIdOf};
use pezsp_core::Get;
use pezsp_keyring::Sr25519Keyring::*;
use pezsp_runtime::{traits::TrailingZeroInput, AccountId32};
use teyrchains_common::AccountId;
use teyrchains_runtimes_test_utils::{
mock_open_hrmp_channel, AccountIdOf, CollatorSessionKeys, RuntimeCallOf, SlotDurations,
};
use xcm::latest::prelude::*;
use xcm_executor::traits::ConvertLocation;
/// Verify that the transaction has succeeded.
#[impl_trait_for_tuples::impl_for_tuples(30)]
pub trait VerifyTransactionOutcome {
fn verify_outcome(&self);
}
impl VerifyTransactionOutcome for Box<dyn VerifyTransactionOutcome> {
fn verify_outcome(&self) {
VerifyTransactionOutcome::verify_outcome(&**self)
}
}
/// Checks that the best finalized header hash in the bridge GRANDPA pallet equals to given one.
pub struct VerifySubmitGrandpaFinalityProofOutcome<Runtime, GPI>
where
Runtime: BridgeGrandpaConfig<GPI>,
GPI: 'static,
{
expected_best_hash: BridgedBlockHash<Runtime, GPI>,
}
impl<Runtime, GPI> VerifySubmitGrandpaFinalityProofOutcome<Runtime, GPI>
where
Runtime: BridgeGrandpaConfig<GPI>,
GPI: 'static,
{
/// Expect given header hash to be the best after transaction.
pub fn expect_best_header_hash(
expected_best_hash: BridgedBlockHash<Runtime, GPI>,
) -> Box<dyn VerifyTransactionOutcome> {
Box::new(Self { expected_best_hash })
}
}
impl<Runtime, GPI> VerifyTransactionOutcome
for VerifySubmitGrandpaFinalityProofOutcome<Runtime, GPI>
where
Runtime: BridgeGrandpaConfig<GPI>,
GPI: 'static,
{
fn verify_outcome(&self) {
assert_eq!(
pezpallet_bridge_grandpa::BestFinalized::<Runtime, GPI>::get().unwrap().1,
self.expected_best_hash
);
assert!(pezpallet_bridge_grandpa::ImportedHeaders::<Runtime, GPI>::contains_key(
self.expected_best_hash
));
}
}
/// Checks that the best teyrchain header hash in the bridge teyrchains pallet equals to given one.
pub struct VerifySubmitTeyrchainHeaderProofOutcome<Runtime, PPI> {
bridged_para_id: u32,
expected_best_hash: ParaHash,
_marker: PhantomData<(Runtime, PPI)>,
}
impl<Runtime, PPI> VerifySubmitTeyrchainHeaderProofOutcome<Runtime, PPI>
where
Runtime: BridgeTeyrchainsConfig<PPI>,
PPI: 'static,
{
/// Expect given header hash to be the best after transaction.
pub fn expect_best_header_hash(
bridged_para_id: u32,
expected_best_hash: ParaHash,
) -> Box<dyn VerifyTransactionOutcome> {
Box::new(Self { bridged_para_id, expected_best_hash, _marker: PhantomData })
}
}
impl<Runtime, PPI> VerifyTransactionOutcome
for VerifySubmitTeyrchainHeaderProofOutcome<Runtime, PPI>
where
Runtime: BridgeTeyrchainsConfig<PPI>,
PPI: 'static,
{
fn verify_outcome(&self) {
assert_eq!(
pezpallet_bridge_teyrchains::ParasInfo::<Runtime, PPI>::get(ParaId(self.bridged_para_id))
.map(|info| info.best_head_hash.head_hash),
Some(self.expected_best_hash),
);
}
}
/// Checks that the latest delivered nonce in the bridge messages pallet equals to given one.
pub struct VerifySubmitMessagesProofOutcome<Runtime: BridgeMessagesConfig<MPI>, MPI: 'static> {
lane: LaneIdOf<Runtime, MPI>,
expected_nonce: MessageNonce,
_marker: PhantomData<(Runtime, MPI)>,
}
impl<Runtime, MPI> VerifySubmitMessagesProofOutcome<Runtime, MPI>
where
Runtime: BridgeMessagesConfig<MPI>,
MPI: 'static,
{
/// Expect given delivered nonce to be the latest after transaction.
pub fn expect_last_delivered_nonce(
lane: LaneIdOf<Runtime, MPI>,
expected_nonce: MessageNonce,
) -> Box<dyn VerifyTransactionOutcome> {
Box::new(Self { lane, expected_nonce, _marker: PhantomData })
}
}
impl<Runtime, MPI> VerifyTransactionOutcome for VerifySubmitMessagesProofOutcome<Runtime, MPI>
where
Runtime: BridgeMessagesConfig<MPI>,
MPI: 'static,
{
fn verify_outcome(&self) {
assert_eq!(
pezpallet_bridge_messages::InboundLanes::<Runtime, MPI>::get(self.lane)
.map(|d| d.last_delivered_nonce()),
Some(self.expected_nonce),
);
}
}
/// Verifies that relayer is rewarded at this chain.
pub struct VerifyRelayerRewarded<Runtime: pezpallet_bridge_relayers::Config<RPI>, RPI: 'static> {
relayer: Runtime::AccountId,
reward_params: Runtime::Reward,
}
impl<Runtime, RPI> VerifyRelayerRewarded<Runtime, RPI>
where
Runtime: pezpallet_bridge_relayers::Config<RPI>,
RPI: 'static,
{
/// Expect given delivered nonce to be the latest after transaction.
pub fn expect_relayer_reward(
relayer: Runtime::AccountId,
reward_params: impl Into<Runtime::Reward>,
) -> Box<dyn VerifyTransactionOutcome> {
Box::new(Self { relayer, reward_params: reward_params.into() })
}
}
impl<Runtime, RPI> VerifyTransactionOutcome for VerifyRelayerRewarded<Runtime, RPI>
where
Runtime: pezpallet_bridge_relayers::Config<RPI>,
RPI: 'static,
{
fn verify_outcome(&self) {
assert!(pezpallet_bridge_relayers::RelayerRewards::<Runtime, RPI>::get(
&self.relayer,
&self.reward_params,
)
.is_some());
}
}
/// Verifies that relayer balance is equal to given value.
pub struct VerifyRelayerBalance<Runtime: pezpallet_balances::Config> {
relayer: Runtime::AccountId,
balance: Runtime::Balance,
}
impl<Runtime> VerifyRelayerBalance<Runtime>
where
Runtime: pezpallet_balances::Config,
{
/// Expect given relayer balance after transaction.
pub fn expect_relayer_balance(
relayer: Runtime::AccountId,
balance: Runtime::Balance,
) -> Box<dyn VerifyTransactionOutcome> {
Box::new(Self { relayer, balance })
}
}
impl<Runtime> VerifyTransactionOutcome for VerifyRelayerBalance<Runtime>
where
Runtime: pezpallet_balances::Config,
{
fn verify_outcome(&self) {
assert_eq!(pezpallet_balances::Pallet::<Runtime>::free_balance(&self.relayer), self.balance,);
}
}
/// Initialize bridge GRANDPA pallet.
pub(crate) fn initialize_bridge_grandpa_pallet<Runtime, GPI>(
init_data: bp_header_chain::InitializationData<BridgedHeader<Runtime, GPI>>,
) where
Runtime: BridgeGrandpaConfig<GPI>
+ cumulus_pallet_teyrchain_system::Config
+ pezpallet_timestamp::Config,
{
pezpallet_bridge_grandpa::Pallet::<Runtime, GPI>::initialize(
RuntimeHelper::<Runtime>::root_origin(),
init_data,
)
.unwrap();
}
/// Runtime calls and their verifiers.
pub type CallsAndVerifiers<Runtime> =
Vec<(RuntimeCallOf<Runtime>, Box<dyn VerifyTransactionOutcome>)>;
pub type InboundRelayerId<Runtime, MPI> = bp_runtime::AccountIdOf<BridgedChainOf<Runtime, MPI>>;
/// Returns relayer id at the bridged chain.
pub fn relayer_id_at_bridged_chain<Runtime: pezpallet_bridge_messages::Config<MPI>, MPI>(
) -> InboundRelayerId<Runtime, MPI> {
Decode::decode(&mut TrailingZeroInput::zeroes()).unwrap()
}
/// Test-case makes sure that Runtime can dispatch XCM messages submitted by relayer,
/// with proofs (finality, message) independently submitted.
pub fn relayed_incoming_message_works<Runtime, AllPalletsWithoutSystem, MPI>(
collator_session_key: CollatorSessionKeys<Runtime>,
slot_durations: SlotDurations,
runtime_para_id: u32,
sibling_teyrchain_id: u32,
local_relay_chain_id: NetworkId,
construct_and_apply_extrinsic: fn(
pezsp_keyring::Sr25519Keyring,
RuntimeCallOf<Runtime>,
) -> pezsp_runtime::DispatchOutcome,
prepare_message_proof_import: impl FnOnce(
Runtime::AccountId,
InboundRelayerId<Runtime, MPI>,
InteriorLocation,
MessageNonce,
Xcm<()>,
bp_runtime::ChainId,
) -> CallsAndVerifiers<Runtime>,
) where
Runtime: BasicTeyrchainRuntime + cumulus_pallet_xcmp_queue::Config + BridgeMessagesConfig<MPI>,
AllPalletsWithoutSystem:
OnInitialize<BlockNumberFor<Runtime>> + OnFinalize<BlockNumberFor<Runtime>>,
MPI: 'static,
AccountIdOf<Runtime>: From<AccountId32>,
{
let relayer_at_target = Bob;
let relayer_id_on_target: AccountId32 = relayer_at_target.public().into();
let relayer_id_on_source = relayer_id_at_bridged_chain::<Runtime, MPI>();
let bridged_chain_id = Runtime::BridgedChain::ID;
assert_ne!(runtime_para_id, sibling_teyrchain_id);
run_test::<Runtime, _>(
collator_session_key,
runtime_para_id,
vec![(
relayer_id_on_target.clone().into(),
// this value should be enough to cover all transaction costs, but computing the actual
// value here is tricky - there are several transaction payment pallets and we don't
// want to introduce additional bounds and traits here just for that, so let's just
// select some presumably large value
core::cmp::max::<Runtime::Balance>(Runtime::ExistentialDeposit::get(), 1u32.into()) *
100_000_000u32.into(),
)],
|| {
let mut alice = [0u8; 32];
alice[0] = 1;
let included_head = RuntimeHelper::<Runtime, AllPalletsWithoutSystem>::run_to_block(
2,
AccountId::from(alice).into(),
);
mock_open_hrmp_channel::<Runtime, cumulus_pallet_teyrchain_system::Pallet<Runtime>>(
runtime_para_id.into(),
sibling_teyrchain_id.into(),
included_head,
&alice,
&slot_durations,
);
// set up relayer details and proofs
let message_destination: InteriorLocation =
[GlobalConsensus(local_relay_chain_id), Teyrchain(sibling_teyrchain_id)].into();
// some random numbers (checked by test)
let message_nonce = 1;
let xcm = vec![Instruction::<()>::ClearOrigin; 42];
let expected_dispatch = xcm::latest::Xcm::<()>({
let mut expected_instructions = xcm.clone();
// dispatch prepends bridge pallet instance
expected_instructions.insert(
0,
DescendOrigin([PalletInstance(
<pezpallet_bridge_messages::Pallet<Runtime, MPI> as PalletInfoAccess>::index()
as u8,
)].into()),
);
expected_instructions
});
execute_and_verify_calls::<Runtime>(
relayer_at_target,
construct_and_apply_extrinsic,
prepare_message_proof_import(
relayer_id_on_target.clone().into(),
relayer_id_on_source.clone().into(),
message_destination,
message_nonce,
xcm.clone().into(),
bridged_chain_id,
),
);
// verify that imported XCM contains original message
let imported_xcm =
RuntimeHelper::<cumulus_pallet_xcmp_queue::Pallet<Runtime>>::take_xcm(
sibling_teyrchain_id.into(),
)
.unwrap();
let dispatched = xcm::latest::Xcm::<()>::try_from(imported_xcm).unwrap();
let mut dispatched_clone = dispatched.clone();
for (idx, expected_instr) in expected_dispatch.0.iter().enumerate() {
assert_eq!(expected_instr, &dispatched.0[idx]);
assert_eq!(expected_instr, &dispatched_clone.0.remove(0));
}
match dispatched_clone.0.len() {
0 => (),
1 => assert!(matches!(dispatched_clone.0[0], SetTopic(_))),
count => assert!(false, "Unexpected messages count: {:?}", count),
}
},
)
}
/// Execute every call and verify its outcome.
fn execute_and_verify_calls<Runtime: pezframe_system::Config>(
submitter: pezsp_keyring::Sr25519Keyring,
construct_and_apply_extrinsic: fn(
pezsp_keyring::Sr25519Keyring,
RuntimeCallOf<Runtime>,
) -> pezsp_runtime::DispatchOutcome,
calls_and_verifiers: CallsAndVerifiers<Runtime>,
) {
for (call, verifier) in calls_and_verifiers {
let dispatch_outcome = construct_and_apply_extrinsic(submitter, call);
assert_ok!(dispatch_outcome);
verifier.verify_outcome();
}
}
pub(crate) mod for_pallet_xcm_bridge_hub {
use super::{super::for_pallet_xcm_bridge_hub::*, *};
/// Helper function to open the bridge/lane for `source` and `destination` while ensuring all
/// required balances are placed into the SA of the source.
pub fn ensure_opened_bridge<
Runtime,
XcmOverBridgePalletInstance,
LocationToAccountId,
TokenLocation>
(source: Location, destination: InteriorLocation, is_paid_xcm_execution: bool, bridge_opener: impl Fn(pezpallet_xcm_bridge_hub::BridgeLocations, Option<Asset>)) -> (pezpallet_xcm_bridge_hub::BridgeLocations, pezpallet_xcm_bridge_hub::LaneIdOf<Runtime, XcmOverBridgePalletInstance>)
where
Runtime: BasicTeyrchainRuntime + BridgeXcmOverBridgeConfig<XcmOverBridgePalletInstance>,
XcmOverBridgePalletInstance: 'static,
<Runtime as pezframe_system::Config>::RuntimeCall: GetDispatchInfo + From<BridgeXcmOverBridgeCall<Runtime, XcmOverBridgePalletInstance>>,
<Runtime as pezpallet_balances::Config>::Balance: From<<<Runtime as pezpallet_bridge_messages::Config<<Runtime as pezpallet_xcm_bridge_hub::Config<XcmOverBridgePalletInstance>>::BridgeMessagesPalletInstance>>::ThisChain as bp_runtime::Chain>::Balance>,
<Runtime as pezpallet_balances::Config>::Balance: From<u128>,
LocationToAccountId: ConvertLocation<AccountIdOf<Runtime>>,
TokenLocation: Get<Location>
{
// construct expected bridge configuration
let locations =
pezpallet_xcm_bridge_hub::Pallet::<Runtime, XcmOverBridgePalletInstance>::bridge_locations(
source.clone().into(),
destination.clone().into(),
)
.expect("valid bridge locations");
assert!(pezpallet_xcm_bridge_hub::Bridges::<Runtime, XcmOverBridgePalletInstance>::get(
locations.bridge_id()
)
.is_none());
// SA of source location needs to have some required balance
if !<Runtime as pezpallet_xcm_bridge_hub::Config<XcmOverBridgePalletInstance>>::AllowWithoutBridgeDeposit::contains(&source) {
// required balance: ED + fee + BridgeDeposit
let bridge_deposit =
<Runtime as pezpallet_xcm_bridge_hub::Config<XcmOverBridgePalletInstance>>::BridgeDeposit::get();
let balance_needed = <Runtime as pezpallet_balances::Config>::ExistentialDeposit::get() + bridge_deposit.into();
let source_account_id = LocationToAccountId::convert_location(&source).expect("valid location");
let _ = <pezpallet_balances::Pallet<Runtime>>::mint_into(&source_account_id, balance_needed)
.expect("mint_into passes");
};
let maybe_paid_execution = if is_paid_xcm_execution {
// random high enough value for `BuyExecution` fees
let buy_execution_fee_amount = 5_000_000_000_000_u128;
let buy_execution_fee = (TokenLocation::get(), buy_execution_fee_amount).into();
let balance_needed = <Runtime as pezpallet_balances::Config>::ExistentialDeposit::get() +
buy_execution_fee_amount.into();
let source_account_id =
LocationToAccountId::convert_location(&source).expect("valid location");
let _ =
<pezpallet_balances::Pallet<Runtime>>::mint_into(&source_account_id, balance_needed)
.expect("mint_into passes");
Some(buy_execution_fee)
} else {
None
};
// call the bridge opener
bridge_opener(*locations.clone(), maybe_paid_execution);
// check opened bridge
let bridge = pezpallet_xcm_bridge_hub::Bridges::<Runtime, XcmOverBridgePalletInstance>::get(
locations.bridge_id(),
)
.expect("opened bridge");
// check state
assert_ok!(
pezpallet_xcm_bridge_hub::Pallet::<Runtime, XcmOverBridgePalletInstance>::do_try_state()
);
// return locations
(*locations, bridge.lane_id)
}
/// Utility for opening bridge with dedicated `pezpallet_xcm_bridge_hub`'s extrinsic.
pub fn open_bridge_with_extrinsic<Runtime, XcmOverBridgePalletInstance>(
(origin, origin_kind): (Location, OriginKind),
bridge_destination_universal_location: InteriorLocation,
maybe_paid_execution: Option<Asset>,
) where
Runtime: pezframe_system::Config
+ pezpallet_xcm_bridge_hub::Config<XcmOverBridgePalletInstance>
+ cumulus_pallet_teyrchain_system::Config
+ pezpallet_xcm::Config,
XcmOverBridgePalletInstance: 'static,
<Runtime as pezframe_system::Config>::RuntimeCall:
GetDispatchInfo + From<BridgeXcmOverBridgeCall<Runtime, XcmOverBridgePalletInstance>>,
{
// open bridge with `Transact` call
let open_bridge_call = RuntimeCallOf::<Runtime>::from(BridgeXcmOverBridgeCall::<
Runtime,
XcmOverBridgePalletInstance,
>::open_bridge {
bridge_destination_universal_location: Box::new(
bridge_destination_universal_location.clone().into(),
),
});
// execute XCM as source origin would do with `Transact -> Origin::Xcm`
assert_ok!(RuntimeHelper::<Runtime>::execute_as_origin(
(origin, origin_kind),
open_bridge_call,
maybe_paid_execution
)
.ensure_complete());
}
/// Utility for opening bridge directly inserting data to the `pezpallet_xcm_bridge_hub`'s storage
/// (used only for legacy purposes).
pub fn open_bridge_with_storage<Runtime, XcmOverBridgePalletInstance>(
locations: pezpallet_xcm_bridge_hub::BridgeLocations,
lane_id: pezpallet_xcm_bridge_hub::LaneIdOf<Runtime, XcmOverBridgePalletInstance>,
) where
Runtime: pezpallet_xcm_bridge_hub::Config<XcmOverBridgePalletInstance>,
XcmOverBridgePalletInstance: 'static,
{
// insert bridge data directly to the storage
assert_ok!(
pezpallet_xcm_bridge_hub::Pallet::<Runtime, XcmOverBridgePalletInstance>::do_open_bridge(
Box::new(locations),
lane_id,
true
)
);
}
/// Helper function to close the bridge/lane for `source` and `destination`.
pub fn close_bridge<Runtime, XcmOverBridgePalletInstance, LocationToAccountId, TokenLocation>(
expected_source: Location,
bridge_destination_universal_location: InteriorLocation,
(origin, origin_kind): (Location, OriginKind),
is_paid_xcm_execution: bool
) where
Runtime: BasicTeyrchainRuntime + BridgeXcmOverBridgeConfig<XcmOverBridgePalletInstance>,
XcmOverBridgePalletInstance: 'static,
<Runtime as pezframe_system::Config>::RuntimeCall: GetDispatchInfo + From<BridgeXcmOverBridgeCall<Runtime, XcmOverBridgePalletInstance>>,
<Runtime as pezpallet_balances::Config>::Balance: From<<<Runtime as pezpallet_bridge_messages::Config<<Runtime as pezpallet_xcm_bridge_hub::Config<XcmOverBridgePalletInstance>>::BridgeMessagesPalletInstance>>::ThisChain as bp_runtime::Chain>::Balance>,
<Runtime as pezpallet_balances::Config>::Balance: From<u128>,
LocationToAccountId: ConvertLocation<AccountIdOf<Runtime>>,
TokenLocation: Get<Location>
{
// construct expected bridge configuration
let locations =
pezpallet_xcm_bridge_hub::Pallet::<Runtime, XcmOverBridgePalletInstance>::bridge_locations(
expected_source.clone().into(),
bridge_destination_universal_location.clone().into(),
)
.expect("valid bridge locations");
assert!(pezpallet_xcm_bridge_hub::Bridges::<Runtime, XcmOverBridgePalletInstance>::get(
locations.bridge_id()
)
.is_some());
// required balance: ED + fee + BridgeDeposit
let maybe_paid_execution = if is_paid_xcm_execution {
// random high enough value for `BuyExecution` fees
let buy_execution_fee_amount = 2_500_000_000_000_u128;
let buy_execution_fee = (TokenLocation::get(), buy_execution_fee_amount).into();
let balance_needed = <Runtime as pezpallet_balances::Config>::ExistentialDeposit::get() +
buy_execution_fee_amount.into();
let source_account_id =
LocationToAccountId::convert_location(&expected_source).expect("valid location");
let _ =
<pezpallet_balances::Pallet<Runtime>>::mint_into(&source_account_id, balance_needed)
.expect("mint_into passes");
Some(buy_execution_fee)
} else {
None
};
// close bridge with `Transact` call
let close_bridge_call = RuntimeCallOf::<Runtime>::from(BridgeXcmOverBridgeCall::<
Runtime,
XcmOverBridgePalletInstance,
>::close_bridge {
bridge_destination_universal_location: Box::new(
bridge_destination_universal_location.into(),
),
may_prune_messages: 16,
});
// execute XCM as source origin would do with `Transact -> Origin::Xcm`
assert_ok!(RuntimeHelper::<Runtime>::execute_as_origin(
(origin, origin_kind),
close_bridge_call,
maybe_paid_execution
)
.ensure_complete());
// bridge is closed
assert!(pezpallet_xcm_bridge_hub::Bridges::<Runtime, XcmOverBridgePalletInstance>::get(
locations.bridge_id()
)
.is_none());
// check state
assert_ok!(
pezpallet_xcm_bridge_hub::Pallet::<Runtime, XcmOverBridgePalletInstance>::do_try_state()
);
}
}
@@ -0,0 +1,808 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Pezcumulus.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Module contains predefined test-case scenarios for `Runtime` with bridging capabilities.
//!
//! This file contains tests, suitable for all bridge runtimes. See `from_teyrchain` and
//! `from_grandpa_chain` submodules for tests, that are specific to the bridged chain type.
pub mod from_grandpa_chain;
pub mod from_teyrchain;
pub(crate) mod helpers;
use crate::{test_cases::bridges_prelude::*, test_data};
use asset_test_utils::BasicTeyrchainRuntime;
use bp_messages::{
target_chain::{DispatchMessage, DispatchMessageData, MessageDispatch},
LaneState, MessageKey, MessagesOperatingMode, OutboundLaneData,
};
use bp_runtime::BasicOperatingMode;
use codec::Encode;
use pezframe_support::{
assert_ok,
dispatch::GetDispatchInfo,
traits::{Contains, Get, OnFinalize, OnInitialize, OriginTrait},
};
use pezframe_system::pezpallet_prelude::BlockNumberFor;
use pezsp_runtime::{traits::Zero, AccountId32};
use teyrchains_common::AccountId;
use teyrchains_runtimes_test_utils::{
mock_open_hrmp_channel, AccountIdOf, BalanceOf, CollatorSessionKeys, ExtBuilder,
GovernanceOrigin, RuntimeCallOf, RuntimeOriginOf, SlotDurations, XcmReceivedFrom,
};
use xcm::{latest::prelude::*, AlwaysLatest};
use xcm_builder::DispatchBlobError;
use xcm_executor::{
traits::{ConvertLocation, TransactAsset, WeightBounds},
XcmExecutor,
};
/// Common bridges exports.
pub(crate) mod bridges_prelude {
pub use bp_teyrchains::{RelayBlockHash, RelayBlockNumber};
pub use pezpallet_bridge_grandpa::{Call as BridgeGrandpaCall, Config as BridgeGrandpaConfig};
pub use pezpallet_bridge_messages::{
Call as BridgeMessagesCall, Config as BridgeMessagesConfig, LanesManagerError,
};
pub use pezpallet_bridge_teyrchains::{
Call as BridgeTeyrchainsCall, Config as BridgeTeyrchainsConfig,
};
}
// Re-export test-case
pub use for_pallet_xcm_bridge_hub::open_and_close_bridge_works;
// Re-export test_case from assets
pub use asset_test_utils::include_teleports_for_native_asset_works;
use pezpallet_bridge_messages::LaneIdOf;
pub type RuntimeHelper<Runtime, AllPalletsWithoutSystem = ()> =
teyrchains_runtimes_test_utils::RuntimeHelper<Runtime, AllPalletsWithoutSystem>;
// Re-export test_case from `teyrchains-runtimes-test-utils`
pub use teyrchains_runtimes_test_utils::test_cases::{
change_storage_constant_by_governance_works, set_storage_keys_by_governance_works,
};
/// Prepare default runtime storage and run test within this context.
pub fn run_test<Runtime, T>(
collator_session_key: CollatorSessionKeys<Runtime>,
runtime_para_id: u32,
balances: Vec<(Runtime::AccountId, Runtime::Balance)>,
test: impl FnOnce() -> T,
) -> T
where
Runtime: BasicTeyrchainRuntime,
{
ExtBuilder::<Runtime>::default()
.with_collators(collator_session_key.collators())
.with_session_keys(collator_session_key.session_keys())
.with_safe_xcm_version(XCM_VERSION)
.with_para_id(runtime_para_id.into())
.with_balances(balances)
.with_tracing()
.build()
.execute_with(|| test())
}
/// Test-case makes sure that `Runtime` can process bridging initialize via governance-like call
pub fn initialize_bridge_by_governance_works<Runtime, GrandpaPalletInstance>(
collator_session_key: CollatorSessionKeys<Runtime>,
runtime_para_id: u32,
governance_origin: GovernanceOrigin<RuntimeOriginOf<Runtime>>,
) where
Runtime: BasicTeyrchainRuntime + BridgeGrandpaConfig<GrandpaPalletInstance>,
GrandpaPalletInstance: 'static,
RuntimeCallOf<Runtime>:
GetDispatchInfo + From<BridgeGrandpaCall<Runtime, GrandpaPalletInstance>>,
{
run_test::<Runtime, _>(collator_session_key, runtime_para_id, vec![], || {
// check mode before
assert_eq!(
pezpallet_bridge_grandpa::PalletOperatingMode::<Runtime, GrandpaPalletInstance>::try_get(),
Err(())
);
// prepare the `initialize` call
let initialize_call = RuntimeCallOf::<Runtime>::from(BridgeGrandpaCall::<
Runtime,
GrandpaPalletInstance,
>::initialize {
init_data: test_data::initialization_data::<Runtime, GrandpaPalletInstance>(12345),
});
// execute XCM with Transacts to `initialize bridge` as governance does
assert_ok!(RuntimeHelper::<Runtime>::execute_as_governance_call(
initialize_call,
governance_origin
));
// check mode after
assert_eq!(
pezpallet_bridge_grandpa::PalletOperatingMode::<Runtime, GrandpaPalletInstance>::try_get(),
Ok(BasicOperatingMode::Normal)
);
})
}
/// Test-case makes sure that `Runtime` can change bridge GRANDPA pallet operating mode via
/// governance-like call.
pub fn change_bridge_grandpa_pallet_mode_by_governance_works<Runtime, GrandpaPalletInstance>(
collator_session_key: CollatorSessionKeys<Runtime>,
runtime_para_id: u32,
governance_origin: GovernanceOrigin<RuntimeOriginOf<Runtime>>,
) where
Runtime: BasicTeyrchainRuntime + BridgeGrandpaConfig<GrandpaPalletInstance>,
GrandpaPalletInstance: 'static,
RuntimeCallOf<Runtime>:
GetDispatchInfo + From<BridgeGrandpaCall<Runtime, GrandpaPalletInstance>>,
{
run_test::<Runtime, _>(collator_session_key, runtime_para_id, vec![], || {
let dispatch_set_operating_mode_call = |old_mode, new_mode| {
// check old mode
assert_eq!(
pezpallet_bridge_grandpa::PalletOperatingMode::<Runtime, GrandpaPalletInstance>::get(),
old_mode,
);
// prepare the `set_operating_mode` call
let set_operating_mode_call = <Runtime as pezframe_system::Config>::RuntimeCall::from(
pezpallet_bridge_grandpa::Call::<Runtime, GrandpaPalletInstance>::set_operating_mode {
operating_mode: new_mode,
},
);
// execute XCM with Transacts to `initialize bridge` as governance does
assert_ok!(RuntimeHelper::<Runtime>::execute_as_governance_call(
set_operating_mode_call,
governance_origin.clone()
));
// check mode after
assert_eq!(
pezpallet_bridge_grandpa::PalletOperatingMode::<Runtime, GrandpaPalletInstance>::try_get(),
Ok(new_mode)
);
};
// check mode before
assert_eq!(
pezpallet_bridge_grandpa::PalletOperatingMode::<Runtime, GrandpaPalletInstance>::try_get(),
Err(())
);
dispatch_set_operating_mode_call(BasicOperatingMode::Normal, BasicOperatingMode::Halted);
dispatch_set_operating_mode_call(BasicOperatingMode::Halted, BasicOperatingMode::Normal);
});
}
/// Test-case makes sure that `Runtime` can change bridge teyrchains pallet operating mode via
/// governance-like call.
pub fn change_bridge_teyrchains_pallet_mode_by_governance_works<Runtime, TeyrchainsPalletInstance>(
collator_session_key: CollatorSessionKeys<Runtime>,
runtime_para_id: u32,
governance_origin: GovernanceOrigin<RuntimeOriginOf<Runtime>>,
) where
Runtime: BasicTeyrchainRuntime + BridgeTeyrchainsConfig<TeyrchainsPalletInstance>,
TeyrchainsPalletInstance: 'static,
RuntimeCallOf<Runtime>:
GetDispatchInfo + From<BridgeTeyrchainsCall<Runtime, TeyrchainsPalletInstance>>,
{
run_test::<Runtime, _>(collator_session_key, runtime_para_id, vec![], || {
let dispatch_set_operating_mode_call = |old_mode, new_mode| {
// check old mode
assert_eq!(
pezpallet_bridge_teyrchains::PalletOperatingMode::<Runtime, TeyrchainsPalletInstance>::get(),
old_mode,
);
// prepare the `set_operating_mode` call
let set_operating_mode_call =
RuntimeCallOf::<Runtime>::from(pezpallet_bridge_teyrchains::Call::<
Runtime,
TeyrchainsPalletInstance,
>::set_operating_mode {
operating_mode: new_mode,
});
// execute XCM with Transacts to `initialize bridge` as governance does
assert_ok!(RuntimeHelper::<Runtime>::execute_as_governance_call(
set_operating_mode_call,
governance_origin.clone()
));
// check mode after
assert_eq!(
pezpallet_bridge_teyrchains::PalletOperatingMode::<Runtime, TeyrchainsPalletInstance>::try_get(),
Ok(new_mode)
);
};
// check mode before
assert_eq!(
pezpallet_bridge_teyrchains::PalletOperatingMode::<Runtime, TeyrchainsPalletInstance>::try_get(),
Err(())
);
dispatch_set_operating_mode_call(BasicOperatingMode::Normal, BasicOperatingMode::Halted);
dispatch_set_operating_mode_call(BasicOperatingMode::Halted, BasicOperatingMode::Normal);
});
}
/// Test-case makes sure that `Runtime` can change bridge messaging pallet operating mode via
/// governance-like call.
pub fn change_bridge_messages_pallet_mode_by_governance_works<Runtime, MessagesPalletInstance>(
collator_session_key: CollatorSessionKeys<Runtime>,
runtime_para_id: u32,
governance_origin: GovernanceOrigin<RuntimeOriginOf<Runtime>>,
) where
Runtime: BasicTeyrchainRuntime + BridgeMessagesConfig<MessagesPalletInstance>,
MessagesPalletInstance: 'static,
RuntimeCallOf<Runtime>:
GetDispatchInfo + From<BridgeMessagesCall<Runtime, MessagesPalletInstance>>,
{
run_test::<Runtime, _>(collator_session_key, runtime_para_id, vec![], || {
let dispatch_set_operating_mode_call = |old_mode, new_mode| {
// check old mode
assert_eq!(
pezpallet_bridge_messages::PalletOperatingMode::<Runtime, MessagesPalletInstance>::get(
),
old_mode,
);
// encode `set_operating_mode` call
let set_operating_mode_call = RuntimeCallOf::<Runtime>::from(BridgeMessagesCall::<
Runtime,
MessagesPalletInstance,
>::set_operating_mode {
operating_mode: new_mode,
});
// execute XCM with Transacts to `initialize bridge` as governance does
assert_ok!(RuntimeHelper::<Runtime>::execute_as_governance_call(
set_operating_mode_call,
governance_origin.clone()
));
// check mode after
assert_eq!(
pezpallet_bridge_messages::PalletOperatingMode::<Runtime, MessagesPalletInstance>::try_get(),
Ok(new_mode)
);
};
// check mode before
assert_eq!(
pezpallet_bridge_messages::PalletOperatingMode::<Runtime, MessagesPalletInstance>::try_get(
),
Err(())
);
dispatch_set_operating_mode_call(
MessagesOperatingMode::Basic(BasicOperatingMode::Normal),
MessagesOperatingMode::RejectingOutboundMessages,
);
dispatch_set_operating_mode_call(
MessagesOperatingMode::RejectingOutboundMessages,
MessagesOperatingMode::Basic(BasicOperatingMode::Halted),
);
dispatch_set_operating_mode_call(
MessagesOperatingMode::Basic(BasicOperatingMode::Halted),
MessagesOperatingMode::Basic(BasicOperatingMode::Normal),
);
});
}
/// Test-case makes sure that `Runtime` can handle xcm `ExportMessage`:
/// Checks if received XCM messages is correctly added to the message outbound queue for delivery.
/// For SystemTeyrchains we expect unpaid execution.
pub fn handle_export_message_from_system_teyrchain_to_outbound_queue_works<
Runtime,
XcmConfig,
MessagesPalletInstance,
>(
collator_session_key: CollatorSessionKeys<Runtime>,
runtime_para_id: u32,
sibling_teyrchain_id: u32,
unwrap_pallet_bridge_messages_event: Box<
dyn Fn(Vec<u8>) -> Option<pezpallet_bridge_messages::Event<Runtime, MessagesPalletInstance>>,
>,
export_message_instruction: fn() -> Instruction<XcmConfig::RuntimeCall>,
existential_deposit: Option<Asset>,
maybe_paid_export_message: Option<Asset>,
prepare_configuration: impl Fn() -> LaneIdOf<Runtime, MessagesPalletInstance>,
) where
Runtime: BasicTeyrchainRuntime + BridgeMessagesConfig<MessagesPalletInstance>,
XcmConfig: xcm_executor::Config,
MessagesPalletInstance: 'static,
{
assert_ne!(runtime_para_id, sibling_teyrchain_id);
let sibling_teyrchain_location = Location::new(1, [Teyrchain(sibling_teyrchain_id)]);
run_test::<Runtime, _>(collator_session_key, runtime_para_id, vec![], || {
let expected_lane_id = prepare_configuration();
// check queue before
assert_eq!(
pezpallet_bridge_messages::OutboundLanes::<Runtime, MessagesPalletInstance>::try_get(
expected_lane_id
),
Ok(OutboundLaneData {
state: LaneState::Opened,
oldest_unpruned_nonce: 1,
latest_received_nonce: 0,
latest_generated_nonce: 0
})
);
// prepare `ExportMessage`
let xcm = if let Some(fee) = maybe_paid_export_message {
// deposit ED to origin (if needed)
if let Some(ed) = existential_deposit {
XcmConfig::AssetTransactor::deposit_asset(
&ed,
&sibling_teyrchain_location,
Some(&XcmContext::with_message_id([0; 32])),
)
.expect("deposited ed");
}
// deposit fee to origin
XcmConfig::AssetTransactor::deposit_asset(
&fee,
&sibling_teyrchain_location,
Some(&XcmContext::with_message_id([0; 32])),
)
.expect("deposited fee");
Xcm(vec![
WithdrawAsset(Assets::from(vec![fee.clone()])),
BuyExecution { fees: fee, weight_limit: Unlimited },
export_message_instruction(),
])
} else {
Xcm(vec![
UnpaidExecution { weight_limit: Unlimited, check_origin: None },
export_message_instruction(),
])
};
// execute XCM
let mut hash = xcm.using_encoded(pezsp_io::hashing::blake2_256);
assert_ok!(XcmExecutor::<XcmConfig>::prepare_and_execute(
sibling_teyrchain_location,
xcm,
&mut hash,
RuntimeHelper::<Runtime>::xcm_max_weight(XcmReceivedFrom::Sibling),
Weight::zero(),
)
.ensure_complete());
// check queue after
assert_eq!(
pezpallet_bridge_messages::OutboundLanes::<Runtime, MessagesPalletInstance>::try_get(
expected_lane_id
),
Ok(OutboundLaneData {
state: LaneState::Opened,
oldest_unpruned_nonce: 1,
latest_received_nonce: 0,
latest_generated_nonce: 1,
})
);
// check events
let mut events = <pezframe_system::Pallet<Runtime>>::events()
.into_iter()
.filter_map(|e| unwrap_pallet_bridge_messages_event(e.event.encode()));
assert!(events.any(|e| matches!(e, pezpallet_bridge_messages::Event::MessageAccepted { .. })));
})
}
/// Test-case makes sure that Runtime can route XCM messages received in inbound queue,
/// We just test here `MessageDispatch` configuration.
/// We expect that runtime can route messages:
/// 1. to Parent (relay chain)
/// 2. to Sibling teyrchain
pub fn message_dispatch_routing_works<
Runtime,
AllPalletsWithoutSystem,
XcmConfig,
HrmpChannelOpener,
MessagesPalletInstance,
RuntimeNetwork,
BridgedNetwork,
NetworkDistanceAsParentCount,
>(
collator_session_key: CollatorSessionKeys<Runtime>,
slot_durations: SlotDurations,
runtime_para_id: u32,
sibling_teyrchain_id: u32,
unwrap_cumulus_pallet_teyrchain_system_event: Box<
dyn Fn(Vec<u8>) -> Option<cumulus_pallet_teyrchain_system::Event<Runtime>>,
>,
unwrap_cumulus_pallet_xcmp_queue_event: Box<
dyn Fn(Vec<u8>) -> Option<cumulus_pallet_xcmp_queue::Event<Runtime>>,
>,
prepare_configuration: impl Fn(),
) where
Runtime: BasicTeyrchainRuntime
+ cumulus_pallet_xcmp_queue::Config
+ BridgeMessagesConfig<MessagesPalletInstance, InboundPayload = test_data::XcmAsPlainPayload>,
AllPalletsWithoutSystem:
OnInitialize<BlockNumberFor<Runtime>> + OnFinalize<BlockNumberFor<Runtime>>,
AccountIdOf<Runtime>: From<AccountId32>
+ Into<<<Runtime as pezframe_system::Config>::RuntimeOrigin as OriginTrait>::AccountId>,
XcmConfig: xcm_executor::Config,
MessagesPalletInstance: 'static,
HrmpChannelOpener: pezframe_support::inherent::ProvideInherent<
Call = cumulus_pallet_teyrchain_system::Call<Runtime>,
>,
RuntimeNetwork: Get<NetworkId>,
BridgedNetwork: Get<NetworkId>,
NetworkDistanceAsParentCount: Get<u8>,
{
struct NetworkWithParentCount<N, C>(core::marker::PhantomData<(N, C)>);
impl<N: Get<NetworkId>, C: Get<u8>> Get<Location> for NetworkWithParentCount<N, C> {
fn get() -> Location {
Location::new(C::get(), [GlobalConsensus(N::get())])
}
}
assert_ne!(runtime_para_id, sibling_teyrchain_id);
#[derive(Debug)]
enum XcmBlobMessageDispatchResult {
Dispatched,
#[allow(dead_code)]
NotDispatched(Option<DispatchBlobError>),
}
run_test::<Runtime, _>(collator_session_key, runtime_para_id, vec![], || {
prepare_configuration();
let dummy_lane_id = LaneIdOf::<Runtime, MessagesPalletInstance>::default();
let mut alice = [0u8; 32];
alice[0] = 1;
let included_head = RuntimeHelper::<Runtime, AllPalletsWithoutSystem>::run_to_block(
2,
AccountId::from(alice).into(),
);
// 1. this message is sent from other global consensus with destination of this Runtime
// relay chain (UMP)
let bridging_message = test_data::simulate_message_exporter_on_bridged_chain::<
BridgedNetwork,
NetworkWithParentCount<RuntimeNetwork, NetworkDistanceAsParentCount>,
AlwaysLatest,
>((RuntimeNetwork::get(), Here));
let result =
<<Runtime as BridgeMessagesConfig<MessagesPalletInstance>>::MessageDispatch>::dispatch(
test_data::dispatch_message(dummy_lane_id, 1, bridging_message),
);
assert_eq!(
format!("{:?}", result.dispatch_level_result),
format!("{:?}", XcmBlobMessageDispatchResult::Dispatched)
);
// check events - UpwardMessageSent
let mut events = <pezframe_system::Pallet<Runtime>>::events()
.into_iter()
.filter_map(|e| unwrap_cumulus_pallet_teyrchain_system_event(e.event.encode()));
assert!(events.any(|e| matches!(
e,
cumulus_pallet_teyrchain_system::Event::UpwardMessageSent { .. }
)));
// 2. this message is sent from other global consensus with destination of this Runtime
// sibling teyrchain (HRMP)
let bridging_message =
test_data::simulate_message_exporter_on_bridged_chain::<
BridgedNetwork,
NetworkWithParentCount<RuntimeNetwork, NetworkDistanceAsParentCount>,
AlwaysLatest,
>((RuntimeNetwork::get(), [Teyrchain(sibling_teyrchain_id)].into()));
// 2.1. WITHOUT opened hrmp channel -> RoutingError
let result =
<<Runtime as BridgeMessagesConfig<MessagesPalletInstance>>::MessageDispatch>::dispatch(
DispatchMessage {
key: MessageKey { lane_id: dummy_lane_id, nonce: 1 },
data: DispatchMessageData { payload: Ok(bridging_message.clone()) },
},
);
assert_eq!(
format!("{:?}", result.dispatch_level_result),
format!(
"{:?}",
XcmBlobMessageDispatchResult::NotDispatched(Some(DispatchBlobError::RoutingError))
)
);
// check events - no XcmpMessageSent
assert_eq!(
<pezframe_system::Pallet<Runtime>>::events()
.into_iter()
.filter_map(|e| unwrap_cumulus_pallet_xcmp_queue_event(e.event.encode()))
.count(),
0
);
// 2.1. WITH hrmp channel -> Ok
mock_open_hrmp_channel::<Runtime, HrmpChannelOpener>(
runtime_para_id.into(),
sibling_teyrchain_id.into(),
included_head,
&alice,
&slot_durations,
);
let result =
<<Runtime as BridgeMessagesConfig<MessagesPalletInstance>>::MessageDispatch>::dispatch(
DispatchMessage {
key: MessageKey { lane_id: dummy_lane_id, nonce: 1 },
data: DispatchMessageData { payload: Ok(bridging_message) },
},
);
assert_eq!(
format!("{:?}", result.dispatch_level_result),
format!("{:?}", XcmBlobMessageDispatchResult::Dispatched)
);
// check events - XcmpMessageSent
let mut events = <pezframe_system::Pallet<Runtime>>::events()
.into_iter()
.filter_map(|e| unwrap_cumulus_pallet_xcmp_queue_event(e.event.encode()));
assert!(
events.any(|e| matches!(e, cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }))
);
})
}
/// Estimates XCM execution fee for paid `ExportMessage` processing.
pub fn can_calculate_weight_for_paid_export_message_with_reserve_transfer<
Runtime,
XcmConfig,
WeightToFee,
>() -> u128
where
Runtime: pezframe_system::Config + pezpallet_balances::Config,
XcmConfig: xcm_executor::Config,
WeightToFee: pezframe_support::weights::WeightToFee<Balance = BalanceOf<Runtime>>,
<WeightToFee as pezframe_support::weights::WeightToFee>::Balance: From<u128> + Into<u128>,
{
// data here are not relevant for weighing
let mut xcm = Xcm(vec![
WithdrawAsset(Assets::from(vec![Asset {
id: AssetId(Location::new(1, [])),
fun: Fungible(34333299),
}])),
BuyExecution {
fees: Asset { id: AssetId(Location::new(1, [])), fun: Fungible(34333299) },
weight_limit: Unlimited,
},
SetAppendix(Xcm(vec![DepositAsset {
assets: Wild(AllCounted(1)),
beneficiary: Location::new(1, [Teyrchain(1000)]),
}])),
ExportMessage {
network: Pezkuwi,
destination: [Teyrchain(1000)].into(),
xcm: Xcm(vec![
ReserveAssetDeposited(Assets::from(vec![Asset {
id: AssetId(Location::new(2, [GlobalConsensus(Kusama)])),
fun: Fungible(1000000000000),
}])),
ClearOrigin,
BuyExecution {
fees: Asset {
id: AssetId(Location::new(2, [GlobalConsensus(Kusama)])),
fun: Fungible(1000000000000),
},
weight_limit: Unlimited,
},
DepositAsset {
assets: Wild(AllCounted(1)),
beneficiary: Location::new(
0,
[xcm::latest::prelude::AccountId32 {
network: None,
id: [
212, 53, 147, 199, 21, 253, 211, 28, 97, 20, 26, 189, 4, 169, 159,
214, 130, 44, 133, 88, 133, 76, 205, 227, 154, 86, 132, 231, 165,
109, 162, 125,
],
}],
),
},
SetTopic([
116, 82, 194, 132, 171, 114, 217, 165, 23, 37, 161, 177, 165, 179, 247, 114,
137, 101, 147, 70, 28, 157, 168, 32, 154, 63, 74, 228, 152, 180, 5, 63,
]),
]),
},
SetTopic([
36, 224, 250, 165, 82, 195, 67, 110, 160, 170, 140, 87, 217, 62, 201, 164, 42, 98, 219,
157, 124, 105, 248, 25, 131, 218, 199, 36, 109, 173, 100, 122,
]),
]);
// get weight
let weight = XcmConfig::Weigher::weight(&mut xcm, Weight::MAX);
assert_ok!(weight);
let weight = weight.unwrap();
// check if sane
let max_expected = Runtime::BlockWeights::get().max_block / 10;
assert!(
weight.all_lte(max_expected),
"calculated weight: {:?}, max_expected: {:?}",
weight,
max_expected
);
// check fee, should not be 0
let estimated_fee = WeightToFee::weight_to_fee(&weight);
assert!(estimated_fee > BalanceOf::<Runtime>::zero());
estimated_fee.into()
}
pub(crate) mod for_pallet_xcm_bridge_hub {
use super::*;
use crate::test_cases::helpers::for_pallet_xcm_bridge_hub::{
close_bridge, ensure_opened_bridge, open_bridge_with_extrinsic,
};
pub(crate) use pezpallet_xcm_bridge_hub::{
Bridge, BridgeState, Call as BridgeXcmOverBridgeCall, Config as BridgeXcmOverBridgeConfig,
LanesManagerOf,
};
/// Test-case makes sure that `Runtime` can open/close bridges.
pub fn open_and_close_bridge_works<Runtime, XcmOverBridgePalletInstance, LocationToAccountId, TokenLocation>(
collator_session_key: CollatorSessionKeys<Runtime>,
runtime_para_id: u32,
expected_source: Location,
destination: InteriorLocation,
origin_with_origin_kind: (Location, OriginKind),
is_paid_xcm_execution: bool,
) where
Runtime: BasicTeyrchainRuntime + BridgeXcmOverBridgeConfig<XcmOverBridgePalletInstance>,
XcmOverBridgePalletInstance: 'static,
<Runtime as pezframe_system::Config>::RuntimeCall: GetDispatchInfo + From<BridgeXcmOverBridgeCall<Runtime, XcmOverBridgePalletInstance>>,
<Runtime as pezpallet_balances::Config>::Balance: From<<<Runtime as pezpallet_bridge_messages::Config<<Runtime as pezpallet_xcm_bridge_hub::Config<XcmOverBridgePalletInstance>>::BridgeMessagesPalletInstance>>::ThisChain as bp_runtime::Chain>::Balance>,
<Runtime as pezpallet_balances::Config>::Balance: From<u128>,
<<Runtime as pezpallet_bridge_messages::Config<<Runtime as pezpallet_xcm_bridge_hub::Config<XcmOverBridgePalletInstance>>::BridgeMessagesPalletInstance>>::ThisChain as bp_runtime::Chain>::AccountId: From<<Runtime as pezframe_system::Config>::AccountId>,
LocationToAccountId: ConvertLocation<AccountIdOf<Runtime>>,
TokenLocation: Get<Location>,
{
run_test::<Runtime, _>(collator_session_key, runtime_para_id, vec![], || {
// construct expected bridge configuration
let locations = pezpallet_xcm_bridge_hub::Pallet::<Runtime, XcmOverBridgePalletInstance>::bridge_locations(
expected_source.clone().into(),
destination.clone().into(),
).expect("valid bridge locations");
let expected_lane_id =
locations.calculate_lane_id(xcm::latest::VERSION).expect("valid laneId");
let lanes_manager = LanesManagerOf::<Runtime, XcmOverBridgePalletInstance>::new();
let expected_deposit = if <Runtime as pezpallet_xcm_bridge_hub::Config<
XcmOverBridgePalletInstance,
>>::AllowWithoutBridgeDeposit::contains(
locations.bridge_origin_relative_location()
) {
Zero::zero()
} else {
<Runtime as pezpallet_xcm_bridge_hub::Config<
XcmOverBridgePalletInstance,
>>::BridgeDeposit::get()
};
// check bridge/lane DOES not exist
assert_eq!(
pezpallet_xcm_bridge_hub::Bridges::<Runtime, XcmOverBridgePalletInstance>::get(
locations.bridge_id()
),
None
);
assert_eq!(
lanes_manager.active_inbound_lane(expected_lane_id).map(drop),
Err(LanesManagerError::UnknownInboundLane)
);
assert_eq!(
lanes_manager.active_outbound_lane(expected_lane_id).map(drop),
Err(LanesManagerError::UnknownOutboundLane)
);
// open bridge with Transact call
assert_eq!(
ensure_opened_bridge::<
Runtime,
XcmOverBridgePalletInstance,
LocationToAccountId,
TokenLocation,
>(
expected_source.clone(),
destination.clone(),
is_paid_xcm_execution,
|locations, maybe_paid_execution| open_bridge_with_extrinsic::<
Runtime,
XcmOverBridgePalletInstance,
>(
origin_with_origin_kind.clone(),
locations.bridge_destination_universal_location().clone(),
maybe_paid_execution
)
)
.0
.bridge_id(),
locations.bridge_id()
);
// check bridge/lane DOES exist
assert_eq!(
pezpallet_xcm_bridge_hub::Bridges::<Runtime, XcmOverBridgePalletInstance>::get(
locations.bridge_id()
),
Some(Bridge {
bridge_origin_relative_location: Box::new(expected_source.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: LocationToAccountId::convert_location(&expected_source)
.expect("valid location")
.into(),
deposit: expected_deposit,
lane_id: expected_lane_id,
})
);
assert_eq!(
lanes_manager.active_inbound_lane(expected_lane_id).map(|lane| lane.state()),
Ok(LaneState::Opened)
);
assert_eq!(
lanes_manager.active_outbound_lane(expected_lane_id).map(|lane| lane.state()),
Ok(LaneState::Opened)
);
// close bridge with Transact call
close_bridge::<Runtime, XcmOverBridgePalletInstance, LocationToAccountId, TokenLocation>(
expected_source,
destination,
origin_with_origin_kind,
is_paid_xcm_execution,
);
// check bridge/lane DOES not exist
assert_eq!(
pezpallet_xcm_bridge_hub::Bridges::<Runtime, XcmOverBridgePalletInstance>::get(
locations.bridge_id()
),
None
);
assert_eq!(
lanes_manager.active_inbound_lane(expected_lane_id).map(drop),
Err(LanesManagerError::UnknownInboundLane)
);
assert_eq!(
lanes_manager.active_outbound_lane(expected_lane_id).map(drop),
Err(LanesManagerError::UnknownOutboundLane)
);
});
}
}
@@ -0,0 +1,296 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Pezcumulus.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Generating test data for bridges with remote GRANDPA chains.
use crate::test_data::{prepare_inbound_xcm, XcmAsPlainPayload};
use bp_messages::{
source_chain::FromBridgedChainMessagesDeliveryProof,
target_chain::FromBridgedChainMessagesProof, ChainWithMessages, LaneState, MessageNonce,
UnrewardedRelayersState,
};
use bp_runtime::{AccountIdOf, BlockNumberOf, Chain, HeaderOf, UnverifiedStorageProofParams};
use bp_test_utils::make_default_justification;
use codec::Encode;
use pezpallet_bridge_grandpa::{BridgedChain, BridgedHeader};
use pezsp_runtime::traits::Header as HeaderT;
use xcm::latest::prelude::*;
use crate::test_cases::helpers::InboundRelayerId;
use bp_header_chain::{justification::GrandpaJustification, ChainWithGrandpa};
use bp_messages::{DeliveredMessages, InboundLaneData, UnrewardedRelayer};
use bp_runtime::HashOf;
use pezpallet_bridge_messages::{
messages_generation::{
encode_all_messages, encode_lane_data, prepare_message_delivery_storage_proof,
prepare_messages_storage_proof,
},
BridgedChainOf, LaneIdOf,
};
use pezsp_runtime::DigestItem;
/// Prepare a batch call with bridged GRANDPA finality and message proof.
pub fn make_complex_relayer_delivery_batch<Runtime, GPI, MPI>(
bridged_header: BridgedHeader<Runtime, GPI>,
bridged_justification: GrandpaJustification<BridgedHeader<Runtime, GPI>>,
message_proof: FromBridgedChainMessagesProof<
HashOf<BridgedChain<Runtime, GPI>>,
LaneIdOf<Runtime, MPI>,
>,
relayer_id_at_bridged_chain: InboundRelayerId<Runtime, MPI>,
) -> pezpallet_utility::Call<Runtime>
where
Runtime: pezpallet_bridge_grandpa::Config<GPI>
+ pezpallet_bridge_messages::Config<MPI, InboundPayload = XcmAsPlainPayload>
+ pezpallet_utility::Config,
GPI: 'static,
MPI: 'static,
<Runtime as pezpallet_utility::Config>::RuntimeCall: From<pezpallet_bridge_grandpa::Call<Runtime, GPI>>
+ From<pezpallet_bridge_messages::Call<Runtime, MPI>>,
BridgedChainOf<Runtime, MPI>: Chain<Hash = HashOf<BridgedChain<Runtime, GPI>>>,
{
let submit_grandpa = pezpallet_bridge_grandpa::Call::<Runtime, GPI>::submit_finality_proof {
finality_target: Box::new(bridged_header),
justification: bridged_justification,
};
let submit_message = pezpallet_bridge_messages::Call::<Runtime, MPI>::receive_messages_proof {
relayer_id_at_bridged_chain,
proof: Box::new(message_proof),
messages_count: 1,
dispatch_weight: Weight::from_parts(1000000000, 0),
};
pezpallet_utility::Call::<Runtime>::batch_all {
calls: vec![submit_grandpa.into(), submit_message.into()],
}
}
/// Prepare a batch call with bridged GRANDPA finality and message delivery proof.
pub fn make_complex_relayer_confirmation_batch<Runtime, GPI, MPI>(
bridged_header: BridgedHeader<Runtime, GPI>,
bridged_justification: GrandpaJustification<BridgedHeader<Runtime, GPI>>,
message_delivery_proof: FromBridgedChainMessagesDeliveryProof<
HashOf<BridgedChain<Runtime, GPI>>,
LaneIdOf<Runtime, MPI>,
>,
relayers_state: UnrewardedRelayersState,
) -> pezpallet_utility::Call<Runtime>
where
Runtime: pezpallet_bridge_grandpa::Config<GPI>
+ pezpallet_bridge_messages::Config<MPI, OutboundPayload = XcmAsPlainPayload>
+ pezpallet_utility::Config,
GPI: 'static,
MPI: 'static,
<Runtime as pezpallet_utility::Config>::RuntimeCall: From<pezpallet_bridge_grandpa::Call<Runtime, GPI>>
+ From<pezpallet_bridge_messages::Call<Runtime, MPI>>,
BridgedChainOf<Runtime, MPI>: Chain<Hash = HashOf<BridgedChain<Runtime, GPI>>>,
{
let submit_grandpa = pezpallet_bridge_grandpa::Call::<Runtime, GPI>::submit_finality_proof {
finality_target: Box::new(bridged_header),
justification: bridged_justification,
};
let submit_message_delivery_proof =
pezpallet_bridge_messages::Call::<Runtime, MPI>::receive_messages_delivery_proof {
proof: message_delivery_proof,
relayers_state,
};
pezpallet_utility::Call::<Runtime>::batch_all {
calls: vec![submit_grandpa.into(), submit_message_delivery_proof.into()],
}
}
/// Prepare a call with message proof.
pub fn make_standalone_relayer_delivery_call<Runtime, GPI, MPI>(
message_proof: FromBridgedChainMessagesProof<
HashOf<BridgedChain<Runtime, GPI>>,
LaneIdOf<Runtime, MPI>,
>,
relayer_id_at_bridged_chain: InboundRelayerId<Runtime, MPI>,
) -> Runtime::RuntimeCall
where
Runtime: pezpallet_bridge_grandpa::Config<GPI>
+ pezpallet_bridge_messages::Config<MPI, InboundPayload = XcmAsPlainPayload>,
MPI: 'static,
Runtime::RuntimeCall: From<pezpallet_bridge_messages::Call<Runtime, MPI>>,
BridgedChainOf<Runtime, MPI>: Chain<Hash = HashOf<BridgedChain<Runtime, GPI>>>,
{
pezpallet_bridge_messages::Call::<Runtime, MPI>::receive_messages_proof {
relayer_id_at_bridged_chain,
proof: Box::new(message_proof),
messages_count: 1,
dispatch_weight: Weight::from_parts(1000000000, 0),
}
.into()
}
/// Prepare a call with message delivery proof.
pub fn make_standalone_relayer_confirmation_call<Runtime, GPI, MPI>(
message_delivery_proof: FromBridgedChainMessagesDeliveryProof<
HashOf<BridgedChain<Runtime, GPI>>,
LaneIdOf<Runtime, MPI>,
>,
relayers_state: UnrewardedRelayersState,
) -> Runtime::RuntimeCall
where
Runtime: pezpallet_bridge_grandpa::Config<GPI>
+ pezpallet_bridge_messages::Config<MPI, OutboundPayload = XcmAsPlainPayload>,
MPI: 'static,
Runtime::RuntimeCall: From<pezpallet_bridge_messages::Call<Runtime, MPI>>,
BridgedChainOf<Runtime, MPI>: Chain<Hash = HashOf<BridgedChain<Runtime, GPI>>>,
{
pezpallet_bridge_messages::Call::<Runtime, MPI>::receive_messages_delivery_proof {
proof: message_delivery_proof,
relayers_state,
}
.into()
}
/// Prepare storage proofs of messages, stored at the (bridged) source GRANDPA chain.
pub fn make_complex_relayer_delivery_proofs<BridgedChain, ThisChainWithMessages, LaneId>(
lane_id: LaneId,
xcm_message: Xcm<()>,
message_nonce: MessageNonce,
message_destination: Junctions,
header_number: BlockNumberOf<BridgedChain>,
is_minimal_call: bool,
) -> (
HeaderOf<BridgedChain>,
GrandpaJustification<HeaderOf<BridgedChain>>,
FromBridgedChainMessagesProof<HashOf<BridgedChain>, LaneId>,
)
where
BridgedChain: ChainWithGrandpa,
ThisChainWithMessages: ChainWithMessages,
LaneId: Copy + Encode,
{
// prepare message
let message_payload = prepare_inbound_xcm(xcm_message, message_destination);
// prepare storage proof containing message
let (state_root, storage_proof) =
prepare_messages_storage_proof::<BridgedChain, ThisChainWithMessages, LaneId>(
lane_id,
message_nonce..=message_nonce,
None,
UnverifiedStorageProofParams::from_db_size(message_payload.len() as u32),
|_| message_payload.clone(),
encode_all_messages,
encode_lane_data,
false,
false,
);
let (header, justification) = make_complex_bridged_grandpa_header_proof::<BridgedChain>(
state_root,
header_number,
is_minimal_call,
);
let message_proof = FromBridgedChainMessagesProof {
bridged_header_hash: header.hash(),
storage_proof,
lane: lane_id,
nonces_start: message_nonce,
nonces_end: message_nonce,
};
(header, justification, message_proof)
}
/// Prepare storage proofs of message confirmations, stored at the (bridged) target GRANDPA chain.
pub fn make_complex_relayer_confirmation_proofs<
BridgedChain,
ThisChainWithMessages,
InnerXcmRuntimeCall,
LaneId,
>(
lane_id: LaneId,
header_number: BlockNumberOf<BridgedChain>,
relayer_id_at_this_chain: AccountIdOf<ThisChainWithMessages>,
relayers_state: UnrewardedRelayersState,
) -> (
HeaderOf<BridgedChain>,
GrandpaJustification<HeaderOf<BridgedChain>>,
FromBridgedChainMessagesDeliveryProof<HashOf<BridgedChain>, LaneId>,
)
where
BridgedChain: ChainWithGrandpa,
ThisChainWithMessages: ChainWithMessages,
LaneId: Copy + Encode,
{
// prepare storage proof containing message delivery proof
let (state_root, storage_proof) =
prepare_message_delivery_storage_proof::<BridgedChain, ThisChainWithMessages, LaneId>(
lane_id,
InboundLaneData {
state: LaneState::Opened,
relayers: vec![
UnrewardedRelayer {
relayer: relayer_id_at_this_chain,
messages: DeliveredMessages::new(1)
};
relayers_state.unrewarded_relayer_entries as usize
]
.into(),
last_confirmed_nonce: 1,
},
UnverifiedStorageProofParams::default(),
);
let (header, justification) =
make_complex_bridged_grandpa_header_proof::<BridgedChain>(state_root, header_number, false);
let message_delivery_proof = FromBridgedChainMessagesDeliveryProof {
bridged_header_hash: header.hash(),
storage_proof,
lane: lane_id,
};
(header, justification, message_delivery_proof)
}
/// Make bridged GRANDPA chain header with given state root.
pub fn make_complex_bridged_grandpa_header_proof<BridgedChain>(
state_root: HashOf<BridgedChain>,
header_number: BlockNumberOf<BridgedChain>,
is_minimal_call: bool,
) -> (HeaderOf<BridgedChain>, GrandpaJustification<HeaderOf<BridgedChain>>)
where
BridgedChain: ChainWithGrandpa,
{
let mut header = bp_test_utils::test_header_with_root::<HeaderOf<BridgedChain>>(
header_number.into(),
state_root.into(),
);
// to compute proper cost of GRANDPA call, let's add some dummy bytes to header, so that the
// `submit_finality_proof` call size would be close to maximal expected (and refundable)
let extra_bytes_required = maximal_expected_submit_finality_proof_call_size::<BridgedChain>()
.saturating_sub(header.encoded_size());
if !is_minimal_call {
header.digest_mut().push(DigestItem::Other(vec![42; extra_bytes_required]));
}
let justification = make_default_justification(&header);
(header, justification)
}
/// Maximal expected `submit_finality_proof` call size.
pub fn maximal_expected_submit_finality_proof_call_size<BridgedChain: ChainWithGrandpa>() -> usize {
bp_header_chain::max_expected_submit_finality_proof_arguments_size::<BridgedChain>(
false,
BridgedChain::MAX_AUTHORITIES_COUNT * 2 / 3 + 1,
) as usize
}
@@ -0,0 +1,381 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Pezcumulus.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Generating test data for bridges with remote teyrchains.
use super::{
from_grandpa_chain::make_complex_bridged_grandpa_header_proof, prepare_inbound_xcm,
XcmAsPlainPayload,
};
use bp_messages::{
source_chain::FromBridgedChainMessagesDeliveryProof,
target_chain::FromBridgedChainMessagesProof, ChainWithMessages, LaneState,
UnrewardedRelayersState, Weight,
};
use bp_runtime::{
AccountIdOf, BlockNumberOf, Chain, HeaderOf, Teyrchain, UnverifiedStorageProofParams,
};
use bp_test_utils::prepare_teyrchain_heads_proof;
use bp_teyrchains::{RelayBlockHash, RelayBlockNumber};
use codec::Encode;
use pezpallet_bridge_grandpa::BridgedHeader;
use pezsp_runtime::traits::Header as HeaderT;
use xcm::latest::prelude::*;
use crate::test_cases::helpers::InboundRelayerId;
use bp_header_chain::{justification::GrandpaJustification, ChainWithGrandpa};
use bp_messages::{DeliveredMessages, InboundLaneData, MessageNonce, UnrewardedRelayer};
use bp_pezkuwi_core::teyrchains::{ParaHash, ParaHead, ParaHeadsProof, ParaId};
use pezpallet_bridge_messages::{
messages_generation::{
encode_all_messages, encode_lane_data, prepare_message_delivery_storage_proof,
prepare_messages_storage_proof,
},
BridgedChainOf, LaneIdOf,
};
use pezsp_runtime::SaturatedConversion;
/// Prepare a batch call with relay finality proof, teyrchain head proof and message proof.
pub fn make_complex_relayer_delivery_batch<Runtime, GPI, PPI, MPI>(
relay_chain_header: BridgedHeader<Runtime, GPI>,
grandpa_justification: GrandpaJustification<BridgedHeader<Runtime, GPI>>,
teyrchain_heads: Vec<(ParaId, ParaHash)>,
para_heads_proof: ParaHeadsProof,
message_proof: FromBridgedChainMessagesProof<ParaHash, LaneIdOf<Runtime, MPI>>,
relayer_id_at_bridged_chain: InboundRelayerId<Runtime, MPI>,
) -> pezpallet_utility::Call<Runtime>
where
Runtime: pezpallet_bridge_grandpa::Config<GPI>
+ pezpallet_bridge_teyrchains::Config<PPI>
+ pezpallet_bridge_messages::Config<MPI, InboundPayload = XcmAsPlainPayload>
+ pezpallet_utility::Config,
GPI: 'static,
PPI: 'static,
MPI: 'static,
ParaHash: From<
<<Runtime as pezpallet_bridge_grandpa::Config<GPI>>::BridgedChain as bp_runtime::Chain>::Hash,
>,
<<Runtime as pezpallet_bridge_grandpa::Config<GPI>>::BridgedChain as bp_runtime::Chain>::Hash:
From<ParaHash>,
BridgedChainOf<Runtime, MPI>: Chain<Hash = ParaHash> + Teyrchain,
<Runtime as pezpallet_utility::Config>::RuntimeCall: From<pezpallet_bridge_grandpa::Call<Runtime, GPI>>
+ From<pezpallet_bridge_teyrchains::Call<Runtime, PPI>>
+ From<pezpallet_bridge_messages::Call<Runtime, MPI>>,
{
let relay_chain_header_hash = relay_chain_header.hash();
let relay_chain_header_number = *relay_chain_header.number();
let submit_grandpa = pezpallet_bridge_grandpa::Call::<Runtime, GPI>::submit_finality_proof {
finality_target: Box::new(relay_chain_header),
justification: grandpa_justification,
};
let submit_para_head = pezpallet_bridge_teyrchains::Call::<Runtime, PPI>::submit_teyrchain_heads {
at_relay_block: (
relay_chain_header_number.saturated_into(),
relay_chain_header_hash.into(),
),
teyrchains: teyrchain_heads,
teyrchain_heads_proof: para_heads_proof,
};
let submit_message = pezpallet_bridge_messages::Call::<Runtime, MPI>::receive_messages_proof {
relayer_id_at_bridged_chain: relayer_id_at_bridged_chain.into(),
proof: Box::new(message_proof),
messages_count: 1,
dispatch_weight: Weight::from_parts(1000000000, 0),
};
pezpallet_utility::Call::<Runtime>::batch_all {
calls: vec![submit_grandpa.into(), submit_para_head.into(), submit_message.into()],
}
}
/// Prepare a batch call with relay finality proof, teyrchain head proof and message delivery
/// proof.
pub fn make_complex_relayer_confirmation_batch<Runtime, GPI, PPI, MPI>(
relay_chain_header: BridgedHeader<Runtime, GPI>,
grandpa_justification: GrandpaJustification<BridgedHeader<Runtime, GPI>>,
teyrchain_heads: Vec<(ParaId, ParaHash)>,
para_heads_proof: ParaHeadsProof,
message_delivery_proof: FromBridgedChainMessagesDeliveryProof<ParaHash, LaneIdOf<Runtime, MPI>>,
relayers_state: UnrewardedRelayersState,
) -> pezpallet_utility::Call<Runtime>
where
Runtime: pezpallet_bridge_grandpa::Config<GPI>
+ pezpallet_bridge_teyrchains::Config<PPI>
+ pezpallet_bridge_messages::Config<MPI, OutboundPayload = XcmAsPlainPayload>
+ pezpallet_utility::Config,
GPI: 'static,
PPI: 'static,
MPI: 'static,
<Runtime as pezpallet_bridge_grandpa::Config<GPI>>::BridgedChain:
bp_runtime::Chain<Hash = RelayBlockHash, BlockNumber = RelayBlockNumber> + ChainWithGrandpa,
BridgedChainOf<Runtime, MPI>: Chain<Hash = ParaHash> + Teyrchain,
<Runtime as pezpallet_utility::Config>::RuntimeCall: From<pezpallet_bridge_grandpa::Call<Runtime, GPI>>
+ From<pezpallet_bridge_teyrchains::Call<Runtime, PPI>>
+ From<pezpallet_bridge_messages::Call<Runtime, MPI>>,
{
let relay_chain_header_hash = relay_chain_header.hash();
let relay_chain_header_number = *relay_chain_header.number();
let submit_grandpa = pezpallet_bridge_grandpa::Call::<Runtime, GPI>::submit_finality_proof {
finality_target: Box::new(relay_chain_header),
justification: grandpa_justification,
};
let submit_para_head = pezpallet_bridge_teyrchains::Call::<Runtime, PPI>::submit_teyrchain_heads {
at_relay_block: (
relay_chain_header_number.saturated_into(),
relay_chain_header_hash.into(),
),
teyrchains: teyrchain_heads,
teyrchain_heads_proof: para_heads_proof,
};
let submit_message_delivery_proof =
pezpallet_bridge_messages::Call::<Runtime, MPI>::receive_messages_delivery_proof {
proof: message_delivery_proof,
relayers_state,
};
pezpallet_utility::Call::<Runtime>::batch_all {
calls: vec![
submit_grandpa.into(),
submit_para_head.into(),
submit_message_delivery_proof.into(),
],
}
}
/// Prepare a call with message proof.
pub fn make_standalone_relayer_delivery_call<Runtime, MPI>(
message_proof: FromBridgedChainMessagesProof<ParaHash, LaneIdOf<Runtime, MPI>>,
relayer_id_at_bridged_chain: InboundRelayerId<Runtime, MPI>,
) -> Runtime::RuntimeCall
where
Runtime: pezpallet_bridge_messages::Config<MPI, InboundPayload = XcmAsPlainPayload>,
MPI: 'static,
Runtime::RuntimeCall: From<pezpallet_bridge_messages::Call<Runtime, MPI>>,
BridgedChainOf<Runtime, MPI>: Chain<Hash = ParaHash> + Teyrchain,
{
pezpallet_bridge_messages::Call::<Runtime, MPI>::receive_messages_proof {
relayer_id_at_bridged_chain: relayer_id_at_bridged_chain.into(),
proof: Box::new(message_proof),
messages_count: 1,
dispatch_weight: Weight::from_parts(1000000000, 0),
}
.into()
}
/// Prepare a call with message delivery proof.
pub fn make_standalone_relayer_confirmation_call<Runtime, MPI>(
message_delivery_proof: FromBridgedChainMessagesDeliveryProof<ParaHash, LaneIdOf<Runtime, MPI>>,
relayers_state: UnrewardedRelayersState,
) -> Runtime::RuntimeCall
where
Runtime: pezpallet_bridge_messages::Config<MPI, OutboundPayload = XcmAsPlainPayload>,
MPI: 'static,
Runtime::RuntimeCall: From<pezpallet_bridge_messages::Call<Runtime, MPI>>,
BridgedChainOf<Runtime, MPI>: Chain<Hash = ParaHash> + Teyrchain,
{
pezpallet_bridge_messages::Call::<Runtime, MPI>::receive_messages_delivery_proof {
proof: message_delivery_proof,
relayers_state,
}
.into()
}
/// Prepare storage proofs of messages, stored at the source chain.
pub fn make_complex_relayer_delivery_proofs<
BridgedRelayChain,
BridgedTeyrchain,
ThisChainWithMessages,
LaneId,
>(
lane_id: LaneId,
xcm_message: Xcm<()>,
message_nonce: MessageNonce,
message_destination: Junctions,
para_header_number: u32,
relay_header_number: u32,
bridged_para_id: u32,
is_minimal_call: bool,
) -> (
HeaderOf<BridgedRelayChain>,
GrandpaJustification<HeaderOf<BridgedRelayChain>>,
ParaHead,
Vec<(ParaId, ParaHash)>,
ParaHeadsProof,
FromBridgedChainMessagesProof<ParaHash, LaneId>,
)
where
BridgedRelayChain:
bp_runtime::Chain<Hash = RelayBlockHash, BlockNumber = RelayBlockNumber> + ChainWithGrandpa,
BridgedTeyrchain: bp_runtime::Chain<Hash = ParaHash> + Teyrchain,
ThisChainWithMessages: ChainWithMessages,
LaneId: Copy + Encode,
{
// prepare message
let message_payload = prepare_inbound_xcm(xcm_message, message_destination);
// prepare para storage proof containing message
let (para_state_root, para_storage_proof) =
prepare_messages_storage_proof::<BridgedTeyrchain, ThisChainWithMessages, LaneId>(
lane_id,
message_nonce..=message_nonce,
None,
UnverifiedStorageProofParams::from_db_size(message_payload.len() as u32),
|_| message_payload.clone(),
encode_all_messages,
encode_lane_data,
false,
false,
);
let (relay_chain_header, justification, bridged_para_head, teyrchain_heads, para_heads_proof) =
make_complex_bridged_teyrchain_heads_proof::<BridgedRelayChain, BridgedTeyrchain>(
para_state_root,
para_header_number,
relay_header_number,
bridged_para_id,
is_minimal_call,
);
let message_proof = FromBridgedChainMessagesProof {
bridged_header_hash: bridged_para_head.hash(),
storage_proof: para_storage_proof,
lane: lane_id,
nonces_start: message_nonce,
nonces_end: message_nonce,
};
(
relay_chain_header,
justification,
bridged_para_head,
teyrchain_heads,
para_heads_proof,
message_proof,
)
}
/// Prepare storage proofs of message confirmations, stored at the target teyrchain.
pub fn make_complex_relayer_confirmation_proofs<
BridgedRelayChain,
BridgedTeyrchain,
ThisChainWithMessages,
LaneId,
>(
lane_id: LaneId,
para_header_number: u32,
relay_header_number: u32,
bridged_para_id: u32,
relayer_id_at_this_chain: AccountIdOf<ThisChainWithMessages>,
relayers_state: UnrewardedRelayersState,
) -> (
HeaderOf<BridgedRelayChain>,
GrandpaJustification<HeaderOf<BridgedRelayChain>>,
ParaHead,
Vec<(ParaId, ParaHash)>,
ParaHeadsProof,
FromBridgedChainMessagesDeliveryProof<ParaHash, LaneId>,
)
where
BridgedRelayChain:
bp_runtime::Chain<Hash = RelayBlockHash, BlockNumber = RelayBlockNumber> + ChainWithGrandpa,
BridgedTeyrchain: bp_runtime::Chain<Hash = ParaHash> + Teyrchain,
ThisChainWithMessages: ChainWithMessages,
LaneId: Copy + Encode,
{
// prepare para storage proof containing message delivery proof
let (para_state_root, para_storage_proof) =
prepare_message_delivery_storage_proof::<BridgedTeyrchain, ThisChainWithMessages, LaneId>(
lane_id,
InboundLaneData {
state: LaneState::Opened,
relayers: vec![
UnrewardedRelayer {
relayer: relayer_id_at_this_chain.into(),
messages: DeliveredMessages::new(1)
};
relayers_state.unrewarded_relayer_entries as usize
]
.into(),
last_confirmed_nonce: 1,
},
UnverifiedStorageProofParams::default(),
);
let (relay_chain_header, justification, bridged_para_head, teyrchain_heads, para_heads_proof) =
make_complex_bridged_teyrchain_heads_proof::<BridgedRelayChain, BridgedTeyrchain>(
para_state_root,
para_header_number,
relay_header_number,
bridged_para_id,
false,
);
let message_delivery_proof = FromBridgedChainMessagesDeliveryProof {
bridged_header_hash: bridged_para_head.hash(),
storage_proof: para_storage_proof,
lane: lane_id,
};
(
relay_chain_header,
justification,
bridged_para_head,
teyrchain_heads,
para_heads_proof,
message_delivery_proof,
)
}
/// Make bridged teyrchain header with given state root and relay header that is finalizing it.
pub fn make_complex_bridged_teyrchain_heads_proof<BridgedRelayChain, BridgedTeyrchain>(
para_state_root: ParaHash,
para_header_number: u32,
relay_header_number: BlockNumberOf<BridgedRelayChain>,
bridged_para_id: u32,
is_minimal_call: bool,
) -> (
HeaderOf<BridgedRelayChain>,
GrandpaJustification<HeaderOf<BridgedRelayChain>>,
ParaHead,
Vec<(ParaId, ParaHash)>,
ParaHeadsProof,
)
where
BridgedRelayChain:
bp_runtime::Chain<Hash = RelayBlockHash, BlockNumber = RelayBlockNumber> + ChainWithGrandpa,
BridgedTeyrchain: bp_runtime::Chain<Hash = ParaHash> + Teyrchain,
{
let bridged_para_head = ParaHead(
bp_test_utils::test_header_with_root::<HeaderOf<BridgedTeyrchain>>(
para_header_number.into(),
para_state_root,
)
.encode(),
);
let (relay_state_root, para_heads_proof, teyrchain_heads) =
prepare_teyrchain_heads_proof::<HeaderOf<BridgedTeyrchain>>(vec![(
bridged_para_id,
bridged_para_head.clone(),
)]);
assert_eq!(bridged_para_head.hash(), teyrchain_heads[0].1);
let (relay_chain_header, justification) =
make_complex_bridged_grandpa_header_proof::<BridgedRelayChain>(
relay_state_root,
relay_header_number,
is_minimal_call,
);
(relay_chain_header, justification, bridged_para_head, teyrchain_heads, para_heads_proof)
}
@@ -0,0 +1,142 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Pezcumulus.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Generating test data, used by all tests.
pub mod from_grandpa_chain;
pub mod from_teyrchain;
use bp_messages::{
target_chain::{DispatchMessage, DispatchMessageData},
MessageKey,
};
use codec::Encode;
use pezframe_support::traits::Get;
use pezpallet_bridge_grandpa::BridgedHeader;
use xcm::latest::prelude::*;
use bp_messages::MessageNonce;
use bp_runtime::BasicOperatingMode;
use bp_test_utils::authority_list;
use xcm::GetVersion;
use xcm_builder::{BridgeMessage, HaulBlob, HaulBlobError, HaulBlobExporter};
use xcm_executor::traits::{validate_export, ExportXcm};
pub(crate) type XcmAsPlainPayload = pezsp_std::vec::Vec<u8>;
pub fn prepare_inbound_xcm(xcm_message: Xcm<()>, destination: InteriorLocation) -> Vec<u8> {
let location = xcm::VersionedInteriorLocation::from(destination);
let xcm = xcm::VersionedXcm::<()>::from(xcm_message);
// (double encoding, because `.encode()` is called on original Xcm BLOB when it is pushed to the
// storage)
BridgeMessage { universal_dest: location, message: xcm }.encode().encode()
}
/// Helper that creates InitializationData mock data, that can be used to initialize bridge
/// GRANDPA pallet
pub fn initialization_data<
Runtime: pezpallet_bridge_grandpa::Config<GrandpaPalletInstance>,
GrandpaPalletInstance: 'static,
>(
block_number: u32,
) -> bp_header_chain::InitializationData<BridgedHeader<Runtime, GrandpaPalletInstance>> {
bp_header_chain::InitializationData {
header: Box::new(bp_test_utils::test_header(block_number.into())),
authority_list: authority_list(),
set_id: 1,
operating_mode: BasicOperatingMode::Normal,
}
}
/// Dummy xcm
pub(crate) fn dummy_xcm() -> Xcm<()> {
vec![Trap(42)].into()
}
pub(crate) fn dispatch_message<LaneId: Encode>(
lane_id: LaneId,
nonce: MessageNonce,
payload: Vec<u8>,
) -> DispatchMessage<Vec<u8>, LaneId> {
DispatchMessage {
key: MessageKey { lane_id, nonce },
data: DispatchMessageData { payload: Ok(payload) },
}
}
/// Macro used for simulate_export_message and capturing bytes
macro_rules! grab_haul_blob (
($name:ident, $grabbed_payload:ident) => {
std::thread_local! {
static $grabbed_payload: std::cell::RefCell<Option<Vec<u8>>> = std::cell::RefCell::new(None);
}
struct $name;
impl HaulBlob for $name {
fn haul_blob(blob: Vec<u8>) -> Result<(), HaulBlobError>{
$grabbed_payload.with(|rm| *rm.borrow_mut() = Some(blob));
Ok(())
}
}
}
);
/// Simulates `HaulBlobExporter` and all its wrapping and captures generated plain bytes,
/// which are transferred over bridge.
pub(crate) fn simulate_message_exporter_on_bridged_chain<
SourceNetwork: Get<NetworkId>,
DestinationNetwork: Get<Location>,
DestinationVersion: GetVersion,
>(
(destination_network, destination_junctions): (NetworkId, Junctions),
) -> Vec<u8> {
grab_haul_blob!(GrabbingHaulBlob, GRABBED_HAUL_BLOB_PAYLOAD);
// lets pretend that some teyrchain on bridged chain exported the message
let universal_source_on_bridged_chain: Junctions =
[GlobalConsensus(SourceNetwork::get()), Teyrchain(5678)].into();
let channel = 1_u32;
// simulate XCM message export
let (ticket, fee) = validate_export::<
HaulBlobExporter<GrabbingHaulBlob, DestinationNetwork, DestinationVersion, ()>,
>(
destination_network,
channel,
universal_source_on_bridged_chain,
destination_junctions,
dummy_xcm(),
)
.expect("validate_export to pass");
tracing::info!(
target: "simulate_message_exporter_on_bridged_chain",
?fee,
"HaulBlobExporter::validate"
);
let xcm_hash =
HaulBlobExporter::<GrabbingHaulBlob, DestinationNetwork, DestinationVersion, ()>::deliver(
ticket,
)
.expect("deliver to pass");
tracing::info!(
target: "simulate_message_exporter_on_bridged_chain",
?xcm_hash,
"HaulBlobExporter::deliver"
);
GRABBED_HAUL_BLOB_PAYLOAD.with(|r| r.take().expect("Encoded message should be here"))
}