MerkleProof.test.js 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. require('@openzeppelin/test-helpers');
  2. const { expectRevert } = require('@openzeppelin/test-helpers');
  3. const { MerkleTree } = require('merkletreejs');
  4. const keccak256 = require('keccak256');
  5. const { expect } = require('chai');
  6. const MerkleProof = artifacts.require('$MerkleProof');
  7. contract('MerkleProof', function () {
  8. beforeEach(async function () {
  9. this.merkleProof = await MerkleProof.new();
  10. });
  11. describe('verify', function () {
  12. it('returns true for a valid Merkle proof', async function () {
  13. const elements = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='.split('');
  14. const merkleTree = new MerkleTree(elements, keccak256, { hashLeaves: true, sortPairs: true });
  15. const root = merkleTree.getHexRoot();
  16. const leaf = keccak256(elements[0]);
  17. const proof = merkleTree.getHexProof(leaf);
  18. expect(await this.merkleProof.$verify(proof, root, leaf)).to.equal(true);
  19. expect(await this.merkleProof.$verifyCalldata(proof, root, leaf)).to.equal(true);
  20. // For demonstration, it is also possible to create valid proofs for certain 64-byte values *not* in elements:
  21. const noSuchLeaf = keccak256(
  22. Buffer.concat([keccak256(elements[0]), keccak256(elements[1])].sort(Buffer.compare)),
  23. );
  24. expect(await this.merkleProof.$verify(proof.slice(1), root, noSuchLeaf)).to.equal(true);
  25. expect(await this.merkleProof.$verifyCalldata(proof.slice(1), root, noSuchLeaf)).to.equal(true);
  26. });
  27. it('returns false for an invalid Merkle proof', async function () {
  28. const correctElements = ['a', 'b', 'c'];
  29. const correctMerkleTree = new MerkleTree(correctElements, keccak256, { hashLeaves: true, sortPairs: true });
  30. const correctRoot = correctMerkleTree.getHexRoot();
  31. const correctLeaf = keccak256(correctElements[0]);
  32. const badElements = ['d', 'e', 'f'];
  33. const badMerkleTree = new MerkleTree(badElements);
  34. const badProof = badMerkleTree.getHexProof(badElements[0]);
  35. expect(await this.merkleProof.$verify(badProof, correctRoot, correctLeaf)).to.equal(false);
  36. expect(await this.merkleProof.$verifyCalldata(badProof, correctRoot, correctLeaf)).to.equal(false);
  37. });
  38. it('returns false for a Merkle proof of invalid length', async function () {
  39. const elements = ['a', 'b', 'c'];
  40. const merkleTree = new MerkleTree(elements, keccak256, { hashLeaves: true, sortPairs: true });
  41. const root = merkleTree.getHexRoot();
  42. const leaf = keccak256(elements[0]);
  43. const proof = merkleTree.getHexProof(leaf);
  44. const badProof = proof.slice(0, proof.length - 5);
  45. expect(await this.merkleProof.$verify(badProof, root, leaf)).to.equal(false);
  46. expect(await this.merkleProof.$verifyCalldata(badProof, root, leaf)).to.equal(false);
  47. });
  48. });
  49. describe('multiProofVerify', function () {
  50. it('returns true for a valid Merkle multi proof', async function () {
  51. const leaves = ['a', 'b', 'c', 'd', 'e', 'f'].map(keccak256).sort(Buffer.compare);
  52. const merkleTree = new MerkleTree(leaves, keccak256, { sort: true });
  53. const root = merkleTree.getRoot();
  54. const proofLeaves = ['b', 'f', 'd'].map(keccak256).sort(Buffer.compare);
  55. const proof = merkleTree.getMultiProof(proofLeaves);
  56. const proofFlags = merkleTree.getProofFlags(proofLeaves, proof);
  57. expect(await this.merkleProof.$multiProofVerify(proof, proofFlags, root, proofLeaves)).to.equal(true);
  58. expect(await this.merkleProof.$multiProofVerifyCalldata(proof, proofFlags, root, proofLeaves)).to.equal(true);
  59. });
  60. it('returns false for an invalid Merkle multi proof', async function () {
  61. const leaves = ['a', 'b', 'c', 'd', 'e', 'f'].map(keccak256).sort(Buffer.compare);
  62. const merkleTree = new MerkleTree(leaves, keccak256, { sort: true });
  63. const root = merkleTree.getRoot();
  64. const badProofLeaves = ['g', 'h', 'i'].map(keccak256).sort(Buffer.compare);
  65. const badMerkleTree = new MerkleTree(badProofLeaves);
  66. const badProof = badMerkleTree.getMultiProof(badProofLeaves);
  67. const badProofFlags = badMerkleTree.getProofFlags(badProofLeaves, badProof);
  68. expect(await this.merkleProof.$multiProofVerify(badProof, badProofFlags, root, badProofLeaves)).to.equal(false);
  69. expect(await this.merkleProof.$multiProofVerifyCalldata(badProof, badProofFlags, root, badProofLeaves)).to.equal(
  70. false,
  71. );
  72. });
  73. it('revert with invalid multi proof #1', async function () {
  74. const fill = Buffer.alloc(32); // This could be anything, we are reconstructing a fake branch
  75. const leaves = ['a', 'b', 'c', 'd'].map(keccak256).sort(Buffer.compare);
  76. const badLeaf = keccak256('e');
  77. const merkleTree = new MerkleTree(leaves, keccak256, { sort: true });
  78. const root = merkleTree.getRoot();
  79. await expectRevert(
  80. this.merkleProof.$multiProofVerify(
  81. [leaves[1], fill, merkleTree.layers[1][1]],
  82. [false, false, false],
  83. root,
  84. [leaves[0], badLeaf], // A, E
  85. ),
  86. 'MerkleProof: invalid multiproof',
  87. );
  88. await expectRevert(
  89. this.merkleProof.$multiProofVerifyCalldata(
  90. [leaves[1], fill, merkleTree.layers[1][1]],
  91. [false, false, false],
  92. root,
  93. [leaves[0], badLeaf], // A, E
  94. ),
  95. 'MerkleProof: invalid multiproof',
  96. );
  97. });
  98. it('revert with invalid multi proof #2', async function () {
  99. const fill = Buffer.alloc(32); // This could be anything, we are reconstructing a fake branch
  100. const leaves = ['a', 'b', 'c', 'd'].map(keccak256).sort(Buffer.compare);
  101. const badLeaf = keccak256('e');
  102. const merkleTree = new MerkleTree(leaves, keccak256, { sort: true });
  103. const root = merkleTree.getRoot();
  104. await expectRevert(
  105. this.merkleProof.$multiProofVerify(
  106. [leaves[1], fill, merkleTree.layers[1][1]],
  107. [false, false, false, false],
  108. root,
  109. [badLeaf, leaves[0]], // A, E
  110. ),
  111. 'reverted with panic code 0x32',
  112. );
  113. await expectRevert(
  114. this.merkleProof.$multiProofVerifyCalldata(
  115. [leaves[1], fill, merkleTree.layers[1][1]],
  116. [false, false, false, false],
  117. root,
  118. [badLeaf, leaves[0]], // A, E
  119. ),
  120. 'reverted with panic code 0x32',
  121. );
  122. });
  123. it('limit case: works for tree containing a single leaf', async function () {
  124. const leaves = ['a'].map(keccak256).sort(Buffer.compare);
  125. const merkleTree = new MerkleTree(leaves, keccak256, { sort: true });
  126. const root = merkleTree.getRoot();
  127. const proofLeaves = ['a'].map(keccak256).sort(Buffer.compare);
  128. const proof = merkleTree.getMultiProof(proofLeaves);
  129. const proofFlags = merkleTree.getProofFlags(proofLeaves, proof);
  130. expect(await this.merkleProof.$multiProofVerify(proof, proofFlags, root, proofLeaves)).to.equal(true);
  131. expect(await this.merkleProof.$multiProofVerifyCalldata(proof, proofFlags, root, proofLeaves)).to.equal(true);
  132. });
  133. it('limit case: can prove empty leaves', async function () {
  134. const leaves = ['a', 'b', 'c', 'd'].map(keccak256).sort(Buffer.compare);
  135. const merkleTree = new MerkleTree(leaves, keccak256, { sort: true });
  136. const root = merkleTree.getRoot();
  137. expect(await this.merkleProof.$multiProofVerify([root], [], root, [])).to.equal(true);
  138. expect(await this.merkleProof.$multiProofVerifyCalldata([root], [], root, [])).to.equal(true);
  139. });
  140. });
  141. });