@aztec/simulator 0.70.0 → 0.72.1

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 (78) hide show
  1. package/dest/acvm/deserialize.d.ts +9 -0
  2. package/dest/acvm/deserialize.d.ts.map +1 -1
  3. package/dest/acvm/deserialize.js +12 -1
  4. package/dest/acvm/oracle/oracle.d.ts +5 -10
  5. package/dest/acvm/oracle/oracle.d.ts.map +1 -1
  6. package/dest/acvm/oracle/oracle.js +25 -32
  7. package/dest/acvm/oracle/typed_oracle.d.ts +5 -3
  8. package/dest/acvm/oracle/typed_oracle.d.ts.map +1 -1
  9. package/dest/acvm/oracle/typed_oracle.js +14 -8
  10. package/dest/avm/errors.d.ts +6 -0
  11. package/dest/avm/errors.d.ts.map +1 -1
  12. package/dest/avm/errors.js +10 -1
  13. package/dest/avm/journal/journal.d.ts +2 -3
  14. package/dest/avm/journal/journal.d.ts.map +1 -1
  15. package/dest/avm/journal/journal.js +5 -6
  16. package/dest/avm/opcodes/accrued_substate.d.ts.map +1 -1
  17. package/dest/avm/opcodes/accrued_substate.js +3 -2
  18. package/dest/avm/opcodes/conversion.d.ts.map +1 -1
  19. package/dest/avm/opcodes/conversion.js +10 -7
  20. package/dest/avm/opcodes/ec_add.js +2 -2
  21. package/dest/avm/opcodes/memory.d.ts.map +1 -1
  22. package/dest/avm/opcodes/memory.js +9 -5
  23. package/dest/avm/opcodes/multi_scalar_mul.d.ts.map +1 -1
  24. package/dest/avm/opcodes/multi_scalar_mul.js +9 -7
  25. package/dest/client/client_execution_context.d.ts +1 -5
  26. package/dest/client/client_execution_context.d.ts.map +1 -1
  27. package/dest/client/client_execution_context.js +3 -9
  28. package/dest/client/db_oracle.d.ts +43 -12
  29. package/dest/client/db_oracle.d.ts.map +1 -1
  30. package/dest/client/private_execution.js +2 -2
  31. package/dest/client/simulator.js +2 -2
  32. package/dest/client/unconstrained_execution.d.ts.map +1 -1
  33. package/dest/client/unconstrained_execution.js +5 -2
  34. package/dest/client/view_data_oracle.d.ts +5 -2
  35. package/dest/client/view_data_oracle.d.ts.map +1 -1
  36. package/dest/client/view_data_oracle.js +32 -11
  37. package/dest/public/enqueued_call_side_effect_trace.d.ts +7 -10
  38. package/dest/public/enqueued_call_side_effect_trace.d.ts.map +1 -1
  39. package/dest/public/enqueued_call_side_effect_trace.js +21 -28
  40. package/dest/public/execution.d.ts +7 -25
  41. package/dest/public/execution.d.ts.map +1 -1
  42. package/dest/public/execution.js +1 -1
  43. package/dest/public/fixtures/index.d.ts +3 -3
  44. package/dest/public/fixtures/index.d.ts.map +1 -1
  45. package/dest/public/fixtures/index.js +52 -50
  46. package/dest/public/public_processor.d.ts +5 -6
  47. package/dest/public/public_processor.d.ts.map +1 -1
  48. package/dest/public/public_processor.js +17 -18
  49. package/dest/public/public_tx_context.js +2 -2
  50. package/dest/public/public_tx_simulator.d.ts +1 -1
  51. package/dest/public/public_tx_simulator.d.ts.map +1 -1
  52. package/dest/public/public_tx_simulator.js +5 -8
  53. package/dest/public/side_effect_trace_interface.d.ts +3 -4
  54. package/dest/public/side_effect_trace_interface.d.ts.map +1 -1
  55. package/package.json +10 -10
  56. package/src/acvm/deserialize.ts +12 -0
  57. package/src/acvm/oracle/oracle.ts +70 -34
  58. package/src/acvm/oracle/typed_oracle.ts +25 -8
  59. package/src/avm/errors.ts +10 -0
  60. package/src/avm/journal/journal.ts +4 -5
  61. package/src/avm/opcodes/accrued_substate.ts +2 -1
  62. package/src/avm/opcodes/conversion.ts +15 -6
  63. package/src/avm/opcodes/ec_add.ts +1 -1
  64. package/src/avm/opcodes/memory.ts +8 -4
  65. package/src/avm/opcodes/multi_scalar_mul.ts +8 -6
  66. package/src/client/client_execution_context.ts +2 -9
  67. package/src/client/db_oracle.ts +55 -12
  68. package/src/client/private_execution.ts +1 -1
  69. package/src/client/simulator.ts +1 -1
  70. package/src/client/unconstrained_execution.ts +4 -1
  71. package/src/client/view_data_oracle.ts +44 -14
  72. package/src/public/enqueued_call_side_effect_trace.ts +22 -34
  73. package/src/public/execution.ts +7 -31
  74. package/src/public/fixtures/index.ts +66 -66
  75. package/src/public/public_processor.ts +21 -19
  76. package/src/public/public_tx_context.ts +1 -1
  77. package/src/public/public_tx_simulator.ts +3 -10
  78. package/src/public/side_effect_trace_interface.ts +3 -3
