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

Add gas limit to message struct #66

Merged
merged 3 commits into from
Oct 31, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
7 changes: 6 additions & 1 deletion src/Channel.sol
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,12 @@ contract Channel is UserConfig {
/// @param from User application contract address which send the message.
/// @param toChainId The Message destination chain id.
/// @param to User application contract address which receive the message.
/// @param gasLimit Gas limit for UA used.
/// @param encoded The calldata which encoded by ABI Encoding.
function _send(address from, uint256 toChainId, address to, bytes calldata encoded) internal returns (bytes32) {
function _send(address from, uint256 toChainId, address to, uint256 gasLimit, bytes calldata encoded)
internal
returns (bytes32)
{
// only cross-chain message
require(toChainId != LOCAL_CHAINID(), "!cross-chain");
// get this message leaf index.
Expand All @@ -84,6 +88,7 @@ contract Channel is UserConfig {
from: from,
toChainId: toChainId,
to: to,
gasLimit: gasLimit,
encoded: encoded
});
// hash the message.
Expand Down
2 changes: 2 additions & 0 deletions src/Common.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ pragma solidity 0.8.17;
/// @param from User application contract address which send the message.
/// @param toChainId The Message destination chain id.
/// @param to User application contract address which receive the message.
/// @param gasLimit Gas limit for UA used.
/// @param encoded The calldata which encoded by ABI Encoding.
struct Message {
address channel;
Expand All @@ -34,6 +35,7 @@ struct Message {
address from;
uint256 toChainId;
address to;
uint256 gasLimit;
bytes encoded; /*(abi.encodePacked(SELECTOR, PARAMS))*/
}

Expand Down
67 changes: 40 additions & 27 deletions src/ORMP.sol
Original file line number Diff line number Diff line change
Expand Up @@ -36,50 +36,66 @@ contract ORMP is ReentrancyGuard, Channel {
/// @notice follow https://eips.ethereum.org/EIPS/eip-5750
/// @param toChainId The Message destination chain id.
/// @param to User application contract address which receive the message.
/// @param gasLimit Gas limit for UA used.
/// @param encoded The calldata which encoded by ABI Encoding.
/// @param refund Return extra fee to refund address.
/// @param params General extensibility for relayer to custom functionality.
function send(uint256 toChainId, address to, bytes calldata encoded, address refund, bytes calldata params)
external
payable
sendNonReentrant
returns (bytes32)
{
function send(
uint256 toChainId,
address to,
uint256 gasLimit,
bytes calldata encoded,
address refund,
bytes calldata params
) external payable sendNonReentrant returns (bytes32) {
// user application address.
address ua = msg.sender;
// fetch user application's config.
Config memory uaConfig = getAppConfig(ua);
// send message by channel, return the hash of the message as id.
bytes32 msgHash = _send(ua, toChainId, to, encoded);
bytes32 msgHash = _send(ua, toChainId, to, gasLimit, encoded);

// handle fee
_handleFee(ua, refund, msgHash, toChainId, gasLimit, encoded, params);

return msgHash;
}

function _handleFee(
address ua,
address refund,
bytes32 msgHash,
uint256 toChainId,
uint256 gasLimit,
bytes calldata encoded,
bytes calldata params
) internal {
// fetch user application's config.
Config memory uaConfig = getAppConfig(ua);
// handle relayer fee
uint256 relayerFee = _handleRelayer(uaConfig.relayer, msgHash, toChainId, ua, encoded.length, params);
uint256 relayerFee = _handleRelayer(uaConfig.relayer, msgHash, toChainId, ua, gasLimit, encoded, params);
// handle oracle fee
uint256 oracleFee = _handleOracle(uaConfig.oracle, msgHash, toChainId, ua);

//refund
// refund
if (msg.value > relayerFee + oracleFee) {
uint256 refundFee = msg.value - (relayerFee + oracleFee);
(bool success,) = refund.call{value: refundFee}("");
require(success, "!refund");
}

return msgHash;
}

/// @notice Get a quote in source native gas, for the amount that send() requires to pay for message delivery.
/// @param toChainId The Message destination chain id.
// @param to User application contract address which receive the message.
// @param ua User application contract address which send the message.
/// @param gasLimit Gas limit for UA used.
/// @param encoded The calldata which encoded by ABI Encoding.
/// @param params General extensibility for relayer to custom functionality.
function fee(uint256 toChainId, address, /*to*/ bytes calldata encoded, bytes calldata params)
function fee(uint256 toChainId, address ua, uint256 gasLimit, bytes calldata encoded, bytes calldata params)
external
view
returns (uint256)
{
address ua = msg.sender;
Config memory uaConfig = getAppConfig(ua);
uint256 relayerFee = IRelayer(uaConfig.relayer).fee(toChainId, ua, encoded.length, params);
uint256 relayerFee = IRelayer(uaConfig.relayer).fee(toChainId, ua, gasLimit, encoded, params);
uint256 oracleFee = IOracle(uaConfig.oracle).fee(toChainId, ua);
return relayerFee + oracleFee;
}
Expand All @@ -89,10 +105,11 @@ contract ORMP is ReentrancyGuard, Channel {
bytes32 msgHash,
uint256 toChainId,
address ua,
uint256 size,
uint256 gasLimit,
bytes calldata encoded,
bytes calldata params
) internal returns (uint256) {
uint256 relayerFee = IRelayer(relayer).fee(toChainId, ua, size, params);
uint256 relayerFee = IRelayer(relayer).fee(toChainId, ua, gasLimit, encoded, params);
IRelayer(relayer).assign{value: relayerFee}(msgHash, params);
return relayerFee;
}
Expand All @@ -107,27 +124,23 @@ contract ORMP is ReentrancyGuard, Channel {
/// @notice Only channel could call this function.
/// @param message Verified receive message info.
/// @param proof Message proof of this message.
/// @param gasLimit The gas limit of message execute.
/// @return dispatchResult Result of the message dispatch.
function recv(Message calldata message, bytes calldata proof, uint256 gasLimit)
function recv(Message calldata message, bytes calldata proof)
external
recvNonReentrant
returns (bool dispatchResult)
{
bytes32 msgHash = _recv(message, proof);
dispatchResult = _dispatch(message, msgHash, gasLimit);
dispatchResult = _dispatch(message, msgHash);
// emit dispatched message event.
emit MessageDispatched(msgHash, dispatchResult);
}

/// @dev Dispatch the cross chain message.
function _dispatch(Message memory message, bytes32 msgHash, uint256 gasLimit)
private
returns (bool dispatchResult)
{
function _dispatch(Message memory message, bytes32 msgHash) private returns (bool dispatchResult) {
// Deliver the message to user application contract address.
(dispatchResult,) = message.to.excessivelySafeCall(
gasLimit, 0, abi.encodePacked(message.encoded, msgHash, message.fromChainId, message.from)
message.gasLimit, 0, abi.encodePacked(message.encoded, msgHash, message.fromChainId, message.from)
);
}
}
21 changes: 12 additions & 9 deletions src/eco/Relayer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,16 @@ contract Relayer {
require(success, "!withdraw");
}

// params = [extraGas]
function fee(uint256 toChainId, address, /*ua*/ uint256 size, bytes calldata params)
public
view
returns (uint256)
{
uint256 extraGas = abi.decode(params, (uint256));
// extraGas = gasLimit
function fee(
uint256 toChainId,
address, /*ua*/
uint256 gasLimit,
bytes calldata encoded,
bytes calldata /*params*/
) public view returns (uint256) {
uint256 size = encoded.length;
uint256 extraGas = gasLimit;
DstPrice memory p = priceOf[toChainId];
DstConfig memory c = configOf[toChainId];

Expand All @@ -112,7 +115,7 @@ contract Relayer {
emit Assigned(msgHash, msg.value, params, IORMP(PROTOCOL).prove());
}

function relay(Message calldata message, bytes calldata proof, uint256 gasLimit) external onlyApproved {
IORMP(PROTOCOL).recv(message, proof, gasLimit);
function relay(Message calldata message, bytes calldata proof) external onlyApproved {
IORMP(PROTOCOL).recv(message, proof);
}
}
18 changes: 10 additions & 8 deletions src/interfaces/IORMP.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,19 @@ interface IORMP {
/// @notice follow https://eips.ethereum.org/EIPS/eip-5750
/// @param toChainId The Message destination chain id.
/// @param to User application contract address which receive the message.
/// @param gasLimit Gas limit for UA used.
/// @param encoded The calldata which encoded by ABI Encoding.
/// @param refund Return extra fee to refund address.
/// @param params General extensibility for relayer to custom functionality.
/// @return Return the hash of the message as message id.
function send(uint256 toChainId, address to, bytes calldata encoded, address refund, bytes calldata params)
external
payable
returns (bytes32);
function send(
uint256 toChainId,
address to,
uint256 gasLimit,
bytes calldata encoded,
address refund,
bytes calldata params
) external payable returns (bytes32);

/// @notice Get a quote in source native gas, for the amount that send() requires to pay for message delivery.
/// @param toChainId The Message destination chain id.
Expand All @@ -46,11 +51,8 @@ interface IORMP {
/// @dev Recv verified message and dispatch to destination user application address.
/// @param message Verified receive message info.
/// @param proof Message proof of this message.
/// @param gasLimit The gas limit of message execute.
/// @return dispatchResult Result of the message dispatch.
function recv(Message calldata message, bytes calldata proof, uint256 gasLimit)
external
returns (bool dispatchResult);
function recv(Message calldata message, bytes calldata proof) external returns (bool dispatchResult);

function prove() external view returns (bytes32[32] memory);

Expand Down
8 changes: 6 additions & 2 deletions src/interfaces/IRelayer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,14 @@ interface IRelayer {
/// @notice Fetch relayer price to relay message to the destination chain.
/// @param toChainId The destination chain id.
/// @param ua The user application which send the message.
/// @param size The size of message encoded payload.
/// @param gasLimit Gas limit for UA used.
/// @param encoded The calldata which encoded by ABI Encoding.
/// @param params General extensibility for relayer to custom functionality.
/// @return Relayer price in source native gas.
function fee(uint256 toChainId, address ua, uint256 size, bytes calldata params) external view returns (uint256);
function fee(uint256 toChainId, address ua, uint256 gasLimit, bytes calldata encoded, bytes calldata params)
external
view
returns (uint256);

/// @notice Assign the relay message task to relayer maintainer.
/// @param msgHash Hash of the message.
Expand Down
14 changes: 8 additions & 6 deletions test/Channel.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,15 @@ contract ChannelTest is Test, Verifier {
}

function test_sendMessage() public {
channel.sendMessage(self, 2, self, "");
channel.sendMessage(self, 2, self, 0, "");
}

function testFail_sendMessage_notCrossChain() public {
channel.sendMessage(self, 1, self, "");
channel.sendMessage(self, 1, self, 0, "");
}

function test_recvMessage() public {
bytes32 msgHash = channel.sendMessage(self, 2, self, "");
bytes32 msgHash = channel.sendMessage(self, 2, self, 0, "");

Message memory message = Message({
channel: address(channel),
Expand All @@ -64,6 +64,7 @@ contract ChannelTest is Test, Verifier {
from: self,
toChainId: 2,
to: self,
gasLimit: 0,
encoded: ""
});
assertEq(msgHash, hash(message));
Expand All @@ -76,14 +77,15 @@ contract ChannelTest is Test, Verifier {
for (uint256 i = 0; i < 100; i++) {
vm.chainId(1);
uint256 index = channel.messageCount();
bytes32 msgHash = channel.sendMessage(self, 2, self, "");
bytes32 msgHash = channel.sendMessage(self, 2, self, 0, "");
Message memory message = Message({
channel: address(channel),
index: index,
fromChainId: 1,
from: self,
toChainId: 2,
to: self,
gasLimit: 0,
encoded: ""
});
assertEq(msgHash, hash(message));
Expand All @@ -101,11 +103,11 @@ contract ChannelTest is Test, Verifier {
contract ChannelWrapper is Channel {
constructor(address dao) Channel(dao) {}

function sendMessage(address from, uint256 toChainId, address to, bytes calldata encoded)
function sendMessage(address from, uint256 toChainId, address to, uint256 gasLimit, bytes calldata encoded)
public
returns (bytes32)
{
return _send(from, toChainId, to, encoded);
return _send(from, toChainId, to, gasLimit, encoded);
}

function recvMessage(Message calldata message, bytes calldata proof) public {
Expand Down
8 changes: 5 additions & 3 deletions test/Common.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,14 @@ contract CommonTest is Test {
from: address(0x0),
toChainId: 2,
to: address(0x0),
gasLimit: 0,
encoded: ""
});
assertEq0(
hex"0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000000",
hex"0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000",
abi.encode(message)
);
assertEq(bytes32(0xb641a5a085fd1455a66efb94dbabf6af79dccb25bdee35bb9cccf535925e0a19), hash(message));
assertEq(bytes32(0x71fb3c3f4e014e5f86a232c7c14dc843164c056fd16a026cfea4fe4f814236e7), hash(message));
}

function test_hashMessage_real() public {
Expand All @@ -46,8 +47,9 @@ contract CommonTest is Test {
from: 0x0f14341A7f464320319025540E8Fe48Ad0fe5aec,
toChainId: 43,
to: 0x000000fbfBc6954C8CBba3130b5Aee7f3Ea5108e,
gasLimit: 0,
encoded: ""
});
assertEq(bytes32(0xec824c8e8f1f19fadc3b4532bc2925af53fdad9162eecc17342909ac8ab787f7), hash(message));
assertEq(bytes32(0x16ef90052810b57bc4e8e2af6c78a9160259b8b754fe2b2cb943adeb716dd024), hash(message));
}
}
20 changes: 14 additions & 6 deletions test/ORMP.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -31,24 +31,32 @@ contract ORMPTest is Test, Verifier {
vm.chainId(1);
ormp = new ORMP(self);
ormp.setDefaultConfig(self, self);
message =
Message({channel: address(ormp), index: 0, fromChainId: 1, from: self, toChainId: 2, to: self, encoded: ""});
message = Message({
channel: address(ormp),
index: 0,
fromChainId: 1,
from: self,
toChainId: 2,
to: self,
gasLimit: 0,
encoded: ""
});
}

function test_send() public {
perform_send();
}

function perform_send() public {
uint256 f = ormp.fee(2, self, "", "");
ormp.send{value: f}(2, self, "", self, "");
uint256 f = ormp.fee(2, self, 0, "", "");
ormp.send{value: f}(2, self, 0, "", self, "");
proof = Proof({blockNumber: block.number, messageIndex: ormp.messageCount() - 1, messageProof: ormp.prove()});
vm.chainId(2);
}

function test_recv() public {
perform_send();
bool r = ormp.recv(message, abi.encode(proof), gasleft());
bool r = ormp.recv(message, abi.encode(proof));
assertEq(r, false);
}

Expand All @@ -59,7 +67,7 @@ contract ORMPTest is Test, Verifier {
function assign(bytes32) external payable {}
function assign(bytes32, bytes calldata) external payable {}

function fee(uint256, address, uint256, bytes calldata) external pure returns (uint256) {
function fee(uint256, address, uint256, bytes calldata, bytes calldata) external pure returns (uint256) {
return 1;
}

Expand Down
Loading
Loading