wormhole.js 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942
  1. const jsonfile = require('jsonfile');
  2. const elliptic = require('elliptic');
  3. const path = require('path');
  4. const Wormhole = artifacts.require("Wormhole");
  5. const MockImplementation = artifacts.require("MockImplementation");
  6. const Implementation = artifacts.require("Implementation");
  7. const MockBatchedVAASender = artifacts.require("MockBatchedVAASender");
  8. const testSigner1PK = "cfb12303a19cde580bb4dd771639b0d26bc68353645571a8cff516ab2ee113a0";
  9. const testSigner2PK = "892330666a850761e7370376430bb8c2aa1494072d3bfeaed0c4fa3d5a9135fe";
  10. const testSigner3PK = "87b45997ea577b93073568f06fc4838cffc1d01f90fc4d57f936957f3c4d99fb";
  11. const testBadSigner1PK = "87b45997ea577b93073568f06fc4838cffc1d01f90fc4d57f936957f3c4d99fc";
  12. const core = '0x' + Buffer.from("Core").toString("hex").padStart(64,0)
  13. const actionContractUpgrade = "01"
  14. const actionGuardianSetUpgrade = "02"
  15. const actionMessageFee = "03"
  16. const actionTransferFee = "04"
  17. const ImplementationFullABI = jsonfile.readFileSync("build/contracts/Implementation.json").abi
  18. // Taken from https://medium.com/fluidity/standing-the-time-of-test-b906fcc374a9
  19. advanceTimeAndBlock = async (time) => {
  20. await advanceTime(time);
  21. await advanceBlock();
  22. return Promise.resolve(web3.eth.getBlock('latest'));
  23. }
  24. advanceTime = (time) => {
  25. return new Promise((resolve, reject) => {
  26. web3.currentProvider.send({
  27. jsonrpc: "2.0",
  28. method: "evm_increaseTime",
  29. params: [time],
  30. id: new Date().getTime()
  31. }, (err, result) => {
  32. if (err) {
  33. return reject(err);
  34. }
  35. return resolve(result);
  36. });
  37. });
  38. }
  39. advanceBlock = () => {
  40. return new Promise((resolve, reject) => {
  41. web3.currentProvider.send({
  42. jsonrpc: "2.0",
  43. method: "evm_mine",
  44. id: new Date().getTime()
  45. }, (err, result) => {
  46. if (err) {
  47. return reject(err);
  48. }
  49. const newBlockHash = web3.eth.getBlock('latest').hash;
  50. return resolve(newBlockHash)
  51. });
  52. });
  53. }
  54. contract("Wormhole", function () {
  55. const testSigner1 = web3.eth.accounts.privateKeyToAccount(testSigner1PK);
  56. const testSigner2 = web3.eth.accounts.privateKeyToAccount(testSigner2PK);
  57. const testSigner3 = web3.eth.accounts.privateKeyToAccount(testSigner3PK);
  58. const testChainId = "2";
  59. const testGovernanceChainId = "1";
  60. const testGovernanceContract = "0x0000000000000000000000000000000000000000000000000000000000000004";
  61. it("should be initialized with the correct signers and values", async function () {
  62. const initialized = new web3.eth.Contract(ImplementationFullABI, Wormhole.address);
  63. const index = await initialized.methods.getCurrentGuardianSetIndex().call();
  64. const set = (await initialized.methods.getGuardianSet(index).call());
  65. // check set
  66. assert.lengthOf(set[0], 1);
  67. assert.equal(set[0][0], testSigner1.address);
  68. // check expiration
  69. assert.equal(set.expirationTime, "0");
  70. // chain id
  71. const chainId = await initialized.methods.chainId().call();
  72. assert.equal(chainId, testChainId);
  73. // governance
  74. const governanceChainId = await initialized.methods.governanceChainId().call();
  75. assert.equal(governanceChainId, testGovernanceChainId);
  76. const governanceContract = await initialized.methods.governanceContract().call();
  77. assert.equal(governanceContract, testGovernanceContract);
  78. })
  79. it("should log a published message correctly", async function () {
  80. const initialized = new web3.eth.Contract(ImplementationFullABI, Wormhole.address);
  81. const accounts = await web3.eth.getAccounts();
  82. const log = await initialized.methods.publishMessage(
  83. "0x123",
  84. "0x123321",
  85. 32
  86. ).send({
  87. value: 0, // fees are set to 0 initially
  88. from: accounts[0]
  89. })
  90. assert.equal(log.events.LogMessagePublished.returnValues.sender.toString(), accounts[0]);
  91. assert.equal(log.events.LogMessagePublished.returnValues.sequence.toString(), "0");
  92. assert.equal(log.events.LogMessagePublished.returnValues.nonce, 291);
  93. assert.equal(log.events.LogMessagePublished.returnValues.payload.toString(), "0x123321");
  94. assert.equal(log.events.LogMessagePublished.returnValues.consistencyLevel, 32);
  95. })
  96. it("should log sequential sequence numbers for multi-VAA transactions", async function () {
  97. const initialized = new web3.eth.Contract(ImplementationFullABI, Wormhole.address);
  98. const accounts = await web3.eth.getAccounts();
  99. const mockIntegration = new web3.eth.Contract(MockBatchedVAASender.abi, MockBatchedVAASender.address);
  100. await mockIntegration.methods.sendMultipleMessages(
  101. "0x1",
  102. "0x1",
  103. 32
  104. ).send({
  105. value: 0, // fees are set to 0 initially
  106. from: accounts[0]
  107. });
  108. const events = (await initialized.getPastEvents('LogMessagePublished', {
  109. fromBlock: 'latest'
  110. }))
  111. let firstSequence = Number(events[0].returnValues.sequence.toString())
  112. let secondSequence = Number(events[1].returnValues.sequence.toString())
  113. assert.equal(secondSequence, firstSequence + 1);
  114. let thirdSequence = Number(events[2].returnValues.sequence.toString())
  115. assert.equal(thirdSequence, secondSequence + 1);
  116. })
  117. it("should increase the sequence for an account", async function () {
  118. const initialized = new web3.eth.Contract(ImplementationFullABI, Wormhole.address);
  119. const accounts = await web3.eth.getAccounts();
  120. const log = await initialized.methods.publishMessage(
  121. "0x1",
  122. "0x1",
  123. 32
  124. ).send({
  125. value: 0, // fees are set to 0 initially
  126. from: accounts[0]
  127. })
  128. assert.equal(log.events.LogMessagePublished.returnValues.sequence.toString(), "1");
  129. })
  130. it("should get the same nonce from all VAAs produced by a transaction", async function () {
  131. const initialized = new web3.eth.Contract(ImplementationFullABI, Wormhole.address);
  132. const accounts = await web3.eth.getAccounts();
  133. const mockIntegration = new web3.eth.Contract(MockBatchedVAASender.abi, MockBatchedVAASender.address);
  134. const nonce = Math.round(Date.now() / 1000);
  135. const nonceHex = nonce.toString(16)
  136. await mockIntegration.methods.sendMultipleMessages(
  137. "0x" + nonceHex,
  138. "0x1",
  139. 32
  140. ).send({
  141. value: 0, // fees are set to 0 initially
  142. from: accounts[0]
  143. });
  144. const events = (await initialized.getPastEvents('LogMessagePublished', {
  145. fromBlock: 'latest'
  146. }))
  147. assert.equal(events[0].returnValues.nonce, nonce);
  148. assert.equal(events[1].returnValues.nonce, nonce);
  149. assert.equal(events[2].returnValues.nonce, nonce);
  150. })
  151. it("parses VMs correctly", async function () {
  152. const initialized = new web3.eth.Contract(ImplementationFullABI, Wormhole.address);
  153. const timestamp = 1000;
  154. const nonce = 1001;
  155. const emitterChainId = 11;
  156. const emitterAddress = "0x0000000000000000000000000000000000000000000000000000000000000eee"
  157. const data = "0xaaaaaa";
  158. const vm = await signAndEncodeVM(
  159. timestamp,
  160. nonce,
  161. emitterChainId,
  162. emitterAddress,
  163. 1337,
  164. data,
  165. [
  166. testSigner1PK,
  167. ],
  168. 0,
  169. 2
  170. );
  171. let result
  172. try {
  173. result = await initialized.methods.parseAndVerifyVM("0x" + vm).call();
  174. } catch (err) {
  175. console.log(err)
  176. assert.fail("parseAndVerifyVM failed")
  177. }
  178. assert.equal(result.vm.version, 1);
  179. assert.equal(result.vm.timestamp, timestamp);
  180. assert.equal(result.vm.nonce, nonce);
  181. assert.equal(result.vm.emitterChainId, emitterChainId);
  182. assert.equal(result.vm.emitterAddress, emitterAddress);
  183. assert.equal(result.vm.payload, data);
  184. assert.equal(result.vm.guardianSetIndex, 0);
  185. assert.equal(result.vm.sequence, 1337);
  186. assert.equal(result.vm.consistencyLevel, 2);
  187. assert.equal(result.valid, true);
  188. assert.equal(result.reason, "");
  189. })
  190. it("should fail quorum on VMs with no signers", async function () {
  191. const initialized = new web3.eth.Contract(ImplementationFullABI, Wormhole.address);
  192. const timestamp = 1000;
  193. const nonce = 1001;
  194. const emitterChainId = 11;
  195. const emitterAddress = "0x0000000000000000000000000000000000000000000000000000000000000eee"
  196. const data = "0xaaaaaa";
  197. const vm = await signAndEncodeVM(
  198. timestamp,
  199. nonce,
  200. emitterChainId,
  201. emitterAddress,
  202. 1337,
  203. data,
  204. [], // no valid signers present
  205. 0,
  206. 2
  207. );
  208. let result = await initialized.methods.parseAndVerifyVM("0x" + vm).call();
  209. assert.equal(result[1], false)
  210. assert.equal(result[2], "no quorum")
  211. })
  212. it("should fail to verify on VMs with bad signer", async function () {
  213. const initialized = new web3.eth.Contract(ImplementationFullABI, Wormhole.address);
  214. const timestamp = 1000;
  215. const nonce = 1001;
  216. const emitterChainId = 11;
  217. const emitterAddress = "0x0000000000000000000000000000000000000000000000000000000000000eee"
  218. const data = "0xaaaaaa";
  219. const vm = await signAndEncodeVM(
  220. timestamp,
  221. nonce,
  222. emitterChainId,
  223. emitterAddress,
  224. 1337,
  225. data,
  226. [
  227. testBadSigner1PK, // not a valid signer
  228. ],
  229. 0,
  230. 2
  231. );
  232. let result = await initialized.methods.parseAndVerifyVM("0x" + vm).call();
  233. assert.equal(result[1], false)
  234. assert.equal(result[2], "VM signature invalid")
  235. })
  236. it("should error on VMs with invalid guardian set index", async function () {
  237. const initialized = new web3.eth.Contract(ImplementationFullABI, Wormhole.address);
  238. const timestamp = 1000;
  239. const nonce = 1001;
  240. const emitterChainId = 11;
  241. const emitterAddress = "0x0000000000000000000000000000000000000000000000000000000000000eee"
  242. const data = "0xaaaaaa";
  243. const vm = await signAndEncodeVM(
  244. timestamp,
  245. nonce,
  246. emitterChainId,
  247. emitterAddress,
  248. 1337,
  249. data,
  250. [
  251. testSigner1PK,
  252. ],
  253. 200,
  254. 2
  255. );
  256. let result = await initialized.methods.parseAndVerifyVM("0x" + vm).call();
  257. assert.equal(result[1], false)
  258. assert.equal(result[2], "invalid guardian set")
  259. })
  260. it("should revert on VMs with duplicate non-monotonic signature indexes", async function () {
  261. const initialized = new web3.eth.Contract(ImplementationFullABI, Wormhole.address);
  262. const timestamp = 1000;
  263. const nonce = 1001;
  264. const emitterChainId = 11;
  265. const emitterAddress = "0x0000000000000000000000000000000000000000000000000000000000000eee"
  266. const data = "0xaaaaaa";
  267. const vm = await signAndEncodeVMFixedIndex(
  268. timestamp,
  269. nonce,
  270. emitterChainId,
  271. emitterAddress,
  272. 1337,
  273. data,
  274. [
  275. testSigner1PK,
  276. testSigner1PK,
  277. testSigner1PK,
  278. ],
  279. 0,
  280. 2
  281. );
  282. try {
  283. await initialized.methods.parseAndVerifyVM("0x" + vm).call();
  284. assert.fail("accepted signature indexes being the same in a VM");
  285. } catch (e) {
  286. assert.equal(e.data[Object.keys(e.data)[0]].reason, 'signature indices must be ascending')
  287. }
  288. })
  289. it("should set and enforce fees", async function () {
  290. const initialized = new web3.eth.Contract(ImplementationFullABI, Wormhole.address);
  291. const accounts = await web3.eth.getAccounts();
  292. const timestamp = 1000;
  293. const nonce = 1001;
  294. const emitterChainId = testGovernanceChainId;
  295. const emitterAddress = testGovernanceContract
  296. data = [
  297. //Core
  298. core,
  299. // Action 3 (Set Message Fee)
  300. actionMessageFee,
  301. // ChainID
  302. web3.eth.abi.encodeParameter("uint16", testChainId).substring(2 + (64 - 4)),
  303. // Message Fee
  304. web3.eth.abi.encodeParameter("uint256", 1111).substring(2),
  305. ].join('')
  306. const vm = await signAndEncodeVM(
  307. timestamp,
  308. nonce,
  309. emitterChainId,
  310. emitterAddress,
  311. 0,
  312. data,
  313. [
  314. testSigner1PK,
  315. ],
  316. 0,
  317. 2
  318. );
  319. let before = await initialized.methods.messageFee().call();
  320. let set = await initialized.methods.submitSetMessageFee("0x" + vm).send({
  321. value: 0,
  322. from: accounts[0],
  323. gasLimit: 1000000
  324. });
  325. let after = await initialized.methods.messageFee().call();
  326. assert.notEqual(before, after);
  327. assert.equal(after, 1111);
  328. // test message publishing
  329. await initialized.methods.publishMessage(
  330. "0x123",
  331. "0x123321",
  332. 32
  333. ).send({
  334. from: accounts[0],
  335. value: 1111
  336. })
  337. let failed = false;
  338. try {
  339. await initialized.methods.publishMessage(
  340. "0x123",
  341. "0x123321",
  342. 32
  343. ).send({
  344. value: 1110,
  345. from: accounts[0]
  346. })
  347. } catch (e) {
  348. failed = true
  349. }
  350. assert.equal(failed, true);
  351. })
  352. it("should transfer out collected fees", async function () {
  353. const initialized = new web3.eth.Contract(ImplementationFullABI, Wormhole.address);
  354. const accounts = await web3.eth.getAccounts();
  355. const receiver = "0x" + zeroPadBytes(Math.floor(Math.random() * Number.MAX_SAFE_INTEGER).toString(16), 20);
  356. const timestamp = 1000;
  357. const nonce = 1001;
  358. const emitterChainId = testGovernanceChainId;
  359. const emitterAddress = testGovernanceContract
  360. data = [
  361. // Core
  362. core,
  363. // Action 4 (Transfer Fees)
  364. actionTransferFee,
  365. // ChainID
  366. web3.eth.abi.encodeParameter("uint16", testChainId).substring(2 + (64 - 4)),
  367. // Amount
  368. web3.eth.abi.encodeParameter("uint256", 11).substring(2),
  369. // Recipient
  370. web3.eth.abi.encodeParameter("address", receiver).substring(2),
  371. ].join('')
  372. const vm = await signAndEncodeVM(
  373. timestamp,
  374. nonce,
  375. emitterChainId,
  376. emitterAddress,
  377. 0,
  378. data,
  379. [
  380. testSigner1PK,
  381. ],
  382. 0,
  383. 2
  384. );
  385. let WHBefore = await web3.eth.getBalance(Wormhole.address);
  386. let receiverBefore = await web3.eth.getBalance(receiver);
  387. let set = await initialized.methods.submitTransferFees("0x" + vm).send({
  388. value: 0,
  389. from: accounts[0],
  390. gasLimit: 1000000
  391. });
  392. let WHAfter = await web3.eth.getBalance(Wormhole.address);
  393. let receiverAfter = await web3.eth.getBalance(receiver);
  394. assert.equal(WHBefore - WHAfter, 11);
  395. assert.equal(receiverAfter - receiverBefore, 11);
  396. })
  397. it("should accept a new guardian set", async function () {
  398. const initialized = new web3.eth.Contract(ImplementationFullABI, Wormhole.address);
  399. const accounts = await web3.eth.getAccounts();
  400. const timestamp = 1000;
  401. const nonce = 1001;
  402. const emitterChainId = testGovernanceChainId;
  403. const emitterAddress = testGovernanceContract
  404. let oldIndex = Number(await initialized.methods.getCurrentGuardianSetIndex().call());
  405. data = [
  406. // Core
  407. core,
  408. // Action 2 (Guardian Set Upgrade)
  409. actionGuardianSetUpgrade,
  410. web3.eth.abi.encodeParameter("uint16", testChainId).substring(2 + (64 - 4)),
  411. web3.eth.abi.encodeParameter("uint32", oldIndex + 1).substring(2 + (64 - 8)),
  412. web3.eth.abi.encodeParameter("uint8", 3).substring(2 + (64 - 2)),
  413. web3.eth.abi.encodeParameter("address", testSigner1.address).substring(2 + (64 - 40)),
  414. web3.eth.abi.encodeParameter("address", testSigner2.address).substring(2 + (64 - 40)),
  415. web3.eth.abi.encodeParameter("address", testSigner3.address).substring(2 + (64 - 40)),
  416. ].join('')
  417. const vm = await signAndEncodeVM(
  418. timestamp,
  419. nonce,
  420. emitterChainId,
  421. emitterAddress,
  422. 0,
  423. data,
  424. [
  425. testSigner1PK,
  426. ],
  427. 0,
  428. 2
  429. );
  430. let set = await initialized.methods.submitNewGuardianSet("0x" + vm).send({
  431. value: 0,
  432. from: accounts[0],
  433. gasLimit: 1000000
  434. });
  435. let index = await initialized.methods.getCurrentGuardianSetIndex().call();
  436. assert.equal(oldIndex + 1, index);
  437. assert.equal(index, 1);
  438. let guardians = await initialized.methods.getGuardianSet(index).call();
  439. assert.equal(guardians.expirationTime, 0);
  440. assert.lengthOf(guardians[0], 3);
  441. assert.equal(guardians[0][0], testSigner1.address);
  442. assert.equal(guardians[0][1], testSigner2.address);
  443. assert.equal(guardians[0][2], testSigner3.address);
  444. let oldGuardians = await initialized.methods.getGuardianSet(oldIndex).call();
  445. const time = (await web3.eth.getBlock("latest")).timestamp;
  446. // old guardian set expiry is set
  447. assert.ok(
  448. oldGuardians.expirationTime > Number(time) + 86000
  449. && oldGuardians.expirationTime < Number(time) + 88000
  450. );
  451. })
  452. it("should accept smart contract upgrades", async function () {
  453. const initialized = new web3.eth.Contract(ImplementationFullABI, Wormhole.address);
  454. const accounts = await web3.eth.getAccounts();
  455. const mock = await MockImplementation.new();
  456. const timestamp = 1000;
  457. const nonce = 1001;
  458. const emitterChainId = testGovernanceChainId;
  459. const emitterAddress = testGovernanceContract
  460. data = [
  461. // Core
  462. core,
  463. // Action 1 (Contract Upgrade)
  464. actionContractUpgrade,
  465. // ChainID
  466. web3.eth.abi.encodeParameter("uint16", testChainId).substring(2 + (64 - 4)),
  467. // New Contract Address
  468. web3.eth.abi.encodeParameter("address", mock.address).substring(2),
  469. ].join('')
  470. const vm = await signAndEncodeVM(
  471. timestamp,
  472. nonce,
  473. emitterChainId,
  474. emitterAddress,
  475. 0,
  476. data,
  477. [
  478. testSigner1PK,
  479. testSigner2PK,
  480. testSigner3PK
  481. ],
  482. 1,
  483. 2
  484. );
  485. let before = await web3.eth.getStorageAt(Wormhole.address, "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc");
  486. assert.equal(before.toLowerCase(), Implementation.address.toLowerCase());
  487. let set = await initialized.methods.submitContractUpgrade("0x" + vm).send({
  488. value: 0,
  489. from: accounts[0],
  490. gasLimit: 1000000
  491. });
  492. let after = await web3.eth.getStorageAt(Wormhole.address, "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc");
  493. assert.equal(after.toLowerCase(), mock.address.toLowerCase());
  494. const mockImpl = new web3.eth.Contract(MockImplementation.abi, Wormhole.address);
  495. let isUpgraded = await mockImpl.methods.testNewImplementationActive().call();
  496. assert.ok(isUpgraded);
  497. })
  498. it("should revert governance packets from old guardian set", async function () {
  499. const initialized = new web3.eth.Contract(ImplementationFullABI, Wormhole.address);
  500. const accounts = await web3.eth.getAccounts();
  501. data = [
  502. // Core
  503. core,
  504. // Action 4 (Transfer Fee)
  505. actionTransferFee,
  506. // ChainID
  507. web3.eth.abi.encodeParameter("uint16", testChainId).substring(2 + (64 - 4)),
  508. // Amount
  509. web3.eth.abi.encodeParameter("uint256", 1).substring(2),
  510. // Recipient
  511. web3.eth.abi.encodeParameter("address", "0x0000000000000000000000000000000000000000").substring(2),
  512. ].join('')
  513. const vm = await signAndEncodeVM(
  514. 0,
  515. 0,
  516. testGovernanceChainId,
  517. testGovernanceContract,
  518. 0,
  519. data,
  520. [
  521. testSigner1PK,
  522. ],
  523. 0,
  524. 2
  525. );
  526. let failed = false;
  527. try {
  528. await initialized.methods.submitTransferFees("0x" + vm).send({
  529. value: 0,
  530. from: accounts[0],
  531. gasLimit: 1000000
  532. });
  533. assert.fail("governance packet of old guardian set accepted")
  534. } catch (e) {
  535. assert.equal(e.data[Object.keys(e.data)[0]].reason, "not signed by current guardian set")
  536. }
  537. })
  538. it("should time out old gardians", async function () {
  539. const initialized = new web3.eth.Contract(ImplementationFullABI, Wormhole.address);
  540. const timestamp = 1000;
  541. const nonce = 1001;
  542. const emitterChainId = 11;
  543. const emitterAddress = "0x0000000000000000000000000000000000000000000000000000000000000eee"
  544. const data = "0xaaaaaa";
  545. const vm = await signAndEncodeVM(
  546. timestamp,
  547. nonce,
  548. emitterChainId,
  549. emitterAddress,
  550. 0,
  551. data,
  552. [
  553. testSigner1PK,
  554. ],
  555. 0,
  556. 2
  557. );
  558. // this should pass
  559. const current = await initialized.methods.parseAndVerifyVM("0x" + vm).call();
  560. assert.equal(current.valid, true)
  561. await advanceTimeAndBlock(100000);
  562. const expired = await initialized.methods.parseAndVerifyVM("0x" + vm).call();
  563. assert.equal(expired.valid, false)
  564. })
  565. it("should revert governance packets from wrong governance chain", async function () {
  566. const initialized = new web3.eth.Contract(ImplementationFullABI, Wormhole.address);
  567. const accounts = await web3.eth.getAccounts();
  568. data = [
  569. // Core
  570. core,
  571. // Action 4 (set fees)
  572. actionTransferFee,
  573. // ChainID
  574. web3.eth.abi.encodeParameter("uint16", testChainId).substring(2 + (64 - 4)),
  575. // Amount
  576. web3.eth.abi.encodeParameter("uint256", 1).substring(2),
  577. // Recipient
  578. web3.eth.abi.encodeParameter("address", "0x0000000000000000000000000000000000000000").substring(2),
  579. ].join('')
  580. const vm = await signAndEncodeVM(
  581. 0,
  582. 0,
  583. 999,
  584. testGovernanceContract,
  585. 0,
  586. data,
  587. [
  588. testSigner1PK,
  589. testSigner2PK,
  590. testSigner3PK,
  591. ],
  592. 1,
  593. 2
  594. );
  595. try {
  596. await initialized.methods.submitTransferFees("0x" + vm).send({
  597. value: 0,
  598. from: accounts[0],
  599. gasLimit: 1000000
  600. });
  601. assert.fail("governance packet from wrong governance chain accepted")
  602. } catch (e) {
  603. assert.equal(e.data[Object.keys(e.data)[0]].reason, "wrong governance chain")
  604. }
  605. })
  606. it("should revert governance packets from wrong governance contract", async function () {
  607. const initialized = new web3.eth.Contract(ImplementationFullABI, Wormhole.address);
  608. const accounts = await web3.eth.getAccounts();
  609. data = [
  610. // Core
  611. core,
  612. // Action 4 (Transfer Fee)
  613. actionTransferFee,
  614. web3.eth.abi.encodeParameter("uint16", testChainId).substring(2 + (64 - 4)),
  615. web3.eth.abi.encodeParameter("uint256", 1).substring(2),
  616. web3.eth.abi.encodeParameter("address", "0x0000000000000000000000000000000000000000").substring(2),
  617. ].join('')
  618. const vm = await signAndEncodeVM(
  619. 0,
  620. 0,
  621. testGovernanceChainId,
  622. core,
  623. 0,
  624. data,
  625. [
  626. testSigner1PK,
  627. testSigner2PK,
  628. testSigner3PK,
  629. ],
  630. 1,
  631. 2
  632. );
  633. try {
  634. await initialized.methods.submitTransferFees("0x" + vm).send({
  635. value: 0,
  636. from: accounts[0],
  637. gasLimit: 1000000
  638. });
  639. assert.fail("governance packet from wrong governance contract accepted")
  640. } catch (e) {
  641. assert.equal(e.data[Object.keys(e.data)[0]].reason, "wrong governance contract")
  642. }
  643. })
  644. it("should revert on governance packets that already have been applied", async function () {
  645. const initialized = new web3.eth.Contract(ImplementationFullABI, Wormhole.address);
  646. const accounts = await web3.eth.getAccounts();
  647. data = [
  648. // Core
  649. core,
  650. // Action 4 (Transfer Fee)
  651. actionTransferFee,
  652. // ChainID
  653. web3.eth.abi.encodeParameter("uint16", testChainId).substring(2 + (64 - 4)),
  654. // Amount
  655. web3.eth.abi.encodeParameter("uint256", 1).substring(2),
  656. // Recipient
  657. web3.eth.abi.encodeParameter("address", "0x0000000000000000000000000000000000000000").substring(2),
  658. ].join('')
  659. const vm = await signAndEncodeVM(
  660. 0,
  661. 0,
  662. testGovernanceChainId,
  663. testGovernanceContract,
  664. 0,
  665. data,
  666. [
  667. testSigner1PK,
  668. testSigner2PK,
  669. testSigner3PK,
  670. ],
  671. 1,
  672. 2
  673. );
  674. await initialized.methods.submitTransferFees("0x" + vm).send({
  675. value: 0,
  676. from: accounts[0],
  677. gasLimit: 1000000
  678. });
  679. try {
  680. await initialized.methods.submitTransferFees("0x" + vm).send({
  681. value: 0,
  682. from: accounts[0],
  683. gasLimit: 1000000
  684. });
  685. assert.fail("governance packet accepted twice")
  686. } catch (e) {
  687. assert.equal(e.data[Object.keys(e.data)[0]].reason, "governance action already consumed")
  688. }
  689. })
  690. });
  691. const signAndEncodeVM = async function (
  692. timestamp,
  693. nonce,
  694. emitterChainId,
  695. emitterAddress,
  696. sequence,
  697. data,
  698. signers,
  699. guardianSetIndex,
  700. consistencyLevel
  701. ) {
  702. const body = [
  703. web3.eth.abi.encodeParameter("uint32", timestamp).substring(2 + (64 - 8)),
  704. web3.eth.abi.encodeParameter("uint32", nonce).substring(2 + (64 - 8)),
  705. web3.eth.abi.encodeParameter("uint16", emitterChainId).substring(2 + (64 - 4)),
  706. web3.eth.abi.encodeParameter("bytes32", emitterAddress).substring(2),
  707. web3.eth.abi.encodeParameter("uint64", sequence).substring(2 + (64 - 16)),
  708. web3.eth.abi.encodeParameter("uint8", consistencyLevel).substring(2 + (64 - 2)),
  709. data.substr(2)
  710. ]
  711. const hash = web3.utils.soliditySha3(web3.utils.soliditySha3("0x" + body.join("")))
  712. let signatures = "";
  713. for (let i in signers) {
  714. const ec = new elliptic.ec("secp256k1");
  715. const key = ec.keyFromPrivate(signers[i]);
  716. const signature = key.sign(hash.substr(2), {canonical: true});
  717. const packSig = [
  718. web3.eth.abi.encodeParameter("uint8", i).substring(2 + (64 - 2)),
  719. zeroPadBytes(signature.r.toString(16), 32),
  720. zeroPadBytes(signature.s.toString(16), 32),
  721. web3.eth.abi.encodeParameter("uint8", signature.recoveryParam).substr(2 + (64 - 2)),
  722. ]
  723. signatures += packSig.join("")
  724. }
  725. const vm = [
  726. web3.eth.abi.encodeParameter("uint8", 1).substring(2 + (64 - 2)),
  727. web3.eth.abi.encodeParameter("uint32", guardianSetIndex).substring(2 + (64 - 8)),
  728. web3.eth.abi.encodeParameter("uint8", signers.length).substring(2 + (64 - 2)),
  729. signatures,
  730. body.join("")
  731. ].join("");
  732. return vm
  733. }
  734. const signAndEncodeVMFixedIndex = async function (
  735. timestamp,
  736. nonce,
  737. emitterChainId,
  738. emitterAddress,
  739. sequence,
  740. data,
  741. signers,
  742. guardianSetIndex,
  743. consistencyLevel
  744. ) {
  745. const body = [
  746. web3.eth.abi.encodeParameter("uint32", timestamp).substring(2 + (64 - 8)),
  747. web3.eth.abi.encodeParameter("uint32", nonce).substring(2 + (64 - 8)),
  748. web3.eth.abi.encodeParameter("uint16", emitterChainId).substring(2 + (64 - 4)),
  749. web3.eth.abi.encodeParameter("bytes32", emitterAddress).substring(2),
  750. web3.eth.abi.encodeParameter("uint64", sequence).substring(2 + (64 - 16)),
  751. web3.eth.abi.encodeParameter("uint8", consistencyLevel).substring(2 + (64 - 2)),
  752. data.substr(2)
  753. ]
  754. const hash = web3.utils.soliditySha3(web3.utils.soliditySha3("0x" + body.join("")))
  755. let signatures = "";
  756. for (let i in signers) {
  757. const ec = new elliptic.ec("secp256k1");
  758. const key = ec.keyFromPrivate(signers[i]);
  759. const signature = key.sign(hash.substr(2), {canonical: true});
  760. const packSig = [
  761. // Fixing the index to be zero to product a non-monotonic VM
  762. web3.eth.abi.encodeParameter("uint8", 0).substring(2 + (64 - 2)),
  763. zeroPadBytes(signature.r.toString(16), 32),
  764. zeroPadBytes(signature.s.toString(16), 32),
  765. web3.eth.abi.encodeParameter("uint8", signature.recoveryParam).substr(2 + (64 - 2)),
  766. ]
  767. signatures += packSig.join("")
  768. }
  769. const vm = [
  770. web3.eth.abi.encodeParameter("uint8", 1).substring(2 + (64 - 2)),
  771. web3.eth.abi.encodeParameter("uint32", guardianSetIndex).substring(2 + (64 - 8)),
  772. web3.eth.abi.encodeParameter("uint8", signers.length).substring(2 + (64 - 2)),
  773. signatures,
  774. body.join("")
  775. ].join("");
  776. return vm
  777. }
  778. function zeroPadBytes(value, length) {
  779. while (value.length < 2 * length) {
  780. value = "0" + value;
  781. }
  782. return value;
  783. }