-
Notifications
You must be signed in to change notification settings - Fork 40
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Unilateral pause implementation #402
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,6 +15,7 @@ import "../interfaces/ITransceiver.sol"; | |
import "../interfaces/IManagerBase.sol"; | ||
|
||
import "./TransceiverRegistry.sol"; | ||
import "forge-std/console.sol"; // TODO - remove this - MAX | ||
|
||
abstract contract ManagerBase is | ||
IManagerBase, | ||
|
@@ -42,6 +43,27 @@ abstract contract ManagerBase is | |
deployer = msg.sender; | ||
} | ||
|
||
modifier inboundNotPaused() { | ||
if (_getUnilateralPauseStorage().inbound) { | ||
revert InboundPaused(); | ||
} | ||
_; | ||
} | ||
|
||
modifier outboundNotPaused() { | ||
if (_getUnilateralPauseStorage().outbound) { | ||
revert OutboundPaused(); | ||
} | ||
_; | ||
} | ||
|
||
modifier outboundWhenPaused() { | ||
if (_getUnilateralPauseStorage().outbound == false) { | ||
revert NotPausedForUpdate(); | ||
} | ||
_; | ||
} | ||
|
||
function _migrate() internal virtual override { | ||
_checkThresholdInvariants(); | ||
_checkTransceiversInvariants(); | ||
|
@@ -57,8 +79,18 @@ abstract contract ManagerBase is | |
|
||
bytes32 private constant THRESHOLD_SLOT = bytes32(uint256(keccak256("ntt.threshold")) - 1); | ||
|
||
bytes32 private constant UNILATERAL_PAUSE_SLOT = | ||
bytes32(uint256(keccak256("ntt.unilateral_pause")) - 1); | ||
|
||
// =============== Storage Getters/Setters ============================================== | ||
|
||
function _getUnilateralPauseStorage() internal pure returns (UnilateralPause storage $) { | ||
uint256 slot = uint256(UNILATERAL_PAUSE_SLOT); | ||
assembly ("memory-safe") { | ||
$.slot := slot | ||
} | ||
} | ||
|
||
function _getThresholdStorage() private pure returns (_Threshold storage $) { | ||
uint256 slot = uint256(THRESHOLD_SLOT); | ||
assembly ("memory-safe") { | ||
|
@@ -273,6 +305,13 @@ abstract contract ManagerBase is | |
return _getThresholdStorage().num; | ||
} | ||
|
||
/// @inheritdoc IManagerBase | ||
function getUnilateralPause() public view returns (UnilateralPause memory) { | ||
UnilateralPause storage u = _getUnilateralPauseStorage(); | ||
|
||
return UnilateralPause({inbound: u.inbound, outbound: u.outbound}); | ||
} | ||
|
||
/// @inheritdoc IManagerBase | ||
function isMessageApproved(bytes32 digest) public view returns (bool) { | ||
uint8 threshold = getThreshold(); | ||
|
@@ -316,6 +355,14 @@ abstract contract ManagerBase is | |
_unpause(); | ||
} | ||
|
||
function setInboundPauseStatus(bool status) external onlyOwnerOrPauser { | ||
_getUnilateralPauseStorage().inbound = status; | ||
} | ||
|
||
function setOutboundPauseStatus(bool status) external onlyOwnerOrPauser { | ||
_getUnilateralPauseStorage().outbound = status; | ||
} | ||
|
||
/// @notice Transfer ownership of the Manager contract and all Transceiver contracts to a new owner. | ||
function transferOwnership(address newOwner) public override onlyOwner { | ||
super.transferOwnership(newOwner); | ||
|
@@ -356,7 +403,7 @@ abstract contract ManagerBase is | |
} | ||
|
||
/// @inheritdoc IManagerBase | ||
function removeTransceiver(address transceiver) external onlyOwner { | ||
function removeTransceiver(address transceiver) external onlyOwner outboundWhenPaused { | ||
_removeTransceiver(transceiver); | ||
|
||
_Threshold storage _threshold = _getThresholdStorage(); | ||
|
@@ -372,7 +419,7 @@ abstract contract ManagerBase is | |
} | ||
|
||
/// @inheritdoc IManagerBase | ||
function setThreshold(uint8 threshold) external onlyOwner { | ||
function setThreshold(uint8 threshold) external onlyOwner outboundWhenPaused { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Isn't the threshold of the local chain more concerned around receiving inbound messages? For example a deployment may want to change the threshold on one chain only, and they would only have to pause the outbound transfer of that chain, not every other chain that sends to it? |
||
if (threshold == 0) { | ||
revert ZeroThreshold(); | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -64,6 +64,8 @@ contract NttManager is INttManager, RateLimiter, ManagerBase { | |
__PausedOwnable_init(msg.sender, msg.sender); | ||
__ReentrancyGuard_init(); | ||
_setOutboundLimit(TrimmedAmountLib.max(tokenDecimals())); | ||
_getUnilateralPauseStorage().inbound = true; | ||
_getUnilateralPauseStorage().outbound = true; | ||
} | ||
|
||
function _initialize() internal virtual override { | ||
|
@@ -104,7 +106,7 @@ contract NttManager is INttManager, RateLimiter, ManagerBase { | |
bytes32 peerContract, | ||
uint8 decimals, | ||
uint256 inboundLimit | ||
) public onlyOwner { | ||
) public onlyOwner outboundWhenPaused { | ||
if (peerChainId == 0) { | ||
revert InvalidPeerChainIdZero(); | ||
} | ||
|
@@ -158,7 +160,7 @@ contract NttManager is INttManager, RateLimiter, ManagerBase { | |
uint256 amount, | ||
uint16 recipientChain, | ||
bytes32 recipient | ||
) external payable nonReentrant whenNotPaused returns (uint64) { | ||
) external payable nonReentrant whenNotPaused outboundNotPaused returns (uint64) { | ||
return | ||
_transferEntryPoint(amount, recipientChain, recipient, recipient, false, new bytes(1)); | ||
} | ||
|
@@ -171,7 +173,7 @@ contract NttManager is INttManager, RateLimiter, ManagerBase { | |
bytes32 refundAddress, | ||
bool shouldQueue, | ||
bytes memory transceiverInstructions | ||
) external payable nonReentrant whenNotPaused returns (uint64) { | ||
) external payable nonReentrant whenNotPaused outboundNotPaused returns (uint64) { | ||
return _transferEntryPoint( | ||
amount, recipientChain, recipient, refundAddress, shouldQueue, transceiverInstructions | ||
); | ||
|
@@ -182,7 +184,7 @@ contract NttManager is INttManager, RateLimiter, ManagerBase { | |
uint16 sourceChainId, | ||
bytes32 sourceNttManagerAddress, | ||
TransceiverStructs.NttManagerMessage memory payload | ||
) external onlyTransceiver whenNotPaused { | ||
) external onlyTransceiver whenNotPaused inboundNotPaused { | ||
_verifyPeer(sourceChainId, sourceNttManagerAddress); | ||
|
||
// Compute manager message digest and record transceiver attestation. | ||
|
@@ -198,7 +200,7 @@ contract NttManager is INttManager, RateLimiter, ManagerBase { | |
uint16 sourceChainId, | ||
bytes32 sourceNttManagerAddress, | ||
TransceiverStructs.NttManagerMessage memory message | ||
) public whenNotPaused { | ||
) public whenNotPaused inboundNotPaused { | ||
(bytes32 digest, bool alreadyExecuted) = | ||
_isMessageExecuted(sourceChainId, sourceNttManagerAddress, message); | ||
|
||
|
@@ -241,7 +243,12 @@ contract NttManager is INttManager, RateLimiter, ManagerBase { | |
} | ||
|
||
/// @inheritdoc INttManager | ||
function completeInboundQueuedTransfer(bytes32 digest) external nonReentrant whenNotPaused { | ||
function completeInboundQueuedTransfer(bytes32 digest) | ||
external | ||
nonReentrant | ||
whenNotPaused | ||
inboundNotPaused | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we can safely complete here without pausing on inbound transfers? Or do you think it's better to be aggressive on the pausing here? |
||
{ | ||
// find the message in the queue | ||
InboundQueuedTransfer memory queuedTransfer = getInboundQueuedTransfer(digest); | ||
if (queuedTransfer.txTimestamp == 0) { | ||
|
@@ -266,6 +273,7 @@ contract NttManager is INttManager, RateLimiter, ManagerBase { | |
payable | ||
nonReentrant | ||
whenNotPaused | ||
outboundNotPaused | ||
returns (uint64) | ||
{ | ||
// find the message in the queue | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit pick but prefer
!
vs== false