Browse Source

working with some errors yet

feature/synctests2
Jordi Baylina 5 years ago
parent
commit
ccaa7ff23b
No known key found for this signature in database GPG Key ID: 7480C80C1BE43112
11 changed files with 24595 additions and 146 deletions
  1. +31
    -5
      circuits/smt/smtinsert.circom
  2. +5
    -2
      circuits/smt/smtinsertlevel.circom
  3. +39
    -30
      circuits/smt/smtinsertsm.circom
  4. +2
    -1
      circuits/smt/smtlevins.circom
  5. +54
    -34
      package-lock.json
  6. +14
    -15
      src/mimc7.js
  7. +127
    -13
      src/smt.js
  8. +28
    -9
      src/smt_memdb.js
  9. +23994
    -0
      test.sig
  10. +138
    -37
      test/smt.js
  11. +163
    -0
      test/smtjs.js

+ 31
- 5
circuits/smt/smtinsert.circom

@ -94,6 +94,15 @@ Insert to a used leaf.
na ┃ Hash ┃ ┃ Hash ┃
┗━━━━━━━┛ ┗━━━━━━━┛
Fnction
fnc[0] fnc[1]
0 0 NOP
0 1 UPDATE
1 0 INSERT
1 1 DELETE
***************************************************************************************************/
include "../gates.circom";
@ -114,6 +123,11 @@ template SMTInsert(nLevels) {
signal input isOld0;
signal input newKey;
signal input newValue;
signal input fnc[2];
signal enabled;
enabled <== fnc[0] + fnc[1] - fnc[0]*fnc[1]
component hash1Old = SMTHash1();
hash1Old.key <== oldKey;
@ -131,6 +145,7 @@ template SMTInsert(nLevels) {
component smtLevIns = SMTLevIns(nLevels);
for (var i=0; i<nLevels; i++) smtLevIns.siblings[i] <== siblings[i];
smtLevIns.enabled <== enabled;
component xors[nLevels];
for (var i=0; i<nLevels; i++) {
@ -143,12 +158,13 @@ template SMTInsert(nLevels) {
for (var i=0; i<nLevels; i++) {
sm[i] = SMTInsertSM();
if (i==0) {
sm[i].prev_top <== 1;
sm[i].prev_top <== enabled;
sm[i].prev_old1 <== 0;
sm[i].prev_old0 <== 0;
sm[i].prev_bot <== 0;
sm[i].prev_new1 <== 0;
sm[i].prev_na <== 0;
sm[i].prev_na <== 1-enabled;
sm[i].prev_upd <== 0;
} else {
sm[i].prev_top <== sm[i-1].st_top;
sm[i].prev_old1 <== sm[i-1].st_old1;
@ -156,9 +172,12 @@ template SMTInsert(nLevels) {
sm[i].prev_bot <== sm[i-1].st_bot;
sm[i].prev_new1 <== sm[i-1].st_new1;
sm[i].prev_na <== sm[i-1].st_na;
sm[i].prev_upd <== sm[i-1].st_upd;
}
sm[i].is0 <== isOld0;
sm[i].xor <== xors[i].out;
sm[i].fnc[0] <== fnc[0];
sm[i].fnc[1] <== fnc[1];
sm[i].levIns <== smtLevIns.levIns[i];
}
sm[nLevels-1].st_na === 1;
@ -172,7 +191,8 @@ template SMTInsert(nLevels) {
levels[i].st_old0 <== sm[i].st_old0;
levels[i].st_bot <== sm[i].st_bot;
levels[i].st_new1 <== sm[i].st_new1;
levels[i].st_na <==sm[i].st_na;
levels[i].st_na <== sm[i].st_na;
levels[i].st_upd <== sm[i].st_upd;
levels[i].sibling <== siblings[i];
levels[i].old1leaf <== hash1Old.out;
@ -188,6 +208,12 @@ template SMTInsert(nLevels) {
}
}
levels[0].oldRoot === oldRoot;
levels[0].newRoot === newRoot;
component topSwitcher = Switcher();
topSwitcher.sel <== fnc[0]*fnc[1];
topSwitcher.L <== levels[0].oldRoot;
topSwitcher.R <== levels[0].newRoot;
topSwitcher.outL === oldRoot*enabled;
topSwitcher.outR === newRoot*enabled;
}

+ 5
- 2
circuits/smt/smtinsertlevel.circom

@ -17,6 +17,8 @@ bot 0 H'(newChild, 0)
new1 0 H'(new1leaf, old1leaf)
na 0 0
upd old1leaf new1leaf
H' is the Hash function with the inputs shifted acordingly.
*****/
@ -29,6 +31,7 @@ template SMTInsertLevel() {
signal input st_bot;
signal input st_new1;
signal input st_na;
signal input st_upd;
signal output oldRoot;
signal output newRoot;
@ -56,7 +59,7 @@ template SMTInsertLevel() {
oldProofHash.L <== oldSwitcher.outL;
oldProofHash.R <== oldSwitcher.outR;
aux[0] <== old1leaf * st_old1;
aux[0] <== old1leaf * (st_old1 + st_upd);
oldRoot <== aux[0] + oldProofHash.out * st_top;
// New side
@ -72,5 +75,5 @@ template SMTInsertLevel() {
newProofHash.R <== newSwitcher.outR;
aux[3] <== newProofHash.out * (st_top + st_old1 + st_bot + st_new1);
newRoot <== aux[3] + new1leaf * st_old0;
newRoot <== aux[3] + new1leaf * (st_old0 + st_upd);
}

+ 39
- 30
circuits/smt/smtinsertsm.circom

@ -33,33 +33,43 @@ fnc[0] fnc[1]
###########
levIns=1 # #
levIns=0 is0=1 ┌────────────▶# old0 #────────┐ any
┌─────┐ │ ## ## │ ┌──────┐
│ │ │ ######### │ any │ │
│ ▼ │ │ ▼ │
│ ########### │ │ ########### │
│ # # ────────────┘ └────────▶# #│
└──# top # # na #
## ## ────┐ ┌──▶## ##
######### │ │ #########
│ │
│ ########### ########### │ any
levIns=1 │ # # xor=1 # # │
is0=0 └───▶# old1 #─────────────▶# new1 #──┘
## ## ## ##
#########│ #########
│ ▲
└───┐ ┌─────┘
xor=0 │ ###########│ xor=1
│ # #
▼# btn #
## ##
#########◀───────┐
│ │
│ │
└────────────┘
xor=0
# #
┌────────────────────────────▶# upd #─────────────────────┐
│ ## ## │
│ ######### │
levIns=1 │ │
fnc[0]=0 │ │ any
│ │
│ │
│ │
│ ########### │
│ levIns=1 # # │
levIns=0 │ is0=1 ┌────────────▶# old0 #────────┐ │ any
┌─────┐ │ fnc[0]=1│ ## ## │ │ ┌──────┐
│ │ │ │ ######### │ any │ │ │
│ ▼ │ │ │ ▼ ▼ │
│ ########### │ │ ########### │
│ # # ────────────┘ └────────▶# #│
└──# top # # na #
## ## ────┐ ┌──▶## ##
######### │ │ #########
│ │
│ ########### ########### │ any
levIns=1 │ # # xor=1 # # │
is0=0 └───▶# old1 #─────────────▶# new1 #──┘
fnc[0]=1 ## ## ## ##
#########│ #########
│ ▲
└───┐ ┌─────┘
xor=0 │ ###########│ xor=1
│ # #
▼# btn #
## ##
#########◀───────┐
│ │
│ │
└────────────┘
xor=0
***************************************************************************************************/
@ -67,8 +77,7 @@ template SMTInsertSM() {
signal input xor;
signal input is0;
signal input levIns;
signal input fnc[0];
signal input fnc[1];
signal input fnc[2];
signal input prev_top;
signal input prev_old0;
@ -102,7 +111,7 @@ template SMTInsertSM() {
st_bot <== -st_new1 + prev_old1 + prev_bot // prev_old1*(1-xor) + prev_bot*(1-xor) =
// = - prev_old1*xor -prev_bot*xor + prev_old1 + prev_bot =
// = -st_new1 + prev_old1 + prev_bot
st_na <== prev_new1 + prev_old0 + prev_na + st_upd;
st_na <== prev_new1 + prev_old0 + prev_na + prev_upd;
st_upd <== aux1*(1-fnc[0]); // prev_top*levIns*(1-fnc[0]) =
// = aux1 * (1-fnc[0])

+ 2
- 1
circuits/smt/smtlevins.circom

@ -55,6 +55,7 @@ a parent with a sibling != 0.
*/
template SMTLevIns(nLevels) {
signal input enabled;
signal input siblings[nLevels];
signal output levIns[nLevels];
signal done[nLevels-1]; // Indicates if the insLevel has aready been detecetd.
@ -67,7 +68,7 @@ template SMTLevIns(nLevels) {
}
// The last level must always have a sibling of 0. If not, then it cannot be inserted.
isZero[nLevels-2].out === 1;
(isZero[nLevels-2].out - 1) * enabled === 0;
levIns[nLevels-1] <== (1-isZero[nLevels-2].out);
done[nLevels-2] <== levIns[nLevels-1];

+ 54
- 34
package-lock.json

@ -2446,7 +2446,8 @@
"bindings": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.3.0.tgz",
"integrity": "sha512-DpLh5EzMR2kzvX1KIlVC0VkC3iZtHKTgdtZ0a3pglBZdaQFjt5S9g9xd1lE+YvXyfd6mtCeRnrUfOLYiTMlNSw=="
"integrity": "sha512-DpLh5EzMR2kzvX1KIlVC0VkC3iZtHKTgdtZ0a3pglBZdaQFjt5S9g9xd1lE+YvXyfd6mtCeRnrUfOLYiTMlNSw==",
"dev": true
},
"bip39": {
"version": "2.5.0",
@ -2465,6 +2466,7 @@
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/bip66/-/bip66-1.1.5.tgz",
"integrity": "sha1-AfqHSHhcpwlV1QESF9GzE5lpyiI=",
"dev": true,
"requires": {
"safe-buffer": "^5.0.1"
}
@ -2499,7 +2501,8 @@
"bn.js": {
"version": "4.11.8",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz",
"integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA=="
"integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==",
"dev": true
},
"body-parser": {
"version": "1.18.3",
@ -2542,12 +2545,14 @@
"brorand": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
"integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8="
"integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=",
"dev": true
},
"browserify-aes": {
"version": "1.2.0",
"resolved": "http://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
"integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==",
"dev": true,
"requires": {
"buffer-xor": "^1.0.3",
"cipher-base": "^1.0.0",
@ -2704,7 +2709,8 @@
"buffer-xor": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz",
"integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk="
"integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=",
"dev": true
},
"builtin-modules": {
"version": "1.1.1",
@ -2802,6 +2808,7 @@
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
"integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==",
"dev": true,
"requires": {
"inherits": "^2.0.1",
"safe-buffer": "^5.0.1"
@ -2961,6 +2968,7 @@
"version": "1.2.0",
"resolved": "http://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
"integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==",
"dev": true,
"requires": {
"cipher-base": "^1.0.1",
"inherits": "^2.0.1",
@ -2973,6 +2981,7 @@
"version": "1.1.7",
"resolved": "http://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
"integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==",
"dev": true,
"requires": {
"cipher-base": "^1.0.3",
"create-hash": "^1.1.0",
@ -3272,6 +3281,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/drbg.js/-/drbg.js-1.0.1.tgz",
"integrity": "sha1-Pja2xCs3BDgjzbwzLVjzHiRFSAs=",
"dev": true,
"requires": {
"browserify-aes": "^1.0.6",
"create-hash": "^1.1.2",
@ -3310,6 +3320,7 @@
"version": "6.4.1",
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz",
"integrity": "sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==",
"dev": true,
"requires": {
"bn.js": "^4.4.0",
"brorand": "^1.0.1",
@ -3600,17 +3611,8 @@
"integrity": "sha1-jZWCAsftuq6Dlwf7pvCf8ydgYhA=",
"dev": true,
"requires": {
"ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git#2863c40e0982acfc0b7163f0285d4c56427c7799",
"ethereumjs-util": "^5.1.1"
},
"dependencies": {
"ethereumjs-abi": {
"version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#2863c40e0982acfc0b7163f0285d4c56427c7799",
"from": "git+https://github.com/ethereumjs/ethereumjs-abi.git#2863c40e0982acfc0b7163f0285d4c56427c7799",
"requires": {
"bn.js": "^4.10.0",
"ethereumjs-util": "^5.0.0"
}
}
}
},
"ethereum-common": {
@ -3622,6 +3624,7 @@
"ethereumjs-abi": {
"version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#2863c40e0982acfc0b7163f0285d4c56427c7799",
"from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
"dev": true,
"requires": {
"bn.js": "^4.10.0",
"ethereumjs-util": "^5.0.0"
@ -3816,6 +3819,7 @@
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.0.tgz",
"integrity": "sha512-CJAKdI0wgMbQFLlLRtZKGcy/L6pzVRgelIZqRqNbuVFM3K9VEnyfbcvz0ncWMRNCe4kaHWjwRYQcYMucmwsnWA==",
"dev": true,
"requires": {
"bn.js": "^4.11.0",
"create-hash": "^1.1.2",
@ -3905,6 +3909,7 @@
"version": "0.1.6",
"resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.6.tgz",
"integrity": "sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w==",
"dev": true,
"requires": {
"is-hex-prefixed": "1.0.0",
"strip-hex-prefix": "1.0.0"
@ -3926,6 +3931,7 @@
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz",
"integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==",
"dev": true,
"requires": {
"md5.js": "^1.3.4",
"safe-buffer": "^5.1.1"
@ -4336,6 +4342,7 @@
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz",
"integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=",
"dev": true,
"requires": {
"inherits": "^2.0.1",
"safe-buffer": "^5.0.1"
@ -4345,6 +4352,7 @@
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.5.tgz",
"integrity": "sha512-eWI5HG9Np+eHV1KQhisXWwM+4EPPYe5dFX1UZZH7k/E3JzDEazVH+VGlZi6R94ZqImq+A3D1mCEtrFIfg/E7sA==",
"dev": true,
"requires": {
"inherits": "^2.0.3",
"minimalistic-assert": "^1.0.1"
@ -4372,6 +4380,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
"integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=",
"dev": true,
"requires": {
"hash.js": "^1.0.3",
"minimalistic-assert": "^1.0.0",
@ -4539,7 +4548,8 @@
"is-hex-prefixed": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz",
"integrity": "sha1-fY035q135dEnFIkTxXPggtd39VQ="
"integrity": "sha1-fY035q135dEnFIkTxXPggtd39VQ=",
"dev": true
},
"is-natural-number": {
"version": "4.0.1",
@ -4593,7 +4603,8 @@
"is-typedarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
"integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
"integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
"dev": true
},
"is-utf8": {
"version": "0.2.1",
@ -4739,6 +4750,7 @@
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/keccak/-/keccak-1.4.0.tgz",
"integrity": "sha512-eZVaCpblK5formjPjeTBik7TAg+pqnDrMHIffSvi9Lh7PQgM1+hSzakUeZFCk9DVVG0dacZJuaz2ntwlzZUIBw==",
"dev": true,
"requires": {
"bindings": "^1.2.1",
"inherits": "^2.0.3",
@ -5038,6 +5050,7 @@
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
"integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==",
"dev": true,
"requires": {
"hash-base": "^3.0.0",
"inherits": "^2.0.1",
@ -5197,12 +5210,14 @@
"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=="
"integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==",
"dev": true
},
"minimalistic-crypto-utils": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
"integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo="
"integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=",
"dev": true
},
"minimatch": {
"version": "3.0.4",
@ -5254,7 +5269,8 @@
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
"dev": true
},
"mz": {
"version": "2.7.0",
@ -5271,7 +5287,8 @@
"nan": {
"version": "2.10.0",
"resolved": "http://registry.npmjs.org/nan/-/nan-2.10.0.tgz",
"integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA=="
"integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==",
"dev": true
},
"nano-json-stream-parser": {
"version": "0.1.2",
@ -5938,6 +5955,7 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz",
"integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==",
"dev": true,
"requires": {
"hash-base": "^3.0.0",
"inherits": "^2.0.1"
@ -5947,6 +5965,7 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/rlp/-/rlp-2.1.0.tgz",
"integrity": "sha512-93U7IKH5j7nmXFVg19MeNBGzQW5uXW1pmCuKY8veeKIhYTE32C2d0mOegfiIAfXcHOKJjjPlJisn8iHDF5AezA==",
"dev": true,
"requires": {
"safe-buffer": "^5.1.1"
}
@ -5960,7 +5979,8 @@
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
"dev": true
},
"safe-event-emitter": {
"version": "1.0.1",
@ -6012,6 +6032,7 @@
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-3.5.2.tgz",
"integrity": "sha512-iin3kojdybY6NArd+UFsoTuapOF7bnJNf2UbcWXaY3z+E1sJDipl60vtzB5hbO/uquBu7z0fd4VC4Irp+xoFVQ==",
"dev": true,
"requires": {
"bindings": "^1.2.1",
"bip66": "^1.1.3",
@ -6143,6 +6164,7 @@
"version": "2.4.11",
"resolved": "http://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
"integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
"dev": true,
"requires": {
"inherits": "^2.0.1",
"safe-buffer": "^5.0.1"
@ -6353,6 +6375,7 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz",
"integrity": "sha1-DF8VX+8RUTczd96du1iNoFUA428=",
"dev": true,
"requires": {
"is-hex-prefixed": "1.0.0"
}
@ -6596,6 +6619,7 @@
"version": "3.1.5",
"resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz",
"integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==",
"dev": true,
"requires": {
"is-typedarray": "^1.0.0"
}
@ -7038,22 +7062,14 @@
"integrity": "sha1-jZWCAsftuq6Dlwf7pvCf8ydgYhA=",
"dev": true,
"requires": {
"ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git#2863c40e0982acfc0b7163f0285d4c56427c7799",
"ethereumjs-util": "^5.1.1"
},
"dependencies": {
"ethereumjs-abi": {
"version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#2863c40e0982acfc0b7163f0285d4c56427c7799",
"from": "git+https://github.com/ethereumjs/ethereumjs-abi.git#2863c40e0982acfc0b7163f0285d4c56427c7799",
"requires": {
"bn.js": "^4.10.0",
"ethereumjs-util": "^5.0.0"
}
}
}
},
"ethereumjs-abi": {
"version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#2863c40e0982acfc0b7163f0285d4c56427c7799",
"from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
"dev": true,
"requires": {
"bn.js": "^4.10.0",
"ethereumjs-util": "^5.0.0"
@ -7098,20 +7114,23 @@
"dev": true,
"requires": {
"underscore": "1.8.3",
"web3-core-helpers": "1.0.0-beta.35"
"web3-core-helpers": "1.0.0-beta.35",
"websocket": "git://github.com/frozeman/WebSocket-Node.git#6c72925e3f8aaaea8dc8450f97627e85263999f2"
},
"dependencies": {
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dev": true,
"requires": {
"ms": "2.0.0"
}
},
"websocket": {
"version": "git://github.com/frozeman/WebSocket-Node.git#6c72925e3f8aaaea8dc8450f97627e85263999f2",
"from": "git://github.com/frozeman/WebSocket-Node.git#6c72925e3f8aaaea8dc8450f97627e85263999f2",
"from": "git://github.com/frozeman/WebSocket-Node.git#browserifyCompatible",
"dev": true,
"requires": {
"debug": "^2.2.0",
"nan": "^2.3.3",
@ -7290,7 +7309,8 @@
"yaeti": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz",
"integrity": "sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc="
"integrity": "sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc=",
"dev": true
},
"yargs": {
"version": "4.8.1",

+ 14
- 15
src/mimc7.js

@ -3,35 +3,34 @@ const bigInt = require("snarkjs").bigInt;
const Web3 = require("web3");
const F = bn128.Fr;
module.exports.hash = MiMC7Hash;
module.exports.getConstants = getConstants;
const SEED = "iden3_mimc";
const nRounds = 91;
function getConstants(seed, nRounds) {
exports.getConstants = (seed, nRounds) => {
const cts = new Array(nRounds);
let c = Web3.utils.keccak256(SEED);
for (let i=1; i<nRounds; i++) {
c = Web3.utils.keccak256(c);
const n1 = Web3.utils.toBN(c).mod(Web3.utils.toBN(F.q.toString()));
cts[i] = Web3.utils.padLeft(Web3.utils.toHex(n1), 64);
const c2 = Web3.utils.padLeft(Web3.utils.toHex(n1), 64);
cts[i] = bigInt(Web3.utils.toBN(c2).toString());
}
cts[0] = "0x0000000000000000000000000000000000000000000000000000000000000000";
cts[0] = bigInt(0);
return cts;
}
};
const cts = exports.getConstants(SEED, 91);
function MiMC7Hash(_x_in, _k, nRounds) {
exports.hash = (_x_in, _k) =>{
const x_in = bigInt(_x_in);
const k = bigInt(_k);
const cts = getConstants(SEED, nRounds);
let r;
for (let i=0; i<nRounds; i++) {
const c = bigInt(Web3.utils.toBN(cts[i]).toString());
let t = (i==0) ? F.add(x_in, k) : F.add(F.add(r, k), c);
let t2 = F.square(t);
let t4 = F.square(t2);
r = F.mul(F.mul(t4, t2), t);
const c = cts[i];
const t = (i==0) ? F.add(x_in, k) : F.add(F.add(r, k), c);
r = F.exp(t, 7);
}
return F.affine(F.add(r, k));
}
};

+ 127
- 13
src/smt.js

@ -1,12 +1,12 @@
const bigInt = require("snarkjs").bigInt;
const SMTMemDB = require("./smt_memdb");
const mimc7 = require("./mimc7");
const mimc7 = require("./mimc7").hash;
function smtHash(arr) {
let r = bigInt(0);
for (let i=0; i<arr.length; i++) {
r = mimc7.hash(r, bigInt(arr[i]), 91 );
r = mimc7(r, bigInt(arr[i]));
}
return r;
}
@ -37,6 +37,92 @@ class SMT {
return res;
}
async delete(_key) {
const key = bigInt(_key);
const resFind = await this.find(key);
if (!resFind.found) throw new Error("Key does not exists");
const res = {
sibblings: [],
delKey: key,
delValue: resFind.foundValue
};
const dels = [];
const ins = [];
let rtOld = smtHash([1, key, resFind.foundValue]);
let rtNew;
dels.push(rtOld);
let mixed;
if (resFind.sibblings.length > 0) {
const record = await this.db.get(resFind.sibblings[resFind.sibblings.length - 1]);
if ((record.length == 3)&&(record[0].equals(bigInt.one))) {
mixed = false;
res.oldKey = record[1];
res.oldValue = record[2];
res.isOld0 = false;
rtNew = resFind.sibblings[resFind.sibblings.length - 1];
} else if (record.length == 2) {
mixed = true;
res.oldKey = key;
res.oldValue = bigInt(0);
res.isOld0 = true;
rtNew = bigInt.zero;
} else {
throw new Error("Invalid node. Database corrupted");
}
} else {
rtNew = bigInt.zero;
res.oldKey = key;
res.oldValue = bigInt(0);
res.isOld0 = true;
}
const keyBits = this._splitBits(key);
for (let level = resFind.sibblings.length-1; level >=0; level--) {
let newSibling = resFind.sibblings[level];
if ((level == resFind.sibblings.length-1)&&(!res.isOld0)) {
newSibling = bigInt.zero;
}
const oldSibling = resFind.sibblings[level];
if (keyBits[level]) {
rtOld = smtHash([oldSibling, rtOld]);
} else {
rtOld = smtHash([rtOld, oldSibling]);
}
dels.push(rtOld);
if (!newSibling.isZero()) {
mixed = true;
}
if (mixed) {
res.sibblings.unshift(resFind.sibblings[level]);
let newNode;
if (keyBits[level]) {
newNode = [newSibling, rtNew];
} else {
newNode = [rtNew, newSibling];
}
rtNew = smtHash(newNode);
ins.push([rtNew, newNode]);
}
}
await this.db.multiIns(ins);
await this.db.setRoot(rtNew);
this.root = rtNew;
await this.db.multiDel(dels);
res.newRoot = rtNew;
res.oldRoot = rtOld;
return res;
}
async insert(_key, _value) {
const key = bigInt(_key);
const value = bigInt(_value);
@ -44,34 +130,60 @@ class SMT {
const res = {};
res.oldRoot = this.root;
const newKeyBits = this._splitBits(key);
let rtOld;
const resFind = await this.find(key);
if (resFind.found) throw new Error("Key already exists");
res.sibblings = resFind.sibblings;
let mixed;
if (!resFind.isOld0) {
const oldKeyits = this._splitBits(resFind.notFoundKey);
for (let i= res.sibblings.length; oldKeyits[i] == newKeyBits[i]; i++) {
res.sibblings.push(bigInt.zero);
}
res.sibblings.push(smtHash([1, resFind.notFoundKey, resFind.notFoundValue]));
rtOld = smtHash([1, resFind.notFoundKey, resFind.notFoundValue]);
res.sibblings.push(rtOld);
addedOne = true;
mixed = false;
} else if (res.sibblings.length >0) {
mixed = true;
rtOld = bigInt.zero;
}
const inserts = [];
const dels = [];
let rt = smtHash([1, key, value]);
inserts.push([rt,[1, key, value]] );
for (let i=res.sibblings.length-1; i>=0; i--) {
if ((i<res.sibblings.length-1)&&(!res.sibblings[i].isZero())) {
mixed = true;
}
if (mixed) {
const oldSibling = resFind.sibblings[i];
if (newKeyBits[i]) {
rtOld = smtHash([oldSibling, rtOld]);
} else {
rtOld = smtHash([rtOld, oldSibling]);
}
dels.push(rtOld);
}
let newRt;
if (newKeyBits[i]) {
rt = smtHash([res.sibblings[i], rt]);
inserts.push([rt,[res.sibblings[i], rt]] );
newRt = smtHash([res.sibblings[i], rt]);
inserts.push([newRt,[res.sibblings[i], rt]] );
} else {
rt = smtHash([rt, res.sibblings[i]]);
inserts.push([rt,[rt, res.sibblings[i]]] );
newRt = smtHash([rt, res.sibblings[i]]);
inserts.push([newRt,[rt, res.sibblings[i]]] );
}
rt = newRt;
}
if (addedOne) res.sibblings.pop();
@ -83,9 +195,11 @@ class SMT {
res.newRoot = rt;
res.isOld0 = resFind.isOld0;
this.root = rt;
await this.db.save(rt, inserts);
await this.db.multiIns(inserts);
await this.db.setRoot(rt);
this.root = rt;
await this.db.multiDel(dels);
return res;
}
@ -131,11 +245,11 @@ class SMT {
}
} else {
if (keyBits[level] == 0) {
const res = await this.find(key, keyBits, record[1], level+1);
res.sibblings.unshift(record[2]);
} else {
res = await this.find(key, keyBits, record[2], level+1);
res = await this._find(key, keyBits, record[0], level+1);
res.sibblings.unshift(record[1]);
} else {
res = await this._find(key, keyBits, record[1], level+1);
res.sibblings.unshift(record[0]);
}
}
return res;

+ 28
- 9
src/smt_memdb.js

@ -10,21 +10,40 @@ class SMTMemDb {
return this.root;
}
async get(key) {
const res = [];
const keyS = bigInt(key).leInt2Buff(32).toString("hex");
for (let i=0; i<this.nodes[keyS].length; i++) {
res.push(bigInt(this.nodes[keyS][i]));
_key2str(k) {
// const keyS = bigInt(key).leInt2Buff(32).toString("hex");
const keyS = bigInt(k).toString();
return keyS;
}
_normalize(n) {
for (let i=0; i<n.length; i++) {
n[i] = bigInt(n[i]);
}
return res;
}
async save(root, inserts) {
async get(key) {
const keyS = this._key2str(key);
return this.nodes[keyS];
}
async setRoot(rt) {
this.root = rt;
}
async multiIns(inserts) {
for (let i=0; i<inserts.length; i++) {
const keyS = bigInt(inserts[i][0]).leInt2Buff(32).toString("hex");
const keyS = this._key2str(inserts[i][0]);
this._normalize(inserts[i][1]);
this.nodes[keyS] = inserts[i][1];
}
this.root = root;
}
async multiDel(dels) {
for (let i=0; i<dels.length; i++) {
const keyS = this._key2str(dels[i]);
delete this.nodes[keyS];
}
}
}

+ 23994
- 0
test.sig
File diff suppressed because it is too large
View File


+ 138
- 37
test/smt.js

@ -13,6 +13,52 @@ function print(circuit, w, s) {
console.log(s + ": " + w[circuit.getSignalIdx(s)]);
}
async function testInsert(tree, key, value, circuit, log ) {
const res = await tree.insert(key,value);
let siblings = res.sibblings;
while (siblings.length<10) siblings.push(bigInt(0));
const w = circuit.calculateWitness({
fnc: [1,0],
oldRoot: res.oldRoot,
newRoot: res.newRoot,
siblings: siblings,
oldKey: res.oldKey,
oldValue: res.oldValue,
isOld0: res.isOld0 ? 1 : 0,
newKey: key,
newValue: value
}, log);
const root1 = w[circuit.getSignalIdx("main.topSwitcher.outR")];
assert(circuit.checkWitness(w));
assert(root1.equals(res.newRoot));
}
async function testDelete(tree, key, circuit) {
const res = await tree.delete(key);
let siblings = res.sibblings;
while (siblings.length<10) siblings.push(bigInt(0));
const w = circuit.calculateWitness({
fnc: [1,1],
oldRoot: res.oldRoot,
newRoot: res.newRoot,
siblings: siblings,
oldKey: res.oldKey,
oldValue: res.oldValue,
isOld0: res.isOld0 ? 1 : 0,
newKey: res.delKey,
newValue: res.delValue
});
const root1 = w[circuit.getSignalIdx("main.topSwitcher.outR")];
assert(circuit.checkWitness(w));
assert(root1.equals(res.newRoot));
}
describe("SMT test", function () {
let circuit;
@ -34,55 +80,110 @@ describe("SMT test", function () {
const key = bigInt(111);
const value = bigInt(222);
const res = await tree.insert(key,value);
let siblings = res.sibblings;
while (siblings.length<10) siblings.push(bigInt(0));
const w = circuit.calculateWitness({
oldRoot: res.oldRoot,
newRoot: res.newRoot,
siblings: siblings,
oldKey: res.oldKey,
oldValue: res.oldValue,
isOld0: res.isOld0 ? 1 : 0,
newKey: key,
newValue: value
});
const root1 = w[circuit.getSignalIdx("main.levels[0].newRoot")];
console.log("root1: " + root1.toString());
console.log("root2: " + res.newRoot.toString());
assert(root1.equals(res.newRoot));
assert(circuit.checkWitness(w));
await testInsert(tree, key, value, circuit);
});
it("It should add another element", async () => {
const key = bigInt(333);
const value = bigInt(444);
const res = await tree.insert(key,value);
let siblings = res.sibblings;
while (siblings.length<10) siblings.push(bigInt(0));
await testInsert(tree, key, value, circuit);
});
it("Should remove an element", async () => {
await testDelete(tree, 111, circuit);
await testDelete(tree, 333, circuit);
});
it("Should test convination of adding and removing 3 elements", async () => {
const keys = [bigInt(8), bigInt(9), bigInt(32)];
const values = [bigInt(88), bigInt(99), bigInt(3232)];
const tree1 = await smt.newMemEmptyTrie();
const tree2 = await smt.newMemEmptyTrie();
const tree3 = await smt.newMemEmptyTrie();
const tree4 = await smt.newMemEmptyTrie();
const tree5 = await smt.newMemEmptyTrie();
const tree6 = await smt.newMemEmptyTrie();
await testInsert(tree1,keys[0],values[0], circuit);
await testInsert(tree1,keys[1],values[1], circuit, console.log);
/* await testInsert(tree1,keys[2],values[2], circuit);
await testInsert(tree2,keys[0],values[0], circuit);
await testInsert(tree2,keys[2],values[2], circuit);
await testInsert(tree2,keys[1],values[1], circuit);
await testInsert(tree3,keys[1],values[1], circuit);
await testInsert(tree3,keys[0],values[0], circuit);
await testInsert(tree3,keys[2],values[2], circuit);
await testInsert(tree4,keys[1],values[1], circuit);
await testInsert(tree4,keys[2],values[2], circuit);
await testInsert(tree4,keys[0],values[0], circuit);
await testInsert(tree5,keys[2],values[2], circuit);
await testInsert(tree5,keys[0],values[0], circuit);
await testInsert(tree5,keys[1],values[1], circuit);
await testInsert(tree6,keys[2],values[2], circuit);
await testInsert(tree6,keys[1],values[1], circuit);
await testInsert(tree6,keys[0],values[0], circuit);
await testDelete(tree1, keys[0], circuit);
await testDelete(tree1, keys[1], circuit);
await testDelete(tree2, keys[1], circuit);
await testDelete(tree2, keys[0], circuit);
await testDelete(tree3, keys[0], circuit);
await testDelete(tree3, keys[2], circuit);
await testDelete(tree4, keys[2], circuit);
await testDelete(tree4, keys[0], circuit);
await testDelete(tree5, keys[1], circuit);
await testDelete(tree5, keys[2], circuit);
await testDelete(tree6, keys[2], circuit);
await testDelete(tree6, keys[1], circuit);
await testDelete(tree1, keys[2], circuit);
await testDelete(tree2, keys[2], circuit);
await testDelete(tree3, keys[1], circuit);
await testDelete(tree4, keys[1], circuit);
await testDelete(tree5, keys[0], circuit);
await testDelete(tree6, keys[0], circuit); */
});
it("Should match a NOp with random vals", async () => {
let siblings = [];
while (siblings.length<10) siblings.push(bigInt(88));
const w = circuit.calculateWitness({
oldRoot: res.oldRoot,
newRoot: res.newRoot,
fnc: [0,0],
oldRoot: 11,
newRoot: 22,
siblings: siblings,
oldKey: res.oldKey,
oldValue: res.oldValue,
isOld0: res.isOld0 ? 1 : 0,
newKey: key,
newValue: value
oldKey: 33,
oldValue: 44,
isOld0: 55,
newKey: 66,
newValue: 77
});
const root1 = w[circuit.getSignalIdx("main.levels[0].newRoot")];
assert(circuit.checkWitness(w));
});
it("Should update an element", async () => {
});
console.log("root1: " + root1.toString());
console.log("root2: " + res.newRoot.toString());
assert(root1.equals(res.newRoot));
it("Should verify existance of an element", async () => {
assert(circuit.checkWitness(w));
});
it("Should verify non existance of an element", async () => {
});
});

+ 163
- 0
test/smtjs.js

@ -0,0 +1,163 @@
const chai = require("chai");
const snarkjs = require("snarkjs");
const smt = require("../src/smt.js");
const assert = chai.assert;
const bigInt = snarkjs.bigInt;
function stringifyBigInts(o) {
if ((typeof(o) == "bigint") || (o instanceof bigInt)) {
return o.toString(10);
} else if (Array.isArray(o)) {
return o.map(stringifyBigInts);
} else if (typeof o == "object") {
const res = {};
for (let k in o) {
res[k] = stringifyBigInts(o[k]);
}
return res;
} else {
return o;
}
}
describe("SMT Javascript test", function () {
this.timeout(100000);
before( async () => {
});
it("Should insert 2 elements and empty them", async () => {
const tree = await smt.newMemEmptyTrie();
const key1 = bigInt(111);
const value1 = bigInt(222);
const key2 = bigInt(333);
const value2 = bigInt(444);
await tree.insert(key1,value1);
await tree.insert(key2,value2);
await tree.delete(key2);
await tree.delete(key1);
assert(tree.root.isZero());
});
it("Should insert 3 elements in dferent order and should be the same", async () => {
const keys = [bigInt(8), bigInt(9), bigInt(32)];
const values = [bigInt(88), bigInt(99), bigInt(3232)];
const tree1 = await smt.newMemEmptyTrie();
const tree2 = await smt.newMemEmptyTrie();
const tree3 = await smt.newMemEmptyTrie();
const tree4 = await smt.newMemEmptyTrie();
const tree5 = await smt.newMemEmptyTrie();
const tree6 = await smt.newMemEmptyTrie();
await tree1.insert(keys[0],values[0]);
await tree1.insert(keys[1],values[1]);
await tree1.insert(keys[2],values[2]);
await tree2.insert(keys[0],values[0]);
await tree2.insert(keys[2],values[2]);
await tree2.insert(keys[1],values[1]);
await tree3.insert(keys[1],values[1]);
await tree3.insert(keys[0],values[0]);
await tree3.insert(keys[2],values[2]);
await tree4.insert(keys[1],values[1]);
await tree4.insert(keys[2],values[2]);
await tree4.insert(keys[0],values[0]);
await tree5.insert(keys[2],values[2]);
await tree5.insert(keys[0],values[0]);
await tree5.insert(keys[1],values[1]);
await tree6.insert(keys[2],values[2]);
await tree6.insert(keys[1],values[1]);
await tree6.insert(keys[0],values[0]);
assert(tree1.root.equals(tree2.root));
assert(tree2.root.equals(tree3.root));
assert(tree3.root.equals(tree4.root));
assert(tree4.root.equals(tree5.root));
assert(tree5.root.equals(tree6.root));
assert.equal(Object.keys(tree1.db.nodes).length, Object.keys(tree2.db.nodes).length);
assert.equal(Object.keys(tree2.db.nodes).length, Object.keys(tree3.db.nodes).length);
assert.equal(Object.keys(tree3.db.nodes).length, Object.keys(tree4.db.nodes).length);
assert.equal(Object.keys(tree4.db.nodes).length, Object.keys(tree5.db.nodes).length);
assert.equal(Object.keys(tree5.db.nodes).length, Object.keys(tree6.db.nodes).length);
await tree1.delete(keys[0]);
await tree1.delete(keys[1]);
await tree2.delete(keys[1]);
await tree2.delete(keys[0]);
assert(tree1.root.equals(tree2.root));
await tree3.delete(keys[0]);
await tree3.delete(keys[2]);
await tree4.delete(keys[2]);
await tree4.delete(keys[0]);
assert(tree3.root.equals(tree4.root));
await tree5.delete(keys[1]);
await tree5.delete(keys[2]);
await tree6.delete(keys[2]);
await tree6.delete(keys[1]);
assert(tree5.root.equals(tree6.root));
await tree1.delete(keys[2]);
await tree2.delete(keys[2]);
await tree3.delete(keys[1]);
await tree4.delete(keys[1]);
await tree5.delete(keys[0]);
await tree6.delete(keys[0]);
assert(tree1.root.isZero());
assert(tree2.root.isZero());
assert(tree3.root.isZero());
assert(tree4.root.isZero());
assert(tree5.root.isZero());
assert(tree6.root.isZero());
assert.equal(Object.keys(tree1.db.nodes).length, 0);
assert.equal(Object.keys(tree2.db.nodes).length, 0);
assert.equal(Object.keys(tree3.db.nodes).length, 0);
assert.equal(Object.keys(tree4.db.nodes).length, 0);
assert.equal(Object.keys(tree5.db.nodes).length, 0);
assert.equal(Object.keys(tree6.db.nodes).length, 0);
});
it("Insert and remove 100 numbers randomly", async () => {
function perm(a) {
const arr = a.slice();
const rArr = [];
for (let i=0; i<arr.length; i++) {
let rIdx = Math.floor(Math.random() * (arr.length - i));
rArr.push(arr[rIdx]);
arr[rIdx] = arr[arr.length - i - 1];
}
return rArr;
}
const tree = await smt.newMemEmptyTrie();
const arr = [];
const N = 100;
for (let i=0; i<N; i++) {
arr.push(bigInt(i));
}
const insArr = perm(arr);
for (let i=0; i<N; i++) {
await tree.insert(insArr[i], i);
}
const delArr = perm(insArr);
for (let i=0; i<N; i++) {
await tree.delete(delArr[i]);
}
assert(tree.root.isZero());
assert.equal(Object.keys(tree.db.nodes).length, 0);
});
});

Loading…
Cancel
Save