Skip to content
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

[SC-452] Refactor forwarders and receivers #17

Merged
merged 50 commits into from
Jun 12, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
7dbdfe8
adding cctp support
hexonaut May 10, 2024
cf2c3c1
complete circle cctp
hexonaut May 10, 2024
ca6a389
remove get sender function as its pointless in this style of callback
hexonaut May 10, 2024
ede7a78
add polygon and avalanche support
hexonaut May 10, 2024
8e5b368
use more general language for cctp domain
hexonaut May 14, 2024
d3d7e21
rename some of the variables
hexonaut May 14, 2024
616c215
more var renaming; use l2 authority
hexonaut May 14, 2024
506ecc0
review fixes
hexonaut May 17, 2024
9bea858
convert domain into a struct + library; starting to split out bridges…
hexonaut May 18, 2024
418dbb4
more refactoring of bridge
hexonaut May 18, 2024
17c1b3d
part way through testing refactor
hexonaut May 20, 2024
5b03ea7
wip cctp
hexonaut May 20, 2024
f2c3076
Merge branch 'master' into SC-440-refactor-e2e
hexonaut May 20, 2024
2ece5a4
still wip for refactoring bridge testing
hexonaut May 20, 2024
1d966d5
large refactor to split out domains and bridges and move into library…
hexonaut May 24, 2024
17d300c
fix optimism
hexonaut May 24, 2024
8975d01
fix AMB
hexonaut May 29, 2024
46c876f
got arbitrum working
hexonaut May 29, 2024
7af6b4c
refactor XChainForwaders into separate libraries and support both dir…
hexonaut Jun 1, 2024
5540ccd
refactor receivers; started adjusting integration tests
hexonaut Jun 1, 2024
0040372
refactor integration tests
hexonaut Jun 3, 2024
d828811
fix all tests; update readme
hexonaut Jun 4, 2024
200b503
rm unused console
hexonaut Jun 5, 2024
5897ab7
Merge branch 'SC-440-refactor-e2e' into SC-452-refactor-forwards-rece…
hexonaut Jun 5, 2024
1928687
remove the chain specific helper functions
hexonaut Jun 5, 2024
5a8dcfc
add constructor tests for coverage
hexonaut Jun 5, 2024
75e24ce
remove constructor test; add diagram
hexonaut Jun 5, 2024
9d081f1
add unit tests for amb receiver
hexonaut Jun 5, 2024
747c428
add cctp unit test
hexonaut Jun 5, 2024
8190995
change cctp authority type
hexonaut Jun 5, 2024
05e3840
Merge branch 'SC-452-refactor-forwards-receivers' into SC-437-fix-cov…
hexonaut Jun 5, 2024
3fcbcce
update to new cctp receiver
hexonaut Jun 5, 2024
5669f7d
add arbitrum receiever unit tests
hexonaut Jun 5, 2024
dc07c16
add optimism test
hexonaut Jun 5, 2024
aade8f0
merge master
hexonaut Jun 7, 2024
9874e5c
use relative paths for library code
hexonaut Jun 8, 2024
46ee62c
fix for subsequent messages
hexonaut Jun 8, 2024
743b951
forge install: openzeppelin-contracts
hexonaut Jun 8, 2024
7eb7d7a
use fallback() instead of forward()
hexonaut Jun 8, 2024
bc5911a
Merge branch 'SC-452-refactor-forwards-receivers' into SC-437-fix-cov…
hexonaut Jun 8, 2024
842bf17
dont use absolute paths
hexonaut Jun 9, 2024
3e47e33
Merge branch 'SC-452-refactor-forwards-receivers' into SC-437-fix-cov…
hexonaut Jun 9, 2024
6be2397
fix tests to remove forward()
hexonaut Jun 9, 2024
177227a
move image up; more about the receiver; typo
hexonaut Jun 11, 2024
611fe91
formatting
hexonaut Jun 11, 2024
f222fe2
rm old unused commented out line
hexonaut Jun 11, 2024
eea3d6e
Merge branch 'SC-452-refactor-forwards-receivers' into SC-437-fix-cov…
hexonaut Jun 12, 2024
0c2893d
review fixes
hexonaut Jun 12, 2024
fd8a5f8
align
hexonaut Jun 12, 2024
1b6d35f
Merge pull request #18 from marsfoundation/SC-437-fix-coverage
hexonaut Jun 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
adding cctp support
  • Loading branch information
