Add to ethereum contract integration test coverage (#1053)

* Fix assert typos

* Restructure data field and add comments

* Move magic hex into named variables

* Add README items for running tests

* Add make test to README

* Show work on core string generation

* Add action transfer fee

* Add more VM integration test cases
This commit is contained in:
Jonathan Claudius 2022-04-14 10:11:04 -04:00 committed by GitHub
parent 8667a6894a
commit 040eace4f7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 249 additions and 23 deletions

View File

@ -28,7 +28,11 @@ For each test run:
Run `npx ganache-cli --deterministic --time "1970-01-01T00:00:00+00:00"` to start a chain.
Run the tests using `npm run test`
Run the all ethereum tests using `make test`
Run a specific test file using `npx truffle test test/wormhole.js`
Run a specific test file while skipping compile `npx truffle test test/wormhole.js --compile-none`
### User methods

View File

@ -9,6 +9,15 @@ const Implementation = artifacts.require("Implementation");
const testSigner1PK = "cfb12303a19cde580bb4dd771639b0d26bc68353645571a8cff516ab2ee113a0";
const testSigner2PK = "892330666a850761e7370376430bb8c2aa1494072d3bfeaed0c4fa3d5a9135fe";
const testSigner3PK = "87b45997ea577b93073568f06fc4838cffc1d01f90fc4d57f936957f3c4d99fb";
const testBadSigner1PK = "87b45997ea577b93073568f06fc4838cffc1d01f90fc4d57f936957f3c4d99fc";
const core = '0x' + Buffer.from("Core").toString("hex").padStart(64,0)
const actionContractUpgrade = "01"
const actionGuardianSetUpgrade = "02"
const actionMessageFee = "03"
const actionTransferFee = "04"
const ImplementationFullABI = jsonfile.readFileSync("build/contracts/Implementation.json").abi
@ -167,6 +176,124 @@ contract("Wormhole", function () {
assert.equal(result.reason, "");
})
it("should fail quorum on VMs with no signers", 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,
1337,
data,
[], // no valid signers present
0,
2
);
let result = await initialized.methods.parseAndVerifyVM("0x" + vm).call();
assert.equal(result[1], false)
assert.equal(result[2], "no quorum")
})
it("should fail to verify on VMs with bad signer", 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,
1337,
data,
[
testBadSigner1PK, // not a valid signer
],
0,
2
);
let result = await initialized.methods.parseAndVerifyVM("0x" + vm).call();
assert.equal(result[1], false)
assert.equal(result[2], "VM signature invalid")
})
it("should error on VMs with invalid guardian set index", 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,
1337,
data,
[
testSigner1PK,
],
200,
2
);
let result = await initialized.methods.parseAndVerifyVM("0x" + vm).call();
assert.equal(result[1], false)
assert.equal(result[2], "invalid guardian set")
})
it("should revert on VMs with duplicate non-monotonic signature indexes", 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 signAndEncodeVMFixedIndex(
timestamp,
nonce,
emitterChainId,
emitterAddress,
1337,
data,
[
testSigner1PK,
testSigner1PK,
testSigner1PK,
],
0,
2
);
try {
await initialized.methods.parseAndVerifyVM("0x" + vm).call();
assert.fail("accepted signature indexes being the same in a VM");
} catch (e) {
assert.equal(e.data[Object.keys(e.data)[0]].reason, 'signature indices must be ascending')
}
})
it("should set and enforce fees", async function () {
const initialized = new web3.eth.Contract(ImplementationFullABI, Wormhole.address);
const accounts = await web3.eth.getAccounts();
@ -175,10 +302,15 @@ contract("Wormhole", function () {
const nonce = 1001;
const emitterChainId = testGovernanceChainId;
const emitterAddress = testGovernanceContract
let data = "0x00000000000000000000000000000000000000000000000000000000436f726503";
data += [
data = [
//Core
core,
// Action 3 (Set Message Fee)
actionMessageFee,
// ChainID
web3.eth.abi.encodeParameter("uint16", testChainId).substring(2 + (64 - 4)),
// Message Fee
web3.eth.abi.encodeParameter("uint256", 1111).substring(2),
].join('')
@ -247,11 +379,17 @@ contract("Wormhole", function () {
const nonce = 1001;
const emitterChainId = testGovernanceChainId;
const emitterAddress = testGovernanceContract
let data = "0x00000000000000000000000000000000000000000000000000000000436f726504";
data += [
data = [
// Core
core,
// Action 4 (Transfer Fees)
actionTransferFee,
// ChainID
web3.eth.abi.encodeParameter("uint16", testChainId).substring(2 + (64 - 4)),
// Amount
web3.eth.abi.encodeParameter("uint256", 11).substring(2),
// Recipient
web3.eth.abi.encodeParameter("address", receiver).substring(2),
].join('')
@ -293,11 +431,14 @@ contract("Wormhole", function () {
const nonce = 1001;
const emitterChainId = testGovernanceChainId;
const emitterAddress = testGovernanceContract
let data = "0x00000000000000000000000000000000000000000000000000000000436f726502";
let oldIndex = Number(await initialized.methods.getCurrentGuardianSetIndex().call());
data += [
data = [
// Core
core,
// Action 2 (Guardian Set Upgrade)
actionGuardianSetUpgrade,
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)),
@ -362,10 +503,15 @@ contract("Wormhole", function () {
const nonce = 1001;
const emitterChainId = testGovernanceChainId;
const emitterAddress = testGovernanceContract
let data = "0x00000000000000000000000000000000000000000000000000000000436f726501";
data += [
data = [
// Core
core,
// Action 1 (Contract Upgrade)
actionContractUpgrade,
// ChainID
web3.eth.abi.encodeParameter("uint16", testChainId).substring(2 + (64 - 4)),
// New Contract Address
web3.eth.abi.encodeParameter("address", mock.address).substring(2),
].join('')
@ -410,10 +556,16 @@ contract("Wormhole", function () {
const initialized = new web3.eth.Contract(ImplementationFullABI, Wormhole.address);
const accounts = await web3.eth.getAccounts();
let data = "0x00000000000000000000000000000000000000000000000000000000436f726504";
data += [
data = [
// Core
core,
// Action 4 (Transfer Fee)
actionTransferFee,
// ChainID
web3.eth.abi.encodeParameter("uint16", testChainId).substring(2 + (64 - 4)),
// Amount
web3.eth.abi.encodeParameter("uint256", 1).substring(2),
// Recipient
web3.eth.abi.encodeParameter("address", "0x0000000000000000000000000000000000000000").substring(2),
].join('')
@ -438,7 +590,7 @@ contract("Wormhole", function () {
from: accounts[0],
gasLimit: 1000000
});
asset.fail("governance packet of old guardian set accepted")
assert.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")
}
@ -483,10 +635,16 @@ contract("Wormhole", function () {
const initialized = new web3.eth.Contract(ImplementationFullABI, Wormhole.address);
const accounts = await web3.eth.getAccounts();
let data = "0x00000000000000000000000000000000000000000000000000000000436f726504";
data += [
data = [
// Core
core,
// Action 4 (set fees)
actionTransferFee,
// ChainID
web3.eth.abi.encodeParameter("uint16", testChainId).substring(2 + (64 - 4)),
// Amount
web3.eth.abi.encodeParameter("uint256", 1).substring(2),
// Recipient
web3.eth.abi.encodeParameter("address", "0x0000000000000000000000000000000000000000").substring(2),
].join('')
@ -512,7 +670,7 @@ contract("Wormhole", function () {
from: accounts[0],
gasLimit: 1000000
});
asset.fail("governance packet from wrong governance chain accepted")
assert.fail("governance packet from wrong governance chain accepted")
} catch (e) {
assert.equal(e.data[Object.keys(e.data)[0]].reason, "wrong governance chain")
}
@ -522,8 +680,11 @@ contract("Wormhole", function () {
const initialized = new web3.eth.Contract(ImplementationFullABI, Wormhole.address);
const accounts = await web3.eth.getAccounts();
let data = "0x00000000000000000000000000000000000000000000000000000000436f726504";
data += [
data = [
// Core
core,
// Action 4 (Transfer Fee)
actionTransferFee,
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),
@ -533,7 +694,7 @@ contract("Wormhole", function () {
0,
0,
testGovernanceChainId,
"0x00000000000000000000000000000000000000000000000000000000436f7265",
core,
0,
data,
[
@ -551,7 +712,7 @@ contract("Wormhole", function () {
from: accounts[0],
gasLimit: 1000000
});
asset.fail("governance packet from wrong governance contract accepted")
assert.fail("governance packet from wrong governance contract accepted")
} catch (e) {
assert.equal(e.data[Object.keys(e.data)[0]].reason, "wrong governance contract")
}
@ -561,10 +722,16 @@ contract("Wormhole", function () {
const initialized = new web3.eth.Contract(ImplementationFullABI, Wormhole.address);
const accounts = await web3.eth.getAccounts();
let data = "0x00000000000000000000000000000000000000000000000000000000436f726504";
data += [
data = [
// Core
core,
// Action 4 (Transfer Fee)
actionTransferFee,
// ChainID
web3.eth.abi.encodeParameter("uint16", testChainId).substring(2 + (64 - 4)),
// Amount
web3.eth.abi.encodeParameter("uint256", 1).substring(2),
// Recipient
web3.eth.abi.encodeParameter("address", "0x0000000000000000000000000000000000000000").substring(2),
].join('')
@ -597,11 +764,12 @@ contract("Wormhole", function () {
gasLimit: 1000000
});
asset.fail("governance packet accepted twice")
assert.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 (
@ -656,9 +824,63 @@ const signAndEncodeVM = async function (
return vm
}
const signAndEncodeVMFixedIndex = async function (
timestamp,
nonce,
emitterChainId,
emitterAddress,
sequence,
data,
signers,
guardianSetIndex,
consistencyLevel
) {
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("uint64", sequence).substring(2 + (64 - 16)),
web3.eth.abi.encodeParameter("uint8", consistencyLevel).substring(2 + (64 - 2)),
data.substr(2)
]
const hash = web3.utils.soliditySha3(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 = [
// Fixing the index to be zero to product a non-monotonic VM
web3.eth.abi.encodeParameter("uint8", 0).substring(2 + (64 - 2)),
zeroPadBytes(signature.r.toString(16), 32),
zeroPadBytes(signature.s.toString(16), 32),
web3.eth.abi.encodeParameter("uint8", signature.recoveryParam).substr(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;
}
}