From 3f1dc34ec6d999d47e2ea76d96842b056087c619 Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Thu, 4 Dec 2025 15:02:54 -0500 Subject: [PATCH 01/12] switch to keccak256 Signed-off-by: Jim Zhang --- contracts/lib/SmtLib.sol | 7 ++++--- helpers/DeployHelper.ts | 34 +++++----------------------------- ignition/modules/libraries.ts | 10 +--------- test/smtLib/smtLib.test.ts | 2 +- 4 files changed, 11 insertions(+), 42 deletions(-) diff --git a/contracts/lib/SmtLib.sol b/contracts/lib/SmtLib.sol index 809fb86c0..bd114b993 100644 --- a/contracts/lib/SmtLib.sol +++ b/contracts/lib/SmtLib.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity 0.8.27; -import {PoseidonUnit2L, PoseidonUnit3L} from "./Poseidon.sol"; import {ArrayUtils} from "./ArrayUtils.sol"; +import {console} from "hardhat/console.sol"; /// @title A sparse merkle tree implementation, which keeps tree history. // Note that this SMT implementation can manage duplicated roots in the history, @@ -567,9 +567,10 @@ library SmtLib { uint256 nodeHash = 0; if (node.nodeType == NodeType.LEAF) { uint256[3] memory params = [node.index, node.value, uint256(1)]; - nodeHash = PoseidonUnit3L.poseidon(params); + nodeHash = uint256(keccak256(abi.encode(params))); + console.log("leaf hash:", nodeHash); } else if (node.nodeType == NodeType.MIDDLE) { - nodeHash = PoseidonUnit2L.poseidon([node.childLeft, node.childRight]); + nodeHash = uint256(keccak256(abi.encode([node.childLeft, node.childRight]))); } return nodeHash; // Note: expected to return 0 if NodeType.EMPTY, which is the only option left } diff --git a/helpers/DeployHelper.ts b/helpers/DeployHelper.ts index 8931eb818..15c7d5130 100644 --- a/helpers/DeployHelper.ts +++ b/helpers/DeployHelper.ts @@ -35,7 +35,7 @@ export class DeployHelper { constructor( private signers: SignerWithAddress[], private readonly enableLogging: boolean = false, - ) {} + ) { } static async initialize( signers: SignerWithAddress[] | null = null, @@ -362,8 +362,6 @@ export class DeployHelper { } async deploySmtLib( - poseidon2Address: string, - poseidon3Address: string, contractName = contractsInfo.SMT_LIB.name, deployStrategy: "basic" | "create2" = "basic", ): Promise { @@ -379,23 +377,13 @@ export class DeployHelper { } const smtLibDeploy = await ignition.deploy(SmtLibModule, { - parameters: { - SmtLibModule: { - poseidon2ElementAddress: poseidon2Address, - poseidon3ElementAddress: poseidon3Address, - }, - }, + parameters: {}, strategy: deployStrategy, }); smtLib = smtLibDeploy.smtLib; } else { - smtLib = await ethers.deployContract(contractName, { - libraries: { - PoseidonUnit2L: poseidon2Address, - PoseidonUnit3L: poseidon3Address, - }, - }); + smtLib = await ethers.deployContract(contractName); } await smtLib.waitForDeployment(); @@ -415,13 +403,7 @@ export class DeployHelper { async deploySmtLibTestWrapper(maxDepth: number = SMT_MAX_DEPTH): Promise { const contractName = "SmtLibTestWrapper"; - this.log("deploying poseidons..."); - const [poseidon2Elements, poseidon3Elements] = await deployPoseidons([2, 3]); - - const smtLib = await this.deploySmtLib( - await poseidon2Elements.getAddress(), - await poseidon3Elements.getAddress(), - ); + const smtLib = await this.deploySmtLib(); const SmtWrapper = await ethers.getContractFactory(contractName, { libraries: { @@ -453,13 +435,7 @@ export class DeployHelper { } async deployBinarySearchTestWrapper(): Promise { - this.log("deploying poseidons..."); - const [poseidon2Elements, poseidon3Elements] = await deployPoseidons([2, 3]); - - const smtLib = await this.deploySmtLib( - await poseidon2Elements.getAddress(), - await poseidon3Elements.getAddress(), - ); + const smtLib = await this.deploySmtLib(); const bsWrapperName = "BinarySearchTestWrapper"; const BSWrapper = await ethers.getContractFactory(bsWrapperName, { diff --git a/ignition/modules/libraries.ts b/ignition/modules/libraries.ts index 61a8588fa..98e76b26e 100644 --- a/ignition/modules/libraries.ts +++ b/ignition/modules/libraries.ts @@ -99,15 +99,7 @@ export const Poseidon6Module = buildModule("Poseidon6Module", (m) => { }); export const SmtLibModule = buildModule("SmtLibModule", (m) => { - const poseidon2Element = m.useModule(Poseidon2AtModule).contract; - const poseidon3Element = m.useModule(Poseidon3AtModule).contract; - - const smtLib = m.contract("SmtLib", [], { - libraries: { - PoseidonUnit2L: poseidon2Element, - PoseidonUnit3L: poseidon3Element, - }, - }); + const smtLib = m.contract("SmtLib", [], {}); return { smtLib }; }); diff --git a/test/smtLib/smtLib.test.ts b/test/smtLib/smtLib.test.ts index be6181ec7..2ba240371 100644 --- a/test/smtLib/smtLib.test.ts +++ b/test/smtLib/smtLib.test.ts @@ -197,7 +197,7 @@ describe("Merkle tree proofs of SMT", () => { ]; for (const testCase of testCasesExistence) { - it(`${testCase.description}`, async () => { + it.only(`${testCase.description}`, async () => { await checkTestCaseMTPProof(smt, testCase); }); } From a60ef3e9eb307497924ab375352b1d659f9402ff Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Thu, 4 Dec 2025 16:52:58 -0500 Subject: [PATCH 02/12] remove debug logs Signed-off-by: Jim Zhang --- contracts/lib/SmtLib.sol | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/contracts/lib/SmtLib.sol b/contracts/lib/SmtLib.sol index bd114b993..47aaddc4a 100644 --- a/contracts/lib/SmtLib.sol +++ b/contracts/lib/SmtLib.sol @@ -2,7 +2,6 @@ pragma solidity 0.8.27; import {ArrayUtils} from "./ArrayUtils.sol"; -import {console} from "hardhat/console.sol"; /// @title A sparse merkle tree implementation, which keeps tree history. // Note that this SMT implementation can manage duplicated roots in the history, @@ -567,8 +566,8 @@ library SmtLib { uint256 nodeHash = 0; if (node.nodeType == NodeType.LEAF) { uint256[3] memory params = [node.index, node.value, uint256(1)]; - nodeHash = uint256(keccak256(abi.encode(params))); - console.log("leaf hash:", nodeHash); + bytes memory encoded = abi.encode(params); + nodeHash = uint256(keccak256(encoded)); } else if (node.nodeType == NodeType.MIDDLE) { nodeHash = uint256(keccak256(abi.encode([node.childLeft, node.childRight]))); } From e8ed9994d71fc91b557c0ec3f0482d6f9669eb1f Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Thu, 4 Dec 2025 16:53:15 -0500 Subject: [PATCH 03/12] fix unit tests Signed-off-by: Jim Zhang --- test/smtLib/smtLib.test.ts | 152 ++++++++++++++++++------------------- 1 file changed, 76 insertions(+), 76 deletions(-) diff --git a/test/smtLib/smtLib.test.ts b/test/smtLib/smtLib.test.ts index 2ba240371..d9a6c20fa 100644 --- a/test/smtLib/smtLib.test.ts +++ b/test/smtLib/smtLib.test.ts @@ -63,7 +63,7 @@ describe("Merkle tree proofs of SMT", () => { leavesToInsert: [{ i: 4, v: 444 }], paramsToGetProof: 4, expectedProof: { - root: "17172838131998611102390183760409471205043596092117126608119446264795219840387", + root: "22958977272721485097221938248834413051866334275107448764221195104671274302803", existence: true, siblings: Array(64).fill(0) as FixedArray, index: 4, @@ -81,11 +81,11 @@ describe("Merkle tree proofs of SMT", () => { ], paramsToGetProof: 2, expectedProof: { - root: "1441373283294527316959936912733986290796958290497398831120725405602534136472", + root: "6271825503835002167390262846571952849048843004006797408985626104559410349007", existence: true, siblings: [ "0", - "17172838131998611102390183760409471205043596092117126608119446264795219840387", + "22958977272721485097221938248834413051866334275107448764221195104671274302803", ].concat(Array(62).fill(0)) as FixedArray, index: 2, value: 222, @@ -104,11 +104,11 @@ describe("Merkle tree proofs of SMT", () => { ], paramsToGetProof: 4, expectedProof: { - root: "7518984336464932918389970949562858717786148793994477177454424989320848411811", + root: "16396604133323839338919891555968694657750322967047028825382984961917414135680", existence: true, siblings: [ "0", - "14251506067749311748434684987325372940957929637576367655195798776182705044439", + "106205607234728094801824820546945464662359795328028380050005012207955127231073", ].concat(Array(62).fill(0)) as FixedArray, index: 4, value: 444, @@ -127,11 +127,11 @@ describe("Merkle tree proofs of SMT", () => { ], paramsToGetProof: 2, expectedProof: { - root: "7518984336464932918389970949562858717786148793994477177454424989320848411811", + root: "16396604133323839338919891555968694657750322967047028825382984961917414135680", existence: true, siblings: [ "0", - "17172838131998611102390183760409471205043596092117126608119446264795219840387", + "22958977272721485097221938248834413051866334275107448764221195104671274302803", ].concat(Array(62).fill(0)) as FixedArray, index: 2, value: 223, @@ -151,14 +151,14 @@ describe("Merkle tree proofs of SMT", () => { paramsToGetProof: { index: 2, historicalRoot: - "1441373283294527316959936912733986290796958290497398831120725405602534136472", + "6271825503835002167390262846571952849048843004006797408985626104559410349007", }, expectedProof: { - root: "1441373283294527316959936912733986290796958290497398831120725405602534136472", + root: "6271825503835002167390262846571952849048843004006797408985626104559410349007", existence: true, siblings: [ "0", - "17172838131998611102390183760409471205043596092117126608119446264795219840387", + "22958977272721485097221938248834413051866334275107448764221195104671274302803", ].concat(Array(62).fill(0)) as FixedArray, index: 2, value: 222, @@ -178,14 +178,14 @@ describe("Merkle tree proofs of SMT", () => { paramsToGetProof: { index: 4, historicalRoot: - "1441373283294527316959936912733986290796958290497398831120725405602534136472", + "6271825503835002167390262846571952849048843004006797408985626104559410349007", }, expectedProof: { - root: "1441373283294527316959936912733986290796958290497398831120725405602534136472", + root: "6271825503835002167390262846571952849048843004006797408985626104559410349007", existence: true, siblings: [ "0", - "7886566820534140840061358290700879102455368051640197098120169021365756575690", + "78830676469618643418259161717399883581838239495817397573365812847452816685443", ].concat(Array(62).fill(0)) as FixedArray, index: 4, value: 444, @@ -197,7 +197,7 @@ describe("Merkle tree proofs of SMT", () => { ]; for (const testCase of testCasesExistence) { - it.only(`${testCase.description}`, async () => { + it(`${testCase.description}`, async () => { await checkTestCaseMTPProof(smt, testCase); }); } @@ -210,7 +210,7 @@ describe("Merkle tree proofs of SMT", () => { leavesToInsert: [{ i: 3, v: 333 }], paramsToGetProof: 3, expectedProof: { - root: "9620424510282781520312293538235812893148558849034106480402397875614354541113", + root: "33418405206138732565596445817172696059570130655374283008161682599505407512429", existence: true, siblings: Array(64).fill(0) as FixedArray, index: "3", @@ -228,12 +228,12 @@ describe("Merkle tree proofs of SMT", () => { ], paramsToGetProof: 7, expectedProof: { - root: "19815655640973429763502848653182332850553075596353874436508539687379197912551", + root: "48360753217216801628383385897610760751395521399060591925309269460841854478880", existence: true, siblings: [ "0", "0", - "9620424510282781520312293538235812893148558849034106480402397875614354541113", + "33418405206138732565596445817172696059570130655374283008161682599505407512429", ].concat(Array(61).fill(0)) as FixedArray, index: "7", value: "777", @@ -252,12 +252,12 @@ describe("Merkle tree proofs of SMT", () => { ], paramsToGetProof: 3, expectedProof: { - root: "2542404438766480113585642347874916876260762595281604113407869433952183945353", + root: "99343494309985223004431568049537972373969240419555846106086358328736705856273", existence: true, siblings: [ "0", "0", - "1429787978940724228837527260031251962874080759861304177793880818323589539601", + "112313283557488934319533843029412977758210563652664683830759028786502416187169", ].concat(Array(61).fill(0)) as FixedArray, index: "3", value: "333", @@ -276,12 +276,12 @@ describe("Merkle tree proofs of SMT", () => { ], paramsToGetProof: 7, expectedProof: { - root: "2542404438766480113585642347874916876260762595281604113407869433952183945353", + root: "99343494309985223004431568049537972373969240419555846106086358328736705856273", existence: true, siblings: [ "0", "0", - "9620424510282781520312293538235812893148558849034106480402397875614354541113", + "33418405206138732565596445817172696059570130655374283008161682599505407512429", ].concat(Array(61).fill(0)) as FixedArray, index: "7", value: "778", @@ -301,15 +301,15 @@ describe("Merkle tree proofs of SMT", () => { paramsToGetProof: { index: 3, historicalRoot: - "19815655640973429763502848653182332850553075596353874436508539687379197912551", + "48360753217216801628383385897610760751395521399060591925309269460841854478880", }, expectedProof: { - root: "19815655640973429763502848653182332850553075596353874436508539687379197912551", + root: "48360753217216801628383385897610760751395521399060591925309269460841854478880", existence: true, siblings: [ "0", "0", - "5240534091252349892032931504453574475032932996013327005816531601253770276629", + "50296473614243876656937981110997905853887938112796700896791687746944667051313", ].concat(Array(61).fill(0)) as FixedArray, index: "3", value: "333", @@ -329,15 +329,15 @@ describe("Merkle tree proofs of SMT", () => { paramsToGetProof: { index: 7, historicalRoot: - "19815655640973429763502848653182332850553075596353874436508539687379197912551", + "48360753217216801628383385897610760751395521399060591925309269460841854478880", }, expectedProof: { - root: "19815655640973429763502848653182332850553075596353874436508539687379197912551", + root: "48360753217216801628383385897610760751395521399060591925309269460841854478880", existence: true, siblings: [ "0", "0", - "9620424510282781520312293538235812893148558849034106480402397875614354541113", + "33418405206138732565596445817172696059570130655374283008161682599505407512429", ].concat(Array(61).fill(0)) as FixedArray, index: "7", value: "777", @@ -404,14 +404,14 @@ describe("Merkle tree proofs of SMT", () => { paramsToGetProof: "2254139687286372760549210172096572575821880629072851135313477335313002867070", expectedProof: { - root: "13608938109359425943273886683542924994850927952989113192708029670282368959472", + root: "60007161943983486146906500160381123178622458462493617850821425381979612616956", existence: true, siblings: [ - "1832641583235778429809211853568910873051692053406604919942416271965516221694", - "7178355728345475638578628524851385851849048771654648953856812774555221490254", - "9602796824988200934471038492033878534627864374776542278379449014085059916942", + "11842043060776711430669533915732787960772621560012996635304388961537219372006", + "58180682306773137243712131495542060329763268994690040321062652108869108341898", + "94191217124907006392892149230646386351460750107001924913640299034519044572186", "0", - "16358410446199419264933021028144760440785144596817177810806370009968803152521", + "68634643421786404149040398078509254181052706327147925186290660391175330864565", ].concat(Array(59).fill(0)) as FixedArray, index: "2254139687286372760549210172096572575821880629072851135313477335313002867070", value: "2254139687286372760549210172096572575821880629072851135313477335313002867070", @@ -438,7 +438,7 @@ describe("Merkle tree proofs of SMT", () => { leavesToInsert: [{ i: 4, v: 444 }], paramsToGetProof: 2, expectedProof: { - root: "17172838131998611102390183760409471205043596092117126608119446264795219840387", + root: "22958977272721485097221938248834413051866334275107448764221195104671274302803", existence: false, siblings: Array(64).fill(0) as FixedArray, index: 2, @@ -457,11 +457,11 @@ describe("Merkle tree proofs of SMT", () => { ], paramsToGetProof: 6, expectedProof: { - root: "1441373283294527316959936912733986290796958290497398831120725405602534136472", + root: "6271825503835002167390262846571952849048843004006797408985626104559410349007", existence: false, siblings: [ "0", - "17172838131998611102390183760409471205043596092117126608119446264795219840387", + "22958977272721485097221938248834413051866334275107448764221195104671274302803", ].concat(Array(62).fill(0)) as FixedArray, index: 6, value: 222, @@ -479,10 +479,10 @@ describe("Merkle tree proofs of SMT", () => { ], paramsToGetProof: 1, expectedProof: { - root: "1441373283294527316959936912733986290796958290497398831120725405602534136472", + root: "6271825503835002167390262846571952849048843004006797408985626104559410349007", existence: false, siblings: [ - "6675047397658061825643898157145998146182607268727302490292227324666463200032", + "96535818096110143691607859947815292580929567133286005889885442808119755306104", ].concat(Array(63).fill(0)) as FixedArray, index: 1, value: 0, @@ -502,14 +502,14 @@ describe("Merkle tree proofs of SMT", () => { paramsToGetProof: { index: 6, historicalRoot: - "1441373283294527316959936912733986290796958290497398831120725405602534136472", + "6271825503835002167390262846571952849048843004006797408985626104559410349007", }, expectedProof: { - root: "1441373283294527316959936912733986290796958290497398831120725405602534136472", + root: "6271825503835002167390262846571952849048843004006797408985626104559410349007", existence: false, siblings: [ "0", - "17172838131998611102390183760409471205043596092117126608119446264795219840387", + "22958977272721485097221938248834413051866334275107448764221195104671274302803", ].concat(Array(62).fill(0)) as FixedArray, index: 6, value: 222, @@ -529,13 +529,13 @@ describe("Merkle tree proofs of SMT", () => { paramsToGetProof: { index: 1, historicalRoot: - "1441373283294527316959936912733986290796958290497398831120725405602534136472", + "6271825503835002167390262846571952849048843004006797408985626104559410349007", }, expectedProof: { - root: "1441373283294527316959936912733986290796958290497398831120725405602534136472", + root: "6271825503835002167390262846571952849048843004006797408985626104559410349007", existence: false, siblings: [ - "6675047397658061825643898157145998146182607268727302490292227324666463200032", + "96535818096110143691607859947815292580929567133286005889885442808119755306104", ].concat(Array(63).fill(0)) as FixedArray, index: 1, value: 0, @@ -555,13 +555,13 @@ describe("Merkle tree proofs of SMT", () => { paramsToGetProof: { index: 1, historicalRoot: - "1441373283294527316959936912733986290796958290497398831120725405602534136472", + "6271825503835002167390262846571952849048843004006797408985626104559410349007", }, expectedProof: { - root: "1441373283294527316959936912733986290796958290497398831120725405602534136472", + root: "6271825503835002167390262846571952849048843004006797408985626104559410349007", existence: false, siblings: [ - "6675047397658061825643898157145998146182607268727302490292227324666463200032", + "96535818096110143691607859947815292580929567133286005889885442808119755306104", ].concat(Array(63).fill(0)) as FixedArray, index: 1, value: 0, @@ -586,7 +586,7 @@ describe("Merkle tree proofs of SMT", () => { leavesToInsert: [{ i: 3, v: 333 }], paramsToGetProof: 7, expectedProof: { - root: "9620424510282781520312293538235812893148558849034106480402397875614354541113", + root: "33418405206138732565596445817172696059570130655374283008161682599505407512429", existence: false, siblings: Array(64).fill(0) as FixedArray, index: "7", @@ -605,12 +605,12 @@ describe("Merkle tree proofs of SMT", () => { ], paramsToGetProof: 11, expectedProof: { - root: "19815655640973429763502848653182332850553075596353874436508539687379197912551", + root: "48360753217216801628383385897610760751395521399060591925309269460841854478880", existence: false, siblings: [ "0", "0", - "5240534091252349892032931504453574475032932996013327005816531601253770276629", + "50296473614243876656937981110997905853887938112796700896791687746944667051313", ].concat(Array(61).fill(0)) as FixedArray, index: "11", value: "333", @@ -628,11 +628,11 @@ describe("Merkle tree proofs of SMT", () => { ], paramsToGetProof: 1, expectedProof: { - root: "19815655640973429763502848653182332850553075596353874436508539687379197912551", + root: "48360753217216801628383385897610760751395521399060591925309269460841854478880", existence: false, siblings: [ "0", - "26063976833489350915848330858375580362565300311897865524107747624425916356", + "112876657748891444100900801150886368101744673616075342940779383085926542312690", ].concat(Array(62).fill(0)) as FixedArray, index: "1", value: "0", @@ -652,15 +652,15 @@ describe("Merkle tree proofs of SMT", () => { paramsToGetProof: { index: 11, historicalRoot: - "19815655640973429763502848653182332850553075596353874436508539687379197912551", + "48360753217216801628383385897610760751395521399060591925309269460841854478880", }, expectedProof: { - root: "19815655640973429763502848653182332850553075596353874436508539687379197912551", + root: "48360753217216801628383385897610760751395521399060591925309269460841854478880", existence: false, siblings: [ "0", "0", - "5240534091252349892032931504453574475032932996013327005816531601253770276629", + "50296473614243876656937981110997905853887938112796700896791687746944667051313", ].concat(Array(61).fill(0)) as FixedArray, index: "11", value: "333", @@ -680,14 +680,14 @@ describe("Merkle tree proofs of SMT", () => { paramsToGetProof: { index: 1, historicalRoot: - "19815655640973429763502848653182332850553075596353874436508539687379197912551", + "48360753217216801628383385897610760751395521399060591925309269460841854478880", }, expectedProof: { - root: "19815655640973429763502848653182332850553075596353874436508539687379197912551", + root: "48360753217216801628383385897610760751395521399060591925309269460841854478880", existence: false, siblings: [ "0", - "26063976833489350915848330858375580362565300311897865524107747624425916356", + "112876657748891444100900801150886368101744673616075342940779383085926542312690", ].concat(Array(62).fill(0)) as FixedArray, index: "1", value: "0", @@ -707,15 +707,15 @@ describe("Merkle tree proofs of SMT", () => { paramsToGetProof: { index: 11, historicalRoot: - "19815655640973429763502848653182332850553075596353874436508539687379197912551", + "48360753217216801628383385897610760751395521399060591925309269460841854478880", }, expectedProof: { - root: "19815655640973429763502848653182332850553075596353874436508539687379197912551", + root: "48360753217216801628383385897610760751395521399060591925309269460841854478880", existence: false, siblings: [ "0", "0", - "5240534091252349892032931504453574475032932996013327005816531601253770276629", + "50296473614243876656937981110997905853887938112796700896791687746944667051313", ].concat(Array(61).fill(0)) as FixedArray, index: "11", value: "333", @@ -782,11 +782,11 @@ describe("Merkle tree proofs of SMT", () => { paramsToGetProof: "2254139687286372760549210172096572575821880629072851135313477335313002867071", expectedProof: { - root: "13608938109359425943273886683542924994850927952989113192708029670282368959472", + root: "60007161943983486146906500160381123178622458462493617850821425381979612616956", existence: false, siblings: [ - "1579434795526423183097986076173558337173432003423506163175532158546629036074", - "4682852777402635256724726626165554137517366900378681615797410665482859853011", + "15649299891651963645168066741171440452486996543233903096221914286509395708217", + "35485910319040178947164101851755181176671699873501710834388743638308164334229", ].concat(Array(62).fill(0)) as FixedArray, index: "2254139687286372760549210172096572575821880629072851135313477335313002867071", value: "6710060555229139303017247577694107284750887011584715720178646167607892089915", @@ -844,12 +844,12 @@ describe("Merkle tree proofs of SMT", () => { paramsToGetProof: "6271287741236698691604141726361751264311688318470481595940384433868807274649", expectedProof: { - root: "13608938109359425943273886683542924994850927952989113192708029670282368959472", + root: "60007161943983486146906500160381123178622458462493617850821425381979612616956", existence: false, siblings: [ - "1579434795526423183097986076173558337173432003423506163175532158546629036074", - "2087966847430044349684271178373838655869903749020106568902582482402101627428", - "4559542841575065171721871277134371244969805411208646727128331102091234595131", + "15649299891651963645168066741171440452486996543233903096221914286509395708217", + "5582158759613617994025042938199822589967969783458491971055101796671660471167", + "73073270877252609309304544688345883755735059749695338671799354601148846061501", ].concat(Array(61).fill(0)) as FixedArray, index: "6271287741236698691604141726361751264311688318470481595940384433868807274649", value: "0", @@ -919,12 +919,12 @@ describe("Merkle tree proofs of SMT", () => { ], paramsToGetProof: genMaxBinaryNumber(64), expectedProof: { - root: "11998361913555620744473305594791175460338619045531124782442564216176360071119", + root: "110199947410708031149228851387837770054145252372569190168540009983771486866316", existence: true, siblings: Array(63) .fill("0") .concat([ - "2316164946517152574748505824782744746774130618858955093234986590959173249001", + "74399159356430181752973204803851211752558544080760906099963521203069195578461", ]) as FixedArray, index: "18446744073709551615", value: "100", @@ -941,12 +941,12 @@ describe("Merkle tree proofs of SMT", () => { ], paramsToGetProof: genMaxBinaryNumber(63) + BigInt(1), expectedProof: { - root: "7851364894145224193468155117213470810715599698407298245809392679874651946419", + root: "16625293229838138370855084455911863446233219628941478502997473061463286175203", existence: true, siblings: Array(63) .fill("0") .concat([ - "1321531033810699781922362637795367691578399901805457949741207048379959301312", + "85255324799129495636878324767942437404808863678639087371922338002905282669100", ]) as FixedArray, index: "9223372036854775808", value: "100", @@ -964,12 +964,12 @@ describe("Merkle tree proofs of SMT", () => { ], paramsToGetProof: "8490314929315140110", expectedProof: { - root: "5640762368545907066458698273870257445508350556310355422307954953617544677976", + root: "16484897135457263633438825596660941524517623843033410328799978262525614569225", existence: true, siblings: Array(63) .fill("0") .concat([ - "21059535177784591611482142343728384369736848354398899541533132315810203341674", + "9906144696231549323869068974423946066277130279313871204886462229234859073912", ]) as FixedArray, index: "8490314929315140110", value: "100", @@ -1401,7 +1401,7 @@ describe("Binary search in SMT root history", () => { ]; for (const tc of testCase) { - it(`${tc.description}`, async () => { + it.only(`${tc.description}`, async () => { await checkRootByTimeAndBlock(rootEntries, tc); }); } @@ -1707,7 +1707,7 @@ describe("Binary search in SMT proofs", () => { timestamp: 0, }, expectedProof: { - root: "17172838131998611102390183760409471205043596092117126608119446264795219840387", + root: "22958977272721485097221938248834413051866334275107448764221195104671274302803", existence: true, siblings: Array(64).fill(0) as FixedArray, index: 4, @@ -1725,7 +1725,7 @@ describe("Binary search in SMT proofs", () => { blockNumber: 0, }, expectedProof: { - root: "17172838131998611102390183760409471205043596092117126608119446264795219840387", + root: "22958977272721485097221938248834413051866334275107448764221195104671274302803", existence: true, siblings: Array(64).fill(0) as FixedArray, index: 4, From 94a535f4961da54e2dadcbedb7aeedb603799fc8 Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Thu, 4 Dec 2025 17:00:30 -0500 Subject: [PATCH 04/12] fix unit test failures due to not deploying poseidon contracts Signed-off-by: Jim Zhang --- test/smtLib/smtLib.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/smtLib/smtLib.test.ts b/test/smtLib/smtLib.test.ts index d9a6c20fa..b74746c25 100644 --- a/test/smtLib/smtLib.test.ts +++ b/test/smtLib/smtLib.test.ts @@ -1233,7 +1233,7 @@ describe("Binary search in SMT root history", () => { beforeEach(async () => { await loadFixture(deployContractsFixture); const { number: latestBlockNumber } = await hre.ethers.provider.getBlock("latest"); - let blocksToMine = 15 - latestBlockNumber; + let blocksToMine = 17 - latestBlockNumber; while (blocksToMine > 0) { await hre.network.provider.request({ @@ -1401,7 +1401,7 @@ describe("Binary search in SMT root history", () => { ]; for (const tc of testCase) { - it.only(`${tc.description}`, async () => { + it(`${tc.description}`, async () => { await checkRootByTimeAndBlock(rootEntries, tc); }); } From 27784d60bf1f6a1962680d434983406eeb788225 Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Fri, 5 Dec 2025 13:28:43 -0500 Subject: [PATCH 05/12] make the hashing algorithm in SmtLib configurable Signed-off-by: Jim Zhang --- contracts/interfaces/IHasher.sol | 21 + contracts/lib/SmtLib.sol | 41 +- contracts/lib/hash/KeccakHasher.sol | 17 + .../test-helpers/SmtLibKeccakTestWrapper.sol | 78 + helpers/DeployHelper.ts | 39 +- ignition/modules/libraries.ts | 10 +- test/smtLib/smtLib.keccak.test.ts | 1908 +++++++++++++++++ test/smtLib/smtLib.test.ts | 150 +- 8 files changed, 2172 insertions(+), 92 deletions(-) create mode 100644 contracts/interfaces/IHasher.sol create mode 100644 contracts/lib/hash/KeccakHasher.sol create mode 100644 contracts/test-helpers/SmtLibKeccakTestWrapper.sol create mode 100644 test/smtLib/smtLib.keccak.test.ts diff --git a/contracts/interfaces/IHasher.sol b/contracts/interfaces/IHasher.sol new file mode 100644 index 000000000..2d62001ff --- /dev/null +++ b/contracts/interfaces/IHasher.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity 0.8.27; + +/** + * @dev IHasher. Interface for generating hashes. Specifically used for Merkle Tree hashing. + */ +abstract contract IHasher { + /** + * @dev hash2. hashes two uint256 parameters and returns the resulting hash as uint256. + * @param params The parameters array of size 2 to be hashed. + * @return The resulting hash as uint256. + */ + function hash2(uint256[2] memory params) external pure virtual returns (uint256); + + /** + * @dev hash3. hashes three uint256 parameters and returns the resulting hash as uint256. + * @param params The parameters array of size 3 to be hashed. + * @return The resulting hash as uint256. + */ + function hash3(uint256[3] memory params) external pure virtual returns (uint256); +} diff --git a/contracts/lib/SmtLib.sol b/contracts/lib/SmtLib.sol index 47aaddc4a..107ee241e 100644 --- a/contracts/lib/SmtLib.sol +++ b/contracts/lib/SmtLib.sol @@ -2,6 +2,8 @@ pragma solidity 0.8.27; import {ArrayUtils} from "./ArrayUtils.sol"; +import {PoseidonUnit2L, PoseidonUnit3L} from "./Poseidon.sol"; +import {IHasher} from "../interfaces/IHasher.sol"; /// @title A sparse merkle tree implementation, which keeps tree history. // Note that this SMT implementation can manage duplicated roots in the history, @@ -53,6 +55,8 @@ library SmtLib { // storage of upgradable contracts that use this struct as a state variable // (see https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#storage-gaps) uint256[45] __gap; + bool isCustomHasherSet; + IHasher hasher; } /** @@ -135,6 +139,15 @@ library SmtLib { _; } + /** + * @dev Sets custom hashers for the SMT. MUST be called before any other SMT operations. + * @param customerHasher IHasher implementation to be used for hashing. + */ + function setHasher(Data storage self, IHasher customerHasher) external { + self.isCustomHasherSet = true; + self.hasher = customerHasher; + } + /** * @dev Add a leaf to the SMT * @param i Index of a leaf @@ -525,16 +538,16 @@ library SmtLib { if (newLeafBitAtDepth) { newNodeMiddle = Node({ nodeType: NodeType.MIDDLE, - childLeft: _getNodeHash(oldLeaf), - childRight: _getNodeHash(newLeaf), + childLeft: _getNodeHash(self, oldLeaf), + childRight: _getNodeHash(self, newLeaf), index: 0, value: 0 }); } else { newNodeMiddle = Node({ nodeType: NodeType.MIDDLE, - childLeft: _getNodeHash(newLeaf), - childRight: _getNodeHash(oldLeaf), + childLeft: _getNodeHash(self, newLeaf), + childRight: _getNodeHash(self, oldLeaf), index: 0, value: 0 }); @@ -545,7 +558,7 @@ library SmtLib { } function _addNode(Data storage self, Node memory node) internal returns (uint256) { - uint256 nodeHash = _getNodeHash(node); + uint256 nodeHash = _getNodeHash(self, node); // We don't have any guarantees if the hash function attached is good enough. // So, if the node hash already exists, we need to check // if the node in the tree exactly matches the one we are trying to add. @@ -562,14 +575,22 @@ library SmtLib { return nodeHash; } - function _getNodeHash(Node memory node) internal pure returns (uint256) { + function _getNodeHash(Data storage self, Node memory node) internal view returns (uint256) { uint256 nodeHash = 0; if (node.nodeType == NodeType.LEAF) { - uint256[3] memory params = [node.index, node.value, uint256(1)]; - bytes memory encoded = abi.encode(params); - nodeHash = uint256(keccak256(encoded)); + if (self.isCustomHasherSet) { + uint256[3] memory params = [node.index, node.value, uint256(1)]; + nodeHash = self.hasher.hash3(params); + } else { + uint256[3] memory params = [node.index, node.value, uint256(1)]; + nodeHash = PoseidonUnit3L.poseidon(params); + } } else if (node.nodeType == NodeType.MIDDLE) { - nodeHash = uint256(keccak256(abi.encode([node.childLeft, node.childRight]))); + if (self.isCustomHasherSet) { + nodeHash = self.hasher.hash2([node.childLeft, node.childRight]); + } else { + nodeHash = PoseidonUnit2L.poseidon([node.childLeft, node.childRight]); + } } return nodeHash; // Note: expected to return 0 if NodeType.EMPTY, which is the only option left } diff --git a/contracts/lib/hash/KeccakHasher.sol b/contracts/lib/hash/KeccakHasher.sol new file mode 100644 index 000000000..5f22d3564 --- /dev/null +++ b/contracts/lib/hash/KeccakHasher.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity 0.8.27; + +import {IHasher} from "../../interfaces/IHasher.sol"; + +/// @title A IHasher implementation using Keccak256. +contract Keccak256Hasher is IHasher { + function hash2(uint256[2] memory params) external pure override returns (uint256) { + bytes memory encoded = abi.encode(params); + return uint256(keccak256(encoded)); + } + + function hash3(uint256[3] memory params) external pure override returns (uint256) { + bytes memory encoded = abi.encode(params); + return uint256(keccak256(encoded)); + } +} diff --git a/contracts/test-helpers/SmtLibKeccakTestWrapper.sol b/contracts/test-helpers/SmtLibKeccakTestWrapper.sol new file mode 100644 index 000000000..58dccb457 --- /dev/null +++ b/contracts/test-helpers/SmtLibKeccakTestWrapper.sol @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity 0.8.27; + +import {SmtLib} from "../lib/SmtLib.sol"; +import {Keccak256Hasher} from "../lib/hash/KeccakHasher.sol"; + +contract SmtLibKeccakTestWrapper { + using SmtLib for SmtLib.Data; + + SmtLib.Data internal smtData; + + constructor(uint256 maxDepth) { + smtData.initialize(maxDepth); + smtData.setHasher(new Keccak256Hasher()); + } + + function add(uint256 i, uint256 v) public { + smtData.addLeaf(i, v); + } + + function getProof(uint256 id) public view returns (SmtLib.Proof memory) { + return smtData.getProof(id); + } + + function getProofByRoot(uint256 id, uint256 root) public view returns (SmtLib.Proof memory) { + return smtData.getProofByRoot(id, root); + } + + function getProofByTime( + uint256 id, + uint256 timestamp + ) public view returns (SmtLib.Proof memory) { + return smtData.getProofByTime(id, timestamp); + } + + function getProofByBlock(uint256 id, uint256 _block) public view returns (SmtLib.Proof memory) { + return smtData.getProofByBlock(id, _block); + } + + function getRootHistory( + uint256 start, + uint256 length + ) public view returns (SmtLib.RootEntryInfo[] memory) { + return smtData.getRootHistory(start, length); + } + + function getRootHistoryLength() public view returns (uint256) { + return smtData.getRootHistoryLength(); + } + + function getRoot() public view returns (uint256) { + return smtData.getRoot(); + } + + function getRootInfo(uint256 root) public view returns (SmtLib.RootEntryInfo memory) { + return smtData.getRootInfo(root); + } + + function getRootInfoListLengthByRoot(uint256 root) public view returns (uint256) { + return smtData.getRootInfoListLengthByRoot(root); + } + + function getRootInfoListByRoot( + uint256 root, + uint256 start, + uint256 length + ) public view returns (SmtLib.RootEntryInfo[] memory) { + return smtData.getRootInfoListByRoot(root, start, length); + } + + function setMaxDepth(uint256 maxDepth) public { + smtData.setMaxDepth(maxDepth); + } + + function getMaxDepth() public view returns (uint256) { + return smtData.getMaxDepth(); + } +} diff --git a/helpers/DeployHelper.ts b/helpers/DeployHelper.ts index 15c7d5130..2b24fdfab 100644 --- a/helpers/DeployHelper.ts +++ b/helpers/DeployHelper.ts @@ -362,6 +362,8 @@ export class DeployHelper { } async deploySmtLib( + poseidon2Address: string, + poseidon3Address: string, contractName = contractsInfo.SMT_LIB.name, deployStrategy: "basic" | "create2" = "basic", ): Promise { @@ -377,13 +379,23 @@ export class DeployHelper { } const smtLibDeploy = await ignition.deploy(SmtLibModule, { - parameters: {}, + parameters: { + SmtLibModule: { + poseidon2ElementAddress: poseidon2Address, + poseidon3ElementAddress: poseidon3Address, + }, + }, strategy: deployStrategy, }); smtLib = smtLibDeploy.smtLib; } else { - smtLib = await ethers.deployContract(contractName); + smtLib = await ethers.deployContract(contractName, { + libraries: { + PoseidonUnit2L: poseidon2Address, + PoseidonUnit3L: poseidon3Address, + }, + }); } await smtLib.waitForDeployment(); @@ -400,10 +412,19 @@ export class DeployHelper { return stateLib; } - async deploySmtLibTestWrapper(maxDepth: number = SMT_MAX_DEPTH): Promise { - const contractName = "SmtLibTestWrapper"; + async deploySmtLibTestWrapper( + maxDepth: number = SMT_MAX_DEPTH, + useKeccakHashing: boolean = false, + ): Promise { + const contractName = useKeccakHashing ? "SmtLibKeccakTestWrapper" : "SmtLibTestWrapper"; + + this.log("deploying poseidons..."); + const [poseidon2Elements, poseidon3Elements] = await deployPoseidons([2, 3]); - const smtLib = await this.deploySmtLib(); + const smtLib = await this.deploySmtLib( + await poseidon2Elements.getAddress(), + await poseidon3Elements.getAddress(), + ); const SmtWrapper = await ethers.getContractFactory(contractName, { libraries: { @@ -435,7 +456,13 @@ export class DeployHelper { } async deployBinarySearchTestWrapper(): Promise { - const smtLib = await this.deploySmtLib(); + this.log("deploying poseidons..."); + const [poseidon2Elements, poseidon3Elements] = await deployPoseidons([2, 3]); + + const smtLib = await this.deploySmtLib( + await poseidon2Elements.getAddress(), + await poseidon3Elements.getAddress(), + ); const bsWrapperName = "BinarySearchTestWrapper"; const BSWrapper = await ethers.getContractFactory(bsWrapperName, { diff --git a/ignition/modules/libraries.ts b/ignition/modules/libraries.ts index 98e76b26e..61a8588fa 100644 --- a/ignition/modules/libraries.ts +++ b/ignition/modules/libraries.ts @@ -99,7 +99,15 @@ export const Poseidon6Module = buildModule("Poseidon6Module", (m) => { }); export const SmtLibModule = buildModule("SmtLibModule", (m) => { - const smtLib = m.contract("SmtLib", [], {}); + const poseidon2Element = m.useModule(Poseidon2AtModule).contract; + const poseidon3Element = m.useModule(Poseidon3AtModule).contract; + + const smtLib = m.contract("SmtLib", [], { + libraries: { + PoseidonUnit2L: poseidon2Element, + PoseidonUnit3L: poseidon3Element, + }, + }); return { smtLib }; }); diff --git a/test/smtLib/smtLib.keccak.test.ts b/test/smtLib/smtLib.keccak.test.ts new file mode 100644 index 000000000..9ec886eff --- /dev/null +++ b/test/smtLib/smtLib.keccak.test.ts @@ -0,0 +1,1908 @@ +import { expect } from "chai"; +import hre from "hardhat"; + +import { addLeaf, FixedArray, genMaxBinaryNumber, MtpProof } from "../utils/state-utils"; +import { DeployHelper } from "../../helpers/DeployHelper"; +import { Contract } from "ethers"; +import { loadFixture } from "@nomicfoundation/hardhat-toolbox/network-helpers"; + +type ParamsProofByHistoricalRoot = { + index: number | bigint | string; + historicalRoot: number | string; +}; +type ParamsProofByBlock = { index: number | bigint | string; blockNumber: number | string }; +type ParamsProofByTime = { index: number | bigint | string; timestamp: number | string }; + +type ParamsProof = + | number + | bigint + | string + | ParamsProofByHistoricalRoot + | ParamsProofByBlock + | ParamsProofByTime + | undefined; + +type TestCaseMTPProof = { + leavesToInsert: { i: number | bigint | string; v: number | bigint | string; error?: string }[]; + paramsToGetProof?: ParamsProof; + expectedProof?: MtpProof; + [key: string]: any; +}; + +type RootEntry = { + timestamp: number; + block: number; + root: number; +}; + +type TestCaseRootHistory = { + description: string; + timestamp: number; + blockNumber: number; + expectedRoot: number; + [key: string]: any; +}; + +describe("Merkle tree proofs of SMT", () => { + let smt; + + async function deployContractsFixture() { + const deployHelper = await DeployHelper.initialize(); + const SMT_MAX_DEPTH = 64; + smt = await deployHelper.deploySmtLibTestWrapper(SMT_MAX_DEPTH, true); + } + + beforeEach(async () => { + await loadFixture(deployContractsFixture); + }); + + describe("SMT existence proof", () => { + describe("keys 4 (100), 2 (010)", () => { + const testCasesExistence: TestCaseMTPProof[] = [ + { + description: "add 1 leaf and generate the proof for it", + leavesToInsert: [{ i: 4, v: 444 }], + paramsToGetProof: 4, + expectedProof: { + root: "22958977272721485097221938248834413051866334275107448764221195104671274302803", + existence: true, + siblings: Array(64).fill(0) as FixedArray, + index: 4, + value: 444, + auxExistence: false, + auxIndex: 0, + auxValue: 0, + }, + }, + { + description: "add 2 leaves (depth = 2) and generate the proof of the second one", + leavesToInsert: [ + { i: 4, v: 444 }, + { i: 2, v: 222 }, + ], + paramsToGetProof: 2, + expectedProof: { + root: "6271825503835002167390262846571952849048843004006797408985626104559410349007", + existence: true, + siblings: [ + "0", + "22958977272721485097221938248834413051866334275107448764221195104671274302803", + ].concat(Array(62).fill(0)) as FixedArray, + index: 2, + value: 222, + auxExistence: false, + auxIndex: 0, + auxValue: 0, + }, + }, + { + description: + "add 2 leaves (depth = 2) update 2nd one and generate the proof of the first one", + leavesToInsert: [ + { i: 4, v: 444 }, + { i: 2, v: 222 }, + { i: 2, v: 223 }, + ], + paramsToGetProof: 4, + expectedProof: { + root: "16396604133323839338919891555968694657750322967047028825382984961917414135680", + existence: true, + siblings: [ + "0", + "106205607234728094801824820546945464662359795328028380050005012207955127231073", + ].concat(Array(62).fill(0)) as FixedArray, + index: 4, + value: 444, + auxExistence: false, + auxIndex: 0, + auxValue: 0, + }, + }, + { + description: + "add 2 leaves (depth = 2) update the 2nd leaf and generate the proof of the second one", + leavesToInsert: [ + { i: 4, v: 444 }, + { i: 2, v: 222 }, + { i: 2, v: 223 }, + ], + paramsToGetProof: 2, + expectedProof: { + root: "16396604133323839338919891555968694657750322967047028825382984961917414135680", + existence: true, + siblings: [ + "0", + "22958977272721485097221938248834413051866334275107448764221195104671274302803", + ].concat(Array(62).fill(0)) as FixedArray, + index: 2, + value: 223, + auxExistence: false, + auxIndex: 0, + auxValue: 0, + }, + }, + { + description: + "add 2 leaves (depth = 2) update the 2nd leaf and generate the proof of the first one for the previous root state", + leavesToInsert: [ + { i: 4, v: 444 }, + { i: 2, v: 222 }, + { i: 2, v: 223 }, + ], + paramsToGetProof: { + index: 2, + historicalRoot: + "6271825503835002167390262846571952849048843004006797408985626104559410349007", + }, + expectedProof: { + root: "6271825503835002167390262846571952849048843004006797408985626104559410349007", + existence: true, + siblings: [ + "0", + "22958977272721485097221938248834413051866334275107448764221195104671274302803", + ].concat(Array(62).fill(0)) as FixedArray, + index: 2, + value: 222, + auxExistence: false, + auxIndex: 0, + auxValue: 0, + }, + }, + { + description: + "add 2 leaves (depth = 2) update the 2nd leaf and generate the proof of the second one for the previous root state", + leavesToInsert: [ + { i: 4, v: 444 }, + { i: 2, v: 222 }, + { i: 2, v: 223 }, + ], + paramsToGetProof: { + index: 4, + historicalRoot: + "6271825503835002167390262846571952849048843004006797408985626104559410349007", + }, + expectedProof: { + root: "6271825503835002167390262846571952849048843004006797408985626104559410349007", + existence: true, + siblings: [ + "0", + "78830676469618643418259161717399883581838239495817397573365812847452816685443", + ].concat(Array(62).fill(0)) as FixedArray, + index: 4, + value: 444, + auxExistence: false, + auxIndex: 0, + auxValue: 0, + }, + }, + ]; + + for (const testCase of testCasesExistence) { + it(`${testCase.description}`, async () => { + await checkTestCaseMTPProof(smt, testCase); + }); + } + }); + + describe("keys 3 (011), 7 (111)", () => { + const testCasesExistence: TestCaseMTPProof[] = [ + { + description: "add 1 leaf and generate the proof for it", + leavesToInsert: [{ i: 3, v: 333 }], + paramsToGetProof: 3, + expectedProof: { + root: "33418405206138732565596445817172696059570130655374283008161682599505407512429", + existence: true, + siblings: Array(64).fill(0) as FixedArray, + index: "3", + value: "333", + auxExistence: false, + auxIndex: "0", + auxValue: "0", + }, + }, + { + description: "add 2 leaves (depth = 2) and generate the proof of the second one", + leavesToInsert: [ + { i: 3, v: 333 }, + { i: 7, v: 777 }, + ], + paramsToGetProof: 7, + expectedProof: { + root: "48360753217216801628383385897610760751395521399060591925309269460841854478880", + existence: true, + siblings: [ + "0", + "0", + "33418405206138732565596445817172696059570130655374283008161682599505407512429", + ].concat(Array(61).fill(0)) as FixedArray, + index: "7", + value: "777", + auxExistence: false, + auxIndex: "0", + auxValue: "0", + }, + }, + { + description: + "add 2 leaves (depth = 2) update 2nd one and generate the proof of the first one", + leavesToInsert: [ + { i: 3, v: 333 }, + { i: 7, v: 777 }, + { i: 7, v: 778 }, + ], + paramsToGetProof: 3, + expectedProof: { + root: "99343494309985223004431568049537972373969240419555846106086358328736705856273", + existence: true, + siblings: [ + "0", + "0", + "112313283557488934319533843029412977758210563652664683830759028786502416187169", + ].concat(Array(61).fill(0)) as FixedArray, + index: "3", + value: "333", + auxExistence: false, + auxIndex: "0", + auxValue: "0", + }, + }, + { + description: + "add 2 leaves (depth = 2) update the 2nd leaf and generate the proof of the second one", + leavesToInsert: [ + { i: 3, v: 333 }, + { i: 7, v: 777 }, + { i: 7, v: 778 }, + ], + paramsToGetProof: 7, + expectedProof: { + root: "99343494309985223004431568049537972373969240419555846106086358328736705856273", + existence: true, + siblings: [ + "0", + "0", + "33418405206138732565596445817172696059570130655374283008161682599505407512429", + ].concat(Array(61).fill(0)) as FixedArray, + index: "7", + value: "778", + auxExistence: false, + auxIndex: "0", + auxValue: "0", + }, + }, + { + description: + "add 2 leaves (depth = 2) update the 2nd leaf and generate the proof of the first one for the previous root state", + leavesToInsert: [ + { i: 3, v: 333 }, + { i: 7, v: 777 }, + { i: 7, v: 778 }, + ], + paramsToGetProof: { + index: 3, + historicalRoot: + "48360753217216801628383385897610760751395521399060591925309269460841854478880", + }, + expectedProof: { + root: "48360753217216801628383385897610760751395521399060591925309269460841854478880", + existence: true, + siblings: [ + "0", + "0", + "50296473614243876656937981110997905853887938112796700896791687746944667051313", + ].concat(Array(61).fill(0)) as FixedArray, + index: "3", + value: "333", + auxExistence: false, + auxIndex: "0", + auxValue: "0", + }, + }, + { + description: + "add 2 leaves (depth = 2) update the 2nd leaf and generate the proof of the second one for the previous root state", + leavesToInsert: [ + { i: 3, v: 333 }, + { i: 7, v: 777 }, + { i: 7, v: 778 }, + ], + paramsToGetProof: { + index: 7, + historicalRoot: + "48360753217216801628383385897610760751395521399060591925309269460841854478880", + }, + expectedProof: { + root: "48360753217216801628383385897610760751395521399060591925309269460841854478880", + existence: true, + siblings: [ + "0", + "0", + "33418405206138732565596445817172696059570130655374283008161682599505407512429", + ].concat(Array(61).fill(0)) as FixedArray, + index: "7", + value: "777", + auxExistence: false, + auxIndex: "0", + auxValue: "0", + }, + }, + ]; + + for (const testCase of testCasesExistence) { + it(`${testCase.description}`, async () => { + await checkTestCaseMTPProof(smt, testCase); + }); + } + }); + + describe("big keys and values", () => { + const testCases: TestCaseMTPProof[] = [ + { + description: "add 10 big keys and values and generate the proof of the last one", + leavesToInsert: [ + { + i: "17986234253083975636920416129693886882270902765181654761797265357667135152117", + v: "17986234253083975636920416129693886882270902765181654761797265357667135152117", + }, + { + i: "18123691505823985756684232913053395870713635907333284540988946526936415011906", + v: "18123691505823985756684232913053395870713635907333284540988946526936415011906", + }, + { + i: "18574761138418725443990802836499920062140432673318152864603722896749742947566", + v: "18574761138418725443990802836499920062140432673318152864603722896749742947566", + }, + { + i: "889985217497699235766882779777015930299841231159370680230752238312340113600", + v: "889985217497699235766882779777015930299841231159370680230752238312340113600", + }, + { + i: "6710060555229139303017247577694107284750887011584715720178646167607892089915", + v: "6710060555229139303017247577694107284750887011584715720178646167607892089915", + }, + { + i: "12497952624796233344034183566409825898225866478213356400863532789405613344341", + v: "12497952624796233344034183566409825898225866478213356400863532789405613344341", + }, + { + i: "3936805208905305247536886538882195169540221794023203457168302765039729764024", + v: "3936805208905305247536886538882195169540221794023203457168302765039729764024", + }, + { + i: "10731848384335329467520994720879479347585446432461329563566584581365237056572", + v: "10731848384335329467520994720879479347585446432461329563566584581365237056572", + }, + { + i: "16500146780965105196157518035139529539214406883902880947728555071906521106240", + v: "16500146780965105196157518035139529539214406883902880947728555071906521106240", + }, + { + i: "2254139687286372760549210172096572575821880629072851135313477335313002867070", + v: "2254139687286372760549210172096572575821880629072851135313477335313002867070", + }, + ], + paramsToGetProof: + "2254139687286372760549210172096572575821880629072851135313477335313002867070", + expectedProof: { + root: "60007161943983486146906500160381123178622458462493617850821425381979612616956", + existence: true, + siblings: [ + "11842043060776711430669533915732787960772621560012996635304388961537219372006", + "58180682306773137243712131495542060329763268994690040321062652108869108341898", + "94191217124907006392892149230646386351460750107001924913640299034519044572186", + "0", + "68634643421786404149040398078509254181052706327147925186290660391175330864565", + ].concat(Array(59).fill(0)) as FixedArray, + index: "2254139687286372760549210172096572575821880629072851135313477335313002867070", + value: "2254139687286372760549210172096572575821880629072851135313477335313002867070", + auxExistence: false, + auxIndex: "0", + auxValue: "0", + }, + }, + ]; + + for (const testCase of testCases) { + it(`${testCase.description}`, async () => { + await checkTestCaseMTPProof(smt, testCase); + }); + } + }); + }); + + describe("SMT non existence proof", () => { + describe("keys 4 (100), 2 (010)", () => { + const testCasesNonExistence: TestCaseMTPProof[] = [ + { + description: "add 1 leaf and generate a proof on non-existing leaf", + leavesToInsert: [{ i: 4, v: 444 }], + paramsToGetProof: 2, + expectedProof: { + root: "22958977272721485097221938248834413051866334275107448764221195104671274302803", + existence: false, + siblings: Array(64).fill(0) as FixedArray, + index: 2, + value: 444, + auxExistence: true, + auxIndex: 4, + auxValue: 444, + }, + }, + { + description: + "add 2 leaves (depth = 2) and generate proof on non-existing leaf WITH aux node", + leavesToInsert: [ + { i: 4, v: 444 }, + { i: 2, v: 222 }, + ], + paramsToGetProof: 6, + expectedProof: { + root: "6271825503835002167390262846571952849048843004006797408985626104559410349007", + existence: false, + siblings: [ + "0", + "22958977272721485097221938248834413051866334275107448764221195104671274302803", + ].concat(Array(62).fill(0)) as FixedArray, + index: 6, + value: 222, + auxExistence: true, + auxIndex: 2, + auxValue: 222, + }, + }, + { + description: + "add 2 leaves (depth = 2) and generate proof on non-existing leaf WITHOUT aux node", + leavesToInsert: [ + { i: 4, v: 444 }, + { i: 2, v: 222 }, + ], + paramsToGetProof: 1, + expectedProof: { + root: "6271825503835002167390262846571952849048843004006797408985626104559410349007", + existence: false, + siblings: [ + "96535818096110143691607859947815292580929567133286005889885442808119755306104", + ].concat(Array(63).fill(0)) as FixedArray, + index: 1, + value: 0, + auxExistence: false, + auxIndex: 0, + auxValue: 0, + }, + }, + { + description: + "add 2 leaves (depth = 2), update the 2nd leaf and generate proof of non-existing leaf WITH aux node (which existed before update)", + leavesToInsert: [ + { i: 4, v: 444 }, + { i: 2, v: 222 }, + { i: 2, v: 223 }, + ], + paramsToGetProof: { + index: 6, + historicalRoot: + "6271825503835002167390262846571952849048843004006797408985626104559410349007", + }, + expectedProof: { + root: "6271825503835002167390262846571952849048843004006797408985626104559410349007", + existence: false, + siblings: [ + "0", + "22958977272721485097221938248834413051866334275107448764221195104671274302803", + ].concat(Array(62).fill(0)) as FixedArray, + index: 6, + value: 222, + auxExistence: true, + auxIndex: 2, + auxValue: 222, + }, + }, + { + description: + "add 2 leaves (depth = 2), update the 2nd leaf and generate proof of non-existing leaf WITHOUT aux node", + leavesToInsert: [ + { i: 4, v: 444 }, + { i: 2, v: 222 }, + { i: 2, v: 223 }, + ], + paramsToGetProof: { + index: 1, + historicalRoot: + "6271825503835002167390262846571952849048843004006797408985626104559410349007", + }, + expectedProof: { + root: "6271825503835002167390262846571952849048843004006797408985626104559410349007", + existence: false, + siblings: [ + "96535818096110143691607859947815292580929567133286005889885442808119755306104", + ].concat(Array(63).fill(0)) as FixedArray, + index: 1, + value: 0, + auxExistence: false, + auxIndex: 0, + auxValue: 0, + }, + }, + { + description: + "add 2 leaves (depth = 2), add 3rd leaf and generate proof of non-existence for the 3rd leaf in the previous root state", + leavesToInsert: [ + { i: 4, v: 444 }, + { i: 2, v: 222 }, + { i: 1, v: 111 }, + ], + paramsToGetProof: { + index: 1, + historicalRoot: + "6271825503835002167390262846571952849048843004006797408985626104559410349007", + }, + expectedProof: { + root: "6271825503835002167390262846571952849048843004006797408985626104559410349007", + existence: false, + siblings: [ + "96535818096110143691607859947815292580929567133286005889885442808119755306104", + ].concat(Array(63).fill(0)) as FixedArray, + index: 1, + value: 0, + auxExistence: false, + auxIndex: 0, + auxValue: 0, + }, + }, + ]; + + for (const testCase of testCasesNonExistence) { + it(`${testCase.description}`, async () => { + await checkTestCaseMTPProof(smt, testCase); + }); + } + }); + + describe("keys 3 (011), 7 (111)", () => { + const testCasesNonExistence: TestCaseMTPProof[] = [ + { + description: "add 1 leaf and generate a proof on non-existing leaf", + leavesToInsert: [{ i: 3, v: 333 }], + paramsToGetProof: 7, + expectedProof: { + root: "33418405206138732565596445817172696059570130655374283008161682599505407512429", + existence: false, + siblings: Array(64).fill(0) as FixedArray, + index: "7", + value: "333", + auxExistence: true, + auxIndex: "3", + auxValue: "333", + }, + }, + { + description: + "add 2 leaves (depth = 2) and generate proof on non-existing leaf WITH aux node", + leavesToInsert: [ + { i: 3, v: 333 }, + { i: 7, v: 777 }, + ], + paramsToGetProof: 11, + expectedProof: { + root: "48360753217216801628383385897610760751395521399060591925309269460841854478880", + existence: false, + siblings: [ + "0", + "0", + "50296473614243876656937981110997905853887938112796700896791687746944667051313", + ].concat(Array(61).fill(0)) as FixedArray, + index: "11", + value: "333", + auxExistence: true, + auxIndex: "3", + auxValue: "333", + }, + }, + { + description: + "add 2 leaves (depth = 2) and generate proof on non-existing leaf WITHOUT aux node", + leavesToInsert: [ + { i: 3, v: 333 }, + { i: 7, v: 777 }, + ], + paramsToGetProof: 1, + expectedProof: { + root: "48360753217216801628383385897610760751395521399060591925309269460841854478880", + existence: false, + siblings: [ + "0", + "112876657748891444100900801150886368101744673616075342940779383085926542312690", + ].concat(Array(62).fill(0)) as FixedArray, + index: "1", + value: "0", + auxExistence: false, + auxIndex: "0", + auxValue: "0", + }, + }, + { + description: + "add 2 leaves (depth = 2), update the 2nd leaf and generate proof of non-existing leaf WITH aux node (which existed before update)", + leavesToInsert: [ + { i: 3, v: 333 }, + { i: 7, v: 777 }, + { i: 7, v: 778 }, + ], + paramsToGetProof: { + index: 11, + historicalRoot: + "48360753217216801628383385897610760751395521399060591925309269460841854478880", + }, + expectedProof: { + root: "48360753217216801628383385897610760751395521399060591925309269460841854478880", + existence: false, + siblings: [ + "0", + "0", + "50296473614243876656937981110997905853887938112796700896791687746944667051313", + ].concat(Array(61).fill(0)) as FixedArray, + index: "11", + value: "333", + auxExistence: true, + auxIndex: "3", + auxValue: "333", + }, + }, + { + description: + "add 2 leaves (depth = 2), update the 2nd leaf and generate proof of non-existing leaf WITHOUT aux node", + leavesToInsert: [ + { i: 3, v: 333 }, + { i: 7, v: 777 }, + { i: 7, v: 778 }, + ], + paramsToGetProof: { + index: 1, + historicalRoot: + "48360753217216801628383385897610760751395521399060591925309269460841854478880", + }, + expectedProof: { + root: "48360753217216801628383385897610760751395521399060591925309269460841854478880", + existence: false, + siblings: [ + "0", + "112876657748891444100900801150886368101744673616075342940779383085926542312690", + ].concat(Array(62).fill(0)) as FixedArray, + index: "1", + value: "0", + auxExistence: false, + auxIndex: "0", + auxValue: "0", + }, + }, + { + description: + "add 2 leaves (depth = 2), add 3rd leaf and generate proof of non-existence for the 3rd leaf in the previous root state", + leavesToInsert: [ + { i: 3, v: 333 }, + { i: 7, v: 777 }, + { i: 11, v: 1111 }, + ], + paramsToGetProof: { + index: 11, + historicalRoot: + "48360753217216801628383385897610760751395521399060591925309269460841854478880", + }, + expectedProof: { + root: "48360753217216801628383385897610760751395521399060591925309269460841854478880", + existence: false, + siblings: [ + "0", + "0", + "50296473614243876656937981110997905853887938112796700896791687746944667051313", + ].concat(Array(61).fill(0)) as FixedArray, + index: "11", + value: "333", + auxExistence: true, + auxIndex: "3", + auxValue: "333", + }, + }, + ]; + + for (const testCase of testCasesNonExistence) { + it(`${testCase.description}`, async () => { + await checkTestCaseMTPProof(smt, testCase); + }); + } + }); + + describe("big keys and values", () => { + const testCases: TestCaseMTPProof[] = [ + { + description: "add 10 leaves and generate a proof on non-existing WITH aux node", + leavesToInsert: [ + { + i: "17986234253083975636920416129693886882270902765181654761797265357667135152117", + v: "17986234253083975636920416129693886882270902765181654761797265357667135152117", + }, + { + i: "18123691505823985756684232913053395870713635907333284540988946526936415011906", + v: "18123691505823985756684232913053395870713635907333284540988946526936415011906", + }, + { + i: "18574761138418725443990802836499920062140432673318152864603722896749742947566", + v: "18574761138418725443990802836499920062140432673318152864603722896749742947566", + }, + { + i: "889985217497699235766882779777015930299841231159370680230752238312340113600", + v: "889985217497699235766882779777015930299841231159370680230752238312340113600", + }, + { + i: "6710060555229139303017247577694107284750887011584715720178646167607892089915", + v: "6710060555229139303017247577694107284750887011584715720178646167607892089915", + }, + { + i: "12497952624796233344034183566409825898225866478213356400863532789405613344341", + v: "12497952624796233344034183566409825898225866478213356400863532789405613344341", + }, + { + i: "3936805208905305247536886538882195169540221794023203457168302765039729764024", + v: "3936805208905305247536886538882195169540221794023203457168302765039729764024", + }, + { + i: "10731848384335329467520994720879479347585446432461329563566584581365237056572", + v: "10731848384335329467520994720879479347585446432461329563566584581365237056572", + }, + { + i: "16500146780965105196157518035139529539214406883902880947728555071906521106240", + v: "16500146780965105196157518035139529539214406883902880947728555071906521106240", + }, + { + i: "2254139687286372760549210172096572575821880629072851135313477335313002867070", + v: "2254139687286372760549210172096572575821880629072851135313477335313002867070", + }, + ], + paramsToGetProof: + "2254139687286372760549210172096572575821880629072851135313477335313002867071", + expectedProof: { + root: "60007161943983486146906500160381123178622458462493617850821425381979612616956", + existence: false, + siblings: [ + "15649299891651963645168066741171440452486996543233903096221914286509395708217", + "35485910319040178947164101851755181176671699873501710834388743638308164334229", + ].concat(Array(62).fill(0)) as FixedArray, + index: "2254139687286372760549210172096572575821880629072851135313477335313002867071", + value: "6710060555229139303017247577694107284750887011584715720178646167607892089915", + auxExistence: true, + auxIndex: + "6710060555229139303017247577694107284750887011584715720178646167607892089915", + auxValue: + "6710060555229139303017247577694107284750887011584715720178646167607892089915", + }, + }, + { + description: "add 10 leaves and generate a proof on non-existing WITHOUT aux node", + leavesToInsert: [ + { + i: "17986234253083975636920416129693886882270902765181654761797265357667135152117", + v: "17986234253083975636920416129693886882270902765181654761797265357667135152117", + }, + { + i: "18123691505823985756684232913053395870713635907333284540988946526936415011906", + v: "18123691505823985756684232913053395870713635907333284540988946526936415011906", + }, + { + i: "18574761138418725443990802836499920062140432673318152864603722896749742947566", + v: "18574761138418725443990802836499920062140432673318152864603722896749742947566", + }, + { + i: "889985217497699235766882779777015930299841231159370680230752238312340113600", + v: "889985217497699235766882779777015930299841231159370680230752238312340113600", + }, + { + i: "6710060555229139303017247577694107284750887011584715720178646167607892089915", + v: "6710060555229139303017247577694107284750887011584715720178646167607892089915", + }, + { + i: "12497952624796233344034183566409825898225866478213356400863532789405613344341", + v: "12497952624796233344034183566409825898225866478213356400863532789405613344341", + }, + { + i: "3936805208905305247536886538882195169540221794023203457168302765039729764024", + v: "3936805208905305247536886538882195169540221794023203457168302765039729764024", + }, + { + i: "10731848384335329467520994720879479347585446432461329563566584581365237056572", + v: "10731848384335329467520994720879479347585446432461329563566584581365237056572", + }, + { + i: "16500146780965105196157518035139529539214406883902880947728555071906521106240", + v: "16500146780965105196157518035139529539214406883902880947728555071906521106240", + }, + { + i: "2254139687286372760549210172096572575821880629072851135313477335313002867070", + v: "2254139687286372760549210172096572575821880629072851135313477335313002867070", + }, + ], + paramsToGetProof: + "6271287741236698691604141726361751264311688318470481595940384433868807274649", + expectedProof: { + root: "60007161943983486146906500160381123178622458462493617850821425381979612616956", + existence: false, + siblings: [ + "15649299891651963645168066741171440452486996543233903096221914286509395708217", + "5582158759613617994025042938199822589967969783458491971055101796671660471167", + "73073270877252609309304544688345883755735059749695338671799354601148846061501", + ].concat(Array(61).fill(0)) as FixedArray, + index: "6271287741236698691604141726361751264311688318470481595940384433868807274649", + value: "0", + auxExistence: false, + auxIndex: "0", + auxValue: "0", + }, + }, + ]; + + for (const testCase of testCases) { + it(`${testCase.description}`, async () => { + await checkTestCaseMTPProof(smt, testCase); + }); + } + }); + + describe("empty tree", () => { + const testCases: TestCaseMTPProof[] = [ + { + description: "generate proof for some key", + leavesToInsert: [], + paramsToGetProof: 1, + expectedProof: { + root: 0, + existence: false, + siblings: Array(64).fill(0) as FixedArray, + index: 1, + value: 0, + auxExistence: false, + auxIndex: 0, + auxValue: 0, + }, + }, + { + description: "generate proof for some key and zero historical root", + leavesToInsert: [{ i: 1, v: 10 }], + paramsToGetProof: { index: 1, historicalRoot: 0 }, + expectedProof: { + root: 0, + existence: false, + siblings: Array(64).fill(0) as FixedArray, + index: 1, + value: 0, + auxExistence: false, + auxIndex: 0, + auxValue: 0, + }, + }, + ]; + + for (const testCase of testCases) { + it(`${testCase.description}`, async () => { + await checkTestCaseMTPProof(smt, testCase); + }); + } + }); + }); + + describe("SMT add leaf edge cases", () => { + const testCases: TestCaseMTPProof[] = [ + { + description: "Positive: add two leaves with maximum depth (less significant bits SET)", + leavesToInsert: [ + { i: genMaxBinaryNumber(63), v: 100 }, //111111111111111111111111111111111111111111111111111111111111111 + { i: genMaxBinaryNumber(64), v: 100 }, //1111111111111111111111111111111111111111111111111111111111111111 + ], + paramsToGetProof: genMaxBinaryNumber(64), + expectedProof: { + root: "110199947410708031149228851387837770054145252372569190168540009983771486866316", + existence: true, + siblings: Array(63) + .fill("0") + .concat([ + "74399159356430181752973204803851211752558544080760906099963521203069195578461", + ]) as FixedArray, + index: "18446744073709551615", + value: "100", + auxExistence: false, + auxIndex: "0", + auxValue: "0", + }, + }, + { + description: "Positive: add two leaves with maximum depth (less significant bits NOT SET)", + leavesToInsert: [ + { i: 0, v: 100 }, + { i: genMaxBinaryNumber(63) + BigInt(1), v: 100 }, // 1000000000000000000000000000000000000000000000000000000000000000 + ], + paramsToGetProof: genMaxBinaryNumber(63) + BigInt(1), + expectedProof: { + root: "16625293229838138370855084455911863446233219628941478502997473061463286175203", + existence: true, + siblings: Array(63) + .fill("0") + .concat([ + "85255324799129495636878324767942437404808863678639087371922338002905282669100", + ]) as FixedArray, + index: "9223372036854775808", + value: "100", + auxExistence: false, + auxIndex: "0", + auxValue: "0", + }, + }, + { + description: + "Positive: add two leaves with maximum depth (less significant bits are both SET and NOT SET)", + leavesToInsert: [ + { i: "17713686966169915918", v: 100 }, //1111010111010011101010000111000111010001000001100101001000001110 + { i: "8490314929315140110", v: 100 }, //0111010111010011101010000111000111010001000001100101001000001110 + ], + paramsToGetProof: "8490314929315140110", + expectedProof: { + root: "16484897135457263633438825596660941524517623843033410328799978262525614569225", + existence: true, + siblings: Array(63) + .fill("0") + .concat([ + "9906144696231549323869068974423946066277130279313871204886462229234859073912", + ]) as FixedArray, + index: "8490314929315140110", + value: "100", + auxExistence: false, + auxIndex: "0", + auxValue: "0", + }, + }, + { + description: "Negative: add two leaves with maximum depth + 1 (less significant bits SET)", + leavesToInsert: [ + { i: genMaxBinaryNumber(64), v: 100 }, //1111111111111111111111111111111111111111111111111111111111111111 + { i: genMaxBinaryNumber(65), v: 100, error: "Max depth reached" }, //11111111111111111111111111111111111111111111111111111111111111111 + ], + }, + { + description: + "Negative: add two leaves with maximum depth + 1 (less significant bits NOT SET)", + leavesToInsert: [ + { i: 0, v: 100 }, + { i: genMaxBinaryNumber(64) + BigInt(1), v: 100, error: "Max depth reached" }, // 10000000000000000000000000000000000000000000000000000000000000000 + ], + }, + { + description: + "Negative: add two leaves with maximum depth + 1 (less significant bits are both SET and NOT SET", + leavesToInsert: [ + { i: "17713686966169915918", v: 100 }, //1111010111010011101010000111000111010001000001100101001000001110 + { i: "36160431039879467534", v: 100, error: "Max depth reached" }, //11111010111010011101010000111000111010001000001100101001000001110 + ], + }, + ]; + + for (const testCase of testCases) { + it(`${testCase.description}`, async () => { + await checkTestCaseMTPProof(smt, testCase); + }); + } + }); +}); + +describe("Root history requests", function () { + this.timeout(5000); + + let smt, historyLength; + let pubStates: { [key: string]: string | number }[] = []; + + before(async () => { + const deployHelper = await DeployHelper.initialize(); + smt = await deployHelper.deploySmtLibTestWrapper(); + + pubStates = []; + pubStates.push(await addLeaf(smt, 1, 10)); + pubStates.push(await addLeaf(smt, 2, 20)); + + historyLength = await smt.getRootHistoryLength(); + }); + + it("should return the root history", async () => { + // 1 root added at Smt init + 2 roots added by addLeaf + expect(historyLength).to.be.equal(3); + + const rootInfos = await smt.getRootHistory(0, historyLength); + expect(rootInfos.length).to.be.equal(historyLength); + + // check the first root, which was added at Smt init + expect(rootInfos[0].root).to.be.equal(0); + expect(rootInfos[0].replacedByRoot).to.be.equal(pubStates[0].root); + expect(rootInfos[0].createdAtTimestamp).to.be.equal(0); + expect(rootInfos[0].replacedAtTimestamp).to.be.equal(pubStates[0].timestamp); + expect(rootInfos[0].createdAtBlock).to.be.equal(0); + expect(rootInfos[0].replacedAtBlock).to.be.equal(pubStates[0].blockNumber); + + const [rootInfo] = await smt.getRootHistory(1, 1); + expect(rootInfo.root).not.to.be.equal(0); + expect(rootInfo.replacedByRoot).not.to.be.equal(0); + expect(rootInfo.createdAtTimestamp).to.be.equal(pubStates[0].timestamp); + expect(rootInfo.replacedAtTimestamp).to.be.equal(pubStates[1].timestamp); + expect(rootInfo.createdAtBlock).to.be.equal(pubStates[0].blockNumber); + expect(rootInfo.replacedAtBlock).to.be.equal(pubStates[1].blockNumber); + + const [rootInfo2] = await smt.getRootHistory(2, 1); + expect(rootInfo2.root).not.to.be.equal(0); + expect(rootInfo2.replacedByRoot).to.be.equal(0); + expect(rootInfo2.createdAtTimestamp).to.be.equal(pubStates[1].timestamp); + expect(rootInfo2.replacedAtTimestamp).to.be.equal(0); + expect(rootInfo2.createdAtBlock).to.be.equal(pubStates[1].blockNumber); + expect(rootInfo2.replacedAtBlock).to.be.equal(0); + }); + + it("should revert if length is zero", async () => { + await expect(smt.getRootHistory(0, 0)).to.be.rejectedWith("Length should be greater than 0"); + }); + + it("should revert if length limit exceeded", async () => { + await expect(smt.getRootHistory(0, 10 ** 6)).to.be.rejectedWith("Length limit exceeded"); + }); + + it("should revert if out of bounds", async () => { + await expect(smt.getRootHistory(historyLength, 100)).to.be.rejectedWith( + "Start index out of bounds", + ); + }); + + it("should NOT revert if startIndex + length >= historyLength", async () => { + let history = await smt.getRootHistory(historyLength - 1n, 100); + expect(history.length).to.be.equal(1); + history = await smt.getRootHistory(historyLength - 2n, 100); + expect(history.length).to.be.equal(2); + }); +}); + +describe("Root history duplicates", function () { + let smt; + + async function deployContractsFixture() { + const deployHelper = await DeployHelper.initialize(); + smt = await deployHelper.deploySmtLibTestWrapper(); + } + + beforeEach(async () => { + await loadFixture(deployContractsFixture); + }); + + it("comprehensive check", async () => { + const leavesToAdd = [ + { i: 1, v: 1 }, // doubleRoot + { i: 1, v: 2 }, // singleRoot + { i: 1, v: 1 }, // doubleRoot + { i: 2, v: 1 }, // tripleRoot + { i: 2, v: 2 }, + { i: 2, v: 1 }, // tripleRoot + { i: 2, v: 2 }, + { i: 2, v: 1 }, // tripleRoot + ]; + + const addResult: { [key: string]: any }[] = []; + + for (const leaf of leavesToAdd) { + addResult.push(await addLeaf(smt, leaf.i, leaf.v)); + } + + const singleRoot = addResult[1].root; + const doubleRoot = addResult[2].root; + const tripleRoot = addResult[7].root; + const nonExistingRoot = 1; + + expect(await smt.getRootInfoListLengthByRoot(singleRoot)).to.be.equal(1); + expect(await smt.getRootInfoListLengthByRoot(doubleRoot)).to.be.equal(2); + expect(await smt.getRootInfoListLengthByRoot(tripleRoot)).to.be.equal(3); + expect(await smt.getRootInfoListLengthByRoot(nonExistingRoot)).to.be.equal(0); + + const riSingleRoot = await smt.getRootInfoListByRoot(singleRoot, 0, 100); + const riDoubleRoot = await smt.getRootInfoListByRoot(doubleRoot, 0, 100); + const riTripleRoot = await smt.getRootInfoListByRoot(tripleRoot, 0, 100); + await expect(smt.getRootInfoListByRoot(nonExistingRoot, 0, 100)).to.be.rejectedWith( + "Root does not exist", + ); + + expect(riSingleRoot.length).to.be.equal(1); + expect(riDoubleRoot.length).to.be.equal(2); + expect(riTripleRoot.length).to.be.equal(3); + + const checkRootInfo = (ri: any, riExp: any, riExpNext: any) => { + expect(ri.root).to.be.equal(riExp.rootInfo.root); + expect(ri.replacedByRoot).to.be.equal(riExpNext.rootInfo.root ?? 0); + expect(ri.createdAtBlock).to.be.equal(riExp.blockNumber); + expect(ri.replacedAtBlock).to.be.equal(riExpNext.rootInfo.createdAtBlock ?? 0); + expect(ri.createdAtTimestamp).to.be.equal(riExp.timestamp); + expect(ri.replacedAtTimestamp).to.be.equal(riExpNext.rootInfo.createdAtTimestamp ?? 0); + }; + + checkRootInfo(riSingleRoot[0], addResult[1], addResult[2]); + checkRootInfo(riDoubleRoot[0], addResult[0], addResult[1]); + checkRootInfo(riDoubleRoot[1], addResult[2], addResult[3]); + checkRootInfo(riTripleRoot[0], addResult[3], addResult[4]); + checkRootInfo(riTripleRoot[1], addResult[5], addResult[6]); + checkRootInfo(riTripleRoot[2], addResult[7], { rootInfo: {} }); + + checkRootInfo(await smt.getRootInfo(singleRoot), addResult[1], addResult[2]); + checkRootInfo(await smt.getRootInfo(doubleRoot), addResult[2], addResult[3]); + checkRootInfo(await smt.getRootInfo(tripleRoot), addResult[7], { rootInfo: {} }); + }); + + it("should revert if length is zero", async () => { + await smt.add(1, 1); + const root = await smt.getRoot(); + await expect(smt.getRootInfoListByRoot(root, 0, 0)).to.be.rejectedWith( + "Length should be greater than 0", + ); + }); + + it("should revert if length limit exceeded", async () => { + await smt.add(1, 1); + const root = await smt.getRoot(); + await expect(smt.getRootInfoListByRoot(root, 0, 10 ** 6)).to.be.rejectedWith( + "Length limit exceeded", + ); + }); + + it("should revert if out of bounds", async () => { + await smt.add(1, 1); + await smt.add(1, 2); + await smt.add(1, 1); + const root = await smt.getRoot(); + await expect(smt.getRootInfoListByRoot(root, 3, 100)).to.be.rejectedWith( + "Start index out of bounds", + ); + }); + + it("should NOT revert if startIndex + length >= historyLength", async () => { + await smt.add(1, 1); + await smt.add(1, 2); + await smt.add(1, 1); + const root = await smt.getRoot(); + const rootInfoListLength = await smt.getRootInfoListLengthByRoot(root); + let list = await smt.getRootInfoListByRoot(root, rootInfoListLength - 1n, 100); + expect(list.length).to.be.equal(1); + list = await smt.getRootInfoListByRoot(root, rootInfoListLength - 2n, 100); + expect(list.length).to.be.equal(2); + }); + + it("should return correct list and length just after init", async () => { + const root = 0; + const [rootInfo] = await smt.getRootInfoListByRoot(root, 0, 1); + expect(rootInfo.root).to.be.equal(0); + expect(rootInfo.replacedByRoot).to.be.equal(0); + expect(rootInfo.createdAtTimestamp).to.be.equal(0); + expect(rootInfo.replacedAtTimestamp).to.be.equal(0); + expect(rootInfo.createdAtBlock).to.be.equal(0); + expect(rootInfo.replacedAtBlock).to.be.equal(0); + + expect(await smt.getRootInfoListLengthByRoot(root)).to.be.equal(1); + }); +}); + +describe("Binary search in SMT root history", () => { + let binarySearch; + + async function addRootEntries(rts: RootEntry[]) { + for (const rt of rts) { + await binarySearch.addRootEntry(rt.root, rt.timestamp, rt.block); + } + } + + async function checkRootByTimeAndBlock(rts: RootEntry[], tc: TestCaseRootHistory) { + await addRootEntries(rts); + + const riByTime = await binarySearch.getRootInfoByTime(tc.timestamp); + expect(riByTime.root).to.equal(tc.expectedRoot); + + const riByBlock = await binarySearch.getHistoricalRootByBlock(tc.blockNumber); + expect(riByBlock.root).to.equal(tc.expectedRoot); + } + + async function deployContractsFixture() { + const deployHelper = await DeployHelper.initialize(); + binarySearch = await deployHelper.deployBinarySearchTestWrapper(); + } + + beforeEach(async () => { + await loadFixture(deployContractsFixture); + const { number: latestBlockNumber } = await hre.ethers.provider.getBlock("latest"); + let blocksToMine = 17 - latestBlockNumber; + + while (blocksToMine > 0) { + await hre.network.provider.request({ + method: "evm_mine", + params: [], + }); + blocksToMine--; + } + }); + + describe("Empty history ", () => { + const rootEntries: RootEntry[] = []; + + const testCase: TestCaseRootHistory[] = [ + { + description: "Should return zero root for some search", + timestamp: 1, + blockNumber: 10, + expectedRoot: 0, + }, + ]; + + for (const tc of testCase) { + it(`${tc.description}`, async () => { + await checkRootByTimeAndBlock(rootEntries, tc); + }); + } + }); + + describe("One root in the root history ", () => { + const rootEntries: RootEntry[] = [ + { + timestamp: 1, + block: 10, + root: 1000, + }, + ]; + + const testCase: TestCaseRootHistory[] = [ + { + description: "Should return the first root when equal", + timestamp: 1, + blockNumber: 10, + expectedRoot: 1000, + }, + { + description: "Should return zero when search for less than the first", + timestamp: 0, + blockNumber: 9, + expectedRoot: 0, + }, + { + description: "Should return the last root when search for greater than the last", + timestamp: 2, + blockNumber: 11, + expectedRoot: 1000, + }, + ]; + + for (const tc of testCase) { + it(`${tc.description}`, async () => { + await checkRootByTimeAndBlock(rootEntries, tc); + }); + } + }); + + describe("Two roots in the root history ", () => { + const rootEntries: RootEntry[] = [ + { + timestamp: 1, + block: 10, + root: 1000, + }, + { + timestamp: 5, + block: 15, + root: 1500, + }, + ]; + + const testCase: TestCaseRootHistory[] = [ + { + description: "Should return the first root when search for equal", + timestamp: rootEntries[0].timestamp, + blockNumber: rootEntries[0].block, + expectedRoot: rootEntries[0].root, + }, + { + description: "Should return the second root when search for equal", + timestamp: rootEntries[1].timestamp, + blockNumber: rootEntries[1].block, + expectedRoot: rootEntries[1].root, + }, + { + description: "Should return zero when search for less than the first", + timestamp: 0, + blockNumber: 9, + expectedRoot: 0, + }, + { + description: "Should return the last root when search for greater than the last", + timestamp: 6, + blockNumber: 16, + expectedRoot: rootEntries[1].root, + }, + ]; + + for (const tc of testCase) { + it(`${tc.description}`, async () => { + await checkRootByTimeAndBlock(rootEntries, tc); + }); + } + }); + + describe("Three roots in the root history ", () => { + const rootEntries: RootEntry[] = [ + { + timestamp: 1, + block: 10, + root: 1000, + }, + { + timestamp: 5, + block: 15, + root: 1500, + }, + { + timestamp: 7, + block: 17, + root: 1700, + }, + ]; + + const testCase: TestCaseRootHistory[] = [ + { + description: "Should return the first root when equal", + timestamp: rootEntries[0].timestamp, + blockNumber: rootEntries[0].block, + expectedRoot: rootEntries[0].root, + }, + { + description: "Should return the second root when equal", + timestamp: rootEntries[1].timestamp, + blockNumber: rootEntries[1].block, + expectedRoot: rootEntries[1].root, + }, + { + description: "Should return the third root when equal", + timestamp: rootEntries[2].timestamp, + blockNumber: rootEntries[2].block, + expectedRoot: rootEntries[2].root, + }, + { + description: "Should return zero root when search for less than the first", + timestamp: 0, + blockNumber: 9, + expectedRoot: 0, + }, + { + description: "Should return the last root when search for greater than the last", + timestamp: 9, + blockNumber: 19, + expectedRoot: rootEntries[2].root, + }, + ]; + + for (const tc of testCase) { + it(`${tc.description}`, async () => { + await checkRootByTimeAndBlock(rootEntries, tc); + }); + } + }); + + describe("Four roots in the root history ", () => { + const rootEntries: RootEntry[] = [ + { + timestamp: 1, + block: 10, + root: 1000, + }, + { + timestamp: 5, + block: 15, + root: 1500, + }, + { + timestamp: 7, + block: 17, + root: 1700, + }, + { + timestamp: 8, + block: 18, + root: 1800, + }, + ]; + + const testCase: TestCaseRootHistory[] = [ + { + description: "Should return the first root when equal", + timestamp: rootEntries[0].timestamp, + blockNumber: rootEntries[0].block, + expectedRoot: rootEntries[0].root, + }, + { + description: "Should return the fourth root when equal", + timestamp: rootEntries[3].timestamp, + blockNumber: rootEntries[3].block, + expectedRoot: rootEntries[3].root, + }, + { + description: "Should return zero when search for less than the first", + timestamp: rootEntries[0].timestamp - 1, + blockNumber: rootEntries[0].block - 1, + expectedRoot: 0, + }, + { + description: "Should return the last root when search for greater than the last", + timestamp: rootEntries[3].timestamp + 1, + blockNumber: rootEntries[3].block + 1, + expectedRoot: rootEntries[3].root, + }, + ]; + + for (const tc of testCase) { + it(`${tc.description}`, async () => { + await checkRootByTimeAndBlock(rootEntries, tc); + }); + } + }); + + describe("Search in between the values", () => { + const rootEntries: RootEntry[] = [ + { + timestamp: 1, + block: 10, + root: 1100, + }, + { + timestamp: 3, + block: 13, + root: 1300, + }, + { + timestamp: 6, + block: 16, + root: 1600, + }, + { + timestamp: 7, + block: 17, + root: 1700, + }, + { + timestamp: 9, + block: 19, + root: 1900, + }, + ]; + + const testCase: TestCaseRootHistory[] = [ + { + description: "Should return the first root when search in between the first and second", + timestamp: 2, + blockNumber: 12, + expectedRoot: rootEntries[0].root, + }, + { + description: + "Should return the fourth root when search in between the fourth and the fifth", + timestamp: 8, + blockNumber: 18, + expectedRoot: rootEntries[3].root, + }, + ]; + + for (const tc of testCase) { + it(`${tc.description}`, async () => { + await checkRootByTimeAndBlock(rootEntries, tc); + }); + } + }); + + describe("Search in array with duplicated values", () => { + const rootEntries: RootEntry[] = [ + { + timestamp: 1, + block: 11, + root: 1100, + }, + { + timestamp: 1, + block: 11, + root: 1101, + }, + { + timestamp: 7, + block: 17, + root: 1700, + }, + { + timestamp: 7, + block: 17, + root: 1701, + }, + { + timestamp: 7, + block: 17, + root: 1702, + }, + ]; + + const testCase: TestCaseRootHistory[] = [ + { + description: "Should return the last root among two equal values when search for the value", + timestamp: 1, + blockNumber: 11, + expectedRoot: rootEntries[1].root, + }, + { + description: + "Should return the last root among three equal values when search for the value", + timestamp: 7, + blockNumber: 17, + expectedRoot: rootEntries[4].root, + }, + ]; + + for (const tc of testCase) { + it(`${tc.description}`, async () => { + await checkRootByTimeAndBlock(rootEntries, tc); + }); + } + }); + + describe("Search in array with duplicated values and in between values", () => { + const rootEntries: RootEntry[] = [ + { + timestamp: 1, + block: 11, + root: 1100, + }, + { + timestamp: 1, + block: 11, + root: 1101, + }, + { + timestamp: 1, + block: 11, + root: 1102, + }, + { + timestamp: 3, + block: 13, + root: 1300, + }, + { + timestamp: 3, + block: 13, + root: 1301, + }, + { + timestamp: 5, + block: 15, + root: 1700, + }, + { + timestamp: 5, + block: 15, + root: 1701, + }, + { + timestamp: 5, + block: 15, + root: 1702, + }, + ]; + + const testCase: TestCaseRootHistory[] = [ + { + description: + "Should search in between the third (1st, 2nd, 3rd equal) and fourth values and return the third", + timestamp: 2, + blockNumber: 12, + expectedRoot: rootEntries[2].root, + }, + { + description: + "Should search in between the fifth (4th, 5th equal) and sixth values and return the fifth", + timestamp: 4, + blockNumber: 14, + expectedRoot: rootEntries[4].root, + }, + ]; + + for (const tc of testCase) { + it(`${tc.description}`, async () => { + await checkRootByTimeAndBlock(rootEntries, tc); + }); + } + }); +}); + +describe("Binary search in SMT proofs", () => { + let smt; + + async function deployContractsFixture() { + const deployHelper = await DeployHelper.initialize(); + const SMT_MAX_DEPTH = 64; + smt = await deployHelper.deploySmtLibTestWrapper(SMT_MAX_DEPTH, true); + } + + beforeEach(async () => { + await loadFixture(deployContractsFixture); + }); + + describe("Zero root proofs", () => { + const testCases: TestCaseMTPProof[] = [ + { + description: "Should return zero proof for some search", + leavesToInsert: [], + paramsToGetProof: { + index: 1, + blockNumber: 1, + }, + expectedProof: { + root: 0, + existence: false, + siblings: Array(64).fill(0) as FixedArray, + index: 1, + value: 0, + auxExistence: false, + auxIndex: 0, + auxValue: 0, + }, + }, + { + description: "Should return zero proof for some search back in time", + leavesToInsert: [{ i: 4, v: 444 }], + paramsToGetProof: { + index: 1, + blockNumber: 1, + }, + expectedProof: { + root: 0, + existence: false, + siblings: Array(64).fill(0) as FixedArray, + index: 1, + value: 0, + auxExistence: false, + auxIndex: 0, + auxValue: 0, + }, + }, + ]; + + for (const testCase of testCases) { + it(`${testCase.description}`, async () => { + await checkTestCaseMTPProof(smt, testCase); + }); + } + }); + + describe("Non-zero root proofs", () => { + const testCases: TestCaseMTPProof[] = [ + { + description: "Should return zero proof for some search current time", + leavesToInsert: [{ i: 4, v: 444 }], + paramsToGetProof: { + index: 4, + timestamp: 0, + }, + expectedProof: { + root: "22958977272721485097221938248834413051866334275107448764221195104671274302803", + existence: true, + siblings: Array(64).fill(0) as FixedArray, + index: 4, + value: 444, + auxExistence: false, + auxIndex: 0, + auxValue: 0, + }, + }, + { + description: "Should return zero proof for some search current block", + leavesToInsert: [{ i: 4, v: 444 }], + paramsToGetProof: { + index: 4, + blockNumber: 0, + }, + expectedProof: { + root: "22958977272721485097221938248834413051866334275107448764221195104671274302803", + existence: true, + siblings: Array(64).fill(0) as FixedArray, + index: 4, + value: 444, + auxExistence: false, + auxIndex: 0, + auxValue: 0, + }, + }, + ]; + + for (const testCase of testCases) { + it(`${testCase.description}`, async () => { + const latestBlockInfo = await hre.ethers.provider.getBlock("latest"); + if (isProofByTime(testCase.paramsToGetProof)) { + testCase.paramsToGetProof.timestamp = latestBlockInfo.timestamp + 1; + } + + if (isProofByBlock(testCase.paramsToGetProof)) { + testCase.paramsToGetProof.blockNumber = latestBlockInfo.number + 1; + } + await checkTestCaseMTPProof(smt, testCase); + }); + } + }); +}); + +describe("Edge cases with exceptions", () => { + let smt; + + async function deployContractsFixture() { + const deployHelper = await DeployHelper.initialize(); + smt = await deployHelper.deploySmtLibTestWrapper(); + } + + beforeEach(async () => { + await loadFixture(deployContractsFixture); + }); + + it("getRootInfo() should throw when root does not exist", async () => { + await smt.add(1, 1); + const root = await smt.getRoot(); + await expect(smt.getRootInfo(root)).not.to.be.rejected; + await expect(smt.getRootInfo(root + 1n)).to.be.rejectedWith("Root does not exist"); + }); + + it("getProofByRoot() should throw when root does not exist", async () => { + await smt.add(1, 1); + const root = await smt.getRoot(); + await expect(smt.getProofByRoot(1, root)).not.to.be.rejected; + await expect(smt.getProofByRoot(1, root + 1n)).to.be.rejectedWith("Root does not exist"); + }); +}); + +describe("maxDepth setting tests", () => { + const maxDepth = 64; + let smt; + + before(async () => { + const deployHelper = await DeployHelper.initialize(); + smt = await deployHelper.deploySmtLibTestWrapper(maxDepth); + }); + + it("Max depth should be 64", async () => { + const maxDepth = await smt.getMaxDepth(); + expect(maxDepth).to.be.equal(64); + }); + + it("Should increase max depth", async () => { + await smt.setMaxDepth(65); + const maxDepth = await smt.getMaxDepth(); + expect(maxDepth).to.be.equal(65); + await smt.setMaxDepth(128); + const maxDepth2 = await smt.getMaxDepth(); + expect(maxDepth2).to.be.equal(128); + }); + + it("Should throw when decrease max depth", async () => { + await expect(smt.setMaxDepth(127)).to.be.rejectedWith("Max depth can only be increased"); + }); + + it("Should throw when max depth is set to the same value", async () => { + await expect(smt.setMaxDepth(128)).to.be.rejectedWith("Max depth can only be increased"); + }); + + it("Should throw when max depth is set to 0", async () => { + await expect(smt.setMaxDepth(0)).to.be.rejectedWith("Max depth must be greater than zero"); + }); + + it("Should throw when max depth is set to greater than hard cap", async () => { + await expect(smt.setMaxDepth(257)).to.be.rejectedWith("Max depth is greater than hard cap"); + await expect(smt.setMaxDepth(1000000000)).to.be.rejectedWith( + "Max depth is greater than hard cap", + ); + }); +}); + +async function checkTestCaseMTPProof(smt: Contract, testCase: TestCaseMTPProof) { + for (const param of testCase.leavesToInsert) { + if (param.error) { + await expect(smt.add(param.i, param.v)).to.be.rejectedWith(param.error); + continue; + } + await smt.add(param.i, param.v); + } + + let proof; + + if (["number", "bigint", "string"].includes(typeof testCase.paramsToGetProof)) { + proof = await smt.getProof(testCase.paramsToGetProof); + } + + if (isProofByHistoricalRoot(testCase.paramsToGetProof)) { + proof = await smt.getProofByRoot( + testCase.paramsToGetProof.index, + testCase.paramsToGetProof.historicalRoot, + ); + } + + if (isProofByTime(testCase.paramsToGetProof)) { + proof = await smt.getProofByTime( + testCase.paramsToGetProof.index, + testCase.paramsToGetProof.timestamp, + ); + } + + if (isProofByBlock(testCase.paramsToGetProof)) { + proof = await smt.getProofByBlock( + testCase.paramsToGetProof.index, + testCase.paramsToGetProof.blockNumber, + ); + } + + if (testCase.expectedProof === undefined) { + return; + } + + checkMtpProof(proof, testCase.expectedProof as MtpProof); +} + +function checkMtpProof(proof, expectedProof: MtpProof) { + expect(proof.root).to.equal(expectedProof.root); + expect(proof.existence).to.equal(expectedProof.existence); + checkSiblings(proof.siblings, expectedProof.siblings); + expect(proof.index).to.equal(expectedProof.index); + expect(proof.value).to.equal(expectedProof.value); + expect(proof.auxExistence).to.equal(expectedProof.auxExistence); + expect(proof.auxIndex).to.equal(expectedProof.auxIndex); + expect(proof.auxValue).to.equal(expectedProof.auxValue); +} + +function checkSiblings(siblings, expectedSiblings: FixedArray) { + expect(siblings.length).to.equal(expectedSiblings.length); + for (let i = 0; i < siblings.length; i++) { + expect(siblings[i]).to.equal(expectedSiblings[i]); + } +} + +function isProofByHistoricalRoot(proof: ParamsProof): proof is ParamsProofByHistoricalRoot { + if (typeof proof !== "object") { + return false; + } + return (proof as ParamsProofByHistoricalRoot).historicalRoot !== undefined; +} + +function isProofByTime(proof: ParamsProof): proof is ParamsProofByTime { + if (typeof proof !== "object") { + return false; + } + return (proof as ParamsProofByTime).timestamp !== undefined; +} + +function isProofByBlock(proof: ParamsProof): proof is ParamsProofByBlock { + if (typeof proof !== "object") { + return false; + } + return (proof as ParamsProofByBlock).blockNumber !== undefined; +} diff --git a/test/smtLib/smtLib.test.ts b/test/smtLib/smtLib.test.ts index b74746c25..be6181ec7 100644 --- a/test/smtLib/smtLib.test.ts +++ b/test/smtLib/smtLib.test.ts @@ -63,7 +63,7 @@ describe("Merkle tree proofs of SMT", () => { leavesToInsert: [{ i: 4, v: 444 }], paramsToGetProof: 4, expectedProof: { - root: "22958977272721485097221938248834413051866334275107448764221195104671274302803", + root: "17172838131998611102390183760409471205043596092117126608119446264795219840387", existence: true, siblings: Array(64).fill(0) as FixedArray, index: 4, @@ -81,11 +81,11 @@ describe("Merkle tree proofs of SMT", () => { ], paramsToGetProof: 2, expectedProof: { - root: "6271825503835002167390262846571952849048843004006797408985626104559410349007", + root: "1441373283294527316959936912733986290796958290497398831120725405602534136472", existence: true, siblings: [ "0", - "22958977272721485097221938248834413051866334275107448764221195104671274302803", + "17172838131998611102390183760409471205043596092117126608119446264795219840387", ].concat(Array(62).fill(0)) as FixedArray, index: 2, value: 222, @@ -104,11 +104,11 @@ describe("Merkle tree proofs of SMT", () => { ], paramsToGetProof: 4, expectedProof: { - root: "16396604133323839338919891555968694657750322967047028825382984961917414135680", + root: "7518984336464932918389970949562858717786148793994477177454424989320848411811", existence: true, siblings: [ "0", - "106205607234728094801824820546945464662359795328028380050005012207955127231073", + "14251506067749311748434684987325372940957929637576367655195798776182705044439", ].concat(Array(62).fill(0)) as FixedArray, index: 4, value: 444, @@ -127,11 +127,11 @@ describe("Merkle tree proofs of SMT", () => { ], paramsToGetProof: 2, expectedProof: { - root: "16396604133323839338919891555968694657750322967047028825382984961917414135680", + root: "7518984336464932918389970949562858717786148793994477177454424989320848411811", existence: true, siblings: [ "0", - "22958977272721485097221938248834413051866334275107448764221195104671274302803", + "17172838131998611102390183760409471205043596092117126608119446264795219840387", ].concat(Array(62).fill(0)) as FixedArray, index: 2, value: 223, @@ -151,14 +151,14 @@ describe("Merkle tree proofs of SMT", () => { paramsToGetProof: { index: 2, historicalRoot: - "6271825503835002167390262846571952849048843004006797408985626104559410349007", + "1441373283294527316959936912733986290796958290497398831120725405602534136472", }, expectedProof: { - root: "6271825503835002167390262846571952849048843004006797408985626104559410349007", + root: "1441373283294527316959936912733986290796958290497398831120725405602534136472", existence: true, siblings: [ "0", - "22958977272721485097221938248834413051866334275107448764221195104671274302803", + "17172838131998611102390183760409471205043596092117126608119446264795219840387", ].concat(Array(62).fill(0)) as FixedArray, index: 2, value: 222, @@ -178,14 +178,14 @@ describe("Merkle tree proofs of SMT", () => { paramsToGetProof: { index: 4, historicalRoot: - "6271825503835002167390262846571952849048843004006797408985626104559410349007", + "1441373283294527316959936912733986290796958290497398831120725405602534136472", }, expectedProof: { - root: "6271825503835002167390262846571952849048843004006797408985626104559410349007", + root: "1441373283294527316959936912733986290796958290497398831120725405602534136472", existence: true, siblings: [ "0", - "78830676469618643418259161717399883581838239495817397573365812847452816685443", + "7886566820534140840061358290700879102455368051640197098120169021365756575690", ].concat(Array(62).fill(0)) as FixedArray, index: 4, value: 444, @@ -210,7 +210,7 @@ describe("Merkle tree proofs of SMT", () => { leavesToInsert: [{ i: 3, v: 333 }], paramsToGetProof: 3, expectedProof: { - root: "33418405206138732565596445817172696059570130655374283008161682599505407512429", + root: "9620424510282781520312293538235812893148558849034106480402397875614354541113", existence: true, siblings: Array(64).fill(0) as FixedArray, index: "3", @@ -228,12 +228,12 @@ describe("Merkle tree proofs of SMT", () => { ], paramsToGetProof: 7, expectedProof: { - root: "48360753217216801628383385897610760751395521399060591925309269460841854478880", + root: "19815655640973429763502848653182332850553075596353874436508539687379197912551", existence: true, siblings: [ "0", "0", - "33418405206138732565596445817172696059570130655374283008161682599505407512429", + "9620424510282781520312293538235812893148558849034106480402397875614354541113", ].concat(Array(61).fill(0)) as FixedArray, index: "7", value: "777", @@ -252,12 +252,12 @@ describe("Merkle tree proofs of SMT", () => { ], paramsToGetProof: 3, expectedProof: { - root: "99343494309985223004431568049537972373969240419555846106086358328736705856273", + root: "2542404438766480113585642347874916876260762595281604113407869433952183945353", existence: true, siblings: [ "0", "0", - "112313283557488934319533843029412977758210563652664683830759028786502416187169", + "1429787978940724228837527260031251962874080759861304177793880818323589539601", ].concat(Array(61).fill(0)) as FixedArray, index: "3", value: "333", @@ -276,12 +276,12 @@ describe("Merkle tree proofs of SMT", () => { ], paramsToGetProof: 7, expectedProof: { - root: "99343494309985223004431568049537972373969240419555846106086358328736705856273", + root: "2542404438766480113585642347874916876260762595281604113407869433952183945353", existence: true, siblings: [ "0", "0", - "33418405206138732565596445817172696059570130655374283008161682599505407512429", + "9620424510282781520312293538235812893148558849034106480402397875614354541113", ].concat(Array(61).fill(0)) as FixedArray, index: "7", value: "778", @@ -301,15 +301,15 @@ describe("Merkle tree proofs of SMT", () => { paramsToGetProof: { index: 3, historicalRoot: - "48360753217216801628383385897610760751395521399060591925309269460841854478880", + "19815655640973429763502848653182332850553075596353874436508539687379197912551", }, expectedProof: { - root: "48360753217216801628383385897610760751395521399060591925309269460841854478880", + root: "19815655640973429763502848653182332850553075596353874436508539687379197912551", existence: true, siblings: [ "0", "0", - "50296473614243876656937981110997905853887938112796700896791687746944667051313", + "5240534091252349892032931504453574475032932996013327005816531601253770276629", ].concat(Array(61).fill(0)) as FixedArray, index: "3", value: "333", @@ -329,15 +329,15 @@ describe("Merkle tree proofs of SMT", () => { paramsToGetProof: { index: 7, historicalRoot: - "48360753217216801628383385897610760751395521399060591925309269460841854478880", + "19815655640973429763502848653182332850553075596353874436508539687379197912551", }, expectedProof: { - root: "48360753217216801628383385897610760751395521399060591925309269460841854478880", + root: "19815655640973429763502848653182332850553075596353874436508539687379197912551", existence: true, siblings: [ "0", "0", - "33418405206138732565596445817172696059570130655374283008161682599505407512429", + "9620424510282781520312293538235812893148558849034106480402397875614354541113", ].concat(Array(61).fill(0)) as FixedArray, index: "7", value: "777", @@ -404,14 +404,14 @@ describe("Merkle tree proofs of SMT", () => { paramsToGetProof: "2254139687286372760549210172096572575821880629072851135313477335313002867070", expectedProof: { - root: "60007161943983486146906500160381123178622458462493617850821425381979612616956", + root: "13608938109359425943273886683542924994850927952989113192708029670282368959472", existence: true, siblings: [ - "11842043060776711430669533915732787960772621560012996635304388961537219372006", - "58180682306773137243712131495542060329763268994690040321062652108869108341898", - "94191217124907006392892149230646386351460750107001924913640299034519044572186", + "1832641583235778429809211853568910873051692053406604919942416271965516221694", + "7178355728345475638578628524851385851849048771654648953856812774555221490254", + "9602796824988200934471038492033878534627864374776542278379449014085059916942", "0", - "68634643421786404149040398078509254181052706327147925186290660391175330864565", + "16358410446199419264933021028144760440785144596817177810806370009968803152521", ].concat(Array(59).fill(0)) as FixedArray, index: "2254139687286372760549210172096572575821880629072851135313477335313002867070", value: "2254139687286372760549210172096572575821880629072851135313477335313002867070", @@ -438,7 +438,7 @@ describe("Merkle tree proofs of SMT", () => { leavesToInsert: [{ i: 4, v: 444 }], paramsToGetProof: 2, expectedProof: { - root: "22958977272721485097221938248834413051866334275107448764221195104671274302803", + root: "17172838131998611102390183760409471205043596092117126608119446264795219840387", existence: false, siblings: Array(64).fill(0) as FixedArray, index: 2, @@ -457,11 +457,11 @@ describe("Merkle tree proofs of SMT", () => { ], paramsToGetProof: 6, expectedProof: { - root: "6271825503835002167390262846571952849048843004006797408985626104559410349007", + root: "1441373283294527316959936912733986290796958290497398831120725405602534136472", existence: false, siblings: [ "0", - "22958977272721485097221938248834413051866334275107448764221195104671274302803", + "17172838131998611102390183760409471205043596092117126608119446264795219840387", ].concat(Array(62).fill(0)) as FixedArray, index: 6, value: 222, @@ -479,10 +479,10 @@ describe("Merkle tree proofs of SMT", () => { ], paramsToGetProof: 1, expectedProof: { - root: "6271825503835002167390262846571952849048843004006797408985626104559410349007", + root: "1441373283294527316959936912733986290796958290497398831120725405602534136472", existence: false, siblings: [ - "96535818096110143691607859947815292580929567133286005889885442808119755306104", + "6675047397658061825643898157145998146182607268727302490292227324666463200032", ].concat(Array(63).fill(0)) as FixedArray, index: 1, value: 0, @@ -502,14 +502,14 @@ describe("Merkle tree proofs of SMT", () => { paramsToGetProof: { index: 6, historicalRoot: - "6271825503835002167390262846571952849048843004006797408985626104559410349007", + "1441373283294527316959936912733986290796958290497398831120725405602534136472", }, expectedProof: { - root: "6271825503835002167390262846571952849048843004006797408985626104559410349007", + root: "1441373283294527316959936912733986290796958290497398831120725405602534136472", existence: false, siblings: [ "0", - "22958977272721485097221938248834413051866334275107448764221195104671274302803", + "17172838131998611102390183760409471205043596092117126608119446264795219840387", ].concat(Array(62).fill(0)) as FixedArray, index: 6, value: 222, @@ -529,13 +529,13 @@ describe("Merkle tree proofs of SMT", () => { paramsToGetProof: { index: 1, historicalRoot: - "6271825503835002167390262846571952849048843004006797408985626104559410349007", + "1441373283294527316959936912733986290796958290497398831120725405602534136472", }, expectedProof: { - root: "6271825503835002167390262846571952849048843004006797408985626104559410349007", + root: "1441373283294527316959936912733986290796958290497398831120725405602534136472", existence: false, siblings: [ - "96535818096110143691607859947815292580929567133286005889885442808119755306104", + "6675047397658061825643898157145998146182607268727302490292227324666463200032", ].concat(Array(63).fill(0)) as FixedArray, index: 1, value: 0, @@ -555,13 +555,13 @@ describe("Merkle tree proofs of SMT", () => { paramsToGetProof: { index: 1, historicalRoot: - "6271825503835002167390262846571952849048843004006797408985626104559410349007", + "1441373283294527316959936912733986290796958290497398831120725405602534136472", }, expectedProof: { - root: "6271825503835002167390262846571952849048843004006797408985626104559410349007", + root: "1441373283294527316959936912733986290796958290497398831120725405602534136472", existence: false, siblings: [ - "96535818096110143691607859947815292580929567133286005889885442808119755306104", + "6675047397658061825643898157145998146182607268727302490292227324666463200032", ].concat(Array(63).fill(0)) as FixedArray, index: 1, value: 0, @@ -586,7 +586,7 @@ describe("Merkle tree proofs of SMT", () => { leavesToInsert: [{ i: 3, v: 333 }], paramsToGetProof: 7, expectedProof: { - root: "33418405206138732565596445817172696059570130655374283008161682599505407512429", + root: "9620424510282781520312293538235812893148558849034106480402397875614354541113", existence: false, siblings: Array(64).fill(0) as FixedArray, index: "7", @@ -605,12 +605,12 @@ describe("Merkle tree proofs of SMT", () => { ], paramsToGetProof: 11, expectedProof: { - root: "48360753217216801628383385897610760751395521399060591925309269460841854478880", + root: "19815655640973429763502848653182332850553075596353874436508539687379197912551", existence: false, siblings: [ "0", "0", - "50296473614243876656937981110997905853887938112796700896791687746944667051313", + "5240534091252349892032931504453574475032932996013327005816531601253770276629", ].concat(Array(61).fill(0)) as FixedArray, index: "11", value: "333", @@ -628,11 +628,11 @@ describe("Merkle tree proofs of SMT", () => { ], paramsToGetProof: 1, expectedProof: { - root: "48360753217216801628383385897610760751395521399060591925309269460841854478880", + root: "19815655640973429763502848653182332850553075596353874436508539687379197912551", existence: false, siblings: [ "0", - "112876657748891444100900801150886368101744673616075342940779383085926542312690", + "26063976833489350915848330858375580362565300311897865524107747624425916356", ].concat(Array(62).fill(0)) as FixedArray, index: "1", value: "0", @@ -652,15 +652,15 @@ describe("Merkle tree proofs of SMT", () => { paramsToGetProof: { index: 11, historicalRoot: - "48360753217216801628383385897610760751395521399060591925309269460841854478880", + "19815655640973429763502848653182332850553075596353874436508539687379197912551", }, expectedProof: { - root: "48360753217216801628383385897610760751395521399060591925309269460841854478880", + root: "19815655640973429763502848653182332850553075596353874436508539687379197912551", existence: false, siblings: [ "0", "0", - "50296473614243876656937981110997905853887938112796700896791687746944667051313", + "5240534091252349892032931504453574475032932996013327005816531601253770276629", ].concat(Array(61).fill(0)) as FixedArray, index: "11", value: "333", @@ -680,14 +680,14 @@ describe("Merkle tree proofs of SMT", () => { paramsToGetProof: { index: 1, historicalRoot: - "48360753217216801628383385897610760751395521399060591925309269460841854478880", + "19815655640973429763502848653182332850553075596353874436508539687379197912551", }, expectedProof: { - root: "48360753217216801628383385897610760751395521399060591925309269460841854478880", + root: "19815655640973429763502848653182332850553075596353874436508539687379197912551", existence: false, siblings: [ "0", - "112876657748891444100900801150886368101744673616075342940779383085926542312690", + "26063976833489350915848330858375580362565300311897865524107747624425916356", ].concat(Array(62).fill(0)) as FixedArray, index: "1", value: "0", @@ -707,15 +707,15 @@ describe("Merkle tree proofs of SMT", () => { paramsToGetProof: { index: 11, historicalRoot: - "48360753217216801628383385897610760751395521399060591925309269460841854478880", + "19815655640973429763502848653182332850553075596353874436508539687379197912551", }, expectedProof: { - root: "48360753217216801628383385897610760751395521399060591925309269460841854478880", + root: "19815655640973429763502848653182332850553075596353874436508539687379197912551", existence: false, siblings: [ "0", "0", - "50296473614243876656937981110997905853887938112796700896791687746944667051313", + "5240534091252349892032931504453574475032932996013327005816531601253770276629", ].concat(Array(61).fill(0)) as FixedArray, index: "11", value: "333", @@ -782,11 +782,11 @@ describe("Merkle tree proofs of SMT", () => { paramsToGetProof: "2254139687286372760549210172096572575821880629072851135313477335313002867071", expectedProof: { - root: "60007161943983486146906500160381123178622458462493617850821425381979612616956", + root: "13608938109359425943273886683542924994850927952989113192708029670282368959472", existence: false, siblings: [ - "15649299891651963645168066741171440452486996543233903096221914286509395708217", - "35485910319040178947164101851755181176671699873501710834388743638308164334229", + "1579434795526423183097986076173558337173432003423506163175532158546629036074", + "4682852777402635256724726626165554137517366900378681615797410665482859853011", ].concat(Array(62).fill(0)) as FixedArray, index: "2254139687286372760549210172096572575821880629072851135313477335313002867071", value: "6710060555229139303017247577694107284750887011584715720178646167607892089915", @@ -844,12 +844,12 @@ describe("Merkle tree proofs of SMT", () => { paramsToGetProof: "6271287741236698691604141726361751264311688318470481595940384433868807274649", expectedProof: { - root: "60007161943983486146906500160381123178622458462493617850821425381979612616956", + root: "13608938109359425943273886683542924994850927952989113192708029670282368959472", existence: false, siblings: [ - "15649299891651963645168066741171440452486996543233903096221914286509395708217", - "5582158759613617994025042938199822589967969783458491971055101796671660471167", - "73073270877252609309304544688345883755735059749695338671799354601148846061501", + "1579434795526423183097986076173558337173432003423506163175532158546629036074", + "2087966847430044349684271178373838655869903749020106568902582482402101627428", + "4559542841575065171721871277134371244969805411208646727128331102091234595131", ].concat(Array(61).fill(0)) as FixedArray, index: "6271287741236698691604141726361751264311688318470481595940384433868807274649", value: "0", @@ -919,12 +919,12 @@ describe("Merkle tree proofs of SMT", () => { ], paramsToGetProof: genMaxBinaryNumber(64), expectedProof: { - root: "110199947410708031149228851387837770054145252372569190168540009983771486866316", + root: "11998361913555620744473305594791175460338619045531124782442564216176360071119", existence: true, siblings: Array(63) .fill("0") .concat([ - "74399159356430181752973204803851211752558544080760906099963521203069195578461", + "2316164946517152574748505824782744746774130618858955093234986590959173249001", ]) as FixedArray, index: "18446744073709551615", value: "100", @@ -941,12 +941,12 @@ describe("Merkle tree proofs of SMT", () => { ], paramsToGetProof: genMaxBinaryNumber(63) + BigInt(1), expectedProof: { - root: "16625293229838138370855084455911863446233219628941478502997473061463286175203", + root: "7851364894145224193468155117213470810715599698407298245809392679874651946419", existence: true, siblings: Array(63) .fill("0") .concat([ - "85255324799129495636878324767942437404808863678639087371922338002905282669100", + "1321531033810699781922362637795367691578399901805457949741207048379959301312", ]) as FixedArray, index: "9223372036854775808", value: "100", @@ -964,12 +964,12 @@ describe("Merkle tree proofs of SMT", () => { ], paramsToGetProof: "8490314929315140110", expectedProof: { - root: "16484897135457263633438825596660941524517623843033410328799978262525614569225", + root: "5640762368545907066458698273870257445508350556310355422307954953617544677976", existence: true, siblings: Array(63) .fill("0") .concat([ - "9906144696231549323869068974423946066277130279313871204886462229234859073912", + "21059535177784591611482142343728384369736848354398899541533132315810203341674", ]) as FixedArray, index: "8490314929315140110", value: "100", @@ -1233,7 +1233,7 @@ describe("Binary search in SMT root history", () => { beforeEach(async () => { await loadFixture(deployContractsFixture); const { number: latestBlockNumber } = await hre.ethers.provider.getBlock("latest"); - let blocksToMine = 17 - latestBlockNumber; + let blocksToMine = 15 - latestBlockNumber; while (blocksToMine > 0) { await hre.network.provider.request({ @@ -1707,7 +1707,7 @@ describe("Binary search in SMT proofs", () => { timestamp: 0, }, expectedProof: { - root: "22958977272721485097221938248834413051866334275107448764221195104671274302803", + root: "17172838131998611102390183760409471205043596092117126608119446264795219840387", existence: true, siblings: Array(64).fill(0) as FixedArray, index: 4, @@ -1725,7 +1725,7 @@ describe("Binary search in SMT proofs", () => { blockNumber: 0, }, expectedProof: { - root: "22958977272721485097221938248834413051866334275107448764221195104671274302803", + root: "17172838131998611102390183760409471205043596092117126608119446264795219840387", existence: true, siblings: Array(64).fill(0) as FixedArray, index: 4, From a5721e96136a69c1ccef5f740ff14a951049f74c Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Fri, 5 Dec 2025 13:30:37 -0500 Subject: [PATCH 06/12] change IHasher to an interface Signed-off-by: Jim Zhang --- contracts/interfaces/IHasher.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contracts/interfaces/IHasher.sol b/contracts/interfaces/IHasher.sol index 2d62001ff..f1b1b5e07 100644 --- a/contracts/interfaces/IHasher.sol +++ b/contracts/interfaces/IHasher.sol @@ -4,18 +4,18 @@ pragma solidity 0.8.27; /** * @dev IHasher. Interface for generating hashes. Specifically used for Merkle Tree hashing. */ -abstract contract IHasher { +interface IHasher { /** * @dev hash2. hashes two uint256 parameters and returns the resulting hash as uint256. * @param params The parameters array of size 2 to be hashed. * @return The resulting hash as uint256. */ - function hash2(uint256[2] memory params) external pure virtual returns (uint256); + function hash2(uint256[2] memory params) external pure returns (uint256); /** * @dev hash3. hashes three uint256 parameters and returns the resulting hash as uint256. * @param params The parameters array of size 3 to be hashed. * @return The resulting hash as uint256. */ - function hash3(uint256[3] memory params) external pure virtual returns (uint256); + function hash3(uint256[3] memory params) external pure returns (uint256); } From a6193c6f7422917692d46158aff8093691c272f7 Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Fri, 5 Dec 2025 13:38:28 -0500 Subject: [PATCH 07/12] make SmtLibKeccakTestWrapper inherit from SmtLibTestWrapper Signed-off-by: Jim Zhang --- contracts/lib/SmtLib.sol | 2 +- .../test-helpers/SmtLibKeccakTestWrapper.sol | 70 +------------------ 2 files changed, 4 insertions(+), 68 deletions(-) diff --git a/contracts/lib/SmtLib.sol b/contracts/lib/SmtLib.sol index 107ee241e..8d3d76c41 100644 --- a/contracts/lib/SmtLib.sol +++ b/contracts/lib/SmtLib.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity 0.8.27; -import {ArrayUtils} from "./ArrayUtils.sol"; import {PoseidonUnit2L, PoseidonUnit3L} from "./Poseidon.sol"; +import {ArrayUtils} from "./ArrayUtils.sol"; import {IHasher} from "../interfaces/IHasher.sol"; /// @title A sparse merkle tree implementation, which keeps tree history. diff --git a/contracts/test-helpers/SmtLibKeccakTestWrapper.sol b/contracts/test-helpers/SmtLibKeccakTestWrapper.sol index 58dccb457..1ea5a1238 100644 --- a/contracts/test-helpers/SmtLibKeccakTestWrapper.sol +++ b/contracts/test-helpers/SmtLibKeccakTestWrapper.sol @@ -2,77 +2,13 @@ pragma solidity 0.8.27; import {SmtLib} from "../lib/SmtLib.sol"; +import {SmtLibTestWrapper} from "./SmtLibTestWrapper.sol"; import {Keccak256Hasher} from "../lib/hash/KeccakHasher.sol"; -contract SmtLibKeccakTestWrapper { +contract SmtLibKeccakTestWrapper is SmtLibTestWrapper { using SmtLib for SmtLib.Data; - SmtLib.Data internal smtData; - - constructor(uint256 maxDepth) { - smtData.initialize(maxDepth); + constructor(uint256 maxDepth) SmtLibTestWrapper(maxDepth) { smtData.setHasher(new Keccak256Hasher()); } - - function add(uint256 i, uint256 v) public { - smtData.addLeaf(i, v); - } - - function getProof(uint256 id) public view returns (SmtLib.Proof memory) { - return smtData.getProof(id); - } - - function getProofByRoot(uint256 id, uint256 root) public view returns (SmtLib.Proof memory) { - return smtData.getProofByRoot(id, root); - } - - function getProofByTime( - uint256 id, - uint256 timestamp - ) public view returns (SmtLib.Proof memory) { - return smtData.getProofByTime(id, timestamp); - } - - function getProofByBlock(uint256 id, uint256 _block) public view returns (SmtLib.Proof memory) { - return smtData.getProofByBlock(id, _block); - } - - function getRootHistory( - uint256 start, - uint256 length - ) public view returns (SmtLib.RootEntryInfo[] memory) { - return smtData.getRootHistory(start, length); - } - - function getRootHistoryLength() public view returns (uint256) { - return smtData.getRootHistoryLength(); - } - - function getRoot() public view returns (uint256) { - return smtData.getRoot(); - } - - function getRootInfo(uint256 root) public view returns (SmtLib.RootEntryInfo memory) { - return smtData.getRootInfo(root); - } - - function getRootInfoListLengthByRoot(uint256 root) public view returns (uint256) { - return smtData.getRootInfoListLengthByRoot(root); - } - - function getRootInfoListByRoot( - uint256 root, - uint256 start, - uint256 length - ) public view returns (SmtLib.RootEntryInfo[] memory) { - return smtData.getRootInfoListByRoot(root, start, length); - } - - function setMaxDepth(uint256 maxDepth) public { - smtData.setMaxDepth(maxDepth); - } - - function getMaxDepth() public view returns (uint256) { - return smtData.getMaxDepth(); - } } From 78c998aaf8dd722e8b3b2c67d2e644dd27822459 Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Fri, 5 Dec 2025 13:39:22 -0500 Subject: [PATCH 08/12] misc fixes Signed-off-by: Jim Zhang --- helpers/DeployHelper.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helpers/DeployHelper.ts b/helpers/DeployHelper.ts index 2b24fdfab..26f61043c 100644 --- a/helpers/DeployHelper.ts +++ b/helpers/DeployHelper.ts @@ -35,7 +35,7 @@ export class DeployHelper { constructor( private signers: SignerWithAddress[], private readonly enableLogging: boolean = false, - ) { } + ) {} static async initialize( signers: SignerWithAddress[] | null = null, From d9de57ef20fd0604c92f0131334da5820276337f Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Fri, 5 Dec 2025 16:18:10 -0500 Subject: [PATCH 09/12] use npx for patch-package Signed-off-by: Jim Zhang --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 954334082..a1b1689cd 100644 --- a/package.json +++ b/package.json @@ -157,7 +157,7 @@ "lint:contracts": "npx solhint contracts/**/*.sol", "prettier:contracts": "prettier --write --plugin=prettier-plugin-solidity 'contracts/**/*.sol'", "slither": "slither .", - "postinstall": "patch-package" + "postinstall": "npx patch-package" }, "overrides": { "ws": "^8.17.1", From 51ef1a6c6563acd1e6f2a146aaa8a4a9530268a5 Mon Sep 17 00:00:00 2001 From: jimthematrix Date: Wed, 7 Jan 2026 09:16:20 -0500 Subject: [PATCH 10/12] Update contracts/lib/SmtLib.sol Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- contracts/lib/SmtLib.sol | 2 ++ 1 file changed, 2 insertions(+) diff --git a/contracts/lib/SmtLib.sol b/contracts/lib/SmtLib.sol index 8d3d76c41..657b2c823 100644 --- a/contracts/lib/SmtLib.sol +++ b/contracts/lib/SmtLib.sol @@ -144,6 +144,8 @@ library SmtLib { * @param customerHasher IHasher implementation to be used for hashing. */ function setHasher(Data storage self, IHasher customerHasher) external { + require(address(customerHasher) != address(0), "Invalid hasher"); + require(self.rootEntries.length == 1, "Hasher must be set before SMT usage"); self.isCustomHasherSet = true; self.hasher = customerHasher; } From 526860ea75ebd7994e14e5d70e1b0eb7123b7fdf Mon Sep 17 00:00:00 2001 From: jimthematrix Date: Wed, 7 Jan 2026 09:16:52 -0500 Subject: [PATCH 11/12] Update contracts/lib/SmtLib.sol for naming of parameters Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- contracts/lib/SmtLib.sol | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/contracts/lib/SmtLib.sol b/contracts/lib/SmtLib.sol index 657b2c823..69c612e9d 100644 --- a/contracts/lib/SmtLib.sol +++ b/contracts/lib/SmtLib.sol @@ -143,11 +143,11 @@ library SmtLib { * @dev Sets custom hashers for the SMT. MUST be called before any other SMT operations. * @param customerHasher IHasher implementation to be used for hashing. */ - function setHasher(Data storage self, IHasher customerHasher) external { - require(address(customerHasher) != address(0), "Invalid hasher"); - require(self.rootEntries.length == 1, "Hasher must be set before SMT usage"); + * @param customHasher IHasher implementation to be used for hashing. + */ + function setHasher(Data storage self, IHasher customHasher) external { self.isCustomHasherSet = true; - self.hasher = customerHasher; + self.hasher = customHasher; } /** From 06eece5d07c807bcbed3fd7eb30386e2d3fb0b1a Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Wed, 7 Jan 2026 09:29:34 -0500 Subject: [PATCH 12/12] reduce the __gap array size to keep the storage slot constant Signed-off-by: Jim Zhang --- contracts/lib/SmtLib.sol | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/contracts/lib/SmtLib.sol b/contracts/lib/SmtLib.sol index 69c612e9d..b5254a632 100644 --- a/contracts/lib/SmtLib.sol +++ b/contracts/lib/SmtLib.sol @@ -54,8 +54,7 @@ library SmtLib { // of the SMT library to add new Data struct fields without shifting down // storage of upgradable contracts that use this struct as a state variable // (see https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#storage-gaps) - uint256[45] __gap; - bool isCustomHasherSet; + uint256[44] __gap; IHasher hasher; } @@ -141,12 +140,11 @@ library SmtLib { /** * @dev Sets custom hashers for the SMT. MUST be called before any other SMT operations. - * @param customerHasher IHasher implementation to be used for hashing. - */ * @param customHasher IHasher implementation to be used for hashing. */ function setHasher(Data storage self, IHasher customHasher) external { - self.isCustomHasherSet = true; + require(address(customHasher) != address(0), "Invalid hasher"); + require(self.rootEntries.length == 1, "Hasher must be set before SMT usage"); self.hasher = customHasher; } @@ -580,7 +578,7 @@ library SmtLib { function _getNodeHash(Data storage self, Node memory node) internal view returns (uint256) { uint256 nodeHash = 0; if (node.nodeType == NodeType.LEAF) { - if (self.isCustomHasherSet) { + if (address(self.hasher) != address(0)) { uint256[3] memory params = [node.index, node.value, uint256(1)]; nodeHash = self.hasher.hash3(params); } else { @@ -588,7 +586,7 @@ library SmtLib { nodeHash = PoseidonUnit3L.poseidon(params); } } else if (node.nodeType == NodeType.MIDDLE) { - if (self.isCustomHasherSet) { + if (address(self.hasher) != address(0)) { nodeHash = self.hasher.hash2([node.childLeft, node.childRight]); } else { nodeHash = PoseidonUnit2L.poseidon([node.childLeft, node.childRight]);