From 738a2deef3fda974efce7e931b729ee2d8991848 Mon Sep 17 00:00:00 2001 From: ron Date: Fri, 18 Oct 2024 09:31:24 +0800 Subject: [PATCH 01/37] Inbound queue v2 --- .../pallets/inbound-queue-v2/src/envelope.rs | 16 ++- .../pallets/inbound-queue-v2/src/lib.rs | 127 +++++------------- .../pallets/inbound-queue-v2/src/mock.rs | 62 +-------- .../pallets/inbound-queue-v2/src/test.rs | 11 -- .../primitives/router-v2/src/inbound/mod.rs | 12 +- .../src/bridge_to_ethereum_config.rs | 3 - 6 files changed, 51 insertions(+), 180 deletions(-) diff --git a/bridges/snowbridge/pallets/inbound-queue-v2/src/envelope.rs b/bridges/snowbridge/pallets/inbound-queue-v2/src/envelope.rs index 31a8992442d8..00c9f13c3959 100644 --- a/bridges/snowbridge/pallets/inbound-queue-v2/src/envelope.rs +++ b/bridges/snowbridge/pallets/inbound-queue-v2/src/envelope.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-FileCopyrightText: 2023 Snowfork -use snowbridge_core::{inbound::Log, ChannelId}; +use snowbridge_core::inbound::Log; use sp_core::{RuntimeDebug, H160, H256}; use sp_std::prelude::*; @@ -9,7 +9,7 @@ use alloy_primitives::B256; use alloy_sol_types::{sol, SolEvent}; sol! { - event OutboundMessageAccepted(bytes32 indexed channel_id, uint64 nonce, bytes32 indexed message_id, bytes payload); + event OutboundMessageAccepted(uint64 indexed nonce, bytes32 indexed message_id, uint32 indexed para_id, bytes32 reward_address, uint128 fee, bytes payload); } /// An inbound message that has had its outer envelope decoded. @@ -17,12 +17,16 @@ sol! { pub struct Envelope { /// The address of the outbound queue on Ethereum that emitted this message as an event log pub gateway: H160, - /// The message Channel - pub channel_id: ChannelId, /// A nonce for enforcing replay protection and ordering. pub nonce: u64, /// An id for tracing the message on its route (has no role in bridge consensus) pub message_id: H256, + /// Destination ParaId + pub para_id: u32, + /// The reward address + pub reward_address: [u8; 32], + /// Total fee paid on source chain + pub fee: u128, /// The inner payload generated from the source application. pub payload: Vec, } @@ -41,9 +45,11 @@ impl TryFrom<&Log> for Envelope { Ok(Self { gateway: log.address, - channel_id: ChannelId::from(event.channel_id.as_ref()), nonce: event.nonce, message_id: H256::from(event.message_id.as_ref()), + reward_address: event.reward_address.clone().into(), + fee: event.fee, + para_id: event.para_id, payload: event.payload, }) } diff --git a/bridges/snowbridge/pallets/inbound-queue-v2/src/lib.rs b/bridges/snowbridge/pallets/inbound-queue-v2/src/lib.rs index 63da2b56ac50..b60bb60cb845 100644 --- a/bridges/snowbridge/pallets/inbound-queue-v2/src/lib.rs +++ b/bridges/snowbridge/pallets/inbound-queue-v2/src/lib.rs @@ -41,7 +41,7 @@ use envelope::Envelope; use frame_support::{ traits::{ fungible::{Inspect, Mutate}, - tokens::{Fortitude, Preservation}, + tokens::{Fortitude, Precision, Preservation}, }, weights::WeightToFee, PalletError, @@ -49,22 +49,18 @@ use frame_support::{ use frame_system::ensure_signed; use scale_info::TypeInfo; use sp_core::H160; -use sp_runtime::traits::Zero; use sp_std::vec; use xcm::prelude::{ - send_xcm, Junction::*, Location, SendError as XcmpSendError, SendXcm, Xcm, XcmContext, XcmHash, + send_xcm, Junction::*, Location, SendError as XcmpSendError, SendXcm, Xcm, XcmHash, }; -use xcm_executor::traits::TransactAsset; use snowbridge_core::{ inbound::{Message, VerificationError, Verifier}, - sibling_sovereign_account, BasicOperatingMode, Channel, ChannelId, ParaId, PricingParameters, - StaticLookup, + sibling_sovereign_account, BasicOperatingMode, ParaId, }; use snowbridge_router_primitives_v2::inbound::{ ConvertMessage, ConvertMessageError, VersionedMessage, }; -use sp_runtime::{traits::Saturating, SaturatedConversion, TokenError}; pub use weights::WeightInfo; @@ -76,7 +72,7 @@ type BalanceOf = pub use pallet::*; -pub const LOG_TARGET: &str = "snowbridge-inbound-queue"; +pub const LOG_TARGET: &str = "snowbridge-inbound-queue:v2"; #[frame_support::pallet] pub mod pallet { @@ -101,7 +97,7 @@ pub mod pallet { /// The verifier for inbound messages from Ethereum type Verifier: Verifier; - /// Message relayers are rewarded with this asset + /// Burn fees from relayer type Token: Mutate + Inspect; /// XCM message sender @@ -117,12 +113,6 @@ pub mod pallet { Balance = BalanceOf, >; - /// Lookup a channel descriptor - type ChannelLookup: StaticLookup; - - /// Lookup pricing parameters - type PricingParameters: Get>>; - type WeightInfo: WeightInfo; #[cfg(feature = "runtime-benchmarks")] @@ -136,9 +126,6 @@ pub mod pallet { /// The upper limit here only used to estimate delivery cost type MaxMessageSize: Get; - - /// To withdraw and deposit an asset. - type AssetTransactor: TransactAsset; } #[pallet::hooks] @@ -149,8 +136,6 @@ pub mod pallet { pub enum Event { /// A message was received from Ethereum MessageReceived { - /// The message channel - channel_id: ChannelId, /// The message nonce nonce: u64, /// ID of the XCM message which was forwarded to the final destination parachain @@ -215,9 +200,9 @@ pub mod pallet { } } - /// The current nonce for each channel + /// The nonce of the message been processed or not #[pallet::storage] - pub type Nonce = StorageMap<_, Twox64Concat, ChannelId, u64, ValueQuery>; + pub type Nonce = StorageMap<_, Identity, u64, bool, ValueQuery>; /// The current operating mode of the pallet. #[pallet::storage] @@ -244,36 +229,8 @@ pub mod pallet { // Verify that the message was submitted from the known Gateway contract ensure!(T::GatewayAddress::get() == envelope.gateway, Error::::InvalidGateway); - // Retrieve the registered channel for this message - let channel = - T::ChannelLookup::lookup(envelope.channel_id).ok_or(Error::::InvalidChannel)?; - - // Verify message nonce - >::try_mutate(envelope.channel_id, |nonce| -> DispatchResult { - if *nonce == u64::MAX { - return Err(Error::::MaxNonceReached.into()) - } - if envelope.nonce != nonce.saturating_add(1) { - Err(Error::::InvalidNonce.into()) - } else { - *nonce = nonce.saturating_add(1); - Ok(()) - } - })?; - - // Reward relayer from the sovereign account of the destination parachain, only if funds - // are available - let sovereign_account = sibling_sovereign_account::(channel.para_id); - let delivery_cost = Self::calculate_delivery_cost(message.encode().len() as u32); - let amount = T::Token::reducible_balance( - &sovereign_account, - Preservation::Preserve, - Fortitude::Polite, - ) - .min(delivery_cost); - if !amount.is_zero() { - T::Token::transfer(&sovereign_account, &who, amount, Preservation::Preserve)?; - } + // Verify the message has not been processed + ensure!(!>::contains_key(envelope.nonce), Error::::InvalidNonce); // Decode payload into `VersionedMessage` let message = VersionedMessage::decode_all(&mut envelope.payload.as_ref()) @@ -290,13 +247,33 @@ pub mod pallet { ); // Burning fees for teleport - Self::burn_fees(channel.para_id, fee)?; + T::Token::burn_from( + &who, + fee, + Preservation::Preserve, + Precision::BestEffort, + Fortitude::Polite, + )?; // Attempt to send XCM to a dest parachain - let message_id = Self::send_xcm(xcm, channel.para_id)?; + let message_id = Self::send_xcm(xcm, envelope.para_id.into())?; + + // Set nonce flag to true + >::try_mutate(envelope.nonce, |done| -> DispatchResult { + *done = true; + Ok(()) + })?; + + // Todo: Deposit fee to RewardLeger which should contains all of: + // a. The submit extrinsic cost on BH + // b. The delivery cost to AH + // c. The execution cost on AH + // d. The execution cost on destination chain(if any) + // e. The reward + + // T::RewardLeger::deposit(envelope.reward_address.into(), envelope.fee.into())?; Self::deposit_event(Event::MessageReceived { - channel_id: envelope.channel_id, nonce: envelope.nonce, message_id, fee_burned: fee, @@ -334,45 +311,5 @@ pub mod pallet { let (xcm_hash, _) = send_xcm::(dest, xcm).map_err(Error::::from)?; Ok(xcm_hash) } - - pub fn calculate_delivery_cost(length: u32) -> BalanceOf { - let weight_fee = T::WeightToFee::weight_to_fee(&T::WeightInfo::submit()); - let len_fee = T::LengthToFee::weight_to_fee(&Weight::from_parts(length as u64, 0)); - weight_fee - .saturating_add(len_fee) - .saturating_add(T::PricingParameters::get().rewards.local) - } - - /// Burn the amount of the fee embedded into the XCM for teleports - pub fn burn_fees(para_id: ParaId, fee: BalanceOf) -> DispatchResult { - let dummy_context = - XcmContext { origin: None, message_id: Default::default(), topic: None }; - let dest = Location::new(1, [Parachain(para_id.into())]); - let fees = (Location::parent(), fee.saturated_into::()).into(); - T::AssetTransactor::can_check_out(&dest, &fees, &dummy_context).map_err(|error| { - log::error!( - target: LOG_TARGET, - "XCM asset check out failed with error {:?}", error - ); - TokenError::FundsUnavailable - })?; - T::AssetTransactor::check_out(&dest, &fees, &dummy_context); - T::AssetTransactor::withdraw_asset(&fees, &dest, None).map_err(|error| { - log::error!( - target: LOG_TARGET, - "XCM asset withdraw failed with error {:?}", error - ); - TokenError::FundsUnavailable - })?; - Ok(()) - } - } - - /// API for accessing the delivery cost of a message - impl Get> for Pallet { - fn get() -> BalanceOf { - // Cost here based on MaxMessagePayloadSize(the worst case) - Self::calculate_delivery_cost(T::MaxMessageSize::get()) - } } } diff --git a/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs b/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs index 3e67d5ab738b..6bf805d9c91f 100644 --- a/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs +++ b/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs @@ -8,11 +8,10 @@ use snowbridge_beacon_primitives::{ types::deneb, BeaconHeader, ExecutionProof, Fork, ForkVersions, VersionedExecutionPayloadHeader, }; use snowbridge_core::{ - gwei, inbound::{Log, Proof, VerificationError}, - meth, Channel, ChannelId, PricingParameters, Rewards, StaticLookup, TokenId, + TokenId, }; -use snowbridge_router_primitives::inbound::MessageToXcm; +use snowbridge_router_primitives_v2::inbound::MessageToXcm; use sp_core::{H160, H256}; use sp_runtime::{ traits::{IdentifyAccount, IdentityLookup, MaybeEquivalence, Verify}, @@ -20,7 +19,6 @@ use sp_runtime::{ }; use sp_std::{convert::From, default::Default}; use xcm::{latest::SendXcm, prelude::*}; -use xcm_executor::AssetsInHolding; use crate::{self as inbound_queue}; @@ -151,63 +149,10 @@ impl SendXcm for MockXcmSender { parameter_types! { pub const OwnParaId: ParaId = ParaId::new(1013); - pub Parameters: PricingParameters = PricingParameters { - exchange_rate: FixedU128::from_rational(1, 400), - fee_per_gas: gwei(20), - rewards: Rewards { local: DOT, remote: meth(1) }, - multiplier: FixedU128::from_rational(1, 1), - }; } pub const DOT: u128 = 10_000_000_000; -pub struct MockChannelLookup; -impl StaticLookup for MockChannelLookup { - type Source = ChannelId; - type Target = Channel; - - fn lookup(channel_id: Self::Source) -> Option { - if channel_id != - hex!("c173fac324158e77fb5840738a1a541f633cbec8884c6a601c567d2b376a0539").into() - { - return None - } - Some(Channel { agent_id: H256::zero(), para_id: ASSET_HUB_PARAID.into() }) - } -} - -pub struct SuccessfulTransactor; -impl TransactAsset for SuccessfulTransactor { - fn can_check_in(_origin: &Location, _what: &Asset, _context: &XcmContext) -> XcmResult { - Ok(()) - } - - fn can_check_out(_dest: &Location, _what: &Asset, _context: &XcmContext) -> XcmResult { - Ok(()) - } - - fn deposit_asset(_what: &Asset, _who: &Location, _context: Option<&XcmContext>) -> XcmResult { - Ok(()) - } - - fn withdraw_asset( - _what: &Asset, - _who: &Location, - _context: Option<&XcmContext>, - ) -> Result { - Ok(AssetsInHolding::default()) - } - - fn internal_transfer_asset( - _what: &Asset, - _from: &Location, - _to: &Location, - _context: &XcmContext, - ) -> Result { - Ok(AssetsInHolding::default()) - } -} - pub struct MockTokenIdConvert; impl MaybeEquivalence for MockTokenIdConvert { fn convert(_id: &TokenId) -> Option { @@ -235,14 +180,11 @@ impl inbound_queue::Config for Test { UniversalLocation, AssetHubFromEthereum, >; - type PricingParameters = Parameters; - type ChannelLookup = MockChannelLookup; #[cfg(feature = "runtime-benchmarks")] type Helper = Test; type WeightToFee = IdentityFee; type LengthToFee = IdentityFee; type MaxMessageSize = ConstU32<1024>; - type AssetTransactor = SuccessfulTransactor; } pub fn last_events(n: usize) -> Vec { diff --git a/bridges/snowbridge/pallets/inbound-queue-v2/src/test.rs b/bridges/snowbridge/pallets/inbound-queue-v2/src/test.rs index 41c38460aabf..776dc3807898 100644 --- a/bridges/snowbridge/pallets/inbound-queue-v2/src/test.rs +++ b/bridges/snowbridge/pallets/inbound-queue-v2/src/test.rs @@ -36,8 +36,6 @@ fn test_submit_happy_path() { assert_ok!(InboundQueue::submit(origin.clone(), message.clone())); expect_events(vec![InboundQueueEvent::MessageReceived { - channel_id: hex!("c173fac324158e77fb5840738a1a541f633cbec8884c6a601c567d2b376a0539") - .into(), nonce: 1, message_id: [ 255, 125, 48, 71, 174, 185, 100, 26, 159, 43, 108, 6, 116, 218, 55, 155, 223, 143, @@ -48,10 +46,6 @@ fn test_submit_happy_path() { .into()]); let delivery_cost = InboundQueue::calculate_delivery_cost(message.encode().len() as u32); - assert!( - Parameters::get().rewards.local < delivery_cost, - "delivery cost exceeds pure reward" - ); assert_eq!(Balances::balance(&relayer), delivery_cost, "relayer was rewarded"); assert!( @@ -132,11 +126,6 @@ fn test_submit_with_invalid_nonce() { }; assert_ok!(InboundQueue::submit(origin.clone(), message.clone())); - let nonce: u64 = >::get(ChannelId::from(hex!( - "c173fac324158e77fb5840738a1a541f633cbec8884c6a601c567d2b376a0539" - ))); - assert_eq!(nonce, 1); - // Submit the same again assert_noop!( InboundQueue::submit(origin.clone(), message.clone()), diff --git a/bridges/snowbridge/primitives/router-v2/src/inbound/mod.rs b/bridges/snowbridge/primitives/router-v2/src/inbound/mod.rs index fbfc52d01c83..998008ab4871 100644 --- a/bridges/snowbridge/primitives/router-v2/src/inbound/mod.rs +++ b/bridges/snowbridge/primitives/router-v2/src/inbound/mod.rs @@ -24,13 +24,13 @@ const MINIMUM_DEPOSIT: u128 = 1; /// Instead having BridgeHub transcode the messages into XCM. #[derive(Clone, Encode, Decode, RuntimeDebug)] pub enum VersionedMessage { - V1(MessageV1), + V2(MessageV2), } -/// For V1, the ethereum side sends messages which are transcoded into XCM. These messages are +/// For V2, the ethereum side sends messages which are transcoded into XCM. These messages are /// self-contained, in that they can be transcoded using only information in the message. #[derive(Clone, Encode, Decode, RuntimeDebug)] -pub struct MessageV1 { +pub struct MessageV2 { /// EIP-155 chain id of the origin Ethereum network pub chain_id: u64, /// The command originating from the Gateway contract @@ -189,11 +189,11 @@ where use Command::*; use VersionedMessage::*; match message { - V1(MessageV1 { chain_id, command: RegisterToken { token, fee } }) => + V2(MessageV2 { chain_id, command: RegisterToken { token, fee } }) => Ok(Self::convert_register_token(message_id, chain_id, token, fee)), - V1(MessageV1 { chain_id, command: SendToken { token, destination, amount, fee } }) => + V2(MessageV2 { chain_id, command: SendToken { token, destination, amount, fee } }) => Ok(Self::convert_send_token(message_id, chain_id, token, destination, amount, fee)), - V1(MessageV1 { + V2(MessageV2 { chain_id, command: SendNativeToken { token_id, destination, amount, fee }, }) => Self::convert_send_native_token( diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs index 2ae0e59e06a1..515a15e52ce2 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs @@ -122,7 +122,6 @@ impl snowbridge_pallet_inbound_queue_v2::Config for Runtime { type XcmSender = XcmRouter; #[cfg(feature = "runtime-benchmarks")] type XcmSender = DoNothingRouter; - type ChannelLookup = EthereumSystem; type GatewayAddress = EthereumGatewayAddress; #[cfg(feature = "runtime-benchmarks")] type Helper = Runtime; @@ -140,8 +139,6 @@ impl snowbridge_pallet_inbound_queue_v2::Config for Runtime { type LengthToFee = ConstantMultiplier; type MaxMessageSize = ConstU32<2048>; type WeightInfo = crate::weights::snowbridge_pallet_inbound_queue_v2::WeightInfo; - type PricingParameters = EthereumSystem; - type AssetTransactor = ::AssetTransactor; } impl snowbridge_pallet_outbound_queue::Config for Runtime { From f6b9968d1d36236d30569d7c5696ca0fd81ba677 Mon Sep 17 00:00:00 2001 From: ron Date: Sun, 20 Oct 2024 13:51:46 +0800 Subject: [PATCH 02/37] Respect V2 contract --- .../pallets/inbound-queue-v2/src/envelope.rs | 16 +- .../pallets/inbound-queue-v2/src/lib.rs | 67 +-- .../pallets/inbound-queue-v2/src/mock.rs | 11 - .../pallets/inbound-queue-v2/src/test.rs | 9 - .../primitives/router-v2/src/inbound/mod.rs | 425 +----------------- .../src/bridge_to_ethereum_config.rs | 15 +- 6 files changed, 29 insertions(+), 514 deletions(-) diff --git a/bridges/snowbridge/pallets/inbound-queue-v2/src/envelope.rs b/bridges/snowbridge/pallets/inbound-queue-v2/src/envelope.rs index 00c9f13c3959..a1dda45f482f 100644 --- a/bridges/snowbridge/pallets/inbound-queue-v2/src/envelope.rs +++ b/bridges/snowbridge/pallets/inbound-queue-v2/src/envelope.rs @@ -2,14 +2,14 @@ // SPDX-FileCopyrightText: 2023 Snowfork use snowbridge_core::inbound::Log; -use sp_core::{RuntimeDebug, H160, H256}; +use sp_core::{RuntimeDebug, H160}; use sp_std::prelude::*; use alloy_primitives::B256; use alloy_sol_types::{sol, SolEvent}; sol! { - event OutboundMessageAccepted(uint64 indexed nonce, bytes32 indexed message_id, uint32 indexed para_id, bytes32 reward_address, uint128 fee, bytes payload); + event OutboundMessageAccepted(uint64 indexed nonce, uint128 fee, bytes32 reward_address, bytes payload); } /// An inbound message that has had its outer envelope decoded. @@ -19,14 +19,10 @@ pub struct Envelope { pub gateway: H160, /// A nonce for enforcing replay protection and ordering. pub nonce: u64, - /// An id for tracing the message on its route (has no role in bridge consensus) - pub message_id: H256, - /// Destination ParaId - pub para_id: u32, - /// The reward address - pub reward_address: [u8; 32], /// Total fee paid on source chain pub fee: u128, + /// The reward address + pub reward_address: [u8; 32], /// The inner payload generated from the source application. pub payload: Vec, } @@ -46,10 +42,8 @@ impl TryFrom<&Log> for Envelope { Ok(Self { gateway: log.address, nonce: event.nonce, - message_id: H256::from(event.message_id.as_ref()), - reward_address: event.reward_address.clone().into(), fee: event.fee, - para_id: event.para_id, + reward_address: event.reward_address.clone().into(), payload: event.payload, }) } diff --git a/bridges/snowbridge/pallets/inbound-queue-v2/src/lib.rs b/bridges/snowbridge/pallets/inbound-queue-v2/src/lib.rs index b60bb60cb845..147dfff9939c 100644 --- a/bridges/snowbridge/pallets/inbound-queue-v2/src/lib.rs +++ b/bridges/snowbridge/pallets/inbound-queue-v2/src/lib.rs @@ -39,10 +39,7 @@ mod test; use codec::{Decode, DecodeAll, Encode}; use envelope::Envelope; use frame_support::{ - traits::{ - fungible::{Inspect, Mutate}, - tokens::{Fortitude, Precision, Preservation}, - }, + traits::fungible::{Inspect, Mutate}, weights::WeightToFee, PalletError, }; @@ -50,17 +47,16 @@ use frame_system::ensure_signed; use scale_info::TypeInfo; use sp_core::H160; use sp_std::vec; -use xcm::prelude::{ - send_xcm, Junction::*, Location, SendError as XcmpSendError, SendXcm, Xcm, XcmHash, +use xcm::{ + prelude::{send_xcm, Junction::*, Location, SendError as XcmpSendError, SendXcm, Xcm, XcmHash}, + VersionedXcm, MAX_XCM_DECODE_DEPTH, }; use snowbridge_core::{ inbound::{Message, VerificationError, Verifier}, sibling_sovereign_account, BasicOperatingMode, ParaId, }; -use snowbridge_router_primitives_v2::inbound::{ - ConvertMessage, ConvertMessageError, VersionedMessage, -}; +use snowbridge_router_primitives_v2::inbound::Message as MessageV2; pub use weights::WeightInfo; @@ -77,6 +73,7 @@ pub const LOG_TARGET: &str = "snowbridge-inbound-queue:v2"; #[frame_support::pallet] pub mod pallet { use super::*; + use codec::DecodeLimit; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; @@ -107,12 +104,6 @@ pub mod pallet { #[pallet::constant] type GatewayAddress: Get; - /// Convert inbound message to XCM - type MessageConverter: ConvertMessage< - AccountId = Self::AccountId, - Balance = BalanceOf, - >; - type WeightInfo: WeightInfo; #[cfg(feature = "runtime-benchmarks")] @@ -140,8 +131,6 @@ pub mod pallet { nonce: u64, /// ID of the XCM message which was forwarded to the final destination parachain message_id: [u8; 32], - /// Fee burned for the teleport - fee_burned: BalanceOf, }, /// Set OperatingMode OperatingModeChanged { mode: BasicOperatingMode }, @@ -169,8 +158,6 @@ pub mod pallet { Verification(VerificationError), /// XCMP send failure Send(SendError), - /// Message conversion error - ConvertMessage(ConvertMessageError), } #[derive(Clone, Encode, Decode, Eq, PartialEq, Debug, TypeInfo, PalletError)] @@ -215,7 +202,7 @@ pub mod pallet { #[pallet::call_index(0)] #[pallet::weight(T::WeightInfo::submit())] pub fn submit(origin: OriginFor, message: Message) -> DispatchResult { - let who = ensure_signed(origin)?; + ensure_signed(origin)?; ensure!(!Self::operating_mode().is_halted(), Error::::Halted); // submit message to verifier for verification @@ -233,30 +220,25 @@ pub mod pallet { ensure!(!>::contains_key(envelope.nonce), Error::::InvalidNonce); // Decode payload into `VersionedMessage` - let message = VersionedMessage::decode_all(&mut envelope.payload.as_ref()) + let message = MessageV2::decode_all(&mut envelope.payload.as_ref()) .map_err(|_| Error::::InvalidPayload)?; - // Decode message into XCM - let (xcm, fee) = Self::do_convert(envelope.message_id, message.clone())?; + let versioned_xcm = VersionedXcm::<()>::decode_with_depth_limit( + MAX_XCM_DECODE_DEPTH, + &mut message.xcm.as_ref(), + ) + .map_err(|_| Error::::InvalidPayload)?; + + let xcm: Xcm<()> = versioned_xcm.try_into().unwrap(); log::info!( target: LOG_TARGET, - "💫 xcm decoded as {:?} with fee {:?}", + "💫 xcm decoded as {:?}", xcm, - fee ); - // Burning fees for teleport - T::Token::burn_from( - &who, - fee, - Preservation::Preserve, - Precision::BestEffort, - Fortitude::Polite, - )?; - // Attempt to send XCM to a dest parachain - let message_id = Self::send_xcm(xcm, envelope.para_id.into())?; + let message_id = Self::send_xcm(xcm, 1000.into())?; // Set nonce flag to true >::try_mutate(envelope.nonce, |done| -> DispatchResult { @@ -273,11 +255,7 @@ pub mod pallet { // T::RewardLeger::deposit(envelope.reward_address.into(), envelope.fee.into())?; - Self::deposit_event(Event::MessageReceived { - nonce: envelope.nonce, - message_id, - fee_burned: fee, - }); + Self::deposit_event(Event::MessageReceived { nonce: envelope.nonce, message_id }); Ok(()) } @@ -297,15 +275,6 @@ pub mod pallet { } impl Pallet { - pub fn do_convert( - message_id: H256, - message: VersionedMessage, - ) -> Result<(Xcm<()>, BalanceOf), Error> { - let (xcm, fee) = T::MessageConverter::convert(message_id, message) - .map_err(|e| Error::::ConvertMessage(e))?; - Ok((xcm, fee)) - } - pub fn send_xcm(xcm: Xcm<()>, dest: ParaId) -> Result> { let dest = Location::new(1, [Parachain(dest.into())]); let (xcm_hash, _) = send_xcm::(dest, xcm).map_err(Error::::from)?; diff --git a/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs b/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs index 6bf805d9c91f..99f03b5020c7 100644 --- a/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs +++ b/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs @@ -11,7 +11,6 @@ use snowbridge_core::{ inbound::{Log, Proof, VerificationError}, TokenId, }; -use snowbridge_router_primitives_v2::inbound::MessageToXcm; use sp_core::{H160, H256}; use sp_runtime::{ traits::{IdentifyAccount, IdentityLookup, MaybeEquivalence, Verify}, @@ -170,16 +169,6 @@ impl inbound_queue::Config for Test { type XcmSender = MockXcmSender; type WeightInfo = (); type GatewayAddress = GatewayAddress; - type MessageConverter = MessageToXcm< - CreateAssetCall, - CreateAssetDeposit, - InboundQueuePalletInstance, - AccountId, - Balance, - MockTokenIdConvert, - UniversalLocation, - AssetHubFromEthereum, - >; #[cfg(feature = "runtime-benchmarks")] type Helper = Test; type WeightToFee = IdentityFee; diff --git a/bridges/snowbridge/pallets/inbound-queue-v2/src/test.rs b/bridges/snowbridge/pallets/inbound-queue-v2/src/test.rs index 776dc3807898..250f2ed61041 100644 --- a/bridges/snowbridge/pallets/inbound-queue-v2/src/test.rs +++ b/bridges/snowbridge/pallets/inbound-queue-v2/src/test.rs @@ -41,17 +41,8 @@ fn test_submit_happy_path() { 255, 125, 48, 71, 174, 185, 100, 26, 159, 43, 108, 6, 116, 218, 55, 155, 223, 143, 141, 22, 124, 110, 241, 18, 122, 217, 130, 29, 139, 76, 97, 201, ], - fee_burned: 110000000000, } .into()]); - - let delivery_cost = InboundQueue::calculate_delivery_cost(message.encode().len() as u32); - - assert_eq!(Balances::balance(&relayer), delivery_cost, "relayer was rewarded"); - assert!( - Balances::balance(&channel_sovereign) <= initial_fund - delivery_cost, - "sovereign account paid reward" - ); }); } diff --git a/bridges/snowbridge/primitives/router-v2/src/inbound/mod.rs b/bridges/snowbridge/primitives/router-v2/src/inbound/mod.rs index 998008ab4871..5696f099d6b1 100644 --- a/bridges/snowbridge/primitives/router-v2/src/inbound/mod.rs +++ b/bridges/snowbridge/primitives/router-v2/src/inbound/mod.rs @@ -24,432 +24,17 @@ const MINIMUM_DEPOSIT: u128 = 1; /// Instead having BridgeHub transcode the messages into XCM. #[derive(Clone, Encode, Decode, RuntimeDebug)] pub enum VersionedMessage { - V2(MessageV2), + V2(Message), } /// For V2, the ethereum side sends messages which are transcoded into XCM. These messages are /// self-contained, in that they can be transcoded using only information in the message. #[derive(Clone, Encode, Decode, RuntimeDebug)] -pub struct MessageV2 { - /// EIP-155 chain id of the origin Ethereum network - pub chain_id: u64, +pub struct Message { + /// The origin address + pub origin: H160, /// The command originating from the Gateway contract - pub command: Command, -} - -#[derive(Clone, Encode, Decode, RuntimeDebug)] -pub enum Command { - /// Register a wrapped token on the AssetHub `ForeignAssets` pallet - RegisterToken { - /// The address of the ERC20 token to be bridged over to AssetHub - token: H160, - /// XCM execution fee on AssetHub - fee: u128, - }, - /// Send Ethereum token to AssetHub or another parachain - SendToken { - /// The address of the ERC20 token to be bridged over to AssetHub - token: H160, - /// The destination for the transfer - destination: Destination, - /// Amount to transfer - amount: u128, - /// XCM execution fee on AssetHub - fee: u128, - }, - /// Send Polkadot token back to the original parachain - SendNativeToken { - /// The Id of the token - token_id: TokenId, - /// The destination for the transfer - destination: Destination, - /// Amount to transfer - amount: u128, - /// XCM execution fee on AssetHub - fee: u128, - }, -} - -/// Destination for bridged tokens -#[derive(Clone, Encode, Decode, RuntimeDebug)] -pub enum Destination { - /// The funds will be deposited into account `id` on AssetHub - AccountId32 { id: [u8; 32] }, - /// The funds will deposited into the sovereign account of destination parachain `para_id` on - /// AssetHub, Account `id` on the destination parachain will receive the funds via a - /// reserve-backed transfer. See - ForeignAccountId32 { - para_id: u32, - id: [u8; 32], - /// XCM execution fee on final destination - fee: u128, - }, - /// The funds will deposited into the sovereign account of destination parachain `para_id` on - /// AssetHub, Account `id` on the destination parachain will receive the funds via a - /// reserve-backed transfer. See - ForeignAccountId20 { - para_id: u32, - id: [u8; 20], - /// XCM execution fee on final destination - fee: u128, - }, -} - -pub struct MessageToXcm< - CreateAssetCall, - CreateAssetDeposit, - InboundQueuePalletInstance, - AccountId, - Balance, - ConvertAssetId, - EthereumUniversalLocation, - GlobalAssetHubLocation, -> where - CreateAssetCall: Get, - CreateAssetDeposit: Get, - Balance: BalanceT, - ConvertAssetId: MaybeEquivalence, - EthereumUniversalLocation: Get, - GlobalAssetHubLocation: Get, -{ - _phantom: PhantomData<( - CreateAssetCall, - CreateAssetDeposit, - InboundQueuePalletInstance, - AccountId, - Balance, - ConvertAssetId, - EthereumUniversalLocation, - GlobalAssetHubLocation, - )>, -} - -/// Reason why a message conversion failed. -#[derive(Copy, Clone, TypeInfo, PalletError, Encode, Decode, RuntimeDebug)] -pub enum ConvertMessageError { - /// The message version is not supported for conversion. - UnsupportedVersion, - InvalidDestination, - InvalidToken, - /// The fee asset is not supported for conversion. - UnsupportedFeeAsset, - CannotReanchor, -} - -/// convert the inbound message to xcm which will be forwarded to the destination chain -pub trait ConvertMessage { - type Balance: BalanceT + From; - type AccountId; - /// Converts a versioned message into an XCM message and an optional topicID - fn convert( - message_id: H256, - message: VersionedMessage, - ) -> Result<(Xcm<()>, Self::Balance), ConvertMessageError>; -} - -pub type CallIndex = [u8; 2]; - -impl< - CreateAssetCall, - CreateAssetDeposit, - InboundQueuePalletInstance, - AccountId, - Balance, - ConvertAssetId, - EthereumUniversalLocation, - GlobalAssetHubLocation, - > ConvertMessage - for MessageToXcm< - CreateAssetCall, - CreateAssetDeposit, - InboundQueuePalletInstance, - AccountId, - Balance, - ConvertAssetId, - EthereumUniversalLocation, - GlobalAssetHubLocation, - > -where - CreateAssetCall: Get, - CreateAssetDeposit: Get, - InboundQueuePalletInstance: Get, - Balance: BalanceT + From, - AccountId: Into<[u8; 32]>, - ConvertAssetId: MaybeEquivalence, - EthereumUniversalLocation: Get, - GlobalAssetHubLocation: Get, -{ - type Balance = Balance; - type AccountId = AccountId; - - fn convert( - message_id: H256, - message: VersionedMessage, - ) -> Result<(Xcm<()>, Self::Balance), ConvertMessageError> { - use Command::*; - use VersionedMessage::*; - match message { - V2(MessageV2 { chain_id, command: RegisterToken { token, fee } }) => - Ok(Self::convert_register_token(message_id, chain_id, token, fee)), - V2(MessageV2 { chain_id, command: SendToken { token, destination, amount, fee } }) => - Ok(Self::convert_send_token(message_id, chain_id, token, destination, amount, fee)), - V2(MessageV2 { - chain_id, - command: SendNativeToken { token_id, destination, amount, fee }, - }) => Self::convert_send_native_token( - message_id, - chain_id, - token_id, - destination, - amount, - fee, - ), - } - } -} - -impl< - CreateAssetCall, - CreateAssetDeposit, - InboundQueuePalletInstance, - AccountId, - Balance, - ConvertAssetId, - EthereumUniversalLocation, - GlobalAssetHubLocation, - > - MessageToXcm< - CreateAssetCall, - CreateAssetDeposit, - InboundQueuePalletInstance, - AccountId, - Balance, - ConvertAssetId, - EthereumUniversalLocation, - GlobalAssetHubLocation, - > -where - CreateAssetCall: Get, - CreateAssetDeposit: Get, - InboundQueuePalletInstance: Get, - Balance: BalanceT + From, - AccountId: Into<[u8; 32]>, - ConvertAssetId: MaybeEquivalence, - EthereumUniversalLocation: Get, - GlobalAssetHubLocation: Get, -{ - fn convert_register_token( - message_id: H256, - chain_id: u64, - token: H160, - fee: u128, - ) -> (Xcm<()>, Balance) { - let network = Ethereum { chain_id }; - let xcm_fee: Asset = (Location::parent(), fee).into(); - let deposit: Asset = (Location::parent(), CreateAssetDeposit::get()).into(); - - let total_amount = fee + CreateAssetDeposit::get(); - let total: Asset = (Location::parent(), total_amount).into(); - - let bridge_location = Location::new(2, GlobalConsensus(network)); - - let owner = GlobalConsensusEthereumConvertsFor::<[u8; 32]>::from_chain_id(&chain_id); - let asset_id = Self::convert_token_address(network, token); - let create_call_index: [u8; 2] = CreateAssetCall::get(); - let inbound_queue_pallet_index = InboundQueuePalletInstance::get(); - - let xcm: Xcm<()> = vec![ - // Teleport required fees. - ReceiveTeleportedAsset(total.into()), - // Pay for execution. - BuyExecution { fees: xcm_fee, weight_limit: Unlimited }, - // Fund the snowbridge sovereign with the required deposit for creation. - DepositAsset { assets: Definite(deposit.into()), beneficiary: bridge_location.clone() }, - // This `SetAppendix` ensures that `xcm_fee` not spent by `Transact` will be - // deposited to snowbridge sovereign, instead of being trapped, regardless of - // `Transact` success or not. - SetAppendix(Xcm(vec![ - RefundSurplus, - DepositAsset { assets: AllCounted(1).into(), beneficiary: bridge_location }, - ])), - // Only our inbound-queue pallet is allowed to invoke `UniversalOrigin`. - DescendOrigin(PalletInstance(inbound_queue_pallet_index).into()), - // Change origin to the bridge. - UniversalOrigin(GlobalConsensus(network)), - // Call create_asset on foreign assets pallet. - Transact { - origin_kind: OriginKind::Xcm, - require_weight_at_most: Weight::from_parts(400_000_000, 8_000), - call: ( - create_call_index, - asset_id, - MultiAddress::<[u8; 32], ()>::Id(owner), - MINIMUM_DEPOSIT, - ) - .encode() - .into(), - }, - // Forward message id to Asset Hub - SetTopic(message_id.into()), - // Once the program ends here, appendix program will run, which will deposit any - // leftover fee to snowbridge sovereign. - ] - .into(); - - (xcm, total_amount.into()) - } - - fn convert_send_token( - message_id: H256, - chain_id: u64, - token: H160, - destination: Destination, - amount: u128, - asset_hub_fee: u128, - ) -> (Xcm<()>, Balance) { - let network = Ethereum { chain_id }; - let asset_hub_fee_asset: Asset = (Location::parent(), asset_hub_fee).into(); - let asset: Asset = (Self::convert_token_address(network, token), amount).into(); - - let (dest_para_id, beneficiary, dest_para_fee) = match destination { - // Final destination is a 32-byte account on AssetHub - Destination::AccountId32 { id } => - (None, Location::new(0, [AccountId32 { network: None, id }]), 0), - // Final destination is a 32-byte account on a sibling of AssetHub - Destination::ForeignAccountId32 { para_id, id, fee } => ( - Some(para_id), - Location::new(0, [AccountId32 { network: None, id }]), - // Total fee needs to cover execution on AssetHub and Sibling - fee, - ), - // Final destination is a 20-byte account on a sibling of AssetHub - Destination::ForeignAccountId20 { para_id, id, fee } => ( - Some(para_id), - Location::new(0, [AccountKey20 { network: None, key: id }]), - // Total fee needs to cover execution on AssetHub and Sibling - fee, - ), - }; - - let total_fees = asset_hub_fee.saturating_add(dest_para_fee); - let total_fee_asset: Asset = (Location::parent(), total_fees).into(); - let inbound_queue_pallet_index = InboundQueuePalletInstance::get(); - - let mut instructions = vec![ - ReceiveTeleportedAsset(total_fee_asset.into()), - BuyExecution { fees: asset_hub_fee_asset, weight_limit: Unlimited }, - DescendOrigin(PalletInstance(inbound_queue_pallet_index).into()), - UniversalOrigin(GlobalConsensus(network)), - ReserveAssetDeposited(asset.clone().into()), - ClearOrigin, - ]; - - match dest_para_id { - Some(dest_para_id) => { - let dest_para_fee_asset: Asset = (Location::parent(), dest_para_fee).into(); - let bridge_location = Location::new(2, GlobalConsensus(network)); - - instructions.extend(vec![ - // After program finishes deposit any leftover assets to the snowbridge - // sovereign. - SetAppendix(Xcm(vec![DepositAsset { - assets: Wild(AllCounted(2)), - beneficiary: bridge_location, - }])), - // Perform a deposit reserve to send to destination chain. - DepositReserveAsset { - assets: Definite(vec![dest_para_fee_asset.clone(), asset].into()), - dest: Location::new(1, [Parachain(dest_para_id)]), - xcm: vec![ - // Buy execution on target. - BuyExecution { fees: dest_para_fee_asset, weight_limit: Unlimited }, - // Deposit assets to beneficiary. - DepositAsset { assets: Wild(AllCounted(2)), beneficiary }, - // Forward message id to destination parachain. - SetTopic(message_id.into()), - ] - .into(), - }, - ]); - }, - None => { - instructions.extend(vec![ - // Deposit both asset and fees to beneficiary so the fees will not get - // trapped. Another benefit is when fees left more than ED on AssetHub could be - // used to create the beneficiary account in case it does not exist. - DepositAsset { assets: Wild(AllCounted(2)), beneficiary }, - ]); - }, - } - - // Forward message id to Asset Hub. - instructions.push(SetTopic(message_id.into())); - - // The `instructions` to forward to AssetHub, and the `total_fees` to locally burn (since - // they are teleported within `instructions`). - (instructions.into(), total_fees.into()) - } - - // Convert ERC20 token address to a location that can be understood by Assets Hub. - fn convert_token_address(network: NetworkId, token: H160) -> Location { - Location::new( - 2, - [GlobalConsensus(network), AccountKey20 { network: None, key: token.into() }], - ) - } - - /// Constructs an XCM message destined for AssetHub that withdraws assets from the sovereign - /// account of the Gateway contract and either deposits those assets into a recipient account or - /// forwards the assets to another parachain. - fn convert_send_native_token( - message_id: H256, - chain_id: u64, - token_id: TokenId, - destination: Destination, - amount: u128, - asset_hub_fee: u128, - ) -> Result<(Xcm<()>, Balance), ConvertMessageError> { - let network = Ethereum { chain_id }; - let asset_hub_fee_asset: Asset = (Location::parent(), asset_hub_fee).into(); - - let beneficiary = match destination { - // Final destination is a 32-byte account on AssetHub - Destination::AccountId32 { id } => - Ok(Location::new(0, [AccountId32 { network: None, id }])), - _ => Err(ConvertMessageError::InvalidDestination), - }?; - - let total_fee_asset: Asset = (Location::parent(), asset_hub_fee).into(); - - let asset_loc = - ConvertAssetId::convert(&token_id).ok_or(ConvertMessageError::InvalidToken)?; - - let mut reanchored_asset_loc = asset_loc.clone(); - reanchored_asset_loc - .reanchor(&GlobalAssetHubLocation::get(), &EthereumUniversalLocation::get()) - .map_err(|_| ConvertMessageError::CannotReanchor)?; - - let asset: Asset = (reanchored_asset_loc, amount).into(); - - let inbound_queue_pallet_index = InboundQueuePalletInstance::get(); - - let instructions = vec![ - ReceiveTeleportedAsset(total_fee_asset.clone().into()), - BuyExecution { fees: asset_hub_fee_asset, weight_limit: Unlimited }, - DescendOrigin(PalletInstance(inbound_queue_pallet_index).into()), - UniversalOrigin(GlobalConsensus(network)), - WithdrawAsset(asset.clone().into()), - // Deposit both asset and fees to beneficiary so the fees will not get - // trapped. Another benefit is when fees left more than ED on AssetHub could be - // used to create the beneficiary account in case it does not exist. - DepositAsset { assets: Wild(AllCounted(2)), beneficiary }, - SetTopic(message_id.into()), - ]; - - // `total_fees` to burn on this chain when sending `instructions` to run on AH (which also - // teleport fees) - Ok((instructions.into(), asset_hub_fee.into())) - } + pub xcm: Vec, } pub struct GlobalConsensusEthereumConvertsFor(PhantomData); diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs index 515a15e52ce2..5a5999e63d15 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs @@ -26,10 +26,7 @@ use parachains_common::{AccountId, Balance}; use snowbridge_beacon_primitives::{Fork, ForkVersions}; use snowbridge_core::{gwei, meth, AllowSiblingsOnly, PricingParameters, Rewards}; use snowbridge_router_primitives::{inbound::MessageToXcm, outbound::EthereumBlobExporter}; -use snowbridge_router_primitives_v2::{ - inbound::MessageToXcm as MessageToXcmV2, - outbound::EthereumBlobExporter as EthereumBlobExporterV2, -}; +use snowbridge_router_primitives_v2::outbound::EthereumBlobExporter as EthereumBlobExporterV2; use sp_core::H160; use testnet_parachains_constants::westend::{ currency::*, @@ -125,16 +122,6 @@ impl snowbridge_pallet_inbound_queue_v2::Config for Runtime { type GatewayAddress = EthereumGatewayAddress; #[cfg(feature = "runtime-benchmarks")] type Helper = Runtime; - type MessageConverter = MessageToXcmV2< - CreateAssetCall, - CreateAssetDeposit, - ConstU8, - AccountId, - Balance, - EthereumSystem, - EthereumUniversalLocation, - AssetHubFromEthereum, - >; type WeightToFee = WeightToFee; type LengthToFee = ConstantMultiplier; type MaxMessageSize = ConstU32<2048>; From 2e827e8fe289e455fe70504dbb7ae5a4e2472b22 Mon Sep 17 00:00:00 2001 From: ron Date: Sun, 20 Oct 2024 23:35:25 +0800 Subject: [PATCH 03/37] More cleanup --- .../inbound-queue-v2/src/benchmarking/mod.rs | 15 ------ .../pallets/inbound-queue-v2/src/envelope.rs | 5 +- .../pallets/inbound-queue-v2/src/lib.rs | 53 +++++-------------- .../pallets/inbound-queue-v2/src/mock.rs | 18 ------- .../pallets/inbound-queue-v2/src/test.rs | 33 ------------ .../src/bridge_to_ethereum_config.rs | 4 -- 6 files changed, 14 insertions(+), 114 deletions(-) diff --git a/bridges/snowbridge/pallets/inbound-queue-v2/src/benchmarking/mod.rs b/bridges/snowbridge/pallets/inbound-queue-v2/src/benchmarking/mod.rs index 52461a8a7fbe..4c5df07b27ac 100644 --- a/bridges/snowbridge/pallets/inbound-queue-v2/src/benchmarking/mod.rs +++ b/bridges/snowbridge/pallets/inbound-queue-v2/src/benchmarking/mod.rs @@ -23,21 +23,6 @@ mod benchmarks { create_message.block_roots_root, ); - let sovereign_account = sibling_sovereign_account::(1000u32.into()); - - let minimum_balance = T::Token::minimum_balance(); - - // So that the receiving account exists - assert_ok!(T::Token::mint_into(&caller, minimum_balance)); - // Fund the sovereign account (parachain sovereign account) so it can transfer a reward - // fee to the caller account - assert_ok!(T::Token::mint_into( - &sovereign_account, - 3_000_000_000_000u128 - .try_into() - .unwrap_or_else(|_| panic!("unable to cast sovereign account balance")), - )); - #[block] { assert_ok!(InboundQueue::::submit( diff --git a/bridges/snowbridge/pallets/inbound-queue-v2/src/envelope.rs b/bridges/snowbridge/pallets/inbound-queue-v2/src/envelope.rs index a1dda45f482f..77ec6c55c48e 100644 --- a/bridges/snowbridge/pallets/inbound-queue-v2/src/envelope.rs +++ b/bridges/snowbridge/pallets/inbound-queue-v2/src/envelope.rs @@ -9,7 +9,7 @@ use alloy_primitives::B256; use alloy_sol_types::{sol, SolEvent}; sol! { - event OutboundMessageAccepted(uint64 indexed nonce, uint128 fee, bytes32 reward_address, bytes payload); + event OutboundMessageAccepted(uint64 indexed nonce, uint128 fee, bytes payload); } /// An inbound message that has had its outer envelope decoded. @@ -21,8 +21,6 @@ pub struct Envelope { pub nonce: u64, /// Total fee paid on source chain pub fee: u128, - /// The reward address - pub reward_address: [u8; 32], /// The inner payload generated from the source application. pub payload: Vec, } @@ -43,7 +41,6 @@ impl TryFrom<&Log> for Envelope { gateway: log.address, nonce: event.nonce, fee: event.fee, - reward_address: event.reward_address.clone().into(), payload: event.payload, }) } diff --git a/bridges/snowbridge/pallets/inbound-queue-v2/src/lib.rs b/bridges/snowbridge/pallets/inbound-queue-v2/src/lib.rs index 147dfff9939c..4f5552b89cf1 100644 --- a/bridges/snowbridge/pallets/inbound-queue-v2/src/lib.rs +++ b/bridges/snowbridge/pallets/inbound-queue-v2/src/lib.rs @@ -38,23 +38,19 @@ mod test; use codec::{Decode, DecodeAll, Encode}; use envelope::Envelope; -use frame_support::{ - traits::fungible::{Inspect, Mutate}, - weights::WeightToFee, - PalletError, -}; +use frame_support::PalletError; use frame_system::ensure_signed; use scale_info::TypeInfo; use sp_core::H160; use sp_std::vec; use xcm::{ - prelude::{send_xcm, Junction::*, Location, SendError as XcmpSendError, SendXcm, Xcm, XcmHash}, + prelude::{send_xcm, Junction::*, Location, SendError as XcmpSendError, SendXcm, Xcm}, VersionedXcm, MAX_XCM_DECODE_DEPTH, }; use snowbridge_core::{ inbound::{Message, VerificationError, Verifier}, - sibling_sovereign_account, BasicOperatingMode, ParaId, + BasicOperatingMode, }; use snowbridge_router_primitives_v2::inbound::Message as MessageV2; @@ -63,9 +59,6 @@ pub use weights::WeightInfo; #[cfg(feature = "runtime-benchmarks")] use snowbridge_beacon_primitives::BeaconHeader; -type BalanceOf = - <::Token as Inspect<::AccountId>>::Balance; - pub use pallet::*; pub const LOG_TARGET: &str = "snowbridge-inbound-queue:v2"; @@ -94,13 +87,10 @@ pub mod pallet { /// The verifier for inbound messages from Ethereum type Verifier: Verifier; - /// Burn fees from relayer - type Token: Mutate + Inspect; - /// XCM message sender type XcmSender: SendXcm; - // Address of the Gateway contract + /// Address of the Gateway contract #[pallet::constant] type GatewayAddress: Get; @@ -108,15 +98,6 @@ pub mod pallet { #[cfg(feature = "runtime-benchmarks")] type Helper: BenchmarkHelper; - - /// Convert a weight value into deductible balance type. - type WeightToFee: WeightToFee>; - - /// Convert a length value into deductible balance type - type LengthToFee: WeightToFee>; - - /// The upper limit here only used to estimate delivery cost - type MaxMessageSize: Get; } #[pallet::hooks] @@ -202,7 +183,7 @@ pub mod pallet { #[pallet::call_index(0)] #[pallet::weight(T::WeightInfo::submit())] pub fn submit(origin: OriginFor, message: Message) -> DispatchResult { - ensure_signed(origin)?; + let _who = ensure_signed(origin)?; ensure!(!Self::operating_mode().is_halted(), Error::::Halted); // submit message to verifier for verification @@ -219,17 +200,17 @@ pub mod pallet { // Verify the message has not been processed ensure!(!>::contains_key(envelope.nonce), Error::::InvalidNonce); - // Decode payload into `VersionedMessage` + // Decode payload into `MessageV2` let message = MessageV2::decode_all(&mut envelope.payload.as_ref()) .map_err(|_| Error::::InvalidPayload)?; + // Decode xcm let versioned_xcm = VersionedXcm::<()>::decode_with_depth_limit( MAX_XCM_DECODE_DEPTH, &mut message.xcm.as_ref(), ) .map_err(|_| Error::::InvalidPayload)?; - - let xcm: Xcm<()> = versioned_xcm.try_into().unwrap(); + let xcm: Xcm<()> = versioned_xcm.try_into().map_err(|_| >::InvalidPayload)?; log::info!( target: LOG_TARGET, @@ -237,23 +218,23 @@ pub mod pallet { xcm, ); - // Attempt to send XCM to a dest parachain - let message_id = Self::send_xcm(xcm, 1000.into())?; - // Set nonce flag to true >::try_mutate(envelope.nonce, |done| -> DispatchResult { *done = true; Ok(()) })?; - // Todo: Deposit fee to RewardLeger which should contains all of: + // Todo: Deposit fee(in Ether) to RewardLeger which should cover all of: + // T::RewardLeger::deposit(who, envelope.fee.into())?; // a. The submit extrinsic cost on BH // b. The delivery cost to AH // c. The execution cost on AH // d. The execution cost on destination chain(if any) // e. The reward - // T::RewardLeger::deposit(envelope.reward_address.into(), envelope.fee.into())?; + // Attempt to forward XCM to AH + let dest = Location::new(1, [Parachain(1000)]); + let (message_id, _) = send_xcm::(dest, xcm).map_err(Error::::from)?; Self::deposit_event(Event::MessageReceived { nonce: envelope.nonce, message_id }); @@ -273,12 +254,4 @@ pub mod pallet { Ok(()) } } - - impl Pallet { - pub fn send_xcm(xcm: Xcm<()>, dest: ParaId) -> Result> { - let dest = Location::new(1, [Parachain(dest.into())]); - let (xcm_hash, _) = send_xcm::(dest, xcm).map_err(Error::::from)?; - Ok(xcm_hash) - } - } } diff --git a/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs b/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs index 99f03b5020c7..2d97f952757b 100644 --- a/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs +++ b/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs @@ -146,10 +146,6 @@ impl SendXcm for MockXcmSender { } } -parameter_types! { - pub const OwnParaId: ParaId = ParaId::new(1013); -} - pub const DOT: u128 = 10_000_000_000; pub struct MockTokenIdConvert; @@ -165,15 +161,11 @@ impl MaybeEquivalence for MockTokenIdConvert { impl inbound_queue::Config for Test { type RuntimeEvent = RuntimeEvent; type Verifier = MockVerifier; - type Token = Balances; type XcmSender = MockXcmSender; type WeightInfo = (); type GatewayAddress = GatewayAddress; #[cfg(feature = "runtime-benchmarks")] type Helper = Test; - type WeightToFee = IdentityFee; - type LengthToFee = IdentityFee; - type MaxMessageSize = ConstU32<1024>; } pub fn last_events(n: usize) -> Vec { @@ -192,16 +184,6 @@ pub fn expect_events(e: Vec) { pub fn setup() { System::set_block_number(1); - Balances::mint_into( - &sibling_sovereign_account::(ASSET_HUB_PARAID.into()), - InitialFund::get(), - ) - .unwrap(); - Balances::mint_into( - &sibling_sovereign_account::(TEMPLATE_PARAID.into()), - InitialFund::get(), - ) - .unwrap(); } pub fn new_tester() -> sp_io::TestExternalities { diff --git a/bridges/snowbridge/pallets/inbound-queue-v2/src/test.rs b/bridges/snowbridge/pallets/inbound-queue-v2/src/test.rs index 250f2ed61041..989787af167d 100644 --- a/bridges/snowbridge/pallets/inbound-queue-v2/src/test.rs +++ b/bridges/snowbridge/pallets/inbound-queue-v2/src/test.rs @@ -17,7 +17,6 @@ use crate::mock::*; fn test_submit_happy_path() { new_tester().execute_with(|| { let relayer: AccountId = Keyring::Bob.into(); - let channel_sovereign = sibling_sovereign_account::(ASSET_HUB_PARAID.into()); let origin = RuntimeOrigin::signed(relayer.clone()); @@ -30,10 +29,6 @@ fn test_submit_happy_path() { }, }; - let initial_fund = InitialFund::get(); - assert_eq!(Balances::balance(&relayer), 0); - assert_eq!(Balances::balance(&channel_sovereign), initial_fund); - assert_ok!(InboundQueue::submit(origin.clone(), message.clone())); expect_events(vec![InboundQueueEvent::MessageReceived { nonce: 1, @@ -52,11 +47,6 @@ fn test_submit_xcm_invalid_channel() { let relayer: AccountId = Keyring::Bob.into(); let origin = RuntimeOrigin::signed(relayer); - // Deposit funds into sovereign account of parachain 1001 - let sovereign_account = sibling_sovereign_account::(TEMPLATE_PARAID.into()); - println!("account: {}", sovereign_account); - let _ = Balances::mint_into(&sovereign_account, 10000); - // Submit message let message = Message { event_log: mock_event_log_invalid_channel(), @@ -78,10 +68,6 @@ fn test_submit_with_invalid_gateway() { let relayer: AccountId = Keyring::Bob.into(); let origin = RuntimeOrigin::signed(relayer); - // Deposit funds into sovereign account of Asset Hub (Statemint) - let sovereign_account = sibling_sovereign_account::(ASSET_HUB_PARAID.into()); - let _ = Balances::mint_into(&sovereign_account, 10000); - // Submit message let message = Message { event_log: mock_event_log_invalid_gateway(), @@ -103,10 +89,6 @@ fn test_submit_with_invalid_nonce() { let relayer: AccountId = Keyring::Bob.into(); let origin = RuntimeOrigin::signed(relayer); - // Deposit funds into sovereign account of Asset Hub (Statemint) - let sovereign_account = sibling_sovereign_account::(ASSET_HUB_PARAID.into()); - let _ = Balances::mint_into(&sovereign_account, 10000); - // Submit message let message = Message { event_log: mock_event_log(), @@ -131,10 +113,6 @@ fn test_submit_no_funds_to_reward_relayers_just_ignore() { let relayer: AccountId = Keyring::Bob.into(); let origin = RuntimeOrigin::signed(relayer); - // Reset balance of sovereign_account to zero first - let sovereign_account = sibling_sovereign_account::(ASSET_HUB_PARAID.into()); - Balances::set_balance(&sovereign_account, 0); - // Submit message let message = Message { event_log: mock_event_log(), @@ -189,10 +167,6 @@ fn test_submit_no_funds_to_reward_relayers_and_ed_preserved() { let relayer: AccountId = Keyring::Bob.into(); let origin = RuntimeOrigin::signed(relayer); - // Reset balance of sovereign account to (ED+1) first - let sovereign_account = sibling_sovereign_account::(ASSET_HUB_PARAID.into()); - Balances::set_balance(&sovereign_account, ExistentialDeposit::get() + 1); - // Submit message successfully let message = Message { event_log: mock_event_log(), @@ -203,10 +177,6 @@ fn test_submit_no_funds_to_reward_relayers_and_ed_preserved() { }; assert_ok!(InboundQueue::submit(origin.clone(), message.clone())); - // Check balance of sovereign account to ED - let amount = Balances::balance(&sovereign_account); - assert_eq!(amount, ExistentialDeposit::get()); - // Submit another message with nonce set as 2 let mut event_log = mock_event_log(); event_log.data[31] = 2; @@ -218,8 +188,5 @@ fn test_submit_no_funds_to_reward_relayers_and_ed_preserved() { }, }; assert_ok!(InboundQueue::submit(origin.clone(), message.clone())); - // Check balance of sovereign account as ED does not change - let amount = Balances::balance(&sovereign_account); - assert_eq!(amount, ExistentialDeposit::get()); }); } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs index 5a5999e63d15..dfbb6752e184 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs @@ -114,7 +114,6 @@ impl snowbridge_pallet_inbound_queue::Config for Runtime { impl snowbridge_pallet_inbound_queue_v2::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Verifier = snowbridge_pallet_ethereum_client::Pallet; - type Token = Balances; #[cfg(not(feature = "runtime-benchmarks"))] type XcmSender = XcmRouter; #[cfg(feature = "runtime-benchmarks")] @@ -122,9 +121,6 @@ impl snowbridge_pallet_inbound_queue_v2::Config for Runtime { type GatewayAddress = EthereumGatewayAddress; #[cfg(feature = "runtime-benchmarks")] type Helper = Runtime; - type WeightToFee = WeightToFee; - type LengthToFee = ConstantMultiplier; - type MaxMessageSize = ConstU32<2048>; type WeightInfo = crate::weights::snowbridge_pallet_inbound_queue_v2::WeightInfo; } From ca8e22653c0c17f3e622adc235c25f30473dde5f Mon Sep 17 00:00:00 2001 From: ron Date: Sun, 20 Oct 2024 23:57:09 +0800 Subject: [PATCH 04/37] Comments --- bridges/snowbridge/pallets/inbound-queue-v2/src/envelope.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bridges/snowbridge/pallets/inbound-queue-v2/src/envelope.rs b/bridges/snowbridge/pallets/inbound-queue-v2/src/envelope.rs index 77ec6c55c48e..8c9b137c64ba 100644 --- a/bridges/snowbridge/pallets/inbound-queue-v2/src/envelope.rs +++ b/bridges/snowbridge/pallets/inbound-queue-v2/src/envelope.rs @@ -19,7 +19,7 @@ pub struct Envelope { pub gateway: H160, /// A nonce for enforcing replay protection and ordering. pub nonce: u64, - /// Total fee paid on source chain + /// Total fee paid in Ether on Ethereum, should cover all the cost pub fee: u128, /// The inner payload generated from the source application. pub payload: Vec, From a4bf3be7f6c32acc39fe5bd03cb6c38b0189eea8 Mon Sep 17 00:00:00 2001 From: claravanstaden Date: Thu, 17 Oct 2024 16:18:46 +0200 Subject: [PATCH 05/37] new pallet --- Cargo.lock | 106 +----------------- Cargo.toml | 1 + bridges/snowbridge/pallets/rewards/Cargo.toml | 70 ++++++++++++ .../snowbridge/pallets/rewards/src/impls.rs | 8 ++ bridges/snowbridge/pallets/rewards/src/lib.rs | 82 ++++++++++++++ .../snowbridge/pallets/rewards/src/weights.rs | 58 ++++++++++ 6 files changed, 225 insertions(+), 100 deletions(-) create mode 100644 bridges/snowbridge/pallets/rewards/Cargo.toml create mode 100644 bridges/snowbridge/pallets/rewards/src/impls.rs create mode 100644 bridges/snowbridge/pallets/rewards/src/lib.rs create mode 100644 bridges/snowbridge/pallets/rewards/src/weights.rs diff --git a/Cargo.lock b/Cargo.lock index e9316be56edf..a465da9f4972 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2415,7 +2415,6 @@ dependencies = [ "snowbridge-pallet-inbound-queue", "snowbridge-pallet-inbound-queue-fixtures", "snowbridge-pallet-outbound-queue", - "snowbridge-pallet-outbound-queue-v2", "snowbridge-pallet-system", "snowbridge-router-primitives", "sp-core 28.0.0", @@ -2490,17 +2489,12 @@ dependencies = [ "serde_json", "snowbridge-beacon-primitives", "snowbridge-core", - "snowbridge-merkle-tree", "snowbridge-outbound-queue-runtime-api", - "snowbridge-outbound-queue-runtime-api-v2", "snowbridge-pallet-ethereum-client", "snowbridge-pallet-inbound-queue", - "snowbridge-pallet-inbound-queue-v2", "snowbridge-pallet-outbound-queue", - "snowbridge-pallet-outbound-queue-v2", "snowbridge-pallet-system", "snowbridge-router-primitives", - "snowbridge-router-primitives-v2", "snowbridge-runtime-common", "snowbridge-runtime-test-common", "snowbridge-system-runtime-api", @@ -20929,18 +20923,6 @@ dependencies = [ "wasm-bindgen-test", ] -[[package]] -name = "snowbridge-merkle-tree" -version = "0.2.0" -dependencies = [ - "frame-support", - "hex", - "parity-scale-codec", - "scale-info", - "sp-core 28.0.0", - "sp-runtime 31.0.1", -] - [[package]] name = "snowbridge-milagro-bls" version = "1.5.4" @@ -20983,18 +20965,6 @@ dependencies = [ "sp-std 14.0.0", ] -[[package]] -name = "snowbridge-outbound-queue-runtime-api-v2" -version = "0.2.0" -dependencies = [ - "frame-support", - "parity-scale-codec", - "snowbridge-core", - "snowbridge-merkle-tree", - "sp-api 26.0.0", - "sp-std 14.0.0", -] - [[package]] name = "snowbridge-pallet-ethereum-client" version = "0.2.0" @@ -21073,46 +21043,6 @@ dependencies = [ "sp-std 14.0.0", ] -[[package]] -name = "snowbridge-pallet-inbound-queue-fixtures-v2" -version = "0.10.0" -dependencies = [ - "hex-literal", - "snowbridge-beacon-primitives", - "snowbridge-core", - "sp-core 28.0.0", - "sp-std 14.0.0", -] - -[[package]] -name = "snowbridge-pallet-inbound-queue-v2" -version = "0.2.0" -dependencies = [ - "alloy-primitives", - "alloy-sol-types", - "frame-benchmarking", - "frame-support", - "frame-system", - "hex-literal", - "log", - "pallet-balances", - "parity-scale-codec", - "scale-info", - "serde", - "snowbridge-beacon-primitives", - "snowbridge-core", - "snowbridge-pallet-ethereum-client", - "snowbridge-pallet-inbound-queue-fixtures-v2", - "snowbridge-router-primitives-v2", - "sp-core 28.0.0", - "sp-io 30.0.0", - "sp-keyring", - "sp-runtime 31.0.1", - "sp-std 14.0.0", - "staging-xcm", - "staging-xcm-executor", -] - [[package]] name = "snowbridge-pallet-outbound-queue" version = "0.2.0" @@ -21137,29 +21067,24 @@ dependencies = [ ] [[package]] -name = "snowbridge-pallet-outbound-queue-v2" -version = "0.2.0" +name = "snowbridge-pallet-rewards" +version = "0.1.0" dependencies = [ - "alloy-primitives", - "alloy-sol-types", - "bridge-hub-common", - "ethabi-decode", "frame-benchmarking", "frame-support", "frame-system", + "hex", "hex-literal", - "pallet-message-queue", + "log", "parity-scale-codec", "scale-info", - "serde", "snowbridge-core", - "snowbridge-merkle-tree", - "sp-arithmetic 23.0.0", "sp-core 28.0.0", "sp-io 30.0.0", - "sp-keyring", "sp-runtime 31.0.1", "sp-std 14.0.0", + "staging-xcm", + "staging-xcm-executor", ] [[package]] @@ -21206,25 +21131,6 @@ dependencies = [ "staging-xcm-executor", ] -[[package]] -name = "snowbridge-router-primitives-v2" -version = "0.9.0" -dependencies = [ - "frame-support", - "hex-literal", - "log", - "parity-scale-codec", - "scale-info", - "snowbridge-core", - "sp-core 28.0.0", - "sp-io 30.0.0", - "sp-runtime 31.0.1", - "sp-std 14.0.0", - "staging-xcm", - "staging-xcm-builder", - "staging-xcm-executor", -] - [[package]] name = "snowbridge-runtime-common" version = "0.2.0" diff --git a/Cargo.toml b/Cargo.toml index 018adee0d69c..dd23722c904f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,6 +57,7 @@ members = [ "bridges/snowbridge/pallets/outbound-queue-v2/runtime-api", "bridges/snowbridge/pallets/outbound-queue/merkle-tree", "bridges/snowbridge/pallets/outbound-queue/runtime-api", + "bridges/snowbridge/pallets/rewards", "bridges/snowbridge/pallets/system", "bridges/snowbridge/pallets/system/runtime-api", "bridges/snowbridge/primitives/beacon", diff --git a/bridges/snowbridge/pallets/rewards/Cargo.toml b/bridges/snowbridge/pallets/rewards/Cargo.toml new file mode 100644 index 000000000000..69df4f2a1d4b --- /dev/null +++ b/bridges/snowbridge/pallets/rewards/Cargo.toml @@ -0,0 +1,70 @@ +[package] +name = "snowbridge-pallet-rewards" +description = "Snowbridge Rewards Pallet" +version = "0.1.0" +authors = ["Snowfork "] +edition.workspace = true +repository.workspace = true +license = "Apache-2.0" +categories = ["cryptography::cryptocurrencies"] + +[lints] +workspace = true + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = { features = [ + "derive", +], workspace = true } +scale-info = { features = ["derive"], workspace = true } +frame-benchmarking = { optional = true, workspace = true } +frame-support = { workspace = true } +frame-system = { workspace = true } +log = { workspace = true } + +sp-core = { workspace = true } +sp-std = { workspace = true } +sp-io = { workspace = true } +sp-runtime = { workspace = true } + +xcm = { workspace = true } +xcm-executor = { workspace = true } + +snowbridge-core = { workspace = true } + +[dev-dependencies] +hex = { workspace = true, default-features = true } +hex-literal = { workspace = true, default-features = true } + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-benchmarking?/std", + "frame-support/std", + "frame-system/std", + "log/std", + "scale-info/std", + "snowbridge-core/std", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", + "sp-std/std", + "xcm-executor/std", + "xcm/std", +] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "snowbridge-core/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "xcm-executor/runtime-benchmarks", +] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", + "sp-runtime/try-runtime", +] diff --git a/bridges/snowbridge/pallets/rewards/src/impls.rs b/bridges/snowbridge/pallets/rewards/src/impls.rs new file mode 100644 index 000000000000..d67b63e584de --- /dev/null +++ b/bridges/snowbridge/pallets/rewards/src/impls.rs @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork + +use crate::{AccountIdOf, BalanceOf}; +pub trait RewardLedger { + // Deposit reward which can later be claimed by `account` + fn deposit(account: AccountIdOf, value: BalanceOf); +} diff --git a/bridges/snowbridge/pallets/rewards/src/lib.rs b/bridges/snowbridge/pallets/rewards/src/lib.rs new file mode 100644 index 000000000000..7aa949366371 --- /dev/null +++ b/bridges/snowbridge/pallets/rewards/src/lib.rs @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork +#![cfg_attr(not(feature = "std"), no_std)] + +pub mod impls; + +use frame_support::{ + traits::{ + fungible::{Inspect}, + }, +}; +use frame_system::pallet_prelude::*; +use sp_core::{RuntimeDebug, H160, H256}; +use frame_support::pallet_prelude::DispatchClass; +use sp_runtime::DispatchResult; +use crate::impls::RewardLedger; +use frame_support::Identity; +use frame_support::StorageMap; +use frame_system::WeightInfo; +use frame_support::pallet_prelude::IsType; + +pub use pallet::*; + +pub type AccountIdOf = ::AccountId; +pub type BalanceOf = +<::Token as Inspect<::AccountId>>::Balance; +#[frame_support::pallet] +pub mod pallet { + use sp_core::U256; + + use super::*; + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::config] + pub trait Config: frame_system::Config { + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + + type WeightInfo: WeightInfo; + } + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + + } + + #[pallet::error] + pub enum Error { + + } + + #[pallet::storage] + pub type RewardsMapping = + StorageMap<_, Identity, H256, U256, ValueQuery>; + + #[pallet::call] + impl Pallet { + #[pallet::call_index(0)] + #[pallet::weight((T::WeightInfo::claim(), DispatchClass::Operational))] + pub fn claim( + origin: OriginFor, + deposit_address: AccountIdOf, + ) -> DispatchResult { + ensure_signed(origin)?; + Self::process_rewards(deposit_address); + Ok(()) + } + } + + impl Pallet { + fn process_rewards(deposit_address: AccountIdOf) -> DispatchResult { + + Ok(()) + } + } + + impl RewardLedger for Pallet { + fn deposit(account: AccountIdOf, value: BalanceOf) {} + } +} diff --git a/bridges/snowbridge/pallets/rewards/src/weights.rs b/bridges/snowbridge/pallets/rewards/src/weights.rs new file mode 100644 index 000000000000..f2e2dc0f9777 --- /dev/null +++ b/bridges/snowbridge/pallets/rewards/src/weights.rs @@ -0,0 +1,58 @@ + +//! Autogenerated weights for `snowbridge_pallet_rewards` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-10-09, STEPS: `2`, REPEAT: `1`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `crake.local`, CPU: `` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("bridge-hub-rococo-dev")`, DB CACHE: `1024` + +// Executed Command: +// target/release/polkadot-parachain +// benchmark +// pallet +// --chain +// bridge-hub-rococo-dev +// --pallet=snowbridge_pallet_rewards +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --template +// ../parachain/templates/module-weight-template.hbs +// --output +// ../parachain/pallets/control/src/weights.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use core::marker::PhantomData; + +pub trait WeightInfo { + fn claim() -> Weight; +} + +// For backwards compatibility and tests. +impl WeightInfo for () { + /// Storage: ParachainInfo ParachainId (r:1 w:0) + /// Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: EthereumOutboundQueue PalletOperatingMode (r:1 w:0) + /// Proof: EthereumOutboundQueue PalletOperatingMode (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) + /// Storage: MessageQueue BookStateFor (r:1 w:1) + /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: MessageQueue ServiceHead (r:1 w:1) + /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen) + /// Storage: MessageQueue Pages (r:0 w:1) + /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) + fn claim() -> Weight { + // Proof Size summary in bytes: + // Measured: `80` + // Estimated: `3517` + // Minimum execution time: 44_000_000 picoseconds. + Weight::from_parts(44_000_000, 3517) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } +} From 4ebf7e89d7b5d0d4ab65e2e1a4904f2fa8d24f2a Mon Sep 17 00:00:00 2001 From: claravanstaden Date: Fri, 18 Oct 2024 10:20:47 +0200 Subject: [PATCH 06/37] cleanup --- .../snowbridge/pallets/rewards/src/impls.rs | 5 ++-- bridges/snowbridge/pallets/rewards/src/lib.rs | 26 +++++-------------- 2 files changed, 10 insertions(+), 21 deletions(-) diff --git a/bridges/snowbridge/pallets/rewards/src/impls.rs b/bridges/snowbridge/pallets/rewards/src/impls.rs index d67b63e584de..ba310fd8ba35 100644 --- a/bridges/snowbridge/pallets/rewards/src/impls.rs +++ b/bridges/snowbridge/pallets/rewards/src/impls.rs @@ -1,8 +1,9 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-FileCopyrightText: 2023 Snowfork -use crate::{AccountIdOf, BalanceOf}; +use crate::AccountIdOf; +use sp_core::U256; pub trait RewardLedger { // Deposit reward which can later be claimed by `account` - fn deposit(account: AccountIdOf, value: BalanceOf); + fn deposit(account: AccountIdOf, value: U256); } diff --git a/bridges/snowbridge/pallets/rewards/src/lib.rs b/bridges/snowbridge/pallets/rewards/src/lib.rs index 7aa949366371..0f3bfb95c89c 100644 --- a/bridges/snowbridge/pallets/rewards/src/lib.rs +++ b/bridges/snowbridge/pallets/rewards/src/lib.rs @@ -3,30 +3,19 @@ #![cfg_attr(not(feature = "std"), no_std)] pub mod impls; +pub mod weights; -use frame_support::{ - traits::{ - fungible::{Inspect}, - }, -}; use frame_system::pallet_prelude::*; -use sp_core::{RuntimeDebug, H160, H256}; -use frame_support::pallet_prelude::DispatchClass; -use sp_runtime::DispatchResult; use crate::impls::RewardLedger; -use frame_support::Identity; -use frame_support::StorageMap; -use frame_system::WeightInfo; -use frame_support::pallet_prelude::IsType; +pub use weights::WeightInfo; pub use pallet::*; pub type AccountIdOf = ::AccountId; -pub type BalanceOf = -<::Token as Inspect<::AccountId>>::Balance; #[frame_support::pallet] pub mod pallet { use sp_core::U256; + use frame_support::pallet_prelude::*; use super::*; @@ -41,7 +30,6 @@ pub mod pallet { } #[pallet::event] - #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { } @@ -53,7 +41,7 @@ pub mod pallet { #[pallet::storage] pub type RewardsMapping = - StorageMap<_, Identity, H256, U256, ValueQuery>; + StorageMap<_, Identity, AccountIdOf, U256, ValueQuery>; #[pallet::call] impl Pallet { @@ -64,19 +52,19 @@ pub mod pallet { deposit_address: AccountIdOf, ) -> DispatchResult { ensure_signed(origin)?; - Self::process_rewards(deposit_address); + let _ = Self::process_rewards(deposit_address); Ok(()) } } impl Pallet { - fn process_rewards(deposit_address: AccountIdOf) -> DispatchResult { + fn process_rewards(_deposit_address: AccountIdOf) -> DispatchResult { Ok(()) } } impl RewardLedger for Pallet { - fn deposit(account: AccountIdOf, value: BalanceOf) {} + fn deposit(_account: AccountIdOf, _value: U256) {} } } From d85bac51aaffab944f77ad44a03fe25b23c5af44 Mon Sep 17 00:00:00 2001 From: claravanstaden Date: Fri, 18 Oct 2024 10:32:08 +0200 Subject: [PATCH 07/37] move rewards interface --- bridges/snowbridge/pallets/rewards/src/lib.rs | 8 +++----- bridges/snowbridge/primitives/core/src/lib.rs | 1 + .../src/impls.rs => primitives/core/src/rewards.rs} | 5 ++--- 3 files changed, 6 insertions(+), 8 deletions(-) rename bridges/snowbridge/{pallets/rewards/src/impls.rs => primitives/core/src/rewards.rs} (64%) diff --git a/bridges/snowbridge/pallets/rewards/src/lib.rs b/bridges/snowbridge/pallets/rewards/src/lib.rs index 0f3bfb95c89c..691c175ca7ee 100644 --- a/bridges/snowbridge/pallets/rewards/src/lib.rs +++ b/bridges/snowbridge/pallets/rewards/src/lib.rs @@ -2,11 +2,10 @@ // SPDX-FileCopyrightText: 2023 Snowfork #![cfg_attr(not(feature = "std"), no_std)] -pub mod impls; pub mod weights; use frame_system::pallet_prelude::*; -use crate::impls::RewardLedger; +use snowbridge_core::rewards::RewardLedger; pub use weights::WeightInfo; pub use pallet::*; @@ -14,7 +13,6 @@ pub use pallet::*; pub type AccountIdOf = ::AccountId; #[frame_support::pallet] pub mod pallet { - use sp_core::U256; use frame_support::pallet_prelude::*; use super::*; @@ -41,7 +39,7 @@ pub mod pallet { #[pallet::storage] pub type RewardsMapping = - StorageMap<_, Identity, AccountIdOf, U256, ValueQuery>; + StorageMap<_, Identity, AccountIdOf, u128, ValueQuery>; #[pallet::call] impl Pallet { @@ -65,6 +63,6 @@ pub mod pallet { } impl RewardLedger for Pallet { - fn deposit(_account: AccountIdOf, _value: U256) {} + fn deposit(_account: AccountIdOf, _value: u128) {} } } diff --git a/bridges/snowbridge/primitives/core/src/lib.rs b/bridges/snowbridge/primitives/core/src/lib.rs index 7ad129a52542..94ef1c3ba900 100644 --- a/bridges/snowbridge/primitives/core/src/lib.rs +++ b/bridges/snowbridge/primitives/core/src/lib.rs @@ -13,6 +13,7 @@ pub mod location; pub mod operating_mode; pub mod outbound; pub mod pricing; +pub mod rewards; pub mod ringbuffer; pub use location::{AgentId, AgentIdOf, TokenId, TokenIdOf}; diff --git a/bridges/snowbridge/pallets/rewards/src/impls.rs b/bridges/snowbridge/primitives/core/src/rewards.rs similarity index 64% rename from bridges/snowbridge/pallets/rewards/src/impls.rs rename to bridges/snowbridge/primitives/core/src/rewards.rs index ba310fd8ba35..aaca6561bb82 100644 --- a/bridges/snowbridge/pallets/rewards/src/impls.rs +++ b/bridges/snowbridge/primitives/core/src/rewards.rs @@ -1,9 +1,8 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-FileCopyrightText: 2023 Snowfork -use crate::AccountIdOf; -use sp_core::U256; +pub type AccountIdOf = ::AccountId; pub trait RewardLedger { // Deposit reward which can later be claimed by `account` - fn deposit(account: AccountIdOf, value: U256); + fn deposit(account: AccountIdOf, value: u128); } From 48d285e1eb9376084facfb4bdce559926367cafc Mon Sep 17 00:00:00 2001 From: claravanstaden Date: Wed, 23 Oct 2024 16:32:28 +0200 Subject: [PATCH 08/37] lock file --- Cargo.lock | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index a465da9f4972..d5374c51104c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2415,6 +2415,7 @@ dependencies = [ "snowbridge-pallet-inbound-queue", "snowbridge-pallet-inbound-queue-fixtures", "snowbridge-pallet-outbound-queue", + "snowbridge-pallet-outbound-queue-v2", "snowbridge-pallet-system", "snowbridge-router-primitives", "sp-core 28.0.0", @@ -2489,12 +2490,17 @@ dependencies = [ "serde_json", "snowbridge-beacon-primitives", "snowbridge-core", + "snowbridge-merkle-tree", "snowbridge-outbound-queue-runtime-api", + "snowbridge-outbound-queue-runtime-api-v2", "snowbridge-pallet-ethereum-client", "snowbridge-pallet-inbound-queue", + "snowbridge-pallet-inbound-queue-v2", "snowbridge-pallet-outbound-queue", + "snowbridge-pallet-outbound-queue-v2", "snowbridge-pallet-system", "snowbridge-router-primitives", + "snowbridge-router-primitives-v2", "snowbridge-runtime-common", "snowbridge-runtime-test-common", "snowbridge-system-runtime-api", @@ -20923,6 +20929,18 @@ dependencies = [ "wasm-bindgen-test", ] +[[package]] +name = "snowbridge-merkle-tree" +version = "0.2.0" +dependencies = [ + "frame-support", + "hex", + "parity-scale-codec", + "scale-info", + "sp-core 28.0.0", + "sp-runtime 31.0.1", +] + [[package]] name = "snowbridge-milagro-bls" version = "1.5.4" @@ -20965,6 +20983,18 @@ dependencies = [ "sp-std 14.0.0", ] +[[package]] +name = "snowbridge-outbound-queue-runtime-api-v2" +version = "0.2.0" +dependencies = [ + "frame-support", + "parity-scale-codec", + "snowbridge-core", + "snowbridge-merkle-tree", + "sp-api 26.0.0", + "sp-std 14.0.0", +] + [[package]] name = "snowbridge-pallet-ethereum-client" version = "0.2.0" @@ -21043,6 +21073,46 @@ dependencies = [ "sp-std 14.0.0", ] +[[package]] +name = "snowbridge-pallet-inbound-queue-fixtures-v2" +version = "0.10.0" +dependencies = [ + "hex-literal", + "snowbridge-beacon-primitives", + "snowbridge-core", + "sp-core 28.0.0", + "sp-std 14.0.0", +] + +[[package]] +name = "snowbridge-pallet-inbound-queue-v2" +version = "0.2.0" +dependencies = [ + "alloy-primitives", + "alloy-sol-types", + "frame-benchmarking", + "frame-support", + "frame-system", + "hex-literal", + "log", + "pallet-balances", + "parity-scale-codec", + "scale-info", + "serde", + "snowbridge-beacon-primitives", + "snowbridge-core", + "snowbridge-pallet-ethereum-client", + "snowbridge-pallet-inbound-queue-fixtures-v2", + "snowbridge-router-primitives-v2", + "sp-core 28.0.0", + "sp-io 30.0.0", + "sp-keyring", + "sp-runtime 31.0.1", + "sp-std 14.0.0", + "staging-xcm", + "staging-xcm-executor", +] + [[package]] name = "snowbridge-pallet-outbound-queue" version = "0.2.0" @@ -21066,6 +21136,32 @@ dependencies = [ "sp-std 14.0.0", ] +[[package]] +name = "snowbridge-pallet-outbound-queue-v2" +version = "0.2.0" +dependencies = [ + "alloy-primitives", + "alloy-sol-types", + "bridge-hub-common", + "ethabi-decode", + "frame-benchmarking", + "frame-support", + "frame-system", + "hex-literal", + "pallet-message-queue", + "parity-scale-codec", + "scale-info", + "serde", + "snowbridge-core", + "snowbridge-merkle-tree", + "sp-arithmetic 23.0.0", + "sp-core 28.0.0", + "sp-io 30.0.0", + "sp-keyring", + "sp-runtime 31.0.1", + "sp-std 14.0.0", +] + [[package]] name = "snowbridge-pallet-rewards" version = "0.1.0" @@ -21131,6 +21227,25 @@ dependencies = [ "staging-xcm-executor", ] +[[package]] +name = "snowbridge-router-primitives-v2" +version = "0.9.0" +dependencies = [ + "frame-support", + "hex-literal", + "log", + "parity-scale-codec", + "scale-info", + "snowbridge-core", + "sp-core 28.0.0", + "sp-io 30.0.0", + "sp-runtime 31.0.1", + "sp-std 14.0.0", + "staging-xcm", + "staging-xcm-builder", + "staging-xcm-executor", +] + [[package]] name = "snowbridge-runtime-common" version = "0.2.0" From 21e4bf871c31bb8925389155103f9556f43d0c15 Mon Sep 17 00:00:00 2001 From: claravanstaden Date: Fri, 18 Oct 2024 12:00:41 +0200 Subject: [PATCH 09/37] progress --- Cargo.lock | 1 + bridges/snowbridge/pallets/rewards/Cargo.toml | 2 + bridges/snowbridge/pallets/rewards/src/lib.rs | 90 +++++++++++++++++-- .../primitives/core/src/location.rs | 10 +++ .../snowbridge/primitives/core/src/rewards.rs | 1 + .../primitives/router/src/inbound/mod.rs | 13 +-- 6 files changed, 100 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d5374c51104c..15836b3c52e5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -21175,6 +21175,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "snowbridge-core", + "snowbridge-router-primitives", "sp-core 28.0.0", "sp-io 30.0.0", "sp-runtime 31.0.1", diff --git a/bridges/snowbridge/pallets/rewards/Cargo.toml b/bridges/snowbridge/pallets/rewards/Cargo.toml index 69df4f2a1d4b..6a5eb2c03ad2 100644 --- a/bridges/snowbridge/pallets/rewards/Cargo.toml +++ b/bridges/snowbridge/pallets/rewards/Cargo.toml @@ -33,6 +33,7 @@ xcm = { workspace = true } xcm-executor = { workspace = true } snowbridge-core = { workspace = true } +snowbridge-router-primitives = { workspace = true } [dev-dependencies] hex = { workspace = true, default-features = true } @@ -48,6 +49,7 @@ std = [ "log/std", "scale-info/std", "snowbridge-core/std", + "snowbridge-router-primitives/std", "sp-core/std", "sp-io/std", "sp-runtime/std", diff --git a/bridges/snowbridge/pallets/rewards/src/lib.rs b/bridges/snowbridge/pallets/rewards/src/lib.rs index 691c175ca7ee..5d876f41a04e 100644 --- a/bridges/snowbridge/pallets/rewards/src/lib.rs +++ b/bridges/snowbridge/pallets/rewards/src/lib.rs @@ -5,8 +5,12 @@ pub mod weights; use frame_system::pallet_prelude::*; +use frame_support::PalletError; use snowbridge_core::rewards::RewardLedger; +use xcm::prelude::{*, send_xcm, SendError as XcmpSendError,}; pub use weights::WeightInfo; +use sp_core::H160; + pub use pallet::*; @@ -14,7 +18,7 @@ pub type AccountIdOf = ::AccountId; #[frame_support::pallet] pub mod pallet { use frame_support::pallet_prelude::*; - + use sp_core::H256; use super::*; #[pallet::pallet] @@ -23,18 +27,67 @@ pub mod pallet { #[pallet::config] pub trait Config: frame_system::Config { type RuntimeEvent: From> + IsType<::RuntimeEvent>; - + type AssetHubParaId: Get; + type EthereumNetwork: Get; + type WethAddress: Get; + /// XCM message sender + type XcmSender: SendXcm; type WeightInfo: WeightInfo; } #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { - + /// A relayer reward was deposited + RewardDeposited { + /// The relayer account to which the reward was deposited. + account_id: AccountIdOf, + /// The reward value. + value: u128, + }, + RewardClaimed { + /// The relayer account that claimed the reward. + account_id: AccountIdOf, + /// The address that received the reward on AH. + deposit_address: AccountIdOf, + /// The claimed reward value. + value: u128, + /// The message ID that was provided, used to track the claim + message_id: H256 + }, } #[pallet::error] pub enum Error { + /// XCMP send failure + Send(SendError), + } + #[derive(Clone, Encode, Decode, Eq, PartialEq, Debug, TypeInfo, PalletError)] + pub enum SendError { + NotApplicable, + NotRoutable, + Transport, + DestinationUnsupported, + ExceedsMaxMessageSize, + MissingArgument, + Fees, + } + + impl From for Error { + fn from(e: XcmpSendError) -> Self { + match e { + XcmpSendError::NotApplicable => Error::::Send(SendError::NotApplicable), + XcmpSendError::Unroutable => Error::::Send(SendError::NotRoutable), + XcmpSendError::Transport(_) => Error::::Send(SendError::Transport), + XcmpSendError::DestinationUnsupported => + Error::::Send(SendError::DestinationUnsupported), + XcmpSendError::ExceedsMaxMessageSize => + Error::::Send(SendError::ExceedsMaxMessageSize), + XcmpSendError::MissingArgument => Error::::Send(SendError::MissingArgument), + XcmpSendError::Fees => Error::::Send(SendError::Fees), + } + } } #[pallet::storage] @@ -48,21 +101,44 @@ pub mod pallet { pub fn claim( origin: OriginFor, deposit_address: AccountIdOf, + value: u128, + message_id: H256 ) -> DispatchResult { - ensure_signed(origin)?; - let _ = Self::process_rewards(deposit_address); + let account_id = ensure_signed(origin)?; + Self::process_claim(account_id, deposit_address, value, message_id)?; Ok(()) } } impl Pallet { - fn process_rewards(_deposit_address: AccountIdOf) -> DispatchResult { + fn process_claim(account_id: AccountIdOf, deposit_address: AccountIdOf, value: u128, message_id: H256) -> DispatchResult { + // Check if the claim value is equal to or less than the accumulated balance. + + let reward_asset = snowbridge_core::location::convert_token_address(T::EthereumNetwork::get(), T::WethAddress::get()); + let deposit: Asset = (reward_asset, value).into(); + let beneficiary: Location = Location::new(0, Parachain(T::AssetHubParaId::get().into())); + let xcm: Xcm<()> = vec![ + DepositAsset { assets: Definite(deposit.into()), beneficiary }, + SetTopic(message_id.into()), + ] + .into(); + + let dest = Location::new(1, [Parachain(T::AssetHubParaId::get().into())]); + let (_xcm_hash, _) = send_xcm::(dest, xcm).map_err(Error::::from)?; + + Self::deposit_event(Event::RewardClaimed { + account_id, deposit_address, value, message_id + }); Ok(()) } } impl RewardLedger for Pallet { - fn deposit(_account: AccountIdOf, _value: u128) {} + fn deposit(account_id: AccountIdOf, value: u128) { + Self::deposit_event(Event::RewardDeposited { + account_id, value + }); + } } } diff --git a/bridges/snowbridge/primitives/core/src/location.rs b/bridges/snowbridge/primitives/core/src/location.rs index aad1c9ece05c..5e86dfc3d0df 100644 --- a/bridges/snowbridge/primitives/core/src/location.rs +++ b/bridges/snowbridge/primitives/core/src/location.rs @@ -8,6 +8,8 @@ pub use polkadot_parachain_primitives::primitives::{ Id as ParaId, IsSystem, Sibling as SiblingParaId, }; pub use sp_core::U256; +use sp_core::H160; +use xcm::opaque::lts::NetworkId; use codec::Encode; use sp_core::H256; @@ -203,3 +205,11 @@ mod tests { } } } + +// Convert ERC20 token address to a location that can be understood by Assets Hub. +pub fn convert_token_address(network: NetworkId, token: H160) -> Location { + Location::new( + 2, + [GlobalConsensus(network), AccountKey20 { network: None, key: token.into() }], + ) +} diff --git a/bridges/snowbridge/primitives/core/src/rewards.rs b/bridges/snowbridge/primitives/core/src/rewards.rs index aaca6561bb82..2d779ce61151 100644 --- a/bridges/snowbridge/primitives/core/src/rewards.rs +++ b/bridges/snowbridge/primitives/core/src/rewards.rs @@ -2,6 +2,7 @@ // SPDX-FileCopyrightText: 2023 Snowfork pub type AccountIdOf = ::AccountId; + pub trait RewardLedger { // Deposit reward which can later be claimed by `account` fn deposit(account: AccountIdOf, value: u128); diff --git a/bridges/snowbridge/primitives/router/src/inbound/mod.rs b/bridges/snowbridge/primitives/router/src/inbound/mod.rs index fbfc52d01c83..64cb18745e28 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/mod.rs @@ -16,7 +16,7 @@ use sp_runtime::{traits::MaybeEquivalence, MultiAddress}; use sp_std::prelude::*; use xcm::prelude::{Junction::AccountKey20, *}; use xcm_executor::traits::ConvertLocation; - +use snowbridge_core::location::convert_token_address; const MINIMUM_DEPOSIT: u128 = 1; /// Messages from Ethereum are versioned. This is because in future, @@ -254,7 +254,7 @@ where let bridge_location = Location::new(2, GlobalConsensus(network)); let owner = GlobalConsensusEthereumConvertsFor::<[u8; 32]>::from_chain_id(&chain_id); - let asset_id = Self::convert_token_address(network, token); + let asset_id = convert_token_address(network, token); let create_call_index: [u8; 2] = CreateAssetCall::get(); let inbound_queue_pallet_index = InboundQueuePalletInstance::get(); @@ -309,7 +309,7 @@ where ) -> (Xcm<()>, Balance) { let network = Ethereum { chain_id }; let asset_hub_fee_asset: Asset = (Location::parent(), asset_hub_fee).into(); - let asset: Asset = (Self::convert_token_address(network, token), amount).into(); + let asset: Asset = (convert_token_address(network, token), amount).into(); let (dest_para_id, beneficiary, dest_para_fee) = match destination { // Final destination is a 32-byte account on AssetHub @@ -390,13 +390,6 @@ where (instructions.into(), total_fees.into()) } - // Convert ERC20 token address to a location that can be understood by Assets Hub. - fn convert_token_address(network: NetworkId, token: H160) -> Location { - Location::new( - 2, - [GlobalConsensus(network), AccountKey20 { network: None, key: token.into() }], - ) - } /// Constructs an XCM message destined for AssetHub that withdraws assets from the sovereign /// account of the Gateway contract and either deposits those assets into a recipient account or From 655a45b1686f04601bb45f3e999ae00b194d1623 Mon Sep 17 00:00:00 2001 From: claravanstaden Date: Fri, 18 Oct 2024 13:03:54 +0200 Subject: [PATCH 10/37] progress --- Cargo.lock | 1 + bridges/snowbridge/pallets/rewards/Cargo.toml | 2 + bridges/snowbridge/pallets/rewards/src/lib.rs | 59 ++++++++--- .../snowbridge/pallets/rewards/src/mock.rs | 100 ++++++++++++++++++ .../snowbridge/pallets/rewards/src/tests.rs | 34 ++++++ .../primitives/core/src/location.rs | 7 +- .../snowbridge/primitives/core/src/rewards.rs | 5 +- 7 files changed, 184 insertions(+), 24 deletions(-) create mode 100644 bridges/snowbridge/pallets/rewards/src/mock.rs create mode 100644 bridges/snowbridge/pallets/rewards/src/tests.rs diff --git a/Cargo.lock b/Cargo.lock index 15836b3c52e5..878f073a3fbd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -21178,6 +21178,7 @@ dependencies = [ "snowbridge-router-primitives", "sp-core 28.0.0", "sp-io 30.0.0", + "sp-keyring", "sp-runtime 31.0.1", "sp-std 14.0.0", "staging-xcm", diff --git a/bridges/snowbridge/pallets/rewards/Cargo.toml b/bridges/snowbridge/pallets/rewards/Cargo.toml index 6a5eb2c03ad2..65b7b698ad2f 100644 --- a/bridges/snowbridge/pallets/rewards/Cargo.toml +++ b/bridges/snowbridge/pallets/rewards/Cargo.toml @@ -38,6 +38,7 @@ snowbridge-router-primitives = { workspace = true } [dev-dependencies] hex = { workspace = true, default-features = true } hex-literal = { workspace = true, default-features = true } +sp-keyring = { workspace = true, default-features = true } [features] default = ["std"] @@ -54,6 +55,7 @@ std = [ "sp-io/std", "sp-runtime/std", "sp-std/std", + "sp-keyring/std", "xcm-executor/std", "xcm/std", ] diff --git a/bridges/snowbridge/pallets/rewards/src/lib.rs b/bridges/snowbridge/pallets/rewards/src/lib.rs index 5d876f41a04e..da108ad4b153 100644 --- a/bridges/snowbridge/pallets/rewards/src/lib.rs +++ b/bridges/snowbridge/pallets/rewards/src/lib.rs @@ -4,22 +4,27 @@ pub mod weights; -use frame_system::pallet_prelude::*; +#[cfg(test)] +mod mock; + +#[cfg(test)] +mod tests; + use frame_support::PalletError; +use frame_system::pallet_prelude::*; use snowbridge_core::rewards::RewardLedger; -use xcm::prelude::{*, send_xcm, SendError as XcmpSendError,}; -pub use weights::WeightInfo; use sp_core::H160; - +pub use weights::WeightInfo; +use xcm::prelude::{send_xcm, SendError as XcmpSendError, *}; pub use pallet::*; pub type AccountIdOf = ::AccountId; #[frame_support::pallet] pub mod pallet { + use super::*; use frame_support::pallet_prelude::*; use sp_core::H256; - use super::*; #[pallet::pallet] pub struct Pallet(_); @@ -53,7 +58,7 @@ pub mod pallet { /// The claimed reward value. value: u128, /// The message ID that was provided, used to track the claim - message_id: H256 + message_id: H256, }, } @@ -61,6 +66,8 @@ pub mod pallet { pub enum Error { /// XCMP send failure Send(SendError), + /// The relayer rewards balance is lower than the claimed amount. + InsufficientFunds, } #[derive(Clone, Encode, Decode, Eq, PartialEq, Debug, TypeInfo, PalletError)] @@ -91,8 +98,7 @@ pub mod pallet { } #[pallet::storage] - pub type RewardsMapping = - StorageMap<_, Identity, AccountIdOf, u128, ValueQuery>; + pub type RewardsMapping = StorageMap<_, Identity, AccountIdOf, u128, ValueQuery>; #[pallet::call] impl Pallet { @@ -102,7 +108,7 @@ pub mod pallet { origin: OriginFor, deposit_address: AccountIdOf, value: u128, - message_id: H256 + message_id: H256, ) -> DispatchResult { let account_id = ensure_signed(origin)?; Self::process_claim(account_id, deposit_address, value, message_id)?; @@ -111,34 +117,53 @@ pub mod pallet { } impl Pallet { - fn process_claim(account_id: AccountIdOf, deposit_address: AccountIdOf, value: u128, message_id: H256) -> DispatchResult { + fn process_claim( + account_id: AccountIdOf, + deposit_address: AccountIdOf, + value: u128, + message_id: H256, + ) -> DispatchResult { // Check if the claim value is equal to or less than the accumulated balance. + let reward_balance = RewardsMapping::::get(account_id.clone()); + if value > reward_balance { + return Err(Error::::InsufficientFunds.into()); + } - let reward_asset = snowbridge_core::location::convert_token_address(T::EthereumNetwork::get(), T::WethAddress::get()); + let reward_asset = snowbridge_core::location::convert_token_address( + T::EthereumNetwork::get(), + T::WethAddress::get(), + ); let deposit: Asset = (reward_asset, value).into(); - let beneficiary: Location = Location::new(0, Parachain(T::AssetHubParaId::get().into())); + let beneficiary: Location = + Location::new(0, Parachain(T::AssetHubParaId::get().into())); let xcm: Xcm<()> = vec![ DepositAsset { assets: Definite(deposit.into()), beneficiary }, SetTopic(message_id.into()), ] - .into(); + .into(); let dest = Location::new(1, [Parachain(T::AssetHubParaId::get().into())]); let (_xcm_hash, _) = send_xcm::(dest, xcm).map_err(Error::::from)?; Self::deposit_event(Event::RewardClaimed { - account_id, deposit_address, value, message_id + account_id, + deposit_address, + value, + message_id, }); Ok(()) } } impl RewardLedger for Pallet { - fn deposit(account_id: AccountIdOf, value: u128) { - Self::deposit_event(Event::RewardDeposited { - account_id, value + fn deposit(account_id: AccountIdOf, value: u128) -> DispatchResult { + RewardsMapping::::mutate(account_id.clone(), |current_value| { + *current_value = current_value.saturating_add(value); }); + Self::deposit_event(Event::RewardDeposited { account_id, value }); + + Ok(()) } } } diff --git a/bridges/snowbridge/pallets/rewards/src/mock.rs b/bridges/snowbridge/pallets/rewards/src/mock.rs new file mode 100644 index 000000000000..5e2b384f85bc --- /dev/null +++ b/bridges/snowbridge/pallets/rewards/src/mock.rs @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork +use super::*; + +use codec::Encode; +use frame_support::{derive_impl, parameter_types}; +use hex_literal::hex; +use sp_core::{ConstU32, H160}; +use sp_runtime::{ + traits::{IdentifyAccount, IdentityLookup, Verify}, + BuildStorage, MultiSignature, +}; +use sp_std::{convert::From, default::Default}; +use xcm::{latest::SendXcm, prelude::*}; + +use crate::{self as snowbridge_pallet_rewards}; + +type Block = frame_system::mocking::MockBlock; + +frame_support::construct_runtime!( + pub enum Test + { + System: frame_system::{Pallet, Call, Storage, Event}, + EthereumRewards: snowbridge_pallet_rewards::{Pallet, Call, Storage, Event}, + } +); + +pub type Signature = MultiSignature; +pub type AccountId = <::Signer as IdentifyAccount>::AccountId; + +type Balance = u128; + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] +impl frame_system::Config for Test { + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Block = Block; +} + +parameter_types! { + pub EthereumNetwork: NetworkId = NetworkId::Ethereum { chain_id: 11155111 }; + pub WethAddress: H160 = hex!("774667629726ec1FaBEbCEc0D9139bD1C8f72a23").into(); +} + +impl snowbridge_pallet_rewards::Config for Test { + type RuntimeEvent = RuntimeEvent; + type AssetHubParaId = ConstU32<1000>; + type EthereumNetwork = EthereumNetwork; + type WethAddress = WethAddress; + type XcmSender = MockXcmSender; + type WeightInfo = (); +} + +// Mock XCM sender that always succeeds +pub struct MockXcmSender; + +impl SendXcm for MockXcmSender { + type Ticket = Xcm<()>; + + fn validate( + dest: &mut Option, + xcm: &mut Option>, + ) -> SendResult { + if let Some(location) = dest { + match location.unpack() { + (_, [Parachain(1001)]) => return Err(XcmpSendError::NotApplicable), + _ => Ok((xcm.clone().unwrap(), Assets::default())), + } + } else { + Ok((xcm.clone().unwrap(), Assets::default())) + } + } + + fn deliver(xcm: Self::Ticket) -> core::result::Result { + let hash = xcm.using_encoded(sp_io::hashing::blake2_256); + Ok(hash) + } +} + +pub const WETH: u128 = 1_000_000_000_000_000_000; + +pub fn last_events(n: usize) -> Vec { + frame_system::Pallet::::events() + .into_iter() + .rev() + .take(n) + .rev() + .map(|e| e.event) + .collect() +} + +pub fn expect_events(e: Vec) { + assert_eq!(last_events(e.len()), e); +} + +pub fn new_tester() -> sp_io::TestExternalities { + let t = frame_system::GenesisConfig::::default().build_storage().unwrap(); + let ext = sp_io::TestExternalities::new(t); + ext +} diff --git a/bridges/snowbridge/pallets/rewards/src/tests.rs b/bridges/snowbridge/pallets/rewards/src/tests.rs new file mode 100644 index 000000000000..2db4f9d75d44 --- /dev/null +++ b/bridges/snowbridge/pallets/rewards/src/tests.rs @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork + +use super::*; +use crate::{ + mock::{expect_events, new_tester, AccountId, EthereumRewards, Test, WETH}, + Event as RewardEvent, +}; +use frame_support::assert_ok; +use sp_keyring::AccountKeyring as Keyring; +#[test] +fn test_deposit() { + new_tester().execute_with(|| { + // Check a new deposit works + let relayer: AccountId = Keyring::Bob.into(); + let result = EthereumRewards::deposit(relayer.clone().into(), 2 * WETH); + assert_ok!(result); + assert_eq!(>::get(relayer.clone()), 2 * WETH); + //expect_events(vec![RewardEvent::RewardDeposited { + // account_id: relayer.clone(), value: 2 * WETH + //}]); + + // Check accumulation works + let result2 = EthereumRewards::deposit(relayer.clone().into(), 3 * WETH); + assert_ok!(result2); + assert_eq!(>::get(relayer), 5 * WETH); + + // Check another relayer deposit works. + let another_relayer: AccountId = Keyring::Ferdie.into(); + let result3 = EthereumRewards::deposit(another_relayer.clone().into(), 1 * WETH); + assert_ok!(result3); + assert_eq!(>::get(another_relayer), 1 * WETH); + }); +} diff --git a/bridges/snowbridge/primitives/core/src/location.rs b/bridges/snowbridge/primitives/core/src/location.rs index 5e86dfc3d0df..55b75998e92b 100644 --- a/bridges/snowbridge/primitives/core/src/location.rs +++ b/bridges/snowbridge/primitives/core/src/location.rs @@ -7,8 +7,8 @@ pub use polkadot_parachain_primitives::primitives::{ Id as ParaId, IsSystem, Sibling as SiblingParaId, }; -pub use sp_core::U256; use sp_core::H160; +pub use sp_core::U256; use xcm::opaque::lts::NetworkId; use codec::Encode; @@ -208,8 +208,5 @@ mod tests { // Convert ERC20 token address to a location that can be understood by Assets Hub. pub fn convert_token_address(network: NetworkId, token: H160) -> Location { - Location::new( - 2, - [GlobalConsensus(network), AccountKey20 { network: None, key: token.into() }], - ) + Location::new(2, [GlobalConsensus(network), AccountKey20 { network: None, key: token.into() }]) } diff --git a/bridges/snowbridge/primitives/core/src/rewards.rs b/bridges/snowbridge/primitives/core/src/rewards.rs index 2d779ce61151..a46f0261c79c 100644 --- a/bridges/snowbridge/primitives/core/src/rewards.rs +++ b/bridges/snowbridge/primitives/core/src/rewards.rs @@ -1,9 +1,10 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-FileCopyrightText: 2023 Snowfork +use frame_support::pallet_prelude::DispatchResult; pub type AccountIdOf = ::AccountId; pub trait RewardLedger { - // Deposit reward which can later be claimed by `account` - fn deposit(account: AccountIdOf, value: u128); + // Deposit reward which can later be claimed by `account` + fn deposit(account: AccountIdOf, value: u128) -> DispatchResult; } From 61fa7714a44976489a09608e5528935d70b56422 Mon Sep 17 00:00:00 2001 From: claravanstaden Date: Fri, 18 Oct 2024 13:41:16 +0200 Subject: [PATCH 11/37] more tests --- bridges/snowbridge/pallets/rewards/src/lib.rs | 5 +++ .../snowbridge/pallets/rewards/src/mock.rs | 1 - .../snowbridge/pallets/rewards/src/tests.rs | 36 +++++++++++++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/bridges/snowbridge/pallets/rewards/src/lib.rs b/bridges/snowbridge/pallets/rewards/src/lib.rs index da108ad4b153..d3786d9e56f9 100644 --- a/bridges/snowbridge/pallets/rewards/src/lib.rs +++ b/bridges/snowbridge/pallets/rewards/src/lib.rs @@ -143,6 +143,11 @@ pub mod pallet { ] .into(); + // Deduct the reward from the claimable balance + RewardsMapping::::mutate(account_id.clone(), |current_value| { + *current_value = current_value.saturating_sub(value); + }); + let dest = Location::new(1, [Parachain(T::AssetHubParaId::get().into())]); let (_xcm_hash, _) = send_xcm::(dest, xcm).map_err(Error::::from)?; diff --git a/bridges/snowbridge/pallets/rewards/src/mock.rs b/bridges/snowbridge/pallets/rewards/src/mock.rs index 5e2b384f85bc..6b0fd99086e0 100644 --- a/bridges/snowbridge/pallets/rewards/src/mock.rs +++ b/bridges/snowbridge/pallets/rewards/src/mock.rs @@ -28,7 +28,6 @@ frame_support::construct_runtime!( pub type Signature = MultiSignature; pub type AccountId = <::Signer as IdentifyAccount>::AccountId; -type Balance = u128; #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { diff --git a/bridges/snowbridge/pallets/rewards/src/tests.rs b/bridges/snowbridge/pallets/rewards/src/tests.rs index 2db4f9d75d44..31fa4093a9ec 100644 --- a/bridges/snowbridge/pallets/rewards/src/tests.rs +++ b/bridges/snowbridge/pallets/rewards/src/tests.rs @@ -8,6 +8,9 @@ use crate::{ }; use frame_support::assert_ok; use sp_keyring::AccountKeyring as Keyring; +use crate::mock::RuntimeOrigin; +use sp_core::H256; +use frame_support::assert_err; #[test] fn test_deposit() { new_tester().execute_with(|| { @@ -32,3 +35,36 @@ fn test_deposit() { assert_eq!(>::get(another_relayer), 1 * WETH); }); } + +#[test] +fn test_claim() { + new_tester().execute_with(|| { + let relayer: AccountId = Keyring::Bob.into(); + let message_id = H256::random(); + + let result = + EthereumRewards::claim(RuntimeOrigin::signed(relayer.clone()), relayer.clone(), 3 * WETH, message_id); + // No rewards yet + assert_err!(result, Error::::InsufficientFunds); + + // Deposit rewards + let result2 = + EthereumRewards::deposit(relayer.clone(), 3 * WETH); + assert_ok!(result2); + + // Claim some rewards + let result3 = + EthereumRewards::claim(RuntimeOrigin::signed(relayer.clone()), relayer.clone(), 2 * WETH, message_id); + assert_ok!(result3); + + // Claim some rewards than available + let result4 = + EthereumRewards::claim(RuntimeOrigin::signed(relayer.clone()), relayer.clone(), 2 * WETH, message_id); + assert_err!(result4, Error::::InsufficientFunds); + + // Claim the remaining balance + let result5 = + EthereumRewards::claim(RuntimeOrigin::signed(relayer.clone()), relayer, 1 * WETH, message_id); + assert_ok!(result5); + }); +} From 63467185d5b7c5177238079cdbd99a49ebfe02c5 Mon Sep 17 00:00:00 2001 From: claravanstaden Date: Fri, 18 Oct 2024 13:42:23 +0200 Subject: [PATCH 12/37] more tests --- bridges/snowbridge/pallets/rewards/src/tests.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bridges/snowbridge/pallets/rewards/src/tests.rs b/bridges/snowbridge/pallets/rewards/src/tests.rs index 31fa4093a9ec..276e942cd434 100644 --- a/bridges/snowbridge/pallets/rewards/src/tests.rs +++ b/bridges/snowbridge/pallets/rewards/src/tests.rs @@ -56,6 +56,7 @@ fn test_claim() { let result3 = EthereumRewards::claim(RuntimeOrigin::signed(relayer.clone()), relayer.clone(), 2 * WETH, message_id); assert_ok!(result3); + assert_eq!(>::get(relayer.clone()), 1 * WETH); // Claim some rewards than available let result4 = @@ -64,7 +65,8 @@ fn test_claim() { // Claim the remaining balance let result5 = - EthereumRewards::claim(RuntimeOrigin::signed(relayer.clone()), relayer, 1 * WETH, message_id); + EthereumRewards::claim(RuntimeOrigin::signed(relayer.clone()), relayer.clone(), 1 * WETH, message_id); assert_ok!(result5); + assert_eq!(>::get(relayer.clone()), 0); }); } From 4b9be93e4a4014305576df8abf217333cb621297 Mon Sep 17 00:00:00 2001 From: claravanstaden Date: Wed, 23 Oct 2024 19:01:25 +0200 Subject: [PATCH 13/37] adds pallet integration --- Cargo.lock | 2 ++ Cargo.toml | 1 + .../pallets/inbound-queue-v2/src/lib.rs | 4 +++ .../pallets/outbound-queue-v2/src/lib.rs | 3 ++ bridges/snowbridge/pallets/rewards/src/lib.rs | 4 ++- .../bridges/bridge-hub-westend/src/lib.rs | 2 ++ .../bridges/bridge-hub-westend/Cargo.toml | 1 + .../src/tests/snowbridge_v2.rs | 28 +++++++++++++++++++ .../bridge-hubs/bridge-hub-westend/Cargo.toml | 1 + .../src/bridge_to_ethereum_config.rs | 22 ++++++++++++++- .../bridge-hubs/bridge-hub-westend/src/lib.rs | 1 + 11 files changed, 67 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 878f073a3fbd..844a67a6eb1e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2416,6 +2416,7 @@ dependencies = [ "snowbridge-pallet-inbound-queue-fixtures", "snowbridge-pallet-outbound-queue", "snowbridge-pallet-outbound-queue-v2", + "snowbridge-pallet-rewards", "snowbridge-pallet-system", "snowbridge-router-primitives", "sp-core 28.0.0", @@ -2498,6 +2499,7 @@ dependencies = [ "snowbridge-pallet-inbound-queue-v2", "snowbridge-pallet-outbound-queue", "snowbridge-pallet-outbound-queue-v2", + "snowbridge-pallet-rewards", "snowbridge-pallet-system", "snowbridge-router-primitives", "snowbridge-router-primitives-v2", diff --git a/Cargo.toml b/Cargo.toml index dd23722c904f..643b1e448cb2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1221,6 +1221,7 @@ snowbridge-pallet-inbound-queue-v2 = { path = "bridges/snowbridge/pallets/inboun snowbridge-pallet-outbound-queue = { path = "bridges/snowbridge/pallets/outbound-queue", default-features = false } snowbridge-pallet-outbound-queue-v2 = { path = "bridges/snowbridge/pallets/outbound-queue-v2", default-features = false } snowbridge-pallet-system = { path = "bridges/snowbridge/pallets/system", default-features = false } +snowbridge-pallet-rewards = { path = "bridges/snowbridge/pallets/rewards", default-features = false } snowbridge-router-primitives = { path = "bridges/snowbridge/primitives/router", default-features = false } snowbridge-router-primitives-v2 = { path = "bridges/snowbridge/primitives/router-v2", default-features = false } snowbridge-runtime-common = { path = "bridges/snowbridge/runtime/runtime-common", default-features = false } diff --git a/bridges/snowbridge/pallets/inbound-queue-v2/src/lib.rs b/bridges/snowbridge/pallets/inbound-queue-v2/src/lib.rs index 4f5552b89cf1..a6bab90988b9 100644 --- a/bridges/snowbridge/pallets/inbound-queue-v2/src/lib.rs +++ b/bridges/snowbridge/pallets/inbound-queue-v2/src/lib.rs @@ -53,6 +53,7 @@ use snowbridge_core::{ BasicOperatingMode, }; use snowbridge_router_primitives_v2::inbound::Message as MessageV2; +use snowbridge_core::rewards::RewardLedger; pub use weights::WeightInfo; @@ -98,6 +99,9 @@ pub mod pallet { #[cfg(feature = "runtime-benchmarks")] type Helper: BenchmarkHelper; + + /// To keep track of relayer rewards. + type RewardLedger: RewardLedger; } #[pallet::hooks] diff --git a/bridges/snowbridge/pallets/outbound-queue-v2/src/lib.rs b/bridges/snowbridge/pallets/outbound-queue-v2/src/lib.rs index 7430c886a699..0d5a4746b5d0 100644 --- a/bridges/snowbridge/pallets/outbound-queue-v2/src/lib.rs +++ b/bridges/snowbridge/pallets/outbound-queue-v2/src/lib.rs @@ -126,6 +126,7 @@ use sp_runtime::{ use sp_std::prelude::*; pub use types::{CommittedMessage, FeeWithBlockNumber, ProcessMessageOriginOf}; pub use weights::WeightInfo; +use snowbridge_core::rewards::RewardLedger; pub use pallet::*; @@ -173,6 +174,8 @@ pub mod pallet { /// Address of the Gateway contract #[pallet::constant] type GatewayAddress: Get; + /// To keep track of relayer rewards. + type RewardLedger: RewardLedger; } #[pallet::event] diff --git a/bridges/snowbridge/pallets/rewards/src/lib.rs b/bridges/snowbridge/pallets/rewards/src/lib.rs index d3786d9e56f9..7090a1a13600 100644 --- a/bridges/snowbridge/pallets/rewards/src/lib.rs +++ b/bridges/snowbridge/pallets/rewards/src/lib.rs @@ -10,6 +10,8 @@ mod mock; #[cfg(test)] mod tests; +extern crate alloc; + use frame_support::PalletError; use frame_system::pallet_prelude::*; use snowbridge_core::rewards::RewardLedger; @@ -137,7 +139,7 @@ pub mod pallet { let beneficiary: Location = Location::new(0, Parachain(T::AssetHubParaId::get().into())); - let xcm: Xcm<()> = vec![ + let xcm: Xcm<()> = alloc::vec![ DepositAsset { assets: Definite(deposit.into()), beneficiary }, SetTopic(message_id.into()), ] diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/src/lib.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/src/lib.rs index e7a28ebf4a46..e92d1c46ccbd 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/src/lib.rs @@ -47,6 +47,8 @@ decl_test_parachains! { PolkadotXcm: bridge_hub_westend_runtime::PolkadotXcm, Balances: bridge_hub_westend_runtime::Balances, EthereumSystem: bridge_hub_westend_runtime::EthereumSystem, + EthereumRewards: bridge_hub_westend_runtime::EthereumRewards, + EthereumInboundQueue: bridge_hub_westend_runtime::EthereumInboundQueue, } }, } diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/Cargo.toml b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/Cargo.toml index d263fc9ac46d..c1e8a98badb3 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/Cargo.toml @@ -51,3 +51,4 @@ snowbridge-pallet-outbound-queue = { workspace = true } snowbridge-pallet-inbound-queue = { workspace = true } snowbridge-pallet-inbound-queue-fixtures = { workspace = true } snowbridge-pallet-outbound-queue-v2 = { workspace = true } +snowbridge-pallet-rewards = { workspace = true } diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2.rs index 961d52810d2f..79240fe1bde9 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2.rs @@ -27,10 +27,12 @@ use snowbridge_router_primitives::inbound::{ use sp_core::H256; use testnet_parachains_constants::westend::snowbridge::EthereumNetwork; use xcm_executor::traits::ConvertLocation; +use snowbridge_core::rewards::RewardLedger; const INITIAL_FUND: u128 = 5_000_000_000_000; pub const CHAIN_ID: u64 = 11155111; pub const WETH: [u8; 20] = hex!("87d1f7fdfEe7f651FaBc8bFCB6E086C278b77A7d"); +pub const ETH: u128 = 1_000_000_000_000_000_000; const ETHEREUM_DESTINATION_ADDRESS: [u8; 20] = hex!("44a57ee2f2FCcb85FDa2B0B18EBD0D8D2333700e"); const XCM_FEE: u128 = 100_000_000_000; const TOKEN_AMOUNT: u128 = 100_000_000_000; @@ -165,3 +167,29 @@ fn send_weth_from_asset_hub_to_ethereum_by_executing_raw_xcm() { ); }); } + +#[test] +fn claim_rewards( +) { + BridgeHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + let relayer = BridgeHubWestendSender::get(); + let reward_address = AssetHubWestendReceiver::get(); + type EthereumRewards = + ::EthereumRewards; + assert_ok!(EthereumRewards::deposit(relayer.into(), 2 * ETH)); + + // Check that the message was sent + assert_expected_events!( + BridgeHubWestend, + vec![ + RuntimeEvent::EthereumRewards(snowbridge_pallet_rewards::Event::RewardDeposited { .. }) => {}, + ] + ); + + type EthereumInboundQueue = + ::EthereumInboundQueue; + let message_id = H256::random(); + }); +} diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/Cargo.toml b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/Cargo.toml index a069b5a9df85..bc94d572f18c 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/Cargo.toml +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/Cargo.toml @@ -110,6 +110,7 @@ snowbridge-core = { workspace = true } snowbridge-pallet-ethereum-client = { workspace = true } snowbridge-pallet-inbound-queue = { workspace = true } snowbridge-pallet-outbound-queue = { workspace = true } +snowbridge-pallet-rewards = { workspace = true } snowbridge-outbound-queue-runtime-api = { workspace = true } snowbridge-router-primitives = { workspace = true } snowbridge-runtime-common = { workspace = true } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs index 605f25a48e69..bb159d7ed078 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs @@ -20,7 +20,7 @@ use crate::{ xcm_config, xcm_config::{TreasuryAccount, UniversalLocation}, Balances, EthereumInboundQueue, EthereumOutboundQueue, EthereumSystem, MessageQueue, Runtime, - RuntimeEvent, TransactionByteFee, + RuntimeEvent, TransactionByteFee, EthereumRewards }; use parachains_common::{AccountId, Balance}; use snowbridge_beacon_primitives::{Fork, ForkVersions}; @@ -121,6 +121,7 @@ impl snowbridge_pallet_inbound_queue_v2::Config for Runtime { type GatewayAddress = EthereumGatewayAddress; #[cfg(feature = "runtime-benchmarks")] type Helper = Runtime; + type RewardLedger = EthereumRewards; type WeightInfo = crate::weights::snowbridge_pallet_inbound_queue_v2::WeightInfo; } @@ -150,6 +151,7 @@ impl snowbridge_pallet_outbound_queue_v2::Config for Runtime { type WeightToFee = WeightToFee; type Verifier = snowbridge_pallet_ethereum_client::Pallet; type GatewayAddress = EthereumGatewayAddress; + type RewardLedger = EthereumRewards; type WeightInfo = crate::weights::snowbridge_pallet_outbound_queue_v2::WeightInfo; } @@ -228,6 +230,24 @@ impl snowbridge_pallet_system::Config for Runtime { type EthereumLocation = EthereumLocation; } +parameter_types! { + pub WethAddress: H160 = H160(hex_literal::hex!("fff9976782d46cc05630d1f6ebab18b2324d6b14")); +} + +pub const ASSET_HUB_ID: u32 = westend_runtime_constants::system_parachain::ASSET_HUB_ID; + +impl snowbridge_pallet_rewards::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type AssetHubParaId = ConstU32; + type EthereumNetwork = EthereumNetwork; + type WethAddress = WethAddress; + #[cfg(not(feature = "runtime-benchmarks"))] + type XcmSender = XcmRouter; + #[cfg(feature = "runtime-benchmarks")] + type XcmSender = DoNothingRouter; + type WeightInfo = (); // TODO generate weights +} + #[cfg(feature = "runtime-benchmarks")] pub mod benchmark_helpers { use crate::{EthereumBeaconClient, Runtime, RuntimeOrigin}; diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs index d34e37a0ecfa..78d4825bac71 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs @@ -559,6 +559,7 @@ construct_runtime!( EthereumSystem: snowbridge_pallet_system = 83, EthereumInboundQueueV2: snowbridge_pallet_inbound_queue_v2 = 84, EthereumOutboundQueueV2: snowbridge_pallet_outbound_queue_v2 = 85, + EthereumRewards: snowbridge_pallet_rewards = 86, // Message Queue. Importantly, is registered last so that messages are processed after // the `on_initialize` hooks of bridging pallets. From 9d6d13d5e1b9e5b25fc8270970840e18e3a82f2c Mon Sep 17 00:00:00 2001 From: claravanstaden Date: Wed, 23 Oct 2024 19:19:17 +0200 Subject: [PATCH 14/37] adds deposit reward --- bridges/snowbridge/pallets/inbound-queue-v2/src/lib.rs | 6 +++--- .../snowbridge/pallets/outbound-queue-v2/src/lib.rs | 10 ++++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/bridges/snowbridge/pallets/inbound-queue-v2/src/lib.rs b/bridges/snowbridge/pallets/inbound-queue-v2/src/lib.rs index a6bab90988b9..23a73d208dfa 100644 --- a/bridges/snowbridge/pallets/inbound-queue-v2/src/lib.rs +++ b/bridges/snowbridge/pallets/inbound-queue-v2/src/lib.rs @@ -187,7 +187,7 @@ pub mod pallet { #[pallet::call_index(0)] #[pallet::weight(T::WeightInfo::submit())] pub fn submit(origin: OriginFor, message: Message) -> DispatchResult { - let _who = ensure_signed(origin)?; + let who = ensure_signed(origin)?; ensure!(!Self::operating_mode().is_halted(), Error::::Halted); // submit message to verifier for verification @@ -228,8 +228,8 @@ pub mod pallet { Ok(()) })?; - // Todo: Deposit fee(in Ether) to RewardLeger which should cover all of: - // T::RewardLeger::deposit(who, envelope.fee.into())?; + T::RewardLedger::deposit(who, envelope.fee.into())?; + // Fee should cover all of: // a. The submit extrinsic cost on BH // b. The delivery cost to AH // c. The execution cost on AH diff --git a/bridges/snowbridge/pallets/outbound-queue-v2/src/lib.rs b/bridges/snowbridge/pallets/outbound-queue-v2/src/lib.rs index 0d5a4746b5d0..0493865529bd 100644 --- a/bridges/snowbridge/pallets/outbound-queue-v2/src/lib.rs +++ b/bridges/snowbridge/pallets/outbound-queue-v2/src/lib.rs @@ -223,6 +223,8 @@ pub mod pallet { InvalidGateway, /// No pending nonce PendingNonceNotExist, + /// Invalid Relayer Reward Account + InvalidRewardAccount, } /// Messages to be committed in the current block. This storage value is killed in @@ -311,11 +313,11 @@ pub mod pallet { ensure!(T::GatewayAddress::get() == envelope.gateway, Error::::InvalidGateway); let nonce = envelope.nonce; - ensure!(>::contains_key(nonce), Error::::PendingNonceNotExist); - // Todo: Reward relayer - // let locked = >::get(nonce); - // T::RewardLeger::deposit(envelope.reward_address.into(), locked.fee.into())?; + let locked = >::get(nonce).ok_or(Error::::PendingNonceNotExist)?; + let reward_account = T::AccountId::decode(&mut &envelope.reward_address[..]).map_err(|_| Error::::InvalidRewardAccount)?; + + T::RewardLedger::deposit(reward_account, locked.fee.into())?; >::remove(nonce); Self::deposit_event(Event::MessageDeliveryProofReceived { nonce }); From 21392745acfc89c387b571ebc27a77410707ecc3 Mon Sep 17 00:00:00 2001 From: claravanstaden Date: Wed, 23 Oct 2024 19:32:05 +0200 Subject: [PATCH 15/37] tests --- .../src/tests/snowbridge_v2.rs | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2.rs index 79240fe1bde9..57ffc9230ba0 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2.rs @@ -173,12 +173,13 @@ fn claim_rewards( ) { BridgeHubWestend::execute_with(|| { type RuntimeEvent = ::RuntimeEvent; + type RuntimeOrigin = ::RuntimeOrigin; let relayer = BridgeHubWestendSender::get(); let reward_address = AssetHubWestendReceiver::get(); type EthereumRewards = ::EthereumRewards; - assert_ok!(EthereumRewards::deposit(relayer.into(), 2 * ETH)); + assert_ok!(EthereumRewards::deposit(relayer.clone().into(), 2 * ETH)); // Check that the message was sent assert_expected_events!( @@ -188,8 +189,21 @@ fn claim_rewards( ] ); - type EthereumInboundQueue = - ::EthereumInboundQueue; let message_id = H256::random(); + let result = EthereumRewards::claim(RuntimeOrigin::signed(relayer.clone()), reward_address.clone(), ETH, message_id); + assert_ok!(result); + + let events = BridgeHubWestend::events(); + assert!( + events.iter().any(|event| matches!( + event, + RuntimeEvent::EthereumRewards(snowbridge_pallet_rewards::Event::RewardClaimed { account_id, deposit_address, value, message_id }) + if *account_id == relayer && *deposit_address == reward_address && *value == ETH, + )), + "RewardClaimed event with correct fields." + ); + + let claim_more_than_available_result = EthereumRewards::claim(RuntimeOrigin::signed(relayer.clone()), reward_address.clone(), 2 * ETH, message_id); + assert_ok!(result); }); } From a6ec370ee332db3912a2dae753170602fedbfb26 Mon Sep 17 00:00:00 2001 From: claravanstaden Date: Wed, 23 Oct 2024 19:59:45 +0200 Subject: [PATCH 16/37] rewards progress --- bridges/snowbridge/pallets/rewards/src/lib.rs | 14 ++++++ .../src/tests/snowbridge_v2.rs | 46 +++++++++++++++++-- 2 files changed, 56 insertions(+), 4 deletions(-) diff --git a/bridges/snowbridge/pallets/rewards/src/lib.rs b/bridges/snowbridge/pallets/rewards/src/lib.rs index 7090a1a13600..afaf11028fc0 100644 --- a/bridges/snowbridge/pallets/rewards/src/lib.rs +++ b/bridges/snowbridge/pallets/rewards/src/lib.rs @@ -21,6 +21,8 @@ use xcm::prelude::{send_xcm, SendError as XcmpSendError, *}; pub use pallet::*; +pub const LOG_TARGET: &str = "xcm-rewards"; + pub type AccountIdOf = ::AccountId; #[frame_support::pallet] pub mod pallet { @@ -138,9 +140,21 @@ pub mod pallet { let deposit: Asset = (reward_asset, value).into(); let beneficiary: Location = Location::new(0, Parachain(T::AssetHubParaId::get().into())); + let bridge_location = Location::new(2, GlobalConsensus(T::EthereumNetwork::get())); + + let xcm_fee: u128 = 10_000_000_000; + let asset_hub_fee_asset: Asset = (Location::parent(), xcm_fee).into(); let xcm: Xcm<()> = alloc::vec![ + // Teleport required fees. + ReceiveTeleportedAsset(asset_hub_fee_asset.clone().into()), + // Pay for execution. + BuyExecution { fees: asset_hub_fee_asset, weight_limit: Unlimited }, DepositAsset { assets: Definite(deposit.into()), beneficiary }, + SetAppendix(Xcm(alloc::vec![ + RefundSurplus, + DepositAsset { assets: AllCounted(1).into(), beneficiary: bridge_location }, + ])), SetTopic(message_id.into()), ] .into(); diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2.rs index 57ffc9230ba0..cf5dae66ca45 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2.rs @@ -24,6 +24,7 @@ use snowbridge_core::{outbound::OperatingMode, AssetMetadata, TokenIdOf}; use snowbridge_router_primitives::inbound::{ Command, Destination, GlobalConsensusEthereumConvertsFor, MessageV1, VersionedMessage, }; +use snowbridge_pallet_rewards::Error::InsufficientFunds; use sp_core::H256; use testnet_parachains_constants::westend::snowbridge::EthereumNetwork; use xcm_executor::traits::ConvertLocation; @@ -169,7 +170,7 @@ fn send_weth_from_asset_hub_to_ethereum_by_executing_raw_xcm() { } #[test] -fn claim_rewards( +fn claim_rewards_works( ) { BridgeHubWestend::execute_with(|| { type RuntimeEvent = ::RuntimeEvent; @@ -197,13 +198,50 @@ fn claim_rewards( assert!( events.iter().any(|event| matches!( event, - RuntimeEvent::EthereumRewards(snowbridge_pallet_rewards::Event::RewardClaimed { account_id, deposit_address, value, message_id }) + RuntimeEvent::EthereumRewards(snowbridge_pallet_rewards::Event::RewardClaimed { account_id, deposit_address, value, message_id: _ }) if *account_id == relayer && *deposit_address == reward_address && *value == ETH, )), "RewardClaimed event with correct fields." ); + }); - let claim_more_than_available_result = EthereumRewards::claim(RuntimeOrigin::signed(relayer.clone()), reward_address.clone(), 2 * ETH, message_id); - assert_ok!(result); + AssetHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + type RuntimeOrigin = ::RuntimeOrigin; + assert_expected_events!( + AssetHubWestend, + vec![RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { .. }) => {},] + ); + }) +} + +#[test] +fn claiming_more_than_accrued_rewards_errors( +) { + BridgeHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + type RuntimeOrigin = ::RuntimeOrigin; + + let relayer = BridgeHubWestendSender::get(); + let reward_address = AssetHubWestendReceiver::get(); + type EthereumRewards = + ::EthereumRewards; + assert_ok!(EthereumRewards::deposit(relayer.clone().into(), 2 * ETH)); + + // Check that the message was sent + assert_expected_events!( + BridgeHubWestend, + vec![ + RuntimeEvent::EthereumRewards(snowbridge_pallet_rewards::Event::RewardDeposited { .. }) => {}, + ] + ); + + let message_id = H256::random(); + let result = EthereumRewards::claim(RuntimeOrigin::signed(relayer.clone()), reward_address.clone(), 3 * ETH, message_id); + assert_err!(result, DispatchError::Module(sp_runtime::ModuleError { + index: 86, + error: [1, 0, 0, 0], + message: Some("InsufficientFunds") + })); }); } From ffffb6b4386b3bbf47bec5d33a06f8fce6cd35e5 Mon Sep 17 00:00:00 2001 From: claravanstaden Date: Wed, 23 Oct 2024 20:21:13 +0200 Subject: [PATCH 17/37] test progress --- bridges/snowbridge/pallets/rewards/src/lib.rs | 4 ++ .../src/tests/snowbridge_v2.rs | 64 +++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/bridges/snowbridge/pallets/rewards/src/lib.rs b/bridges/snowbridge/pallets/rewards/src/lib.rs index afaf11028fc0..4aa035577d55 100644 --- a/bridges/snowbridge/pallets/rewards/src/lib.rs +++ b/bridges/snowbridge/pallets/rewards/src/lib.rs @@ -145,11 +145,15 @@ pub mod pallet { let xcm_fee: u128 = 10_000_000_000; let asset_hub_fee_asset: Asset = (Location::parent(), xcm_fee).into(); + // TODO burn teleported DOT let xcm: Xcm<()> = alloc::vec![ // Teleport required fees. ReceiveTeleportedAsset(asset_hub_fee_asset.clone().into()), // Pay for execution. BuyExecution { fees: asset_hub_fee_asset, weight_limit: Unlimited }, + DescendOrigin(PalletInstance(80).into()), + UniversalOrigin(GlobalConsensus(T::EthereumNetwork::get())), + ReserveAssetDeposited(deposit.clone().into()), DepositAsset { assets: Definite(deposit.into()), beneficiary }, SetAppendix(Xcm(alloc::vec![ RefundSurplus, diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2.rs index cf5dae66ca45..97ddcd00494b 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2.rs @@ -203,6 +203,62 @@ fn claim_rewards_works( )), "RewardClaimed event with correct fields." ); + + /* + RuntimeEvent::EthereumRewards( + Event::RewardDeposited { + account_id: d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d (5GrwvaEF...), + value: 2000000000000000000, + }, + ), + RuntimeEvent::XcmpQueue( + Event::XcmpMessageSent { + message_hash: [ + 67, + 163, + 22, + 82, + 218, + 183, + 34, + 222, + 244, + 233, + 106, + 252, + 199, + 81, + 2, + 159, + 116, + 19, + 241, + 135, + 208, + 19, + 23, + 71, + 203, + 196, + 169, + 208, + 235, + 101, + 32, + 59, + ], + }, + ), + RuntimeEvent::EthereumRewards( + Event::RewardClaimed { + account_id: d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d (5GrwvaEF...), + deposit_address: 8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48 (5FHneW46...), + value: 1000000000000000000, + message_id: 0xd053923c8090a1c15ec02ff65beabcb11b1537b5bda1803704776d71f2bfde7d, + }, + ), + + */ }); AssetHubWestend::execute_with(|| { @@ -213,6 +269,14 @@ fn claim_rewards_works( vec![RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { .. }) => {},] ); }) + /* + RuntimeEvent::Balances( + Event::Minted { + who: ce796ae65569a670d0c1cc1ac12515a3ce21b5fbf729d63d7b289baad070139d (5GjRnmh5...), + amount: 2814000000, + }, + ), + */ } #[test] From 1bbf90dbf6e2ece14b2a4eb56458da738cdbf471 Mon Sep 17 00:00:00 2001 From: claravanstaden Date: Thu, 24 Oct 2024 09:56:21 +0200 Subject: [PATCH 18/37] fix test --- .../src/tests/snowbridge_v2.rs | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2.rs index 97ddcd00494b..11c0c2b77417 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2.rs @@ -170,8 +170,31 @@ fn send_weth_from_asset_hub_to_ethereum_by_executing_raw_xcm() { } #[test] -fn claim_rewards_works( -) { +fn claim_rewards_works() { + let weth: [u8; 20] = hex!("fff9976782d46cc05630d1f6ebab18b2324d6b14"); + let assethub_location = BridgeHubWestend::sibling_location_of(AssetHubWestend::para_id()); + let assethub_sovereign = BridgeHubWestend::sovereign_account_id_of(assethub_location); + let weth_asset_location: Location = + (Parent, Parent, EthereumNetwork::get(), AccountKey20 { network: None, key: weth }).into(); + + BridgeHubWestend::fund_accounts(vec![(assethub_sovereign.clone(), INITIAL_FUND)]); + + AssetHubWestend::execute_with(|| { + type RuntimeOrigin = ::RuntimeOrigin; + + assert_ok!(::ForeignAssets::force_create( + RuntimeOrigin::root(), + weth_asset_location.clone().try_into().unwrap(), + assethub_sovereign.clone().into(), + true, + 1, + )); + + assert!(::ForeignAssets::asset_exists( + weth_asset_location.clone().try_into().unwrap(), + )); + }); + BridgeHubWestend::execute_with(|| { type RuntimeEvent = ::RuntimeEvent; type RuntimeOrigin = ::RuntimeOrigin; From dd5ad4a259fa3fcd9efa798d11a5535e6595657f Mon Sep 17 00:00:00 2001 From: claravanstaden Date: Thu, 24 Oct 2024 13:08:06 +0200 Subject: [PATCH 19/37] use BalanceOf --- .../pallets/inbound-queue-v2/src/lib.rs | 15 +++++-- .../pallets/outbound-queue-v2/src/lib.rs | 16 +++++-- bridges/snowbridge/pallets/rewards/src/lib.rs | 39 +++++++++++------ .../snowbridge/pallets/rewards/src/mock.rs | 1 - .../snowbridge/pallets/rewards/src/tests.rs | 43 ++++++++++++------- .../snowbridge/primitives/core/src/rewards.rs | 11 +++-- .../primitives/router-v2/src/inbound/mod.rs | 2 +- .../src/bridge_to_ethereum_config.rs | 4 ++ 8 files changed, 90 insertions(+), 41 deletions(-) diff --git a/bridges/snowbridge/pallets/inbound-queue-v2/src/lib.rs b/bridges/snowbridge/pallets/inbound-queue-v2/src/lib.rs index 23a73d208dfa..e6d9f3325db1 100644 --- a/bridges/snowbridge/pallets/inbound-queue-v2/src/lib.rs +++ b/bridges/snowbridge/pallets/inbound-queue-v2/src/lib.rs @@ -48,12 +48,13 @@ use xcm::{ VersionedXcm, MAX_XCM_DECODE_DEPTH, }; +use frame_support::traits::fungible::{Inspect, Mutate}; use snowbridge_core::{ inbound::{Message, VerificationError, Verifier}, + rewards::RewardLedger, BasicOperatingMode, }; use snowbridge_router_primitives_v2::inbound::Message as MessageV2; -use snowbridge_core::rewards::RewardLedger; pub use weights::WeightInfo; @@ -61,6 +62,9 @@ pub use weights::WeightInfo; use snowbridge_beacon_primitives::BeaconHeader; pub use pallet::*; +pub type AccountIdOf = ::AccountId; +type BalanceOf = + <::Token as Inspect<::AccountId>>::Balance; pub const LOG_TARGET: &str = "snowbridge-inbound-queue:v2"; @@ -71,7 +75,6 @@ pub mod pallet { use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; - use sp_core::H256; #[pallet::pallet] pub struct Pallet(_); @@ -87,6 +90,8 @@ pub mod pallet { /// The verifier for inbound messages from Ethereum type Verifier: Verifier; + /// Message relayers are rewarded with this asset + type Token: Mutate + Inspect; /// XCM message sender type XcmSender: SendXcm; @@ -101,7 +106,7 @@ pub mod pallet { type Helper: BenchmarkHelper; /// To keep track of relayer rewards. - type RewardLedger: RewardLedger; + type RewardLedger: RewardLedger>; } #[pallet::hooks] @@ -143,6 +148,7 @@ pub mod pallet { Verification(VerificationError), /// XCMP send failure Send(SendError), + InvalidFee, } #[derive(Clone, Encode, Decode, Eq, PartialEq, Debug, TypeInfo, PalletError)] @@ -228,7 +234,8 @@ pub mod pallet { Ok(()) })?; - T::RewardLedger::deposit(who, envelope.fee.into())?; + let fee: BalanceOf = envelope.fee.try_into().map_err(|_| >::InvalidFee)?; + T::RewardLedger::deposit(who, fee)?; // Fee should cover all of: // a. The submit extrinsic cost on BH // b. The delivery cost to AH diff --git a/bridges/snowbridge/pallets/outbound-queue-v2/src/lib.rs b/bridges/snowbridge/pallets/outbound-queue-v2/src/lib.rs index 0493865529bd..7f27e74f4bef 100644 --- a/bridges/snowbridge/pallets/outbound-queue-v2/src/lib.rs +++ b/bridges/snowbridge/pallets/outbound-queue-v2/src/lib.rs @@ -115,6 +115,7 @@ use frame_support::{ use snowbridge_core::{ inbound::Message as DeliveryMessage, outbound::v2::{CommandWrapper, Fee, GasMeter, Message}, + rewards::RewardLedger, BasicOperatingMode, }; use snowbridge_merkle_tree::merkle_root; @@ -126,10 +127,12 @@ use sp_runtime::{ use sp_std::prelude::*; pub use types::{CommittedMessage, FeeWithBlockNumber, ProcessMessageOriginOf}; pub use weights::WeightInfo; -use snowbridge_core::rewards::RewardLedger; +use frame_support::traits::fungible::{Inspect, Mutate}; pub use pallet::*; +type BalanceOf = + <::Token as Inspect<::AccountId>>::Balance; #[frame_support::pallet] pub mod pallet { use super::*; @@ -146,6 +149,8 @@ pub mod pallet { type RuntimeEvent: From> + IsType<::RuntimeEvent>; type Hashing: Hash; + /// Message relayers are rewarded with this asset + type Token: Mutate + Inspect; type MessageQueue: EnqueueMessage; @@ -175,7 +180,7 @@ pub mod pallet { #[pallet::constant] type GatewayAddress: Get; /// To keep track of relayer rewards. - type RewardLedger: RewardLedger; + type RewardLedger: RewardLedger>; } #[pallet::event] @@ -225,6 +230,7 @@ pub mod pallet { PendingNonceNotExist, /// Invalid Relayer Reward Account InvalidRewardAccount, + InvalidFee, } /// Messages to be committed in the current block. This storage value is killed in @@ -315,9 +321,11 @@ pub mod pallet { let nonce = envelope.nonce; let locked = >::get(nonce).ok_or(Error::::PendingNonceNotExist)?; - let reward_account = T::AccountId::decode(&mut &envelope.reward_address[..]).map_err(|_| Error::::InvalidRewardAccount)?; + let reward_account = T::AccountId::decode(&mut &envelope.reward_address[..]) + .map_err(|_| Error::::InvalidRewardAccount)?; - T::RewardLedger::deposit(reward_account, locked.fee.into())?; + let amount: BalanceOf = locked.fee.try_into().map_err(|_| Error::::InvalidFee)?; + T::RewardLedger::deposit(reward_account, amount)?; >::remove(nonce); Self::deposit_event(Event::MessageDeliveryProofReceived { nonce }); diff --git a/bridges/snowbridge/pallets/rewards/src/lib.rs b/bridges/snowbridge/pallets/rewards/src/lib.rs index 4aa035577d55..9f7f8bee04e4 100644 --- a/bridges/snowbridge/pallets/rewards/src/lib.rs +++ b/bridges/snowbridge/pallets/rewards/src/lib.rs @@ -12,18 +12,23 @@ mod tests; extern crate alloc; -use frame_support::PalletError; +use frame_support::{ + sp_runtime::Saturating, + traits::fungible::{Inspect, Mutate}, + PalletError, +}; use frame_system::pallet_prelude::*; +pub use pallet::*; use snowbridge_core::rewards::RewardLedger; use sp_core::H160; pub use weights::WeightInfo; use xcm::prelude::{send_xcm, SendError as XcmpSendError, *}; - -pub use pallet::*; - -pub const LOG_TARGET: &str = "xcm-rewards"; +use xcm_executor::traits::TransactAsset; +pub const LOG_TARGET: &str = "rewards"; pub type AccountIdOf = ::AccountId; +type BalanceOf = + <::Token as Inspect<::AccountId>>::Balance; #[frame_support::pallet] pub mod pallet { use super::*; @@ -41,6 +46,10 @@ pub mod pallet { type WethAddress: Get; /// XCM message sender type XcmSender: SendXcm; + /// To withdraw and deposit an asset. + type AssetTransactor: TransactAsset; + /// Message relayers are rewarded with this asset + type Token: Mutate + Inspect; type WeightInfo: WeightInfo; } @@ -52,7 +61,7 @@ pub mod pallet { /// The relayer account to which the reward was deposited. account_id: AccountIdOf, /// The reward value. - value: u128, + value: BalanceOf, }, RewardClaimed { /// The relayer account that claimed the reward. @@ -60,7 +69,7 @@ pub mod pallet { /// The address that received the reward on AH. deposit_address: AccountIdOf, /// The claimed reward value. - value: u128, + value: BalanceOf, /// The message ID that was provided, used to track the claim message_id: H256, }, @@ -72,6 +81,7 @@ pub mod pallet { Send(SendError), /// The relayer rewards balance is lower than the claimed amount. InsufficientFunds, + InvalidAmount, } #[derive(Clone, Encode, Decode, Eq, PartialEq, Debug, TypeInfo, PalletError)] @@ -102,7 +112,8 @@ pub mod pallet { } #[pallet::storage] - pub type RewardsMapping = StorageMap<_, Identity, AccountIdOf, u128, ValueQuery>; + pub type RewardsMapping = + StorageMap<_, Identity, AccountIdOf, BalanceOf, ValueQuery>; #[pallet::call] impl Pallet { @@ -111,7 +122,7 @@ pub mod pallet { pub fn claim( origin: OriginFor, deposit_address: AccountIdOf, - value: u128, + value: BalanceOf, message_id: H256, ) -> DispatchResult { let account_id = ensure_signed(origin)?; @@ -124,7 +135,7 @@ pub mod pallet { fn process_claim( account_id: AccountIdOf, deposit_address: AccountIdOf, - value: u128, + value: BalanceOf, message_id: H256, ) -> DispatchResult { // Check if the claim value is equal to or less than the accumulated balance. @@ -137,7 +148,9 @@ pub mod pallet { T::EthereumNetwork::get(), T::WethAddress::get(), ); - let deposit: Asset = (reward_asset, value).into(); + let cost2: u128 = + TryInto::::try_into(value).map_err(|_| Error::::InvalidAmount)?; + let deposit: Asset = (reward_asset, cost2).into(); let beneficiary: Location = Location::new(0, Parachain(T::AssetHubParaId::get().into())); let bridge_location = Location::new(2, GlobalConsensus(T::EthereumNetwork::get())); @@ -181,8 +194,8 @@ pub mod pallet { } } - impl RewardLedger for Pallet { - fn deposit(account_id: AccountIdOf, value: u128) -> DispatchResult { + impl RewardLedger, BalanceOf> for Pallet { + fn deposit(account_id: AccountIdOf, value: BalanceOf) -> DispatchResult { RewardsMapping::::mutate(account_id.clone(), |current_value| { *current_value = current_value.saturating_add(value); }); diff --git a/bridges/snowbridge/pallets/rewards/src/mock.rs b/bridges/snowbridge/pallets/rewards/src/mock.rs index 6b0fd99086e0..10877cd313e7 100644 --- a/bridges/snowbridge/pallets/rewards/src/mock.rs +++ b/bridges/snowbridge/pallets/rewards/src/mock.rs @@ -28,7 +28,6 @@ frame_support::construct_runtime!( pub type Signature = MultiSignature; pub type AccountId = <::Signer as IdentifyAccount>::AccountId; - #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type AccountId = AccountId; diff --git a/bridges/snowbridge/pallets/rewards/src/tests.rs b/bridges/snowbridge/pallets/rewards/src/tests.rs index 276e942cd434..4ea36bb85180 100644 --- a/bridges/snowbridge/pallets/rewards/src/tests.rs +++ b/bridges/snowbridge/pallets/rewards/src/tests.rs @@ -3,14 +3,12 @@ use super::*; use crate::{ - mock::{expect_events, new_tester, AccountId, EthereumRewards, Test, WETH}, + mock::{expect_events, new_tester, AccountId, EthereumRewards, RuntimeOrigin, Test, WETH}, Event as RewardEvent, }; -use frame_support::assert_ok; -use sp_keyring::AccountKeyring as Keyring; -use crate::mock::RuntimeOrigin; +use frame_support::{assert_err, assert_ok}; use sp_core::H256; -use frame_support::assert_err; +use sp_keyring::AccountKeyring as Keyring; #[test] fn test_deposit() { new_tester().execute_with(|| { @@ -42,30 +40,45 @@ fn test_claim() { let relayer: AccountId = Keyring::Bob.into(); let message_id = H256::random(); - let result = - EthereumRewards::claim(RuntimeOrigin::signed(relayer.clone()), relayer.clone(), 3 * WETH, message_id); + let result = EthereumRewards::claim( + RuntimeOrigin::signed(relayer.clone()), + relayer.clone(), + 3 * WETH, + message_id, + ); // No rewards yet assert_err!(result, Error::::InsufficientFunds); // Deposit rewards - let result2 = - EthereumRewards::deposit(relayer.clone(), 3 * WETH); + let result2 = EthereumRewards::deposit(relayer.clone(), 3 * WETH); assert_ok!(result2); // Claim some rewards - let result3 = - EthereumRewards::claim(RuntimeOrigin::signed(relayer.clone()), relayer.clone(), 2 * WETH, message_id); + let result3 = EthereumRewards::claim( + RuntimeOrigin::signed(relayer.clone()), + relayer.clone(), + 2 * WETH, + message_id, + ); assert_ok!(result3); assert_eq!(>::get(relayer.clone()), 1 * WETH); // Claim some rewards than available - let result4 = - EthereumRewards::claim(RuntimeOrigin::signed(relayer.clone()), relayer.clone(), 2 * WETH, message_id); + let result4 = EthereumRewards::claim( + RuntimeOrigin::signed(relayer.clone()), + relayer.clone(), + 2 * WETH, + message_id, + ); assert_err!(result4, Error::::InsufficientFunds); // Claim the remaining balance - let result5 = - EthereumRewards::claim(RuntimeOrigin::signed(relayer.clone()), relayer.clone(), 1 * WETH, message_id); + let result5 = EthereumRewards::claim( + RuntimeOrigin::signed(relayer.clone()), + relayer.clone(), + 1 * WETH, + message_id, + ); assert_ok!(result5); assert_eq!(>::get(relayer.clone()), 0); }); diff --git a/bridges/snowbridge/primitives/core/src/rewards.rs b/bridges/snowbridge/primitives/core/src/rewards.rs index a46f0261c79c..80e0d9b492d8 100644 --- a/bridges/snowbridge/primitives/core/src/rewards.rs +++ b/bridges/snowbridge/primitives/core/src/rewards.rs @@ -2,9 +2,14 @@ // SPDX-FileCopyrightText: 2023 Snowfork use frame_support::pallet_prelude::DispatchResult; -pub type AccountIdOf = ::AccountId; -pub trait RewardLedger { +pub trait RewardLedger { // Deposit reward which can later be claimed by `account` - fn deposit(account: AccountIdOf, value: u128) -> DispatchResult; + fn deposit(account: AccountId, value: Balance) -> DispatchResult; +} + +impl RewardLedger for () { + fn deposit(_: AccountId, _: Balance) -> DispatchResult { + Ok(()) + } } diff --git a/bridges/snowbridge/primitives/router-v2/src/inbound/mod.rs b/bridges/snowbridge/primitives/router-v2/src/inbound/mod.rs index 5696f099d6b1..0feee09191a3 100644 --- a/bridges/snowbridge/primitives/router-v2/src/inbound/mod.rs +++ b/bridges/snowbridge/primitives/router-v2/src/inbound/mod.rs @@ -14,7 +14,7 @@ use sp_core::{Get, RuntimeDebug, H160, H256}; use sp_io::hashing::blake2_256; use sp_runtime::{traits::MaybeEquivalence, MultiAddress}; use sp_std::prelude::*; -use xcm::prelude::{Junction::AccountKey20, *}; +use xcm::prelude::*; use xcm_executor::traits::ConvertLocation; const MINIMUM_DEPOSIT: u128 = 1; diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs index bb159d7ed078..20ac44ac49de 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs @@ -122,6 +122,7 @@ impl snowbridge_pallet_inbound_queue_v2::Config for Runtime { #[cfg(feature = "runtime-benchmarks")] type Helper = Runtime; type RewardLedger = EthereumRewards; + type Token = Balances; type WeightInfo = crate::weights::snowbridge_pallet_inbound_queue_v2::WeightInfo; } @@ -152,6 +153,7 @@ impl snowbridge_pallet_outbound_queue_v2::Config for Runtime { type Verifier = snowbridge_pallet_ethereum_client::Pallet; type GatewayAddress = EthereumGatewayAddress; type RewardLedger = EthereumRewards; + type Token = Balances; type WeightInfo = crate::weights::snowbridge_pallet_outbound_queue_v2::WeightInfo; } @@ -246,6 +248,8 @@ impl snowbridge_pallet_rewards::Config for Runtime { #[cfg(feature = "runtime-benchmarks")] type XcmSender = DoNothingRouter; type WeightInfo = (); // TODO generate weights + type Token = Balances; + type AssetTransactor = ::AssetTransactor; } #[cfg(feature = "runtime-benchmarks")] From 496d89efe1cd5877c69d892a9725a5c610bb995a Mon Sep 17 00:00:00 2001 From: claravanstaden Date: Thu, 24 Oct 2024 13:56:55 +0200 Subject: [PATCH 20/37] burn fees --- bridges/snowbridge/pallets/rewards/src/lib.rs | 36 ++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/bridges/snowbridge/pallets/rewards/src/lib.rs b/bridges/snowbridge/pallets/rewards/src/lib.rs index 9f7f8bee04e4..db3ff115b62e 100644 --- a/bridges/snowbridge/pallets/rewards/src/lib.rs +++ b/bridges/snowbridge/pallets/rewards/src/lib.rs @@ -13,14 +13,15 @@ mod tests; extern crate alloc; use frame_support::{ - sp_runtime::Saturating, + sp_runtime::{SaturatedConversion, Saturating}, traits::fungible::{Inspect, Mutate}, PalletError, }; use frame_system::pallet_prelude::*; pub use pallet::*; -use snowbridge_core::rewards::RewardLedger; +use snowbridge_core::{rewards::RewardLedger, ParaId}; use sp_core::H160; +use sp_runtime::TokenError; pub use weights::WeightInfo; use xcm::prelude::{send_xcm, SendError as XcmpSendError, *}; use xcm_executor::traits::TransactAsset; @@ -82,6 +83,7 @@ pub mod pallet { /// The relayer rewards balance is lower than the claimed amount. InsufficientFunds, InvalidAmount, + InvalidFee, } #[derive(Clone, Encode, Decode, Eq, PartialEq, Debug, TypeInfo, PalletError)] @@ -155,10 +157,12 @@ pub mod pallet { Location::new(0, Parachain(T::AssetHubParaId::get().into())); let bridge_location = Location::new(2, GlobalConsensus(T::EthereumNetwork::get())); - let xcm_fee: u128 = 10_000_000_000; + let xcm_fee: u128 = 10_000_000_000; // TODO not sure what this should be let asset_hub_fee_asset: Asset = (Location::parent(), xcm_fee).into(); - // TODO burn teleported DOT + let fee: BalanceOf = xcm_fee.try_into().map_err(|_| Error::::InvalidFee)?; + Self::burn_fees(T::AssetHubParaId::get().into(), fee)?; + let xcm: Xcm<()> = alloc::vec![ // Teleport required fees. ReceiveTeleportedAsset(asset_hub_fee_asset.clone().into()), @@ -192,6 +196,30 @@ pub mod pallet { }); Ok(()) } + + /// Burn the amount of the fee embedded into the XCM for teleports + pub fn burn_fees(para_id: ParaId, fee: BalanceOf) -> DispatchResult { + let dummy_context = + XcmContext { origin: None, message_id: Default::default(), topic: None }; + let dest = Location::new(1, [Parachain(para_id.into())]); + let fees = (Location::parent(), fee.saturated_into::()).into(); + T::AssetTransactor::can_check_out(&dest, &fees, &dummy_context).map_err(|error| { + log::error!( + target: LOG_TARGET, + "XCM asset check out failed with error {:?}", error + ); + TokenError::FundsUnavailable + })?; + T::AssetTransactor::check_out(&dest, &fees, &dummy_context); + T::AssetTransactor::withdraw_asset(&fees, &dest, None).map_err(|error| { + log::error!( + target: LOG_TARGET, + "XCM asset withdraw failed with error {:?}", error + ); + TokenError::FundsUnavailable + })?; + Ok(()) + } } impl RewardLedger, BalanceOf> for Pallet { From 8d049b7b41864e425aa91b7fe6cec0cdb4b61341 Mon Sep 17 00:00:00 2001 From: claravanstaden Date: Thu, 24 Oct 2024 15:04:02 +0200 Subject: [PATCH 21/37] todo --- bridges/snowbridge/pallets/rewards/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/bridges/snowbridge/pallets/rewards/src/lib.rs b/bridges/snowbridge/pallets/rewards/src/lib.rs index db3ff115b62e..1975d171b5d5 100644 --- a/bridges/snowbridge/pallets/rewards/src/lib.rs +++ b/bridges/snowbridge/pallets/rewards/src/lib.rs @@ -198,6 +198,7 @@ pub mod pallet { } /// Burn the amount of the fee embedded into the XCM for teleports + /// Todo refactor out pub fn burn_fees(para_id: ParaId, fee: BalanceOf) -> DispatchResult { let dummy_context = XcmContext { origin: None, message_id: Default::default(), topic: None }; From 6487068292cb789952617d8d12d2ce4dd960b5f2 Mon Sep 17 00:00:00 2001 From: claravanstaden Date: Thu, 24 Oct 2024 16:08:34 +0200 Subject: [PATCH 22/37] cleanup --- Cargo.lock | 1 + .../pallets/inbound-queue/src/lib.rs | 31 +------- bridges/snowbridge/pallets/rewards/src/lib.rs | 32 +------- bridges/snowbridge/primitives/core/Cargo.toml | 5 +- .../snowbridge/primitives/core/src/fees.rs | 44 +++++++++++ bridges/snowbridge/primitives/core/src/lib.rs | 1 + .../src/tests/snowbridge.rs | 1 - .../src/tests/snowbridge_v2.rs | 76 +------------------ 8 files changed, 58 insertions(+), 133 deletions(-) create mode 100644 bridges/snowbridge/primitives/core/src/fees.rs diff --git a/Cargo.lock b/Cargo.lock index 844a67a6eb1e..0623c659bc00 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20894,6 +20894,7 @@ dependencies = [ "frame-system", "hex", "hex-literal", + "log", "parity-scale-codec", "polkadot-parachain-primitives", "scale-info", diff --git a/bridges/snowbridge/pallets/inbound-queue/src/lib.rs b/bridges/snowbridge/pallets/inbound-queue/src/lib.rs index 423b92b9fae0..f13c46ab5a78 100644 --- a/bridges/snowbridge/pallets/inbound-queue/src/lib.rs +++ b/bridges/snowbridge/pallets/inbound-queue/src/lib.rs @@ -52,11 +52,12 @@ use sp_core::H160; use sp_runtime::traits::Zero; use sp_std::vec; use xcm::prelude::{ - send_xcm, Junction::*, Location, SendError as XcmpSendError, SendXcm, Xcm, XcmContext, XcmHash, + send_xcm, Junction::*, Location, SendError as XcmpSendError, SendXcm, Xcm, XcmHash, }; use xcm_executor::traits::TransactAsset; use snowbridge_core::{ + fees::burn_fees, inbound::{Message, VerificationError, Verifier}, sibling_sovereign_account, BasicOperatingMode, Channel, ChannelId, ParaId, PricingParameters, StaticLookup, @@ -64,7 +65,7 @@ use snowbridge_core::{ use snowbridge_router_primitives::inbound::{ ConvertMessage, ConvertMessageError, VersionedMessage, }; -use sp_runtime::{traits::Saturating, SaturatedConversion, TokenError}; +use sp_runtime::Saturating; pub use weights::WeightInfo; @@ -290,7 +291,7 @@ pub mod pallet { ); // Burning fees for teleport - Self::burn_fees(channel.para_id, fee)?; + burn_fees::>(channel.para_id, fee)?; // Attempt to send XCM to a dest parachain let message_id = Self::send_xcm(xcm, channel.para_id)?; @@ -342,30 +343,6 @@ pub mod pallet { .saturating_add(len_fee) .saturating_add(T::PricingParameters::get().rewards.local) } - - /// Burn the amount of the fee embedded into the XCM for teleports - pub fn burn_fees(para_id: ParaId, fee: BalanceOf) -> DispatchResult { - let dummy_context = - XcmContext { origin: None, message_id: Default::default(), topic: None }; - let dest = Location::new(1, [Parachain(para_id.into())]); - let fees = (Location::parent(), fee.saturated_into::()).into(); - T::AssetTransactor::can_check_out(&dest, &fees, &dummy_context).map_err(|error| { - log::error!( - target: LOG_TARGET, - "XCM asset check out failed with error {:?}", error - ); - TokenError::FundsUnavailable - })?; - T::AssetTransactor::check_out(&dest, &fees, &dummy_context); - T::AssetTransactor::withdraw_asset(&fees, &dest, None).map_err(|error| { - log::error!( - target: LOG_TARGET, - "XCM asset withdraw failed with error {:?}", error - ); - TokenError::FundsUnavailable - })?; - Ok(()) - } } /// API for accessing the delivery cost of a message diff --git a/bridges/snowbridge/pallets/rewards/src/lib.rs b/bridges/snowbridge/pallets/rewards/src/lib.rs index 1975d171b5d5..e9bf4bbf88b1 100644 --- a/bridges/snowbridge/pallets/rewards/src/lib.rs +++ b/bridges/snowbridge/pallets/rewards/src/lib.rs @@ -13,15 +13,14 @@ mod tests; extern crate alloc; use frame_support::{ - sp_runtime::{SaturatedConversion, Saturating}, + sp_runtime::Saturating, traits::fungible::{Inspect, Mutate}, PalletError, }; use frame_system::pallet_prelude::*; pub use pallet::*; -use snowbridge_core::{rewards::RewardLedger, ParaId}; +use snowbridge_core::{fees::burn_fees, rewards::RewardLedger}; use sp_core::H160; -use sp_runtime::TokenError; pub use weights::WeightInfo; use xcm::prelude::{send_xcm, SendError as XcmpSendError, *}; use xcm_executor::traits::TransactAsset; @@ -161,7 +160,7 @@ pub mod pallet { let asset_hub_fee_asset: Asset = (Location::parent(), xcm_fee).into(); let fee: BalanceOf = xcm_fee.try_into().map_err(|_| Error::::InvalidFee)?; - Self::burn_fees(T::AssetHubParaId::get().into(), fee)?; + burn_fees::>(T::AssetHubParaId::get().into(), fee)?; let xcm: Xcm<()> = alloc::vec![ // Teleport required fees. @@ -196,31 +195,6 @@ pub mod pallet { }); Ok(()) } - - /// Burn the amount of the fee embedded into the XCM for teleports - /// Todo refactor out - pub fn burn_fees(para_id: ParaId, fee: BalanceOf) -> DispatchResult { - let dummy_context = - XcmContext { origin: None, message_id: Default::default(), topic: None }; - let dest = Location::new(1, [Parachain(para_id.into())]); - let fees = (Location::parent(), fee.saturated_into::()).into(); - T::AssetTransactor::can_check_out(&dest, &fees, &dummy_context).map_err(|error| { - log::error!( - target: LOG_TARGET, - "XCM asset check out failed with error {:?}", error - ); - TokenError::FundsUnavailable - })?; - T::AssetTransactor::check_out(&dest, &fees, &dummy_context); - T::AssetTransactor::withdraw_asset(&fees, &dest, None).map_err(|error| { - log::error!( - target: LOG_TARGET, - "XCM asset withdraw failed with error {:?}", error - ); - TokenError::FundsUnavailable - })?; - Ok(()) - } } impl RewardLedger, BalanceOf> for Pallet { diff --git a/bridges/snowbridge/primitives/core/Cargo.toml b/bridges/snowbridge/primitives/core/Cargo.toml index fa37c795b2d1..8bd2107f95a7 100644 --- a/bridges/snowbridge/primitives/core/Cargo.toml +++ b/bridges/snowbridge/primitives/core/Cargo.toml @@ -16,6 +16,7 @@ serde = { optional = true, features = ["alloc", "derive"], workspace = true } codec = { workspace = true } scale-info = { features = ["derive"], workspace = true } hex-literal = { workspace = true, default-features = true } +log = { workspace = true } polkadot-parachain-primitives = { workspace = true } xcm = { workspace = true } @@ -32,10 +33,10 @@ sp-arithmetic = { workspace = true } snowbridge-beacon-primitives = { workspace = true } ethabi = { workspace = true } +xcm-executor = { workspace = true, default-features = false } [dev-dependencies] hex = { workspace = true, default-features = true } -xcm-executor = { workspace = true, default-features = true } [features] default = ["std"] @@ -44,6 +45,7 @@ std = [ "ethabi/std", "frame-support/std", "frame-system/std", + "log/std", "polkadot-parachain-primitives/std", "scale-info/std", "serde/std", @@ -55,6 +57,7 @@ std = [ "sp-std/std", "xcm-builder/std", "xcm/std", + "xcm-executor/std", ] serde = ["dep:serde", "scale-info/serde"] runtime-benchmarks = [ diff --git a/bridges/snowbridge/primitives/core/src/fees.rs b/bridges/snowbridge/primitives/core/src/fees.rs new file mode 100644 index 000000000000..3b122cc32bda --- /dev/null +++ b/bridges/snowbridge/primitives/core/src/fees.rs @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork +use crate::ParaId; +use log; +use sp_runtime::{DispatchResult, SaturatedConversion, Saturating, TokenError}; +use xcm::opaque::lts::{Junction::Parachain, Location, XcmContext}; +use xcm_executor::traits::TransactAsset; +const LOG_TARGET: &str = "xcm_fees"; + +/// Burns the fees embedded in the XCM for teleports. +pub fn burn_fees(para_id: ParaId, fee: Balance) -> DispatchResult +where + AssetTransactor: TransactAsset, + Balance: Saturating + TryInto + Copy, +{ + let dummy_context = XcmContext { origin: None, message_id: Default::default(), topic: None }; + let dest = Location::new(1, [Parachain(para_id.into())]); + let fees = (Location::parent(), fee.saturated_into::()).into(); + + // Check if the asset can be checked out + AssetTransactor::can_check_out(&dest, &fees, &dummy_context).map_err(|error| { + log::error!( + target: LOG_TARGET, + "XCM asset check out failed with error {:?}", + error + ); + TokenError::FundsUnavailable + })?; + + // Check out the asset + AssetTransactor::check_out(&dest, &fees, &dummy_context); + + // Withdraw the asset and handle potential errors + AssetTransactor::withdraw_asset(&fees, &dest, None).map_err(|error| { + log::error!( + target: LOG_TARGET, + "XCM asset withdraw failed with error {:?}", + error + ); + TokenError::FundsUnavailable + })?; + + Ok(()) +} diff --git a/bridges/snowbridge/primitives/core/src/lib.rs b/bridges/snowbridge/primitives/core/src/lib.rs index 94ef1c3ba900..39ff6d7e5900 100644 --- a/bridges/snowbridge/primitives/core/src/lib.rs +++ b/bridges/snowbridge/primitives/core/src/lib.rs @@ -8,6 +8,7 @@ #[cfg(test)] mod tests; +pub mod fees; pub mod inbound; pub mod location; pub mod operating_mode; diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs index 3bbf0f2fa62f..bae4f1077127 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs @@ -253,7 +253,6 @@ fn send_weth_asset_from_asset_hub_to_ethereum() { }); BridgeHubWestend::execute_with(|| { - use bridge_hub_westend_runtime::xcm_config::TreasuryAccount; type RuntimeEvent = ::RuntimeEvent; // Check that the transfer token back to Ethereum message was queue in the Ethereum // Outbound Queue diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2.rs index 11c0c2b77417..449bfd913602 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2.rs @@ -13,21 +13,13 @@ // See the License for the specific language governing permissions and // limitations under the License. use crate::imports::*; -use asset_hub_westend_runtime::xcm_config::bridging::to_ethereum::DefaultBridgeHubEthereumBaseFee; use bridge_hub_westend_runtime::EthereumInboundQueue; -use codec::{Decode, Encode}; -use emulated_integration_tests_common::RESERVABLE_ASSET_ID; -use frame_support::pallet_prelude::TypeInfo; use hex_literal::hex; -use rococo_westend_system_emulated_network::asset_hub_westend_emulated_chain::genesis::AssetHubWestendAssetOwner; -use snowbridge_core::{outbound::OperatingMode, AssetMetadata, TokenIdOf}; use snowbridge_router_primitives::inbound::{ - Command, Destination, GlobalConsensusEthereumConvertsFor, MessageV1, VersionedMessage, + Command, Destination, MessageV1, VersionedMessage, }; -use snowbridge_pallet_rewards::Error::InsufficientFunds; use sp_core::H256; use testnet_parachains_constants::westend::snowbridge::EthereumNetwork; -use xcm_executor::traits::ConvertLocation; use snowbridge_core::rewards::RewardLedger; const INITIAL_FUND: u128 = 5_000_000_000_000; @@ -148,7 +140,6 @@ fn send_weth_from_asset_hub_to_ethereum_by_executing_raw_xcm() { }); BridgeHubWestend::execute_with(|| { - use bridge_hub_westend_runtime::xcm_config::TreasuryAccount; type RuntimeEvent = ::RuntimeEvent; // Check that the transfer token back to Ethereum message was queue in the Ethereum // Outbound Queue @@ -226,80 +217,15 @@ fn claim_rewards_works() { )), "RewardClaimed event with correct fields." ); - - /* - RuntimeEvent::EthereumRewards( - Event::RewardDeposited { - account_id: d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d (5GrwvaEF...), - value: 2000000000000000000, - }, - ), - RuntimeEvent::XcmpQueue( - Event::XcmpMessageSent { - message_hash: [ - 67, - 163, - 22, - 82, - 218, - 183, - 34, - 222, - 244, - 233, - 106, - 252, - 199, - 81, - 2, - 159, - 116, - 19, - 241, - 135, - 208, - 19, - 23, - 71, - 203, - 196, - 169, - 208, - 235, - 101, - 32, - 59, - ], - }, - ), - RuntimeEvent::EthereumRewards( - Event::RewardClaimed { - account_id: d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d (5GrwvaEF...), - deposit_address: 8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48 (5FHneW46...), - value: 1000000000000000000, - message_id: 0xd053923c8090a1c15ec02ff65beabcb11b1537b5bda1803704776d71f2bfde7d, - }, - ), - - */ }); AssetHubWestend::execute_with(|| { type RuntimeEvent = ::RuntimeEvent; - type RuntimeOrigin = ::RuntimeOrigin; assert_expected_events!( AssetHubWestend, vec![RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { .. }) => {},] ); }) - /* - RuntimeEvent::Balances( - Event::Minted { - who: ce796ae65569a670d0c1cc1ac12515a3ce21b5fbf729d63d7b289baad070139d (5GjRnmh5...), - amount: 2814000000, - }, - ), - */ } #[test] From 0e9cca01a38644312ff4285f06c4536f5a9eabc4 Mon Sep 17 00:00:00 2001 From: ron Date: Fri, 25 Oct 2024 00:12:35 +0800 Subject: [PATCH 23/37] Rebase inbound queue --- .../inbound-queue-v2/src/benchmarking/mod.rs | 15 - .../pallets/inbound-queue-v2/src/envelope.rs | 15 +- .../pallets/inbound-queue-v2/src/lib.rs | 203 ++------- .../pallets/inbound-queue-v2/src/mock.rs | 155 ++----- .../pallets/inbound-queue-v2/src/test.rs | 53 --- .../primitives/router/src/inbound/v2.rs | 430 +----------------- .../src/bridge_to_ethereum_config.rs | 19 +- 7 files changed, 101 insertions(+), 789 deletions(-) diff --git a/bridges/snowbridge/pallets/inbound-queue-v2/src/benchmarking/mod.rs b/bridges/snowbridge/pallets/inbound-queue-v2/src/benchmarking/mod.rs index 52461a8a7fbe..4c5df07b27ac 100644 --- a/bridges/snowbridge/pallets/inbound-queue-v2/src/benchmarking/mod.rs +++ b/bridges/snowbridge/pallets/inbound-queue-v2/src/benchmarking/mod.rs @@ -23,21 +23,6 @@ mod benchmarks { create_message.block_roots_root, ); - let sovereign_account = sibling_sovereign_account::(1000u32.into()); - - let minimum_balance = T::Token::minimum_balance(); - - // So that the receiving account exists - assert_ok!(T::Token::mint_into(&caller, minimum_balance)); - // Fund the sovereign account (parachain sovereign account) so it can transfer a reward - // fee to the caller account - assert_ok!(T::Token::mint_into( - &sovereign_account, - 3_000_000_000_000u128 - .try_into() - .unwrap_or_else(|_| panic!("unable to cast sovereign account balance")), - )); - #[block] { assert_ok!(InboundQueue::::submit( diff --git a/bridges/snowbridge/pallets/inbound-queue-v2/src/envelope.rs b/bridges/snowbridge/pallets/inbound-queue-v2/src/envelope.rs index 31a8992442d8..8c9b137c64ba 100644 --- a/bridges/snowbridge/pallets/inbound-queue-v2/src/envelope.rs +++ b/bridges/snowbridge/pallets/inbound-queue-v2/src/envelope.rs @@ -1,15 +1,15 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-FileCopyrightText: 2023 Snowfork -use snowbridge_core::{inbound::Log, ChannelId}; +use snowbridge_core::inbound::Log; -use sp_core::{RuntimeDebug, H160, H256}; +use sp_core::{RuntimeDebug, H160}; use sp_std::prelude::*; use alloy_primitives::B256; use alloy_sol_types::{sol, SolEvent}; sol! { - event OutboundMessageAccepted(bytes32 indexed channel_id, uint64 nonce, bytes32 indexed message_id, bytes payload); + event OutboundMessageAccepted(uint64 indexed nonce, uint128 fee, bytes payload); } /// An inbound message that has had its outer envelope decoded. @@ -17,12 +17,10 @@ sol! { pub struct Envelope { /// The address of the outbound queue on Ethereum that emitted this message as an event log pub gateway: H160, - /// The message Channel - pub channel_id: ChannelId, /// A nonce for enforcing replay protection and ordering. pub nonce: u64, - /// An id for tracing the message on its route (has no role in bridge consensus) - pub message_id: H256, + /// Total fee paid in Ether on Ethereum, should cover all the cost + pub fee: u128, /// The inner payload generated from the source application. pub payload: Vec, } @@ -41,9 +39,8 @@ impl TryFrom<&Log> for Envelope { Ok(Self { gateway: log.address, - channel_id: ChannelId::from(event.channel_id.as_ref()), nonce: event.nonce, - message_id: H256::from(event.message_id.as_ref()), + fee: event.fee, payload: event.payload, }) } diff --git a/bridges/snowbridge/pallets/inbound-queue-v2/src/lib.rs b/bridges/snowbridge/pallets/inbound-queue-v2/src/lib.rs index c26859dcf5d7..0eaadcb6a3c3 100644 --- a/bridges/snowbridge/pallets/inbound-queue-v2/src/lib.rs +++ b/bridges/snowbridge/pallets/inbound-queue-v2/src/lib.rs @@ -38,49 +38,35 @@ mod test; use codec::{Decode, DecodeAll, Encode}; use envelope::Envelope; -use frame_support::{ - traits::{ - fungible::{Inspect, Mutate}, - tokens::{Fortitude, Preservation}, - }, - weights::WeightToFee, - PalletError, -}; +use frame_support::PalletError; use frame_system::ensure_signed; use scale_info::TypeInfo; use sp_core::H160; -use sp_runtime::traits::Zero; use sp_std::vec; -use xcm::prelude::{ - send_xcm, Junction::*, Location, SendError as XcmpSendError, SendXcm, Xcm, XcmContext, XcmHash, +use xcm::{ + prelude::{send_xcm, Junction::*, Location, SendError as XcmpSendError, SendXcm, Xcm}, + VersionedXcm, MAX_XCM_DECODE_DEPTH, }; -use xcm_executor::traits::TransactAsset; use snowbridge_core::{ inbound::{Message, VerificationError, Verifier}, - sibling_sovereign_account, BasicOperatingMode, Channel, ChannelId, ParaId, PricingParameters, - StaticLookup, -}; -use snowbridge_router_primitives::inbound::v2::{ - ConvertMessage, ConvertMessageError, VersionedMessage, + BasicOperatingMode, }; -use sp_runtime::{traits::Saturating, SaturatedConversion, TokenError}; +use snowbridge_router_primitives::inbound::v2::Message as MessageV2; pub use weights::WeightInfo; #[cfg(feature = "runtime-benchmarks")] use snowbridge_beacon_primitives::BeaconHeader; -type BalanceOf = - <::Token as Inspect<::AccountId>>::Balance; - pub use pallet::*; -pub const LOG_TARGET: &str = "snowbridge-inbound-queue"; +pub const LOG_TARGET: &str = "snowbridge-inbound-queue:v2"; #[frame_support::pallet] pub mod pallet { use super::*; + use codec::DecodeLimit; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; @@ -101,44 +87,17 @@ pub mod pallet { /// The verifier for inbound messages from Ethereum type Verifier: Verifier; - /// Message relayers are rewarded with this asset - type Token: Mutate + Inspect; - /// XCM message sender type XcmSender: SendXcm; - // Address of the Gateway contract + /// Address of the Gateway contract #[pallet::constant] type GatewayAddress: Get; - /// Convert inbound message to XCM - type MessageConverter: ConvertMessage< - AccountId = Self::AccountId, - Balance = BalanceOf, - >; - - /// Lookup a channel descriptor - type ChannelLookup: StaticLookup; - - /// Lookup pricing parameters - type PricingParameters: Get>>; - type WeightInfo: WeightInfo; #[cfg(feature = "runtime-benchmarks")] type Helper: BenchmarkHelper; - - /// Convert a weight value into deductible balance type. - type WeightToFee: WeightToFee>; - - /// Convert a length value into deductible balance type - type LengthToFee: WeightToFee>; - - /// The upper limit here only used to estimate delivery cost - type MaxMessageSize: Get; - - /// To withdraw and deposit an asset. - type AssetTransactor: TransactAsset; } #[pallet::hooks] @@ -149,14 +108,10 @@ pub mod pallet { pub enum Event { /// A message was received from Ethereum MessageReceived { - /// The message channel - channel_id: ChannelId, /// The message nonce nonce: u64, /// ID of the XCM message which was forwarded to the final destination parachain message_id: [u8; 32], - /// Fee burned for the teleport - fee_burned: BalanceOf, }, /// Set OperatingMode OperatingModeChanged { mode: BasicOperatingMode }, @@ -184,8 +139,6 @@ pub mod pallet { Verification(VerificationError), /// XCMP send failure Send(SendError), - /// Message conversion error - ConvertMessage(ConvertMessageError), } #[derive(Clone, Encode, Decode, Eq, PartialEq, Debug, TypeInfo, PalletError)] @@ -215,9 +168,9 @@ pub mod pallet { } } - /// The current nonce for each channel + /// The nonce of the message been processed or not #[pallet::storage] - pub type Nonce = StorageMap<_, Twox64Concat, ChannelId, u64, ValueQuery>; + pub type Nonce = StorageMap<_, Identity, u64, bool, ValueQuery>; /// The current operating mode of the pallet. #[pallet::storage] @@ -230,7 +183,7 @@ pub mod pallet { #[pallet::call_index(0)] #[pallet::weight(T::WeightInfo::submit())] pub fn submit(origin: OriginFor, message: Message) -> DispatchResult { - let who = ensure_signed(origin)?; + let _who = ensure_signed(origin)?; ensure!(!Self::operating_mode().is_halted(), Error::::Halted); // submit message to verifier for verification @@ -244,63 +197,46 @@ pub mod pallet { // Verify that the message was submitted from the known Gateway contract ensure!(T::GatewayAddress::get() == envelope.gateway, Error::::InvalidGateway); - // Retrieve the registered channel for this message - let channel = - T::ChannelLookup::lookup(envelope.channel_id).ok_or(Error::::InvalidChannel)?; - - // Verify message nonce - >::try_mutate(envelope.channel_id, |nonce| -> DispatchResult { - if *nonce == u64::MAX { - return Err(Error::::MaxNonceReached.into()) - } - if envelope.nonce != nonce.saturating_add(1) { - Err(Error::::InvalidNonce.into()) - } else { - *nonce = nonce.saturating_add(1); - Ok(()) - } - })?; - - // Reward relayer from the sovereign account of the destination parachain, only if funds - // are available - let sovereign_account = sibling_sovereign_account::(channel.para_id); - let delivery_cost = Self::calculate_delivery_cost(message.encode().len() as u32); - let amount = T::Token::reducible_balance( - &sovereign_account, - Preservation::Preserve, - Fortitude::Polite, - ) - .min(delivery_cost); - if !amount.is_zero() { - T::Token::transfer(&sovereign_account, &who, amount, Preservation::Preserve)?; - } + // Verify the message has not been processed + ensure!(!>::contains_key(envelope.nonce), Error::::InvalidNonce); - // Decode payload into `VersionedMessage` - let message = VersionedMessage::decode_all(&mut envelope.payload.as_ref()) + // Decode payload into `MessageV2` + let message = MessageV2::decode_all(&mut envelope.payload.as_ref()) .map_err(|_| Error::::InvalidPayload)?; - // Decode message into XCM - let (xcm, fee) = Self::do_convert(envelope.message_id, message.clone())?; + // Decode xcm + let versioned_xcm = VersionedXcm::<()>::decode_with_depth_limit( + MAX_XCM_DECODE_DEPTH, + &mut message.xcm.as_ref(), + ) + .map_err(|_| Error::::InvalidPayload)?; + let xcm: Xcm<()> = versioned_xcm.try_into().map_err(|_| >::InvalidPayload)?; log::info!( target: LOG_TARGET, - "💫 xcm decoded as {:?} with fee {:?}", + "💫 xcm decoded as {:?}", xcm, - fee ); - // Burning fees for teleport - Self::burn_fees(channel.para_id, fee)?; + // Set nonce flag to true + >::try_mutate(envelope.nonce, |done| -> DispatchResult { + *done = true; + Ok(()) + })?; + + // Todo: Deposit fee(in Ether) to RewardLeger which should cover all of: + // T::RewardLeger::deposit(who, envelope.fee.into())?; + // a. The submit extrinsic cost on BH + // b. The delivery cost to AH + // c. The execution cost on AH + // d. The execution cost on destination chain(if any) + // e. The reward - // Attempt to send XCM to a dest parachain - let message_id = Self::send_xcm(xcm, channel.para_id)?; + // Attempt to forward XCM to AH + let dest = Location::new(1, [Parachain(1000)]); + let (message_id, _) = send_xcm::(dest, xcm).map_err(Error::::from)?; - Self::deposit_event(Event::MessageReceived { - channel_id: envelope.channel_id, - nonce: envelope.nonce, - message_id, - fee_burned: fee, - }); + Self::deposit_event(Event::MessageReceived { nonce: envelope.nonce, message_id }); Ok(()) } @@ -318,61 +254,4 @@ pub mod pallet { Ok(()) } } - - impl Pallet { - pub fn do_convert( - message_id: H256, - message: VersionedMessage, - ) -> Result<(Xcm<()>, BalanceOf), Error> { - let (xcm, fee) = T::MessageConverter::convert(message_id, message) - .map_err(|e| Error::::ConvertMessage(e))?; - Ok((xcm, fee)) - } - - pub fn send_xcm(xcm: Xcm<()>, dest: ParaId) -> Result> { - let dest = Location::new(1, [Parachain(dest.into())]); - let (xcm_hash, _) = send_xcm::(dest, xcm).map_err(Error::::from)?; - Ok(xcm_hash) - } - - pub fn calculate_delivery_cost(length: u32) -> BalanceOf { - let weight_fee = T::WeightToFee::weight_to_fee(&T::WeightInfo::submit()); - let len_fee = T::LengthToFee::weight_to_fee(&Weight::from_parts(length as u64, 0)); - weight_fee - .saturating_add(len_fee) - .saturating_add(T::PricingParameters::get().rewards.local) - } - - /// Burn the amount of the fee embedded into the XCM for teleports - pub fn burn_fees(para_id: ParaId, fee: BalanceOf) -> DispatchResult { - let dummy_context = - XcmContext { origin: None, message_id: Default::default(), topic: None }; - let dest = Location::new(1, [Parachain(para_id.into())]); - let fees = (Location::parent(), fee.saturated_into::()).into(); - T::AssetTransactor::can_check_out(&dest, &fees, &dummy_context).map_err(|error| { - log::error!( - target: LOG_TARGET, - "XCM asset check out failed with error {:?}", error - ); - TokenError::FundsUnavailable - })?; - T::AssetTransactor::check_out(&dest, &fees, &dummy_context); - T::AssetTransactor::withdraw_asset(&fees, &dest, None).map_err(|error| { - log::error!( - target: LOG_TARGET, - "XCM asset withdraw failed with error {:?}", error - ); - TokenError::FundsUnavailable - })?; - Ok(()) - } - } - - /// API for accessing the delivery cost of a message - impl Get> for Pallet { - fn get() -> BalanceOf { - // Cost here based on MaxMessagePayloadSize(the worst case) - Self::calculate_delivery_cost(T::MaxMessageSize::get()) - } - } } diff --git a/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs b/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs index fad62628c0f0..f08ce202f9bc 100644 --- a/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs +++ b/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs @@ -8,11 +8,9 @@ use snowbridge_beacon_primitives::{ types::deneb, BeaconHeader, ExecutionProof, Fork, ForkVersions, VersionedExecutionPayloadHeader, }; use snowbridge_core::{ - gwei, inbound::{Log, Proof, VerificationError}, - meth, Channel, ChannelId, PricingParameters, Rewards, StaticLookup, TokenId, + TokenId, }; -use snowbridge_router_primitives::inbound::v2::MessageToXcm; use sp_core::{H160, H256}; use sp_runtime::{ traits::{IdentifyAccount, IdentityLookup, MaybeEquivalence, Verify}, @@ -20,7 +18,6 @@ use sp_runtime::{ }; use sp_std::{convert::From, default::Default}; use xcm::{latest::SendXcm, prelude::*}; -use xcm_executor::AssetsInHolding; use crate::{self as inbound_queue}; @@ -149,65 +146,8 @@ impl SendXcm for MockXcmSender { } } -parameter_types! { - pub const OwnParaId: ParaId = ParaId::new(1013); - pub Parameters: PricingParameters = PricingParameters { - exchange_rate: FixedU128::from_rational(1, 400), - fee_per_gas: gwei(20), - rewards: Rewards { local: DOT, remote: meth(1) }, - multiplier: FixedU128::from_rational(1, 1), - }; -} - pub const DOT: u128 = 10_000_000_000; -pub struct MockChannelLookup; -impl StaticLookup for MockChannelLookup { - type Source = ChannelId; - type Target = Channel; - - fn lookup(channel_id: Self::Source) -> Option { - if channel_id != - hex!("c173fac324158e77fb5840738a1a541f633cbec8884c6a601c567d2b376a0539").into() - { - return None - } - Some(Channel { agent_id: H256::zero(), para_id: ASSET_HUB_PARAID.into() }) - } -} - -pub struct SuccessfulTransactor; -impl TransactAsset for SuccessfulTransactor { - fn can_check_in(_origin: &Location, _what: &Asset, _context: &XcmContext) -> XcmResult { - Ok(()) - } - - fn can_check_out(_dest: &Location, _what: &Asset, _context: &XcmContext) -> XcmResult { - Ok(()) - } - - fn deposit_asset(_what: &Asset, _who: &Location, _context: Option<&XcmContext>) -> XcmResult { - Ok(()) - } - - fn withdraw_asset( - _what: &Asset, - _who: &Location, - _context: Option<&XcmContext>, - ) -> Result { - Ok(AssetsInHolding::default()) - } - - fn internal_transfer_asset( - _what: &Asset, - _from: &Location, - _to: &Location, - _context: &XcmContext, - ) -> Result { - Ok(AssetsInHolding::default()) - } -} - pub struct MockTokenIdConvert; impl MaybeEquivalence for MockTokenIdConvert { fn convert(_id: &TokenId) -> Option { @@ -221,28 +161,11 @@ impl MaybeEquivalence for MockTokenIdConvert { impl inbound_queue::Config for Test { type RuntimeEvent = RuntimeEvent; type Verifier = MockVerifier; - type Token = Balances; type XcmSender = MockXcmSender; type WeightInfo = (); type GatewayAddress = GatewayAddress; - type MessageConverter = MessageToXcm< - CreateAssetCall, - CreateAssetDeposit, - InboundQueuePalletInstance, - AccountId, - Balance, - MockTokenIdConvert, - UniversalLocation, - AssetHubFromEthereum, - >; - type PricingParameters = Parameters; - type ChannelLookup = MockChannelLookup; #[cfg(feature = "runtime-benchmarks")] type Helper = Test; - type WeightToFee = IdentityFee; - type LengthToFee = IdentityFee; - type MaxMessageSize = ConstU32<1024>; - type AssetTransactor = SuccessfulTransactor; } pub fn last_events(n: usize) -> Vec { @@ -261,16 +184,6 @@ pub fn expect_events(e: Vec) { pub fn setup() { System::set_block_number(1); - Balances::mint_into( - &sibling_sovereign_account::(ASSET_HUB_PARAID.into()), - InitialFund::get(), - ) - .unwrap(); - Balances::mint_into( - &sibling_sovereign_account::(TEMPLATE_PARAID.into()), - InitialFund::get(), - ) - .unwrap(); } pub fn new_tester() -> sp_io::TestExternalities { @@ -286,47 +199,47 @@ pub fn new_tester() -> sp_io::TestExternalities { // cargo test --test register_token -- --nocapture pub fn mock_event_log() -> Log { Log { - // gateway address - address: hex!("eda338e4dc46038493b885327842fd3e301cab39").into(), - topics: vec![ - hex!("7153f9357c8ea496bba60bf82e67143e27b64462b49041f8e689e1b05728f84f").into(), - // channel id - hex!("c173fac324158e77fb5840738a1a541f633cbec8884c6a601c567d2b376a0539").into(), - // message id - hex!("5f7060e971b0dc81e63f0aa41831091847d97c1a4693ac450cc128c7214e65e0").into(), - ], - // Nonce + Payload - data: hex!("00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000002e000f000000000000000087d1f7fdfee7f651fabc8bfcb6e086c278b77a7d00e40b54020000000000000000000000000000000000000000000000000000000000").into(), - } + // gateway address + address: hex!("eda338e4dc46038493b885327842fd3e301cab39").into(), + topics: vec![ + hex!("7153f9357c8ea496bba60bf82e67143e27b64462b49041f8e689e1b05728f84f").into(), + // channel id + hex!("c173fac324158e77fb5840738a1a541f633cbec8884c6a601c567d2b376a0539").into(), + // message id + hex!("5f7060e971b0dc81e63f0aa41831091847d97c1a4693ac450cc128c7214e65e0").into(), + ], + // Nonce + Payload + data: hex!("00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000002e000f000000000000000087d1f7fdfee7f651fabc8bfcb6e086c278b77a7d00e40b54020000000000000000000000000000000000000000000000000000000000").into(), + } } pub fn mock_event_log_invalid_channel() -> Log { Log { - address: hex!("eda338e4dc46038493b885327842fd3e301cab39").into(), - topics: vec![ - hex!("7153f9357c8ea496bba60bf82e67143e27b64462b49041f8e689e1b05728f84f").into(), - // invalid channel id - hex!("0000000000000000000000000000000000000000000000000000000000000000").into(), - hex!("5f7060e971b0dc81e63f0aa41831091847d97c1a4693ac450cc128c7214e65e0").into(), - ], - data: hex!("00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000001e000f000000000000000087d1f7fdfee7f651fabc8bfcb6e086c278b77a7d0000").into(), - } + address: hex!("eda338e4dc46038493b885327842fd3e301cab39").into(), + topics: vec![ + hex!("7153f9357c8ea496bba60bf82e67143e27b64462b49041f8e689e1b05728f84f").into(), + // invalid channel id + hex!("0000000000000000000000000000000000000000000000000000000000000000").into(), + hex!("5f7060e971b0dc81e63f0aa41831091847d97c1a4693ac450cc128c7214e65e0").into(), + ], + data: hex!("00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000001e000f000000000000000087d1f7fdfee7f651fabc8bfcb6e086c278b77a7d0000").into(), + } } pub fn mock_event_log_invalid_gateway() -> Log { Log { - // gateway address - address: H160::zero(), - topics: vec![ - hex!("7153f9357c8ea496bba60bf82e67143e27b64462b49041f8e689e1b05728f84f").into(), - // channel id - hex!("c173fac324158e77fb5840738a1a541f633cbec8884c6a601c567d2b376a0539").into(), - // message id - hex!("5f7060e971b0dc81e63f0aa41831091847d97c1a4693ac450cc128c7214e65e0").into(), - ], - // Nonce + Payload - data: hex!("00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000001e000f000000000000000087d1f7fdfee7f651fabc8bfcb6e086c278b77a7d0000").into(), - } + // gateway address + address: H160::zero(), + topics: vec![ + hex!("7153f9357c8ea496bba60bf82e67143e27b64462b49041f8e689e1b05728f84f").into(), + // channel id + hex!("c173fac324158e77fb5840738a1a541f633cbec8884c6a601c567d2b376a0539").into(), + // message id + hex!("5f7060e971b0dc81e63f0aa41831091847d97c1a4693ac450cc128c7214e65e0").into(), + ], + // Nonce + Payload + data: hex!("00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000001e000f000000000000000087d1f7fdfee7f651fabc8bfcb6e086c278b77a7d0000").into(), + } } pub fn mock_execution_proof() -> ExecutionProof { diff --git a/bridges/snowbridge/pallets/inbound-queue-v2/src/test.rs b/bridges/snowbridge/pallets/inbound-queue-v2/src/test.rs index 41c38460aabf..989787af167d 100644 --- a/bridges/snowbridge/pallets/inbound-queue-v2/src/test.rs +++ b/bridges/snowbridge/pallets/inbound-queue-v2/src/test.rs @@ -17,7 +17,6 @@ use crate::mock::*; fn test_submit_happy_path() { new_tester().execute_with(|| { let relayer: AccountId = Keyring::Bob.into(); - let channel_sovereign = sibling_sovereign_account::(ASSET_HUB_PARAID.into()); let origin = RuntimeOrigin::signed(relayer.clone()); @@ -30,34 +29,15 @@ fn test_submit_happy_path() { }, }; - let initial_fund = InitialFund::get(); - assert_eq!(Balances::balance(&relayer), 0); - assert_eq!(Balances::balance(&channel_sovereign), initial_fund); - assert_ok!(InboundQueue::submit(origin.clone(), message.clone())); expect_events(vec![InboundQueueEvent::MessageReceived { - channel_id: hex!("c173fac324158e77fb5840738a1a541f633cbec8884c6a601c567d2b376a0539") - .into(), nonce: 1, message_id: [ 255, 125, 48, 71, 174, 185, 100, 26, 159, 43, 108, 6, 116, 218, 55, 155, 223, 143, 141, 22, 124, 110, 241, 18, 122, 217, 130, 29, 139, 76, 97, 201, ], - fee_burned: 110000000000, } .into()]); - - let delivery_cost = InboundQueue::calculate_delivery_cost(message.encode().len() as u32); - assert!( - Parameters::get().rewards.local < delivery_cost, - "delivery cost exceeds pure reward" - ); - - assert_eq!(Balances::balance(&relayer), delivery_cost, "relayer was rewarded"); - assert!( - Balances::balance(&channel_sovereign) <= initial_fund - delivery_cost, - "sovereign account paid reward" - ); }); } @@ -67,11 +47,6 @@ fn test_submit_xcm_invalid_channel() { let relayer: AccountId = Keyring::Bob.into(); let origin = RuntimeOrigin::signed(relayer); - // Deposit funds into sovereign account of parachain 1001 - let sovereign_account = sibling_sovereign_account::(TEMPLATE_PARAID.into()); - println!("account: {}", sovereign_account); - let _ = Balances::mint_into(&sovereign_account, 10000); - // Submit message let message = Message { event_log: mock_event_log_invalid_channel(), @@ -93,10 +68,6 @@ fn test_submit_with_invalid_gateway() { let relayer: AccountId = Keyring::Bob.into(); let origin = RuntimeOrigin::signed(relayer); - // Deposit funds into sovereign account of Asset Hub (Statemint) - let sovereign_account = sibling_sovereign_account::(ASSET_HUB_PARAID.into()); - let _ = Balances::mint_into(&sovereign_account, 10000); - // Submit message let message = Message { event_log: mock_event_log_invalid_gateway(), @@ -118,10 +89,6 @@ fn test_submit_with_invalid_nonce() { let relayer: AccountId = Keyring::Bob.into(); let origin = RuntimeOrigin::signed(relayer); - // Deposit funds into sovereign account of Asset Hub (Statemint) - let sovereign_account = sibling_sovereign_account::(ASSET_HUB_PARAID.into()); - let _ = Balances::mint_into(&sovereign_account, 10000); - // Submit message let message = Message { event_log: mock_event_log(), @@ -132,11 +99,6 @@ fn test_submit_with_invalid_nonce() { }; assert_ok!(InboundQueue::submit(origin.clone(), message.clone())); - let nonce: u64 = >::get(ChannelId::from(hex!( - "c173fac324158e77fb5840738a1a541f633cbec8884c6a601c567d2b376a0539" - ))); - assert_eq!(nonce, 1); - // Submit the same again assert_noop!( InboundQueue::submit(origin.clone(), message.clone()), @@ -151,10 +113,6 @@ fn test_submit_no_funds_to_reward_relayers_just_ignore() { let relayer: AccountId = Keyring::Bob.into(); let origin = RuntimeOrigin::signed(relayer); - // Reset balance of sovereign_account to zero first - let sovereign_account = sibling_sovereign_account::(ASSET_HUB_PARAID.into()); - Balances::set_balance(&sovereign_account, 0); - // Submit message let message = Message { event_log: mock_event_log(), @@ -209,10 +167,6 @@ fn test_submit_no_funds_to_reward_relayers_and_ed_preserved() { let relayer: AccountId = Keyring::Bob.into(); let origin = RuntimeOrigin::signed(relayer); - // Reset balance of sovereign account to (ED+1) first - let sovereign_account = sibling_sovereign_account::(ASSET_HUB_PARAID.into()); - Balances::set_balance(&sovereign_account, ExistentialDeposit::get() + 1); - // Submit message successfully let message = Message { event_log: mock_event_log(), @@ -223,10 +177,6 @@ fn test_submit_no_funds_to_reward_relayers_and_ed_preserved() { }; assert_ok!(InboundQueue::submit(origin.clone(), message.clone())); - // Check balance of sovereign account to ED - let amount = Balances::balance(&sovereign_account); - assert_eq!(amount, ExistentialDeposit::get()); - // Submit another message with nonce set as 2 let mut event_log = mock_event_log(); event_log.data[31] = 2; @@ -238,8 +188,5 @@ fn test_submit_no_funds_to_reward_relayers_and_ed_preserved() { }, }; assert_ok!(InboundQueue::submit(origin.clone(), message.clone())); - // Check balance of sovereign account as ED does not change - let amount = Balances::balance(&sovereign_account); - assert_eq!(amount, ExistentialDeposit::get()); }); } diff --git a/bridges/snowbridge/primitives/router/src/inbound/v2.rs b/bridges/snowbridge/primitives/router/src/inbound/v2.rs index 05c054080620..8ab4c98501b4 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/v2.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/v2.rs @@ -2,16 +2,17 @@ // SPDX-FileCopyrightText: 2023 Snowfork //! Converts messages from Ethereum to XCM messages -use crate::inbound::{CallIndex, GlobalConsensusEthereumConvertsFor}; use codec::{Decode, Encode}; use core::marker::PhantomData; use frame_support::{traits::tokens::Balance as BalanceT, weights::Weight, PalletError}; use scale_info::TypeInfo; use snowbridge_core::TokenId; use sp_core::{Get, RuntimeDebug, H160, H256}; +use sp_io::hashing::blake2_256; use sp_runtime::{traits::MaybeEquivalence, MultiAddress}; use sp_std::prelude::*; use xcm::prelude::{Junction::AccountKey20, *}; +use xcm_executor::traits::ConvertLocation; const MINIMUM_DEPOSIT: u128 = 1; @@ -20,429 +21,36 @@ const MINIMUM_DEPOSIT: u128 = 1; /// Instead having BridgeHub transcode the messages into XCM. #[derive(Clone, Encode, Decode, RuntimeDebug)] pub enum VersionedMessage { - V1(MessageV1), + V2(Message), } -/// For V1, the ethereum side sends messages which are transcoded into XCM. These messages are +/// For V2, the ethereum side sends messages which are transcoded into XCM. These messages are /// self-contained, in that they can be transcoded using only information in the message. #[derive(Clone, Encode, Decode, RuntimeDebug)] -pub struct MessageV1 { - /// EIP-155 chain id of the origin Ethereum network - pub chain_id: u64, +pub struct Message { + /// The origin address + pub origin: H160, /// The command originating from the Gateway contract - pub command: Command, + pub xcm: Vec, } -#[derive(Clone, Encode, Decode, RuntimeDebug)] -pub enum Command { - /// Register a wrapped token on the AssetHub `ForeignAssets` pallet - RegisterToken { - /// The address of the ERC20 token to be bridged over to AssetHub - token: H160, - /// XCM execution fee on AssetHub - fee: u128, - }, - /// Send Ethereum token to AssetHub or another parachain - SendToken { - /// The address of the ERC20 token to be bridged over to AssetHub - token: H160, - /// The destination for the transfer - destination: Destination, - /// Amount to transfer - amount: u128, - /// XCM execution fee on AssetHub - fee: u128, - }, - /// Send Polkadot token back to the original parachain - SendNativeToken { - /// The Id of the token - token_id: TokenId, - /// The destination for the transfer - destination: Destination, - /// Amount to transfer - amount: u128, - /// XCM execution fee on AssetHub - fee: u128, - }, -} - -/// Destination for bridged tokens -#[derive(Clone, Encode, Decode, RuntimeDebug)] -pub enum Destination { - /// The funds will be deposited into account `id` on AssetHub - AccountId32 { id: [u8; 32] }, - /// The funds will deposited into the sovereign account of destination parachain `para_id` on - /// AssetHub, Account `id` on the destination parachain will receive the funds via a - /// reserve-backed transfer. See - ForeignAccountId32 { - para_id: u32, - id: [u8; 32], - /// XCM execution fee on final destination - fee: u128, - }, - /// The funds will deposited into the sovereign account of destination parachain `para_id` on - /// AssetHub, Account `id` on the destination parachain will receive the funds via a - /// reserve-backed transfer. See - ForeignAccountId20 { - para_id: u32, - id: [u8; 20], - /// XCM execution fee on final destination - fee: u128, - }, -} - -pub struct MessageToXcm< - CreateAssetCall, - CreateAssetDeposit, - InboundQueuePalletInstance, - AccountId, - Balance, - ConvertAssetId, - EthereumUniversalLocation, - GlobalAssetHubLocation, -> where - CreateAssetCall: Get, - CreateAssetDeposit: Get, - Balance: BalanceT, - ConvertAssetId: MaybeEquivalence, - EthereumUniversalLocation: Get, - GlobalAssetHubLocation: Get, -{ - _phantom: PhantomData<( - CreateAssetCall, - CreateAssetDeposit, - InboundQueuePalletInstance, - AccountId, - Balance, - ConvertAssetId, - EthereumUniversalLocation, - GlobalAssetHubLocation, - )>, -} - -/// Reason why a message conversion failed. -#[derive(Copy, Clone, TypeInfo, PalletError, Encode, Decode, RuntimeDebug)] -pub enum ConvertMessageError { - /// The message version is not supported for conversion. - UnsupportedVersion, - InvalidDestination, - InvalidToken, - /// The fee asset is not supported for conversion. - UnsupportedFeeAsset, - CannotReanchor, -} - -/// convert the inbound message to xcm which will be forwarded to the destination chain -pub trait ConvertMessage { - type Balance: BalanceT + From; - type AccountId; - /// Converts a versioned message into an XCM message and an optional topicID - fn convert( - message_id: H256, - message: VersionedMessage, - ) -> Result<(Xcm<()>, Self::Balance), ConvertMessageError>; -} - -impl< - CreateAssetCall, - CreateAssetDeposit, - InboundQueuePalletInstance, - AccountId, - Balance, - ConvertAssetId, - EthereumUniversalLocation, - GlobalAssetHubLocation, - > ConvertMessage - for MessageToXcm< - CreateAssetCall, - CreateAssetDeposit, - InboundQueuePalletInstance, - AccountId, - Balance, - ConvertAssetId, - EthereumUniversalLocation, - GlobalAssetHubLocation, - > +pub struct GlobalConsensusEthereumConvertsFor(PhantomData); +impl ConvertLocation for GlobalConsensusEthereumConvertsFor where - CreateAssetCall: Get, - CreateAssetDeposit: Get, - InboundQueuePalletInstance: Get, - Balance: BalanceT + From, - AccountId: Into<[u8; 32]>, - ConvertAssetId: MaybeEquivalence, - EthereumUniversalLocation: Get, - GlobalAssetHubLocation: Get, + AccountId: From<[u8; 32]> + Clone, { - type Balance = Balance; - type AccountId = AccountId; - - fn convert( - message_id: H256, - message: VersionedMessage, - ) -> Result<(Xcm<()>, Self::Balance), ConvertMessageError> { - use Command::*; - use VersionedMessage::*; - match message { - V1(MessageV1 { chain_id, command: RegisterToken { token, fee } }) => - Ok(Self::convert_register_token(message_id, chain_id, token, fee)), - V1(MessageV1 { chain_id, command: SendToken { token, destination, amount, fee } }) => - Ok(Self::convert_send_token(message_id, chain_id, token, destination, amount, fee)), - V1(MessageV1 { - chain_id, - command: SendNativeToken { token_id, destination, amount, fee }, - }) => Self::convert_send_native_token( - message_id, - chain_id, - token_id, - destination, - amount, - fee, - ), + fn convert_location(location: &Location) -> Option { + match location.unpack() { + (_, [GlobalConsensus(Ethereum { chain_id })]) => + Some(Self::from_chain_id(chain_id).into()), + _ => None, } } } -impl< - CreateAssetCall, - CreateAssetDeposit, - InboundQueuePalletInstance, - AccountId, - Balance, - ConvertAssetId, - EthereumUniversalLocation, - GlobalAssetHubLocation, - > - MessageToXcm< - CreateAssetCall, - CreateAssetDeposit, - InboundQueuePalletInstance, - AccountId, - Balance, - ConvertAssetId, - EthereumUniversalLocation, - GlobalAssetHubLocation, - > -where - CreateAssetCall: Get, - CreateAssetDeposit: Get, - InboundQueuePalletInstance: Get, - Balance: BalanceT + From, - AccountId: Into<[u8; 32]>, - ConvertAssetId: MaybeEquivalence, - EthereumUniversalLocation: Get, - GlobalAssetHubLocation: Get, -{ - fn convert_register_token( - message_id: H256, - chain_id: u64, - token: H160, - fee: u128, - ) -> (Xcm<()>, Balance) { - let network = Ethereum { chain_id }; - let xcm_fee: Asset = (Location::parent(), fee).into(); - let deposit: Asset = (Location::parent(), CreateAssetDeposit::get()).into(); - - let total_amount = fee + CreateAssetDeposit::get(); - let total: Asset = (Location::parent(), total_amount).into(); - - let bridge_location = Location::new(2, GlobalConsensus(network)); - - let owner = GlobalConsensusEthereumConvertsFor::<[u8; 32]>::from_chain_id(&chain_id); - let asset_id = Self::convert_token_address(network, token); - let create_call_index: [u8; 2] = CreateAssetCall::get(); - let inbound_queue_pallet_index = InboundQueuePalletInstance::get(); - - let xcm: Xcm<()> = vec![ - // Teleport required fees. - ReceiveTeleportedAsset(total.into()), - // Pay for execution. - BuyExecution { fees: xcm_fee, weight_limit: Unlimited }, - // Fund the snowbridge sovereign with the required deposit for creation. - DepositAsset { assets: Definite(deposit.into()), beneficiary: bridge_location.clone() }, - // This `SetAppendix` ensures that `xcm_fee` not spent by `Transact` will be - // deposited to snowbridge sovereign, instead of being trapped, regardless of - // `Transact` success or not. - SetAppendix(Xcm(vec![ - RefundSurplus, - DepositAsset { assets: AllCounted(1).into(), beneficiary: bridge_location }, - ])), - // Only our inbound-queue pallet is allowed to invoke `UniversalOrigin`. - DescendOrigin(PalletInstance(inbound_queue_pallet_index).into()), - // Change origin to the bridge. - UniversalOrigin(GlobalConsensus(network)), - // Call create_asset on foreign assets pallet. - Transact { - origin_kind: OriginKind::Xcm, - require_weight_at_most: Weight::from_parts(400_000_000, 8_000), - call: ( - create_call_index, - asset_id, - MultiAddress::<[u8; 32], ()>::Id(owner), - MINIMUM_DEPOSIT, - ) - .encode() - .into(), - }, - // Forward message id to Asset Hub - SetTopic(message_id.into()), - // Once the program ends here, appendix program will run, which will deposit any - // leftover fee to snowbridge sovereign. - ] - .into(); - - (xcm, total_amount.into()) - } - - fn convert_send_token( - message_id: H256, - chain_id: u64, - token: H160, - destination: Destination, - amount: u128, - asset_hub_fee: u128, - ) -> (Xcm<()>, Balance) { - let network = Ethereum { chain_id }; - let asset_hub_fee_asset: Asset = (Location::parent(), asset_hub_fee).into(); - let asset: Asset = (Self::convert_token_address(network, token), amount).into(); - - let (dest_para_id, beneficiary, dest_para_fee) = match destination { - // Final destination is a 32-byte account on AssetHub - Destination::AccountId32 { id } => - (None, Location::new(0, [AccountId32 { network: None, id }]), 0), - // Final destination is a 32-byte account on a sibling of AssetHub - Destination::ForeignAccountId32 { para_id, id, fee } => ( - Some(para_id), - Location::new(0, [AccountId32 { network: None, id }]), - // Total fee needs to cover execution on AssetHub and Sibling - fee, - ), - // Final destination is a 20-byte account on a sibling of AssetHub - Destination::ForeignAccountId20 { para_id, id, fee } => ( - Some(para_id), - Location::new(0, [AccountKey20 { network: None, key: id }]), - // Total fee needs to cover execution on AssetHub and Sibling - fee, - ), - }; - - let total_fees = asset_hub_fee.saturating_add(dest_para_fee); - let total_fee_asset: Asset = (Location::parent(), total_fees).into(); - let inbound_queue_pallet_index = InboundQueuePalletInstance::get(); - - let mut instructions = vec![ - ReceiveTeleportedAsset(total_fee_asset.into()), - BuyExecution { fees: asset_hub_fee_asset, weight_limit: Unlimited }, - DescendOrigin(PalletInstance(inbound_queue_pallet_index).into()), - UniversalOrigin(GlobalConsensus(network)), - ReserveAssetDeposited(asset.clone().into()), - ClearOrigin, - ]; - - match dest_para_id { - Some(dest_para_id) => { - let dest_para_fee_asset: Asset = (Location::parent(), dest_para_fee).into(); - let bridge_location = Location::new(2, GlobalConsensus(network)); - - instructions.extend(vec![ - // After program finishes deposit any leftover assets to the snowbridge - // sovereign. - SetAppendix(Xcm(vec![DepositAsset { - assets: Wild(AllCounted(2)), - beneficiary: bridge_location, - }])), - // Perform a deposit reserve to send to destination chain. - DepositReserveAsset { - assets: Definite(vec![dest_para_fee_asset.clone(), asset].into()), - dest: Location::new(1, [Parachain(dest_para_id)]), - xcm: vec![ - // Buy execution on target. - BuyExecution { fees: dest_para_fee_asset, weight_limit: Unlimited }, - // Deposit assets to beneficiary. - DepositAsset { assets: Wild(AllCounted(2)), beneficiary }, - // Forward message id to destination parachain. - SetTopic(message_id.into()), - ] - .into(), - }, - ]); - }, - None => { - instructions.extend(vec![ - // Deposit both asset and fees to beneficiary so the fees will not get - // trapped. Another benefit is when fees left more than ED on AssetHub could be - // used to create the beneficiary account in case it does not exist. - DepositAsset { assets: Wild(AllCounted(2)), beneficiary }, - ]); - }, - } - - // Forward message id to Asset Hub. - instructions.push(SetTopic(message_id.into())); - - // The `instructions` to forward to AssetHub, and the `total_fees` to locally burn (since - // they are teleported within `instructions`). - (instructions.into(), total_fees.into()) - } - - // Convert ERC20 token address to a location that can be understood by Assets Hub. - fn convert_token_address(network: NetworkId, token: H160) -> Location { - Location::new( - 2, - [GlobalConsensus(network), AccountKey20 { network: None, key: token.into() }], - ) - } - - /// Constructs an XCM message destined for AssetHub that withdraws assets from the sovereign - /// account of the Gateway contract and either deposits those assets into a recipient account or - /// forwards the assets to another parachain. - fn convert_send_native_token( - message_id: H256, - chain_id: u64, - token_id: TokenId, - destination: Destination, - amount: u128, - asset_hub_fee: u128, - ) -> Result<(Xcm<()>, Balance), ConvertMessageError> { - let network = Ethereum { chain_id }; - let asset_hub_fee_asset: Asset = (Location::parent(), asset_hub_fee).into(); - - let beneficiary = match destination { - // Final destination is a 32-byte account on AssetHub - Destination::AccountId32 { id } => - Ok(Location::new(0, [AccountId32 { network: None, id }])), - _ => Err(ConvertMessageError::InvalidDestination), - }?; - - let total_fee_asset: Asset = (Location::parent(), asset_hub_fee).into(); - - let asset_loc = - ConvertAssetId::convert(&token_id).ok_or(ConvertMessageError::InvalidToken)?; - - let mut reanchored_asset_loc = asset_loc.clone(); - reanchored_asset_loc - .reanchor(&GlobalAssetHubLocation::get(), &EthereumUniversalLocation::get()) - .map_err(|_| ConvertMessageError::CannotReanchor)?; - - let asset: Asset = (reanchored_asset_loc, amount).into(); - - let inbound_queue_pallet_index = InboundQueuePalletInstance::get(); - - let instructions = vec![ - ReceiveTeleportedAsset(total_fee_asset.clone().into()), - BuyExecution { fees: asset_hub_fee_asset, weight_limit: Unlimited }, - DescendOrigin(PalletInstance(inbound_queue_pallet_index).into()), - UniversalOrigin(GlobalConsensus(network)), - WithdrawAsset(asset.clone().into()), - // Deposit both asset and fees to beneficiary so the fees will not get - // trapped. Another benefit is when fees left more than ED on AssetHub could be - // used to create the beneficiary account in case it does not exist. - DepositAsset { assets: Wild(AllCounted(2)), beneficiary }, - SetTopic(message_id.into()), - ]; - - // `total_fees` to burn on this chain when sending `instructions` to run on AH (which also - // teleport fees) - Ok((instructions.into(), asset_hub_fee.into())) +impl GlobalConsensusEthereumConvertsFor { + pub fn from_chain_id(chain_id: &u64) -> [u8; 32] { + (b"ethereum-chain", chain_id).using_encoded(blake2_256) } } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs index eb046b7edaa1..970ce143dc6c 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs @@ -26,7 +26,7 @@ use parachains_common::{AccountId, Balance}; use snowbridge_beacon_primitives::{Fork, ForkVersions}; use snowbridge_core::{gwei, meth, AllowSiblingsOnly, PricingParameters, Rewards}; use snowbridge_router_primitives::{ - inbound::{v1::MessageToXcm, v2::MessageToXcm as MessageToXcmV2}, + inbound::v1::MessageToXcm, outbound::{v1::EthereumBlobExporter, v2::EthereumBlobExporter as EthereumBlobExporterV2}, }; use sp_core::H160; @@ -116,31 +116,14 @@ impl snowbridge_pallet_inbound_queue::Config for Runtime { impl snowbridge_pallet_inbound_queue_v2::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Verifier = snowbridge_pallet_ethereum_client::Pallet; - type Token = Balances; #[cfg(not(feature = "runtime-benchmarks"))] type XcmSender = XcmRouter; #[cfg(feature = "runtime-benchmarks")] type XcmSender = DoNothingRouter; - type ChannelLookup = EthereumSystem; type GatewayAddress = EthereumGatewayAddress; #[cfg(feature = "runtime-benchmarks")] type Helper = Runtime; - type MessageConverter = MessageToXcmV2< - CreateAssetCall, - CreateAssetDeposit, - ConstU8, - AccountId, - Balance, - EthereumSystem, - EthereumUniversalLocation, - AssetHubFromEthereum, - >; - type WeightToFee = WeightToFee; - type LengthToFee = ConstantMultiplier; - type MaxMessageSize = ConstU32<2048>; type WeightInfo = crate::weights::snowbridge_pallet_inbound_queue_v2::WeightInfo; - type PricingParameters = EthereumSystem; - type AssetTransactor = ::AssetTransactor; } impl snowbridge_pallet_outbound_queue::Config for Runtime { From 93959efc4bf72ce98f40f37d6b99c23242e4e45b Mon Sep 17 00:00:00 2001 From: claravanstaden Date: Fri, 25 Oct 2024 10:37:40 +0200 Subject: [PATCH 24/37] tests --- Cargo.lock | 1 + .../pallets/inbound-queue-v2/Cargo.toml | 1 + .../pallets/inbound-queue-v2/src/mock.rs | 50 +++++++++++++++++++ 3 files changed, 52 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 0623c659bc00..b8389877d164 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -21106,6 +21106,7 @@ dependencies = [ "snowbridge-core", "snowbridge-pallet-ethereum-client", "snowbridge-pallet-inbound-queue-fixtures-v2", + "snowbridge-pallet-rewards", "snowbridge-router-primitives-v2", "sp-core 28.0.0", "sp-io 30.0.0", diff --git a/bridges/snowbridge/pallets/inbound-queue-v2/Cargo.toml b/bridges/snowbridge/pallets/inbound-queue-v2/Cargo.toml index e2ebd90eecca..9bfaac0b9e34 100644 --- a/bridges/snowbridge/pallets/inbound-queue-v2/Cargo.toml +++ b/bridges/snowbridge/pallets/inbound-queue-v2/Cargo.toml @@ -44,6 +44,7 @@ snowbridge-pallet-inbound-queue-fixtures-v2 = { optional = true, workspace = tru frame-benchmarking = { workspace = true, default-features = true } sp-keyring = { workspace = true, default-features = true } snowbridge-pallet-ethereum-client = { workspace = true, default-features = true } +snowbridge-pallet-rewards = { workspace = true, default-features = true } hex-literal = { workspace = true, default-features = true } [features] diff --git a/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs b/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs index 4603161a3ab7..cadb1bfa6bca 100644 --- a/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs +++ b/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs @@ -30,6 +30,7 @@ frame_support::construct_runtime!( System: frame_system::{Pallet, Call, Storage, Event}, Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, EthereumBeaconClient: snowbridge_pallet_ethereum_client::{Pallet, Call, Storage, Event}, + EthereumRewards: snowbridge_pallet_rewards::{Pallet, Call, Storage, Event}, InboundQueue: inbound_queue::{Pallet, Call, Storage, Event}, } ); @@ -167,6 +168,23 @@ impl inbound_queue::Config for Test { type GatewayAddress = GatewayAddress; #[cfg(feature = "runtime-benchmarks")] type Helper = Test; + type Token = Balances; + type RewardLedger = EthereumRewards; +} + +parameter_types! { + pub WethAddress: H160 = hex!("774667629726ec1FaBEbCEc0D9139bD1C8f72a23").into(); +} + +impl snowbridge_pallet_rewards::Config for Test { + type RuntimeEvent = RuntimeEvent; + type AssetHubParaId = ConstU32<1000>; + type EthereumNetwork = EthereumNetwork; + type WethAddress = WethAddress; + type XcmSender = MockXcmSender; + type AssetTransactor = SuccessfulTransactor; + type Token = Balance; + type WeightInfo = (); } pub fn last_events(n: usize) -> Vec { @@ -270,5 +288,37 @@ pub fn mock_execution_proof() -> ExecutionProof { } } +pub struct SuccessfulTransactor; +impl TransactAsset for SuccessfulTransactor { + fn can_check_in(_origin: &Location, _what: &Asset, _context: &XcmContext) -> XcmResult { + Ok(()) + } + + fn can_check_out(_dest: &Location, _what: &Asset, _context: &XcmContext) -> XcmResult { + Ok(()) + } + + fn deposit_asset(_what: &Asset, _who: &Location, _context: Option<&XcmContext>) -> XcmResult { + Ok(()) + } + + fn withdraw_asset( + _what: &Asset, + _who: &Location, + _context: Option<&XcmContext>, + ) -> Result { + Ok(AssetsInHolding::default()) + } + + fn internal_transfer_asset( + _what: &Asset, + _from: &Location, + _to: &Location, + _context: &XcmContext, + ) -> Result { + Ok(AssetsInHolding::default()) + } +} + pub const ASSET_HUB_PARAID: u32 = 1000u32; pub const TEMPLATE_PARAID: u32 = 1001u32; From 1d5957be5e62d5a6111d057ca6a0fa549da94683 Mon Sep 17 00:00:00 2001 From: claravanstaden Date: Fri, 25 Oct 2024 11:57:34 +0200 Subject: [PATCH 25/37] merge damage --- Cargo.lock | 27 +++++++++++++++++++ bridges/snowbridge/primitives/core/src/lib.rs | 2 +- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 431ad1dd74ae..130897e01bc6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2417,6 +2417,7 @@ dependencies = [ "snowbridge-pallet-inbound-queue-fixtures", "snowbridge-pallet-outbound-queue", "snowbridge-pallet-outbound-queue-v2", + "snowbridge-pallet-rewards", "snowbridge-pallet-system", "snowbridge-router-primitives", "sp-core 28.0.0", @@ -2499,6 +2500,7 @@ dependencies = [ "snowbridge-pallet-inbound-queue-v2", "snowbridge-pallet-outbound-queue", "snowbridge-pallet-outbound-queue-v2", + "snowbridge-pallet-rewards", "snowbridge-pallet-system", "snowbridge-router-primitives", "snowbridge-runtime-common", @@ -20893,6 +20895,7 @@ dependencies = [ "frame-system", "hex", "hex-literal", + "log", "parity-scale-codec", "polkadot-parachain-primitives", "scale-info", @@ -21092,6 +21095,7 @@ dependencies = [ "snowbridge-core", "snowbridge-pallet-ethereum-client", "snowbridge-pallet-inbound-queue-fixtures-v2", + "snowbridge-pallet-rewards", "snowbridge-router-primitives", "sp-core 28.0.0", "sp-io 30.0.0", @@ -21151,6 +21155,29 @@ dependencies = [ "sp-std 14.0.0", ] +[[package]] +name = "snowbridge-pallet-rewards" +version = "0.1.0" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "hex", + "hex-literal", + "log", + "parity-scale-codec", + "scale-info", + "snowbridge-core", + "snowbridge-router-primitives", + "sp-core 28.0.0", + "sp-io 30.0.0", + "sp-keyring", + "sp-runtime 31.0.1", + "sp-std 14.0.0", + "staging-xcm", + "staging-xcm-executor", +] + [[package]] name = "snowbridge-pallet-system" version = "0.2.0" diff --git a/bridges/snowbridge/primitives/core/src/lib.rs b/bridges/snowbridge/primitives/core/src/lib.rs index d25505677a52..de4dd1a67c6d 100644 --- a/bridges/snowbridge/primitives/core/src/lib.rs +++ b/bridges/snowbridge/primitives/core/src/lib.rs @@ -39,7 +39,7 @@ pub use operating_mode::BasicOperatingMode; pub use pricing::{PricingParameters, Rewards}; -pub use reward::RewardLedger; +pub use rewards::RewardLedger; pub fn sibling_sovereign_account(para_id: ParaId) -> T::AccountId where From a66a0ca854b38d4953bd4c34be1b54ed25d08b22 Mon Sep 17 00:00:00 2001 From: claravanstaden Date: Fri, 25 Oct 2024 12:27:53 +0200 Subject: [PATCH 26/37] test test compilation --- .../pallets/inbound-queue-v2/src/lib.rs | 2 +- .../pallets/inbound-queue-v2/src/mock.rs | 15 +++++++++------ .../pallets/inbound-queue-v2/src/test.rs | 4 +--- .../primitives/router/src/inbound/v1.rs | 10 +++++----- .../primitives/router/src/inbound/v2.rs | 9 ++------- 5 files changed, 18 insertions(+), 22 deletions(-) diff --git a/bridges/snowbridge/pallets/inbound-queue-v2/src/lib.rs b/bridges/snowbridge/pallets/inbound-queue-v2/src/lib.rs index b00960b5bc27..98575ac6113b 100644 --- a/bridges/snowbridge/pallets/inbound-queue-v2/src/lib.rs +++ b/bridges/snowbridge/pallets/inbound-queue-v2/src/lib.rs @@ -193,7 +193,7 @@ pub mod pallet { #[pallet::call_index(0)] #[pallet::weight(T::WeightInfo::submit())] pub fn submit(origin: OriginFor, message: Message) -> DispatchResult { - let _who = ensure_signed(origin)?; + let who = ensure_signed(origin)?; ensure!(!Self::operating_mode().is_halted(), Error::::Halted); // submit message to verifier for verification diff --git a/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs b/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs index a63313fe5e1c..5a30c0ac6f9a 100644 --- a/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs +++ b/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs @@ -2,7 +2,7 @@ // SPDX-FileCopyrightText: 2023 Snowfork use super::*; -use frame_support::{derive_impl, parameter_types, traits::ConstU32, weights::IdentityFee}; +use frame_support::{derive_impl, parameter_types, traits::ConstU32}; use hex_literal::hex; use snowbridge_beacon_primitives::{ types::deneb, BeaconHeader, ExecutionProof, Fork, ForkVersions, VersionedExecutionPayloadHeader, @@ -11,17 +11,20 @@ use snowbridge_core::{ inbound::{Log, Proof, VerificationError}, TokenId, }; -use sp_core::{H160, H256}; +use sp_core::H160; use sp_runtime::{ traits::{IdentifyAccount, IdentityLookup, MaybeEquivalence, Verify}, - BuildStorage, FixedU128, MultiSignature, + BuildStorage, MultiSignature, }; use sp_std::{convert::From, default::Default}; use xcm::{latest::SendXcm, prelude::*}; +use xcm_executor::traits::TransactAsset; +use xcm_executor::AssetsInHolding; use crate::{self as inbound_queue}; -type Block = frame_system::mocking::MockBlock; +pub type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +pub type Block = frame_system::mocking::MockBlock; frame_support::construct_runtime!( pub enum Test @@ -168,7 +171,7 @@ impl inbound_queue::Config for Test { #[cfg(feature = "runtime-benchmarks")] type Helper = Test; type Token = Balances; - type RewardLedger = EthereumRewards; + type RewardLedger = (); } parameter_types! { @@ -182,7 +185,7 @@ impl snowbridge_pallet_rewards::Config for Test { type WethAddress = WethAddress; type XcmSender = MockXcmSender; type AssetTransactor = SuccessfulTransactor; - type Token = Balance; + type Token = Balances; type WeightInfo = (); } diff --git a/bridges/snowbridge/pallets/inbound-queue-v2/src/test.rs b/bridges/snowbridge/pallets/inbound-queue-v2/src/test.rs index 989787af167d..a62a1b9d1a4c 100644 --- a/bridges/snowbridge/pallets/inbound-queue-v2/src/test.rs +++ b/bridges/snowbridge/pallets/inbound-queue-v2/src/test.rs @@ -3,11 +3,9 @@ use super::*; use frame_support::{assert_noop, assert_ok}; -use hex_literal::hex; -use snowbridge_core::{inbound::Proof, ChannelId}; +use snowbridge_core::inbound::Proof; use sp_keyring::AccountKeyring as Keyring; use sp_runtime::DispatchError; -use sp_std::convert::From; use crate::{Error, Event as InboundQueueEvent}; diff --git a/bridges/snowbridge/primitives/router/src/inbound/v1.rs b/bridges/snowbridge/primitives/router/src/inbound/v1.rs index e7440b93bcdd..05c054080620 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/v1.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/v1.rs @@ -20,15 +20,15 @@ const MINIMUM_DEPOSIT: u128 = 1; /// Instead having BridgeHub transcode the messages into XCM. #[derive(Clone, Encode, Decode, RuntimeDebug)] pub enum VersionedMessage { - V2(Message), + V1(MessageV1), } -/// For V2, the ethereum side sends messages which are transcoded into XCM. These messages are +/// For V1, the ethereum side sends messages which are transcoded into XCM. These messages are /// self-contained, in that they can be transcoded using only information in the message. #[derive(Clone, Encode, Decode, RuntimeDebug)] -pub struct Message { - /// The origin address - pub origin: H160, +pub struct MessageV1 { + /// EIP-155 chain id of the origin Ethereum network + pub chain_id: u64, /// The command originating from the Gateway contract pub command: Command, } diff --git a/bridges/snowbridge/primitives/router/src/inbound/v2.rs b/bridges/snowbridge/primitives/router/src/inbound/v2.rs index 8ab4c98501b4..91aef5c111fb 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/v2.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/v2.rs @@ -4,17 +4,12 @@ use codec::{Decode, Encode}; use core::marker::PhantomData; -use frame_support::{traits::tokens::Balance as BalanceT, weights::Weight, PalletError}; -use scale_info::TypeInfo; -use snowbridge_core::TokenId; -use sp_core::{Get, RuntimeDebug, H160, H256}; +use sp_core::{RuntimeDebug, H160}; use sp_io::hashing::blake2_256; -use sp_runtime::{traits::MaybeEquivalence, MultiAddress}; use sp_std::prelude::*; -use xcm::prelude::{Junction::AccountKey20, *}; +use xcm::prelude::*; use xcm_executor::traits::ConvertLocation; -const MINIMUM_DEPOSIT: u128 = 1; /// Messages from Ethereum are versioned. This is because in future, /// we may want to evolve the protocol so that the ethereum side sends XCM messages directly. From 45a582bc340c0e513203a18ed115a00532e14060 Mon Sep 17 00:00:00 2001 From: claravanstaden Date: Fri, 25 Oct 2024 12:43:40 +0200 Subject: [PATCH 27/37] fix unit tests --- Cargo.lock | 1 + bridges/snowbridge/pallets/rewards/Cargo.toml | 1 + .../snowbridge/pallets/rewards/src/mock.rs | 67 ++++++++++++++----- .../snowbridge/pallets/rewards/src/tests.rs | 6 +- 4 files changed, 55 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 130897e01bc6..83b266a352ab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -21165,6 +21165,7 @@ dependencies = [ "hex", "hex-literal", "log", + "pallet-balances", "parity-scale-codec", "scale-info", "snowbridge-core", diff --git a/bridges/snowbridge/pallets/rewards/Cargo.toml b/bridges/snowbridge/pallets/rewards/Cargo.toml index 65b7b698ad2f..79b03a0e2227 100644 --- a/bridges/snowbridge/pallets/rewards/Cargo.toml +++ b/bridges/snowbridge/pallets/rewards/Cargo.toml @@ -39,6 +39,7 @@ snowbridge-router-primitives = { workspace = true } hex = { workspace = true, default-features = true } hex-literal = { workspace = true, default-features = true } sp-keyring = { workspace = true, default-features = true } +pallet-balances = { workspace = true, default-features = true } [features] default = ["std"] diff --git a/bridges/snowbridge/pallets/rewards/src/mock.rs b/bridges/snowbridge/pallets/rewards/src/mock.rs index 10877cd313e7..a4c8e88f6224 100644 --- a/bridges/snowbridge/pallets/rewards/src/mock.rs +++ b/bridges/snowbridge/pallets/rewards/src/mock.rs @@ -12,15 +12,17 @@ use sp_runtime::{ }; use sp_std::{convert::From, default::Default}; use xcm::{latest::SendXcm, prelude::*}; +use xcm_executor::AssetsInHolding; use crate::{self as snowbridge_pallet_rewards}; -type Block = frame_system::mocking::MockBlock; +pub type Block = frame_system::mocking::MockBlock; frame_support::construct_runtime!( pub enum Test { System: frame_system::{Pallet, Call, Storage, Event}, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, EthereumRewards: snowbridge_pallet_rewards::{Pallet, Call, Storage, Event}, } ); @@ -28,13 +30,28 @@ frame_support::construct_runtime!( pub type Signature = MultiSignature; pub type AccountId = <::Signer as IdentifyAccount>::AccountId; +parameter_types! { + pub const ExistentialDeposit: u128 = 1; +} + +type Balance = u128; + +#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] +impl pallet_balances::Config for Test { + type Balance = Balance; + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; +} + #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type AccountId = AccountId; type Lookup = IdentityLookup; + type AccountData = pallet_balances::AccountData; type Block = Block; } + parameter_types! { pub EthereumNetwork: NetworkId = NetworkId::Ethereum { chain_id: 11155111 }; pub WethAddress: H160 = hex!("774667629726ec1FaBEbCEc0D9139bD1C8f72a23").into(); @@ -46,6 +63,8 @@ impl snowbridge_pallet_rewards::Config for Test { type EthereumNetwork = EthereumNetwork; type WethAddress = WethAddress; type XcmSender = MockXcmSender; + type Token = Balances; + type AssetTransactor = SuccessfulTransactor; type WeightInfo = (); } @@ -77,22 +96,40 @@ impl SendXcm for MockXcmSender { pub const WETH: u128 = 1_000_000_000_000_000_000; -pub fn last_events(n: usize) -> Vec { - frame_system::Pallet::::events() - .into_iter() - .rev() - .take(n) - .rev() - .map(|e| e.event) - .collect() -} - -pub fn expect_events(e: Vec) { - assert_eq!(last_events(e.len()), e); -} - pub fn new_tester() -> sp_io::TestExternalities { let t = frame_system::GenesisConfig::::default().build_storage().unwrap(); let ext = sp_io::TestExternalities::new(t); ext } + +pub struct SuccessfulTransactor; +impl TransactAsset for SuccessfulTransactor { + fn can_check_in(_origin: &Location, _what: &Asset, _context: &XcmContext) -> XcmResult { + Ok(()) + } + + fn can_check_out(_dest: &Location, _what: &Asset, _context: &XcmContext) -> XcmResult { + Ok(()) + } + + fn deposit_asset(_what: &Asset, _who: &Location, _context: Option<&XcmContext>) -> XcmResult { + Ok(()) + } + + fn withdraw_asset( + _what: &Asset, + _who: &Location, + _context: Option<&XcmContext>, + ) -> Result { + Ok(AssetsInHolding::default()) + } + + fn internal_transfer_asset( + _what: &Asset, + _from: &Location, + _to: &Location, + _context: &XcmContext, + ) -> Result { + Ok(AssetsInHolding::default()) + } +} diff --git a/bridges/snowbridge/pallets/rewards/src/tests.rs b/bridges/snowbridge/pallets/rewards/src/tests.rs index 4ea36bb85180..23fd4aa7da9d 100644 --- a/bridges/snowbridge/pallets/rewards/src/tests.rs +++ b/bridges/snowbridge/pallets/rewards/src/tests.rs @@ -3,8 +3,7 @@ use super::*; use crate::{ - mock::{expect_events, new_tester, AccountId, EthereumRewards, RuntimeOrigin, Test, WETH}, - Event as RewardEvent, + mock::{new_tester, AccountId, EthereumRewards, RuntimeOrigin, Test, WETH}, }; use frame_support::{assert_err, assert_ok}; use sp_core::H256; @@ -17,9 +16,6 @@ fn test_deposit() { let result = EthereumRewards::deposit(relayer.clone().into(), 2 * WETH); assert_ok!(result); assert_eq!(>::get(relayer.clone()), 2 * WETH); - //expect_events(vec![RewardEvent::RewardDeposited { - // account_id: relayer.clone(), value: 2 * WETH - //}]); // Check accumulation works let result2 = EthereumRewards::deposit(relayer.clone().into(), 3 * WETH); From 93e46f9c49bb330423786f50794856a50c383857 Mon Sep 17 00:00:00 2001 From: claravanstaden Date: Wed, 30 Oct 2024 13:04:42 +0200 Subject: [PATCH 28/37] move rewards work to relayer pallet --- Cargo.lock | 7 + bridges/modules/relayers/Cargo.toml | 15 +- bridges/modules/relayers/src/lib.rs | 249 ++++++++++++++++++ bridges/modules/relayers/src/mock.rs | 80 ++++++ bridges/modules/relayers/src/weights.rs | 21 ++ bridges/snowbridge/pallets/rewards/src/lib.rs | 13 +- .../snowbridge/pallets/rewards/src/mock.rs | 5 +- .../snowbridge/primitives/core/src/fees.rs | 7 +- .../primitives/core/src/location.rs | 1 + .../bridge-hub-westend/tests/snowbridge.rs | 2 + 10 files changed, 386 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 83b266a352ab..55fd82a1c68a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11079,6 +11079,8 @@ dependencies = [ "frame-benchmarking", "frame-support", "frame-system", + "hex", + "hex-literal", "log", "pallet-balances", "pallet-bridge-grandpa", @@ -11088,11 +11090,16 @@ dependencies = [ "pallet-utility", "parity-scale-codec", "scale-info", + "snowbridge-core", + "snowbridge-router-primitives", "sp-arithmetic 23.0.0", "sp-core 28.0.0", "sp-io 30.0.0", + "sp-keyring", "sp-runtime 31.0.1", "sp-std 14.0.0", + "staging-xcm", + "staging-xcm-executor", ] [[package]] diff --git a/bridges/modules/relayers/Cargo.toml b/bridges/modules/relayers/Cargo.toml index 0bf889bcca0e..e035ce30bf8d 100644 --- a/bridges/modules/relayers/Cargo.toml +++ b/bridges/modules/relayers/Cargo.toml @@ -32,8 +32,17 @@ pallet-transaction-payment = { workspace = true } sp-arithmetic = { workspace = true } sp-runtime = { workspace = true } sp-std = { workspace = true } +sp-core = { workspace = true } + +snowbridge-core = { workspace = true } +snowbridge-router-primitives = { workspace = true } + +xcm = { workspace = true } +xcm-executor = { workspace = true } [dev-dependencies] +hex = { workspace = true, default-features = true } +hex-literal = { workspace = true, default-features = true } bp-runtime = { workspace = true } pallet-balances = { workspace = true, default-features = true } sp-io = { workspace = true } @@ -42,7 +51,7 @@ bp-parachains = { workspace = true } bp-polkadot-core = { workspace = true } bp-test-utils = { workspace = true } pallet-utility = { workspace = true } -sp-core = { workspace = true } +sp-keyring = { workspace = true, default-features = true } [features] default = ["std"] @@ -64,12 +73,16 @@ std = [ "pallet-bridge-parachains/std", "pallet-transaction-payment/std", "pallet-utility/std", + "snowbridge-core/std", + "snowbridge-router-primitives/std", "scale-info/std", "sp-arithmetic/std", "sp-core/std", "sp-io/std", "sp-runtime/std", "sp-std/std", + "xcm-executor/std", + "xcm/std", ] runtime-benchmarks = [ "frame-benchmarking/runtime-benchmarks", diff --git a/bridges/modules/relayers/src/lib.rs b/bridges/modules/relayers/src/lib.rs index f06c2e16ac24..74134b5b5236 100644 --- a/bridges/modules/relayers/src/lib.rs +++ b/bridges/modules/relayers/src/lib.rs @@ -35,6 +35,19 @@ pub use payment_adapter::DeliveryConfirmationPaymentsAdapter; pub use stake_adapter::StakeAndSlashNamed; pub use weights::WeightInfo; pub use weights_ext::WeightInfoExt; +use frame_support::{ + traits::fungible::{Inspect, Mutate}, + PalletError, +}; +use frame_system::pallet_prelude::*; +pub use pallet::*; +use snowbridge_core::{fees::burn_fees, rewards::RewardLedger}; +use sp_core::H160; +use xcm::prelude::{send_xcm, SendError as XcmpSendError, *}; +use xcm_executor::traits::TransactAsset; +use sp_core::H256; + +extern crate alloc; mod mock; mod payment_adapter; @@ -49,6 +62,10 @@ pub mod weights; /// The target that will be used when publishing logs related to this pallet. pub const LOG_TARGET: &str = "runtime::bridge-relayers"; +pub type AccountIdOf = ::AccountId; +pub type BalanceOf = +<>::Token as Inspect<::AccountId>>::Balance; + #[frame_support::pallet] pub mod pallet { use super::*; @@ -82,6 +99,18 @@ pub mod pallet { type WeightInfo: WeightInfoExt; /// Lane identifier type. type LaneId: LaneIdType + Send + Sync; + /// AssetHub parachain ID + type AssetHubParaId: Get; + /// Ethereum network ID including the chain ID + type EthereumNetwork: Get; + /// Message relayers are rewarded with this asset + type WethAddress: Get; + /// XCM message sender + type XcmSender: SendXcm; + /// To withdraw and deposit an asset. + type AssetTransactor: TransactAsset; + type AssetHubXCMFee: Get; + type Token: Mutate + Inspect; } #[pallet::pallet] @@ -228,6 +257,20 @@ pub mod pallet { }, ) } + + /// Claim accumulated rewards. + #[pallet::call_index(3)] + #[pallet::weight((T::WeightInfo::claim(), DispatchClass::Operational))] + pub fn claim( + origin: OriginFor, + deposit_address: AccountIdOf, + value: BalanceOf, + message_id: H256, + ) -> DispatchResult { + let account_id = ensure_signed(origin)?; + Self::process_claim(account_id, deposit_address, value, message_id)?; + Ok(()) + } } impl, I: 'static> Pallet { @@ -391,6 +434,68 @@ pub mod pallet { Ok(()) } + + fn process_claim( + account_id: AccountIdOf, + deposit_address: AccountIdOf, + value: BalanceOf, + message_id: H256, + ) -> DispatchResult { + // Check if the claim value is equal to or less than the accumulated balance. + let reward_balance = RewardsMapping::::get(account_id.clone()); + if value > reward_balance { + return Err(Error::::InsufficientFunds.into()); + } + + let reward_asset = snowbridge_core::location::convert_token_address( + T::EthereumNetwork::get(), + T::WethAddress::get(), + ); + let cost2: u128 = + TryInto::::try_into(value).map_err(|_| Error::::InvalidAmount)?; + let deposit: Asset = (reward_asset, cost2).into(); + let beneficiary: Location = + Location::new(0, Parachain(T::AssetHubParaId::get().into())); + + let asset_hub_fee_asset: Asset = (Location::parent(), T::AssetHubXCMFee::get()).into(); + + //let account_32 = T::AccountId::decode(&mut &bytes[..]).unwrap_or_default(); + let origin_location: Location = Location::new(0, [Parachain(T::AssetHubParaId::get())]); + let fee: BalanceOf = T::AssetHubXCMFee::get().try_into().map_err(|_| Error::::InvalidFee)?; + burn_fees::>(origin_location, fee)?; + + let xcm: Xcm<()> = alloc::vec![ + // Teleport required fees. + ReceiveTeleportedAsset(asset_hub_fee_asset.clone().into()), + // Pay for execution. + BuyExecution { fees: asset_hub_fee_asset, weight_limit: Unlimited }, + DescendOrigin(PalletInstance(80).into()), + UniversalOrigin(GlobalConsensus(T::EthereumNetwork::get())), + ReserveAssetDeposited(deposit.clone().into()), + DepositAsset { assets: Definite(deposit.into()), beneficiary: beneficiary.clone() }, + SetAppendix(Xcm(alloc::vec![ + RefundSurplus, + DepositAsset { assets: AllCounted(1).into(), beneficiary }, + ])), + SetTopic(message_id.into()), + ] + .into(); + + // Deduct the reward from the claimable balance + RewardsMapping::::mutate(account_id.clone(), |current_value| { + *current_value = current_value.saturating_sub(value); + }); + + let dest = Location::new(1, [Parachain(T::AssetHubParaId::get().into())]); + let (_xcm_hash, _) = send_xcm::(dest, xcm).map_err(Error::::from)?; + + Self::deposit_event(Event::RewardClaimed { + account_id, + deposit_address, + value, + }); + Ok(()) + } } #[pallet::event] @@ -433,6 +538,21 @@ pub mod pallet { /// Registration that was removed. registration: Registration, T::Reward>, }, + /// A relayer reward was deposited + RewardDeposited { + /// The relayer account to which the reward was deposited. + account_id: AccountIdOf, + /// The reward value. + value: BalanceOf, + }, + RewardClaimed { + /// The relayer account that claimed the reward. + account_id: AccountIdOf, + /// The address that received the reward on AH. + deposit_address: AccountIdOf, + /// The claimed reward value. + value: BalanceOf, + }, } #[pallet::error] @@ -454,6 +574,39 @@ pub mod pallet { NotRegistered, /// Failed to `deregister` relayer, because lease is still active. RegistrationIsStillActive, + /// XCMP send failure + Send(SendError), + /// The relayer rewards balance is lower than the claimed amount. + InsufficientFunds, + InvalidAmount, + InvalidFee, + } + + #[derive(Clone, Encode, Decode, Eq, PartialEq, Debug, TypeInfo, PalletError)] + pub enum SendError { + NotApplicable, + NotRoutable, + Transport, + DestinationUnsupported, + ExceedsMaxMessageSize, + MissingArgument, + Fees, + } + + impl, I: 'static> From for Error { + fn from(e: XcmpSendError) -> Self { + match e { + XcmpSendError::NotApplicable => Error::::Send(SendError::NotApplicable), + XcmpSendError::Unroutable => Error::::Send(SendError::NotRoutable), + XcmpSendError::Transport(_) => Error::::Send(SendError::Transport), + XcmpSendError::DestinationUnsupported => + Error::::Send(SendError::DestinationUnsupported), + XcmpSendError::ExceedsMaxMessageSize => + Error::::Send(SendError::ExceedsMaxMessageSize), + XcmpSendError::MissingArgument => Error::::Send(SendError::MissingArgument), + XcmpSendError::Fees => Error::::Send(SendError::Fees), + } + } } /// Map of the relayer => accumulated reward. @@ -484,6 +637,21 @@ pub mod pallet { Registration, T::Reward>, OptionQuery, >; + + #[pallet::storage] + pub type RewardsMapping, I: 'static = ()> = + StorageMap<_, Identity, AccountIdOf, BalanceOf, ValueQuery>; + + impl, I: 'static> RewardLedger, BalanceOf> for Pallet { + fn deposit(account_id: AccountIdOf, value: BalanceOf) -> DispatchResult { + RewardsMapping::::mutate(account_id.clone(), |current_value| { + *current_value = current_value.saturating_add(value); + }); + Self::deposit_event(Event::RewardDeposited { account_id, value }); + + Ok(()) + } + } } #[cfg(test)] @@ -500,6 +668,7 @@ mod tests { }; use frame_system::{EventRecord, Pallet as System, Phase}; use sp_runtime::DispatchError; + use frame_support::assert_err; fn get_ready_for_events() { System::::set_block_number(1); @@ -943,4 +1112,84 @@ mod tests { assert!(Pallet::::is_registration_active(®ISTER_RELAYER)); }); } + + const WETH: u64 = 1_000_000_000_000_000_000; + + use bp_polkadot_core::AccountId; + use sp_keyring::AccountKeyring as Keyring; + #[test] + fn test_deposit() { + run_test(|| { + // Check a new deposit works + let relayer: AccountId = Keyring::Bob.into(); + println!("relayer ID: {:?}", relayer); + let result = Pallet::::deposit(relayer, 2 * WETH); + /* + assert_ok!(result); + assert_eq!(>::get(relayer.clone()), 2 * WETH); + + // Check accumulation works + let result2 = Pallet::::deposit(relayer.clone().into(), 3 * WETH); + assert_ok!(result2); + assert_eq!(>::get(relayer), 5 * WETH); + + // Check another relayer deposit works. + let another_relayer: AccountId = Keyring::Ferdie.into(); + let result3 = Pallet::::deposit(another_relayer.clone().into(), 1 * WETH); + assert_ok!(result3); + assert_eq!(>::get(another_relayer), 1 * WETH);*/ + }); + } + + #[test] + fn test_claim() { + //run_test(|| { + /* + let relayer: AccountId = Keyring::Bob.into(); + let message_id = H256::random(); + + let result = Pallet::::claim( + RuntimeOrigin::signed(relayer.clone()), + relayer.clone(), + 3 * WETH, + message_id, + ); + // No rewards yet + assert_err!(result, Error::::InsufficientFunds); + + // Deposit rewards + let result2 = Pallet::::deposit(relayer.clone(), 3 * WETH); + assert_ok!(result2); + + // Claim some rewards + let result3 = Pallet::::claim( + RuntimeOrigin::signed(relayer.clone()), + relayer.clone(), + 2 * WETH, + message_id, + ); + assert_ok!(result3); + assert_eq!(>::get(relayer.clone()), 1 * WETH); + + // Claim some rewards than available + let result4 = Pallet::::claim( + RuntimeOrigin::signed(relayer.clone()), + relayer.clone(), + 2 * WETH, + message_id, + ); + assert_err!(result4, Error::::InsufficientFunds); + + // Claim the remaining balance + let result5 = Pallet::::claim( + RuntimeOrigin::signed(relayer.clone()), + relayer.clone(), + 1 * WETH, + message_id, + ); + assert_ok!(result5); + assert_eq!(>::get(relayer.clone()), 0); + });*/ + } + } diff --git a/bridges/modules/relayers/src/mock.rs b/bridges/modules/relayers/src/mock.rs index d186e968e648..4450583c7388 100644 --- a/bridges/modules/relayers/src/mock.rs +++ b/bridges/modules/relayers/src/mock.rs @@ -40,6 +40,13 @@ use sp_runtime::{ traits::{BlakeTwo256, ConstU32}, BuildStorage, FixedPointNumber, Perquintill, StateVersion, }; +use sp_core::H160; +use xcm::{latest::SendXcm, prelude::*}; +use crate::XcmpSendError; +use hex_literal::hex; +use xcm_executor::AssetsInHolding; +use xcm_executor::traits::TransactAsset; +use sp_runtime::traits::ConstU128; /// Account identifier at `ThisChain`. pub type ThisChainAccountId = u64; @@ -277,6 +284,11 @@ impl pallet_bridge_messages::Config for TestRuntime { type BridgedHeaderChain = BridgeGrandpa; } +parameter_types! { + pub EthereumNetwork: NetworkId = NetworkId::Ethereum { chain_id: 11155111 }; + pub WethAddress: H160 = hex!("774667629726ec1FaBEbCEc0D9139bD1C8f72a23").into(); +} + impl pallet_bridge_relayers::Config for TestRuntime { type RuntimeEvent = RuntimeEvent; type Reward = ThisChainBalance; @@ -284,6 +296,15 @@ impl pallet_bridge_relayers::Config for TestRuntime { type StakeAndSlash = TestStakeAndSlash; type WeightInfo = (); type LaneId = TestLaneIdType; + + type Token = Balances; + type AssetHubParaId = ConstU32<1000>; + type EthereumNetwork = EthereumNetwork; + type WethAddress = WethAddress; + type XcmSender = MockXcmSender; + + type AssetTransactor = SuccessfulTransactor; + type AssetHubXCMFee = ConstU128<1_000_000_000_000u128>; } #[cfg(feature = "runtime-benchmarks")] @@ -371,6 +392,65 @@ impl MessageDispatch for DummyMessageDispatch { } } +// Mock XCM sender that always succeeds +pub struct MockXcmSender; + +impl SendXcm for MockXcmSender { + type Ticket = Xcm<()>; + + fn validate( + dest: &mut Option, + xcm: &mut Option>, + ) -> SendResult { + if let Some(location) = dest { + match location.unpack() { + (_, [Parachain(1001)]) => return Err(XcmpSendError::NotApplicable), + _ => Ok((xcm.clone().unwrap(), Assets::default())), + } + } else { + Ok((xcm.clone().unwrap(), Assets::default())) + } + } + + fn deliver(xcm: Self::Ticket) -> core::result::Result { + let hash = xcm.using_encoded(sp_io::hashing::blake2_256); + Ok(hash) + } +} + +pub struct SuccessfulTransactor; +impl TransactAsset for SuccessfulTransactor { + fn can_check_in(_origin: &Location, _what: &Asset, _context: &XcmContext) -> XcmResult { + Ok(()) + } + + fn can_check_out(_dest: &Location, _what: &Asset, _context: &XcmContext) -> XcmResult { + Ok(()) + } + + fn deposit_asset(_what: &Asset, _who: &Location, _context: Option<&XcmContext>) -> XcmResult { + Ok(()) + } + + fn withdraw_asset( + _what: &Asset, + _who: &Location, + _context: Option<&XcmContext>, + ) -> Result { + Ok(AssetsInHolding::default()) + } + + fn internal_transfer_asset( + _what: &Asset, + _from: &Location, + _to: &Location, + _context: &XcmContext, + ) -> Result { + Ok(AssetsInHolding::default()) + } +} + + /// Reward account params that we are using in tests. pub fn test_reward_account_param() -> RewardsAccountParams { RewardsAccountParams::new( diff --git a/bridges/modules/relayers/src/weights.rs b/bridges/modules/relayers/src/weights.rs index c2c065b0c0a2..642d26752e6a 100644 --- a/bridges/modules/relayers/src/weights.rs +++ b/bridges/modules/relayers/src/weights.rs @@ -55,6 +55,7 @@ pub trait WeightInfo { fn deregister() -> Weight; fn slash_and_deregister() -> Weight; fn register_relayer_reward() -> Weight; + fn claim() -> Weight; } /// Weights for `pallet_bridge_relayers` that are generated using one of the Bridge testnets. @@ -157,6 +158,16 @@ impl WeightInfo for BridgeWeight { .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } + + fn claim() -> Weight { + // Proof Size summary in bytes: + // Measured: `80` + // Estimated: `3517` + // Minimum execution time: 44_000_000 picoseconds. + Weight::from_parts(44_000_000, 3517) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } } // For backwards compatibility and tests @@ -256,4 +267,14 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } + + fn claim() -> Weight { + // Proof Size summary in bytes: + // Measured: `80` + // Estimated: `3517` + // Minimum execution time: 44_000_000 picoseconds. + Weight::from_parts(44_000_000, 3517) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } } diff --git a/bridges/snowbridge/pallets/rewards/src/lib.rs b/bridges/snowbridge/pallets/rewards/src/lib.rs index e9bf4bbf88b1..58d4f6629b69 100644 --- a/bridges/snowbridge/pallets/rewards/src/lib.rs +++ b/bridges/snowbridge/pallets/rewards/src/lib.rs @@ -48,6 +48,7 @@ pub mod pallet { type XcmSender: SendXcm; /// To withdraw and deposit an asset. type AssetTransactor: TransactAsset; + type AssetHubXCMFee: Get; /// Message relayers are rewarded with this asset type Token: Mutate + Inspect; type WeightInfo: WeightInfo; @@ -154,13 +155,13 @@ pub mod pallet { let deposit: Asset = (reward_asset, cost2).into(); let beneficiary: Location = Location::new(0, Parachain(T::AssetHubParaId::get().into())); - let bridge_location = Location::new(2, GlobalConsensus(T::EthereumNetwork::get())); - let xcm_fee: u128 = 10_000_000_000; // TODO not sure what this should be - let asset_hub_fee_asset: Asset = (Location::parent(), xcm_fee).into(); + let asset_hub_fee_asset: Asset = (Location::parent(), T::AssetHubXCMFee::get()).into(); - let fee: BalanceOf = xcm_fee.try_into().map_err(|_| Error::::InvalidFee)?; - burn_fees::>(T::AssetHubParaId::get().into(), fee)?; + let account_32 = T::AccountId::decode(&mut &bytes[..]).unwrap_or_default(); + let origin_location = Location::new(0, [Parachain(T::AssetHubParaId::get().into()), AccountId32{network: None, id: account_id.into()}].into()); + let fee: BalanceOf = T::AssetHubXCMFee::get().try_into().map_err(|_| Error::::InvalidFee)?; + burn_fees::>(origin_location, fee)?; let xcm: Xcm<()> = alloc::vec![ // Teleport required fees. @@ -173,7 +174,7 @@ pub mod pallet { DepositAsset { assets: Definite(deposit.into()), beneficiary }, SetAppendix(Xcm(alloc::vec![ RefundSurplus, - DepositAsset { assets: AllCounted(1).into(), beneficiary: bridge_location }, + DepositAsset { assets: AllCounted(1).into(), beneficiary: beneficiary }, ])), SetTopic(message_id.into()), ] diff --git a/bridges/snowbridge/pallets/rewards/src/mock.rs b/bridges/snowbridge/pallets/rewards/src/mock.rs index a4c8e88f6224..272d54d18d28 100644 --- a/bridges/snowbridge/pallets/rewards/src/mock.rs +++ b/bridges/snowbridge/pallets/rewards/src/mock.rs @@ -5,7 +5,7 @@ use super::*; use codec::Encode; use frame_support::{derive_impl, parameter_types}; use hex_literal::hex; -use sp_core::{ConstU32, H160}; +use sp_core::{ConstU128, ConstU32, H160}; use sp_runtime::{ traits::{IdentifyAccount, IdentityLookup, Verify}, BuildStorage, MultiSignature, @@ -65,6 +65,7 @@ impl snowbridge_pallet_rewards::Config for Test { type XcmSender = MockXcmSender; type Token = Balances; type AssetTransactor = SuccessfulTransactor; + type AssetHubXCMFee = ConstU128<1_000_000_000_000u128>; type WeightInfo = (); } @@ -94,8 +95,6 @@ impl SendXcm for MockXcmSender { } } -pub const WETH: u128 = 1_000_000_000_000_000_000; - pub fn new_tester() -> sp_io::TestExternalities { let t = frame_system::GenesisConfig::::default().build_storage().unwrap(); let ext = sp_io::TestExternalities::new(t); diff --git a/bridges/snowbridge/primitives/core/src/fees.rs b/bridges/snowbridge/primitives/core/src/fees.rs index 3b122cc32bda..0ddfeab81513 100644 --- a/bridges/snowbridge/primitives/core/src/fees.rs +++ b/bridges/snowbridge/primitives/core/src/fees.rs @@ -1,20 +1,19 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-FileCopyrightText: 2023 Snowfork -use crate::ParaId; use log; use sp_runtime::{DispatchResult, SaturatedConversion, Saturating, TokenError}; -use xcm::opaque::lts::{Junction::Parachain, Location, XcmContext}; +use xcm::opaque::lts::{Location, XcmContext}; use xcm_executor::traits::TransactAsset; const LOG_TARGET: &str = "xcm_fees"; /// Burns the fees embedded in the XCM for teleports. -pub fn burn_fees(para_id: ParaId, fee: Balance) -> DispatchResult +pub fn burn_fees(dest: Location, fee: Balance) -> DispatchResult where AssetTransactor: TransactAsset, Balance: Saturating + TryInto + Copy, { let dummy_context = XcmContext { origin: None, message_id: Default::default(), topic: None }; - let dest = Location::new(1, [Parachain(para_id.into())]); + //let dest = Location::new(1, [Parachain(para_id.into())]); let fees = (Location::parent(), fee.saturated_into::()).into(); // Check if the asset can be checked out diff --git a/bridges/snowbridge/primitives/core/src/location.rs b/bridges/snowbridge/primitives/core/src/location.rs index 55b75998e92b..dc59e5a58fc6 100644 --- a/bridges/snowbridge/primitives/core/src/location.rs +++ b/bridges/snowbridge/primitives/core/src/location.rs @@ -210,3 +210,4 @@ mod tests { pub fn convert_token_address(network: NetworkId, token: H160) -> Location { Location::new(2, [GlobalConsensus(network), AccountKey20 { network: None, key: token.into() }]) } + diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/snowbridge.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/snowbridge.rs index c5f3871c0790..605a557b8c58 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/snowbridge.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/snowbridge.rs @@ -123,6 +123,8 @@ fn max_message_queue_service_weight_is_more_than_beacon_extrinsic_weights() { #[test] fn ethereum_client_consensus_extrinsics_work() { + println!("RuntimeBlockWeights: {:?}", bridge_hub_westend_runtime::RuntimeBlockWeights::get()); + snowbridge_runtime_test_common::ethereum_extrinsic( collator_session_keys(), BRIDGE_HUB_WESTEND_PARACHAIN_ID, From a97785f6ac5aa0aedc3624fa8e064c04f064db6a Mon Sep 17 00:00:00 2001 From: claravanstaden Date: Wed, 30 Oct 2024 13:09:40 +0200 Subject: [PATCH 29/37] tests --- bridges/modules/relayers/src/lib.rs | 45 +++++++++++++---------------- 1 file changed, 20 insertions(+), 25 deletions(-) diff --git a/bridges/modules/relayers/src/lib.rs b/bridges/modules/relayers/src/lib.rs index 74134b5b5236..8e7d5797f830 100644 --- a/bridges/modules/relayers/src/lib.rs +++ b/bridges/modules/relayers/src/lib.rs @@ -1115,42 +1115,37 @@ mod tests { const WETH: u64 = 1_000_000_000_000_000_000; - use bp_polkadot_core::AccountId; - use sp_keyring::AccountKeyring as Keyring; #[test] fn test_deposit() { run_test(|| { // Check a new deposit works - let relayer: AccountId = Keyring::Bob.into(); - println!("relayer ID: {:?}", relayer); + let relayer: u64 = 1; let result = Pallet::::deposit(relayer, 2 * WETH); - /* assert_ok!(result); - assert_eq!(>::get(relayer.clone()), 2 * WETH); + assert_eq!(>::get(relayer), 2 * WETH); // Check accumulation works - let result2 = Pallet::::deposit(relayer.clone().into(), 3 * WETH); + let result2 = Pallet::::deposit(relayer, 3 * WETH); assert_ok!(result2); assert_eq!(>::get(relayer), 5 * WETH); // Check another relayer deposit works. - let another_relayer: AccountId = Keyring::Ferdie.into(); - let result3 = Pallet::::deposit(another_relayer.clone().into(), 1 * WETH); + let another_relayer: u64 = 2; + let result3 = Pallet::::deposit(another_relayer, 1 * WETH); assert_ok!(result3); - assert_eq!(>::get(another_relayer), 1 * WETH);*/ + assert_eq!(>::get(another_relayer), 1 * WETH); }); } #[test] fn test_claim() { - //run_test(|| { - /* - let relayer: AccountId = Keyring::Bob.into(); + run_test(|| { + let relayer: u64 = 1; let message_id = H256::random(); let result = Pallet::::claim( - RuntimeOrigin::signed(relayer.clone()), - relayer.clone(), + RuntimeOrigin::signed(relayer), + relayer, 3 * WETH, message_id, ); @@ -1158,23 +1153,23 @@ mod tests { assert_err!(result, Error::::InsufficientFunds); // Deposit rewards - let result2 = Pallet::::deposit(relayer.clone(), 3 * WETH); + let result2 = Pallet::::deposit(relayer, 3 * WETH); assert_ok!(result2); // Claim some rewards let result3 = Pallet::::claim( - RuntimeOrigin::signed(relayer.clone()), - relayer.clone(), + RuntimeOrigin::signed(relayer), + relayer, 2 * WETH, message_id, ); assert_ok!(result3); - assert_eq!(>::get(relayer.clone()), 1 * WETH); + assert_eq!(>::get(relayer), 1 * WETH); // Claim some rewards than available let result4 = Pallet::::claim( - RuntimeOrigin::signed(relayer.clone()), - relayer.clone(), + RuntimeOrigin::signed(relayer), + relayer, 2 * WETH, message_id, ); @@ -1182,14 +1177,14 @@ mod tests { // Claim the remaining balance let result5 = Pallet::::claim( - RuntimeOrigin::signed(relayer.clone()), - relayer.clone(), + RuntimeOrigin::signed(relayer), + relayer, 1 * WETH, message_id, ); assert_ok!(result5); - assert_eq!(>::get(relayer.clone()), 0); - });*/ + assert_eq!(>::get(relayer), 0); + }); } } From f6efbd4742c82d2cc53febac837adec8b6ac230a Mon Sep 17 00:00:00 2001 From: claravanstaden Date: Wed, 30 Oct 2024 13:50:32 +0200 Subject: [PATCH 30/37] refactoring --- bridges/modules/relayers/src/lib.rs | 88 +++++-------------- .../snowbridge/primitives/core/src/fees.rs | 7 +- 2 files changed, 26 insertions(+), 69 deletions(-) diff --git a/bridges/modules/relayers/src/lib.rs b/bridges/modules/relayers/src/lib.rs index 8e7d5797f830..8900d86d2480 100644 --- a/bridges/modules/relayers/src/lib.rs +++ b/bridges/modules/relayers/src/lib.rs @@ -40,12 +40,10 @@ use frame_support::{ PalletError, }; use frame_system::pallet_prelude::*; -pub use pallet::*; -use snowbridge_core::{fees::burn_fees, rewards::RewardLedger}; +use snowbridge_core::rewards::RewardLedger; use sp_core::H160; use xcm::prelude::{send_xcm, SendError as XcmpSendError, *}; use xcm_executor::traits::TransactAsset; -use sp_core::H256; extern crate alloc; @@ -71,7 +69,6 @@ pub mod pallet { use super::*; use bp_messages::LaneIdType; use frame_support::pallet_prelude::*; - use frame_system::pallet_prelude::*; /// `RelayerRewardsKeyProvider` for given configuration. type RelayerRewardsKeyProviderOf = RelayerRewardsKeyProvider< @@ -263,12 +260,10 @@ pub mod pallet { #[pallet::weight((T::WeightInfo::claim(), DispatchClass::Operational))] pub fn claim( origin: OriginFor, - deposit_address: AccountIdOf, - value: BalanceOf, - message_id: H256, + deposit_location: Location, ) -> DispatchResult { let account_id = ensure_signed(origin)?; - Self::process_claim(account_id, deposit_address, value, message_id)?; + Self::process_claim(account_id, deposit_location)?; Ok(()) } } @@ -437,61 +432,44 @@ pub mod pallet { fn process_claim( account_id: AccountIdOf, - deposit_address: AccountIdOf, - value: BalanceOf, - message_id: H256, + deposit_location: Location, ) -> DispatchResult { - // Check if the claim value is equal to or less than the accumulated balance. - let reward_balance = RewardsMapping::::get(account_id.clone()); - if value > reward_balance { + let value = RewardsMapping::::get(account_id.clone()); + if value.is_zero() { return Err(Error::::InsufficientFunds.into()); } - let reward_asset = snowbridge_core::location::convert_token_address( T::EthereumNetwork::get(), T::WethAddress::get(), ); - let cost2: u128 = + let reward_balance: u128 = TryInto::::try_into(value).map_err(|_| Error::::InvalidAmount)?; - let deposit: Asset = (reward_asset, cost2).into(); - let beneficiary: Location = - Location::new(0, Parachain(T::AssetHubParaId::get().into())); + let deposit: Asset = (reward_asset, reward_balance).into(); let asset_hub_fee_asset: Asset = (Location::parent(), T::AssetHubXCMFee::get()).into(); - //let account_32 = T::AccountId::decode(&mut &bytes[..]).unwrap_or_default(); - let origin_location: Location = Location::new(0, [Parachain(T::AssetHubParaId::get())]); - let fee: BalanceOf = T::AssetHubXCMFee::get().try_into().map_err(|_| Error::::InvalidFee)?; - burn_fees::>(origin_location, fee)?; - let xcm: Xcm<()> = alloc::vec![ - // Teleport required fees. - ReceiveTeleportedAsset(asset_hub_fee_asset.clone().into()), - // Pay for execution. - BuyExecution { fees: asset_hub_fee_asset, weight_limit: Unlimited }, DescendOrigin(PalletInstance(80).into()), UniversalOrigin(GlobalConsensus(T::EthereumNetwork::get())), ReserveAssetDeposited(deposit.clone().into()), - DepositAsset { assets: Definite(deposit.into()), beneficiary: beneficiary.clone() }, + BuyExecution { fees: asset_hub_fee_asset, weight_limit: Unlimited }, + DepositAsset { assets: AllCounted(1).into(), beneficiary: deposit_location.clone() }, SetAppendix(Xcm(alloc::vec![ RefundSurplus, - DepositAsset { assets: AllCounted(1).into(), beneficiary }, + DepositAsset { assets: AllCounted(1).into(), beneficiary: deposit_location.clone() }, ])), - SetTopic(message_id.into()), ] .into(); - // Deduct the reward from the claimable balance - RewardsMapping::::mutate(account_id.clone(), |current_value| { - *current_value = current_value.saturating_sub(value); - }); + // Remove the reward since it has been claimed. + RewardsMapping::::remove(account_id.clone()); let dest = Location::new(1, [Parachain(T::AssetHubParaId::get().into())]); let (_xcm_hash, _) = send_xcm::(dest, xcm).map_err(Error::::from)?; Self::deposit_event(Event::RewardClaimed { account_id, - deposit_address, + deposit_location, value, }); Ok(()) @@ -548,8 +526,8 @@ pub mod pallet { RewardClaimed { /// The relayer account that claimed the reward. account_id: AccountIdOf, - /// The address that received the reward on AH. - deposit_address: AccountIdOf, + /// The location that received the reward on AH. + deposit_location: Location, /// The claimed reward value. value: BalanceOf, }, @@ -579,7 +557,6 @@ pub mod pallet { /// The relayer rewards balance is lower than the claimed amount. InsufficientFunds, InvalidAmount, - InvalidFee, } #[derive(Clone, Encode, Decode, Eq, PartialEq, Debug, TypeInfo, PalletError)] @@ -657,6 +634,7 @@ pub mod pallet { #[cfg(test)] mod tests { use super::*; + use sp_core::H256; use bp_messages::LaneIdType; use mock::{RuntimeEvent as TestEvent, *}; @@ -1141,13 +1119,12 @@ mod tests { fn test_claim() { run_test(|| { let relayer: u64 = 1; - let message_id = H256::random(); + let interior: InteriorLocation = [Parachain(1000), Junction::AccountId32{network: None, id: H256::random().into()}].into(); + let claim_location = Location::new(1, interior); let result = Pallet::::claim( RuntimeOrigin::signed(relayer), - relayer, - 3 * WETH, - message_id, + claim_location.clone(), ); // No rewards yet assert_err!(result, Error::::InsufficientFunds); @@ -1156,33 +1133,12 @@ mod tests { let result2 = Pallet::::deposit(relayer, 3 * WETH); assert_ok!(result2); - // Claim some rewards + // Claim rewards let result3 = Pallet::::claim( RuntimeOrigin::signed(relayer), - relayer, - 2 * WETH, - message_id, + claim_location, ); assert_ok!(result3); - assert_eq!(>::get(relayer), 1 * WETH); - - // Claim some rewards than available - let result4 = Pallet::::claim( - RuntimeOrigin::signed(relayer), - relayer, - 2 * WETH, - message_id, - ); - assert_err!(result4, Error::::InsufficientFunds); - - // Claim the remaining balance - let result5 = Pallet::::claim( - RuntimeOrigin::signed(relayer), - relayer, - 1 * WETH, - message_id, - ); - assert_ok!(result5); assert_eq!(>::get(relayer), 0); }); } diff --git a/bridges/snowbridge/primitives/core/src/fees.rs b/bridges/snowbridge/primitives/core/src/fees.rs index 0ddfeab81513..3b122cc32bda 100644 --- a/bridges/snowbridge/primitives/core/src/fees.rs +++ b/bridges/snowbridge/primitives/core/src/fees.rs @@ -1,19 +1,20 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-FileCopyrightText: 2023 Snowfork +use crate::ParaId; use log; use sp_runtime::{DispatchResult, SaturatedConversion, Saturating, TokenError}; -use xcm::opaque::lts::{Location, XcmContext}; +use xcm::opaque::lts::{Junction::Parachain, Location, XcmContext}; use xcm_executor::traits::TransactAsset; const LOG_TARGET: &str = "xcm_fees"; /// Burns the fees embedded in the XCM for teleports. -pub fn burn_fees(dest: Location, fee: Balance) -> DispatchResult +pub fn burn_fees(para_id: ParaId, fee: Balance) -> DispatchResult where AssetTransactor: TransactAsset, Balance: Saturating + TryInto + Copy, { let dummy_context = XcmContext { origin: None, message_id: Default::default(), topic: None }; - //let dest = Location::new(1, [Parachain(para_id.into())]); + let dest = Location::new(1, [Parachain(para_id.into())]); let fees = (Location::parent(), fee.saturated_into::()).into(); // Check if the asset can be checked out From 6e7818770a5fad1223b22fe05e4737a8ca3811bf Mon Sep 17 00:00:00 2001 From: claravanstaden Date: Wed, 30 Oct 2024 14:12:38 +0200 Subject: [PATCH 31/37] fix inbound queue --- Cargo.lock | 31 +-- Cargo.toml | 2 - .../pallets/inbound-queue-v2/Cargo.toml | 4 +- .../pallets/inbound-queue-v2/src/mock.rs | 44 +++- bridges/snowbridge/pallets/rewards/Cargo.toml | 75 ------- bridges/snowbridge/pallets/rewards/src/lib.rs | 211 ------------------ .../snowbridge/pallets/rewards/src/mock.rs | 134 ----------- .../snowbridge/pallets/rewards/src/tests.rs | 81 ------- .../snowbridge/pallets/rewards/src/weights.rs | 58 ----- .../bridges/bridge-hub-westend/Cargo.toml | 2 +- .../bridge-hubs/bridge-hub-westend/Cargo.toml | 2 - 11 files changed, 48 insertions(+), 596 deletions(-) delete mode 100644 bridges/snowbridge/pallets/rewards/Cargo.toml delete mode 100644 bridges/snowbridge/pallets/rewards/src/lib.rs delete mode 100644 bridges/snowbridge/pallets/rewards/src/mock.rs delete mode 100644 bridges/snowbridge/pallets/rewards/src/tests.rs delete mode 100644 bridges/snowbridge/pallets/rewards/src/weights.rs diff --git a/Cargo.lock b/Cargo.lock index 55fd82a1c68a..e51574970537 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2405,6 +2405,7 @@ dependencies = [ "pallet-assets", "pallet-balances", "pallet-bridge-messages", + "pallet-bridge-relayers", "pallet-message-queue", "pallet-xcm", "pallet-xcm-bridge-hub", @@ -2417,7 +2418,6 @@ dependencies = [ "snowbridge-pallet-inbound-queue-fixtures", "snowbridge-pallet-outbound-queue", "snowbridge-pallet-outbound-queue-v2", - "snowbridge-pallet-rewards", "snowbridge-pallet-system", "snowbridge-router-primitives", "sp-core 28.0.0", @@ -2500,7 +2500,6 @@ dependencies = [ "snowbridge-pallet-inbound-queue-v2", "snowbridge-pallet-outbound-queue", "snowbridge-pallet-outbound-queue-v2", - "snowbridge-pallet-rewards", "snowbridge-pallet-system", "snowbridge-router-primitives", "snowbridge-runtime-common", @@ -21089,12 +21088,15 @@ version = "0.2.0" dependencies = [ "alloy-primitives", "alloy-sol-types", + "bp-messages", + "bp-relayers", "frame-benchmarking", "frame-support", "frame-system", "hex-literal", "log", "pallet-balances", + "pallet-bridge-relayers", "parity-scale-codec", "scale-info", "serde", @@ -21102,7 +21104,6 @@ dependencies = [ "snowbridge-core", "snowbridge-pallet-ethereum-client", "snowbridge-pallet-inbound-queue-fixtures-v2", - "snowbridge-pallet-rewards", "snowbridge-router-primitives", "sp-core 28.0.0", "sp-io 30.0.0", @@ -21162,30 +21163,6 @@ dependencies = [ "sp-std 14.0.0", ] -[[package]] -name = "snowbridge-pallet-rewards" -version = "0.1.0" -dependencies = [ - "frame-benchmarking", - "frame-support", - "frame-system", - "hex", - "hex-literal", - "log", - "pallet-balances", - "parity-scale-codec", - "scale-info", - "snowbridge-core", - "snowbridge-router-primitives", - "sp-core 28.0.0", - "sp-io 30.0.0", - "sp-keyring", - "sp-runtime 31.0.1", - "sp-std 14.0.0", - "staging-xcm", - "staging-xcm-executor", -] - [[package]] name = "snowbridge-pallet-system" version = "0.2.0" diff --git a/Cargo.toml b/Cargo.toml index 070ecce5208c..aec739df16b4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,7 +56,6 @@ members = [ "bridges/snowbridge/pallets/outbound-queue-v2", "bridges/snowbridge/pallets/outbound-queue-v2/runtime-api", "bridges/snowbridge/pallets/outbound-queue/runtime-api", - "bridges/snowbridge/pallets/rewards", "bridges/snowbridge/pallets/system", "bridges/snowbridge/pallets/system/runtime-api", "bridges/snowbridge/primitives/beacon", @@ -1218,7 +1217,6 @@ snowbridge-pallet-inbound-queue-v2 = { path = "bridges/snowbridge/pallets/inboun snowbridge-pallet-outbound-queue = { path = "bridges/snowbridge/pallets/outbound-queue", default-features = false } snowbridge-pallet-outbound-queue-v2 = { path = "bridges/snowbridge/pallets/outbound-queue-v2", default-features = false } snowbridge-pallet-system = { path = "bridges/snowbridge/pallets/system", default-features = false } -snowbridge-pallet-rewards = { path = "bridges/snowbridge/pallets/rewards", default-features = false } snowbridge-router-primitives = { path = "bridges/snowbridge/primitives/router", default-features = false } snowbridge-runtime-common = { path = "bridges/snowbridge/runtime/runtime-common", default-features = false } snowbridge-runtime-test-common = { path = "bridges/snowbridge/runtime/test-common", default-features = false } diff --git a/bridges/snowbridge/pallets/inbound-queue-v2/Cargo.toml b/bridges/snowbridge/pallets/inbound-queue-v2/Cargo.toml index 898ca6dd5745..d80c477c6fbd 100644 --- a/bridges/snowbridge/pallets/inbound-queue-v2/Cargo.toml +++ b/bridges/snowbridge/pallets/inbound-queue-v2/Cargo.toml @@ -44,7 +44,9 @@ snowbridge-pallet-inbound-queue-fixtures-v2 = { optional = true, workspace = tru frame-benchmarking = { workspace = true, default-features = true } sp-keyring = { workspace = true, default-features = true } snowbridge-pallet-ethereum-client = { workspace = true, default-features = true } -snowbridge-pallet-rewards = { workspace = true, default-features = true } +pallet-bridge-relayers = { workspace = true, default-features = true } +bp-relayers = { workspace = true, default-features = true } +bp-messages = { workspace = true, default-features = true } hex-literal = { workspace = true, default-features = true } [features] diff --git a/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs b/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs index 5a30c0ac6f9a..abc296bb7de6 100644 --- a/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs +++ b/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs @@ -20,6 +20,12 @@ use sp_std::{convert::From, default::Default}; use xcm::{latest::SendXcm, prelude::*}; use xcm_executor::traits::TransactAsset; use xcm_executor::AssetsInHolding; +use sp_core::ConstU128; +use bp_relayers::{ + PayRewardFromAccount, RewardsAccountParams, +}; +use bp_relayers::PaymentProcedure; +use bp_messages::{HashedLaneId, LaneIdType}; use crate::{self as inbound_queue}; @@ -32,7 +38,7 @@ frame_support::construct_runtime!( System: frame_system::{Pallet, Call, Storage, Event}, Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, EthereumBeaconClient: snowbridge_pallet_ethereum_client::{Pallet, Call, Storage, Event}, - EthereumRewards: snowbridge_pallet_rewards::{Pallet, Call, Storage, Event}, + BridgeRelayers: pallet_bridge_relayers::{Pallet, Call, Storage, Event}, InboundQueue: inbound_queue::{Pallet, Call, Storage, Event}, } ); @@ -178,15 +184,45 @@ parameter_types! { pub WethAddress: H160 = hex!("774667629726ec1FaBEbCEc0D9139bD1C8f72a23").into(); } -impl snowbridge_pallet_rewards::Config for Test { +pub type TestLaneIdType = HashedLaneId; + +pub struct TestPaymentProcedure; + + +impl TestPaymentProcedure { + pub fn rewards_account(params: RewardsAccountParams) -> AccountId { + PayRewardFromAccount::<(), AccountId, TestLaneIdType>::rewards_account(params) + } +} + +impl PaymentProcedure for TestPaymentProcedure { + type Error = (); + type LaneId = TestLaneIdType; + + fn pay_reward( + relayer: &AccountId, + _lane_id: RewardsAccountParams, + _reward: Balance, + ) -> Result<(), Self::Error> { + Ok(()) + } +} + +impl pallet_bridge_relayers::Config for Test { type RuntimeEvent = RuntimeEvent; + type Reward = Balance; + type PaymentProcedure = TestPaymentProcedure; + type StakeAndSlash = (); + type WeightInfo = (); + type LaneId = TestLaneIdType; + type Token = Balances; type AssetHubParaId = ConstU32<1000>; type EthereumNetwork = EthereumNetwork; type WethAddress = WethAddress; type XcmSender = MockXcmSender; + type AssetTransactor = SuccessfulTransactor; - type Token = Balances; - type WeightInfo = (); + type AssetHubXCMFee = ConstU128<1_000_000_000_000u128>; } pub fn last_events(n: usize) -> Vec { diff --git a/bridges/snowbridge/pallets/rewards/Cargo.toml b/bridges/snowbridge/pallets/rewards/Cargo.toml deleted file mode 100644 index 79b03a0e2227..000000000000 --- a/bridges/snowbridge/pallets/rewards/Cargo.toml +++ /dev/null @@ -1,75 +0,0 @@ -[package] -name = "snowbridge-pallet-rewards" -description = "Snowbridge Rewards Pallet" -version = "0.1.0" -authors = ["Snowfork "] -edition.workspace = true -repository.workspace = true -license = "Apache-2.0" -categories = ["cryptography::cryptocurrencies"] - -[lints] -workspace = true - -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] - -[dependencies] -codec = { features = [ - "derive", -], workspace = true } -scale-info = { features = ["derive"], workspace = true } -frame-benchmarking = { optional = true, workspace = true } -frame-support = { workspace = true } -frame-system = { workspace = true } -log = { workspace = true } - -sp-core = { workspace = true } -sp-std = { workspace = true } -sp-io = { workspace = true } -sp-runtime = { workspace = true } - -xcm = { workspace = true } -xcm-executor = { workspace = true } - -snowbridge-core = { workspace = true } -snowbridge-router-primitives = { workspace = true } - -[dev-dependencies] -hex = { workspace = true, default-features = true } -hex-literal = { workspace = true, default-features = true } -sp-keyring = { workspace = true, default-features = true } -pallet-balances = { workspace = true, default-features = true } - -[features] -default = ["std"] -std = [ - "codec/std", - "frame-benchmarking?/std", - "frame-support/std", - "frame-system/std", - "log/std", - "scale-info/std", - "snowbridge-core/std", - "snowbridge-router-primitives/std", - "sp-core/std", - "sp-io/std", - "sp-runtime/std", - "sp-std/std", - "sp-keyring/std", - "xcm-executor/std", - "xcm/std", -] -runtime-benchmarks = [ - "frame-benchmarking/runtime-benchmarks", - "frame-support/runtime-benchmarks", - "frame-system/runtime-benchmarks", - "snowbridge-core/runtime-benchmarks", - "sp-runtime/runtime-benchmarks", - "xcm-executor/runtime-benchmarks", -] -try-runtime = [ - "frame-support/try-runtime", - "frame-system/try-runtime", - "sp-runtime/try-runtime", -] diff --git a/bridges/snowbridge/pallets/rewards/src/lib.rs b/bridges/snowbridge/pallets/rewards/src/lib.rs deleted file mode 100644 index 58d4f6629b69..000000000000 --- a/bridges/snowbridge/pallets/rewards/src/lib.rs +++ /dev/null @@ -1,211 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// SPDX-FileCopyrightText: 2023 Snowfork -#![cfg_attr(not(feature = "std"), no_std)] - -pub mod weights; - -#[cfg(test)] -mod mock; - -#[cfg(test)] -mod tests; - -extern crate alloc; - -use frame_support::{ - sp_runtime::Saturating, - traits::fungible::{Inspect, Mutate}, - PalletError, -}; -use frame_system::pallet_prelude::*; -pub use pallet::*; -use snowbridge_core::{fees::burn_fees, rewards::RewardLedger}; -use sp_core::H160; -pub use weights::WeightInfo; -use xcm::prelude::{send_xcm, SendError as XcmpSendError, *}; -use xcm_executor::traits::TransactAsset; -pub const LOG_TARGET: &str = "rewards"; - -pub type AccountIdOf = ::AccountId; -type BalanceOf = - <::Token as Inspect<::AccountId>>::Balance; -#[frame_support::pallet] -pub mod pallet { - use super::*; - use frame_support::pallet_prelude::*; - use sp_core::H256; - - #[pallet::pallet] - pub struct Pallet(_); - - #[pallet::config] - pub trait Config: frame_system::Config { - type RuntimeEvent: From> + IsType<::RuntimeEvent>; - type AssetHubParaId: Get; - type EthereumNetwork: Get; - type WethAddress: Get; - /// XCM message sender - type XcmSender: SendXcm; - /// To withdraw and deposit an asset. - type AssetTransactor: TransactAsset; - type AssetHubXCMFee: Get; - /// Message relayers are rewarded with this asset - type Token: Mutate + Inspect; - type WeightInfo: WeightInfo; - } - - #[pallet::event] - #[pallet::generate_deposit(pub(super) fn deposit_event)] - pub enum Event { - /// A relayer reward was deposited - RewardDeposited { - /// The relayer account to which the reward was deposited. - account_id: AccountIdOf, - /// The reward value. - value: BalanceOf, - }, - RewardClaimed { - /// The relayer account that claimed the reward. - account_id: AccountIdOf, - /// The address that received the reward on AH. - deposit_address: AccountIdOf, - /// The claimed reward value. - value: BalanceOf, - /// The message ID that was provided, used to track the claim - message_id: H256, - }, - } - - #[pallet::error] - pub enum Error { - /// XCMP send failure - Send(SendError), - /// The relayer rewards balance is lower than the claimed amount. - InsufficientFunds, - InvalidAmount, - InvalidFee, - } - - #[derive(Clone, Encode, Decode, Eq, PartialEq, Debug, TypeInfo, PalletError)] - pub enum SendError { - NotApplicable, - NotRoutable, - Transport, - DestinationUnsupported, - ExceedsMaxMessageSize, - MissingArgument, - Fees, - } - - impl From for Error { - fn from(e: XcmpSendError) -> Self { - match e { - XcmpSendError::NotApplicable => Error::::Send(SendError::NotApplicable), - XcmpSendError::Unroutable => Error::::Send(SendError::NotRoutable), - XcmpSendError::Transport(_) => Error::::Send(SendError::Transport), - XcmpSendError::DestinationUnsupported => - Error::::Send(SendError::DestinationUnsupported), - XcmpSendError::ExceedsMaxMessageSize => - Error::::Send(SendError::ExceedsMaxMessageSize), - XcmpSendError::MissingArgument => Error::::Send(SendError::MissingArgument), - XcmpSendError::Fees => Error::::Send(SendError::Fees), - } - } - } - - #[pallet::storage] - pub type RewardsMapping = - StorageMap<_, Identity, AccountIdOf, BalanceOf, ValueQuery>; - - #[pallet::call] - impl Pallet { - #[pallet::call_index(0)] - #[pallet::weight((T::WeightInfo::claim(), DispatchClass::Operational))] - pub fn claim( - origin: OriginFor, - deposit_address: AccountIdOf, - value: BalanceOf, - message_id: H256, - ) -> DispatchResult { - let account_id = ensure_signed(origin)?; - Self::process_claim(account_id, deposit_address, value, message_id)?; - Ok(()) - } - } - - impl Pallet { - fn process_claim( - account_id: AccountIdOf, - deposit_address: AccountIdOf, - value: BalanceOf, - message_id: H256, - ) -> DispatchResult { - // Check if the claim value is equal to or less than the accumulated balance. - let reward_balance = RewardsMapping::::get(account_id.clone()); - if value > reward_balance { - return Err(Error::::InsufficientFunds.into()); - } - - let reward_asset = snowbridge_core::location::convert_token_address( - T::EthereumNetwork::get(), - T::WethAddress::get(), - ); - let cost2: u128 = - TryInto::::try_into(value).map_err(|_| Error::::InvalidAmount)?; - let deposit: Asset = (reward_asset, cost2).into(); - let beneficiary: Location = - Location::new(0, Parachain(T::AssetHubParaId::get().into())); - - let asset_hub_fee_asset: Asset = (Location::parent(), T::AssetHubXCMFee::get()).into(); - - let account_32 = T::AccountId::decode(&mut &bytes[..]).unwrap_or_default(); - let origin_location = Location::new(0, [Parachain(T::AssetHubParaId::get().into()), AccountId32{network: None, id: account_id.into()}].into()); - let fee: BalanceOf = T::AssetHubXCMFee::get().try_into().map_err(|_| Error::::InvalidFee)?; - burn_fees::>(origin_location, fee)?; - - let xcm: Xcm<()> = alloc::vec![ - // Teleport required fees. - ReceiveTeleportedAsset(asset_hub_fee_asset.clone().into()), - // Pay for execution. - BuyExecution { fees: asset_hub_fee_asset, weight_limit: Unlimited }, - DescendOrigin(PalletInstance(80).into()), - UniversalOrigin(GlobalConsensus(T::EthereumNetwork::get())), - ReserveAssetDeposited(deposit.clone().into()), - DepositAsset { assets: Definite(deposit.into()), beneficiary }, - SetAppendix(Xcm(alloc::vec![ - RefundSurplus, - DepositAsset { assets: AllCounted(1).into(), beneficiary: beneficiary }, - ])), - SetTopic(message_id.into()), - ] - .into(); - - // Deduct the reward from the claimable balance - RewardsMapping::::mutate(account_id.clone(), |current_value| { - *current_value = current_value.saturating_sub(value); - }); - - let dest = Location::new(1, [Parachain(T::AssetHubParaId::get().into())]); - let (_xcm_hash, _) = send_xcm::(dest, xcm).map_err(Error::::from)?; - - Self::deposit_event(Event::RewardClaimed { - account_id, - deposit_address, - value, - message_id, - }); - Ok(()) - } - } - - impl RewardLedger, BalanceOf> for Pallet { - fn deposit(account_id: AccountIdOf, value: BalanceOf) -> DispatchResult { - RewardsMapping::::mutate(account_id.clone(), |current_value| { - *current_value = current_value.saturating_add(value); - }); - Self::deposit_event(Event::RewardDeposited { account_id, value }); - - Ok(()) - } - } -} diff --git a/bridges/snowbridge/pallets/rewards/src/mock.rs b/bridges/snowbridge/pallets/rewards/src/mock.rs deleted file mode 100644 index 272d54d18d28..000000000000 --- a/bridges/snowbridge/pallets/rewards/src/mock.rs +++ /dev/null @@ -1,134 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// SPDX-FileCopyrightText: 2023 Snowfork -use super::*; - -use codec::Encode; -use frame_support::{derive_impl, parameter_types}; -use hex_literal::hex; -use sp_core::{ConstU128, ConstU32, H160}; -use sp_runtime::{ - traits::{IdentifyAccount, IdentityLookup, Verify}, - BuildStorage, MultiSignature, -}; -use sp_std::{convert::From, default::Default}; -use xcm::{latest::SendXcm, prelude::*}; -use xcm_executor::AssetsInHolding; - -use crate::{self as snowbridge_pallet_rewards}; - -pub type Block = frame_system::mocking::MockBlock; - -frame_support::construct_runtime!( - pub enum Test - { - System: frame_system::{Pallet, Call, Storage, Event}, - Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, - EthereumRewards: snowbridge_pallet_rewards::{Pallet, Call, Storage, Event}, - } -); - -pub type Signature = MultiSignature; -pub type AccountId = <::Signer as IdentifyAccount>::AccountId; - -parameter_types! { - pub const ExistentialDeposit: u128 = 1; -} - -type Balance = u128; - -#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] -impl pallet_balances::Config for Test { - type Balance = Balance; - type ExistentialDeposit = ExistentialDeposit; - type AccountStore = System; -} - -#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] -impl frame_system::Config for Test { - type AccountId = AccountId; - type Lookup = IdentityLookup; - type AccountData = pallet_balances::AccountData; - type Block = Block; -} - - -parameter_types! { - pub EthereumNetwork: NetworkId = NetworkId::Ethereum { chain_id: 11155111 }; - pub WethAddress: H160 = hex!("774667629726ec1FaBEbCEc0D9139bD1C8f72a23").into(); -} - -impl snowbridge_pallet_rewards::Config for Test { - type RuntimeEvent = RuntimeEvent; - type AssetHubParaId = ConstU32<1000>; - type EthereumNetwork = EthereumNetwork; - type WethAddress = WethAddress; - type XcmSender = MockXcmSender; - type Token = Balances; - type AssetTransactor = SuccessfulTransactor; - type AssetHubXCMFee = ConstU128<1_000_000_000_000u128>; - type WeightInfo = (); -} - -// Mock XCM sender that always succeeds -pub struct MockXcmSender; - -impl SendXcm for MockXcmSender { - type Ticket = Xcm<()>; - - fn validate( - dest: &mut Option, - xcm: &mut Option>, - ) -> SendResult { - if let Some(location) = dest { - match location.unpack() { - (_, [Parachain(1001)]) => return Err(XcmpSendError::NotApplicable), - _ => Ok((xcm.clone().unwrap(), Assets::default())), - } - } else { - Ok((xcm.clone().unwrap(), Assets::default())) - } - } - - fn deliver(xcm: Self::Ticket) -> core::result::Result { - let hash = xcm.using_encoded(sp_io::hashing::blake2_256); - Ok(hash) - } -} - -pub fn new_tester() -> sp_io::TestExternalities { - let t = frame_system::GenesisConfig::::default().build_storage().unwrap(); - let ext = sp_io::TestExternalities::new(t); - ext -} - -pub struct SuccessfulTransactor; -impl TransactAsset for SuccessfulTransactor { - fn can_check_in(_origin: &Location, _what: &Asset, _context: &XcmContext) -> XcmResult { - Ok(()) - } - - fn can_check_out(_dest: &Location, _what: &Asset, _context: &XcmContext) -> XcmResult { - Ok(()) - } - - fn deposit_asset(_what: &Asset, _who: &Location, _context: Option<&XcmContext>) -> XcmResult { - Ok(()) - } - - fn withdraw_asset( - _what: &Asset, - _who: &Location, - _context: Option<&XcmContext>, - ) -> Result { - Ok(AssetsInHolding::default()) - } - - fn internal_transfer_asset( - _what: &Asset, - _from: &Location, - _to: &Location, - _context: &XcmContext, - ) -> Result { - Ok(AssetsInHolding::default()) - } -} diff --git a/bridges/snowbridge/pallets/rewards/src/tests.rs b/bridges/snowbridge/pallets/rewards/src/tests.rs deleted file mode 100644 index 23fd4aa7da9d..000000000000 --- a/bridges/snowbridge/pallets/rewards/src/tests.rs +++ /dev/null @@ -1,81 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// SPDX-FileCopyrightText: 2023 Snowfork - -use super::*; -use crate::{ - mock::{new_tester, AccountId, EthereumRewards, RuntimeOrigin, Test, WETH}, -}; -use frame_support::{assert_err, assert_ok}; -use sp_core::H256; -use sp_keyring::AccountKeyring as Keyring; -#[test] -fn test_deposit() { - new_tester().execute_with(|| { - // Check a new deposit works - let relayer: AccountId = Keyring::Bob.into(); - let result = EthereumRewards::deposit(relayer.clone().into(), 2 * WETH); - assert_ok!(result); - assert_eq!(>::get(relayer.clone()), 2 * WETH); - - // Check accumulation works - let result2 = EthereumRewards::deposit(relayer.clone().into(), 3 * WETH); - assert_ok!(result2); - assert_eq!(>::get(relayer), 5 * WETH); - - // Check another relayer deposit works. - let another_relayer: AccountId = Keyring::Ferdie.into(); - let result3 = EthereumRewards::deposit(another_relayer.clone().into(), 1 * WETH); - assert_ok!(result3); - assert_eq!(>::get(another_relayer), 1 * WETH); - }); -} - -#[test] -fn test_claim() { - new_tester().execute_with(|| { - let relayer: AccountId = Keyring::Bob.into(); - let message_id = H256::random(); - - let result = EthereumRewards::claim( - RuntimeOrigin::signed(relayer.clone()), - relayer.clone(), - 3 * WETH, - message_id, - ); - // No rewards yet - assert_err!(result, Error::::InsufficientFunds); - - // Deposit rewards - let result2 = EthereumRewards::deposit(relayer.clone(), 3 * WETH); - assert_ok!(result2); - - // Claim some rewards - let result3 = EthereumRewards::claim( - RuntimeOrigin::signed(relayer.clone()), - relayer.clone(), - 2 * WETH, - message_id, - ); - assert_ok!(result3); - assert_eq!(>::get(relayer.clone()), 1 * WETH); - - // Claim some rewards than available - let result4 = EthereumRewards::claim( - RuntimeOrigin::signed(relayer.clone()), - relayer.clone(), - 2 * WETH, - message_id, - ); - assert_err!(result4, Error::::InsufficientFunds); - - // Claim the remaining balance - let result5 = EthereumRewards::claim( - RuntimeOrigin::signed(relayer.clone()), - relayer.clone(), - 1 * WETH, - message_id, - ); - assert_ok!(result5); - assert_eq!(>::get(relayer.clone()), 0); - }); -} diff --git a/bridges/snowbridge/pallets/rewards/src/weights.rs b/bridges/snowbridge/pallets/rewards/src/weights.rs deleted file mode 100644 index f2e2dc0f9777..000000000000 --- a/bridges/snowbridge/pallets/rewards/src/weights.rs +++ /dev/null @@ -1,58 +0,0 @@ - -//! Autogenerated weights for `snowbridge_pallet_rewards` -//! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-10-09, STEPS: `2`, REPEAT: `1`, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `crake.local`, CPU: `` -//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("bridge-hub-rococo-dev")`, DB CACHE: `1024` - -// Executed Command: -// target/release/polkadot-parachain -// benchmark -// pallet -// --chain -// bridge-hub-rococo-dev -// --pallet=snowbridge_pallet_rewards -// --extrinsic=* -// --execution=wasm -// --wasm-execution=compiled -// --template -// ../parachain/templates/module-weight-template.hbs -// --output -// ../parachain/pallets/control/src/weights.rs - -#![cfg_attr(rustfmt, rustfmt_skip)] -#![allow(unused_parens)] -#![allow(unused_imports)] -#![allow(missing_docs)] - -use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; -use core::marker::PhantomData; - -pub trait WeightInfo { - fn claim() -> Weight; -} - -// For backwards compatibility and tests. -impl WeightInfo for () { - /// Storage: ParachainInfo ParachainId (r:1 w:0) - /// Proof: ParachainInfo ParachainId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: EthereumOutboundQueue PalletOperatingMode (r:1 w:0) - /// Proof: EthereumOutboundQueue PalletOperatingMode (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) - /// Storage: MessageQueue BookStateFor (r:1 w:1) - /// Proof: MessageQueue BookStateFor (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) - /// Storage: MessageQueue ServiceHead (r:1 w:1) - /// Proof: MessageQueue ServiceHead (max_values: Some(1), max_size: Some(5), added: 500, mode: MaxEncodedLen) - /// Storage: MessageQueue Pages (r:0 w:1) - /// Proof: MessageQueue Pages (max_values: None, max_size: Some(65585), added: 68060, mode: MaxEncodedLen) - fn claim() -> Weight { - // Proof Size summary in bytes: - // Measured: `80` - // Estimated: `3517` - // Minimum execution time: 44_000_000 picoseconds. - Weight::from_parts(44_000_000, 3517) - .saturating_add(RocksDbWeight::get().reads(4_u64)) - .saturating_add(RocksDbWeight::get().writes(3_u64)) - } -} diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/Cargo.toml b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/Cargo.toml index c1e8a98badb3..27e238082e3f 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/Cargo.toml @@ -51,4 +51,4 @@ snowbridge-pallet-outbound-queue = { workspace = true } snowbridge-pallet-inbound-queue = { workspace = true } snowbridge-pallet-inbound-queue-fixtures = { workspace = true } snowbridge-pallet-outbound-queue-v2 = { workspace = true } -snowbridge-pallet-rewards = { workspace = true } +pallet-bridge-relayers = { workspace = true } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/Cargo.toml b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/Cargo.toml index 537c2f8b18bd..a7ccc01f417e 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/Cargo.toml +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/Cargo.toml @@ -110,7 +110,6 @@ snowbridge-core = { workspace = true } snowbridge-pallet-ethereum-client = { workspace = true } snowbridge-pallet-inbound-queue = { workspace = true } snowbridge-pallet-outbound-queue = { workspace = true } -snowbridge-pallet-rewards = { workspace = true } snowbridge-outbound-queue-runtime-api = { workspace = true } snowbridge-router-primitives = { workspace = true } snowbridge-runtime-common = { workspace = true } @@ -119,7 +118,6 @@ snowbridge-pallet-outbound-queue-v2 = { workspace = true } snowbridge-outbound-queue-runtime-api-v2 = { workspace = true } snowbridge-merkle-tree = { workspace = true } - [dev-dependencies] bridge-hub-test-utils = { workspace = true, default-features = true } bridge-runtime-common = { features = ["integrity-test"], workspace = true, default-features = true } From c79b9b82d78d5112926c7b0131cbbcbed247c69d Mon Sep 17 00:00:00 2001 From: claravanstaden Date: Wed, 30 Oct 2024 15:36:35 +0200 Subject: [PATCH 32/37] fixes --- Cargo.lock | 6 + .../pallets/inbound-queue-v2/src/mock.rs | 19 +-- .../pallets/outbound-queue-v2/Cargo.toml | 6 + .../pallets/outbound-queue-v2/src/lib.rs | 13 +- .../pallets/outbound-queue-v2/src/mock.rs | 134 +++++++++++++++++- .../src/tests/snowbridge_v2.rs | 38 +++-- 6 files changed, 183 insertions(+), 33 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e51574970537..c13a3d10168a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -21143,12 +21143,16 @@ version = "0.2.0" dependencies = [ "alloy-primitives", "alloy-sol-types", + "bp-messages", + "bp-relayers", "bridge-hub-common", "ethabi-decode", "frame-benchmarking", "frame-support", "frame-system", "hex-literal", + "pallet-balances", + "pallet-bridge-relayers", "pallet-message-queue", "parity-scale-codec", "scale-info", @@ -21161,6 +21165,8 @@ dependencies = [ "sp-keyring", "sp-runtime 31.0.1", "sp-std 14.0.0", + "staging-xcm", + "staging-xcm-executor", ] [[package]] diff --git a/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs b/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs index abc296bb7de6..1945316954e2 100644 --- a/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs +++ b/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs @@ -2,6 +2,8 @@ // SPDX-FileCopyrightText: 2023 Snowfork use super::*; +use bp_messages::{HashedLaneId, LaneIdType}; +use bp_relayers::{PayRewardFromAccount, PaymentProcedure, RewardsAccountParams}; use frame_support::{derive_impl, parameter_types, traits::ConstU32}; use hex_literal::hex; use snowbridge_beacon_primitives::{ @@ -11,21 +13,14 @@ use snowbridge_core::{ inbound::{Log, Proof, VerificationError}, TokenId, }; -use sp_core::H160; +use sp_core::{ConstU128, H160}; use sp_runtime::{ traits::{IdentifyAccount, IdentityLookup, MaybeEquivalence, Verify}, BuildStorage, MultiSignature, }; use sp_std::{convert::From, default::Default}; use xcm::{latest::SendXcm, prelude::*}; -use xcm_executor::traits::TransactAsset; -use xcm_executor::AssetsInHolding; -use sp_core::ConstU128; -use bp_relayers::{ - PayRewardFromAccount, RewardsAccountParams, -}; -use bp_relayers::PaymentProcedure; -use bp_messages::{HashedLaneId, LaneIdType}; +use xcm_executor::{traits::TransactAsset, AssetsInHolding}; use crate::{self as inbound_queue}; @@ -38,8 +33,8 @@ frame_support::construct_runtime!( System: frame_system::{Pallet, Call, Storage, Event}, Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, EthereumBeaconClient: snowbridge_pallet_ethereum_client::{Pallet, Call, Storage, Event}, - BridgeRelayers: pallet_bridge_relayers::{Pallet, Call, Storage, Event}, InboundQueue: inbound_queue::{Pallet, Call, Storage, Event}, + BridgeRelayers: pallet_bridge_relayers::{Pallet, Call, Storage, Event}, } ); @@ -177,7 +172,7 @@ impl inbound_queue::Config for Test { #[cfg(feature = "runtime-benchmarks")] type Helper = Test; type Token = Balances; - type RewardLedger = (); + type RewardLedger = BridgeRelayers; } parameter_types! { @@ -188,7 +183,6 @@ pub type TestLaneIdType = HashedLaneId; pub struct TestPaymentProcedure; - impl TestPaymentProcedure { pub fn rewards_account(params: RewardsAccountParams) -> AccountId { PayRewardFromAccount::<(), AccountId, TestLaneIdType>::rewards_account(params) @@ -220,7 +214,6 @@ impl pallet_bridge_relayers::Config for Test { type EthereumNetwork = EthereumNetwork; type WethAddress = WethAddress; type XcmSender = MockXcmSender; - type AssetTransactor = SuccessfulTransactor; type AssetHubXCMFee = ConstU128<1_000_000_000_000u128>; } diff --git a/bridges/snowbridge/pallets/outbound-queue-v2/Cargo.toml b/bridges/snowbridge/pallets/outbound-queue-v2/Cargo.toml index 8a6afa290189..c7ac69e5497a 100644 --- a/bridges/snowbridge/pallets/outbound-queue-v2/Cargo.toml +++ b/bridges/snowbridge/pallets/outbound-queue-v2/Cargo.toml @@ -40,6 +40,12 @@ snowbridge-merkle-tree = { workspace = true } [dev-dependencies] pallet-message-queue = { workspace = true } sp-keyring = { workspace = true, default-features = true } +pallet-bridge-relayers = { workspace = true, default-features = true } +bp-relayers = { workspace = true, default-features = true } +bp-messages = { workspace = true, default-features = true } +pallet-balances = { workspace = true, default-features = true } +xcm = { workspace = true } +xcm-executor = { workspace = true } [features] default = ["std"] diff --git a/bridges/snowbridge/pallets/outbound-queue-v2/src/lib.rs b/bridges/snowbridge/pallets/outbound-queue-v2/src/lib.rs index f5796152959b..ad703fc7d825 100644 --- a/bridges/snowbridge/pallets/outbound-queue-v2/src/lib.rs +++ b/bridges/snowbridge/pallets/outbound-queue-v2/src/lib.rs @@ -109,13 +109,18 @@ use codec::Decode; use envelope::Envelope; use frame_support::{ storage::StorageStreamIter, - traits::{tokens::Balance, EnqueueMessage, Get, ProcessMessageError}, + traits::{ + fungible::{Inspect, Mutate}, + tokens::Balance, + EnqueueMessage, Get, ProcessMessageError, + }, weights::{Weight, WeightToFee}, }; use snowbridge_core::{ inbound::Message as DeliveryMessage, outbound::v2::{CommandWrapper, Fee, GasMeter, InboundMessage, Message}, - BasicOperatingMode, rewards::RewardLedger, + rewards::RewardLedger, + BasicOperatingMode, }; use snowbridge_merkle_tree::merkle_root; use sp_core::H256; @@ -126,7 +131,6 @@ use sp_runtime::{ use sp_std::prelude::*; pub use types::{PendingOrder, ProcessMessageOriginOf}; pub use weights::WeightInfo; -use frame_support::traits::fungible::{Inspect, Mutate}; pub use pallet::*; @@ -328,7 +332,8 @@ pub mod pallet { let account = T::AccountId::decode(&mut &envelope.reward_address[..]).unwrap_or( T::AccountId::decode(&mut TrailingZeroInput::zeroes()).expect("zero address"), ); - T::RewardLedger::deposit(account, order.fee.into())?; + let fee: BalanceOf = order.fee.try_into().map_err(|_| >::InvalidFee)?; + T::RewardLedger::deposit(account, fee)?; >::remove(nonce); diff --git a/bridges/snowbridge/pallets/outbound-queue-v2/src/mock.rs b/bridges/snowbridge/pallets/outbound-queue-v2/src/mock.rs index 7a5c5f9b401c..b1af4f84e1b8 100644 --- a/bridges/snowbridge/pallets/outbound-queue-v2/src/mock.rs +++ b/bridges/snowbridge/pallets/outbound-queue-v2/src/mock.rs @@ -9,6 +9,9 @@ use frame_support::{ BoundedVec, }; +use bp_messages::HashedLaneId; +use bp_relayers::{PayRewardFromAccount, PaymentProcedure, RewardsAccountParams}; +use codec::Encode; use hex_literal::hex; use snowbridge_core::{ gwei, @@ -18,12 +21,17 @@ use snowbridge_core::{ pricing::{PricingParameters, Rewards}, ParaId, }; -use sp_core::{ConstU32, H160, H256}; +use sp_core::{ConstU128, ConstU32, H160, H256}; use sp_runtime::{ traits::{BlakeTwo256, IdentityLookup, Keccak256}, AccountId32, BuildStorage, FixedU128, }; use sp_std::marker::PhantomData; +use xcm::{ + latest::SendXcm, + prelude::{SendError as XcmpSendError, *}, +}; +use xcm_executor::{traits::TransactAsset, AssetsInHolding}; type Block = frame_system::mocking::MockBlock; type AccountId = AccountId32; @@ -32,8 +40,10 @@ frame_support::construct_runtime!( pub enum Test { System: frame_system::{Pallet, Call, Storage, Event}, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, MessageQueue: pallet_message_queue::{Pallet, Call, Storage, Event}, OutboundQueue: crate::{Pallet, Storage, Event}, + BridgeRelayers: pallet_bridge_relayers::{Pallet, Call, Storage, Event}, } ); @@ -47,6 +57,7 @@ impl frame_system::Config for Test { type Hashing = BlakeTwo256; type AccountId = AccountId; type Lookup = IdentityLookup; + type AccountData = pallet_balances::AccountData; type RuntimeEvent = RuntimeEvent; type PalletInfo = PalletInfo; type Nonce = u64; @@ -72,6 +83,17 @@ impl pallet_message_queue::Config for Test { type QueuePausedQuery = (); } +parameter_types! { + pub const ExistentialDeposit: u128 = 1; +} + +#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] +impl pallet_balances::Config for Test { + type Balance = Balance; + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; +} + // Mock verifier pub struct MockVerifier; @@ -94,6 +116,8 @@ parameter_types! { pub const GatewayAddress: H160 = H160(GATEWAY_ADDRESS); } +type Balance = u128; + pub const DOT: u128 = 10_000_000_000; impl crate::Config for Test { type RuntimeEvent = RuntimeEvent; @@ -107,7 +131,81 @@ impl crate::Config for Test { type Balance = u128; type WeightToFee = IdentityFee; type WeightInfo = (); - type RewardLedger = (); + type Token = Balances; + type RewardLedger = BridgeRelayers; +} + +parameter_types! { + pub WethAddress: H160 = hex!("774667629726ec1FaBEbCEc0D9139bD1C8f72a23").into(); +} + +pub type TestLaneIdType = HashedLaneId; + +pub struct TestPaymentProcedure; + +impl TestPaymentProcedure { + pub fn rewards_account(params: RewardsAccountParams) -> AccountId { + PayRewardFromAccount::<(), AccountId, TestLaneIdType>::rewards_account(params) + } +} + +impl PaymentProcedure for TestPaymentProcedure { + type Error = (); + type LaneId = TestLaneIdType; + + fn pay_reward( + _relayer: &AccountId, + _lane_id: RewardsAccountParams, + _reward: Balance, + ) -> Result<(), Self::Error> { + Ok(()) + } +} + +parameter_types! { + pub const EthereumNetwork: xcm::v3::NetworkId = xcm::v3::NetworkId::Ethereum { chain_id: 11155111 }; +} + +impl pallet_bridge_relayers::Config for Test { + type RuntimeEvent = RuntimeEvent; + type Reward = Balance; + type PaymentProcedure = TestPaymentProcedure; + type StakeAndSlash = (); + type WeightInfo = (); + type LaneId = TestLaneIdType; + type Token = Balances; + type AssetHubParaId = ConstU32<1000>; + type EthereumNetwork = EthereumNetwork; + type WethAddress = WethAddress; + type XcmSender = MockXcmSender; + + type AssetTransactor = SuccessfulTransactor; + type AssetHubXCMFee = ConstU128<1_000_000_000_000u128>; +} + +// Mock XCM sender that always succeeds +pub struct MockXcmSender; + +impl SendXcm for MockXcmSender { + type Ticket = Xcm<()>; + + fn validate( + dest: &mut Option, + xcm: &mut Option>, + ) -> SendResult { + if let Some(location) = dest { + match location.unpack() { + _ => Ok((xcm.clone().unwrap(), Assets::default())), + } + } else { + Ok((xcm.clone().unwrap(), Assets::default())) + } + } + + fn deliver(xcm: Self::Ticket) -> core::result::Result { + let hash = xcm.using_encoded(sp_io::hashing::blake2_256); + Ok(hash) + } } fn setup() { @@ -193,3 +291,35 @@ pub fn mock_message(sibling_para_id: u32) -> Message { .unwrap(), } } + +pub struct SuccessfulTransactor; +impl TransactAsset for SuccessfulTransactor { + fn can_check_in(_origin: &Location, _what: &Asset, _context: &XcmContext) -> XcmResult { + Ok(()) + } + + fn can_check_out(_dest: &Location, _what: &Asset, _context: &XcmContext) -> XcmResult { + Ok(()) + } + + fn deposit_asset(_what: &Asset, _who: &Location, _context: Option<&XcmContext>) -> XcmResult { + Ok(()) + } + + fn withdraw_asset( + _what: &Asset, + _who: &Location, + _context: Option<&XcmContext>, + ) -> Result { + Ok(AssetsInHolding::default()) + } + + fn internal_transfer_asset( + _what: &Asset, + _from: &Location, + _to: &Location, + _context: &XcmContext, + ) -> Result { + Ok(AssetsInHolding::default()) + } +} diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2.rs index 8a7ff2a45ce8..77b28506a1db 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2.rs @@ -15,13 +15,13 @@ use crate::imports::*; use bridge_hub_westend_runtime::EthereumInboundQueue; use hex_literal::hex; +use snowbridge_core::rewards::RewardLedger; use snowbridge_router_primitives::inbound::{ v1::{Command, Destination, MessageV1, VersionedMessage}, GlobalConsensusEthereumConvertsFor, }; use sp_core::H256; use testnet_parachains_constants::westend::snowbridge::EthereumNetwork; -use snowbridge_core::rewards::RewardLedger; const INITIAL_FUND: u128 = 5_000_000_000_000; pub const CHAIN_ID: u64 = 11155111; @@ -193,8 +193,7 @@ fn claim_rewards_works() { let relayer = BridgeHubWestendSender::get(); let reward_address = AssetHubWestendReceiver::get(); - type EthereumRewards = - ::EthereumRewards; + type EthereumRewards = ::EthereumRewards; assert_ok!(EthereumRewards::deposit(relayer.clone().into(), 2 * ETH)); // Check that the message was sent @@ -206,7 +205,12 @@ fn claim_rewards_works() { ); let message_id = H256::random(); - let result = EthereumRewards::claim(RuntimeOrigin::signed(relayer.clone()), reward_address.clone(), ETH, message_id); + let result = EthereumRewards::claim( + RuntimeOrigin::signed(relayer.clone()), + reward_address.clone(), + ETH, + message_id, + ); assert_ok!(result); let events = BridgeHubWestend::events(); @@ -230,16 +234,14 @@ fn claim_rewards_works() { } #[test] -fn claiming_more_than_accrued_rewards_errors( -) { +fn claiming_more_than_accrued_rewards_errors() { BridgeHubWestend::execute_with(|| { type RuntimeEvent = ::RuntimeEvent; type RuntimeOrigin = ::RuntimeOrigin; let relayer = BridgeHubWestendSender::get(); let reward_address = AssetHubWestendReceiver::get(); - type EthereumRewards = - ::EthereumRewards; + type EthereumRewards = ::EthereumRewards; assert_ok!(EthereumRewards::deposit(relayer.clone().into(), 2 * ETH)); // Check that the message was sent @@ -251,11 +253,19 @@ fn claiming_more_than_accrued_rewards_errors( ); let message_id = H256::random(); - let result = EthereumRewards::claim(RuntimeOrigin::signed(relayer.clone()), reward_address.clone(), 3 * ETH, message_id); - assert_err!(result, DispatchError::Module(sp_runtime::ModuleError { - index: 86, - error: [1, 0, 0, 0], - message: Some("InsufficientFunds") - })); + let result = EthereumRewards::claim( + RuntimeOrigin::signed(relayer.clone()), + reward_address.clone(), + 3 * ETH, + message_id, + ); + assert_err!( + result, + DispatchError::Module(sp_runtime::ModuleError { + index: 86, + error: [1, 0, 0, 0], + message: Some("InsufficientFunds") + }) + ); }); } From 5d4e4954fa454e6ab21d0bbb92d39a6543f41380 Mon Sep 17 00:00:00 2001 From: claravanstaden Date: Wed, 30 Oct 2024 15:58:19 +0200 Subject: [PATCH 33/37] configure inbound id --- bridges/modules/relayers/src/lib.rs | 3 ++- bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs | 3 ++- bridges/snowbridge/pallets/outbound-queue-v2/src/mock.rs | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/bridges/modules/relayers/src/lib.rs b/bridges/modules/relayers/src/lib.rs index 8900d86d2480..738495ff2d55 100644 --- a/bridges/modules/relayers/src/lib.rs +++ b/bridges/modules/relayers/src/lib.rs @@ -98,6 +98,7 @@ pub mod pallet { type LaneId: LaneIdType + Send + Sync; /// AssetHub parachain ID type AssetHubParaId: Get; + type InboundQueuePalletInstance: Get; /// Ethereum network ID including the chain ID type EthereumNetwork: Get; /// Message relayers are rewarded with this asset @@ -449,7 +450,7 @@ pub mod pallet { let asset_hub_fee_asset: Asset = (Location::parent(), T::AssetHubXCMFee::get()).into(); let xcm: Xcm<()> = alloc::vec![ - DescendOrigin(PalletInstance(80).into()), + DescendOrigin(PalletInstance(T::InboundQueuePalletInstance::get()).into()), UniversalOrigin(GlobalConsensus(T::EthereumNetwork::get())), ReserveAssetDeposited(deposit.clone().into()), BuyExecution { fees: asset_hub_fee_asset, weight_limit: Unlimited }, diff --git a/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs b/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs index 1945316954e2..0f6fc34efdb1 100644 --- a/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs +++ b/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs @@ -13,7 +13,7 @@ use snowbridge_core::{ inbound::{Log, Proof, VerificationError}, TokenId, }; -use sp_core::{ConstU128, H160}; +use sp_core::{ConstU128, H160, ConstU8}; use sp_runtime::{ traits::{IdentifyAccount, IdentityLookup, MaybeEquivalence, Verify}, BuildStorage, MultiSignature, @@ -212,6 +212,7 @@ impl pallet_bridge_relayers::Config for Test { type Token = Balances; type AssetHubParaId = ConstU32<1000>; type EthereumNetwork = EthereumNetwork; + type InboundQueuePalletInstance = ConstU8<80>; type WethAddress = WethAddress; type XcmSender = MockXcmSender; type AssetTransactor = SuccessfulTransactor; diff --git a/bridges/snowbridge/pallets/outbound-queue-v2/src/mock.rs b/bridges/snowbridge/pallets/outbound-queue-v2/src/mock.rs index b1af4f84e1b8..566600caf3f9 100644 --- a/bridges/snowbridge/pallets/outbound-queue-v2/src/mock.rs +++ b/bridges/snowbridge/pallets/outbound-queue-v2/src/mock.rs @@ -21,7 +21,7 @@ use snowbridge_core::{ pricing::{PricingParameters, Rewards}, ParaId, }; -use sp_core::{ConstU128, ConstU32, H160, H256}; +use sp_core::{ConstU128, ConstU8, ConstU32, H160, H256}; use sp_runtime::{ traits::{BlakeTwo256, IdentityLookup, Keccak256}, AccountId32, BuildStorage, FixedU128, @@ -176,6 +176,7 @@ impl pallet_bridge_relayers::Config for Test { type Token = Balances; type AssetHubParaId = ConstU32<1000>; type EthereumNetwork = EthereumNetwork; + type InboundQueuePalletInstance = ConstU8<80>; type WethAddress = WethAddress; type XcmSender = MockXcmSender; From 1295cada0b2520292ab9bd82fd352774ec49648a Mon Sep 17 00:00:00 2001 From: claravanstaden Date: Thu, 31 Oct 2024 10:11:29 +0200 Subject: [PATCH 34/37] fix integration tests --- bridges/modules/relayers/src/lib.rs | 16 +++--- .../bridges/bridge-hub-westend/src/lib.rs | 2 +- .../src/tests/snowbridge_v2.rs | 57 +++---------------- .../src/bridge_common_config.rs | 35 ++++++++++++ .../src/weights/pallet_bridge_relayers.rs | 9 +++ .../src/bridge_common_config.rs | 25 ++++++++ .../src/bridge_to_ethereum_config.rs | 31 ++-------- .../bridge-hubs/bridge-hub-westend/src/lib.rs | 1 - .../src/weights/pallet_bridge_relayers.rs | 9 +++ 9 files changed, 101 insertions(+), 84 deletions(-) diff --git a/bridges/modules/relayers/src/lib.rs b/bridges/modules/relayers/src/lib.rs index 738495ff2d55..4069678fbf4e 100644 --- a/bridges/modules/relayers/src/lib.rs +++ b/bridges/modules/relayers/src/lib.rs @@ -439,21 +439,21 @@ pub mod pallet { if value.is_zero() { return Err(Error::::InsufficientFunds.into()); } - let reward_asset = snowbridge_core::location::convert_token_address( - T::EthereumNetwork::get(), - T::WethAddress::get(), - ); let reward_balance: u128 = TryInto::::try_into(value).map_err(|_| Error::::InvalidAmount)?; - let deposit: Asset = (reward_asset, reward_balance).into(); - let asset_hub_fee_asset: Asset = (Location::parent(), T::AssetHubXCMFee::get()).into(); + let reward_location = snowbridge_core::location::convert_token_address( + T::EthereumNetwork::get(), + T::WethAddress::get(), + ); + let reward_asset: Asset = (reward_location.clone(), reward_balance).into(); + let fee_asset: Asset = (reward_location, T::AssetHubXCMFee::get()).into(); let xcm: Xcm<()> = alloc::vec![ DescendOrigin(PalletInstance(T::InboundQueuePalletInstance::get()).into()), UniversalOrigin(GlobalConsensus(T::EthereumNetwork::get())), - ReserveAssetDeposited(deposit.clone().into()), - BuyExecution { fees: asset_hub_fee_asset, weight_limit: Unlimited }, + ReserveAssetDeposited(reward_asset.clone().into()), + BuyExecution { fees: fee_asset, weight_limit: Unlimited }, DepositAsset { assets: AllCounted(1).into(), beneficiary: deposit_location.clone() }, SetAppendix(Xcm(alloc::vec![ RefundSurplus, diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/src/lib.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/src/lib.rs index e92d1c46ccbd..075fd84492e9 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/src/lib.rs @@ -47,8 +47,8 @@ decl_test_parachains! { PolkadotXcm: bridge_hub_westend_runtime::PolkadotXcm, Balances: bridge_hub_westend_runtime::Balances, EthereumSystem: bridge_hub_westend_runtime::EthereumSystem, - EthereumRewards: bridge_hub_westend_runtime::EthereumRewards, EthereumInboundQueue: bridge_hub_westend_runtime::EthereumInboundQueue, + BridgeRelayers: bridge_hub_westend_runtime::BridgeRelayers, } }, } diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2.rs index 77b28506a1db..423c764620a1 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2.rs @@ -18,9 +18,7 @@ use hex_literal::hex; use snowbridge_core::rewards::RewardLedger; use snowbridge_router_primitives::inbound::{ v1::{Command, Destination, MessageV1, VersionedMessage}, - GlobalConsensusEthereumConvertsFor, }; -use sp_core::H256; use testnet_parachains_constants::westend::snowbridge::EthereumNetwork; const INITIAL_FUND: u128 = 5_000_000_000_000; @@ -193,23 +191,21 @@ fn claim_rewards_works() { let relayer = BridgeHubWestendSender::get(); let reward_address = AssetHubWestendReceiver::get(); - type EthereumRewards = ::EthereumRewards; - assert_ok!(EthereumRewards::deposit(relayer.clone().into(), 2 * ETH)); + type BridgeRelayers = ::BridgeRelayers; + assert_ok!(BridgeRelayers::deposit(relayer.clone().into(), 2 * ETH)); // Check that the message was sent assert_expected_events!( BridgeHubWestend, vec![ - RuntimeEvent::EthereumRewards(snowbridge_pallet_rewards::Event::RewardDeposited { .. }) => {}, + RuntimeEvent::BridgeRelayers(pallet_bridge_relayers::Event::RewardDeposited { .. }) => {}, ] ); - let message_id = H256::random(); - let result = EthereumRewards::claim( + let relayer_location = Location::new(1, [Parachain(1000), Junction::AccountId32{ id: reward_address.into(), network: None}]); + let result = BridgeRelayers::claim( RuntimeOrigin::signed(relayer.clone()), - reward_address.clone(), - ETH, - message_id, + relayer_location.clone(), ); assert_ok!(result); @@ -217,8 +213,8 @@ fn claim_rewards_works() { assert!( events.iter().any(|event| matches!( event, - RuntimeEvent::EthereumRewards(snowbridge_pallet_rewards::Event::RewardClaimed { account_id, deposit_address, value, message_id: _ }) - if *account_id == relayer && *deposit_address == reward_address && *value == ETH, + RuntimeEvent::BridgeRelayers(pallet_bridge_relayers::Event::RewardClaimed { account_id, deposit_location, value, }) + if *account_id == relayer && *deposit_location == relayer_location && *value > 1 *ETH, )), "RewardClaimed event with correct fields." ); @@ -232,40 +228,3 @@ fn claim_rewards_works() { ); }) } - -#[test] -fn claiming_more_than_accrued_rewards_errors() { - BridgeHubWestend::execute_with(|| { - type RuntimeEvent = ::RuntimeEvent; - type RuntimeOrigin = ::RuntimeOrigin; - - let relayer = BridgeHubWestendSender::get(); - let reward_address = AssetHubWestendReceiver::get(); - type EthereumRewards = ::EthereumRewards; - assert_ok!(EthereumRewards::deposit(relayer.clone().into(), 2 * ETH)); - - // Check that the message was sent - assert_expected_events!( - BridgeHubWestend, - vec![ - RuntimeEvent::EthereumRewards(snowbridge_pallet_rewards::Event::RewardDeposited { .. }) => {}, - ] - ); - - let message_id = H256::random(); - let result = EthereumRewards::claim( - RuntimeOrigin::signed(relayer.clone()), - reward_address.clone(), - 3 * ETH, - message_id, - ); - assert_err!( - result, - DispatchError::Module(sp_runtime::ModuleError { - index: 86, - error: [1, 0, 0, 0], - message: Some("InsufficientFunds") - }) - ); - }); -} diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs index 5dca45d326b8..9f4b3233aaa1 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs @@ -24,6 +24,12 @@ use super::{weights, AccountId, Balance, Balances, BlockNumber, Runtime, RuntimeEvent}; use bp_parachains::SingleParaStoredHeaderDataBuilder; use frame_support::{parameter_types, traits::ConstU32}; +use testnet_parachains_constants::rococo::snowbridge::EthereumNetwork; +use sp_core::H160; +use crate::xcm_config; +use crate::XcmRouter; +use sp_runtime::traits::ConstU128; +use sp_runtime::traits::ConstU8; parameter_types! { pub const RelayChainHeadersToKeep: u32 = 1024; @@ -83,6 +89,17 @@ impl pallet_bridge_relayers::Config fo >; type WeightInfo = weights::pallet_bridge_relayers::WeightInfo; type LaneId = bp_messages::LegacyLaneId; + type AssetHubParaId = ConstU32; + type EthereumNetwork = EthereumNetwork; + type WethAddress = WethAddress; + #[cfg(not(feature = "runtime-benchmarks"))] + type XcmSender = XcmRouter; + #[cfg(feature = "runtime-benchmarks")] + type XcmSender = DoNothingRouter; + type Token = Balances; + type AssetTransactor = ::AssetTransactor; + type InboundQueuePalletInstance = ConstU8<80>; + type AssetHubXCMFee = ConstU128<1_000_000_000_000>; } /// Allows collect and claim rewards for relayers @@ -105,8 +122,26 @@ impl pallet_bridge_relayers::Config for >; type WeightInfo = weights::pallet_bridge_relayers::WeightInfo; type LaneId = bp_messages::HashedLaneId; + type AssetHubParaId = ConstU32; + type EthereumNetwork = EthereumNetwork; + type WethAddress = WethAddress; + #[cfg(not(feature = "runtime-benchmarks"))] + type XcmSender = XcmRouter; + #[cfg(feature = "runtime-benchmarks")] + type XcmSender = DoNothingRouter; + type Token = Balances; + type AssetTransactor = ::AssetTransactor; + type InboundQueuePalletInstance = ConstU8<80>; + type AssetHubXCMFee = ConstU128<1_000_000_000_000>; } +parameter_types! { + pub WethAddress: H160 = H160(hex_literal::hex!("fff9976782d46cc05630d1f6ebab18b2324d6b14")); +} + +pub const ASSET_HUB_ID: u32 = rococo_runtime_constants::system_parachain::ASSET_HUB_ID; + + /// Add GRANDPA bridge pallet to track Rococo Bulletin chain. pub type BridgeGrandpaRococoBulletinInstance = pallet_bridge_grandpa::Instance4; impl pallet_bridge_grandpa::Config for Runtime { diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_relayers.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_relayers.rs index b7318361c7d9..f951797537c7 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_relayers.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_relayers.rs @@ -120,4 +120,13 @@ impl pallet_bridge_relayers::WeightInfo for WeightInfo< .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } + fn claim() -> Weight { + // Proof Size summary in bytes: + // Measured: `80` + // Estimated: `3517` + // Minimum execution time: 44_000_000 picoseconds. + Weight::from_parts(44_000_000, 3517) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_common_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_common_config.rs index 0872d0498f85..ae026be7ba92 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_common_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_common_config.rs @@ -24,6 +24,13 @@ use super::{weights, AccountId, Balance, Balances, BlockNumber, Runtime, RuntimeEvent}; use bp_messages::LegacyLaneId; use frame_support::parameter_types; +use sp_core::H160; +use testnet_parachains_constants::westend::snowbridge::EthereumNetwork; +use crate::XcmRouter; +use crate::xcm_config; +use sp_runtime::traits::ConstU32; +use sp_runtime::traits::ConstU128; +use sp_runtime::traits::ConstU8; parameter_types! { pub storage RequiredStakeForStakeAndSlash: Balance = 1_000_000; @@ -33,6 +40,12 @@ parameter_types! { pub storage DeliveryRewardInBalance: u64 = 1_000_000; } +parameter_types! { + pub WethAddress: H160 = H160(hex_literal::hex!("fff9976782d46cc05630d1f6ebab18b2324d6b14")); +} + +pub const ASSET_HUB_ID: u32 = westend_runtime_constants::system_parachain::ASSET_HUB_ID; + /// Allows collect and claim rewards for relayers pub type RelayersForLegacyLaneIdsMessagesInstance = (); impl pallet_bridge_relayers::Config for Runtime { @@ -53,4 +66,16 @@ impl pallet_bridge_relayers::Config fo >; type WeightInfo = weights::pallet_bridge_relayers::WeightInfo; type LaneId = LegacyLaneId; + + type AssetHubParaId = ConstU32; + type EthereumNetwork = EthereumNetwork; + type WethAddress = WethAddress; + #[cfg(not(feature = "runtime-benchmarks"))] + type XcmSender = XcmRouter; + #[cfg(feature = "runtime-benchmarks")] + type XcmSender = DoNothingRouter; + type Token = Balances; + type AssetTransactor = ::AssetTransactor; + type InboundQueuePalletInstance = ConstU8<80>; + type AssetHubXCMFee = ConstU128<1_000_000_000_000>; } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs index 6fece1520fe4..0b6bd0c85764 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs @@ -16,11 +16,12 @@ #[cfg(not(feature = "runtime-benchmarks"))] use crate::XcmRouter; +use crate::BridgeRelayers; use crate::{ xcm_config, xcm_config::{TreasuryAccount, UniversalLocation}, Balances, EthereumInboundQueue, EthereumOutboundQueue, EthereumSystem, MessageQueue, Runtime, - RuntimeEvent, TransactionByteFee, EthereumRewards + RuntimeEvent, TransactionByteFee, }; use parachains_common::{AccountId, Balance}; use snowbridge_beacon_primitives::{Fork, ForkVersions}; @@ -35,6 +36,7 @@ use testnet_parachains_constants::westend::{ fee::WeightToFee, snowbridge::{EthereumLocation, EthereumNetwork, INBOUND_QUEUE_PALLET_INDEX}, }; +use sp_runtime::traits::ConstU8; use crate::xcm_config::RelayNetwork; #[cfg(feature = "runtime-benchmarks")] @@ -42,7 +44,7 @@ use benchmark_helpers::DoNothingRouter; use frame_support::{parameter_types, weights::ConstantMultiplier}; use pallet_xcm::EnsureXcm; use sp_runtime::{ - traits::{ConstU32, ConstU8, Keccak256}, + traits::{ConstU32, Keccak256}, FixedU128, }; use xcm::prelude::{GlobalConsensus, InteriorLocation, Location, Parachain}; @@ -123,7 +125,7 @@ impl snowbridge_pallet_inbound_queue_v2::Config for Runtime { type GatewayAddress = EthereumGatewayAddress; #[cfg(feature = "runtime-benchmarks")] type Helper = Runtime; - type RewardLedger = EthereumRewards; + type RewardLedger = BridgeRelayers; type Token = Balances; type WeightInfo = crate::weights::snowbridge_pallet_inbound_queue_v2::WeightInfo; } @@ -154,10 +156,9 @@ impl snowbridge_pallet_outbound_queue_v2::Config for Runtime { type WeightToFee = WeightToFee; type Verifier = snowbridge_pallet_ethereum_client::Pallet; type GatewayAddress = EthereumGatewayAddress; - type RewardLedger = EthereumRewards; + type RewardLedger = BridgeRelayers; type Token = Balances; type WeightInfo = crate::weights::snowbridge_pallet_outbound_queue_v2::WeightInfo; - type RewardLedger = (); } #[cfg(any(feature = "std", feature = "fast-runtime", feature = "runtime-benchmarks", test))] @@ -235,26 +236,6 @@ impl snowbridge_pallet_system::Config for Runtime { type EthereumLocation = EthereumLocation; } -parameter_types! { - pub WethAddress: H160 = H160(hex_literal::hex!("fff9976782d46cc05630d1f6ebab18b2324d6b14")); -} - -pub const ASSET_HUB_ID: u32 = westend_runtime_constants::system_parachain::ASSET_HUB_ID; - -impl snowbridge_pallet_rewards::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type AssetHubParaId = ConstU32; - type EthereumNetwork = EthereumNetwork; - type WethAddress = WethAddress; - #[cfg(not(feature = "runtime-benchmarks"))] - type XcmSender = XcmRouter; - #[cfg(feature = "runtime-benchmarks")] - type XcmSender = DoNothingRouter; - type WeightInfo = (); // TODO generate weights - type Token = Balances; - type AssetTransactor = ::AssetTransactor; -} - #[cfg(feature = "runtime-benchmarks")] pub mod benchmark_helpers { use crate::{EthereumBeaconClient, Runtime, RuntimeOrigin}; diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs index 0782ea874793..38cbe2337a32 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs @@ -559,7 +559,6 @@ construct_runtime!( EthereumSystem: snowbridge_pallet_system = 83, EthereumInboundQueueV2: snowbridge_pallet_inbound_queue_v2 = 84, EthereumOutboundQueueV2: snowbridge_pallet_outbound_queue_v2 = 85, - EthereumRewards: snowbridge_pallet_rewards = 86, // Message Queue. Importantly, is registered last so that messages are processed after // the `on_initialize` hooks of bridging pallets. diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_bridge_relayers.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_bridge_relayers.rs index 74be73df1403..98ebb57f2273 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_bridge_relayers.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_bridge_relayers.rs @@ -120,4 +120,13 @@ impl pallet_bridge_relayers::WeightInfo for WeightInfo< .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } + fn claim() -> Weight { + // Proof Size summary in bytes: + // Measured: `80` + // Estimated: `3517` + // Minimum execution time: 44_000_000 picoseconds. + Weight::from_parts(44_000_000, 3517) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } } From 44cd54e7e70a9178ffbc40903fc31705c87726cb Mon Sep 17 00:00:00 2001 From: claravanstaden Date: Thu, 31 Oct 2024 10:12:56 +0200 Subject: [PATCH 35/37] fmt --- .../snowbridge/pallets/inbound-queue-v2/src/mock.rs | 2 +- .../pallets/outbound-queue-v2/src/mock.rs | 2 +- .../bridge-hub-westend/src/tests/snowbridge_v2.rs | 13 +++++++------ .../bridge-hub-westend/src/bridge_common_config.rs | 7 ++----- .../src/bridge_to_ethereum_config.rs | 7 +++---- 5 files changed, 14 insertions(+), 17 deletions(-) diff --git a/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs b/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs index 0f6fc34efdb1..20b884ccc57b 100644 --- a/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs +++ b/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs @@ -13,7 +13,7 @@ use snowbridge_core::{ inbound::{Log, Proof, VerificationError}, TokenId, }; -use sp_core::{ConstU128, H160, ConstU8}; +use sp_core::{ConstU128, ConstU8, H160}; use sp_runtime::{ traits::{IdentifyAccount, IdentityLookup, MaybeEquivalence, Verify}, BuildStorage, MultiSignature, diff --git a/bridges/snowbridge/pallets/outbound-queue-v2/src/mock.rs b/bridges/snowbridge/pallets/outbound-queue-v2/src/mock.rs index 566600caf3f9..bcffaf5173d9 100644 --- a/bridges/snowbridge/pallets/outbound-queue-v2/src/mock.rs +++ b/bridges/snowbridge/pallets/outbound-queue-v2/src/mock.rs @@ -21,7 +21,7 @@ use snowbridge_core::{ pricing::{PricingParameters, Rewards}, ParaId, }; -use sp_core::{ConstU128, ConstU8, ConstU32, H160, H256}; +use sp_core::{ConstU128, ConstU32, ConstU8, H160, H256}; use sp_runtime::{ traits::{BlakeTwo256, IdentityLookup, Keccak256}, AccountId32, BuildStorage, FixedU128, diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2.rs index 423c764620a1..503485c49b9f 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2.rs @@ -16,8 +16,8 @@ use crate::imports::*; use bridge_hub_westend_runtime::EthereumInboundQueue; use hex_literal::hex; use snowbridge_core::rewards::RewardLedger; -use snowbridge_router_primitives::inbound::{ - v1::{Command, Destination, MessageV1, VersionedMessage}, +use snowbridge_router_primitives::inbound::v1::{ + Command, Destination, MessageV1, VersionedMessage, }; use testnet_parachains_constants::westend::snowbridge::EthereumNetwork; @@ -202,11 +202,12 @@ fn claim_rewards_works() { ] ); - let relayer_location = Location::new(1, [Parachain(1000), Junction::AccountId32{ id: reward_address.into(), network: None}]); - let result = BridgeRelayers::claim( - RuntimeOrigin::signed(relayer.clone()), - relayer_location.clone(), + let relayer_location = Location::new( + 1, + [Parachain(1000), Junction::AccountId32 { id: reward_address.into(), network: None }], ); + let result = + BridgeRelayers::claim(RuntimeOrigin::signed(relayer.clone()), relayer_location.clone()); assert_ok!(result); let events = BridgeHubWestend::events(); diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_common_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_common_config.rs index ae026be7ba92..3782acbe42f4 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_common_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_common_config.rs @@ -22,15 +22,12 @@ //! GRANDPA tracking pallet only needs to be aware of one chain. use super::{weights, AccountId, Balance, Balances, BlockNumber, Runtime, RuntimeEvent}; +use crate::{xcm_config, XcmRouter}; use bp_messages::LegacyLaneId; use frame_support::parameter_types; use sp_core::H160; +use sp_runtime::traits::{ConstU128, ConstU32, ConstU8}; use testnet_parachains_constants::westend::snowbridge::EthereumNetwork; -use crate::XcmRouter; -use crate::xcm_config; -use sp_runtime::traits::ConstU32; -use sp_runtime::traits::ConstU128; -use sp_runtime::traits::ConstU8; parameter_types! { pub storage RequiredStakeForStakeAndSlash: Balance = 1_000_000; diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs index 0b6bd0c85764..cf9384c62a84 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs @@ -16,12 +16,11 @@ #[cfg(not(feature = "runtime-benchmarks"))] use crate::XcmRouter; -use crate::BridgeRelayers; use crate::{ xcm_config, xcm_config::{TreasuryAccount, UniversalLocation}, - Balances, EthereumInboundQueue, EthereumOutboundQueue, EthereumSystem, MessageQueue, Runtime, - RuntimeEvent, TransactionByteFee, + Balances, BridgeRelayers, EthereumInboundQueue, EthereumOutboundQueue, EthereumSystem, + MessageQueue, Runtime, RuntimeEvent, TransactionByteFee, }; use parachains_common::{AccountId, Balance}; use snowbridge_beacon_primitives::{Fork, ForkVersions}; @@ -31,12 +30,12 @@ use snowbridge_router_primitives::{ outbound::{v1::EthereumBlobExporter, v2::EthereumBlobExporter as EthereumBlobExporterV2}, }; use sp_core::H160; +use sp_runtime::traits::ConstU8; use testnet_parachains_constants::westend::{ currency::*, fee::WeightToFee, snowbridge::{EthereumLocation, EthereumNetwork, INBOUND_QUEUE_PALLET_INDEX}, }; -use sp_runtime::traits::ConstU8; use crate::xcm_config::RelayNetwork; #[cfg(feature = "runtime-benchmarks")] From 2afc09ff8c0899aa47e1324a0a837d250d8a74f3 Mon Sep 17 00:00:00 2001 From: claravanstaden Date: Thu, 31 Oct 2024 10:22:15 +0200 Subject: [PATCH 36/37] use configured inbound queue pallet index --- bridges/modules/relayers/src/lib.rs | 3 ++- .../bridge-hub-rococo/src/bridge_common_config.rs | 6 +++--- .../bridge-hub-westend/src/bridge_common_config.rs | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/bridges/modules/relayers/src/lib.rs b/bridges/modules/relayers/src/lib.rs index 4069678fbf4e..70ef23590fe4 100644 --- a/bridges/modules/relayers/src/lib.rs +++ b/bridges/modules/relayers/src/lib.rs @@ -466,8 +466,9 @@ pub mod pallet { RewardsMapping::::remove(account_id.clone()); let dest = Location::new(1, [Parachain(T::AssetHubParaId::get().into())]); - let (_xcm_hash, _) = send_xcm::(dest, xcm).map_err(Error::::from)?; + let (_xcm_hash, xcm_delivery_fee) = send_xcm::(dest, xcm).map_err(Error::::from)?; + // TODO charge delivery fee Self::deposit_event(Event::RewardClaimed { account_id, deposit_location, diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs index 9f4b3233aaa1..4c5a7ccb18b4 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs @@ -24,7 +24,7 @@ use super::{weights, AccountId, Balance, Balances, BlockNumber, Runtime, RuntimeEvent}; use bp_parachains::SingleParaStoredHeaderDataBuilder; use frame_support::{parameter_types, traits::ConstU32}; -use testnet_parachains_constants::rococo::snowbridge::EthereumNetwork; +use testnet_parachains_constants::rococo::snowbridge::{EthereumNetwork, INBOUND_QUEUE_PALLET_INDEX}; use sp_core::H160; use crate::xcm_config; use crate::XcmRouter; @@ -98,7 +98,7 @@ impl pallet_bridge_relayers::Config fo type XcmSender = DoNothingRouter; type Token = Balances; type AssetTransactor = ::AssetTransactor; - type InboundQueuePalletInstance = ConstU8<80>; + type InboundQueuePalletInstance = ConstU8; type AssetHubXCMFee = ConstU128<1_000_000_000_000>; } @@ -131,7 +131,7 @@ impl pallet_bridge_relayers::Config for type XcmSender = DoNothingRouter; type Token = Balances; type AssetTransactor = ::AssetTransactor; - type InboundQueuePalletInstance = ConstU8<80>; + type InboundQueuePalletInstance = ConstU8; type AssetHubXCMFee = ConstU128<1_000_000_000_000>; } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_common_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_common_config.rs index 3782acbe42f4..d0c5eac79b3f 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_common_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_common_config.rs @@ -27,7 +27,7 @@ use bp_messages::LegacyLaneId; use frame_support::parameter_types; use sp_core::H160; use sp_runtime::traits::{ConstU128, ConstU32, ConstU8}; -use testnet_parachains_constants::westend::snowbridge::EthereumNetwork; +use testnet_parachains_constants::westend::snowbridge::{EthereumNetwork, INBOUND_QUEUE_PALLET_INDEX}; parameter_types! { pub storage RequiredStakeForStakeAndSlash: Balance = 1_000_000; @@ -73,6 +73,6 @@ impl pallet_bridge_relayers::Config fo type XcmSender = DoNothingRouter; type Token = Balances; type AssetTransactor = ::AssetTransactor; - type InboundQueuePalletInstance = ConstU8<80>; + type InboundQueuePalletInstance = ConstU8; type AssetHubXCMFee = ConstU128<1_000_000_000_000>; } From 8dadc2d541defe46c7704783e7dbf06951a8d500 Mon Sep 17 00:00:00 2001 From: claravanstaden Date: Thu, 31 Oct 2024 14:58:04 +0200 Subject: [PATCH 37/37] update tests --- bridges/modules/relayers/src/lib.rs | 96 ++++++++++++------- bridges/modules/relayers/src/mock.rs | 15 ++- .../pallets/inbound-queue-v2/src/mock.rs | 8 +- .../pallets/inbound-queue/src/lib.rs | 3 +- .../pallets/outbound-queue-v2/src/mock.rs | 7 +- .../snowbridge/primitives/core/src/fees.rs | 6 +- .../src/tests/snowbridge_v2.rs | 8 +- .../src/bridge_common_config.rs | 17 ++-- .../src/bridge_common_config.rs | 11 ++- 9 files changed, 103 insertions(+), 68 deletions(-) diff --git a/bridges/modules/relayers/src/lib.rs b/bridges/modules/relayers/src/lib.rs index 70ef23590fe4..521b3ebd966a 100644 --- a/bridges/modules/relayers/src/lib.rs +++ b/bridges/modules/relayers/src/lib.rs @@ -30,18 +30,21 @@ use sp_arithmetic::traits::{AtLeast32BitUnsigned, Zero}; use sp_runtime::{traits::CheckedSub, Saturating}; use sp_std::marker::PhantomData; -pub use pallet::*; -pub use payment_adapter::DeliveryConfirmationPaymentsAdapter; -pub use stake_adapter::StakeAndSlashNamed; -pub use weights::WeightInfo; -pub use weights_ext::WeightInfoExt; use frame_support::{ - traits::fungible::{Inspect, Mutate}, + traits::{ + fungible::{Inspect, Mutate}, + tokens::Preservation, + }, PalletError, }; use frame_system::pallet_prelude::*; +pub use pallet::*; +pub use payment_adapter::DeliveryConfirmationPaymentsAdapter; use snowbridge_core::rewards::RewardLedger; use sp_core::H160; +pub use stake_adapter::StakeAndSlashNamed; +pub use weights::WeightInfo; +pub use weights_ext::WeightInfoExt; use xcm::prelude::{send_xcm, SendError as XcmpSendError, *}; use xcm_executor::traits::TransactAsset; @@ -62,7 +65,7 @@ pub const LOG_TARGET: &str = "runtime::bridge-relayers"; pub type AccountIdOf = ::AccountId; pub type BalanceOf = -<>::Token as Inspect<::AccountId>>::Balance; + <>::Token as Inspect<::AccountId>>::Balance; #[frame_support::pallet] pub mod pallet { @@ -109,6 +112,9 @@ pub mod pallet { type AssetTransactor: TransactAsset; type AssetHubXCMFee: Get; type Token: Mutate + Inspect; + /// TreasuryAccount to collect fees + #[pallet::constant] + type TreasuryAccount: Get; } #[pallet::pallet] @@ -256,13 +262,14 @@ pub mod pallet { ) } - /// Claim accumulated rewards. + /// Claim accumulated relayer rewards. Balance is minted on AH. + /// Fees: + /// BH execution fee - paid in DOT when executing the claim extrinsic + /// XCM delivery fee to AH - paid in DOT to Treasury on BH + /// AH execution fee - paid in Weth, deducated from relayer accumulated rewards #[pallet::call_index(3)] #[pallet::weight((T::WeightInfo::claim(), DispatchClass::Operational))] - pub fn claim( - origin: OriginFor, - deposit_location: Location, - ) -> DispatchResult { + pub fn claim(origin: OriginFor, deposit_location: Location) -> DispatchResult { let account_id = ensure_signed(origin)?; Self::process_claim(account_id, deposit_location)?; Ok(()) @@ -431,10 +438,8 @@ pub mod pallet { Ok(()) } - fn process_claim( - account_id: AccountIdOf, - deposit_location: Location, - ) -> DispatchResult { + /// Claim rewards on AH, based on accumulated rewards balance in storage. + fn process_claim(account_id: AccountIdOf, deposit_location: Location) -> DispatchResult { let value = RewardsMapping::::get(account_id.clone()); if value.is_zero() { return Err(Error::::InsufficientFunds.into()); @@ -454,26 +459,45 @@ pub mod pallet { UniversalOrigin(GlobalConsensus(T::EthereumNetwork::get())), ReserveAssetDeposited(reward_asset.clone().into()), BuyExecution { fees: fee_asset, weight_limit: Unlimited }, - DepositAsset { assets: AllCounted(1).into(), beneficiary: deposit_location.clone() }, + DepositAsset { + assets: AllCounted(1).into(), + beneficiary: deposit_location.clone() + }, SetAppendix(Xcm(alloc::vec![ RefundSurplus, - DepositAsset { assets: AllCounted(1).into(), beneficiary: deposit_location.clone() }, + DepositAsset { + assets: AllCounted(1).into(), + beneficiary: deposit_location.clone() + }, ])), ] - .into(); + .into(); // Remove the reward since it has been claimed. RewardsMapping::::remove(account_id.clone()); let dest = Location::new(1, [Parachain(T::AssetHubParaId::get().into())]); - let (_xcm_hash, xcm_delivery_fee) = send_xcm::(dest, xcm).map_err(Error::::from)?; + let (_xcm_hash, xcm_delivery_fee) = + send_xcm::(dest, xcm).map_err(Error::::from)?; + + match xcm_delivery_fee.get(0) { + Some(fee_asset) => + if let Fungible(amount) = fee_asset.fun { + let xcm_delivery_fee_balance: BalanceOf = + TryInto::>::try_into(amount) + .map_err(|_| Error::::InvalidAmount)?; + let treasury_account = T::TreasuryAccount::get(); + T::Token::transfer( + &account_id, + &treasury_account, + xcm_delivery_fee_balance, + Preservation::Preserve, + )?; + }, + None => (), + }; - // TODO charge delivery fee - Self::deposit_event(Event::RewardClaimed { - account_id, - deposit_location, - value, - }); + Self::deposit_event(Event::RewardClaimed { account_id, deposit_location, value }); Ok(()) } } @@ -619,7 +643,7 @@ pub mod pallet { #[pallet::storage] pub type RewardsMapping, I: 'static = ()> = - StorageMap<_, Identity, AccountIdOf, BalanceOf, ValueQuery>; + StorageMap<_, Identity, AccountIdOf, BalanceOf, ValueQuery>; impl, I: 'static> RewardLedger, BalanceOf> for Pallet { fn deposit(account_id: AccountIdOf, value: BalanceOf) -> DispatchResult { @@ -636,19 +660,18 @@ pub mod pallet { #[cfg(test)] mod tests { use super::*; - use sp_core::H256; use bp_messages::LaneIdType; use mock::{RuntimeEvent as TestEvent, *}; + use sp_core::H256; use crate::Event::{RewardPaid, RewardRegistered}; use bp_relayers::RewardsAccountOwner; use frame_support::{ - assert_noop, assert_ok, + assert_err, assert_noop, assert_ok, traits::fungible::{Inspect, Mutate}, }; use frame_system::{EventRecord, Pallet as System, Phase}; use sp_runtime::DispatchError; - use frame_support::assert_err; fn get_ready_for_events() { System::::set_block_number(1); @@ -1121,7 +1144,11 @@ mod tests { fn test_claim() { run_test(|| { let relayer: u64 = 1; - let interior: InteriorLocation = [Parachain(1000), Junction::AccountId32{network: None, id: H256::random().into()}].into(); + let interior: InteriorLocation = [ + Parachain(1000), + Junction::AccountId32 { network: None, id: H256::random().into() }, + ] + .into(); let claim_location = Location::new(1, interior); let result = Pallet::::claim( @@ -1136,13 +1163,10 @@ mod tests { assert_ok!(result2); // Claim rewards - let result3 = Pallet::::claim( - RuntimeOrigin::signed(relayer), - claim_location, - ); + let result3 = + Pallet::::claim(RuntimeOrigin::signed(relayer), claim_location); assert_ok!(result3); assert_eq!(>::get(relayer), 0); }); } - } diff --git a/bridges/modules/relayers/src/mock.rs b/bridges/modules/relayers/src/mock.rs index 4450583c7388..541ab2f2ffdd 100644 --- a/bridges/modules/relayers/src/mock.rs +++ b/bridges/modules/relayers/src/mock.rs @@ -18,6 +18,7 @@ use crate as pallet_bridge_relayers; +use crate::XcmpSendError; use bp_header_chain::ChainWithGrandpa; use bp_messages::{ target_chain::{DispatchMessage, MessageDispatch}, @@ -34,19 +35,15 @@ use frame_support::{ traits::fungible::Mutate, weights::{ConstantMultiplier, IdentityFee, RuntimeDbWeight, Weight}, }; +use hex_literal::hex; use pallet_transaction_payment::Multiplier; -use sp_core::{ConstU64, ConstU8, H256}; +use sp_core::{ConstU64, ConstU8, H160, H256}; use sp_runtime::{ - traits::{BlakeTwo256, ConstU32}, + traits::{BlakeTwo256, ConstU128, ConstU32}, BuildStorage, FixedPointNumber, Perquintill, StateVersion, }; -use sp_core::H160; use xcm::{latest::SendXcm, prelude::*}; -use crate::XcmpSendError; -use hex_literal::hex; -use xcm_executor::AssetsInHolding; -use xcm_executor::traits::TransactAsset; -use sp_runtime::traits::ConstU128; +use xcm_executor::{traits::TransactAsset, AssetsInHolding}; /// Account identifier at `ThisChain`. pub type ThisChainAccountId = u64; @@ -305,6 +302,7 @@ impl pallet_bridge_relayers::Config for TestRuntime { type AssetTransactor = SuccessfulTransactor; type AssetHubXCMFee = ConstU128<1_000_000_000_000u128>; + type InboundQueuePalletInstance = ConstU8<80>; } #[cfg(feature = "runtime-benchmarks")] @@ -450,7 +448,6 @@ impl TransactAsset for SuccessfulTransactor { } } - /// Reward account params that we are using in tests. pub fn test_reward_account_param() -> RewardsAccountParams { RewardsAccountParams::new( diff --git a/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs b/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs index 20b884ccc57b..84445511d6c8 100644 --- a/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs +++ b/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs @@ -4,7 +4,7 @@ use super::*; use bp_messages::{HashedLaneId, LaneIdType}; use bp_relayers::{PayRewardFromAccount, PaymentProcedure, RewardsAccountParams}; -use frame_support::{derive_impl, parameter_types, traits::ConstU32}; +use frame_support::{derive_impl, parameter_types, traits::ConstU32, PalletId}; use hex_literal::hex; use snowbridge_beacon_primitives::{ types::deneb, BeaconHeader, ExecutionProof, Fork, ForkVersions, VersionedExecutionPayloadHeader, @@ -15,7 +15,7 @@ use snowbridge_core::{ }; use sp_core::{ConstU128, ConstU8, H160}; use sp_runtime::{ - traits::{IdentifyAccount, IdentityLookup, MaybeEquivalence, Verify}, + traits::{AccountIdConversion, IdentifyAccount, IdentityLookup, MaybeEquivalence, Verify}, BuildStorage, MultiSignature, }; use sp_std::{convert::From, default::Default}; @@ -177,6 +177,7 @@ impl inbound_queue::Config for Test { parameter_types! { pub WethAddress: H160 = hex!("774667629726ec1FaBEbCEc0D9139bD1C8f72a23").into(); + pub TreasuryAccount: AccountId = PalletId(*b"py/trsry").into_account_truncating(); } pub type TestLaneIdType = HashedLaneId; @@ -216,7 +217,8 @@ impl pallet_bridge_relayers::Config for Test { type WethAddress = WethAddress; type XcmSender = MockXcmSender; type AssetTransactor = SuccessfulTransactor; - type AssetHubXCMFee = ConstU128<1_000_000_000_000u128>; + type AssetHubXCMFee = ConstU128<15u128>; + type TreasuryAccount = TreasuryAccount; } pub fn last_events(n: usize) -> Vec { diff --git a/bridges/snowbridge/pallets/inbound-queue/src/lib.rs b/bridges/snowbridge/pallets/inbound-queue/src/lib.rs index 7a5265d0d07d..6a2fec5587a8 100644 --- a/bridges/snowbridge/pallets/inbound-queue/src/lib.rs +++ b/bridges/snowbridge/pallets/inbound-queue/src/lib.rs @@ -291,7 +291,8 @@ pub mod pallet { ); // Burning fees for teleport - burn_fees::>(channel.para_id, fee)?; + let parachain_location = Location::new(1, [Parachain(channel.para_id.into())]); + burn_fees::>(parachain_location, fee)?; // Attempt to send XCM to a dest parachain let message_id = Self::send_xcm(xcm, channel.para_id)?; diff --git a/bridges/snowbridge/pallets/outbound-queue-v2/src/mock.rs b/bridges/snowbridge/pallets/outbound-queue-v2/src/mock.rs index bcffaf5173d9..3d4cff6e1024 100644 --- a/bridges/snowbridge/pallets/outbound-queue-v2/src/mock.rs +++ b/bridges/snowbridge/pallets/outbound-queue-v2/src/mock.rs @@ -12,6 +12,7 @@ use frame_support::{ use bp_messages::HashedLaneId; use bp_relayers::{PayRewardFromAccount, PaymentProcedure, RewardsAccountParams}; use codec::Encode; +use frame_support::PalletId; use hex_literal::hex; use snowbridge_core::{ gwei, @@ -23,7 +24,7 @@ use snowbridge_core::{ }; use sp_core::{ConstU128, ConstU32, ConstU8, H160, H256}; use sp_runtime::{ - traits::{BlakeTwo256, IdentityLookup, Keccak256}, + traits::{AccountIdConversion, BlakeTwo256, IdentityLookup, Keccak256}, AccountId32, BuildStorage, FixedU128, }; use sp_std::marker::PhantomData; @@ -137,6 +138,7 @@ impl crate::Config for Test { parameter_types! { pub WethAddress: H160 = hex!("774667629726ec1FaBEbCEc0D9139bD1C8f72a23").into(); + pub TreasuryAccount: AccountId = PalletId(*b"py/trsry").into_account_truncating(); } pub type TestLaneIdType = HashedLaneId; @@ -181,7 +183,8 @@ impl pallet_bridge_relayers::Config for Test { type XcmSender = MockXcmSender; type AssetTransactor = SuccessfulTransactor; - type AssetHubXCMFee = ConstU128<1_000_000_000_000u128>; + type AssetHubXCMFee = ConstU128<15u128>; + type TreasuryAccount = TreasuryAccount; } // Mock XCM sender that always succeeds diff --git a/bridges/snowbridge/primitives/core/src/fees.rs b/bridges/snowbridge/primitives/core/src/fees.rs index 3b122cc32bda..44c800a2d820 100644 --- a/bridges/snowbridge/primitives/core/src/fees.rs +++ b/bridges/snowbridge/primitives/core/src/fees.rs @@ -1,20 +1,18 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-FileCopyrightText: 2023 Snowfork -use crate::ParaId; use log; use sp_runtime::{DispatchResult, SaturatedConversion, Saturating, TokenError}; -use xcm::opaque::lts::{Junction::Parachain, Location, XcmContext}; +use xcm::opaque::lts::{Location, XcmContext}; use xcm_executor::traits::TransactAsset; const LOG_TARGET: &str = "xcm_fees"; /// Burns the fees embedded in the XCM for teleports. -pub fn burn_fees(para_id: ParaId, fee: Balance) -> DispatchResult +pub fn burn_fees(dest: Location, fee: Balance) -> DispatchResult where AssetTransactor: TransactAsset, Balance: Saturating + TryInto + Copy, { let dummy_context = XcmContext { origin: None, message_id: Default::default(), topic: None }; - let dest = Location::new(1, [Parachain(para_id.into())]); let fees = (Location::parent(), fee.saturated_into::()).into(); // Check if the asset can be checked out diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2.rs index 503485c49b9f..97b692b85844 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2.rs @@ -167,7 +167,12 @@ fn claim_rewards_works() { let weth_asset_location: Location = (Parent, Parent, EthereumNetwork::get(), AccountKey20 { network: None, key: weth }).into(); - BridgeHubWestend::fund_accounts(vec![(assethub_sovereign.clone(), INITIAL_FUND)]); + let relayer = BridgeHubWestendSender::get(); + + BridgeHubWestend::fund_accounts(vec![ + (assethub_sovereign.clone(), INITIAL_FUND), + (relayer.clone(), INITIAL_FUND), + ]); AssetHubWestend::execute_with(|| { type RuntimeOrigin = ::RuntimeOrigin; @@ -189,7 +194,6 @@ fn claim_rewards_works() { type RuntimeEvent = ::RuntimeEvent; type RuntimeOrigin = ::RuntimeOrigin; - let relayer = BridgeHubWestendSender::get(); let reward_address = AssetHubWestendReceiver::get(); type BridgeRelayers = ::BridgeRelayers; assert_ok!(BridgeRelayers::deposit(relayer.clone().into(), 2 * ETH)); diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs index 4c5a7ccb18b4..34d78d082754 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs @@ -22,14 +22,14 @@ //! GRANDPA tracking pallet only needs to be aware of one chain. use super::{weights, AccountId, Balance, Balances, BlockNumber, Runtime, RuntimeEvent}; +use crate::{xcm_config, TreasuryAccount, XcmRouter}; use bp_parachains::SingleParaStoredHeaderDataBuilder; use frame_support::{parameter_types, traits::ConstU32}; -use testnet_parachains_constants::rococo::snowbridge::{EthereumNetwork, INBOUND_QUEUE_PALLET_INDEX}; use sp_core::H160; -use crate::xcm_config; -use crate::XcmRouter; -use sp_runtime::traits::ConstU128; -use sp_runtime::traits::ConstU8; +use sp_runtime::traits::{ConstU128, ConstU8}; +use testnet_parachains_constants::rococo::snowbridge::{ + EthereumNetwork, INBOUND_QUEUE_PALLET_INDEX, +}; parameter_types! { pub const RelayChainHeadersToKeep: u32 = 1024; @@ -99,7 +99,8 @@ impl pallet_bridge_relayers::Config fo type Token = Balances; type AssetTransactor = ::AssetTransactor; type InboundQueuePalletInstance = ConstU8; - type AssetHubXCMFee = ConstU128<1_000_000_000_000>; + type AssetHubXCMFee = ConstU128<15>; + type TreasuryAccount = TreasuryAccount; } /// Allows collect and claim rewards for relayers @@ -132,7 +133,8 @@ impl pallet_bridge_relayers::Config for type Token = Balances; type AssetTransactor = ::AssetTransactor; type InboundQueuePalletInstance = ConstU8; - type AssetHubXCMFee = ConstU128<1_000_000_000_000>; + type AssetHubXCMFee = ConstU128<15>; + type TreasuryAccount = TreasuryAccount; } parameter_types! { @@ -141,7 +143,6 @@ parameter_types! { pub const ASSET_HUB_ID: u32 = rococo_runtime_constants::system_parachain::ASSET_HUB_ID; - /// Add GRANDPA bridge pallet to track Rococo Bulletin chain. pub type BridgeGrandpaRococoBulletinInstance = pallet_bridge_grandpa::Instance4; impl pallet_bridge_grandpa::Config for Runtime { diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_common_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_common_config.rs index d0c5eac79b3f..7a250156d258 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_common_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_common_config.rs @@ -22,12 +22,14 @@ //! GRANDPA tracking pallet only needs to be aware of one chain. use super::{weights, AccountId, Balance, Balances, BlockNumber, Runtime, RuntimeEvent}; -use crate::{xcm_config, XcmRouter}; +use crate::{xcm_config, xcm_config::TreasuryAccount, XcmRouter}; use bp_messages::LegacyLaneId; use frame_support::parameter_types; use sp_core::H160; use sp_runtime::traits::{ConstU128, ConstU32, ConstU8}; -use testnet_parachains_constants::westend::snowbridge::{EthereumNetwork, INBOUND_QUEUE_PALLET_INDEX}; +use testnet_parachains_constants::westend::snowbridge::{ + EthereumNetwork, INBOUND_QUEUE_PALLET_INDEX, +}; parameter_types! { pub storage RequiredStakeForStakeAndSlash: Balance = 1_000_000; @@ -74,5 +76,8 @@ impl pallet_bridge_relayers::Config fo type Token = Balances; type AssetTransactor = ::AssetTransactor; type InboundQueuePalletInstance = ConstU8; - type AssetHubXCMFee = ConstU128<1_000_000_000_000>; + /// Execution cost on AH in Weth. Cost is approximately 0.000000000000000008, added a slightly + /// buffer. + type AssetHubXCMFee = ConstU128<15>; + type TreasuryAccount = TreasuryAccount; }