Add wormhole receiver + docs on how to use it (#216)

* Add wormhole receiver + docs on how to use it

* Fix a mistake in comment

* Update docs + add wormhole receiver address to the registry
This commit is contained in:
Ali Behjati 2022-05-20 13:12:50 +02:00 committed by GitHub
parent b2becab951
commit 09fcb158dd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 689 additions and 2 deletions

View File

@ -1,5 +1,10 @@
# Migrations Metadata
MIGRATIONS_DIR=./migrations/test
# By default tests are run against the tilt Wormhole deployment. If you wish to test against
# the read-only Wormhole receiver instead, uncomment the following line:
# MIGRATIONS_DIR=./migrations/prod-receiver
MIGRATIONS_NETWORK=development
# Wormhole Core Migrations

View File

@ -1,6 +1,6 @@
# Deploying Contracts to Production
Running the Truffle migrations in [`migrations/prod`](migrations/prod) will deploy the contracts to production.
Running the Truffle migrations in [`migrations/prod`](migrations/prod) or [`migrations/prod-receiver`](migrations/prod-receiver/) will deploy the contracts to production. The `prod-receiver` migrations should be used when you need to deploy to a chain that is unsupported by the Wormhole network. The Wormhole Receiver contract acts as a read-only Wormhole endpoint that can verify Wormhole messages even if the Wormhole network has not yet connected the chain.
This is the deployment process:
@ -20,6 +20,9 @@ npx apply-registry
# Perform the migration
npx truffle migrate --network $MIGRATIONS_NETWORK
# Perform this in first time mainnet deployments with Wormhole Receiver. (Or when guardian sets are upgraded)
npm run receiver-submit-guardian-sets -- --network $MIGRATIONS_NETWORK
```
As a sanity check, it is recommended to deploy the migrations in `migrations/prod` to the Truffle `development` network first. You can do this by using the configuration values in [`.env.prod.development`](.env.prod.development).
@ -29,7 +32,8 @@ As a result of this process for some files (with the network id in their name) i
## `networks` directory
Truffle stores the address of the deployed contracts in the build artifacts, which can make local development difficult. We use [`truffle-deploy-registry`](https://github.com/MedXProtocol/truffle-deploy-registry) to store the addresses separately from the artifacts, in the [`networks`](networks) directory. When we need to perform operations on the deployed contracts, such as performing additional migrations, we can run `npx apply-registry` to populate the artifacts with the correct addresses.
Each file in the network directory is named after the network id and contains address of Migration contract and PythUpgradable contract. If you are upgrading the contract it should not change. In case you are deploying to a new network make sure to commit this file.
Each file in the network directory is named after the network id and contains address of Migration contract and PythUpgradable contract
(and Wormhole Receiver if we use `prod-receiver`). If you are upgrading the contract it should not change. In case you are deploying to a new network make sure to commit this file.
## `.openzeppelin` directory
In order to handle upgrades safely this directory stores details of the contracts structure, such as implementation addresses

View File

@ -0,0 +1,40 @@
// contracts/Getters.sol
// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.0;
import "./ReceiverState.sol";
contract ReceiverGetters is ReceiverState {
function owner() public view returns (address) {
return _state.owner;
}
function getGuardianSet(uint32 index) public view returns (ReceiverStructs.GuardianSet memory) {
return _state.guardianSets[index];
}
function getCurrentGuardianSetIndex() public view returns (uint32) {
return _state.guardianSetIndex;
}
function getGuardianSetExpiry() public view returns (uint32) {
return _state.guardianSetExpiry;
}
function governanceActionIsConsumed(bytes32 hash) public view returns (bool) {
return _state.consumedGovernanceActions[hash];
}
function isInitialized(address impl) public view returns (bool) {
return _state.initializedImplementations[impl];
}
function governanceChainId() public view returns (uint16){
return _state.provider.governanceChainId;
}
function governanceContract() public view returns (bytes32){
return _state.provider.governanceContract;
}
}

View File

@ -0,0 +1,95 @@
// contracts/Governance.sol
// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.0;
import "./ReceiverStructs.sol";
import "./ReceiverGovernanceStructs.sol";
import "./ReceiverMessages.sol";
import "./ReceiverSetters.sol";
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Upgrade.sol";
abstract contract ReceiverGovernance is ReceiverGovernanceStructs, ReceiverMessages, ReceiverSetters, ERC1967Upgrade {
event ContractUpgraded(address indexed oldContract, address indexed newContract);
event OwnershipTransfered(address indexed oldOwner, address indexed newOwner);
// "Core" (left padded)
bytes32 constant module = 0x00000000000000000000000000000000000000000000000000000000436f7265;
function submitNewGuardianSet(bytes memory _vm) public {
ReceiverStructs.VM memory vm = parseVM(_vm);
(bool isValid, string memory reason) = verifyGovernanceVM(vm);
require(isValid, reason);
ReceiverGovernanceStructs.GuardianSetUpgrade memory upgrade = parseGuardianSetUpgrade(vm.payload);
require(upgrade.module == module, "invalid Module");
require(upgrade.newGuardianSet.keys.length > 0, "new guardian set is empty");
require(upgrade.newGuardianSetIndex == getCurrentGuardianSetIndex() + 1, "index must increase in steps of 1");
setGovernanceActionConsumed(vm.hash);
expireGuardianSet(getCurrentGuardianSetIndex());
storeGuardianSet(upgrade.newGuardianSet, upgrade.newGuardianSetIndex);
updateGuardianSetIndex(upgrade.newGuardianSetIndex);
}
function upgradeImplementation(address newImplementation) public onlyOwner {
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 verifyGovernanceVM(ReceiverStructs.VM memory vm) internal view returns (bool, string memory){
// validate vm
(bool isValid, string memory reason) = verifyVM(vm);
if (!isValid){
return (false, reason);
}
// only current guardianset can sign governance packets
if (vm.guardianSetIndex != getCurrentGuardianSetIndex()) {
return (false, "not signed by current guardian set");
}
// verify source
if (uint16(vm.emitterChainId) != governanceChainId()) {
return (false, "wrong governance chain");
}
if (vm.emitterAddress != governanceContract()) {
return (false, "wrong governance contract");
}
// prevent re-entry
if (governanceActionIsConsumed(vm.hash)){
return (false, "governance action already consumed");
}
return (true, "");
}
function transferOwnership(address newOwner) public onlyOwner {
require(newOwner != address(0), "new owner cannot be the zero address");
address currentOwner = owner();
setOwner(newOwner);
emit OwnershipTransfered(currentOwner, newOwner);
}
modifier onlyOwner() {
require(owner() == msg.sender, "caller is not the owner");
_;
}
}

View File

@ -0,0 +1,59 @@
// contracts/GovernanceStructs.sol
// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.0;
import "../libraries/external/BytesLib.sol";
import "./ReceiverStructs.sol";
contract ReceiverGovernanceStructs {
using BytesLib for bytes;
enum GovernanceAction {
UpgradeContract,
UpgradeGuardianset
}
struct GuardianSetUpgrade {
bytes32 module;
uint8 action;
uint16 chain;
ReceiverStructs.GuardianSet newGuardianSet;
uint32 newGuardianSetIndex;
}
function parseGuardianSetUpgrade(bytes memory encodedUpgrade) public pure returns (GuardianSetUpgrade memory gsu) {
uint index = 0;
gsu.module = encodedUpgrade.toBytes32(index);
index += 32;
gsu.action = encodedUpgrade.toUint8(index);
index += 1;
require(gsu.action == 2, "invalid GuardianSetUpgrade");
gsu.chain = encodedUpgrade.toUint16(index);
index += 2;
gsu.newGuardianSetIndex = encodedUpgrade.toUint32(index);
index += 4;
uint8 guardianLength = encodedUpgrade.toUint8(index);
index += 1;
gsu.newGuardianSet = ReceiverStructs.GuardianSet({
keys : new address[](guardianLength),
expirationTime : 0
});
for(uint i = 0; i < guardianLength; i++) {
gsu.newGuardianSet.keys[i] = encodedUpgrade.toAddress(index);
index += 20;
}
require(encodedUpgrade.length == index, "invalid GuardianSetUpgrade");
}
}

View File

@ -0,0 +1,29 @@
// contracts/Implementation.sol
// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.0;
pragma experimental ABIEncoderV2;
import "./ReceiverGovernance.sol";
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Upgrade.sol";
contract ReceiverImplementation is ReceiverGovernance {
modifier initializer() {
address implementation = ERC1967Upgrade._getImplementation();
require(
!isInitialized(implementation),
"already initialized"
);
setInitialized(implementation);
_;
}
fallback() external payable {revert("unsupported");}
receive() external payable {revert("the Wormhole Receiver contract does not accept assets");}
}

View File

@ -0,0 +1,148 @@
// contracts/Messages.sol
// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.0;
pragma experimental ABIEncoderV2;
import "./ReceiverGetters.sol";
import "./ReceiverStructs.sol";
import "../libraries/external/BytesLib.sol";
contract ReceiverMessages is ReceiverGetters {
using BytesLib for bytes;
/// @dev parseAndVerifyVM serves to parse an encodedVM and wholy validate it for consumption
function parseAndVerifyVM(bytes calldata encodedVM) public view returns (ReceiverStructs.VM memory vm, bool valid, string memory reason) {
vm = parseVM(encodedVM);
(valid, reason) = verifyVM(vm);
}
/**
* @dev `verifyVM` serves to validate an arbitrary vm against a valid Guardian set
* - it aims to make sure the VM is for a known guardianSet
* - it aims to ensure the guardianSet is not expired
* - it aims to ensure the VM has reached quorum
* - it aims to verify the signatures provided against the guardianSet
*/
function verifyVM(ReceiverStructs.VM memory vm) public view returns (bool valid, string memory reason) {
/// @dev Obtain the current guardianSet for the guardianSetIndex provided
ReceiverStructs.GuardianSet memory guardianSet = getGuardianSet(vm.guardianSetIndex);
/**
* @dev Checks whether the guardianSet has zero keys
* WARNING: This keys check is critical to ensure the guardianSet has keys present AND to ensure
* that guardianSet key size doesn't fall to zero and negatively impact quorum assessment. If guardianSet
* key length is 0 and vm.signatures length is 0, this could compromise the integrity of both vm and
* signature verification.
*/
if(guardianSet.keys.length == 0){
return (false, "invalid guardian set");
}
/// @dev Checks if VM guardian set index matches the current index (unless the current set is expired).
if(vm.guardianSetIndex != getCurrentGuardianSetIndex() && guardianSet.expirationTime < block.timestamp){
return (false, "guardian set has expired");
}
/**
* @dev We're using a fixed point number transformation with 1 decimal to deal with rounding.
* WARNING: This quorum check is critical to assessing whether we have enough Guardian signatures to validate a VM
* if making any changes to this, obtain additional peer review. If guardianSet key length is 0 and
* vm.signatures length is 0, this could compromise the integrity of both vm and signature verification.
*/
if(((guardianSet.keys.length * 10 / 3) * 2) / 10 + 1 > vm.signatures.length){
return (false, "no quorum");
}
/// @dev Verify the proposed vm.signatures against the guardianSet
(bool signaturesValid, string memory invalidReason) = verifySignatures(vm.hash, vm.signatures, guardianSet);
if(!signaturesValid){
return (false, invalidReason);
}
/// If we are here, we've validated the VM is a valid multi-sig that matches the guardianSet.
return (true, "");
}
/**
* @dev verifySignatures serves to validate arbitrary sigatures against an arbitrary guardianSet
* - it intentionally does not solve for expectations within guardianSet (you should use verifyVM if you need these protections)
* - it intentioanlly does not solve for quorum (you should use verifyVM if you need these protections)
* - it intentionally returns true when signatures is an empty set (you should use verifyVM if you need these protections)
*/
function verifySignatures(bytes32 hash, ReceiverStructs.Signature[] memory signatures, ReceiverStructs.GuardianSet memory guardianSet) public pure returns (bool valid, string memory reason) {
uint8 lastIndex = 0;
for (uint i = 0; i < signatures.length; i++) {
ReceiverStructs.Signature memory sig = signatures[i];
/// Ensure that provided signature indices are ascending only
require(i == 0 || sig.guardianIndex > lastIndex, "signature indices must be ascending");
lastIndex = sig.guardianIndex;
/// Check to see if the signer of the signature does not match a specific Guardian key at the provided index
if(ecrecover(hash, sig.v, sig.r, sig.s) != guardianSet.keys[sig.guardianIndex]){
return (false, "VM signature invalid");
}
}
/// If we are here, we've validated that the provided signatures are valid for the provided guardianSet
return (true, "");
}
/**
* @dev parseVM serves to parse an encodedVM into a vm struct
* - it intentionally performs no validation functions, it simply parses raw into a struct
*/
function parseVM(bytes memory encodedVM) public pure virtual returns (ReceiverStructs.VM memory vm) {
uint index = 0;
vm.version = encodedVM.toUint8(index);
index += 1;
require(vm.version == 1, "VM version incompatible");
vm.guardianSetIndex = encodedVM.toUint32(index);
index += 4;
// Parse Signatures
uint256 signersLen = encodedVM.toUint8(index);
index += 1;
vm.signatures = new ReceiverStructs.Signature[](signersLen);
for (uint i = 0; i < signersLen; i++) {
vm.signatures[i].guardianIndex = encodedVM.toUint8(index);
index += 1;
vm.signatures[i].r = encodedVM.toBytes32(index);
index += 32;
vm.signatures[i].s = encodedVM.toBytes32(index);
index += 32;
vm.signatures[i].v = encodedVM.toUint8(index) + 27;
index += 1;
}
// Hash the body
bytes memory body = encodedVM.slice(index, encodedVM.length - index);
vm.hash = keccak256(abi.encodePacked(keccak256(body)));
// Parse the body
vm.timestamp = encodedVM.toUint32(index);
index += 4;
vm.nonce = encodedVM.toUint32(index);
index += 4;
vm.emitterChainId = encodedVM.toUint16(index);
index += 2;
vm.emitterAddress = encodedVM.toBytes32(index);
index += 32;
vm.sequence = encodedVM.toUint64(index);
index += 8;
vm.consistencyLevel = encodedVM.toUint8(index);
index += 1;
vm.payload = encodedVM.slice(index, encodedVM.length - index);
}
}

View File

@ -0,0 +1,41 @@
// contracts/Setters.sol
// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.0;
import "./ReceiverState.sol";
contract ReceiverSetters is ReceiverState {
function setOwner(address owner_) internal {
_state.owner = owner_;
}
function updateGuardianSetIndex(uint32 newIndex) internal {
_state.guardianSetIndex = newIndex;
}
function expireGuardianSet(uint32 index) internal {
_state.guardianSets[index].expirationTime = uint32(block.timestamp) + 86400;
}
function storeGuardianSet(ReceiverStructs.GuardianSet memory set, uint32 index) internal {
_state.guardianSets[index] = set;
}
function setInitialized(address implementatiom) internal {
_state.initializedImplementations[implementatiom] = true;
}
function setGovernanceActionConsumed(bytes32 hash) internal {
_state.consumedGovernanceActions[hash] = true;
}
function setGovernanceChainId(uint16 chainId) internal {
_state.provider.governanceChainId = chainId;
}
function setGovernanceContract(bytes32 governanceContract) internal {
_state.provider.governanceContract = governanceContract;
}
}

View File

@ -0,0 +1,35 @@
// contracts/Implementation.sol
// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.0;
pragma experimental ABIEncoderV2;
import "./ReceiverGovernance.sol";
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Upgrade.sol";
contract ReceiverSetup is ReceiverSetters, ERC1967Upgrade {
function setup(
address implementation,
address[] memory initialGuardians,
uint16 governanceChainId,
bytes32 governanceContract
) public {
require(initialGuardians.length > 0, "no guardians specified");
setOwner(msg.sender);
ReceiverStructs.GuardianSet memory initialGuardianSet = ReceiverStructs.GuardianSet({
keys : initialGuardians,
expirationTime : 0
});
storeGuardianSet(initialGuardianSet, 0);
// initial guardian set index is 0, which is the default value of the storage slot anyways
setGovernanceChainId(governanceChainId);
setGovernanceContract(governanceContract);
_upgradeTo(implementation);
}
}

View File

@ -0,0 +1,47 @@
// contracts/State.sol
// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.0;
import "./ReceiverStructs.sol";
contract ReceiverEvents {
event LogGuardianSetChanged(
uint32 oldGuardianIndex,
uint32 newGuardianIndex
);
event LogMessagePublished(
address emitter_address,
uint32 nonce,
bytes payload
);
}
contract ReceiverStorage {
struct WormholeState {
ReceiverStructs.Provider provider;
// contract deployer
address owner;
// Mapping of guardian_set_index => guardian set
mapping(uint32 => ReceiverStructs.GuardianSet) guardianSets;
// Current active guardian set index
uint32 guardianSetIndex;
// Period for which a guardian set stays active after it has been replaced
uint32 guardianSetExpiry;
// Mapping of consumed governance actions
mapping(bytes32 => bool) consumedGovernanceActions;
// Mapping of initialized implementations
mapping(address => bool) initializedImplementations;
}
}
contract ReceiverState {
ReceiverStorage.WormholeState _state;
}

View File

@ -0,0 +1,39 @@
// contracts/Structs.sol
// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.0;
interface ReceiverStructs {
struct Provider {
uint16 governanceChainId;
bytes32 governanceContract;
}
struct GuardianSet {
address[] keys;
uint32 expirationTime;
}
struct Signature {
bytes32 r;
bytes32 s;
uint8 v;
uint8 guardianIndex;
}
struct VM {
uint8 version;
uint32 timestamp;
uint32 nonce;
uint16 emitterChainId;
bytes32 emitterAddress;
uint64 sequence;
uint8 consistencyLevel;
bytes payload;
uint32 guardianSetIndex;
Signature[] signatures;
bytes32 hash;
}
}

View File

@ -0,0 +1,13 @@
// contracts/Wormhole.sol
// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
contract WormholeReceiver is ERC1967Proxy {
constructor (address setup, bytes memory initData) ERC1967Proxy(
setup,
initData
) { }
}

View File

@ -0,0 +1,12 @@
const Migrations = artifacts.require("Migrations");
const tdr = require('truffle-deploy-registry');
module.exports = async function (deployer, network) {
await deployer.deploy(Migrations);
let migrationsInstance = await Migrations.deployed();
if (!tdr.isDryRunNetworkName(network)) {
await tdr.appendInstance(migrationsInstance);
}
};

View File

@ -0,0 +1,38 @@
require("dotenv").config({ path: "../.env" });
const tdr = require('truffle-deploy-registry');
const ReceiverSetup = artifacts.require("ReceiverSetup");
const ReceiverImplementation = artifacts.require("ReceiverImplementation");
const WormholeReceiver = artifacts.require("WormholeReceiver");
// CONFIG
const initialSigners = JSON.parse(process.env.INIT_SIGNERS);
const governanceChainId = process.env.INIT_GOV_CHAIN_ID;
const governanceContract = process.env.INIT_GOV_CONTRACT; // bytes32
module.exports = async function (deployer, network) {
// deploy setup
await deployer.deploy(ReceiverSetup);
// deploy implementation
await deployer.deploy(ReceiverImplementation);
// encode initialisation data
const setup = new web3.eth.Contract(ReceiverSetup.abi, ReceiverSetup.address);
const initData = setup.methods
.setup(
ReceiverImplementation.address,
initialSigners,
governanceChainId,
governanceContract
)
.encodeABI();
// deploy proxy
const wormholeReceiver = await deployer.deploy(WormholeReceiver, ReceiverSetup.address, initData);
if (!tdr.isDryRunNetworkName(network)) {
await tdr.appendInstance(wormholeReceiver);
}
};

View File

@ -0,0 +1,34 @@
require('dotenv').config({ path: "../.env" });
const bs58 = require("bs58");
const PythUpgradable = artifacts.require("PythUpgradable");
const WormholeReceiver = artifacts.require("WormholeReceiver");
const pyth2WormholeChainId = process.env.PYTH_TO_WORMHOLE_CHAIN_ID;
const pyth2WormholeEmitter = process.env.PYTH_TO_WORMHOLE_EMITTER;
const { deployProxy } = require("@openzeppelin/truffle-upgrades");
const tdr = require('truffle-deploy-registry');
console.log("pyth2WormholeEmitter: " + pyth2WormholeEmitter)
console.log("pyth2WormholeChainId: " + pyth2WormholeChainId)
module.exports = async function (deployer, network) {
// Deploy the proxy. This will return an instance of PythUpgradable,
// with the address field corresponding to the fronting ERC1967Proxy.
let proxyInstance = await deployProxy(PythUpgradable,
[
(await WormholeReceiver.deployed()).address,
pyth2WormholeChainId,
pyth2WormholeEmitter
],
{ deployer });
// Add the ERC1967Proxy address to the PythUpgradable contract's
// entry in the registry. This allows us to call upgradeProxy
// functions with the value of PythUpgradable.deployed().address:
// e.g. upgradeProxy(PythUpgradable.deployed().address, NewImplementation)
if (!tdr.isDryRunNetworkName(network)) {
await tdr.appendInstance(proxyInstance);
}
};

View File

@ -23,6 +23,7 @@
"test": "truffle test",
"migrate": "mkdir -p build/contracts && cp node_modules/@openzeppelin/contracts/build/contracts/* build/contracts/ && truffle migrate",
"flatten": "mkdir -p node_modules/@poanet/solidity-flattener/contracts && cp -r contracts/* node_modules/@poanet/solidity-flattener/contracts/ && poa-solidity-flattener",
"receiver-submit-guardian-sets": "truffle exec scripts/receiverSubmitGuardianSetUpgrades.js",
"verify": "patch -u -f node_modules/truffle-plugin-verify/constants.js -i truffle-verify-constants.patch; truffle run verify $npm_config_module@$npm_config_contract_address --network $npm_config_network"
},
"author": "",

View File

@ -0,0 +1,46 @@
// run this script with truffle exec
const jsonfile = require("jsonfile");
const WormholeReceiver = artifacts.require("WormholeReceiver");
const WormholeReceiverImplementationFullABI = jsonfile.readFileSync(
"../build/contracts/wormhole-receiver/ReceiverImplementation.json"
).abi;
const GUARDIAN_SET_UPGRADE_1_VAA =
"010000000001007ac31b282c2aeeeb37f3385ee0de5f8e421d30b9e5ae8ba3d4375c1c77a86e77159bb697d9c456d6f8c02d22a94b1279b65b0d6a9957e7d3857423845ac758e300610ac1d2000000030001000000000000000000000000000000000000000000000000000000000000000400000000000005390000000000000000000000000000000000000000000000000000000000436f7265020000000000011358cc3ae5c097b213ce3c81979e1b9f9570746aa5ff6cb952589bde862c25ef4392132fb9d4a42157114de8460193bdf3a2fcf81f86a09765f4762fd1107a0086b32d7a0977926a205131d8731d39cbeb8c82b2fd82faed2711d59af0f2499d16e726f6b211b39756c042441be6d8650b69b54ebe715e234354ce5b4d348fb74b958e8966e2ec3dbd4958a7cdeb5f7389fa26941519f0863349c223b73a6ddee774a3bf913953d695260d88bc1aa25a4eee363ef0000ac0076727b35fbea2dac28fee5ccb0fea768eaf45ced136b9d9e24903464ae889f5c8a723fc14f93124b7c738843cbb89e864c862c38cddcccf95d2cc37a4dc036a8d232b48f62cdd4731412f4890da798f6896a3331f64b48c12d1d57fd9cbe7081171aa1be1d36cafe3867910f99c09e347899c19c38192b6e7387ccd768277c17dab1b7a5027c0b3cf178e21ad2e77ae06711549cfbb1f9c7a9d8096e85e1487f35515d02a92753504a8d75471b9f49edb6fbebc898f403e4773e95feb15e80c9a99c8348d";
const GUARDIAN_SET_UPGRADE_2_VAA =
"01000000010d0012e6b39c6da90c5dfd3c228edbb78c7a4c97c488ff8a346d161a91db067e51d638c17216f368aa9bdf4836b8645a98018ca67d2fec87d769cabfdf2406bf790a0002ef42b288091a670ef3556596f4f47323717882881eaf38e03345078d07a156f312b785b64dae6e9a87e3d32872f59cb1931f728cecf511762981baf48303668f0103cef2616b84c4e511ff03329e0853f1bd7ee9ac5ba71d70a4d76108bddf94f69c2a8a84e4ee94065e8003c334e899184943634e12043d0dda78d93996da073d190104e76d166b9dac98f602107cc4b44ac82868faf00b63df7d24f177aa391e050902413b71046434e67c770b19aecdf7fce1d1435ea0be7262e3e4c18f50ddc8175c0105d9450e8216d741e0206a50f93b750a47e0a258b80eb8fed1314cc300b3d905092de25cd36d366097b7103ae2d184121329ba3aa2d7c6cc53273f11af14798110010687477c8deec89d36a23e7948feb074df95362fc8dcbd8ae910ac556a1dee1e755c56b9db5d710c940938ed79bc1895a3646523a58bc55f475a23435a373ecfdd0107fb06734864f79def4e192497362513171530daea81f07fbb9f698afe7e66c6d44db21323144f2657d4a5386a954bb94eef9f64148c33aef6e477eafa2c5c984c01088769e82216310d1827d9bd48645ec23e90de4ef8a8de99e2d351d1df318608566248d80cdc83bdcac382b3c30c670352be87f9069aab5037d0b747208eae9c650109e9796497ff9106d0d1c62e184d83716282870cef61a1ee13d6fc485b521adcce255c96f7d1bca8d8e7e7d454b65783a830bddc9d94092091a268d311ecd84c26010c468c9fb6d41026841ff9f8d7368fa309d4dbea3ea4bbd2feccf94a92cc8a20a226338a8e2126cd16f70eaf15b4fc9be2c3fa19def14e071956a605e9d1ac4162010e23fcb6bd445b7c25afb722250c1acbc061ed964ba9de1326609ae012acdfb96942b2a102a2de99ab96327859a34a2b49a767dbdb62e0a1fb26af60fe44fd496a00106bb0bac77ac68b347645f2fb1ad789ea9bd76fb9b2324f25ae06f97e65246f142df717f662e73948317182c62ce87d79c73def0dba12e5242dfc038382812cfe00126da03c5e56cb15aeeceadc1e17a45753ab4dc0ec7bf6a75ca03143ed4a294f6f61bc3f478a457833e43084ecd7c985bf2f55a55f168aac0e030fc49e845e497101626e9d9a5d9e343f00010000000000000000000000000000000000000000000000000000000000000004c1759167c43f501c2000000000000000000000000000000000000000000000000000000000436f7265020000000000021358cc3ae5c097b213ce3c81979e1b9f9570746aa5ff6cb952589bde862c25ef4392132fb9d4a42157114de8460193bdf3a2fcf81f86a09765f4762fd1107a0086b32d7a0977926a205131d8731d39cbeb8c82b2fd82faed2711d59af0f2499d16e726f6b211b39756c042441be6d8650b69b54ebe715e234354ce5b4d348fb74b958e8966e2ec3dbd4958a7cd66b9590e1c41e0b226937bf9217d1d67fd4e91f574a3bf913953d695260d88bc1aa25a4eee363ef0000ac0076727b35fbea2dac28fee5ccb0fea768eaf45ced136b9d9e24903464ae889f5c8a723fc14f93124b7c738843cbb89e864c862c38cddcccf95d2cc37a4dc036a8d232b48f62cdd4731412f4890da798f6896a3331f64b48c12d1d57fd9cbe7081171aa1be1d36cafe3867910f99c09e347899c19c38192b6e7387ccd768277c17dab1b7a5027c0b3cf178e21ad2e77ae06711549cfbb1f9c7a9d8096e85e1487f35515d02a92753504a8d75471b9f49edb6fbebc898f403e4773e95feb15e80c9a99c8348d";
// const TEST_VAA =
// "01000000020d005f7c6d5d57806e39e2b72f1b35e105b560dcbaa53ca159713897f666bbcca9566a3153bec04131423d31b3c612b0036711a8f3e092d382ee33666310ce9c13f00001f2bb445b90ce41374692d79037ae2fc76d45de890328404ccde3137a244774ca23cc0f74a3b4e89739cdc78a21e7605ec7f2e082e849d74ea284729916e430f40102fac6f17962e6225becdc69d4f3dbef29f7eda52cf189c3cbdec4d1fad98ba63e05aa8d446bd348fbf3dfeeb1753f857421f4d9b47f10a5eccb8927a289fa2e200103a5f7768647a609d20aaf90e09370f7261e2055b6eaded0941d8222a01d2618c11ef5912d8c00f571dd63157579a8ab39584186d5c6995d70ca255ee97d3f9b390104d529bd9ae735d480822cc094cbc74fe66010d233d81bf84278f0b439bc98df956361dd85b8a8ccb2f55bf94606ffb1dfc2260499c25c1027f51a5f7e7d4240ba010584ba8ebace2a0f39e4ca01c0d3f5b8686d18652cc0cd0f6516ce20ded88c796e002231f1198b7501839eb7fe442db09745d34d58c8a8f107a34dc50e19312eea0106744ce85d12622933bb7ffececee1d7eb27a1460f8a2062c2b39fe1524baebe9d2c543cdb9a762ef233fb3fe874f810ae0457ed1be3b087096d377feab781c44e010c972bab8988fb8df3864f0946771ad80affe3d46a9fc8a1ac5377cb14137fb9ec4e6f61e0deffe103cb090dde734edd72885c84023b2ed10d81a1edddfb13d9f7000db6bfe9f7a0a0c9088b9fc5ead7520af1e22dc58034e46d6a90e75a3dc4f9eb4026940bc9b0ce421cf1b3ea61f5e1863b7075e0c0baeeb9bc5793173e9777f6ac000e452480aa2500b30bd3dbc87f3f9f78b6b0c221ec0343db3bbc22833798ff1d8a480dbc9ae10960623c29373d0ac48f42ec33de03c935019f5fc73bc02b95b7b2010f252ddc2ffaecf009f77ce9e57844211723f13b2300c5791114a319943b3ed6c23f54f121061347973a0b23a8a40e8351a7ce848bd0d24581a1d763032e70062e001090ee9b3e84ea1eac58c043a683aa3c1e8b47a94bda3c1db4109e8d92e3f0f2f50562ef7cca4a90008970ec1e0975c9cefc5bd506d823c0d5f2bad70fb582bd9f011110515f473681be0b8457a9c930736520c62687c393f9f12ddc5daa858951ffee5b569a863561d85e5d76f3b0d0c7febdba8272563383ef2fb48cd23c2a856287006279c15f708b000000050000000000000000000000005a58505a96d1dbf8df91cb21b54419fc36e93fde000000000000aff20f010000000000000000000000000000000000000000000000000000000a6830e3a20000000000000000000000000d500b1d8e8ef31e21c99d1db9a6444d3adf1270000500000000000000000000000031adfc3e96825ecb4e6a6bc09349b4a1a9080c1300060000000000000000000000000000000000000000000000000000000000000000";
module.exports = async function (callback) {
try {
const accounts = await web3.eth.getAccounts();
const initialized = new web3.eth.Contract(
WormholeReceiverImplementationFullABI,
WormholeReceiver.address
);
// Upgrade set 0 to set 1
await initialized.methods
.submitNewGuardianSet("0x" + GUARDIAN_SET_UPGRADE_1_VAA)
.send({
value: 0,
from: accounts[0],
gasLimit: 2000000,
});
// Upgrade set 1 to set 2
await initialized.methods
.submitNewGuardianSet("0x" + GUARDIAN_SET_UPGRADE_2_VAA)
.send({
value: 0,
from: accounts[0],
gasLimit: 2000000,
});
// console.log(
// await initialized.methods.parseAndVerifyVM("0x" + TEST_VAA).call()
// );
callback();
} catch (e) {
callback(e);
}
};

View File

@ -7,6 +7,7 @@ const PythStructs = artifacts.require("PythStructs");
const { deployProxy, upgradeProxy } = require("@openzeppelin/truffle-upgrades");
const { expectRevert } = require("@openzeppelin/test-helpers");
// Use "WormholeReceiver" if you are testing with Wormhole Receiver
const Wormhole = artifacts.require("Wormhole");
const PythUpgradable = artifacts.require("PythUpgradable");