hexonaut committed May 10, 2024
commit 7dbdfe8c0a831105a82b2c0fd76795675b64bc63
56 changes: 56 additions & 0 deletions src/CCTPReceiver.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity ^0.8.0;

/**
* @title CCTPReceiver
* @notice Receive messages from CCTP-style bridge.
*/
abstract contract CCTPReceiver {

address public immutable l2CrossDomain;
uint32 public immutable sourceDomain;
address public immutable l1Authority;

constructor(
address _l2CrossDomain,
uint32 _sourceDomain,
address _l1Authority
) {
l2CrossDomain = _l2CrossDomain;
sourceDomain = _sourceDomain;
l1Authority = _l1Authority;
}

function _getL1MessageSender() internal view returns (address) {
return l1Authority;
}

function _onlyCrossChainMessage() internal view {
require(msg.sender == address(this), "Receiver/invalid-sender");
}

modifier onlyCrossChainMessage() {
_onlyCrossChainMessage();
_;
}

function handleReceiveMessage(
uint32 _sourceDomain,
bytes32 sender,
bytes calldata messageBody
) external returns (bool) {
require(msg.sender == l2CrossDomain, "Receiver/invalid-sender");
require(_sourceDomain == sourceDomain, "Receiver/invalid-sourceDomain");
require(sender == bytes32(uint256(uint160(l1Authority))), "Receiver/invalid-l1Authority");

(bool success, bytes memory ret) = address(this).call(messageBody);
if (!success) {
assembly {
revert(add(ret, 0x20), mload(ret))
}
}

return true;
}

}
63 changes: 63 additions & 0 deletions src/XChainForwarders.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@ interface ICrossDomainZkEVM {
) external payable;
}

interface ICrossDomainCircleCCTP {
function sendMessage(
uint32 destinationDomain,
bytes32 recipient,
bytes calldata messageBody
) external;
}

