diff --git a/src/testing/ZkEVMDomain.sol b/src/testing/ZkEVMDomain.sol deleted file mode 100644 index dc851cf..0000000 --- a/src/testing/ZkEVMDomain.sol +++ /dev/null @@ -1,112 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-or-later -pragma solidity >=0.8.0; - -import { StdChains } from "forge-std/StdChains.sol"; -import { Vm } from "forge-std/Vm.sol"; - -import { Domain, BridgedDomain } from "./BridgedDomain.sol"; -import { RecordedLogs } from "./RecordedLogs.sol"; - -interface IBridgeMessageReceiver { - function onMessageReceived(address originAddress, uint32 originNetwork, bytes memory data) external payable; -} - -interface IZkEVMBridgeLike { - function bridgeMessage( - uint32 destinationNetwork, - address destinationAddress, - bool forceUpdateGlobalExitRoot, - bytes calldata metadata - ) external payable; -} - -contract ZkEVMDomain is BridgedDomain { - IZkEVMBridgeLike public L1_MESSENGER; - - bytes32 constant BRIDGE_EVENT_TOPIC = - keccak256("BridgeEvent(uint8,uint32,address,uint32,address,uint256,bytes,uint32)"); - - uint256 internal lastFromHostLogIndex; - uint256 internal lastToHostLogIndex; - - constructor(StdChains.Chain memory _chain, Domain _hostDomain) Domain(_chain) BridgedDomain(_hostDomain) { - bytes32 name = keccak256(bytes(_chain.chainAlias)); - if (name == keccak256("zkevm")) { - L1_MESSENGER = IZkEVMBridgeLike(0x2a3DD3EB832aF982ec71669E178424b10Dca2EDe); - } else { - revert("Unsupported chain"); - } - vm.recordLogs(); - } - - function relayFromHost(bool switchToGuest) external override { - selectFork(); - - // Read all L1 -> L2 messages and relay them under zkevm fork - Vm.Log[] memory logs = RecordedLogs.getLogs(); - for (; lastFromHostLogIndex < logs.length; lastFromHostLogIndex++) { - Vm.Log memory log = logs[lastFromHostLogIndex]; - if (_isBridgeMessageEvent(log, true)) _claimMessage(log); - } - - if (!switchToGuest) { - hostDomain.selectFork(); - } - } - - function relayToHost(bool switchToHost) external override { - hostDomain.selectFork(); - - // Read all L2 -> L1 messages and relay them under Primary fork - Vm.Log[] memory logs = RecordedLogs.getLogs(); - for (; lastToHostLogIndex < logs.length; lastToHostLogIndex++) { - Vm.Log memory log = logs[lastToHostLogIndex]; - if (_isBridgeMessageEvent(log, false)) _claimMessage(log); - } - - if (!switchToHost) { - selectFork(); - } - } - - function _isBridgeMessageEvent(Vm.Log memory log, bool host) internal view returns (bool) { - // early return to prevent abi decode errors - if (log.topics[0] != BRIDGE_EVENT_TOPIC) return false; - - (uint8 messageType, uint32 originNetwork,,,,,,) = - abi.decode(log.data, (uint8, uint32, address, uint32, address, uint256, bytes, uint32)); - return - log.emitter == address(L1_MESSENGER) && messageType == 1 && (host ? originNetwork == 0 : originNetwork == 1); - } - - function _claimMessage(Vm.Log memory log) internal { - ( - /* uint8 messageType */ - , - uint32 originNetwork, - address originAddress, - /* uint32 destinationNetwork */ - , - address destinationAddress, - uint256 msgValue, - bytes memory metadata, - /* uint32 depositCount */ - ) = abi.decode(log.data, (uint8, uint32, address, uint32, address, uint256, bytes, uint32)); - - // mock bridged eth balance increase - uint256 prevBalance = address(L1_MESSENGER).balance; - vm.deal(address(L1_MESSENGER), prevBalance + msgValue); - - // mock bridge callback - // ref: https://github.com/0xPolygonHermez/zkevm-contracts/blob/main/contracts/PolygonZkEVMBridge.sol#L455-L465 - vm.prank(address(L1_MESSENGER)); - (bool success, bytes memory response) = destinationAddress.call{value: msgValue}( - abi.encodeCall(IBridgeMessageReceiver.onMessageReceived, (originAddress, originNetwork, metadata)) - ); - if (!success) { - assembly { - revert(add(response, 32), mload(response)) - } - } - } -} diff --git a/src/testing/bridges/AMBBridgeTesting.sol b/src/testing/bridges/AMBBridgeTesting.sol new file mode 100644 index 0000000..1293bba --- /dev/null +++ b/src/testing/bridges/AMBBridgeTesting.sol @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later +pragma solidity >=0.8.0; + +import { Vm } from "forge-std/Vm.sol"; + +import { RecordedLogs } from "src/testing/utils/RecordedLogs.sol"; +import { BridgeData } from "./BridgeData.sol"; + +library AMBBridgeTesting { + + using DomainHelpers for *; + + Vm private constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code"))))); + + function createBridge(Domain memory source, Domain memory destination) internal returns (BridgeData memory bridge) { + return init(BridgeData({ + source: ethereum, + destination: arbitrumInstance, + sourceCrossChainMessenger: _getMessengerFromChainAlias(source.chain.chainAlias), + destinationCrossChainMessenger: _getMessengerFromChainAlias(destination.chain.chainAlias), + lastSourceLogIndex: 0, + lastDestinationLogIndex: 0, + extraData: "" + })); + } + + function getMessengerFromChainAlias(string memory chainAlias) internal pure returns (address) { + bytes32 name = keccak256(bytes(chainAlias)); + if (name == keccak256("mainnet")) { + return 0x0a992d191DEeC32aFe36203Ad87D7d289a738F81; + } else if (name == keccak256("avalanche")) { + return 0x8186359aF5F57FbB40c6b14A588d2A59C0C29880; + } else if (name == keccak256("optimism")) { + return 0x4D41f22c5a0e5c74090899E5a8Fb597a8842b3e8; + } else if (name == keccak256("arbitrum_one")) { + return 0xC30362313FBBA5cf9163F0bb16a0e01f01A896ca; + } else if (name == keccak256("base")) { + return 0xAD09780d193884d503182aD4588450C416D6F9D4; + } else if (name == keccak256("polygon")) { + return 0xF3be9355363857F3e001be68856A2f96b4C39Ba9; + } else { + revert("Unsupported chain"); + } + } + + function init(BridgeData memory bridge) internal returns (BridgeData memory bridge) { + + } + + function relayMessagesToDestination(BridgeData memory bridge, bool switchToDestinationFork) internal { + + } + + function relayMessagesToSource(BridgeData memory bridge, bool switchToSourceFork) internal { + + } + +} diff --git a/src/testing/bridges/ArbitrumBridgeTesting.sol b/src/testing/bridges/ArbitrumBridgeTesting.sol index b0ffffb..9935c53 100644 --- a/src/testing/bridges/ArbitrumBridgeTesting.sol +++ b/src/testing/bridges/ArbitrumBridgeTesting.sol @@ -44,6 +44,7 @@ contract ArbSysOverride { library ArbitrumBridgeTesting { using DomainHelpers for *; + using RecordedLogs for *; Vm private constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code"))))); @@ -123,11 +124,11 @@ library ArbitrumBridgeTesting { // Read all L1 -> L2 messages and relay them under Arbitrum fork Vm.Log[] memory logs = RecordedLogs.getLogs(); - for (; lastFromHostLogIndex < logs.length; lastFromHostLogIndex++) { - Vm.Log memory log = logs[lastFromHostLogIndex]; + for (; bridge.lastSourceLogIndex < logs.length; bridge.lastSourceLogIndex++) { + Vm.Log memory log = logs[bridge.lastSourceLogIndex]; if (log.topics[0] == MESSAGE_DELIVERED_TOPIC) { // We need both the current event and the one that follows for all the relevant data - Vm.Log memory logWithData = logs[lastFromHostLogIndex + 1]; + Vm.Log memory logWithData = logs[bridge.lastSourceLogIndex + 1]; (,, address sender,,,) = abi.decode(log.data, (address, uint8, address, bytes32, uint256, uint64)); (address target, bytes memory message) = _parseData(logWithData.data); vm.startPrank(sender); @@ -150,17 +151,15 @@ library ArbitrumBridgeTesting { bridge.source.selectFork(); // Read all L2 -> L1 messages and relay them under host fork - Vm.Log[] memory logs = RecordedLogs.getLogs(); - for (; lastToHostLogIndex < logs.length; lastToHostLogIndex++) { - Vm.Log memory log = logs[lastToHostLogIndex]; - if (log.topics[0] == SEND_TO_L1_TOPIC) { - (address sender, address target, bytes memory message) = abi.decode(log.data, (address, address, bytes)); - //l2ToL1Sender = sender; - (bool success, bytes memory response) = InboxLike(data.sourceCrossChainMessenger).bridge().executeCall(target, 0, message); - if (!success) { - assembly { - revert(add(response, 32), mload(response)) - } + Vm.Log[] memory logs = bridge.ingestAndFilterLogs(false, SEND_TO_L1_TOPIC, address(0)); + for (uint256 i = 0; i < logs.length; i++) { + Vm.Log memory log = logs[i]; + (address sender, address target, bytes memory message) = abi.decode(log.data, (address, address, bytes)); + //l2ToL1Sender = sender; + (bool success, bytes memory response) = InboxLike(data.sourceCrossChainMessenger).bridge().executeCall(target, 0, message); + if (!success) { + assembly { + revert(add(response, 32), mload(response)) } } } diff --git a/src/testing/bridges/CCTPBridge.sol b/src/testing/bridges/CCTPBridge.sol deleted file mode 100644 index e3e6b49..0000000 --- a/src/testing/bridges/CCTPBridge.sol +++ /dev/null @@ -1,106 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-or-later -pragma solidity >=0.8.0; - -import { StdChains } from "forge-std/StdChains.sol"; -import { Vm } from "forge-std/Vm.sol"; - -import { Domain, BridgedDomain } from "./BridgedDomain.sol"; -import { RecordedLogs } from "./RecordedLogs.sol"; - -interface MessengerLike { - function receiveMessage(bytes calldata message, bytes calldata attestation) external returns (bool success); -} - -contract CCTPBridge is IUnidirectionalBridge { - - bytes32 private constant SENT_MESSAGE_TOPIC = keccak256("MessageSent(bytes)"); - - MessengerLike public SOURCE_MESSENGER; - MessengerLike public DESTINATION_MESSENGER; - - uint256 internal lastFromHostLogIndex; - uint256 internal lastToHostLogIndex; - - constructor(StdChains.Chain memory _chain, Domain _hostDomain) Domain(_chain) BridgedDomain(_hostDomain) { - SOURCE_MESSENGER = MessengerLike(_getMessengerFromChainAlias(_hostDomain.details().chainAlias)); - DESTINATION_MESSENGER = MessengerLike(_getMessengerFromChainAlias(_chain.chainAlias)); - - // Set minimum required signatures to zero for both domains - selectFork(); - vm.store( - address(DESTINATION_MESSENGER), - bytes32(uint256(4)), - 0 - ); - hostDomain.selectFork(); - vm.store( - address(SOURCE_MESSENGER), - bytes32(uint256(4)), - 0 - ); - - vm.recordLogs(); - } - - function _getMessengerFromChainAlias(string memory chainAlias) internal pure returns (address) { - bytes32 name = keccak256(bytes(chainAlias)); - if (name == keccak256("mainnet")) { - return 0x0a992d191DEeC32aFe36203Ad87D7d289a738F81; - } else if (name == keccak256("avalanche")) { - return 0x8186359aF5F57FbB40c6b14A588d2A59C0C29880; - } else if (name == keccak256("optimism")) { - return 0x4D41f22c5a0e5c74090899E5a8Fb597a8842b3e8; - } else if (name == keccak256("arbitrum_one")) { - return 0xC30362313FBBA5cf9163F0bb16a0e01f01A896ca; - } else if (name == keccak256("base")) { - return 0xAD09780d193884d503182aD4588450C416D6F9D4; - } else if (name == keccak256("polygon")) { - return 0xF3be9355363857F3e001be68856A2f96b4C39Ba9; - } else { - revert("Unsupported chain"); - } - } - - function relayFromHost(bool switchToGuest) external override { - selectFork(); - - // Read all L1 -> L2 messages and relay them under CCTP fork - Vm.Log[] memory logs = RecordedLogs.getLogs(); - for (; lastFromHostLogIndex < logs.length; lastFromHostLogIndex++) { - Vm.Log memory log = logs[lastFromHostLogIndex]; - if (log.topics[0] == SENT_MESSAGE_TOPIC && log.emitter == address(SOURCE_MESSENGER)) { - DESTINATION_MESSENGER.receiveMessage(removeFirst64Bytes(log.data), ""); - } - } - - if (!switchToGuest) { - hostDomain.selectFork(); - } - } - - function relayToHost(bool switchToHost) external override { - hostDomain.selectFork(); - - // Read all L2 -> L1 messages and relay them under host fork - Vm.Log[] memory logs = RecordedLogs.getLogs(); - for (; lastToHostLogIndex < logs.length; lastToHostLogIndex++) { - Vm.Log memory log = logs[lastToHostLogIndex]; - if (log.topics[0] == SENT_MESSAGE_TOPIC && log.emitter == address(DESTINATION_MESSENGER)) { - SOURCE_MESSENGER.receiveMessage(removeFirst64Bytes(log.data), ""); - } - } - - if (!switchToHost) { - selectFork(); - } - } - - function removeFirst64Bytes(bytes memory inputData) public pure returns (bytes memory) { - bytes memory returnValue = new bytes(inputData.length - 64); - for (uint256 i = 0; i < inputData.length - 64; i++) { - returnValue[i] = inputData[i + 64]; - } - return returnValue; - } - -} diff --git a/src/testing/bridges/CCTPBridgeTesting.sol b/src/testing/bridges/CCTPBridgeTesting.sol index 3b0c86f..65e531e 100644 --- a/src/testing/bridges/CCTPBridgeTesting.sol +++ b/src/testing/bridges/CCTPBridgeTesting.sol @@ -6,25 +6,27 @@ import { Vm } from "forge-std/Vm.sol"; import { RecordedLogs } from "src/testing/utils/RecordedLogs.sol"; import { BridgeData } from "./BridgeData.sol"; -library ArbitrumBridgeTesting { +library CCTPBridgeTesting { + + bytes32 private constant SENT_MESSAGE_TOPIC = keccak256("MessageSent(bytes)"); using DomainHelpers for *; Vm private constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code"))))); - function createBridge(Domain memory source, Domain memory destination) internal returns (BridgeData memory bridge) { + function createCircleBridge(Domain memory source, Domain memory destination) internal returns (BridgeData memory bridge) { return init(BridgeData({ source: ethereum, destination: arbitrumInstance, - sourceCrossChainMessenger: _getMessengerFromChainAlias(source.chain.chainAlias), - destinationCrossChainMessenger: _getMessengerFromChainAlias(destination.chain.chainAlias), + sourceCrossChainMessenger: getCircleMessengerFromChainAlias(source.chain.chainAlias), + destinationCrossChainMessenger: getCircleMessengerFromChainAlias(destination.chain.chainAlias), lastSourceLogIndex: 0, lastDestinationLogIndex: 0, extraData: "" })); } - function getMessengerFromChainAlias(string memory chainAlias) internal pure returns (address) { + function getCircleMessengerFromChainAlias(string memory chainAlias) internal pure returns (address) { bytes32 name = keccak256(bytes(chainAlias)); if (name == keccak256("mainnet")) { return 0x0a992d191DEeC32aFe36203Ad87D7d289a738F81; @@ -44,15 +46,47 @@ library ArbitrumBridgeTesting { } function init(BridgeData memory bridge) internal returns (BridgeData memory bridge) { - + // Set minimum required signatures to zero for both domains + bridge.destination.selectFork(); + vm.store( + bridge.destinationCrossChainMessenger, + bytes32(uint256(4)), + 0 + ); + bridge.source.selectFork(); + vm.store( + bridge.sourceCrossChainMessenger, + bytes32(uint256(4)), + 0 + ); + + vm.recordLogs(); } function relayMessagesToDestination(BridgeData memory bridge, bool switchToDestinationFork) internal { - + bridge.destination.selectFork(); + + Vm.Log[] memory logs = bridge.ingestAndFilterLogs(true, SENT_MESSAGE_TOPIC, address(0)); + for (uint256 i = 0; i < logs.length; i++) { + bridge.destinationCrossChainMessenger.receiveMessage(abi.decode(logs[i].data, (bytes)), ""); + } + + if (!switchToDestinationFork) { + bridge.source.selectFork(); + } } function relayMessagesToSource(BridgeData memory bridge, bool switchToSourceFork) internal { + bridge.source.selectFork(); + Vm.Log[] memory logs = bridge.ingestAndFilterLogs(false, SENT_MESSAGE_TOPIC, address(0)); + for (uint256 i = 0; i < logs.length; i++) { + bridge.sourceCrossChainMessenger.receiveMessage(abi.decode(logs[i].data, (bytes)), ""); + } + + if (!switchToSourceFork) { + bridge.destination.selectFork(); + } } } diff --git a/src/testing/bridges/OptimismBridgeTesting.sol b/src/testing/bridges/OptimismBridgeTesting.sol new file mode 100644 index 0000000..a3547bb --- /dev/null +++ b/src/testing/bridges/OptimismBridgeTesting.sol @@ -0,0 +1,183 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later +pragma solidity >=0.8.0; + +import { Vm } from "forge-std/Vm.sol"; + +import { RecordedLogs } from "src/testing/utils/RecordedLogs.sol"; +import { BridgeData } from "./BridgeData.sol"; + +interface InboxLike { + function createRetryableTicket( + address destAddr, + uint256 arbTxCallValue, + uint256 maxSubmissionCost, + address submissionRefundAddress, + address valueRefundAddress, + uint256 maxGas, + uint256 gasPriceBid, + bytes calldata data + ) external payable returns (uint256); + function bridge() external view returns (BridgeLike); +} + +interface BridgeLike { + function rollup() external view returns (address); + function executeCall( + address, + uint256, + bytes calldata + ) external returns (bool, bytes memory); + function setOutbox(address, bool) external; +} + +contract ArbSysOverride { + + event SendTxToL1(address sender, address target, bytes data); + + function sendTxToL1(address target, bytes calldata message) external payable returns (uint256) { + emit SendTxToL1(msg.sender, target, message); + return 0; + } + +} + +library OptimismBridgeTesting { + + using DomainHelpers for *; + + Vm private constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code"))))); + + bytes32 private constant MESSAGE_DELIVERED_TOPIC = keccak256("MessageDelivered(uint256,bytes32,address,uint8,address,bytes32,uint256,uint64)"); + bytes32 private constant SEND_TO_L1_TOPIC = keccak256("SendTxToL1(address,address,bytes)"); + + function createNativeBridge(Domain memory ethereum, Domain memory arbitrumInstance) internal returns (BridgeData memory bridge) { + ( + address sourceCrossChainMessenger, + address destinationCrossChainMessenger + ) = getMessengerFromChainAlias(ethereum.chain.chainAlias, arbitrumInstance.chain.chainAlias) + + return init(BridgeData({ + source: ethereum, + destination: arbitrumInstance, + sourceCrossChainMessenger: sourceCrossChainMessenger, + destinationCrossChainMessenger: destinationCrossChainMessenger, + lastSourceLogIndex: 0, + lastDestinationLogIndex: 0, + extraData: "" + })); + } + + function getMessengerFromChainAlias( + string memory sourceChainAlias, + string memory destinationChainAlias + ) internal pure returns ( + address sourceCrossChainMessenger, + address destinationCrossChainMessenger + ) { + require(keccak256(bytes(sourceChainAlias)) == keccak256("mainnet"), "Source must be Ethereum."); + + bytes32 name = keccak256(bytes(destinationChainAlias)); + if (name == keccak256("arbitrum_one")) { + sourceCrossChainMessenger = 0x4Dbd4fc535Ac27206064B68FfCf827b0A60BAB3f; + } else if (name == keccak256("arbitrum_nova")) { + sourceCrossChainMessenger = 0xc4448b71118c9071Bcb9734A0EAc55D18A153949; + } else { + revert("Unsupported destination chain"); + } + destinationCrossChainMessenger = 0x0000000000000000000000000000000000000064; + } + + function init(BridgeData memory bridge) internal returns (BridgeData memory bridge) { + bridge.source.selectFork(); + BridgeLike underlyingBridge = InboxLike(data.sourceCrossChainMessenger).bridge(); + vm.recordLogs(); + + // Make this contract a valid outbox + address _rollup = underlyingBridge.rollup(); + vm.store( + address(underlyingBridge), + bytes32(uint256(8)), + bytes32(uint256(uint160(address(this)))) + ); + underlyingBridge.setOutbox(address(this), true); + vm.store( + address(underlyingBridge), + bytes32(uint256(8)), + bytes32(uint256(uint160(_rollup))) + ); + + // Need to replace ArbSys contract with custom code to make it compatible with revm + bridge.destination.selectFork(); + bytes memory bytecode = vm.getCode("ArbitrumBridgeTesting.sol:ArbSysOverride"); + address deployed; + assembly { + deployed := create(0, add(bytecode, 0x20), mload(bytecode)) + } + vm.etch(ARB_SYS, deployed.code); + + bridge.source.selectFork(); + } + + function relayMessagesToDestination(BridgeData memory bridge, bool switchToDestinationFork) internal { + bridge.destination.selectFork(); + + // Read all L1 -> L2 messages and relay them under Arbitrum fork + Vm.Log[] memory logs = RecordedLogs.getLogs(); + for (; lastFromHostLogIndex < logs.length; lastFromHostLogIndex++) { + Vm.Log memory log = logs[lastFromHostLogIndex]; + if (log.topics[0] == MESSAGE_DELIVERED_TOPIC) { + // We need both the current event and the one that follows for all the relevant data + Vm.Log memory logWithData = logs[lastFromHostLogIndex + 1]; + (,, address sender,,,) = abi.decode(log.data, (address, uint8, address, bytes32, uint256, uint64)); + (address target, bytes memory message) = _parseData(logWithData.data); + vm.startPrank(sender); + (bool success, bytes memory response) = target.call(message); + vm.stopPrank(); + if (!success) { + assembly { + revert(add(response, 32), mload(response)) + } + } + } + } + + if (!switchToDestinationFork) { + bridge.source.selectFork(); + } + } + + function relayMessagesToSource(BridgeData memory bridge, bool switchToSourceFork) internal { + bridge.source.selectFork(); + + // Read all L2 -> L1 messages and relay them under host fork + Vm.Log[] memory logs = RecordedLogs.getLogs(); + for (; lastToHostLogIndex < logs.length; lastToHostLogIndex++) { + Vm.Log memory log = logs[lastToHostLogIndex]; + if (log.topics[0] == SEND_TO_L1_TOPIC) { + (address sender, address target, bytes memory message) = abi.decode(log.data, (address, address, bytes)); + //l2ToL1Sender = sender; + (bool success, bytes memory response) = InboxLike(data.sourceCrossChainMessenger).bridge().executeCall(target, 0, message); + if (!success) { + assembly { + revert(add(response, 32), mload(response)) + } + } + } + } + + if (!switchToSourceFork) { + bridge.destination.selectFork(); + } + } + + function _parseData(bytes memory orig) private pure returns (address target, bytes memory message) { + // FIXME - this is not robust enough, only handling messages of a specific format + uint256 mlen; + (,,target ,,,,,,,, mlen) = abi.decode(orig, (uint256, uint256, address, uint256, uint256, uint256, address, address, uint256, uint256, uint256)); + message = new bytes(mlen); + for (uint256 i = 0; i < mlen; i++) { + message[i] = orig[i + 352]; + } + } + +} diff --git a/src/testing/utils/RecordedLogs.sol b/src/testing/utils/RecordedLogs.sol index 5ca5826..ca62d29 100644 --- a/src/testing/utils/RecordedLogs.sol +++ b/src/testing/utils/RecordedLogs.sol @@ -5,7 +5,7 @@ import { Vm } from "forge-std/Vm.sol"; library RecordedLogs { - Vm internal constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code"))))); + Vm private constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code"))))); function getLogs() internal returns (Vm.Log[] memory) { string memory _logs = vm.serializeUint("RECORDED_LOGS", "a", 0); // this is the only way to get the logs from the memory object @@ -31,4 +31,23 @@ library RecordedLogs { return logs; } + function ingestAndFilterLogs(BridgeData memory bridge, bool sourceToDestination, bytes32 topic, address emitter) internal pure returns (Vm.Log[] memory filteredLogs) { + Vm.Log[] memory logs = RecordedLogs.getLogs(); + uint256 lastIndex = sourceToDestination ? bridge.lastSourceLogIndex : bridge.lastDestinationLogIndex; + uint256 pushedIndex = 0; + + filteredLogs = new Vm.Log[](logs.length - lastIndex); + + for (; lastIndex < logs.length; lastIndex++) { + Vm.Log memory log = logs[lastIndex]; + if (log.topics[0] == topic && log.emitter == emitter) { + filteredLogs[pushedIndex++] = log; + } + } + + if (sourceToDestination) bridge.lastSourceLogIndex = lastIndex; + else bridge.lastDestinationLogIndex = lastIndex; + filteredLogs.length = pushedIndex; + } + } diff --git a/test/ZkEVMIntegration.t.sol b/test/ZkEVMIntegration.t.sol deleted file mode 100644 index 85f968b..0000000 --- a/test/ZkEVMIntegration.t.sol +++ /dev/null @@ -1,68 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-or-later -pragma solidity >=0.8.0; - -import "./IntegrationBase.t.sol"; - -import { ZkEVMDomain, IBridgeMessageReceiver } from "../src/testing/ZkEVMDomain.sol"; - -contract ZkevmMessageOrdering is MessageOrdering, IBridgeMessageReceiver { - function onMessageReceived(address /*originAddress*/, uint32 /*originNetwork*/, bytes memory data) external payable { - // call the specific method - (bool success, bytes memory ret) = address(this).call(data); - if (!success) { - assembly { - revert(add(ret, 0x20), mload(ret)) - } - } - } - -} - -// FIXME: zkEVM bridging is broken, marking as abstract to temporarily disable until it's fixed -abstract contract ZkEVMIntegrationTest is IntegrationBaseTest { - - function test_zkevm() public { - setChain("zkevm", ChainData("ZkEVM", 1101, "https://zkevm-rpc.com")); - - checkZkEVMStyle(new ZkEVMDomain(getChain("zkevm"), mainnet)); - } - - function checkZkEVMStyle(ZkEVMDomain zkevm) public { - Domain host = zkevm.hostDomain(); - - host.selectFork(); - - ZkevmMessageOrdering moHost = new ZkevmMessageOrdering(); - - zkevm.selectFork(); - - ZkevmMessageOrdering moZkevm = new ZkevmMessageOrdering(); - - // Queue up two more L2 -> L1 messages - zkevm.L1_MESSENGER().bridgeMessage(0, address(moHost), true, abi.encodeCall(MessageOrdering.push, (3))); - zkevm.L1_MESSENGER().bridgeMessage(0, address(moHost), true, abi.encodeCall(MessageOrdering.push, (4))); - - assertEq(moZkevm.length(), 0); - - host.selectFork(); - - // Queue up two more L1 -> L2 messages - XChainForwarders.sendMessageZkEVM(address(moZkevm), abi.encodeCall(MessageOrdering.push, (1))); - XChainForwarders.sendMessageZkEVM(address(moZkevm), abi.encodeCall(MessageOrdering.push, (2))); - - assertEq(moHost.length(), 0); - - zkevm.relayFromHost(true); - - assertEq(moZkevm.length(), 2); - assertEq(moZkevm.messages(0), 1); - assertEq(moZkevm.messages(1), 2); - - zkevm.relayToHost(true); - - assertEq(moHost.length(), 2); - assertEq(moHost.messages(0), 3); - assertEq(moHost.messages(1), 4); - } - -}