@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.
Files changed (51) hide show
  1. package/artifacts/contracts/EndpointV2.sol/EndpointV2.json +2364 -0
  2. package/artifacts/contracts/EndpointV2Alt.sol/EndpointV2Alt.json +2379 -0
  3. package/artifacts/contracts/MessageLibManager.sol/MessageLibManager.json +783 -0
  4. package/artifacts/contracts/MessagingChannel.sol/MessagingChannel.json +426 -0
  5. package/artifacts/contracts/MessagingComposer.sol/MessagingComposer.json +320 -0
  6. package/artifacts/contracts/MessagingContext.sol/MessagingContext.json +42 -0
  7. package/artifacts/contracts/interfaces/ILayerZeroComposer.sol/ILayerZeroComposer.json +44 -0
  8. package/artifacts/contracts/interfaces/ILayerZeroEndpointV2.sol/ILayerZeroEndpointV2.json +1844 -0
  9. package/artifacts/contracts/interfaces/ILayerZeroReceiver.sol/ILayerZeroReceiver.json +121 -0
  10. package/artifacts/contracts/interfaces/IMessageLib.sol/IMessageLib.json +149 -0
  11. package/artifacts/contracts/interfaces/IMessageLibManager.sol/IMessageLibManager.json +629 -0
  12. package/artifacts/contracts/interfaces/IMessagingChannel.sol/IMessagingChannel.json +344 -0
  13. package/artifacts/contracts/interfaces/IMessagingComposer.sol/IMessagingComposer.json +246 -0
  14. package/artifacts/contracts/interfaces/IMessagingContext.sol/IMessagingContext.json +42 -0
  15. package/artifacts/contracts/interfaces/ISendLib.sol/ISendLib.json +364 -0
  16. package/artifacts/contracts/libs/AddressCast.sol/AddressCast.json +10 -0
  17. package/artifacts/contracts/libs/CalldataBytesLib.sol/CalldataBytesLib.json +10 -0
  18. package/artifacts/contracts/libs/Errors.sol/Errors.json +226 -0
  19. package/artifacts/contracts/libs/GUID.sol/GUID.json +10 -0
  20. package/artifacts/contracts/libs/Transfer.sol/Transfer.json +32 -0
  21. package/artifacts/contracts/messagelib/BlockedMessageLib.sol/BlockedMessageLib.json +94 -0
  22. package/artifacts/contracts/messagelib/SimpleMessageLib.sol/SimpleMessageLib.json +591 -0
  23. package/artifacts/contracts/messagelib/libs/BitMaps.sol/BitMaps.json +10 -0
  24. package/artifacts/contracts/messagelib/libs/ExecutorOptions.sol/ExecutorOptions.json +26 -0
  25. package/artifacts/contracts/messagelib/libs/PacketV1Codec.sol/PacketV1Codec.json +10 -0
  26. package/contracts/EndpointV2.sol +403 -0
  27. package/contracts/EndpointV2Alt.sol +50 -0
  28. package/contracts/MessageLibManager.sol +326 -0
  29. package/contracts/MessagingChannel.sol +161 -0
  30. package/contracts/MessagingComposer.sol +80 -0
  31. package/contracts/MessagingContext.sol +36 -0
  32. package/contracts/interfaces/ILayerZeroComposer.sol +24 -0
  33. package/contracts/interfaces/ILayerZeroEndpointV2.sol +98 -0
  34. package/contracts/interfaces/ILayerZeroReceiver.sol +20 -0
  35. package/contracts/interfaces/IMessageLib.sol +26 -0
  36. package/contracts/interfaces/IMessageLibManager.sol +68 -0
  37. package/contracts/interfaces/IMessagingChannel.sol +32 -0
  38. package/contracts/interfaces/IMessagingComposer.sol +38 -0
  39. package/contracts/interfaces/IMessagingContext.sol +9 -0
  40. package/contracts/interfaces/ISendLib.sol +36 -0
  41. package/contracts/libs/AddressCast.sol +40 -0
  42. package/contracts/libs/CalldataBytesLib.sol +58 -0
  43. package/contracts/libs/Errors.sol +42 -0
  44. package/contracts/libs/GUID.sol +19 -0
  45. package/contracts/libs/Transfer.sol +34 -0
  46. package/contracts/messagelib/BlockedMessageLib.sol +30 -0
  47. package/contracts/messagelib/SimpleMessageLib.sol +149 -0
  48. package/contracts/messagelib/libs/BitMaps.sol +26 -0
  49. package/contracts/messagelib/libs/ExecutorOptions.sol +85 -0
  50. package/contracts/messagelib/libs/PacketV1Codec.sol +108 -0
  51. 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
+ }