Skip to content

Commit e12c241

Browse files
authored
Merge pull request #36 from iden3/feauture/proof-json
Feauture/proof json
2 parents efbe122 + c982136 commit e12c241

File tree

6 files changed

+261
-109
lines changed

6 files changed

+261
-109
lines changed

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@iden3/js-merkletree",
3-
"version": "1.0.2",
3+
"version": "1.1.0",
44
"description": "javascript sparse merkle tree library",
55
"typings": "dist/types/index.d.ts",
66
"main": "dist/node/cjs/index.js",

src/lib/hash/hash.ts

Lines changed: 47 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -54,54 +54,72 @@ export class Hash implements IHash {
5454
const bytes = swapEndianness(this.value);
5555
return BigInt(bytes2BinaryString(bytes));
5656
}
57-
}
58-
59-
export const ZERO_HASH = new Hash();
6057

61-
// returned bytes endianess will be big-endian
62-
export const newHashFromBigInt = (bigNum: bigint): Hash => {
63-
if (!checkBigIntInField(bigNum)) {
64-
throw 'NewBigIntFromHashBytes: Value not inside the Finite Field';
58+
static fromString(s: string): Hash {
59+
try {
60+
return Hash.fromBigInt(BigInt(s));
61+
} catch (e) {
62+
const deserializedHash = JSON.parse(s);
63+
const bytes = Uint8Array.from(Object.values(deserializedHash.bytes));
64+
return new Hash(bytes);
65+
}
6566
}
67+
static fromBigInt(i: bigint): Hash {
68+
if (!checkBigIntInField(i)) {
69+
throw new Error('NewBigIntFromHashBytes: Value not inside the Finite Field')
70+
}
6671

67-
const bytes = bigIntToUINT8Array(bigNum);
72+
const bytes = bigIntToUINT8Array(i);
6873

69-
const hash = new Hash();
70-
hash.value = bytes;
71-
return hash;
72-
};
74+
return new Hash(swapEndianness(bytes));
75+
}
7376

74-
export const newHashFromHex = (h: string): Hash => {
75-
if (!h) {
76-
return ZERO_HASH;
77+
static fromHex(h: string | undefined): Hash {
78+
if (!h) {
79+
return ZERO_HASH;
80+
}
81+
return new Hash(Hex.decodeString(h));
7782
}
7883

79-
// TODO: add in field check
84+
toJSON() {
85+
return this.string();
86+
}
87+
}
8088

81-
const hash = new Hash();
82-
hash.value = swapEndianness(Hex.decodeString(h));
83-
return hash;
84-
};
89+
export const ZERO_HASH = new Hash();
8590

86-
// return object of class Hash from a decimal string
87-
export const newHashFromString = (decimalString: string): Hash => {
88-
const bigNum = BigInt(decimalString);
91+
/**
92+
* @deprecated The method should not be used and will be removed in the next major version,
93+
* please use Hash.fromBigInt instead
94+
*/
95+
export const newHashFromBigInt = (bigNum: bigint): Hash => {
96+
return Hash.fromBigInt(bigNum);
97+
};
8998

90-
if (!checkBigIntInField(bigNum)) {
91-
throw 'NewBigIntFromHashBytes: Value not inside the Finite Field';
92-
}
99+
/**
100+
* @deprecated The method should not be used and will be removed in the next major version,
101+
* please use Hash.fromBigInt instead
102+
*/
103+
export const newHashFromHex = (h: string): Hash => {
104+
return Hash.fromHex(h);
105+
};
93106

94-
return newHashFromBigInt(bigNum);
107+
/**
108+
* @deprecated The method should not be used and will be removed in the next major version,
109+
* please use Hash.fromBigString instead
110+
*/
111+
export const newHashFromString = (decimalString: string): Hash => {
112+
return Hash.fromString(decimalString);
95113
};
96114

97115
export const hashElems = (e: Array<bigint>): Hash => {
98116
const hashBigInt = poseidon.hash(e);
99-
return newHashFromBigInt(hashBigInt);
117+
return Hash.fromBigInt(hashBigInt);
100118
};
101119

102120
export const hashElemsKey = (k: bigint, e: Array<bigint>): Hash => {
103121
const hashBigInt = poseidon.hash([...e, k]);
104-
return newHashFromBigInt(hashBigInt);
122+
return Hash.fromBigInt(hashBigInt);
105123
};
106124

107125
export const circomSiblingsFromSiblings = (siblings: Siblings, levels: number): Siblings => {

src/lib/merkletree/merkletree.ts

Lines changed: 55 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { ITreeStorage } from '../../types/storage';
2-
import { Hash, ZERO_HASH, circomSiblingsFromSiblings, newHashFromBigInt } from '../hash/hash';
2+
import { Hash, ZERO_HASH, circomSiblingsFromSiblings } from '../hash/hash';
33

44
import { Node } from '../../types';
55
import { NODE_TYPE_EMPTY, NODE_TYPE_LEAF, NODE_TYPE_MIDDLE } from '../../constants';
66
import { NodeEmpty, NodeLeaf, NodeMiddle } from '../node/node';
7-
import { bytesEqual, getPath, setBitBigEndian } from '../utils';
8-
import { Siblings } from '../../types/merkletree';
7+
import { bytesEqual, getPath } from '../utils';
8+
import { NodeAux, Siblings } from '../../types/merkletree';
99
import { checkBigIntInField } from '../utils/crypto';
1010
import { CircomProcessorProof, CircomVerifierProof } from './circom';
1111
import {
@@ -48,8 +48,8 @@ export class Merkletree {
4848
}
4949

5050
this.#root = await this.root();
51-
const kHash = newHashFromBigInt(k);
52-
const vHash = newHashFromBigInt(v);
51+
const kHash = Hash.fromBigInt(k);
52+
const vHash = Hash.fromBigInt(v);
5353

5454
const newNodeLeaf = new NodeLeaf(kHash, vHash);
5555
const path = getPath(this.maxLevels, kHash.value);
@@ -193,7 +193,7 @@ export class Merkletree {
193193
}
194194

195195
async get(k: bigint): Promise<{ key: bigint; value: bigint; siblings: Siblings }> {
196-
const kHash = newHashFromBigInt(k);
196+
const kHash = Hash.fromBigInt(k);
197197
const path = getPath(this.maxLevels, kHash.value);
198198

199199
let nextKey = await this.root();
@@ -254,8 +254,8 @@ export class Merkletree {
254254
throw 'key not inside the finite field';
255255
}
256256

257-
const kHash = newHashFromBigInt(k);
258-
const vHash = newHashFromBigInt(v);
257+
const kHash = Hash.fromBigInt(k);
258+
const vHash = Hash.fromBigInt(v);
259259

260260
const path = getPath(this.maxLevels, kHash.value);
261261

@@ -352,7 +352,7 @@ export class Merkletree {
352352
throw ErrNotWritable;
353353
}
354354

355-
const kHash = newHashFromBigInt(k);
355+
const kHash = Hash.fromBigInt(k);
356356
const path = getPath(this.maxLevels, kHash.value);
357357

358358
let nextKey = this.#root;
@@ -442,8 +442,8 @@ export class Merkletree {
442442
break;
443443
case NODE_TYPE_MIDDLE:
444444
await f(n);
445-
await this.walk((n as NodeMiddle).childL, f);
446-
await this.walk((n as NodeMiddle).childR, f);
445+
await this.recWalk((n as NodeMiddle).childL, f);
446+
await this.recWalk((n as NodeMiddle).childR, f);
447447
break;
448448
default:
449449
throw ErrInvalidNodeFound;
@@ -454,7 +454,7 @@ export class Merkletree {
454454
if (bytesEqual(rootKey.value, ZERO_HASH.value)) {
455455
rootKey = await this.root();
456456
}
457-
await this.walk(rootKey, f);
457+
await this.recWalk(rootKey, f);
458458
}
459459

460460
async generateCircomVerifierProof(k: bigint, rootKey: Hash): Promise<CircomVerifierProof> {
@@ -471,16 +471,16 @@ export class Merkletree {
471471
const { proof, value } = await this.generateProof(k, rootKey);
472472
const cp = new CircomVerifierProof();
473473
cp.root = rootKey;
474-
cp.siblings = proof.siblings;
474+
cp.siblings = proof.allSiblings();
475475
if (typeof proof.nodeAux !== 'undefined') {
476476
cp.oldKey = proof.nodeAux.key;
477477
cp.oldValue = proof.nodeAux.value;
478478
} else {
479479
cp.oldKey = ZERO_HASH;
480480
cp.oldValue = ZERO_HASH;
481481
}
482-
cp.key = newHashFromBigInt(k);
483-
cp.value = newHashFromBigInt(value);
482+
cp.key = Hash.fromBigInt(k);
483+
cp.value = Hash.fromBigInt(value);
484484

485485
if (proof.existence) {
486486
cp.fnc = 0;
@@ -492,36 +492,62 @@ export class Merkletree {
492492
}
493493

494494
async generateProof(k: bigint, rootKey?: Hash): Promise<{ proof: Proof; value: bigint }> {
495-
const p = new Proof();
496495
let siblingKey: Hash;
497496

498-
const kHash = newHashFromBigInt(k);
497+
const kHash = Hash.fromBigInt(k);
499498
const path = getPath(this.maxLevels, kHash.value);
500499
if (!rootKey) {
501500
rootKey = await this.root();
502501
}
503502
let nextKey = rootKey;
504503

505-
for (p.depth = 0; p.depth < this.maxLevels; p.depth += 1) {
504+
let depth = 0;
505+
let existence = false;
506+
const siblings: Siblings = [];
507+
let nodeAux: NodeAux | undefined;
508+
509+
for (depth = 0; depth < this.maxLevels; depth += 1) {
506510
const n = await this.getNode(nextKey);
507511
if (typeof n === 'undefined') {
508512
throw ErrNotFound;
509513
}
510514
switch (n.type) {
511515
case NODE_TYPE_EMPTY:
512-
return { proof: p, value: BigInt('0') };
516+
return {
517+
proof: new Proof({
518+
existence,
519+
nodeAux,
520+
siblings
521+
}),
522+
value: BigInt('0')
523+
};
513524
case NODE_TYPE_LEAF:
514525
if (bytesEqual(kHash.value, (n as NodeLeaf).entry[0].value)) {
515-
p.existence = true;
516-
return { proof: p, value: (n as NodeLeaf).entry[1].bigInt() };
526+
existence = true;
527+
528+
return {
529+
proof: new Proof({
530+
existence,
531+
nodeAux,
532+
siblings
533+
}),
534+
value: (n as NodeLeaf).entry[1].bigInt()
535+
};
517536
}
518-
p.nodeAux = {
537+
nodeAux = {
519538
key: (n as NodeLeaf).entry[0],
520539
value: (n as NodeLeaf).entry[1]
521540
};
522-
return { proof: p, value: (n as NodeLeaf).entry[1].bigInt() };
541+
return {
542+
proof: new Proof({
543+
existence,
544+
nodeAux,
545+
siblings
546+
}),
547+
value: (n as NodeLeaf).entry[1].bigInt()
548+
};
523549
case NODE_TYPE_MIDDLE:
524-
if (path[p.depth]) {
550+
if (path[depth]) {
525551
nextKey = (n as NodeMiddle).childR;
526552
siblingKey = (n as NodeMiddle).childL;
527553
} else {
@@ -532,11 +558,7 @@ export class Merkletree {
532558
default:
533559
throw ErrInvalidNodeFound;
534560
}
535-
536-
if (!bytesEqual(siblingKey.value, ZERO_HASH.value)) {
537-
setBitBigEndian(p.notEmpties, p.depth);
538-
p.siblings.push(siblingKey);
539-
}
561+
siblings.push(siblingKey);
540562
}
541563
throw ErrKeyNotFound;
542564
}
@@ -563,8 +585,8 @@ export class Merkletree {
563585
throw 'key/value undefined';
564586
}
565587

566-
cp.oldKey = newHashFromBigInt(key);
567-
cp.oldValue = newHashFromBigInt(value);
588+
cp.oldKey = Hash.fromBigInt(key);
589+
cp.oldValue = Hash.fromBigInt(value);
568590

569591
if (bytesEqual(cp.oldKey.value, ZERO_HASH.value)) {
570592
cp.isOld0 = true;
@@ -573,8 +595,8 @@ export class Merkletree {
573595
cp.siblings = circomSiblingsFromSiblings(siblings, this.maxLevels);
574596
await this.add(k, v);
575597

576-
cp.newKey = newHashFromBigInt(k);
577-
cp.newValue = newHashFromBigInt(v);
598+
cp.newKey = Hash.fromBigInt(k);
599+
cp.newValue = Hash.fromBigInt(v);
578600
cp.newRoot = await this.root();
579601

580602
return cp;

0 commit comments

Comments
 (0)