wormhole/ethereum/contracts/nft/NFTBridgeGovernance.sol

184 lines
6.2 KiB
Solidity

// contracts/NFTBridgeGovernance.sol
// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Upgrade.sol";
import "../libraries/external/BytesLib.sol";
import "./NFTBridgeGetters.sol";
import "./NFTBridgeSetters.sol";
import "./NFTBridgeStructs.sol";
import "./token/NFT.sol";
import "./token/NFTImplementation.sol";
import "../interfaces/IWormhole.sol";
contract NFTBridgeGovernance is NFTBridgeGetters, NFTBridgeSetters, ERC1967Upgrade {
using BytesLib for bytes;
// "NFTBridge" (left padded)
bytes32 constant module = 0x00000000000000000000000000000000000000000000004e4654427269646765;
// Execute a RegisterChain governance message
function registerChain(bytes memory encodedVM) public {
(IWormhole.VM memory vm, bool valid, string memory reason) = verifyGovernanceVM(encodedVM);
require(valid, reason);
setGovernanceActionConsumed(vm.hash);
NFTBridgeStructs.RegisterChain memory chain = parseRegisterChain(vm.payload);
require((chain.chainId == chainId() && !isFork()) || chain.chainId == 0, "invalid chain id");
setBridgeImplementation(chain.emitterChainID, chain.emitterAddress);
}
// Execute a UpgradeContract governance message
function upgrade(bytes memory encodedVM) public {
require(!isFork(), "invalid fork");
(IWormhole.VM memory vm, bool valid, string memory reason) = verifyGovernanceVM(encodedVM);
require(valid, reason);
setGovernanceActionConsumed(vm.hash);
NFTBridgeStructs.UpgradeContract memory implementation = parseUpgrade(vm.payload);
require(implementation.chainId == chainId(), "wrong chain id");
upgradeImplementation(address(uint160(uint256(implementation.newContract))));
}
/**
* @dev Updates the `chainId` and `evmChainId` on a forked chain via Governance VAA/VM
*/
function submitRecoverChainId(bytes memory encodedVM) public {
require(isFork(), "not a fork");
(IWormhole.VM memory vm, bool valid, string memory reason) = verifyGovernanceVM(encodedVM);
require(valid, reason);
setGovernanceActionConsumed(vm.hash);
NFTBridgeStructs.RecoverChainId memory rci = parseRecoverChainId(vm.payload);
// Verify the VAA is for this chain
require(rci.evmChainId == block.chainid, "invalid EVM Chain");
// Update the chainIds
setEvmChainId(rci.evmChainId);
setChainId(rci.newChainId);
}
function verifyGovernanceVM(bytes memory encodedVM) internal view returns (IWormhole.VM memory parsedVM, bool isValid, string memory invalidReason){
(IWormhole.VM memory vm, bool valid, string memory reason) = wormhole().parseAndVerifyVM(encodedVM);
if(!valid){
return (vm, valid, reason);
}
if (vm.emitterChainId != governanceChainId()) {
return (vm, false, "wrong governance chain");
}
if (vm.emitterAddress != governanceContract()) {
return (vm, false, "wrong governance contract");
}
if(governanceActionIsConsumed(vm.hash)){
return (vm, false, "governance action already consumed");
}
return (vm, true, "");
}
event ContractUpgraded(address indexed oldContract, address indexed newContract);
function upgradeImplementation(address newImplementation) internal {
address currentImplementation = _getImplementation();
_upgradeTo(newImplementation);
// Call initialize function of the new implementation
(bool success, bytes memory reason) = newImplementation.delegatecall(abi.encodeWithSignature("initialize()"));
require(success, string(reason));
emit ContractUpgraded(currentImplementation, newImplementation);
}
function parseRegisterChain(bytes memory encoded) public pure returns(NFTBridgeStructs.RegisterChain memory chain) {
uint index = 0;
// governance header
chain.module = encoded.toBytes32(index);
index += 32;
require(chain.module == module, "invalid RegisterChain: wrong module");
chain.action = encoded.toUint8(index);
index += 1;
require(chain.action == 1, "invalid RegisterChain: wrong action");
chain.chainId = encoded.toUint16(index);
index += 2;
// payload
chain.emitterChainID = encoded.toUint16(index);
index += 2;
chain.emitterAddress = encoded.toBytes32(index);
index += 32;
require(encoded.length == index, "invalid RegisterChain: wrong length");
}
function parseUpgrade(bytes memory encoded) public pure returns(NFTBridgeStructs.UpgradeContract memory chain) {
uint index = 0;
// governance header
chain.module = encoded.toBytes32(index);
index += 32;
require(chain.module == module, "invalid UpgradeContract: wrong module");
chain.action = encoded.toUint8(index);
index += 1;
require(chain.action == 2, "invalid UpgradeContract: wrong action");
chain.chainId = encoded.toUint16(index);
index += 2;
// payload
chain.newContract = encoded.toBytes32(index);
index += 32;
require(encoded.length == index, "invalid UpgradeContract: wrong length");
}
/// @dev Parse a recoverChainId (action 3) with minimal validation
function parseRecoverChainId(bytes memory encodedRecoverChainId) public pure returns (NFTBridgeStructs.RecoverChainId memory rci) {
uint index = 0;
rci.module = encodedRecoverChainId.toBytes32(index);
index += 32;
require(rci.module == module, "invalid RecoverChainId: wrong module");
rci.action = encodedRecoverChainId.toUint8(index);
index += 1;
require(rci.action == 3, "invalid RecoverChainId: wrong action");
rci.evmChainId = encodedRecoverChainId.toUint256(index);
index += 32;
rci.newChainId = encodedRecoverChainId.toUint16(index);
index += 2;
require(encodedRecoverChainId.length == index, "invalid RecoverChainId");
}
}