@@ -2,15 +2,12 @@ import { MerkleTreeId, UnencryptedL2Log } from '@aztec/circuit-types';
2
2
  import { FunctionSelector, NoteSelector } from '@aztec/foundation/abi';
3
3
  import { AztecAddress } from '@aztec/foundation/aztec-address';
4
4
  import { Fr } from '@aztec/foundation/fields';
5
- import { createLogger } from '@aztec/foundation/log';
6
5
 
7
6
  import { type ACVMField } from '../acvm_types.js';
8
- import { frToBoolean, frToNumber, fromACVMField } from '../deserialize.js';
7
+ import { frToBoolean, frToNumber, fromACVMField, fromBoundedVec } from '../deserialize.js';
9
8
  import { toACVMField } from '../serialize.js';
10
9
  import { type TypedOracle } from './typed_oracle.js';
11
10
 
12
- const logger = createLogger('simulator:acvm:oracle');
13
-
14
11
  /**
15
12
  * A data source that has all the apis required by Aztec.nr.
16
13
  */
@@ -22,11 +19,6 @@ export class Oracle {
22
19
  return toACVMField(val);
23
20
  }
24
21
 
25
- async storeArrayInExecutionCache(values: ACVMField[]): Promise<ACVMField> {
26
- const hash = await this.typedOracle.storeArrayInExecutionCache(values.map(fromACVMField));
27
- return toACVMField(hash);
28
- }
29
-
30
22
  // Since the argument is a slice, noir automatically adds a length field to oracle call.
