@layerzerolabs/lz-evm-protocol-v2 2.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/artifacts/contracts/EndpointV2.sol/EndpointV2.json +2364 -0
- package/artifacts/contracts/EndpointV2Alt.sol/EndpointV2Alt.json +2379 -0
- package/artifacts/contracts/MessageLibManager.sol/MessageLibManager.json +783 -0
- package/artifacts/contracts/MessagingChannel.sol/MessagingChannel.json +426 -0
- package/artifacts/contracts/MessagingComposer.sol/MessagingComposer.json +320 -0
- package/artifacts/contracts/MessagingContext.sol/MessagingContext.json +42 -0
- package/artifacts/contracts/interfaces/ILayerZeroComposer.sol/ILayerZeroComposer.json +44 -0
- package/artifacts/contracts/interfaces/ILayerZeroEndpointV2.sol/ILayerZeroEndpointV2.json +1844 -0
- package/artifacts/contracts/interfaces/ILayerZeroReceiver.sol/ILayerZeroReceiver.json +121 -0
- package/artifacts/contracts/interfaces/IMessageLib.sol/IMessageLib.json +149 -0
- package/artifacts/contracts/interfaces/IMessageLibManager.sol/IMessageLibManager.json +629 -0
- package/artifacts/contracts/interfaces/IMessagingChannel.sol/IMessagingChannel.json +344 -0
- package/artifacts/contracts/interfaces/IMessagingComposer.sol/IMessagingComposer.json +246 -0
- package/artifacts/contracts/interfaces/IMessagingContext.sol/IMessagingContext.json +42 -0
- package/artifacts/contracts/interfaces/ISendLib.sol/ISendLib.json +364 -0
- package/artifacts/contracts/libs/AddressCast.sol/AddressCast.json +10 -0
- package/artifacts/contracts/libs/CalldataBytesLib.sol/CalldataBytesLib.json +10 -0
- package/artifacts/contracts/libs/Errors.sol/Errors.json +226 -0
- package/artifacts/contracts/libs/GUID.sol/GUID.json +10 -0
- package/artifacts/contracts/libs/Transfer.sol/Transfer.json +32 -0
- package/artifacts/contracts/messagelib/BlockedMessageLib.sol/BlockedMessageLib.json +94 -0
- package/artifacts/contracts/messagelib/SimpleMessageLib.sol/SimpleMessageLib.json +591 -0
- package/artifacts/contracts/messagelib/libs/BitMaps.sol/BitMaps.json +10 -0
- package/artifacts/contracts/messagelib/libs/ExecutorOptions.sol/ExecutorOptions.json +26 -0
- package/artifacts/contracts/messagelib/libs/PacketV1Codec.sol/PacketV1Codec.json +10 -0
- package/contracts/EndpointV2.sol +403 -0
- package/contracts/EndpointV2Alt.sol +50 -0
- package/contracts/MessageLibManager.sol +326 -0
- package/contracts/MessagingChannel.sol +161 -0
- package/contracts/MessagingComposer.sol +80 -0
- package/contracts/MessagingContext.sol +36 -0
- package/contracts/interfaces/ILayerZeroComposer.sol +24 -0
- package/contracts/interfaces/ILayerZeroEndpointV2.sol +98 -0
- package/contracts/interfaces/ILayerZeroReceiver.sol +20 -0
- package/contracts/interfaces/IMessageLib.sol +26 -0
- package/contracts/interfaces/IMessageLibManager.sol +68 -0
- package/contracts/interfaces/IMessagingChannel.sol +32 -0
- package/contracts/interfaces/IMessagingComposer.sol +38 -0
- package/contracts/interfaces/IMessagingContext.sol +9 -0
- package/contracts/interfaces/ISendLib.sol +36 -0
- package/contracts/libs/AddressCast.sol +40 -0
- package/contracts/libs/CalldataBytesLib.sol +58 -0
- package/contracts/libs/Errors.sol +42 -0
- package/contracts/libs/GUID.sol +19 -0
- package/contracts/libs/Transfer.sol +34 -0
- package/contracts/messagelib/BlockedMessageLib.sol +30 -0
- package/contracts/messagelib/SimpleMessageLib.sol +149 -0
- package/contracts/messagelib/libs/BitMaps.sol +26 -0
- package/contracts/messagelib/libs/ExecutorOptions.sol +85 -0
- package/contracts/messagelib/libs/PacketV1Codec.sol +108 -0
- package/package.json +27 -0
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
// SPDX-License-Identifier: LZBL-1.2
|
|
2
|
+
|
|
3
|
+
pragma solidity ^0.8.22;
|
|
4
|
+
|
|
5
|
+
import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
|
|
6
|
+
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
|
|
7
|
+
|
|
8
|
+
import { IMessageLib, MessageLibType } from "./interfaces/IMessageLib.sol";
|
|
9
|
+
import { IMessageLibManager, SetConfigParam } from "./interfaces/IMessageLibManager.sol";
|
|
10
|
+
import { Errors } from "./libs/Errors.sol";
|
|
11
|
+
import { BlockedMessageLib } from "./messagelib/BlockedMessageLib.sol";
|
|
12
|
+
|
|
13
|
+
abstract contract MessageLibManager is Ownable, IMessageLibManager {
|
|
14
|
+
address private constant DEFAULT_LIB = address(0);
|
|
15
|
+
|
|
16
|
+
// the library that reverts both on send and quote
|
|
17
|
+
// must be configured on construction and be immutable
|
|
18
|
+
address public immutable blockedLibrary;
|
|
19
|
+
|
|
20
|
+
// only registered libraries all valid libraries
|
|
21
|
+
// the blockedLibrary will be registered on construction
|
|
22
|
+
address[] internal registeredLibraries;
|
|
23
|
+
mapping(address lib => bool) public isRegisteredLibrary;
|
|
24
|
+
|
|
25
|
+
// both sendLibrary and receiveLibrary config can be lazily resolved
|
|
26
|
+
mapping(address sender => mapping(uint32 dstEid => address lib)) internal sendLibrary;
|
|
27
|
+
mapping(address receiver => mapping(uint32 srcEid => address lib)) internal receiveLibrary;
|
|
28
|
+
mapping(address receiver => mapping(uint32 srcEid => Timeout)) public receiveLibraryTimeout;
|
|
29
|
+
|
|
30
|
+
mapping(uint32 dstEid => address lib) public defaultSendLibrary;
|
|
31
|
+
mapping(uint32 srcEid => address lib) public defaultReceiveLibrary;
|
|
32
|
+
mapping(uint32 srcEid => Timeout) public defaultReceiveLibraryTimeout;
|
|
33
|
+
|
|
34
|
+
constructor() {
|
|
35
|
+
blockedLibrary = address(new BlockedMessageLib());
|
|
36
|
+
registerLibrary(blockedLibrary);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
modifier onlyRegistered(address _lib) {
|
|
40
|
+
if (!isRegisteredLibrary[_lib]) revert Errors.OnlyRegisteredLib();
|
|
41
|
+
_;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
modifier isSendLib(address _lib) {
|
|
45
|
+
if (_lib != DEFAULT_LIB) {
|
|
46
|
+
if (IMessageLib(_lib).messageLibType() == MessageLibType.Receive) revert Errors.OnlySendLib();
|
|
47
|
+
}
|
|
48
|
+
_;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
modifier isReceiveLib(address _lib) {
|
|
52
|
+
if (_lib != DEFAULT_LIB) {
|
|
53
|
+
if (IMessageLib(_lib).messageLibType() == MessageLibType.Send) revert Errors.OnlyReceiveLib();
|
|
54
|
+
}
|
|
55
|
+
_;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
modifier onlyRegisteredOrDefault(address _lib) {
|
|
59
|
+
if (!isRegisteredLibrary[_lib] && _lib != DEFAULT_LIB) revert Errors.OnlyRegisteredOrDefaultLib();
|
|
60
|
+
_;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/// @dev check if the library supported the eid.
|
|
64
|
+
modifier onlySupportedEid(address _lib, uint32 _eid) {
|
|
65
|
+
/// @dev doesnt need to check for default lib, because when they are initially added they get passed through this modifier
|
|
66
|
+
if (_lib != DEFAULT_LIB) {
|
|
67
|
+
if (!IMessageLib(_lib).isSupportedEid(_eid)) revert Errors.UnsupportedEid();
|
|
68
|
+
}
|
|
69
|
+
_;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function getRegisteredLibraries() external view returns (address[] memory) {
|
|
73
|
+
return registeredLibraries;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/// @notice The Send Library is the Oapp specified library that will be used to send the message to the destination
|
|
77
|
+
/// endpoint. If the Oapp does not specify a Send Library, the default Send Library will be used.
|
|
78
|
+
/// @dev If the Oapp does not have a selected Send Library, this function will resolve to the default library
|
|
79
|
+
/// configured by LayerZero
|
|
80
|
+
/// @return lib address of the Send Library
|
|
81
|
+
/// @param _sender The address of the Oapp that is sending the message
|
|
82
|
+
/// @param _dstEid The destination endpoint id
|
|
83
|
+
function getSendLibrary(address _sender, uint32 _dstEid) public view returns (address lib) {
|
|
84
|
+
lib = sendLibrary[_sender][_dstEid];
|
|
85
|
+
if (lib == DEFAULT_LIB) {
|
|
86
|
+
lib = defaultSendLibrary[_dstEid];
|
|
87
|
+
if (lib == address(0x0)) revert Errors.DefaultSendLibUnavailable();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function isDefaultSendLibrary(address _sender, uint32 _dstEid) public view returns (bool) {
|
|
92
|
+
return sendLibrary[_sender][_dstEid] == DEFAULT_LIB;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/// @dev the receiveLibrary can be lazily resolved that if not set it will point to the default configured by LayerZero
|
|
96
|
+
function getReceiveLibrary(address _receiver, uint32 _srcEid) public view returns (address lib, bool isDefault) {
|
|
97
|
+
lib = receiveLibrary[_receiver][_srcEid];
|
|
98
|
+
if (lib == DEFAULT_LIB) {
|
|
99
|
+
lib = defaultReceiveLibrary[_srcEid];
|
|
100
|
+
if (lib == address(0x0)) revert Errors.DefaultReceiveLibUnavailable();
|
|
101
|
+
isDefault = true;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/// @dev called when the endpoint checks if the msgLib attempting to verify the msg is the configured msgLib of the Oapp
|
|
106
|
+
/// @dev this check provides the ability for Oapp to lock in a trusted msgLib
|
|
107
|
+
/// @dev it will fist check if the msgLib is the currently configured one. then check if the msgLib is the one in grace period of msgLib versioning upgrade
|
|
108
|
+
function isValidReceiveLibrary(
|
|
109
|
+
address _receiver,
|
|
110
|
+
uint32 _srcEid,
|
|
111
|
+
address _actualReceiveLib
|
|
112
|
+
) public view returns (bool) {
|
|
113
|
+
// early return true if the _actualReceiveLib is the currently configured one
|
|
114
|
+
(address expectedReceiveLib, bool isDefault) = getReceiveLibrary(_receiver, _srcEid);
|
|
115
|
+
if (_actualReceiveLib == expectedReceiveLib) {
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// check the timeout condition otherwise
|
|
120
|
+
// if the Oapp is using defaultReceiveLibrary, use the default Timeout config
|
|
121
|
+
// otherwise, use the Timeout configured by the Oapp
|
|
122
|
+
Timeout memory timeout = isDefault
|
|
123
|
+
? defaultReceiveLibraryTimeout[_srcEid]
|
|
124
|
+
: receiveLibraryTimeout[_receiver][_srcEid];
|
|
125
|
+
|
|
126
|
+
// requires the _actualReceiveLib to be the same as the one in grace period and the grace period has not expired
|
|
127
|
+
// block.number is uint256 so timeout.expiry must > 0, which implies a non-ZERO value
|
|
128
|
+
if (timeout.lib == _actualReceiveLib && timeout.expiry > block.number) {
|
|
129
|
+
// timeout lib set and has not expired
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// returns false by default
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
//------- Owner interfaces
|
|
138
|
+
/// @dev all libraries have to implement the erc165 interface to prevent wrong configurations
|
|
139
|
+
/// @dev only owner
|
|
140
|
+
function registerLibrary(address _lib) public onlyOwner {
|
|
141
|
+
// must have the right interface
|
|
142
|
+
if (!IERC165(_lib).supportsInterface(type(IMessageLib).interfaceId)) revert Errors.UnsupportedInterface();
|
|
143
|
+
// must have not been registered
|
|
144
|
+
if (isRegisteredLibrary[_lib]) revert Errors.AlreadyRegistered();
|
|
145
|
+
|
|
146
|
+
// insert into both the map and the list
|
|
147
|
+
isRegisteredLibrary[_lib] = true;
|
|
148
|
+
registeredLibraries.push(_lib);
|
|
149
|
+
|
|
150
|
+
emit LibraryRegistered(_lib);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/// @dev owner setting the defaultSendLibrary
|
|
154
|
+
/// @dev can set to the blockedLibrary, which is a registered library
|
|
155
|
+
/// @dev the msgLib must enable the support before they can be registered to the endpoint as the default
|
|
156
|
+
/// @dev only owner
|
|
157
|
+
function setDefaultSendLibrary(
|
|
158
|
+
uint32 _eid,
|
|
159
|
+
address _newLib
|
|
160
|
+
) external onlyOwner onlyRegistered(_newLib) isSendLib(_newLib) onlySupportedEid(_newLib, _eid) {
|
|
161
|
+
address oldLib = defaultSendLibrary[_eid];
|
|
162
|
+
// must provide a different value
|
|
163
|
+
if (oldLib == _newLib) revert Errors.SameValue();
|
|
164
|
+
defaultSendLibrary[_eid] = _newLib;
|
|
165
|
+
emit DefaultSendLibrarySet(_eid, _newLib);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/// @dev owner setting the defaultSendLibrary
|
|
169
|
+
/// @dev must be a registered library (including blockLibrary) with the eid support enabled
|
|
170
|
+
/// @dev in version migration, it can add a grace period to the old library. if the grace period is 0, it will delete the timeout configuration.
|
|
171
|
+
/// @dev only owner
|
|
172
|
+
function setDefaultReceiveLibrary(
|
|
173
|
+
uint32 _eid,
|
|
174
|
+
address _newLib,
|
|
175
|
+
uint256 _gracePeriod
|
|
176
|
+
) external onlyOwner onlyRegistered(_newLib) isReceiveLib(_newLib) onlySupportedEid(_newLib, _eid) {
|
|
177
|
+
address oldLib = defaultReceiveLibrary[_eid];
|
|
178
|
+
// must provide a different value
|
|
179
|
+
if (oldLib == _newLib) revert Errors.SameValue();
|
|
180
|
+
|
|
181
|
+
defaultReceiveLibrary[_eid] = _newLib;
|
|
182
|
+
emit DefaultReceiveLibrarySet(_eid, oldLib, _newLib);
|
|
183
|
+
|
|
184
|
+
if (_gracePeriod > 0) {
|
|
185
|
+
// override the current default timeout to the [old_lib + new expiry]
|
|
186
|
+
Timeout storage timeout = defaultReceiveLibraryTimeout[_eid];
|
|
187
|
+
timeout.lib = oldLib;
|
|
188
|
+
timeout.expiry = block.number + _gracePeriod;
|
|
189
|
+
emit DefaultReceiveLibraryTimeoutSet(_eid, oldLib, timeout.expiry);
|
|
190
|
+
} else {
|
|
191
|
+
// otherwise, remove the old configuration.
|
|
192
|
+
delete defaultReceiveLibraryTimeout[_eid];
|
|
193
|
+
emit DefaultReceiveLibraryTimeoutSet(_eid, oldLib, 0);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/// @dev owner setting the defaultSendLibrary
|
|
198
|
+
/// @dev must be a registered library (including blockLibrary) with the eid support enabled
|
|
199
|
+
/// @dev can used to (1) extend the current configuration (2) force remove the current configuration (3) change to a new configuration
|
|
200
|
+
/// @param _expiry the block number when lib expires
|
|
201
|
+
function setDefaultReceiveLibraryTimeout(
|
|
202
|
+
uint32 _eid,
|
|
203
|
+
address _lib,
|
|
204
|
+
uint256 _expiry
|
|
205
|
+
) external onlyRegistered(_lib) isReceiveLib(_lib) onlySupportedEid(_lib, _eid) onlyOwner {
|
|
206
|
+
if (_expiry == 0) {
|
|
207
|
+
// force remove the current configuration
|
|
208
|
+
delete defaultReceiveLibraryTimeout[_eid];
|
|
209
|
+
} else {
|
|
210
|
+
// override it with new configuration
|
|
211
|
+
if (_expiry <= block.number) revert Errors.InvalidExpiry();
|
|
212
|
+
Timeout storage timeout = defaultReceiveLibraryTimeout[_eid];
|
|
213
|
+
timeout.lib = _lib;
|
|
214
|
+
timeout.expiry = _expiry;
|
|
215
|
+
}
|
|
216
|
+
emit DefaultReceiveLibraryTimeoutSet(_eid, _lib, _expiry);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/// @dev returns true only if both the default send/receive libraries are set
|
|
220
|
+
function isSupportedEid(uint32 _eid) external view returns (bool) {
|
|
221
|
+
return defaultSendLibrary[_eid] != address(0) && defaultReceiveLibrary[_eid] != address(0);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
//------- OApp interfaces
|
|
225
|
+
/// @dev Oapp setting the sendLibrary
|
|
226
|
+
/// @dev must be a registered library (including blockLibrary) with the eid support enabled
|
|
227
|
+
/// @dev authenticated by the Oapp
|
|
228
|
+
function setSendLibrary(
|
|
229
|
+
address _oapp,
|
|
230
|
+
uint32 _eid,
|
|
231
|
+
address _newLib
|
|
232
|
+
) external onlyRegisteredOrDefault(_newLib) isSendLib(_newLib) onlySupportedEid(_newLib, _eid) {
|
|
233
|
+
_assertAuthorized(_oapp);
|
|
234
|
+
|
|
235
|
+
address oldLib = sendLibrary[_oapp][_eid];
|
|
236
|
+
// must provide a different value
|
|
237
|
+
if (oldLib == _newLib) revert Errors.SameValue();
|
|
238
|
+
sendLibrary[_oapp][_eid] = _newLib;
|
|
239
|
+
emit SendLibrarySet(_oapp, _eid, _newLib);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/// @dev Oapp setting the receiveLibrary
|
|
243
|
+
/// @dev must be a registered library (including blockLibrary) with the eid support enabled
|
|
244
|
+
/// @dev in version migration, it can add a grace period to the old library. if the grace period is 0, it will delete the timeout configuration.
|
|
245
|
+
/// @dev authenticated by the Oapp
|
|
246
|
+
/// @param _gracePeriod the number of blocks from now until oldLib expires
|
|
247
|
+
function setReceiveLibrary(
|
|
248
|
+
address _oapp,
|
|
249
|
+
uint32 _eid,
|
|
250
|
+
address _newLib,
|
|
251
|
+
uint256 _gracePeriod
|
|
252
|
+
) external onlyRegisteredOrDefault(_newLib) isReceiveLib(_newLib) onlySupportedEid(_newLib, _eid) {
|
|
253
|
+
_assertAuthorized(_oapp);
|
|
254
|
+
|
|
255
|
+
address oldLib = receiveLibrary[_oapp][_eid];
|
|
256
|
+
// must provide new values
|
|
257
|
+
if (oldLib == _newLib) revert Errors.SameValue();
|
|
258
|
+
receiveLibrary[_oapp][_eid] = _newLib;
|
|
259
|
+
emit ReceiveLibrarySet(_oapp, _eid, oldLib, _newLib);
|
|
260
|
+
|
|
261
|
+
if (_gracePeriod > 0) {
|
|
262
|
+
// to simplify the logic, we only allow to set timeout if neither the new lib nor old lib is DEFAULT_LIB, which would should read the default timeout configurations
|
|
263
|
+
// (1) if the Oapp wants to fall back to the DEFAULT, then set the newLib to DEFAULT with grace period == 0
|
|
264
|
+
// (2) if the Oapp wants to change to a non DEFAULT from DEFAULT, then set the newLib to 'non-default' with _gracePeriod == 0, then use setReceiveLibraryTimeout() interface
|
|
265
|
+
if (oldLib == DEFAULT_LIB || _newLib == DEFAULT_LIB) revert Errors.OnlyNonDefaultLib();
|
|
266
|
+
|
|
267
|
+
// write to storage
|
|
268
|
+
Timeout memory timeout = Timeout({ lib: oldLib, expiry: block.number + _gracePeriod });
|
|
269
|
+
receiveLibraryTimeout[_oapp][_eid] = timeout;
|
|
270
|
+
emit ReceiveLibraryTimeoutSet(_oapp, _eid, oldLib, timeout.expiry);
|
|
271
|
+
} else {
|
|
272
|
+
delete receiveLibraryTimeout[_oapp][_eid];
|
|
273
|
+
emit ReceiveLibraryTimeoutSet(_oapp, _eid, oldLib, 0);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/// @dev Oapp setting the defaultSendLibrary
|
|
278
|
+
/// @dev must be a registered library (including blockLibrary) with the eid support enabled
|
|
279
|
+
/// @dev can used to (1) extend the current configuration (2) force remove the current configuration (3) change to a new configuration
|
|
280
|
+
/// @param _expiry the block number when lib expires
|
|
281
|
+
function setReceiveLibraryTimeout(
|
|
282
|
+
address _oapp,
|
|
283
|
+
uint32 _eid,
|
|
284
|
+
address _lib,
|
|
285
|
+
uint256 _expiry
|
|
286
|
+
) external onlyRegistered(_lib) isReceiveLib(_lib) onlySupportedEid(_lib, _eid) {
|
|
287
|
+
_assertAuthorized(_oapp);
|
|
288
|
+
|
|
289
|
+
(, bool isDefault) = getReceiveLibrary(_oapp, _eid);
|
|
290
|
+
// if current library is DEFAULT, Oapp cant set the timeout
|
|
291
|
+
if (isDefault) revert Errors.OnlyNonDefaultLib();
|
|
292
|
+
|
|
293
|
+
if (_expiry == 0) {
|
|
294
|
+
// force remove the current configuration
|
|
295
|
+
delete receiveLibraryTimeout[_oapp][_eid];
|
|
296
|
+
} else {
|
|
297
|
+
// override it with new configuration
|
|
298
|
+
if (_expiry <= block.number) revert Errors.InvalidExpiry();
|
|
299
|
+
Timeout storage timeout = receiveLibraryTimeout[_oapp][_eid];
|
|
300
|
+
timeout.lib = _lib;
|
|
301
|
+
timeout.expiry = _expiry;
|
|
302
|
+
}
|
|
303
|
+
emit ReceiveLibraryTimeoutSet(_oapp, _eid, _lib, _expiry);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
//------- library config setter/getter. all pass-through functions to the msgLib
|
|
307
|
+
|
|
308
|
+
/// @dev authenticated by the _oapp
|
|
309
|
+
function setConfig(address _oapp, address _lib, SetConfigParam[] calldata _params) external onlyRegistered(_lib) {
|
|
310
|
+
_assertAuthorized(_oapp);
|
|
311
|
+
|
|
312
|
+
IMessageLib(_lib).setConfig(_oapp, _params);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/// @dev a view function to query the current configuration of the OApp
|
|
316
|
+
function getConfig(
|
|
317
|
+
address _oapp,
|
|
318
|
+
address _lib,
|
|
319
|
+
uint32 _eid,
|
|
320
|
+
uint32 _configType
|
|
321
|
+
) external view onlyRegistered(_lib) returns (bytes memory config) {
|
|
322
|
+
return IMessageLib(_lib).getConfig(_eid, _oapp, _configType);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function _assertAuthorized(address _oapp) internal virtual;
|
|
326
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
// SPDX-License-Identifier: LZBL-1.2
|
|
2
|
+
|
|
3
|
+
pragma solidity ^0.8.22;
|
|
4
|
+
|
|
5
|
+
import { IMessagingChannel } from "./interfaces/IMessagingChannel.sol";
|
|
6
|
+
import { Errors } from "./libs/Errors.sol";
|
|
7
|
+
import { GUID } from "./libs/GUID.sol";
|
|
8
|
+
|
|
9
|
+
abstract contract MessagingChannel is IMessagingChannel {
|
|
10
|
+
bytes32 public constant EMPTY_PAYLOAD_HASH = bytes32(0);
|
|
11
|
+
bytes32 public constant NIL_PAYLOAD_HASH = bytes32(type(uint256).max);
|
|
12
|
+
|
|
13
|
+
// The universally unique id (UUID) of this deployed Endpoint
|
|
14
|
+
uint32 public immutable eid;
|
|
15
|
+
|
|
16
|
+
mapping(address receiver => mapping(uint32 srcEid => mapping(bytes32 sender => uint64 nonce)))
|
|
17
|
+
public lazyInboundNonce;
|
|
18
|
+
mapping(address receiver => mapping(uint32 srcEid => mapping(bytes32 sender => mapping(uint64 inboundNonce => bytes32 payloadHash))))
|
|
19
|
+
public inboundPayloadHash;
|
|
20
|
+
mapping(address sender => mapping(uint32 dstEid => mapping(bytes32 receiver => uint64 nonce))) public outboundNonce;
|
|
21
|
+
|
|
22
|
+
/// @param _eid is the universally unique id (UUID) of this deployed Endpoint
|
|
23
|
+
constructor(uint32 _eid) {
|
|
24
|
+
eid = _eid;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/// @dev increase and return the next outbound nonce
|
|
28
|
+
function _outbound(address _sender, uint32 _dstEid, bytes32 _receiver) internal returns (uint64 nonce) {
|
|
29
|
+
unchecked {
|
|
30
|
+
nonce = ++outboundNonce[_sender][_dstEid][_receiver];
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/// @dev inbound won't update the nonce eagerly to allow unordered verification
|
|
35
|
+
/// @dev instead, it will update the nonce lazily when the message is received
|
|
36
|
+
/// @dev messages can only be cleared in order to preserve censorship-resistance
|
|
37
|
+
function _inbound(
|
|
38
|
+
address _receiver,
|
|
39
|
+
uint32 _srcEid,
|
|
40
|
+
bytes32 _sender,
|
|
41
|
+
uint64 _nonce,
|
|
42
|
+
bytes32 _payloadHash
|
|
43
|
+
) internal {
|
|
44
|
+
if (_payloadHash == EMPTY_PAYLOAD_HASH) revert Errors.InvalidPayloadHash();
|
|
45
|
+
inboundPayloadHash[_receiver][_srcEid][_sender][_nonce] = _payloadHash;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/// @dev returns the max index of the longest gapless sequence of verified msg nonces.
|
|
49
|
+
/// @dev the uninitialized value is 0. the first nonce is always 1
|
|
50
|
+
/// @dev it starts from the lazyInboundNonce (last checkpoint) and iteratively check if the next nonce has been verified
|
|
51
|
+
/// @dev this function can OOG if too many backlogs, but it can be trivially fixed by just clearing some prior messages
|
|
52
|
+
/// @dev NOTE: Oapp explicitly skipped nonces count as "verified" for these purposes
|
|
53
|
+
/// @dev eg. [1,2,3,4,6,7] => 4, [1,2,6,8,10] => 2, [1,3,4,5,6] => 1
|
|
54
|
+
function inboundNonce(address _receiver, uint32 _srcEid, bytes32 _sender) public view returns (uint64) {
|
|
55
|
+
uint64 nonceCursor = lazyInboundNonce[_receiver][_srcEid][_sender];
|
|
56
|
+
|
|
57
|
+
// find the effective inbound currentNonce
|
|
58
|
+
unchecked {
|
|
59
|
+
while (_hasPayloadHash(_receiver, _srcEid, _sender, nonceCursor + 1)) {
|
|
60
|
+
++nonceCursor;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return nonceCursor;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/// @dev checks if the storage slot is not initialized. Assumes computationally infeasible that payload can hash to 0
|
|
67
|
+
function _hasPayloadHash(
|
|
68
|
+
address _receiver,
|
|
69
|
+
uint32 _srcEid,
|
|
70
|
+
bytes32 _sender,
|
|
71
|
+
uint64 _nonce
|
|
72
|
+
) internal view returns (bool) {
|
|
73
|
+
return inboundPayloadHash[_receiver][_srcEid][_sender][_nonce] != EMPTY_PAYLOAD_HASH;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/// @dev the caller must provide _nonce to prevent skipping the unintended nonce
|
|
77
|
+
/// @dev it could happen in some race conditions, e.g. to skip nonce 3, but nonce 3 was consumed first
|
|
78
|
+
/// @dev usage: skipping the next nonce to prevent message verification, e.g. skip a message when Precrime throws alerts
|
|
79
|
+
/// @dev if the Oapp wants to skip a verified message, it should call the clear() function instead
|
|
80
|
+
/// @dev after skipping, the lazyInboundNonce is set to the provided nonce, which makes the inboundNonce also the provided nonce
|
|
81
|
+
/// @dev ie. allows the Oapp to increment the lazyInboundNonce without having had that corresponding msg be verified
|
|
82
|
+
function skip(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce) external {
|
|
83
|
+
_assertAuthorized(_oapp);
|
|
84
|
+
|
|
85
|
+
if (_nonce != inboundNonce(_oapp, _srcEid, _sender) + 1) revert Errors.InvalidNonce(_nonce);
|
|
86
|
+
lazyInboundNonce[_oapp][_srcEid][_sender] = _nonce;
|
|
87
|
+
emit InboundNonceSkipped(_srcEid, _sender, _oapp, _nonce);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/// @dev Marks a packet as verified, but disallows execution until it is re-verified.
|
|
91
|
+
/// @dev Reverts if the provided _payloadHash does not match the currently verified payload hash.
|
|
92
|
+
/// @dev A non-verified nonce can be nilified by passing EMPTY_PAYLOAD_HASH for _payloadHash.
|
|
93
|
+
/// @dev Assumes the computational intractability of finding a payload that hashes to bytes32.max.
|
|
94
|
+
/// @dev Authenticated by the caller
|
|
95
|
+
function nilify(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce, bytes32 _payloadHash) external {
|
|
96
|
+
_assertAuthorized(_oapp);
|
|
97
|
+
|
|
98
|
+
bytes32 curPayloadHash = inboundPayloadHash[_oapp][_srcEid][_sender][_nonce];
|
|
99
|
+
if (curPayloadHash != _payloadHash) revert Errors.PayloadHashNotFound(curPayloadHash, _payloadHash);
|
|
100
|
+
if (_nonce <= lazyInboundNonce[_oapp][_srcEid][_sender] && curPayloadHash == EMPTY_PAYLOAD_HASH)
|
|
101
|
+
revert Errors.InvalidNonce(_nonce);
|
|
102
|
+
// set it to nil
|
|
103
|
+
inboundPayloadHash[_oapp][_srcEid][_sender][_nonce] = NIL_PAYLOAD_HASH;
|
|
104
|
+
emit PacketNilified(_srcEid, _sender, _oapp, _nonce, _payloadHash);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/// @dev Marks a nonce as unexecutable and un-verifiable. The nonce can never be re-verified or executed.
|
|
108
|
+
/// @dev Reverts if the provided _payloadHash does not match the currently verified payload hash.
|
|
109
|
+
/// @dev Only packets with nonces less than or equal to the lazy inbound nonce can be burned.
|
|
110
|
+
/// @dev Reverts if the nonce has already been executed.
|
|
111
|
+
/// @dev Authenticated by the caller
|
|
112
|
+
function burn(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce, bytes32 _payloadHash) external {
|
|
113
|
+
_assertAuthorized(_oapp);
|
|
114
|
+
|
|
115
|
+
bytes32 curPayloadHash = inboundPayloadHash[_oapp][_srcEid][_sender][_nonce];
|
|
116
|
+
if (curPayloadHash != _payloadHash) revert Errors.PayloadHashNotFound(curPayloadHash, _payloadHash);
|
|
117
|
+
if (curPayloadHash == EMPTY_PAYLOAD_HASH || _nonce > lazyInboundNonce[_oapp][_srcEid][_sender])
|
|
118
|
+
revert Errors.InvalidNonce(_nonce);
|
|
119
|
+
delete inboundPayloadHash[_oapp][_srcEid][_sender][_nonce];
|
|
120
|
+
emit PacketBurnt(_srcEid, _sender, _oapp, _nonce, _payloadHash);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/// @dev calling this function will clear the stored message and increment the lazyInboundNonce to the provided nonce
|
|
124
|
+
/// @dev if a lot of messages are queued, the messages can be cleared with a smaller step size to prevent OOG
|
|
125
|
+
/// @dev NOTE: this function does not change inboundNonce, it only changes the lazyInboundNonce up to the provided nonce
|
|
126
|
+
function _clearPayload(
|
|
127
|
+
address _receiver,
|
|
128
|
+
uint32 _srcEid,
|
|
129
|
+
bytes32 _sender,
|
|
130
|
+
uint64 _nonce,
|
|
131
|
+
bytes memory _payload
|
|
132
|
+
) internal returns (bytes32 actualHash) {
|
|
133
|
+
uint64 currentNonce = lazyInboundNonce[_receiver][_srcEid][_sender];
|
|
134
|
+
if (_nonce > currentNonce) {
|
|
135
|
+
unchecked {
|
|
136
|
+
// try to lazily update the inboundNonce till the _nonce
|
|
137
|
+
for (uint64 i = currentNonce + 1; i <= _nonce; ++i) {
|
|
138
|
+
if (!_hasPayloadHash(_receiver, _srcEid, _sender, i)) revert Errors.InvalidNonce(i);
|
|
139
|
+
}
|
|
140
|
+
lazyInboundNonce[_receiver][_srcEid][_sender] = _nonce;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// check the hash of the payload to verify the executor has given the proper payload that has been verified
|
|
145
|
+
actualHash = keccak256(_payload);
|
|
146
|
+
bytes32 expectedHash = inboundPayloadHash[_receiver][_srcEid][_sender][_nonce];
|
|
147
|
+
if (expectedHash != actualHash) revert Errors.PayloadHashNotFound(expectedHash, actualHash);
|
|
148
|
+
|
|
149
|
+
// remove it from the storage
|
|
150
|
+
delete inboundPayloadHash[_receiver][_srcEid][_sender][_nonce];
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/// @dev returns the GUID for the next message given the path
|
|
154
|
+
/// @dev the Oapp might want to include the GUID into the message in some cases
|
|
155
|
+
function nextGuid(address _sender, uint32 _dstEid, bytes32 _receiver) external view returns (bytes32) {
|
|
156
|
+
uint64 nextNonce = outboundNonce[_sender][_dstEid][_receiver] + 1;
|
|
157
|
+
return GUID.generate(nextNonce, eid, _sender, _dstEid, _receiver);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function _assertAuthorized(address _oapp) internal virtual;
|
|
161
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// SPDX-License-Identifier: LZBL-1.2
|
|
2
|
+
|
|
3
|
+
pragma solidity ^0.8.22;
|
|
4
|
+
|
|
5
|
+
import { IMessagingComposer } from "./interfaces/IMessagingComposer.sol";
|
|
6
|
+
import { ILayerZeroComposer } from "./interfaces/ILayerZeroComposer.sol";
|
|
7
|
+
import { Errors } from "./libs/Errors.sol";
|
|
8
|
+
|
|
9
|
+
abstract contract MessagingComposer is IMessagingComposer {
|
|
10
|
+
bytes32 private constant NO_MESSAGE_HASH = bytes32(0);
|
|
11
|
+
bytes32 private constant RECEIVED_MESSAGE_HASH = bytes32(uint256(1));
|
|
12
|
+
|
|
13
|
+
mapping(address from => mapping(address to => mapping(bytes32 guid => mapping(uint16 index => bytes32 messageHash))))
|
|
14
|
+
public composeQueue;
|
|
15
|
+
|
|
16
|
+
/// @dev the Oapp sends the lzCompose message to the endpoint
|
|
17
|
+
/// @dev the composer MUST assert the sender because anyone can send compose msg with this function
|
|
18
|
+
/// @dev with the same GUID, the Oapp can send compose to multiple _composer at the same time
|
|
19
|
+
/// @dev authenticated by the msg.sender
|
|
20
|
+
/// @param _to the address which will receive the composed message
|
|
21
|
+
/// @param _guid the message guid
|
|
22
|
+
/// @param _message the message
|
|
23
|
+
function sendCompose(address _to, bytes32 _guid, uint16 _index, bytes calldata _message) external {
|
|
24
|
+
// must have not been sent before
|
|
25
|
+
if (composeQueue[msg.sender][_to][_guid][_index] != NO_MESSAGE_HASH) revert Errors.ComposeExists();
|
|
26
|
+
composeQueue[msg.sender][_to][_guid][_index] = keccak256(_message);
|
|
27
|
+
emit ComposeSent(msg.sender, _to, _guid, _index, _message);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/// @dev execute a composed messages from the sender to the composer (receiver)
|
|
31
|
+
/// @dev the execution provides the execution context (caller, extraData) to the receiver.
|
|
32
|
+
/// the receiver can optionally assert the caller and validate the untrusted extraData
|
|
33
|
+
/// @dev can not re-entrant
|
|
34
|
+
/// @param _from the address which sends the composed message. in most cases, it is the Oapp's address.
|
|
35
|
+
/// @param _to the address which receives the composed message
|
|
36
|
+
/// @param _guid the message guid
|
|
37
|
+
/// @param _message the message
|
|
38
|
+
/// @param _extraData the extra data provided by the executor. this data is untrusted and should be validated.
|
|
39
|
+
function lzCompose(
|
|
40
|
+
address _from,
|
|
41
|
+
address _to,
|
|
42
|
+
bytes32 _guid,
|
|
43
|
+
uint16 _index,
|
|
44
|
+
bytes calldata _message,
|
|
45
|
+
bytes calldata _extraData
|
|
46
|
+
) external payable {
|
|
47
|
+
// assert the validity
|
|
48
|
+
bytes32 expectedHash = composeQueue[_from][_to][_guid][_index];
|
|
49
|
+
bytes32 actualHash = keccak256(_message);
|
|
50
|
+
if (expectedHash != actualHash) revert Errors.ComposeNotFound(expectedHash, actualHash);
|
|
51
|
+
|
|
52
|
+
// marks the message as received to prevent reentrancy
|
|
53
|
+
// cannot just delete the value, otherwise the message can be sent again and could result in some undefined behaviour
|
|
54
|
+
// even though the sender(composing Oapp) is implicitly fully trusted by the composer.
|
|
55
|
+
// eg. sender may not even realize it has such a bug
|
|
56
|
+
composeQueue[_from][_to][_guid][_index] = RECEIVED_MESSAGE_HASH;
|
|
57
|
+
ILayerZeroComposer(_to).lzCompose{ value: msg.value }(_from, _guid, _message, msg.sender, _extraData);
|
|
58
|
+
emit ComposeDelivered(_from, _to, _guid, _index);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/// @param _from the address which sends the composed message
|
|
62
|
+
/// @param _to the address which receives the composed message
|
|
63
|
+
/// @param _guid the message guid
|
|
64
|
+
/// @param _message the message
|
|
65
|
+
/// @param _extraData the extra data provided by the executor
|
|
66
|
+
/// @param _reason the reason why the message is not received
|
|
67
|
+
function lzComposeAlert(
|
|
68
|
+
address _from,
|
|
69
|
+
address _to,
|
|
70
|
+
bytes32 _guid,
|
|
71
|
+
uint16 _index,
|
|
72
|
+
uint256 _gas,
|
|
73
|
+
uint256 _value,
|
|
74
|
+
bytes calldata _message,
|
|
75
|
+
bytes calldata _extraData,
|
|
76
|
+
bytes calldata _reason
|
|
77
|
+
) external {
|
|
78
|
+
emit LzComposeAlert(_from, _to, msg.sender, _guid, _index, _gas, _value, _message, _extraData, _reason);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// SPDX-License-Identifier: LZBL-1.2
|
|
2
|
+
|
|
3
|
+
pragma solidity ^0.8.22;
|
|
4
|
+
|
|
5
|
+
import { IMessagingContext } from "./interfaces/IMessagingContext.sol";
|
|
6
|
+
import { Errors } from "./libs/Errors.sol";
|
|
7
|
+
|
|
8
|
+
/// this contract acts as a non-reentrancy guard and a source of messaging context
|
|
9
|
+
/// the context includes the remote eid and the sender address
|
|
10
|
+
/// it separates the send and receive context to allow messaging receipts (send back on receive())
|
|
11
|
+
abstract contract MessagingContext is IMessagingContext {
|
|
12
|
+
uint256 private constant NOT_ENTERED = 1;
|
|
13
|
+
uint256 private _sendContext = NOT_ENTERED;
|
|
14
|
+
|
|
15
|
+
/// @dev the sendContext is set to 8 bytes 0s + 4 bytes eid + 20 bytes sender
|
|
16
|
+
modifier sendContext(uint32 _dstEid, address _sender) {
|
|
17
|
+
if (_sendContext != NOT_ENTERED) revert Errors.SendReentrancy();
|
|
18
|
+
_sendContext = (uint256(_dstEid) << 160) | uint160(_sender);
|
|
19
|
+
_;
|
|
20
|
+
_sendContext = NOT_ENTERED;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/// @dev returns true if sending message
|
|
24
|
+
function isSendingMessage() public view returns (bool) {
|
|
25
|
+
return _sendContext != NOT_ENTERED;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/// @dev returns (eid, sender) if sending message, (0, 0) otherwise
|
|
29
|
+
function getSendContext() external view returns (uint32, address) {
|
|
30
|
+
return isSendingMessage() ? _getSendContext(_sendContext) : (0, address(0));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function _getSendContext(uint256 _context) internal pure returns (uint32, address) {
|
|
34
|
+
return (uint32(_context >> 160), address(uint160(_context)));
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
pragma solidity >=0.8.0;
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @title ILayerZeroComposer
|
|
7
|
+
*/
|
|
8
|
+
interface ILayerZeroComposer {
|
|
9
|
+
/**
|
|
10
|
+
* @notice Composes a LayerZero message from an OApp.
|
|
11
|
+
* @param _from The address initiating the composition, typically the OApp where the lzReceive was called.
|
|
12
|
+
* @param _guid The unique identifier for the corresponding LayerZero src/dst tx.
|
|
13
|
+
* @param _message The composed message payload in bytes. NOT necessarily the same payload passed via lzReceive.
|
|
14
|
+
* @param _executor The address of the executor for the composed message.
|
|
15
|
+
* @param _extraData Additional arbitrary data in bytes passed by the entity who executes the lzCompose.
|
|
16
|
+
*/
|
|
17
|
+
function lzCompose(
|
|
18
|
+
address _from,
|
|
19
|
+
bytes32 _guid,
|
|
20
|
+
bytes calldata _message,
|
|
21
|
+
address _executor,
|
|
22
|
+
bytes calldata _extraData
|
|
23
|
+
) external payable;
|
|
24
|
+
}
|