Last active
May 26, 2024 15:40
-
-
Save 0xmp/4da10ab59c04f71cdb9557ff320f9fc2 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: MIT | |
pragma solidity 0.8.24; | |
import "forge-std/src/Test.sol"; | |
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; | |
import "../../contracts/bridge/Bridge.sol"; | |
import "../../contracts/bridge/QuotaManager.sol"; | |
// `forge test --mt test_MPBridgeCallGas -vvvv --fork-url 127.0.0.1:8545 --isolate` | |
// From the traces, we can see that onMessageInvocation runs out of gas, consuming "only" 2991360 instead of the intended 3M gas | |
contract DummyTarget { | |
function onMessageInvocation(bytes calldata _data) external payable { | |
uint256 gasBefore = gasleft(); | |
uint256 toConsume = abi.decode(_data, (uint256)); | |
while (gasBefore - gasleft() < toConsume - 200) {} // Waste up to `toConsume - 200` gas | |
} | |
receive() external payable {} | |
} | |
contract TestBridge is Test { | |
using LibMath for uint256; | |
Bridge public bridge; | |
QuotaManager public quotaManager; | |
DummyTarget public dummyTarget; | |
function setUp() public { | |
bridge = new Bridge(); | |
ERC1967Proxy proxy = new ERC1967Proxy(address(bridge), abi.encodeCall(Bridge.init, (address(this), address(this)))); | |
bridge = Bridge(payable(address(proxy))); | |
quotaManager = new QuotaManager(); | |
proxy = new ERC1967Proxy(address(quotaManager), abi.encodeCall(QuotaManager.init, (address(this), address(this), 24 * 3600))); | |
quotaManager = QuotaManager(address(proxy)); | |
quotaManager.updateQuota(address(0), 1000 ether); | |
dummyTarget = new DummyTarget(); | |
vm.label(address(bridge), "Bridge"); | |
vm.label(address(quotaManager), "QuotaManager"); | |
vm.label(address(dummyTarget), "DummyTarget"); | |
vm.deal(address(bridge), 100 ether); | |
vm.deal(address(this), 1 ether); | |
} | |
function test_MPBridgeCallGas() public { | |
uint24 gasInnerCall = 3e6; // 3M gas required in tx | |
bytes memory data = abi.encodeCall(DummyTarget.onMessageInvocation, (abi.encode(gasInnerCall, ""))); | |
console2.log("Gas to be consumed in inner call is %s", gasInnerCall); | |
IBridge.Message memory message = IBridge.Message({ | |
id: 0, | |
fee: 1 wei, | |
gasLimit: uint32(gasInnerCall + 800_000 + (data.length + 256) / 16), | |
from: address(32), | |
srcChainId: 2, | |
srcOwner: address(48), | |
destChainId: 1, | |
destOwner: address(dummyTarget), // Not address(this) otherwise msg.gasLimit == gasleft() | |
to: address(dummyTarget), | |
value: 1 wei, | |
data: data | |
}); | |
bridge.processMessage{gas: gasInnerCall + 155_000}(message, ""); // 155_000 has been chosen so that 63/64 * gasleft() is <= msg.gasLimit in external call | |
} | |
function getAddress(uint64 _chainId, bytes32 _name) external view returns (address) { // this is AddressManager for simplicity | |
if (_name == bytes32("signal_service") && _chainId == block.chainid) return address(this); | |
if (_name == bytes32("signal_service")) return address(17); | |
else if (_name == bytes32("bridge") && _chainId == block.chainid) return address(bridge); | |
else if (_name == bytes32("bridge")) return address(34); | |
else if (_name == bytes32("quota_manager") && _chainId == block.chainid) return address(quotaManager); | |
else if (_name == bytes32("erc20_vault") && _chainId == block.chainid) return address(51); | |
else console2.log("Unknown resolved address: %s", string(abi.encodePacked(_name))); | |
revert("Get address failed."); | |
} | |
function proveSignalReceived( // this is SignalService for simplicity | |
uint64 _chainId, | |
address _app, | |
bytes32 _signal, | |
bytes calldata _proof | |
) | |
external | |
virtual | |
returns (uint256 numCacheOps_) | |
{ | |
return 0; | |
} | |
receive() external payable {} // To receive the fee for processing the message | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment