From b75a7e6a8ecca45607139024132c7498e47e9978 Mon Sep 17 00:00:00 2001 From: fcarreiro Date: Thu, 27 Mar 2025 10:46:52 +0000 Subject: [PATCH 1/3] feat(avm): merkle hints (part 2) --- .../barretenberg/vm2/common/avm_inputs.hpp | 4 ++- .../vm2/common/avm_inputs.testdata.bin | Bin 110408 -> 115253 bytes .../vm2/simulation/lib/db_interfaces.hpp | 3 ++ .../vm2/simulation/lib/raw_data_dbs.cpp | 32 +++++++++++++++++- .../vm2/simulation/lib/raw_data_dbs.hpp | 5 +++ .../vm2/simulation/testing/mock_dbs.hpp | 4 +++ .../src/public/hinting_db_sources.ts | 13 +++++++ yarn-project/stdlib/src/avm/avm.ts | 6 ++++ yarn-project/stdlib/src/tests/factories.ts | 30 ++++++++++++++-- .../stdlib/src/trees/nullifier_leaf.ts | 4 +++ 10 files changed, 96 insertions(+), 5 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/vm2/common/avm_inputs.hpp b/barretenberg/cpp/src/barretenberg/vm2/common/avm_inputs.hpp index 3fbc3d72c9ac..a391c7383e3b 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/common/avm_inputs.hpp +++ b/barretenberg/cpp/src/barretenberg/vm2/common/avm_inputs.hpp @@ -174,6 +174,7 @@ struct ExecutionHints { std::vector getSiblingPathHints; std::vector getPreviousValueIndexHints; std::vector> getLeafPreimageHintsPublicDataTree; + std::vector> getLeafPreimageHintsNullifierTree; bool operator==(const ExecutionHints& other) const = default; @@ -183,7 +184,8 @@ struct ExecutionHints { bytecodeCommitments, getSiblingPathHints, getPreviousValueIndexHints, - getLeafPreimageHintsPublicDataTree); + getLeafPreimageHintsPublicDataTree, + getLeafPreimageHintsNullifierTree); }; //////////////////////////////////////////////////////////////////////////// diff --git a/barretenberg/cpp/src/barretenberg/vm2/common/avm_inputs.testdata.bin b/barretenberg/cpp/src/barretenberg/vm2/common/avm_inputs.testdata.bin index 9924c420ed1df94f24791217917064594056d5b9..7791f37c80f8c8914925c869bbeefc972ee864bc 100644 GIT binary patch delta 1870 zcmZwFO-K}B9LMoylgdofgHoGNnotM@q3-+3?7DBWnP@MEBCG{R*;*H~6{J##RiaWW zCew(}%FQH7OKm&WK`64;7!Qdc7?vAKrD)J0YwY>S%;C9z=fm&+FmEQ;4K=RsuTNw) zgc}m04T;Rg6%W~R?8u11uL3L(6oJIwqyw!aHE!ZZpqQR@9CUg!J+&B zPJ?l!i_$nPv@Z|Cms>;C*Xf!;p`pA1O-sh<$T%HdPSuoI5vY4`fzBCaPDEJ`PDeOB z;q>6Ro3D=Vu#P{ab0!6cvJae|a0bE|z)zcO1o zukQYyMCU9r7el!ToRM%Q!kMtnH*9Wh`?l6h=d3a(qO^cB5zb6FGuAz8Rkv8CO*3?k zmAM$oI5_hPxAGH@N&krjIB*lS^UGcBe^cpPk%B{+296_~mvCMj>pVT26V|hfbnbx6i71!; Nb4*9{QU>$7;~(FS8~*?R delta 26 icmdnm!hYf&+lE?BM)u}9&h2%aj9k3iOEnn}%m4s|APG4D diff --git a/barretenberg/cpp/src/barretenberg/vm2/simulation/lib/db_interfaces.hpp b/barretenberg/cpp/src/barretenberg/vm2/simulation/lib/db_interfaces.hpp index 163f93d1f290..15f165d26d93 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/simulation/lib/db_interfaces.hpp +++ b/barretenberg/cpp/src/barretenberg/vm2/simulation/lib/db_interfaces.hpp @@ -3,6 +3,7 @@ #include #include "barretenberg/crypto/merkle_tree/hash_path.hpp" +#include "barretenberg/crypto/merkle_tree/indexed_tree/indexed_leaf.hpp" #include "barretenberg/crypto/merkle_tree/response.hpp" #include "barretenberg/crypto/merkle_tree/types.hpp" #include "barretenberg/vm2/common/avm_inputs.hpp" @@ -33,6 +34,8 @@ class LowLevelMerkleDBInterface { // We don't template the preimage methods because templated methods cannot be virtual. virtual crypto::merkle_tree::IndexedLeaf get_leaf_preimage_public_data_tree(crypto::merkle_tree::index_t leaf_index) const = 0; + virtual crypto::merkle_tree::IndexedLeaf get_leaf_preimage_nullifier_tree( + crypto::merkle_tree::index_t leaf_index) const = 0; }; // High level access to a merkle db. In general these will be constrained. diff --git a/barretenberg/cpp/src/barretenberg/vm2/simulation/lib/raw_data_dbs.cpp b/barretenberg/cpp/src/barretenberg/vm2/simulation/lib/raw_data_dbs.cpp index f6ac295482df..36f5a83e804c 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/simulation/lib/raw_data_dbs.cpp +++ b/barretenberg/cpp/src/barretenberg/vm2/simulation/lib/raw_data_dbs.cpp @@ -5,6 +5,7 @@ #include #include "barretenberg/common/log.hpp" +#include "barretenberg/crypto/merkle_tree/indexed_tree/indexed_leaf.hpp" #include "barretenberg/vm2/simulation/lib/contract_crypto.hpp" namespace bb::avm2::simulation { @@ -101,7 +102,9 @@ HintedRawMerkleDB::HintedRawMerkleDB(const ExecutionHints& hints, const TreeSnap "\n * get_previous_value_index hints: ", hints.getPreviousValueIndexHints.size(), "\n * get_leaf_preimage hints_public_data_tree: ", - hints.getLeafPreimageHintsPublicDataTree.size()); + hints.getLeafPreimageHintsPublicDataTree.size(), + "\n * get_leaf_preimage hints_nullifier_tree: ", + hints.getLeafPreimageHintsNullifierTree.size()); debug("Initializing HintedRawMerkleDB with snapshots...", "\n * nullifierTree: ", tree_roots.nullifierTree.root, @@ -149,6 +152,15 @@ HintedRawMerkleDB::HintedRawMerkleDB(const ExecutionHints& hints, const TreeSnap /*nextVal=*/get_leaf_preimage_hint.nextValue, }; } + + for (const auto& get_leaf_preimage_hint : hints.getLeafPreimageHintsNullifierTree) { + GetLeafPreimageKey key = { get_leaf_preimage_hint.hintKey, get_leaf_preimage_hint.index }; + get_leaf_preimage_hints_nullifier_tree[key] = { + /*val=*/get_leaf_preimage_hint.leaf, + /*nextIdx=*/get_leaf_preimage_hint.nextIndex, + /*nextVal=*/get_leaf_preimage_hint.nextValue, + }; + } } const AppendOnlyTreeSnapshot& HintedRawMerkleDB::get_tree_info(world_state::MerkleTreeId tree_id) const @@ -225,4 +237,22 @@ crypto::merkle_tree::IndexedLeaf Hinte return it->second; } +crypto::merkle_tree::IndexedLeaf HintedRawMerkleDB:: + get_leaf_preimage_nullifier_tree(crypto::merkle_tree::index_t leaf_index) const +{ + auto tree_info = get_tree_info(world_state::MerkleTreeId::NULLIFIER_TREE); + GetLeafPreimageKey key = { tree_info, leaf_index }; + auto it = get_leaf_preimage_hints_nullifier_tree.find(key); + if (it == get_leaf_preimage_hints_nullifier_tree.end()) { + throw std::runtime_error(format("Leaf preimage (NULLIFIER_TREE) not found for key (root: ", + tree_info.root, + ", size: ", + tree_info.nextAvailableLeafIndex, + ", leaf_index: ", + leaf_index, + ")")); + } + return it->second; +} + } // namespace bb::avm2::simulation diff --git a/barretenberg/cpp/src/barretenberg/vm2/simulation/lib/raw_data_dbs.hpp b/barretenberg/cpp/src/barretenberg/vm2/simulation/lib/raw_data_dbs.hpp index e26f8f474e78..b67e63fba55f 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/simulation/lib/raw_data_dbs.hpp +++ b/barretenberg/cpp/src/barretenberg/vm2/simulation/lib/raw_data_dbs.hpp @@ -2,6 +2,7 @@ #include "barretenberg/common/utils.hpp" #include "barretenberg/crypto/merkle_tree/hash_path.hpp" +#include "barretenberg/crypto/merkle_tree/indexed_tree/indexed_leaf.hpp" #include "barretenberg/vm2/common/avm_inputs.hpp" #include "barretenberg/vm2/common/aztec_types.hpp" #include "barretenberg/vm2/common/field.hpp" @@ -42,6 +43,8 @@ class HintedRawMerkleDB final : public LowLevelMerkleDBInterface { const FF& value) const override; crypto::merkle_tree::IndexedLeaf get_leaf_preimage_public_data_tree( crypto::merkle_tree::index_t leaf_index) const override; + crypto::merkle_tree::IndexedLeaf get_leaf_preimage_nullifier_tree( + crypto::merkle_tree::index_t leaf_index) const override; private: TreeSnapshots tree_roots; @@ -55,6 +58,8 @@ class HintedRawMerkleDB final : public LowLevelMerkleDBInterface { using GetLeafPreimageKey = utils::HashableTuple; unordered_flat_map> get_leaf_preimage_hints_public_data_tree; + unordered_flat_map> + get_leaf_preimage_hints_nullifier_tree; const AppendOnlyTreeSnapshot& get_tree_info(world_state::MerkleTreeId tree_id) const; }; diff --git a/barretenberg/cpp/src/barretenberg/vm2/simulation/testing/mock_dbs.hpp b/barretenberg/cpp/src/barretenberg/vm2/simulation/testing/mock_dbs.hpp index 718dd097cf73..f45d338f415e 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/simulation/testing/mock_dbs.hpp +++ b/barretenberg/cpp/src/barretenberg/vm2/simulation/testing/mock_dbs.hpp @@ -36,6 +36,10 @@ class MockLowLevelMerkleDB : public LowLevelMerkleDBInterface { get_leaf_preimage_public_data_tree, (crypto::merkle_tree::index_t leaf_index), (const, override)); + MOCK_METHOD(crypto::merkle_tree::IndexedLeaf, + get_leaf_preimage_nullifier_tree, + (crypto::merkle_tree::index_t leaf_index), + (const, override)); }; class MockHighLevelMerkleDB : public HighLevelMerkleDBInterface { diff --git a/yarn-project/simulator/src/public/hinting_db_sources.ts b/yarn-project/simulator/src/public/hinting_db_sources.ts index d65521b25d34..e889032fdf99 100644 --- a/yarn-project/simulator/src/public/hinting_db_sources.ts +++ b/yarn-project/simulator/src/public/hinting_db_sources.ts @@ -7,6 +7,7 @@ import { AvmContractClassHint, AvmContractInstanceHint, type AvmExecutionHints, + AvmGetLeafPreimageHintNullifierTree, AvmGetLeafPreimageHintPublicDataTree, AvmGetPreviousValueIndexHint, AvmGetSiblingPathHint, @@ -17,6 +18,7 @@ import { AppendOnlyTreeSnapshot, type IndexedTreeId, MerkleTreeId, + NullifierLeaf, PublicDataTreeLeaf, type SequentialInsertionResult, getTreeName, @@ -147,6 +149,17 @@ export class HintingPublicTreesDB extends PublicTreesDB { ), ); break; + case MerkleTreeId.NULLIFIER_TREE: + this.hints.getLeafPreimageHintsNullifierTree.push( + new AvmGetLeafPreimageHintNullifierTree( + key, + index, + preimage.asLeaf() as NullifierLeaf, + preimage.getNextIndex(), + new Fr(preimage.getNextKey()), + ), + ); + break; default: HintingPublicTreesDB.log.debug(`getLeafPreimage not hinted for tree ${getTreeName(treeId)} yet!`); break; diff --git a/yarn-project/stdlib/src/avm/avm.ts b/yarn-project/stdlib/src/avm/avm.ts index 41bac0ed980f..34f6f1556b42 100644 --- a/yarn-project/stdlib/src/avm/avm.ts +++ b/yarn-project/stdlib/src/avm/avm.ts @@ -9,6 +9,7 @@ import { AztecAddress } from '../aztec-address/index.js'; import { PublicKeys } from '../keys/public_keys.js'; import { AppendOnlyTreeSnapshot } from '../trees/append_only_tree_snapshot.js'; import type { MerkleTreeId } from '../trees/merkle_tree_id.js'; +import { NullifierLeaf } from '../trees/nullifier_leaf.js'; import { PublicDataTreeLeaf } from '../trees/public_data_leaf.js'; import { AvmCircuitPublicInputs } from './avm_circuit_public_inputs.js'; import { serializeWithMessagePack } from './message_pack.js'; @@ -187,6 +188,7 @@ function AvmGetLeafPreimageHintFactory(klass: { } export class AvmGetLeafPreimageHintPublicDataTree extends AvmGetLeafPreimageHintFactory(PublicDataTreeLeaf) {} +export class AvmGetLeafPreimageHintNullifierTree extends AvmGetLeafPreimageHintFactory(NullifierLeaf) {} //////////////////////////////////////////////////////////////////////////// // Hints (other) @@ -225,6 +227,7 @@ export class AvmExecutionHints { public readonly getSiblingPathHints: AvmGetSiblingPathHint[] = [], public readonly getPreviousValueIndexHints: AvmGetPreviousValueIndexHint[] = [], public readonly getLeafPreimageHintsPublicDataTree: AvmGetLeafPreimageHintPublicDataTree[] = [], + public readonly getLeafPreimageHintsNullifierTree: AvmGetLeafPreimageHintNullifierTree[] = [], ) {} static empty() { @@ -241,6 +244,7 @@ export class AvmExecutionHints { getSiblingPathHints: AvmGetSiblingPathHint.schema.array(), getPreviousValueIndexHints: AvmGetPreviousValueIndexHint.schema.array(), getLeafPreimageHintsPublicDataTree: AvmGetLeafPreimageHintPublicDataTree.schema.array(), + getLeafPreimageHintsNullifierTree: AvmGetLeafPreimageHintNullifierTree.schema.array(), }) .transform( ({ @@ -251,6 +255,7 @@ export class AvmExecutionHints { getSiblingPathHints, getPreviousValueIndexHints, getLeafPreimageHintsPublicDataTree, + getLeafPreimageHintsNullifierTree, }) => new AvmExecutionHints( enqueuedCalls, @@ -260,6 +265,7 @@ export class AvmExecutionHints { getSiblingPathHints, getPreviousValueIndexHints, getLeafPreimageHintsPublicDataTree, + getLeafPreimageHintsNullifierTree, ), ); } diff --git a/yarn-project/stdlib/src/tests/factories.ts b/yarn-project/stdlib/src/tests/factories.ts index 2bbdaf151b7a..331bfd41ba1e 100644 --- a/yarn-project/stdlib/src/tests/factories.ts +++ b/yarn-project/stdlib/src/tests/factories.ts @@ -62,6 +62,7 @@ import { AvmContractInstanceHint, AvmEnqueuedCallHint, AvmExecutionHints, + AvmGetLeafPreimageHintNullifierTree, AvmGetLeafPreimageHintPublicDataTree, AvmGetPreviousValueIndexHint, AvmGetSiblingPathHint, @@ -142,7 +143,7 @@ import { PublicTubeData } from '../rollup/public_tube_data.js'; import { RootRollupInputs, RootRollupPublicInputs } from '../rollup/root_rollup.js'; import { PrivateBaseStateDiffHints } from '../rollup/state_diff_hints.js'; import { AppendOnlyTreeSnapshot } from '../trees/append_only_tree_snapshot.js'; -import { NullifierLeafPreimage } from '../trees/nullifier_leaf.js'; +import { NullifierLeaf, NullifierLeafPreimage } from '../trees/nullifier_leaf.js'; import { PublicDataTreeLeaf, PublicDataTreeLeafPreimage } from '../trees/public_data_leaf.js'; import { BlockHeader } from '../tx/block_header.js'; import { CallContext } from '../tx/call_context.js'; @@ -988,6 +989,15 @@ export function makePublicDataTreeLeaf(seed = 0): PublicDataTreeLeaf { return new PublicDataTreeLeaf(new Fr(seed), new Fr(seed + 1)); } +/** + * Makes arbitrary nullifier leaf. + * @param seed - The seed to use for generating the nullifier leaf. + * @returns A nullifier leaf. + */ +export function makeNullifierLeaf(seed = 0): NullifierLeaf { + return new NullifierLeaf(new Fr(seed)); +} + /** * Makes arbitrary public data tree leaf preimages. * @param seed - The seed to use for generating the public data tree leaf preimage. @@ -1295,6 +1305,18 @@ export function makeAvmGetLeafPreimageHintPublicDataTree(seed = 0): AvmGetLeafPr ); } +export function makeAvmGetLeafPreimageHintNullifierTree(seed = 0): AvmGetLeafPreimageHintNullifierTree { + // We want a possibly large index, but non-random. + const index = BigInt(`0x${sha256(Buffer.from(seed.toString())).toString('hex')}`) % (1n << 64n); + return new AvmGetLeafPreimageHintNullifierTree( + makeAppendOnlyTreeSnapshot(seed), + /*index=*/ index, + /*leaf=*/ makeNullifierLeaf(seed + 3), + /*nextIndex=*/ index + 1n, + /*nextValue*/ new Fr(seed + 0x500), + ); +} + /** * Makes arbitrary AvmContractInstanceHint. * @param seed - The seed to use for generating the state reference. @@ -1361,11 +1383,12 @@ export async function makeAvmExecutionHints( bytecodeCommitments: await makeArrayAsync(baseLength + 5, makeAvmBytecodeCommitmentHint, seed + 0x4900), getSiblingPathHints: makeArray(baseLength + 5, makeAvmGetSiblingPathHint, seed + 0x4b00), getPreviousValueIndexHints: makeArray(baseLength + 5, makeAvmGetPreviousValueIndexHint, seed + 0x4d00), - getLeafPreimageHintPublicDataTrees: makeArray( + getLeafPreimageHintPublicDataTree: makeArray( baseLength + 5, makeAvmGetLeafPreimageHintPublicDataTree, seed + 0x4f00, ), + getLeafPreimageHintNullifierTree: makeArray(baseLength + 5, makeAvmGetLeafPreimageHintNullifierTree, seed + 0x5100), ...overrides, }; @@ -1376,7 +1399,8 @@ export async function makeAvmExecutionHints( fields.bytecodeCommitments, fields.getSiblingPathHints, fields.getPreviousValueIndexHints, - fields.getLeafPreimageHintPublicDataTrees, + fields.getLeafPreimageHintPublicDataTree, + fields.getLeafPreimageHintNullifierTree, ); } diff --git a/yarn-project/stdlib/src/trees/nullifier_leaf.ts b/yarn-project/stdlib/src/trees/nullifier_leaf.ts index 5f64514c31c6..3fd69d897157 100644 --- a/yarn-project/stdlib/src/trees/nullifier_leaf.ts +++ b/yarn-project/stdlib/src/trees/nullifier_leaf.ts @@ -130,4 +130,8 @@ export class NullifierLeaf implements IndexedTreeLeaf { static fromBuffer(buf: Buffer): NullifierLeaf { return new NullifierLeaf(Fr.fromBuffer(buf)); } + + static get schema() { + return z.object({ nullifier: schemas.Fr }).transform(({ nullifier }) => new NullifierLeaf(nullifier)); + } } From d57b6b7f60e4c84dcdca4968706489d22c9c7208 Mon Sep 17 00:00:00 2001 From: fcarreiro Date: Thu, 27 Mar 2025 11:19:11 +0000 Subject: [PATCH 2/3] high level changes --- .../simulator/src/public/public_db_sources.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/yarn-project/simulator/src/public/public_db_sources.ts b/yarn-project/simulator/src/public/public_db_sources.ts index a7623b8bd0da..99e557c6cc20 100644 --- a/yarn-project/simulator/src/public/public_db_sources.ts +++ b/yarn-project/simulator/src/public/public_db_sources.ts @@ -437,6 +437,9 @@ export class PublicTreesDB extends ForwardMerkleTree implements PublicStateDBInt public async getL1ToL2LeafValue(leafIndex: bigint): Promise { const timer = new Timer(); const leafValue = await this.getLeafValue(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, leafIndex); + // TODO(fcarreiro): We need this for the hints. Might move it to the hinting layer. + await this.getSiblingPath(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, leafIndex); + this.logger.debug(`[DB] Fetched L1 to L2 message leaf value`, { eventName: 'public-db-access', duration: timer.ms(), @@ -448,6 +451,9 @@ export class PublicTreesDB extends ForwardMerkleTree implements PublicStateDBInt public async getNoteHash(leafIndex: bigint): Promise { const timer = new Timer(); const leafValue = await this.getLeafValue(MerkleTreeId.NOTE_HASH_TREE, leafIndex); + // TODO(fcarreiro): We need this for the hints. Might move it to the hinting layer. + await this.getSiblingPath(MerkleTreeId.NOTE_HASH_TREE, leafIndex); + this.logger.debug(`[DB] Fetched note hash leaf value`, { eventName: 'public-db-access', duration: timer.ms(), @@ -458,7 +464,13 @@ export class PublicTreesDB extends ForwardMerkleTree implements PublicStateDBInt public async getNullifierIndex(nullifier: Fr): Promise { const timer = new Timer(); - const index = (await this.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, [nullifier.toBuffer()]))[0]; + const maybeLowLeaf = await this.getPreviousValueIndex(MerkleTreeId.NULLIFIER_TREE, nullifier.toBigInt()); + const index = maybeLowLeaf?.alreadyPresent ? maybeLowLeaf.index : undefined; + if (index !== undefined) { + // TODO(fcarreiro): We need this for the hints. Might move it to the hinting layer. + await this.getSiblingPath(MerkleTreeId.NULLIFIER_TREE, index); + } + this.logger.debug(`[DB] Fetched nullifier index`, { eventName: 'public-db-access', duration: timer.ms(), From b3114cb4666fd08e284ad77a583dfccba6e2bcc8 Mon Sep 17 00:00:00 2001 From: fcarreiro Date: Thu, 27 Mar 2025 11:57:56 +0000 Subject: [PATCH 3/3] get leaf value --- .../barretenberg/vm2/common/avm_inputs.hpp | 17 +++++++++- .../vm2/common/avm_inputs.testdata.bin | Bin 115253 -> 118448 bytes .../vm2/simulation/lib/db_interfaces.hpp | 2 ++ .../vm2/simulation/lib/raw_data_dbs.cpp | 17 +++++++++- .../vm2/simulation/lib/raw_data_dbs.hpp | 4 +++ .../vm2/simulation/testing/mock_dbs.hpp | 4 +++ .../src/public/hinting_db_sources.ts | 24 ++++++++++++++- .../simulator/src/public/public_db_sources.ts | 13 +++++--- yarn-project/stdlib/src/avm/avm.ts | 29 ++++++++++++++++++ yarn-project/stdlib/src/tests/factories.ts | 14 +++++++++ .../stdlib/src/trees/nullifier_leaf.ts | 12 ++++---- .../src/native/merkle_trees_facade.ts | 2 +- 12 files changed, 123 insertions(+), 15 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/vm2/common/avm_inputs.hpp b/barretenberg/cpp/src/barretenberg/vm2/common/avm_inputs.hpp index a391c7383e3b..8904b52e53ac 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/common/avm_inputs.hpp +++ b/barretenberg/cpp/src/barretenberg/vm2/common/avm_inputs.hpp @@ -147,6 +147,19 @@ template struct GetLeafPreimageHint { MSGPACK_FIELDS(hintKey, index, leaf, nextIndex, nextValue); }; +struct GetLeafValueHint { + AppendOnlyTreeSnapshot hintKey; + // params + world_state::MerkleTreeId treeId; + uint64_t index; + // return + FF value; + + bool operator==(const GetLeafValueHint& other) const = default; + + MSGPACK_FIELDS(hintKey, treeId, index, value); +}; + //////////////////////////////////////////////////////////////////////////// // Hints (other) //////////////////////////////////////////////////////////////////////////// @@ -175,6 +188,7 @@ struct ExecutionHints { std::vector getPreviousValueIndexHints; std::vector> getLeafPreimageHintsPublicDataTree; std::vector> getLeafPreimageHintsNullifierTree; + std::vector getLeafValueHints; bool operator==(const ExecutionHints& other) const = default; @@ -185,7 +199,8 @@ struct ExecutionHints { getSiblingPathHints, getPreviousValueIndexHints, getLeafPreimageHintsPublicDataTree, - getLeafPreimageHintsNullifierTree); + getLeafPreimageHintsNullifierTree, + getLeafValueHints); }; //////////////////////////////////////////////////////////////////////////// diff --git a/barretenberg/cpp/src/barretenberg/vm2/common/avm_inputs.testdata.bin b/barretenberg/cpp/src/barretenberg/vm2/common/avm_inputs.testdata.bin index 7791f37c80f8c8914925c869bbeefc972ee864bc..3bae0a36cc560d9d0dcee8ce1069889286fc5dd6 100644 GIT binary patch delta 1364 zcmYk)TS${(7zgm)DcT~-(S@5Gv^+H^BsKNh_NmjUn}TQDaBo+*w@J;^ ztGi)WV* zTronBVui42w-Eed7}z7cG{p(wP`nUg5`^$m3@;Lem)l7~IJH*@X?kXk7^-&Gl{oEH z74|drPWQ=cMy4q5m`N+LCrab^Rn;bnq2GZJRj%Lgs?=FlHp$Zn#vUS z(v6@!2pd9nvau`K^0HxW3^sG!s+4r)J=5xxbl_0n%GKpMN%yM*Si<14mEhnE*bP{s z-5OOq-Wi($YoNiIG&mEOVer|q5BG-qu66?(2}^_}VAPw`Z&RC|ofs_#HW4-uHUOia zFAzF2{${YW0azw%By0pmKi?0pci(hm{9WKI!Y0BdU|#TgWpDMPMcZNEY{D{O8JHK8 zHN7l5?@}Ug&N^N{$yD&mWx?|E&HK|&J6eWTV9l`R-$9!V%g-sW3JXX7ejH5gk!J6LE33ot)kPxtiS_Q{Kpz=eeK3FiazpYzyY zSc?B5v` zR|9VwlM`H+TTIwS*apn&J-t7rF=dR$0GALhB3uN_>**bm+&9|)2e@>kUeBK2`3Dw* BH%R~h delta 440 zcmdlmmwjss`-WOhMvmq>&h2%ajMI2IR_2xFAB1=@!)fB3si`c27i*Re8i`>v)blm>Il<~j}0M!GnY5)KL diff --git a/barretenberg/cpp/src/barretenberg/vm2/simulation/lib/db_interfaces.hpp b/barretenberg/cpp/src/barretenberg/vm2/simulation/lib/db_interfaces.hpp index 15f165d26d93..b264f7d6b9c0 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/simulation/lib/db_interfaces.hpp +++ b/barretenberg/cpp/src/barretenberg/vm2/simulation/lib/db_interfaces.hpp @@ -31,6 +31,8 @@ class LowLevelMerkleDBInterface { crypto::merkle_tree::index_t leaf_index) const = 0; virtual crypto::merkle_tree::GetLowIndexedLeafResponse get_low_indexed_leaf(world_state::MerkleTreeId tree_id, const FF& value) const = 0; + // Returns the value if it exists, 0 otherwise. + virtual FF get_leaf_value(world_state::MerkleTreeId tree_id, crypto::merkle_tree::index_t leaf_index) const = 0; // We don't template the preimage methods because templated methods cannot be virtual. virtual crypto::merkle_tree::IndexedLeaf get_leaf_preimage_public_data_tree(crypto::merkle_tree::index_t leaf_index) const = 0; diff --git a/barretenberg/cpp/src/barretenberg/vm2/simulation/lib/raw_data_dbs.cpp b/barretenberg/cpp/src/barretenberg/vm2/simulation/lib/raw_data_dbs.cpp index 36f5a83e804c..185a9aefc258 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/simulation/lib/raw_data_dbs.cpp +++ b/barretenberg/cpp/src/barretenberg/vm2/simulation/lib/raw_data_dbs.cpp @@ -104,7 +104,9 @@ HintedRawMerkleDB::HintedRawMerkleDB(const ExecutionHints& hints, const TreeSnap "\n * get_leaf_preimage hints_public_data_tree: ", hints.getLeafPreimageHintsPublicDataTree.size(), "\n * get_leaf_preimage hints_nullifier_tree: ", - hints.getLeafPreimageHintsNullifierTree.size()); + hints.getLeafPreimageHintsNullifierTree.size(), + "\n * get_leaf_value_hints: ", + hints.getLeafValueHints.size()); debug("Initializing HintedRawMerkleDB with snapshots...", "\n * nullifierTree: ", tree_roots.nullifierTree.root, @@ -161,6 +163,11 @@ HintedRawMerkleDB::HintedRawMerkleDB(const ExecutionHints& hints, const TreeSnap /*nextVal=*/get_leaf_preimage_hint.nextValue, }; } + + for (const auto& get_leaf_value_hint : hints.getLeafValueHints) { + GetLeafValueKey key = { get_leaf_value_hint.hintKey, get_leaf_value_hint.treeId, get_leaf_value_hint.index }; + get_leaf_value_hints[key] = get_leaf_value_hint.value; + } } const AppendOnlyTreeSnapshot& HintedRawMerkleDB::get_tree_info(world_state::MerkleTreeId tree_id) const @@ -219,6 +226,14 @@ crypto::merkle_tree::GetLowIndexedLeafResponse HintedRawMerkleDB::get_low_indexe return it->second; } +FF HintedRawMerkleDB::get_leaf_value(world_state::MerkleTreeId tree_id, crypto::merkle_tree::index_t leaf_index) const +{ + auto tree_info = get_tree_info(tree_id); + GetLeafValueKey key = { tree_info, tree_id, leaf_index }; + auto it = get_leaf_value_hints.find(key); + return it == get_leaf_value_hints.end() ? 0 : it->second; +} + crypto::merkle_tree::IndexedLeaf HintedRawMerkleDB:: get_leaf_preimage_public_data_tree(crypto::merkle_tree::index_t leaf_index) const { diff --git a/barretenberg/cpp/src/barretenberg/vm2/simulation/lib/raw_data_dbs.hpp b/barretenberg/cpp/src/barretenberg/vm2/simulation/lib/raw_data_dbs.hpp index b67e63fba55f..59d7c4c9ed34 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/simulation/lib/raw_data_dbs.hpp +++ b/barretenberg/cpp/src/barretenberg/vm2/simulation/lib/raw_data_dbs.hpp @@ -41,6 +41,7 @@ class HintedRawMerkleDB final : public LowLevelMerkleDBInterface { crypto::merkle_tree::index_t leaf_index) const override; crypto::merkle_tree::GetLowIndexedLeafResponse get_low_indexed_leaf(world_state::MerkleTreeId tree_id, const FF& value) const override; + FF get_leaf_value(world_state::MerkleTreeId tree_id, crypto::merkle_tree::index_t leaf_index) const override; crypto::merkle_tree::IndexedLeaf get_leaf_preimage_public_data_tree( crypto::merkle_tree::index_t leaf_index) const override; crypto::merkle_tree::IndexedLeaf get_leaf_preimage_nullifier_tree( @@ -60,6 +61,9 @@ class HintedRawMerkleDB final : public LowLevelMerkleDBInterface { get_leaf_preimage_hints_public_data_tree; unordered_flat_map> get_leaf_preimage_hints_nullifier_tree; + using GetLeafValueKey = + utils::HashableTuple; + unordered_flat_map get_leaf_value_hints; const AppendOnlyTreeSnapshot& get_tree_info(world_state::MerkleTreeId tree_id) const; }; diff --git a/barretenberg/cpp/src/barretenberg/vm2/simulation/testing/mock_dbs.hpp b/barretenberg/cpp/src/barretenberg/vm2/simulation/testing/mock_dbs.hpp index f45d338f415e..b1ea741ca0d2 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/simulation/testing/mock_dbs.hpp +++ b/barretenberg/cpp/src/barretenberg/vm2/simulation/testing/mock_dbs.hpp @@ -32,6 +32,10 @@ class MockLowLevelMerkleDB : public LowLevelMerkleDBInterface { get_low_indexed_leaf, (world_state::MerkleTreeId tree_id, const FF& value), (const, override)); + MOCK_METHOD(FF, + get_leaf_value, + (world_state::MerkleTreeId tree_id, crypto::merkle_tree::index_t leaf_index), + (const, override)); MOCK_METHOD(crypto::merkle_tree::IndexedLeaf, get_leaf_preimage_public_data_tree, (crypto::merkle_tree::index_t leaf_index), diff --git a/yarn-project/simulator/src/public/hinting_db_sources.ts b/yarn-project/simulator/src/public/hinting_db_sources.ts index e889032fdf99..a25abd612f6f 100644 --- a/yarn-project/simulator/src/public/hinting_db_sources.ts +++ b/yarn-project/simulator/src/public/hinting_db_sources.ts @@ -9,6 +9,7 @@ import { type AvmExecutionHints, AvmGetLeafPreimageHintNullifierTree, AvmGetLeafPreimageHintPublicDataTree, + AvmGetLeafValueHint, AvmGetPreviousValueIndexHint, AvmGetSiblingPathHint, } from '@aztec/stdlib/avm'; @@ -18,12 +19,15 @@ import { AppendOnlyTreeSnapshot, type IndexedTreeId, MerkleTreeId, + type MerkleTreeLeafType, NullifierLeaf, PublicDataTreeLeaf, type SequentialInsertionResult, getTreeName, } from '@aztec/stdlib/trees'; +import { strict as assert } from 'assert'; + import type { PublicContractsDBInterface } from '../common/db_interfaces.js'; import { PublicTreesDB } from './public_db_sources.js'; @@ -161,7 +165,8 @@ export class HintingPublicTreesDB extends PublicTreesDB { ); break; default: - HintingPublicTreesDB.log.debug(`getLeafPreimage not hinted for tree ${getTreeName(treeId)} yet!`); + // Use getLeafValue for the other trees. + throw new Error('getLeafPreimage only supported for PublicDataTree and NullifierTree!'); break; } } @@ -169,6 +174,23 @@ export class HintingPublicTreesDB extends PublicTreesDB { return preimage; } + public override async getLeafValue( + treeId: ID, + index: bigint, + ): Promise | undefined> { + // Use getLeafPreimage for PublicDataTree and NullifierTree. + assert(treeId == MerkleTreeId.NOTE_HASH_TREE || treeId == MerkleTreeId.L1_TO_L2_MESSAGE_TREE); + + const value = await super.getLeafValue(treeId, index); + if (value) { + const key = await this.getHintKey(treeId); + // We can cast to Fr because we know the type of the tree. + this.hints.getLeafValueHints.push(new AvmGetLeafValueHint(key, treeId, index, value as Fr)); + } + + return value; + } + // State modification. public override async sequentialInsert( treeId: ID, diff --git a/yarn-project/simulator/src/public/public_db_sources.ts b/yarn-project/simulator/src/public/public_db_sources.ts index 99e557c6cc20..4c6ce5eb743b 100644 --- a/yarn-project/simulator/src/public/public_db_sources.ts +++ b/yarn-project/simulator/src/public/public_db_sources.ts @@ -464,12 +464,15 @@ export class PublicTreesDB extends ForwardMerkleTree implements PublicStateDBInt public async getNullifierIndex(nullifier: Fr): Promise { const timer = new Timer(); - const maybeLowLeaf = await this.getPreviousValueIndex(MerkleTreeId.NULLIFIER_TREE, nullifier.toBigInt()); - const index = maybeLowLeaf?.alreadyPresent ? maybeLowLeaf.index : undefined; - if (index !== undefined) { - // TODO(fcarreiro): We need this for the hints. Might move it to the hinting layer. - await this.getSiblingPath(MerkleTreeId.NULLIFIER_TREE, index); + const lowLeafResult = await this.getPreviousValueIndex(MerkleTreeId.NULLIFIER_TREE, nullifier.toBigInt()); + if (!lowLeafResult) { + throw new Error('Low leaf not found'); } + // TODO(fcarreiro): We need this for the hints. Might move it to the hinting layer. + await this.getSiblingPath(MerkleTreeId.NULLIFIER_TREE, lowLeafResult.index); + // TODO(fcarreiro): We need this for the hints. Might move it to the hinting layer. + await this.getLeafPreimage(MerkleTreeId.NULLIFIER_TREE, lowLeafResult.index); + const index = lowLeafResult.alreadyPresent ? lowLeafResult.index : undefined; this.logger.debug(`[DB] Fetched nullifier index`, { eventName: 'public-db-access', diff --git a/yarn-project/stdlib/src/avm/avm.ts b/yarn-project/stdlib/src/avm/avm.ts index 34f6f1556b42..8f07cf83fb1b 100644 --- a/yarn-project/stdlib/src/avm/avm.ts +++ b/yarn-project/stdlib/src/avm/avm.ts @@ -187,9 +187,34 @@ function AvmGetLeafPreimageHintFactory(klass: { }; } +// Note: only supported for PUBLIC_DATA_TREE and NULLIFIER_TREE. export class AvmGetLeafPreimageHintPublicDataTree extends AvmGetLeafPreimageHintFactory(PublicDataTreeLeaf) {} export class AvmGetLeafPreimageHintNullifierTree extends AvmGetLeafPreimageHintFactory(NullifierLeaf) {} +// Hint for MerkleTreeDB.getLeafValue. +// Note: only supported for NOTE_HASH_TREE and L1_TO_L2_MESSAGE_TREE. +export class AvmGetLeafValueHint { + constructor( + public readonly hintKey: AppendOnlyTreeSnapshot, + // params + public readonly treeId: MerkleTreeId, + public readonly index: bigint, + // return + public readonly value: Fr, + ) {} + + static get schema() { + return z + .object({ + hintKey: AppendOnlyTreeSnapshot.schema, + treeId: z.number().int().nonnegative(), + index: schemas.BigInt, + value: schemas.Fr, + }) + .transform(({ hintKey, treeId, index, value }) => new AvmGetLeafValueHint(hintKey, treeId, index, value)); + } +} + //////////////////////////////////////////////////////////////////////////// // Hints (other) //////////////////////////////////////////////////////////////////////////// @@ -228,6 +253,7 @@ export class AvmExecutionHints { public readonly getPreviousValueIndexHints: AvmGetPreviousValueIndexHint[] = [], public readonly getLeafPreimageHintsPublicDataTree: AvmGetLeafPreimageHintPublicDataTree[] = [], public readonly getLeafPreimageHintsNullifierTree: AvmGetLeafPreimageHintNullifierTree[] = [], + public readonly getLeafValueHints: AvmGetLeafValueHint[] = [], ) {} static empty() { @@ -245,6 +271,7 @@ export class AvmExecutionHints { getPreviousValueIndexHints: AvmGetPreviousValueIndexHint.schema.array(), getLeafPreimageHintsPublicDataTree: AvmGetLeafPreimageHintPublicDataTree.schema.array(), getLeafPreimageHintsNullifierTree: AvmGetLeafPreimageHintNullifierTree.schema.array(), + getLeafValueHints: AvmGetLeafValueHint.schema.array(), }) .transform( ({ @@ -256,6 +283,7 @@ export class AvmExecutionHints { getPreviousValueIndexHints, getLeafPreimageHintsPublicDataTree, getLeafPreimageHintsNullifierTree, + getLeafValueHints, }) => new AvmExecutionHints( enqueuedCalls, @@ -266,6 +294,7 @@ export class AvmExecutionHints { getPreviousValueIndexHints, getLeafPreimageHintsPublicDataTree, getLeafPreimageHintsNullifierTree, + getLeafValueHints, ), ); } diff --git a/yarn-project/stdlib/src/tests/factories.ts b/yarn-project/stdlib/src/tests/factories.ts index 331bfd41ba1e..ec7aef6a33ea 100644 --- a/yarn-project/stdlib/src/tests/factories.ts +++ b/yarn-project/stdlib/src/tests/factories.ts @@ -64,6 +64,7 @@ import { AvmExecutionHints, AvmGetLeafPreimageHintNullifierTree, AvmGetLeafPreimageHintPublicDataTree, + AvmGetLeafValueHint, AvmGetPreviousValueIndexHint, AvmGetSiblingPathHint, RevertCode, @@ -1317,6 +1318,17 @@ export function makeAvmGetLeafPreimageHintNullifierTree(seed = 0): AvmGetLeafPre ); } +export function makeAvmGetLeafValueHint(seed = 0): AvmGetLeafValueHint { + // We want a possibly large index, but non-random. + const index = BigInt(`0x${sha256(Buffer.from(seed.toString())).toString('hex')}`) % (1n << 64n); + return new AvmGetLeafValueHint( + makeAppendOnlyTreeSnapshot(seed), + /*treeId=*/ (seed + 1) % 5, + /*index=*/ index, + /*value=*/ new Fr(seed + 3), + ); +} + /** * Makes arbitrary AvmContractInstanceHint. * @param seed - The seed to use for generating the state reference. @@ -1389,6 +1401,7 @@ export async function makeAvmExecutionHints( seed + 0x4f00, ), getLeafPreimageHintNullifierTree: makeArray(baseLength + 5, makeAvmGetLeafPreimageHintNullifierTree, seed + 0x5100), + getLeafValueHints: makeArray(baseLength + 5, makeAvmGetLeafValueHint, seed + 0x5300), ...overrides, }; @@ -1401,6 +1414,7 @@ export async function makeAvmExecutionHints( fields.getPreviousValueIndexHints, fields.getLeafPreimageHintPublicDataTree, fields.getLeafPreimageHintNullifierTree, + fields.getLeafValueHints, ); } diff --git a/yarn-project/stdlib/src/trees/nullifier_leaf.ts b/yarn-project/stdlib/src/trees/nullifier_leaf.ts index 3fd69d897157..eb93d2c69bd0 100644 --- a/yarn-project/stdlib/src/trees/nullifier_leaf.ts +++ b/yarn-project/stdlib/src/trees/nullifier_leaf.ts @@ -88,7 +88,7 @@ export class NullifierLeafPreimage implements IndexedTreeLeafPreimage { } static fromLeaf(leaf: NullifierLeaf, nextKey: bigint, nextIndex: bigint): NullifierLeafPreimage { - return new NullifierLeafPreimage(leaf.nullifier, new Fr(nextKey), nextIndex); + return new NullifierLeafPreimage(leaf.value, new Fr(nextKey), nextIndex); } static clone(preimage: NullifierLeafPreimage): NullifierLeafPreimage { @@ -104,19 +104,19 @@ export class NullifierLeaf implements IndexedTreeLeaf { /** * Nullifier value. */ - public nullifier: Fr, + public value: Fr, ) {} getKey(): bigint { - return this.nullifier.toBigInt(); + return this.value.toBigInt(); } toBuffer(): Buffer { - return this.nullifier.toBuffer(); + return this.value.toBuffer(); } isEmpty(): boolean { - return this.nullifier.isZero(); + return this.value.isZero(); } updateTo(_another: NullifierLeaf): NullifierLeaf { @@ -132,6 +132,6 @@ export class NullifierLeaf implements IndexedTreeLeaf { } static get schema() { - return z.object({ nullifier: schemas.Fr }).transform(({ nullifier }) => new NullifierLeaf(nullifier)); + return z.object({ value: schemas.Fr }).transform(({ value }) => new NullifierLeaf(value)); } } diff --git a/yarn-project/world-state/src/native/merkle_trees_facade.ts b/yarn-project/world-state/src/native/merkle_trees_facade.ts index 1fd7db9c7ccd..0e49e2feeb6b 100644 --- a/yarn-project/world-state/src/native/merkle_trees_facade.ts +++ b/yarn-project/world-state/src/native/merkle_trees_facade.ts @@ -295,7 +295,7 @@ export function serializeLeaf(leaf: Fr | NullifierLeaf | PublicDataTreeLeaf): Se if (leaf instanceof Fr) { return leaf.toBuffer(); } else if (leaf instanceof NullifierLeaf) { - return { value: leaf.nullifier.toBuffer() }; + return { value: leaf.value.toBuffer() }; } else { return { value: leaf.value.toBuffer(), slot: leaf.slot.toBuffer() }; }