referrerpolicy=no-referrer-when-downgrade

bridge_hub_westend_runtime/
bridge_to_ethereum_config.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// This file is part of Cumulus.
3// SPDX-License-Identifier: Apache-2.0
4
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// 	http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
17use crate::{
18	bridge_common_config::BridgeReward,
19	xcm_config,
20	xcm_config::{RelayNetwork, RootLocation, TreasuryAccount, UniversalLocation, XcmConfig},
21	Balances, BridgeRelayers, EthereumBeaconClient, EthereumInboundQueue, EthereumInboundQueueV2,
22	EthereumOutboundQueue, EthereumOutboundQueueV2, EthereumSystem, EthereumSystemV2, MessageQueue,
23	Runtime, RuntimeEvent, TransactionByteFee,
24};
25use bp_asset_hub_westend::CreateForeignAssetDeposit;
26use frame_support::{parameter_types, traits::Contains, weights::ConstantMultiplier};
27use frame_system::EnsureRootWithSuccess;
28use hex_literal::hex;
29use pallet_xcm::EnsureXcm;
30use parachains_common::{AccountId, Balance};
31use snowbridge_beacon_primitives::{Fork, ForkVersions};
32use snowbridge_core::{gwei, meth, AllowSiblingsOnly, PricingParameters, Rewards};
33use snowbridge_outbound_queue_primitives::{
34	v1::{ConstantGasMeter, EthereumBlobExporter},
35	v2::{ConstantGasMeter as ConstantGasMeterV2, EthereumBlobExporter as EthereumBlobExporterV2},
36};
37use sp_core::H160;
38use sp_runtime::{
39	traits::{ConstU32, ConstU8, Keccak256},
40	FixedU128,
41};
42use testnet_parachains_constants::westend::{
43	currency::*,
44	fee::WeightToFee,
45	snowbridge::{
46		AssetHubParaId, EthereumLocation, EthereumNetwork, FRONTEND_PALLET_INDEX,
47		INBOUND_QUEUE_PALLET_INDEX_V1, INBOUND_QUEUE_PALLET_INDEX_V2,
48	},
49};
50use westend_runtime_constants::system_parachain::ASSET_HUB_ID;
51use xcm::prelude::{GlobalConsensus, InteriorLocation, Location, PalletInstance, Parachain};
52use xcm_executor::XcmExecutor;
53
54pub const SLOTS_PER_EPOCH: u32 = snowbridge_pallet_ethereum_client::config::SLOTS_PER_EPOCH as u32;
55
56/// Exports message to the Ethereum Gateway contract.
57pub type SnowbridgeExporter = EthereumBlobExporter<
58	UniversalLocation,
59	EthereumNetwork,
60	snowbridge_pallet_outbound_queue::Pallet<Runtime>,
61	snowbridge_core::AgentIdOf,
62	EthereumSystem,
63>;
64
65pub type SnowbridgeExporterV2 = EthereumBlobExporterV2<
66	UniversalLocation,
67	EthereumNetwork,
68	EthereumOutboundQueueV2,
69	EthereumSystemV2,
70	AssetHubParaId,
71>;
72
73// Ethereum Bridge
74parameter_types! {
75	pub storage EthereumGatewayAddress: H160 = H160(hex!("b1185ede04202fe62d38f5db72f71e38ff3e8305"));
76}
77
78parameter_types! {
79	pub const CreateAssetCall: [u8;2] = [53, 0];
80	pub Parameters: PricingParameters<u128> = PricingParameters {
81		exchange_rate: FixedU128::from_rational(1, 400),
82		fee_per_gas: gwei(20),
83		rewards: Rewards { local: 1 * UNITS, remote: meth(1) },
84		multiplier: FixedU128::from_rational(1, 1),
85	};
86	pub AssetHubFromEthereum: Location = Location::new(1, [GlobalConsensus(RelayNetwork::get()), Parachain(ASSET_HUB_ID)]);
87	pub EthereumUniversalLocation: InteriorLocation = [GlobalConsensus(EthereumNetwork::get())].into();
88	pub AssetHubUniversalLocation: InteriorLocation = [GlobalConsensus(RelayNetwork::get()), Parachain(ASSET_HUB_ID)].into();
89	pub InboundQueueV2Location: InteriorLocation = [PalletInstance(INBOUND_QUEUE_PALLET_INDEX_V2)].into();
90	pub const SnowbridgeReward: BridgeReward = BridgeReward::Snowbridge;
91	pub SnowbridgeFrontendLocation: Location = Location::new(1, [Parachain(ASSET_HUB_ID), PalletInstance(FRONTEND_PALLET_INDEX)]);
92}
93
94impl snowbridge_pallet_inbound_queue::Config for Runtime {
95	type RuntimeEvent = RuntimeEvent;
96	type Verifier = snowbridge_pallet_ethereum_client::Pallet<Runtime>;
97	type Token = Balances;
98	#[cfg(not(feature = "runtime-benchmarks"))]
99	type XcmSender = crate::XcmRouter;
100	#[cfg(feature = "runtime-benchmarks")]
101	type XcmSender = benchmark_helpers::DoNothingRouter;
102	type ChannelLookup = EthereumSystem;
103	type GatewayAddress = EthereumGatewayAddress;
104	#[cfg(feature = "runtime-benchmarks")]
105	type Helper = Runtime;
106	type MessageConverter = snowbridge_inbound_queue_primitives::v1::MessageToXcm<
107		CreateAssetCall,
108		CreateForeignAssetDeposit,
109		ConstU8<INBOUND_QUEUE_PALLET_INDEX_V1>,
110		AccountId,
111		Balance,
112		EthereumSystem,
113		EthereumUniversalLocation,
114		AssetHubFromEthereum,
115	>;
116	type WeightToFee = WeightToFee;
117	type LengthToFee = ConstantMultiplier<Balance, TransactionByteFee>;
118	type MaxMessageSize = ConstU32<2048>;
119	type WeightInfo = crate::weights::snowbridge_pallet_inbound_queue::WeightInfo<Runtime>;
120	type PricingParameters = EthereumSystem;
121	type AssetTransactor = <xcm_config::XcmConfig as xcm_executor::Config>::AssetTransactor;
122}
123
124impl snowbridge_pallet_inbound_queue_v2::Config for Runtime {
125	type RuntimeEvent = RuntimeEvent;
126	type Verifier = EthereumBeaconClient;
127	#[cfg(not(feature = "runtime-benchmarks"))]
128	type XcmSender = crate::XcmRouter;
129	#[cfg(feature = "runtime-benchmarks")]
130	type XcmSender = benchmark_helpers::DoNothingRouter;
131	type GatewayAddress = EthereumGatewayAddress;
132	#[cfg(feature = "runtime-benchmarks")]
133	type Helper = Runtime;
134	type WeightInfo = crate::weights::snowbridge_pallet_inbound_queue_v2::WeightInfo<Runtime>;
135	type AssetHubParaId = AssetHubParaId;
136	type XcmExecutor = XcmExecutor<XcmConfig>;
137	type MessageConverter = snowbridge_inbound_queue_primitives::v2::MessageToXcm<
138		CreateAssetCall,
139		CreateForeignAssetDeposit,
140		EthereumNetwork,
141		InboundQueueV2Location,
142		EthereumSystem,
143		EthereumGatewayAddress,
144		EthereumUniversalLocation,
145		AssetHubFromEthereum,
146		AssetHubUniversalLocation,
147		AccountId,
148	>;
149	type AccountToLocation = xcm_builder::AliasesIntoAccountId32<
150		xcm_config::RelayNetwork,
151		<Runtime as frame_system::Config>::AccountId,
152	>;
153	type RewardKind = BridgeReward;
154	type DefaultRewardKind = SnowbridgeReward;
155	type RewardPayment = BridgeRelayers;
156}
157
158impl snowbridge_pallet_outbound_queue::Config for Runtime {
159	type RuntimeEvent = RuntimeEvent;
160	type Hashing = Keccak256;
161	type MessageQueue = MessageQueue;
162	type Decimals = ConstU8<12>;
163	type MaxMessagePayloadSize = ConstU32<2048>;
164	type MaxMessagesPerBlock = ConstU32<32>;
165	type GasMeter = ConstantGasMeter;
166	type Balance = Balance;
167	type WeightToFee = WeightToFee;
168	type WeightInfo = crate::weights::snowbridge_pallet_outbound_queue::WeightInfo<Runtime>;
169	type PricingParameters = EthereumSystem;
170	type Channels = EthereumSystem;
171}
172
173impl snowbridge_pallet_outbound_queue_v2::Config for Runtime {
174	type RuntimeEvent = RuntimeEvent;
175	type Hashing = Keccak256;
176	type MessageQueue = MessageQueue;
177	// Maximum payload size for outbound messages.
178	type MaxMessagePayloadSize = ConstU32<2048>;
179	// Maximum number of outbound messages that can be committed per block.
180	// It's benchmarked, including the entire process flow(initialize,submit,commit) in the
181	// worst-case, Benchmark results in `../weights/snowbridge_pallet_outbound_queue_v2.
182	// rs` show that the `process` function consumes less than 1% of the block capacity, which is
183	// safe enough.
184	type MaxMessagesPerBlock = ConstU32<32>;
185	type GasMeter = ConstantGasMeterV2;
186	type Balance = Balance;
187	type WeightToFee = WeightToFee;
188	type Verifier = EthereumBeaconClient;
189	type GatewayAddress = EthereumGatewayAddress;
190	type WeightInfo = crate::weights::snowbridge_pallet_outbound_queue_v2::WeightInfo<Runtime>;
191	type EthereumNetwork = EthereumNetwork;
192	type RewardKind = BridgeReward;
193	type DefaultRewardKind = SnowbridgeReward;
194	type RewardPayment = BridgeRelayers;
195	#[cfg(feature = "runtime-benchmarks")]
196	type Helper = Runtime;
197}
198
199#[cfg(not(any(feature = "std", feature = "fast-runtime", feature = "runtime-benchmarks", test)))]
200parameter_types! {
201	pub const ChainForkVersions: ForkVersions = ForkVersions {
202		genesis: Fork {
203			version: hex!("90000069"),
204			epoch: 0,
205		},
206		altair: Fork {
207			version: hex!("90000070"),
208			epoch: 50,
209		},
210		bellatrix: Fork {
211			version: hex!("90000071"),
212			epoch: 100,
213		},
214		capella: Fork {
215			version: hex!("90000072"),
216			epoch: 56832,
217		},
218		deneb: Fork {
219			version: hex!("90000073"),
220			epoch: 132608,
221		},
222		electra: Fork {
223			version: hex!("90000074"),
224			epoch: 222464, // https://github.com/ethereum/EIPs/pull/9322/files
225		},
226	};
227}
228
229#[cfg(any(feature = "std", feature = "fast-runtime", feature = "runtime-benchmarks", test))]
230parameter_types! {
231	pub const ChainForkVersions: ForkVersions = ForkVersions {
232		genesis: Fork {
233			version: hex!("00000000"),
234			epoch: 0,
235		},
236		altair: Fork {
237			version: hex!("01000000"),
238			epoch: 0,
239		},
240		bellatrix: Fork {
241			version: hex!("02000000"),
242			epoch: 0,
243		},
244		capella: Fork {
245			version: hex!("03000000"),
246			epoch: 0,
247		},
248		deneb: Fork {
249			version: hex!("04000000"),
250			epoch: 0,
251		},
252		electra: Fork {
253			version: hex!("05000000"),
254			epoch: 0,
255		},
256	};
257}
258
259impl snowbridge_pallet_ethereum_client::Config for Runtime {
260	type RuntimeEvent = RuntimeEvent;
261	type ForkVersions = ChainForkVersions;
262	type FreeHeadersInterval = ConstU32<SLOTS_PER_EPOCH>;
263	type WeightInfo = crate::weights::snowbridge_pallet_ethereum_client::WeightInfo<Runtime>;
264}
265
266impl snowbridge_pallet_system::Config for Runtime {
267	type RuntimeEvent = RuntimeEvent;
268	type OutboundQueue = EthereumOutboundQueue;
269	type SiblingOrigin = EnsureXcm<AllowSiblingsOnly>;
270	type AgentIdOf = snowbridge_core::AgentIdOf;
271	type TreasuryAccount = TreasuryAccount;
272	type Token = Balances;
273	type WeightInfo = crate::weights::snowbridge_pallet_system::WeightInfo<Runtime>;
274	#[cfg(feature = "runtime-benchmarks")]
275	type Helper = ();
276	type DefaultPricingParameters = Parameters;
277	type InboundDeliveryCost = EthereumInboundQueue;
278	type UniversalLocation = UniversalLocation;
279	type EthereumLocation = EthereumLocation;
280}
281
282pub struct AllowFromEthereumFrontend;
283impl Contains<Location> for AllowFromEthereumFrontend {
284	fn contains(location: &Location) -> bool {
285		match location.unpack() {
286			(1, [Parachain(para_id), PalletInstance(index)]) =>
287				return *para_id == ASSET_HUB_ID && *index == FRONTEND_PALLET_INDEX,
288			_ => false,
289		}
290	}
291}
292
293impl snowbridge_pallet_system_v2::Config for Runtime {
294	type RuntimeEvent = RuntimeEvent;
295	type OutboundQueue = EthereumOutboundQueueV2;
296	type InboundQueue = EthereumInboundQueueV2;
297	type FrontendOrigin = EnsureXcm<AllowFromEthereumFrontend>;
298	type WeightInfo = crate::weights::snowbridge_pallet_system_v2::WeightInfo<Runtime>;
299	type GovernanceOrigin = EnsureRootWithSuccess<crate::AccountId, RootLocation>;
300	#[cfg(feature = "runtime-benchmarks")]
301	type Helper = ();
302}
303
304#[cfg(feature = "runtime-benchmarks")]
305pub mod benchmark_helpers {
306	use crate::{
307		bridge_to_ethereum_config::EthereumGatewayAddress, vec, EthereumBeaconClient, Runtime,
308		RuntimeOrigin, System,
309	};
310	use codec::Encode;
311	use hex_literal::hex;
312	use snowbridge_beacon_primitives::BeaconHeader;
313	use snowbridge_pallet_inbound_queue::BenchmarkHelper;
314	use snowbridge_pallet_inbound_queue_v2::BenchmarkHelper as InboundQueueBenchmarkHelperV2;
315	use snowbridge_pallet_outbound_queue_v2::BenchmarkHelper as OutboundQueueBenchmarkHelperV2;
316	use sp_core::H256;
317	use xcm::latest::{Assets, Location, SendError, SendResult, SendXcm, Xcm, XcmHash};
318
319	impl<T: snowbridge_pallet_ethereum_client::Config> BenchmarkHelper<T> for Runtime {
320		fn initialize_storage(beacon_header: BeaconHeader, block_roots_root: H256) {
321			EthereumBeaconClient::store_finalized_header(beacon_header, block_roots_root).unwrap();
322			System::set_storage(
323				RuntimeOrigin::root(),
324				vec![(
325					EthereumGatewayAddress::key().to_vec(),
326					hex!("EDa338E4dC46038493b885327842fD3E301CaB39").to_vec(),
327				)],
328			)
329			.unwrap();
330		}
331	}
332
333	impl<T: snowbridge_pallet_inbound_queue_v2::Config> InboundQueueBenchmarkHelperV2<T> for Runtime {
334		fn initialize_storage(beacon_header: BeaconHeader, block_roots_root: H256) {
335			EthereumBeaconClient::store_finalized_header(beacon_header, block_roots_root).unwrap();
336		}
337	}
338
339	impl<T: snowbridge_pallet_outbound_queue_v2::Config> OutboundQueueBenchmarkHelperV2<T> for Runtime {
340		fn initialize_storage(beacon_header: BeaconHeader, block_roots_root: H256) {
341			EthereumBeaconClient::store_finalized_header(beacon_header, block_roots_root).unwrap();
342		}
343	}
344
345	pub struct DoNothingRouter;
346	impl SendXcm for DoNothingRouter {
347		type Ticket = Xcm<()>;
348
349		fn validate(
350			_dest: &mut Option<Location>,
351			xcm: &mut Option<Xcm<()>>,
352		) -> SendResult<Self::Ticket> {
353			Ok((xcm.clone().unwrap(), Assets::new()))
354		}
355		fn deliver(xcm: Xcm<()>) -> Result<XcmHash, SendError> {
356			let hash = xcm.using_encoded(sp_io::hashing::blake2_256);
357			Ok(hash)
358		}
359	}
360
361	impl snowbridge_pallet_system::BenchmarkHelper<RuntimeOrigin> for () {
362		fn make_xcm_origin(location: Location) -> RuntimeOrigin {
363			RuntimeOrigin::from(pallet_xcm::Origin::Xcm(location))
364		}
365	}
366
367	impl snowbridge_pallet_system_v2::BenchmarkHelper<RuntimeOrigin> for () {
368		fn make_xcm_origin(location: Location) -> RuntimeOrigin {
369			RuntimeOrigin::from(pallet_xcm::Origin::Xcm(location))
370		}
371	}
372}
373
374#[cfg(test)]
375mod tests {
376	use super::*;
377
378	#[test]
379	fn bridge_hub_inbound_queue_pallet_index_is_correct() {
380		assert_eq!(
381			INBOUND_QUEUE_PALLET_INDEX_V1,
382			<EthereumInboundQueue as frame_support::traits::PalletInfoAccess>::index() as u8
383		);
384	}
385
386	#[test]
387	fn bridge_hub_inbound_v2_queue_pallet_index_is_correct() {
388		assert_eq!(
389			INBOUND_QUEUE_PALLET_INDEX_V2,
390			<EthereumInboundQueueV2 as frame_support::traits::PalletInfoAccess>::index() as u8
391		);
392	}
393}
394
395pub(crate) mod migrations {
396	use frame_support::pallet_prelude::*;
397	use snowbridge_core::TokenId;
398
399	#[frame_support::storage_alias]
400	pub type OldNativeToForeignId<T: snowbridge_pallet_system::Config> = StorageMap<
401		snowbridge_pallet_system::Pallet<T>,
402		Blake2_128Concat,
403		xcm::v4::Location,
404		TokenId,
405		OptionQuery,
406	>;
407
408	/// One shot migration for NetworkId::Westend to NetworkId::ByGenesis(WESTEND_GENESIS_HASH)
409	pub struct MigrationForXcmV5<T: snowbridge_pallet_system::Config>(core::marker::PhantomData<T>);
410	impl<T: snowbridge_pallet_system::Config> frame_support::traits::OnRuntimeUpgrade
411		for MigrationForXcmV5<T>
412	{
413		fn on_runtime_upgrade() -> Weight {
414			let mut weight = T::DbWeight::get().reads(1);
415
416			let translate_westend = |pre: xcm::v4::Location| -> Option<xcm::v5::Location> {
417				weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));
418				Some(xcm::v5::Location::try_from(pre).expect("valid location"))
419			};
420			snowbridge_pallet_system::ForeignToNativeId::<T>::translate_values(translate_westend);
421
422			weight
423		}
424	}
425}