| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654 |
- const jsonfile = require('jsonfile');
- const elliptic = require('elliptic');
- const path = require('path');
- const Wormhole = artifacts.require("Wormhole");
- const MockImplementation = artifacts.require("MockImplementation");
- const Implementation = artifacts.require("Implementation");
- const testSigner1PK = "13b422ac887f1912629e34928674cb4a81e59d96a4d74653e41c2a305ba754a5";
- const testSigner2PK = "892330666a850761e7370376430bb8c2aa1494072d3bfeaed0c4fa3d5a9135fe";
- const testSigner3PK = "87b45997ea577b93073568f06fc4838cffc1d01f90fc4d57f936957f3c4d99fb";
- const ImplementationFullABI = jsonfile.readFileSync("build/contracts/Implementation.json").abi
- // Taken from https://medium.com/fluidity/standing-the-time-of-test-b906fcc374a9
- advanceTimeAndBlock = async (time) => {
- await advanceTime(time);
- await advanceBlock();
- return Promise.resolve(web3.eth.getBlock('latest'));
- }
- advanceTime = (time) => {
- return new Promise((resolve, reject) => {
- web3.currentProvider.send({
- jsonrpc: "2.0",
- method: "evm_increaseTime",
- params: [time],
- id: new Date().getTime()
- }, (err, result) => {
- if (err) {
- return reject(err);
- }
- return resolve(result);
- });
- });
- }
- advanceBlock = () => {
- return new Promise((resolve, reject) => {
- web3.currentProvider.send({
- jsonrpc: "2.0",
- method: "evm_mine",
- id: new Date().getTime()
- }, (err, result) => {
- if (err) {
- return reject(err);
- }
- const newBlockHash = web3.eth.getBlock('latest').hash;
- return resolve(newBlockHash)
- });
- });
- }
- contract("Wormhole", function () {
- const testSigner1 = web3.eth.accounts.privateKeyToAccount(testSigner1PK);
- const testSigner2 = web3.eth.accounts.privateKeyToAccount(testSigner2PK);
- const testSigner3 = web3.eth.accounts.privateKeyToAccount(testSigner3PK);
- const testChainId = "2";
- const testGovernanceChainId = "3";
- const testGovernanceContract = "0x0000000000000000000000000000000000000000000000000000000000000004";
- it("should be initialized with the correct signers and values", async function(){
- const initialized = new web3.eth.Contract(ImplementationFullABI, Wormhole.address);
- const index = await initialized.methods.getCurrentGuardianSetIndex().call();
- const set = (await initialized.methods.getGuardianSet(index).call());
- // check set
- assert.lengthOf(set, 2);
- assert.equal(set[0][0], testSigner1.address);
- assert.equal(set[0][1], testSigner2.address);
- // check expiration
- assert.equal(set.expirationTime, "0");
- // chain id
- const chainId = await initialized.methods.chainId().call();
- assert.equal(chainId, testChainId);
- // governance
- const governanceChainId = await initialized.methods.governanceChainId().call();
- assert.equal(governanceChainId, testGovernanceChainId);
- const governanceContract = await initialized.methods.governanceContract().call();
- assert.equal(governanceContract, testGovernanceContract);
- })
- it("initialize should be non-reentrant", async function(){
- const initialized = new web3.eth.Contract(ImplementationFullABI, Wormhole.address);
- try{
- await initialized.methods.initialize([
- testSigner1.address
- ], testChainId, testGovernanceChainId, testGovernanceContract).estimateGas();
- } catch (error) {
- assert.equal(error.message, "Returned error: VM Exception while processing transaction: revert already initialized")
- return
- }
- assert.fail("did not fail")
- })
- it("should log a published message correctly", async function(){
- const initialized = new web3.eth.Contract(ImplementationFullABI, Wormhole.address);
- const accounts = await web3.eth.getAccounts();
- const log = await initialized.methods.publishMessage(
- "0x123",
- "0x123321",
- false
- ).send({
- value : 0, // fees are set to 0 initially
- from : accounts[0]
- })
- assert.equal(log.events.LogMessagePublished.returnValues.sender.toString(), accounts[0]);
- assert.equal(log.events.LogMessagePublished.returnValues.nonce, 291);
- assert.equal(log.events.LogMessagePublished.returnValues.payload.toString(), "0x123321");
- })
- it("parses VMs correctly", async function(){
- const initialized = new web3.eth.Contract(ImplementationFullABI, Wormhole.address);
- const timestamp = 1000;
- const nonce = 1001;
- const emitterChainId = 11;
- const emitterAddress = "0x0000000000000000000000000000000000000000000000000000000000000eee"
- const data = "0xaaaaaa";
- const vm = await signAndEncodeVM(
- timestamp,
- nonce,
- emitterChainId,
- emitterAddress,
- data,
- [
- testSigner1PK,
- testSigner2PK
- ],
- 0
- );
- let result
- try {
- result = await initialized.methods.parseAndVerifyVM("0x" + vm).call();
- } catch(err) {
- console.log(err)
- assert.fail("parseAndVerifyVM failed")
- }
- assert.equal(result.vm.version, 1);
- assert.equal(result.vm.timestamp, timestamp);
- assert.equal(result.vm.nonce, nonce);
- assert.equal(result.vm.emitterChainId, emitterChainId);
- assert.equal(result.vm.emitterAddress, emitterAddress);
- assert.equal(result.vm.payload, data);
- assert.equal(result.vm.guardianSetIndex, 0);
- assert.equal(result.valid, true);
- assert.equal(result.reason, "");
- })
- it("should set and enforce fees", async function(){
- const initialized = new web3.eth.Contract(ImplementationFullABI, Wormhole.address);
- const accounts = await web3.eth.getAccounts();
- const timestamp = 1000;
- const nonce = 1001;
- const emitterChainId = testGovernanceChainId;
- const emitterAddress = testGovernanceContract
- let data = "0x00000000000000000000000000000000000000000000000000000000436f726503";
- data += [
- web3.eth.abi.encodeParameter("uint16", testChainId).substring(2 + (64 - 4)),
- web3.eth.abi.encodeParameter("uint256", 1111).substring(2),
- web3.eth.abi.encodeParameter("uint256", 2222).substring(2),
- ].join('')
- const vm = await signAndEncodeVM(
- timestamp,
- nonce,
- emitterChainId,
- emitterAddress,
- data,
- [
- testSigner1PK,
- testSigner2PK
- ],
- 0
- );
- let before = await initialized.methods.messageFee().call();
- let set = await initialized.methods.submitSetMessageFee("0x" + vm).send({
- value : 0,
- from : accounts[0],
- gasLimit : 1000000
- });
- let after = await initialized.methods.messageFee().call();
- assert.notEqual(before, after);
- assert.equal(after, 1111);
- // test non-persisted message
- await initialized.methods.publishMessage(
- "0x123",
- "0x123321",
- false
- ).send({
- from : accounts[0],
- value : 1111
- })
- // test persisted message
- await initialized.methods.publishMessage(
- "0x123",
- "0x123321",
- true
- ).send({
- from : accounts[0],
- value : 2222
- })
- let failed = false;
- try {
- await initialized.methods.publishMessage(
- "0x123",
- "0x123321",
- false
- ).send({
- value : 1110,
- from : accounts[0]
- })
- } catch(e) {
- failed = true
- }
- assert.equal(failed, true);
- })
- it("should transfer out collected fees", async function(){
- const initialized = new web3.eth.Contract(ImplementationFullABI, Wormhole.address);
- const accounts = await web3.eth.getAccounts();
- const receiver = "0x" + zeroPadBytes( Math.floor(Math.random() * Number.MAX_SAFE_INTEGER).toString(16), 20);
- const timestamp = 1000;
- const nonce = 1001;
- const emitterChainId = testGovernanceChainId;
- const emitterAddress = testGovernanceContract
- let data = "0x00000000000000000000000000000000000000000000000000000000436f726504";
- data += [
- web3.eth.abi.encodeParameter("uint16", testChainId).substring(2 + (64 - 4)),
- web3.eth.abi.encodeParameter("uint256", 11).substring(2),
- web3.eth.abi.encodeParameter("address", receiver).substring(2),
- ].join('')
- const vm = await signAndEncodeVM(
- timestamp,
- nonce,
- emitterChainId,
- emitterAddress,
- data,
- [
- testSigner1PK,
- testSigner2PK
- ],
- 0
- );
- let WHBefore = await web3.eth.getBalance(Wormhole.address);
- let receiverBefore = await web3.eth.getBalance(receiver);
- let set = await initialized.methods.submitTransferFees("0x" + vm).send({
- value : 0,
- from : accounts[0],
- gasLimit : 1000000
- });
- let WHAfter = await web3.eth.getBalance(Wormhole.address);
- let receiverAfter = await web3.eth.getBalance(receiver);
- assert.equal(WHBefore - WHAfter, 11);
- assert.equal(receiverAfter - receiverBefore, 11);
- })
- it("should accept a new guardian set", async function(){
- const initialized = new web3.eth.Contract(ImplementationFullABI, Wormhole.address);
- const accounts = await web3.eth.getAccounts();
- const timestamp = 1000;
- const nonce = 1001;
- const emitterChainId = testGovernanceChainId;
- const emitterAddress = testGovernanceContract
- let data = "0x00000000000000000000000000000000000000000000000000000000436f726502";
- let oldIndex = Number(await initialized.methods.getCurrentGuardianSetIndex().call());
- data += [
- web3.eth.abi.encodeParameter("uint16", testChainId).substring(2 + (64 - 4)),
- web3.eth.abi.encodeParameter("uint32", oldIndex + 1).substring(2 + (64 - 8)),
- web3.eth.abi.encodeParameter("uint8", 3).substring(2 + (64 - 2)),
- web3.eth.abi.encodeParameter("address", testSigner1.address).substring(2 + (64 - 40)),
- web3.eth.abi.encodeParameter("address", testSigner2.address).substring(2 + (64 - 40)),
- web3.eth.abi.encodeParameter("address", testSigner3.address).substring(2 + (64 - 40)),
- ].join('')
- const vm = await signAndEncodeVM(
- timestamp,
- nonce,
- emitterChainId,
- emitterAddress,
- data,
- [
- testSigner1PK,
- testSigner2PK
- ],
- 0
- );
- let set = await initialized.methods.submitNewGuardianSet("0x" + vm).send({
- value : 0,
- from : accounts[0],
- gasLimit : 1000000
- });
- let index = await initialized.methods.getCurrentGuardianSetIndex().call();
- assert.equal(oldIndex + 1, index);
- assert.equal(index, 1);
- let guardians = await initialized.methods.getGuardianSet(index).call();
- assert.equal(guardians.expirationTime, 0);
- assert.lengthOf(guardians[0], 3);
- assert.equal(guardians[0][0], testSigner1.address);
- assert.equal(guardians[0][1], testSigner2.address);
- assert.equal(guardians[0][2], testSigner3.address);
- let oldGuardians = await initialized.methods.getGuardianSet(oldIndex).call();
- const time = (await web3.eth.getBlock("latest")).timestamp;
- // old guardian set expiry is set
- assert.ok(
- oldGuardians.expirationTime > Number(time) + 86000
- && oldGuardians.expirationTime < Number(time) + 88000
- );
- })
- it("should accept smart contract upgrades", async function(){
- const initialized = new web3.eth.Contract(ImplementationFullABI, Wormhole.address);
- const accounts = await web3.eth.getAccounts();
- const mock = await MockImplementation.new();
- const timestamp = 1000;
- const nonce = 1001;
- const emitterChainId = testGovernanceChainId;
- const emitterAddress = testGovernanceContract
- let data = "0x00000000000000000000000000000000000000000000000000000000436f726501";
- data += [
- web3.eth.abi.encodeParameter("uint16", testChainId).substring(2 + (64 - 4)),
- web3.eth.abi.encodeParameter("address", mock.address).substring(2),
- ].join('')
- const vm = await signAndEncodeVM(
- timestamp,
- nonce,
- emitterChainId,
- emitterAddress,
- data,
- [
- testSigner1PK,
- testSigner2PK,
- testSigner3PK
- ],
- 1
- );
- let before = await web3.eth.getStorageAt(Wormhole.address, "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc");
- assert.equal(before.toLowerCase(), Implementation.address.toLowerCase());
- let set = await initialized.methods.submitContractUpgrade("0x" + vm).send({
- value : 0,
- from : accounts[0],
- gasLimit: 1000000
- });
- let after = await web3.eth.getStorageAt(Wormhole.address, "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc");
- assert.equal(after.toLowerCase(), mock.address.toLowerCase());
- const mockImpl = new web3.eth.Contract(MockImplementation.abi, Wormhole.address);
- let isUpgraded = await mockImpl.methods.testNewImplementationActive().call();
- assert.ok(isUpgraded);
- })
- it("should revert governance packets from old guardian set", async function(){
- const initialized = new web3.eth.Contract(ImplementationFullABI, Wormhole.address);
- const accounts = await web3.eth.getAccounts();
- let data = "0x00000000000000000000000000000000000000000000000000000000436f726504";
- data += [
- web3.eth.abi.encodeParameter("uint16", testChainId).substring(2 + (64 - 4)),
- web3.eth.abi.encodeParameter("uint256", 1).substring(2),
- web3.eth.abi.encodeParameter("address", "0x0000000000000000000000000000000000000000").substring(2),
- ].join('')
- const vm = await signAndEncodeVM(
- 0,
- 0,
- testGovernanceChainId,
- testGovernanceContract,
- data,
- [
- testSigner1PK,
- testSigner2PK
- ],
- 0
- );
- let failed = false;
- try {
- await initialized.methods.submitTransferFees("0x" + vm).send({
- value : 0,
- from : accounts[0],
- gasLimit : 1000000
- });
- asset.fail("governance packet of old guardian set accepted")
- } catch(e) {
- assert.equal(e.data[Object.keys(e.data)[0]].reason, "not signed by current guardian set")
- }
- })
- it("should time out old gardians", async function(){
- const initialized = new web3.eth.Contract(ImplementationFullABI, Wormhole.address);
- const timestamp = 1000;
- const nonce = 1001;
- const emitterChainId = 11;
- const emitterAddress = "0x0000000000000000000000000000000000000000000000000000000000000eee"
- const data = "0xaaaaaa";
- const vm = await signAndEncodeVM(
- timestamp,
- nonce,
- emitterChainId,
- emitterAddress,
- data,
- [
- testSigner1PK,
- testSigner2PK
- ],
- 0
- );
- // this should pass
- const current = await initialized.methods.parseAndVerifyVM("0x" + vm).call();
- assert.equal(current.valid, true)
- await advanceTimeAndBlock(100000);
- const expired = await initialized.methods.parseAndVerifyVM("0x" + vm).call();
- assert.equal(expired.valid, false)
- })
- it("should revert governance packets from wrong governance chain", async function(){
- const initialized = new web3.eth.Contract(ImplementationFullABI, Wormhole.address);
- const accounts = await web3.eth.getAccounts();
- let data = "0x00000000000000000000000000000000000000000000000000000000436f726504";
- data += [
- web3.eth.abi.encodeParameter("uint16", testChainId).substring(2 + (64 - 4)),
- web3.eth.abi.encodeParameter("uint256", 1).substring(2),
- web3.eth.abi.encodeParameter("address", "0x0000000000000000000000000000000000000000").substring(2),
- ].join('')
- const vm = await signAndEncodeVM(
- 0,
- 0,
- 999,
- testGovernanceContract,
- data,
- [
- testSigner1PK,
- testSigner2PK,
- testSigner3PK,
- ],
- 1
- );
- try {
- await initialized.methods.submitTransferFees("0x" + vm).send({
- value : 0,
- from : accounts[0],
- gasLimit : 1000000
- });
- asset.fail("governance packet from wrong governance chain accepted")
- } catch(e) {
- assert.equal(e.data[Object.keys(e.data)[0]].reason, "wrong governance chain")
- }
- })
- it("should revert governance packets from wrong governance contract", async function(){
- const initialized = new web3.eth.Contract(ImplementationFullABI, Wormhole.address);
- const accounts = await web3.eth.getAccounts();
- let data = "0x00000000000000000000000000000000000000000000000000000000436f726504";
- data += [
- web3.eth.abi.encodeParameter("uint16", testChainId).substring(2 + (64 - 4)),
- web3.eth.abi.encodeParameter("uint256", 1).substring(2),
- web3.eth.abi.encodeParameter("address", "0x0000000000000000000000000000000000000000").substring(2),
- ].join('')
- const vm = await signAndEncodeVM(
- 0,
- 0,
- testGovernanceChainId,
- "0x00000000000000000000000000000000000000000000000000000000436f7265",
- data,
- [
- testSigner1PK,
- testSigner2PK,
- testSigner3PK,
- ],
- 1
- );
- try {
- await initialized.methods.submitTransferFees("0x" + vm).send({
- value : 0,
- from : accounts[0],
- gasLimit : 1000000
- });
- asset.fail("governance packet from wrong governance contract accepted")
- } catch(e) {
- assert.equal(e.data[Object.keys(e.data)[0]].reason, "wrong governance contract")
- }
- })
- it("should revert on governance packets that already have been applied", async function(){
- const initialized = new web3.eth.Contract(ImplementationFullABI, Wormhole.address);
- const accounts = await web3.eth.getAccounts();
- let data = "0x00000000000000000000000000000000000000000000000000000000436f726504";
- data += [
- web3.eth.abi.encodeParameter("uint16", testChainId).substring(2 + (64 - 4)),
- web3.eth.abi.encodeParameter("uint256", 1).substring(2),
- web3.eth.abi.encodeParameter("address", "0x0000000000000000000000000000000000000000").substring(2),
- ].join('')
- const vm = await signAndEncodeVM(
- 0,
- 0,
- testGovernanceChainId,
- testGovernanceContract,
- data,
- [
- testSigner1PK,
- testSigner2PK,
- testSigner3PK,
- ],
- 1
- );
- await initialized.methods.submitTransferFees("0x" + vm).send({
- value : 0,
- from : accounts[0],
- gasLimit : 1000000
- });
- try {
- await initialized.methods.submitTransferFees("0x" + vm).send({
- value : 0,
- from : accounts[0],
- gasLimit : 1000000
- });
- asset.fail("governance packet accepted twice")
- } catch(e) {
- assert.equal(e.data[Object.keys(e.data)[0]].reason, "governance action already consumed")
- }
- })
- });
- const signAndEncodeVM = async function(
- timestamp,
- nonce,
- emitterChainId,
- emitterAddress,
- data,
- signers,
- guardianSetIndex
- ){
- const body = [
- web3.eth.abi.encodeParameter("uint32", timestamp).substring(2 + (64 - 8)),
- web3.eth.abi.encodeParameter("uint32", nonce).substring(2 + (64 - 8)),
- web3.eth.abi.encodeParameter("uint16", emitterChainId).substring(2 + (64 - 4)),
- web3.eth.abi.encodeParameter("bytes32", emitterAddress).substring(2),
- web3.eth.abi.encodeParameter("uint8", data.length / 2 - 1).substring(2 + (64 - 2)),
- data.substr(2)
- ]
- const hash = web3.utils.soliditySha3("0x"+body.join(""))
- let signatures = "";
- for(let i in signers){
- const ec = new elliptic.ec("secp256k1");
- const key = ec.keyFromPrivate(signers[i]);
- const signature = key.sign(hash.substr(2), { canonical: true });
- const packSig = [
- zeroPadBytes(signature.r.toString(16), 32),
- zeroPadBytes(signature.s.toString(16), 32),
- web3.eth.abi.encodeParameter("uint8", signature.recoveryParam).substr(2 + (64 - 2)),
- web3.eth.abi.encodeParameter("uint8", i).substring(2 + (64 - 2))
- ]
- signatures += packSig.join("")
- }
- const vm = [
- web3.eth.abi.encodeParameter("uint8", 1).substring(2 + (64 - 2)),
- web3.eth.abi.encodeParameter("uint32", guardianSetIndex).substring(2 + (64 - 8)),
- web3.eth.abi.encodeParameter("uint8", signers.length).substring(2 + (64 - 2)),
- signatures,
- body.join("")
- ].join("");
- return vm
- }
- function zeroPadBytes(value, length) {
- while (value.length < 2 * length) {
- value = "0" + value;
- }
- return value;
- }
|