Skip to content

Commit

Permalink
Merkle Tree storage implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
magestrio committed Apr 27, 2023
1 parent 1c5b538 commit b4572df
Show file tree
Hide file tree
Showing 9 changed files with 930 additions and 88 deletions.
10 changes: 5 additions & 5 deletions zkdb/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,28 +40,28 @@
"devDependencies": {
"@babel/preset-env": "^7.16.4",
"@babel/preset-typescript": "^7.16.0",
"@types/jest": "^27.0.3",
"@types/jest": "^27.5.2",
"@types/safer-buffer": "^2.1.0",
"@typescript-eslint/eslint-plugin": "^5.5.0",
"@typescript-eslint/parser": "^5.5.0",
"eslint": "^8.7.0",
"eslint-plugin-snarkyjs": "^0.1.0",
"husky": "^7.0.1",
"ipfs-core-types": "^0.14.0",
"jest": "^27.3.1",
"jest": "^27.5.1",
"lint-staged": "^11.0.1",
"prettier": "^2.3.2",
"ts-jest": "^27.0.7",
"ts-jest": "^27.1.5",
"tsconfig-paths": "^4.1.2",
"typescript": "^4.7.2"
},
"peerDependencies": {
"snarkyjs": "^0.8.0"
"snarkyjs": "^0.9.8"
},
"dependencies": {
"ipfs-core": "^0.18.0",
"multiformats": "^11.0.1",
"safer-buffer": "^2.1.2",
"snarkyjs": "^0.8.0"
"snarkyjs": "^0.9.8"
}
}
83 changes: 0 additions & 83 deletions zkdb/src/Add.test.ts

This file was deleted.

240 changes: 240 additions & 0 deletions zkdb/src/storage/merkle-tree-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
import {
Poseidon,
Field,
MerkleTree,
isReady,
shutdown,
Circuit,
Bool,
} from 'snarkyjs';
import * as IPFS from 'ipfs-core';
import { NodesMap, BaseMerkleTree } from './trees/merkle/BaseMerkleTree.js';
import MerkleTreeJSON from './trees/merkle/MerkleTreeJSON.js';
import MerkleTreeIPDL from './trees/merkle/MerkleTreeIPDL.js';
import crypto from 'crypto';

const DEFAULT_HEIGHT = 20;

(async function run() {
await isReady;

const storage = await IPFS.create();
const merkleTreeJSON = new MerkleTreeJSON(DEFAULT_HEIGHT, storage);
await getNodeTest(merkleTreeJSON);
await getRootTest(merkleTreeJSON);
await getSetLeafMerkleTreeTest(merkleTreeJSON);
await getFillMerkleTreeTest(merkleTreeJSON);

const merkleTreeIPDL = new MerkleTreeIPDL(DEFAULT_HEIGHT, storage);
await getNodeTest(merkleTreeIPDL);
await getRootTest(merkleTreeIPDL);
await getSetLeafMerkleTreeTest(merkleTreeIPDL);
await getFillMerkleTreeTest(merkleTreeIPDL);

await shutdown();
})();

function randomInteger(min: number, max: number): number {
const randomValue = Math.random() * (max - min) + min;
return Math.floor(randomValue);
}

function randomBigInt(min: bigint, max: bigint): bigint {
const range = max - min + 1n;
const randomBytes = crypto.getRandomValues(new Uint8Array(8));
const randomValue = BigInt(
'0x' +
Array.from(randomBytes)
.map((b) => b.toString(16).padStart(2, '0'))
.join('')
);
return (randomValue % range) + min;
}

async function getRootTest(merkleTree: BaseMerkleTree) {
// Setup
const LEAF_AMOUNT = 30;
const expectedMerkleTree = new MerkleTree(DEFAULT_HEIGHT);

// Execute
let isPassed = Bool(true);

const avaiableIndexies = [0n];

for (let i = 0; i < LEAF_AMOUNT; i++) {
const leafIndex = randomBigInt(0n, merkleTree.leafCount);
const leafValue = Poseidon.hash(Field(leafIndex).toFields());
await merkleTree.setLeaf(leafIndex, leafValue);
expectedMerkleTree.setLeaf(leafIndex, leafValue);

avaiableIndexies.push(leafIndex);

const expectedResult = expectedMerkleTree.getRoot();
const actualResult = await merkleTree.getRoot();

if (actualResult === null) {
isPassed = Bool(false);
break;
}

isPassed = isPassed.and(actualResult.equals(expectedResult));
}

//Verify
Circuit.log('getRoot test is passed', isPassed);
}

