-
Notifications
You must be signed in to change notification settings - Fork 13
Expand file tree
/
Copy pathCrossDomainMessageLib.sol
More file actions
98 lines (81 loc) · 4.98 KB
/
CrossDomainMessageLib.sol
File metadata and controls
98 lines (81 loc) · 4.98 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {PredeployAddresses} from "./PredeployAddresses.sol";
import {IL2ToL2CrossDomainMessenger} from "../interfaces/IL2ToL2CrossDomainMessenger.sol";
import {Identifier} from "../interfaces/IIdentifier.sol";
library CrossDomainMessageLib {
/// @notice Thrown when trying to validate a cross chain message with a block number
/// that is greater than 2^64.
error BlockNumberTooHigh();
/// @notice Thrown when trying to validate a cross chain message with a timestamp
/// that is greater than 2^64.
error TimestampTooHigh();
/// @notice Thrown when trying to validate a cross chain message with a log index
/// that is greater than 2^32.
error LogIndexTooHigh();
/// @notice The error emitted when a required message has not been relayed.
error RequiredMessageNotSuccessful(bytes32 msgHash);
/// @notice The error emitted when the caller is not the L2toL2CrossDomainMessenger.
error CallerNotL2toL2CrossDomainMessenger();
/// @notice The error emitted when the original sender of the cross-domain message is not this same address as this contract.
error InvalidCrossDomainSender();
/// @notice The mask for the most significant bits of the checksum.
/// @dev Used to set the most significant byte to zero.
bytes32 internal constant _MSB_MASK = bytes32(~uint256(0xff << 248));
/// @notice Mask used to set the first byte of the bare checksum to 3 (0x03).
bytes32 internal constant _TYPE_3_MASK = bytes32(uint256(0x03 << 248));
/// @notice Checks if the msgHash has been relayed and reverts with a special error signature
/// that the auto-relayer performs special handling on if the msgHash has not been relayed.
/// If the auto-relayer encounters this error, it will parse the msgHash and wait for the
/// msgHash to be relayed before relaying the message that calls this function. This ensures
/// that any required message is relayed before the message that depends on it.
/// @param msgHash The hash of the message to check if it has been relayed.
function requireMessageSuccess(bytes32 msgHash) internal view {
if (
!IL2ToL2CrossDomainMessenger(PredeployAddresses.L2_TO_L2_CROSS_DOMAIN_MESSENGER).successfulMessages(msgHash)
) {
revert RequiredMessageNotSuccessful(msgHash);
}
}
/// @notice Checks if the caller is the L2toL2CrossDomainMessenger. It is important to use this check
/// on cross-domain messages that should only be relayed through the L2toL2CrossDomainMessenger.
function requireCallerIsCrossDomainMessenger() internal view {
if (msg.sender != address(PredeployAddresses.L2_TO_L2_CROSS_DOMAIN_MESSENGER)) {
revert CallerNotL2toL2CrossDomainMessenger();
}
}
/// @notice While relaying a message through the L2toL2CrossDomainMessenger, checks
/// that the original sender of the cross-domain message is this same address.
/// It is important to use this check on cross-domain messages that should only be
/// sent and relayed by the same contract on different chains.
function requireCrossDomainCallback() internal view {
requireCallerIsCrossDomainMessenger();
if (
IL2ToL2CrossDomainMessenger(PredeployAddresses.L2_TO_L2_CROSS_DOMAIN_MESSENGER).crossDomainMessageSender()
!= address(this)
) revert InvalidCrossDomainSender();
}
/// @notice Calculates a custom checksum for a cross chain message `Identifier` and `msgHash`.
/// @param _id The identifier of the message.
/// @param _msgHash The hash of the message.
/// @return checksum_ The checksum of the message.
function calculateChecksum(Identifier memory _id, bytes32 _msgHash) public pure returns (bytes32 checksum_) {
if (_id.blockNumber > type(uint64).max) revert BlockNumberTooHigh();
if (_id.logIndex > type(uint32).max) revert LogIndexTooHigh();
if (_id.timestamp > type(uint64).max) revert TimestampTooHigh();
// Hash the origin address and message hash together
bytes32 logHash = keccak256(abi.encodePacked(_id.origin, _msgHash));
// Downsize the identifier fields to match the needed type for the custom checksum calculation.
uint64 blockNumber = uint64(_id.blockNumber);
uint64 timestamp = uint64(_id.timestamp);
uint32 logIndex = uint32(_id.logIndex);
// Pack identifier fields with a left zero padding (uint96(0))
bytes32 idPacked = bytes32(abi.encodePacked(uint96(0), blockNumber, timestamp, logIndex));
// Hash the logHash with the packed identifier data
bytes32 idLogHash = keccak256(abi.encodePacked(logHash, idPacked));
// Create the final hash by combining idLogHash with chainId
bytes32 bareChecksum = keccak256(abi.encodePacked(idLogHash, _id.chainId));
// Apply bit masking to create the final checksum
checksum_ = (bareChecksum & _MSB_MASK) | _TYPE_3_MASK;
}
}