forked from scroll-tech/scroll
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathL2ScrollMessenger.sol
170 lines (138 loc) · 6.22 KB
/
L2ScrollMessenger.sol
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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
// SPDX-License-Identifier: MIT
pragma solidity =0.8.16;
import {IL2ScrollMessenger} from "./IL2ScrollMessenger.sol";
import {L2MessageQueue} from "./predeploys/L2MessageQueue.sol";
import {PatriciaMerkleTrieVerifier} from "../libraries/verifier/PatriciaMerkleTrieVerifier.sol";
import {ScrollConstants} from "../libraries/constants/ScrollConstants.sol";
import {AddressAliasHelper} from "../libraries/common/AddressAliasHelper.sol";
import {IScrollMessenger} from "../libraries/IScrollMessenger.sol";
import {ScrollMessengerBase} from "../libraries/ScrollMessengerBase.sol";
// solhint-disable reason-string
// solhint-disable not-rely-on-time
/// @title L2ScrollMessenger
/// @notice The `L2ScrollMessenger` contract can:
///
/// 1. send messages from layer 2 to layer 1;
/// 2. relay messages from layer 1 layer 2;
/// 3. drop expired message due to sequencer problems.
///
/// @dev It should be a predeployed contract on layer 2 and should hold infinite amount
/// of Ether (Specifically, `uint256(-1)`), which can be initialized in Genesis Block.
contract L2ScrollMessenger is ScrollMessengerBase, IL2ScrollMessenger {
/*************
* Constants *
*************/
/// @notice The address of L2MessageQueue.
address public immutable messageQueue;
/*************
* Variables *
*************/
/// @notice Mapping from L2 message hash to the timestamp when the message is sent.
mapping(bytes32 => uint256) public messageSendTimestamp;
/// @notice Mapping from L1 message hash to a boolean value indicating if the message has been successfully executed.
mapping(bytes32 => bool) public isL1MessageExecuted;
/// @dev The storage slots used by previous versions of this contract.
uint256[2] private __used;
/***************
* Constructor *
***************/
constructor(address _counterpart, address _messageQueue) ScrollMessengerBase(_counterpart) {
if (_messageQueue == address(0)) {
revert ErrorZeroAddress();
}
_disableInitializers();
messageQueue = _messageQueue;
}
function initialize(address) external initializer {
ScrollMessengerBase.__ScrollMessengerBase_init(address(0), address(0));
}
/*****************************
* Public Mutating Functions *
*****************************/
/// @inheritdoc IScrollMessenger
function sendMessage(
address _to,
uint256 _value,
bytes memory _message,
uint256 _gasLimit
) external payable override whenNotPaused {
_sendMessage(_to, _value, _message, _gasLimit);
}
/// @inheritdoc IScrollMessenger
function sendMessage(
address _to,
uint256 _value,
bytes calldata _message,
uint256 _gasLimit,
address
) external payable override whenNotPaused {
_sendMessage(_to, _value, _message, _gasLimit);
}
/// @inheritdoc IL2ScrollMessenger
function relayMessage(
address _from,
address _to,
uint256 _value,
uint256 _nonce,
bytes memory _message
) external override whenNotPaused {
// It is impossible to deploy a contract with the same address, reentrance is prevented in nature.
require(AddressAliasHelper.undoL1ToL2Alias(_msgSender()) == counterpart, "Caller is not L1ScrollMessenger");
bytes32 _xDomainCalldataHash = keccak256(_encodeXDomainCalldata(_from, _to, _value, _nonce, _message));
require(!isL1MessageExecuted[_xDomainCalldataHash], "Message was already successfully executed");
_executeMessage(_from, _to, _value, _message, _xDomainCalldataHash);
}
/**********************
* Internal Functions *
**********************/
/// @dev Internal function to send cross domain message.
/// @param _to The address of account who receive the message.
/// @param _value The amount of ether passed when call target contract.
/// @param _message The content of the message.
/// @param _gasLimit Optional gas limit to complete the message relay on corresponding chain.
function _sendMessage(
address _to,
uint256 _value,
bytes memory _message,
uint256 _gasLimit
) internal nonReentrant {
require(msg.value == _value, "msg.value mismatch");
uint256 _nonce = L2MessageQueue(messageQueue).nextMessageIndex();
bytes32 _xDomainCalldataHash = keccak256(_encodeXDomainCalldata(_msgSender(), _to, _value, _nonce, _message));
// normally this won't happen, since each message has different nonce, but just in case.
require(messageSendTimestamp[_xDomainCalldataHash] == 0, "Duplicated message");
messageSendTimestamp[_xDomainCalldataHash] = block.timestamp;
L2MessageQueue(messageQueue).appendMessage(_xDomainCalldataHash);
emit SentMessage(_msgSender(), _to, _value, _nonce, _gasLimit, _message);
}
/// @dev Internal function to execute a L1 => L2 message.
/// @param _from The address of the sender of the message.
/// @param _to The address of the recipient of the message.
/// @param _value The msg.value passed to the message call.
/// @param _message The content of the message.
/// @param _xDomainCalldataHash The hash of the message.
function _executeMessage(
address _from,
address _to,
uint256 _value,
bytes memory _message,
bytes32 _xDomainCalldataHash
) internal {
// @note check more `_to` address to avoid attack in the future when we add more gateways.
require(_to != messageQueue, "Forbid to call message queue");
_validateTargetAddress(_to);
// @note This usually will never happen, just in case.
require(_from != xDomainMessageSender, "Invalid message sender");
xDomainMessageSender = _from;
// solhint-disable-next-line avoid-low-level-calls
(bool success, ) = _to.call{value: _value}(_message);
// reset value to refund gas.
xDomainMessageSender = ScrollConstants.DEFAULT_XDOMAIN_MESSAGE_SENDER;
if (success) {
isL1MessageExecuted[_xDomainCalldataHash] = true;
emit RelayedMessage(_xDomainCalldataHash);
} else {
emit FailedRelayedMessage(_xDomainCalldataHash);
}
}
}