async function getNodeTest(merkleTree: BaseMerkleTree) {
// Setup
const LEAF_AMOUNT = 30;
const expectedMerkleTree = new MerkleTree(DEFAULT_HEIGHT);

const zeroes: Field[] = JSON.parse(JSON.stringify(expectedMerkleTree)).zeroes;

// Execute
let isPassed = Bool(true);

const avaiableIndexies = [1n];

for (let i = 0; i < LEAF_AMOUNT; i++) {
const leafIndex = BigInt(i); //randomBigInt(0n, merkleTree.leafCount);
const leafValue = Poseidon.hash(Field(leafIndex).toFields());
await merkleTree.setLeaf(leafIndex, leafValue);
expectedMerkleTree.setLeaf(leafIndex, leafValue);

let randomLevel = randomInteger(0, DEFAULT_HEIGHT);
let randomIndex = 0n;

if (i % 2 === 0) {
randomIndex = randomBigInt(0n, merkleTree.leafCount);
} else {
const position = randomInteger(1, avaiableIndexies.length);
randomIndex = avaiableIndexies[position];
}

avaiableIndexies.push(leafIndex);

const expectedResult = expectedMerkleTree.getNode(randomLevel, randomIndex);
const actualResult = await merkleTree.getNode(randomLevel, randomIndex);

let isZero = Bool(false);
for (const zero of zeroes) {
if (expectedResult.equals(Field(zero)).toBoolean()) {
isZero = Bool(true);
break;
}
}
Circuit.log('is zero', isZero);
Circuit.log('expectedResult', expectedResult);
Circuit.log('actualResult', actualResult);

isPassed = isPassed.and(expectedResult.equals(actualResult));

if (isPassed.not().toBoolean()) {
break;
}
}

//Verify
Circuit.log('getNodeTest is passed', isPassed);
}

async function getSetLeafMerkleTreeTest(merkleTree: BaseMerkleTree) {
// Setup
const LEAF_AMOUNT = 131;
const expectedMerkleTree = new MerkleTree(DEFAULT_HEIGHT);

// Execute
for (let i = 0; i < LEAF_AMOUNT; i++) {
const leafIndex = randomBigInt(0n, merkleTree.leafCount);
const leafValue = Poseidon.hash(Field(leafIndex).toFields());
await merkleTree.setLeaf(leafIndex, leafValue);
expectedMerkleTree.setLeaf(leafIndex, leafValue);
}

//Verify
const actualResult: NodesMap = await merkleTree.getNodes();
const expectedResult: NodesMap = JSON.parse(
JSON.stringify(expectedMerkleTree)
).nodes;

Circuit.log('actualResult', actualResult);
Circuit.log('expectedResult', expectedResult);

console.log('add leaf is passed', verifyNodes(actualResult, expectedResult));
}

async function getFillMerkleTreeTest(merkleTree: BaseMerkleTree) {
// Setup
const LEAF_AMOUNT = 150;
let fields: Field[] = [];
for (let i = 0; i < LEAF_AMOUNT; i++) {
fields = fields.concat(Poseidon.hash(Field(i).toFields()));
}

const expectedMerkleTree = new MerkleTree(DEFAULT_HEIGHT);
expectedMerkleTree.fill(fields);
const expectedResult: NodesMap = JSON.parse(
JSON.stringify(expectedMerkleTree)
).nodes;

// Execute
await merkleTree.fill(fields);

//Verify
const actualResult: NodesMap = await merkleTree.getNodes();

console.log('fill is passed', verifyNodes(actualResult, expectedResult));
}

function verifyNodes(
actualNodeMap: NodesMap,
expectedNodeMap: NodesMap
): boolean {
const actualLevels = Object.keys(actualNodeMap).sort();
const expectedLevels = Object.keys(expectedNodeMap).sort();

if (actualLevels.length !== expectedLevels.length) {
return false;
}

for (let i = 0; i < actualLevels.length; i++) {
const level = actualLevels[i];
if (level !== expectedLevels[i]) {
console.log('actualLevels', level);
console.log('expectedLevels', expectedLevels[i]);
return false;
}

const actualNodes = actualNodeMap[level as unknown as number];
const expectedNodes = expectedNodeMap[level as unknown as number];
const actualNodeKeys = Object.keys(actualNodes).sort();
const expectedNodeKeys = Object.keys(expectedNodes).sort();

if (actualNodeKeys.length !== expectedNodeKeys.length) {
console.log('actualNodeKeys.length', actualNodeKeys.length);
console.log('expectedNodeKeys.length', expectedNodeKeys.length);
return false;
}

for (let j = 0; j < actualNodeKeys.length; j++) {
const nodeKey = actualNodeKeys[j];
if (nodeKey !== expectedNodeKeys[j]) {
Circuit.log('expected node key', expectedNodeKeys[j]);
Circuit.log('actual node key', nodeKey);
return false;
}

const actualField = actualNodes[nodeKey];
const expectedField = expectedNodes[nodeKey];

if (!actualField.equals(expectedField)) {
Circuit.log('expected field', expectedField);
Circuit.log('actual field', actualField);
return false;
}
}
}

return true;
}
Loading

0 comments on commit b4572df

Please sign in to comment.