@aztec/simulator 0.66.0 → 0.67.1-devnet

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 (158) hide show
  1. package/dest/acvm/acvm.js +3 -3
  2. package/dest/acvm/oracle/oracle.d.ts +2 -2
  3. package/dest/acvm/oracle/oracle.d.ts.map +1 -1
  4. package/dest/acvm/oracle/oracle.js +5 -5
  5. package/dest/acvm/oracle/typed_oracle.d.ts +3 -3
  6. package/dest/acvm/oracle/typed_oracle.d.ts.map +1 -1
  7. package/dest/acvm/oracle/typed_oracle.js +5 -5
  8. package/dest/acvm/serialize.js +2 -2
  9. package/dest/avm/avm_context.d.ts +2 -2
  10. package/dest/avm/avm_context.d.ts.map +1 -1
  11. package/dest/avm/avm_context.js +3 -4
  12. package/dest/avm/avm_execution_environment.d.ts +4 -6
  13. package/dest/avm/avm_execution_environment.d.ts.map +1 -1
  14. package/dest/avm/avm_execution_environment.js +8 -13
  15. package/dest/avm/avm_memory_types.d.ts +3 -3
  16. package/dest/avm/avm_memory_types.d.ts.map +1 -1
  17. package/dest/avm/avm_memory_types.js +29 -29
  18. package/dest/avm/avm_simulator.d.ts +3 -3
  19. package/dest/avm/avm_simulator.d.ts.map +1 -1
  20. package/dest/avm/avm_simulator.js +27 -15
  21. package/dest/avm/avm_tree.d.ts +2 -1
  22. package/dest/avm/avm_tree.d.ts.map +1 -1
  23. package/dest/avm/avm_tree.js +6 -2
  24. package/dest/avm/errors.d.ts +3 -3
  25. package/dest/avm/errors.d.ts.map +1 -1
  26. package/dest/avm/errors.js +8 -15
  27. package/dest/avm/fixtures/index.d.ts +2 -0
  28. package/dest/avm/fixtures/index.d.ts.map +1 -1
  29. package/dest/avm/fixtures/index.js +7 -7
  30. package/dest/avm/journal/journal.d.ts +31 -8
  31. package/dest/avm/journal/journal.d.ts.map +1 -1
  32. package/dest/avm/journal/journal.js +139 -42
  33. package/dest/avm/opcodes/conversion.d.ts +4 -4
  34. package/dest/avm/opcodes/conversion.d.ts.map +1 -1
  35. package/dest/avm/opcodes/conversion.js +22 -18
  36. package/dest/avm/opcodes/external_calls.d.ts.map +1 -1
  37. package/dest/avm/opcodes/external_calls.js +4 -11
  38. package/dest/avm/opcodes/misc.d.ts.map +1 -1
  39. package/dest/avm/opcodes/misc.js +3 -3
  40. package/dest/avm/test_utils.d.ts +1 -0
  41. package/dest/avm/test_utils.d.ts.map +1 -1
  42. package/dest/avm/test_utils.js +4 -1
  43. package/dest/client/client_execution_context.d.ts +3 -3
  44. package/dest/client/client_execution_context.d.ts.map +1 -1
  45. package/dest/client/client_execution_context.js +15 -8
  46. package/dest/client/db_oracle.d.ts +9 -5
  47. package/dest/client/db_oracle.d.ts.map +1 -1
  48. package/dest/client/execution_note_cache.d.ts +9 -1
  49. package/dest/client/execution_note_cache.d.ts.map +1 -1
  50. package/dest/client/execution_note_cache.js +10 -3
  51. package/dest/client/index.d.ts +1 -0
  52. package/dest/client/index.d.ts.map +1 -1
  53. package/dest/client/index.js +2 -1
  54. package/dest/client/private_execution.d.ts.map +1 -1
  55. package/dest/client/private_execution.js +4 -4
  56. package/dest/client/simulator.d.ts.map +1 -1
  57. package/dest/client/simulator.js +4 -4
  58. package/dest/client/unconstrained_execution.d.ts.map +1 -1
  59. package/dest/client/unconstrained_execution.js +3 -3
  60. package/dest/client/view_data_oracle.d.ts +4 -4
  61. package/dest/client/view_data_oracle.d.ts.map +1 -1
  62. package/dest/client/view_data_oracle.js +9 -9
  63. package/dest/common/debug_fn_name.d.ts +2 -2
  64. package/dest/common/debug_fn_name.d.ts.map +1 -1
  65. package/dest/common/debug_fn_name.js +8 -14
  66. package/dest/providers/acvm_native.js +4 -4
  67. package/dest/providers/acvm_wasm.js +2 -2
  68. package/dest/providers/factory.d.ts +2 -2
  69. package/dest/providers/factory.d.ts.map +1 -1
  70. package/dest/providers/factory.js +4 -4
  71. package/dest/providers/index.d.ts +0 -1
  72. package/dest/providers/index.d.ts.map +1 -1
  73. package/dest/providers/index.js +1 -2
  74. package/dest/public/enqueued_call_side_effect_trace.d.ts +15 -26
  75. package/dest/public/enqueued_call_side_effect_trace.d.ts.map +1 -1
  76. package/dest/public/enqueued_call_side_effect_trace.js +42 -62
  77. package/dest/public/execution.d.ts +2 -2
  78. package/dest/public/execution.d.ts.map +1 -1
  79. package/dest/public/execution.js +1 -1
  80. package/dest/public/executor_metrics.d.ts.map +1 -1
  81. package/dest/public/executor_metrics.js +2 -5
  82. package/dest/public/fee_payment.d.ts.map +1 -1
  83. package/dest/public/fee_payment.js +4 -3
  84. package/dest/public/fixtures/index.d.ts +24 -1
  85. package/dest/public/fixtures/index.d.ts.map +1 -1
  86. package/dest/public/fixtures/index.js +17 -11
  87. package/dest/public/index.d.ts +0 -1
  88. package/dest/public/index.d.ts.map +1 -1
  89. package/dest/public/index.js +1 -2
  90. package/dest/public/public_db_sources.d.ts.map +1 -1
  91. package/dest/public/public_db_sources.js +10 -9
  92. package/dest/public/public_processor.d.ts +12 -12
  93. package/dest/public/public_processor.d.ts.map +1 -1
  94. package/dest/public/public_processor.js +80 -68
  95. package/dest/public/public_processor_metrics.d.ts +1 -1
  96. package/dest/public/public_processor_metrics.d.ts.map +1 -1
  97. package/dest/public/public_tx_context.d.ts +13 -10
  98. package/dest/public/public_tx_context.d.ts.map +1 -1
  99. package/dest/public/public_tx_context.js +72 -45
  100. package/dest/public/public_tx_simulator.d.ts +2 -2
  101. package/dest/public/public_tx_simulator.d.ts.map +1 -1
  102. package/dest/public/public_tx_simulator.js +54 -23
  103. package/dest/public/side_effect_trace_interface.d.ts +6 -18
  104. package/dest/public/side_effect_trace_interface.d.ts.map +1 -1
  105. package/dest/public/transitional_adapters.d.ts +2 -2
  106. package/dest/public/transitional_adapters.d.ts.map +1 -1
  107. package/dest/public/transitional_adapters.js +10 -39
  108. package/package.json +16 -9
  109. package/src/acvm/acvm.ts +2 -2
  110. package/src/acvm/oracle/oracle.ts +4 -4
  111. package/src/acvm/oracle/typed_oracle.ts +5 -5
  112. package/src/acvm/serialize.ts +1 -1
  113. package/src/avm/avm_context.ts +2 -3
  114. package/src/avm/avm_execution_environment.ts +6 -31
  115. package/src/avm/avm_memory_types.ts +31 -29
  116. package/src/avm/avm_simulator.ts +28 -22
  117. package/src/avm/avm_tree.ts +6 -1
  118. package/src/avm/errors.ts +12 -14
  119. package/src/avm/fixtures/index.ts +6 -5
  120. package/src/avm/journal/journal.ts +230 -71
  121. package/src/avm/opcodes/conversion.ts +21 -16
  122. package/src/avm/opcodes/external_calls.ts +3 -19
  123. package/src/avm/opcodes/misc.ts +2 -2
  124. package/src/avm/test_utils.ts +4 -0
  125. package/src/client/client_execution_context.ts +19 -9
  126. package/src/client/db_oracle.ts +10 -5
  127. package/src/client/execution_note_cache.ts +13 -3
  128. package/src/client/index.ts +1 -0
  129. package/src/client/private_execution.ts +3 -3
  130. package/src/client/simulator.ts +4 -4
  131. package/src/client/unconstrained_execution.ts +2 -2
  132. package/src/client/view_data_oracle.ts +11 -9
  133. package/src/common/debug_fn_name.ts +7 -13
  134. package/src/providers/acvm_native.ts +3 -3
  135. package/src/providers/acvm_wasm.ts +2 -2
  136. package/src/providers/factory.ts +3 -3
  137. package/src/providers/index.ts +0 -1
  138. package/src/public/enqueued_call_side_effect_trace.ts +62 -86
  139. package/src/public/execution.ts +1 -2
  140. package/src/public/executor_metrics.ts +0 -4
  141. package/src/public/fee_payment.ts +3 -2
  142. package/src/public/fixtures/index.ts +25 -12
  143. package/src/public/index.ts +0 -1
  144. package/src/public/public_db_sources.ts +9 -8
  145. package/src/public/public_processor.ts +109 -105
  146. package/src/public/public_processor_metrics.ts +1 -1
  147. package/src/public/public_tx_context.ts +97 -50
  148. package/src/public/public_tx_simulator.ts +69 -38
  149. package/src/public/side_effect_trace_interface.ts +10 -16
  150. package/src/public/transitional_adapters.ts +12 -48
  151. package/dest/public/dual_side_effect_trace.d.ts +0 -77
  152. package/dest/public/dual_side_effect_trace.d.ts.map +0 -1
  153. package/dest/public/dual_side_effect_trace.js +0 -119
  154. package/dest/public/side_effect_trace.d.ts +0 -96
  155. package/dest/public/side_effect_trace.d.ts.map +0 -1
  156. package/dest/public/side_effect_trace.js +0 -309
  157. package/src/public/dual_side_effect_trace.ts +0 -242
  158. package/src/public/side_effect_trace.ts +0 -536
