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:
parent
8667a6894a
commit
040eace4f7
|
@ -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
|
||||
|
||||
|
|
|
@ -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,6 +824,60 @@ 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;
|
||||
|
|
Loading…
Reference in New Issue