@aztec/simulator 0.84.0 → 0.85.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/dest/common/debug_fn_name.d.ts +1 -1
  2. package/dest/common/debug_fn_name.d.ts.map +1 -1
  3. package/dest/common/index.d.ts +0 -1
  4. package/dest/common/index.d.ts.map +1 -1
  5. package/dest/common/index.js +0 -1
  6. package/dest/private/acvm/oracle/oracle.d.ts +1 -1
  7. package/dest/private/acvm/oracle/oracle.d.ts.map +1 -1
  8. package/dest/private/acvm/oracle/oracle.js +3 -3
  9. package/dest/private/acvm/oracle/typed_oracle.d.ts +3 -3
  10. package/dest/private/acvm/oracle/typed_oracle.d.ts.map +1 -1
  11. package/dest/private/acvm/oracle/typed_oracle.js +1 -1
  12. package/dest/private/execution_data_provider.d.ts +39 -5
  13. package/dest/private/execution_data_provider.d.ts.map +1 -1
  14. package/dest/private/index.d.ts +1 -0
  15. package/dest/private/index.d.ts.map +1 -1
  16. package/dest/private/index.js +1 -0
  17. package/dest/private/message_load_oracle_inputs.d.ts.map +1 -0
  18. package/dest/private/simulator.js +1 -1
  19. package/dest/private/utility_execution_oracle.d.ts +3 -3
  20. package/dest/private/utility_execution_oracle.d.ts.map +1 -1
  21. package/dest/private/utility_execution_oracle.js +2 -6
  22. package/dest/public/avm/fixtures/base_avm_simulation_tester.js +4 -4
  23. package/dest/public/avm/test_utils.d.ts +1 -1
  24. package/dest/public/avm/test_utils.d.ts.map +1 -1
  25. package/dest/public/avm/test_utils.js +2 -2
  26. package/dest/{common → public}/db_interfaces.d.ts +0 -38
  27. package/dest/public/db_interfaces.d.ts.map +1 -0
  28. package/dest/public/db_interfaces.js +3 -0
  29. package/dest/public/hinting_db_sources.d.ts +11 -1
  30. package/dest/public/hinting_db_sources.d.ts.map +1 -1
  31. package/dest/public/hinting_db_sources.js +80 -13
  32. package/dest/public/index.d.ts +1 -1
  33. package/dest/public/index.d.ts.map +1 -1
  34. package/dest/public/index.js +1 -1
  35. package/dest/public/public_db_sources.d.ts +3 -3
  36. package/dest/public/public_db_sources.d.ts.map +1 -1
  37. package/dest/public/public_db_sources.js +27 -9
  38. package/dest/public/public_processor/public_processor.d.ts +1 -1
  39. package/dest/public/public_processor/public_processor.d.ts.map +1 -1
  40. package/dest/public/public_processor/public_processor.js +3 -3
  41. package/dest/public/public_tx_simulator/public_tx_context.d.ts.map +1 -1
  42. package/dest/public/public_tx_simulator/public_tx_context.js +6 -4
  43. package/dest/public/public_tx_simulator/public_tx_simulator.d.ts.map +1 -1
  44. package/dest/public/public_tx_simulator/public_tx_simulator.js +1 -1
  45. package/dest/public/state_manager/nullifiers.js +2 -2
  46. package/dest/public/state_manager/public_storage.d.ts +1 -1
  47. package/dest/public/state_manager/public_storage.d.ts.map +1 -1
  48. package/dest/public/state_manager/state_manager.js +4 -4
  49. package/package.json +14 -18
  50. package/src/common/debug_fn_name.ts +1 -1
  51. package/src/common/index.ts +0 -1
  52. package/src/private/acvm/oracle/oracle.ts +6 -4
  53. package/src/private/acvm/oracle/typed_oracle.ts +3 -2
  54. package/src/private/execution_data_provider.ts +49 -5
  55. package/src/private/index.ts +1 -0
  56. package/src/private/simulator.ts +2 -2
  57. package/src/private/utility_execution_oracle.ts +5 -7
  58. package/src/public/avm/fixtures/base_avm_simulation_tester.ts +2 -2
  59. package/src/public/avm/test_utils.ts +2 -2
  60. package/src/{common → public}/db_interfaces.ts +0 -48
  61. package/src/public/hinting_db_sources.ts +121 -19
  62. package/src/public/index.ts +1 -1
  63. package/src/public/public_db_sources.ts +36 -8
  64. package/src/public/public_processor/public_processor.ts +1 -3
  65. package/src/public/public_tx_simulator/public_tx_context.ts +10 -10
  66. package/src/public/public_tx_simulator/public_tx_simulator.ts +1 -2
  67. package/src/public/state_manager/nullifiers.ts +2 -2
  68. package/src/public/state_manager/public_storage.ts +1 -1
  69. package/src/public/state_manager/state_manager.ts +4 -4
  70. package/dest/common/db_interfaces.d.ts.map +0 -1
  71. package/dest/common/db_interfaces.js +0 -1
  72. package/dest/common/message_load_oracle_inputs.d.ts.map +0 -1
  73. /package/dest/{common → private}/message_load_oracle_inputs.d.ts +0 -0
  74. /package/dest/{common → private}/message_load_oracle_inputs.js +0 -0
  75. /package/src/{common → private}/message_load_oracle_inputs.ts +0 -0