31
23
  async storeInExecutionCache(_length: ACVMField[], values: ACVMField[]): Promise<ACVMField> {
32
24
  const hash = await this.typedOracle.storeInExecutionCache(values.map(fromACVMField));
@@ -384,34 +376,78 @@ export class Oracle {
384
376
  await this.typedOracle.syncNotes();
385
377
  }
386
378
 
387
- async store([contract]: ACVMField[], [key]: ACVMField[], values: ACVMField[]) {
388
- const processedContract = AztecAddress.fromField(fromACVMField(contract));
389
- const processedKey = fromACVMField(key);
390
- const processedValues = values.map(fromACVMField);
391
- logger.debug(`Storing data for key ${processedKey} in contract ${processedContract}. Data: [${processedValues}]`);
392
- await this.typedOracle.store(processedContract, processedKey, processedValues);
393
- }
394
-
395
- /**
396
- * Load data from pxe db.
397
- * @param contract - The contract address.
398
- * @param key - The key to load.
399
- * @param tSize - The size of the serialized object to return.
400
- * @returns The data found flag and the serialized object concatenated in one array.
401
- */
402
- async load([contract]: ACVMField[], [key]: ACVMField[], [tSize]: ACVMField[]): Promise<(ACVMField | ACVMField[])[]> {
403
- const processedContract = AztecAddress.fromField(fromACVMField(contract));
404
- const processedKey = fromACVMField(key);
405
- const values = await this.typedOracle.load(processedContract, processedKey);
379
+ async deliverNote(
380
+ [contractAddress]: ACVMField[],
381
+ [storageSlot]: ACVMField[],
382
+ [nonce]: ACVMField[],
383
+ content: ACVMField[],
384
+ [contentLength]: ACVMField[],
385
+ [noteHash]: ACVMField[],
386
+ [nullifier]: ACVMField[],
387
+ [txHash]: ACVMField[],
388
+ [recipient]: ACVMField[],
389
+ ): Promise<ACVMField> {
390
+ // TODO(#10728): try-catch this block and return false if we get an exception so that the contract can decide what
391
+ // to do if a note fails delivery (e.g. not increment the tagging index, or add it to some pending work list).
392
+ // Delivery might fail due to temporary issues, such as poor node connectivity.
393
+ await this.typedOracle.deliverNote(
394
+ AztecAddress.fromString(contractAddress),
395
+ fromACVMField(storageSlot),
396
+ fromACVMField(nonce),
397
+ fromBoundedVec(content, contentLength),
398
+ fromACVMField(noteHash),
399
+ fromACVMField(nullifier),
400
+ fromACVMField(txHash),
401
+ AztecAddress.fromString(recipient),
402
+ );
403
+
404
+ return toACVMField(true);
405
+ }
406
+
407
+ async dbStore([contractAddress]: ACVMField[], [slot]: ACVMField[], values: ACVMField[]) {
408
+ await this.typedOracle.dbStore(
409
+ AztecAddress.fromField(fromACVMField(contractAddress)),
410
+ fromACVMField(slot),
411
+ values.map(fromACVMField),
412
+ );
413
+ }
414
+
415
+ async dbLoad(
416
+ [contractAddress]: ACVMField[],
417
+ [slot]: ACVMField[],
418
+ [tSize]: ACVMField[],
419
+ ): Promise<(ACVMField | ACVMField[])[]> {
420
+ const values = await this.typedOracle.dbLoad(
421
+ AztecAddress.fromField(fromACVMField(contractAddress)),
422
+ fromACVMField(slot),
423
+ );
424
+
425
+ // We are going to return a Noir Option struct to represent the possibility of null values. Options are a struct
426
+ // with two fields: `some` (a boolean) and `value` (a field array in this case).
406
427
  if (values === null) {
407
- // No data was found so we set the data-found flag to 0 and we pad with zeros get the correct return size.
408
- const processedTSize = frToNumber(fromACVMField(tSize));
409
- logger.debug(`No data found for key ${processedKey} in contract ${processedContract}`);
410
- return [toACVMField(0), Array(processedTSize).fill(toACVMField(0))];
428
+ // No data was found so we set `some` to 0 and pad `value` with zeros get the correct return size.
429
+ return [toACVMField(0), Array(frToNumber(fromACVMField(tSize))).fill(toACVMField(0))];
411
430
  } else {
412
- // Data was found so we set the data-found flag to 1 and return it along with the data.
413
- logger.debug(`Returning data for key ${processedKey} in contract ${processedContract}. Data: [${values}]`);
431
+ // Data was found so we set `some` to 1 and return it along with `value`.
414
432
  return [toACVMField(1), values.map(toACVMField)];
415
433
  }
416
434
  }
435
+
436
+ async dbDelete([contractAddress]: ACVMField[], [slot]: ACVMField[]) {
437
+ await this.typedOracle.dbDelete(AztecAddress.fromField(fromACVMField(contractAddress)), fromACVMField(slot));
438
+ }
439
+
440
+ async dbCopy(
441
+ [contractAddress]: ACVMField[],
442
+ [srcSlot]: ACVMField[],
443
+ [dstSlot]: ACVMField[],
444
+ [numEntries]: ACVMField[],
445
+ ) {
446
+ await this.typedOracle.dbCopy(
447
+ AztecAddress.fromField(fromACVMField(contractAddress)),
448
+ fromACVMField(srcSlot),
449
+ fromACVMField(dstSlot),
450
+ frToNumber(fromACVMField(numEntries)),
451
+ );
452
+ }
417
453
  }
@@ -56,10 +56,6 @@ export abstract class TypedOracle {
56
56
  return Fr.random();
57
57
  }
58
58
 
59
- storeArrayInExecutionCache(_args: Fr[]): Promise<Fr> {
60
- throw new OracleMethodNotAvailableError('storeArrayInExecutionCache');
61
- }
62
-
63
59
  storeInExecutionCache(_values: Fr[]): Promise<Fr> {
64
60
  throw new OracleMethodNotAvailableError('storeInExecutionCache');
65
61
  }
@@ -237,11 +233,32 @@ export abstract class TypedOracle {
237
233
  throw new OracleMethodNotAvailableError('syncNotes');
238
234
  }
239
235
 
240
- store(_contract: AztecAddress, _key: Fr, _values: Fr[]): Promise<void> {
241
- throw new OracleMethodNotAvailableError('store');
236
+ deliverNote(
237
+ _contractAddress: AztecAddress,
238
+ _storageSlot: Fr,
239
+ _nonce: Fr,
240
+ _content: Fr[],
241
+ _noteHash: Fr,
242
+ _nullifier: Fr,
243
+ _txHash: Fr,
244
+ _recipient: AztecAddress,
245
+ ): Promise<void> {
246
+ throw new OracleMethodNotAvailableError('deliverNote');
247
+ }
248
+
249
+ dbStore(_contractAddress: AztecAddress, _key: Fr, _values: Fr[]): Promise<void> {
250
+ throw new OracleMethodNotAvailableError('dbStore');
251
+ }
252
+
253
+ dbLoad(_contractAddress: AztecAddress, _key: Fr): Promise<Fr[] | null> {
254
+ throw new OracleMethodNotAvailableError('dbLoad');
255
+ }
256
+
257
+ dbDelete(_contractAddress: AztecAddress, _key: Fr): Promise<void> {
258
+ throw new OracleMethodNotAvailableError('dbDelete');
242
259
  }
243
260
 
244
- load(_contract: AztecAddress, _key: Fr): Promise<Fr[] | null> {
245
- throw new OracleMethodNotAvailableError('load');
261
+ dbCopy(_contractAddress: AztecAddress, _srcKey: Fr, _dstKey: Fr, _numEntries: number): Promise<void> {
262
+ throw new OracleMethodNotAvailableError('dbCopy');
246
263
  }
247
264
  }
package/src/avm/errors.ts CHANGED
@@ -148,6 +148,16 @@ export class MSMPointNotOnCurveError extends AvmExecutionError {
148
148
  }
149
149
  }
150
150
 
151
+ /**
152
+ * Error is thrown when some inputs of ToRadixBE are not valid.
153
+ */
154
+ export class InvalidToRadixInputsError extends AvmExecutionError {
155
+ constructor(errorString: string) {
156
+ super(errorString);
157
+ this.name = 'InvalidToRadixInputsError';
158
+ }
159
+ }
160
+
151
161
  /**
152
162
  * Error is thrown when a static call attempts to alter some state
153
163
  */
@@ -524,14 +524,13 @@ export class AvmPersistableStateManager {
524
524
  }
525
525
 
526
526
  /**
527
- * Write an unencrypted log
527
+ * Write a public log
528
528
  * @param contractAddress - address of the contract that emitted the log
529
- * @param event - log event selector
530
529
  * @param log - log contents
531
530
  */
532
- public writeUnencryptedLog(contractAddress: AztecAddress, log: Fr[]) {
533
- this.log.debug(`UnencryptedL2Log(${contractAddress}) += event with ${log.length} fields.`);
534
- this.trace.traceUnencryptedLog(contractAddress, log);
531
+ public writePublicLog(contractAddress: AztecAddress, log: Fr[]) {
532
+ this.log.debug(`PublicLog(${contractAddress}) += event with ${log.length} fields.`);
533
+ this.trace.tracePublicLog(contractAddress, log);
535
534
  }
536
535
 
537
536
  /**
@@ -201,6 +201,7 @@ export class L1ToL2MessageExists extends Instruction {
201
201
  }
202
202
 
203
203
  export class EmitUnencryptedLog extends Instruction {
204
+ // TODO(#11124): rename unencrypted -> public
204
205
  static type: string = 'EMITUNENCRYPTEDLOG';
205
206
  static readonly opcode: Opcode = Opcode.EMITUNENCRYPTEDLOG;
206
207
  // Informs (de)serialization. See Instruction.deserialize.
@@ -228,7 +229,7 @@ export class EmitUnencryptedLog extends Instruction {
228
229
 
229
230
  context.machineState.consumeGas(this.gasCost(logSize));
230
231
  const log = memory.getSlice(logOffset, logSize).map(f => f.toFr());
231
- context.persistableState.writeUnencryptedLog(contractAddress, log);
232
+ context.persistableState.writePublicLog(contractAddress, log);
232
233
 
233
234
  memory.assert({ reads: 1 + logSize, addressing });
234
235
  }
@@ -1,12 +1,12 @@
1
1
  import { type AvmContext } from '../avm_context.js';
2
2
  import { TypeTag, Uint1, Uint8 } from '../avm_memory_types.js';
3
- import { InstructionExecutionError } from '../errors.js';
3
+ import { InvalidToRadixInputsError } from '../errors.js';
4
4
  import { Opcode, OperandType } from '../serialization/instruction_serialization.js';
5
5
  import { Addressing } from './addressing_mode.js';
6
6
  import { Instruction } from './instruction.js';
7
7
 
8
8
  export class ToRadixBE extends Instruction {
9
- static type: string = 'TORADIXLE';
9
+ static type: string = 'TORADIXBE';
10
10
  static readonly opcode: Opcode = Opcode.TORADIXBE;
11
11
 
12
12
  // Informs (de)serialization. See Instruction.deserialize.
@@ -49,12 +49,21 @@ export class ToRadixBE extends Instruction {
49
49
 
50
50
  let value: bigint = memory.get(srcOffset).toBigInt();
51
51
  const radix: bigint = memory.get(radixOffset).toBigInt();
52
- if (numLimbs < 1) {
53
- throw new InstructionExecutionError(`ToRadixBE instruction's numLimbs should be > 0 (was ${numLimbs})`);
52
+
53
+ if (radix < 2 || radix > 256) {
54
+ throw new InvalidToRadixInputsError(`ToRadixBE instruction's radix should be in range [2,256] (was ${radix}).`);
54
55
  }
55
- if (radix > 256) {
56
- throw new InstructionExecutionError(`ToRadixBE instruction's radix should be <= 256 (was ${radix})`);
56
+
57
+ if (numLimbs < 1 && value != BigInt(0n)) {
58
+ throw new InvalidToRadixInputsError(
59
+ `ToRadixBE instruction's input value is not zero (was ${value}) but numLimbs zero.`,
60
+ );
57
61
  }
62
+
63
+ if (outputBits != 0 && radix != BigInt(2n)) {
64
+ throw new InvalidToRadixInputsError(`Radix ${radix} is not equal to 2 and bit mode is activated.`);
65
+ }
66
+
58
67
  const radixBN: bigint = BigInt(radix);
59
68
  const limbArray = new Array(numLimbs);
60
69
 
@@ -81,7 +81,7 @@ export class EcAdd extends Instruction {
81
81
  } else if (p2IsInfinite) {
82
82
  dest = p1;
83
83
  } else {
84
- dest = grumpkin.add(p1, p2);
84
+ dest = await grumpkin.add(p1, p2);
85
85
  }
86
86
 
87
87
  // Important to use setSlice() and not set() in the two following statements as
@@ -190,7 +190,10 @@ export class CalldataCopy extends Instruction {
190
190
  const copySize = memory.get(copySizeOffset).toNumber();
191
191
  context.machineState.consumeGas(this.gasCost(copySize));
192
192
 
193
- const transformedData = context.environment.calldata.slice(cdStart, cdStart + copySize).map(f => new Field(f));
193
+ // Values which are out-of-range of the calldata array will be set with Field(0);
194
+ const slice = context.environment.calldata.slice(cdStart, cdStart + copySize).map(f => new Field(f));
195
+ // slice has size = MIN(copySize, calldata.length - cdStart) as TS truncates out-of-range portion
196
+ const transformedData = [...slice, ...Array(copySize - slice.length).fill(new Field(0))];
194
197
 
195
198
  memory.setSlice(dstOffset, transformedData);
196
199
 
@@ -253,9 +256,10 @@ export class ReturndataCopy extends Instruction {
253
256
  const copySize = memory.get(copySizeOffset).toNumber();
254
257
  context.machineState.consumeGas(this.gasCost(copySize));
255
258
 
256
- const transformedData = context.machineState.nestedReturndata
257
- .slice(rdStart, rdStart + copySize)
258
- .map(f => new Field(f));
259
+ // Values which are out-of-range of the returndata array will be set with Field(0);
260
+ const slice = context.machineState.nestedReturndata.slice(rdStart, rdStart + copySize).map(f => new Field(f));
261
+ // slice has size = MIN(copySize, returndata.length - rdStart) as TS truncates out-of-range portion
262
+ const transformedData = [...slice, ...Array(copySize - slice.length).fill(new Field(0))];
259
263
 
260
264
  memory.setSlice(dstOffset, transformedData);
261
265
 
@@ -95,20 +95,22 @@ export class MultiScalarMul extends Instruction {
95
95
  const [firstBaseScalarPair, ...rest]: Array<[Point, Fq]> = grumpkinPoints.map((p, idx) => [p, scalarFqVector[idx]]);
96
96
  // Fold the points and scalars into a single point
97
97
  // We have to ensure get the first point, since the identity element (point at infinity) isn't quite working in ts
98
- const outputPoint = rest.reduce((acc, curr) => {
98
+ let acc = await grumpkin.mul(firstBaseScalarPair[0], firstBaseScalarPair[1]);
99
+ for (const curr of rest) {
99
100
  if (curr[1] === Fq.ZERO) {
100
101
  // If we multiply by 0, the result will the point at infinity - so we ignore it
101
- return acc;
102
+ continue;
102
103
  } else if (curr[0].inf) {
103
104
  // If we multiply the point at infinity by a scalar, it's still the point at infinity
104
- return acc;
105
+ continue;
105
106
  } else if (acc.inf) {
106
107
  // If we accumulator is the point at infinity, we can just return the current point
107
- return curr[0];
108
+ acc = curr[0];
108
109
  } else {
109
- return grumpkin.add(acc, grumpkin.mul(curr[0], curr[1]));
110
+ acc = await grumpkin.add(acc, await grumpkin.mul(curr[0], curr[1]));
110
111
  }
111
- }, grumpkin.mul(firstBaseScalarPair[0], firstBaseScalarPair[1]));
112
+ }
113
+ const outputPoint = acc;
112
114
 
113
115
  // Important to use setSlice() and not set() in the two following statements as
114
116
  // this checks that the offsets lie within memory range.
@@ -95,7 +95,7 @@ export class ClientExecutionContext extends ViewDataOracle {
95
95
  const args = this.executionCache.getPreimage(this.argsHash);
96
96
 
97
97
  if (args.length !== argumentsSize) {
98
- throw new Error('Invalid arguments size');
98
+ throw new Error(`Invalid arguments size: expected ${argumentsSize}, got ${args.length}`);
99
99
  }
100
100
 
101
101
  const privateContextInputs = new PrivateContextInputs(
@@ -160,14 +160,6 @@ export class ClientExecutionContext extends ViewDataOracle {
160
160
  return this.publicTeardownFunctionCall;
161
161
  }
162
162
 
163
- /**
164
- * Store values in the execution cache.
165
- * @param values - Values to store.
166
- */
167
- public override storeArrayInExecutionCache(args: Fr[]): Promise<Fr> {
168
- return Promise.resolve(this.executionCache.store(args));
169
- }
170
-
171
163
  /**
172
164
  * Store values in the execution cache.
173
165
  * @param values - Values to store.
@@ -327,6 +319,7 @@ export class ClientExecutionContext extends ViewDataOracle {
327
319
  * This fn exists because sha hashing the preimage
328
320
  * is too large to compile (16,200 fields, 518,400 bytes) => the oracle hashes it.
329
321
  * See private_context.nr
322
+ * TODO(#8945): Contract class logs are currently sha hashes. When these are fields, delete this.
330
323
  * @param log - The unencrypted log to be emitted.
331
324
  */
332
325
  public override emitContractClassLog(log: UnencryptedL2Log, counter: number) {
@@ -233,26 +233,69 @@ export interface DBOracle extends CommitmentsDB {
233
233
  */
234
234
  processTaggedLogs(logs: TxScopedL2Log[], recipient: AztecAddress): Promise<void>;
235
235
 
236
+ /**
237
+ * Delivers the preimage and metadata of a committed note so that it can be later requested via the `getNotes`
238
+ * oracle.
239
+ *
240
+ * @param contractAddress - The address of the contract that created the note (i.e. the siloing contract)
241
+ * @param storageSlot - The storage slot of the note - used for indexing in `getNotes`
242
+ * @param nonce - The nonce of the note used by the kernel to compute the unique note hash
243
+ * @param content - The note's content: this is the primary item to return in `getNotes`
244
+ * @param noteHash - The non-unique non-siloed note hash
245
+ * @param nullifier - The inner (non-siloed) note nullifier
246
+ * @param txHash - The transaction in which the note was added to the note hash tree
247
+ * @param recipient - The account that discovered the note
248
+ */
249
+ deliverNote(
250
+ contractAddress: AztecAddress,
251
+ storageSlot: Fr,
252
+ nonce: Fr,
253
+ content: Fr[],
254
+ noteHash: Fr,
255
+ nullifier: Fr,
256
+ txHash: Fr,
257
+ recipient: AztecAddress,
258
+ ): Promise<void>;
259
+
236
260
  /**
237
261
  * Removes all of a contract's notes that have been nullified from the note database.
238
262
  */
239
263
  removeNullifiedNotes(contractAddress: AztecAddress): Promise<void>;
240
264
 
241
265
  /**
242
- * Used by contracts during execution to store arbitrary data in the local PXE database. The data is siloed/scoped
243
- * to a specific `contract`.
244
- * @param contract - An address of a contract that is requesting to store the data.
245
- * @param key - A field element representing the key to store the data under.
246
- * @param values - An array of field elements representing the data to store.
266
+ * Stores arbitrary information in a per-contract non-volatile database, which can later be retrieved with `dbLoad`.
267
+ * * If data was already stored at this slot, it is overwrriten.
268
+ * @param contractAddress - The contract address to scope the data under.
269
+ * @param slot - The slot in the database in which to store the value. Slots need not be contiguous.
270
+ * @param values - The data to store.
247
271
  */
248
- store(contract: AztecAddress, key: Fr, values: Fr[]): Promise<void>;
272
+ dbStore(contractAddress: AztecAddress, slot: Fr, values: Fr[]): Promise<void>;
249
273
 
250
274
  /**
251
- * Used by contracts during execution to load arbitrary data from the local PXE database. The data is siloed/scoped
252
- * to a specific `contract`.
253
- * @param contract - An address of a contract that is requesting to load the data.
254
- * @param key - A field element representing the key under which to load the data..
255
- * @returns An array of field elements representing the stored data or `null` if no data is stored under the key.
275
+ * Returns data previously stored via `dbStore` in the per-contract non-volatile database.
276
+ * @param contractAddress - The contract address under which the data is scoped.
277
+ * @param slot - The slot in the database to read.
278
+ * @returns The stored data or `null` if no data is stored under the slot.
279
+ */
280
+ dbLoad(contractAddress: AztecAddress, slot: Fr): Promise<Fr[] | null>;
281
+
282
+ /**
283
+ * Deletes data in the per-contract non-volatile database. Does nothing if no data was present.
284
+ * @param contractAddress - The contract address under which the data is scoped.
285
+ * @param slot - The slot in the database to delete.
286
+ */
287
+ dbDelete(contractAddress: AztecAddress, slot: Fr): Promise<void>;
288
+
289
+ /**
290
+ * Copies a number of contiguous entries in the per-contract non-volatile database. This allows for efficient data
291
+ * structures by avoiding repeated calls to `dbLoad` and `dbStore`.
292
+ * Supports overlapping source and destination regions (which will result in the overlapped source values being
293
+ * overwritten). All copied slots must exist in the database (i.e. have been stored and not deleted)
294
+ *
295
+ * @param contractAddress - The contract address under which the data is scoped.
296
+ * @param srcSlot - The first slot to copy from.
297
+ * @param dstSlot - The first slot to copy to.
298
+ * @param numEntries - The number of entries to copy.
256
299
  */
257
- load(contract: AztecAddress, key: Fr): Promise<Fr[] | null>;
300
+ dbCopy(contractAddress: AztecAddress, srcSlot: Fr, dstSlot: Fr, numEntries: number): Promise<void>;
258
301
  }
@@ -29,7 +29,7 @@ export async function executePrivateFunction(
29
29
  log = createLogger('simulator:private_execution'),
30
30
  ): Promise<PrivateCallExecutionResult> {
31
31
  const functionName = await context.getDebugFunctionName();
32
- log.verbose(`Executing private function ${functionName}@${contractAddress}`);
32
+ log.verbose(`Executing private function ${functionName}`, { contract: contractAddress });
33
33
  const acir = artifact.bytecode;
34
34
  const initialWitness = context.getInitialWitness(artifact);
35
35
  const acvmCallback = new Oracle(context);
@@ -192,7 +192,7 @@ export class AcirSimulator {
192
192
  const execRequest: FunctionCall = {
193
193
  name: artifact.name,
194
194
  to: contractAddress,
195
- selector: FunctionSelector.empty(),
195
+ selector: FunctionSelector.fromNameAndParameters(artifact),
196
196
  type: FunctionType.UNCONSTRAINED,
197
197
  isStatic: artifact.isStatic,
198
198
  args: encodeArguments(artifact, [
@@ -22,7 +22,10 @@ export async function executeUnconstrainedFunction(
22
22
  args: Fr[],
23
23
  log = createLogger('simulator:unconstrained_execution'),
24
24
  ): Promise<AbiDecoded> {
25
- log.verbose(`Executing unconstrained function ${contractAddress}:${functionSelector}(${artifact.name})`);
25
+ log.verbose(`Executing unconstrained function ${artifact.name}`, {
26
+ contract: contractAddress,
27
+ selector: functionSelector,
28
+ });
26
29
 
27
30
  const acir = artifact.bytecode;
28
31
  const initialWitness = toACVMWitness(0, args);
@@ -310,23 +310,53 @@ export class ViewDataOracle extends TypedOracle {
310
310
  await this.db.removeNullifiedNotes(this.contractAddress);
311
311
  }
312
312
 
313
- public override store(contract: AztecAddress, key: Fr, values: Fr[]): Promise<void> {
314
- if (!contract.equals(this.contractAddress)) {
315
- // TODO(#10727): instead of this check check that this.contractAddress is allowed to process notes for contract
316
- throw new Error(
317
- `Contract address ${contract} does not match the oracle's contract address ${this.contractAddress}`,
318
- );
313
+ public override async deliverNote(
314
+ contractAddress: AztecAddress,
315
+ storageSlot: Fr,
316
+ nonce: Fr,
317
+ content: Fr[],
318
+ noteHash: Fr,
319
+ nullifier: Fr,
320
+ txHash: Fr,
321
+ recipient: AztecAddress,
322
+ ) {
323
+ // TODO(#10727): allow other contracts to deliver notes
324
+ if (!this.contractAddress.equals(contractAddress)) {
325
+ throw new Error(`Got a note delivery request from ${contractAddress}, expected ${this.contractAddress}`);
319
326
  }
320
- return this.db.store(this.contractAddress, key, values);
327
+
328
+ await this.db.deliverNote(contractAddress, storageSlot, nonce, content, noteHash, nullifier, txHash, recipient);
321
329
  }
322
330
 
323
- public override load(contract: AztecAddress, key: Fr): Promise<Fr[] | null> {
324
- if (!contract.equals(this.contractAddress)) {
325
- // TODO(#10727): instead of this check check that this.contractAddress is allowed to process notes for contract
326
- throw new Error(
327
- `Contract address ${contract} does not match the oracle's contract address ${this.contractAddress}`,
328
- );
331
+ public override dbStore(contractAddress: AztecAddress, slot: Fr, values: Fr[]): Promise<void> {
332
+ if (!contractAddress.equals(this.contractAddress)) {
333
+ // TODO(#10727): instead of this check that this.contractAddress is allowed to access the external DB
334
+ throw new Error(`Contract ${contractAddress} is not allowed to access ${this.contractAddress}'s PXE DB`);
335
+ }
336
+ return this.db.dbStore(this.contractAddress, slot, values);
337
+ }
338
+
339
+ public override dbLoad(contractAddress: AztecAddress, slot: Fr): Promise<Fr[] | null> {
340
+ if (!contractAddress.equals(this.contractAddress)) {
341
+ // TODO(#10727): instead of this check that this.contractAddress is allowed to access the external DB
342
+ throw new Error(`Contract ${contractAddress} is not allowed to access ${this.contractAddress}'s PXE DB`);
343
+ }
344
+ return this.db.dbLoad(this.contractAddress, slot);
345
+ }
346
+
347
+ public override dbDelete(contractAddress: AztecAddress, slot: Fr): Promise<void> {
348
+ if (!contractAddress.equals(this.contractAddress)) {
349
+ // TODO(#10727): instead of this check that this.contractAddress is allowed to access the external DB
350
+ throw new Error(`Contract ${contractAddress} is not allowed to access ${this.contractAddress}'s PXE DB`);
351
+ }
352
+ return this.db.dbDelete(this.contractAddress, slot);
353
+ }
354
+
355
+ public override dbCopy(contractAddress: AztecAddress, srcSlot: Fr, dstSlot: Fr, numEntries: number): Promise<void> {
356
+ if (!contractAddress.equals(this.contractAddress)) {
357
+ // TODO(#10727): instead of this check that this.contractAddress is allowed to access the external DB
358
+ throw new Error(`Contract ${contractAddress} is not allowed to access ${this.contractAddress}'s PXE DB`);
329
359
  }
330
- return this.db.load(this.contractAddress, key);
360
+ return this.db.dbCopy(this.contractAddress, srcSlot, dstSlot, numEntries);
331
361
  }
332
362
  }