Remove merkleTree.js in favor of merkletreejs dependency (#2578)
This commit is contained in:
committed by
GitHub
parent
5171e46c47
commit
508a879ef0
173
package-lock.json
generated
173
package-lock.json
generated
@ -30,8 +30,10 @@
|
||||
"ethereumjs-wallet": "^1.0.1",
|
||||
"hardhat": "^2.0.6",
|
||||
"hardhat-gas-reporter": "^1.0.4",
|
||||
"keccak256": "^1.0.2",
|
||||
"lodash.startcase": "^4.4.0",
|
||||
"lodash.zip": "^4.2.0",
|
||||
"merkletreejs": "^0.2.13",
|
||||
"micromatch": "^4.0.2",
|
||||
"mocha": "^8.0.1",
|
||||
"rimraf": "^3.0.2",
|
||||
@ -4818,6 +4820,12 @@
|
||||
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/buffer-reverse": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/buffer-reverse/-/buffer-reverse-1.0.1.tgz",
|
||||
"integrity": "sha1-SSg8jvpvkBvAH6MwTQYCeXGuL2A=",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/buffer-to-arraybuffer": {
|
||||
"version": "0.0.5",
|
||||
"resolved": "https://registry.npmjs.org/buffer-to-arraybuffer/-/buffer-to-arraybuffer-0.0.5.tgz",
|
||||
@ -8869,7 +8877,104 @@
|
||||
"bundleDependencies": [
|
||||
"source-map-support",
|
||||
"yargs",
|
||||
"ethereumjs-util"
|
||||
"ethereumjs-util",
|
||||
"@types/bn.js",
|
||||
"@types/node",
|
||||
"@types/pbkdf2",
|
||||
"@types/secp256k1",
|
||||
"ansi-regex",
|
||||
"ansi-styles",
|
||||
"base-x",
|
||||
"blakejs",
|
||||
"bn.js",
|
||||
"brorand",
|
||||
"browserify-aes",
|
||||
"bs58",
|
||||
"bs58check",
|
||||
"buffer-from",
|
||||
"buffer-xor",
|
||||
"camelcase",
|
||||
"cipher-base",
|
||||
"cliui",
|
||||
"color-convert",
|
||||
"color-name",
|
||||
"create-hash",
|
||||
"create-hmac",
|
||||
"cross-spawn",
|
||||
"decamelize",
|
||||
"elliptic",
|
||||
"emoji-regex",
|
||||
"end-of-stream",
|
||||
"ethereum-cryptography",
|
||||
"ethjs-util",
|
||||
"evp_bytestokey",
|
||||
"execa",
|
||||
"find-up",
|
||||
"get-caller-file",
|
||||
"get-stream",
|
||||
"hash-base",
|
||||
"hash.js",
|
||||
"hmac-drbg",
|
||||
"inherits",
|
||||
"invert-kv",
|
||||
"is-fullwidth-code-point",
|
||||
"is-hex-prefixed",
|
||||
"is-stream",
|
||||
"isexe",
|
||||
"keccak",
|
||||
"lcid",
|
||||
"locate-path",
|
||||
"map-age-cleaner",
|
||||
"md5.js",
|
||||
"mem",
|
||||
"mimic-fn",
|
||||
"minimalistic-assert",
|
||||
"minimalistic-crypto-utils",
|
||||
"nice-try",
|
||||
"node-addon-api",
|
||||
"node-gyp-build",
|
||||
"npm-run-path",
|
||||
"once",
|
||||
"os-locale",
|
||||
"p-defer",
|
||||
"p-finally",
|
||||
"p-is-promise",
|
||||
"p-limit",
|
||||
"p-locate",
|
||||
"p-try",
|
||||
"path-exists",
|
||||
"path-key",
|
||||
"pbkdf2",
|
||||
"pump",
|
||||
"randombytes",
|
||||
"readable-stream",
|
||||
"require-directory",
|
||||
"require-main-filename",
|
||||
"ripemd160",
|
||||
"rlp",
|
||||
"safe-buffer",
|
||||
"scrypt-js",
|
||||
"secp256k1",
|
||||
"semver",
|
||||
"set-blocking",
|
||||
"setimmediate",
|
||||
"sha.js",
|
||||
"shebang-command",
|
||||
"shebang-regex",
|
||||
"signal-exit",
|
||||
"source-map",
|
||||
"string_decoder",
|
||||
"string-width",
|
||||
"strip-ansi",
|
||||
"strip-eof",
|
||||
"strip-hex-prefix",
|
||||
"util-deprecate",
|
||||
"which",
|
||||
"which-module",
|
||||
"wrap-ansi",
|
||||
"wrappy",
|
||||
"y18n",
|
||||
"yargs-parser"
|
||||
],
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
@ -12219,6 +12324,16 @@
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/keccak256": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/keccak256/-/keccak256-1.0.2.tgz",
|
||||
"integrity": "sha512-f2EncSgmHmmQOkgxZ+/f2VaWTNkFL6f39VIrpoX+p8cEXJVyyCs/3h9GNz/ViHgwchxvv7oG5mjT2Tk4ZqInag==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"bn.js": "^4.11.8",
|
||||
"keccak": "^3.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/keyv": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz",
|
||||
@ -13185,6 +13300,20 @@
|
||||
"safe-buffer": "^5.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/merkletreejs": {
|
||||
"version": "0.2.13",
|
||||
"resolved": "https://registry.npmjs.org/merkletreejs/-/merkletreejs-0.2.13.tgz",
|
||||
"integrity": "sha512-hnM1XX0C+3yfAytRiX7FKC+bYg+GC83aQq7EytAp6nbcUBRdXU6/AVkmNdsAaJJ9IaKZt0w76r0QeWY/Fq+uFw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"buffer-reverse": "^1.0.1",
|
||||
"crypto-js": "^3.1.9-1",
|
||||
"treeify": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 7.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/methods": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
|
||||
@ -17567,6 +17696,15 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/treeify": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/treeify/-/treeify-1.1.0.tgz",
|
||||
"integrity": "sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/true-case-path": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-2.2.1.tgz",
|
||||
@ -23031,6 +23169,12 @@
|
||||
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
|
||||
"dev": true
|
||||
},
|
||||
"buffer-reverse": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/buffer-reverse/-/buffer-reverse-1.0.1.tgz",
|
||||
"integrity": "sha1-SSg8jvpvkBvAH6MwTQYCeXGuL2A=",
|
||||
"dev": true
|
||||
},
|
||||
"buffer-to-arraybuffer": {
|
||||
"version": "0.0.5",
|
||||
"resolved": "https://registry.npmjs.org/buffer-to-arraybuffer/-/buffer-to-arraybuffer-0.0.5.tgz",
|
||||
@ -28994,6 +29138,16 @@
|
||||
"node-gyp-build": "^4.2.0"
|
||||
}
|
||||
},
|
||||
"keccak256": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/keccak256/-/keccak256-1.0.2.tgz",
|
||||
"integrity": "sha512-f2EncSgmHmmQOkgxZ+/f2VaWTNkFL6f39VIrpoX+p8cEXJVyyCs/3h9GNz/ViHgwchxvv7oG5mjT2Tk4ZqInag==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"bn.js": "^4.11.8",
|
||||
"keccak": "^3.0.1"
|
||||
}
|
||||
},
|
||||
"keyv": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz",
|
||||
@ -29827,6 +29981,17 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"merkletreejs": {
|
||||
"version": "0.2.13",
|
||||
"resolved": "https://registry.npmjs.org/merkletreejs/-/merkletreejs-0.2.13.tgz",
|
||||
"integrity": "sha512-hnM1XX0C+3yfAytRiX7FKC+bYg+GC83aQq7EytAp6nbcUBRdXU6/AVkmNdsAaJJ9IaKZt0w76r0QeWY/Fq+uFw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"buffer-reverse": "^1.0.1",
|
||||
"crypto-js": "^3.1.9-1",
|
||||
"treeify": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"methods": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
|
||||
@ -33311,6 +33476,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"treeify": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/treeify/-/treeify-1.1.0.tgz",
|
||||
"integrity": "sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A==",
|
||||
"dev": true
|
||||
},
|
||||
"true-case-path": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-2.2.1.tgz",
|
||||
|
||||
@ -66,8 +66,10 @@
|
||||
"ethereumjs-wallet": "^1.0.1",
|
||||
"hardhat": "^2.0.6",
|
||||
"hardhat-gas-reporter": "^1.0.4",
|
||||
"keccak256": "^1.0.2",
|
||||
"lodash.startcase": "^4.4.0",
|
||||
"lodash.zip": "^4.2.0",
|
||||
"merkletreejs": "^0.2.13",
|
||||
"micromatch": "^4.0.2",
|
||||
"mocha": "^8.0.1",
|
||||
"rimraf": "^3.0.2",
|
||||
@ -75,6 +77,5 @@
|
||||
"solidity-coverage": "^0.7.11",
|
||||
"solidity-docgen": "^0.5.3",
|
||||
"web3": "^1.3.0"
|
||||
},
|
||||
"dependencies": {}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,135 +0,0 @@
|
||||
const { keccak256, keccakFromString, bufferToHex } = require('ethereumjs-util');
|
||||
|
||||
class MerkleTree {
|
||||
constructor (elements) {
|
||||
// Filter empty strings and hash elements
|
||||
this.elements = elements.filter(el => el).map(el => keccakFromString(el));
|
||||
|
||||
// Sort elements
|
||||
this.elements.sort(Buffer.compare);
|
||||
// Deduplicate elements
|
||||
this.elements = this.bufDedup(this.elements);
|
||||
|
||||
// Create layers
|
||||
this.layers = this.getLayers(this.elements);
|
||||
}
|
||||
|
||||
getLayers (elements) {
|
||||
if (elements.length === 0) {
|
||||
return [['']];
|
||||
}
|
||||
|
||||
const layers = [];
|
||||
layers.push(elements);
|
||||
|
||||
// Get next layer until we reach the root
|
||||
while (layers[layers.length - 1].length > 1) {
|
||||
layers.push(this.getNextLayer(layers[layers.length - 1]));
|
||||
}
|
||||
|
||||
return layers;
|
||||
}
|
||||
|
||||
getNextLayer (elements) {
|
||||
return elements.reduce((layer, el, idx, arr) => {
|
||||
if (idx % 2 === 0) {
|
||||
// Hash the current element with its pair element
|
||||
layer.push(this.combinedHash(el, arr[idx + 1]));
|
||||
}
|
||||
|
||||
return layer;
|
||||
}, []);
|
||||
}
|
||||
|
||||
combinedHash (first, second) {
|
||||
if (!first) { return second; }
|
||||
if (!second) { return first; }
|
||||
|
||||
return keccak256(this.sortAndConcat(first, second));
|
||||
}
|
||||
|
||||
getRoot () {
|
||||
return this.layers[this.layers.length - 1][0];
|
||||
}
|
||||
|
||||
getHexRoot () {
|
||||
return bufferToHex(this.getRoot());
|
||||
}
|
||||
|
||||
getProof (el) {
|
||||
let idx = this.bufIndexOf(el, this.elements);
|
||||
|
||||
if (idx === -1) {
|
||||
throw new Error('Element does not exist in Merkle tree');
|
||||
}
|
||||
|
||||
return this.layers.reduce((proof, layer) => {
|
||||
const pairElement = this.getPairElement(idx, layer);
|
||||
|
||||
if (pairElement) {
|
||||
proof.push(pairElement);
|
||||
}
|
||||
|
||||
idx = Math.floor(idx / 2);
|
||||
|
||||
return proof;
|
||||
}, []);
|
||||
}
|
||||
|
||||
getHexProof (el) {
|
||||
const proof = this.getProof(el);
|
||||
|
||||
return this.bufArrToHexArr(proof);
|
||||
}
|
||||
|
||||
getPairElement (idx, layer) {
|
||||
const pairIdx = idx % 2 === 0 ? idx + 1 : idx - 1;
|
||||
|
||||
if (pairIdx < layer.length) {
|
||||
return layer[pairIdx];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
bufIndexOf (el, arr) {
|
||||
let hash;
|
||||
|
||||
// Convert element to 32 byte hash if it is not one already
|
||||
if (el.length !== 32 || !Buffer.isBuffer(el)) {
|
||||
hash = keccakFromString(el);
|
||||
} else {
|
||||
hash = el;
|
||||
}
|
||||
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
if (hash.equals(arr[i])) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
bufDedup (elements) {
|
||||
return elements.filter((el, idx) => {
|
||||
return idx === 0 || !elements[idx - 1].equals(el);
|
||||
});
|
||||
}
|
||||
|
||||
bufArrToHexArr (arr) {
|
||||
if (arr.some(el => !Buffer.isBuffer(el))) {
|
||||
throw new Error('Array is not an array of buffers');
|
||||
}
|
||||
|
||||
return arr.map(el => '0x' + el.toString('hex'));
|
||||
}
|
||||
|
||||
sortAndConcat (...args) {
|
||||
return Buffer.concat([...args].sort(Buffer.compare));
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
MerkleTree,
|
||||
};
|
||||
@ -1,7 +1,7 @@
|
||||
require('@openzeppelin/test-helpers');
|
||||
|
||||
const { MerkleTree } = require('../../helpers/merkleTree.js');
|
||||
const { keccakFromString, bufferToHex } = require('ethereumjs-util');
|
||||
const { MerkleTree } = require('merkletreejs');
|
||||
const keccak256 = require('keccak256');
|
||||
|
||||
const { expect } = require('chai');
|
||||
|
||||
@ -15,43 +15,43 @@ contract('MerkleProof', function (accounts) {
|
||||
describe('verify', function () {
|
||||
it('returns true for a valid Merkle proof', async function () {
|
||||
const elements = ['a', 'b', 'c', 'd'];
|
||||
const merkleTree = new MerkleTree(elements);
|
||||
const merkleTree = new MerkleTree(elements, keccak256, { hashLeaves: true });
|
||||
|
||||
const root = merkleTree.getHexRoot();
|
||||
|
||||
const proof = merkleTree.getHexProof(elements[0]);
|
||||
const leaf = keccak256(elements[0]);
|
||||
|
||||
const leaf = bufferToHex(keccakFromString(elements[0]));
|
||||
const proof = merkleTree.getHexProof(leaf);
|
||||
|
||||
expect(await this.merkleProof.verify(proof, root, leaf)).to.equal(true);
|
||||
});
|
||||
|
||||
it('returns false for an invalid Merkle proof', async function () {
|
||||
const correctElements = ['a', 'b', 'c'];
|
||||
const correctMerkleTree = new MerkleTree(correctElements);
|
||||
const correctMerkleTree = new MerkleTree(correctElements, keccak256, { hashLeaves: true });
|
||||
|
||||
const correctRoot = correctMerkleTree.getHexRoot();
|
||||
|
||||
const correctLeaf = bufferToHex(keccakFromString(correctElements[0]));
|
||||
const correctLeaf = keccak256(correctElements[0]);
|
||||
|
||||
const badElements = ['d', 'e', 'f'];
|
||||
const badMerkleTree = new MerkleTree(badElements);
|
||||
|
||||
const badProof = badMerkleTree.getHexProof(badElements[0]);
|
||||
const badProof = badMerkleTree.getHexProof(badElements[0], keccak256, { hashLeaves: true });
|
||||
|
||||
expect(await this.merkleProof.verify(badProof, correctRoot, correctLeaf)).to.equal(false);
|
||||
});
|
||||
|
||||
it('returns false for a Merkle proof of invalid length', async function () {
|
||||
const elements = ['a', 'b', 'c'];
|
||||
const merkleTree = new MerkleTree(elements);
|
||||
const merkleTree = new MerkleTree(elements, keccak256, { hashLeaves: true });
|
||||
|
||||
const root = merkleTree.getHexRoot();
|
||||
|
||||
const proof = merkleTree.getHexProof(elements[0]);
|
||||
const badProof = proof.slice(0, proof.length - 5);
|
||||
const leaf = keccak256(elements[0]);
|
||||
|
||||
const leaf = bufferToHex(keccakFromString(elements[0]));
|
||||
const proof = merkleTree.getHexProof(leaf);
|
||||
const badProof = proof.slice(0, proof.length - 5);
|
||||
|
||||
expect(await this.merkleProof.verify(badProof, root, leaf)).to.equal(false);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user