Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

a #7

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open

a #7

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 100 additions & 0 deletions eng-sessions/merkle-tree/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,103 @@ yarn test
# To run tests, watching for changes.
yarn test --watch
```

## generateProof function usagement.

```bash
let indexzeroexpectation = new HashPath([[e01], [e11]]);
console.log('a', indexzeroexpectation);
console.log('b', await treee.generateProof(0));

HashPath {
data: [
[
<Buffer 16 ab ab 34 1f b7 f3 70 e2 7e 4d ad cf 81 76 6d d0 df d0 ae 64 46 94 77 bb 2c f6 61 49 38 b2 af>
],
[
<Buffer 56 4a 42 6c 73 ba 2d ca 7b c3 67 df 3f 29 73 7e 94 8f ae c9 60 b8 d0 a1 5b 26 a5 0d 84 b3 15 67>
]
]
}

HashPath {
data: [
[
<Buffer 16 ab ab 34 1f b7 f3 70 e2 7e 4d ad cf 81 76 6d d0 df d0 ae 64 46 94 77 bb 2c f6 61 49 38 b2 af>
],
[
<Buffer 56 4a 42 6c 73 ba 2d ca 7b c3 67 df 3f 29 73 7e 94 8f ae c9 60 b8 d0 a1 5b 26 a5 0d 84 b3 15 67>
]
]
}


let indexoneexpectation = new HashPath([[e00], [e11]]);
console.log('a', indexoneexpectation);
console.log('b', await treee.generateProof(1));

HashPath {
data: [
[
<Buffer f5 a5 fd 42 d1 6a 20 30 27 98 ef 6e d3 09 97 9b 43 00 3d 23 20 d9 f0 e8 ea 98 31 a9 27 59 fb 4b>
],
[
<Buffer 56 4a 42 6c 73 ba 2d ca 7b c3 67 df 3f 29 73 7e 94 8f ae c9 60 b8 d0 a1 5b 26 a5 0d 84 b3 15 67>
]
]
}

HashPath {
data: [
[
<Buffer f5 a5 fd 42 d1 6a 20 30 27 98 ef 6e d3 09 97 9b 43 00 3d 23 20 d9 f0 e8 ea 98 31 a9 27 59 fb 4b>
],
[
<Buffer 56 4a 42 6c 73 ba 2d ca 7b c3 67 df 3f 29 73 7e 94 8f ae c9 60 b8 d0 a1 5b 26 a5 0d 84 b3 15 67>
]
]
}

let indextwoexpectation = new HashPath([[e03], [e10]]);
console.log('xxx', await treee.generateProof(2));
console.log('jasgg', indextwoexpectation);

HashPath {
data: [
[
<Buffer e7 b4 bb 67 55 1d de 95 89 c1 55 3d fd a3 7a 94 2a 18 ca f1 84 f9 cc 16 29 d2 5c f5 c6 0b e4 16>
],
[
<Buffer 0f b6 10 eb 85 69 bb 12 e0 76 87 ea 9d 70 25 e3 ef ae 87 0a f0 a0 9a 36 cd fb 22 2d ff 79 50 b3>
]
]
}

HashPath {
data: [
[
<Buffer e7 b4 bb 67 55 1d de 95 89 c1 55 3d fd a3 7a 94 2a 18 ca f1 84 f9 cc 16 29 d2 5c f5 c6 0b e4 16>
],
[
<Buffer 0f b6 10 eb 85 69 bb 12 e0 76 87 ea 9d 70 25 e3 ef ae 87 0a f0 a0 9a 36 cd fb 22 2d ff 79 50 b3>
]
]
}

