diff --git a/CHANGELOG.md b/CHANGELOG.md index 726d7a9..57be5fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## 1.0.6 - Added an option to disable leaf sorting. +- Added `SimpleMerkleTree` class that supports `bytes32` leaves with no extra hashing. ## 1.0.5 diff --git a/package-lock.json b/package-lock.json index d95fc03..d23ff93 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,7 +1,7 @@ { "name": "@openzeppelin/merkle-tree", "version": "1.0.6", - "lockfileVersion": 2, + "lockfileVersion": 3, "requires": true, "packages": { "": { @@ -10,7 +10,9 @@ "license": "MIT", "dependencies": { "@ethersproject/abi": "^5.7.0", - "ethereum-cryptography": "^1.1.2" + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0" }, "devDependencies": { "@types/mocha": "^10.0.0", @@ -424,18 +426,18 @@ } }, "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", "dev": true, "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", "dev": true }, "node_modules/@jridgewell/trace-mapping": { @@ -448,70 +450,6 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, - "node_modules/@noble/hashes": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.1.2.tgz", - "integrity": "sha512-KYRCASVTv6aeUi1tsF8/vpyR7zpfs3FUzy2Jqm+MU+LmUKhQ0y2FpfwqkCcxSg2ua4GALJd8k2R76WxwZGbQpA==", - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ] - }, - "node_modules/@noble/secp256k1": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.6.3.tgz", - "integrity": "sha512-T04e4iTurVy7I8Sw4+c5OSN9/RkPlo1uKxAomtxQNLq8j1uPAqnsqG1bqvY3Jv7c13gyr6dui0zmh/I3+f/JaQ==", - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ] - }, - "node_modules/@scure/base": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.1.tgz", - "integrity": "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==", - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ] - }, - "node_modules/@scure/bip32": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.1.0.tgz", - "integrity": "sha512-ftTW3kKX54YXLCxH6BB7oEEoJfoE2pIgw7MINKAs5PsS6nqKPuKk1haTF/EuHmYqG330t5GSrdmtRuHaY1a62Q==", - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "dependencies": { - "@noble/hashes": "~1.1.1", - "@noble/secp256k1": "~1.6.0", - "@scure/base": "~1.1.0" - } - }, - "node_modules/@scure/bip39": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.1.0.tgz", - "integrity": "sha512-pwrPOS16VeTKg98dYXQyIjJEcWfz7/1YJIwxUEPFfQPtc86Ym/1sVgQ2RLoD43AazMk2l/unK4ITySSpW2+82w==", - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "dependencies": { - "@noble/hashes": "~1.1.1", - "@scure/base": "~1.1.0" - } - }, "node_modules/@tsconfig/node10": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", @@ -531,34 +469,37 @@ "dev": true }, "node_modules/@tsconfig/node16": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", - "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", "dev": true }, "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", - "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", "dev": true }, "node_modules/@types/mocha": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.0.tgz", - "integrity": "sha512-rADY+HtTOA52l9VZWtgQfn4p+UDVM2eDVkMZT1I6syp0YKxW2F9v+0pbRZLsvskhQv/vMb6ZfCay81GHbz5SHg==", + "version": "10.0.6", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.6.tgz", + "integrity": "sha512-dJvrYWxP/UcXm36Qn36fxhUKu8A/xMRXVT2cliFF1Z7UA9liG5Psj3ezNSZw+5puH2czDXRLcXQxf8JbJt0ejg==", "dev": true }, "node_modules/@types/node": { - "version": "18.11.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.7.tgz", - "integrity": "sha512-LhFTglglr63mNXUSRYD8A+ZAIu5sFqNJ4Y2fPuY7UlrySJH87rRRlhtVmMHplmfk5WkoJGmDjE9oiTfyX94CpQ==", + "version": "20.10.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.5.tgz", + "integrity": "sha512-nNPsNE65wjMxEKI93yOP+NPGGBJz/PoN3kZsVLee0XMiJolxSekEVD8wRwBUBqkwc7UWop0edW50yrCQW4CyRw==", "dev": true, - "peer": true + "peer": true, + "dependencies": { + "undici-types": "~5.26.4" + } }, "node_modules/acorn": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", - "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", + "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -568,9 +509,9 @@ } }, "node_modules/acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "version": "8.3.1", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.1.tgz", + "integrity": "sha512-TgUZgYvqZprrl7YldZNoa9OciCAyZR+Ejm9eXzKCmjsF5IKp/wgQ7Z/ZpjpGTIUPwrHQIcYeI8qDh4PsEwxMbw==", "dev": true, "engines": { "node": ">=0.4.0" @@ -610,9 +551,9 @@ } }, "node_modules/anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, "dependencies": { "normalize-path": "^3.0.0", @@ -687,9 +628,9 @@ "dev": true }, "node_modules/c8": { - "version": "7.12.0", - "resolved": "https://registry.npmjs.org/c8/-/c8-7.12.0.tgz", - "integrity": "sha512-CtgQrHOkyxr5koX1wEUmN/5cfDa2ckbHRA4Gy5LAL0zaCFtVWJS5++n+w4/sr2GWGerBxgTjpKeDclk/Qk6W/A==", + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/c8/-/c8-7.14.0.tgz", + "integrity": "sha512-i04rtkkcNcCf7zsQcSv/T9EbUn4RXQ6mropeMcjFOsQXQ0iGLAr/xT6TImQg4+U9hmNpN9XdvPkjUL1IzbgxJw==", "dev": true, "dependencies": { "@bcoe/v8-coverage": "^0.2.3", @@ -712,15 +653,6 @@ "node": ">=10.12.0" } }, - "node_modules/c8/node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true, - "engines": { - "node": ">=10" - } - }, "node_modules/camelcase": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", @@ -749,18 +681,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/chalk/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", @@ -824,9 +744,9 @@ "dev": true }, "node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true }, "node_modules/create-require": { @@ -885,9 +805,9 @@ } }, "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", "dev": true, "engines": { "node": ">=0.3.1" @@ -939,34 +859,23 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/ethereum-cryptography": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-1.1.2.tgz", - "integrity": "sha512-XDSJlg4BD+hq9N2FjvotwUET9Tfxpxc3kWGE2AqUG5vcbeunnbImVk3cj6e/xT3phdW21mE8R5IugU4fspQDcQ==", - "dependencies": { - "@noble/hashes": "1.1.2", - "@noble/secp256k1": "1.6.3", - "@scure/bip32": "1.1.0", - "@scure/bip39": "1.1.0" - } - }, "node_modules/fast-check": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-3.3.0.tgz", - "integrity": "sha512-Zu6tZ4g0T4H9Tiz3tdNPEHrSbuICj7yhdOM9RCZKNMkpjZ9avDV3ORklXaEmh4zvkX24/bGZ9DxKKqWfXttUqw==", + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-3.15.0.tgz", + "integrity": "sha512-iBz6c+EXL6+nI931x/sbZs1JYTZtLG6Cko0ouS8LRTikhDR7+wZk4TYzdRavlnByBs2G6+nuuJ7NYL9QplNt8Q==", "dev": true, "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - }, { "type": "individual", "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" } ], "dependencies": { - "pure-rand": "^5.0.2" + "pure-rand": "^6.0.0" }, "engines": { "node": ">=8.0.0" @@ -1029,9 +938,9 @@ "dev": true }, "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, "hasInstallScript": true, "optional": true, @@ -1242,44 +1151,32 @@ "dev": true }, "node_modules/istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, "dependencies": { "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", + "make-dir": "^4.0.0", "supports-color": "^7.1.0" }, "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-report/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" + "node": ">=10" } }, "node_modules/istanbul-reports": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", - "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", + "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", "dev": true, "dependencies": { "html-escaper": "^2.0.0", @@ -1337,16 +1234,28 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", "dev": true, "dependencies": { - "semver": "^6.0.0" + "semver": "^7.5.3" }, "engines": { - "node": ">=8" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -1381,9 +1290,9 @@ } }, "node_modules/mocha": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.1.0.tgz", - "integrity": "sha512-vUF7IYxEoN7XhQpFLxQAEMtE4W91acW4B6En9l97MwE9stL1A9gusXfoHZCLVHDUJ/7V5+lbCM6yMqzo5vNymg==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", + "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", "dev": true, "dependencies": { "ansi-colors": "4.1.1", @@ -1420,13 +1329,28 @@ "url": "https://opencollective.com/mochajs" } }, - "node_modules/mocha/node_modules/diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, "engines": { - "node": ">=0.3.1" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/mocha/node_modules/yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true, + "engines": { + "node": ">=10" } }, "node_modules/ms": { @@ -1550,14 +1474,20 @@ } }, "node_modules/pure-rand": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-5.0.3.tgz", - "integrity": "sha512-9N8x1h8dptBQpHyC7aZMS+iNOAm97WMGY0AFrguU1cpfW3I5jINkWe5BIY5md0ofy+1TCIELsVcm/GJXZSaPbw==", + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.4.tgz", + "integrity": "sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA==", "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ] }, "node_modules/randombytes": { "version": "2.1.0", @@ -1625,12 +1555,18 @@ ] }, "node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, "bin": { "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, "node_modules/serialize-javascript": { @@ -1708,18 +1644,15 @@ } }, "node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "dependencies": { "has-flag": "^4.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "node": ">=8" } }, "node_modules/test-exclude": { @@ -1771,9 +1704,9 @@ } }, "node_modules/ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", @@ -1813,10 +1746,19 @@ } } }, + "node_modules/ts-node/node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/typescript": { - "version": "4.8.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", - "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -1826,6 +1768,13 @@ "node": ">=4.2.0" } }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true, + "peer": true + }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", @@ -1833,27 +1782,27 @@ "dev": true }, "node_modules/v8-to-istanbul": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz", - "integrity": "sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w==", + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz", + "integrity": "sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==", "dev": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.12", "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0" + "convert-source-map": "^2.0.0" }, "engines": { "node": ">=10.12.0" } }, "node_modules/v8-to-istanbul/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.17", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", - "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", + "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", "dev": true, "dependencies": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, "node_modules/which": { @@ -1909,6 +1858,12 @@ "node": ">=10" } }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", @@ -1928,9 +1883,9 @@ } }, "node_modules/yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dev": true, "engines": { "node": ">=10" @@ -1972,1353 +1927,5 @@ "url": "https://github.com/sponsors/sindresorhus" } } - }, - "dependencies": { - "@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true - }, - "@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "0.3.9" - } - }, - "@ethersproject/abi": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.7.0.tgz", - "integrity": "sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA==", - "requires": { - "@ethersproject/address": "^5.7.0", - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/constants": "^5.7.0", - "@ethersproject/hash": "^5.7.0", - "@ethersproject/keccak256": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/strings": "^5.7.0" - } - }, - "@ethersproject/abstract-provider": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz", - "integrity": "sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw==", - "requires": { - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/networks": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/transactions": "^5.7.0", - "@ethersproject/web": "^5.7.0" - } - }, - "@ethersproject/abstract-signer": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz", - "integrity": "sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ==", - "requires": { - "@ethersproject/abstract-provider": "^5.7.0", - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0" - } - }, - "@ethersproject/address": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.7.0.tgz", - "integrity": "sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA==", - "requires": { - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/keccak256": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/rlp": "^5.7.0" - } - }, - "@ethersproject/base64": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.7.0.tgz", - "integrity": "sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ==", - "requires": { - "@ethersproject/bytes": "^5.7.0" - } - }, - "@ethersproject/bignumber": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.7.0.tgz", - "integrity": "sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw==", - "requires": { - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "bn.js": "^5.2.1" - } - }, - "@ethersproject/bytes": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.7.0.tgz", - "integrity": "sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A==", - "requires": { - "@ethersproject/logger": "^5.7.0" - } - }, - "@ethersproject/constants": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.7.0.tgz", - "integrity": "sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA==", - "requires": { - "@ethersproject/bignumber": "^5.7.0" - } - }, - "@ethersproject/hash": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.7.0.tgz", - "integrity": "sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g==", - "requires": { - "@ethersproject/abstract-signer": "^5.7.0", - "@ethersproject/address": "^5.7.0", - "@ethersproject/base64": "^5.7.0", - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/keccak256": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/strings": "^5.7.0" - } - }, - "@ethersproject/keccak256": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.7.0.tgz", - "integrity": "sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg==", - "requires": { - "@ethersproject/bytes": "^5.7.0", - "js-sha3": "0.8.0" - } - }, - "@ethersproject/logger": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.7.0.tgz", - "integrity": "sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig==" - }, - "@ethersproject/networks": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.7.1.tgz", - "integrity": "sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ==", - "requires": { - "@ethersproject/logger": "^5.7.0" - } - }, - "@ethersproject/properties": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.7.0.tgz", - "integrity": "sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw==", - "requires": { - "@ethersproject/logger": "^5.7.0" - } - }, - "@ethersproject/rlp": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.7.0.tgz", - "integrity": "sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w==", - "requires": { - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0" - } - }, - "@ethersproject/signing-key": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.7.0.tgz", - "integrity": "sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q==", - "requires": { - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "bn.js": "^5.2.1", - "elliptic": "6.5.4", - "hash.js": "1.1.7" - } - }, - "@ethersproject/strings": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.7.0.tgz", - "integrity": "sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg==", - "requires": { - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/constants": "^5.7.0", - "@ethersproject/logger": "^5.7.0" - } - }, - "@ethersproject/transactions": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.7.0.tgz", - "integrity": "sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ==", - "requires": { - "@ethersproject/address": "^5.7.0", - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/constants": "^5.7.0", - "@ethersproject/keccak256": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/rlp": "^5.7.0", - "@ethersproject/signing-key": "^5.7.0" - } - }, - "@ethersproject/web": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.7.1.tgz", - "integrity": "sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w==", - "requires": { - "@ethersproject/base64": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/strings": "^5.7.0" - } - }, - "@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true - }, - "@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", - "dev": true - }, - "@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "dev": true - }, - "@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "@noble/hashes": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.1.2.tgz", - "integrity": "sha512-KYRCASVTv6aeUi1tsF8/vpyR7zpfs3FUzy2Jqm+MU+LmUKhQ0y2FpfwqkCcxSg2ua4GALJd8k2R76WxwZGbQpA==" - }, - "@noble/secp256k1": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.6.3.tgz", - "integrity": "sha512-T04e4iTurVy7I8Sw4+c5OSN9/RkPlo1uKxAomtxQNLq8j1uPAqnsqG1bqvY3Jv7c13gyr6dui0zmh/I3+f/JaQ==" - }, - "@scure/base": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.1.tgz", - "integrity": "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==" - }, - "@scure/bip32": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.1.0.tgz", - "integrity": "sha512-ftTW3kKX54YXLCxH6BB7oEEoJfoE2pIgw7MINKAs5PsS6nqKPuKk1haTF/EuHmYqG330t5GSrdmtRuHaY1a62Q==", - "requires": { - "@noble/hashes": "~1.1.1", - "@noble/secp256k1": "~1.6.0", - "@scure/base": "~1.1.0" - } - }, - "@scure/bip39": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.1.0.tgz", - "integrity": "sha512-pwrPOS16VeTKg98dYXQyIjJEcWfz7/1YJIwxUEPFfQPtc86Ym/1sVgQ2RLoD43AazMk2l/unK4ITySSpW2+82w==", - "requires": { - "@noble/hashes": "~1.1.1", - "@scure/base": "~1.1.0" - } - }, - "@tsconfig/node10": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "dev": true - }, - "@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true - }, - "@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true - }, - "@tsconfig/node16": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", - "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", - "dev": true - }, - "@types/istanbul-lib-coverage": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", - "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", - "dev": true - }, - "@types/mocha": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.0.tgz", - "integrity": "sha512-rADY+HtTOA52l9VZWtgQfn4p+UDVM2eDVkMZT1I6syp0YKxW2F9v+0pbRZLsvskhQv/vMb6ZfCay81GHbz5SHg==", - "dev": true - }, - "@types/node": { - "version": "18.11.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.7.tgz", - "integrity": "sha512-LhFTglglr63mNXUSRYD8A+ZAIu5sFqNJ4Y2fPuY7UlrySJH87rRRlhtVmMHplmfk5WkoJGmDjE9oiTfyX94CpQ==", - "dev": true, - "peer": true - }, - "acorn": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", - "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", - "dev": true - }, - "acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "dev": true - }, - "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true - }, - "bn.js": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", - "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" - }, - "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==" - }, - "browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, - "c8": { - "version": "7.12.0", - "resolved": "https://registry.npmjs.org/c8/-/c8-7.12.0.tgz", - "integrity": "sha512-CtgQrHOkyxr5koX1wEUmN/5cfDa2ckbHRA4Gy5LAL0zaCFtVWJS5++n+w4/sr2GWGerBxgTjpKeDclk/Qk6W/A==", - "dev": true, - "requires": { - "@bcoe/v8-coverage": "^0.2.3", - "@istanbuljs/schema": "^0.1.3", - "find-up": "^5.0.0", - "foreground-child": "^2.0.0", - "istanbul-lib-coverage": "^3.2.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-reports": "^3.1.4", - "rimraf": "^3.0.2", - "test-exclude": "^6.0.0", - "v8-to-istanbul": "^9.0.0", - "yargs": "^16.2.0", - "yargs-parser": "^20.2.9" - }, - "dependencies": { - "yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true - } - } - }, - "camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "dependencies": { - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - } - }, - "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true - }, - "create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - }, - "dependencies": { - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "dev": true - }, - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true - }, - "elliptic": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", - "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", - "requires": { - "bn.js": "^4.11.9", - "brorand": "^1.1.0", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.1", - "inherits": "^2.0.4", - "minimalistic-assert": "^1.0.1", - "minimalistic-crypto-utils": "^1.0.1" - }, - "dependencies": { - "bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" - } - } - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true - }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - }, - "ethereum-cryptography": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-1.1.2.tgz", - "integrity": "sha512-XDSJlg4BD+hq9N2FjvotwUET9Tfxpxc3kWGE2AqUG5vcbeunnbImVk3cj6e/xT3phdW21mE8R5IugU4fspQDcQ==", - "requires": { - "@noble/hashes": "1.1.2", - "@noble/secp256k1": "1.6.3", - "@scure/bip32": "1.1.0", - "@scure/bip39": "1.1.0" - } - }, - "fast-check": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-3.3.0.tgz", - "integrity": "sha512-Zu6tZ4g0T4H9Tiz3tdNPEHrSbuICj7yhdOM9RCZKNMkpjZ9avDV3ORklXaEmh4zvkX24/bGZ9DxKKqWfXttUqw==", - "dev": true, - "requires": { - "pure-rand": "^5.0.2" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, - "flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true - }, - "foreground-child": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", - "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.0", - "signal-exit": "^3.0.2" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "optional": true - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "dependencies": { - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - } - } - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "hash.js": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", - "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", - "requires": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" - } - }, - "he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true - }, - "hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", - "requires": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" - } - }, - "html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "dev": true - }, - "is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", - "dev": true - }, - "istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", - "dev": true, - "requires": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" - }, - "dependencies": { - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "istanbul-reports": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", - "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", - "dev": true, - "requires": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - } - }, - "js-sha3": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", - "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "requires": { - "argparse": "^2.0.1" - } - }, - "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "requires": { - "p-locate": "^5.0.0" - } - }, - "log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "requires": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - } - }, - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "requires": { - "semver": "^6.0.0" - } - }, - "make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" - }, - "minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==" - }, - "minimatch": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", - "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", - "dev": true, - "requires": { - "brace-expansion": "^2.0.1" - } - }, - "mocha": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.1.0.tgz", - "integrity": "sha512-vUF7IYxEoN7XhQpFLxQAEMtE4W91acW4B6En9l97MwE9stL1A9gusXfoHZCLVHDUJ/7V5+lbCM6yMqzo5vNymg==", - "dev": true, - "requires": { - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.4", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.2.0", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "5.0.1", - "ms": "2.1.3", - "nanoid": "3.3.3", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "workerpool": "6.2.1", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" - }, - "dependencies": { - "diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", - "dev": true - } - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "nanoid": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", - "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", - "dev": true - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "requires": { - "yocto-queue": "^0.1.0" - } - }, - "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "requires": { - "p-limit": "^3.0.2" - } - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true - }, - "prettier": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", - "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", - "dev": true - }, - "pure-rand": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-5.0.3.tgz", - "integrity": "sha512-9N8x1h8dptBQpHyC7aZMS+iNOAm97WMGY0AFrguU1cpfW3I5jINkWe5BIY5md0ofy+1TCIELsVcm/GJXZSaPbw==", - "dev": true - }, - "randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "requires": { - "safe-buffer": "^5.1.0" - } - }, - "readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - }, - "serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", - "dev": true, - "requires": { - "randombytes": "^2.1.0" - } - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true - }, - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "requires": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "dependencies": { - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - } - } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", - "dev": true, - "requires": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - } - }, - "typescript": { - "version": "4.8.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", - "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", - "dev": true - }, - "v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true - }, - "v8-to-istanbul": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz", - "integrity": "sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0" - }, - "dependencies": { - "@jridgewell/trace-mapping": { - "version": "0.3.17", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", - "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" - } - } - } - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "workerpool": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", - "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", - "dev": true - }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, - "y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true - }, - "yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "requires": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - } - }, - "yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", - "dev": true - }, - "yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", - "dev": true, - "requires": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" - } - }, - "yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true - }, - "yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true - } } } diff --git a/package.json b/package.json index f6a159c..deb44a9 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,9 @@ "license": "MIT", "dependencies": { "@ethersproject/abi": "^5.7.0", - "ethereum-cryptography": "^1.1.2" + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0" }, "devDependencies": { "@types/mocha": "^10.0.0", diff --git a/src/bytes.ts b/src/bytes.ts index aed0e0a..938c013 100644 --- a/src/bytes.ts +++ b/src/bytes.ts @@ -1,19 +1,12 @@ -import { bytesToHex } from 'ethereum-cryptography/utils'; +import type { BytesLike } from '@ethersproject/bytes'; +type HexString = string; -export type Bytes = Uint8Array; +import { arrayify as toBytes, hexlify as toHex, concat } from '@ethersproject/bytes'; -export function compareBytes(a: Bytes, b: Bytes): number { - const n = Math.min(a.length, b.length); - - for (let i = 0; i < n; i++) { - if (a[i] !== b[i]) { - return a[i]! - b[i]!; - } - } - - return a.length - b.length; +function compare(a: BytesLike, b: BytesLike): number { + const diff = BigInt(toHex(a)) - BigInt(toHex(b)); + return diff > 0 ? 1 : diff < 0 ? -1 : 0; } -export function hex(b: Bytes): string { - return '0x' + bytesToHex(b); -} +export type { HexString, BytesLike }; +export { toBytes, toHex, concat, compare }; diff --git a/src/core.test.ts b/src/core.test.ts index 66a5ef3..c2059d7 100644 --- a/src/core.test.ts +++ b/src/core.test.ts @@ -1,6 +1,7 @@ import fc from 'fast-check'; import assert from 'assert/strict'; -import { equalsBytes } from 'ethereum-cryptography/utils'; +import { HashZero as zero } from '@ethersproject/constants'; +import { keccak256 } from '@ethersproject/keccak256'; import { makeMerkleTree, getProof, @@ -10,12 +11,9 @@ import { isValidMerkleTree, renderMerkleTree, } from './core'; -import { compareBytes, hex } from './bytes'; -import { keccak256 } from 'ethereum-cryptography/keccak'; +import { toHex, compare } from './bytes'; -const zero = new Uint8Array(32); - -const leaf = fc.uint8Array({ minLength: 32, maxLength: 32 }).map(x => PrettyBytes.from(x)); +const leaf = fc.uint8Array({ minLength: 32, maxLength: 32 }).map(toHex); const leaves = fc.array(leaf, { minLength: 1 }); const leavesAndIndex = leaves.chain(xs => fc.tuple(fc.constant(xs), fc.nat({ max: xs.length - 1 }))); const leavesAndIndices = leaves.chain(xs => fc.tuple(fc.constant(xs), fc.uniqueArray(fc.nat({ max: xs.length - 1 })))); @@ -33,7 +31,7 @@ describe('core properties', () => { const proof = getProof(tree, treeIndex); const leaf = leaves[leafIndex]!; const impliedRoot = processProof(leaf, proof); - return equalsBytes(root, impliedRoot); + return root === impliedRoot; }), ); }); @@ -49,7 +47,7 @@ describe('core properties', () => { if (leafIndices.length !== proof.leaves.length) return false; if (leafIndices.some(i => !proof.leaves.includes(leaves[i]!))) return false; const impliedRoot = processMultiProof(proof); - return equalsBytes(root, impliedRoot); + return root === impliedRoot; }), ); }); @@ -57,12 +55,12 @@ describe('core properties', () => { describe('core error conditions', () => { it('zero leaves', () => { - assert.throws(() => makeMerkleTree([]), /^Error: Expected non-zero number of leaves$/); + assert.throws(() => makeMerkleTree([]), /^InvalidArgumentError: Expected non-zero number of leaves$/); }); it('multiproof duplicate index', () => { const tree = makeMerkleTree(new Array(2).fill(zero)); - assert.throws(() => getMultiProof(tree, [1, 1]), /^Error: Cannot prove duplicated index$/); + assert.throws(() => getMultiProof(tree, [1, 1]), /^InvalidArgumentError: Cannot prove duplicated index$/); }); it('tree validity', () => { @@ -70,7 +68,7 @@ describe('core error conditions', () => { assert(!isValidMerkleTree([zero, zero]), 'even number of nodes'); assert(!isValidMerkleTree([zero, zero, zero]), 'inner node not hash of children'); - assert.throws(() => renderMerkleTree([]), /^Error: Expected non-zero number of nodes$/); + assert.throws(() => renderMerkleTree([]), /^InvalidArgumentError: Expected non-zero number of nodes$/); }); it('multiproof invariants', () => { @@ -78,17 +76,11 @@ describe('core error conditions', () => { const tree = makeMerkleTree([leaf, zero]); const badMultiProof = { - leaves: [128, 129].map(n => keccak256(Uint8Array.of(n))).sort(compareBytes), + leaves: [128, 129].map(n => keccak256(Uint8Array.of(n))).sort(compare), proof: [leaf, leaf], proofFlags: [true, true, false], }; - assert.throws(() => processMultiProof(badMultiProof), /^Error: Broken invariant$/); + assert.throws(() => processMultiProof(badMultiProof), /^InvariantError$/); }); }); - -class PrettyBytes extends Uint8Array { - [fc.toStringMethod]() { - return hex(this); - } -} diff --git a/src/core.ts b/src/core.ts index b8163ea..b523ba7 100644 --- a/src/core.ts +++ b/src/core.ts @@ -1,9 +1,8 @@ -import { keccak256 } from 'ethereum-cryptography/keccak'; -import { concatBytes, bytesToHex, equalsBytes } from 'ethereum-cryptography/utils'; -import { Bytes, compareBytes } from './bytes'; -import { throwError } from './utils/throw-error'; +import { keccak256 } from '@ethersproject/keccak256'; +import { BytesLike, HexString, toHex, toBytes, concat, compare } from './bytes'; +import { invariant, throwError, validateArgument } from './utils/errors'; -const hashPair = (a: Bytes, b: Bytes) => keccak256(concatBytes(...[a, b].sort(compareBytes))); +const hashPair = (a: BytesLike, b: BytesLike): HexString => keccak256(concat([a, b].sort(compare))); const leftChildIndex = (i: number) => 2 * i + 1; const rightChildIndex = (i: number) => 2 * i + 2; @@ -13,26 +12,21 @@ const siblingIndex = (i: number) => (i > 0 ? i - (-1) ** (i % 2) : throwError('R const isTreeNode = (tree: unknown[], i: number) => i >= 0 && i < tree.length; const isInternalNode = (tree: unknown[], i: number) => isTreeNode(tree, leftChildIndex(i)); const isLeafNode = (tree: unknown[], i: number) => isTreeNode(tree, i) && !isInternalNode(tree, i); -const isValidMerkleNode = (node: Bytes) => node instanceof Uint8Array && node.length === 32; +const isValidMerkleNode = (node: BytesLike) => toBytes(node).length === 32; -const checkTreeNode = (tree: unknown[], i: number) => void (isTreeNode(tree, i) || throwError('Index is not in tree')); -const checkInternalNode = (tree: unknown[], i: number) => - void (isInternalNode(tree, i) || throwError('Index is not an internal tree node')); const checkLeafNode = (tree: unknown[], i: number) => void (isLeafNode(tree, i) || throwError('Index is not a leaf')); -const checkValidMerkleNode = (node: Bytes) => +const checkValidMerkleNode = (node: BytesLike) => void (isValidMerkleNode(node) || throwError('Merkle tree nodes must be Uint8Array of length 32')); -export function makeMerkleTree(leaves: Bytes[]): Bytes[] { +export function makeMerkleTree(leaves: BytesLike[]): HexString[] { leaves.forEach(checkValidMerkleNode); - if (leaves.length === 0) { - throw new Error('Expected non-zero number of leaves'); - } + validateArgument(leaves.length !== 0, 'Expected non-zero number of leaves'); - const tree = new Array(2 * leaves.length - 1); + const tree = new Array(2 * leaves.length - 1); for (const [i, leaf] of leaves.entries()) { - tree[tree.length - 1 - i] = leaf; + tree[tree.length - 1 - i] = toHex(leaf); } for (let i = tree.length - 1 - leaves.length; i >= 0; i--) { tree[i] = hashPair(tree[leftChildIndex(i)]!, tree[rightChildIndex(i)]!); @@ -41,22 +35,22 @@ export function makeMerkleTree(leaves: Bytes[]): Bytes[] { return tree; } -export function getProof(tree: Bytes[], index: number): Bytes[] { +export function getProof(tree: BytesLike[], index: number): HexString[] { checkLeafNode(tree, index); const proof = []; while (index > 0) { - proof.push(tree[siblingIndex(index)]!); + proof.push(toHex(tree[siblingIndex(index)]!)); index = parentIndex(index); } return proof; } -export function processProof(leaf: Bytes, proof: Bytes[]): Bytes { +export function processProof(leaf: BytesLike, proof: BytesLike[]): HexString { checkValidMerkleNode(leaf); proof.forEach(checkValidMerkleNode); - return proof.reduce(hashPair, leaf); + return toHex(proof.reduce(hashPair, leaf)); } export interface MultiProof { @@ -65,13 +59,14 @@ export interface MultiProof { proofFlags: boolean[]; } -export function getMultiProof(tree: Bytes[], indices: number[]): MultiProof { +export function getMultiProof(tree: BytesLike[], indices: number[]): MultiProof { indices.forEach(i => checkLeafNode(tree, i)); indices.sort((a, b) => b - a); - if (indices.slice(1).some((i, p) => i === indices[p])) { - throw new Error('Cannot prove duplicated index'); - } + validateArgument( + indices.slice(1).every((i, p) => i !== indices[p]), + 'Cannot prove duplicated index', + ); const stack = indices.concat(); // copy const proof = []; @@ -87,33 +82,34 @@ export function getMultiProof(tree: Bytes[], indices: number[]): MultiProof tree[i]!), + leaves: indices.map(i => toHex(tree[i]!)), proof, proofFlags, }; } -export function processMultiProof(multiproof: MultiProof): Bytes { +export function processMultiProof(multiproof: MultiProof): HexString { multiproof.leaves.forEach(checkValidMerkleNode); multiproof.proof.forEach(checkValidMerkleNode); - if (multiproof.proof.length < multiproof.proofFlags.filter(b => !b).length) { - throw new Error('Invalid multiproof format'); - } - - if (multiproof.leaves.length + multiproof.proof.length !== multiproof.proofFlags.length + 1) { - throw new Error('Provided leaves and multiproof are not compatible'); - } + validateArgument( + multiproof.proof.length >= multiproof.proofFlags.filter(b => !b).length, + 'Invalid multiproof format', + ); + validateArgument( + multiproof.leaves.length + multiproof.proof.length === multiproof.proofFlags.length + 1, + 'Provided leaves and multiproof are not compatible', + ); const stack = multiproof.leaves.concat(); // copy const proof = multiproof.proof.concat(); // copy @@ -121,20 +117,16 @@ export function processMultiProof(multiproof: MultiProof): Bytes { for (const flag of multiproof.proofFlags) { const a = stack.shift(); const b = flag ? stack.shift() : proof.shift(); - if (a === undefined || b === undefined) { - throw new Error('Broken invariant'); - } + invariant(a !== undefined && b !== undefined); stack.push(hashPair(a, b)); } - if (stack.length + proof.length !== 1) { - throw new Error('Broken invariant'); - } + invariant(stack.length + proof.length === 1); - return stack.pop() ?? proof.shift()!; + return toHex(stack.pop() ?? proof.shift()!); } -export function isValidMerkleTree(tree: Bytes[]): boolean { +export function isValidMerkleTree(tree: BytesLike[]): boolean { for (const [i, node] of tree.entries()) { if (!isValidMerkleNode(node)) { return false; @@ -147,7 +139,7 @@ export function isValidMerkleTree(tree: Bytes[]): boolean { if (l < tree.length) { return false; } - } else if (!equalsBytes(node, hashPair(tree[l]!, tree[r]!))) { + } else if (compare(node, hashPair(tree[l]!, tree[r]!))) { return false; } } @@ -155,10 +147,8 @@ export function isValidMerkleTree(tree: Bytes[]): boolean { return tree.length > 0; } -export function renderMerkleTree(tree: Bytes[]): string { - if (tree.length === 0) { - throw new Error('Expected non-zero number of nodes'); - } +export function renderMerkleTree(tree: BytesLike[]): HexString { + validateArgument(tree.length !== 0, 'Expected non-zero number of nodes'); const stack: [number, number[]][] = [[0, []]]; @@ -178,7 +168,7 @@ export function renderMerkleTree(tree: Bytes[]): string { .join('') + i + ') ' + - bytesToHex(tree[i]!), + toHex(tree[i]!), ); if (rightChildIndex(i) < tree.length) { diff --git a/src/index.test.ts b/src/index.test.ts new file mode 100644 index 0000000..095cff5 --- /dev/null +++ b/src/index.test.ts @@ -0,0 +1,9 @@ +import assert from 'assert/strict'; +import { SimpleMerkleTree, StandardMerkleTree } from '.'; + +describe('index properties', () => { + it('classes are exported', () => { + assert.notEqual(SimpleMerkleTree, undefined); + assert.notEqual(StandardMerkleTree, undefined); + }); +}); diff --git a/src/index.ts b/src/index.ts index 7d16c9d..f00f42f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1 +1,2 @@ +export { SimpleMerkleTree } from './simple'; export { StandardMerkleTree } from './standard'; diff --git a/src/merkletree.ts b/src/merkletree.ts new file mode 100644 index 0000000..5d79c5e --- /dev/null +++ b/src/merkletree.ts @@ -0,0 +1,174 @@ +import { BytesLike, HexString, compare } from './bytes'; + +import { + MultiProof, + makeMerkleTree, + isValidMerkleTree, + getProof, + getMultiProof, + processProof, + processMultiProof, + renderMerkleTree, +} from './core'; + +import { MerkleTreeOptions, defaultOptions } from './options'; +import { validateArgument, invariant } from './utils/errors'; + +export interface MerkleTreeData { + format: string; + tree: HexString[]; + values: { value: T; treeIndex: number }[]; +} + +export interface MerkleTree { + root: HexString; + render(): string; + dump(): MerkleTreeData; + entries(): Iterable<[number, T]>; + validate(): void; + leafHash(leaf: T): HexString; + leafLookup(leaf: T): number; + getProof(leaf: number | T): HexString[]; + getMultiProof(leaves: (number | T)[]): MultiProof; + verify(leaf: number | T, proof: HexString[]): boolean; + verifyMultiProof(multiproof: MultiProof): boolean; +} + +export abstract class MerkleTreeImpl implements MerkleTree { + private readonly hashLookup: { [hash: HexString]: number }; + + protected constructor( + protected readonly tree: HexString[], + protected readonly values: MerkleTreeData['values'], + public readonly leafHash: MerkleTree['leafHash'], + ) { + validateArgument( + values.every(({ value }) => typeof value != 'number'), + 'Leaf values cannot be numbers', + ); + this.hashLookup = Object.fromEntries(values.map(({ treeIndex }, valueIndex) => [tree[treeIndex], valueIndex])); + } + + protected static prepare( + values: T[], + options: MerkleTreeOptions = {}, + leafHash: MerkleTree['leafHash'], + ): [tree: HexString[], indexedValues: MerkleTreeData['values']] { + const sortLeaves = options.sortLeaves ?? defaultOptions.sortLeaves; + const hashedValues = values.map((value, valueIndex) => ({ value, valueIndex, hash: leafHash(value) })); + + if (sortLeaves) { + hashedValues.sort((a, b) => compare(a.hash, b.hash)); + } + + const tree = makeMerkleTree(hashedValues.map(v => v.hash)); + + const indexedValues = values.map(value => ({ value, treeIndex: 0 })); + for (const [leafIndex, { valueIndex }] of hashedValues.entries()) { + indexedValues[valueIndex]!.treeIndex = tree.length - leafIndex - 1; + } + + return [tree, indexedValues]; + } + + get root(): HexString { + return this.tree[0]!; + } + + abstract dump(): MerkleTreeData; + + render() { + return renderMerkleTree(this.tree); + } + + *entries(): Iterable<[number, T]> { + for (const [i, { value }] of this.values.entries()) { + yield [i, value]; + } + } + + validate(): void { + this.values.forEach((_, i) => this._validateValueAt(i)); + invariant(isValidMerkleTree(this.tree), 'Merkle tree is invalid'); + } + + leafLookup(leaf: T): number { + const lookup = this.hashLookup[this.leafHash(leaf)]; + validateArgument(typeof lookup !== 'undefined', 'Leaf is not in tree'); + return lookup; + } + + getProof(leaf: number | T): HexString[] { + // input validity + const valueIndex = typeof leaf === 'number' ? leaf : this.leafLookup(leaf); + this._validateValueAt(valueIndex); + + // rebuild tree index and generate proof + const { treeIndex } = this.values[valueIndex]!; + const proof = getProof(this.tree, treeIndex); + + // sanity check proof + invariant(this._verify(this.tree[treeIndex]!, proof), 'Unable to prove value'); + + // return proof in hex format + return proof; + } + + getMultiProof(leaves: (number | T)[]): MultiProof { + // input validity + const valueIndices = leaves.map(leaf => (typeof leaf === 'number' ? leaf : this.leafLookup(leaf))); + for (const valueIndex of valueIndices) this._validateValueAt(valueIndex); + + // rebuild tree indices and generate proof + const indices = valueIndices.map(i => this.values[i]!.treeIndex); + const proof = getMultiProof(this.tree, indices); + + // sanity check proof + invariant(this._verifyMultiProof(proof), 'Unable to prove values'); + + // return multiproof in hex format + return { + leaves: proof.leaves.map(hash => this.values[this.hashLookup[hash]!]!.value), + proof: proof.proof, + proofFlags: proof.proofFlags, + }; + } + + verify(leaf: number | T, proof: HexString[]): boolean { + return this._verify(this._leafHash(leaf), proof); + } + + verifyMultiProof(multiproof: MultiProof): boolean { + return this._verifyMultiProof({ + leaves: multiproof.leaves.map(l => this._leafHash(l)), + proof: multiproof.proof, + proofFlags: multiproof.proofFlags, + }); + } + + private _validateValueAt(index: number): void { + const value = this.values[index]; + validateArgument(value !== undefined, 'Index out of bounds'); + invariant( + this.tree[value.treeIndex] === this.leafHash(value.value), + 'Merkle tree does not contain the expected value', + ); + } + + private _leafHash(leaf: number | T): HexString { + if (typeof leaf === 'number') { + const lookup = this.values[leaf]; + validateArgument(lookup !== undefined, 'Index out of bounds'); + leaf = lookup.value; + } + return this.leafHash(leaf); + } + + private _verify(leafHash: BytesLike, proof: BytesLike[]): boolean { + return this.root === processProof(leafHash, proof); + } + + private _verifyMultiProof(multiproof: MultiProof): boolean { + return this.root === processMultiProof(multiproof); + } +} diff --git a/src/options.ts b/src/options.ts index 3516460..da40f98 100644 --- a/src/options.ts +++ b/src/options.ts @@ -4,7 +4,7 @@ export type MerkleTreeOptions = Partial<{ sortLeaves: boolean; }>; -// Recommended (default) options. +// Recommended (default) MerkleTree options. // - leaves are sorted by default to facilitate onchain verification of multiproofs. export const defaultOptions: Required = { sortLeaves: true, diff --git a/src/simple.test.ts b/src/simple.test.ts new file mode 100644 index 0000000..af40c8b --- /dev/null +++ b/src/simple.test.ts @@ -0,0 +1,146 @@ +import assert from 'assert/strict'; +import { HashZero as zero } from '@ethersproject/constants'; +import { keccak256 } from '@ethersproject/keccak256'; +import { SimpleMerkleTree } from './simple'; + +describe('simple merkle tree', () => { + for (const opts of [{}, { sortLeaves: true }, { sortLeaves: false }]) { + describe(`with options '${JSON.stringify(opts)}'`, () => { + const leaves = 'abcdef'.split('').map(c => keccak256(Buffer.from(c))); + const otherLeaves = 'abc'.split('').map(c => keccak256(Buffer.from(c))); + const tree = SimpleMerkleTree.of(leaves, opts); + const otherTree = SimpleMerkleTree.of(otherLeaves, opts); + + it('generates a valid tree', () => { + tree.validate(); + }); + + it('generates valid single proofs for all leaves', () => { + for (const [id, leaf] of tree.entries()) { + const proof1 = tree.getProof(id); + const proof2 = tree.getProof(leaf); + + assert.deepEqual(proof1, proof2); + + assert(tree.verify(id, proof1)); + assert(tree.verify(leaf, proof1)); + assert(SimpleMerkleTree.verify(tree.root, leaf, proof1)); + } + }); + + it('rejects invalid proofs', () => { + const leaf = leaves[0]!; + const invalidProof = otherTree.getProof(leaf); + + assert(!tree.verify(leaf, invalidProof)); + assert(!SimpleMerkleTree.verify(tree.root, leaf, invalidProof)); + }); + + it('generates valid multiproofs', () => { + for (const ids of [[], [0, 1], [0, 1, 5], [1, 3, 4, 5], [0, 2, 4, 5], [0, 1, 2, 3, 4, 5], [4, 1, 5, 0, 2]]) { + const proof1 = tree.getMultiProof(ids); + const proof2 = tree.getMultiProof(ids.map(i => leaves[i]!)); + + assert.deepEqual(proof1, proof2); + + assert(tree.verifyMultiProof(proof1)); + assert(SimpleMerkleTree.verifyMultiProof(tree.root, proof1)); + } + }); + + it('rejects invalid multiproofs', () => { + const multiProof = otherTree.getMultiProof(leaves.slice(0, 3)); + + assert(!tree.verifyMultiProof(multiProof)); + assert(!SimpleMerkleTree.verifyMultiProof(tree.root, multiProof)); + }); + + it('renders tree representation', () => { + assert.equal( + tree.render(), + opts.sortLeaves == false + ? [ + '0) 0x9012f1e18a87790d2e01faace75aaaca38e53df437cdce2c0552464dda4af49c', + '├─ 1) 0x68203f90e9d07dc5859259d7536e87a6ba9d345f2552b5b9de2999ddce9ce1bf', + '│ ├─ 3) 0xd253a52d4cb00de2895e85f2529e2976e6aaaa5c18106b68ab66813e14415669', + '│ │ ├─ 7) 0xf1918e8562236eb17adc8502332f4c9c82bc14e19bfc0aa10ab674ff75b3d2f3', + '│ │ └─ 8) 0x0b42b6393c1f53060fe3ddbfcd7aadcca894465a5a438f69c87d790b2299b9b2', + '│ └─ 4) 0x805b21d846b189efaeb0377d6bb0d201b3872a363e607c25088f025b0c6ae1f8', + '│ ├─ 9) 0xb5553de315e0edf504d9150af82dafa5c4667fa618ed0a6f19c69b41166c5510', + '│ └─ 10) 0x3ac225168df54212a25c1c01fd35bebfea408fdac2e31ddd6f80a4bbf9a5f1cb', + '└─ 2) 0xf0b49bb4b0d9396e0315755ceafaa280707b32e75e6c9053f5cdf2679dcd5c6a', + ' ├─ 5) 0xd1e8aeb79500496ef3dc2e57ba746a8315d048b7a664a2bf948db4fa91960483', + ' └─ 6) 0xa8982c89d80987fb9a510e25981ee9170206be21af3c8e0eb312ef1d3382e761', + ].join('\n') + : [ + '0) 0x1b404f199ea828ec5771fb30139c222d8417a82175fefad5cd42bc3a189bd8d5', + '├─ 1) 0xec554bdfb01d31fa838d0830339b0e6e8a70e0d55a8f172ffa8bebbf8e8d5ba0', + '│ ├─ 3) 0x434d51cfeb80272378f4c3a8fd2824561c2cad9fce556ea600d46f20550976a6', + '│ │ ├─ 7) 0xb5553de315e0edf504d9150af82dafa5c4667fa618ed0a6f19c69b41166c5510', + '│ │ └─ 8) 0xa8982c89d80987fb9a510e25981ee9170206be21af3c8e0eb312ef1d3382e761', + '│ └─ 4) 0x7dea550f679f3caab547cbbc5ee1a4c978c8c039b572ba00af1baa6481b88360', + '│ ├─ 9) 0x3ac225168df54212a25c1c01fd35bebfea408fdac2e31ddd6f80a4bbf9a5f1cb', + '│ └─ 10) 0x0b42b6393c1f53060fe3ddbfcd7aadcca894465a5a438f69c87d790b2299b9b2', + '└─ 2) 0xaf46af0745b433e1d5bed9a04b1fdf4002f67a733c20db2fca5b2af6120d9bcb', + ' ├─ 5) 0xf1918e8562236eb17adc8502332f4c9c82bc14e19bfc0aa10ab674ff75b3d2f3', + ' └─ 6) 0xd1e8aeb79500496ef3dc2e57ba746a8315d048b7a664a2bf948db4fa91960483', + ].join('\n'), + ); + }); + + it('dump and load', () => { + const recoveredTree = SimpleMerkleTree.load(tree.dump()); + + recoveredTree.validate(); + assert.deepEqual(tree, recoveredTree); + }); + + it('reject out of bounds value index', () => { + assert.throws(() => tree.getProof(leaves.length), /^InvalidArgumentError: Index out of bounds$/); + }); + + it('reject invalid leaf size', () => { + const invalidLeaf = [zero + '00']; // 33 bytes (all zero) + assert.throws( + () => SimpleMerkleTree.of(invalidLeaf, opts), + `InvalidArgumentError: ${invalidLeaf} is not a valid 32 bytes object (pos: 0)`, + ); + }); + }); + } + + describe('tree dumps', () => { + it('reject unrecognized tree dump', () => { + assert.throws( + () => SimpleMerkleTree.load({ format: 'nonstandard' } as any), + /^InvalidArgumentError: Unknown format 'nonstandard'$/, + ); + + assert.throws( + () => SimpleMerkleTree.load({ format: 'standard-v1' } as any), + /^InvalidArgumentError: Unknown format 'standard-v1'$/, + ); + }); + + it('reject malformed tree dump', () => { + const loadedTree1 = SimpleMerkleTree.load({ + format: 'simple-v1', + tree: [zero], + values: [ + { + value: '0x0000000000000000000000000000000000000000000000000000000000000001', + treeIndex: 0, + }, + ], + }); + assert.throws(() => loadedTree1.getProof(0), /^InvariantError: Merkle tree does not contain the expected value$/); + + const loadedTree2 = SimpleMerkleTree.load({ + format: 'simple-v1', + tree: [zero, zero, zero], + values: [{ value: zero, treeIndex: 2 }], + }); + assert.throws(() => loadedTree2.getProof(0), /^InvariantError: Unable to prove value$/); + }); + }); +}); diff --git a/src/simple.ts b/src/simple.ts new file mode 100644 index 0000000..94f87e8 --- /dev/null +++ b/src/simple.ts @@ -0,0 +1,42 @@ +import { defaultAbiCoder } from '@ethersproject/abi'; +import { BytesLike, HexString, toHex } from './bytes'; +import { MultiProof, processProof, processMultiProof } from './core'; +import { MerkleTreeData, MerkleTreeImpl } from './merkletree'; +import { MerkleTreeOptions } from './options'; +import { validateArgument } from './utils/errors'; + +export interface SimpleMerkleTreeData extends MerkleTreeData { + format: 'simple-v1'; +} + +export function formatLeaf(value: BytesLike): HexString { + return defaultAbiCoder.encode(['bytes32'], [value]); +} + +export class SimpleMerkleTree extends MerkleTreeImpl { + static of(values: BytesLike[], options: MerkleTreeOptions = {}): SimpleMerkleTree { + const [tree, indexedValues] = MerkleTreeImpl.prepare(values, options, formatLeaf); + return new SimpleMerkleTree(tree, indexedValues, formatLeaf); + } + + static load(data: SimpleMerkleTreeData): SimpleMerkleTree { + validateArgument(data.format === 'simple-v1', `Unknown format '${data.format}'`); + return new SimpleMerkleTree(data.tree, data.values, formatLeaf); + } + + static verify(root: BytesLike, leaf: BytesLike, proof: BytesLike[]): boolean { + return toHex(root) === processProof(formatLeaf(leaf), proof); + } + + static verifyMultiProof(root: BytesLike, multiproof: MultiProof): boolean { + return toHex(root) === processMultiProof(multiproof); + } + + dump(): SimpleMerkleTreeData { + return { + format: 'simple-v1', + tree: this.tree, + values: this.values.map(({ value, treeIndex }) => ({ value: toHex(value), treeIndex })), + }; + } +} diff --git a/src/standard.test.ts b/src/standard.test.ts index f8f5473..5729c55 100644 --- a/src/standard.test.ts +++ b/src/standard.test.ts @@ -1,25 +1,41 @@ import assert from 'assert/strict'; -import { keccak256 } from 'ethereum-cryptography/keccak'; -import { hex } from './bytes'; -import { MerkleTreeOptions } from './options'; -import { StandardMerkleTree } from './standard'; - -const zeroBytes = new Uint8Array(32); -const zero = hex(zeroBytes); - -const makeTree = (s: string, opts: MerkleTreeOptions = {}) => { - const l = s.split('').map(c => [c]); - const t = StandardMerkleTree.of(l, ['string'], opts); - return { l, t }; -}; +import { HashZero as zero } from '@ethersproject/constants'; +import { keccak256 } from '@ethersproject/keccak256'; +import { StandardMerkleTree, StandardMerkleTreeData } from './standard'; describe('standard merkle tree', () => { + it('Supports complex leaf types', () => { + const leaves = [ + [0, []], + [1, ['openzeppelin']], + [2, ['hello', 'world']], + [3, ['merkle', 'tree']], + ]; + const types = ['uint256', 'string[]']; + StandardMerkleTree.of(leaves, types); + }); + for (const opts of [{}, { sortLeaves: true }, { sortLeaves: false }]) { describe(`with options '${JSON.stringify(opts)}'`, () => { - const { l: leaves, t: tree } = makeTree('abcdef', opts); - const { l: otherLeaves, t: otherTree } = makeTree('abc', opts); + const leaves = 'abcdef'.split('').map(c => [c]); + const otherLeaves = 'abc'.split('').map(c => [c]); - it('generates valid single proofs for all leaves', () => { + const tree = StandardMerkleTree.of(leaves, ['string'], opts); + const otherTree = StandardMerkleTree.of(otherLeaves, ['string'], opts); + + it('rejects loading a tree without leaf encoding', () => { + assert.throws( + () => + StandardMerkleTree.load({ + format: 'standard-v1', + tree: [zero], + values: [{ value: ['0'], treeIndex: 0 }], + } as StandardMerkleTreeData<[string]>), + /^InvalidArgumentError: Expected leaf encoding$/, + ); + }); + + it('generates a valid tree', () => { tree.validate(); }); @@ -37,7 +53,7 @@ describe('standard merkle tree', () => { }); it('rejects invalid proofs', () => { - const leaf = ['a']; + const leaf = leaves[0]!; const invalidProof = otherTree.getProof(leaf); assert(!tree.verify(leaf, invalidProof)); @@ -57,7 +73,7 @@ describe('standard merkle tree', () => { }); it('rejects invalid multiproofs', () => { - const multiProof = otherTree.getMultiProof([['a'], ['b'], ['c']]); + const multiProof = otherTree.getMultiProof(leaves.slice(0, 3)); assert(!tree.verifyMultiProof(multiProof)); assert(!StandardMerkleTree.verifyMultiProof(tree.root, ['string'], multiProof)); @@ -68,30 +84,30 @@ describe('standard merkle tree', () => { tree.render(), opts.sortLeaves == false ? [ - '0) 23be0977360f08bb0bd7f709a7d543d2cd779c79c66d74e0441919871647de2b', - '├─ 1) 8f7234e8cfe39c08ca84a3a3e3274f574af26fd15165fe29e09cbab742daccd9', - '│ ├─ 3) 03707d7802a71ca56a8ad8028da98c4f1dbec55b31b4a25d536b5309cc20eda9', - '│ │ ├─ 7) eba909cf4bb90c6922771d7f126ad0fd11dfde93f3937a196274e1ac20fd2f5b', - '│ │ └─ 8) 9cf5a63718145ba968a01c1d557020181c5b252f665cf7386d370eddb176517b', - '│ └─ 4) fa914d99a18dc32d9725b3ef1c50426deb40ec8d0885dac8edcc5bfd6d030016', - '│ ├─ 9) 19ba6c6333e0e9a15bf67523e0676e2f23eb8e574092552d5e888c64a4bb3681', - '│ └─ 10) 9c15a6a0eaeed500fd9eed4cbeab71f797cefcc67bfd46683e4d2e6ff7f06d1c', - '└─ 2) 7b0c6cd04b82bfc0e250030a5d2690c52585e0cc6a4f3bc7909d7723b0236ece', - ' ├─ 5) c62a8cfa41edc0ef6f6ae27a2985b7d39c7fea770787d7e104696c6e81f64848', - ' └─ 6) 9a4f64e953595df82d1b4f570d34c4f4f0cfaf729a61e9d60e83e579e1aa283e', + '0) 0x23be0977360f08bb0bd7f709a7d543d2cd779c79c66d74e0441919871647de2b', + '├─ 1) 0x8f7234e8cfe39c08ca84a3a3e3274f574af26fd15165fe29e09cbab742daccd9', + '│ ├─ 3) 0x03707d7802a71ca56a8ad8028da98c4f1dbec55b31b4a25d536b5309cc20eda9', + '│ │ ├─ 7) 0xeba909cf4bb90c6922771d7f126ad0fd11dfde93f3937a196274e1ac20fd2f5b', + '│ │ └─ 8) 0x9cf5a63718145ba968a01c1d557020181c5b252f665cf7386d370eddb176517b', + '│ └─ 4) 0xfa914d99a18dc32d9725b3ef1c50426deb40ec8d0885dac8edcc5bfd6d030016', + '│ ├─ 9) 0x19ba6c6333e0e9a15bf67523e0676e2f23eb8e574092552d5e888c64a4bb3681', + '│ └─ 10) 0x9c15a6a0eaeed500fd9eed4cbeab71f797cefcc67bfd46683e4d2e6ff7f06d1c', + '└─ 2) 0x7b0c6cd04b82bfc0e250030a5d2690c52585e0cc6a4f3bc7909d7723b0236ece', + ' ├─ 5) 0xc62a8cfa41edc0ef6f6ae27a2985b7d39c7fea770787d7e104696c6e81f64848', + ' └─ 6) 0x9a4f64e953595df82d1b4f570d34c4f4f0cfaf729a61e9d60e83e579e1aa283e', ].join('\n') : [ - '0) 6deb52b5da8fd108f79fab00341f38d2587896634c646ee52e49f845680a70c8', - '├─ 1) 52426e0f1f65ff7e209a13b8c29cffe82e3acaf3dad0a9b9088f3b9a61a929c3', - '│ ├─ 3) 8076923e76cf01a7c048400a2304c9a9c23bbbdac3a98ea3946340fdafbba34f', - '│ │ ├─ 7) 9cf5a63718145ba968a01c1d557020181c5b252f665cf7386d370eddb176517b', - '│ │ └─ 8) 9c15a6a0eaeed500fd9eed4cbeab71f797cefcc67bfd46683e4d2e6ff7f06d1c', - '│ └─ 4) 965b92c6cf08303cc4feb7f3e0819c436c2cec17c6f0688a6af139c9a368707c', - '│ ├─ 9) 9a4f64e953595df82d1b4f570d34c4f4f0cfaf729a61e9d60e83e579e1aa283e', - '│ └─ 10) 19ba6c6333e0e9a15bf67523e0676e2f23eb8e574092552d5e888c64a4bb3681', - '└─ 2) fd3cf45654e88d1cc5d663578c82c76f4b5e3826bacaa1216441443504538f51', - ' ├─ 5) eba909cf4bb90c6922771d7f126ad0fd11dfde93f3937a196274e1ac20fd2f5b', - ' └─ 6) c62a8cfa41edc0ef6f6ae27a2985b7d39c7fea770787d7e104696c6e81f64848', + '0) 0x6deb52b5da8fd108f79fab00341f38d2587896634c646ee52e49f845680a70c8', + '├─ 1) 0x52426e0f1f65ff7e209a13b8c29cffe82e3acaf3dad0a9b9088f3b9a61a929c3', + '│ ├─ 3) 0x8076923e76cf01a7c048400a2304c9a9c23bbbdac3a98ea3946340fdafbba34f', + '│ │ ├─ 7) 0x9cf5a63718145ba968a01c1d557020181c5b252f665cf7386d370eddb176517b', + '│ │ └─ 8) 0x9c15a6a0eaeed500fd9eed4cbeab71f797cefcc67bfd46683e4d2e6ff7f06d1c', + '│ └─ 4) 0x965b92c6cf08303cc4feb7f3e0819c436c2cec17c6f0688a6af139c9a368707c', + '│ ├─ 9) 0x9a4f64e953595df82d1b4f570d34c4f4f0cfaf729a61e9d60e83e579e1aa283e', + '│ └─ 10) 0x19ba6c6333e0e9a15bf67523e0676e2f23eb8e574092552d5e888c64a4bb3681', + '└─ 2) 0xfd3cf45654e88d1cc5d663578c82c76f4b5e3826bacaa1216441443504538f51', + ' ├─ 5) 0xeba909cf4bb90c6922771d7f126ad0fd11dfde93f3937a196274e1ac20fd2f5b', + ' └─ 6) 0xc62a8cfa41edc0ef6f6ae27a2985b7d39c7fea770787d7e104696c6e81f64848', ].join('\n'), ); }); @@ -100,37 +116,57 @@ describe('standard merkle tree', () => { const recoveredTree = StandardMerkleTree.load(tree.dump()); recoveredTree.validate(); - assert.deepEqual(tree, recoveredTree); + + // assert.deepEqual(tree, recoveredTree); + for (const [key, value] of Object.entries(tree)) { + if (typeof value === 'function') continue; // leafHash is a function that is not reference-equal + assert.deepEqual(value, (recoveredTree as any)[key]); + } }); it('reject out of bounds value index', () => { - assert.throws(() => tree.getProof(leaves.length), /^Error: Index out of bounds$/); + assert.throws(() => tree.getProof(leaves.length), /^InvalidArgumentError: Index out of bounds$/); }); + }); + } - it('reject unrecognized tree dump', () => { - assert.throws( - () => StandardMerkleTree.load({ format: 'nonstandard' } as any), - /^Error: Unknown format 'nonstandard'$/, - ); + describe('tree dumps', () => { + it('reject unrecognized tree dump', () => { + assert.throws( + () => + StandardMerkleTree.load({ + format: 'nonstandard', + leafEncoding: ['string'], + } as any), + /^InvalidArgumentError: Unknown format 'nonstandard'$/, + ); + + assert.throws( + () => + StandardMerkleTree.load({ + format: 'simple-v1', + leafEncoding: ['string'], + } as any), + /^InvalidArgumentError: Unknown format 'simple-v1'$/, + ); + }); + + it('reject malformed tree dump', () => { + const loadedTree1 = StandardMerkleTree.load({ + format: 'standard-v1', + tree: [zero], + values: [{ value: ['0'], treeIndex: 0 }], + leafEncoding: ['uint256'], }); + assert.throws(() => loadedTree1.getProof(0), /^InvariantError: Merkle tree does not contain the expected value$/); - it('reject malformed tree dump', () => { - const loadedTree1 = StandardMerkleTree.load({ - format: 'standard-v1', - tree: [zero], - values: [{ value: ['0'], treeIndex: 0 }], - leafEncoding: ['uint256'], - }); - assert.throws(() => loadedTree1.getProof(0), /^Error: Merkle tree does not contain the expected value$/); - - const loadedTree2 = StandardMerkleTree.load({ - format: 'standard-v1', - tree: [zero, zero, hex(keccak256(keccak256(zeroBytes)))], - values: [{ value: ['0'], treeIndex: 2 }], - leafEncoding: ['uint256'], - }); - assert.throws(() => loadedTree2.getProof(0), /^Error: Unable to prove value$/); + const loadedTree2 = StandardMerkleTree.load({ + format: 'standard-v1', + tree: [zero, zero, keccak256(keccak256(zero))], + values: [{ value: ['0'], treeIndex: 2 }], + leafEncoding: ['uint256'], }); + assert.throws(() => loadedTree2.getProof(0), /^InvariantError: Unable to prove value$/); }); - } + }); }); diff --git a/src/standard.ts b/src/standard.ts index 9d069bf..e15ef77 100644 --- a/src/standard.ts +++ b/src/standard.ts @@ -1,213 +1,69 @@ -import { equalsBytes, hexToBytes } from 'ethereum-cryptography/utils'; -import { Bytes, compareBytes, hex } from './bytes'; -import { - getProof, - isValidMerkleTree, - makeMerkleTree, - processProof, - renderMerkleTree, - MultiProof, - getMultiProof, - processMultiProof, -} from './core'; -import { MerkleTreeOptions, defaultOptions } from './options'; -import { checkBounds } from './utils/check-bounds'; -import { throwError } from './utils/throw-error'; -import { standardLeafHash } from './utils/standard-leaf-hash'; - -interface StandardMerkleTreeData { +import { keccak256 } from '@ethersproject/keccak256'; +import { defaultAbiCoder } from '@ethersproject/abi'; +import { BytesLike, HexString, toHex } from './bytes'; +import { MultiProof, processProof, processMultiProof } from './core'; +import { MerkleTreeData, MerkleTreeImpl } from './merkletree'; +import { MerkleTreeOptions } from './options'; +import { validateArgument } from './utils/errors'; + +export interface StandardMerkleTreeData extends MerkleTreeData { format: 'standard-v1'; - tree: string[]; - values: { - value: T; - treeIndex: number; - }[]; leafEncoding: string[]; } -export class StandardMerkleTree { - private readonly hashLookup: { [hash: string]: number }; +export function standardLeafHash(types: string[], value: T): HexString { + return keccak256(keccak256(defaultAbiCoder.encode(types, value))); +} - private constructor( - private readonly tree: Bytes[], - private readonly values: { value: T; treeIndex: number }[], - private readonly leafEncoding: string[], +export class StandardMerkleTree extends MerkleTreeImpl { + protected constructor( + protected readonly tree: HexString[], + protected readonly values: StandardMerkleTreeData['values'], + protected readonly leafEncoding: string[], ) { - this.hashLookup = Object.fromEntries( - values.map(({ value }, valueIndex) => [hex(standardLeafHash(value, leafEncoding)), valueIndex]), - ); + super(tree, values, leaf => standardLeafHash(leafEncoding, leaf)); } - static of(values: T[], leafEncoding: string[], options: MerkleTreeOptions = {}) { - const sortLeaves = options.sortLeaves ?? defaultOptions.sortLeaves; - - const hashedValues = values.map((value, valueIndex) => ({ - value, - valueIndex, - hash: standardLeafHash(value, leafEncoding), - })); - - if (sortLeaves) { - hashedValues.sort((a, b) => compareBytes(a.hash, b.hash)); - } - - const tree = makeMerkleTree(hashedValues.map(v => v.hash)); - - const indexedValues = values.map(value => ({ value, treeIndex: 0 })); - for (const [leafIndex, { valueIndex }] of hashedValues.entries()) { - indexedValues[valueIndex]!.treeIndex = tree.length - leafIndex - 1; - } - + static of( + values: T[], + leafEncoding: string[], + options: MerkleTreeOptions = {}, + ): StandardMerkleTree { + const [tree, indexedValues] = MerkleTreeImpl.prepare(values, options, leaf => standardLeafHash(leafEncoding, leaf)); return new StandardMerkleTree(tree, indexedValues, leafEncoding); } static load(data: StandardMerkleTreeData): StandardMerkleTree { - if (data.format !== 'standard-v1') { - throw new Error(`Unknown format '${data.format}'`); - } - return new StandardMerkleTree(data.tree.map(hexToBytes), data.values, data.leafEncoding); + validateArgument(data.format === 'standard-v1', `Unknown format '${data.format}'`); + validateArgument(data.leafEncoding !== undefined, 'Expected leaf encoding'); + return new StandardMerkleTree(data.tree, data.values, data.leafEncoding); } - static verify(root: string, leafEncoding: string[], leaf: T, proof: string[]): boolean { - const impliedRoot = processProof(standardLeafHash(leaf, leafEncoding), proof.map(hexToBytes)); - return equalsBytes(impliedRoot, hexToBytes(root)); + static verify(root: BytesLike, leafEncoding: string[], leaf: T, proof: BytesLike[]): boolean { + return toHex(root) === processProof(standardLeafHash(leafEncoding, leaf), proof); } static verifyMultiProof( - root: string, + root: BytesLike, leafEncoding: string[], - multiproof: MultiProof, + multiproof: MultiProof, ): boolean { - const leafHashes = multiproof.leaves.map(leaf => standardLeafHash(leaf, leafEncoding)); - const proofBytes = multiproof.proof.map(hexToBytes); - - const impliedRoot = processMultiProof({ - leaves: leafHashes, - proof: proofBytes, - proofFlags: multiproof.proofFlags, - }); - - return equalsBytes(impliedRoot, hexToBytes(root)); + return ( + toHex(root) === + processMultiProof({ + leaves: multiproof.leaves.map(leaf => standardLeafHash(leafEncoding, leaf)), + proof: multiproof.proof, + proofFlags: multiproof.proofFlags, + }) + ); } dump(): StandardMerkleTreeData { return { format: 'standard-v1', - tree: this.tree.map(hex), - values: this.values, leafEncoding: this.leafEncoding, + tree: this.tree, + values: this.values, }; } - - render() { - return renderMerkleTree(this.tree); - } - - get root(): string { - return hex(this.tree[0]!); - } - - *entries(): Iterable<[number, T]> { - for (const [i, { value }] of this.values.entries()) { - yield [i, value]; - } - } - - validate() { - for (let i = 0; i < this.values.length; i++) { - this.validateValue(i); - } - if (!isValidMerkleTree(this.tree)) { - throw new Error('Merkle tree is invalid'); - } - } - - leafHash(leaf: T): string { - return hex(standardLeafHash(leaf, this.leafEncoding)); - } - - leafLookup(leaf: T): number { - return this.hashLookup[this.leafHash(leaf)] ?? throwError('Leaf is not in tree'); - } - - getProof(leaf: number | T): string[] { - // input validity - const valueIndex = typeof leaf === 'number' ? leaf : this.leafLookup(leaf); - this.validateValue(valueIndex); - - // rebuild tree index and generate proof - const { treeIndex } = this.values[valueIndex]!; - const proof = getProof(this.tree, treeIndex); - - // sanity check proof - if (!this._verify(this.tree[treeIndex]!, proof)) { - throw new Error('Unable to prove value'); - } - - // return proof in hex format - return proof.map(hex); - } - - getMultiProof(leaves: (number | T)[]): MultiProof { - // input validity - const valueIndices = leaves.map(leaf => (typeof leaf === 'number' ? leaf : this.leafLookup(leaf))); - for (const valueIndex of valueIndices) this.validateValue(valueIndex); - - // rebuild tree indices and generate proof - const indices = valueIndices.map(i => this.values[i]!.treeIndex); - const proof = getMultiProof(this.tree, indices); - - // sanity check proof - if (!this._verifyMultiProof(proof)) { - throw new Error('Unable to prove values'); - } - - // return multiproof in hex format - return { - leaves: proof.leaves.map(hash => this.values[this.hashLookup[hex(hash)]!]!.value), - proof: proof.proof.map(hex), - proofFlags: proof.proofFlags, - }; - } - - verify(leaf: number | T, proof: string[]): boolean { - return this._verify(this.getLeafHash(leaf), proof.map(hexToBytes)); - } - - private _verify(leafHash: Bytes, proof: Bytes[]): boolean { - const impliedRoot = processProof(leafHash, proof); - return equalsBytes(impliedRoot, this.tree[0]!); - } - - verifyMultiProof(multiproof: MultiProof): boolean { - return this._verifyMultiProof({ - leaves: multiproof.leaves.map(l => this.getLeafHash(l)), - proof: multiproof.proof.map(hexToBytes), - proofFlags: multiproof.proofFlags, - }); - } - - private _verifyMultiProof(multiproof: MultiProof): boolean { - const impliedRoot = processMultiProof(multiproof); - return equalsBytes(impliedRoot, this.tree[0]!); - } - - private validateValue(valueIndex: number): Bytes { - checkBounds(this.values, valueIndex); - const { value, treeIndex } = this.values[valueIndex]!; - checkBounds(this.tree, treeIndex); - const leaf = standardLeafHash(value, this.leafEncoding); - if (!equalsBytes(leaf, this.tree[treeIndex]!)) { - throw new Error('Merkle tree does not contain the expected value'); - } - return leaf; - } - - private getLeafHash(leaf: number | T): Bytes { - if (typeof leaf === 'number') { - return this.validateValue(leaf); - } else { - return standardLeafHash(leaf, this.leafEncoding); - } - } } diff --git a/src/utils/check-bounds.ts b/src/utils/check-bounds.ts deleted file mode 100644 index 70d3c84..0000000 --- a/src/utils/check-bounds.ts +++ /dev/null @@ -1,5 +0,0 @@ -export function checkBounds(array: unknown[], index: number) { - if (index < 0 || index >= array.length) { - throw new Error('Index out of bounds'); - } -} diff --git a/src/utils/errors.ts b/src/utils/errors.ts new file mode 100644 index 0000000..ee76786 --- /dev/null +++ b/src/utils/errors.ts @@ -0,0 +1,29 @@ +export function throwError(message?: string): never { + throw new Error(message); +} + +export class InvariantError extends Error { + constructor(message?: string) { + super(message); + this.name = 'InvariantError'; + } +} + +export class InvalidArgumentError extends Error { + constructor(message?: string) { + super(message); + this.name = 'InvalidArgumentError'; + } +} + +export function validateArgument(condition: unknown, message?: string): asserts condition { + if (!condition) { + throw new InvalidArgumentError(message); + } +} + +export function invariant(condition: unknown, message?: string): asserts condition { + if (!condition) { + throw new InvariantError(message); + } +} diff --git a/src/utils/standard-leaf-hash.ts b/src/utils/standard-leaf-hash.ts deleted file mode 100644 index df65671..0000000 --- a/src/utils/standard-leaf-hash.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { keccak256 } from 'ethereum-cryptography/keccak'; -import { hexToBytes } from 'ethereum-cryptography/utils'; -import { defaultAbiCoder } from '@ethersproject/abi'; -import { Bytes } from '../bytes'; - -export function standardLeafHash(value: T, types: string[]): Bytes { - return keccak256(keccak256(hexToBytes(defaultAbiCoder.encode(types, value)))); -} diff --git a/src/utils/throw-error.ts b/src/utils/throw-error.ts deleted file mode 100644 index 503ccf6..0000000 --- a/src/utils/throw-error.ts +++ /dev/null @@ -1,3 +0,0 @@ -export function throwError(message?: string): never { - throw new Error(message); -} diff --git a/test/dumps.test.ts b/test/dumps.test.ts index 08cb2ec..5a63381 100644 --- a/test/dumps.test.ts +++ b/test/dumps.test.ts @@ -1,7 +1,7 @@ import assert from 'assert/strict'; import fs from 'fs'; import path from 'path'; -import { StandardMerkleTree } from '../src/standard'; +import { StandardMerkleTree, SimpleMerkleTree } from '../src'; const DUMPS_DIR = 'test/dumps/'; @@ -9,8 +9,17 @@ describe('load dumped trees', () => { for (const file of fs.readdirSync(DUMPS_DIR).map(filename => path.join(DUMPS_DIR, filename))) { it(file, function () { const dump = JSON.parse(fs.readFileSync(file, 'utf-8')); - const tree = StandardMerkleTree.load(dump); - tree.validate(); + + switch (dump.format) { + case 'standard-v1': + StandardMerkleTree.load(dump).validate(); + break; + case 'simple-v1': + SimpleMerkleTree.load(dump).validate(); + break; + default: + assert.fail(`Unknown format '${dump.format}'`); + } }); } }); diff --git a/test/dumps/simple-v1.sorted.1_0_6.json b/test/dumps/simple-v1.sorted.1_0_6.json new file mode 100644 index 0000000..db6b897 --- /dev/null +++ b/test/dumps/simple-v1.sorted.1_0_6.json @@ -0,0 +1 @@ +{"format":"simple-v1","tree":["0x1b404f199ea828ec5771fb30139c222d8417a82175fefad5cd42bc3a189bd8d5","0xec554bdfb01d31fa838d0830339b0e6e8a70e0d55a8f172ffa8bebbf8e8d5ba0","0xaf46af0745b433e1d5bed9a04b1fdf4002f67a733c20db2fca5b2af6120d9bcb","0x434d51cfeb80272378f4c3a8fd2824561c2cad9fce556ea600d46f20550976a6","0x7dea550f679f3caab547cbbc5ee1a4c978c8c039b572ba00af1baa6481b88360","0xf1918e8562236eb17adc8502332f4c9c82bc14e19bfc0aa10ab674ff75b3d2f3","0xd1e8aeb79500496ef3dc2e57ba746a8315d048b7a664a2bf948db4fa91960483","0xb5553de315e0edf504d9150af82dafa5c4667fa618ed0a6f19c69b41166c5510","0xa8982c89d80987fb9a510e25981ee9170206be21af3c8e0eb312ef1d3382e761","0x3ac225168df54212a25c1c01fd35bebfea408fdac2e31ddd6f80a4bbf9a5f1cb","0x0b42b6393c1f53060fe3ddbfcd7aadcca894465a5a438f69c87d790b2299b9b2"],"values":[{"value":"0x3ac225168df54212a25c1c01fd35bebfea408fdac2e31ddd6f80a4bbf9a5f1cb","treeIndex":9},{"value":"0xb5553de315e0edf504d9150af82dafa5c4667fa618ed0a6f19c69b41166c5510","treeIndex":7},{"value":"0x0b42b6393c1f53060fe3ddbfcd7aadcca894465a5a438f69c87d790b2299b9b2","treeIndex":10},{"value":"0xf1918e8562236eb17adc8502332f4c9c82bc14e19bfc0aa10ab674ff75b3d2f3","treeIndex":5},{"value":"0xa8982c89d80987fb9a510e25981ee9170206be21af3c8e0eb312ef1d3382e761","treeIndex":8},{"value":"0xd1e8aeb79500496ef3dc2e57ba746a8315d048b7a664a2bf948db4fa91960483","treeIndex":6}]} diff --git a/test/dumps/simple-v1.unsorted.1_0_6.json b/test/dumps/simple-v1.unsorted.1_0_6.json new file mode 100644 index 0000000..507cb32 --- /dev/null +++ b/test/dumps/simple-v1.unsorted.1_0_6.json @@ -0,0 +1 @@ +{"format":"simple-v1","tree":["0x9012f1e18a87790d2e01faace75aaaca38e53df437cdce2c0552464dda4af49c","0x68203f90e9d07dc5859259d7536e87a6ba9d345f2552b5b9de2999ddce9ce1bf","0xf0b49bb4b0d9396e0315755ceafaa280707b32e75e6c9053f5cdf2679dcd5c6a","0xd253a52d4cb00de2895e85f2529e2976e6aaaa5c18106b68ab66813e14415669","0x805b21d846b189efaeb0377d6bb0d201b3872a363e607c25088f025b0c6ae1f8","0xd1e8aeb79500496ef3dc2e57ba746a8315d048b7a664a2bf948db4fa91960483","0xa8982c89d80987fb9a510e25981ee9170206be21af3c8e0eb312ef1d3382e761","0xf1918e8562236eb17adc8502332f4c9c82bc14e19bfc0aa10ab674ff75b3d2f3","0x0b42b6393c1f53060fe3ddbfcd7aadcca894465a5a438f69c87d790b2299b9b2","0xb5553de315e0edf504d9150af82dafa5c4667fa618ed0a6f19c69b41166c5510","0x3ac225168df54212a25c1c01fd35bebfea408fdac2e31ddd6f80a4bbf9a5f1cb"],"values":[{"value":"0x3ac225168df54212a25c1c01fd35bebfea408fdac2e31ddd6f80a4bbf9a5f1cb","treeIndex":10},{"value":"0xb5553de315e0edf504d9150af82dafa5c4667fa618ed0a6f19c69b41166c5510","treeIndex":9},{"value":"0x0b42b6393c1f53060fe3ddbfcd7aadcca894465a5a438f69c87d790b2299b9b2","treeIndex":8},{"value":"0xf1918e8562236eb17adc8502332f4c9c82bc14e19bfc0aa10ab674ff75b3d2f3","treeIndex":7},{"value":"0xa8982c89d80987fb9a510e25981ee9170206be21af3c8e0eb312ef1d3382e761","treeIndex":6},{"value":"0xd1e8aeb79500496ef3dc2e57ba746a8315d048b7a664a2bf948db4fa91960483","treeIndex":5}]} diff --git a/test/dumps/standard-v1.1_0_6.json b/test/dumps/standard-v1.1_0_6.json deleted file mode 100644 index 223302e..0000000 --- a/test/dumps/standard-v1.1_0_6.json +++ /dev/null @@ -1 +0,0 @@ -{"format":"standard-v1","tree":["0x23be0977360f08bb0bd7f709a7d543d2cd779c79c66d74e0441919871647de2b","0x8f7234e8cfe39c08ca84a3a3e3274f574af26fd15165fe29e09cbab742daccd9","0x7b0c6cd04b82bfc0e250030a5d2690c52585e0cc6a4f3bc7909d7723b0236ece","0x03707d7802a71ca56a8ad8028da98c4f1dbec55b31b4a25d536b5309cc20eda9","0xfa914d99a18dc32d9725b3ef1c50426deb40ec8d0885dac8edcc5bfd6d030016","0xc62a8cfa41edc0ef6f6ae27a2985b7d39c7fea770787d7e104696c6e81f64848","0x9a4f64e953595df82d1b4f570d34c4f4f0cfaf729a61e9d60e83e579e1aa283e","0xeba909cf4bb90c6922771d7f126ad0fd11dfde93f3937a196274e1ac20fd2f5b","0x9cf5a63718145ba968a01c1d557020181c5b252f665cf7386d370eddb176517b","0x19ba6c6333e0e9a15bf67523e0676e2f23eb8e574092552d5e888c64a4bb3681","0x9c15a6a0eaeed500fd9eed4cbeab71f797cefcc67bfd46683e4d2e6ff7f06d1c"],"values":[{"value":["a"],"treeIndex":10},{"value":["b"],"treeIndex":9},{"value":["c"],"treeIndex":8},{"value":["d"],"treeIndex":7},{"value":["e"],"treeIndex":6},{"value":["f"],"treeIndex":5}],"leafEncoding":["string"]} diff --git a/test/dumps/standard-v1.sorted.1_0_6.json b/test/dumps/standard-v1.sorted.1_0_6.json new file mode 100644 index 0000000..18091c2 --- /dev/null +++ b/test/dumps/standard-v1.sorted.1_0_6.json @@ -0,0 +1 @@ +{"format":"standard-v1","leafEncoding":["string"],"tree":["0x6deb52b5da8fd108f79fab00341f38d2587896634c646ee52e49f845680a70c8","0x52426e0f1f65ff7e209a13b8c29cffe82e3acaf3dad0a9b9088f3b9a61a929c3","0xfd3cf45654e88d1cc5d663578c82c76f4b5e3826bacaa1216441443504538f51","0x8076923e76cf01a7c048400a2304c9a9c23bbbdac3a98ea3946340fdafbba34f","0x965b92c6cf08303cc4feb7f3e0819c436c2cec17c6f0688a6af139c9a368707c","0xeba909cf4bb90c6922771d7f126ad0fd11dfde93f3937a196274e1ac20fd2f5b","0xc62a8cfa41edc0ef6f6ae27a2985b7d39c7fea770787d7e104696c6e81f64848","0x9cf5a63718145ba968a01c1d557020181c5b252f665cf7386d370eddb176517b","0x9c15a6a0eaeed500fd9eed4cbeab71f797cefcc67bfd46683e4d2e6ff7f06d1c","0x9a4f64e953595df82d1b4f570d34c4f4f0cfaf729a61e9d60e83e579e1aa283e","0x19ba6c6333e0e9a15bf67523e0676e2f23eb8e574092552d5e888c64a4bb3681"],"values":[{"value":["a"],"treeIndex":8},{"value":["b"],"treeIndex":10},{"value":["c"],"treeIndex":7},{"value":["d"],"treeIndex":5},{"value":["e"],"treeIndex":9},{"value":["f"],"treeIndex":6}]} diff --git a/test/dumps/standard-v1.unsorted.1_0_6.json b/test/dumps/standard-v1.unsorted.1_0_6.json new file mode 100644 index 0000000..602a600 --- /dev/null +++ b/test/dumps/standard-v1.unsorted.1_0_6.json @@ -0,0 +1 @@ +{"format":"standard-v1","leafEncoding":["string"],"tree":["0x23be0977360f08bb0bd7f709a7d543d2cd779c79c66d74e0441919871647de2b","0x8f7234e8cfe39c08ca84a3a3e3274f574af26fd15165fe29e09cbab742daccd9","0x7b0c6cd04b82bfc0e250030a5d2690c52585e0cc6a4f3bc7909d7723b0236ece","0x03707d7802a71ca56a8ad8028da98c4f1dbec55b31b4a25d536b5309cc20eda9","0xfa914d99a18dc32d9725b3ef1c50426deb40ec8d0885dac8edcc5bfd6d030016","0xc62a8cfa41edc0ef6f6ae27a2985b7d39c7fea770787d7e104696c6e81f64848","0x9a4f64e953595df82d1b4f570d34c4f4f0cfaf729a61e9d60e83e579e1aa283e","0xeba909cf4bb90c6922771d7f126ad0fd11dfde93f3937a196274e1ac20fd2f5b","0x9cf5a63718145ba968a01c1d557020181c5b252f665cf7386d370eddb176517b","0x19ba6c6333e0e9a15bf67523e0676e2f23eb8e574092552d5e888c64a4bb3681","0x9c15a6a0eaeed500fd9eed4cbeab71f797cefcc67bfd46683e4d2e6ff7f06d1c"],"values":[{"value":["a"],"treeIndex":10},{"value":["b"],"treeIndex":9},{"value":["c"],"treeIndex":8},{"value":["d"],"treeIndex":7},{"value":["e"],"treeIndex":6},{"value":["f"],"treeIndex":5}]}