// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Cumulus.
// Cumulus is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Cumulus is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Cumulus. If not, see .
#![cfg(test)]
use bp_asset_hub_westend::ASSET_HUB_WESTEND_PARACHAIN_ID;
use bp_bridge_hub_westend::BRIDGE_HUB_WESTEND_PARACHAIN_ID;
use bp_polkadot_core::Signature;
use bridge_hub_westend_runtime::{
bridge_to_rococo_config, xcm_config::XcmConfig, AllPalletsWithoutSystem,
BridgeRejectObsoleteHeadersAndMessages, Executive, MessageQueueServiceWeight, Runtime,
RuntimeCall, RuntimeEvent, SessionKeys, SignedExtra, UncheckedExtrinsic,
};
use codec::{Decode, Encode};
use cumulus_primitives_core::XcmError::{FailedToTransactAsset, NotHoldingFees};
use frame_support::parameter_types;
use parachains_common::{AccountId, AuraId, Balance};
use snowbridge_pallet_ethereum_client::WeightInfo;
use sp_core::H160;
use sp_keyring::AccountKeyring::Alice;
use sp_runtime::{
generic::{Era, SignedPayload},
AccountId32,
};
parameter_types! {
pub const DefaultBridgeHubEthereumBaseFee: Balance = 2_750_872_500_000;
}
fn collator_session_keys() -> bridge_hub_test_utils::CollatorSessionKeys {
bridge_hub_test_utils::CollatorSessionKeys::new(
AccountId::from(Alice),
AccountId::from(Alice),
SessionKeys { aura: AuraId::from(Alice.public()) },
)
}
#[test]
pub fn transfer_token_to_ethereum_works() {
snowbridge_runtime_test_common::send_transfer_token_message_success::(
11155111,
collator_session_keys(),
BRIDGE_HUB_WESTEND_PARACHAIN_ID,
ASSET_HUB_WESTEND_PARACHAIN_ID,
H160::random(),
H160::random(),
DefaultBridgeHubEthereumBaseFee::get(),
Box::new(|runtime_event_encoded: Vec| {
match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) {
Ok(RuntimeEvent::EthereumOutboundQueue(event)) => Some(event),
_ => None,
}
}),
)
}
#[test]
pub fn unpaid_transfer_token_to_ethereum_fails_with_barrier() {
snowbridge_runtime_test_common::send_unpaid_transfer_token_message::(
11155111,
collator_session_keys(),
BRIDGE_HUB_WESTEND_PARACHAIN_ID,
ASSET_HUB_WESTEND_PARACHAIN_ID,
H160::random(),
H160::random(),
)
}
#[test]
pub fn transfer_token_to_ethereum_fee_not_enough() {
snowbridge_runtime_test_common::send_transfer_token_message_failure::(
11155111,
collator_session_keys(),
BRIDGE_HUB_WESTEND_PARACHAIN_ID,
ASSET_HUB_WESTEND_PARACHAIN_ID,
DefaultBridgeHubEthereumBaseFee::get() + 20_000_000_000,
H160::random(),
H160::random(),
// fee not enough
20_000_000_000,
NotHoldingFees,
)
}
#[test]
pub fn transfer_token_to_ethereum_insufficient_fund() {
snowbridge_runtime_test_common::send_transfer_token_message_failure::(
11155111,
collator_session_keys(),
BRIDGE_HUB_WESTEND_PARACHAIN_ID,
ASSET_HUB_WESTEND_PARACHAIN_ID,
1_000_000_000,
H160::random(),
H160::random(),
DefaultBridgeHubEthereumBaseFee::get(),
FailedToTransactAsset("Funds are unavailable"),
)
}
#[test]
fn max_message_queue_service_weight_is_more_than_beacon_extrinsic_weights() {
let max_message_queue_weight = MessageQueueServiceWeight::get();
let force_checkpoint =
::WeightInfo::force_checkpoint();
let submit_checkpoint =
::WeightInfo::submit();
max_message_queue_weight.all_gt(force_checkpoint);
max_message_queue_weight.all_gt(submit_checkpoint);
}
#[test]
fn ethereum_client_consensus_extrinsics_work() {
snowbridge_runtime_test_common::ethereum_extrinsic(
collator_session_keys(),
BRIDGE_HUB_WESTEND_PARACHAIN_ID,
construct_and_apply_extrinsic,
);
}
#[test]
fn ethereum_to_polkadot_message_extrinsics_work() {
snowbridge_runtime_test_common::ethereum_to_polkadot_message_extrinsics_work(
collator_session_keys(),
BRIDGE_HUB_WESTEND_PARACHAIN_ID,
construct_and_apply_extrinsic,
);
}
/// Tests that the digest items are as expected when a Ethereum Outbound message is received.
/// If the MessageQueue pallet is configured before (i.e. the MessageQueue pallet is listed before
/// the EthereumOutboundQueue in the construct_runtime macro) the EthereumOutboundQueue, this test
/// will fail.
#[test]
pub fn ethereum_outbound_queue_processes_messages_before_message_queue_works() {
snowbridge_runtime_test_common::ethereum_outbound_queue_processes_messages_before_message_queue_works::<
Runtime,
XcmConfig,
AllPalletsWithoutSystem,
>(
11155111,
collator_session_keys(),
BRIDGE_HUB_WESTEND_PARACHAIN_ID,
ASSET_HUB_WESTEND_PARACHAIN_ID,
H160::random(),
H160::random(),
DefaultBridgeHubEthereumBaseFee::get(),
Box::new(|runtime_event_encoded: Vec| {
match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) {
Ok(RuntimeEvent::EthereumOutboundQueue(event)) => Some(event),
_ => None,
}
}),
)
}
fn construct_extrinsic(
sender: sp_keyring::AccountKeyring,
call: RuntimeCall,
) -> UncheckedExtrinsic {
let account_id = AccountId32::from(sender.public());
let extra: SignedExtra = (
frame_system::CheckNonZeroSender::::new(),
frame_system::CheckSpecVersion::::new(),
frame_system::CheckTxVersion::::new(),
frame_system::CheckGenesis::::new(),
frame_system::CheckEra::::from(Era::immortal()),
frame_system::CheckNonce::::from(
frame_system::Pallet::::account(&account_id).nonce,
),
frame_system::CheckWeight::::new(),
pallet_transaction_payment::ChargeTransactionPayment::::from(0),
BridgeRejectObsoleteHeadersAndMessages::default(),
(bridge_to_rococo_config::OnBridgeHubWestendRefundBridgeHubRococoMessages::default(),),
cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim::new(),
frame_metadata_hash_extension::CheckMetadataHash::::new(false),
);
let payload = SignedPayload::new(call.clone(), extra.clone()).unwrap();
let signature = payload.using_encoded(|e| sender.sign(e));
UncheckedExtrinsic::new_signed(call, account_id.into(), Signature::Sr25519(signature), extra)
}
fn construct_and_apply_extrinsic(
origin: sp_keyring::AccountKeyring,
call: RuntimeCall,
) -> sp_runtime::DispatchOutcome {
let xt = construct_extrinsic(origin, call);
let r = Executive::apply_extrinsic(xt);
r.unwrap()
}