@@ -1,3 +1,4 @@
1
+ import type { L1_TO_L2_MSG_TREE_HEIGHT } from '@aztec/constants';
1
2
  import type { Fr, Point } from '@aztec/foundation/fields';
2
3
  import type {
3
4
  EventSelector,
@@ -14,8 +15,8 @@ import type { NoteStatus } from '@aztec/stdlib/note';
14
15
  import { type MerkleTreeId, type NullifierMembershipWitness, PublicDataWitness } from '@aztec/stdlib/trees';
15
16
  import type { BlockHeader, TxHash } from '@aztec/stdlib/tx';
16
17
 
17
- import type { CommitmentsDBInterface } from '../common/db_interfaces.js';
18
18
  import type { NoteData } from './acvm/index.js';
19
+ import type { MessageLoadOracleInputs } from './message_load_oracle_inputs.js';
19
20
 
20
21
  /**
21
22
  * Error thrown when a contract is not found in the database.
@@ -38,7 +39,7 @@ export class ContractClassNotFoundError extends Error {
38
39
  /**
39
40
  * The interface for the data layer required to perform private and utility execution.
40
41
  */
41
- export interface ExecutionDataProvider extends CommitmentsDBInterface {
42
+ export interface ExecutionDataProvider {
42
43
  /**
43
44
  * Returns a contract instance associated with an address, if available.
44
45
  * @param address - Address.
@@ -117,6 +118,40 @@ export interface ExecutionDataProvider extends CommitmentsDBInterface {
117
118
  */
118
119
  getNullifierIndex(nullifier: Fr): Promise<bigint | undefined>;
119
120
 
121
+ /**
122
+ * Gets the index of a nullifier in the nullifier tree.
123
+ * @param nullifier - The nullifier.
124
+ * @returns - The index of the nullifier. Undefined if it does not exist in the tree.
125
+ */
126
+ getNullifierIndex(nullifier: Fr): Promise<bigint | undefined>;
127
+
128
+ /**
129
+ * Returns a nullifier membership witness for the given nullifier or undefined if not found.
130
+ * REFACTOR: Same as getL1ToL2MembershipWitness, can be combined with aztec-node method that does almost the same thing.
131
+ * @param nullifier - Nullifier we're looking for.
132
+ */
133
+ getNullifierMembershipWitnessAtLatestBlock(nullifier: Fr): Promise<NullifierMembershipWitness | undefined>;
134
+
135
+ /**
136
+ * Fetches a message from the db, given its key.
137
+ * @param contractAddress - Address of a contract by which the message was emitted.
138
+ * @param messageHash - Hash of the message.
139
+ * @param secret - Secret used to compute a nullifier.
140
+ * @dev Contract address and secret are only used to compute the nullifier to get non-nullified messages
141
+ * @returns The l1 to l2 membership witness (index of message in the tree and sibling path).
142
+ */
143
+ getL1ToL2MembershipWitness(
144
+ contractAddress: AztecAddress,
145
+ messageHash: Fr,
146
+ secret: Fr,
147
+ ): Promise<MessageLoadOracleInputs<typeof L1_TO_L2_MSG_TREE_HEIGHT>>;
148
+
149
+ /**
150
+ * @param leafIndex the leaf to look up
151
+ * @returns The l1 to l2 leaf message hash or undefined if not found.
152
+ */
153
+ getL1ToL2MessageHash(leafIndex: bigint): Promise<Fr | undefined>;
154
+
120
155
  /**
121
156
  * Retrieve the databases view of the Block Header object.
122
157
  * This structure is fed into the circuits simulator and is used to prove against certain historical roots.
@@ -259,10 +294,17 @@ export interface ExecutionDataProvider extends CommitmentsDBInterface {
259
294
  content: Fr[],
260
295
  noteHash: Fr,
261
296
  nullifier: Fr,
262
- txHash: Fr,
297
+ txHash: TxHash,
263
298
  recipient: AztecAddress,
264
299
  ): Promise<void>;
265
300
 
301
+ /**
302
+ * Gets note hash in the note hash tree at the given leaf index.
303
+ * @param leafIndex - the leaf to look up.
304
+ * @returns - The note hash at that index. Undefined if leaf index is not found.
305
+ */
306
+ getNoteHash(leafIndex: bigint): Promise<Fr | undefined>;
307
+
266
308
  /**
267
309
  * Searches for a log with the corresponding `tag` and returns it along with contextual transaction information.
268
310
  * Returns null if no such log exists, and throws if more than one exists.
@@ -329,16 +371,18 @@ export interface ExecutionDataProvider extends CommitmentsDBInterface {
329
371
  * @param contractAddress - The address of the contract that emitted the log.
330
372
  * @param recipient - The address of the recipient.
331
373
  * @param eventSelector - The event selector of the event.
332
- * @param logContent - The content of the private event log.
374
+ * @param msgContent - The content of the private event message.
333
375
  * @param txHash - The hash of the transaction that emitted the log.
334
376
  * @param logIndexInTx - The index of the log within the transaction.
377
+ * @param txIndexInBlock - The index of the transaction in which the log was emitted in the block.
335
378
  */
336
379
  storePrivateEventLog(
337
380
  contractAddress: AztecAddress,
338
381
  recipient: AztecAddress,
339
382
  eventSelector: EventSelector,
340
- logContent: Fr[],
383
+ msgContent: Fr[],
341
384
  txHash: TxHash,
342
385
  logIndexInTx: number,
386
+ txIndexInBlock: number,
343
387
  ): Promise<void>;
344
388
  }
@@ -13,3 +13,4 @@ export { extractCallStack } from './acvm/acvm.js';
13
13
  export { type NoteData, TypedOracle } from './acvm/oracle/typed_oracle.js';
14
14
  export { Oracle } from './acvm/oracle/oracle.js';
15
15
  export { HashedValuesCache } from './hashed_values_cache.js';
16
+ export { MessageLoadOracleInputs } from './message_load_oracle_inputs.js';
@@ -115,7 +115,7 @@ export class AcirSimulator {
115
115
  }
116
116
  }
117
117
 
118
- // docs:start:execute_unconstrained_function
118
+ // docs:start:execute_utility_function
119
119
  /**
120
120
  * Runs a utility function.
121
121
  * @param call - The function call to execute.
@@ -163,5 +163,5 @@ export class AcirSimulator {
163
163
  throw createSimulationError(err instanceof Error ? err : new Error('Unknown error during private execution'));
164
164
  }
165
165
  }
166
- // docs:end:execute_unconstrained_function
166
+ // docs:end:execute_utility_function
167
167
  }
@@ -256,10 +256,6 @@ export class UtilityExecutionOracle extends TypedOracle {
256
256
  }
257
257
 
258
258
  public override debugLog(message: string, fields: Fr[]): void {
259
- // TODO(#10558) Remove this check once the debug log is fixed
260
- if (message.startsWith('Context.note_hashes, after pushing new note hash:')) {
261
- return;
262
- }
263
259
  this.log.verbose(`${applyStringFormatting(message, fields)}`, { module: `${this.log.module}:debug_log` });
264
260
  }
265
261
 
@@ -291,7 +287,7 @@ export class UtilityExecutionOracle extends TypedOracle {
291
287
  content: Fr[],
292
288
  noteHash: Fr,
293
289
  nullifier: Fr,
294
- txHash: Fr,
290
+ txHash: TxHash,
295
291
  recipient: AztecAddress,
296
292
  ) {
297
293
  // TODO(#10727): allow other contracts to deliver notes
@@ -370,17 +366,19 @@ export class UtilityExecutionOracle extends TypedOracle {
370
366
  contractAddress: AztecAddress,
371
367
  recipient: AztecAddress,
372
368
  eventSelector: EventSelector,
373
- logContent: Fr[],
369
+ msgContent: Fr[],
374
370
  txHash: TxHash,
375
371
  logIndexInTx: number,
372
+ txIndexInBlock: number,
376
373
  ): Promise<void> {
377
374
  return this.executionDataProvider.storePrivateEventLog(
378
375
  contractAddress,
379
376
  recipient,
380
377
  eventSelector,
381
- logContent,
378
+ msgContent,
382
379
  txHash,
383
380
  logIndexInTx,
381
+ txIndexInBlock,
384
382
  );
385
383
  }
386
384
  }
@@ -44,7 +44,7 @@ export abstract class BaseAvmSimulationTester {
44
44
  const leafSlot = await computePublicDataTreeLeafSlot(address, slot);
45
45
  // get existing preimage
46
46
  const publicDataWrite = new PublicDataWrite(leafSlot, value);
47
- await this.merkleTrees.batchInsert(MerkleTreeId.PUBLIC_DATA_TREE, [publicDataWrite.toBuffer()], 0);
47
+ await this.merkleTrees.sequentialInsert(MerkleTreeId.PUBLIC_DATA_TREE, [publicDataWrite.toBuffer()]);
48
48
  }
49
49
 
50
50
  /**
@@ -98,6 +98,6 @@ export abstract class BaseAvmSimulationTester {
98
98
  AztecAddress.fromNumber(DEPLOYER_CONTRACT_ADDRESS),
99
99
  contractAddress.toField(),
100
100
  );
101
- await this.merkleTrees.batchInsert(MerkleTreeId.NULLIFIER_TREE, [contractAddressNullifier.toBuffer()], 0);
101
+ await this.merkleTrees.sequentialInsert(MerkleTreeId.NULLIFIER_TREE, [contractAddressNullifier.toBuffer()]);
102
102
  }
103
103
  }
@@ -38,8 +38,8 @@ export function mockNoteHashExists(worldStateDB: PublicTreesDB, _leafIndex: Fr,
38
38
  });
39
39
  }
40
40
 
41
- export function mockGetNullifierIndex(worldStateDB: PublicTreesDB, leafIndex: Fr, _ignoredValue?: Fr) {
42
- (worldStateDB as jest.Mocked<PublicTreesDB>).getNullifierIndex.mockResolvedValue(leafIndex.toBigInt());
41
+ export function mockCheckNullifierExists(worldStateDB: PublicTreesDB, exists: boolean, _ignoredValue?: Fr) {
42
+ (worldStateDB as jest.Mocked<PublicTreesDB>).checkNullifierExists.mockResolvedValue(exists);
43
43
  }
44
44
 
45
45
  export function mockL1ToL2MessageExists(
@@ -1,11 +1,7 @@
1
- import type { L1_TO_L2_MSG_TREE_HEIGHT } from '@aztec/constants';
2
1
  import type { Fr } from '@aztec/foundation/fields';
3
2
  import type { FunctionSelector } from '@aztec/stdlib/abi';
4
3
  import type { AztecAddress } from '@aztec/stdlib/aztec-address';
5
4
  import type { ContractClassPublic, ContractInstanceWithAddress } from '@aztec/stdlib/contract';
6
- import type { NullifierMembershipWitness } from '@aztec/stdlib/trees';
7
-
8
- import type { MessageLoadOracleInputs } from './message_load_oracle_inputs.js';
9
5
 
10
6
  /**
11
7
  * Database interface for providing access to public state.
@@ -62,47 +58,3 @@ export interface PublicContractsDBInterface {
62
58
  */
63
59
  getDebugFunctionName(contractAddress: AztecAddress, selector: FunctionSelector): Promise<string | undefined>;
64
60
  }
