feat: initialize Kurdistan SDK - independent fork of Polkadot SDK
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2023 Snowfork <hello@snowfork.com>
|
||||
use super::*;
|
||||
|
||||
use crate::Pallet as InboundQueue;
|
||||
use frame_benchmarking::v2::*;
|
||||
use frame_support::assert_ok;
|
||||
use frame_system::RawOrigin;
|
||||
|
||||
#[benchmarks]
|
||||
mod benchmarks {
|
||||
use super::*;
|
||||
|
||||
#[benchmark]
|
||||
fn submit() -> Result<(), BenchmarkError> {
|
||||
let caller: T::AccountId = whitelisted_caller();
|
||||
|
||||
let create_message = T::Helper::initialize_storage();
|
||||
|
||||
#[block]
|
||||
{
|
||||
assert_ok!(InboundQueue::<T>::submit(
|
||||
RawOrigin::Signed(caller.clone()).into(),
|
||||
Box::new(create_message.event),
|
||||
));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl_benchmark_test_suite!(InboundQueue, crate::mock::new_tester(), crate::mock::Test);
|
||||
}
|
||||
@@ -0,0 +1,310 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2023 Snowfork <hello@snowfork.com>
|
||||
//! Inbound Queue
|
||||
//!
|
||||
//! # Overview
|
||||
//!
|
||||
//! Receives messages emitted by the Gateway contract on Ethereum, whereupon they are verified,
|
||||
//! translated to XCM, and finally sent to AssetHub for further processing.
|
||||
//!
|
||||
//! Message relayers are rewarded in wrapped Ether that is included within the message. This
|
||||
//! wrapped Ether is derived from Ether that the message origin has locked up on Ethereum.
|
||||
//!
|
||||
//! # Extrinsics
|
||||
//!
|
||||
//! ## Governance
|
||||
//!
|
||||
//! * [`Call::set_operating_mode`]: Set the operating mode of the pallet. Can be used to disable
|
||||
//! processing of inbound messages.
|
||||
//!
|
||||
//! ## Message Submission
|
||||
//!
|
||||
//! * [`Call::submit`]: Submit a message for verification and dispatch to the final destination
|
||||
//! teyrchain.
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
mod benchmarking;
|
||||
pub mod weights;
|
||||
|
||||
#[cfg(test)]
|
||||
mod mock;
|
||||
|
||||
#[cfg(test)]
|
||||
mod test;
|
||||
|
||||
pub use crate::weights::WeightInfo;
|
||||
use bp_relayers::RewardLedger;
|
||||
use frame_system::ensure_signed;
|
||||
use snowbridge_core::{
|
||||
reward::{AddTip, AddTipError},
|
||||
sparse_bitmap::{SparseBitmap, SparseBitmapImpl},
|
||||
BasicOperatingMode,
|
||||
};
|
||||
use snowbridge_inbound_queue_primitives::{
|
||||
v2::{ConvertMessage, ConvertMessageError, Message},
|
||||
EventProof, VerificationError, Verifier,
|
||||
};
|
||||
use sp_core::H160;
|
||||
use sp_runtime::traits::TryConvert;
|
||||
use sp_std::prelude::*;
|
||||
use xcm::prelude::{ExecuteXcm, Junction::*, Location, SendXcm, *};
|
||||
|
||||
pub use pallet::*;
|
||||
|
||||
pub const LOG_TARGET: &str = "snowbridge-pallet-inbound-queue-v2";
|
||||
|
||||
pub type AccountIdOf<T> = <T as frame_system::Config>::AccountId;
|
||||
|
||||
pub type Nonce<T> = SparseBitmapImpl<crate::NonceBitmap<T>>;
|
||||
|
||||
#[frame_support::pallet]
|
||||
pub mod pallet {
|
||||
use super::*;
|
||||
|
||||
use frame_support::pallet_prelude::*;
|
||||
use frame_system::pallet_prelude::*;
|
||||
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
use snowbridge_inbound_queue_primitives::EventFixture;
|
||||
|
||||
#[pallet::pallet]
|
||||
pub struct Pallet<T>(_);
|
||||
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
pub trait BenchmarkHelper<T> {
|
||||
fn initialize_storage() -> EventFixture;
|
||||
}
|
||||
|
||||
#[pallet::config]
|
||||
pub trait Config: frame_system::Config {
|
||||
#[allow(deprecated)]
|
||||
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
|
||||
/// The verifier for inbound messages from Ethereum.
|
||||
type Verifier: Verifier;
|
||||
/// XCM message sender.
|
||||
type XcmSender: SendXcm;
|
||||
/// Handler for XCM fees.
|
||||
type XcmExecutor: ExecuteXcm<Self::RuntimeCall>;
|
||||
/// Address of the Gateway contract.
|
||||
#[pallet::constant]
|
||||
type GatewayAddress: Get<H160>;
|
||||
/// AssetHub teyrchain ID.
|
||||
type AssetHubParaId: Get<u32>;
|
||||
/// Convert a command from Ethereum to an XCM message.
|
||||
type MessageConverter: ConvertMessage;
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
type Helper: BenchmarkHelper<Self>;
|
||||
/// Reward discriminator type.
|
||||
type RewardKind: Parameter + MaxEncodedLen + Send + Sync + Copy + Clone;
|
||||
/// The default RewardKind discriminator for rewards allocated to relayers from this pallet.
|
||||
#[pallet::constant]
|
||||
type DefaultRewardKind: Get<Self::RewardKind>;
|
||||
/// Relayer reward payment.
|
||||
type RewardPayment: RewardLedger<Self::AccountId, Self::RewardKind, u128>;
|
||||
/// AccountId to Location converter
|
||||
type AccountToLocation: for<'a> TryConvert<&'a Self::AccountId, Location>;
|
||||
type WeightInfo: WeightInfo;
|
||||
}
|
||||
|
||||
#[pallet::event]
|
||||
#[pallet::generate_deposit(pub(super) fn deposit_event)]
|
||||
pub enum Event<T: Config> {
|
||||
/// A message was received from Ethereum
|
||||
MessageReceived {
|
||||
/// The message nonce
|
||||
nonce: u64,
|
||||
/// ID of the XCM message which was forwarded to the final destination teyrchain
|
||||
message_id: [u8; 32],
|
||||
},
|
||||
/// Set OperatingMode
|
||||
OperatingModeChanged { mode: BasicOperatingMode },
|
||||
}
|
||||
|
||||
#[pallet::error]
|
||||
pub enum Error<T> {
|
||||
/// Message came from an invalid outbound channel on the Ethereum side.
|
||||
InvalidGateway,
|
||||
/// Account could not be converted to bytes
|
||||
InvalidAccount,
|
||||
/// Message has an invalid envelope.
|
||||
InvalidMessage,
|
||||
/// Message has an unexpected nonce.
|
||||
InvalidNonce,
|
||||
/// Fee provided is invalid.
|
||||
InvalidFee,
|
||||
/// Message has an invalid payload.
|
||||
InvalidPayload,
|
||||
/// Message channel is invalid
|
||||
InvalidChannel,
|
||||
/// The max nonce for the type has been reached
|
||||
MaxNonceReached,
|
||||
/// Cannot convert location
|
||||
InvalidAccountConversion,
|
||||
/// Invalid network specified
|
||||
InvalidNetwork,
|
||||
/// Pallet is halted
|
||||
Halted,
|
||||
/// The operation required fees to be paid which the initiator could not meet.
|
||||
FeesNotMet,
|
||||
/// The desired destination was unreachable, generally because there is a no way of routing
|
||||
/// to it.
|
||||
Unreachable,
|
||||
/// There was some other issue (i.e. not to do with routing) in sending the message.
|
||||
/// Perhaps a lack of space for buffering the message.
|
||||
SendFailure,
|
||||
/// Invalid foreign ERC-20 token ID
|
||||
InvalidAsset,
|
||||
/// Cannot reachor a foreign ERC-20 asset location.
|
||||
CannotReanchor,
|
||||
/// Message verification error
|
||||
Verification(VerificationError),
|
||||
}
|
||||
|
||||
impl<T: Config> From<SendError> for Error<T> {
|
||||
fn from(e: SendError) -> Self {
|
||||
match e {
|
||||
SendError::Fees => Error::<T>::FeesNotMet,
|
||||
SendError::NotApplicable => Error::<T>::Unreachable,
|
||||
_ => Error::<T>::SendFailure,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> From<ConvertMessageError> for Error<T> {
|
||||
fn from(e: ConvertMessageError) -> Self {
|
||||
match e {
|
||||
ConvertMessageError::InvalidAsset => Error::<T>::InvalidAsset,
|
||||
ConvertMessageError::CannotReanchor => Error::<T>::CannotReanchor,
|
||||
ConvertMessageError::InvalidNetwork => Error::<T>::InvalidNetwork,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// StorageMap used for encoding a SparseBitmapImpl that tracks whether a specific nonce has
|
||||
/// been processed or not. Message nonces are unique and never repeated.
|
||||
#[pallet::storage]
|
||||
pub type NonceBitmap<T: Config> = StorageMap<_, Twox64Concat, u64, u128, ValueQuery>;
|
||||
|
||||
/// The current operating mode of the pallet.
|
||||
#[pallet::storage]
|
||||
pub type OperatingMode<T: Config> = StorageValue<_, BasicOperatingMode, ValueQuery>;
|
||||
|
||||
/// Keep track of tips added for a message as an additional relayer incentivization. The
|
||||
/// key for the storage map is the nonce of the message to which the tip should be added.
|
||||
/// The value is the tip amount, in Ether.
|
||||
#[pallet::storage]
|
||||
pub type Tips<T: Config> = StorageMap<_, Blake2_128Concat, u64, u128, OptionQuery>;
|
||||
|
||||
#[pallet::call]
|
||||
impl<T: Config> Pallet<T> {
|
||||
/// Submit an inbound message originating from the Gateway contract on Ethereum
|
||||
#[pallet::call_index(0)]
|
||||
#[pallet::weight(T::WeightInfo::submit())]
|
||||
pub fn submit(origin: OriginFor<T>, event: Box<EventProof>) -> DispatchResult {
|
||||
let who = ensure_signed(origin)?;
|
||||
ensure!(!OperatingMode::<T>::get().is_halted(), Error::<T>::Halted);
|
||||
|
||||
// submit message for verification
|
||||
T::Verifier::verify(&event.event_log, &event.proof)
|
||||
.map_err(|e| Error::<T>::Verification(e))?;
|
||||
|
||||
// Decode event log into a bridge message
|
||||
let message =
|
||||
Message::try_from(&event.event_log).map_err(|_| Error::<T>::InvalidMessage)?;
|
||||
|
||||
Self::process_message(who, message)
|
||||
}
|
||||
|
||||
/// Halt or resume all pallet operations. May only be called by root.
|
||||
#[pallet::call_index(1)]
|
||||
#[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))]
|
||||
pub fn set_operating_mode(
|
||||
origin: OriginFor<T>,
|
||||
mode: BasicOperatingMode,
|
||||
) -> DispatchResult {
|
||||
ensure_root(origin)?;
|
||||
OperatingMode::<T>::set(mode);
|
||||
Self::deposit_event(Event::OperatingModeChanged { mode });
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> Pallet<T> {
|
||||
pub fn process_message(relayer: T::AccountId, message: Message) -> DispatchResult {
|
||||
// Verify that the message was submitted from the known Gateway contract
|
||||
ensure!(T::GatewayAddress::get() == message.gateway, Error::<T>::InvalidGateway);
|
||||
|
||||
let (nonce, relayer_fee) = (message.nonce, message.relayer_fee);
|
||||
|
||||
// Verify the message has not been processed
|
||||
ensure!(!Nonce::<T>::get(nonce), Error::<T>::InvalidNonce);
|
||||
|
||||
let xcm =
|
||||
T::MessageConverter::convert(message).map_err(|error| Error::<T>::from(error))?;
|
||||
|
||||
// Forward XCM to AH
|
||||
let dest = Location::new(1, [Teyrchain(T::AssetHubParaId::get())]);
|
||||
|
||||
// Mark message as received
|
||||
Nonce::<T>::set(nonce);
|
||||
|
||||
let message_id =
|
||||
Self::send_xcm(dest.clone(), &relayer, xcm.clone()).map_err(|error| {
|
||||
tracing::error!(target: LOG_TARGET, ?error, ?dest, ?xcm, "XCM send failed with error");
|
||||
Error::<T>::from(error)
|
||||
})?;
|
||||
|
||||
// Pay relayer reward
|
||||
let tip = Tips::<T>::take(nonce).unwrap_or_default();
|
||||
let total_tip = relayer_fee.saturating_add(tip);
|
||||
if total_tip > 0 {
|
||||
T::RewardPayment::register_reward(&relayer, T::DefaultRewardKind::get(), total_tip);
|
||||
}
|
||||
|
||||
Self::deposit_event(Event::MessageReceived { nonce, message_id });
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn send_xcm(
|
||||
dest: Location,
|
||||
fee_payer: &T::AccountId,
|
||||
xcm: Xcm<()>,
|
||||
) -> Result<XcmHash, SendError> {
|
||||
let (ticket, fee) = validate_send::<T::XcmSender>(dest, xcm)?;
|
||||
let fee_payer = T::AccountToLocation::try_convert(fee_payer).map_err(|err| {
|
||||
tracing::error!(
|
||||
target: LOG_TARGET,
|
||||
?err,
|
||||
"Failed to convert account to XCM location",
|
||||
);
|
||||
SendError::NotApplicable
|
||||
})?;
|
||||
T::XcmExecutor::charge_fees(fee_payer.clone(), fee.clone()).map_err(|error| {
|
||||
tracing::error!(
|
||||
target: LOG_TARGET,
|
||||
?error,
|
||||
"Charging fees failed with error",
|
||||
);
|
||||
SendError::Fees
|
||||
})?;
|
||||
T::XcmSender::deliver(ticket)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> AddTip for Pallet<T> {
|
||||
fn add_tip(nonce: u64, amount: u128) -> Result<(), AddTipError> {
|
||||
ensure!(amount > 0, AddTipError::AmountZero);
|
||||
// If the nonce is already processed, return an error
|
||||
ensure!(!Nonce::<T>::get(nonce.into()), AddTipError::NonceConsumed);
|
||||
// Otherwise add the tip.
|
||||
Tips::<T>::mutate(nonce, |tip| {
|
||||
*tip = Some(tip.unwrap_or_default().saturating_add(amount));
|
||||
});
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2023 Snowfork <hello@snowfork.com>
|
||||
use super::*;
|
||||
|
||||
use crate::{self as inbound_queue_v2};
|
||||
use frame_support::{derive_impl, parameter_types, traits::ConstU32};
|
||||
use hex_literal::hex;
|
||||
use snowbridge_beacon_primitives::{
|
||||
types::deneb, BeaconHeader, ExecutionProof, VersionedExecutionPayloadHeader,
|
||||
};
|
||||
use snowbridge_core::{ParaId, TokenId};
|
||||
use snowbridge_inbound_queue_primitives::{
|
||||
v2::{CreateAssetCallInfo, MessageToXcm},
|
||||
Log, Proof, VerificationError,
|
||||
};
|
||||
use sp_core::H160;
|
||||
use sp_runtime::{
|
||||
traits::{IdentityLookup, MaybeConvert},
|
||||
BuildStorage,
|
||||
};
|
||||
use sp_std::{convert::From, default::Default, marker::PhantomData};
|
||||
use xcm::{opaque::latest::ZAGROS_GENESIS_HASH, prelude::*};
|
||||
type Block = frame_system::mocking::MockBlock<Test>;
|
||||
use snowbridge_test_utils::mock_rewards::{BridgeReward, MockRewardLedger};
|
||||
pub use snowbridge_test_utils::mock_xcm::{MockXcmExecutor, MockXcmSender};
|
||||
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
use snowbridge_inbound_queue_primitives::EventFixture;
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
use snowbridge_pallet_inbound_queue_v2_fixtures::register_token::make_register_token_message;
|
||||
|
||||
frame_support::construct_runtime!(
|
||||
pub enum Test
|
||||
{
|
||||
System: frame_system::{Pallet, Call, Storage, Event<T>},
|
||||
Balances: pallet_balances::{Pallet, Call, Storage, Config<T>, Event<T>},
|
||||
InboundQueue: inbound_queue_v2::{Pallet, Call, Storage, Event<T>},
|
||||
}
|
||||
);
|
||||
|
||||
pub(crate) const ERROR_ADDRESS: [u8; 20] = hex!("0000000000000000000000000000000000000911");
|
||||
|
||||
pub type AccountId = sp_runtime::AccountId32;
|
||||
type Balance = u128;
|
||||
|
||||
#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
|
||||
impl frame_system::Config for Test {
|
||||
type AccountId = AccountId;
|
||||
type Lookup = IdentityLookup<Self::AccountId>;
|
||||
type AccountData = pallet_balances::AccountData<u128>;
|
||||
type Block = Block;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
impl Verifier for MockVerifier {
|
||||
fn verify(log: &Log, _: &Proof) -> Result<(), VerificationError> {
|
||||
if log.address == ERROR_ADDRESS.into() {
|
||||
return Err(VerificationError::InvalidProof);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
const GATEWAY_ADDRESS: [u8; 20] = hex!["b1185ede04202fe62d38f5db72f71e38ff3e8305"];
|
||||
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
impl<T: Config> BenchmarkHelper<T> for Test {
|
||||
// not implemented since the MockVerifier is used for tests
|
||||
fn initialize_storage() -> EventFixture {
|
||||
make_register_token_message()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MockTokenIdConvert;
|
||||
impl MaybeConvert<TokenId, Location> for MockTokenIdConvert {
|
||||
fn maybe_convert(_id: TokenId) -> Option<Location> {
|
||||
Some(Location::parent())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MockAccountLocationConverter<AccountId>(PhantomData<AccountId>);
|
||||
impl<'a, AccountId: Clone + Clone> TryConvert<&'a AccountId, Location>
|
||||
for MockAccountLocationConverter<AccountId>
|
||||
{
|
||||
fn try_convert(_who: &AccountId) -> Result<Location, &AccountId> {
|
||||
Ok(Location::here())
|
||||
}
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub const EthereumNetwork: NetworkId = Ethereum { chain_id: 11155111 };
|
||||
pub const GatewayAddress: H160 = H160(GATEWAY_ADDRESS);
|
||||
pub InboundQueueLocation: InteriorLocation = [PalletInstance(84)].into();
|
||||
pub SnowbridgeReward: BridgeReward = BridgeReward::Snowbridge;
|
||||
pub const CreateAssetCallIndex: [u8;2] = [53, 0];
|
||||
pub const SetReservesCallIndex: [u8;2] = [53, 33];
|
||||
pub const CreateAssetDeposit: u128 = 10_000_000_000u128;
|
||||
pub const LocalNetwork: NetworkId = ByGenesis(ZAGROS_GENESIS_HASH);
|
||||
pub CreateAssetCall: CreateAssetCallInfo = CreateAssetCallInfo {
|
||||
create_call: CreateAssetCallIndex::get(),
|
||||
deposit: CreateAssetDeposit::get(),
|
||||
min_balance: 1,
|
||||
set_reserves_call: SetReservesCallIndex::get(),
|
||||
};
|
||||
pub AssetHubParaId: ParaId = ParaId::from(1000);
|
||||
}
|
||||
|
||||
impl inbound_queue_v2::Config for Test {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type Verifier = MockVerifier;
|
||||
type XcmSender = MockXcmSender;
|
||||
type XcmExecutor = MockXcmExecutor;
|
||||
type GatewayAddress = GatewayAddress;
|
||||
type AssetHubParaId = ConstU32<1000>;
|
||||
type MessageConverter = MessageToXcm<
|
||||
CreateAssetCall,
|
||||
EthereumNetwork,
|
||||
LocalNetwork,
|
||||
GatewayAddress,
|
||||
InboundQueueLocation,
|
||||
AssetHubParaId,
|
||||
MockTokenIdConvert,
|
||||
AccountId,
|
||||
>;
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
type Helper = Test;
|
||||
type WeightInfo = ();
|
||||
type AccountToLocation = MockAccountLocationConverter<AccountId>;
|
||||
type RewardKind = BridgeReward;
|
||||
type DefaultRewardKind = SnowbridgeReward;
|
||||
type RewardPayment = MockRewardLedger;
|
||||
}
|
||||
|
||||
pub fn setup() {
|
||||
System::set_block_number(1);
|
||||
}
|
||||
|
||||
pub fn new_tester() -> sp_io::TestExternalities {
|
||||
let storage = frame_system::GenesisConfig::<Test>::default().build_storage().unwrap();
|
||||
let mut ext: sp_io::TestExternalities = storage.into();
|
||||
ext.execute_with(setup);
|
||||
ext
|
||||
}
|
||||
|
||||
// Generated from smoketests:
|
||||
// cd smoketests
|
||||
// ./make-bindings
|
||||
// cargo test --test register_token -- --nocapture
|
||||
pub fn mock_event_log() -> Log {
|
||||
Log {
|
||||
// gateway address
|
||||
address: hex!("b1185ede04202fe62d38f5db72f71e38ff3e8305").into(),
|
||||
topics: vec![
|
||||
hex!("550e2067494b1736ea5573f2d19cdc0ac95b410fff161bf16f11c6229655ec9c").into(),
|
||||
],
|
||||
// Nonce + Payload
|
||||
data: hex!("00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000b1185ede04202fe62d38f5db72f71e38ff3e830500000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000009184e72a0000000000000000000000000000000000000000000000000000000015d3ef798000000000000000000000000000000000000000000000000000000015d3ef798000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000b8ea8cb425d85536b158d661da1ef0895bb92f1d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mock_event_log_invalid_gateway() -> Log {
|
||||
Log {
|
||||
// gateway address
|
||||
address: H160::zero(),
|
||||
topics: vec![
|
||||
hex!("550e2067494b1736ea5573f2d19cdc0ac95b410fff161bf16f11c6229655ec9c").into(),
|
||||
],
|
||||
// Nonce + Payload
|
||||
data: hex!("00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000b1185ede04202fe62d38f5db72f71e38ff3e830500000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000009184e72a0000000000000000000000000000000000000000000000000000000015d3ef798000000000000000000000000000000000000000000000000000000015d3ef798000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000b8ea8cb425d85536b158d661da1ef0895bb92f1d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mock_event_log_invalid_message() -> Log {
|
||||
Log {
|
||||
// gateway address
|
||||
address: hex!("b8ea8cb425d85536b158d661da1ef0895bb92f1d").into(),
|
||||
topics: vec![
|
||||
hex!("b61699d45635baed7500944331ea827538a50dbfef79180f2079e9185da627aa").into(),
|
||||
],
|
||||
// Nonce + Payload
|
||||
data: hex!("000000000000000000000000000000000000000000000000000000b8ea8cb425d85536b158d661da1ef0895bb92f1d000000000000000000000000000000000000000000000000001dcd6500000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000059682f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002cdeadbeef774667629726ec1fabebcec0d9139bd1c8f72a23deadbeef0000000000000000000000001dcd650000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mock_execution_proof() -> ExecutionProof {
|
||||
ExecutionProof {
|
||||
header: BeaconHeader::default(),
|
||||
ancestry_proof: None,
|
||||
execution_header: VersionedExecutionPayloadHeader::Deneb(deneb::ExecutionPayloadHeader {
|
||||
parent_hash: Default::default(),
|
||||
fee_recipient: Default::default(),
|
||||
state_root: Default::default(),
|
||||
receipts_root: Default::default(),
|
||||
logs_bloom: vec![],
|
||||
prev_randao: Default::default(),
|
||||
block_number: 0,
|
||||
gas_limit: 0,
|
||||
gas_used: 0,
|
||||
timestamp: 0,
|
||||
extra_data: vec![],
|
||||
base_fee_per_gas: Default::default(),
|
||||
block_hash: Default::default(),
|
||||
transactions_root: Default::default(),
|
||||
withdrawals_root: Default::default(),
|
||||
blob_gas_used: 0,
|
||||
excess_blob_gas: 0,
|
||||
}),
|
||||
execution_branch: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
// Generated from smoketests:
|
||||
// cd smoketests
|
||||
// ./make-bindings
|
||||
// cargo test --test register_token_v2 -- --nocapture
|
||||
pub fn mock_event_log_v2() -> Log {
|
||||
Log {
|
||||
// gateway address
|
||||
address: hex!("b1185ede04202fe62d38f5db72f71e38ff3e8305").into(),
|
||||
topics: vec![
|
||||
hex!("550e2067494b1736ea5573f2d19cdc0ac95b410fff161bf16f11c6229655ec9c").into(),
|
||||
],
|
||||
// Nonce + Payload
|
||||
data: hex!("00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000b1185ede04202fe62d38f5db72f71e38ff3e830500000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000009184e72a0000000000000000000000000000000000000000000000000000000015d3ef798000000000000000000000000000000000000000000000000000000015d3ef798000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000b8ea8cb425d85536b158d661da1ef0895bb92f1d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").into(),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,529 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2023 Snowfork <hello@snowfork.com>
|
||||
use super::*;
|
||||
|
||||
use crate::{mock::*, Error};
|
||||
use codec::Encode;
|
||||
use frame_support::{assert_noop, assert_ok};
|
||||
use snowbridge_inbound_queue_primitives::{v2::XcmPayload, EventProof, Proof};
|
||||
use snowbridge_test_utils::{
|
||||
mock_rewards::{RegisteredRewardAmount, RegisteredRewardsCount},
|
||||
mock_xcm::{set_charge_fees_override, set_sender_override},
|
||||
};
|
||||
use sp_keyring::sr25519::Keyring;
|
||||
use sp_runtime::DispatchError;
|
||||
|
||||
#[test]
|
||||
fn test_submit_happy_path() {
|
||||
new_tester().execute_with(|| {
|
||||
let relayer: AccountId = Keyring::Bob.into();
|
||||
|
||||
let origin = RuntimeOrigin::signed(relayer.clone());
|
||||
|
||||
// Submit message
|
||||
let event = EventProof {
|
||||
event_log: mock_event_log(),
|
||||
proof: Proof {
|
||||
receipt_proof: Default::default(),
|
||||
execution_proof: mock_execution_proof(),
|
||||
},
|
||||
};
|
||||
|
||||
assert_ok!(InboundQueue::submit(origin.clone(), Box::new(event.clone())));
|
||||
|
||||
let events = frame_system::Pallet::<Test>::events();
|
||||
assert!(
|
||||
events.iter().any(|event| matches!(
|
||||
event.event,
|
||||
RuntimeEvent::InboundQueue(Event::MessageReceived { nonce, ..})
|
||||
if nonce == 1
|
||||
)),
|
||||
"no message received event emitted."
|
||||
);
|
||||
|
||||
assert_eq!(RegisteredRewardsCount::get(), 1, "Relayer reward should have been registered");
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_submit_with_invalid_gateway() {
|
||||
new_tester().execute_with(|| {
|
||||
let relayer: AccountId = Keyring::Bob.into();
|
||||
let origin = RuntimeOrigin::signed(relayer);
|
||||
|
||||
// Submit message
|
||||
let event = EventProof {
|
||||
event_log: mock_event_log_invalid_gateway(),
|
||||
proof: Proof {
|
||||
receipt_proof: Default::default(),
|
||||
execution_proof: mock_execution_proof(),
|
||||
},
|
||||
};
|
||||
assert_noop!(
|
||||
InboundQueue::submit(origin.clone(), Box::new(event.clone())),
|
||||
Error::<Test>::InvalidGateway
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_submit_verification_fails_with_invalid_proof() {
|
||||
new_tester().execute_with(|| {
|
||||
let relayer: AccountId = Keyring::Bob.into();
|
||||
|
||||
let origin = RuntimeOrigin::signed(relayer.clone());
|
||||
|
||||
// Submit message
|
||||
let mut event = EventProof {
|
||||
event_log: mock_event_log(),
|
||||
proof: Proof {
|
||||
receipt_proof: Default::default(),
|
||||
execution_proof: mock_execution_proof(),
|
||||
},
|
||||
};
|
||||
// The mock verifier will error once it matches this address.
|
||||
event.event_log.address = ERROR_ADDRESS.into();
|
||||
|
||||
assert_noop!(
|
||||
InboundQueue::submit(origin.clone(), Box::new(event.clone())),
|
||||
Error::<Test>::Verification(VerificationError::InvalidProof)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_submit_fails_with_malformed_message() {
|
||||
new_tester().execute_with(|| {
|
||||
let relayer: AccountId = Keyring::Bob.into();
|
||||
|
||||
let origin = RuntimeOrigin::signed(relayer.clone());
|
||||
|
||||
// Submit message
|
||||
let event = EventProof {
|
||||
event_log: mock_event_log_invalid_message(),
|
||||
proof: Proof {
|
||||
receipt_proof: Default::default(),
|
||||
execution_proof: mock_execution_proof(),
|
||||
},
|
||||
};
|
||||
|
||||
assert_noop!(
|
||||
InboundQueue::submit(origin.clone(), Box::new(event.clone())),
|
||||
Error::<Test>::InvalidMessage
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_using_same_nonce_fails() {
|
||||
new_tester().execute_with(|| {
|
||||
let relayer: AccountId = Keyring::Bob.into();
|
||||
|
||||
let origin = RuntimeOrigin::signed(relayer.clone());
|
||||
|
||||
// Submit message
|
||||
let event = EventProof {
|
||||
event_log: mock_event_log(),
|
||||
proof: Proof {
|
||||
receipt_proof: Default::default(),
|
||||
execution_proof: mock_execution_proof(),
|
||||
},
|
||||
};
|
||||
|
||||
assert_ok!(InboundQueue::submit(origin.clone(), Box::new(event.clone())));
|
||||
|
||||
let events = frame_system::Pallet::<Test>::events();
|
||||
assert!(
|
||||
events.iter().any(|event| matches!(
|
||||
event.event,
|
||||
RuntimeEvent::InboundQueue(Event::MessageReceived { nonce, ..})
|
||||
if nonce == 1
|
||||
)),
|
||||
"no event emitted."
|
||||
);
|
||||
|
||||
assert_noop!(
|
||||
InboundQueue::submit(origin.clone(), Box::new(event.clone())),
|
||||
Error::<Test>::InvalidNonce
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_set_operating_mode() {
|
||||
new_tester().execute_with(|| {
|
||||
let relayer: AccountId = Keyring::Bob.into();
|
||||
let origin = RuntimeOrigin::signed(relayer);
|
||||
let event = EventProof {
|
||||
event_log: mock_event_log(),
|
||||
proof: Proof {
|
||||
receipt_proof: Default::default(),
|
||||
execution_proof: mock_execution_proof(),
|
||||
},
|
||||
};
|
||||
|
||||
assert_ok!(InboundQueue::set_operating_mode(
|
||||
RuntimeOrigin::root(),
|
||||
snowbridge_core::BasicOperatingMode::Halted
|
||||
));
|
||||
|
||||
assert_noop!(InboundQueue::submit(origin, Box::new(event)), Error::<Test>::Halted);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_set_operating_mode_root_only() {
|
||||
new_tester().execute_with(|| {
|
||||
assert_noop!(
|
||||
InboundQueue::set_operating_mode(
|
||||
RuntimeOrigin::signed(Keyring::Bob.into()),
|
||||
snowbridge_core::BasicOperatingMode::Halted
|
||||
),
|
||||
DispatchError::BadOrigin
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_xcm_send_failure() {
|
||||
crate::test::new_tester().execute_with(|| {
|
||||
set_sender_override(
|
||||
|dest: &mut Option<Location>, xcm: &mut Option<Xcm<()>>| {
|
||||
if let Some(location) = dest {
|
||||
match location.unpack() {
|
||||
(_, [Teyrchain(1001)]) => return Err(SendError::NotApplicable),
|
||||
_ => Ok((xcm.clone().unwrap(), Assets::default())),
|
||||
}
|
||||
} else {
|
||||
Ok((xcm.clone().unwrap(), Assets::default()))
|
||||
}
|
||||
},
|
||||
|_| Err(SendError::DestinationUnsupported),
|
||||
);
|
||||
let relayer: AccountId = Keyring::Bob.into();
|
||||
|
||||
let origin = mock::RuntimeOrigin::signed(relayer.clone());
|
||||
|
||||
// Submit message
|
||||
let event = EventProof {
|
||||
event_log: mock_event_log(),
|
||||
proof: Proof {
|
||||
receipt_proof: Default::default(),
|
||||
execution_proof: mock_execution_proof(),
|
||||
},
|
||||
};
|
||||
|
||||
assert_noop!(
|
||||
crate::test::InboundQueue::submit(origin.clone(), Box::new(event.clone())),
|
||||
Error::<Test>::SendFailure
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_xcm_send_validate_failure() {
|
||||
crate::test::new_tester().execute_with(|| {
|
||||
set_sender_override(
|
||||
|_, _| return Err(SendError::NotApplicable),
|
||||
|xcm| {
|
||||
let hash = xcm.using_encoded(sp_io::hashing::blake2_256);
|
||||
Ok(hash)
|
||||
},
|
||||
);
|
||||
let relayer: AccountId = Keyring::Bob.into();
|
||||
|
||||
let origin = mock::RuntimeOrigin::signed(relayer.clone());
|
||||
|
||||
// Submit message
|
||||
let event = EventProof {
|
||||
event_log: mock_event_log(),
|
||||
proof: Proof {
|
||||
receipt_proof: Default::default(),
|
||||
execution_proof: mock_execution_proof(),
|
||||
},
|
||||
};
|
||||
|
||||
assert_noop!(
|
||||
crate::test::InboundQueue::submit(origin.clone(), Box::new(event.clone())),
|
||||
Error::<Test>::Unreachable
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_xcm_charge_fees_failure() {
|
||||
crate::test::new_tester().execute_with(|| {
|
||||
set_charge_fees_override(|_, _| Err(XcmError::FeesNotMet));
|
||||
|
||||
let relayer: AccountId = Keyring::Bob.into();
|
||||
|
||||
let origin = mock::RuntimeOrigin::signed(relayer.clone());
|
||||
|
||||
// Submit message
|
||||
let event = EventProof {
|
||||
event_log: mock_event_log(),
|
||||
proof: Proof {
|
||||
receipt_proof: Default::default(),
|
||||
execution_proof: mock_execution_proof(),
|
||||
},
|
||||
};
|
||||
|
||||
assert_noop!(
|
||||
crate::test::InboundQueue::submit(origin.clone(), Box::new(event.clone())),
|
||||
Error::<Test>::FeesNotMet
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_register_token() {
|
||||
new_tester().execute_with(|| {
|
||||
let relayer: AccountId = Keyring::Bob.into();
|
||||
let origin = RuntimeOrigin::signed(relayer);
|
||||
let event = EventProof {
|
||||
event_log: mock_event_log_v2(),
|
||||
proof: Proof {
|
||||
receipt_proof: Default::default(),
|
||||
execution_proof: mock_execution_proof(),
|
||||
},
|
||||
};
|
||||
|
||||
assert_ok!(InboundQueue::submit(origin, Box::new(event)));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_switch_operating_mode() {
|
||||
new_tester().execute_with(|| {
|
||||
let relayer: AccountId = Keyring::Bob.into();
|
||||
let origin = RuntimeOrigin::signed(relayer);
|
||||
let event = EventProof {
|
||||
event_log: mock_event_log(),
|
||||
proof: Proof {
|
||||
receipt_proof: Default::default(),
|
||||
execution_proof: mock_execution_proof(),
|
||||
},
|
||||
};
|
||||
|
||||
assert_ok!(InboundQueue::set_operating_mode(
|
||||
RuntimeOrigin::root(),
|
||||
snowbridge_core::BasicOperatingMode::Halted
|
||||
));
|
||||
|
||||
assert_noop!(
|
||||
InboundQueue::submit(origin.clone(), Box::new(event.clone())),
|
||||
Error::<Test>::Halted
|
||||
);
|
||||
|
||||
assert_ok!(InboundQueue::set_operating_mode(
|
||||
RuntimeOrigin::root(),
|
||||
snowbridge_core::BasicOperatingMode::Normal
|
||||
));
|
||||
|
||||
assert_ok!(InboundQueue::submit(origin, Box::new(event)));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn zero_reward_does_not_register_reward() {
|
||||
new_tester().execute_with(|| {
|
||||
let relayer: AccountId = Keyring::Bob.into();
|
||||
let origin = H160::random();
|
||||
assert_ok!(InboundQueue::process_message(
|
||||
relayer,
|
||||
Message {
|
||||
nonce: 0,
|
||||
assets: vec![],
|
||||
xcm: XcmPayload::Raw(vec![]),
|
||||
claimer: None,
|
||||
execution_fee: 1_000_000_000,
|
||||
relayer_fee: 0,
|
||||
gateway: GatewayAddress::get(),
|
||||
origin,
|
||||
value: 3_000_000_000,
|
||||
}
|
||||
));
|
||||
|
||||
assert_eq!(
|
||||
RegisteredRewardsCount::get(),
|
||||
0,
|
||||
"Zero relayer reward should not be registered"
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_tip_cumulative() {
|
||||
new_tester().execute_with(|| {
|
||||
let nonce: u64 = 10;
|
||||
let amount1: u128 = 500;
|
||||
let amount2: u128 = 300;
|
||||
|
||||
assert_eq!(Tips::<Test>::get(nonce), None);
|
||||
assert_ok!(InboundQueue::add_tip(nonce, amount1));
|
||||
assert_eq!(Tips::<Test>::get(nonce), Some(amount1));
|
||||
assert_ok!(InboundQueue::add_tip(nonce, amount2));
|
||||
assert_eq!(Tips::<Test>::get(nonce), Some(amount1 + amount2));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_tip_nonce_consumed() {
|
||||
new_tester().execute_with(|| {
|
||||
let nonce: u64 = 20;
|
||||
let amount: u128 = 400;
|
||||
Nonce::<Test>::set(nonce.into());
|
||||
|
||||
assert_noop!(InboundQueue::add_tip(nonce, amount), AddTipError::NonceConsumed);
|
||||
assert_eq!(Tips::<Test>::get(nonce), None);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_tip_amount_zero() {
|
||||
new_tester().execute_with(|| {
|
||||
let nonce: u64 = 30;
|
||||
let amount: u128 = 0;
|
||||
|
||||
assert_noop!(InboundQueue::add_tip(nonce, amount), AddTipError::AmountZero);
|
||||
assert_eq!(Tips::<Test>::get(nonce), None);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inbound_tip_is_paid_out_to_relayer() {
|
||||
new_tester().execute_with(|| {
|
||||
let nonce: u64 = 77;
|
||||
let tip: u128 = 12_345;
|
||||
let relayer_fee: u128 = 2_000;
|
||||
|
||||
// Add tip for nonce before message is processed
|
||||
assert_ok!(InboundQueue::add_tip(nonce, tip));
|
||||
assert_eq!(Tips::<Test>::get(nonce), Some(tip));
|
||||
|
||||
// Process inbound message with relayer_fee
|
||||
let relayer: AccountId = Keyring::Bob.into();
|
||||
assert_ok!(InboundQueue::process_message(
|
||||
relayer,
|
||||
Message {
|
||||
nonce,
|
||||
assets: vec![],
|
||||
xcm: XcmPayload::Raw(vec![]),
|
||||
claimer: None,
|
||||
execution_fee: 1_000_000_000,
|
||||
relayer_fee,
|
||||
gateway: mock::GatewayAddress::get(),
|
||||
origin: H160::random(),
|
||||
value: 3_000_000_000,
|
||||
},
|
||||
));
|
||||
|
||||
// Reward should be registered from relayer_fee + tip
|
||||
assert_eq!(
|
||||
RegisteredRewardsCount::get(),
|
||||
1,
|
||||
"Reward should be registered from relayer_fee + tip"
|
||||
);
|
||||
|
||||
// Check the actual reward amount paid out (should be relayer_fee + tip)
|
||||
assert_eq!(
|
||||
RegisteredRewardAmount::get(),
|
||||
relayer_fee + tip,
|
||||
"Reward amount should equal relayer_fee + tip"
|
||||
);
|
||||
|
||||
// Tip should be consumed from storage
|
||||
assert_eq!(Tips::<Test>::get(nonce), None);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn relayer_fee_paid_out_when_no_tip_exists() {
|
||||
new_tester().execute_with(|| {
|
||||
let nonce: u64 = 88;
|
||||
let relayer_fee: u128 = 5_000;
|
||||
|
||||
// Ensure no tip exists for this nonce
|
||||
assert_eq!(Tips::<Test>::get(nonce), None);
|
||||
|
||||
// Process inbound message with relayer_fee but no tip
|
||||
let relayer: AccountId = Keyring::Bob.into();
|
||||
assert_ok!(InboundQueue::process_message(
|
||||
relayer,
|
||||
Message {
|
||||
nonce,
|
||||
assets: vec![],
|
||||
xcm: XcmPayload::Raw(vec![]),
|
||||
claimer: None,
|
||||
execution_fee: 1_000_000_000,
|
||||
relayer_fee,
|
||||
gateway: mock::GatewayAddress::get(),
|
||||
origin: H160::random(),
|
||||
value: 3_000_000_000,
|
||||
},
|
||||
));
|
||||
|
||||
// Relayer fee should be paid out even without tip
|
||||
assert_eq!(
|
||||
RegisteredRewardsCount::get(),
|
||||
1,
|
||||
"Relayer fee should be paid out even when no tip exists"
|
||||
);
|
||||
|
||||
// Check the actual reward amount paid out
|
||||
assert_eq!(
|
||||
RegisteredRewardAmount::get(),
|
||||
relayer_fee,
|
||||
"Reward amount should equal relayer_fee when no tip exists"
|
||||
);
|
||||
|
||||
// Confirm no tip storage was affected
|
||||
assert_eq!(Tips::<Test>::get(nonce), None);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tip_paid_out_when_no_relayer_fee() {
|
||||
new_tester().execute_with(|| {
|
||||
let nonce: u64 = 99;
|
||||
let tip: u128 = 8_500;
|
||||
|
||||
// Add tip for nonce before message is processed
|
||||
assert_ok!(InboundQueue::add_tip(nonce, tip));
|
||||
assert_eq!(Tips::<Test>::get(nonce), Some(tip));
|
||||
|
||||
// Process inbound message with zero relayer_fee but with tip
|
||||
let relayer: AccountId = Keyring::Bob.into();
|
||||
assert_ok!(InboundQueue::process_message(
|
||||
relayer,
|
||||
Message {
|
||||
nonce,
|
||||
assets: vec![],
|
||||
xcm: XcmPayload::Raw(vec![]),
|
||||
claimer: None,
|
||||
execution_fee: 1_000_000_000,
|
||||
relayer_fee: 0,
|
||||
gateway: mock::GatewayAddress::get(),
|
||||
origin: H160::random(),
|
||||
value: 3_000_000_000,
|
||||
},
|
||||
));
|
||||
|
||||
// Tip should be paid out even without relayer fee
|
||||
assert_eq!(
|
||||
RegisteredRewardsCount::get(),
|
||||
1,
|
||||
"Tip should be paid out even when relayer_fee is 0"
|
||||
);
|
||||
|
||||
// Check the actual reward amount paid out (should be just the tip)
|
||||
assert_eq!(
|
||||
RegisteredRewardAmount::get(),
|
||||
tip,
|
||||
"Reward amount should equal tip when relayer_fee is 0"
|
||||
);
|
||||
|
||||
// Tip should be consumed from storage
|
||||
assert_eq!(Tips::<Test>::get(nonce), None);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2023 Snowfork <hello@snowfork.com>
|
||||
//! Autogenerated weights for `snowbridge_inbound_queue`
|
||||
//!
|
||||
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
|
||||
//! DATE: 2023-07-14, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
|
||||
//! WORST CASE MAP SIZE: `1000000`
|
||||
//! HOSTNAME: `macbook pro 14 m2`, CPU: `m2-arm64`
|
||||
//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-pezkuwichain-dev"), DB CACHE: 1024
|
||||
|
||||
#![cfg_attr(rustfmt, rustfmt_skip)]
|
||||
#![allow(unused_parens)]
|
||||
#![allow(unused_imports)]
|
||||
|
||||
use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};
|
||||
use sp_std::marker::PhantomData;
|
||||
|
||||
/// Weight functions needed for ethereum_beacon_client.
|
||||
pub trait WeightInfo {
|
||||
fn submit() -> Weight;
|
||||
}
|
||||
|
||||
// For backwards compatibility and tests
|
||||
impl WeightInfo for () {
|
||||
fn submit() -> Weight {
|
||||
// Proof Size summary in bytes:
|
||||
// Measured: `309`
|
||||
// Estimated: `3774`
|
||||
// Minimum execution time: 59_000_000 picoseconds.
|
||||
Weight::from_parts(60_000_000, 0)
|
||||
.saturating_add(Weight::from_parts(0, 3774))
|
||||
.saturating_add(RocksDbWeight::get().reads(7))
|
||||
.saturating_add(RocksDbWeight::get().writes(2))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user