From a859780c55c9c317b608b9c64c80e3b61fcbe494 Mon Sep 17 00:00:00 2001 From: fcarreiro Date: Fri, 4 Apr 2025 11:45:58 +0000 Subject: [PATCH 1/2] feat(avm): checkpointing hints --- .../vm2/common/avm_inputs.testdata.bin | Bin 255361 -> 277447 bytes .../src/public/hinting_db_sources.ts | 105 ++++++++++++--- yarn-project/stdlib/src/avm/avm.ts | 122 ++++++++++++++++++ yarn-project/stdlib/src/tests/factories.ts | 33 +++++ 4 files changed, 242 insertions(+), 18 deletions(-) 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 dd78ef3625fcbbfd99641c6cee4c1bef50e8869e..e7cbb01d16620c4c23f96563738c2b9f0d0529de 100644 GIT binary patch delta 7838 zcmaLbyNg^^90&01Om(iH1`7@376BWp5R!e(&OT>m=drWz^b$7{44SO0i(=t|2wF>u z6_y`AQkgC<0Ezta5TAfJn6hYIr!kC!@Kv72mbZ(9~a*I^Fs6S;fJ^B z55wEPpWXP*FFknp+1pporXSy*e9HeiV`io)Gov%JW@d&mvpO?pW@affr!(_rW{xuR zI@2;U^OR}n%z~L|QD#AB+Gb{fGHsn%G&60=Eb7dXnOUUFlFlrfnI+0B>&%LoS*Fa2 z&a9f570RsY%$k{5rOcYnbj-{eWjZ?3H8UN`bakd@X1bK=>CC#B=}~4~XZmJloicr$ z8JL+qWd=GkG&2Lr40UE?W`>j*>CA?i8O6-tY(riGf*SKqUsc#K0^upb`UJVqlgSP>F#qF)&LEsKh{* z7?>pnRAQh@49pS(DlyO{24;x?l^Ey}1GB_{N(^*~fmvceB?h|0z$`JK5(8agV3rtA ziGeOLFiQ-m#6XuAm?Z{OVxUV5%n}1CG0-IjW{Cln80ZoMv&4W(40MTsSz=iYMVJfj4b&283_wP;a93D+}PS3WR(^GhQ z?W@M63}>HL%Uh>U9yM?H<)_0bzdZLWF2DPzdEGBxyizRBpI6J{Q}?JjSG@bjsY?!9 z&u`s1o*bn||MhFbuP!_*SF0yCu>IiScM7L3eZ!eHj(U*51?D6Wa}r283s8nj(iud;8AQ@qiG;HdIb5qd z8`vp2>oWSy)o`ZaCr3T}V4FD!B%J{yodqbvW$6qe>8wP;8ANqw5IO8{XSZ_tfxvL4 zfh|Wpu;HR~29R_HkaQNH3|Ex15(#Gz)tw~b&Qj#C%bmT->4zA@nTE_9^^l26(iuR~ z89>VEK^d+}XAsq$B;w8};?7d!uvd5bFWYXb_@g>a>P`~Xog@;@Lga8*cQ$ZTeQ*6i z!f>X8UXJ>phaKUJAmNN4<_w?=*O`+P`}IXDM>H!JXsEIX0c?K%JvLP-Bld3B;TPV$J}{a3GyQ zRCkhyJEMp@OOeAdcWzeBP1Bh!0dmxr0JzSa1Y%ACNoN7daLAk_;?5}I&M4x}Qsi)x zJGUz5mg!6v7dh&S3+yu|fuu8lm@|Mf90_L>ac2~9XB2T~DRQ{Qo!gaj+jOSOmK^nE z3l5|+fS8j&!dZec+z`$v;?5}I&M4x}Qsi)(J9jGQj_FJnL^H&z(05X9I7Z|2%H~^zDBK0#DTd delta 34 ocmX^9U7+za|AtylM(*Z1&h|P^Mj&R|UdPGo?7W?QG4p{L0N>CI7ytkO diff --git a/yarn-project/simulator/src/public/hinting_db_sources.ts b/yarn-project/simulator/src/public/hinting_db_sources.ts index 8274fa6f6ea9..c4ad67f71db0 100644 --- a/yarn-project/simulator/src/public/hinting_db_sources.ts +++ b/yarn-project/simulator/src/public/hinting_db_sources.ts @@ -1,9 +1,13 @@ +import { sha256Trunc } from '@aztec/foundation/crypto'; import { Fr } from '@aztec/foundation/fields'; import { type Logger, createLogger } from '@aztec/foundation/log'; import type { IndexedTreeLeafPreimage, SiblingPath } from '@aztec/foundation/trees'; import type { FunctionSelector } from '@aztec/stdlib/abi'; import { AvmBytecodeCommitmentHint, + AvmCheckpointActionCommitCheckpointHint, + AvmCheckpointActionCreateCheckpointHint, + AvmCheckpointActionRevertCheckpointHint, AvmContractClassHint, AvmContractInstanceHint, type AvmExecutionHints, @@ -28,6 +32,7 @@ import { PublicDataTreeLeafPreimage, type SequentialInsertionResult, getTreeName, + merkleTreeIds, } from '@aztec/stdlib/trees'; import { strict as assert } from 'assert'; @@ -94,11 +99,18 @@ export class HintingPublicContractsDB implements PublicContractsDBInterface { } } +class CheckpointKey { + constructor(public readonly id: number, public readonly hash: Fr) {} +} + /** * A public trees database that forwards requests and collects AVM hints. */ export class HintingPublicTreesDB extends PublicTreesDB { private static readonly log: Logger = createLogger('HintingPublicTreesDB'); + // We use 0 as the initial state even if we never checkpointed. + private nextCheckpointId: number = 1; + private checkpointStack: number[] = [0]; // stack only for debugging purposes. constructor(db: PublicTreesDB, private hints: AvmExecutionHints) { super(db); @@ -200,11 +212,8 @@ export class HintingPublicTreesDB extends PublicTreesDB { const result = await super.sequentialInsert(treeId, leaves); const afterState = await this.getHintKey(treeId); - HintingPublicTreesDB.log.debug( - `Evolved tree state (${getTreeName(treeId)}): ${beforeState.root}, ${beforeState.nextAvailableLeafIndex} -> ${ - afterState.root - }, ${afterState.nextAvailableLeafIndex}.`, - ); + HintingPublicTreesDB.log.debug('[sequentialInsert] Evolved tree state.'); + HintingPublicTreesDB.logTreeChange(beforeState, afterState, treeId); switch (treeId) { case MerkleTreeId.PUBLIC_DATA_TREE: @@ -255,12 +264,41 @@ export class HintingPublicTreesDB extends PublicTreesDB { return result; } + public override async createCheckpoint(): Promise { + const hintKey = await this.getCheckpointHintKey(); + + await super.createCheckpoint(); + this.checkpointStack.push(this.nextCheckpointId++); + const newCheckpointId = this.getCurrentCheckpointId(); + + this.hints.createCheckpointHints.push( + new AvmCheckpointActionCreateCheckpointHint(hintKey.id, hintKey.hash, newCheckpointId), + ); + + HintingPublicTreesDB.log.debug( + `[createCheckpoint] Checkpoint evolved ${hintKey.id} -> ${newCheckpointId} at checkpoint key ${hintKey.hash}.`, + ); + } + + public override async commitCheckpoint(): Promise { + const hintKey = await this.getCheckpointHintKey(); + + await super.commitCheckpoint(); + this.checkpointStack.pop(); + const newCheckpointId = this.getCurrentCheckpointId(); + + this.hints.commitCheckpointHints.push( + new AvmCheckpointActionCommitCheckpointHint(hintKey.id, hintKey.hash, newCheckpointId), + ); + + HintingPublicTreesDB.log.debug( + `[commitCheckpoint] Checkpoint evolved ${hintKey.id} -> ${newCheckpointId} at checkpoint key ${hintKey.hash}.`, + ); + } + public override async revertCheckpoint(): Promise { - HintingPublicTreesDB.log.debug('revertCheckpoint not hinted yet!'); - // TODO(fcarreiro): we probably want to hint on StateReference hash. - // WARNING: is this enough? we might actually need the number of the checkpoint or similar... - // We will need to keep a stack of checkpoints on the C++ side. - const beforeState = { + const hintKey = await this.getCheckpointHintKey(); + const beforeState: Record = { [MerkleTreeId.PUBLIC_DATA_TREE]: await this.getHintKey(MerkleTreeId.PUBLIC_DATA_TREE), [MerkleTreeId.NULLIFIER_TREE]: await this.getHintKey(MerkleTreeId.NULLIFIER_TREE), [MerkleTreeId.NOTE_HASH_TREE]: await this.getHintKey(MerkleTreeId.NOTE_HASH_TREE), @@ -269,8 +307,10 @@ export class HintingPublicTreesDB extends PublicTreesDB { }; await super.revertCheckpoint(); + this.checkpointStack.pop(); + const newCheckpointId = this.getCurrentCheckpointId(); - const afterState = { + const afterState: Record = { [MerkleTreeId.PUBLIC_DATA_TREE]: await this.getHintKey(MerkleTreeId.PUBLIC_DATA_TREE), [MerkleTreeId.NULLIFIER_TREE]: await this.getHintKey(MerkleTreeId.NULLIFIER_TREE), [MerkleTreeId.NOTE_HASH_TREE]: await this.getHintKey(MerkleTreeId.NOTE_HASH_TREE), @@ -278,13 +318,21 @@ export class HintingPublicTreesDB extends PublicTreesDB { [MerkleTreeId.ARCHIVE]: await this.getHintKey(MerkleTreeId.ARCHIVE), }; - HintingPublicTreesDB.log.debug('Evolved tree state:'); - for (const treeId of Object.keys(beforeState)) { - const id: MerkleTreeId = treeId as unknown as MerkleTreeId; - const treeName = getTreeName(id); - HintingPublicTreesDB.log.debug( - `${treeName}: ${beforeState[id].root}, ${beforeState[id].nextAvailableLeafIndex} -> ${afterState[id].root}, ${afterState[id].nextAvailableLeafIndex}.`, - ); + this.hints.revertCheckpointHints.push( + AvmCheckpointActionRevertCheckpointHint.create( + hintKey.id, + hintKey.hash, + beforeState, + newCheckpointId, + afterState, + ), + ); + + HintingPublicTreesDB.log.debug( + `[revertCheckpoint] Checkpoint evolved ${hintKey.id} -> ${newCheckpointId} at checkpoint key ${hintKey.hash}.`, + ); + for (const treeId of merkleTreeIds()) { + HintingPublicTreesDB.logTreeChange(beforeState[treeId], afterState[treeId], treeId); } } @@ -293,4 +341,25 @@ export class HintingPublicTreesDB extends PublicTreesDB { const treeInfo = await super.getTreeInfo(treeId); return new AppendOnlyTreeSnapshot(Fr.fromBuffer(treeInfo.root), Number(treeInfo.size)); } + + private getCurrentCheckpointId(): number { + return this.checkpointStack[this.checkpointStack.length - 1]; + } + + private async getCheckpointHintKey(): Promise { + const stateReferenceFields = (await super.getStateReference()).toFields(); + const hash = Fr.fromBuffer(sha256Trunc(Buffer.concat(stateReferenceFields.map(field => field.toBuffer())))); + return new CheckpointKey(this.getCurrentCheckpointId(), hash); + } + + private static logTreeChange( + beforeState: AppendOnlyTreeSnapshot, + afterState: AppendOnlyTreeSnapshot, + treeId: MerkleTreeId, + ) { + const treeName = getTreeName(treeId); + HintingPublicTreesDB.log.debug( + `[${treeName}] Evolved tree state: ${beforeState.root}, ${beforeState.nextAvailableLeafIndex} -> ${afterState.root}, ${afterState.nextAvailableLeafIndex}.`, + ); + } } diff --git a/yarn-project/stdlib/src/avm/avm.ts b/yarn-project/stdlib/src/avm/avm.ts index 6bcf0c808c41..ffa685c86325 100644 --- a/yarn-project/stdlib/src/avm/avm.ts +++ b/yarn-project/stdlib/src/avm/avm.ts @@ -262,6 +262,116 @@ function AvmSequentialInsertHintFactory(klass: IndexedTreeLeafPreimagesClasses) export class AvmSequentialInsertHintPublicDataTree extends AvmSequentialInsertHintFactory(PublicDataTreeLeafPreimage) {} export class AvmSequentialInsertHintNullifierTree extends AvmSequentialInsertHintFactory(NullifierLeafPreimage) {} +// Hint for checkpoint actions that don't change the state. +class AvmCheckpointActionNoChangeHint { + constructor( + // key + public readonly oldCheckpointId: number, + public readonly oldHash: Fr, + // new checkpoint + public readonly newCheckpointId: number, + ) {} + + static get schema() { + return z.object({ + oldCheckpointId: z.number().int().nonnegative(), + oldHash: schemas.Fr, + newCheckpointId: z.number().int().nonnegative(), + }); + } +} + +// Hint for MerkleTreeDB.createCheckpoint. +export class AvmCheckpointActionCreateCheckpointHint extends AvmCheckpointActionNoChangeHint {} + +// Hint for MerkleTreeDB.commitCheckpoint. +export class AvmCheckpointActionCommitCheckpointHint extends AvmCheckpointActionNoChangeHint {} + +// Hint for MerkleTreeDB.revertCheckpoint. +export class AvmCheckpointActionRevertCheckpointHint { + // We use explicit fields for MessagePack. + constructor( + public readonly oldCheckpointId: number, + public readonly oldHash: Fr, + public readonly newCheckpointId: number, + public readonly beforePublicDataTree: AppendOnlyTreeSnapshot, + public readonly beforeNullifierTree: AppendOnlyTreeSnapshot, + public readonly beforeNoteHashTree: AppendOnlyTreeSnapshot, + public readonly beforeL1ToL2MessageTree: AppendOnlyTreeSnapshot, + public readonly afterPublicDataTree: AppendOnlyTreeSnapshot, + public readonly afterNullifierTree: AppendOnlyTreeSnapshot, + public readonly afterNoteHashTree: AppendOnlyTreeSnapshot, + public readonly afterL1ToL2MessageTree: AppendOnlyTreeSnapshot, + ) {} + + static create( + oldCheckpointId: number, + oldHash: Fr, + stateBefore: Record, + newCheckpointId: number, + stateAfter: Record, + ): AvmCheckpointActionRevertCheckpointHint { + return new AvmCheckpointActionRevertCheckpointHint( + oldCheckpointId, + oldHash, + newCheckpointId, + stateBefore[MerkleTreeId.PUBLIC_DATA_TREE], + stateBefore[MerkleTreeId.NULLIFIER_TREE], + stateBefore[MerkleTreeId.NOTE_HASH_TREE], + stateBefore[MerkleTreeId.L1_TO_L2_MESSAGE_TREE], + stateAfter[MerkleTreeId.PUBLIC_DATA_TREE], + stateAfter[MerkleTreeId.NULLIFIER_TREE], + stateAfter[MerkleTreeId.NOTE_HASH_TREE], + stateAfter[MerkleTreeId.L1_TO_L2_MESSAGE_TREE], + ); + } + + static get schema() { + return z + .object({ + oldCheckpointId: z.number().int().nonnegative(), + oldHash: schemas.Fr, + newCheckpointId: z.number().int().nonnegative(), + beforePublicDataTree: AppendOnlyTreeSnapshot.schema, + beforeNullifierTree: AppendOnlyTreeSnapshot.schema, + beforeNoteHashTree: AppendOnlyTreeSnapshot.schema, + beforeL1ToL2MessageTree: AppendOnlyTreeSnapshot.schema, + afterPublicDataTree: AppendOnlyTreeSnapshot.schema, + afterNullifierTree: AppendOnlyTreeSnapshot.schema, + afterNoteHashTree: AppendOnlyTreeSnapshot.schema, + afterL1ToL2MessageTree: AppendOnlyTreeSnapshot.schema, + }) + .transform( + ({ + oldCheckpointId, + oldHash, + newCheckpointId, + beforePublicDataTree, + beforeNullifierTree, + beforeNoteHashTree, + beforeL1ToL2MessageTree, + afterPublicDataTree, + afterNullifierTree, + afterNoteHashTree, + afterL1ToL2MessageTree, + }) => + new AvmCheckpointActionRevertCheckpointHint( + oldCheckpointId, + oldHash, + newCheckpointId, + beforePublicDataTree, + beforeNullifierTree, + beforeNoteHashTree, + beforeL1ToL2MessageTree, + afterPublicDataTree, + afterNullifierTree, + afterNoteHashTree, + afterL1ToL2MessageTree, + ), + ); + } +} + //////////////////////////////////////////////////////////////////////////// // Hints (other) //////////////////////////////////////////////////////////////////////////// @@ -386,6 +496,9 @@ export class AvmExecutionHints { public readonly getLeafValueHints: AvmGetLeafValueHint[] = [], public readonly sequentialInsertHintsPublicDataTree: AvmSequentialInsertHintPublicDataTree[] = [], public readonly sequentialInsertHintsNullifierTree: AvmSequentialInsertHintNullifierTree[] = [], + public readonly createCheckpointHints: AvmCheckpointActionCreateCheckpointHint[] = [], + public readonly commitCheckpointHints: AvmCheckpointActionCommitCheckpointHint[] = [], + public readonly revertCheckpointHints: AvmCheckpointActionRevertCheckpointHint[] = [], ) {} static empty() { @@ -406,6 +519,9 @@ export class AvmExecutionHints { getLeafValueHints: AvmGetLeafValueHint.schema.array(), sequentialInsertHintsPublicDataTree: AvmSequentialInsertHintPublicDataTree.schema.array(), sequentialInsertHintsNullifierTree: AvmSequentialInsertHintNullifierTree.schema.array(), + createCheckpointHints: AvmCheckpointActionCreateCheckpointHint.schema.array(), + commitCheckpointHints: AvmCheckpointActionCommitCheckpointHint.schema.array(), + revertCheckpointHints: AvmCheckpointActionRevertCheckpointHint.schema.array(), }) .transform( ({ @@ -420,6 +536,9 @@ export class AvmExecutionHints { getLeafValueHints, sequentialInsertHintsPublicDataTree, sequentialInsertHintsNullifierTree, + createCheckpointHints, + commitCheckpointHints, + revertCheckpointHints, }) => new AvmExecutionHints( tx, @@ -433,6 +552,9 @@ export class AvmExecutionHints { getLeafValueHints, sequentialInsertHintsPublicDataTree, sequentialInsertHintsNullifierTree, + createCheckpointHints, + commitCheckpointHints, + revertCheckpointHints, ), ); } diff --git a/yarn-project/stdlib/src/tests/factories.ts b/yarn-project/stdlib/src/tests/factories.ts index 66377d47b001..318ec658a619 100644 --- a/yarn-project/stdlib/src/tests/factories.ts +++ b/yarn-project/stdlib/src/tests/factories.ts @@ -56,6 +56,9 @@ import { ContractStorageUpdateRequest } from '../avm/contract_storage_update_req import { AvmAccumulatedData, AvmBytecodeCommitmentHint, + AvmCheckpointActionCommitCheckpointHint, + AvmCheckpointActionCreateCheckpointHint, + AvmCheckpointActionRevertCheckpointHint, AvmCircuitInputs, AvmCircuitPublicInputs, AvmContractClassHint, @@ -1382,6 +1385,30 @@ export function makeAvmSequentialInsertHintNullifierTree(seed = 0): AvmSequentia ); } +export function makeAvmCheckpointActionCreateCheckpointHint(seed = 0) { + return new AvmCheckpointActionCreateCheckpointHint(seed, new Fr(seed + 1), seed + 2); +} + +export function makeAvmCheckpointActionCommitCheckpointHint(seed = 0) { + return new AvmCheckpointActionCommitCheckpointHint(seed, new Fr(seed + 1), seed + 2); +} + +export function makeAvmCheckpointActionRevertCheckpointHint(seed = 0) { + return new AvmCheckpointActionRevertCheckpointHint( + seed, + new Fr(seed + 1), + seed + 2, + makeAppendOnlyTreeSnapshot(seed + 3), + makeAppendOnlyTreeSnapshot(seed + 4), + makeAppendOnlyTreeSnapshot(seed + 5), + makeAppendOnlyTreeSnapshot(seed + 6), + makeAppendOnlyTreeSnapshot(seed + 7), + makeAppendOnlyTreeSnapshot(seed + 8), + makeAppendOnlyTreeSnapshot(seed + 9), + makeAppendOnlyTreeSnapshot(seed + 10), + ); +} + /** * Makes arbitrary AvmContractInstanceHint. * @param seed - The seed to use for generating the state reference. @@ -1481,6 +1508,9 @@ export async function makeAvmExecutionHints( makeAvmSequentialInsertHintNullifierTree, seed + 0x5700, ), + createCheckpointHints: makeArray(baseLength + 5, makeAvmCheckpointActionCreateCheckpointHint, seed + 0x5900), + commitCheckpointHints: makeArray(baseLength + 5, makeAvmCheckpointActionCommitCheckpointHint, seed + 0x5b00), + revertCheckpointHints: makeArray(baseLength + 5, makeAvmCheckpointActionRevertCheckpointHint, seed + 0x5d00), ...overrides, }; @@ -1496,6 +1526,9 @@ export async function makeAvmExecutionHints( fields.getLeafValueHints, fields.sequentialInsertHintsPublicDataTree, fields.sequentialInsertHintsNullifierTree, + fields.createCheckpointHints, + fields.commitCheckpointHints, + fields.revertCheckpointHints, ); } From f7d5758874b907721eaebee8084b6ce3ad1a3a7e Mon Sep 17 00:00:00 2001 From: fcarreiro Date: Fri, 4 Apr 2025 13:56:03 +0000 Subject: [PATCH 2/2] done? --- .../barretenberg/vm2/common/avm_inputs.hpp | 50 ++++- .../vm2/common/avm_inputs.testdata.bin | Bin 277447 -> 275377 bytes .../vm2/simulation/concrete_dbs.cpp | 15 ++ .../vm2/simulation/concrete_dbs.hpp | 3 + .../vm2/simulation/lib/db_interfaces.hpp | 14 +- .../vm2/simulation/lib/raw_data_dbs.cpp | 191 +++++++++++++++--- .../vm2/simulation/lib/raw_data_dbs.hpp | 12 ++ .../vm2/simulation/testing/mock_dbs.hpp | 10 +- .../vm2/simulation/tx_execution.cpp | 20 +- .../vm2/simulation/tx_execution.hpp | 13 +- .../barretenberg/vm2/simulation_helper.cpp | 2 +- .../src/public/hinting_db_sources.ts | 59 +++--- yarn-project/stdlib/src/avm/avm.ts | 126 +++++------- yarn-project/stdlib/src/tests/factories.ts | 42 ++-- 14 files changed, 386 insertions(+), 171 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/vm2/common/avm_inputs.hpp b/barretenberg/cpp/src/barretenberg/vm2/common/avm_inputs.hpp index 934876bfbf6f..096a44fa8c9f 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/common/avm_inputs.hpp +++ b/barretenberg/cpp/src/barretenberg/vm2/common/avm_inputs.hpp @@ -3,6 +3,7 @@ #pragma once #include +#include #include #include "barretenberg/common/utils.hpp" @@ -36,6 +37,11 @@ struct AppendOnlyTreeSnapshot { std::size_t hash() const noexcept { return utils::hash_as_tuple(root, nextAvailableLeafIndex); } bool operator==(const AppendOnlyTreeSnapshot& other) const = default; + friend std::ostream& operator<<(std::ostream& os, const AppendOnlyTreeSnapshot& obj) + { + os << "root: " << obj.root << ", nextAvailableLeafIndex: " << obj.nextAvailableLeafIndex; + return os; + } MSGPACK_FIELDS(root, nextAvailableLeafIndex); }; @@ -187,6 +193,36 @@ template struct SequentialInsertHint { MSGPACK_FIELDS(hintKey, treeId, leaf, lowLeavesWitnessData, insertionWitnessData, stateAfter); }; +struct CheckpointActionNoStateChangeHint { + // key + uint32_t actionCounter; + // current checkpoint evolution + uint32_t oldCheckpointId; + uint32_t newCheckpointId; + + bool operator==(const CheckpointActionNoStateChangeHint& other) const = default; + + MSGPACK_FIELDS(actionCounter, oldCheckpointId, newCheckpointId); +}; + +using CreateCheckpointHint = CheckpointActionNoStateChangeHint; +using CommitCheckpointHint = CheckpointActionNoStateChangeHint; + +struct RevertCheckpointHint { + // key + uint32_t actionCounter; + // current checkpoint evolution + uint32_t oldCheckpointId; + uint32_t newCheckpointId; + // state evolution + TreeSnapshots stateBefore; + TreeSnapshots stateAfter; + + bool operator==(const RevertCheckpointHint& other) const = default; + + MSGPACK_FIELDS(actionCounter, oldCheckpointId, newCheckpointId, stateBefore, stateAfter); +}; + //////////////////////////////////////////////////////////////////////////// // Hints (other) //////////////////////////////////////////////////////////////////////////// @@ -248,6 +284,9 @@ struct ExecutionHints { std::vector getLeafValueHints; std::vector> sequentialInsertHintsPublicDataTree; std::vector> sequentialInsertHintsNullifierTree; + std::vector createCheckpointHints; + std::vector commitCheckpointHints; + std::vector revertCheckpointHints; bool operator==(const ExecutionHints& other) const = default; @@ -261,7 +300,10 @@ struct ExecutionHints { getLeafPreimageHintsNullifierTree, getLeafValueHints, sequentialInsertHintsPublicDataTree, - sequentialInsertHintsNullifierTree); + sequentialInsertHintsNullifierTree, + createCheckpointHints, + commitCheckpointHints, + revertCheckpointHints); }; //////////////////////////////////////////////////////////////////////////// @@ -278,9 +320,3 @@ struct AvmProvingInputs { }; } // namespace bb::avm2 - -// Define hash function so that they can be used as keys in maps. -// See https://en.cppreference.com/w/cpp/utility/hash. -template <> struct std::hash { - std::size_t operator()(const bb::avm2::AppendOnlyTreeSnapshot& st) const noexcept { return st.hash(); } -}; 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 e7cbb01d16620c4c23f96563738c2b9f0d0529de..4c79d413107038d55a1c33ee513d24e35a523800 100644 GIT binary patch delta 6967 zcmbW5No-t25QaTzmJ0_zMG--Ogb?!Ck>Y*d*Wei9c#rMaiNH}djG3>KmL_}{A>Q+ zeq{f`cj|HH?)|ax)J#0q5MPd^qKl7?u0Dv*Pc%$N$LA99nOLfA;?YsjgIIJm_*3y& zp300Y!pLHbED1)wX#cbrLj8x=)aBPR|Dpu=F8ziTl1y?*sfQ*mqQoN@@fp)ficWk+s3d=PX2xe|lYA zEXCb*!QFOqE&PxF_jQ4|4)=9+3&i!X@2CNlGzv-@FtQONn}U(M7NQYDn=rIF7-}P% zFtQmVTZG7F8~K9!eELt{5^k;q{=%<&Te!Ja*mu;1QrZP6Z5Y{(ksZNEdfwY1)GxoG zu5@HP?;6cWqZw(zjC7Ij^+?sIGyLb@bZEhTI(zU=wO~Icy)X0m(`Y{$?MDmtlirr; zz8dXEXV{Oe#`Hc2Mf=f${b;lwT?vWQRl!8siz7n{R%3OqaC7KL{y};l+#LFkM*qoBxl;1}%aai4&X_T(4#3;1Y88qG*&n32UtFVg5mI>U>s zaqA|(Za{og1pm?GKLPvM8W4yD`}sM&4-$^{qtSk}U_a??Q156zTCyK4*w0Ucs<7?Z z4F56cKL-8B6o0jp8cRj%qm%K)=vm$~$Me3oqRRZcL+a%VU&s7|33b)4-b*j;$NbX~ zRTSygrU(m8{C&YWF$&Yk4} z3U+4(yE9V~g7U;gdH)?pLLW)!V+qrQGA*N=j5{cQzU!PbMCT0gIrD@vFQA}Zn-X|l zO=$>9v3r|;xLN5Z3H>Z#hEQf?l)fYCHj5*gsX~hC5Q;2{*^5g6W&SH$&h`Id9Ex$G z7-xzlqF53ruw64aK$@BmifoJ7OUNi&NeAWk&m5EqLYZKc6rrR96znJsc9fJ|(kseat!`(oX)={Qew(Ejrfd;!=|H@97$!1l}-XbVZuB!7RA~#9oO%^># z>q!>9V%ME3f@tio4ffZjJ|qR%7PPknAhsK`4cLF(*m(oSG}M5pDW+Ma6s!s~LeD?4 zAC`hLKieYqQnGy8bg_JHx>!CnU22-;uTe7A1o=3M82nOV8bgwiLmhj|GLUV(7>HXh z2BOwW%`nh90j&!_I5QcXnM@M`a_j_vyP!2Wkt;;}m;$R@2I2ec~4z<8Q+XS>N0O6o+S`nx%1Sp5X z_cml8J9jV;&m9azbBBsC(2fNv*b#v6i^Ac@;r5(Ep?jM$K;fs(JDw*H2A~N<#Tj6i zGO{ZG;D-%^A2v)!NJerfbZ<)rvU3P$gy#?jqB%q*7-)}x_5>jO++sSg#5zNOawv3f fTL!Yz2m|pn!ay{Qs3Zg3u|SbK-#`DkI{EM)d|bKw delta 6491 zcmZ{oOK4qH6o#uuup&r6#L#dof-?s~Z-~X(2rkk_Y{l2~Tf6m@_=T6hN ze>GjW^uVPDcavYHp4?tLyEJrqdGU?)wKJ=m6H7nT4qd4aeSG=tXR`mMl4j$9w)}xg zt$llS`5c#b+}@YyrtPt&L)UbgH63|P_T{t*(wRR{sdee5-DcCSye4~k#%$W1KTxUl z=%!V(X-}-F>Y84&rkdAeKiVL@`2&?&pKjW3HtmZw{kmqrtm)5dvh%a%j11%tRBD5| z>0z_!U|y5mwVNK!AE?xZbkkw8=}@d0)-@w$&2V0m`J<-YNd7>jHmaMBnN3Gy&6utk zH*3c7nrwQ`1R2jCsMIEO(@C@GL|&8Kw3|+b2mYB$p3`A=G3_(QEG!q(?sVFnnRXY_ z-t2VRouob6IhH*BP=LE<|8@+x(*bv8z+DXZ`tznx47k$)cV@s{47k$)cV@s{4ER&? zCP)mp(*bv8z+DJZ{Iz&yt1~jd^EdxI%&`DEhH`buiYP-$+k`*RWpjCBuhrL!^^vO>+3R#=r9QH=H-bi= zoT-m|g^|5JAD&HG4&2^nc!bwY>h_%{9)M;Kfi8G}nmj;F9-t--Fei4?wdApxHy93m%{*4^Wc_sL4a9*`xK{GLP^TPQCsL`zm_?nmqu`9s*tP z05y4lnmj;F9zx9?2j4IA2q%No>yv@+We-5J2cX$QpbH+LCJ#`P2dK$IsM+JOi)9|+ zw37N>y|4P%1JLXNX!a23f(NL{1JvXJYVr_j_W1XMGLLYmO1(Z*`F{2QGO2#!J0EId|e8e|VZvj?ErL!b*Dpe7GclLx5DL#V@2Y<-l>C-u`vcI#Z@0q@p3 z$rJmT5=cx5B&HOk0wtJ;5==x1CZc@yl?F=8_JNaSvmH<8H_j_=Kr%NVnOh(uw`@>8 z>j99E0Z7OK7$z3Tk1UlThxIoyCvvmH{+-0Gd5yYNLm2NLBzaDFB!h1eg`>)K4h3LbyVuene+;loi0s3gBf0 zndPVub}M^;nmj<=;4#YzKb77T!X+>D`jY3zSOL7O0A5y*SuQ95n1TynQjlrp8;?-4 z$2X-ng>dmqy}o$*arOW-djOg}1iIh>YVrUzd4M`!imkPzuoSzcmxS=&R_e#~Zkk{P z@UjAUSwVOU3IHYr0F#1DGcScu^HTiwSy?K=-97dC?(Qeq1JLXN=ths(f&zd^0l=gH z;CvNue->a?Xk9H+2!AD{eo`;R5mo>%E65}l{s$1~f(NL{1JvXJYVr_j_Bi-fnMe4? sBlSnL#}s=2nmqs=mO?gjK>@&|0ANx8FewNyE3DU#B-ihLp4|BKe+QoSpa1{> diff --git a/barretenberg/cpp/src/barretenberg/vm2/simulation/concrete_dbs.cpp b/barretenberg/cpp/src/barretenberg/vm2/simulation/concrete_dbs.cpp index 07ee695eee17..4b874ca426bf 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/simulation/concrete_dbs.cpp +++ b/barretenberg/cpp/src/barretenberg/vm2/simulation/concrete_dbs.cpp @@ -49,4 +49,19 @@ FF MerkleDB::storage_read(const FF& leaf_slot) const return value; } +void MerkleDB::create_checkpoint() +{ + raw_merkle_db.create_checkpoint(); +} + +void MerkleDB::commit_checkpoint() +{ + raw_merkle_db.commit_checkpoint(); +} + +void MerkleDB::revert_checkpoint() +{ + raw_merkle_db.revert_checkpoint(); +} + } // namespace bb::avm2::simulation diff --git a/barretenberg/cpp/src/barretenberg/vm2/simulation/concrete_dbs.hpp b/barretenberg/cpp/src/barretenberg/vm2/simulation/concrete_dbs.hpp index aa1609345d21..d91afc8d9ab9 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/simulation/concrete_dbs.hpp +++ b/barretenberg/cpp/src/barretenberg/vm2/simulation/concrete_dbs.hpp @@ -46,6 +46,9 @@ class MerkleDB final : public HighLevelMerkleDBInterface { // Unconstrained. const TreeSnapshots& get_tree_roots() const override; + void create_checkpoint() override; + void commit_checkpoint() override; + void revert_checkpoint() override; // Constrained. // TODO: When actually using this, consider siloing inside (and taking a silo gadget in the constructor). 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 ea1a0b8726fc..f111de945288 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/simulation/lib/db_interfaces.hpp +++ b/barretenberg/cpp/src/barretenberg/vm2/simulation/lib/db_interfaces.hpp @@ -40,15 +40,14 @@ class LowLevelMerkleDBInterface { virtual crypto::merkle_tree::IndexedLeaf get_leaf_preimage_nullifier_tree( crypto::merkle_tree::index_t leaf_index) const = 0; - // Inserts a leaf into the public data tree sequentially, getting witnesses at every step. - // Note: This method doesn't support inserting empty leaves. virtual world_state::SequentialInsertionResult insert_indexed_leaves_public_data_tree(const crypto::merkle_tree::PublicDataLeafValue& leaf_value) = 0; - - // Inserts a leaf into the nullifier tree sequentially, getting witnesses at every step. - // Note: This method doesn't support inserting empty leaves. virtual world_state::SequentialInsertionResult insert_indexed_leaves_nullifier_tree(const crypto::merkle_tree::NullifierLeafValue& leaf_value) = 0; + + virtual void create_checkpoint() = 0; + virtual void commit_checkpoint() = 0; + virtual void revert_checkpoint() = 0; }; // High level access to a merkle db. In general these will be constrained. @@ -58,6 +57,11 @@ class HighLevelMerkleDBInterface { virtual const TreeSnapshots& get_tree_roots() const = 0; virtual FF storage_read(const FF& key) const = 0; + + virtual void create_checkpoint() = 0; + virtual void commit_checkpoint() = 0; + virtual void revert_checkpoint() = 0; + virtual LowLevelMerkleDBInterface& as_unconstrained() 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 d8fb21700aa0..5fc1761a8004 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 @@ -3,6 +3,7 @@ #include #include #include +#include #include "barretenberg/common/log.hpp" #include "barretenberg/crypto/merkle_tree/indexed_tree/indexed_leaf.hpp" @@ -10,6 +11,22 @@ namespace bb::avm2::simulation { +namespace { + +std::string to_string(const TreeSnapshots& snapshots) +{ + return format("PUBLIC_DATA_TREE: ", + snapshots.publicDataTree, + "\nNULLIFIER_TREE: ", + snapshots.nullifierTree, + "\nNOTE_HASH_TREE: ", + snapshots.noteHashTree, + "\nL1_TO_L2_MESSAGE_TREE: ", + snapshots.l1ToL2MessageTree); +} + +} // namespace + // HintedRawContractDB starts. HintedRawContractDB::HintedRawContractDB(const ExecutionHints& hints) { @@ -98,41 +115,27 @@ HintedRawMerkleDB::HintedRawMerkleDB(const ExecutionHints& hints, const TreeSnap : tree_roots(tree_roots) { vinfo("Initializing HintedRawMerkleDB with...", - "\n * get_sibling_path hints: ", + "\n * get_sibling_path_hints: ", hints.getSiblingPathHints.size(), - "\n * get_previous_value_index hints: ", + "\n * get_previous_value_index_hints: ", hints.getPreviousValueIndexHints.size(), - "\n * get_leaf_preimage hints_public_data_tree: ", + "\n * get_leaf_preimage_hints_public_data_tree: ", hints.getLeafPreimageHintsPublicDataTree.size(), - "\n * get_leaf_preimage hints_nullifier_tree: ", + "\n * get_leaf_preimage_hints_nullifier_tree: ", hints.getLeafPreimageHintsNullifierTree.size(), "\n * get_leaf_value_hints: ", hints.getLeafValueHints.size(), "\n * sequential_insert_hints_public_data_tree: ", hints.sequentialInsertHintsPublicDataTree.size(), "\n * sequential_insert_hints_nullifier_tree: ", - hints.sequentialInsertHintsNullifierTree.size()); - debug("Initializing HintedRawMerkleDB with snapshots...", - "\n * nullifierTree: ", - tree_roots.nullifierTree.root, - " (size: ", - tree_roots.nullifierTree.nextAvailableLeafIndex, - ")", - "\n * publicDataTree: ", - tree_roots.publicDataTree.root, - " (size: ", - tree_roots.publicDataTree.nextAvailableLeafIndex, - ")", - "\n * noteHashTree: ", - tree_roots.noteHashTree.root, - " (size: ", - tree_roots.noteHashTree.nextAvailableLeafIndex, - ")", - "\n * l1ToL2MessageTree: ", - tree_roots.l1ToL2MessageTree.root, - " (size: ", - tree_roots.l1ToL2MessageTree.nextAvailableLeafIndex, - ")"); + hints.sequentialInsertHintsNullifierTree.size(), + "\n * create_checkpoint_hints: ", + hints.createCheckpointHints.size(), + "\n * commit_checkpoint_hints: ", + hints.commitCheckpointHints.size(), + "\n * revert_checkpoint_hints: ", + hints.revertCheckpointHints.size()); + debug("Initializing HintedRawMerkleDB with snapshots...\n", to_string(tree_roots)); for (const auto& get_sibling_path_hint : hints.getSiblingPathHints) { GetSiblingPathKey key = { get_sibling_path_hint.hintKey, @@ -179,6 +182,18 @@ HintedRawMerkleDB::HintedRawMerkleDB(const ExecutionHints& hints, const TreeSnap sequential_insert_hint.leaf }; sequential_insert_hints_nullifier_tree[key] = sequential_insert_hint; } + + for (const auto& create_checkpoint_hint : hints.createCheckpointHints) { + create_checkpoint_hints[create_checkpoint_hint.actionCounter] = create_checkpoint_hint; + } + + for (const auto& commit_checkpoint_hint : hints.commitCheckpointHints) { + commit_checkpoint_hints[commit_checkpoint_hint.actionCounter] = commit_checkpoint_hint; + } + + for (const auto& revert_checkpoint_hint : hints.revertCheckpointHints) { + revert_checkpoint_hints[revert_checkpoint_hint.actionCounter] = revert_checkpoint_hint; + } } const AppendOnlyTreeSnapshot& HintedRawMerkleDB::get_tree_info(world_state::MerkleTreeId tree_id) const @@ -361,4 +376,128 @@ world_state::SequentialInsertionResult return result; } +void HintedRawMerkleDB::create_checkpoint() +{ + auto it = create_checkpoint_hints.find(checkpoint_action_counter); + if (it == create_checkpoint_hints.end()) { + throw std::runtime_error( + format("[create_checkpoint@", checkpoint_action_counter, "] Hint not found for action counter!")); + } + const auto& hint = it->second; + + // Sanity check. + if (hint.oldCheckpointId != checkpoint_stack.top()) { + throw std::runtime_error(format("[create_checkpoint@", + checkpoint_action_counter, + "] Old checkpoint id does not match the current checkpoint id: ", + hint.oldCheckpointId, + " != ", + checkpoint_stack.top())); + } + + debug("[create_checkpoint@", + checkpoint_action_counter, + "] Checkpoint evolved ", + hint.oldCheckpointId, + " -> ", + hint.newCheckpointId); + + checkpoint_stack.push(hint.newCheckpointId); + checkpoint_action_counter++; +} + +void HintedRawMerkleDB::commit_checkpoint() +{ + auto it = commit_checkpoint_hints.find(checkpoint_action_counter); + if (it == commit_checkpoint_hints.end()) { + throw std::runtime_error( + format("[commit_checkpoint@", checkpoint_action_counter, "] Hint not found for action counter!")); + } + const auto& hint = it->second; + + // Sanity check. + if (hint.oldCheckpointId != checkpoint_stack.top()) { + throw std::runtime_error(format("[commit_checkpoint@", + checkpoint_action_counter, + "] Old checkpoint id does not match the current checkpoint id: ", + hint.oldCheckpointId, + " != ", + checkpoint_stack.top())); + } + + checkpoint_stack.pop(); + + // Sanity check. + if (hint.newCheckpointId != checkpoint_stack.top()) { + throw std::runtime_error(format("[commit_checkpoint@", + checkpoint_action_counter, + "] New checkpoint id does not match the current checkpoint id: ", + hint.newCheckpointId, + " != ", + checkpoint_stack.top())); + } + + debug("[commit_checkpoint@", + checkpoint_action_counter, + "] Checkpoint evolved ", + hint.oldCheckpointId, + " -> ", + hint.newCheckpointId); + + checkpoint_action_counter++; +} + +void HintedRawMerkleDB::revert_checkpoint() +{ + auto it = revert_checkpoint_hints.find(checkpoint_action_counter); + if (it == revert_checkpoint_hints.end()) { + throw std::runtime_error( + format("[revert_checkpoint@", checkpoint_action_counter, "] Hint not found for action counter!")); + } + const auto& hint = it->second; + + // Sanity check of checkpoint stack. + if (hint.oldCheckpointId != checkpoint_stack.top()) { + throw std::runtime_error(format("[revert_checkpoint@", + checkpoint_action_counter, + "] Old checkpoint id does not match the current checkpoint id: ", + hint.oldCheckpointId, + " != ", + checkpoint_stack.top())); + } + + // Sanity check of tree snapshots. + if (hint.stateBefore != tree_roots) { + vinfo("Hint tree snapshots: ", to_string(hint.stateBefore)); + vinfo("Current tree roots: ", to_string(tree_roots)); + throw std::runtime_error(format("[revert_checkpoint@", + checkpoint_action_counter, + "] Hint tree snapshots do not match the current tree roots.")); + } + + checkpoint_stack.pop(); + + // Sanity check. + if (hint.newCheckpointId != checkpoint_stack.top()) { + throw std::runtime_error(format("[revert_checkpoint@", + checkpoint_action_counter, + "] New checkpoint id does not match the current checkpoint id: ", + hint.newCheckpointId, + " != ", + checkpoint_stack.top())); + } + + // Evolve trees. + tree_roots = hint.stateAfter; + + debug("[revert_checkpoint@", + checkpoint_action_counter, + "] Checkpoint evolved ", + hint.oldCheckpointId, + " -> ", + hint.newCheckpointId); + + checkpoint_action_counter++; +} + } // 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 74d3e02e8fb9..068fdb42c3ca 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 @@ -1,5 +1,6 @@ #pragma once +#include #include #include "barretenberg/crypto/merkle_tree/hash_path.hpp" @@ -57,8 +58,16 @@ class HintedRawMerkleDB final : public LowLevelMerkleDBInterface { world_state::SequentialInsertionResult insert_indexed_leaves_nullifier_tree(const crypto::merkle_tree::NullifierLeafValue& leaf_value) override; + void create_checkpoint() override; + void commit_checkpoint() override; + void revert_checkpoint() override; + private: TreeSnapshots tree_roots; + uint32_t checkpoint_action_counter = 0; + // We start with a checkpoint id of 0, which is the assumed initial state checkpoint. + // This stack is for debugging purposes only. + std::stack checkpoint_stack{ { 0 } }; // Query hints. using GetSiblingPathKey = @@ -85,6 +94,9 @@ class HintedRawMerkleDB final : public LowLevelMerkleDBInterface { unordered_flat_map> sequential_insert_hints_nullifier_tree; + unordered_flat_map create_checkpoint_hints; + unordered_flat_map commit_checkpoint_hints; + unordered_flat_map revert_checkpoint_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 08c6873a9b1e..6ff31bd2b906 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/simulation/testing/mock_dbs.hpp +++ b/barretenberg/cpp/src/barretenberg/vm2/simulation/testing/mock_dbs.hpp @@ -54,6 +54,9 @@ class MockLowLevelMerkleDB : public LowLevelMerkleDBInterface { insert_indexed_leaves_nullifier_tree, (const crypto::merkle_tree::NullifierLeafValue& leaf_value), (override)); + MOCK_METHOD(void, create_checkpoint, (), (override)); + MOCK_METHOD(void, commit_checkpoint, (), (override)); + MOCK_METHOD(void, revert_checkpoint, (), (override)); }; class MockHighLevelMerkleDB : public HighLevelMerkleDBInterface { @@ -62,8 +65,13 @@ class MockHighLevelMerkleDB : public HighLevelMerkleDBInterface { MockHighLevelMerkleDB(); ~MockHighLevelMerkleDB() override; - MOCK_METHOD(FF, storage_read, (const FF& key), (const, override)); MOCK_METHOD(const TreeSnapshots&, get_tree_roots, (), (const, override)); + MOCK_METHOD(FF, storage_read, (const FF& key), (const, override)); + + MOCK_METHOD(void, create_checkpoint, (), (override)); + MOCK_METHOD(void, commit_checkpoint, (), (override)); + MOCK_METHOD(void, revert_checkpoint, (), (override)); + MOCK_METHOD(LowLevelMerkleDBInterface&, as_unconstrained, (), (const, override)); }; diff --git a/barretenberg/cpp/src/barretenberg/vm2/simulation/tx_execution.cpp b/barretenberg/cpp/src/barretenberg/vm2/simulation/tx_execution.cpp index d20d77e36cde..dbf61b8ca100 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/simulation/tx_execution.cpp +++ b/barretenberg/cpp/src/barretenberg/vm2/simulation/tx_execution.cpp @@ -13,9 +13,10 @@ void TxExecution::simulate(const Tx& tx) tx.teardownEnqueuedCall ? "1 teardown enqueued call" : "no teardown enqueued call"); // TODO: This method is currently wrong. We need to lift the context to this level. + // TODO: Checkpointing is not yet correctly implemented. // Insert non-revertibles. - // TODO: We need a context at this level to be able to do the insertions. + insert_non_revertibles(tx); // Setup. for (const auto& call : tx.setupEnqueuedCalls) { @@ -25,7 +26,7 @@ void TxExecution::simulate(const Tx& tx) } // Insert revertibles. - // TODO: We need a context at this level to be able to do the insertions. + insert_revertibles(tx); // App logic. for (const auto& call : tx.appLogicEnqueuedCalls) { @@ -55,4 +56,19 @@ std::unique_ptr TxExecution::make_enqueued_context(AztecAddres return execution_provider.make_enqueued_context(address, msg_sender, calldata, is_static); } +void TxExecution::insert_non_revertibles(const Tx&) +{ + // 1. Write the already siloed nullifiers. + // 2. Write the note hashes. + // 3. Write the new contracts. +} + +void TxExecution::insert_revertibles(const Tx&) +{ + merkle_db.create_checkpoint(); + // 1. Write the nullifiers. + // 2. Write the note hashes. + // 3. Write the new contracts. +} + } // namespace bb::avm2::simulation diff --git a/barretenberg/cpp/src/barretenberg/vm2/simulation/tx_execution.hpp b/barretenberg/cpp/src/barretenberg/vm2/simulation/tx_execution.hpp index 7a3c8b9541a3..cac962ed6e3d 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/simulation/tx_execution.hpp +++ b/barretenberg/cpp/src/barretenberg/vm2/simulation/tx_execution.hpp @@ -2,14 +2,18 @@ #include "barretenberg/vm2/common/avm_inputs.hpp" #include "barretenberg/vm2/simulation/execution.hpp" +#include "barretenberg/vm2/simulation/lib/db_interfaces.hpp" namespace bb::avm2::simulation { // In charge of executing a transaction. class TxExecution final { public: - TxExecution(ExecutionInterface& call_execution) - : call_execution(call_execution){}; + // FIXME: mekle db here should only be temporary, we should access via context. + TxExecution(ExecutionInterface& call_execution, HighLevelMerkleDBInterface& merkle_db) + : call_execution(call_execution) + , merkle_db(merkle_db) + {} void simulate(const Tx& tx); @@ -21,7 +25,10 @@ class TxExecution final { private: ExecutionInterface& call_execution; // More things need to be lifted into the tx execution?? - // MerkleDB + HighLevelMerkleDBInterface& merkle_db; + + void insert_non_revertibles(const Tx& tx); + void insert_revertibles(const Tx& tx); }; } // namespace bb::avm2::simulation diff --git a/barretenberg/cpp/src/barretenberg/vm2/simulation_helper.cpp b/barretenberg/cpp/src/barretenberg/vm2/simulation_helper.cpp index ea40d29726a5..c5b6e4436c3a 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/simulation_helper.cpp +++ b/barretenberg/cpp/src/barretenberg/vm2/simulation_helper.cpp @@ -125,7 +125,7 @@ template EventsContainer AvmSimulationHelper::simulate_with_setting Alu alu(alu_emitter); Execution execution(alu, execution_components, instruction_info_db, execution_emitter, context_stack_emitter); - TxExecution tx_execution(execution); + TxExecution tx_execution(execution, merkle_db); Sha256 sha256(sha256_compression_emitter); tx_execution.simulate(inputs.hints.tx); diff --git a/yarn-project/simulator/src/public/hinting_db_sources.ts b/yarn-project/simulator/src/public/hinting_db_sources.ts index c4ad67f71db0..8740e0b43c8d 100644 --- a/yarn-project/simulator/src/public/hinting_db_sources.ts +++ b/yarn-project/simulator/src/public/hinting_db_sources.ts @@ -5,17 +5,17 @@ import type { IndexedTreeLeafPreimage, SiblingPath } from '@aztec/foundation/tre import type { FunctionSelector } from '@aztec/stdlib/abi'; import { AvmBytecodeCommitmentHint, - AvmCheckpointActionCommitCheckpointHint, - AvmCheckpointActionCreateCheckpointHint, - AvmCheckpointActionRevertCheckpointHint, + AvmCommitCheckpointHint, AvmContractClassHint, AvmContractInstanceHint, + AvmCreateCheckpointHint, type AvmExecutionHints, AvmGetLeafPreimageHintNullifierTree, AvmGetLeafPreimageHintPublicDataTree, AvmGetLeafValueHint, AvmGetPreviousValueIndexHint, AvmGetSiblingPathHint, + AvmRevertCheckpointHint, AvmSequentialInsertHintNullifierTree, AvmSequentialInsertHintPublicDataTree, } from '@aztec/stdlib/avm'; @@ -99,18 +99,18 @@ export class HintingPublicContractsDB implements PublicContractsDBInterface { } } -class CheckpointKey { - constructor(public readonly id: number, public readonly hash: Fr) {} -} - /** * A public trees database that forwards requests and collects AVM hints. */ export class HintingPublicTreesDB extends PublicTreesDB { private static readonly log: Logger = createLogger('HintingPublicTreesDB'); - // We use 0 as the initial state even if we never checkpointed. + // This stack is only for debugging purposes. + // The top of the stack is the current checkpoint id. + // We need the stack to be non-empty and use 0 as an arbitrary initial checkpoint id. + // This is not necessarily a checkpoint that happened, but whatever tree state we start with. + private checkpointStack: number[] = [0]; private nextCheckpointId: number = 1; - private checkpointStack: number[] = [0]; // stack only for debugging purposes. + private checkpointActionCounter: number = 0; // yes, a side-effect counter. constructor(db: PublicTreesDB, private hints: AvmExecutionHints) { super(db); @@ -265,39 +265,42 @@ export class HintingPublicTreesDB extends PublicTreesDB { } public override async createCheckpoint(): Promise { - const hintKey = await this.getCheckpointHintKey(); + const actionCounter = this.checkpointActionCounter++; + const oldCheckpointId = this.getCurrentCheckpointId(); + const treesStateHash = await this.getTreesStateHash(); await super.createCheckpoint(); this.checkpointStack.push(this.nextCheckpointId++); const newCheckpointId = this.getCurrentCheckpointId(); - this.hints.createCheckpointHints.push( - new AvmCheckpointActionCreateCheckpointHint(hintKey.id, hintKey.hash, newCheckpointId), - ); + this.hints.createCheckpointHints.push(new AvmCreateCheckpointHint(actionCounter, oldCheckpointId, newCheckpointId)); HintingPublicTreesDB.log.debug( - `[createCheckpoint] Checkpoint evolved ${hintKey.id} -> ${newCheckpointId} at checkpoint key ${hintKey.hash}.`, + `[createCheckpoint:${actionCounter}] Checkpoint evolved ${oldCheckpointId} -> ${newCheckpointId} at trees state ${treesStateHash}.`, ); } public override async commitCheckpoint(): Promise { - const hintKey = await this.getCheckpointHintKey(); + const actionCounter = this.checkpointActionCounter++; + const oldCheckpointId = this.getCurrentCheckpointId(); + const treesStateHash = await this.getTreesStateHash(); await super.commitCheckpoint(); this.checkpointStack.pop(); const newCheckpointId = this.getCurrentCheckpointId(); - this.hints.commitCheckpointHints.push( - new AvmCheckpointActionCommitCheckpointHint(hintKey.id, hintKey.hash, newCheckpointId), - ); + this.hints.commitCheckpointHints.push(new AvmCommitCheckpointHint(actionCounter, oldCheckpointId, newCheckpointId)); HintingPublicTreesDB.log.debug( - `[commitCheckpoint] Checkpoint evolved ${hintKey.id} -> ${newCheckpointId} at checkpoint key ${hintKey.hash}.`, + `[commitCheckpoint:${actionCounter}] Checkpoint evolved ${oldCheckpointId} -> ${newCheckpointId} at trees state ${treesStateHash}.`, ); } public override async revertCheckpoint(): Promise { - const hintKey = await this.getCheckpointHintKey(); + const actionCounter = this.checkpointActionCounter++; + const oldCheckpointId = this.getCurrentCheckpointId(); + const treesStateHash = await this.getTreesStateHash(); + const beforeState: Record = { [MerkleTreeId.PUBLIC_DATA_TREE]: await this.getHintKey(MerkleTreeId.PUBLIC_DATA_TREE), [MerkleTreeId.NULLIFIER_TREE]: await this.getHintKey(MerkleTreeId.NULLIFIER_TREE), @@ -319,17 +322,11 @@ export class HintingPublicTreesDB extends PublicTreesDB { }; this.hints.revertCheckpointHints.push( - AvmCheckpointActionRevertCheckpointHint.create( - hintKey.id, - hintKey.hash, - beforeState, - newCheckpointId, - afterState, - ), + AvmRevertCheckpointHint.create(actionCounter, oldCheckpointId, newCheckpointId, beforeState, afterState), ); HintingPublicTreesDB.log.debug( - `[revertCheckpoint] Checkpoint evolved ${hintKey.id} -> ${newCheckpointId} at checkpoint key ${hintKey.hash}.`, + `[revertCheckpoint:${actionCounter}] Checkpoint evolved ${oldCheckpointId} -> ${newCheckpointId} at trees state ${treesStateHash}.`, ); for (const treeId of merkleTreeIds()) { HintingPublicTreesDB.logTreeChange(beforeState[treeId], afterState[treeId], treeId); @@ -346,10 +343,10 @@ export class HintingPublicTreesDB extends PublicTreesDB { return this.checkpointStack[this.checkpointStack.length - 1]; } - private async getCheckpointHintKey(): Promise { + // For logging/debugging purposes. + private async getTreesStateHash(): Promise { const stateReferenceFields = (await super.getStateReference()).toFields(); - const hash = Fr.fromBuffer(sha256Trunc(Buffer.concat(stateReferenceFields.map(field => field.toBuffer())))); - return new CheckpointKey(this.getCurrentCheckpointId(), hash); + return Fr.fromBuffer(sha256Trunc(Buffer.concat(stateReferenceFields.map(field => field.toBuffer())))); } private static logTreeChange( diff --git a/yarn-project/stdlib/src/avm/avm.ts b/yarn-project/stdlib/src/avm/avm.ts index ffa685c86325..cf25670366b3 100644 --- a/yarn-project/stdlib/src/avm/avm.ts +++ b/yarn-project/stdlib/src/avm/avm.ts @@ -10,7 +10,7 @@ import { AppendOnlyTreeSnapshot } from '../trees/append_only_tree_snapshot.js'; import { MerkleTreeId } from '../trees/merkle_tree_id.js'; import { NullifierLeafPreimage } from '../trees/nullifier_leaf.js'; import { PublicDataTreeLeafPreimage } from '../trees/public_data_leaf.js'; -import type { Tx } from '../tx/index.js'; +import { TreeSnapshots, type Tx } from '../tx/index.js'; import { AvmCircuitPublicInputs } from './avm_circuit_public_inputs.js'; import { serializeWithMessagePack } from './message_pack.js'; @@ -263,111 +263,87 @@ export class AvmSequentialInsertHintPublicDataTree extends AvmSequentialInsertHi export class AvmSequentialInsertHintNullifierTree extends AvmSequentialInsertHintFactory(NullifierLeafPreimage) {} // Hint for checkpoint actions that don't change the state. -class AvmCheckpointActionNoChangeHint { +class AvmCheckpointActionNoStateChangeHint { constructor( // key + public readonly actionCounter: number, + // current checkpoint evolution public readonly oldCheckpointId: number, - public readonly oldHash: Fr, - // new checkpoint public readonly newCheckpointId: number, ) {} static get schema() { - return z.object({ - oldCheckpointId: z.number().int().nonnegative(), - oldHash: schemas.Fr, - newCheckpointId: z.number().int().nonnegative(), - }); + return z + .object({ + actionCounter: z.number().int().nonnegative(), + oldCheckpointId: z.number().int().nonnegative(), + newCheckpointId: z.number().int().nonnegative(), + }) + .transform( + ({ actionCounter, oldCheckpointId, newCheckpointId }) => + new AvmCheckpointActionNoStateChangeHint(actionCounter, oldCheckpointId, newCheckpointId), + ); } } // Hint for MerkleTreeDB.createCheckpoint. -export class AvmCheckpointActionCreateCheckpointHint extends AvmCheckpointActionNoChangeHint {} +export class AvmCreateCheckpointHint extends AvmCheckpointActionNoStateChangeHint {} // Hint for MerkleTreeDB.commitCheckpoint. -export class AvmCheckpointActionCommitCheckpointHint extends AvmCheckpointActionNoChangeHint {} +export class AvmCommitCheckpointHint extends AvmCheckpointActionNoStateChangeHint {} // Hint for MerkleTreeDB.revertCheckpoint. -export class AvmCheckpointActionRevertCheckpointHint { +export class AvmRevertCheckpointHint { // We use explicit fields for MessagePack. constructor( + // key + public readonly actionCounter: number, + // current checkpoint evolution public readonly oldCheckpointId: number, - public readonly oldHash: Fr, public readonly newCheckpointId: number, - public readonly beforePublicDataTree: AppendOnlyTreeSnapshot, - public readonly beforeNullifierTree: AppendOnlyTreeSnapshot, - public readonly beforeNoteHashTree: AppendOnlyTreeSnapshot, - public readonly beforeL1ToL2MessageTree: AppendOnlyTreeSnapshot, - public readonly afterPublicDataTree: AppendOnlyTreeSnapshot, - public readonly afterNullifierTree: AppendOnlyTreeSnapshot, - public readonly afterNoteHashTree: AppendOnlyTreeSnapshot, - public readonly afterL1ToL2MessageTree: AppendOnlyTreeSnapshot, + // state evolution + public readonly stateBefore: TreeSnapshots, + public readonly stateAfter: TreeSnapshots, ) {} static create( + actionCounter: number, oldCheckpointId: number, - oldHash: Fr, - stateBefore: Record, newCheckpointId: number, + stateBefore: Record, stateAfter: Record, - ): AvmCheckpointActionRevertCheckpointHint { - return new AvmCheckpointActionRevertCheckpointHint( + ): AvmRevertCheckpointHint { + return new AvmRevertCheckpointHint( + actionCounter, oldCheckpointId, - oldHash, newCheckpointId, - stateBefore[MerkleTreeId.PUBLIC_DATA_TREE], - stateBefore[MerkleTreeId.NULLIFIER_TREE], - stateBefore[MerkleTreeId.NOTE_HASH_TREE], - stateBefore[MerkleTreeId.L1_TO_L2_MESSAGE_TREE], - stateAfter[MerkleTreeId.PUBLIC_DATA_TREE], - stateAfter[MerkleTreeId.NULLIFIER_TREE], - stateAfter[MerkleTreeId.NOTE_HASH_TREE], - stateAfter[MerkleTreeId.L1_TO_L2_MESSAGE_TREE], + new TreeSnapshots( + stateBefore[MerkleTreeId.L1_TO_L2_MESSAGE_TREE], + stateBefore[MerkleTreeId.NOTE_HASH_TREE], + stateBefore[MerkleTreeId.NULLIFIER_TREE], + stateBefore[MerkleTreeId.PUBLIC_DATA_TREE], + ), + new TreeSnapshots( + stateAfter[MerkleTreeId.L1_TO_L2_MESSAGE_TREE], + stateAfter[MerkleTreeId.NOTE_HASH_TREE], + stateAfter[MerkleTreeId.NULLIFIER_TREE], + stateAfter[MerkleTreeId.PUBLIC_DATA_TREE], + ), ); } static get schema() { return z .object({ + actionCounter: z.number().int().nonnegative(), oldCheckpointId: z.number().int().nonnegative(), - oldHash: schemas.Fr, newCheckpointId: z.number().int().nonnegative(), - beforePublicDataTree: AppendOnlyTreeSnapshot.schema, - beforeNullifierTree: AppendOnlyTreeSnapshot.schema, - beforeNoteHashTree: AppendOnlyTreeSnapshot.schema, - beforeL1ToL2MessageTree: AppendOnlyTreeSnapshot.schema, - afterPublicDataTree: AppendOnlyTreeSnapshot.schema, - afterNullifierTree: AppendOnlyTreeSnapshot.schema, - afterNoteHashTree: AppendOnlyTreeSnapshot.schema, - afterL1ToL2MessageTree: AppendOnlyTreeSnapshot.schema, + stateBefore: TreeSnapshots.schema, + stateAfter: TreeSnapshots.schema, }) .transform( - ({ - oldCheckpointId, - oldHash, - newCheckpointId, - beforePublicDataTree, - beforeNullifierTree, - beforeNoteHashTree, - beforeL1ToL2MessageTree, - afterPublicDataTree, - afterNullifierTree, - afterNoteHashTree, - afterL1ToL2MessageTree, - }) => - new AvmCheckpointActionRevertCheckpointHint( - oldCheckpointId, - oldHash, - newCheckpointId, - beforePublicDataTree, - beforeNullifierTree, - beforeNoteHashTree, - beforeL1ToL2MessageTree, - afterPublicDataTree, - afterNullifierTree, - afterNoteHashTree, - afterL1ToL2MessageTree, - ), + ({ actionCounter, oldCheckpointId, newCheckpointId, stateBefore, stateAfter }) => + new AvmRevertCheckpointHint(actionCounter, oldCheckpointId, newCheckpointId, stateBefore, stateAfter), ); } } @@ -496,9 +472,9 @@ export class AvmExecutionHints { public readonly getLeafValueHints: AvmGetLeafValueHint[] = [], public readonly sequentialInsertHintsPublicDataTree: AvmSequentialInsertHintPublicDataTree[] = [], public readonly sequentialInsertHintsNullifierTree: AvmSequentialInsertHintNullifierTree[] = [], - public readonly createCheckpointHints: AvmCheckpointActionCreateCheckpointHint[] = [], - public readonly commitCheckpointHints: AvmCheckpointActionCommitCheckpointHint[] = [], - public readonly revertCheckpointHints: AvmCheckpointActionRevertCheckpointHint[] = [], + public readonly createCheckpointHints: AvmCreateCheckpointHint[] = [], + public readonly commitCheckpointHints: AvmCommitCheckpointHint[] = [], + public readonly revertCheckpointHints: AvmRevertCheckpointHint[] = [], ) {} static empty() { @@ -519,9 +495,9 @@ export class AvmExecutionHints { getLeafValueHints: AvmGetLeafValueHint.schema.array(), sequentialInsertHintsPublicDataTree: AvmSequentialInsertHintPublicDataTree.schema.array(), sequentialInsertHintsNullifierTree: AvmSequentialInsertHintNullifierTree.schema.array(), - createCheckpointHints: AvmCheckpointActionCreateCheckpointHint.schema.array(), - commitCheckpointHints: AvmCheckpointActionCommitCheckpointHint.schema.array(), - revertCheckpointHints: AvmCheckpointActionRevertCheckpointHint.schema.array(), + createCheckpointHints: AvmCreateCheckpointHint.schema.array(), + commitCheckpointHints: AvmCommitCheckpointHint.schema.array(), + revertCheckpointHints: AvmRevertCheckpointHint.schema.array(), }) .transform( ({ diff --git a/yarn-project/stdlib/src/tests/factories.ts b/yarn-project/stdlib/src/tests/factories.ts index 318ec658a619..d154bcdc4595 100644 --- a/yarn-project/stdlib/src/tests/factories.ts +++ b/yarn-project/stdlib/src/tests/factories.ts @@ -56,13 +56,12 @@ import { ContractStorageUpdateRequest } from '../avm/contract_storage_update_req import { AvmAccumulatedData, AvmBytecodeCommitmentHint, - AvmCheckpointActionCommitCheckpointHint, - AvmCheckpointActionCreateCheckpointHint, - AvmCheckpointActionRevertCheckpointHint, AvmCircuitInputs, AvmCircuitPublicInputs, + AvmCommitCheckpointHint, AvmContractClassHint, AvmContractInstanceHint, + AvmCreateCheckpointHint, AvmEnqueuedCallHint, AvmExecutionHints, AvmGetLeafPreimageHintNullifierTree, @@ -70,6 +69,7 @@ import { AvmGetLeafValueHint, AvmGetPreviousValueIndexHint, AvmGetSiblingPathHint, + AvmRevertCheckpointHint, AvmSequentialInsertHintNullifierTree, AvmSequentialInsertHintPublicDataTree, AvmTxHint, @@ -1385,27 +1385,29 @@ export function makeAvmSequentialInsertHintNullifierTree(seed = 0): AvmSequentia ); } -export function makeAvmCheckpointActionCreateCheckpointHint(seed = 0) { - return new AvmCheckpointActionCreateCheckpointHint(seed, new Fr(seed + 1), seed + 2); +export function makeAvmCheckpointActionCreateCheckpointHint(seed = 0): AvmCreateCheckpointHint { + return new AvmCreateCheckpointHint( + /*actionCounter=*/ seed, + /*oldCheckpointId=*/ seed + 1, + /*newCheckpointId=*/ seed + 2, + ); } -export function makeAvmCheckpointActionCommitCheckpointHint(seed = 0) { - return new AvmCheckpointActionCommitCheckpointHint(seed, new Fr(seed + 1), seed + 2); +export function makeAvmCheckpointActionCommitCheckpointHint(seed = 0): AvmCommitCheckpointHint { + return new AvmCommitCheckpointHint( + /*actionCounter=*/ seed, + /*oldCheckpointId=*/ seed + 1, + /*newCheckpointId=*/ seed + 2, + ); } -export function makeAvmCheckpointActionRevertCheckpointHint(seed = 0) { - return new AvmCheckpointActionRevertCheckpointHint( - seed, - new Fr(seed + 1), - seed + 2, - makeAppendOnlyTreeSnapshot(seed + 3), - makeAppendOnlyTreeSnapshot(seed + 4), - makeAppendOnlyTreeSnapshot(seed + 5), - makeAppendOnlyTreeSnapshot(seed + 6), - makeAppendOnlyTreeSnapshot(seed + 7), - makeAppendOnlyTreeSnapshot(seed + 8), - makeAppendOnlyTreeSnapshot(seed + 9), - makeAppendOnlyTreeSnapshot(seed + 10), +export function makeAvmCheckpointActionRevertCheckpointHint(seed = 0): AvmRevertCheckpointHint { + return new AvmRevertCheckpointHint( + /*actionCounter=*/ seed, + /*oldCheckpointId=*/ seed + 1, + /*newCheckpointId=*/ seed + 2, + /*beforeState=*/ makeTreeSnapshots(seed + 3), + /*afterState=*/ makeTreeSnapshots(seed + 7), ); }