65
-
66
- /** Database interface for providing access to note hash tree, l1 to l2 message tree, and nullifier tree. */
67
- export interface CommitmentsDBInterface {
68
- /**
69
- * Fetches a message from the db, given its key.
70
- * @param contractAddress - Address of a contract by which the message was emitted.
71
- * @param messageHash - Hash of the message.
72
- * @param secret - Secret used to compute a nullifier.
73
- * @dev Contract address and secret are only used to compute the nullifier to get non-nullified messages
74
- * @returns The l1 to l2 membership witness (index of message in the tree and sibling path).
75
- */
76
- getL1ToL2MembershipWitness(
77
- contractAddress: AztecAddress,
78
- messageHash: Fr,
79
- secret: Fr,
80
- ): Promise<MessageLoadOracleInputs<typeof L1_TO_L2_MSG_TREE_HEIGHT>>;
81
-
82
- /**
83
- * @param leafIndex the leaf to look up
84
- * @returns The l1 to l2 leaf message hash or undefined if not found.
85
- */
86
- getL1ToL2MessageHash(leafIndex: bigint): Promise<Fr | undefined>;
87
-
88
- /**
89
- * Gets note hash in the note hash tree at the given leaf index.
90
- * @param leafIndex - the leaf to look up.
91
- * @returns - The note hash at that index. Undefined if leaf index is not found.
92
- */
93
- getNoteHash(leafIndex: bigint): Promise<Fr | undefined>;
94
-
95
- /**
96
- * Gets the index of a nullifier in the nullifier tree.
97
- * @param nullifier - The nullifier.
98
- * @returns - The index of the nullifier. Undefined if it does not exist in the tree.
99
- */
100
- getNullifierIndex(nullifier: Fr): Promise<bigint | undefined>;
101
-
102
- /**
103
- * Returns a nullifier membership witness for the given nullifier or undefined if not found.
104
- * REFACTOR: Same as getL1ToL2MembershipWitness, can be combined with aztec-node method that does almost the same thing.
105
- * @param nullifier - Nullifier we're looking for.
106
- */
107
- getNullifierMembershipWitnessAtLatestBlock(nullifier: Fr): Promise<NullifierMembershipWitness | undefined>;
108
- }
@@ -1,17 +1,22 @@
1
+ import { sha256Trunc } from '@aztec/foundation/crypto';
1
2
  import { Fr } from '@aztec/foundation/fields';
