-
Notifications
You must be signed in to change notification settings - Fork 10
Test MessageRelayer #142
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
base: main
Are you sure you want to change the base?
Test MessageRelayer #142
Changes from 27 commits
b8d8acd
a60d464
d2aed97
6b84dba
6205519
c67ec39
9074f4d
fa42d46
cc68261
f0ee2db
ffd4e33
ac4471c
a3de214
bd61c84
443b9e2
f781997
5347332
521fd25
4608dc8
28fb7bd
d093a79
0800551
0b1c6e7
3b60423
df1457e
9fb7b06
a0fed52
240c6d9
4794e23
7e30320
bac25c5
d77e2ae
0c38142
9ff57d2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| // SPDX-License-Identifier: MIT | ||
| pragma solidity ^0.8.28; | ||
|
|
||
| import {GenericRecipient} from "./GenericRecipient.t.sol"; | ||
| import {InitialState} from "./InitialState.t.sol"; | ||
|
|
||
| import {IETHBridge} from "src/protocol/IETHBridge.sol"; | ||
| import {IMessageRelayer} from "src/protocol/IMessageRelayer.sol"; | ||
|
|
||
| // This is a concrete class because if we are not using the MessageRelayer, | ||
| // we do not need to investigate any other properties of the message | ||
| contract DepositRecipientIsNotMessageRelayer is InitialState { | ||
| function setUp() public override { | ||
| super.setUp(); | ||
| // bypass the relayer and send the message directly to the recipient | ||
| // do not bother changing the default message encoding (to a `receiveMessage` function) | ||
| // because the recipient handles any message | ||
| ethDeposit.to = address(to); | ||
| } | ||
|
|
||
| function test_DepositRecipientIsNotMessageRelayer_relayMessage_shouldInvokeRecipient() public { | ||
| vm.expectEmit(); | ||
| emit GenericRecipient.FunctionCalled(); | ||
| _relayMessage(); | ||
| } | ||
|
|
||
| function test_DepositRecipientIsNotMessageRelayer_relayMessage_shouldNotInvokeReceiveMessage() public { | ||
| vm.expectCall(address(messageRelayer), ethDeposit.data, 0); | ||
| _relayMessage(); | ||
| } | ||
| } | ||
|
|
||
| abstract contract DepositRecipientIsMessageRelayer is InitialState { | ||
| function test_DepositRecipientIsMessageRelayer_relayMessage_shouldInvokeReceiveMessage() public ifRelaySucceeds { | ||
| vm.expectCall(address(messageRelayer), ethDeposit.data); | ||
| _relayMessage(); | ||
| } | ||
|
|
||
| function test_DepositRecipientIsMessageRelayer_claimDeposit_shouldInvokeReceiveMessage() public ifClaimSucceeds { | ||
| vm.expectCall(address(messageRelayer), ethDeposit.data); | ||
| _claimDeposit(); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| // SPDX-License-Identifier: MIT | ||
| pragma solidity ^0.8.28; | ||
|
|
||
| import {GenericRecipient} from "./GenericRecipient.t.sol"; | ||
| import {ValidUserTipRecipientOverrulesRelayer} from "./TipRecipientScenarios.t.sol"; | ||
| import {IMessageRelayer} from "src/protocol/IMessageRelayer.sol"; | ||
|
|
||
| import {InitialState} from "./InitialState.t.sol"; | ||
| import {IETHBridge} from "src/protocol/IETHBridge.sol"; | ||
|
|
||
| // Use ValidUserTipRecipientOverrulesRelayer as the default scenario. | ||
| // Any valid tip arrangement should suffice for these tests. | ||
| abstract contract FundAmountScenarios is ValidUserTipRecipientOverrulesRelayer { | ||
| function test_FundAmountScenarios_relayMessage_shouldInvokeRecipient() public ifRelaySucceeds { | ||
| vm.expectEmit(); | ||
| emit GenericRecipient.FunctionCalled(); | ||
| _relayMessage(); | ||
| } | ||
|
|
||
| function test_FundAmountScenarios_relayMessage_shouldNotRetainFundsInRelayer() public ifRelaySucceeds { | ||
| assertEq(address(messageRelayer).balance, 0, "relayer should not have funds"); | ||
| _relayMessage(); | ||
| assertEq(address(messageRelayer).balance, 0, "relayer should not retain funds"); | ||
| } | ||
|
|
||
| function test_FundAmountScenarios_relayMessage_shouldSendAmountToRecipient() public ifRelaySucceeds { | ||
| uint256 balanceBefore = address(to).balance; | ||
| uint256 transferAmount = ethDeposit.amount - tip; | ||
| _relayMessage(); | ||
| assertEq(address(to).balance, balanceBefore + transferAmount, "recipient balance mismatch"); | ||
| } | ||
|
|
||
| function redundant_FundAmountScenarios_relayMessage_shouldSendTipToRecipient() public { | ||
| // This test (if it were implemented) would be redundant with the tip recipient scenarios | ||
| // It is included for completeness, so this file accounts for all the distributed funds | ||
| } | ||
| } | ||
|
|
||
| contract AmountExceedsTip is FundAmountScenarios {} | ||
|
|
||
| contract NoAmountNoTip is FundAmountScenarios { | ||
| function setUp() public override { | ||
| super.setUp(); | ||
| ethDeposit.amount = 0; | ||
| tip = 0; | ||
| _encodeReceiveCall(); | ||
| } | ||
| } | ||
|
|
||
| contract NoAmountNonzeroTip is FundAmountScenarios { | ||
| function setUp() public override { | ||
| super.setUp(); | ||
| ethDeposit.amount = 0; | ||
| relayShouldSucceed = false; | ||
| claimShouldSucceed = false; | ||
| } | ||
| } | ||
|
|
||
| contract AmountLessThanTip is FundAmountScenarios { | ||
| function setUp() public override { | ||
| super.setUp(); | ||
| ethDeposit.amount = tip - 1 wei; | ||
| relayShouldSucceed = false; | ||
| claimShouldSucceed = false; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,58 @@ | ||
| // SPDX-License-Identifier: MIT | ||
| pragma solidity ^0.8.28; | ||
|
|
||
| import {AmountExceedsTip} from "./FundAmountScenarios.t.sol"; | ||
| import {GenericRecipient} from "./GenericRecipient.t.sol"; | ||
|
|
||
| import {InitialState} from "./InitialState.t.sol"; | ||
| import {IETHBridge} from "src/protocol/IETHBridge.sol"; | ||
| import {IMessageRelayer} from "src/protocol/IMessageRelayer.sol"; | ||
|
|
||
| // Found by experimentation | ||
| uint256 constant OOG_INSIDE_RECIPIENT = 60_000; | ||
|
|
||
| // Use AmountExceedsTip as the default scenario. | ||
| // Any valid tip arrangement and funding amount should suffice for these tests. | ||
| abstract contract GasLimitScenarios is AmountExceedsTip {} | ||
|
|
||
| contract NoGasLimit_SufficientGasProvided is GasLimitScenarios {} | ||
|
|
||
| contract NoGasLimit_InsufficientGasProvided is GasLimitScenarios { | ||
| function setUp() public override { | ||
| super.setUp(); | ||
| gasProvidedWithCall = OOG_INSIDE_RECIPIENT; | ||
| relayShouldSucceed = false; | ||
| claimShouldSucceed = false; | ||
| } | ||
| } | ||
|
|
||
| contract SufficientGasLimit_SufficientGasProvided is GasLimitScenarios { | ||
| function setUp() public override { | ||
| super.setUp(); | ||
| gasLimit = to.GAS_REQUIRED() + 100; | ||
| _encodeReceiveCall(); | ||
| } | ||
| } | ||
|
|
||
| contract SufficientGasLimit_InsufficientGasProvided is GasLimitScenarios { | ||
| function setUp() public override { | ||
| super.setUp(); | ||
| gasLimit = to.GAS_REQUIRED() + 100; | ||
| _encodeReceiveCall(); | ||
| gasProvidedWithCall = OOG_INSIDE_RECIPIENT; | ||
| relayShouldSucceed = false; | ||
| claimShouldSucceed = false; | ||
| } | ||
| } | ||
|
|
||
| contract InsufficientGasLimit_SufficientGasProvided is GasLimitScenarios { | ||
| function setUp() public override { | ||
| super.setUp(); | ||
| // the amount forwarded to the recipient is slightly higher than gasLimit so deduct 150 as compensation | ||
| // TODO: understand why this is necessary | ||
| gasLimit = to.GAS_REQUIRED() - 150; | ||
| _encodeReceiveCall(); | ||
| relayShouldSucceed = false; | ||
| claimShouldSucceed = false; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,53 @@ | ||||||||||||||||||||||||
| // SPDX-License-Identifier: MIT | ||||||||||||||||||||||||
| pragma solidity ^0.8.28; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| import {IMessageRelayer} from "src/protocol/IMessageRelayer.sol"; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| interface IGenericRecipient { | ||||||||||||||||||||||||
| function setSuccess(bool _callWillSucceed) external; | ||||||||||||||||||||||||
| function setReentrancyAttack(bool _shouldAttack) external; | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| contract GenericRecipient is IGenericRecipient { | ||||||||||||||||||||||||
| bool private callWillSucceed = true; | ||||||||||||||||||||||||
| bool private shouldReenterAttack = false; | ||||||||||||||||||||||||
| address private relayer; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| // Consume a minimum amount of gas so we can test gas limits | ||||||||||||||||||||||||
| uint256 public constant GAS_REQUIRED = 20_000; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| error CallFailed(); | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| event FunctionCalled(); | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| constructor(address _relayer) { | ||||||||||||||||||||||||
| relayer = _relayer; | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| function setSuccess(bool _callWillSucceed) external { | ||||||||||||||||||||||||
| callWillSucceed = _callWillSucceed; | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| function setReentrancyAttack(bool _shouldAttack) external { | ||||||||||||||||||||||||
| shouldReenterAttack = _shouldAttack; | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| fallback() external payable { | ||||||||||||||||||||||||
| _simulateFunctionCall(); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| receive() external payable { | ||||||||||||||||||||||||
| _simulateFunctionCall(); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| function _simulateFunctionCall() internal { | ||||||||||||||||||||||||
| require(callWillSucceed, CallFailed()); | ||||||||||||||||||||||||
| require(gasleft() >= GAS_REQUIRED, "Insufficient gas"); | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| emit FunctionCalled(); | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| if (shouldReenterAttack) { | ||||||||||||||||||||||||
| IMessageRelayer(relayer).receiveMessage(address(this), 0, address(this), 0, "0x"); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
Comment on lines
+23
to
+52
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix syntax error in require statement. The Apply this diff to fix the syntax error: function _simulateFunctionCall() internal {
- require(callWillSucceed, CallFailed());
+ if (!callWillSucceed) {
+ revert CallFailed();
+ }
emit FunctionCalled();
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.