Snowbridge Ethereum Deneb fork preparation (#3029)

- Prepares for the Deneb hardfork on Sepolia testnet on 31 January
(needs to be deployed to Rococo before then)
- Removes `beacon-minimal-spec` flag for simpler config
- Adds test comments

---------

Co-authored-by: Ron <yrong1997@gmail.com>
Co-authored-by: claravanstaden <Cats 4 life!>
Co-authored-by: Alistair Singh <alistair.singh7@gmail.com>
This commit is contained in:
Clara van Staden
2024-01-30 08:24:04 +02:00
committed by GitHub
parent b8f55d1b76
commit 85191e94b5
79 changed files with 4721 additions and 4296 deletions
@@ -100,7 +100,7 @@ pub mod accounts {
pub const CHARLIE: &str = "Charlie";
pub const DAVE: &str = "Dave";
pub const EVE: &str = "Eve";
pub const FERDIE: &str = "Ferdei";
pub const FERDIE: &str = "Ferdie";
pub const ALICE_STASH: &str = "Alice//stash";
pub const BOB_STASH: &str = "Bob//stash";
pub const CHARLIE_STASH: &str = "Charlie//stash";
@@ -47,3 +47,5 @@ snowbridge-core = { path = "../../../../../../../bridges/snowbridge/parachain/pr
snowbridge-router-primitives = { path = "../../../../../../../bridges/snowbridge/parachain/primitives/router", default-features = false }
snowbridge-pallet-system = { path = "../../../../../../../bridges/snowbridge/parachain/pallets/system", default-features = false }
snowbridge-pallet-outbound-queue = { path = "../../../../../../../bridges/snowbridge/parachain/pallets/outbound-queue", default-features = false }
snowbridge-pallet-inbound-queue = { path = "../../../../../../../bridges/snowbridge/parachain/pallets/inbound-queue", default-features = false }
snowbridge-pallet-inbound-queue-fixtures = { path = "../../../../../../../bridges/snowbridge/parachain/pallets/inbound-queue/fixtures" }
@@ -43,11 +43,6 @@ pub use emulated_integration_tests_common::{
PROOF_SIZE_THRESHOLD, REF_TIME_THRESHOLD, XCM_V3,
};
pub use parachains_common::{AccountId, Balance};
pub use rococo_system_emulated_network::{
penpal_emulated_chain::PenpalAParaPallet as PenpalAPallet,
BridgeHubRococoParaReceiver as BridgeHubRococoReceiver, PenpalAPara as PenpalA,
PenpalAParaReceiver as PenpalAReceiver, PenpalAParaSender as PenpalASender,
};
pub use rococo_westend_system_emulated_network::{
asset_hub_rococo_emulated_chain::{
genesis::ED as ASSET_HUB_ROCOCO_ED, AssetHubRococoParaPallet as AssetHubRococoPallet,
@@ -58,14 +53,17 @@ pub use rococo_westend_system_emulated_network::{
bridge_hub_rococo_emulated_chain::{
genesis::ED as BRIDGE_HUB_ROCOCO_ED, BridgeHubRococoParaPallet as BridgeHubRococoPallet,
},
penpal_emulated_chain::PenpalAParaPallet as PenpalAPallet,
rococo_emulated_chain::{genesis::ED as ROCOCO_ED, RococoRelayPallet as RococoPallet},
AssetHubRococoPara as AssetHubRococo, AssetHubRococoParaReceiver as AssetHubRococoReceiver,
AssetHubRococoParaSender as AssetHubRococoSender, AssetHubWestendPara as AssetHubWestend,
AssetHubWestendParaReceiver as AssetHubWestendReceiver,
AssetHubWestendParaSender as AssetHubWestendSender, BridgeHubRococoPara as BridgeHubRococo,
BridgeHubRococoParaReceiver as BridgeHubRococoReceiver,
BridgeHubRococoParaSender as BridgeHubRococoSender, BridgeHubWestendPara as BridgeHubWestend,
RococoRelay as Rococo, RococoRelayReceiver as RococoReceiver,
RococoRelaySender as RococoSender,
PenpalAPara as PenpalA, PenpalAParaReceiver as PenpalAReceiver,
PenpalAParaSender as PenpalASender, RococoRelay as Rococo,
RococoRelayReceiver as RococoReceiver, RococoRelaySender as RococoSender,
};
pub const ASSET_ID: u32 = 1;
@@ -13,17 +13,24 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::*;
use bridge_hub_rococo_runtime::{EthereumBeaconClient, EthereumInboundQueue, RuntimeOrigin};
use codec::{Decode, Encode};
use emulated_integration_tests_common::xcm_emulator::ConvertLocation;
use frame_support::pallet_prelude::TypeInfo;
use hex_literal::hex;
use parachains_common::rococo::snowbridge::EthereumNetwork;
use rococo_westend_system_emulated_network::BridgeHubRococoParaSender as BridgeHubRococoSender;
use snowbridge_core::outbound::OperatingMode;
use snowbridge_pallet_system;
use snowbridge_router_primitives::inbound::{
Command, Destination, GlobalConsensusEthereumConvertsFor, MessageV1, VersionedMessage,
use snowbridge_pallet_inbound_queue_fixtures::{
register_token::make_register_token_message,
register_token_with_insufficient_fee::make_register_token_with_infufficient_fee_message,
send_token::make_send_token_message, send_token_to_penpal::make_send_token_to_penpal_message,
InboundQueueFixture,
};
use snowbridge_pallet_system;
use snowbridge_router_primitives::inbound::GlobalConsensusEthereumConvertsFor;
use sp_core::H256;
use sp_runtime::{ArithmeticError::Underflow, DispatchError::Arithmetic};
const INITIAL_FUND: u128 = 5_000_000_000 * ROCOCO_ED;
const CHAIN_ID: u64 = 11155111;
@@ -31,7 +38,6 @@ const TREASURY_ACCOUNT: [u8; 32] =
hex!("6d6f646c70792f74727372790000000000000000000000000000000000000000");
const WETH: [u8; 20] = hex!("87d1f7fdfEe7f651FaBc8bFCB6E086C278b77A7d");
const ETHEREUM_DESTINATION_ADDRESS: [u8; 20] = hex!("44a57ee2f2FCcb85FDa2B0B18EBD0D8D2333700e");
const XCM_FEE: u128 = 4_000_000_000;
#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)]
pub enum ControlCall {
@@ -48,17 +54,34 @@ pub enum SnowbridgeControl {
Control(ControlCall),
}
pub fn send_inbound_message(fixture: InboundQueueFixture) -> DispatchResult {
EthereumBeaconClient::store_execution_header(
fixture.message.proof.block_hash,
fixture.execution_header,
0,
H256::default(),
);
EthereumInboundQueue::submit(
RuntimeOrigin::signed(BridgeHubRococoSender::get()),
fixture.message,
)
}
/// Create an agent on Ethereum. An agent is a representation of an entity in the Polkadot
/// ecosystem (like a parachain) on Ethereum.
#[test]
#[ignore]
fn create_agent() {
let origin_para: u32 = 1001;
// Fund the origin parachain sovereign account so that it can pay execution fees.
BridgeHubRococo::fund_para_sovereign(origin_para.into(), INITIAL_FUND);
let sudo_origin = <Rococo as Chain>::RuntimeOrigin::root();
let destination = Rococo::child_location_of(BridgeHubRococo::para_id()).into();
let create_agent_call = SnowbridgeControl::Control(ControlCall::CreateAgent {});
// Construct XCM to create an agent for para 1001
let remote_xcm = VersionedXcm::from(Xcm(vec![
UnpaidExecution { weight_limit: Unlimited, check_origin: None },
DescendOrigin(Parachain(origin_para).into()),
@@ -69,7 +92,7 @@ fn create_agent() {
},
]));
//Rococo Global Consensus
// Rococo Global Consensus
// Send XCM message from Relay Chain to Bridge Hub source Parachain
Rococo::execute_with(|| {
assert_ok!(<Rococo as RococoPallet>::XcmPallet::send(
@@ -79,7 +102,7 @@ fn create_agent() {
));
type RuntimeEvent = <Rococo as Chain>::RuntimeEvent;
// Check that the Transact message was sent
assert_expected_events!(
Rococo,
vec![
@@ -90,7 +113,7 @@ fn create_agent() {
BridgeHubRococo::execute_with(|| {
type RuntimeEvent = <BridgeHubRococo as Chain>::RuntimeEvent;
// Check that a message was sent to Ethereum to create the agent
assert_expected_events!(
BridgeHubRococo,
vec![
@@ -102,10 +125,13 @@ fn create_agent() {
});
}
/// Create a channel for a consensus system. A channel is a bidirectional messaging channel
/// between BridgeHub and Ethereum.
#[test]
#[ignore]
fn create_channel() {
let origin_para: u32 = 1001;
// Fund AssetHub sovereign account so that it can pay execution fees.
BridgeHubRococo::fund_para_sovereign(origin_para.into(), INITIAL_FUND);
let sudo_origin = <Rococo as Chain>::RuntimeOrigin::root();
@@ -113,7 +139,7 @@ fn create_channel() {
Rococo::child_location_of(BridgeHubRococo::para_id()).into();
let create_agent_call = SnowbridgeControl::Control(ControlCall::CreateAgent {});
// Construct XCM to create an agent for para 1001
let create_agent_xcm = VersionedXcm::from(Xcm(vec![
UnpaidExecution { weight_limit: Unlimited, check_origin: None },
DescendOrigin(Parachain(origin_para).into()),
@@ -126,7 +152,7 @@ fn create_channel() {
let create_channel_call =
SnowbridgeControl::Control(ControlCall::CreateChannel { mode: OperatingMode::Normal });
// Construct XCM to create a channel for para 1001
let create_channel_xcm = VersionedXcm::from(Xcm(vec![
UnpaidExecution { weight_limit: Unlimited, check_origin: None },
DescendOrigin(Parachain(origin_para).into()),
@@ -137,7 +163,7 @@ fn create_channel() {
},
]));
//Rococo Global Consensus
// Rococo Global Consensus
// Send XCM message from Relay Chain to Bridge Hub source Parachain
Rococo::execute_with(|| {
assert_ok!(<Rococo as RococoPallet>::XcmPallet::send(
@@ -165,6 +191,7 @@ fn create_channel() {
BridgeHubRococo::execute_with(|| {
type RuntimeEvent = <BridgeHubRococo as Chain>::RuntimeEvent;
// Check that the Channel was created
assert_expected_events!(
BridgeHubRococo,
vec![
@@ -176,25 +203,18 @@ fn create_channel() {
});
}
/// Tests the registering of a token as an asset on AssetHub.
#[test]
fn register_weth_token_from_ethereum_to_asset_hub() {
// Fund AssetHub sovereign account so that it can pay execution fees.
BridgeHubRococo::fund_para_sovereign(AssetHubRococo::para_id().into(), INITIAL_FUND);
let message_id_: H256 = [1; 32].into();
BridgeHubRococo::execute_with(|| {
type RuntimeEvent = <BridgeHubRococo as Chain>::RuntimeEvent;
type EthereumInboundQueue =
<BridgeHubRococo as BridgeHubRococoPallet>::EthereumInboundQueue;
let message = VersionedMessage::V1(MessageV1 {
chain_id: CHAIN_ID,
command: Command::RegisterToken { token: WETH.into(), fee: XCM_FEE },
});
let (xcm, fee) = EthereumInboundQueue::do_convert(message_id_, message).unwrap();
assert_ok!(EthereumInboundQueue::burn_fees(AssetHubRococo::para_id().into(), fee));
let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubRococo::para_id().into()).unwrap();
// Construct RegisterToken message and sent to inbound queue
let register_token_message = make_register_token_message();
send_inbound_message(register_token_message.clone()).unwrap();
assert_expected_events!(
BridgeHubRococo,
@@ -216,46 +236,38 @@ fn register_weth_token_from_ethereum_to_asset_hub() {
});
}
/// Tests sending a token to a 3rd party parachain, called PenPal. The token reserve is
/// still located on AssetHub.
#[test]
fn send_token_from_ethereum_to_penpal() {
let asset_hub_sovereign = BridgeHubRococo::sovereign_account_id_of(Location::new(
1,
[Parachain(AssetHubRococo::para_id().into())],
));
// Fund AssetHub sovereign account so it can pay execution fees for the asset transfer
BridgeHubRococo::fund_accounts(vec![(asset_hub_sovereign.clone(), INITIAL_FUND)]);
// Fund PenPal sender and receiver
PenpalA::fund_accounts(vec![
(PenpalAReceiver::get(), INITIAL_FUND),
(PenpalASender::get(), INITIAL_FUND),
]);
// The Weth asset location, identified by the contract address on Ethereum
let weth_asset_location: Location =
(Parent, Parent, EthereumNetwork::get(), AccountKey20 { network: None, key: WETH }).into();
// Converts the Weth asset location into an asset ID
let weth_asset_id: v3::Location = weth_asset_location.try_into().unwrap();
let origin_location = (Parent, Parent, EthereumNetwork::get()).into();
// Fund ethereum sovereign in asset hub
// Fund ethereum sovereign on AssetHub
let ethereum_sovereign: AccountId =
GlobalConsensusEthereumConvertsFor::<AccountId>::convert_location(&origin_location)
.unwrap();
AssetHubRococo::fund_accounts(vec![(ethereum_sovereign.clone(), INITIAL_FUND)]);
// Create asset on assethub.
AssetHubRococo::execute_with(|| {
assert_ok!(<AssetHubRococo as AssetHubRococoPallet>::ForeignAssets::create(
pallet_xcm::Origin::Xcm(origin_location).into(),
weth_asset_id,
asset_hub_sovereign.clone().into(),
1000,
));
assert!(<AssetHubRococo as AssetHubRococoPallet>::ForeignAssets::asset_exists(
weth_asset_id
));
});
// Create asset on penpal.
// Create asset on the Penpal parachain.
PenpalA::execute_with(|| {
assert_ok!(<PenpalA as PenpalAPallet>::ForeignAssets::create(
<PenpalA as Chain>::RuntimeOrigin::signed(PenpalASender::get()),
@@ -267,27 +279,14 @@ fn send_token_from_ethereum_to_penpal() {
assert!(<PenpalA as PenpalAPallet>::ForeignAssets::asset_exists(weth_asset_id));
});
let message_id_: H256 = [1; 32].into();
BridgeHubRococo::execute_with(|| {
type RuntimeEvent = <BridgeHubRococo as Chain>::RuntimeEvent;
type EthereumInboundQueue =
<BridgeHubRococo as BridgeHubRococoPallet>::EthereumInboundQueue;
let message = VersionedMessage::V1(MessageV1 {
chain_id: CHAIN_ID,
command: Command::SendToken {
token: WETH.into(),
destination: Destination::ForeignAccountId32 {
para_id: 2000,
id: PenpalAReceiver::get().into(),
fee: XCM_FEE,
},
amount: 1_000_000_000,
fee: XCM_FEE,
},
});
let (xcm, _) = EthereumInboundQueue::do_convert(message_id_, message).unwrap();
let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubRococo::para_id().into()).unwrap();
// Construct RegisterToken message and sent to inbound queue
send_inbound_message(make_register_token_message()).unwrap();
// Construct SendToken message and sent to inbound queue
send_inbound_message(make_send_token_to_penpal_message()).unwrap();
assert_expected_events!(
BridgeHubRococo,
@@ -299,7 +298,7 @@ fn send_token_from_ethereum_to_penpal() {
AssetHubRococo::execute_with(|| {
type RuntimeEvent = <AssetHubRococo as Chain>::RuntimeEvent;
// Check that the assets were issued on AssetHub
assert_expected_events!(
AssetHubRococo,
vec![
@@ -311,7 +310,7 @@ fn send_token_from_ethereum_to_penpal() {
PenpalA::execute_with(|| {
type RuntimeEvent = <PenpalA as Chain>::RuntimeEvent;
// Check that the assets were issued on PenPal
assert_expected_events!(
PenpalA,
vec![
@@ -321,37 +320,25 @@ fn send_token_from_ethereum_to_penpal() {
});
}
/// Tests the registering of a token as an asset on AssetHub, and then subsequently sending
/// a token from Ethereum to AssetHub.
#[test]
fn send_token_from_ethereum_to_asset_hub() {
BridgeHubRococo::fund_para_sovereign(AssetHubRococo::para_id().into(), INITIAL_FUND);
// Fund ethereum sovereign in asset hub
// Fund ethereum sovereign on AssetHub
AssetHubRococo::fund_accounts(vec![(AssetHubRococoReceiver::get(), INITIAL_FUND)]);
let message_id_: H256 = [1; 32].into();
BridgeHubRococo::execute_with(|| {
type RuntimeEvent = <BridgeHubRococo as Chain>::RuntimeEvent;
type EthereumInboundQueue =
<BridgeHubRococo as BridgeHubRococoPallet>::EthereumInboundQueue;
let message = VersionedMessage::V1(MessageV1 {
chain_id: CHAIN_ID,
command: Command::RegisterToken { token: WETH.into(), fee: XCM_FEE },
});
let (xcm, _) = EthereumInboundQueue::do_convert(message_id_, message).unwrap();
let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubRococo::para_id().into()).unwrap();
let message = VersionedMessage::V1(MessageV1 {
chain_id: CHAIN_ID,
command: Command::SendToken {
token: WETH.into(),
destination: Destination::AccountId32 { id: AssetHubRococoReceiver::get().into() },
amount: 1_000_000_000,
fee: XCM_FEE,
},
});
let (xcm, _) = EthereumInboundQueue::do_convert(message_id_, message).unwrap();
let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubRococo::para_id().into()).unwrap();
// Construct RegisterToken message and sent to inbound queue
send_inbound_message(make_register_token_message()).unwrap();
// Construct SendToken message and sent to inbound queue
send_inbound_message(make_send_token_message()).unwrap();
// Check that the message was sent
assert_expected_events!(
BridgeHubRococo,
vec![
@@ -363,6 +350,7 @@ fn send_token_from_ethereum_to_asset_hub() {
AssetHubRococo::execute_with(|| {
type RuntimeEvent = <AssetHubRococo as Chain>::RuntimeEvent;
// Check that the token was received and issued as a foreign asset on AssetHub
assert_expected_events!(
AssetHubRococo,
vec![
@@ -372,6 +360,10 @@ fn send_token_from_ethereum_to_asset_hub() {
});
}
/// Tests the full cycle of token transfers:
/// - registering a token on AssetHub
/// - sending a token to AssetHub
/// - returning the token to Ethereum
#[test]
fn send_weth_asset_from_asset_hub_to_ethereum() {
use asset_hub_rococo_runtime::xcm_config::bridging::to_ethereum::DefaultBridgeHubEthereumBaseFee;
@@ -391,31 +383,25 @@ fn send_weth_asset_from_asset_hub_to_ethereum() {
AssetHubRococo::fund_accounts(vec![(AssetHubRococoReceiver::get(), INITIAL_FUND)]);
const WETH_AMOUNT: u128 = 1_000_000_000;
let message_id_: H256 = [1; 32].into();
BridgeHubRococo::execute_with(|| {
type RuntimeEvent = <BridgeHubRococo as Chain>::RuntimeEvent;
type EthereumInboundQueue =
<BridgeHubRococo as BridgeHubRococoPallet>::EthereumInboundQueue;
let message = VersionedMessage::V1(MessageV1 {
chain_id: CHAIN_ID,
command: Command::RegisterToken { token: WETH.into(), fee: XCM_FEE },
});
let (xcm, _) = EthereumInboundQueue::do_convert(message_id_, message).unwrap();
let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubRococo::para_id().into()).unwrap();
let message = VersionedMessage::V1(MessageV1 {
chain_id: CHAIN_ID,
command: Command::SendToken {
token: WETH.into(),
destination: Destination::AccountId32 { id: AssetHubRococoReceiver::get().into() },
amount: WETH_AMOUNT,
fee: XCM_FEE,
},
});
let (xcm, _) = EthereumInboundQueue::do_convert(message_id_, message).unwrap();
let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubRococo::para_id().into()).unwrap();
// Construct RegisterToken message and sent to inbound queue
send_inbound_message(make_register_token_message()).unwrap();
// Check that the register token message was sent using xcm
assert_expected_events!(
BridgeHubRococo,
vec![
RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {},
]
);
// Construct SendToken message and sent to inbound queue
send_inbound_message(make_send_token_message()).unwrap();
// Check that the send token message was sent using xcm
assert_expected_events!(
BridgeHubRococo,
vec![
@@ -428,6 +414,7 @@ fn send_weth_asset_from_asset_hub_to_ethereum() {
type RuntimeEvent = <AssetHubRococo as Chain>::RuntimeEvent;
type RuntimeOrigin = <AssetHubRococo as Chain>::RuntimeOrigin;
// Check that AssetHub has issued the foreign asset
assert_expected_events!(
AssetHubRococo,
vec![
@@ -459,6 +446,7 @@ fn send_weth_asset_from_asset_hub_to_ethereum() {
let free_balance_before = <AssetHubRococo as AssetHubRococoPallet>::Balances::free_balance(
AssetHubRococoReceiver::get(),
);
// Send the Weth back to Ethereum
<AssetHubRococo as AssetHubRococoPallet>::PolkadotXcm::reserve_transfer_assets(
RuntimeOrigin::signed(AssetHubRococoReceiver::get()),
Box::new(destination),
@@ -470,14 +458,15 @@ fn send_weth_asset_from_asset_hub_to_ethereum() {
let free_balance_after = <AssetHubRococo as AssetHubRococoPallet>::Balances::free_balance(
AssetHubRococoReceiver::get(),
);
// assert at least DefaultBridgeHubEthereumBaseFee charged from the sender
// Assert at least DefaultBridgeHubEthereumBaseFee charged from the sender
let free_balance_diff = free_balance_before - free_balance_after;
assert!(free_balance_diff > DefaultBridgeHubEthereumBaseFee::get());
});
BridgeHubRococo::execute_with(|| {
type RuntimeEvent = <BridgeHubRococo as Chain>::RuntimeEvent;
// Check that the transfer token back to Ethereum message was queue in the Ethereum
// Outbound Queue
assert_expected_events!(
BridgeHubRococo,
vec![
@@ -485,6 +474,7 @@ fn send_weth_asset_from_asset_hub_to_ethereum() {
]
);
let events = BridgeHubRococo::events();
// Check that the local fee was credited to the Snowbridge sovereign account
assert!(
events.iter().any(|event| matches!(
event,
@@ -493,6 +483,7 @@ fn send_weth_asset_from_asset_hub_to_ethereum() {
)),
"Snowbridge sovereign takes local fee."
);
// Check that the remote fee was credited to the AssetHub sovereign account
assert!(
events.iter().any(|event| matches!(
event,
@@ -503,3 +494,44 @@ fn send_weth_asset_from_asset_hub_to_ethereum() {
);
});
}
#[test]
fn register_weth_token_in_asset_hub_fail_for_insufficient_fee() {
BridgeHubRococo::fund_para_sovereign(AssetHubRococo::para_id().into(), INITIAL_FUND);
BridgeHubRococo::execute_with(|| {
type RuntimeEvent = <BridgeHubRococo as Chain>::RuntimeEvent;
// Construct RegisterToken message and sent to inbound queue
let message = make_register_token_with_infufficient_fee_message();
send_inbound_message(message).unwrap();
assert_expected_events!(
BridgeHubRococo,
vec![
RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {},
]
);
});
AssetHubRococo::execute_with(|| {
type RuntimeEvent = <AssetHubRococo as Chain>::RuntimeEvent;
assert_expected_events!(
AssetHubRococo,
vec![
RuntimeEvent::MessageQueue(pallet_message_queue::Event::Processed { success:false, .. }) => {},
]
);
});
}
#[test]
fn send_token_from_ethereum_to_asset_hub_fail_for_insufficient_fund() {
// Insufficient fund
BridgeHubRococo::fund_para_sovereign(AssetHubRococo::para_id().into(), 1_000);
BridgeHubRococo::execute_with(|| {
assert_err!(send_inbound_message(make_register_token_message()), Arithmetic(Underflow));
});
}
@@ -294,9 +294,7 @@ try-runtime = [
]
experimental = ["pallet-aura/experimental"]
fast-runtime = [
"snowbridge-pallet-ethereum-client/beacon-spec-minimal",
]
fast-runtime = []
# A feature that should be enabled when the runtime should be built for on-chain
# deployment. This will disable stuff that shouldn't be part of the on-chain wasm
@@ -498,13 +498,6 @@ impl pallet_utility::Config for Runtime {
}
// Ethereum Bridge
#[cfg(not(feature = "runtime-benchmarks"))]
parameter_types! {
pub storage EthereumGatewayAddress: H160 = H160::zero();
}
#[cfg(feature = "runtime-benchmarks")]
parameter_types! {
pub storage EthereumGatewayAddress: H160 = H160(hex_literal::hex!("EDa338E4dC46038493b885327842fD3E301CaB39"));
}
@@ -600,29 +593,33 @@ impl snowbridge_pallet_outbound_queue::Config for Runtime {
type Channels = EthereumSystem;
}
#[cfg(feature = "fast-runtime")]
#[cfg(any(feature = "std", feature = "fast-runtime", feature = "runtime-benchmarks", test))]
parameter_types! {
pub const ChainForkVersions: ForkVersions = ForkVersions {
genesis: Fork {
version: [0, 0, 0, 1], // 0x00000001
version: [0, 0, 0, 0], // 0x00000000
epoch: 0,
},
altair: Fork {
version: [1, 0, 0, 1], // 0x01000001
version: [1, 0, 0, 0], // 0x01000000
epoch: 0,
},
bellatrix: Fork {
version: [2, 0, 0, 1], // 0x02000001
version: [2, 0, 0, 0], // 0x02000000
epoch: 0,
},
capella: Fork {
version: [3, 0, 0, 1], // 0x03000001
version: [3, 0, 0, 0], // 0x03000000
epoch: 0,
},
deneb: Fork {
version: [4, 0, 0, 0], // 0x04000000
epoch: 0,
}
};
}
#[cfg(not(feature = "fast-runtime"))]
#[cfg(not(any(feature = "std", feature = "fast-runtime", feature = "runtime-benchmarks", test)))]
parameter_types! {
pub const ChainForkVersions: ForkVersions = ForkVersions {
genesis: Fork {
@@ -641,6 +638,10 @@ parameter_types! {
version: [144, 0, 0, 114], // 0x90000072
epoch: 56832,
},
deneb: Fork {
version: [144, 0, 0, 115], // 0x90000073
epoch: 132608,
},
};
}
@@ -34,6 +34,7 @@ use bp_runtime::ChainId;
use frame_support::{
parameter_types,
traits::{ConstU32, Contains, Equals, Everything, Nothing},
StoragePrefixedMap,
};
use frame_system::EnsureRoot;
use pallet_xcm::XcmPassthrough;
@@ -161,9 +162,12 @@ impl Contains<RuntimeCall> for SafeCallFilter {
match call {
RuntimeCall::System(frame_system::Call::set_storage { items })
if items.iter().all(|(k, _)| {
k.eq(&DeliveryRewardInBalance::key()) |
k.eq(&RequiredStakeForStakeAndSlash::key()) |
k.eq(&EthereumGatewayAddress::key())
k.eq(&DeliveryRewardInBalance::key()) ||
k.eq(&RequiredStakeForStakeAndSlash::key()) ||
k.eq(&EthereumGatewayAddress::key()) ||
// Allow resetting of Ethereum nonces in Rococo only.
k.starts_with(&snowbridge_pallet_inbound_queue::Nonce::<Runtime>::final_prefix()) ||
k.starts_with(&snowbridge_pallet_outbound_queue::Nonce::<Runtime>::final_prefix())
}) =>
return true,
_ => (),
@@ -223,7 +227,14 @@ impl Contains<RuntimeCall> for SafeCallFilter {
snowbridge_pallet_inbound_queue::Call::set_operating_mode { .. },
) | RuntimeCall::EthereumOutboundQueue(
snowbridge_pallet_outbound_queue::Call::set_operating_mode { .. },
) | RuntimeCall::EthereumSystem(..)
) | RuntimeCall::EthereumSystem(
snowbridge_pallet_system::Call::upgrade { .. } |
snowbridge_pallet_system::Call::set_operating_mode { .. } |
snowbridge_pallet_system::Call::set_pricing_parameters { .. } |
snowbridge_pallet_system::Call::force_update_channel { .. } |
snowbridge_pallet_system::Call::force_transfer_native_from_agent { .. } |
snowbridge_pallet_system::Call::set_token_transfer_fees { .. },
)
)
}
}
@@ -16,16 +16,25 @@
#![cfg(test)]
use bp_polkadot_core::Signature;
use bridge_hub_rococo_runtime::{
xcm_config::XcmConfig, MessageQueueServiceWeight, Runtime, RuntimeEvent, SessionKeys,
bridge_to_bulletin_config::OnBridgeHubRococoRefundRococoBulletinMessages,
bridge_to_westend_config::OnBridgeHubRococoRefundBridgeHubWestendMessages,
xcm_config::XcmConfig, BridgeRejectObsoleteHeadersAndMessages, Executive,
MessageQueueServiceWeight, Runtime, RuntimeCall, RuntimeEvent, SessionKeys, SignedExtra,
UncheckedExtrinsic,
};
use codec::Decode;
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;
@@ -107,3 +116,62 @@ fn max_message_queue_service_weight_is_more_than_beacon_extrinsic_weights() {
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(),
1013,
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(),
1013,
construct_and_apply_extrinsic,
);
}
fn construct_extrinsic(
sender: sp_keyring::AccountKeyring,
call: RuntimeCall,
) -> UncheckedExtrinsic {
let account_id = AccountId32::from(sender.public());
let extra: SignedExtra = (
frame_system::CheckNonZeroSender::<Runtime>::new(),
frame_system::CheckSpecVersion::<Runtime>::new(),
frame_system::CheckTxVersion::<Runtime>::new(),
frame_system::CheckGenesis::<Runtime>::new(),
frame_system::CheckEra::<Runtime>::from(Era::immortal()),
frame_system::CheckNonce::<Runtime>::from(
frame_system::Pallet::<Runtime>::account(&account_id).nonce,
),
frame_system::CheckWeight::<Runtime>::new(),
pallet_transaction_payment::ChargeTransactionPayment::<Runtime>::from(0),
BridgeRejectObsoleteHeadersAndMessages::default(),
(
OnBridgeHubRococoRefundBridgeHubWestendMessages::default(),
OnBridgeHubRococoRefundRococoBulletinMessages::default(),
),
);
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.clone()),
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()
}
@@ -31,6 +31,7 @@ use parachains_common::{
rococo::{consensus::RELAY_CHAIN_SLOT_DURATION_MILLIS, fee::WeightToFee},
AccountId, AuraId, Balance, SLOT_DURATION,
};
use snowbridge_core::ChannelId;
use sp_consensus_aura::SlotDuration;
use sp_core::H160;
use sp_keyring::AccountKeyring::Alice;
@@ -222,6 +223,72 @@ mod bridge_hub_westend_tests {
)
}
#[test]
fn change_ethereum_nonces_by_governance_works() {
let channel_id_one: ChannelId = [1; 32].into();
let channel_id_two: ChannelId = [2; 32].into();
let nonce = 42;
// Reset a single inbound channel
bridge_hub_test_utils::test_cases::set_storage_keys_by_governance_works::<Runtime>(
collator_session_keys(),
bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID,
Box::new(|call| RuntimeCall::System(call).encode()),
vec![
(snowbridge_pallet_outbound_queue::Nonce::<Runtime>::hashed_key_for::<ChannelId>(
channel_id_one,
)
.to_vec(), 0u64.encode()),
(snowbridge_pallet_inbound_queue::Nonce::<Runtime>::hashed_key_for::<ChannelId>(
channel_id_one,
)
.to_vec(), 0u64.encode()),
],
|| {
// Outbound
snowbridge_pallet_outbound_queue::Nonce::<Runtime>::insert::<ChannelId, u64>(
channel_id_one,
nonce,
);
snowbridge_pallet_outbound_queue::Nonce::<Runtime>::insert::<ChannelId, u64>(
channel_id_two,
nonce,
);
// Inbound
snowbridge_pallet_inbound_queue::Nonce::<Runtime>::insert::<ChannelId, u64>(
channel_id_one,
nonce,
);
snowbridge_pallet_inbound_queue::Nonce::<Runtime>::insert::<ChannelId, u64>(
channel_id_two,
nonce,
);
},
|| {
// Outbound
assert_eq!(
snowbridge_pallet_outbound_queue::Nonce::<Runtime>::get(channel_id_one),
0
);
assert_eq!(
snowbridge_pallet_outbound_queue::Nonce::<Runtime>::get(channel_id_two),
nonce
);
// Inbound
assert_eq!(
snowbridge_pallet_inbound_queue::Nonce::<Runtime>::get(channel_id_one),
0
);
assert_eq!(
snowbridge_pallet_inbound_queue::Nonce::<Runtime>::get(channel_id_two),
nonce
);
},
);
}
#[test]
fn change_delivery_reward_by_governance_works() {
bridge_hub_test_utils::test_cases::change_storage_constant_by_governance_works::<
@@ -72,7 +72,9 @@ pub type RuntimeHelper<Runtime, AllPalletsWithoutSystem = ()> =
parachains_runtimes_test_utils::RuntimeHelper<Runtime, AllPalletsWithoutSystem>;
// Re-export test_case from `parachains-runtimes-test-utils`
pub use parachains_runtimes_test_utils::test_cases::change_storage_constant_by_governance_works;
pub use parachains_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>(
@@ -33,7 +33,7 @@ use polkadot_parachain_primitives::primitives::{
HeadData, HrmpChannelId, RelayChainBlockNumber, XcmpMessageFormat,
};
use sp_consensus_aura::{SlotDuration, AURA_ENGINE_ID};
use sp_core::Encode;
use sp_core::{Encode, U256};
use sp_runtime::{traits::Header, BuildStorage, Digest, DigestItem};
use xcm::{
latest::{Asset, Location, XcmContext, XcmHash},
@@ -300,6 +300,10 @@ where
<Runtime as frame_system::Config>::RuntimeOrigin::root()
}
pub fn block_number() -> U256 {
frame_system::Pallet::<Runtime>::block_number().into()
}
pub fn origin_of(
account_id: AccountIdOf<Runtime>,
) -> <Runtime as frame_system::Config>::RuntimeOrigin {
@@ -91,3 +91,55 @@ pub fn change_storage_constant_by_governance_works<Runtime, StorageConstant, Sto
);
})
}
/// Test-case makes sure that `Runtime` can change storage constant via governance-like call
pub fn set_storage_keys_by_governance_works<Runtime>(
collator_session_key: CollatorSessionKeys<Runtime>,
runtime_para_id: u32,
runtime_call_encode: Box<dyn Fn(frame_system::Call<Runtime>) -> Vec<u8>>,
storage_items: Vec<(Vec<u8>, Vec<u8>)>,
initialize_storage: impl FnOnce() -> (),
assert_storage: impl FnOnce() -> (),
) where
Runtime: frame_system::Config
+ pallet_balances::Config
+ pallet_session::Config
+ pallet_xcm::Config
+ parachain_info::Config
+ pallet_collator_selection::Config
+ cumulus_pallet_parachain_system::Config,
ValidatorIdOf<Runtime>: From<AccountIdOf<Runtime>>,
{
let mut runtime = ExtBuilder::<Runtime>::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();
runtime.execute_with(|| {
initialize_storage();
});
runtime.execute_with(|| {
// encode `kill_storage` call
let kill_storage_call = runtime_call_encode(frame_system::Call::<Runtime>::set_storage {
items: storage_items.clone(),
});
// estimate - storing just 1 value
use frame_system::WeightInfo;
let require_weight_at_most =
<Runtime as frame_system::Config>::SystemWeightInfo::set_storage(
storage_items.len().try_into().unwrap(),
);
// execute XCM with Transact to `set_storage` as governance does
assert_ok!(RuntimeHelper::<Runtime>::execute_as_governance(
kill_storage_call,
require_weight_at_most
)
.ensure_complete());
});
runtime.execute_with(|| {
assert_storage();
});
}