diff --git a/README.md b/README.md index 63d7438..b1e650e 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,10 @@ -# TODOs - -- Add poc-gmp-router as a repo in here (do "forge install"). -- Need to access UniversalAddress as a library. -- Need to access ITransceiver as a library. -- Need to access IRouter as a library. - # Multi-Chain Template ## Overview -Implement a transceiver to allow the GMP router to communicate over Wormhole. +This implementation provides a reference implementation of a transceiver that allows the GMP router to communicate over Wormhole. + +A transceiver MUST implement the `ITransceiver` interface defined in the GMP repo. ## Runtime Support diff --git a/evm/src/WormholeTransceiver.sol b/evm/src/WormholeTransceiver.sol index 19c14b5..46db860 100644 --- a/evm/src/WormholeTransceiver.sol +++ b/evm/src/WormholeTransceiver.sol @@ -1,52 +1,55 @@ // SPDX-License-Identifier: Apache 2 pragma solidity >=0.8.8 <0.9.0; +import "poc-gmp-router/interfaces/IRouter.sol"; +import "poc-gmp-router/interfaces/ITransceiver.sol"; + import "wormhole-solidity-sdk/libraries/BytesParsing.sol"; import "wormhole-solidity-sdk/interfaces/IWormhole.sol"; -import "./interfaces/IRouter.sol"; -import "./interfaces/ITransceiver.sol"; import "./interfaces/IWormholeTransceiver.sol"; import "./libraries/TransceiverHelpers.sol"; -contract WormholeTransceiver is IWormholeTransceiver, ITransceiver { +contract WormholeTransceiver is IWormholeTransceiver { using BytesParsing for bytes; string public constant versionString = "WormholeTransceiver-0.0.1"; address public admin; + address public pendingAdmin; // ==================== Immutables =============================================== - uint16 public immutable ourChainId; - uint256 immutable evmChainId; + uint16 public immutable ourChain; + uint256 immutable evmChain; IRouter public immutable router; IWormhole public immutable wormhole; uint8 public immutable consistencyLevel; - uint256 public immutable gasLimit; // ==================== Constants ================================================ constructor( - uint16 _ourChainId, - uint256 _evmChainId, + uint16 _ourChain, + uint256 _evmChain, address _admin, address _router, - address wormholeCoreBridge, - uint8 _consistencyLevel, - uint256 _gasLimit + address _wormhole, + uint8 _consistencyLevel ) { - evmChainId = _evmChainId; - ourChainId = _ourChainId; + assert(_ourChain != 0); + assert(_evmChain != 0); + assert(_admin != address(0)); + assert(_router != address(0)); + assert(_wormhole != address(0)); + // Not checking consistency level since maybe zero is valid? + evmChain = _evmChain; + ourChain = _ourChain; admin = _admin; router = IRouter(_router); - wormhole = IWormhole(wormholeCoreBridge); + wormhole = IWormhole(_wormhole); consistencyLevel = _consistencyLevel; - gasLimit = _gasLimit; } // =============== Storage =============================================== - bytes32 private constant WORMHOLE_CONSUMED_VAAS_SLOT = bytes32(uint256(keccak256("whTransceiver.consumedVAAs")) - 1); - bytes32 private constant WORMHOLE_PEERS_SLOT = bytes32(uint256(keccak256("whTransceiver.peers")) - 1); // =============== Storage Setters/Getters ======================================== @@ -71,61 +74,82 @@ contract WormholeTransceiver is IWormholeTransceiver, ITransceiver { /// @dev The msg.sender must be the current admin contract. /// @param newAdmin The address of the new admin. function updateAdmin(address newAdmin) external onlyAdmin { - // Update the storage. + // SPEC: MUST check that the caller is the current admin and there is not a pending transfer. + // - This is handled by onlyAdmin. + + // SPEC: If possible, MUST NOT allow the admin to discard admin via this command (e.g. newAdmin != address(0) on EVM) + if (newAdmin == address(0)) { + revert InvalidAdminZeroAddress(); + } + + // SPEC: Immediately sets newAdmin as the admin of the integrator. admin = newAdmin; emit AdminUpdated(msg.sender, newAdmin); } - // /// @notice Starts the two step process of transferring admin privileges from the current admin to another contract. - // /// @dev The msg.sender must be the current admin contract. - // /// @param integrator The address of the integrator contract. - // /// @param newAdmin The address of the new admin. - // function transferAdmin(address newAdmin) external onlyAdmin { - // // Get the storage for this integrator contract - // mapping(address => IntegratorConfig) storage integratorConfigs = _getIntegratorConfigsStorage(); - - // // Update the storage with this request. - // integratorConfigs[integrator].transfer = newAdmin; - // emit AdminUpdateRequested(integrator, msg.sender, newAdmin); - // } - - // /// @notice Starts the two step process of transferring admin privileges from the current admin to another contract. - // /// @dev The msg.sender must be the current admin contract. - // /// @param integrator The address of the integrator contract. - // function claimAdmin(address integrator) external { - // // Get the storage for this integrator contract - // mapping(address => IntegratorConfig) storage integratorConfigs = _getIntegratorConfigsStorage(); - - // address oldAdmin = integratorConfigs[integrator].admin; - // address newAdmin = integratorConfigs[integrator].transfer; - // if ((msg.sender == oldAdmin) || (msg.sender == newAdmin)) { - // // Update the storage with this request. - // integratorConfigs[integrator].admin = newAdmin; - // integratorConfigs[integrator].transfer = address(0); - // } - // emit AdminUpdated(integrator, oldAdmin, newAdmin); - // } + /// @notice Starts the two step process of transferring admin privileges from the current admin to another contract. + /// @dev The msg.sender must be the current admin contract. + /// @param newAdmin The address of the new admin. + function transferAdmin(address newAdmin) external onlyAdmin { + // SPEC: MUST check that the caller is the current admin and there is not a pending transfer. + // - This is handled by onlyAdmin. + + // SPEC: Initiates the first step of a two-step process in which the current admin (to cancel) or new admin must claim. + pendingAdmin = newAdmin; + emit AdminUpdateRequested(msg.sender, newAdmin); + } + + /// @notice Completes the two step process of transferring admin privileges from the current admin to another contract. + /// @dev The msg.sender must be the pending admin or the current admin contract (which cancels the transfer). + function claimAdmin() external { + // This doesn't use onlyAdmin because the pending admin must be non-zero. + + // SPEC: MUST check that the caller is the current admin OR the pending admin. + if ((admin != msg.sender) && (pendingAdmin != msg.sender)) { + revert CallerNotAdmin(msg.sender); + } + + // SPEC: MUST check that there is an admin transfer pending (e. g. pendingAdmin != address(0) on EVM). + if (pendingAdmin == address(0)) { + revert NoAdminUpdatePending(); + } + + // SPEC: Cancels / Completes the second step of the two-step transfer. Sets the admin to the caller and clears the pending admin. + address oldAdmin = admin; + admin = msg.sender; + pendingAdmin = address(0); + emit AdminUpdated(oldAdmin, msg.sender); + } + + /// @notice Sets the admin contract to null, making the configuration immutable. THIS IS NOT REVERSIBLE. + /// @dev The msg.sender must be the current admin contract. + function discardAdmin() external onlyAdmin { + // SPEC: MUST check that the caller is the current admin and there is not a pending transfer. + // - This is handled by onlyAdmin. + + // SPEC: Clears the current admin. THIS IS NOT REVERSIBLE. This ensures that the Integrator configuration becomes immutable. + admin = address(0); + emit AdminDiscarded(msg.sender); + } /// @inheritdoc IWormholeTransceiver - function setWormholePeer(uint16 peerChainId, bytes32 peerContract) external payable onlyAdmin { - if (peerChainId == 0) { - revert InvalidWormholeChainIdZero(); + function setWormholePeer(uint16 peerChain, bytes32 peerContract) external payable onlyAdmin { + if (peerChain == 0) { + revert InvalidWormholeChainZero(); } if (peerContract == bytes32(0)) { revert InvalidWormholePeerZeroAddress(); } - bytes32 oldPeerContract = _getWormholePeersStorage()[peerChainId]; + bytes32 oldPeerContract = _getWormholePeersStorage()[peerChain]; - // We don't want to allow updating a peer since this adds complexity in the accountant - // If the owner makes a mistake with peer registration they should deploy a new Wormhole - // transceiver and register this new transceiver with the Router + // SPEC: MUST not set the peer if it is already set. if (oldPeerContract != bytes32(0)) { - revert PeerAlreadySet(peerChainId, oldPeerContract); + revert PeerAlreadySet(peerChain, oldPeerContract); } - _getWormholePeersStorage()[peerChainId] = peerContract; - emit SetWormholePeer(peerChainId, peerContract); + _getWormholePeersStorage()[peerChain] = peerContract; + emit SetWormholePeer(peerChain, peerContract); } // =============== Interface =============================================================== @@ -136,21 +160,21 @@ contract WormholeTransceiver is IWormholeTransceiver, ITransceiver { } /// @inheritdoc ITransceiver - function quoteDeliveryPrice(uint16 destinationChainId) external view returns (uint256) { - return _quoteDeliveryPrice(destinationChainId); + function quoteDeliveryPrice(uint16 dstChain) external view returns (uint256) { + return _quoteDeliveryPrice(dstChain); } /// @inheritdoc ITransceiver /// @dev The caller should set the delivery price in msg.value. function sendMessage( - UniversalAddress sourceAddress, - uint16 recipientChain, - UniversalAddress recipientAddress, + UniversalAddress srcAddr, + uint16 dstChain, + UniversalAddress dstAddr, uint64 sequence, bytes32 payloadHash, - bytes32 refundAddress + bytes32 refundAddr ) external payable { - _sendMessage(sourceAddress, recipientChain, recipientAddress, sequence, payloadHash, refundAddress); + _sendMessage(srcAddr, dstChain, dstAddr, sequence, payloadHash, refundAddr); } /// @inheritdoc IWormholeTransceiver @@ -160,61 +184,60 @@ contract WormholeTransceiver is IWormholeTransceiver, ITransceiver { // ============= Internal =============================================================== - function _quoteDeliveryPrice(uint16 /*destinationChainId*/ ) internal view returns (uint256) { + function _quoteDeliveryPrice(uint16 /*dstChain*/ ) internal view returns (uint256) { return wormhole.messageFee(); } function _sendMessage( - UniversalAddress sourceAddress, - uint16 recipientChain, - UniversalAddress recipientAddress, + UniversalAddress srcAddr, + uint16 dstChain, + UniversalAddress dstAddr, uint64 sequence, bytes32 payloadHash, - bytes32 // refundAddress - ) internal { - bytes memory payload = _encodePayload(sourceAddress, sequence, recipientChain, recipientAddress, payloadHash); + bytes32 // refundAddr + ) internal onlyRouter { + bytes memory payload = _encodePayload(srcAddr, sequence, dstChain, dstAddr, payloadHash); wormhole.publishMessage{value: msg.value}(0, payload, consistencyLevel); - emit SendTransceiverMessage(sourceAddress, recipientChain, recipientAddress, sequence, payloadHash); + emit MessageSent(srcAddr, dstChain, dstAddr, sequence, payloadHash); } function _receiveMessage(bytes memory encodedMessage) internal { - uint16 sourceChainId; - bytes memory payload; - (sourceChainId, payload) = _verifyMessage(encodedMessage); + // Verify the wormhole message and extract the source chain and payload. + (uint16 srcChain, bytes memory payload) = _verifyMessage(encodedMessage); - ( - UniversalAddress sourceAddress, - uint64 sequence, - uint16 recipientChain, - UniversalAddress recipientAddress, - bytes32 payloadHash - ) = _decodePayload(payload); + // Decode our payload. + (UniversalAddress srcAddr, uint64 sequence, uint16 dstChain, UniversalAddress dstAddr, bytes32 payloadHash) = + _decodePayload(payload); - if (recipientChain != ourChainId) { - revert InvalidChainId(recipientChain); + // Make sure this payload is meant for us. + if (dstChain != ourChain) { + revert InvalidChain(dstChain); } - router.attestMessage(sourceChainId, sourceAddress, sequence, ourChainId, recipientAddress, payloadHash); + // Post the message to the router. + router.attestMessage(srcChain, srcAddr, sequence, ourChain, dstAddr, payloadHash); + + // We don't need to emit an event here because _verifyMessage already did. } function _encodePayload( - UniversalAddress sourceAddress, + UniversalAddress srcAddr, uint64 sequence, - uint16 recipientChain, - UniversalAddress recipientAddress, + uint16 dstChain, + UniversalAddress dstAddr, bytes32 payloadHash ) internal pure returns (bytes memory payload) { - return abi.encodePacked(sourceAddress, sequence, recipientChain, recipientAddress, payloadHash); + return abi.encodePacked(srcAddr, sequence, dstChain, dstAddr, payloadHash); } function _decodePayload(bytes memory payload) internal pure returns ( - UniversalAddress sourceAddress, + UniversalAddress srcAddr, uint64 sequence, - uint16 recipientChain, - UniversalAddress recipientAddress, + uint16 dstChain, + UniversalAddress dstAddr, bytes32 payloadHash ) { @@ -222,13 +245,13 @@ contract WormholeTransceiver is IWormholeTransceiver, ITransceiver { uint256 offset = 0; (b32, offset) = payload.asBytes32(offset); - sourceAddress = UniversalAddressLibrary.fromBytes32(b32); + srcAddr = UniversalAddressLibrary.fromBytes32(b32); (sequence, offset) = payload.asUint64(offset); - (recipientChain, offset) = payload.asUint16(offset); + (dstChain, offset) = payload.asUint16(offset); (b32, offset) = payload.asBytes32(offset); - recipientAddress = UniversalAddressLibrary.fromBytes32(b32); + dstAddr = UniversalAddressLibrary.fromBytes32(b32); (payloadHash, offset) = payload.asBytes32(offset); @@ -236,7 +259,7 @@ contract WormholeTransceiver is IWormholeTransceiver, ITransceiver { } function _verifyMessage(bytes memory encodedMessage) internal returns (uint16, bytes memory) { - // verify VAA against Wormhole Core Bridge contract + // Verify VAA against Wormhole Core Bridge contract. (IWormhole.VM memory vm, bool valid, string memory reason) = wormhole.parseAndVerifyVM(encodedMessage); // ensure that the VAA is valid @@ -244,19 +267,17 @@ contract WormholeTransceiver is IWormholeTransceiver, ITransceiver { revert InvalidVaa(reason); } - // ensure that the message came from a registered peer contract + // Ensure that the message came from the registered peer contract. if (!_verifyPeer(vm)) { revert InvalidWormholePeer(vm.emitterChainId, vm.emitterAddress); } - // emit `ReceivedMessage` event - emit ReceivedMessage(vm.hash, vm.emitterChainId, vm.emitterAddress, vm.sequence); - + emit MessageReceived(vm.hash, vm.emitterChainId, vm.emitterAddress, vm.sequence); return (vm.emitterChainId, vm.payload); } function _verifyPeer(IWormhole.VM memory vm) internal view returns (bool) { - checkFork(evmChainId); + checkFork(evmChain); return getWormholePeer(vm.emitterChainId) == vm.emitterAddress; } @@ -270,7 +291,17 @@ contract WormholeTransceiver is IWormholeTransceiver, ITransceiver { modifier onlyAdmin() { if (admin != msg.sender) { - revert CallerNotAdmin(); + revert CallerNotAdmin(msg.sender); + } + if (pendingAdmin != address(0)) { + revert AdminTransferPending(); + } + _; + } + + modifier onlyRouter() { + if (address(router) != msg.sender) { + revert CallerNotRouter(msg.sender); } _; } diff --git a/evm/src/interfaces/IRouter.sol b/evm/src/interfaces/IRouter.sol deleted file mode 100644 index bfa60b2..0000000 --- a/evm/src/interfaces/IRouter.sol +++ /dev/null @@ -1,52 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.13; - -import "../libraries/UniversalAddress.sol"; - -interface IRouter { - /// @notice Send a message to another chain. - /// @param recipientChain The Wormhole chain ID of the recipient. - /// @param recipientAddress The universal address of the peer on the recipient chain. - /// @param refundAddress The source chain refund address passed to the Transceiver. - /// @param payloadHash keccak256 of a message to be sent to the recipient chain. - /// @return uint64 The sequence number of the message. - function sendMessage( - uint16 recipientChain, - UniversalAddress recipientAddress, - address refundAddress, - bytes32 payloadHash - ) external payable returns (uint64); - - /// @notice Called by a Transceiver contract to attest to a message. - /// @param sourceChainId The Wormhole chain ID of the sender. - /// @param sourceAddress The universal address of the peer on the sending chain. - /// @param sequence The sequence number of the message (per integrator). - /// @param destinationChainId The Wormhole chain ID of the destination. - /// @param destinationAddress The destination address of the message. - /// @param payloadHash The keccak256 of payload from the integrator. - function attestMessage( - uint16 sourceChainId, - UniversalAddress sourceAddress, - uint64 sequence, - uint16 destinationChainId, - UniversalAddress destinationAddress, - bytes32 payloadHash - ) external; - - /// @notice Called by a integrator contract to receive a message and mark it executed. - /// @param sourceChainId The Wormhole chain ID of the sender. - /// @param sourceAddress The universal address of the peer on the sending chain. - /// @param sequence The sequence number of the message (per integrator). - /// @param destinationChainId The Wormhole chain ID of the destination. - /// @param destinationAddress The destination address of the message. - /// @param payloadHash The keccak256 of payload from the integrator. - /// @return uint128 The bitmap - function receiveMessage( - uint16 sourceChainId, - UniversalAddress sourceAddress, - uint64 sequence, - uint16 destinationChainId, - UniversalAddress destinationAddress, - bytes32 payloadHash - ) external payable returns (uint128, uint128); -} diff --git a/evm/src/interfaces/ITransceiver.sol b/evm/src/interfaces/ITransceiver.sol deleted file mode 100644 index a41eaf0..0000000 --- a/evm/src/interfaces/ITransceiver.sol +++ /dev/null @@ -1,37 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.13; - -import "../libraries/UniversalAddress.sol"; - -interface ITransceiver { - /// @notice The caller is not the NttManager. - /// @dev Selector: 0xc5aa6153. - /// @param caller The address of the caller. - error CallerNotRouter(address caller); - - /// @notice Returns the string type of the transceiver. E.g. "wormhole", "axelar", etc. - function getTransceiverType() external view returns (string memory); - - /// @notice Fetch the delivery price for sending a message to the recipient chain. - /// Note that the message is a fixed size, so is not needed for this calculation. - /// @param destinationChainId The Wormhole chain ID of the target chain. - /// @return deliveryPrice The cost of delivering a message to the recipient chain, - /// in this chain's native token. - function quoteDeliveryPrice(uint16 destinationChainId) external view returns (uint256); - - /// @dev Send a message to another chain. - /// @param sourceAddress The universal address of the sender. - /// @param recipientChain The Wormhole chain ID of the recipient. - /// @param recipientAddress The universal address of the recipient. - /// @param sequence The per-integrator sequence number associated with the message. - /// @param payloadHash The hash of the message to be sent to the recipient chain. - /// @param refundAddress The address of the refund recipient - function sendMessage( - UniversalAddress sourceAddress, - uint16 recipientChain, - UniversalAddress recipientAddress, - uint64 sequence, - bytes32 payloadHash, - bytes32 refundAddress - ) external payable; -} diff --git a/evm/src/interfaces/IWormholeTransceiver.sol b/evm/src/interfaces/IWormholeTransceiver.sol index 99b123e..f8701d7 100644 --- a/evm/src/interfaces/IWormholeTransceiver.sol +++ b/evm/src/interfaces/IWormholeTransceiver.sol @@ -1,91 +1,106 @@ // SPDX-License-Identifier: Apache 2 pragma solidity >=0.8.8 <0.9.0; -import "./ITransceiver.sol"; +import "poc-gmp-router/interfaces/ITransceiver.sol"; -interface IWormholeTransceiver { +interface IWormholeTransceiver is ITransceiver { /// @notice Emitted when the admin is changed for an integrator. /// @dev Topic0 - /// 0xTODO + /// 0x101b8081ff3b56bbf45deb824d86a3b0fd38b7e3dd42421105cf8abe9106db0b /// @param oldAdmin The address of the old admin contract. /// @param newAdmin The address of the new admin contract. event AdminUpdated(address oldAdmin, address newAdmin); /// @notice Emitted when an admin change request is received for an integrator. /// @dev Topic0 - /// 0xTODO - /// @param integrator The address of the integrator contract. + /// 0x51976906b2cd2da8d93d96f315416f8f479f1bd9f7b6c963ad81733c123587e2 /// @param oldAdmin The address of the old admin contract. /// @param newAdmin The address of the new admin contract. - event AdminUpdateRequested(address integrator, address oldAdmin, address newAdmin); + event AdminUpdateRequested(address oldAdmin, address newAdmin); + + /// @notice Emitted when the admin is discarded (set to zero). + /// @dev Topic0 + /// 0xf59e4ee73808efe6c573443d5085333da02eaad3e9890ee65f92053f08b84f4b + /// @param oldAdmin The address of the old admin contract. + event AdminDiscarded(address oldAdmin); /// @notice Emitted when a peer transceiver is set. /// @dev Topic0 - /// 0xa559263ee060c7a2560843b3a064ff0376c9753ae3e2449b595a3b615d326466. + /// 0xa559263ee060c7a2560843b3a064ff0376c9753ae3e2449b595a3b615d326466 /// @param chainId The chain ID of the peer. /// @param peerContract The address of the peer contract. event SetWormholePeer(uint16 chainId, bytes32 peerContract); /// @notice Emitted when a message is received. /// @dev Topic0 - /// 0xf6fc529540981400dc64edf649eb5e2e0eb5812a27f8c81bac2c1d317e71a5f0. + /// 0x5ca30e27fb77c6ca1ed58126d3c96e979a7a3ac370738f72ea998af0dcff2e04 /// @param digest The digest of the message. - /// @param emitterChainId The chain ID of the emitter. + /// @param emitterChain The chain ID of the emitter. /// @param emitterAddress The address of the emitter. /// @param sequence The sequence of the message. - event ReceivedMessage(bytes32 digest, uint16 emitterChainId, bytes32 emitterAddress, uint64 sequence); + event MessageReceived(bytes32 digest, uint16 emitterChain, bytes32 emitterAddress, uint64 sequence); /// @notice Emitted when a message is sent from the transceiver. /// @dev Topic0 - /// TODO. - /// @param sourceAddress The universal address of the sender. - /// @param recipientChain The Wormhole chain ID of the recipient. - /// @param recipientAddress The universal address of the recipient. + /// 0x538adc4f44128ecd04c5b39098e01f60938ab03d019a24fde085a6d2e643864f + /// @param srcAddr The universal address of the sender. + /// @param dstChain The Wormhole chain ID of the recipient. + /// @param dstAddr The universal address of the recipient. /// @param sequence The per-integrator sequence number associated with the message. /// @param payloadHash The hash of the message to be sent to the recipient chain.. - event SendTransceiverMessage( - UniversalAddress sourceAddress, - uint16 recipientChain, - UniversalAddress recipientAddress, - uint64 sequence, - bytes32 payloadHash + event MessageSent( + UniversalAddress srcAddr, uint16 dstChain, UniversalAddress dstAddr, uint64 sequence, bytes32 payloadHash ); + /// @notice Error when the caller is not the registered admin. + /// @dev Selector: 0xe3fb72e9 + /// @param caller The address of the caller. + error CallerNotAdmin(address caller); + + /// @notice Error when an admin action is attempted while an admin transfer is pending. + /// @dev Selector: 9e78953d + error AdminTransferPending(); + + /// @notice Error when an attempt to claim the admin is made when there is no transfer pending. + /// @dev Selector: 0x1ee0a99f + error NoAdminUpdatePending(); + + /// @notice Error when the admin is the zero address. + /// @dev Selector: 0x554ff5d7 + error InvalidAdminZeroAddress(); + /// @notice Error if the VAA is invalid. - /// @dev Selector: 0x8ee2e336. + /// @dev Selector: 0x8ee2e336 /// @param reason The reason the VAA is invalid. error InvalidVaa(string reason); /// @notice Error if the peer has already been set. - /// @dev Selector: 0xb55eeae9. + /// @dev Selector: 0xb55eeae9 /// @param chainId The chain ID of the peer. /// @param peerAddress The address of the peer. error PeerAlreadySet(uint16 chainId, bytes32 peerAddress); /// @notice Error the peer contract cannot be the zero address. - /// @dev Selector: 0x26e0c7de. + /// @dev Selector: 0x26e0c7de error InvalidWormholePeerZeroAddress(); /// @notice The chain ID cannot be zero. - /// @dev Selector: 0x3dd98b24. - error InvalidWormholeChainIdZero(); - - /// @notice Error when the caller is not the registered admin. - error CallerNotAdmin(); + /// @dev Selector: 0x30dfa769 + error InvalidWormholeChainZero(); /// @notice Error when the chain ID doesn't match this chain. - /// @dev Selector: 0x79b1ce56. + /// @dev Selector: 0x587c94c3 /// @param chainId The chain ID of the peer. - error InvalidChainId(uint16 chainId); + error InvalidChain(uint16 chainId); /// @notice Error when the peer transceiver is invalid. - /// @dev Selector: 0x79b1ce56. + /// @dev Selector: 0x79b1ce56 /// @param chainId The chain ID of the peer. /// @param peerAddress The address of the invalid peer. error InvalidWormholePeer(uint16 chainId, bytes32 peerAddress); /// @notice Length of transceiver payload is wrong. - /// @dev Selector: 0x406e719e. + /// @dev Selector: 0xc37906a0 /// @param received Number of payload bytes received. /// @param expected Number of payload bytes expected. error InvalidPayloadLength(uint256 received, uint256 expected); diff --git a/evm/src/libraries/UniversalAddress.sol b/evm/src/libraries/UniversalAddress.sol deleted file mode 100644 index 8903876..0000000 --- a/evm/src/libraries/UniversalAddress.sol +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.13; - -type UniversalAddress is bytes32; - -library UniversalAddressLibrary { - // When a bytes32 field can't be converted into a 20 byte EVM address, because the 12 padding bytes - // are non-zero - error NotAnEvmAddress(bytes32); - - function fromAddress(address addr) internal pure returns (UniversalAddress) { - return fromBytes32(bytes32(uint256(uint160(addr)))); - } - - function toAddress(UniversalAddress uAddr) internal pure returns (address) { - bytes32 internalAddr = toBytes32(uAddr); - // Check if the higher 96 bits (left-most 12 bytes) are non-zero - if (uint256(internalAddr) >> 160 != 0) { - revert NotAnEvmAddress(internalAddr); - } - return address(uint160(uint256(internalAddr))); - } - - function fromBytes32(bytes32 addr) internal pure returns (UniversalAddress) { - return UniversalAddress.wrap(addr); - } - - function toBytes32(UniversalAddress addr) internal pure returns (bytes32) { - return UniversalAddress.unwrap(addr); - } -} - -using UniversalAddressLibrary for UniversalAddress global; - -function equals(UniversalAddress addr1, UniversalAddress addr2) pure returns (bool) { - return UniversalAddressLibrary.toBytes32(addr1) == UniversalAddressLibrary.toBytes32(addr2); -} - -function notEquals(UniversalAddress addr1, UniversalAddress addr2) pure returns (bool) { - return UniversalAddressLibrary.toBytes32(addr1) != UniversalAddressLibrary.toBytes32(addr2); -} - -using {equals as ==, notEquals as !=} for UniversalAddress global; diff --git a/evm/test/WormholeTransceiver.t.sol b/evm/test/WormholeTransceiver.t.sol index 2105921..2d5ddbe 100644 --- a/evm/test/WormholeTransceiver.t.sol +++ b/evm/test/WormholeTransceiver.t.sol @@ -4,39 +4,38 @@ pragma solidity ^0.8.13; import {Test, console} from "forge-std/Test.sol"; import "wormhole-solidity-sdk/libraries/BytesParsing.sol"; import "wormhole-solidity-sdk/interfaces/IWormhole.sol"; -import "../src/libraries/UniversalAddress.sol"; +import "poc-gmp-router/libraries/UniversalAddress.sol"; import "../src/WormholeTransceiver.sol"; import "../src/interfaces/IWormholeTransceiver.sol"; contract WormholeTransceiverForTest is WormholeTransceiver { constructor( - uint16 _ourChainId, - uint256 _ourEvmChainId, + uint16 _ourChain, + uint256 _ourevmChain, address _admin, address _router, address srcWormhole, - uint8 _consistencyLevel, - uint256 _gasLimit - ) WormholeTransceiver(_ourChainId, _ourEvmChainId, _admin, _router, srcWormhole, _consistencyLevel, _gasLimit) {} + uint8 _consistencyLevel + ) WormholeTransceiver(_ourChain, _ourevmChain, _admin, _router, srcWormhole, _consistencyLevel) {} function encodePayload( - UniversalAddress sourceAddress, + UniversalAddress srcAddr, uint64 sequence, - uint16 recipientChain, - UniversalAddress recipientAddress, + uint16 dstChain, + UniversalAddress dstAddr, bytes32 payloadHash ) external pure returns (bytes memory payload) { - return _encodePayload(sourceAddress, sequence, recipientChain, recipientAddress, payloadHash); + return _encodePayload(srcAddr, sequence, dstChain, dstAddr, payloadHash); } function decodePayload(bytes memory payload) external pure returns ( - UniversalAddress sourceAddress, + UniversalAddress srcAddr, uint64 sequence, - uint16 recipientChain, - UniversalAddress recipientAddress, + uint16 dstChain, + UniversalAddress dstAddr, bytes32 payloadHash ) { @@ -47,7 +46,7 @@ contract WormholeTransceiverForTest is WormholeTransceiver { contract MockWormhole { uint256 public constant fixedMessageFee = 250; - uint16 public immutable ourChainId; + uint16 public immutable ourChain; bool public validFlag; string public invalidReason; @@ -64,8 +63,8 @@ contract MockWormhole { bytes public lastVaa; bytes32 public lastVaaHash; - constructor(uint16 _ourChainId) { - ourChainId = _ourChainId; + constructor(uint16 _ourChain) { + ourChain = _ourChain; validFlag = true; } @@ -95,7 +94,7 @@ contract MockWormhole { bytes32 sender = UniversalAddressLibrary.fromAddress(msg.sender).toBytes32(); bytes32 hash = keccak256(payload); - lastVaa = abi.encode(ourChainId, sender, sequence, hash, payload); + lastVaa = abi.encode(ourChain, sender, sequence, hash, payload); lastVaaHash = hash; } @@ -119,32 +118,32 @@ contract MockWormhole { } contract MockRouter { - uint16 public immutable ourChainId; + uint16 public immutable ourChain; // These are set on calls. - uint16 public lastSourceChainId; + uint16 public lastSourceChain; UniversalAddress public lastSourceAddress; uint64 public lastSequence; - uint16 public lastDestinationChainId; + uint16 public lastDestinationChain; UniversalAddress public lastDestinationAddress; bytes32 public lastPayloadHash; - constructor(uint16 _ourChainId) { - ourChainId = _ourChainId; + constructor(uint16 _ourChain) { + ourChain = _ourChain; } function attestMessage( - uint16 sourceChainId, - UniversalAddress sourceAddress, + uint16 srcChain, + UniversalAddress srcAddr, uint64 sequence, - uint16 destinationChainId, + uint16 dstChain, UniversalAddress destinationAddress, bytes32 payloadHash ) external { - lastSourceChainId = sourceChainId; - lastSourceAddress = sourceAddress; + lastSourceChain = srcChain; + lastSourceAddress = srcAddr; lastSequence = sequence; - lastDestinationChainId = destinationChainId; + lastDestinationChain = dstChain; lastDestinationAddress = destinationAddress; lastPayloadHash = payloadHash; } @@ -155,7 +154,7 @@ contract WormholeTransceiverTest is Test { MockRouter srcRouter; MockRouter destRouter; address routerAddr; - address integrator = address(0xabcded); + address integratorAddr = address(0xabcded); address userA = address(0xabcdec); address someoneElse = address(0xabcdeb); MockWormhole srcWormhole; @@ -163,93 +162,230 @@ contract WormholeTransceiverTest is Test { MockWormhole destWormhole; WormholeTransceiverForTest public destTransceiver; uint8 consistencyLevel = 200; - uint256 gasLimit = 10000; - uint16 ourChainId = 42; - uint256 ourEvmChainId = 31337; + uint16 ourChain = 42; + uint256 ourevmChain = 31337; - uint16 srcChainId = 42; - uint16 destChainId = 43; + uint16 srcChain = 42; + uint16 destChain = 43; - uint16 peerChainId1 = 1; + uint16 peerChain1 = 1; address peerAddress1 = address(0x123456); - uint16 peerChainId2 = 2; + uint16 peerChain2 = 2; address peerAddress2 = address(0x123456); - uint16 peerChainId3 = 3; + uint16 peerChain3 = 3; function setUp() public { - srcRouter = new MockRouter(srcChainId); + srcRouter = new MockRouter(srcChain); routerAddr = address(srcRouter); - destRouter = new MockRouter(destChainId); - srcWormhole = new MockWormhole(srcChainId); + destRouter = new MockRouter(destChain); + srcWormhole = new MockWormhole(srcChain); srcTransceiver = new WormholeTransceiverForTest( - srcChainId, ourEvmChainId, admin, routerAddr, address(srcWormhole), consistencyLevel, gasLimit + srcChain, ourevmChain, admin, routerAddr, address(srcWormhole), consistencyLevel ); - destWormhole = new MockWormhole(destChainId); + destWormhole = new MockWormhole(destChain); destTransceiver = new WormholeTransceiverForTest( - destChainId, ourEvmChainId, admin, address(destRouter), address(destWormhole), consistencyLevel, gasLimit + destChain, ourevmChain, admin, address(destRouter), address(destWormhole), consistencyLevel ); + + // Give everyone some money to play with. + vm.deal(integratorAddr, 1 ether); + vm.deal(routerAddr, 1 ether); + vm.deal(userA, 1 ether); + vm.deal(someoneElse, 1 ether); } function test_init() public view { - require(srcTransceiver.ourChainId() == ourChainId, "ourChainId is not right"); + require(srcTransceiver.ourChain() == ourChain, "ourChain is not right"); require(srcTransceiver.admin() == admin, "admin is not right"); require(address(srcTransceiver.router()) == routerAddr, "srcRouter is not right"); require(address(srcTransceiver.wormhole()) == address(srcWormhole), "srcWormhole is not right"); require(srcTransceiver.consistencyLevel() == consistencyLevel, "consistencyLevel is not right"); - require(srcTransceiver.gasLimit() == gasLimit, "gasLimit is not right"); + } + + function test_invalidInit() public { + // ourChain can't be zero. + vm.expectRevert(); + new WormholeTransceiver(0, ourevmChain, admin, address(destRouter), address(destWormhole), consistencyLevel); + + // evmChain can't be zero. + vm.expectRevert(); + new WormholeTransceiver(destChain, 0, admin, address(destRouter), address(destWormhole), consistencyLevel); + + // admin can't be zero. + vm.expectRevert(); + new WormholeTransceiver( + destChain, ourevmChain, address(0), address(destRouter), address(destWormhole), consistencyLevel + ); + + // router can't be zero. + vm.expectRevert(); + new WormholeTransceiver(destChain, ourevmChain, admin, address(0), address(destWormhole), consistencyLevel); + + // wormhole can't be zero. + vm.expectRevert(); + new WormholeTransceiver(destChain, ourevmChain, admin, address(destRouter), address(0), consistencyLevel); + } + + function test_updateAdmin() public { + // Only the admin can initiate this call. + vm.startPrank(userA); + vm.expectRevert(abi.encodeWithSelector(IWormholeTransceiver.CallerNotAdmin.selector, userA)); + srcTransceiver.updateAdmin(someoneElse); + + // Can't set the admin to zero. + vm.startPrank(admin); + vm.expectRevert(abi.encodeWithSelector(IWormholeTransceiver.InvalidAdminZeroAddress.selector)); + srcTransceiver.updateAdmin(address(0)); + + // This should work. + vm.startPrank(admin); + srcTransceiver.updateAdmin(userA); + } + + function test_transferAdmin() public { + // Set up to do a receive below. + vm.startPrank(admin); + destTransceiver.setWormholePeer( + srcChain, UniversalAddressLibrary.fromAddress(address(srcTransceiver)).toBytes32() + ); + + // Only the admin can initiate this call. + vm.startPrank(userA); + vm.expectRevert(abi.encodeWithSelector(IWormholeTransceiver.CallerNotAdmin.selector, userA)); + srcTransceiver.transferAdmin(someoneElse); + + // This should work. + vm.startPrank(admin); + srcTransceiver.transferAdmin(userA); + + // Attempting to do another transfer when one is in progress should revert. + vm.startPrank(admin); + vm.expectRevert(abi.encodeWithSelector(IWormholeTransceiver.AdminTransferPending.selector)); + srcTransceiver.transferAdmin(someoneElse); + + // Attempting to update when a transfer is in progress should revert. + vm.startPrank(admin); + vm.expectRevert(abi.encodeWithSelector(IWormholeTransceiver.AdminTransferPending.selector)); + srcTransceiver.updateAdmin(someoneElse); + + // Attempting to set a peer when a transfer is in progress should revert. + vm.startPrank(admin); + vm.expectRevert(abi.encodeWithSelector(IWormholeTransceiver.AdminTransferPending.selector)); + srcTransceiver.setWormholePeer(0, UniversalAddressLibrary.fromAddress(peerAddress1).toBytes32()); + + // But you can quote the delivery price while a transfer is pending. + srcTransceiver.quoteDeliveryPrice(peerChain1); + + // And you can send a message while a transfer is pending. + UniversalAddress srcAddr = UniversalAddressLibrary.fromAddress(address(userA)); + uint16 dstChain = destChain; + UniversalAddress dstAddr = UniversalAddressLibrary.fromAddress(address(peerAddress1)); + uint64 sequence = 42; + bytes32 payloadHash = keccak256("message one"); + bytes32 refundAddr = UniversalAddressLibrary.fromAddress(address(userA)).toBytes32(); + uint256 deliverPrice = 382; + + vm.startPrank(routerAddr); + srcTransceiver.sendMessage{value: deliverPrice}(srcAddr, dstChain, dstAddr, sequence, payloadHash, refundAddr); + + // And you can receive a message while a transfer is pending. + destTransceiver.receiveMessage(srcWormhole.lastVaa()); + } + + function test_claimAdmin() public { + // Can't claim when a transfer is not pending. + vm.startPrank(admin); + vm.expectRevert(abi.encodeWithSelector(IWormholeTransceiver.NoAdminUpdatePending.selector)); + srcTransceiver.claimAdmin(); + + // Start a transfer. + srcTransceiver.transferAdmin(userA); + + // If someone other than the current or pending admin tries to claim, it should revert. + vm.startPrank(someoneElse); + vm.expectRevert(abi.encodeWithSelector(IWormholeTransceiver.CallerNotAdmin.selector, someoneElse)); + srcTransceiver.claimAdmin(); + + // The admin claiming should cancel the transfer. + vm.startPrank(admin); + srcTransceiver.claimAdmin(); + require(srcTransceiver.admin() == admin, "cancel set the admin incorrectly"); + require(srcTransceiver.pendingAdmin() == address(0), "cancel did not clear the pending admin"); + + // The new admin claiming it should work. + srcTransceiver.transferAdmin(userA); + vm.startPrank(userA); + srcTransceiver.claimAdmin(); + require(srcTransceiver.admin() == userA, "transfer set the admin incorrectly"); + require(srcTransceiver.pendingAdmin() == address(0), "transfer did not clear the pending admin"); + } + + function test_discardAdmin() public { + // Only the admin can initiate this call. + vm.startPrank(userA); + vm.expectRevert(abi.encodeWithSelector(IWormholeTransceiver.CallerNotAdmin.selector, userA)); + srcTransceiver.discardAdmin(); + + // This should work. + vm.startPrank(admin); + srcTransceiver.discardAdmin(); + require(srcTransceiver.admin() == address(0), "transfer set the admin incorrectly"); + require(srcTransceiver.pendingAdmin() == address(0), "transfer did not clear the pending admin"); + + // So now the old admin can't do anything. + vm.expectRevert(abi.encodeWithSelector(IWormholeTransceiver.CallerNotAdmin.selector, admin)); + srcTransceiver.updateAdmin(someoneElse); } function test_setWormholePeer() public { // Only the admin can set a peer. vm.startPrank(someoneElse); - vm.expectRevert(abi.encodeWithSelector(IWormholeTransceiver.CallerNotAdmin.selector)); + vm.expectRevert(abi.encodeWithSelector(IWormholeTransceiver.CallerNotAdmin.selector, someoneElse)); srcTransceiver.setWormholePeer(0, UniversalAddressLibrary.fromAddress(peerAddress1).toBytes32()); // Peer chain can't be zero. vm.startPrank(admin); - vm.expectRevert(abi.encodeWithSelector(IWormholeTransceiver.InvalidWormholeChainIdZero.selector)); + vm.expectRevert(abi.encodeWithSelector(IWormholeTransceiver.InvalidWormholeChainZero.selector)); srcTransceiver.setWormholePeer(0, UniversalAddressLibrary.fromAddress(peerAddress1).toBytes32()); // Peer contract can't be zero. vm.expectRevert(abi.encodeWithSelector(IWormholeTransceiver.InvalidWormholePeerZeroAddress.selector)); - srcTransceiver.setWormholePeer(peerChainId1, UniversalAddressLibrary.fromAddress(address(0)).toBytes32()); + srcTransceiver.setWormholePeer(peerChain1, UniversalAddressLibrary.fromAddress(address(0)).toBytes32()); // This should work. srcTransceiver.setWormholePeer( - peerChainId1, UniversalAddressLibrary.fromAddress(address(peerAddress1)).toBytes32() + peerChain1, UniversalAddressLibrary.fromAddress(address(peerAddress1)).toBytes32() ); // You can't set a peer when it's already set. - vm.expectRevert( - abi.encodeWithSelector(IWormholeTransceiver.PeerAlreadySet.selector, peerChainId1, peerAddress1) - ); + vm.expectRevert(abi.encodeWithSelector(IWormholeTransceiver.PeerAlreadySet.selector, peerChain1, peerAddress1)); srcTransceiver.setWormholePeer( - peerChainId1, UniversalAddressLibrary.fromAddress(address(peerAddress2)).toBytes32() + peerChain1, UniversalAddressLibrary.fromAddress(address(peerAddress2)).toBytes32() ); // But you can set the peer for another chain. srcTransceiver.setWormholePeer( - peerChainId2, UniversalAddressLibrary.fromAddress(address(peerAddress2)).toBytes32() + peerChain2, UniversalAddressLibrary.fromAddress(address(peerAddress2)).toBytes32() ); // Test the getter. require( - srcTransceiver.getWormholePeer(peerChainId1) + srcTransceiver.getWormholePeer(peerChain1) == UniversalAddressLibrary.fromAddress(address(peerAddress1)).toBytes32(), "Peer for chain one is wrong" ); require( - srcTransceiver.getWormholePeer(peerChainId2) + srcTransceiver.getWormholePeer(peerChain2) == UniversalAddressLibrary.fromAddress(address(peerAddress2)).toBytes32(), "Peer for chain two is wrong" ); // If you get a peer for a chain that's not set, it returns zero. require( - srcTransceiver.getWormholePeer(peerChainId3) == UniversalAddressLibrary.fromAddress(address(0)).toBytes32(), + srcTransceiver.getWormholePeer(peerChain3) == UniversalAddressLibrary.fromAddress(address(0)).toBytes32(), "Peer for chain three should not be set" ); } @@ -263,77 +399,72 @@ contract WormholeTransceiverTest is Test { } function test_quoteDeliveryPrice() public view { - require( - srcTransceiver.quoteDeliveryPrice(peerChainId1) == srcWormhole.fixedMessageFee(), "message fee is wrong" - ); + require(srcTransceiver.quoteDeliveryPrice(peerChain1) == srcWormhole.fixedMessageFee(), "message fee is wrong"); } function test_sendMessage() public { - UniversalAddress sourceAddress = UniversalAddressLibrary.fromAddress(address(userA)); - uint16 recipientChain = peerChainId1; - UniversalAddress recipientAddress = UniversalAddressLibrary.fromAddress(address(peerAddress1)); + UniversalAddress srcAddr = UniversalAddressLibrary.fromAddress(address(userA)); + uint16 dstChain = peerChain1; + UniversalAddress dstAddr = UniversalAddressLibrary.fromAddress(address(peerAddress1)); uint64 sequence = 42; bytes32 payloadHash = keccak256("message one"); - bytes32 refundAddress = UniversalAddressLibrary.fromAddress(address(userA)).toBytes32(); + bytes32 refundAddr = UniversalAddressLibrary.fromAddress(address(userA)).toBytes32(); uint256 deliverPrice = 382; - srcTransceiver.sendMessage{value: deliverPrice}( - sourceAddress, recipientChain, recipientAddress, sequence, payloadHash, refundAddress - ); + vm.startPrank(routerAddr); + srcTransceiver.sendMessage{value: deliverPrice}(srcAddr, dstChain, dstAddr, sequence, payloadHash, refundAddr); require(srcWormhole.messagesSent() == 1, "Message count is wrong"); require(srcWormhole.lastNonce() == 0, "Nonce is wrong"); require(srcWormhole.lastConsistencyLevel() == consistencyLevel, "Consistency level is wrong"); require(srcWormhole.lastDeliveryPrice() == deliverPrice, "Deliver price is wrong"); - bytes memory expectedPayload = - srcTransceiver.encodePayload(sourceAddress, sequence, recipientChain, recipientAddress, payloadHash); + bytes memory expectedPayload = srcTransceiver.encodePayload(srcAddr, sequence, dstChain, dstAddr, payloadHash); require( keccak256(abi.encodePacked(srcWormhole.lastPayload())) == keccak256(expectedPayload), "Payload is wrong" ); + + // Only the router can call send message. + vm.startPrank(someoneElse); + vm.expectRevert(abi.encodeWithSelector(ITransceiver.CallerNotRouter.selector, someoneElse)); + srcTransceiver.sendMessage{value: deliverPrice}(srcAddr, dstChain, dstAddr, sequence, payloadHash, refundAddr); } function test_receiveMessage() public { - vm.deal(integrator, 1 ether); - // Set the peers on the transceivers. vm.startPrank(admin); srcTransceiver.setWormholePeer( - destChainId, UniversalAddressLibrary.fromAddress(address(destTransceiver)).toBytes32() + destChain, UniversalAddressLibrary.fromAddress(address(destTransceiver)).toBytes32() ); destTransceiver.setWormholePeer( - srcChainId, UniversalAddressLibrary.fromAddress(address(srcTransceiver)).toBytes32() + srcChain, UniversalAddressLibrary.fromAddress(address(srcTransceiver)).toBytes32() ); - vm.startPrank(integrator); - UniversalAddress sourceAddress = UniversalAddressLibrary.fromAddress(address(userA)); - uint16 recipientChain = destChainId; - UniversalAddress recipientAddress = UniversalAddressLibrary.fromAddress(address(peerAddress1)); + UniversalAddress srcAddr = UniversalAddressLibrary.fromAddress(address(userA)); + uint16 dstChain = destChain; + UniversalAddress dstAddr = UniversalAddressLibrary.fromAddress(address(peerAddress1)); uint64 sequence = 42; bytes32 payloadHash = keccak256("message one"); - bytes32 refundAddress = UniversalAddressLibrary.fromAddress(address(userA)).toBytes32(); + bytes32 refundAddr = UniversalAddressLibrary.fromAddress(address(userA)).toBytes32(); uint256 deliverPrice = 382; - srcTransceiver.sendMessage{value: deliverPrice}( - sourceAddress, recipientChain, recipientAddress, sequence, payloadHash, refundAddress - ); + vm.startPrank(routerAddr); + srcTransceiver.sendMessage{value: deliverPrice}(srcAddr, dstChain, dstAddr, sequence, payloadHash, refundAddr); bytes memory vaa = srcWormhole.lastVaa(); // This should work. destTransceiver.receiveMessage(vaa); - require(destRouter.lastSourceChainId() == srcChainId, "sourceChainId is wrong"); - require(destRouter.lastSourceAddress() == sourceAddress, "sourceAddress is wrong"); + require(destRouter.lastSourceChain() == srcChain, "srcChain is wrong"); + require(destRouter.lastSourceAddress() == srcAddr, "srcAddr is wrong"); require(destRouter.lastSequence() == sequence, "sequence is wrong"); - require(destRouter.lastDestinationChainId() == recipientChain, "destinationChainId is wrong"); - require(destRouter.lastDestinationAddress() == recipientAddress, "recipientAddress is wrong"); + require(destRouter.lastDestinationChain() == dstChain, "dstChain is wrong"); + require(destRouter.lastDestinationAddress() == dstAddr, "dstAddr is wrong"); require(destRouter.lastPayloadHash() == payloadHash, "payloadHash is wrong"); // Can't post it to the wrong transceiver. vm.expectRevert( - abi.encodeWithSelector( - IWormholeTransceiver.InvalidWormholePeer.selector, srcChainId, address(srcTransceiver) - ) + abi.encodeWithSelector(IWormholeTransceiver.InvalidWormholePeer.selector, srcChain, address(srcTransceiver)) ); srcTransceiver.receiveMessage(vaa); @@ -345,40 +476,33 @@ contract WormholeTransceiverTest is Test { // Can't post to the wrong chain. WormholeTransceiverForTest destTransceiver2 = new WormholeTransceiverForTest( - destChainId + 1, - ourEvmChainId, - admin, - address(destRouter), - address(destWormhole), - consistencyLevel, - gasLimit + destChain + 1, ourevmChain, admin, address(destRouter), address(destWormhole), consistencyLevel ); vm.startPrank(admin); destTransceiver2.setWormholePeer( - srcChainId, UniversalAddressLibrary.fromAddress(address(srcTransceiver)).toBytes32() + srcChain, UniversalAddressLibrary.fromAddress(address(srcTransceiver)).toBytes32() ); - vm.startPrank(integrator); - vm.expectRevert(abi.encodeWithSelector(IWormholeTransceiver.InvalidChainId.selector, destChainId)); + vm.startPrank(integratorAddr); + vm.expectRevert(abi.encodeWithSelector(IWormholeTransceiver.InvalidChain.selector, destChain)); destTransceiver2.receiveMessage(vaa); } function test_encodeDecode() public { - UniversalAddress sourceAddress = UniversalAddressLibrary.fromAddress(address(userA)); + UniversalAddress srcAddr = UniversalAddressLibrary.fromAddress(address(userA)); uint64 sequence = 42; - uint16 recipientChain = srcChainId; - UniversalAddress recipientAddress = UniversalAddressLibrary.fromAddress(address(peerAddress1)); + uint16 dstChain = srcChain; + UniversalAddress dstAddr = UniversalAddressLibrary.fromAddress(address(peerAddress1)); bytes32 payloadHash = keccak256("message one"); - bytes memory payload = - srcTransceiver.encodePayload(sourceAddress, sequence, recipientChain, recipientAddress, payloadHash); + bytes memory payload = srcTransceiver.encodePayload(srcAddr, sequence, dstChain, dstAddr, payloadHash); (UniversalAddress sa, uint64 sn, uint16 rc, UniversalAddress ra, bytes32 ph) = srcTransceiver.decodePayload(payload); - require(sa == sourceAddress, "sourceAddress is wrong"); + require(sa == srcAddr, "srcAddr is wrong"); require(sn == sequence, "sequence is wrong"); - require(rc == recipientChain, "recipientChain is wrong"); - require(ra == recipientAddress, "recipientAddress is wrong"); + require(rc == dstChain, "dstChain is wrong"); + require(ra == dstAddr, "dstAddr is wrong"); require(ph == payloadHash, "payloadHash is wrong"); // Decoding something too short should revert.