Skip to content

Commit 38722b9

Browse files
gator-boiRahul Maganti
and
Rahul Maganti
authored
evm: reorganize WormholeTransceiver and NttManager (#196)
* evm: separate Transceiver and NttManger into separate directories * evm: move natspec to ITransceiver interface * evm: clean up WormholeTransceiver * evm: remove internal admin functions * evm: clean up NttManager * evm: add _checkImmutables to WormholeTransceiverState * fix build issues on rebase * evm: address pr feedback --------- Co-authored-by: gator-boi <gator-boi@users.noreply.github.com> Co-authored-by: Rahul Maganti <rahulmaganti@Rahuls-MacBook-Pro.local>
1 parent f78b859 commit 38722b9

24 files changed

+1429
-1155
lines changed

evm/src/NttManager.sol evm/src/NttManager/NttManager.sol

+176-513
Large diffs are not rendered by default.
+363
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,363 @@
1+
// SPDX-License-Identifier: Apache 2
2+
pragma solidity >=0.8.8 <0.9.0;
3+
4+
import "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
5+
import "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol";
6+
import "openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Burnable.sol";
7+
8+
import "wormhole-solidity-sdk/Utils.sol";
9+
import "wormhole-solidity-sdk/libraries/BytesParsing.sol";
10+
11+
import "../libraries/external/OwnableUpgradeable.sol";
12+
import "../libraries/external/ReentrancyGuardUpgradeable.sol";
13+
import "../libraries/TransceiverStructs.sol";
14+
import "../libraries/TransceiverHelpers.sol";
15+
import "../libraries/RateLimiter.sol";
16+
import "../libraries/PausableOwnable.sol";
17+
import "../libraries/Implementation.sol";
18+
19+
import "../interfaces/INttManager.sol";
20+
import "../interfaces/INttManagerState.sol";
21+
import "../interfaces/INttManagerEvents.sol";
22+
import "../interfaces/INTTToken.sol";
23+
import "../interfaces/ITransceiver.sol";
24+
25+
import "./TransceiverRegistry.sol";
26+
import "./../NttTrimmer.sol";
27+
28+
abstract contract NttManagerState is
29+
INttManagerState,
30+
INttManagerEvents,
31+
RateLimiter,
32+
NttTrimmer,
33+
TransceiverRegistry,
34+
PausableOwnable,
35+
ReentrancyGuardUpgradeable,
36+
Implementation
37+
{
38+
// =============== Immutables ============================================================
39+
40+
address public immutable token;
41+
address immutable deployer;
42+
INttManager.Mode public immutable mode;
43+
uint16 public immutable chainId;
44+
uint256 immutable evmChainId;
45+
46+
// =============== Setup =================================================================
47+
48+
constructor(
49+
address _token,
50+
INttManager.Mode _mode,
51+
uint16 _chainId,
52+
uint64 _rateLimitDuration
53+
) RateLimiter(_rateLimitDuration) NttTrimmer(_token) {
54+
token = _token;
55+
mode = _mode;
56+
chainId = _chainId;
57+
evmChainId = block.chainid;
58+
// save the deployer (check this on initialization)
59+
deployer = msg.sender;
60+
}
61+
62+
function __NttManager_init() internal onlyInitializing {
63+
// check if the owner is the deployer of this contract
64+
if (msg.sender != deployer) {
65+
revert UnexpectedDeployer(deployer, msg.sender);
66+
}
67+
__PausedOwnable_init(msg.sender, msg.sender);
68+
__ReentrancyGuard_init();
69+
}
70+
71+
function _initialize() internal virtual override {
72+
__NttManager_init();
73+
_checkThresholdInvariants();
74+
_checkTransceiversInvariants();
75+
}
76+
77+
function _migrate() internal virtual override {
78+
_checkThresholdInvariants();
79+
_checkTransceiversInvariants();
80+
}
81+
82+
// =============== Storage ==============================================================
83+
84+
bytes32 private constant MESSAGE_ATTESTATIONS_SLOT =
85+
bytes32(uint256(keccak256("ntt.messageAttestations")) - 1);
86+
87+
bytes32 private constant MESSAGE_SEQUENCE_SLOT =
88+
bytes32(uint256(keccak256("ntt.messageSequence")) - 1);
89+
90+
bytes32 private constant PEERS_SLOT = bytes32(uint256(keccak256("ntt.peers")) - 1);
91+
92+
bytes32 private constant THRESHOLD_SLOT = bytes32(uint256(keccak256("ntt.threshold")) - 1);
93+
94+
// =============== Storage Getters/Setters ==============================================
95+
96+
function _getThresholdStorage() private pure returns (INttManager._Threshold storage $) {
97+
uint256 slot = uint256(THRESHOLD_SLOT);
98+
assembly ("memory-safe") {
99+
$.slot := slot
100+
}
101+
}
102+
103+
function _getMessageAttestationsStorage()
104+
internal
105+
pure
106+
returns (mapping(bytes32 => INttManager.AttestationInfo) storage $)
107+
{
108+
uint256 slot = uint256(MESSAGE_ATTESTATIONS_SLOT);
109+
assembly ("memory-safe") {
110+
$.slot := slot
111+
}
112+
}
113+
114+
function _getMessageSequenceStorage() internal pure returns (INttManager._Sequence storage $) {
115+
uint256 slot = uint256(MESSAGE_SEQUENCE_SLOT);
116+
assembly ("memory-safe") {
117+
$.slot := slot
118+
}
119+
}
120+
121+
function _getPeersStorage() internal pure returns (mapping(uint16 => bytes32) storage $) {
122+
uint256 slot = uint256(PEERS_SLOT);
123+
assembly ("memory-safe") {
124+
$.slot := slot
125+
}
126+
}
127+
128+
// =============== Public Getters ========================================================
129+
130+
/// @inheritdoc INttManagerState
131+
function getMode() public view returns (uint8) {
132+
return uint8(mode);
133+
}
134+
135+
/// @inheritdoc INttManagerState
136+
function getThreshold() public view returns (uint8) {
137+
return _getThresholdStorage().num;
138+
}
139+
140+
/// @inheritdoc INttManagerState
141+
function isMessageApproved(bytes32 digest) public view returns (bool) {
142+
uint8 threshold = getThreshold();
143+
return messageAttestations(digest) >= threshold && threshold > 0;
144+
}
145+
146+
/// @inheritdoc INttManagerState
147+
function nextMessageSequence() external view returns (uint64) {
148+
return _getMessageSequenceStorage().num;
149+
}
150+
151+
/// @inheritdoc INttManagerState
152+
function isMessageExecuted(bytes32 digest) public view returns (bool) {
153+
return _getMessageAttestationsStorage()[digest].executed;
154+
}
155+
156+
/// @inheritdoc INttManagerState
157+
function getPeer(uint16 chainId_) public view returns (bytes32) {
158+
return _getPeersStorage()[chainId_];
159+
}
160+
161+
/// @inheritdoc INttManagerState
162+
function transceiverAttestedToMessage(bytes32 digest, uint8 index) public view returns (bool) {
163+
return
164+
_getMessageAttestationsStorage()[digest].attestedTransceivers & uint64(1 << index) == 1;
165+
}
166+
167+
/// @inheritdoc INttManagerState
168+
function messageAttestations(bytes32 digest) public view returns (uint8 count) {
169+
return countSetBits(_getMessageAttestations(digest));
170+
}
171+
172+
// =============== ADMIN ==============================================================
173+
174+
/// @inheritdoc INttManagerState
175+
function upgrade(address newImplementation) external onlyOwner {
176+
_upgrade(newImplementation);
177+
}
178+
179+
/// @inheritdoc INttManagerState
180+
function pause() public onlyOwnerOrPauser {
181+
_pause();
182+
}
183+
184+
/// @notice Transfer ownership of the Manager contract and all Endpoint contracts to a new owner.
185+
function transferOwnership(address newOwner) public override onlyOwner {
186+
super.transferOwnership(newOwner);
187+
// loop through all the registered transceivers and set the new owner of each transceiver to the newOwner
188+
address[] storage _registeredTransceivers = _getRegisteredTransceiversStorage();
189+
_checkRegisteredTransceiversInvariants();
190+
191+
for (uint256 i = 0; i < _registeredTransceivers.length; i++) {
192+
ITransceiver(_registeredTransceivers[i]).transferTransceiverOwnership(newOwner);
193+
}
194+
}
195+
196+
/// @inheritdoc INttManagerState
197+
function setTransceiver(address transceiver) external onlyOwner {
198+
_setTransceiver(transceiver);
199+
200+
INttManager._Threshold storage _threshold = _getThresholdStorage();
201+
// We do not automatically increase the threshold here.
202+
// Automatically increasing the threshold can result in a scenario
203+
// where in-flight messages can't be redeemed.
204+
// For example: Assume there is 1 Transceiver and the threshold is 1.
205+
// If we were to add a new Transceiver, the threshold would increase to 2.
206+
// However, all messages that are either in-flight or that are sent on
207+
// a source chain that does not yet have 2 Transceivers will only have been
208+
// sent from a single transceiver, so they would never be able to get
209+
// redeemed.
210+
// Instead, we leave it up to the owner to manually update the threshold
211+
// after some period of time, ideally once all chains have the new Transceiver
212+
// and transfers that were sent via the old configuration are all complete.
213+
// However if the threshold is 0 (the initial case) we do increment to 1.
214+
if (_threshold.num == 0) {
215+
_threshold.num = 1;
216+
}
217+
218+
emit TransceiverAdded(transceiver, _getNumTransceiversStorage().enabled, _threshold.num);
219+
}
220+
221+
/// @inheritdoc INttManagerState
222+
function removeTransceiver(address transceiver) external onlyOwner {
223+
_removeTransceiver(transceiver);
224+
225+
INttManager._Threshold storage _threshold = _getThresholdStorage();
226+
uint8 numEnabledTransceivers = _getNumTransceiversStorage().enabled;
227+
228+
if (numEnabledTransceivers < _threshold.num) {
229+
_threshold.num = numEnabledTransceivers;
230+
}
231+
232+
emit TransceiverRemoved(transceiver, _threshold.num);
233+
}
234+
235+
/// @inheritdoc INttManagerState
236+
function setThreshold(uint8 threshold) external onlyOwner {
237+
if (threshold == 0) {
238+
revert ZeroThreshold();
239+
}
240+
241+
INttManager._Threshold storage _threshold = _getThresholdStorage();
242+
uint8 oldThreshold = _threshold.num;
243+
244+
_threshold.num = threshold;
245+
_checkThresholdInvariants();
246+
247+
emit ThresholdChanged(oldThreshold, threshold);
248+
}
249+
250+
/// @inheritdoc INttManagerState
251+
function setPeer(uint16 peerChainId, bytes32 peerContract) public onlyOwner {
252+
if (peerChainId == 0) {
253+
revert InvalidPeerChainIdZero();
254+
}
255+
if (peerContract == bytes32(0)) {
256+
revert InvalidPeerZeroAddress();
257+
}
258+
259+
bytes32 oldPeerContract = _getPeersStorage()[peerChainId];
260+
261+
_getPeersStorage()[peerChainId] = peerContract;
262+
263+
emit PeerUpdated(peerChainId, oldPeerContract, peerContract);
264+
}
265+
266+
/// @inheritdoc INttManagerState
267+
function setOutboundLimit(uint256 limit) external onlyOwner {
268+
_setOutboundLimit(_nttTrimmer(limit));
269+
}
270+
271+
/// @inheritdoc INttManagerState
272+
function setInboundLimit(uint256 limit, uint16 chainId_) external onlyOwner {
273+
_setInboundLimit(_nttTrimmer(limit), chainId_);
274+
}
275+
276+
// =============== Internal ==============================================================
277+
278+
function _setTransceiverAttestedToMessage(bytes32 digest, uint8 index) internal {
279+
_getMessageAttestationsStorage()[digest].attestedTransceivers |= uint64(1 << index);
280+
}
281+
282+
function _setTransceiverAttestedToMessage(bytes32 digest, address transceiver) internal {
283+
_setTransceiverAttestedToMessage(digest, _getTransceiverInfosStorage()[transceiver].index);
284+
285+
emit MessageAttestedTo(
286+
digest, transceiver, _getTransceiverInfosStorage()[transceiver].index
287+
);
288+
}
289+
290+
/// @dev Returns the bitmap of attestations from enabled transceivers for a given message.
291+
function _getMessageAttestations(bytes32 digest) internal view returns (uint64) {
292+
uint64 enabledTransceiverBitmap = _getEnabledTransceiversBitmap();
293+
return
294+
_getMessageAttestationsStorage()[digest].attestedTransceivers & enabledTransceiverBitmap;
295+
}
296+
297+
function _getEnabledTransceiverAttestedToMessage(
298+
bytes32 digest,
299+
uint8 index
300+
) internal view returns (bool) {
301+
return _getMessageAttestations(digest) & uint64(1 << index) != 0;
302+
}
303+
304+
/// @dev Verify that the peer address saved for `sourceChainId` matches the `peerAddress`.
305+
function _verifyPeer(uint16 sourceChainId, bytes32 peerAddress) internal view {
306+
if (getPeer(sourceChainId) != peerAddress) {
307+
revert InvalidPeer(sourceChainId, peerAddress);
308+
}
309+
}
310+
311+
// @dev Mark a message as executed.
312+
// This function will retuns `true` if the message has already been executed.
313+
function _replayProtect(bytes32 digest) internal returns (bool) {
314+
// check if this message has already been executed
315+
if (isMessageExecuted(digest)) {
316+
return true;
317+
}
318+
319+
// mark this message as executed
320+
_getMessageAttestationsStorage()[digest].executed = true;
321+
322+
return false;
323+
}
324+
325+
function _useMessageSequence() internal returns (uint64 currentSequence) {
326+
currentSequence = _getMessageSequenceStorage().num;
327+
_getMessageSequenceStorage().num++;
328+
}
329+
330+
/// ============== Invariants =============================================
331+
332+
/// @dev When we add new immutables, this function should be updated
333+
function _checkImmutables() internal view override {
334+
assert(this.token() == token);
335+
assert(this.mode() == mode);
336+
assert(this.chainId() == chainId);
337+
assert(this.rateLimitDuration() == rateLimitDuration);
338+
}
339+
340+
function _checkRegisteredTransceiversInvariants() internal view {
341+
if (_getRegisteredTransceiversStorage().length != _getNumTransceiversStorage().registered) {
342+
revert RetrievedIncorrectRegisteredTransceivers(
343+
_getRegisteredTransceiversStorage().length, _getNumTransceiversStorage().registered
344+
);
345+
}
346+
}
347+
348+
function _checkThresholdInvariants() internal view {
349+
uint8 threshold = _getThresholdStorage().num;
350+
_NumTransceivers memory numTransceivers = _getNumTransceiversStorage();
351+
352+
// invariant: threshold <= enabledTransceivers.length
353+
if (threshold > numTransceivers.enabled) {
354+
revert ThresholdTooHigh(threshold, numTransceivers.enabled);
355+
}
356+
357+
if (numTransceivers.registered > 0) {
358+
if (threshold == 0) {
359+
revert ZeroThreshold();
360+
}
361+
}
362+
}
363+
}

evm/src/TransceiverRegistry.sol evm/src/NttManager/TransceiverRegistry.sol

+3-3
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ abstract contract TransceiverRegistry {
5050
_;
5151
}
5252

53-
/// =============== STORAGE ===============================================
53+
// =============== Storage ===============================================
5454

5555
bytes32 private constant TRANSCEIVER_INFOS_SLOT =
5656
bytes32(uint256(keccak256("ntt.transceiverInfos")) - 1);
@@ -110,7 +110,7 @@ abstract contract TransceiverRegistry {
110110
}
111111
}
112112

113-
/// =============== GETTERS/SETTERS ========================================
113+
// =============== Storage Getters/Setters ========================================
114114

115115
function _setTransceiver(address transceiver) internal returns (uint8 index) {
116116
mapping(address => TransceiverInfo) storage transceiverInfos = _getTransceiverInfosStorage();
@@ -213,7 +213,7 @@ abstract contract TransceiverRegistry {
213213
result = _getEnabledTransceiversStorage();
214214
}
215215

216-
/// ============== INVARIANTS =============================================
216+
// ============== Invariants =============================================
217217

218218
/// @dev Check that the transceiver nttManager is in a valid state.
219219
/// Checking these invariants is somewhat costly, but we only need to do it

0 commit comments

Comments
 (0)