Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Transact from eth to sub #1141

Open
wants to merge 45 commits into
base: bridge-next-gen
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
55cc688
Change for contracts
yrong Feb 7, 2024
07395ce
Convert logic for transact command
yrong Feb 8, 2024
d41bd08
Reuse msg.sender for both originKinds
yrong Feb 13, 2024
16e7cbe
Revert to burn&teleport
yrong Feb 13, 2024
a01aaab
Update contract bindings
yrong Feb 13, 2024
b660ea8
Update sdk
yrong Feb 13, 2024
89feaf3
Merge branch 'main' into ron/transact-from-eth-to-sub
yrong Feb 14, 2024
a94183a
Remove unused
yrong Feb 14, 2024
7a110d3
Add smoke test
yrong Feb 15, 2024
b57f0c1
Improve smoke test
yrong Feb 15, 2024
3ad5b7d
Add polkadot-sdk as soft link
yrong Feb 15, 2024
e93c725
Revert typos config
yrong Feb 15, 2024
65714ef
Add valid check
yrong Feb 16, 2024
4719a42
Remove TransactMessage and inline the params
yrong Feb 19, 2024
a78b1f9
Remove from .gitignore
yrong Feb 19, 2024
c192361
Add quoteTransactFee
yrong Feb 19, 2024
acf2362
Rename to destinationFee
yrong Feb 20, 2024
207460d
Test quoteTransactFee
yrong Feb 20, 2024
f8f0f57
Check call length
yrong Feb 22, 2024
00abc13
Initialize ticket with explicit cost
yrong Feb 22, 2024
5084432
Rename to sendCall
yrong Feb 22, 2024
d7cc7d8
Reuse weight type with compact u64 encode support
yrong Feb 23, 2024
2f3560c
Fix smoke test with sovereign account of the sender
yrong Feb 23, 2024
e99ce53
Fix generate inbound fixture
yrong Feb 26, 2024
2e4140c
Merge branch 'main' into ron/transact-from-eth-to-sub
yrong Feb 29, 2024
dd3378e
Merge branch 'main' into ron/transact-from-eth-to-sub
yrong Mar 6, 2024
3d91da4
Support TransactFeeMode
yrong Mar 6, 2024
ac7e1f5
Merge branch 'main' into ron/transact-from-eth-to-sub
yrong Mar 7, 2024
72f6d3e
Merge branch 'main' into ron/transact-from-eth-to-sub
yrong Mar 19, 2024
68a567f
Remove prefunding mode
yrong Mar 19, 2024
923187e
Merge branch 'main' into ron/transact-from-eth-to-sub
yrong Apr 11, 2024
3dd1adc
Add param checks back
yrong Apr 11, 2024
f07507f
Remove script unused
yrong Apr 11, 2024
70413d8
Remove the fund script not in use
yrong Apr 11, 2024
a364298
Update relayer
yrong Apr 11, 2024
bdf4c71
Merge branch 'bridge-next-gen' into ron/transact-from-eth-to-sub
yrong Apr 16, 2024
748eea4
Add rfc doc
yrong Apr 17, 2024
e27c0eb
Ignore destination fee for the transact
yrong Apr 17, 2024
e5c1a70
Update rfc
yrong Apr 22, 2024
53791d2
Update rfc with fee section
yrong Apr 22, 2024
61d5ea5
Cleanup
yrong Apr 24, 2024
dfa331d
Merge branch 'bridge-next-gen' into ron/transact-from-eth-to-sub
yrong Apr 24, 2024
b935ca8
Update rfc doc
yrong Apr 29, 2024
2847751
Update rfc
yrong May 5, 2024
bfdff26
Remove unused destinationFee
yrong May 5, 2024
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
33 changes: 32 additions & 1 deletion contracts/src/Gateway.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ import {
Command,
MultiAddress,
Ticket,
Costs
Costs,
OriginKind,
Weight
} from "./Types.sol";
import {IGateway} from "./interfaces/IGateway.sol";
import {IInitializable} from "./interfaces/IInitializable.sol";
Expand All @@ -27,6 +29,7 @@ import {SafeNativeTransfer} from "./utils/SafeTransfer.sol";
import {Call} from "./utils/Call.sol";
import {Math} from "./utils/Math.sol";
import {ScaleCodec} from "./utils/ScaleCodec.sol";
import {SubstrateTypes} from "./SubstrateTypes.sol";