2
3
  import { type Logger, createLogger } from '@aztec/foundation/log';
3
4
  import type { IndexedTreeLeafPreimage, SiblingPath } from '@aztec/foundation/trees';
4
5
  import type { FunctionSelector } from '@aztec/stdlib/abi';
5
6
  import {
7
+ AvmAppendLeavesHint,
6
8
  AvmBytecodeCommitmentHint,
9
+ AvmCommitCheckpointHint,
7
10
  AvmContractClassHint,
8
11
  AvmContractInstanceHint,
12
+ AvmCreateCheckpointHint,
9
13
  type AvmExecutionHints,
10
14
  AvmGetLeafPreimageHintNullifierTree,
11
15
  AvmGetLeafPreimageHintPublicDataTree,
12
16
  AvmGetLeafValueHint,
13
17
  AvmGetPreviousValueIndexHint,
14
18
  AvmGetSiblingPathHint,
19
+ AvmRevertCheckpointHint,
15
20
  AvmSequentialInsertHintNullifierTree,
16
21
  AvmSequentialInsertHintPublicDataTree,
17
22
  } from '@aztec/stdlib/avm';
@@ -28,11 +33,12 @@ import {
28
33
  PublicDataTreeLeafPreimage,
29
34
  type SequentialInsertionResult,
30
35
  getTreeName,
36
+ merkleTreeIds,
31
37
  } from '@aztec/stdlib/trees';
32
38
 
33
39
  import { strict as assert } from 'assert';
34
40
 
35
- import type { PublicContractsDBInterface } from '../common/db_interfaces.js';
41
+ import type { PublicContractsDBInterface } from './db_interfaces.js';
36
42
  import { PublicTreesDB } from './public_db_sources.js';
37
43
 
38
44
  /**
@@ -99,6 +105,13 @@ export class HintingPublicContractsDB implements PublicContractsDBInterface {
99
105
  */
