From 3ac93d3cab93dd018f360e74688738aa830667af Mon Sep 17 00:00:00 2001 From: echo Date: Tue, 31 Oct 2023 12:02:42 +0800 Subject: [PATCH] fix #63 --- src/Channel.sol | 9 +++-- src/Common.sol | 2 ++ src/ORMP.sol | 67 ++++++++++++++++++++++--------------- src/eco/Relayer.sol | 19 ++++++----- src/interfaces/IORMP.sol | 18 +++++----- src/interfaces/IRelayer.sol | 8 +++-- test/Channel.t.sol | 18 +++++----- test/Common.t.sol | 6 ++-- test/ORMP.t.sol | 20 +++++++---- test/bench/ORMP.b.sol | 9 ++--- test/eco/Relayer.t.sol | 18 +++++++--- 11 files changed, 122 insertions(+), 72 deletions(-) diff --git a/src/Channel.sol b/src/Channel.sol index 7142709..7b0f416 100644 --- a/src/Channel.sol +++ b/src/Channel.sol @@ -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. @@ -84,7 +88,8 @@ contract Channel is UserConfig { from: from, toChainId: toChainId, to: to, - encoded: encoded + encoded: encoded, + gasLimit: gasLimit }); // hash the message. bytes32 msgHash = hash(message); diff --git a/src/Common.sol b/src/Common.sol index f51a814..6f996ff 100644 --- a/src/Common.sol +++ b/src/Common.sol @@ -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; @@ -34,6 +35,7 @@ struct Message { address from; uint256 toChainId; address to; + uint256 gasLimit; bytes encoded; /*(abi.encodePacked(SELECTOR, PARAMS))*/ } diff --git a/src/ORMP.sol b/src/ORMP.sol index 117fdd5..5ba0cb6 100644 --- a/src/ORMP.sol +++ b/src/ORMP.sol @@ -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; } @@ -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; } @@ -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) ); } } diff --git a/src/eco/Relayer.sol b/src/eco/Relayer.sol index 1286dd5..04a3baf 100644 --- a/src/eco/Relayer.sol +++ b/src/eco/Relayer.sol @@ -89,12 +89,15 @@ contract Relayer { } // params = [extraGas] - function fee(uint256 toChainId, address, /*ua*/ uint256 size, bytes calldata params) - public - view - returns (uint256) - { - uint256 extraGas = abi.decode(params, (uint256)); + 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]; @@ -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); } } diff --git a/src/interfaces/IORMP.sol b/src/interfaces/IORMP.sol index a2b559a..972efac 100644 --- a/src/interfaces/IORMP.sol +++ b/src/interfaces/IORMP.sol @@ -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. @@ -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); diff --git a/src/interfaces/IRelayer.sol b/src/interfaces/IRelayer.sol index b2ff3ab..ce4b137 100644 --- a/src/interfaces/IRelayer.sol +++ b/src/interfaces/IRelayer.sol @@ -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. diff --git a/test/Channel.t.sol b/test/Channel.t.sol index e0d28c7..f4a43b6 100644 --- a/test/Channel.t.sol +++ b/test/Channel.t.sol @@ -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), @@ -64,7 +64,8 @@ contract ChannelTest is Test, Verifier { from: self, toChainId: 2, to: self, - encoded: "" + encoded: "", + gasLimit: 0 }); assertEq(msgHash, hash(message)); Proof memory proof = Proof({blockNumber: block.number, messageIndex: 0, messageProof: channel.prove()}); @@ -76,7 +77,7 @@ 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, @@ -84,7 +85,8 @@ contract ChannelTest is Test, Verifier { from: self, toChainId: 2, to: self, - encoded: "" + encoded: "", + gasLimit: 0 }); assertEq(msgHash, hash(message)); Proof memory proof = Proof({blockNumber: block.number, messageIndex: index, messageProof: channel.prove()}); @@ -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 { diff --git a/test/Common.t.sol b/test/Common.t.sol index b21a2f0..f70948e 100644 --- a/test/Common.t.sol +++ b/test/Common.t.sol @@ -29,7 +29,8 @@ contract CommonTest is Test { from: address(0x0), toChainId: 2, to: address(0x0), - encoded: "" + encoded: "", + gasLimit: 0 }); assertEq0( hex"0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000000", @@ -46,7 +47,8 @@ contract CommonTest is Test { from: 0x0f14341A7f464320319025540E8Fe48Ad0fe5aec, toChainId: 43, to: 0x000000fbfBc6954C8CBba3130b5Aee7f3Ea5108e, - encoded: "" + encoded: "", + gasLimit: 0 }); assertEq(bytes32(0xec824c8e8f1f19fadc3b4532bc2925af53fdad9162eecc17342909ac8ab787f7), hash(message)); } diff --git a/test/ORMP.t.sol b/test/ORMP.t.sol index 5720e45..f9818d9 100644 --- a/test/ORMP.t.sol +++ b/test/ORMP.t.sol @@ -31,8 +31,16 @@ 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, + encoded: "", + gasLimit: 0 + }); } function test_send() public { @@ -40,15 +48,15 @@ contract ORMPTest is Test, Verifier { } 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); } @@ -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; } diff --git a/test/bench/ORMP.b.sol b/test/bench/ORMP.b.sol index d61dd5b..f6108d1 100644 --- a/test/bench/ORMP.b.sol +++ b/test/bench/ORMP.b.sol @@ -54,7 +54,8 @@ contract ORMPBenchmarkTest is Test { from: self, toChainId: toChainId, to: self, - encoded: encoded + encoded: encoded, + gasLimit: 0 }); perform_recv(message); } @@ -70,7 +71,7 @@ contract ORMPBenchmarkTest is Test { oracle.setDapi(message.fromChainId, self); vm.prank(address(relayer)); - ormp.recv(message, abi.encode(proof), 0); + ormp.recv(message, abi.encode(proof)); } function messageRoot() public view returns (bytes32) { @@ -79,7 +80,7 @@ contract ORMPBenchmarkTest is Test { function perform_send(uint256 fromChainId, uint256 toChainId, bytes calldata encoded) public { vm.createSelectFork(fromChainId.toChainName()); - uint256 f = ormp.fee(toChainId, self, encoded, abi.encode(uint256(0))); - ormp.send{value: f}(toChainId, self, encoded, self, abi.encode(uint256(0))); + uint256 f = ormp.fee(toChainId, self, 0, encoded, abi.encode(uint256(0))); + ormp.send{value: f}(toChainId, self, 0, encoded, self, abi.encode(uint256(0))); } } diff --git a/test/eco/Relayer.t.sol b/test/eco/Relayer.t.sol index 2fe0ab5..f38912b 100644 --- a/test/eco/Relayer.t.sol +++ b/test/eco/Relayer.t.sol @@ -78,22 +78,30 @@ contract RelayerTest is Test { function test_setPrice() public { relayer.setDstPrice(1, 10 ** 10, 1); relayer.setDstConfig(1, 1, 1); - uint256 f = relayer.fee(1, self, 1, abi.encode(uint256(1))); + uint256 f = relayer.fee(1, self, 1, hex"00", abi.encode(uint256(1))); assertEq(f, 3); } function test_assign() public { relayer.setDstPrice(1, 10 ** 10, 1); relayer.setDstConfig(1, 1, 1); - uint256 v = relayer.fee(1, address(1), 1, abi.encode(uint256(1))); + uint256 v = relayer.fee(1, address(1), 1, hex"00", abi.encode(uint256(1))); relayer.assign{value: v}(bytes32(0), abi.encode(uint256(1))); assertEq(v, 3); } function test_relay() public { - Message memory message = - Message({channel: address(0xc), index: 0, fromChainId: 1, from: self, toChainId: 2, to: self, encoded: ""}); - relayer.relay(message, "", gasleft()); + Message memory message = Message({ + channel: address(0xc), + index: 0, + fromChainId: 1, + from: self, + toChainId: 2, + to: self, + encoded: "", + gasLimit: 0 + }); + relayer.relay(message, ""); } function recv(Message calldata message, bytes calldata proof, uint256) external returns (bool) {}