Skip to content
This repository has been archived by the owner on Oct 22, 2024. It is now read-only.

Transact from sub to eth #116

Open
wants to merge 19 commits into
base: bridge-next-gen
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 28 additions & 1 deletion bridges/snowbridge/primitives/core/src/outbound.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
use frame_support::PalletError;
use scale_info::TypeInfo;
use sp_arithmetic::traits::{BaseArithmetic, Unsigned};
use sp_core::{RuntimeDebug, H256};
use sp_core::{RuntimeDebug, H160, H256};
use sp_std::vec::Vec;
pub use v1::{AgentExecuteCommand, Command, Initializer, Message, OperatingMode, QueuedMessage};

/// Enqueued outbound messages need to be versioned to prevent data corruption
Expand Down Expand Up @@ -235,12 +236,22 @@
/// The amount of tokens to transfer
amount: u128,
},
/// Execute a contract on the target chain
Transact {
/// Target contract address
target: H160,
/// The call data to the contract
payload: Vec<u8>,
/// The dynamic gas component that needs to be specified when executing the contract
gas_limit: u64,
},
}

impl AgentExecuteCommand {
fn index(&self) -> u8 {
match self {
AgentExecuteCommand::TransferToken { .. } => 0,
AgentExecuteCommand::Transact { .. } => 1,

Check warning on line 254 in bridges/snowbridge/primitives/core/src/outbound.rs

View check run for this annotation

Codecov / codecov/patch

bridges/snowbridge/primitives/core/src/outbound.rs#L254

Added line #L254 was not covered by tests
}
}

Expand All @@ -256,6 +267,14 @@
Token::Uint(U256::from(*amount)),
])),
]),
AgentExecuteCommand::Transact { target, payload, gas_limit } => ethabi::encode(&[
Token::Uint(self.index().into()),
Token::Bytes(ethabi::encode(&[
Token::Address(*target),
Token::Bytes(payload.clone()),
Token::Uint(U256::from(*gas_limit)),

Check warning on line 275 in bridges/snowbridge/primitives/core/src/outbound.rs

View check run for this annotation

Codecov / codecov/patch

bridges/snowbridge/primitives/core/src/outbound.rs#L270-L275

Added lines #L270 - L275 were not covered by tests
])),
]),
}
}
}
Expand Down Expand Up @@ -386,6 +405,7 @@
// * Assume dest account in ERC20 contract does not yet have a storage slot
// * ERC20.transferFrom possibly does other business logic besides updating balances
AgentExecuteCommand::TransferToken { .. } => 100_000,
AgentExecuteCommand::Transact { gas_limit, .. } => *gas_limit,

Check warning on line 408 in bridges/snowbridge/primitives/core/src/outbound.rs

View check run for this annotation

Codecov / codecov/patch

bridges/snowbridge/primitives/core/src/outbound.rs#L408

Added line #L408 was not covered by tests
},
Command::Upgrade { initializer, .. } => {
let initializer_max_gas = match *initializer {
Expand All @@ -411,3 +431,10 @@
}

pub const ETHER_DECIMALS: u8 = 18;

#[derive(Clone, Encode, Decode, PartialEq, RuntimeDebug, TypeInfo)]
pub struct TransactInfo {
pub target: H160,
pub call: Vec<u8>,
pub gas_limit: u64,
}
44 changes: 40 additions & 4 deletions bridges/snowbridge/primitives/router/src/outbound/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@

use core::slice::Iter;

use codec::{Decode, Encode};
use codec::{Decode, DecodeAll, Encode};

use frame_support::{ensure, traits::Get};
use frame_system::unique;
use snowbridge_core::{
outbound::{AgentExecuteCommand, Command, Message, SendMessage},
outbound::{AgentExecuteCommand, Command, Message, SendMessage, TransactInfo},
ChannelId, ParaId,
};
use sp_core::{H160, H256};
Expand Down Expand Up @@ -154,6 +155,10 @@
AssetResolutionFailed,
InvalidFeeAsset,
SetTopicExpected,
TransactExpected,
TransactSovereignAccountExpected,
TransactDecodeFailed,
UnexpectedInstruction,
}

