From 13f0303a646a615c35623bebe5009b4d4a629e27 Mon Sep 17 00:00:00 2001 From: microproofs Date: Fri, 31 May 2024 11:34:42 -0400 Subject: [PATCH] add another test --- aiken.lock | 4 +- offchain/src/index.test.ts | 61 ++++++++++++++++-- offchain/src/index.ts | 129 +++++++++++++++++++++++++++++++++---- 3 files changed, 174 insertions(+), 20 deletions(-) diff --git a/aiken.lock b/aiken.lock index 2f0e27c..86476a2 100644 --- a/aiken.lock +++ b/aiken.lock @@ -24,5 +24,5 @@ requirements = [] source = "github" [etags] -"aiken-lang/fuzz@main" = [{ secs_since_epoch = 1716834372, nanos_since_epoch = 391046000 }, "98cf81aa68f9ccf68bc5aba9be06d06cb1db6e8eff60b668ed5e8ddf3588206b"] -"aiken-lang/stdlib@main" = [{ secs_since_epoch = 1716834372, nanos_since_epoch = 160697000 }, "dfda6bc70aad760f7f836c0db06b07e0a398bb3667f4d944d7d7255d54a454af"] +"aiken-lang/fuzz@main" = [{ secs_since_epoch = 1717167992, nanos_since_epoch = 648648000 }, "98cf81aa68f9ccf68bc5aba9be06d06cb1db6e8eff60b668ed5e8ddf3588206b"] +"aiken-lang/stdlib@main" = [{ secs_since_epoch = 1717167992, nanos_since_epoch = 410365000 }, "dfda6bc70aad760f7f836c0db06b07e0a398bb3667f4d944d7d7255d54a454af"] diff --git a/offchain/src/index.test.ts b/offchain/src/index.test.ts index 5647d4a..75b89cd 100644 --- a/offchain/src/index.test.ts +++ b/offchain/src/index.test.ts @@ -3,7 +3,7 @@ import { Buffer } from "buffer"; import { SparseMerkleTree } from "./index.js"; -test("Test 1", () => { +test("Test Insert", () => { const x = new SparseMerkleTree(); let rootList = [x.branchHash]; const expectedList = [ @@ -75,7 +75,7 @@ test("Test 1", () => { ); }); -test("Test 2", () => { +test("Test Modification Proof", () => { const x = new SparseMerkleTree(); let rootList: Uint8Array[] = []; const expectedList: string[] = []; @@ -92,13 +92,62 @@ test("Test 2", () => { x.insert("fig (0)"); x.insert("grape (110606)"); - const thing1 = x.merkleProof("grape (110606)"); + const other = x.modificationProof("grape (110606)"); - console.log(thing1.toString(), "\n\n"); + console.log(other.toString(), "\n\n"); x.insert("grapefruit (0)"); - const thing = x.merkleProof("grapefruit (0)"); + const expected = { + startingSide: "left", + remainingProofs: [ + [ + "26363294ff627e13438ecc429926a7cb64686944ec0587128338e3b447dc30e5", + 255, + "right", + ], + ], + leftLeaf: + "3378b5c960257ffe7c3e86d00563739bdf7db730e10732f6b943a4c1802fd05e", + rightLeaf: + "55d5551e8e1323d35afe53cf8698867c9de9a408e97ee968dc8414d527cc719c", + leftProofs: [], + rightProofs: [ + ["0bca11bb74090bc698bc7b811c23e87d97744b10c16f2c7d5e23d82bd5f41bea", 253], + ], + continuingSideProofs: [ + ["3d7b9d20ff5e977c69307d9d264fe6b36cd0fc08b390578b09d33a9f044d77dd", 253], + ], + intersectingHeight: 251, + leftRightHeight: 254, + }; + + const actual = x.modificationProof("grapefruit (0)"); + + console.log(actual.toString()); + + expect(actual.toString()).toStrictEqual(JSON.stringify(expected)); +}); + +test("Test Member Proof", () => { + const x = new SparseMerkleTree(); + let rootList: Uint8Array[] = []; + const expectedList: string[] = []; + + x.insert("apple (0)"); + x.insert("apricot (0)"); + x.insert("banana (328)"); + x.insert("blackberry (0)"); + x.insert("blueberry (92383)"); + x.insert("cherry (0)"); + x.insert("coconut (0)"); + x.insert("cranberry (0)"); + x.insert("durian (0)"); + x.insert("fig (0)"); + x.insert("grape (110606)"); + x.insert("grapefruit (0)"); + + const thing = x.memberProof("grapefruit (0)"); - console.log(thing.toString()); + console.log(thing); }); diff --git a/offchain/src/index.ts b/offchain/src/index.ts index a189f2a..010682f 100644 --- a/offchain/src/index.ts +++ b/offchain/src/index.ts @@ -137,7 +137,7 @@ export class Leaf { } } - doMerkeProof(_key: BitSet, _mutProof: MerkleProof): MerkleProof { + doModificationProof(_key: BitSet, _mutProof: MerkleProof): MerkleProof { throw new Error("Not possible"); } @@ -149,6 +149,10 @@ export class Leaf { mutProof.leftLeaf = blake2b(this.value, undefined, 32); } + doMemberProof(_key: BitSet): [Uint8Array, number, Side][] { + throw new Error("Not possible"); + } + static boundaryLeaf(isMin: boolean) { if (isMin) { let x = new Leaf(Buffer.from("00", "hex")); @@ -365,7 +369,7 @@ export class Branch { } } - doMerkeProof(key: BitSet, mutProof: MerkleProof) { + doModificationProof(key: BitSet, mutProof: MerkleProof) { let leftHeight = this.height - 1; if (this.leftChild instanceof Leaf) { @@ -432,25 +436,26 @@ export class Branch { } } - if (key.equals(this.leftChild.key)) { + if (key.equals(this.rightChild.key)) { + mutProof.intersectingHeight = this.height; + mutProof.startingSide = "left"; + this.leftChild.traverseRight(mutProof); + return; + } else if (key.equals(this.leftChild.key)) { mutProof.intersectingHeight = this.height; mutProof.startingSide = "right"; this.rightChild.traverseLeft(mutProof); - return; - } else if (key.equals(this.rightChild.key)) { - mutProof.intersectingHeight = this.height; - mutProof.startingSide = "left"; - this.leftChild.traverseRight(mutProof); return; } else if (leftHeight >= 0 && rightHeight < 0) { - this.leftChild.doMerkeProof(key, mutProof); + this.leftChild.doModificationProof(key, mutProof); if ( mutProof.startingSide === "left" && typeof mutProof.rightLeaf === "undefined" ) { this.rightChild.traverseLeft(mutProof); + mutProof.leftRightHeight = this.height; } else if ( mutProof.startingSide === "right" && typeof mutProof.leftLeaf === "undefined" @@ -468,13 +473,14 @@ export class Branch { } return; } else if (leftHeight < 0 && rightHeight >= 0) { - this.rightChild.doMerkeProof(key, mutProof); + this.rightChild.doModificationProof(key, mutProof); if ( mutProof.startingSide === "right" && typeof mutProof.leftLeaf === "undefined" ) { this.leftChild.traverseRight(mutProof); + mutProof.leftRightHeight = this.height; } else if ( mutProof.startingSide === "left" && typeof mutProof.rightLeaf === "undefined" @@ -508,6 +514,92 @@ export class Branch { mutProof.insertLeftProof(this.leftChild.getHash(), this.height); } + + doMemberProof(key: BitSet): [Uint8Array, number, Side][] { + let leftHeight = this.height - 1; + + if (this.leftChild instanceof Leaf) { + while (leftHeight > -1) { + if ( + key + .slice(leftHeight + 1) + .equals(this.leftChild.key.slice(leftHeight + 1)) + ) { + break; + } + + leftHeight--; + } + } else { + while (leftHeight >= this.leftChild.height) { + if ( + key + .slice(leftHeight + 1) + .equals( + this.leftChild.key.slice(leftHeight - this.leftChild.height) + ) + ) { + break; + } + + leftHeight--; + } + if (leftHeight < this.leftChild.height) { + leftHeight = -1; + } + } + + let rightHeight = this.height - 1; + + if (this.rightChild instanceof Leaf) { + while (rightHeight > -1) { + if ( + key + .slice(rightHeight + 1) + .equals(this.rightChild.key.slice(rightHeight + 1)) + ) { + break; + } + + rightHeight--; + } + } else { + while (rightHeight >= this.rightChild.height) { + if ( + key + .slice(rightHeight + 1) + .equals( + this.rightChild.key.slice(rightHeight - this.rightChild.height) + ) + ) { + break; + } + + rightHeight--; + } + if (rightHeight < this.rightChild.height) { + rightHeight = -1; + } + } + + if (key.equals(this.leftChild.key)) { + return [[this.rightChild.getHash(), this.height, "right"]]; + } else if (key.equals(this.rightChild.key)) { + return [[this.leftChild.getHash(), this.height, "left"]]; + } else if (leftHeight >= 0 && rightHeight < 0) { + return [ + [this.rightChild.getHash(), this.height, "right"], + ...this.leftChild.doMemberProof(key), + ]; + } else if (leftHeight < 0 && rightHeight >= 0) { + return [ + [this.leftChild.getHash(), this.height, "left"], + ...this.rightChild.doMemberProof(key), + ]; + } else { + throw new Error("Impossible"); + } + } } export class SparseMerkleTree extends Branch { @@ -533,7 +625,7 @@ export class SparseMerkleTree extends Branch { super.doInsert(initialKey, value); } - merkleProof(value: string | Buffer) { + modificationProof(value: string | Buffer) { const bufferValue: Uint8Array = typeof value == "string" ? new TextEncoder().encode(value) @@ -545,8 +637,21 @@ export class SparseMerkleTree extends Branch { let merkleProof = new MerkleProof(); - super.doMerkeProof(initialKey, merkleProof); + super.doModificationProof(initialKey, merkleProof); return merkleProof; } + + memberProof(value: string | Buffer) { + const bufferValue: Uint8Array = + typeof value == "string" + ? new TextEncoder().encode(value) + : new Uint8Array(value); + + const initialKey = new BitSet( + Buffer.from(blake2b(bufferValue, undefined, 32)).reverse() + ); + + return super.doMemberProof(initialKey).reverse(); + } }