expect(await treee.generateProof(0)).toEqual(indexzeroexpectation);
```

You can verify from image for example why index 0 (e00) need e01 and e11 because with this two hash your reach root.

for index 1 (e01) need e00 and e11 because with this two hash your reach root.

for index 2 (e02) need e03 and e10 because with this two hash your reach root. at this point e11 changes as e10 because index 2 (e02) is the right at depth 2.

![Merkle Tree](https://github.com/omgbbqhaxx/interview-tests/blob/master/eng-sessions/merkle-tree/src/proof.png)

# Important Resources

- https://hackmd.io/@kullervo/commitmentVector
- https://medium.com/@chaisomsri96/statelessness-series-part4-exploring-the-verkle-trie-structure-d97a8c85363e
- https://docs.aztec.network/protocol-specs/state/tree-implementations
- https://hackmd.io/@aztec-network/ryJ8wxfKK
38 changes: 38 additions & 0 deletions eng-sessions/merkle-tree/src/merkle_tree.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ describe('merkle_tree', () => {
const db = levelup(memdown());
const tree = await MerkleTree.new(db, 'test', 32);
const root = tree.getRoot();

expect(root.toString('hex')).toEqual('1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc5');
});

Expand All @@ -44,9 +45,13 @@ describe('merkle_tree', () => {
[e10, e11],
]);

console.log('expected', expected);

expect(await tree.getHashPath(0)).toEqual(expected);
expect(await tree.getHashPath(1)).toEqual(expected);

console.log('qq', await tree.getHashPath(0));

expected = new HashPath([
[e02, e03],
[e10, e11],
Expand All @@ -59,6 +64,39 @@ describe('merkle_tree', () => {
expect(root).toEqual(Buffer.from('e645e6b5445483a358c4d15c1923c616a0e6884906b05c196d341ece93b2de42', 'hex'));
});

it('we need generate a proof from index.', async () => {
const db = levelup(memdown());

const hasher = new Sha256Hasher();
const e00 = hasher.hash(values[0]);
const e01 = hasher.hash(values[1]);
const e02 = hasher.hash(values[2]);
const e03 = hasher.hash(values[3]);
const e10 = hasher.compress(e00, e01);
const e11 = hasher.compress(e02, e03);
const root = hasher.compress(e10, e11);

const treee = await MerkleTree.new(db, 'test', 2);

for (let i = 0; i < 4; ++i) {
await treee.updateElement(i, values[i]);
}

let indexzeroexpectation = new HashPath([[e01], [e11]]);
console.log('a', indexzeroexpectation);
console.log('b', await treee.generateProof(0));

let indexoneexpectation = new HashPath([[e00], [e11]]);
console.log('a', indexoneexpectation);
console.log('b', await treee.generateProof(1));

let indextwoexpectation = new HashPath([[e03], [e10]]);
console.log('xxx', await treee.generateProof(2));
console.log('jasgg', indextwoexpectation);

expect(await treee.generateProof(0)).toEqual(indexzeroexpectation);
});

it('should be able to restore from previous data', async () => {
const levelDown = memdown();
const db = levelup(levelDown);
Expand Down
133 changes: 109 additions & 24 deletions eng-sessions/merkle-tree/src/merkle_tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,23 +28,42 @@ export class MerkleTree {
throw Error('Bad depth');
}

// Implement.
// Minimum 2 depth required..
if (!root) {
console.log('constructor');
let emptyroothash = this.hasher.hash(Buffer.alloc(LEAF_BYTES));
//console.log('hash', hash);

//should have correct empty tree root for depth 32 thirty two
//here for first test pass.
for (let i = 0; i < depth; i++) {
const parent = this.hasher.compress(emptyroothash, emptyroothash);
this.db.put(parent, this.hasher.concat([emptyroothash, emptyroothash]));
emptyroothash = parent;
}

//console.log('hash2x', hash);
this.root = emptyroothash;
} else {
this.root = root;
}
}

/**
* Constructs or restores a new MerkleTree instance with the given `name` and `depth`.
* The `db` contains the tree data.
*/

static async new(db: LevelUp, name: string, depth = MAX_DEPTH) {
const meta: Buffer = await db.get(Buffer.from(name)).catch(() => {});
if (meta) {
const root = meta.slice(0, 32);
const depth = meta.readUInt32LE(32);
return new MerkleTree(db, name, depth, root);
} else {
const tree = new MerkleTree(db, name, depth);
await tree.writeMetaData();
return tree;
try {
const metadata = await db.get(Buffer.from(name));
const rootHash = metadata.slice(0, 32);
const savedDepth = metadata.readUInt32LE(32);
return new MerkleTree(db, name, savedDepth, rootHash);
} catch (error) {
const newTree = new MerkleTree(db, name, depth);
await newTree.writeMetaData();
return newTree;
}
}

Expand All @@ -63,24 +82,90 @@ export class MerkleTree {
return this.root;
}

/**
* Returns the hash path for `index`.
* e.g. To return the HashPath for index 2, return the nodes marked `*` at each layer.
* d0: [ root ]
* d1: [*] [*]
* d2: [*] [*] [ ] [ ]
* d3: [ ] [ ] [*] [*] [ ] [ ] [ ] [ ]
*/
async getHashPath(index: number) {
// Implement.
return new HashPath();
let currentNodes = (await this.db.get(this.root)) as Buffer;
let hashPath = new HashPath();

for (let i = this.depth - 1; i >= 0; i--) {
let [leftNode, rightNode] = [currentNodes.slice(0, 32), currentNodes.slice(32, 64)];
hashPath.data[i] = [leftNode, rightNode];

if (i !== 0) {
let nextNode = this.isRight(index, this.depth - 1 - i) ? rightNode : leftNode;
currentNodes = await this.db.get(nextNode);
}
}

return hashPath;
}

async generateProof(index: number) {
let currentNodes = (await this.db.get(this.root)) as Buffer;
let hashPath = new HashPath();

for (let i = this.depth - 1; i >= 0; i--) {
let [leftNode, rightNode] = [currentNodes.slice(0, 32), currentNodes.slice(32, 64)];

console.log('i is here', i);

if (i !== 0) {
let nextNode = this.isRight(index, this.depth - 1 - i) ? rightNode : leftNode;
if (this.isRight(index, this.depth - 1 - i)) {
hashPath.data[i] = [leftNode];
} else {
hashPath.data[i] = [rightNode];
}
currentNodes = await this.db.get(nextNode);
} else {
//console.log('why im here', i);
//console.log('why im here', leftNode);
//console.log('why im here', rightNode);
//console.log('is right', this.isRight(index, this.depth - 1 - i));
if (this.isRight(index, this.depth - 1 - i)) {
hashPath.data[i] = [leftNode];
} else {
hashPath.data[i] = [rightNode];
}
}
}

return hashPath;
}

/**
* Updates the tree with `value` at `index`. Returns the new tree root.
*/
async updateElement(index: number, value: Buffer) {
// Implement.
const batch = this.db.batch();

const updateRecursively = async (parent: Buffer, currentDepth: number): Promise<Buffer> => {
if (currentDepth === this.depth) {
return this.hasher.hash(value);
}

const nodes = (await this.db.get(parent)) as Buffer;
let [leftNode, rightNode] = [nodes.slice(0, 32), nodes.slice(32, 64)];
const isRight = this.isRight(index, currentDepth);
const updatedNode = await updateRecursively(isRight ? rightNode : leftNode, currentDepth + 1);

if (isRight) {
rightNode = updatedNode;
} else {
leftNode = updatedNode;
}

const newParent = this.hasher.compress(leftNode, rightNode);
batch.put(newParent, this.hasher.concat([leftNode, rightNode]));

return newParent;
};

this.root = await updateRecursively(this.root, 0);
await batch.write();
await this.writeMetaData();

return this.root;
}

isRight(index: number, currentDepth: number) {
let bitPosition = this.depth - currentDepth - 1;
return (index >> bitPosition) & 1;
}
}
Binary file added eng-sessions/merkle-tree/src/proof.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions eng-sessions/merkle-tree/src/sha256_hasher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ export class Sha256Hasher {
.digest();
}

concat(buffers: Buffer[]): Buffer {
return Buffer.concat(buffers);
}

/**
* Given `data` which is to be become an entry in the tree, return a digest that represents that data.
*/
Expand Down