From e342d071ca49a901cbdee63876ba943a7f020112 Mon Sep 17 00:00:00 2001 From: Lohann Paterno Coutinho Ferreira Date: Mon, 16 Dec 2024 20:12:58 -0300 Subject: [PATCH] Make nonce sequential --- src/Gateway.sol | 44 ++++++++++++++++-------------------------- src/utils/GasUtils.sol | 2 +- test/Example.t.sol | 2 +- test/Gateway.t.sol | 8 ++++---- 4 files changed, 23 insertions(+), 33 deletions(-) diff --git a/src/Gateway.sol b/src/Gateway.sol index 54505a5..f742917 100644 --- a/src/Gateway.sol +++ b/src/Gateway.sol @@ -54,11 +54,6 @@ contract Gateway is IGateway, IExecutor, IUpgradable, GatewayEIP712 { using RouteStore for RouteStore.NetworkInfo; using NetworkIDHelpers for NetworkID; - /** - * @dev Non-zero value used to initialize the `prevMessageHash` storage - */ - bytes32 internal constant FIRST_MESSAGE_PLACEHOLDER = bytes32(uint256(2 ** 256 - 1)); - /** * @dev Selector of `GmpCreated` event. * keccak256("GmpCreated(bytes32,bytes32,address,uint16,uint256,uint256,bytes)"); @@ -75,7 +70,7 @@ contract Gateway is IGateway, IExecutor, IUpgradable, GatewayEIP712 { mapping(bytes32 => GmpInfo) private _messages; // Hash of the previous GMP message submitted. - bytes32 public prevMessageHash; + uint256 internal nonce; // Replay protection mechanism, stores the hash of the executed messages // messageHash => shardId @@ -97,12 +92,12 @@ contract Gateway is IGateway, IExecutor, IUpgradable, GatewayEIP712 { // EIP-712 typed hash function initialize(address proxyAdmin, TssKey[] calldata keys, Network[] calldata networks) external { require(PROXY_ADDRESS == address(this) || msg.sender == FACTORY, "only proxy can be initialize"); - require(prevMessageHash == 0, "already initialized"); + require(nonce == 0, "already initialized"); ERC1967.setAdmin(proxyAdmin); - // Initialize the prevMessageHash with a non-zero value to avoid the first GMP to spent more gas, + // Initialize the `nonce` as one to avoid the first GMP to spent more gas, // once initialize the storage cost 21k gas, while alter it cost just 2800 gas. - prevMessageHash = FIRST_MESSAGE_PLACEHOLDER; + nonce = 1; // Register networks RouteStore.getMainStorage().initialize(networks, NetworkID.wrap(NETWORK_ID)); @@ -115,6 +110,10 @@ contract Gateway is IGateway, IExecutor, IUpgradable, GatewayEIP712 { emit KeySetChanged(bytes32(0), revoked, keys); } + function prevMessageHash() external view returns (uint256) { + return nonce; + } + function gmpInfo(bytes32 id) external view returns (GmpInfo memory) { return _messages[id]; } @@ -303,30 +302,21 @@ contract Gateway is IGateway, IExecutor, IUpgradable, GatewayEIP712 { GmpSender source = msg.sender.toSender(false); // Salt is equal to the previous message id (EIP-712 hash), this allows us to establish a sequence and eaily query the message history. - bytes32 prevHash = prevMessageHash; - - // if the messageHash is the first message, we use a zero salt - uint256 salt = BranchlessMath.ternary(prevHash == FIRST_MESSAGE_PLACEHOLDER, 0, uint256(prevHash)); - - // Create GMP message and update prevMessageHash - bytes memory payload; - { - GmpMessage memory message = - GmpMessage(source, NETWORK_ID, destinationAddress, routeId, executionGasLimit, salt, data); - prevHash = message.eip712hash(); - prevMessageHash = prevHash; - payload = message.data; - } + uint256 nextNonce = nonce++; + + // Create GMP message and update nonce + GmpMessage memory message = + GmpMessage(source, NETWORK_ID, destinationAddress, routeId, executionGasLimit, nextNonce, data); // Emit `GmpCreated` event without copy the data, to simplify the gas estimation. - _emitGmpCreated(prevHash, source, destinationAddress, routeId, executionGasLimit, salt, payload); + _emitGmpCreated(message.eip712hash(), source, destinationAddress, routeId, executionGasLimit, nextNonce, message.data); } /** * @dev Emit `GmpCreated` event without copy the data, to simplify the gas estimation. */ function _emitGmpCreated( - bytes32 prevHash, + bytes32 messageID, GmpSender source, address destinationAddress, uint16 destinationNetwork, @@ -348,8 +338,8 @@ contract Gateway is IGateway, IExecutor, IUpgradable, GatewayEIP712 { mstore(add(ptr, 0x60), 0x80) // data offset let size := and(add(mload(payload), 31), 0xffffffe0) size := add(size, 160) - log4(ptr, size, GMP_CREATED_EVENT_SELECTOR, prevHash, source, destinationAddress) - mstore(0, prevHash) + log4(ptr, size, GMP_CREATED_EVENT_SELECTOR, messageID, source, destinationAddress) + mstore(0, messageID) return(0, 32) } } diff --git a/src/utils/GasUtils.sol b/src/utils/GasUtils.sol index e1ca990..6a52cc8 100644 --- a/src/utils/GasUtils.sol +++ b/src/utils/GasUtils.sol @@ -25,7 +25,7 @@ library GasUtils { /** * @dev Base cost of the `IGateway.submitMessage` method. */ - uint256 internal constant SUBMIT_BASE_COST = 23449; + uint256 internal constant SUBMIT_BASE_COST = 23449 + 19; /** * @dev Compute the gas cost of memory expansion. diff --git a/test/Example.t.sol b/test/Example.t.sol index a7de3db..94c0376 100644 --- a/test/Example.t.sol +++ b/test/Example.t.sol @@ -109,7 +109,7 @@ contract ExampleTest is Test { dest: address(dstToken), destNetwork: DEST_NETWORK_ID, gasLimit: 100_000, - salt: 0, + salt: 1, data: abi.encode(MockERC20.CrossChainTransfer({from: ALICE, to: BOB, amount: 100})) }); diff --git a/test/Gateway.t.sol b/test/Gateway.t.sol index 6a5a4db..240f687 100644 --- a/test/Gateway.t.sol +++ b/test/Gateway.t.sol @@ -420,7 +420,7 @@ contract GatewayBase is Test { dest: address(bytes20(keccak256("dummy_address"))), destNetwork: DEST_NETWORK_ID, gasLimit: 0, - salt: 0, + salt: 1, data: new bytes(messageSize) }); @@ -631,13 +631,13 @@ contract GatewayBase is Test { dest: address(receiver), destNetwork: DEST_NETWORK_ID, gasLimit: 100_000, - salt: 0, + salt: 1, data: abi.encodePacked(uint256(100_000)) }); bytes32 id = gmp.eip712hash(); // Check the previous message hash - assertEq(gateway.prevMessageHash(), bytes32(uint256(2 ** 256 - 1)), "wrong previous message hash"); + assertEq(gateway.prevMessageHash(), 1, "wrong previous message hash"); CallOptions memory ctx = CallOptions({ from: gmpSender.toAddress(), @@ -675,7 +675,7 @@ contract GatewayBase is Test { assertEq(ctx.executionCost, expectedCost, "unexpected execution gas cost in first call"); // Now the second GMP message should have the salt equals to previous gmp hash - gmp.salt = uint256(id); + gmp.salt += 1; id = gmp.eip712hash(); // Expect event