From effde7f756b88a3e0152e8cc6aba568f37a71ecd Mon Sep 17 00:00:00 2001 From: Csongor Kiss Date: Wed, 16 Nov 2022 18:03:28 +0000 Subject: [PATCH] ethereum: Implement shutdown for all 3 contracts (fixes #1937) These are stripped down versions of the original contracts that can be dropped in place via an upgrade to disable core functionality. Governance features are still enabled, which means it's possible to upgrade back to a working implementation. --- ethereum/contracts/Shutdown.sol | 31 ++++++++++++++++ ethereum/contracts/bridge/BridgeShutdown.sol | 36 +++++++++++++++++++ ethereum/contracts/nft/NFTBridge.sol | 4 +-- ethereum/contracts/nft/NFTBridgeGetters.sol | 2 +- .../contracts/nft/NFTBridgeGovernance.sol | 2 +- .../contracts/nft/NFTBridgeImplementation.sol | 2 +- ethereum/contracts/nft/NFTBridgeSetters.sol | 2 +- ethereum/contracts/nft/NFTBridgeSetup.sol | 2 +- ethereum/contracts/nft/NFTBridgeShutdown.sol | 30 ++++++++++++++++ .../scripts/deploy_core_bridge_shutdown.js | 11 ++++++ .../scripts/deploy_nft_bridge_shutdown.js | 11 ++++++ .../scripts/deploy_token_bridge_shutdown.js | 11 ++++++ ethereum/simulate_upgrade | 28 +++++++++++---- ethereum/simulate_upgrades | 18 ++++++---- 14 files changed, 171 insertions(+), 19 deletions(-) create mode 100644 ethereum/contracts/Shutdown.sol create mode 100644 ethereum/contracts/bridge/BridgeShutdown.sol create mode 100644 ethereum/contracts/nft/NFTBridgeShutdown.sol create mode 100644 ethereum/scripts/deploy_core_bridge_shutdown.js create mode 100644 ethereum/scripts/deploy_nft_bridge_shutdown.js create mode 100644 ethereum/scripts/deploy_token_bridge_shutdown.js diff --git a/ethereum/contracts/Shutdown.sol b/ethereum/contracts/Shutdown.sol new file mode 100644 index 000000000..ca4a31f53 --- /dev/null +++ b/ethereum/contracts/Shutdown.sol @@ -0,0 +1,31 @@ +// contracts/Shutdown.sol +// SPDX-License-Identifier: Apache 2 + +pragma solidity ^0.8.0; +pragma experimental ABIEncoderV2; + +import "./Governance.sol"; + +import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Upgrade.sol"; + +/** + * @title Shutdown + * @notice This contract implements a stripped-down version of the Wormhole core + * messaging protocol that is a drop-in replacement for Wormhole's + * implementation contract, effectively disabling all non-governance + * functionality. + * In particular, outgoing messages are disabled, but the contract + * remains upgradeable through governance. + */ +contract Shutdown is Governance { + + function initialize() public { + address implementation = ERC1967Upgrade._getImplementation(); + setInitialized(implementation); + + // this function needs to be exposed for an upgrade to pass + // NOTE: leave this function empty! It specifically does not have an + // 'initializer' modifier, to allow this contract to be upgraded to + // multiple times. + } +} diff --git a/ethereum/contracts/bridge/BridgeShutdown.sol b/ethereum/contracts/bridge/BridgeShutdown.sol new file mode 100644 index 000000000..11c5c5d93 --- /dev/null +++ b/ethereum/contracts/bridge/BridgeShutdown.sol @@ -0,0 +1,36 @@ +// contracts/BridgeShutdown.sol +// SPDX-License-Identifier: Apache 2 + +pragma solidity ^0.8.0; + +import "./BridgeGovernance.sol"; + +import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; +import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Upgrade.sol"; + +/** + * @title BridgeShutdown + * @notice This contract implements a stripped-down version of the token bridge + * asset transfer protocol that is a drop-in replacement for the Bridge + * implementation contract, effectively disabling all non-governance + * functionality. + * In particular, sending and receiving assets is disabled, but the + * contract remains upgradeable through governance. + * @dev Technically the ReentrancyGuard is not used in this contract, + * but it adds a storage variable, so as a matter of principle, we + * inherit that here too in order keep the storage layout identical to + * the actual implementation contract (which does use the reentrancy + * guard). + */ +contract BridgeShutdown is BridgeGovernance, ReentrancyGuard { + + function initialize() public { + address implementation = ERC1967Upgrade._getImplementation(); + setInitialized(implementation); + + // this function needs to be exposed for an upgrade to pass + // NOTE: leave this function empty! It specifically does not have an + // 'initializer' modifier, to allow this contract to be upgraded to + // multiple times. + } +} diff --git a/ethereum/contracts/nft/NFTBridge.sol b/ethereum/contracts/nft/NFTBridge.sol index 98742bff0..42e60e22d 100644 --- a/ethereum/contracts/nft/NFTBridge.sol +++ b/ethereum/contracts/nft/NFTBridge.sol @@ -1,4 +1,4 @@ -// contracts/Bridge.sol +// contracts/NFTBridge.sol // SPDX-License-Identifier: Apache 2 pragma solidity ^0.8.0; @@ -241,7 +241,7 @@ contract NFTBridge is NFTBridgeGovernance { transfer.tokenID = encoded.toUint256(index); index += 32; - + // Ignore length due to malformatted payload index += 1; transfer.uri = string(encoded.slice(index, encoded.length - index - 34)); diff --git a/ethereum/contracts/nft/NFTBridgeGetters.sol b/ethereum/contracts/nft/NFTBridgeGetters.sol index 666fb0584..9c510a829 100644 --- a/ethereum/contracts/nft/NFTBridgeGetters.sol +++ b/ethereum/contracts/nft/NFTBridgeGetters.sol @@ -1,4 +1,4 @@ -// contracts/Getters.sol +// contracts/NFTBridgeGetters.sol // SPDX-License-Identifier: Apache 2 pragma solidity ^0.8.0; diff --git a/ethereum/contracts/nft/NFTBridgeGovernance.sol b/ethereum/contracts/nft/NFTBridgeGovernance.sol index 3cc4f9545..0213088d0 100644 --- a/ethereum/contracts/nft/NFTBridgeGovernance.sol +++ b/ethereum/contracts/nft/NFTBridgeGovernance.sol @@ -1,4 +1,4 @@ -// contracts/Bridge.sol +// contracts/NFTBridgeGovernance.sol // SPDX-License-Identifier: Apache 2 pragma solidity ^0.8.0; diff --git a/ethereum/contracts/nft/NFTBridgeImplementation.sol b/ethereum/contracts/nft/NFTBridgeImplementation.sol index b00c3eb6e..499850843 100644 --- a/ethereum/contracts/nft/NFTBridgeImplementation.sol +++ b/ethereum/contracts/nft/NFTBridgeImplementation.sol @@ -1,4 +1,4 @@ -// contracts/Implementation.sol +// contracts/NFTBridgeImplementation.sol // SPDX-License-Identifier: Apache 2 pragma solidity ^0.8.0; diff --git a/ethereum/contracts/nft/NFTBridgeSetters.sol b/ethereum/contracts/nft/NFTBridgeSetters.sol index b5048dbb2..518729e73 100644 --- a/ethereum/contracts/nft/NFTBridgeSetters.sol +++ b/ethereum/contracts/nft/NFTBridgeSetters.sol @@ -1,4 +1,4 @@ -// contracts/Setters.sol +// contracts/NFTBridgeSetters.sol // SPDX-License-Identifier: Apache 2 pragma solidity ^0.8.0; diff --git a/ethereum/contracts/nft/NFTBridgeSetup.sol b/ethereum/contracts/nft/NFTBridgeSetup.sol index c1c7aeaf5..f313b2ea5 100644 --- a/ethereum/contracts/nft/NFTBridgeSetup.sol +++ b/ethereum/contracts/nft/NFTBridgeSetup.sol @@ -1,4 +1,4 @@ -// contracts/BridgeSetup.sol +// contracts/NFTBridgeSetup.sol // SPDX-License-Identifier: Apache 2 pragma solidity ^0.8.0; diff --git a/ethereum/contracts/nft/NFTBridgeShutdown.sol b/ethereum/contracts/nft/NFTBridgeShutdown.sol new file mode 100644 index 000000000..a797b9f61 --- /dev/null +++ b/ethereum/contracts/nft/NFTBridgeShutdown.sol @@ -0,0 +1,30 @@ +// contracts/NFTBridgeShutdown.sol +// SPDX-License-Identifier: Apache 2 + +pragma solidity ^0.8.0; + +import "./NFTBridgeGovernance.sol"; + +import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Upgrade.sol"; + +/** + * @title BridgeShutdown + * @notice This contract implements a stripped-down version of the NFT bridge + * asset transfer protocol that is a drop-in replacement for the + * NFTBridge implementation contract, effectively disabling all + * non-governance functionality. + * In particular, sending and receiving assets is disabled, but the + * contract remains upgradeable through governance. + */ +contract NFTBridgeShutdown is NFTBridgeGovernance { + + function initialize() public { + address implementation = ERC1967Upgrade._getImplementation(); + setInitialized(implementation); + + // this function needs to be exposed for an upgrade to pass + // NOTE: leave this function empty! It specifically does not have an + // 'initializer' modifier, to allow this contract to be upgraded to + // multiple times. + } +} diff --git a/ethereum/scripts/deploy_core_bridge_shutdown.js b/ethereum/scripts/deploy_core_bridge_shutdown.js new file mode 100644 index 000000000..b2d53f948 --- /dev/null +++ b/ethereum/scripts/deploy_core_bridge_shutdown.js @@ -0,0 +1,11 @@ +const Shutdown = artifacts.require("Shutdown"); +module.exports = async function(callback) { + try { + const contract = (await Shutdown.new()); + console.log('tx: ' + contract.transactionHash); + console.log('Shutdown address: ' + contract.address); + callback(); + } catch (e) { + callback(e); + } +}; diff --git a/ethereum/scripts/deploy_nft_bridge_shutdown.js b/ethereum/scripts/deploy_nft_bridge_shutdown.js new file mode 100644 index 000000000..2d67c8d7c --- /dev/null +++ b/ethereum/scripts/deploy_nft_bridge_shutdown.js @@ -0,0 +1,11 @@ +const Shutdown = artifacts.require("NFTBridgeShutdown"); +module.exports = async function(callback) { + try { + const contract = (await Shutdown.new()); + console.log('tx: ' + contract.transactionHash); + console.log('NFTBridgeShutdown address: ' + contract.address); + callback(); + } catch (e) { + callback(e); + } +}; diff --git a/ethereum/scripts/deploy_token_bridge_shutdown.js b/ethereum/scripts/deploy_token_bridge_shutdown.js new file mode 100644 index 000000000..3ab532a20 --- /dev/null +++ b/ethereum/scripts/deploy_token_bridge_shutdown.js @@ -0,0 +1,11 @@ +const Shutdown = artifacts.require("BridgeShutdown"); +module.exports = async function(callback) { + try { + const contract = (await Shutdown.new()); + console.log('tx: ' + contract.transactionHash); + console.log('Bridge address: ' + contract.address); + callback(); + } catch (e) { + callback(e); + } +}; diff --git a/ethereum/simulate_upgrade b/ethereum/simulate_upgrade index 8299d1fcc..b340aa98e 100755 --- a/ethereum/simulate_upgrade +++ b/ethereum/simulate_upgrade @@ -15,7 +15,7 @@ function usage() { cat <&2 Usage: - $(basename "$0") [-h] [-m s] [-c s] [-x] [-k] [-d] [-a s] [-l s] -- Simulate an upgrade on a fork of mainnet, and check for any errors. + $(basename "$0") [-h] [-m s] [-c s] [-x] [-k] [-d] [-a s] [-l s] [-s] -- Simulate an upgrade on a fork of mainnet, and check for any errors. where: -h show this help text @@ -24,8 +24,9 @@ Usage: -x run anvil -d don't compile contract first -k keep anvil alive - -l file to loge to (by default creates a new tmp file) + -l file to log to (by default creates a new tmp file) -a new code address (by default it builds the most recent contract in the repository) + -s shutdown EOF exit 1 } @@ -40,8 +41,9 @@ chain_name="" run_anvil=false skip_compile=false keepalive_anvil=false +shutdown=false anvil_out=$(mktemp) -while getopts ':hm:c:a:xkdl:' option; do +while getopts ':hm:c:a:xkdl:s' option; do case "$option" in h) usage ;; @@ -60,6 +62,8 @@ while getopts ':hm:c:a:xkdl:' option; do k) keepalive_anvil=true run_anvil=true ;; + s) shutdown=true + ;; :) printf "missing argument for -%s\n" "$OPTARG" >&2 usage ;; @@ -111,15 +115,27 @@ SCRIPT="" case "$module" in bridge|core) MODULE=Core - SCRIPT="scripts/deploy_core_bridge.js" + if [[ $shutdown = true ]]; then + SCRIPT="scripts/deploy_core_bridge_shutdown.js" + else + SCRIPT="scripts/deploy_core_bridge.js" + fi ;; token_bridge) MODULE=TokenBridge - SCRIPT="scripts/deploy_token_bridge.js" + if [[ $shutdown = true ]]; then + SCRIPT="scripts/deploy_token_bridge_shutdown.js" + else + SCRIPT="scripts/deploy_token_bridge.js" + fi ;; nft_bridge) MODULE=NFTBridge - SCRIPT="scripts/deploy_nft_bridge.js" + if [[ $shutdown = true ]]; then + SCRIPT="scripts/deploy_nft_bridge_shutdown.js" + else + SCRIPT="scripts/deploy_nft_bridge.js" + fi ;; *) echo "unknown module $module" >&2 usage diff --git a/ethereum/simulate_upgrades b/ethereum/simulate_upgrades index b4db38191..7e1d6e2c9 100755 --- a/ethereum/simulate_upgrades +++ b/ethereum/simulate_upgrades @@ -28,17 +28,23 @@ ANVIL_PID=$! # Sleep for 10 seconds here to give some time for the fork to complete. sleep 10 -echo "========================= Updating core contract #1 ============================" +echo "========================= Updating core contract ============================" ./simulate_upgrade -m bridge -c $chain -d -echo "========================= Updating core contract #2 ============================" +echo "========================= Shutting down core contract =======================" +./simulate_upgrade -m bridge -c $chain -d -s +echo "========================= Re-enabling core contract =========================" ./simulate_upgrade -m bridge -c $chain -d -echo "===================== Updating token bridge contract #1 ========================" +echo "===================== Updating token bridge contract ========================" ./simulate_upgrade -m token_bridge -c $chain -d -echo "===================== Updating token bridge contract #2 ========================" +echo "===================== Shutting down token bridge contract ===================" +./simulate_upgrade -m token_bridge -c $chain -d -s +echo "===================== Re-enabling token bridge contract =====================" ./simulate_upgrade -m token_bridge -c $chain -d -echo "====================== Updating NFT bridge contract #1 =========================" +echo "====================== Updating NFT bridge contract =========================" ./simulate_upgrade -m nft_bridge -c $chain -d -echo "====================== Updating NFT bridge contract #2 =========================" +echo "====================== Shutting down NFT bridge contract ====================" +./simulate_upgrade -m nft_bridge -c $chain -d -s +echo "====================== Re-enabling NFT bridge contract ======================" ./simulate_upgrade -m nft_bridge -c $chain -d