From 9879c8a44070dde2295499682192975de0dd7f2c Mon Sep 17 00:00:00 2001 From: claravanstaden Date: Mon, 28 Oct 2024 12:17:28 +0200 Subject: [PATCH 1/4] adds pna tests --- .../src/tests/snowbridge.rs | 349 +++++++++++++++++- 1 file changed, 330 insertions(+), 19 deletions(-) diff --git a/integration-tests/emulated/tests/bridges/bridge-hub-polkadot/src/tests/snowbridge.rs b/integration-tests/emulated/tests/bridges/bridge-hub-polkadot/src/tests/snowbridge.rs index 30f28c9951..15eb61c101 100644 --- a/integration-tests/emulated/tests/bridges/bridge-hub-polkadot/src/tests/snowbridge.rs +++ b/integration-tests/emulated/tests/bridges/bridge-hub-polkadot/src/tests/snowbridge.rs @@ -23,10 +23,11 @@ use bridge_hub_polkadot_runtime::{ Runtime, RuntimeOrigin, }; use codec::{Decode, Encode}; -use emulated_integration_tests_common::xcm_emulator::ConvertLocation; +use emulated_integration_tests_common::{xcm_emulator::ConvertLocation, RESERVABLE_ASSET_ID}; use frame_support::pallet_prelude::TypeInfo; use hex_literal::hex; use polkadot_system_emulated_network::{ + asset_hub_polkadot_emulated_chain::genesis::AssetHubPolkadotAssetOwner, penpal_emulated_chain::CustomizableAssetFromSystemAssetHub, BridgeHubPolkadotParaSender as BridgeHubPolkadotSender, }; @@ -38,7 +39,7 @@ use snowbridge_core::{ inbound::{InboundQueueFixture, Log, Message, Proof}, meth, outbound::OperatingMode, - Rewards, + AssetMetadata, Rewards, TokenIdOf, }; use snowbridge_pallet_system::PricingParametersOf; use snowbridge_router_primitives::inbound::{ @@ -56,7 +57,7 @@ pub const GATEWAY_ADDRESS: [u8; 20] = hex!("EDa338E4dC46038493b885327842fD3E301C const INITIAL_FUND: u128 = 5_000_000_000 * POLKADOT_ED; const INSUFFICIENT_XCM_FEE: u128 = 1000; const XCM_FEE: u128 = 4_000_000_000; -const WETH_AMOUNT: u128 = 1_000_000_000; +const TOKEN_AMOUNT: u128 = 100_000_000_000; #[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] pub enum ControlCall { @@ -241,15 +242,13 @@ fn register_weth_token_from_ethereum_to_asset_hub() { vec![(EthereumGatewayAddress::key().to_vec(), H160(GATEWAY_ADDRESS).encode())], )); // Construct RegisterToken message and sent to inbound queue - let message_id: H256 = [1; 32].into(); let message = VersionedMessage::V1(MessageV1 { chain_id: CHAIN_ID, command: Command::RegisterToken { token: WETH.into(), fee: XCM_FEE }, }); // Convert the message to XCM - let (xcm, _) = EthereumInboundQueue::do_convert(message_id, message).unwrap(); - // Send the XCM - let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubPolkadot::para_id()).unwrap(); + let (xcm, _) = EthereumInboundQueue::do_convert([0; 32].into(), message).unwrap(); + let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubPolkadot::para_id().into()).unwrap(); assert_expected_events!( BridgeHubPolkadot, @@ -429,7 +428,7 @@ fn send_token_from_ethereum_to_asset_hub() { destination: Destination::AccountId32 { id: AssetHubPolkadotReceiver::get().into(), }, - amount: WETH_AMOUNT, + amount: TOKEN_AMOUNT, fee: XCM_FEE, }, }); @@ -471,13 +470,6 @@ fn send_weth_asset_from_asset_hub_to_ethereum() { [Parachain(AssetHubPolkadot::para_id().into())], )); - AssetHubPolkadot::force_default_xcm_version(Some(XCM_VERSION)); - BridgeHubPolkadot::force_default_xcm_version(Some(XCM_VERSION)); - AssetHubPolkadot::force_xcm_version( - Location::new(2, [GlobalConsensus(Ethereum { chain_id: CHAIN_ID })]), - XCM_VERSION, - ); - BridgeHubPolkadot::fund_accounts(vec![ (assethub_sovereign.clone(), INITIAL_FUND), (RelayTreasuryPalletAccount::get(), INITIAL_FUND), @@ -545,7 +537,7 @@ fn send_weth_asset_from_asset_hub_to_ethereum() { destination: Destination::AccountId32 { id: AssetHubPolkadotReceiver::get().into(), }, - amount: WETH_AMOUNT, + amount: TOKEN_AMOUNT, fee: XCM_FEE, }, }); @@ -587,9 +579,9 @@ fn send_weth_asset_from_asset_hub_to_ethereum() { AccountKey20 { network: None, key: WETH }, ], )), - fun: Fungible(WETH_AMOUNT), + fun: Fungible(TOKEN_AMOUNT), }]; - let multi_assets = VersionedAssets::from(Assets::from(assets)); + let versioned_assets = VersionedAssets::from(Assets::from(assets)); let destination = VersionedLocation::from(Location::new( 2, @@ -611,7 +603,7 @@ fn send_weth_asset_from_asset_hub_to_ethereum() { RuntimeOrigin::signed(AssetHubPolkadotReceiver::get()), Box::new(destination), Box::new(beneficiary), - Box::new(multi_assets), + Box::new(versioned_assets), 0, Unlimited, ) @@ -963,3 +955,322 @@ fn send_token_from_ethereum_to_non_existent_account_on_asset_hub_with_sufficient ); }); } + +#[test] +fn transfer_relay_token() { + let assethub_sovereign = BridgeHubPolkadot::sovereign_account_id_of( + BridgeHubPolkadot::sibling_location_of(AssetHubPolkadot::para_id()), + ); + BridgeHubPolkadot::fund_accounts(vec![(assethub_sovereign.clone(), INITIAL_FUND)]); + + let asset_id: Location = Location { parents: 1, interior: [].into() }; + let expected_asset_id: Location = + Location { parents: 1, interior: [GlobalConsensus(Polkadot)].into() }; + + let expected_token_id = TokenIdOf::convert_location(&expected_asset_id).unwrap(); + + let ethereum_sovereign: AccountId = + GlobalConsensusEthereumConvertsFor::<[u8; 32]>::convert_location(&Location::new( + 2, + [GlobalConsensus(EthereumNetwork::get())], + )) + .unwrap() + .into(); + + // Register token + BridgeHubPolkadot::execute_with(|| { + type RuntimeOrigin = ::RuntimeOrigin; + type RuntimeEvent = ::RuntimeEvent; + + assert_ok!(::Balances::force_set_balance( + RuntimeOrigin::root(), + sp_runtime::MultiAddress::Id(BridgeHubPolkadotSender::get()), + INITIAL_FUND * 10, + )); + + assert_ok!(::EthereumSystem::register_token( + RuntimeOrigin::root(), + Box::new(VersionedLocation::V4(asset_id.clone())), + AssetMetadata { + name: "wnd".as_bytes().to_vec().try_into().unwrap(), + symbol: "wnd".as_bytes().to_vec().try_into().unwrap(), + decimals: 12, + }, + )); + // Check that a message was sent to Ethereum to create the agent + assert_expected_events!( + BridgeHubPolkadot, + vec![RuntimeEvent::EthereumSystem(snowbridge_pallet_system::Event::RegisterToken { .. }) => {},] + ); + }); + + // Send token to Ethereum + AssetHubPolkadot::execute_with(|| { + type RuntimeOrigin = ::RuntimeOrigin; + type RuntimeEvent = ::RuntimeEvent; + + let assets = vec![Asset { id: AssetId(Location::parent()), fun: Fungible(TOKEN_AMOUNT) }]; + let versioned_assets = VersionedAssets::V4(Assets::from(assets)); + + let destination = VersionedLocation::V4(Location::new( + 2, + [GlobalConsensus(Ethereum { chain_id: CHAIN_ID })], + )); + + let beneficiary = VersionedLocation::V4(Location::new( + 0, + [AccountKey20 { network: None, key: ETHEREUM_DESTINATION_ADDRESS.into() }], + )); + + assert_ok!(::PolkadotXcm::limited_reserve_transfer_assets( + RuntimeOrigin::signed(AssetHubPolkadotSender::get()), + Box::new(destination), + Box::new(beneficiary), + Box::new(versioned_assets), + 0, + Unlimited, + )); + + let events = AssetHubPolkadot::events(); + // Check that the native asset transferred to some reserved account(sovereign of Ethereum) + assert!( + events.iter().any(|event| matches!( + event, + RuntimeEvent::Balances(pallet_balances::Event::Transfer { amount, to, ..}) + if *amount == TOKEN_AMOUNT && *to == ethereum_sovereign.clone(), + )), + "native token reserved to Ethereum sovereign account." + ); + }); + + // Send token back from ethereum + BridgeHubPolkadot::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + // Check that the transfer token back to Ethereum message was queue in the Ethereum + // Outbound Queue + assert_expected_events!( + BridgeHubPolkadot, + vec![RuntimeEvent::EthereumOutboundQueue(snowbridge_pallet_outbound_queue::Event::MessageQueued{ .. }) => {},] + ); + + // Send relay token back to AH + let message_id: H256 = [0; 32].into(); + let message = VersionedMessage::V1(MessageV1 { + chain_id: CHAIN_ID, + command: Command::SendNativeToken { + token_id: expected_token_id, + destination: Destination::AccountId32 { + id: AssetHubPolkadotReceiver::get().into(), + }, + amount: TOKEN_AMOUNT, + fee: XCM_FEE, + }, + }); + // Convert the message to XCM + let (xcm, _) = EthereumInboundQueue::do_convert(message_id, message).unwrap(); + // Send the XCM + let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubPolkadot::para_id().into()).unwrap(); + + assert_expected_events!( + BridgeHubPolkadot, + vec![RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {},] + ); + }); + + AssetHubPolkadot::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + assert_expected_events!( + AssetHubPolkadot, + vec![RuntimeEvent::Balances(pallet_balances::Event::Burned{ .. }) => {},] + ); + + let events = AssetHubPolkadot::events(); + + // Check that the native token burnt from some reserved account + assert!( + events.iter().any(|event| matches!( + event, + RuntimeEvent::Balances(pallet_balances::Event::Burned { who, ..}) + if *who == ethereum_sovereign.clone(), + )), + "native token burnt from Ethereum sovereign account." + ); + + // Check that the token was minted to beneficiary + assert!( + events.iter().any(|event| matches!( + event, + RuntimeEvent::Balances(pallet_balances::Event::Minted { who, amount }) + if *amount >= TOKEN_AMOUNT && *who == AssetHubPolkadotReceiver::get() + )), + "Token minted to beneficiary." + ); + }); +} + +#[test] +fn transfer_ah_token() { + let assethub_sovereign = BridgeHubPolkadot::sovereign_account_id_of( + BridgeHubPolkadot::sibling_location_of(AssetHubPolkadot::para_id()), + ); + BridgeHubPolkadot::fund_accounts(vec![(assethub_sovereign.clone(), INITIAL_FUND)]); + + let ethereum_destination = Location::new(2, [GlobalConsensus(Ethereum { chain_id: CHAIN_ID })]); + + let ethereum_sovereign: AccountId = + GlobalConsensusEthereumConvertsFor::<[u8; 32]>::convert_location(ðereum_destination) + .unwrap() + .into(); + AssetHubPolkadot::fund_accounts(vec![(ethereum_sovereign.clone(), INITIAL_FUND)]); + + let asset_id: Location = + [PalletInstance(ASSETS_PALLET_ID), GeneralIndex(RESERVABLE_ASSET_ID.into())].into(); + + let asset_id_in_bh: Location = Location::new( + 1, + [ + Parachain(AssetHubPolkadot::para_id().into()), + PalletInstance(ASSETS_PALLET_ID), + GeneralIndex(RESERVABLE_ASSET_ID.into()), + ], + ); + + let asset_id_after_reanchored = Location::new( + 1, + [GlobalConsensus(Polkadot), Parachain(AssetHubPolkadot::para_id().into())], + ) + .appended_with(asset_id.clone().interior) + .unwrap(); + + let token_id = TokenIdOf::convert_location(&asset_id_after_reanchored).unwrap(); + + // Register token + BridgeHubPolkadot::execute_with(|| { + type RuntimeOrigin = ::RuntimeOrigin; + + assert_ok!(::EthereumSystem::register_token( + RuntimeOrigin::root(), + Box::new(VersionedLocation::V4(asset_id_in_bh.clone())), + AssetMetadata { + name: "ah_asset".as_bytes().to_vec().try_into().unwrap(), + symbol: "ah_asset".as_bytes().to_vec().try_into().unwrap(), + decimals: 12, + }, + )); + }); + + // Mint some token + AssetHubPolkadot::mint_asset( + ::RuntimeOrigin::signed(AssetHubPolkadotAssetOwner::get()), + RESERVABLE_ASSET_ID, + AssetHubPolkadotSender::get(), + TOKEN_AMOUNT, + ); + + // Send token to Ethereum + AssetHubPolkadot::execute_with(|| { + type RuntimeOrigin = ::RuntimeOrigin; + type RuntimeEvent = ::RuntimeEvent; + + // Send partial of the token, will fail if send all + let assets = + vec![Asset { id: AssetId(asset_id.clone()), fun: Fungible(TOKEN_AMOUNT / 10) }]; + let versioned_assets = VersionedAssets::V4(Assets::from(assets)); + + let beneficiary = VersionedLocation::V4(Location::new( + 0, + [AccountKey20 { network: None, key: ETHEREUM_DESTINATION_ADDRESS.into() }], + )); + + assert_ok!(::PolkadotXcm::limited_reserve_transfer_assets( + RuntimeOrigin::signed(AssetHubPolkadotSender::get()), + Box::new(VersionedLocation::from(ethereum_destination)), + Box::new(beneficiary), + Box::new(versioned_assets), + 0, + Unlimited, + )); + + assert_expected_events!( + AssetHubPolkadot, + vec![RuntimeEvent::Assets(pallet_assets::Event::Transferred{ .. }) => {},] + ); + + let events = AssetHubPolkadot::events(); + // Check that the native asset transferred to some reserved account(sovereign of Ethereum) + assert!( + events.iter().any(|event| matches!( + event, + RuntimeEvent::Assets(pallet_assets::Event::Transferred { asset_id, to, ..}) + if *asset_id == RESERVABLE_ASSET_ID && *to == ethereum_sovereign.clone() + )), + "native token reserved to Ethereum sovereign account." + ); + }); + + // Send token back from Ethereum + BridgeHubPolkadot::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + // Check that the transfer token back to Ethereum message was queue in the Ethereum + // Outbound Queue + assert_expected_events!( + BridgeHubPolkadot, + vec![RuntimeEvent::EthereumOutboundQueue(snowbridge_pallet_outbound_queue::Event::MessageQueued{ .. }) => {},] + ); + + let message = VersionedMessage::V1(MessageV1 { + chain_id: CHAIN_ID, + command: Command::SendNativeToken { + token_id, + destination: Destination::AccountId32 { + id: AssetHubPolkadotReceiver::get().into(), + }, + amount: TOKEN_AMOUNT / 10, + fee: XCM_FEE, + }, + }); + // Convert the message to XCM + let (xcm, _) = EthereumInboundQueue::do_convert([0; 32].into(), message).unwrap(); + // Send the XCM + let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubPolkadot::para_id().into()).unwrap(); + + assert_expected_events!( + BridgeHubPolkadot, + vec![RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {},] + ); + }); + + AssetHubPolkadot::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + assert_expected_events!( + AssetHubPolkadot, + vec![RuntimeEvent::Assets(pallet_assets::Event::Burned{..}) => {},] + ); + + let events = AssetHubPolkadot::events(); + + // Check that the native token burnt from some reserved account + assert!( + events.iter().any(|event| matches!( + event, + RuntimeEvent::Assets(pallet_assets::Event::Burned { owner, .. }) + if *owner == ethereum_sovereign.clone(), + )), + "token burnt from Ethereum sovereign account." + ); + + // Check that the token was minted to beneficiary + assert!( + events.iter().any(|event| matches!( + event, + RuntimeEvent::Assets(pallet_assets::Event::Issued { owner, .. }) + if *owner == AssetHubPolkadotReceiver::get() + )), + "Token minted to beneficiary." + ); + }); +} From 79e8917d749552b3eb490d9a16b22e3c9e49a23a Mon Sep 17 00:00:00 2001 From: claravanstaden Date: Mon, 28 Oct 2024 14:04:09 +0200 Subject: [PATCH 2/4] clippy --- .../bridge-hub-polkadot/src/tests/snowbridge.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/integration-tests/emulated/tests/bridges/bridge-hub-polkadot/src/tests/snowbridge.rs b/integration-tests/emulated/tests/bridges/bridge-hub-polkadot/src/tests/snowbridge.rs index 15eb61c101..61d685a3a9 100644 --- a/integration-tests/emulated/tests/bridges/bridge-hub-polkadot/src/tests/snowbridge.rs +++ b/integration-tests/emulated/tests/bridges/bridge-hub-polkadot/src/tests/snowbridge.rs @@ -248,7 +248,7 @@ fn register_weth_token_from_ethereum_to_asset_hub() { }); // Convert the message to XCM let (xcm, _) = EthereumInboundQueue::do_convert([0; 32].into(), message).unwrap(); - let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubPolkadot::para_id().into()).unwrap(); + let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubPolkadot::para_id()).unwrap(); assert_expected_events!( BridgeHubPolkadot, @@ -1019,7 +1019,7 @@ fn transfer_relay_token() { let beneficiary = VersionedLocation::V4(Location::new( 0, - [AccountKey20 { network: None, key: ETHEREUM_DESTINATION_ADDRESS.into() }], + [AccountKey20 { network: None, key: ETHEREUM_DESTINATION_ADDRESS }], )); assert_ok!(::PolkadotXcm::limited_reserve_transfer_assets( @@ -1070,7 +1070,7 @@ fn transfer_relay_token() { // Convert the message to XCM let (xcm, _) = EthereumInboundQueue::do_convert(message_id, message).unwrap(); // Send the XCM - let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubPolkadot::para_id().into()).unwrap(); + let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubPolkadot::para_id()).unwrap(); assert_expected_events!( BridgeHubPolkadot, @@ -1181,7 +1181,7 @@ fn transfer_ah_token() { let beneficiary = VersionedLocation::V4(Location::new( 0, - [AccountKey20 { network: None, key: ETHEREUM_DESTINATION_ADDRESS.into() }], + [AccountKey20 { network: None, key: ETHEREUM_DESTINATION_ADDRESS }], )); assert_ok!(::PolkadotXcm::limited_reserve_transfer_assets( @@ -1235,7 +1235,7 @@ fn transfer_ah_token() { // Convert the message to XCM let (xcm, _) = EthereumInboundQueue::do_convert([0; 32].into(), message).unwrap(); // Send the XCM - let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubPolkadot::para_id().into()).unwrap(); + let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubPolkadot::para_id()).unwrap(); assert_expected_events!( BridgeHubPolkadot, From a5d5a04c95591a1ba6d42dfe550dc6a4220d3427 Mon Sep 17 00:00:00 2001 From: claravanstaden Date: Mon, 28 Oct 2024 15:43:01 +0200 Subject: [PATCH 3/4] run to next block --- Cargo.lock | 1 + Cargo.toml | 1 + .../bridge-hub-polkadot/Cargo.toml | 1 + .../snowbridge_pallet_ethereum_client.rs | 2 +- .../bridge-hub-polkadot/tests/snowbridge.rs | 174 +++++++++++++++++- 5 files changed, 175 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b6a138eba0..d62c45ddec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1953,6 +1953,7 @@ dependencies = [ "snowbridge-core", "snowbridge-outbound-queue-runtime-api", "snowbridge-pallet-ethereum-client", + "snowbridge-pallet-ethereum-client-fixtures", "snowbridge-pallet-inbound-queue", "snowbridge-pallet-outbound-queue", "snowbridge-pallet-system", diff --git a/Cargo.toml b/Cargo.toml index 87a852cfda..f6c1a3710b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -204,6 +204,7 @@ snowbridge-outbound-queue-runtime-api = { version = "0.10.0", default-features = snowbridge-pallet-ethereum-client = { version = "0.10.0", default-features = false } snowbridge-pallet-inbound-queue = { version = "0.10.0", default-features = false } snowbridge-pallet-inbound-queue-fixtures = { version = "0.18.0" } +snowbridge-pallet-ethereum-client-fixtures = { version = "0.18.0" } snowbridge-pallet-outbound-queue = { version = "0.10.0", default-features = false } snowbridge-pallet-system = { version = "0.10.0", default-features = false } snowbridge-router-primitives = { version = "0.16.0", default-features = false } diff --git a/system-parachains/bridge-hubs/bridge-hub-polkadot/Cargo.toml b/system-parachains/bridge-hubs/bridge-hub-polkadot/Cargo.toml index 1f56851440..34a83bba78 100644 --- a/system-parachains/bridge-hubs/bridge-hub-polkadot/Cargo.toml +++ b/system-parachains/bridge-hubs/bridge-hub-polkadot/Cargo.toml @@ -125,6 +125,7 @@ pallet-bridge-relayers = { workspace = true, features = ["integrity-test"] } sp-keyring = { workspace = true } static_assertions = { workspace = true } snowbridge-runtime-test-common = { workspace = true } +snowbridge-pallet-ethereum-client-fixtures = { workspace = true } parachains-runtimes-test-utils = { workspace = true } [features] diff --git a/system-parachains/bridge-hubs/bridge-hub-polkadot/src/weights/snowbridge_pallet_ethereum_client.rs b/system-parachains/bridge-hubs/bridge-hub-polkadot/src/weights/snowbridge_pallet_ethereum_client.rs index 6a08d4fb51..9ba101d701 100644 --- a/system-parachains/bridge-hubs/bridge-hub-polkadot/src/weights/snowbridge_pallet_ethereum_client.rs +++ b/system-parachains/bridge-hubs/bridge-hub-polkadot/src/weights/snowbridge_pallet_ethereum_client.rs @@ -111,7 +111,7 @@ impl snowbridge_pallet_ethereum_client::WeightInfo for // Measured: `92787` // Estimated: `93857` // Minimum execution time: 169_086_936_000 picoseconds. - Weight::from_parts(69_539_621_000, 0) + Weight::from_parts(169_539_621_000, 0) .saturating_add(Weight::from_parts(0, 93857)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(1)) diff --git a/system-parachains/bridge-hubs/bridge-hub-polkadot/tests/snowbridge.rs b/system-parachains/bridge-hubs/bridge-hub-polkadot/tests/snowbridge.rs index e4b4027d81..0aec0ec4c0 100644 --- a/system-parachains/bridge-hubs/bridge-hub-polkadot/tests/snowbridge.rs +++ b/system-parachains/bridge-hubs/bridge-hub-polkadot/tests/snowbridge.rs @@ -27,7 +27,7 @@ use bridge_hub_polkadot_runtime::{ }; use codec::{Decode, Encode}; use cumulus_primitives_core::XcmError::{FailedToTransactAsset, TooExpensive}; -use frame_support::{parameter_types, traits::Contains}; +use frame_support::{parameter_types, traits::{Contains, fungible::Mutate, OnFinalize, OnInitialize}}; use parachains_common::{AccountId, AuraId, Balance}; pub use parachains_runtimes_test_utils::test_cases::change_storage_constant_by_governance_works; use snowbridge_pallet_ethereum_client::WeightInfo; @@ -40,11 +40,23 @@ use sp_runtime::{ use xcm::latest::prelude::*; use xcm_builder::HandleFee; use xcm_executor::traits::{FeeManager, FeeReason}; - +use sp_core::Get; +use sp_runtime::SaturatedConversion; +use sp_runtime::Saturating; +use parachains_runtimes_test_utils::{ + AccountIdOf, BalanceOf, CollatorSessionKeys, ExtBuilder, ValidatorIdOf, +}; +use frame_system::pallet_prelude::BlockNumberFor; +use frame_support::assert_err; +use frame_support::assert_ok; +use snowbridge_pallet_ethereum_client_fixtures::*; parameter_types! { pub const DefaultBridgeHubEthereumBaseFee: Balance = 2_750_872_500_000; } +type RuntimeHelper = +parachains_runtimes_test_utils::RuntimeHelper; + fn collator_session_keys() -> bridge_hub_test_utils::CollatorSessionKeys { bridge_hub_test_utils::CollatorSessionKeys::new( AccountId::from(Alice), @@ -199,7 +211,7 @@ fn max_message_queue_service_weight_is_more_than_beacon_extrinsic_weights() { // FAIL-CI @bkontur can you help me to check why it's exceeding the weight limits? #[test] fn ethereum_client_consensus_extrinsics_work() { - snowbridge_runtime_test_common::ethereum_extrinsic( + ethereum_extrinsic( collator_session_keys(), 1013, construct_and_apply_extrinsic, @@ -238,6 +250,162 @@ fn ethereum_outbound_queue_processes_messages_before_message_queue_works() { ) } +pub fn ethereum_extrinsic( + collator_session_key: CollatorSessionKeys, + runtime_para_id: u32, + construct_and_apply_extrinsic: fn( + sp_keyring::AccountKeyring, + ::RuntimeCall, + ) -> sp_runtime::DispatchOutcome, +) where + Runtime: frame_system::Config + + pallet_balances::Config + + pallet_session::Config + + pallet_xcm::Config + + pallet_utility::Config + + parachain_info::Config + + pallet_collator_selection::Config + + cumulus_pallet_parachain_system::Config + + snowbridge_pallet_outbound_queue::Config + + snowbridge_pallet_system::Config + + snowbridge_pallet_ethereum_client::Config + + pallet_timestamp::Config, + ValidatorIdOf: From>, + ::RuntimeCall: + From>, + ::RuntimeCall: From>, + AccountIdOf: From, +{ + ExtBuilder::::default() + .with_collators(collator_session_key.collators()) + .with_session_keys(collator_session_key.session_keys()) + .with_para_id(runtime_para_id.into()) + .with_tracing() + .build() + .execute_with(|| { + let initial_checkpoint = make_checkpoint(); + let update = make_finalized_header_update(); + let sync_committee_update = make_sync_committee_update(); + let mut invalid_update = make_finalized_header_update(); + let mut invalid_sync_committee_update = make_sync_committee_update(); + invalid_update.finalized_header.slot = 4354; + invalid_sync_committee_update.finalized_header.slot = 4354; + + let alice = Alice; + let alice_account = alice.to_account_id(); + >::mint_into( + &alice_account.clone().into(), + 10_000_000_000_000_u128.saturated_into::>(), + ) + .unwrap(); + let balance_before = + >::free_balance(&alice_account.clone().into()); + + assert_ok!(>::force_checkpoint( + RuntimeHelper::::root_origin(), + initial_checkpoint.clone(), + )); + let balance_after_checkpoint = + >::free_balance(&alice_account.clone().into()); + + let update_call: ::RuntimeCall = + snowbridge_pallet_ethereum_client::Call::::submit { + update: Box::new(*update.clone()), + } + .into(); + + let invalid_update_call: ::RuntimeCall = + snowbridge_pallet_ethereum_client::Call::::submit { + update: Box::new(*invalid_update), + } + .into(); + + let update_sync_committee_call: ::RuntimeCall = + snowbridge_pallet_ethereum_client::Call::::submit { + update: Box::new(*sync_committee_update), + } + .into(); + + let invalid_update_sync_committee_call: ::RuntimeCall = + snowbridge_pallet_ethereum_client::Call::::submit { + update: Box::new(*invalid_sync_committee_update), + } + .into(); + + let block_number = >::block_number(); + let next_block_number = >::block_number() + .saturating_add(BlockNumberFor::::from(1u32)); + + // Finalized header update + let update_outcome = construct_and_apply_extrinsic(alice, update_call.into()); + assert_ok!(update_outcome); + let balance_after_update = + >::free_balance(&alice_account.clone().into()); + println!("block_number: {}", block_number); + + // Invalid finalized header update + let invalid_update_outcome = + construct_and_apply_extrinsic(alice, invalid_update_call.into()); + assert_err!( + invalid_update_outcome, + snowbridge_pallet_ethereum_client::Error::::InvalidUpdateSlot + ); + let balance_after_invalid_update = + >::free_balance(&alice_account.clone().into()); + println!("block_number: {}", block_number); + + // finish current block + >::on_finalize(block_number); + >::on_finalize(block_number); + + // start next block + >::set_block_number(next_block_number); + >::on_initialize(next_block_number); + >::on_initialize(next_block_number); + let block_number = >::block_number(); + println!("block_number: {}", block_number); + + let next_block_number = >::block_number() + .saturating_add(BlockNumberFor::::from(1u32)); + + // Sync committee update + let sync_committee_outcome = + construct_and_apply_extrinsic(alice, update_sync_committee_call.into()); + assert_ok!(sync_committee_outcome); + let balance_after_sync_com_update = + >::free_balance(&alice_account.clone().into()); + + // Invalid sync committee update + let invalid_sync_committee_outcome = + construct_and_apply_extrinsic(alice, invalid_update_sync_committee_call.into()); + assert_err!( + invalid_sync_committee_outcome, + snowbridge_pallet_ethereum_client::Error::::InvalidUpdateSlot + ); + let balance_after_invalid_sync_com_update = + >::free_balance(&alice_account.clone().into()); + + // Assert paid operations are charged and free operations are free + // Checkpoint is a free operation + assert!(balance_before == balance_after_checkpoint); + let gap = + ::FreeHeadersInterval::get(); + // Large enough header gap is free + if update.finalized_header.slot >= initial_checkpoint.header.slot + gap as u64 { + assert!(balance_after_checkpoint == balance_after_update); + } else { + // Otherwise paid + assert!(balance_after_checkpoint > balance_after_update); + } + // An invalid update is paid + assert!(balance_after_update > balance_after_invalid_update); + // A successful sync committee update is free + assert!(balance_after_invalid_update == balance_after_sync_com_update); + // An invalid sync committee update is paid + assert!(balance_after_sync_com_update > balance_after_invalid_sync_com_update); + }); +} + fn construct_extrinsic( sender: sp_keyring::AccountKeyring, call: RuntimeCall, From ac092d56af6c365b73fbd7f860643645abc48418 Mon Sep 17 00:00:00 2001 From: claravanstaden Date: Mon, 28 Oct 2024 16:11:16 +0200 Subject: [PATCH 4/4] fiddle --- .../bridge-hub-polkadot/tests/snowbridge.rs | 66 ++++++++++++++----- 1 file changed, 49 insertions(+), 17 deletions(-) diff --git a/system-parachains/bridge-hubs/bridge-hub-polkadot/tests/snowbridge.rs b/system-parachains/bridge-hubs/bridge-hub-polkadot/tests/snowbridge.rs index 0aec0ec4c0..0a9e437ac5 100644 --- a/system-parachains/bridge-hubs/bridge-hub-polkadot/tests/snowbridge.rs +++ b/system-parachains/bridge-hubs/bridge-hub-polkadot/tests/snowbridge.rs @@ -332,16 +332,26 @@ pub fn ethereum_extrinsic( } .into(); - let block_number = >::block_number(); - let next_block_number = >::block_number() - .saturating_add(BlockNumberFor::::from(1u32)); + // Block 1 + let mut block_number = 1u32; + println!("BLOCK NUMBER {}", block_number); // Finalized header update let update_outcome = construct_and_apply_extrinsic(alice, update_call.into()); assert_ok!(update_outcome); let balance_after_update = >::free_balance(&alice_account.clone().into()); - println!("block_number: {}", block_number); + + // Finish Block 1 + >::on_finalize(block_number.into()); + >::on_finalize(block_number.into()); + + // Start Block 2 + block_number = 2u32; + >::set_block_number(block_number.into()); + >::on_initialize(block_number.into()); + >::on_initialize(block_number.into()); + println!("BLOCK NUMBER {}", block_number); // Invalid finalized header update let invalid_update_outcome = @@ -352,21 +362,17 @@ pub fn ethereum_extrinsic( ); let balance_after_invalid_update = >::free_balance(&alice_account.clone().into()); - println!("block_number: {}", block_number); - // finish current block - >::on_finalize(block_number); - >::on_finalize(block_number); + // Finish Block 2 + >::on_finalize(block_number.into()); + >::on_finalize(block_number.into()); - // start next block - >::set_block_number(next_block_number); - >::on_initialize(next_block_number); - >::on_initialize(next_block_number); - let block_number = >::block_number(); - println!("block_number: {}", block_number); - - let next_block_number = >::block_number() - .saturating_add(BlockNumberFor::::from(1u32)); + // Start Block 3 + block_number = 3u32; + >::set_block_number(block_number.into()); + >::on_initialize(block_number.into()); + >::on_initialize(block_number.into()); + println!("BLOCK NUMBER {}", block_number); // Sync committee update let sync_committee_outcome = @@ -375,6 +381,17 @@ pub fn ethereum_extrinsic( let balance_after_sync_com_update = >::free_balance(&alice_account.clone().into()); + // Finish Block 3 + >::on_finalize(block_number.into()); + >::on_finalize(block_number.into()); + + // Start Block 4 + block_number = 4u32; + >::set_block_number(block_number.into()); + >::on_initialize(block_number.into()); + >::on_initialize(block_number.into()); + println!("BLOCK NUMBER {}", block_number); + // Invalid sync committee update let invalid_sync_committee_outcome = construct_and_apply_extrinsic(alice, invalid_update_sync_committee_call.into()); @@ -385,6 +402,17 @@ pub fn ethereum_extrinsic( let balance_after_invalid_sync_com_update = >::free_balance(&alice_account.clone().into()); + // Finish Block 4 + >::on_finalize(block_number.into()); + >::on_finalize(block_number.into()); + + // Start Block 5 + block_number = 5u32; + >::set_block_number(block_number.into()); + >::on_initialize(block_number.into()); + >::on_initialize(block_number.into()); + println!("BLOCK NUMBER {}", block_number); + // Assert paid operations are charged and free operations are free // Checkpoint is a free operation assert!(balance_before == balance_after_checkpoint); @@ -403,6 +431,10 @@ pub fn ethereum_extrinsic( assert!(balance_after_invalid_update == balance_after_sync_com_update); // An invalid sync committee update is paid assert!(balance_after_sync_com_update > balance_after_invalid_sync_com_update); + + // Finish Block 5 + >::on_finalize(block_number.into()); + >::on_finalize(block_number.into()); }); }