Skip to content

Commit

Permalink
part way through testing refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
hexonaut committed May 20, 2024
1 parent 418dbb4 commit 17c1b3d
Show file tree
Hide file tree
Showing 7 changed files with 165 additions and 84 deletions.
2 changes: 1 addition & 1 deletion src/testing/Domain.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ struct Domain {

library DomainHelpers {

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 createFork(StdChains.Chain memory chain, uint256 blockNumber) internal returns (Domain memory domain) {
domain = Domain({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
// 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, DomainHelpers } from "src/testing/Domain.sol";
import { RecordedLogs } from "src/testing/utils/RecordedLogs.sol";
import { RecordedLogs } from "src/testing/utils/RecordedLogs.sol";
import { BridgeData } from "./BridgeData.sol";

interface InboxLike {
function createRetryableTicket(
Expand Down Expand Up @@ -42,63 +41,85 @@ contract ArbSysOverride {

}

contract ArbitrumNativeBridge is IBidirectionalBridge {
library ArbitrumBridgeTesting {

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: ""
}));
}

address public l2ToL1Sender;

BridgeData public data;

constructor(BridgeData memory _data) {
data = _data;
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;
}

data.source.selectFork();
BridgeLike bridge = InboxLike(data.sourceCrossChainMessenger).bridge();
function init(BridgeData memory bridge) internal returns (BridgeData memory bridge) {
bridge.source.selectFork();
BridgeLike underlyingBridge = InboxLike(data.sourceCrossChainMessenger).bridge();
vm.recordLogs();
vm.makePersistent(address(this));

// Make this contract a valid outbox
address _rollup = bridge.rollup();
address _rollup = underlyingBridge.rollup();
vm.store(
address(bridge),
address(underlyingBridge),
bytes32(uint256(8)),
bytes32(uint256(uint160(address(this))))
);
bridge.setOutbox(address(this), true);
underlyingBridge.setOutbox(address(this), true);
vm.store(
address(bridge),
address(underlyingBridge),
bytes32(uint256(8)),
bytes32(uint256(uint160(_rollup)))
);

// Need to replace ArbSys contract with custom code to make it compatible with revm
destination.selectFork();
bytes memory bytecode = vm.getCode("ArbitrumDomain.sol:ArbSysOverride");
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);

source.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];
}
bridge.source.selectFork();
}

function relayMessagesToSource(bool switchToDestinationFork) external override {
destination.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();
Expand All @@ -108,7 +129,7 @@ contract ArbitrumNativeBridge is IBidirectionalBridge {
// 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);
(address target, bytes memory message) = _parseData(logWithData.data);
vm.startPrank(sender);
(bool success, bytes memory response) = target.call(message);
vm.stopPrank();
Expand All @@ -121,20 +142,20 @@ contract ArbitrumNativeBridge is IBidirectionalBridge {
}

if (!switchToDestinationFork) {
source.selectFork();
bridge.source.selectFork();
}
}

function relayMessagesToSource(bool switchToSourceFork) external override {
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;
//l2ToL1Sender = sender;
(bool success, bytes memory response) = InboxLike(data.sourceCrossChainMessenger).bridge().executeCall(target, 0, message);
if (!success) {
assembly {
Expand All @@ -145,7 +166,17 @@ contract ArbitrumNativeBridge is IBidirectionalBridge {
}

if (!switchToSourceFork) {
destination.selectFork();
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];
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ struct BridgeData {
// These are used internally for log tracking
uint256 lastSourceLogIndex;
uint256 lastDestinationLogIndex;
bytes extraData;
}
94 changes: 94 additions & 0 deletions src/testing/bridges/CCTPBridgeTesting.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// 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 ArbitrumBridgeTesting {

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 {

}

}
8 changes: 0 additions & 8 deletions src/testing/bridges/IBidirectionalBridge.sol

This file was deleted.

6 changes: 0 additions & 6 deletions src/testing/bridges/IUnidirectionalBridge.sol

This file was deleted.

31 changes: 0 additions & 31 deletions src/testing/bridges/data/Arbitrum.sol

This file was deleted.

0 comments on commit 17c1b3d

Please sign in to comment.