MerkleProof.test.js 8.4 KB

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