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 13 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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,4 @@ contracts/beefy-state.json

go/
gocache/
/parachain
yrong marked this conversation as resolved.
Show resolved Hide resolved
23 changes: 22 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,
TransactMessage,
OriginKind
} 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,21 @@ contract Gateway is IGateway, IInitializable {
assets.assetHubCreateAssetFee = config.assetHubCreateAssetFee;
assets.assetHubReserveTransferFee = config.assetHubReserveTransferFee;
}

/// @inheritdoc IGateway
function transact(ParaID destinationChain, TransactMessage calldata message) external payable {
vgeddes marked this conversation as resolved.
Show resolved Hide resolved
if (message.validate() == false) {
revert InvalidTransact();
}
Ticket memory ticket;
Costs memory costs;
costs.foreign = message.fee;
vgeddes marked this conversation as resolved.
Show resolved Hide resolved
bytes memory payload = SubstrateTypes.Transact(
msg.sender, message.originKind.encode(), message.fee, message.weightAtMost, message.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);
}
}
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, TransactMessage, 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
);
}
}
41 changes: 41 additions & 0 deletions contracts/src/Types.sol
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,44 @@ 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;
}

using {validate} for TransactMessage global;

struct TransactMessage {
vgeddes marked this conversation as resolved.
Show resolved Hide resolved
OriginKind originKind;
uint128 fee;
Weight weightAtMost;
bytes call;
}

function validate(TransactMessage calldata message) pure returns (bool) {
if (
message.call.length == 0 || message.fee == 0 || message.weightAtMost.refTime == 0
|| message.weightAtMost.proofSize == 0
Copy link
Collaborator

Choose a reason for hiding this comment

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

All this validation can be done inline in Gateway.transact. Its very little code and I don't think it requires its own function.

Copy link
Contributor

@claravanstaden claravanstaden Apr 10, 2024

Choose a reason for hiding this comment

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

Where did this validation move to? Should it not be done here somewhere?

I see it was removed here: 00abc13 and removed because of @vgeddes comment: #1141 (comment)

Should we not still at least check destinationFee > 0? Ie does it make sense to allow a likely invalid message to be sent and the user to pay for it when it will likely not succeed?

Copy link
Contributor Author

@yrong yrong Apr 11, 2024

Choose a reason for hiding this comment

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

Agree. I think we need these checks on source chain so add it back here 3dd1adc

) {
return false;
}
return true;
}
5 changes: 4 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, TransactMessage} from "../Types.sol";
import {Verification} from "../Verification.sol";
import {UD60x18} from "prb/math/src/UD60x18.sol";

Expand Down Expand Up @@ -105,4 +105,7 @@ 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, TransactMessage calldata message) external payable;
}
47 changes: 42 additions & 5 deletions contracts/test/Gateway.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -42,22 +42,28 @@ import {
ParaID,
Command,
multiAddressFromBytes32,
multiAddressFromBytes20
multiAddressFromBytes20,
TransactMessage,
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 +120,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 +839,27 @@ contract GatewayTest is Test {
fee = IGateway(address(gateway)).quoteRegisterTokenFee();
assertEq(fee, 10000000000000000);
}

/**
* Transact
*/
function testTransactFromXcmOrigin() public {
TransactMessage memory message = TransactMessage(OriginKind.Xcm, 1, Weight(1, 1), bytes("0x1"));
bytes memory payload = SubstrateTypes.Transact(account1, 0x03, message.fee, message.weightAtMost, message.call);
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, message);
}

function testTransactFromSignedOrigin() public {
TransactMessage memory message = TransactMessage(OriginKind.SovereignAccount, 1, Weight(1, 1), bytes("0x1"));
bytes memory payload = SubstrateTypes.Transact(account1, 0x01, message.fee, message.weightAtMost, message.call);
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, message);
}
}
13 changes: 12 additions & 1 deletion contracts/test/mocks/GatewayUpgradeMock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,16 @@
// 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,
TransactMessage
} 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 +70,6 @@ contract GatewayUpgradeMock is IGateway, IInitializable {
function pricingParameters() external pure returns (UD60x18, uint128) {
return (convert(0), uint128(0));
}

function transact(ParaID destinationChain, TransactMessage calldata message) external payable {}
}
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
7 changes: 6 additions & 1 deletion web/packages/test/scripts/start-services.sh
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,13 @@ echo "Config assethub"
source scripts/configure-assethub.sh
configure_assethub

# 8. config penpal
echo "Config penpal"
source scripts/configure-penpal.sh
configure_penpal

if [ "$skip_relayer" == "false" ]; then
# 8. start relayer
# 9. start relayer
echo "Starting relayers"
source scripts/start-relayer.sh
deploy_relayer
Expand Down
Loading