From cbbe1b8e8260d91ad0bed9ff3abb3e317fd2c71f Mon Sep 17 00:00:00 2001 From: Alexander Date: Fri, 16 Feb 2024 17:33:46 +0100 Subject: [PATCH] =?UTF-8?q?feat:=20ensure=20that=20user=E2=80=99s=20correc?= =?UTF-8?q?tly=20pay=20the=20escrow=20for=20processing=20messages?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/IncentivizedMessageEscrow.sol | 41 +++++++++++++++++-- src/interfaces/IIncentivizedMessageEscrow.sol | 13 ++++++ .../processMessage/GasSpendControl.sol | 2 +- .../processMessage/NoReceive.t.sol | 2 +- .../processMessage/Reentry.call.t.sol | 2 +- .../processMessage/_handleCall.t.sol | 2 +- .../reemitAckMessage/ReemitAckMessage.t.sol | 19 +++++++++ test/TestCommon.t.sol | 4 +- 8 files changed, 75 insertions(+), 10 deletions(-) create mode 100644 test/IncentivizedMessageEscrow/reemitAckMessage/ReemitAckMessage.t.sol diff --git a/src/IncentivizedMessageEscrow.sol b/src/IncentivizedMessageEscrow.sol index 0682380..307ecec 100644 --- a/src/IncentivizedMessageEscrow.sol +++ b/src/IncentivizedMessageEscrow.sol @@ -1,6 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.13; +import { Address } from "openzeppelin/utils/Address.sol"; + import { IIncentivizedMessageEscrow } from "./interfaces/IIncentivizedMessageEscrow.sol"; import { ICrossChainReceiver } from "./interfaces/ICrossChainReceiver.sol"; import { Bytes65 } from "./utils/Bytes65.sol"; @@ -285,7 +287,7 @@ abstract contract IncentivizedMessageEscrow is IIncentivizedMessageEscrow, Bytes if (msg.value > sum) { // We know: msg.value >= sum, thus msg.value - sum >= 0. gasRefund = msg.value - sum; - payable(incentive.refundGasTo).transfer(gasRefund); + Address.sendValue(payable(incentive.refundGasTo), msg.value - uint256(gasRefund)); return (gasRefund, messageIdentifier); } } @@ -316,12 +318,13 @@ abstract contract IncentivizedMessageEscrow is IIncentivizedMessageEscrow, Bytes (bytes32 chainIdentifier, bytes memory implementationIdentifier, bytes calldata message) = _verifyPacket(messagingProtocolContext, rawMessage); // Figure out if this is a call or an ack. + uint128 cost = 0; bytes1 context = bytes1(message[0]); if (context == CTX_SOURCE_TO_DESTINATION) { bytes memory receiveAckWithContext = _handleMessage(chainIdentifier, implementationIdentifier, message, feeRecipient, gasLimit); // The cost management is made by _sendPacket so we don't have to check if enough gas has been provided. - _sendPacket(chainIdentifier, implementationIdentifier, receiveAckWithContext); + cost = _sendPacket(chainIdentifier, implementationIdentifier, receiveAckWithContext); } else if (context == CTX_DESTINATION_TO_SOURCE) { // Notice that sometimes ack actually handles deadlines which have been passed. // However, these are much different from "timeouts". @@ -338,6 +341,16 @@ abstract contract IncentivizedMessageEscrow is IIncentivizedMessageEscrow, Bytes } else { revert NotImplementedError(); } + + // Check if there is a mis-match between the cost and the value of the message. + if (uint128(msg.value) != cost) { + if (uint128(msg.value) > cost) { + // Send the unused gas back to the the user. + Address.sendValue(payable(msg.sender), msg.value - uint256(cost)); + } else { + revert NotEnoughGasProvided(uint128(msg.value), cost); + } + } } //--- Internal Functions ---// @@ -883,7 +896,17 @@ abstract contract IncentivizedMessageEscrow is IIncentivizedMessageEscrow, Bytes if (storedAckHash == bytes32(0) || storedAckHash != keccak256(receiveAckWithContext)) revert CannotRetryWrongMessage(storedAckHash, keccak256(receiveAckWithContext)); // Send the package again. - _sendPacket(sourceIdentifier, implementationIdentifier, receiveAckWithContext); + uint128 cost = _sendPacket(sourceIdentifier, implementationIdentifier, receiveAckWithContext); + + // Check if there is a mis-match between the cost and the value of the message. + if (uint128(msg.value) != cost) { + if (uint128(msg.value) > cost) { + // Send the unused gas back to the the user. + Address.sendValue(payable(msg.sender), msg.value - uint256(cost)); + } else { + revert NotEnoughGasProvided(uint128(msg.value), cost); + } + } } /** @@ -951,10 +974,20 @@ abstract contract IncentivizedMessageEscrow is IIncentivizedMessageEscrow, Bytes emit TimeoutInitiated(messageIdentifier); // Send the message - _sendPacket( + uint128 cost = _sendPacket( sourceIdentifier, destinationIncentives, receiveAckWithContext ); + + // Check if there is a mis-match between the cost and the value of the message. + if (uint128(msg.value) != cost) { + if (uint128(msg.value) > cost) { + // Send the unused gas back to the the user. + Address.sendValue(payable(msg.sender), msg.value - uint256(cost)); + } else { + revert NotEnoughGasProvided(uint128(msg.value), cost); + } + } } } diff --git a/src/interfaces/IIncentivizedMessageEscrow.sol b/src/interfaces/IIncentivizedMessageEscrow.sol index 444197d..aa06451 100644 --- a/src/interfaces/IIncentivizedMessageEscrow.sol +++ b/src/interfaces/IIncentivizedMessageEscrow.sol @@ -36,4 +36,17 @@ interface IIncentivizedMessageEscrow is IMessageEscrowStructs, IMessageEscrowErr * @return amount The number of assets to pay. */ function estimateAdditionalCost() external view returns(address asset, uint256 amount); + + function timeoutMessage( + bytes calldata destinationIncentives, + bytes32 sourceIdentifier, + uint256 originBlockNumber, + bytes calldata message + ) external payable; + + function reemitAckMessage( + bytes32 sourceIdentifier, + bytes calldata implementationIdentifier, + bytes calldata receiveAckWithContext + ) external payable; } \ No newline at end of file diff --git a/test/IncentivizedMessageEscrow/processMessage/GasSpendControl.sol b/test/IncentivizedMessageEscrow/processMessage/GasSpendControl.sol index 7578311..7b084a3 100644 --- a/test/IncentivizedMessageEscrow/processMessage/GasSpendControl.sol +++ b/test/IncentivizedMessageEscrow/processMessage/GasSpendControl.sol @@ -58,7 +58,7 @@ contract GasSpendControlTest is TestCommon { messageIdentifier, _DESTINATION_ADDRESS_APPLICATION, destinationFeeRecipient, - uint48(0x366e9), // Gas used + uint48(0x36708), // Gas used uint64(1), bytes1(0xff), // This states that the call went wrong. message diff --git a/test/IncentivizedMessageEscrow/processMessage/NoReceive.t.sol b/test/IncentivizedMessageEscrow/processMessage/NoReceive.t.sol index dd0a54e..422ea1a 100644 --- a/test/IncentivizedMessageEscrow/processMessage/NoReceive.t.sol +++ b/test/IncentivizedMessageEscrow/processMessage/NoReceive.t.sol @@ -54,7 +54,7 @@ contract processPacketNoReceiveTest is TestCommon { messageIdentifier, _DESTINATION_ADDRESS_THIS, feeRecipient, - uint48(0x80bc), // Gas used + uint48(0x80db), // Gas used uint64(1), abi.encodePacked(bytes1(0xff)), message diff --git a/test/IncentivizedMessageEscrow/processMessage/Reentry.call.t.sol b/test/IncentivizedMessageEscrow/processMessage/Reentry.call.t.sol index edcad4c..f19503e 100644 --- a/test/IncentivizedMessageEscrow/processMessage/Reentry.call.t.sol +++ b/test/IncentivizedMessageEscrow/processMessage/Reentry.call.t.sol @@ -61,7 +61,7 @@ contract CallReentryTest is TestCommon, ICrossChainReceiver { messageIdentifier, _DESTINATION_ADDRESS_APPLICATION, feeRecipient, - uint48(0xf8dd), // Gas used + uint48(0xf8f3), // Gas used uint64(1), uint8(1) ) diff --git a/test/IncentivizedMessageEscrow/processMessage/_handleCall.t.sol b/test/IncentivizedMessageEscrow/processMessage/_handleCall.t.sol index 09b95f6..f1f0814 100644 --- a/test/IncentivizedMessageEscrow/processMessage/_handleCall.t.sol +++ b/test/IncentivizedMessageEscrow/processMessage/_handleCall.t.sol @@ -40,7 +40,7 @@ contract processPacketCallTest is TestCommon { messageIdentifier, _DESTINATION_ADDRESS_APPLICATION, feeRecipient, - uint48(0x7af8), // Gas used + uint48(0x7b03), // Gas used uint64(1), mockAck ) diff --git a/test/IncentivizedMessageEscrow/reemitAckMessage/ReemitAckMessage.t.sol b/test/IncentivizedMessageEscrow/reemitAckMessage/ReemitAckMessage.t.sol new file mode 100644 index 0000000..c007a80 --- /dev/null +++ b/test/IncentivizedMessageEscrow/reemitAckMessage/ReemitAckMessage.t.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import "forge-std/Test.sol"; +import { TestCommon } from "../../TestCommon.t.sol"; + + +contract ReemitAckMessageTest is TestCommon { + + function test_ack_process_message() public { + bytes memory message = _MESSAGE; + bytes32 feeRecipient = bytes32(uint256(uint160(address(this)))); + + bytes32 destinationFeeRecipient = bytes32(uint256(uint160(address(this)))); + + (, bytes memory messageWithContext) = setupForAck(address(application), message, destinationFeeRecipient); + + } +} \ No newline at end of file diff --git a/test/TestCommon.t.sol b/test/TestCommon.t.sol index 4142b0b..484ab4e 100644 --- a/test/TestCommon.t.sol +++ b/test/TestCommon.t.sol @@ -21,8 +21,8 @@ interface ICansubmitMessage is IMessageEscrowStructs{ contract TestCommon is Test, IMessageEscrowEvents, IMessageEscrowStructs { - uint256 constant GAS_SPENT_ON_SOURCE = 6583; - uint256 constant GAS_SPENT_ON_DESTINATION = 31480; + uint256 constant GAS_SPENT_ON_SOURCE = 6586; + uint256 constant GAS_SPENT_ON_DESTINATION = 31491; bytes32 constant _DESTINATION_IDENTIFIER = bytes32(uint256(0x123123) + uint256(2**255));