/**
* @title XChainForwarders
* @notice Helper functions to abstract over L1 -> L2 message passing.
Expand Down Expand Up @@ -196,4 +204,59 @@ library XChainForwarders {
);
}

/// ================================ CCTP ================================

function sendMessageCCTP(
address l1CrossDomain,
uint32 destinationDomain,
bytes32 recipient,
bytes memory messageBody
) internal {
ICrossDomainCircleCCTP(l1CrossDomain).sendMessage(
destinationDomain,
recipient,
messageBody
);
}

function sendMessageCCTP(
address l1CrossDomain,
uint32 destinationDomain,
address recipient,
bytes memory messageBody
) internal {
sendMessageCCTP(
l1CrossDomain,
destinationDomain,
bytes32(uint256(uint160(recipient))),
messageBody
);
}

function sendMessageCircleCCTP(
uint32 destinationDomain,
bytes32 recipient,
bytes memory messageBody
) internal {
sendMessageCCTP(
0x0a992d191DEeC32aFe36203Ad87D7d289a738F81,
destinationDomain,
recipient,
messageBody
);
}

function sendMessageCircleCCTP(
uint32 destinationDomain,
address recipient,
bytes memory messageBody
) internal {
sendMessageCCTP(
0x0a992d191DEeC32aFe36203Ad87D7d289a738F81,
destinationDomain,
bytes32(uint256(uint160(recipient))),
messageBody
);
}

}
94 changes: 94 additions & 0 deletions src/testing/CircleCCTPDomain.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 { 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 CircleCCTPDomain is BridgedDomain {

bytes32 private constant SENT_MESSAGE_TOPIC = keccak256("MessageSent(bytes)");

MessengerLike public constant L1_MESSENGER = MessengerLike(0x0a992d191DEeC32aFe36203Ad87D7d289a738F81);
MessengerLike public L2_MESSENGER;

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("avalanche")) {
L2_MESSENGER = MessengerLike(0x8186359aF5F57FbB40c6b14A588d2A59C0C29880);
} else if (name == keccak256("optimism")) {
L2_MESSENGER = MessengerLike(0x4D41f22c5a0e5c74090899E5a8Fb597a8842b3e8);
} else if (name == keccak256("arbitrum_one")) {
L2_MESSENGER = MessengerLike(0xC30362313FBBA5cf9163F0bb16a0e01f01A896ca);
} else if (name == keccak256("base")) {
L2_MESSENGER = MessengerLike(0xAD09780d193884d503182aD4588450C416D6F9D4);
} else if (name == keccak256("polygon")) {
L2_MESSENGER = MessengerLike(0xF3be9355363857F3e001be68856A2f96b4C39Ba9);
} else {
revert("Unsupported chain");
}

selectFork();

// Set minimum required signatures to zero
vm.store(
address(L2_MESSENGER),
bytes32(uint256(4)),
0
);

hostDomain.selectFork();

vm.store(
address(L1_MESSENGER),
bytes32(uint256(4)),
0
);

vm.recordLogs();
}

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(L1_MESSENGER)) {
L2_MESSENGER.receiveMessage(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(L2_MESSENGER)) {
L1_MESSENGER.receiveMessage(log.data, "");
}
}

if (!switchToHost) {
selectFork();
}
}

}
118 changes: 118 additions & 0 deletions test/CCTPIntegration.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity >=0.8.0;

import "./IntegrationBase.t.sol";

import { CircleCCTPDomain } from "../src/testing/CircleCCTPDomain.sol";

import { CCTPReceiver } from "../src/CCTPReceiver.sol";

contract MessageOrderingCCTP is MessageOrdering, CCTPReceiver {

constructor(
address _l2CrossDomain,
uint32 _chainId,
address _l1Authority
) CCTPReceiver(
_l2CrossDomain,
_chainId,
_l1Authority
) {}

function push(uint256 messageId) public override onlyCrossChainMessage {
super.push(messageId);
}

}

contract CircleCCTPIntegrationTest is IntegrationBaseTest {

function test_optimism() public {
CircleCCTPDomain cctp = new CircleCCTPDomain(getChain("optimism"), mainnet);
checkCircleCCTPStyle(cctp, 2);
}

function checkCircleCCTPStyle(CircleCCTPDomain cctp, uint32 guestDomain) public {
Domain host = cctp.hostDomain();

host.selectFork();

MessageOrdering moHost = new MessageOrdering();

cctp.selectFork();

MessageOrderingCCTP moCCTP = new MessageOrderingCCTP(
address(cctp.L2_MESSENGER()),
0, // Ethereum
l1Authority
);

// Queue up some L2 -> L1 messages
XChainForwarders.sendMessageCCTP(
address(cctp.L2_MESSENGER()),
0, // Ethereum
address(moHost),
abi.encodeWithSelector(MessageOrdering.push.selector, 3)
);
XChainForwarders.sendMessageCCTP(
address(cctp.L2_MESSENGER()),
0,
address(moHost),
abi.encodeWithSelector(MessageOrdering.push.selector, 4)
);

assertEq(moCCTP.length(), 0);

// Do not relay right away
host.selectFork();

// Queue up two more L1 -> L2 messages
vm.startPrank(l1Authority);
XChainForwarders.sendMessageCircleCCTP(
guestDomain,
address(moCCTP),
abi.encodeWithSelector(MessageOrdering.push.selector, 1)
);
XChainForwarders.sendMessageCircleCCTP(
guestDomain,
address(moCCTP),
abi.encodeWithSelector(MessageOrdering.push.selector, 2)
);
vm.stopPrank();

assertEq(moHost.length(), 0);

cctp.relayFromHost(true);

assertEq(moCCTP.length(), 2);
assertEq(moCCTP.messages(0), 1);
assertEq(moCCTP.messages(1), 2);

cctp.relayToHost(true);

assertEq(moHost.length(), 2);
assertEq(moHost.messages(0), 3);
assertEq(moHost.messages(1), 4);

return;

// Validate the message receiver failure modes
vm.startPrank(notL1Authority);
XChainForwarders.sendMessageCircleCCTP(
guestDomain,
address(moCCTP),
abi.encodeWithSelector(MessageOrdering.push.selector, 999)
);
vm.stopPrank();

vm.expectRevert("handleReceiveMessage() failed");
cctp.relayFromHost(true);

cctp.selectFork();
vm.expectRevert("Receiver/invalid-sender");
moCCTP.push(999);

// TODO test the source domain doesn't match will revert
}

}
Loading