macro_rules! match_expression {
Expand All @@ -175,8 +180,12 @@
}

fn convert(&mut self) -> Result<(AgentExecuteCommand, [u8; 32]), XcmConverterError> {
// Get withdraw/deposit and make native tokens create message.
let result = self.native_tokens_unlock_message()?;
let result = match self.peek() {
Ok(Transact { .. }) => self.transact_message()?,

Check warning on line 184 in bridges/snowbridge/primitives/router/src/outbound/mod.rs

View check run for this annotation

Codecov / codecov/patch

bridges/snowbridge/primitives/router/src/outbound/mod.rs#L184

Added line #L184 was not covered by tests
// Get withdraw/deposit and make native tokens create message.
Ok(WithdrawAsset { .. }) => self.native_tokens_unlock_message()?,
_ => return Err(XcmConverterError::UnexpectedInstruction),
alistair-singh marked this conversation as resolved.
Show resolved Hide resolved
};

// All xcm instructions must be consumed before exit.
if self.next().is_ok() {
Expand All @@ -186,6 +195,33 @@
Ok(result)
}

fn transact_message(&mut self) -> Result<(AgentExecuteCommand, [u8; 32]), XcmConverterError> {
let call_data = if let Transact { origin_kind, call, .. } = self.next()? {
ensure!(
*origin_kind == OriginKind::SovereignAccount,
XcmConverterError::TransactSovereignAccountExpected

Check warning on line 202 in bridges/snowbridge/primitives/router/src/outbound/mod.rs

View check run for this annotation

Codecov / codecov/patch

bridges/snowbridge/primitives/router/src/outbound/mod.rs#L198-L202

Added lines #L198 - L202 were not covered by tests
);
call

Check warning on line 204 in bridges/snowbridge/primitives/router/src/outbound/mod.rs

View check run for this annotation

Codecov / codecov/patch

bridges/snowbridge/primitives/router/src/outbound/mod.rs#L204

Added line #L204 was not covered by tests
} else {
return Err(XcmConverterError::TransactExpected)

Check warning on line 206 in bridges/snowbridge/primitives/router/src/outbound/mod.rs

View check run for this annotation

Codecov / codecov/patch

bridges/snowbridge/primitives/router/src/outbound/mod.rs#L206

Added line #L206 was not covered by tests
};

let message = TransactInfo::decode_all(&mut call_data.clone().into_encoded().as_slice())
.map_err(|_| XcmConverterError::TransactDecodeFailed)?;

Check warning on line 210 in bridges/snowbridge/primitives/router/src/outbound/mod.rs

View check run for this annotation

Codecov / codecov/patch

bridges/snowbridge/primitives/router/src/outbound/mod.rs#L209-L210

Added lines #L209 - L210 were not covered by tests

// Check if there is a SetTopic and skip over it if found.
let message_hash = unique(&message);
let topic_id = match_expression!(self.next()?, SetTopic(id), id).unwrap_or(&message_hash);
Ok((
AgentExecuteCommand::Transact {
target: message.target,
payload: message.call,
gas_limit: message.gas_limit,

Check warning on line 219 in bridges/snowbridge/primitives/router/src/outbound/mod.rs

View check run for this annotation

Codecov / codecov/patch

bridges/snowbridge/primitives/router/src/outbound/mod.rs#L213-L219

Added lines #L213 - L219 were not covered by tests
},
*topic_id,

Check warning on line 221 in bridges/snowbridge/primitives/router/src/outbound/mod.rs

View check run for this annotation

Codecov / codecov/patch

bridges/snowbridge/primitives/router/src/outbound/mod.rs#L221

Added line #L221 was not covered by tests
))
}

fn native_tokens_unlock_message(
&mut self,
) -> Result<(AgentExecuteCommand, [u8; 32]), XcmConverterError> {
Expand Down
8 changes: 4 additions & 4 deletions bridges/snowbridge/primitives/router/src/outbound/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -631,15 +631,15 @@ fn xcm_converter_with_fees_greater_than_reserve_fails() {
}

#[test]
fn xcm_converter_convert_with_empty_xcm_yields_unexpected_end_of_xcm() {
fn xcm_converter_convert_with_empty_xcm_yields_unexpected_instruction() {
let network = BridgedNetwork::get();

let message: Xcm<()> = vec![].into();

let mut converter = XcmConverter::new(&message, &network);

let result = converter.convert();
assert_eq!(result.err(), Some(XcmConverterError::UnexpectedEndOfXcm));
assert_eq!(result.err(), Some(XcmConverterError::UnexpectedInstruction));
yrong marked this conversation as resolved.
Show resolved Hide resolved
}

#[test]
Expand Down Expand Up @@ -675,7 +675,7 @@ fn xcm_converter_convert_with_extra_instructions_yields_end_of_xcm_message_expec
}

#[test]
fn xcm_converter_convert_without_withdraw_asset_yields_withdraw_expected() {
fn xcm_converter_convert_without_withdraw_asset_yields_unexpected_instruction() {
let network = BridgedNetwork::get();

let token_address: [u8; 20] = hex!("1000000000000000000000000000000000000000");
Expand All @@ -701,7 +701,7 @@ fn xcm_converter_convert_without_withdraw_asset_yields_withdraw_expected() {
let mut converter = XcmConverter::new(&message, &network);

let result = converter.convert();
assert_eq!(result.err(), Some(XcmConverterError::WithdrawAssetExpected));
assert_eq!(result.err(), Some(XcmConverterError::UnexpectedInstruction));
}

#[test]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ decl_test_parachains! {
Assets: penpal_runtime::Assets,
ForeignAssets: penpal_runtime::ForeignAssets,
Balances: penpal_runtime::Balances,
TransactHelper: penpal_runtime::TransactHelper,
}
},
pub struct PenpalB {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ emulated-integration-tests-common = { path = "../../../common", default-features
rococo-westend-system-emulated-network = { path = "../../../networks/rococo-westend-system" }
rococo-system-emulated-network = { path = "../../../networks/rococo-system" }
asset-hub-rococo-runtime = { path = "../../../../../runtimes/assets/asset-hub-rococo", default-features = false }
penpal-runtime = { path = "../../../../../../parachains/runtimes/testing/penpal", default-features = false }

# Snowbridge
snowbridge-core = { path = "../../../../../../../bridges/snowbridge/primitives/core", default-features = false }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -535,3 +535,111 @@ fn send_token_from_ethereum_to_asset_hub_fail_for_insufficient_fund() {
assert_err!(send_inbound_message(make_register_token_message()), Arithmetic(Underflow));
});
}

#[test]
fn transact_from_penpal_to_ethereum() {
BridgeHubRococo::fund_para_sovereign(PenpalA::para_id().into(), INITIAL_FUND);

let sudo_origin = <Rococo as Chain>::RuntimeOrigin::root();
let destination: VersionedLocation =
Rococo::child_location_of(BridgeHubRococo::para_id()).into();

// Construct XCM to create an agent for penpal
let create_agent_xcm = VersionedXcm::from(Xcm(vec![
UnpaidExecution { weight_limit: Unlimited, check_origin: None },
DescendOrigin(Parachain(PenpalA::para_id().into()).into()),
Transact {
require_weight_at_most: 3000000000.into(),
origin_kind: OriginKind::Xcm,
call: SnowbridgeControl::Control(ControlCall::CreateAgent {}).encode().into(),
},
]));

// Construct XCM to create a channel for penpal
let create_channel_xcm = VersionedXcm::from(Xcm(vec![
UnpaidExecution { weight_limit: Unlimited, check_origin: None },
DescendOrigin(Parachain(PenpalA::para_id().into()).into()),
Transact {
require_weight_at_most: 3000000000.into(),
origin_kind: OriginKind::Xcm,
call: SnowbridgeControl::Control(ControlCall::CreateChannel {
mode: OperatingMode::Normal,
})
.encode()
.into(),
},
]));

// Send XCM message from Relay Chain to create agent/channel for penpal on BH
Rococo::execute_with(|| {
assert_ok!(<Rococo as RococoPallet>::XcmPallet::send(
sudo_origin.clone(),
bx!(destination.clone()),
bx!(create_agent_xcm),
));

assert_ok!(<Rococo as RococoPallet>::XcmPallet::send(
sudo_origin,
bx!(destination),
bx!(create_channel_xcm),
));

type RuntimeEvent = <Rococo as Chain>::RuntimeEvent;

assert_expected_events!(
Rococo,
vec![
RuntimeEvent::XcmPallet(pallet_xcm::Event::Sent { .. }) => {},
]
);
});

BridgeHubRococo::execute_with(|| {
type RuntimeEvent = <BridgeHubRococo as Chain>::RuntimeEvent;

// Check that the Channel was created
assert_expected_events!(
BridgeHubRococo,
vec![
RuntimeEvent::EthereumSystem(snowbridge_pallet_system::Event::CreateChannel {
..
}) => {},
]
);
});

PenpalA::execute_with(|| {
type RuntimeEvent = <PenpalA as Chain>::RuntimeEvent;
type RuntimeOrigin = <PenpalA as Chain>::RuntimeOrigin;
let sender = PenpalASender::get();
assert_ok!(<PenpalA as PenpalAPallet>::TransactHelper::transact_to_ethereum(
RuntimeOrigin::signed(sender),
//contract
hex!("ee9170abfbf9421ad6dd07f6bdec9d89f2b581e0").into(),
//call
hex!("00071468656c6c6f").to_vec(),
//fee
4_000_000_000,
//gas
80_000,
));

assert_expected_events!(
PenpalA,
vec![
RuntimeEvent::TransactHelper(penpal_runtime::pallets::transact_helper::Event::SentExportMessage { .. }) => {},
]
);
});

BridgeHubRococo::execute_with(|| {
type RuntimeEvent = <BridgeHubRococo as Chain>::RuntimeEvent;

assert_expected_events!(
BridgeHubRococo,
vec![
RuntimeEvent::EthereumOutboundQueue(snowbridge_pallet_outbound_queue::Event::MessageQueued { .. }) => {},
]
);
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,9 @@ impl Contains<RuntimeCall> for SafeCallFilter {
snowbridge_pallet_system::Call::set_pricing_parameters { .. } |
snowbridge_pallet_system::Call::force_update_channel { .. } |
snowbridge_pallet_system::Call::force_transfer_native_from_agent { .. } |
snowbridge_pallet_system::Call::set_token_transfer_fees { .. },
snowbridge_pallet_system::Call::set_token_transfer_fees { .. } |
snowbridge_pallet_system::Call::create_agent { .. } |
snowbridge_pallet_system::Call::create_channel { .. },
)
)
}
Expand Down
3 changes: 3 additions & 0 deletions cumulus/parachains/runtimes/testing/penpal/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#[cfg(feature = "std")]
include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));

pub mod pallets;
mod weights;
pub mod xcm_config;

Expand Down Expand Up @@ -660,6 +661,8 @@ construct_runtime!(
Assets: pallet_assets::<Instance1> = 50,
ForeignAssets: pallet_assets::<Instance2> = 51,

TransactHelper: crate::pallets::transact_helper = 52,

Sudo: pallet_sudo = 255,
}
);
Expand Down
15 changes: 15 additions & 0 deletions cumulus/parachains/runtimes/testing/penpal/src/pallets/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
pub mod transact_helper;
Loading
Loading