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