import {
UpgradeParams,
Expand Down Expand Up @@ -86,6 +89,7 @@ contract Gateway is IGateway, IInitializable {
error InvalidAgentExecutionPayload();
error InvalidCodeHash();
error InvalidConstructorParams();
error InvalidTransact();

// handler functions are privileged
modifier onlySelf() {
Expand Down Expand Up @@ -617,4 +621,31 @@ contract Gateway is IGateway, IInitializable {
assets.assetHubCreateAssetFee = config.assetHubCreateAssetFee;
assets.assetHubReserveTransferFee = config.assetHubReserveTransferFee;
}

/// @inheritdoc IGateway
function transact(
claravanstaden marked this conversation as resolved.
Show resolved Hide resolved
ParaID destinationChain,
OriginKind originKind,
uint128 destinationFee,
Weight calldata weightAtMost,
bytes calldata call
alistair-singh marked this conversation as resolved.
Show resolved Hide resolved
) external payable {
if (call.length < 2 || destinationFee == 0 || weightAtMost.refTime == 0 || weightAtMost.proofSize == 0) {
revert InvalidTransact();
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel a bit uncomfortable having the source chain interpreting and validating these fields - I think that's the job of the destination chain. Or in other words, I think we can pass these values through safely without validation.

Copy link
Contributor Author

@yrong yrong Feb 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

00abc13

@vgeddes Do you think better to add some native costs specific for the transact to avoid spamming? Similar concern from Alistair in #1141 (comment)

Ticket memory ticket;
Costs memory costs;
bytes memory payload =
SubstrateTypes.Transact(msg.sender, originKind.encode(), destinationFee, weightAtMost, call);
ticket.dest = destinationChain;
ticket.costs = costs;
ticket.payload = payload;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would rather initialise the ticket like this to make clear that we aren't mistakenly leaving costs uninitialized

Suggested change
ticket.dest = destinationChain;
ticket.costs = costs;
ticket.payload = payload;
Ticket memory ticket = Ticket({
dest: destinationChain,
costs: Costs({
native: 0,
foreign: 0,
}),
payload: payload
});

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_submitOutbound(ticket);
}

/// @inheritdoc IGateway
function quoteTransactFee() external view returns (uint256) {
PricingStorage.Layout storage pricing = PricingStorage.layout();
return _convertToNative(pricing.exchangeRate, pricing.deliveryCost);
}
}
22 changes: 21 additions & 1 deletion contracts/src/SubstrateTypes.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
pragma solidity 0.8.23;

import {ScaleCodec} from "./utils/ScaleCodec.sol";
import {ParaID} from "./Types.sol";
import {ParaID, Weight, OriginKind} from "./Types.sol";

/**
* @title SCALE encoders for common Substrate types
Expand Down Expand Up @@ -133,4 +133,24 @@ library SubstrateTypes {
ScaleCodec.encodeU128(xcmFee)
);
}

// Arbitrary transact
function Transact(address sender, bytes1 originKind, uint128 fee, Weight memory weight, bytes memory call)
internal
view
returns (bytes memory)
{
return bytes.concat(
bytes1(0x00),
ScaleCodec.encodeU64(uint64(block.chainid)),
bytes1(0x02),
SubstrateTypes.H160(sender),
originKind,
ScaleCodec.encodeU128(fee),
ScaleCodec.encodeU64(weight.refTime),
ScaleCodec.encodeU64(weight.proofSize),
ScaleCodec.checkedEncodeCompactU32(call.length),
call
);
}
}
22 changes: 22 additions & 0 deletions contracts/src/Types.sol
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,25 @@ struct TokenInfo {
bool isRegistered;
bytes31 __padding;
}

struct Weight {
uint64 refTime;
uint64 proofSize;
}

/// @dev OriginKind from https://github.com/Snowfork/polkadot-sdk/blob/348a1a010481002e41594ed75e5d78b7c2dbed92/polkadot/xcm/src/v2/mod.rs#L86
enum OriginKind {
SovereignAccount,
Xcm
}

using {encode} for OriginKind global;

function encode(OriginKind kind) pure returns (bytes1 result) {
if (kind == OriginKind.SovereignAccount) {
result = 0x01;
} else if (kind == OriginKind.Xcm) {
result = 0x03;
}
return result;
}
14 changes: 13 additions & 1 deletion contracts/src/interfaces/IGateway.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// SPDX-FileCopyrightText: 2023 Snowfork <[email protected]>
pragma solidity 0.8.23;

import {OperatingMode, InboundMessage, ParaID, ChannelID, MultiAddress} from "../Types.sol";
import {OperatingMode, InboundMessage, ParaID, ChannelID, MultiAddress, OriginKind, Weight} from "../Types.sol";
import {Verification} from "../Verification.sol";
import {UD60x18} from "prb/math/src/UD60x18.sol";

Expand Down Expand Up @@ -105,4 +105,16 @@ interface IGateway {
uint128 destinationFee,
uint128 amount
) external payable;

vgeddes marked this conversation as resolved.
Show resolved Hide resolved
/// @dev Call transact in destinationChain
function transact(
ParaID destinationChain,
OriginKind originKind,
uint128 destinationFee,
Weight calldata weightAtMost,
bytes calldata call
) external payable;

/// @dev Quote a fee in Ether for transact
function quoteTransactFee() external view returns (uint256);
}
59 changes: 54 additions & 5 deletions contracts/test/Gateway.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -42,22 +42,27 @@ import {
ParaID,
Command,
multiAddressFromBytes32,
multiAddressFromBytes20
multiAddressFromBytes20,
Weight,
OriginKind
} from "../src/Types.sol";

import {WETH9} from "canonical-weth/WETH9.sol";
import "./mocks/GatewayUpgradeMock.sol";
import {UD60x18, ud60x18, convert} from "prb/math/src/UD60x18.sol";

contract GatewayTest is Test {
ParaID public bridgeHubParaID = ParaID.wrap(1001);
bytes32 public bridgeHubAgentID = keccak256("1001");
ParaID public bridgeHubParaID = ParaID.wrap(1013);
bytes32 public bridgeHubAgentID = keccak256("1013");
address public bridgeHubAgent;

ParaID public assetHubParaID = ParaID.wrap(1002);
bytes32 public assetHubAgentID = keccak256("1002");
ParaID public assetHubParaID = ParaID.wrap(1000);
bytes32 public assetHubAgentID = keccak256("1000");
address public assetHubAgent;

ParaID public penpalParaID = ParaID.wrap(2000);
bytes32 public penpalAgentID = keccak256("2000");

address public relayer;

bytes32[] public proof = [bytes32(0x2f9ee6cfdf244060dc28aa46347c5219e303fc95062dd672b4e406ca5c29764b)];
Expand Down Expand Up @@ -114,6 +119,14 @@ contract GatewayTest is Test {
bridgeHubAgent = IGateway(address(gateway)).agentOf(bridgeHubAgentID);
assetHubAgent = IGateway(address(gateway)).agentOf(assetHubAgentID);

// Initialize agent/channel for Penpal
GatewayMock(address(gateway)).createAgentPublic(abi.encode(CreateAgentParams({agentID: penpalAgentID})));
GatewayMock(address(gateway)).createChannelPublic(
abi.encode(
CreateChannelParams({channelID: penpalParaID.into(), agentID: penpalAgentID, mode: OperatingMode.Normal})
)
);

// fund the message relayer account
relayer = makeAddr("relayer");

Expand Down Expand Up @@ -825,4 +838,40 @@ contract GatewayTest is Test {
fee = IGateway(address(gateway)).quoteRegisterTokenFee();
assertEq(fee, 10000000000000000);
}

/**
* Transact
*/
function testTransactFromXcmOrigin() public {
bytes memory payload = SubstrateTypes.Transact(account1, 0x03, 1, Weight(1, 1), bytes("0x1"));
console.logBytes(payload);
vm.expectEmit(true, false, false, true);
emit IGateway.OutboundMessageAccepted(penpalParaID.into(), 1, messageID, payload);
hoax(address(account1));
IGateway(address(gateway)).transact{value: 1 ether}(penpalParaID, OriginKind.Xcm, 1, Weight(1, 1), bytes("0x1"));
}

function testTransactFromSovereignAccount() public {
bytes memory payload = SubstrateTypes.Transact(account1, 0x01, 1, Weight(1, 1), bytes("0x1"));
console.logBytes(payload);
vm.expectEmit(true, false, false, true);
emit IGateway.OutboundMessageAccepted(penpalParaID.into(), 1, messageID, payload);
hoax(address(account1));
IGateway(address(gateway)).transact{value: 1 ether}(
penpalParaID, OriginKind.SovereignAccount, 1, Weight(1, 1), bytes("0x1")
);
}

function testTransactFromSovereignAccountWithFee() public {
bytes memory payload = SubstrateTypes.Transact(account1, 0x01, 1, Weight(1, 1), bytes("0x1"));
console.logBytes(payload);
uint256 fee = IGateway(address(gateway)).quoteTransactFee();
assertEq(fee, 2500000000000000);
vm.expectEmit(true, false, false, true);
emit IGateway.OutboundMessageAccepted(penpalParaID.into(), 1, messageID, payload);
hoax(address(account1));
IGateway(address(gateway)).transact{value: fee}(
penpalParaID, OriginKind.SovereignAccount, 1, Weight(1, 1), bytes("0x1")
);
}
}
24 changes: 23 additions & 1 deletion contracts/test/mocks/GatewayUpgradeMock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,17 @@
// SPDX-FileCopyrightText: 2023 Snowfork <[email protected]>
pragma solidity 0.8.23;

import {Channel, InboundMessage, OperatingMode, ParaID, Command, ChannelID, MultiAddress} from "../../src/Types.sol";
import {
Channel,
InboundMessage,
OperatingMode,
ParaID,
Command,
ChannelID,
MultiAddress,
OriginKind,
Weight
} from "../../src/Types.sol";
import {IGateway} from "../../src/interfaces/IGateway.sol";
import {IInitializable} from "../../src/interfaces/IInitializable.sol";
import {Verification} from "../../src/Verification.sol";
Expand Down Expand Up @@ -61,4 +71,16 @@ contract GatewayUpgradeMock is IGateway, IInitializable {
function pricingParameters() external pure returns (UD60x18, uint128) {
return (convert(0), uint128(0));
}

function transact(
ParaID destinationChain,
OriginKind originKind,
uint128 destinationFee,
Weight calldata weightAtMost,
bytes calldata call
) external payable {}

function quoteTransactFee() external pure returns (uint256) {
return 1;
}
}
37 changes: 36 additions & 1 deletion relayer/contracts/gateway.go

Large diffs are not rendered by default.

74 changes: 74 additions & 0 deletions smoketest/tests/transact_from_eth_to_penpal.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
use ethers::{core::types::Address, prelude::U256, types::Bytes};
use futures::StreamExt;
use hex_literal::hex;
use snowbridge_smoketest::{
constants::*,
contracts::{gateway_upgrade_mock::TransactMessage, i_gateway, shared_types::Weight},
helper::{initial_clients, print_event_log_for_unit_tests},
parachains::penpal::api::system::events::Remarked,
};

#[tokio::test]
async fn transact_from_eth_to_penpal() {
let test_clients = initial_clients().await.expect("initialize clients");
let ethereum_client = *(test_clients.ethereum_signed_client.clone());
let penpal_client = *(test_clients.penpal_client.clone());

let gateway_addr: Address = GATEWAY_PROXY_CONTRACT.into();
let gateway = i_gateway::IGateway::new(gateway_addr, ethereum_client.clone());

let message = TransactMessage {
origin_kind: 0,
fee: 40_000_000_000,
weight_at_most: Weight { ref_time: 40_000_000, proof_size: 8_000 },
//system.remark
call: Bytes::from(hex!("00071468656c6c6f").to_vec()),
};

let receipt = gateway
.transact(PENPAL_PARA_ID, message)
.value::<U256>(100_000_000_000_000_000_u128.into())
.send()
.await
.unwrap()
.await
.unwrap()
.unwrap();

println!(
"receipt transaction hash: {:#?}, transaction block: {:#?}",
hex::encode(receipt.transaction_hash),
receipt.block_number
);

// Log for OutboundMessageAccepted
let outbound_message_accepted_log = receipt.logs.last().unwrap();

// print log for unit tests
print_event_log_for_unit_tests(outbound_message_accepted_log);

assert_eq!(receipt.status.unwrap().as_u64(), 1u64);

let wait_for_blocks = 50;
let mut blocks = penpal_client
.blocks()
.subscribe_finalized()
.await
.expect("block subscription")
.take(wait_for_blocks);

let mut event_found = false;
while let Some(Ok(block)) = blocks.next().await {
println!("Polling penpal block {} for system remark event.", block.number());

let events = block.events().await.unwrap();
for _ in events.find::<Remarked>() {
println!("Remarked event found in penpal block {}.", block.number());
event_found = true;
}
if event_found {
break
}
}
assert!(event_found)
}
20 changes: 20 additions & 0 deletions web/packages/test/scripts/configure-penpal.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/usr/bin/env bash
set -eu

source scripts/set-env.sh
source scripts/xcm-helper.sh

fund_bridgehub_sovereign() {
local call="0x0a08007369626cf5030000000000000000000000000000000000000000000000000000070010a5d4e8"
send_governance_transact_from_relaychain $PENPAL_PARAID "$call"
}

configure_penpal() {
fund_bridgehub_sovereign
}

if [ -z "${from_start_services:-}" ]; then
echo "config penpal only!"
configure_penpal
wait
fi
4 changes: 3 additions & 1 deletion web/packages/test/scripts/set-env.sh
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,11 @@ assethub_ws_url="${ASSET_HUB_WS_URL:-ws://127.0.0.1:12144}"
assethub_seed="${ASSET_HUB_SEED:-//Alice}"
export ASSET_HUB_PARAID="${ASSET_HUB_PARAID:-1000}"
export ASSET_HUB_AGENT_ID="${ASSET_HUB_AGENT_ID:-0x81c5ab2571199e3188135178f3c2c8e2d268be1313d029b30f534fa579b69b79}"

export ASSET_HUB_CHANNEL_ID="0xc173fac324158e77fb5840738a1a541f633cbec8884c6a601c567d2b376a0539"
export PENPAL_CHANNEL_ID="0xa69fbbae90bb6096d59b1930bbcfc8a3ef23959d226b1861deb7ad8fb06c6fa3"
export PRIMARY_GOVERNANCE_CHANNEL_ID="0x0000000000000000000000000000000000000000000000000000000000000001"
export SECONDARY_GOVERNANCE_CHANNEL_ID="0x0000000000000000000000000000000000000000000000000000000000000002"
export PENPAL_PARAID="${PENPAL_PARAID:-2000}"

# Token decimal of the relaychain(KSM|ROC:12,DOT:10)
export FOREIGN_TOKEN_DECIMALS=12
Expand All @@ -62,6 +62,8 @@ reset_ethereum="${RESET_ETHEREUM:-true}"
## Important accounts

# Useful tool to get these account values: https://www.shawntabrizi.com/substrate-js-utilities/
# Account for bridgehub (Sibling parachain 1013 in testnet)
bridgehub_sovereign_account="${BRIDGEHUB_SOVEREIGN_ACCOUNT:-0x7369626cf5030000000000000000000000000000000000000000000000000000}"
# Account for assethub (Sibling parachain 1000 5Eg2fntNprdN3FgH4sfEaaZhYtddZQSQUqvYJ1f2mLtinVhV in testnet)
assethub_sovereign_account="${ASSETHUB_SOVEREIGN_ACCOUNT:-0x7369626ce8030000000000000000000000000000000000000000000000000000}"
# Account for penpal (Sibling parachain 2000 5Eg2fntJ27qsari4FGrGhrMqKFDRnkNSR6UshkZYBGXmSuC8 in testnet)
Expand Down
Loading
Loading