100
106
  export class HintingPublicTreesDB extends PublicTreesDB {
101
107
  private static readonly log: Logger = createLogger('HintingPublicTreesDB');
108
+ // This stack is only for debugging purposes.
109
+ // The top of the stack is the current checkpoint id.
110
+ // We need the stack to be non-empty and use 0 as an arbitrary initial checkpoint id.
111
+ // This is not necessarily a checkpoint that happened, but whatever tree state we start with.
112
+ private checkpointStack: number[] = [0];
113
+ private nextCheckpointId: number = 1;
114
+ private checkpointActionCounter: number = 0; // yes, a side-effect counter.
102
115
 
103
116
  constructor(db: PublicTreesDB, private hints: AvmExecutionHints) {
104
117
  super(db);
@@ -200,11 +213,8 @@ export class HintingPublicTreesDB extends PublicTreesDB {
200
213
  const result = await super.sequentialInsert<TreeHeight, ID>(treeId, leaves);
201
214
 
202
215
  const afterState = await this.getHintKey(treeId);
203
- HintingPublicTreesDB.log.debug(
204
- `Evolved tree state (${getTreeName(treeId)}): ${beforeState.root}, ${beforeState.nextAvailableLeafIndex} -> ${
205
- afterState.root
206
- }, ${afterState.nextAvailableLeafIndex}.`,
207
- );
216
+ HintingPublicTreesDB.log.debug('[sequentialInsert] Evolved tree state.');
217
+ HintingPublicTreesDB.logTreeChange(beforeState, afterState, treeId);
208
218
 
209
219
  switch (treeId) {
210
220
  case MerkleTreeId.PUBLIC_DATA_TREE:
@@ -255,12 +265,58 @@ export class HintingPublicTreesDB extends PublicTreesDB {
255
265
  return result;
256
266
  }
257
267
 
268
+ public override async appendLeaves<ID extends MerkleTreeId>(
269
+ treeId: ID,
270
+ leaves: MerkleTreeLeafType<ID>[],
271
+ ): Promise<void> {
272
+ // Use sequentialInsert for PublicDataTree and NullifierTree.
273
+ assert(treeId == MerkleTreeId.NOTE_HASH_TREE || treeId == MerkleTreeId.L1_TO_L2_MESSAGE_TREE);
274
+
275
+ // We need to process each leaf individually because we need the sibling path after insertion, to be able to constraint the insertion.
276
+ // TODO(https://github.com/AztecProtocol/aztec-packages/issues/13380): This can be changed if the world state appendLeaves returns the sibling paths.
277
+ for (const leaf of leaves) {
278
+ await this.appendLeafInternal(treeId, leaf);
279
+ }
280
+ }
281
+
282
+ public override async createCheckpoint(): Promise<void> {
283
+ const actionCounter = this.checkpointActionCounter++;
284
+ const oldCheckpointId = this.getCurrentCheckpointId();
285
+ const treesStateHash = await this.getTreesStateHash();
286
+
287
+ await super.createCheckpoint();
288
+ this.checkpointStack.push(this.nextCheckpointId++);
289
+ const newCheckpointId = this.getCurrentCheckpointId();
290
+
291
+ this.hints.createCheckpointHints.push(new AvmCreateCheckpointHint(actionCounter, oldCheckpointId, newCheckpointId));
292
+
293
+ HintingPublicTreesDB.log.debug(
294
+ `[createCheckpoint:${actionCounter}] Checkpoint evolved ${oldCheckpointId} -> ${newCheckpointId} at trees state ${treesStateHash}.`,
295
+ );
296
+ }
297
+
298
+ public override async commitCheckpoint(): Promise<void> {
299
+ const actionCounter = this.checkpointActionCounter++;
300
+ const oldCheckpointId = this.getCurrentCheckpointId();
301
+ const treesStateHash = await this.getTreesStateHash();
302
+
303
+ await super.commitCheckpoint();
304
+ this.checkpointStack.pop();
305
+ const newCheckpointId = this.getCurrentCheckpointId();
306
+
307
+ this.hints.commitCheckpointHints.push(new AvmCommitCheckpointHint(actionCounter, oldCheckpointId, newCheckpointId));
308
+
309
+ HintingPublicTreesDB.log.debug(
310
+ `[commitCheckpoint:${actionCounter}] Checkpoint evolved ${oldCheckpointId} -> ${newCheckpointId} at trees state ${treesStateHash}.`,
311
+ );
312
+ }
313
+
258
314
  public override async revertCheckpoint(): Promise<void> {
259
- HintingPublicTreesDB.log.debug('revertCheckpoint not hinted yet!');
260
- // TODO(fcarreiro): we probably want to hint on StateReference hash.
261
- // WARNING: is this enough? we might actually need the number of the checkpoint or similar...
262
- // We will need to keep a stack of checkpoints on the C++ side.
263
- const beforeState = {
315
+ const actionCounter = this.checkpointActionCounter++;
316
+ const oldCheckpointId = this.getCurrentCheckpointId();
317
+ const treesStateHash = await this.getTreesStateHash();
318
+
319
+ const beforeState: Record<MerkleTreeId, AppendOnlyTreeSnapshot> = {
264
320
  [MerkleTreeId.PUBLIC_DATA_TREE]: await this.getHintKey(MerkleTreeId.PUBLIC_DATA_TREE),
265
321
  [MerkleTreeId.NULLIFIER_TREE]: await this.getHintKey(MerkleTreeId.NULLIFIER_TREE),
266
322
  [MerkleTreeId.NOTE_HASH_TREE]: await this.getHintKey(MerkleTreeId.NOTE_HASH_TREE),
@@ -269,8 +325,10 @@ export class HintingPublicTreesDB extends PublicTreesDB {
269
325
  };
270
326
 
271
327
  await super.revertCheckpoint();
328
+ this.checkpointStack.pop();
329
+ const newCheckpointId = this.getCurrentCheckpointId();
272
330
 
273
- const afterState = {
331
+ const afterState: Record<MerkleTreeId, AppendOnlyTreeSnapshot> = {
274
332
  [MerkleTreeId.PUBLIC_DATA_TREE]: await this.getHintKey(MerkleTreeId.PUBLIC_DATA_TREE),
275
333
  [MerkleTreeId.NULLIFIER_TREE]: await this.getHintKey(MerkleTreeId.NULLIFIER_TREE),
276
334
  [MerkleTreeId.NOTE_HASH_TREE]: await this.getHintKey(MerkleTreeId.NOTE_HASH_TREE),
@@ -278,13 +336,15 @@ export class HintingPublicTreesDB extends PublicTreesDB {
278
336
  [MerkleTreeId.ARCHIVE]: await this.getHintKey(MerkleTreeId.ARCHIVE),
279
337
  };
280
338
 
281
- HintingPublicTreesDB.log.debug('Evolved tree state:');
282
- for (const treeId of Object.keys(beforeState)) {
283
- const id: MerkleTreeId = treeId as unknown as MerkleTreeId;
284
- const treeName = getTreeName(id);
285
- HintingPublicTreesDB.log.debug(
286
- `${treeName}: ${beforeState[id].root}, ${beforeState[id].nextAvailableLeafIndex} -> ${afterState[id].root}, ${afterState[id].nextAvailableLeafIndex}.`,
287
- );
339
+ this.hints.revertCheckpointHints.push(
340
+ AvmRevertCheckpointHint.create(actionCounter, oldCheckpointId, newCheckpointId, beforeState, afterState),
341
+ );
342
+
343
+ HintingPublicTreesDB.log.debug(
344
+ `[revertCheckpoint:${actionCounter}] Checkpoint evolved ${oldCheckpointId} -> ${newCheckpointId} at trees state ${treesStateHash}.`,
345
+ );
346
+ for (const treeId of merkleTreeIds()) {
347
+ HintingPublicTreesDB.logTreeChange(beforeState[treeId], afterState[treeId], treeId);
288
348
  }
289
349
  }
290
350
 
@@ -293,4 +353,46 @@ export class HintingPublicTreesDB extends PublicTreesDB {
293
353
  const treeInfo = await super.getTreeInfo(treeId);
294
354
  return new AppendOnlyTreeSnapshot(Fr.fromBuffer(treeInfo.root), Number(treeInfo.size));
295
355
  }
356
+
357
+ private getCurrentCheckpointId(): number {
358
+ return this.checkpointStack[this.checkpointStack.length - 1];
359
+ }
360
+
361
+ // For logging/debugging purposes.
362
+ private async getTreesStateHash(): Promise<Fr> {
363
+ const stateReferenceFields = (await super.getStateReference()).toFields();
364
+ return Fr.fromBuffer(sha256Trunc(Buffer.concat(stateReferenceFields.map(field => field.toBuffer()))));
365
+ }
366
+
367
+ private static logTreeChange(
368
+ beforeState: AppendOnlyTreeSnapshot,
369
+ afterState: AppendOnlyTreeSnapshot,
370
+ treeId: MerkleTreeId,
371
+ ) {
372
+ const treeName = getTreeName(treeId);
373
+ HintingPublicTreesDB.log.debug(
374
+ `[${treeName}] Evolved tree state: ${beforeState.root}, ${beforeState.nextAvailableLeafIndex} -> ${afterState.root}, ${afterState.nextAvailableLeafIndex}.`,
375
+ );
376
+ }
377
+
378
+ private async appendLeafInternal<ID extends MerkleTreeId, N extends number>(
379
+ treeId: ID,
380
+ leaf: MerkleTreeLeafType<ID>,
381
+ ): Promise<SiblingPath<N>> {
382
+ // Use sequentialInsert for PublicDataTree and NullifierTree.
383
+ assert(treeId == MerkleTreeId.NOTE_HASH_TREE || treeId == MerkleTreeId.L1_TO_L2_MESSAGE_TREE);
384
+
385
+ const beforeState = await this.getHintKey(treeId);
386
+
387
+ await super.appendLeaves<ID>(treeId, [leaf]);
388
+
389
+ const afterState = await this.getHintKey(treeId);
390
+
391
+ HintingPublicTreesDB.log.debug('[appendLeaves] Evolved tree state.');
392
+ HintingPublicTreesDB.logTreeChange(beforeState, afterState, treeId);
393
+
394
+ this.hints.appendLeavesHints.push(new AvmAppendLeavesHint(beforeState, afterState, treeId, [leaf as Fr]));
395
+
396
+ return await this.getSiblingPath<N>(treeId, BigInt(beforeState.nextAvailableLeafIndex));
397
+ }
296
398
  }
@@ -1,4 +1,4 @@
1
- export * from '../common/db_interfaces.js';
1
+ export * from './db_interfaces.js';
2
2
  export * from './public_tx_simulator/index.js';
3
3
  export * from './public_db_sources.js';
4
4
  export { PublicProcessor, PublicProcessorFactory } from './public_processor/public_processor.js';
@@ -1,3 +1,4 @@
1
+ import { NULLIFIER_SUBTREE_HEIGHT, PUBLIC_DATA_SUBTREE_HEIGHT } from '@aztec/constants';
1
2
  import { Fr } from '@aztec/foundation/fields';
2
3
  import { createLogger } from '@aztec/foundation/log';
3
4
  import { Timer } from '@aztec/foundation/timer';
@@ -24,10 +25,10 @@ import type {
24
25
  } from '@aztec/stdlib/interfaces/server';
25
26
  import { ContractClassLog, PrivateLog } from '@aztec/stdlib/logs';
26
27
  import type { PublicDBAccessStats } from '@aztec/stdlib/stats';
27
- import { MerkleTreeId, type PublicDataTreeLeafPreimage } from '@aztec/stdlib/trees';
28
+ import { MerkleTreeId, NullifierLeaf, PublicDataTreeLeaf, type PublicDataTreeLeafPreimage } from '@aztec/stdlib/trees';
28
29
  import type { BlockHeader, StateReference, Tx } from '@aztec/stdlib/tx';
29
30
 
30
- import type { PublicContractsDBInterface, PublicStateDBInterface } from '../common/db_interfaces.js';
31
+ import type { PublicContractsDBInterface, PublicStateDBInterface } from './db_interfaces.js';
31
32
  import { TxContractCache } from './tx_contract_cache.js';
32
33
 
33
34
  /**
@@ -392,7 +393,7 @@ class ForwardMerkleTree implements MerkleTreeWriteOperations {
392
393
  export class PublicTreesDB extends ForwardMerkleTree implements PublicStateDBInterface {
393
394
  private logger = createLogger('simulator:public-trees-db');
394
395
 
395
- constructor(private readonly db: MerkleTreeWriteOperations) {
396
+ constructor(db: MerkleTreeWriteOperations) {
396
397
  super(db);
397
398
  }
398
399
 
@@ -462,7 +463,7 @@ export class PublicTreesDB extends ForwardMerkleTree implements PublicStateDBInt
462
463
  return leafValue;
463
464
  }
464
465
 
465
- public async getNullifierIndex(nullifier: Fr): Promise<bigint | undefined> {
466
+ public async checkNullifierExists(nullifier: Fr): Promise<boolean> {
466
467
  const timer = new Timer();
467
468
  const lowLeafResult = await this.getPreviousValueIndex(MerkleTreeId.NULLIFIER_TREE, nullifier.toBigInt());
468
469
  if (!lowLeafResult) {
@@ -472,13 +473,40 @@ export class PublicTreesDB extends ForwardMerkleTree implements PublicStateDBInt
472
473
  await this.getSiblingPath(MerkleTreeId.NULLIFIER_TREE, lowLeafResult.index);
473
474
  // TODO(fcarreiro): We need this for the hints. Might move it to the hinting layer.
474
475
  await this.getLeafPreimage(MerkleTreeId.NULLIFIER_TREE, lowLeafResult.index);
475
- const index = lowLeafResult.alreadyPresent ? lowLeafResult.index : undefined;
476
+ const exists = lowLeafResult.alreadyPresent;
476
477
 
477
- this.logger.debug(`[DB] Fetched nullifier index`, {
478
+ this.logger.debug(`[DB] Checked nullifier exists`, {
478
479
  eventName: 'public-db-access',
479
480
  duration: timer.ms(),
480
- operation: 'get-nullifier-index',
481
+ operation: 'check-nullifier-exists',
481
482
  } satisfies PublicDBAccessStats);
482
- return index;
483
+ return exists;
484
+ }
485
+
486
+ public async padTree(treeId: MerkleTreeId, leavesToInsert: number): Promise<void> {
487
+ switch (treeId) {
488
+ // Indexed trees.
489
+ case MerkleTreeId.NULLIFIER_TREE:
490
+ await this.batchInsert(
491
+ treeId,
492
+ Array(leavesToInsert).fill(NullifierLeaf.empty().toBuffer()),
493
+ NULLIFIER_SUBTREE_HEIGHT,
494
+ );
495
+ break;
496
+ case MerkleTreeId.PUBLIC_DATA_TREE:
497
+ await this.batchInsert(
498
+ treeId,
499
+ Array(leavesToInsert).fill(PublicDataTreeLeaf.empty().toBuffer()),
500
+ PUBLIC_DATA_SUBTREE_HEIGHT,
501
+ );
502
+ break;
503
+ // Non-indexed trees.
504
+ case MerkleTreeId.L1_TO_L2_MESSAGE_TREE:
505
+ case MerkleTreeId.NOTE_HASH_TREE:
506
+ await this.appendLeaves(treeId, Array(leavesToInsert).fill(Fr.ZERO));
507
+ break;
508
+ default:
509
+ throw new Error(`Padding not supported for tree ${treeId}`);
510
+ }
483
511
  }
484
512
  }
@@ -67,7 +67,6 @@ export class PublicProcessorFactory {
67
67
  globalVariables,
68
68
  /*doMerkleOperations=*/ true,
69
69
  skipFeeEnforcement,
70
- this.telemetryClient,
71
70
  );
72
71
 
73
72
  return new PublicProcessor(
@@ -86,7 +85,6 @@ export class PublicProcessorFactory {
86
85
  globalVariables: GlobalVariables,
87
86
  doMerkleOperations: boolean,
88
87
  skipFeeEnforcement: boolean,
89
- telemetryClient: TelemetryClient,
90
88
  ): PublicTxSimulator {
91
89
  return new TelemetryPublicTxSimulator(
92
90
  treesDB,
@@ -94,7 +92,7 @@ export class PublicProcessorFactory {
94
92
  globalVariables,
95
93
  doMerkleOperations,
96
94
  skipFeeEnforcement,
97
- telemetryClient,
95
+ this.telemetryClient,
98
96
  );
99
97
  }
100
98
  }
@@ -4,7 +4,6 @@ import {
4
4
  MAX_NOTE_HASHES_PER_TX,
5
5
  MAX_NULLIFIERS_PER_TX,
6
6
  MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX,
7
- NULLIFIER_SUBTREE_HEIGHT,
8
7
  } from '@aztec/constants';
9
8
  import { padArrayEnd } from '@aztec/foundation/collection';
10
9
  import { Fr } from '@aztec/foundation/fields';
@@ -111,9 +110,16 @@ export class PublicTxContext {
111
110
  const firstNullifier = nonRevertibleAccumulatedDataFromPrivate.nullifiers[0];
112
111
 
113
112
  // We wrap the DB to collect AVM hints.
114
- const hints = new AvmExecutionHints(AvmTxHint.fromTx(tx));
113
+ const hints = new AvmExecutionHints(await AvmTxHint.fromTx(tx));
115
114
  const hintingContractsDB = new HintingPublicContractsDB(contractsDB, hints);
116
115
  const hintingTreesDB = new HintingPublicTreesDB(treesDB, hints);
116
+ const startStateReference = await treesDB.getStateReference();
117
+ hints.startingTreeRoots = new TreeSnapshots(
118
+ startStateReference.l1ToL2MessageTree,
119
+ startStateReference.partial.noteHashTree,
120
+ startStateReference.partial.nullifierTree,
121
+ startStateReference.partial.publicDataTree,
122
+ );
117
123
 
118
124
  // Transaction level state manager that will be forked for revertible phases.
119
125
  const txStateManager = PublicPersistableStateManager.create(
@@ -394,16 +400,10 @@ export class PublicTxContext {
394
400
  );
395
401
  const numNoteHashesToPad =
396
402
  MAX_NOTE_HASHES_PER_TX - countAccumulatedItems(avmCircuitPublicInputs.accumulatedData.noteHashes);
397
- await stateManager
398
- .deprecatedGetTreesForPIGeneration()
399
- .appendLeaves(MerkleTreeId.NOTE_HASH_TREE, padArrayEnd([], Fr.ZERO, numNoteHashesToPad));
403
+ await stateManager.deprecatedGetTreesForPIGeneration().padTree(MerkleTreeId.NOTE_HASH_TREE, numNoteHashesToPad);
400
404
  const numNullifiersToPad =
401
405
  MAX_NULLIFIERS_PER_TX - countAccumulatedItems(avmCircuitPublicInputs.accumulatedData.nullifiers);
402
- await stateManager.deprecatedGetTreesForPIGeneration().batchInsert(
403
- MerkleTreeId.NULLIFIER_TREE,
404
- padArrayEnd([], Fr.ZERO, numNullifiersToPad).map(nullifier => nullifier.toBuffer()),
405
- NULLIFIER_SUBTREE_HEIGHT,
406
- );
406
+ await stateManager.deprecatedGetTreesForPIGeneration().padTree(MerkleTreeId.NULLIFIER_TREE, numNullifiersToPad);
407
407
 
408
408
  const paddedState = await stateManager.deprecatedGetTreesForPIGeneration().getStateReference();
409
409
  avmCircuitPublicInputs.endTreeSnapshots = new TreeSnapshots(
@@ -69,7 +69,6 @@ export class PublicTxSimulator {
69
69
  public async simulate(tx: Tx): Promise<PublicTxResult> {
70
70
  try {
71
71
  const txHash = await this.computeTxHash(tx);
72
-
73
72
  this.log.debug(`Simulating ${tx.publicFunctionCalldata.length} public calls for tx ${txHash}`, { txHash });
74
73
 
75
74
  const context = await PublicTxContext.create(
@@ -437,7 +436,7 @@ export class PublicTxSimulator {
437
436
  ): AvmProvingRequest {
438
437
  return {
439
438
  type: ProvingRequestType.PUBLIC_VM,
440
- inputs: new AvmCircuitInputs('public_dispatch', [], hints, publicInputs),
439
+ inputs: new AvmCircuitInputs(hints, publicInputs),
441
440
  };
442
441
  }
443
442
  }
@@ -64,8 +64,8 @@ export class NullifierManager {
64
64
  // `${MerkleTreeId[MerkleTreeId.NULLIFIER_TREE]} low leaf index should always be found (even if target leaf does not exist)`,
65
65
  //);
66
66
  //existsInTree = leafOrLowLeafIndex.alreadyPresent;
67
- const leafIndex = await this.hostNullifiers.getNullifierIndex(siloedNullifier);
68
- existsInTree = leafIndex !== undefined;
67
+ const exists = await this.hostNullifiers.checkNullifierExists(siloedNullifier);
68
+ existsInTree = exists;
69
69
  }
70
70
  const exists = cacheHit || existsInTree;
71
71
  return Promise.resolve({ exists, cacheHit });
@@ -1,7 +1,7 @@
1
1
  import { Fr } from '@aztec/foundation/fields';
2
2
  import type { AztecAddress } from '@aztec/stdlib/aztec-address';
3
3
 
4
- import type { PublicStateDBInterface } from '../../common/db_interfaces.js';
4
+ import type { PublicStateDBInterface } from '../db_interfaces.js';
5
5
 
6
6
  type PublicStorageReadResult = {
7
7
  value: Fr;