@@ -1,10 +1,10 @@
1
- import { isNoirCallStackUnresolved } from '@aztec/circuit-types';
2
- import { GasFees, GlobalVariables, MAX_L2_GAS_PER_ENQUEUED_CALL } from '@aztec/circuits.js';
1
+ import { TxHash, isNoirCallStackUnresolved } from '@aztec/circuit-types';
2
+ import { GasFees, GlobalVariables, MAX_L2_GAS_PER_TX_PUBLIC_PORTION } from '@aztec/circuits.js';
3
3
  import { type FunctionArtifact, FunctionSelector } from '@aztec/foundation/abi';
4
4
  import { AztecAddress } from '@aztec/foundation/aztec-address';
5
5
  import { EthAddress } from '@aztec/foundation/eth-address';
6
6
  import { Fr } from '@aztec/foundation/fields';
7
- import { AvmTestContractArtifact } from '@aztec/noir-contracts.js';
7
+ import { AvmTestContractArtifact } from '@aztec/noir-contracts.js/AvmTest';
8
8
 
9
9
  import { strict as assert } from 'assert';
10
10
  import { mock } from 'jest-mock-extended';
@@ -45,6 +45,7 @@ export function initPersistableStateManager(overrides?: {
45
45
  nullifiers?: NullifierManager;
46
46
  doMerkleOperations?: boolean;
47
47
  merkleTrees?: AvmEphemeralForest;
48
+ txHash?: TxHash;
48
49
  }): AvmPersistableStateManager {
49
50
  const worldStateDB = overrides?.worldStateDB || mock<WorldStateDB>();
50
51
  return new AvmPersistableStateManager(
@@ -54,6 +55,7 @@ export function initPersistableStateManager(overrides?: {
54
55
  overrides?.nullifiers || new NullifierManager(worldStateDB),
55
56
  overrides?.doMerkleOperations || false,
56
57
  overrides?.merkleTrees || mock<AvmEphemeralForest>(),
58
+ overrides?.txHash || new TxHash(new Fr(27).toBuffer()),
57
59
  );
58
60
  }
59
61
 
@@ -64,7 +66,6 @@ export function initExecutionEnvironment(overrides?: Partial<AvmExecutionEnviron
64
66
  return new AvmExecutionEnvironment(
65
67
  overrides?.address ?? AztecAddress.zero(),
66
68
  overrides?.sender ?? AztecAddress.zero(),
67
- overrides?.functionSelector ?? FunctionSelector.empty(),
68
69
  overrides?.contractCallDepth ?? Fr.zero(),
69
70
  overrides?.transactionFee ?? Fr.zero(),
70
71
  overrides?.globals ?? GlobalVariables.empty(),
@@ -94,7 +95,7 @@ export function initGlobalVariables(overrides?: Partial<GlobalVariables>): Globa
94
95
  */
95
96
  export function initMachineState(overrides?: Partial<AvmMachineState>): AvmMachineState {
96
97
  return AvmMachineState.fromState({
97
- l2GasLeft: overrides?.l2GasLeft ?? MAX_L2_GAS_PER_ENQUEUED_CALL,
98
+ l2GasLeft: overrides?.l2GasLeft ?? MAX_L2_GAS_PER_TX_PUBLIC_PORTION,
98
99
  daGasLeft: overrides?.daGasLeft ?? 1e8,
99
100
  });
100
101
  }
@@ -1,23 +1,33 @@
1
- import { MerkleTreeId } from '@aztec/circuit-types';
1
+ import { MerkleTreeId, type TxHash } from '@aztec/circuit-types';
2
2
  import {
3
- type AztecAddress,
4
- type Gas,
5
- type NullifierLeafPreimage,
3
+ AztecAddress,
4
+ CANONICAL_AUTH_REGISTRY_ADDRESS,
5
+ DEPLOYER_CONTRACT_ADDRESS,
6
+ FEE_JUICE_ADDRESS,
7
+ MULTI_CALL_ENTRYPOINT_ADDRESS,
8
+ NullifierLeafPreimage,
6
9
  type PublicCallRequest,
7
10
  type PublicDataTreeLeafPreimage,
11
+ REGISTERER_CONTRACT_ADDRESS,
12
+ ROUTER_ADDRESS,
8
13
  SerializableContractInstance,
9
14
  } from '@aztec/circuits.js';
10
- import { computePublicDataTreeLeafSlot, siloNoteHash, siloNullifier } from '@aztec/circuits.js/hash';
15
+ import {
16
+ computeNoteHashNonce,
17
+ computePublicDataTreeLeafSlot,
18
+ computeUniqueNoteHash,
19
+ siloNoteHash,
20
+ siloNullifier,
21
+ } from '@aztec/circuits.js/hash';
11
22
  import { Fr } from '@aztec/foundation/fields';
12
23
  import { jsonStringify } from '@aztec/foundation/json-rpc';
13
- import { createDebugLogger } from '@aztec/foundation/log';
24
+ import { createLogger } from '@aztec/foundation/log';
14
25
 
15
26
  import { strict as assert } from 'assert';
16
27
 
17
28
  import { getPublicFunctionDebugName } from '../../common/debug_fn_name.js';
18
29
  import { type WorldStateDB } from '../../public/public_db_sources.js';
19
30
  import { type PublicSideEffectTraceInterface } from '../../public/side_effect_trace_interface.js';
20
- import { type AvmContractCallResult } from '../avm_contract_call_result.js';
21
31
  import { type AvmExecutionEnvironment } from '../avm_execution_environment.js';
22
32
  import { AvmEphemeralForest } from '../avm_tree.js';
23
33
  import { NullifierCollisionError, NullifierManager } from './nullifiers.js';
@@ -33,7 +43,7 @@ import { PublicStorage } from './public_storage.js';
33
43
  * Manages merging of successful/reverted child state into current state.
34
44
  */
35
45
  export class AvmPersistableStateManager {
36
- private readonly log = createDebugLogger('aztec:avm_simulator:state_manager');
46
+ private readonly log = createLogger('simulator:avm:state_manager');
37
47
 
38
48
  /** Make sure a forked state is never merged twice. */
39
49
  private alreadyMergedIntoParent = false;
@@ -51,6 +61,7 @@ export class AvmPersistableStateManager {
51
61
  private readonly doMerkleOperations: boolean = false,
52
62
  /** Ephmeral forest for merkle tree operations */
53
63
  public merkleTrees: AvmEphemeralForest,
64
+ public readonly txHash: TxHash,
54
65
  ) {}
55
66
 
56
67
  /**
@@ -61,6 +72,7 @@ export class AvmPersistableStateManager {
61
72
  trace: PublicSideEffectTraceInterface,
62
73
  pendingSiloedNullifiers: Fr[],
63
74
  doMerkleOperations: boolean = false,
75
+ txHash: TxHash,
64
76
  ) {
65
77
  const parentNullifiers = NullifierManager.newWithPendingSiloedNullifiers(worldStateDB, pendingSiloedNullifiers);
66
78
  const ephemeralForest = await AvmEphemeralForest.create(worldStateDB.getMerkleInterface());
@@ -71,6 +83,7 @@ export class AvmPersistableStateManager {
71
83
  /*nullifiers=*/ parentNullifiers.fork(),
72
84
  doMerkleOperations,
73
85
  ephemeralForest,
86
+ txHash,
74
87
  );
75
88
  }
76
89
 
@@ -81,6 +94,7 @@ export class AvmPersistableStateManager {
81
94
  worldStateDB: WorldStateDB,
82
95
  trace: PublicSideEffectTraceInterface,
83
96
  doMerkleOperations: boolean = false,
97
+ txHash: TxHash,
84
98
  ) {
85
99
  const ephemeralForest = await AvmEphemeralForest.create(worldStateDB.getMerkleInterface());
86
100
  return new AvmPersistableStateManager(
@@ -90,6 +104,7 @@ export class AvmPersistableStateManager {
90
104
  /*nullifiers=*/ new NullifierManager(worldStateDB),
91
105
  /*doMerkleOperations=*/ doMerkleOperations,
92
106
  ephemeralForest,
107
+ txHash,
93
108
  );
94
109
  }
95
110
 
@@ -104,6 +119,7 @@ export class AvmPersistableStateManager {
104
119
  this.nullifiers.fork(),
105
120
  this.doMerkleOperations,
106
121
  this.merkleTrees.fork(),
122
+ this.txHash,
107
123
  );
108
124
  }
109
125
 
@@ -148,14 +164,16 @@ export class AvmPersistableStateManager {
148
164
  * @param slot - the slot in the contract's storage being written to
149
165
  * @param value - the value being written to the slot
150
166
  */
151
- public async writeStorage(contractAddress: AztecAddress, slot: Fr, value: Fr): Promise<void> {
167
+ public async writeStorage(contractAddress: AztecAddress, slot: Fr, value: Fr, protocolWrite = false): Promise<void> {
152
168
  this.log.debug(`Storage write (address=${contractAddress}, slot=${slot}): value=${value}`);
169
+ const leafSlot = computePublicDataTreeLeafSlot(contractAddress, slot);
170
+ this.log.debug(`leafSlot=${leafSlot}`);
153
171
  // Cache storage writes for later reference/reads
154
172
  this.publicStorage.write(contractAddress, slot, value);
155
- const leafSlot = computePublicDataTreeLeafSlot(contractAddress, slot);
173
+
156
174
  if (this.doMerkleOperations) {
157
175
  const result = await this.merkleTrees.writePublicStorage(leafSlot, value);
158
- assert(result !== undefined, 'Public data tree insertion error. You might want to disable skipMerkleOperations.');
176
+ assert(result !== undefined, 'Public data tree insertion error. You might want to disable doMerkleOperations.');
159
177
  this.log.debug(`Inserted public data tree leaf at leafSlot ${leafSlot}, value: ${value}`);
160
178
 
161
179
  const lowLeafInfo = result.lowWitness;
@@ -164,16 +182,20 @@ export class AvmPersistableStateManager {
164
182
  const lowLeafPath = lowLeafInfo.siblingPath;
165
183
 
166
184
  const newLeafPreimage = result.element as PublicDataTreeLeafPreimage;
167
- let insertionPath;
168
-
185
+ let insertionPath: Fr[] | undefined;
169
186
  if (!result.update) {
170
187
  insertionPath = result.insertionPath;
188
+ assert(
189
+ newLeafPreimage.value.equals(value),
190
+ `Value mismatch when performing public data write (got value: ${value}, value in ephemeral tree: ${newLeafPreimage.value})`,
191
+ );
171
192
  }
172
193
 
173
194
  this.trace.tracePublicStorageWrite(
174
195
  contractAddress,
175
196
  slot,
176
197
  value,
198
+ protocolWrite,
177
199
  lowLeafPreimage,
178
200
  new Fr(lowLeafIndex),
179
201
  lowLeafPath,
@@ -181,7 +203,7 @@ export class AvmPersistableStateManager {
181
203
  insertionPath,
182
204
  );
183
205
  } else {
184
- this.trace.tracePublicStorageWrite(contractAddress, slot, value);
206
+ this.trace.tracePublicStorageWrite(contractAddress, slot, value, protocolWrite);
185
207
  }
186
208
  }
187
209
 
@@ -195,8 +217,8 @@ export class AvmPersistableStateManager {
195
217
  public async readStorage(contractAddress: AztecAddress, slot: Fr): Promise<Fr> {
196
218
  const { value, cached } = await this.publicStorage.read(contractAddress, slot);
197
219
  this.log.debug(`Storage read (address=${contractAddress}, slot=${slot}): value=${value}, cached=${cached}`);
198
-
199
220
  const leafSlot = computePublicDataTreeLeafSlot(contractAddress, slot);
221
+ this.log.debug(`leafSlot=${leafSlot}`);
200
222
 
201
223
  if (this.doMerkleOperations) {
202
224
  // Get leaf if present, low leaf if absent
@@ -211,12 +233,18 @@ export class AvmPersistableStateManager {
211
233
  const leafPath = await this.merkleTrees.getSiblingPath(MerkleTreeId.PUBLIC_DATA_TREE, leafIndex);
212
234
  const leafPreimage = preimage as PublicDataTreeLeafPreimage;
213
235
 
236
+ this.log.debug(`leafPreimage.slot: ${leafPreimage.slot}, leafPreimage.value: ${leafPreimage.value}`);
214
237
  this.log.debug(
215
238
  `leafPreimage.nextSlot: ${leafPreimage.nextSlot}, leafPreimage.nextIndex: ${Number(leafPreimage.nextIndex)}`,
216
239
  );
217
- this.log.debug(`leafPreimage.slot: ${leafPreimage.slot}, leafPreimage.value: ${leafPreimage.value}`);
218
240
 
219
- if (!alreadyPresent) {
241
+ if (alreadyPresent) {
242
+ assert(
243
+ leafPreimage.value.equals(value),
244
+ `Value mismatch when performing public data read (got value: ${value}, value in ephemeral tree: ${leafPreimage.value})`,
245
+ );
246
+ } else {
247
+ this.log.debug(`Slot has never been written before!`);
220
248
  // Sanity check that the leaf slot is skipped by low leaf when it doesn't exist
221
249
  assert(
222
250
  leafPreimage.slot.toBigInt() < leafSlot.toBigInt() &&
@@ -224,9 +252,6 @@ export class AvmPersistableStateManager {
224
252
  'Public data tree low leaf should skip the target leaf slot when the target leaf does not exist or is the max value.',
225
253
  );
226
254
  }
227
- this.log.debug(
228
- `Tracing storage leaf preimage slot=${slot}, leafSlot=${leafSlot}, value=${value}, nextKey=${leafPreimage.nextSlot}, nextIndex=${leafPreimage.nextIndex}`,
229
- );
230
255
  // On non-existence, AVM circuit will need to recognize that leafPreimage.slot != leafSlot,
231
256
  // prove that this is a low leaf that skips leafSlot, and then prove membership of the leaf.
232
257
  this.trace.tracePublicStorageRead(contractAddress, slot, value, leafPreimage, new Fr(leafIndex), leafPath);
@@ -277,20 +302,41 @@ export class AvmPersistableStateManager {
277
302
  }
278
303
 
279
304
  /**
280
- * Write a note hash, trace the write.
305
+ * Write a raw note hash, silo it and make it unique, then trace the write.
281
306
  * @param noteHash - the unsiloed note hash to write
282
307
  */
283
308
  public writeNoteHash(contractAddress: AztecAddress, noteHash: Fr): void {
284
- this.log.debug(`noteHashes(${contractAddress}) += @${noteHash}.`);
309
+ const siloedNoteHash = siloNoteHash(contractAddress, noteHash);
310
+
311
+ this.writeSiloedNoteHash(siloedNoteHash);
312
+ }
313
+
314
+ /**
315
+ * Write a note hash, make it unique, trace the write.
316
+ * @param noteHash - the non unique note hash to write
317
+ */
318
+ public writeSiloedNoteHash(noteHash: Fr): void {
319
+ const txHash = Fr.fromBuffer(this.txHash.toBuffer());
320
+ const nonce = computeNoteHashNonce(txHash, this.trace.getNoteHashCount());
321
+ const uniqueNoteHash = computeUniqueNoteHash(nonce, noteHash);
322
+
323
+ this.writeUniqueNoteHash(uniqueNoteHash);
324
+ }
325
+
326
+ /**
327
+ * Write a note hash, trace the write.
328
+ * @param noteHash - the siloed unique hash to write
329
+ */
330
+ public writeUniqueNoteHash(noteHash: Fr): void {
331
+ this.log.debug(`noteHashes += @${noteHash}.`);
285
332
 
286
333
  if (this.doMerkleOperations) {
287
334
  // Should write a helper for this
288
335
  const leafIndex = new Fr(this.merkleTrees.treeMap.get(MerkleTreeId.NOTE_HASH_TREE)!.leafCount);
289
- const siloedNoteHash = siloNoteHash(contractAddress, noteHash);
290
- const insertionPath = this.merkleTrees.appendNoteHash(siloedNoteHash);
291
- this.trace.traceNewNoteHash(contractAddress, noteHash, leafIndex, insertionPath);
336
+ const insertionPath = this.merkleTrees.appendNoteHash(noteHash);
337
+ this.trace.traceNewNoteHash(noteHash, leafIndex, insertionPath);
292
338
  } else {
293
- this.trace.traceNewNoteHash(contractAddress, noteHash);
339
+ this.trace.traceNewNoteHash(noteHash);
294
340
  }
295
341
  }
296
342
 
@@ -303,8 +349,47 @@ export class AvmPersistableStateManager {
303
349
  public async checkNullifierExists(contractAddress: AztecAddress, nullifier: Fr): Promise<boolean> {
304
350
  this.log.debug(`Checking existence of nullifier (address=${contractAddress}, nullifier=${nullifier})`);
305
351
  const siloedNullifier = siloNullifier(contractAddress, nullifier);
352
+ const [exists, leafOrLowLeafPreimage, leafOrLowLeafIndex, leafOrLowLeafPath] = await this.getNullifierMembership(
353
+ siloedNullifier,
354
+ );
355
+
356
+ if (this.doMerkleOperations) {
357
+ this.trace.traceNullifierCheck(
358
+ siloedNullifier,
359
+ exists,
360
+ leafOrLowLeafPreimage,
361
+ leafOrLowLeafIndex,
362
+ leafOrLowLeafPath,
363
+ );
364
+ } else {
365
+ this.trace.traceNullifierCheck(siloedNullifier, exists);
366
+ }
367
+ return Promise.resolve(exists);
368
+ }
369
+
370
+ /**
371
+ * Helper to get membership information for a siloed nullifier when checking its existence.
372
+ * Optionally trace the nullifier check.
373
+ *
374
+ * @param siloedNullifier - the siloed nullifier to get membership information for
375
+ * @returns
376
+ * - exists - whether the nullifier exists in the nullifier set
377
+ * - leafOrLowLeafPreimage - the preimage of the nullifier leaf or its low-leaf if it doesn't exist
378
+ * - leafOrLowLeafIndex - the leaf index of the nullifier leaf or its low-leaf if it doesn't exist
379
+ * - leafOrLowLeafPath - the sibling path of the nullifier leaf or its low-leaf if it doesn't exist
380
+ */
381
+ private async getNullifierMembership(
382
+ siloedNullifier: Fr,
383
+ ): Promise<
384
+ [
385
+ /*exists=*/ boolean,
386
+ /*leafOrLowLeafPreimage=*/ NullifierLeafPreimage,
387
+ /*leafOrLowLeafIndex=*/ Fr,
388
+ /*leafOrLowLeafIndexPath=*/ Fr[],
389
+ ]
390
+ > {
306
391
  const [exists, isPending, _] = await this.nullifiers.checkExists(siloedNullifier);
307
- this.log.debug(`Checked siloed nullifier ${siloedNullifier} (exists=${exists}, pending=${isPending})`);
392
+ this.log.debug(`Checked siloed nullifier ${siloedNullifier} (exists=${exists}), pending=${isPending}`);
308
393
 
309
394
  if (this.doMerkleOperations) {
310
395
  // Get leaf if present, low leaf if absent
@@ -319,7 +404,7 @@ export class AvmPersistableStateManager {
319
404
 
320
405
  assert(
321
406
  alreadyPresent == exists,
322
- 'WorldStateDB contains nullifier leaf, but merkle tree does not.... This is a bug!',
407
+ 'WorldStateDB contains nullifier leaf, but merkle tree does not (or vice versa).... This is a bug!',
323
408
  );
324
409
 
325
410
  if (exists) {
@@ -327,17 +412,15 @@ export class AvmPersistableStateManager {
327
412
  } else {
328
413
  // Sanity check that the leaf value is skipped by low leaf when it doesn't exist
329
414
  assert(
330
- siloedNullifier.toBigInt() > leafPreimage.nullifier.toBigInt() &&
331
- siloedNullifier.toBigInt() < leafPreimage.nextNullifier.toBigInt(),
415
+ leafPreimage.nullifier.toBigInt() < siloedNullifier.toBigInt() &&
416
+ (leafPreimage.nextIndex === 0n || leafPreimage.nextNullifier.toBigInt() > siloedNullifier.toBigInt()),
332
417
  'Nullifier tree low leaf should skip the target leaf nullifier when the target leaf does not exist.',
333
418
  );
334
419
  }
335
-
336
- this.trace.traceNullifierCheck(siloedNullifier, exists, leafPreimage, new Fr(leafIndex), leafPath);
420
+ return [exists, leafPreimage, new Fr(leafIndex), leafPath];
337
421
  } else {
338
- this.trace.traceNullifierCheck(siloedNullifier, exists);
422
+ return [exists, NullifierLeafPreimage.empty(), Fr.ZERO, []];
339
423
  }
340
- return Promise.resolve(exists);
341
424
  }
342
425
 
343
426
  /**
@@ -484,18 +567,59 @@ export class AvmPersistableStateManager {
484
567
  const instanceWithAddress = await this.worldStateDB.getContractInstance(contractAddress);
485
568
  const exists = instanceWithAddress !== undefined;
486
569
 
487
- // TODO: nullifier check!
570
+ let [existsInTree, leafOrLowLeafPreimage, leafOrLowLeafIndex, leafOrLowLeafPath] = [
571
+ exists,
572
+ NullifierLeafPreimage.empty(),
573
+ Fr.ZERO,
574
+ new Array<Fr>(),
575
+ ];
576
+ if (!contractAddressIsCanonical(contractAddress)) {
577
+ const contractAddressNullifier = siloNullifier(
578
+ AztecAddress.fromNumber(DEPLOYER_CONTRACT_ADDRESS),
579
+ contractAddress.toField(),
580
+ );
581
+ [existsInTree, leafOrLowLeafPreimage, leafOrLowLeafIndex, leafOrLowLeafPath] = await this.getNullifierMembership(
582
+ /*siloedNullifier=*/ contractAddressNullifier,
583
+ );
584
+ assert(
585
+ exists == existsInTree,
586
+ 'WorldStateDB contains contract instance, but nullifier tree does not contain contract address (or vice versa).... This is a bug!',
587
+ );
588
+ }
589
+
488
590
  if (exists) {
489
591
  const instance = new SerializableContractInstance(instanceWithAddress);
490
592
  this.log.debug(
491
593
  `Got contract instance (address=${contractAddress}): exists=${exists}, instance=${jsonStringify(instance)}`,
492
594
  );
493
- this.trace.traceGetContractInstance(contractAddress, exists, instance);
595
+ if (this.doMerkleOperations) {
596
+ this.trace.traceGetContractInstance(
597
+ contractAddress,
598
+ exists,
599
+ instance,
600
+ leafOrLowLeafPreimage,
601
+ leafOrLowLeafIndex,
602
+ leafOrLowLeafPath,
603
+ );
604
+ } else {
605
+ this.trace.traceGetContractInstance(contractAddress, exists, instance);
606
+ }
494
607
 
495
608
  return Promise.resolve(instance);
496
609
  } else {
497
610
  this.log.debug(`Contract instance NOT FOUND (address=${contractAddress})`);
498
- this.trace.traceGetContractInstance(contractAddress, exists);
611
+ if (this.doMerkleOperations) {
612
+ this.trace.traceGetContractInstance(
613
+ contractAddress,
614
+ exists,
615
+ /*instance=*/ undefined,
616
+ leafOrLowLeafPreimage,
617
+ leafOrLowLeafIndex,
618
+ leafOrLowLeafPath,
619
+ );
620
+ } else {
621
+ this.trace.traceGetContractInstance(contractAddress, exists);
622
+ }
499
623
  return Promise.resolve(undefined);
500
624
  }
501
625
  }
@@ -508,6 +632,26 @@ export class AvmPersistableStateManager {
508
632
  const instanceWithAddress = await this.worldStateDB.getContractInstance(contractAddress);
509
633
  const exists = instanceWithAddress !== undefined;
510
634
 
635
+ let [existsInTree, leafOrLowLeafPreimage, leafOrLowLeafIndex, leafOrLowLeafPath] = [
636
+ exists,
637
+ NullifierLeafPreimage.empty(),
638
+ Fr.ZERO,
639
+ new Array<Fr>(),
640
+ ];
641
+ if (!contractAddressIsCanonical(contractAddress)) {
642
+ const contractAddressNullifier = siloNullifier(
643
+ AztecAddress.fromNumber(DEPLOYER_CONTRACT_ADDRESS),
644
+ contractAddress.toField(),
645
+ );
646
+ [existsInTree, leafOrLowLeafPreimage, leafOrLowLeafIndex, leafOrLowLeafPath] = await this.getNullifierMembership(
647
+ /*siloedNullifier=*/ contractAddressNullifier,
648
+ );
649
+ assert(
650
+ exists == existsInTree,
651
+ 'WorldStateDB contains contract instance, but nullifier tree does not contain contract address (or vice versa).... This is a bug!',
652
+ );
653
+ }
654
+
511
655
  if (exists) {
512
656
  const instance = new SerializableContractInstance(instanceWithAddress);
513
657
  const contractClass = await this.worldStateDB.getContractClass(instance.contractClassId);
@@ -529,51 +673,66 @@ export class AvmPersistableStateManager {
529
673
  publicBytecodeCommitment: bytecodeCommitment,
530
674
  };
531
675
 
532
- this.trace.traceGetBytecode(
533
- contractAddress,
534
- exists,
535
- contractClass.packedBytecode,
536
- instance,
537
- contractClassPreimage,
538
- );
676
+ if (this.doMerkleOperations) {
677
+ this.trace.traceGetBytecode(
678
+ contractAddress,
679
+ exists,
680
+ contractClass.packedBytecode,
681
+ instance,
682
+ contractClassPreimage,
683
+ leafOrLowLeafPreimage,
684
+ leafOrLowLeafIndex,
685
+ leafOrLowLeafPath,
686
+ );
687
+ } else {
688
+ this.trace.traceGetBytecode(
689
+ contractAddress,
690
+ exists,
691
+ contractClass.packedBytecode,
692
+ instance,
693
+ contractClassPreimage,
694
+ );
695
+ }
539
696
 
540
697
  return contractClass.packedBytecode;
541
698
  } else {
542
699
  // If the contract instance is not found, we assume it has not been deployed.
543
700
  // It doesnt matter what the values of the contract instance are in this case, as long as we tag it with exists=false.
544
701
  // This will hint to the avm circuit to just perform the non-membership check on the address and disregard the bytecode hash
545
- this.trace.traceGetBytecode(contractAddress, exists); // bytecode, instance, class undefined
702
+ if (this.doMerkleOperations) {
703
+ this.trace.traceGetBytecode(
704
+ contractAddress,
705
+ exists,
706
+ /*instance=*/ undefined,
707
+ /*contractClass=*/ undefined,
708
+ /*bytecode=*/ undefined,
709
+ leafOrLowLeafPreimage,
710
+ leafOrLowLeafIndex,
711
+ leafOrLowLeafPath,
712
+ );
713
+ } else {
714
+ this.trace.traceGetBytecode(contractAddress, exists); // bytecode, instance, class undefined
715
+ }
546
716
  return undefined;
547
717
  }
548
718
  }
549
719
 
550
- public async traceNestedCall(
551
- forkedState: AvmPersistableStateManager,
552
- nestedEnvironment: AvmExecutionEnvironment,
553
- startGasLeft: Gas,
554
- bytecode: Buffer,
555
- avmCallResults: AvmContractCallResult,
556
- ) {
557
- const functionName = await getPublicFunctionDebugName(
558
- this.worldStateDB,
559
- nestedEnvironment.address,
560
- nestedEnvironment.functionSelector,
561
- nestedEnvironment.calldata,
562
- );
563
-
564
- this.log.verbose(`[AVM] Tracing nested external contract call ${functionName}`);
565
-
566
- this.trace.traceNestedCall(
567
- forkedState.trace,
568
- nestedEnvironment,
569
- startGasLeft,
570
- bytecode,
571
- avmCallResults,
572
- functionName,
573
- );
574
- }
575
-
576
720
  public traceEnqueuedCall(publicCallRequest: PublicCallRequest, calldata: Fr[], reverted: boolean) {
577
721
  this.trace.traceEnqueuedCall(publicCallRequest, calldata, reverted);
578
722
  }
723
+
724
+ public async getPublicFunctionDebugName(avmEnvironment: AvmExecutionEnvironment): Promise<string> {
725
+ return await getPublicFunctionDebugName(this.worldStateDB, avmEnvironment.address, avmEnvironment.calldata);
726
+ }
727
+ }
728
+
729
+ function contractAddressIsCanonical(contractAddress: AztecAddress): boolean {
730
+ return (
731
+ contractAddress.equals(AztecAddress.fromNumber(CANONICAL_AUTH_REGISTRY_ADDRESS)) ||
732
+ contractAddress.equals(AztecAddress.fromNumber(DEPLOYER_CONTRACT_ADDRESS)) ||
733
+ contractAddress.equals(AztecAddress.fromNumber(REGISTERER_CONTRACT_ADDRESS)) ||
734
+ contractAddress.equals(AztecAddress.fromNumber(MULTI_CALL_ENTRYPOINT_ADDRESS)) ||
735
+ contractAddress.equals(AztecAddress.fromNumber(FEE_JUICE_ADDRESS)) ||
736
+ contractAddress.equals(AztecAddress.fromNumber(ROUTER_ADDRESS))
737
+ );
579
738
  }
@@ -12,57 +12,62 @@ export class ToRadixBE extends Instruction {
12
12
  // Informs (de)serialization. See Instruction.deserialize.
13
13
  static readonly wireFormat: OperandType[] = [
14
14
  OperandType.UINT8, // Opcode
15
- OperandType.UINT8, // Indirect
15
+ OperandType.UINT16, // Indirect
16
16
  OperandType.UINT16, // src memory address
17
- OperandType.UINT16, // dst memory address
18
17
  OperandType.UINT16, // radix memory address
19
- OperandType.UINT16, // number of limbs (Immediate)
20
- OperandType.UINT8, // output is in "bits" mode (Immediate - Uint1 still takes up a whole byte)
18
+ OperandType.UINT16, // number of limbs address
19
+ OperandType.UINT16, // output is in "bits" mode memory address (boolean/Uint1 is stored)
20
+ OperandType.UINT16, // dst memory address
21
21
  ];
22
22
 
23
23
  constructor(
24
24
  private indirect: number,
25
25
  private srcOffset: number,
26
- private dstOffset: number,
27
26
  private radixOffset: number,
28
- private numLimbs: number,
29
- private outputBits: number, // effectively a bool
27
+ private numLimbsOffset: number,
28
+ private outputBitsOffset: number,
29
+ private dstOffset: number,
30
30
  ) {
31
31
  super();
32
32
  }
33
33
 
34
34
  public async execute(context: AvmContext): Promise<void> {
35
35
  const memory = context.machineState.memory.track(this.type);
36
- const operands = [this.srcOffset, this.dstOffset, this.radixOffset];
36
+ const operands = [this.srcOffset, this.radixOffset, this.numLimbsOffset, this.outputBitsOffset, this.dstOffset];
37
37
  const addressing = Addressing.fromWire(this.indirect, operands.length);
38
- const [srcOffset, dstOffset, radixOffset] = addressing.resolve(operands, memory);
39
- context.machineState.consumeGas(this.gasCost(this.numLimbs));
38
+ const [srcOffset, radixOffset, numLimbsOffset, outputBitsOffset, dstOffset] = addressing.resolve(operands, memory);
40
39
 
41
40
  // The radix gadget only takes in a Field
42
41
  memory.checkTag(TypeTag.FIELD, srcOffset);
43
42
  memory.checkTag(TypeTag.UINT32, radixOffset);
43
+ memory.checkTag(TypeTag.UINT32, numLimbsOffset);
44
+ memory.checkTag(TypeTag.UINT1, outputBitsOffset);
45
+
46
+ const numLimbs = memory.get(numLimbsOffset).toNumber();
47
+ context.machineState.consumeGas(this.gasCost(numLimbs));
48
+ const outputBits = memory.get(outputBitsOffset).toNumber();
44
49
 
45
50
  let value: bigint = memory.get(srcOffset).toBigInt();
46
51
  const radix: bigint = memory.get(radixOffset).toBigInt();
47
- if (this.numLimbs < 1) {
48
- throw new InstructionExecutionError(`ToRadixBE instruction's numLimbs should be > 0 (was ${this.numLimbs})`);
52
+ if (numLimbs < 1) {
53
+ throw new InstructionExecutionError(`ToRadixBE instruction's numLimbs should be > 0 (was ${numLimbs})`);
49
54
  }
50
55
  if (radix > 256) {
51
56
  throw new InstructionExecutionError(`ToRadixBE instruction's radix should be <= 256 (was ${radix})`);
52
57
  }
53
58
  const radixBN: bigint = BigInt(radix);
54
- const limbArray = new Array(this.numLimbs);
59
+ const limbArray = new Array(numLimbs);
55
60
 
56
- for (let i = this.numLimbs - 1; i >= 0; i--) {
61
+ for (let i = numLimbs - 1; i >= 0; i--) {
57
62
  const limb = value % radixBN;
58
63
  limbArray[i] = limb;
59
64
  value /= radixBN;
60
65
  }
61
66
 
62
- const outputType = this.outputBits != 0 ? Uint1 : Uint8;
67
+ const outputType = outputBits != 0 ? Uint1 : Uint8;
63
68
  const res = limbArray.map(byte => new outputType(byte));
64
69
  memory.setSlice(dstOffset, res);
65
70
 
66
- memory.assert({ reads: 2, writes: this.numLimbs, addressing });
71
+ memory.assert({ reads: 4, writes: numLimbs, addressing });
67
72
  }
68
73
  }
@@ -1,5 +1,3 @@
1
- import { Fr, FunctionSelector, Gas, PUBLIC_DISPATCH_SELECTOR } from '@aztec/circuits.js';
2
-
3
1
  import type { AvmContext } from '../avm_context.js';
4
2
  import { type AvmContractCallResult } from '../avm_contract_call_result.js';
5
3
  import { type Field, TypeTag, Uint1 } from '../avm_memory_types.js';
@@ -45,7 +43,6 @@ abstract class ExternalCall extends Instruction {
45
43
 
46
44
  const callAddress = memory.getAs<Field>(addrOffset);
47
45
  const calldata = memory.getSlice(argsOffset, calldataSize).map(f => f.toFr());
48
- const functionSelector = new Fr(PUBLIC_DISPATCH_SELECTOR);
49
46
  // If we are already in a static call, we propagate the environment.
50
47
  const callType = context.environment.isStaticCall ? 'STATICCALL' : this.type;
51
48
 
@@ -62,15 +59,10 @@ abstract class ExternalCall extends Instruction {
62
59
  const allocatedGas = { l2Gas: allocatedL2Gas, daGas: allocatedDaGas };
63
60
  context.machineState.consumeGas(allocatedGas);
64
61
 
65
- const nestedContext = context.createNestedContractCallContext(
66
- callAddress.toAztecAddress(),
67
- calldata,
68
- allocatedGas,
69
- callType,
70
- FunctionSelector.fromField(functionSelector),
71
- );
62
+ const aztecAddress = callAddress.toAztecAddress();
63
+ const nestedContext = context.createNestedContractCallContext(aztecAddress, calldata, allocatedGas, callType);
72
64
 
73
- const simulator = new AvmSimulator(nestedContext);
65
+ const simulator = await AvmSimulator.build(nestedContext);
74
66
  const nestedCallResults: AvmContractCallResult = await simulator.execute();
75
67
  const success = !nestedCallResults.reverted;
76
68
 
@@ -102,14 +94,6 @@ abstract class ExternalCall extends Instruction {
102
94
  } else {
103
95
  context.persistableState.reject(nestedContext.persistableState);
104
96
  }
105
- await context.persistableState.traceNestedCall(
106
- /*nestedState=*/ nestedContext.persistableState,
107
- /*nestedEnvironment=*/ nestedContext.environment,
108
- /*startGasLeft=*/ Gas.from(allocatedGas),
109
- /*bytecode=*/ simulator.getBytecode()!,
110
- /*avmCallResults=*/ nestedCallResults,
111
- );
112
-
113
97
  memory.assert({ reads: calldataSize + 4, writes: 1, addressing });
114
98
  }
115
99