trustless-generic-relayer/ethereum/contracts/mock/MockWormhole.sol

282 lines
8.8 KiB
Solidity

// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.17;
import "../interfaces/IWormhole.sol";
import "../libraries/external/BytesLib.sol";
contract MockWormhole is IWormhole {
using BytesLib for bytes;
uint256 private constant VM_VERSION_SIZE = 1;
uint256 private constant VM_GUARDIAN_SET_SIZE = 4;
uint256 private constant VM_SIGNATURE_COUNT_SIZE = 1;
uint256 private constant VM_TIMESTAMP_SIZE = 4;
uint256 private constant VM_NONCE_SIZE = 4;
uint256 private constant VM_EMITTER_CHAIN_ID_SIZE = 2;
uint256 private constant VM_EMITTER_ADDRESS_SIZE = 32;
uint256 private constant VM_SEQUENCE_SIZE = 8;
uint256 private constant VM_CONSISTENCY_LEVEL_SIZE = 1;
uint256 private constant VM_SIZE_MINIMUM = VM_VERSION_SIZE + VM_GUARDIAN_SET_SIZE + VM_SIGNATURE_COUNT_SIZE
+ VM_TIMESTAMP_SIZE + VM_NONCE_SIZE + VM_EMITTER_CHAIN_ID_SIZE + VM_EMITTER_ADDRESS_SIZE + VM_SEQUENCE_SIZE
+ VM_CONSISTENCY_LEVEL_SIZE;
uint256 private constant SIGNATURE_GUARDIAN_INDEX_SIZE = 1;
uint256 private constant SIGNATURE_R_SIZE = 32;
uint256 private constant SIGNATURE_S_SIZE = 32;
uint256 private constant SIGNATURE_V_SIZE = 1;
uint256 private constant SIGNATURE_SIZE_TOTAL =
SIGNATURE_GUARDIAN_INDEX_SIZE + SIGNATURE_R_SIZE + SIGNATURE_S_SIZE + SIGNATURE_V_SIZE;
mapping(address => uint64) public sequences;
// Dictionary of VMs that must be mocked as invalid.
mapping(bytes32 => bool) public invalidVMs;
uint256 currentMsgFee;
uint16 immutable wormholeChainId;
uint256 immutable boundEvmChainId;
constructor(uint16 initChainId, uint256 initEvmChainId) {
wormholeChainId = initChainId;
boundEvmChainId = initEvmChainId;
}
function invalidateVM(bytes calldata encodedVm) external {
VM memory vm = _parseVM(encodedVm);
invalidVMs[vm.hash] = true;
}
function publishMessage(uint32 nonce, bytes memory payload, uint8 consistencyLevel)
external
payable
returns (uint64 sequence)
{
require(msg.value == currentMsgFee, "invalid fee");
sequence = sequences[msg.sender]++;
emit LogMessagePublished(msg.sender, sequence, nonce, payload, consistencyLevel);
}
function parseVM(bytes calldata encodedVm) external pure returns (VM memory vm) {
vm = _parseVM(encodedVm);
}
function parseAndVerifyVM(bytes calldata encodedVm)
external
view
returns (VM memory vm, bool valid, string memory reason)
{
vm = _parseVM(encodedVm);
//behold the rigorous checking!
valid = !invalidVMs[vm.hash];
reason = "";
}
function _parseVM(bytes calldata encodedVm) internal pure returns (VM memory vm) {
require(encodedVm.length >= VM_SIZE_MINIMUM, "vm too small");
bytes memory body;
uint256 offset = 0;
vm.version = encodedVm.toUint8(offset);
offset += 1;
vm.guardianSetIndex = encodedVm.toUint32(offset);
offset += 4;
(vm.signatures, offset) = parseSignatures(encodedVm, offset);
body = encodedVm[offset:];
vm.timestamp = encodedVm.toUint32(offset);
offset += 4;
vm.nonce = encodedVm.toUint32(offset);
offset += 4;
vm.emitterChainId = encodedVm.toUint16(offset);
offset += 2;
vm.emitterAddress = encodedVm.toBytes32(offset);
offset += 32;
vm.sequence = encodedVm.toUint64(offset);
offset += 8;
vm.consistencyLevel = encodedVm.toUint8(offset);
offset += 1;
vm.payload = encodedVm[offset:];
vm.hash = keccak256(abi.encodePacked(keccak256(body)));
}
function parseSignatures(bytes calldata encodedVm, uint256 offset)
internal
pure
returns (Signature[] memory signatures, uint256 offsetAfterParse)
{
uint256 sigCount = uint256(encodedVm.toUint8(offset));
offset += 1;
require(encodedVm.length >= (VM_SIZE_MINIMUM + sigCount * SIGNATURE_SIZE_TOTAL), "vm too small");
signatures = new Signature[](sigCount);
for (uint256 i = 0; i < sigCount; ++i) {
uint8 guardianIndex = encodedVm.toUint8(offset);
offset += 1;
bytes32 r = encodedVm.toBytes32(offset);
offset += 32;
bytes32 s = encodedVm.toBytes32(offset);
offset += 32;
uint8 v = encodedVm.toUint8(offset);
offset += 1;
signatures[i] = Signature({
r: r,
s: s,
// The hardcoded 27 comes from the base offset for public key recovery ids, public key type and network
// used in ECDSA signatures for bitcoin and ethereum.
// See https://bitcoin.stackexchange.com/a/5089
v: v + 27,
guardianIndex: guardianIndex
});
}
return (signatures, offset);
}
function initialize() external {}
function quorum(uint256 /*numGuardians*/ ) external pure returns (uint256 /*numSignaturesRequiredForQuorum*/ ) {
return 1;
}
/**
* General state and chain observers
*/
function chainId() external view returns (uint16) {
return wormholeChainId;
}
function evmChainId() external view returns (uint256) {
return boundEvmChainId;
}
function getCurrentGuardianSetIndex() external pure returns (uint32) {
return 0;
}
function getGuardianSet(uint32 /*index*/ ) external pure returns (GuardianSet memory) {
revert("unsupported getGuardianSet in wormhole mock");
}
function getGuardianSetExpiry() external pure returns (uint32) {
return 0;
}
function governanceActionIsConsumed(bytes32 /*hash*/ ) external pure returns (bool) {
return false;
}
function isInitialized(address /*impl*/ ) external pure returns (bool) {
return true;
}
function isFork() external pure returns (bool) {
return false;
}
function governanceChainId() external pure returns (uint16) {
return 1;
}
function governanceContract() external pure returns (bytes32) {
return bytes32(0x0000000000000000000000000000000000000000000000000000000000000004);
}
function messageFee() external view returns (uint256) {
return currentMsgFee;
}
function nextSequence(address emitter) external view returns (uint64) {
return sequences[emitter];
}
function verifyVM(VM memory /*vm*/ ) external pure returns (bool, /*valid*/ string memory /*reason*/ ) {
revert("unsupported verifyVM in wormhole mock");
}
function verifySignatures(bytes32, /*hash*/ Signature[] memory, /*signatures*/ GuardianSet memory /*guardianSet*/ )
external
pure
returns (bool, /*valid*/ string memory /*reason*/ )
{
revert("unsupported verifySignatures in wormhole mock");
}
function parseContractUpgrade(bytes memory /*encodedUpgrade*/ )
external
pure
returns (ContractUpgrade memory /*cu*/ )
{
revert("unsupported parseContractUpgrade in wormhole mock");
}
function parseGuardianSetUpgrade(bytes memory /*encodedUpgrade*/ )
external
pure
returns (GuardianSetUpgrade memory /*gsu*/ )
{
revert("unsupported parseGuardianSetUpgrade in wormhole mock");
}
function parseSetMessageFee(bytes memory /*encodedSetMessageFee*/ )
external
pure
returns (SetMessageFee memory /*smf*/ )
{
revert("unsupported parseSetMessageFee in wormhole mock");
}
function parseTransferFees(bytes memory /*encodedTransferFees*/ )
external
pure
returns (TransferFees memory /*tf*/ )
{
revert("unsupported parseTransferFees in wormhole mock");
}
function parseRecoverChainId(bytes memory /*encodedRecoverChainId*/ )
external
pure
returns (RecoverChainId memory /*rci*/ )
{
revert("unsupported parseRecoverChainId in wormhole mock");
}
function submitContractUpgrade(bytes memory /*_vm*/ ) external pure {
revert("unsupported submitContractUpgrade in wormhole mock");
}
function submitSetMessageFee(bytes memory /*_vm*/ ) external pure {
revert("unsupported submitSetMessageFee in wormhole mock");
}
function setMessageFee(uint256 newFee) external {
currentMsgFee = newFee;
}
function submitNewGuardianSet(bytes memory /*_vm*/ ) external pure {
revert("unsupported submitNewGuardianSet in wormhole mock");
}
function submitTransferFees(bytes memory /*_vm*/ ) external pure {
revert("unsupported submitTransferFees in wormhole mock");
}
function submitRecoverChainId(bytes memory /*_vm*/ ) external pure {
revert("unsupported submitRecoverChainId in wormhole mock");
}
}