@aztec/simulator 0.69.1-devnet → 0.70.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 (119) hide show
  1. package/dest/acvm/acvm.d.ts +1 -2
  2. package/dest/acvm/acvm.d.ts.map +1 -1
  3. package/dest/acvm/index.d.ts +1 -1
  4. package/dest/acvm/index.d.ts.map +1 -1
  5. package/dest/acvm/index.js +2 -2
  6. package/dest/acvm/oracle/oracle.d.ts +4 -4
  7. package/dest/acvm/oracle/oracle.d.ts.map +1 -1
  8. package/dest/acvm/oracle/oracle.js +14 -14
  9. package/dest/acvm/oracle/typed_oracle.d.ts +6 -16
  10. package/dest/acvm/oracle/typed_oracle.d.ts.map +1 -1
  11. package/dest/acvm/oracle/typed_oracle.js +10 -20
  12. package/dest/avm/avm_simulator.d.ts.map +1 -1
  13. package/dest/avm/avm_simulator.js +7 -2
  14. package/dest/avm/fixtures/index.d.ts +1 -1
  15. package/dest/avm/fixtures/index.d.ts.map +1 -1
  16. package/dest/avm/fixtures/index.js +2 -2
  17. package/dest/avm/journal/nullifiers.d.ts +1 -1
  18. package/dest/avm/journal/nullifiers.d.ts.map +1 -1
  19. package/dest/avm/journal/public_storage.d.ts +1 -1
  20. package/dest/avm/journal/public_storage.d.ts.map +1 -1
  21. package/dest/client/client_execution_context.d.ts +29 -18
  22. package/dest/client/client_execution_context.d.ts.map +1 -1
  23. package/dest/client/client_execution_context.js +42 -29
  24. package/dest/client/execution_note_cache.d.ts +27 -4
  25. package/dest/client/execution_note_cache.d.ts.map +1 -1
  26. package/dest/client/execution_note_cache.js +62 -12
  27. package/dest/client/index.d.ts +11 -3
  28. package/dest/client/index.d.ts.map +1 -1
  29. package/dest/client/index.js +11 -4
  30. package/dest/client/private_execution.d.ts +3 -2
  31. package/dest/client/private_execution.d.ts.map +1 -1
  32. package/dest/client/private_execution.js +9 -7
  33. package/dest/client/simulator.d.ts +4 -2
  34. package/dest/client/simulator.d.ts.map +1 -1
  35. package/dest/client/simulator.js +13 -8
  36. package/dest/client/unconstrained_execution.d.ts +2 -1
  37. package/dest/client/unconstrained_execution.d.ts.map +1 -1
  38. package/dest/client/unconstrained_execution.js +6 -4
  39. package/dest/client/view_data_oracle.d.ts +1 -1
  40. package/dest/common/hashed_values_cache.d.ts +28 -0
  41. package/dest/common/hashed_values_cache.d.ts.map +1 -0
  42. package/dest/common/{packed_values_cache.js → hashed_values_cache.js} +22 -22
  43. package/dest/common/index.d.ts +1 -1
  44. package/dest/common/index.js +1 -1
  45. package/dest/common/message_load_oracle_inputs.d.ts +15 -0
  46. package/dest/common/message_load_oracle_inputs.d.ts.map +1 -0
  47. package/dest/common/message_load_oracle_inputs.js +15 -0
  48. package/dest/common/simulation_provider.d.ts +19 -0
  49. package/dest/common/simulation_provider.d.ts.map +1 -0
  50. package/dest/common/simulation_provider.js +27 -0
  51. package/dest/common.d.ts +2 -0
  52. package/dest/common.d.ts.map +1 -0
  53. package/dest/common.js +2 -0
  54. package/dest/providers/acvm_native.d.ts +5 -2
  55. package/dest/providers/acvm_native.d.ts.map +1 -1
  56. package/dest/providers/acvm_native.js +5 -2
  57. package/dest/providers/acvm_wasm.d.ts +8 -2
  58. package/dest/providers/acvm_wasm.d.ts.map +1 -1
  59. package/dest/providers/acvm_wasm.js +31 -5
  60. package/dest/providers/acvm_wasm_with_blobs.d.ts +14 -2
  61. package/dest/providers/acvm_wasm_with_blobs.d.ts.map +1 -1
  62. package/dest/providers/acvm_wasm_with_blobs.js +24 -4
  63. package/dest/providers/factory.d.ts +1 -1
  64. package/dest/providers/factory.d.ts.map +1 -1
  65. package/dest/providers/factory.js +1 -1
  66. package/dest/providers/index.d.ts +1 -1
  67. package/dest/providers/index.d.ts.map +1 -1
  68. package/dest/providers/index.js +2 -2
  69. package/dest/public/db_interfaces.d.ts +1 -1
  70. package/dest/public/db_interfaces.d.ts.map +1 -1
  71. package/dest/public/fixtures/index.d.ts +3 -3
  72. package/dest/public/fixtures/index.d.ts.map +1 -1
  73. package/dest/public/fixtures/index.js +48 -18
  74. package/dest/public/public_db_sources.d.ts +2 -1
  75. package/dest/public/public_db_sources.d.ts.map +1 -1
  76. package/dest/public/public_db_sources.js +2 -2
  77. package/dest/public/public_tx_simulator.js +2 -2
  78. package/{src/index.ts → dest/server.d.ts} +1 -2
  79. package/dest/server.d.ts.map +1 -0
  80. package/dest/server.js +6 -0
  81. package/package.json +11 -12
  82. package/src/acvm/acvm.ts +1 -1
  83. package/src/acvm/index.ts +1 -1
  84. package/src/acvm/oracle/oracle.ts +14 -14
  85. package/src/acvm/oracle/typed_oracle.ts +12 -20
  86. package/src/avm/avm_simulator.ts +6 -1
  87. package/src/avm/fixtures/index.ts +2 -1
  88. package/src/avm/journal/nullifiers.ts +1 -1
  89. package/src/avm/journal/public_storage.ts +1 -1
  90. package/src/client/client_execution_context.ts +47 -30
  91. package/src/client/execution_note_cache.ts +67 -14
  92. package/src/client/index.ts +11 -3
  93. package/src/client/private_execution.ts +21 -17
  94. package/src/client/simulator.ts +20 -7
  95. package/src/client/unconstrained_execution.ts +17 -13
  96. package/src/common/hashed_values_cache.ts +55 -0
  97. package/src/common/index.ts +1 -1
  98. package/src/common/message_load_oracle_inputs.ts +15 -0
  99. package/src/common/simulation_provider.ts +45 -0
  100. package/src/common.ts +1 -0
  101. package/src/providers/acvm_native.ts +12 -2
  102. package/src/providers/acvm_wasm.ts +36 -9
  103. package/src/providers/acvm_wasm_with_blobs.ts +34 -9
  104. package/src/providers/factory.ts +1 -1
  105. package/src/providers/index.ts +1 -1
  106. package/src/public/db_interfaces.ts +1 -1
  107. package/src/public/fixtures/index.ts +62 -26
  108. package/src/public/public_db_sources.ts +3 -6
  109. package/src/public/public_tx_simulator.ts +1 -1
  110. package/{dest/index.d.ts → src/server.ts} +0 -3
  111. package/dest/common/packed_values_cache.d.ts +0 -28
  112. package/dest/common/packed_values_cache.d.ts.map +0 -1
  113. package/dest/index.d.ts.map +0 -1
  114. package/dest/index.js +0 -8
  115. package/dest/providers/simulation_provider.d.ts +0 -9
  116. package/dest/providers/simulation_provider.d.ts.map +0 -1
  117. package/dest/providers/simulation_provider.js +0 -2
  118. package/src/common/packed_values_cache.ts +0 -55
  119. package/src/providers/simulation_provider.ts +0 -10
@@ -25,32 +25,55 @@ export class ExecutionNoteCache {
25
25
  private noteMap: Map<bigint, PendingNote[]> = new Map();
26
26
 
27
27
  /**
28
- * The list of nullifiers created in this transaction.
29
- * This mapping maps from a contract address to the nullifiers emitted from the contract.
28
+ * This maps from a contract address to the nullifiers emitted from the contract.
30
29
  * The note which is nullified might be new or not (i.e., was generated in a previous transaction).
31
30
  * Note that their value (bigint representation) is used because Frs cannot be looked up in Sets.
32
31
  */
33
32
  private nullifierMap: Map<bigint, Set<bigint>> = new Map();
34
33
 
34
+ /**
35
+ * All nullifiers emitted in this transaction.
36
+ */
37
+ private allNullifiers: Set<bigint> = new Set();
38
+
35
39
  private minRevertibleSideEffectCounter = 0;
36
40
 
37
- constructor(private readonly txHash: Fr) {}
41
+ private inRevertiblePhase = false;
42
+
43
+ /**
44
+ * We don't need to use the tx request hash for nonces if another non revertible nullifier is emitted.
45
+ * In that case we disable injecting the tx request hash as a nullifier.
46
+ */
47
+ private usedTxRequestHashForNonces = true;
38
48
 
49
+ constructor(private readonly txRequestHash: Fr) {}
50
+
51
+ /**
52
+ * Enters the revertible phase of the transaction.
53
+ * @param minRevertibleSideEffectCounter - The counter at which the transaction enters the revertible phase.
54
+ */
39
55
  public setMinRevertibleSideEffectCounter(minRevertibleSideEffectCounter: number) {
40
- if (this.minRevertibleSideEffectCounter && this.minRevertibleSideEffectCounter !== minRevertibleSideEffectCounter) {
56
+ if (this.inRevertiblePhase) {
41
57
  throw new Error(
42
- `Cannot override minRevertibleSideEffectCounter. Current value: ${minRevertibleSideEffectCounter}. Previous value: ${this.minRevertibleSideEffectCounter}`,
58
+ `Cannot enter the revertible phase twice. Current counter: ${minRevertibleSideEffectCounter}. Previous enter counter: ${this.minRevertibleSideEffectCounter}`,
43
59
  );
44
60
  }
45
-
61
+ this.inRevertiblePhase = true;
46
62
  this.minRevertibleSideEffectCounter = minRevertibleSideEffectCounter;
47
63
 
64
+ let nonceGenerator = this.txRequestHash;
65
+ const nullifiers = this.getAllNullifiers();
66
+ if (nullifiers.length > 0) {
67
+ nonceGenerator = new Fr(nullifiers[0]);
68
+ this.usedTxRequestHashForNonces = false;
69
+ }
70
+
48
71
  // The existing pending notes are all non-revertible.
49
72
  // They cannot be squashed by nullifiers emitted after minRevertibleSideEffectCounter is set.
50
73
  // Their indexes in the tx are known at this point and won't change. So we can assign a nonce to each one of them.
51
74
  // The nonces will be used to create the "complete" nullifier.
52
75
  const updatedNotes = this.notes.map(({ note, counter }, i) => {
53
- const nonce = computeNoteHashNonce(this.txHash, i);
76
+ const nonce = computeNoteHashNonce(nonceGenerator, i);
54
77
  const uniqueNoteHash = computeUniqueNoteHash(nonce, siloNoteHash(note.contractAddress, note.noteHash));
55
78
  return {
56
79
  counter,
@@ -64,6 +87,17 @@ export class ExecutionNoteCache {
64
87
  updatedNotes.forEach(n => this.#addNote(n));
65
88
  }
66
89
 
90
+ public finish() {
91
+ // If we never entered the revertible phase, we need to use the tx request hash as a nonce for the notes if no nullifiers have been emitted.
92
+ if (!this.inRevertiblePhase) {
93
+ this.usedTxRequestHashForNonces = this.getAllNullifiers().length === 0;
94
+ }
95
+ // If we entered the revertible phase, the nonce generator was decided based on wether or not a nullifier was emitted before entering.
96
+ return {
97
+ usedTxRequestHashForNonces: this.usedTxRequestHashForNonces,
98
+ };
99
+ }
100
+
67
101
  /**
68
102
  * Add a new note to cache.
69
103
  * @param note - New note created during execution.
@@ -88,10 +122,6 @@ export class ExecutionNoteCache {
88
122
  */
89
123
  public nullifyNote(contractAddress: AztecAddress, innerNullifier: Fr, noteHash: Fr) {
90
124
  const siloedNullifier = siloNullifier(contractAddress, innerNullifier);
91
- const nullifiers = this.getNullifiers(contractAddress);
92
- nullifiers.add(siloedNullifier.value);
93
- this.nullifierMap.set(contractAddress.toBigInt(), nullifiers);
94
-
95
125
  let nullifiedNoteHashCounter: number | undefined = undefined;
96
126
  // Find and remove the matching new note and log(s) if the emitted noteHash is not empty.
97
127
  if (!noteHash.isEmpty()) {
@@ -105,10 +135,28 @@ export class ExecutionNoteCache {
105
135
  nullifiedNoteHashCounter = note.counter;
106
136
  this.noteMap.set(contractAddress.toBigInt(), notesInContract);
107
137
  this.notes = this.notes.filter(n => n.counter !== note.counter);
138
+
139
+ // If the note is non revertible and the nullifier was emitted in the revertible phase, both the note hash and the nullifier will be emitted
140
+ if (this.inRevertiblePhase && note.counter < this.minRevertibleSideEffectCounter) {
141
+ this.recordNullifier(contractAddress, siloedNullifier);
142
+ }
143
+ } else {
144
+ // If the note being nullified comes from a previous tx the nullifier will be emitted.
145
+ this.recordNullifier(contractAddress, siloedNullifier);
108
146
  }
109
147
  return nullifiedNoteHashCounter;
110
148
  }
111
149
 
150
+ /**
151
+ * Adds a nullifier to the cache. Note cache needs to track all nullifiers to decide which nullifier to use for note siloing.
152
+ * @param contractAddress - Contract address that emitted the nullifier.
153
+ * @param innerNullifier
154
+ */
155
+ public nullifierCreated(contractAddress: AztecAddress, innerNullifier: Fr) {
156
+ const siloedNullifier = siloNullifier(contractAddress, innerNullifier);
157
+ this.recordNullifier(contractAddress, siloedNullifier);
158
+ }
159
+
112
160
  /**
113
161
  * Return notes created up to current point in execution.
114
162
  * If a nullifier for a note in this list is emitted, the note will be deleted.
@@ -152,8 +200,13 @@ export class ExecutionNoteCache {
152
200
  }
153
201
 
154
202
  getAllNullifiers(): Fr[] {
155
- return [...this.nullifierMap.values()].flatMap(nullifierArray =>
156
- [...nullifierArray.values()].map(val => new Fr(val)),
157
- );
203
+ return [...this.allNullifiers].map(n => new Fr(n));
204
+ }
205
+
206
+ recordNullifier(contractAddress: AztecAddress, siloedNullifier: Fr) {
207
+ const nullifiers = this.getNullifiers(contractAddress);
208
+ nullifiers.add(siloedNullifier.toBigInt());
209
+ this.nullifierMap.set(contractAddress.toBigInt(), nullifiers);
210
+ this.allNullifiers.add(siloedNullifier.toBigInt());
158
211
  }
159
212
  }
@@ -1,6 +1,14 @@
1
- export * from './simulator.js';
2
- export * from './db_oracle.js';
1
+ export { AcirSimulator } from './simulator.js';
2
+ export { DBOracle, ContractClassNotFoundError, ContractNotFoundError } from './db_oracle.js';
3
3
  export * from './pick_notes.js';
4
- export * from './execution_note_cache.js';
4
+ export { ExecutionNoteCache } from './execution_note_cache.js';
5
5
  export { extractPrivateCircuitPublicInputs } from './private_execution.js';
6
+ export { witnessMapToFields } from '../acvm/deserialize.js';
7
+ export { toACVMWitness } from '../acvm/serialize.js';
8
+ export { extractCallStack } from '../acvm/acvm.js';
6
9
  export { WASMSimulator } from '../providers/acvm_wasm.js';
10
+ export { NoteData, TypedOracle } from '../acvm/oracle/typed_oracle.js';
11
+ export { Oracle } from '../acvm/oracle/oracle.js';
12
+ export { type SimulationProvider } from '../common/simulation_provider.js';
13
+ export { MessageLoadOracleInputs } from '../common/message_load_oracle_inputs.js';
14
+ export { resolveAssertionMessageFromRevertData, resolveOpcodeLocations } from '../common/errors.js';
@@ -1,4 +1,4 @@
1
- import { PrivateExecutionResult } from '@aztec/circuit-types';
1
+ import { PrivateCallExecutionResult } from '@aztec/circuit-types';
2
2
  import { type CircuitWitnessGenerationStats } from '@aztec/circuit-types/stats';
3
3
  import {
4
4
  Fr,
@@ -12,38 +12,42 @@ import { createLogger } from '@aztec/foundation/log';
12
12
  import { Timer } from '@aztec/foundation/timer';
13
13
 
14
14
  import { fromACVMField, witnessMapToFields } from '../acvm/deserialize.js';
15
- import { type ACVMWitness, Oracle, acvm, extractCallStack } from '../acvm/index.js';
15
+ import { type ACVMWitness, Oracle, extractCallStack } from '../acvm/index.js';
16
16
  import { ExecutionError, resolveAssertionMessageFromError } from '../common/errors.js';
17
+ import { type SimulationProvider } from '../server.js';
17
18
  import { type ClientExecutionContext } from './client_execution_context.js';
18
19
 
19
20
  /**
20
21
  * Execute a private function and return the execution result.
21
22
  */
22
23
  export async function executePrivateFunction(
24
+ simulator: SimulationProvider,
23
25
  context: ClientExecutionContext,
24
26
  artifact: FunctionArtifact,
25
27
  contractAddress: AztecAddress,
26
28
  functionSelector: FunctionSelector,
27
29
  log = createLogger('simulator:private_execution'),
28
- ): Promise<PrivateExecutionResult> {
30
+ ): Promise<PrivateCallExecutionResult> {
29
31
  const functionName = await context.getDebugFunctionName();
30
32
  log.verbose(`Executing private function ${functionName}@${contractAddress}`);
31
33
  const acir = artifact.bytecode;
32
34
  const initialWitness = context.getInitialWitness(artifact);
33
35
  const acvmCallback = new Oracle(context);
34
36
  const timer = new Timer();
35
- const acirExecutionResult = await acvm(acir, initialWitness, acvmCallback).catch((err: Error) => {
36
- err.message = resolveAssertionMessageFromError(err, artifact);
37
- throw new ExecutionError(
38
- err.message,
39
- {
40
- contractAddress,
41
- functionSelector,
42
- },
43
- extractCallStack(err, artifact.debug),
44
- { cause: err },
45
- );
46
- });
37
+ const acirExecutionResult = await simulator
38
+ .executeUserCircuit(acir, initialWitness, acvmCallback)
39
+ .catch((err: Error) => {
40
+ err.message = resolveAssertionMessageFromError(err, artifact);
41
+ throw new ExecutionError(
42
+ err.message,
43
+ {
44
+ contractAddress,
45
+ functionSelector,
46
+ },
47
+ extractCallStack(err, artifact.debug),
48
+ { cause: err },
49
+ );
50
+ });
47
51
  const duration = timer.ms();
48
52
  const partialWitness = acirExecutionResult.partialWitness;
49
53
  const publicInputs = extractPrivateCircuitPublicInputs(artifact, partialWitness);
@@ -61,7 +65,7 @@ export async function executePrivateFunction(
61
65
 
62
66
  const contractClassLogs = context.getContractClassLogs();
63
67
 
64
- const rawReturnValues = await context.unpackReturns(publicInputs.returnsHash);
68
+ const rawReturnValues = await context.loadFromExecutionCache(publicInputs.returnsHash);
65
69
 
66
70
  const noteHashLeafIndexMap = context.getNoteHashLeafIndexMap();
67
71
  const newNotes = context.getNewNotes();
@@ -72,7 +76,7 @@ export async function executePrivateFunction(
72
76
 
73
77
  log.debug(`Returning from call to ${contractAddress.toString()}:${functionSelector}`);
74
78
 
75
- return new PrivateExecutionResult(
79
+ return new PrivateCallExecutionResult(
76
80
  acir,
77
81
  Buffer.from(artifact.verificationKey!, 'base64'),
78
82
  partialWitness,
@@ -1,4 +1,10 @@
1
- import type { AztecNode, FunctionCall, Note, PrivateExecutionResult, TxExecutionRequest } from '@aztec/circuit-types';
1
+ import {
2
+ type AztecNode,
3
+ type FunctionCall,
4
+ type Note,
5
+ PrivateExecutionResult,
6
+ type TxExecutionRequest,
7
+ } from '@aztec/circuit-types';
2
8
  import { CallContext } from '@aztec/circuits.js';
3
9
  import {
4
10
  type ArrayType,
@@ -13,7 +19,8 @@ import { Fr } from '@aztec/foundation/fields';
13
19
  import { type Logger, createLogger } from '@aztec/foundation/log';
14
20
 
15
21
  import { createSimulationError } from '../common/errors.js';
16
- import { PackedValuesCache } from '../common/packed_values_cache.js';
22
+ import { HashedValuesCache } from '../common/hashed_values_cache.js';
23
+ import { type SimulationProvider } from '../common/simulation_provider.js';
17
24
  import { ClientExecutionContext } from './client_execution_context.js';
18
25
  import { type DBOracle } from './db_oracle.js';
19
26
  import { ExecutionNoteCache } from './execution_note_cache.js';
@@ -27,7 +34,7 @@ import { ViewDataOracle } from './view_data_oracle.js';
27
34
  export class AcirSimulator {
28
35
  private log: Logger;
29
36
 
30
- constructor(private db: DBOracle, private node: AztecNode) {
37
+ constructor(private db: DBOracle, private node: AztecNode, private simulationProvider: SimulationProvider) {
31
38
  this.log = createLogger('simulator');
32
39
  }
33
40
 
@@ -69,7 +76,8 @@ export class AcirSimulator {
69
76
  entryPointArtifact.isStatic,
70
77
  );
71
78
 
72
- const txHash = request.toTxRequest().hash();
79
+ const txRequestHash = request.toTxRequest().hash();
80
+ const noteCache = new ExecutionNoteCache(txRequestHash);
73
81
 
74
82
  const context = new ClientExecutionContext(
75
83
  request.firstCallArgsHash,
@@ -77,10 +85,11 @@ export class AcirSimulator {
77
85
  callContext,
78
86
  header,
79
87
  request.authWitnesses,
80
- PackedValuesCache.create(request.argsOfCalls),
81
- new ExecutionNoteCache(txHash),
88
+ HashedValuesCache.create(request.argsOfCalls),
89
+ noteCache,
82
90
  this.db,
83
91
  this.node,
92
+ this.simulationProvider,
84
93
  startSideEffectCounter,
85
94
  undefined,
86
95
  scopes,
@@ -88,12 +97,15 @@ export class AcirSimulator {
88
97
 
89
98
  try {
90
99
  const executionResult = await executePrivateFunction(
100
+ this.simulationProvider,
91
101
  context,
92
102
  entryPointArtifact,
93
103
  contractAddress,
94
104
  request.functionSelector,
95
105
  );
96
- return executionResult;
106
+ const { usedTxRequestHashForNonces } = noteCache.finish();
107
+ const firstNullifierHint = usedTxRequestHashForNonces ? Fr.ZERO : noteCache.getAllNullifiers()[0];
108
+ return new PrivateExecutionResult(executionResult, firstNullifierHint);
97
109
  } catch (err) {
98
110
  throw createSimulationError(err instanceof Error ? err : new Error('Unknown error during private execution'));
99
111
  }
@@ -120,6 +132,7 @@ export class AcirSimulator {
120
132
 
121
133
  try {
122
134
  return await executeUnconstrainedFunction(
135
+ this.simulationProvider,
123
136
  context,
124
137
  entryPointArtifact,
125
138
  contractAddress,
@@ -4,8 +4,9 @@ import { type Fr } from '@aztec/foundation/fields';
4
4
  import { createLogger } from '@aztec/foundation/log';
5
5
 
6
6
  import { witnessMapToFields } from '../acvm/deserialize.js';
7
- import { Oracle, acvm, extractCallStack, toACVMWitness } from '../acvm/index.js';
7
+ import { Oracle, extractCallStack, toACVMWitness } from '../acvm/index.js';
8
8
  import { ExecutionError, resolveAssertionMessageFromError } from '../common/errors.js';
9
+ import { type SimulationProvider } from '../server.js';
9
10
  import { type ViewDataOracle } from './view_data_oracle.js';
10
11
 
11
12
  // docs:start:execute_unconstrained_function
@@ -13,6 +14,7 @@ import { type ViewDataOracle } from './view_data_oracle.js';
13
14
  * Execute an unconstrained function and return the decoded values.
14
15
  */
15
16
  export async function executeUnconstrainedFunction(
17
+ simulatorProvider: SimulationProvider,
16
18
  oracle: ViewDataOracle,
17
19
  artifact: FunctionArtifact,
18
20
  contractAddress: AztecAddress,
@@ -24,18 +26,20 @@ export async function executeUnconstrainedFunction(
24
26
 
25
27
  const acir = artifact.bytecode;
26
28
  const initialWitness = toACVMWitness(0, args);
27
- const acirExecutionResult = await acvm(acir, initialWitness, new Oracle(oracle)).catch((err: Error) => {
28
- err.message = resolveAssertionMessageFromError(err, artifact);
29
- throw new ExecutionError(
30
- err.message,
31
- {
32
- contractAddress,
33
- functionSelector,
34
- },
35
- extractCallStack(err, artifact.debug),
36
- { cause: err },
37
- );
38
- });
29
+ const acirExecutionResult = await simulatorProvider
30
+ .executeUserCircuit(acir, initialWitness, new Oracle(oracle))
31
+ .catch((err: Error) => {
32
+ err.message = resolveAssertionMessageFromError(err, artifact);
33
+ throw new ExecutionError(
34
+ err.message,
35
+ {
36
+ contractAddress,
37
+ functionSelector,
38
+ },
39
+ extractCallStack(err, artifact.debug),
40
+ { cause: err },
41
+ );
42
+ });
39
43
 
40
44
  const returnWitness = witnessMapToFields(acirExecutionResult.returnWitness);
41
45
  return decodeFromAbi(artifact.returnTypes, returnWitness);
@@ -0,0 +1,55 @@
1
+ import { HashedValues } from '@aztec/circuit-types';
2
+ import { Fr } from '@aztec/circuits.js';
3
+
4
+ /**
5
+ * A cache for hashed values (arguments, returns) during transaction execution.
6
+ */
7
+ export class HashedValuesCache {
8
+ private cache: Map<bigint, Fr[]>;
9
+
10
+ constructor(initialArguments: HashedValues[] = []) {
11
+ this.cache = new Map();
12
+ for (const initialArg of initialArguments) {
13
+ this.cache.set(initialArg.hash.toBigInt(), initialArg.values);
14
+ }
15
+ }
16
+
17
+ /**
18
+ * Creates a new hashed values cache.
19
+ * @param initialArguments - The initial arguments to add to the cache.
20
+ * @returns The new hashed values cache.
21
+ */
22
+ public static create(initialArguments: HashedValues[] = []) {
23
+ return new HashedValuesCache(initialArguments);
24
+ }
25
+
26
+ /**
27
+ * Gets preimage of a hash.
28
+ * @param hash - The hash to get the preimage of.
29
+ * @returns The preimage.
30
+ */
31
+ public getPreimage(hash: Fr): Fr[] {
32
+ if (hash.equals(Fr.ZERO)) {
33
+ return [];
34
+ }
35
+ const hashedValues = this.cache.get(hash.value);
36
+ if (!hashedValues) {
37
+ throw new Error(`Preimage for hash ${hash.toString()} not found in cache`);
38
+ }
39
+ return hashedValues;
40
+ }
41
+
42
+ /**
43
+ * Stores values in cache and returns its hash.
44
+ * @param values - The values to store.
45
+ * @returns The hash of the values.
46
+ */
47
+ public store(values: Fr[]) {
48
+ if (values.length === 0) {
49
+ return Fr.ZERO;
50
+ }
51
+ const hashedValues = HashedValues.fromValues(values);
52
+ this.cache.set(hashedValues.hash.value, hashedValues.values);
53
+ return hashedValues.hash;
54
+ }
55
+ }
@@ -1,2 +1,2 @@
1
- export * from './packed_values_cache.js';
1
+ export * from './hashed_values_cache.js';
2
2
  export * from './errors.js';
@@ -0,0 +1,15 @@
1
+ import { type SiblingPath } from '@aztec/circuit-types';
2
+ import { Fr } from '@aztec/circuits.js';
3
+
4
+ export class MessageLoadOracleInputs<N extends number> {
5
+ constructor(
6
+ /** The index of the message commitment in the merkle tree. */
7
+ public index: bigint,
8
+ /** The path in the merkle tree to the message. */
9
+ public siblingPath: SiblingPath<N>,
10
+ ) {}
11
+
12
+ toFields(): Fr[] {
13
+ return [new Fr(this.index), ...this.siblingPath.toFields()];
14
+ }
15
+ }
@@ -0,0 +1,45 @@
1
+ import { type NoirCompiledCircuit } from '@aztec/types/noir';
2
+
3
+ import { type ExecutionError } from '@noir-lang/acvm_js';
4
+ import { abiDecodeError } from '@noir-lang/noirc_abi';
5
+ import { type Abi, type WitnessMap } from '@noir-lang/types';
6
+
7
+ import { type ACIRCallback, type ACIRExecutionResult } from '../acvm/acvm.js';
8
+ import { type ACVMWitness } from '../acvm/acvm_types.js';
9
+
10
+ /**
11
+ * Low level simulation interface
12
+ */
13
+ export interface SimulationProvider {
14
+ executeProtocolCircuit(input: WitnessMap, compiledCircuit: NoirCompiledCircuit): Promise<WitnessMap>;
15
+ executeUserCircuit(acir: Buffer, initialWitness: ACVMWitness, callback: ACIRCallback): Promise<ACIRExecutionResult>;
16
+ }
17
+
18
+ export type ErrorWithPayload = ExecutionError & { decodedAssertionPayload?: any };
19
+
20
+ // Error handling taken from noir/noir-repo/tooling/noir_js/src/witness_generation.ts.
21
+ // TODO: import this in isolation without having to import noir_js in its entirety.
22
+ export function parseErrorPayload(abi: Abi, originalError: ExecutionError): Error {
23
+ const payload = originalError.rawAssertionPayload;
24
+ if (!payload) {
25
+ return originalError;
26
+ }
27
+ const enrichedError = originalError as ErrorWithPayload;
28
+
29
+ try {
30
+ // Decode the payload
31
+ const decodedPayload = abiDecodeError(abi, payload);
32
+
33
+ if (typeof decodedPayload === 'string') {
34
+ // If it's a string, just add it to the error message
35
+ enrichedError.message = `Circuit execution failed: ${decodedPayload}`;
36
+ } else {
37
+ // If not, attach the payload to the original error
38
+ enrichedError.decodedAssertionPayload = decodedPayload;
39
+ }
40
+ } catch (_errorDecoding) {
41
+ // Ignore errors decoding the payload
42
+ }
43
+
44
+ return enrichedError;
45
+ }
package/src/common.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './common/index.js';
@@ -7,7 +7,9 @@ import { type WitnessMap } from '@noir-lang/types';
7
7
  import * as proc from 'child_process';
8
8
  import { promises as fs } from 'fs';
9
9
 
10
- import { type SimulationProvider } from './simulation_provider.js';
10
+ import { type ACIRCallback, type ACIRExecutionResult } from '../acvm/acvm.js';
11
+ import { type ACVMWitness } from '../acvm/acvm_types.js';
12
+ import { type SimulationProvider } from '../common/simulation_provider.js';
11
13
 
12
14
  const logger = createLogger('simulator:acvm-native');
13
15
 
@@ -134,7 +136,7 @@ export async function executeNativeCircuit(
134
136
 
135
137
  export class NativeACVMSimulator implements SimulationProvider {
136
138
  constructor(private workingDirectory: string, private pathToAcvm: string, private witnessFilename?: string) {}
137
- async simulateCircuit(input: WitnessMap, compiledCircuit: NoirCompiledCircuit): Promise<WitnessMap> {
139
+ async executeProtocolCircuit(input: WitnessMap, compiledCircuit: NoirCompiledCircuit): Promise<WitnessMap> {
138
140
  // Execute the circuit on those initial witness values
139
141
 
140
142
  const operation = async (directory: string) => {
@@ -158,4 +160,12 @@ export class NativeACVMSimulator implements SimulationProvider {
158
160
 
159
161
  return await runInDirectory(this.workingDirectory, operation, false);
160
162
  }
163
+
164
+ executeUserCircuit(
165
+ _acir: Buffer,
166
+ _initialWitness: ACVMWitness,
167
+ _callback: ACIRCallback,
168
+ ): Promise<ACIRExecutionResult> {
169
+ throw new Error('Not implemented');
170
+ }
161
171
  }
@@ -1,25 +1,52 @@
1
1
  import { foreignCallHandler } from '@aztec/noir-protocol-circuits-types/client';
2
2
  import { type NoirCompiledCircuit } from '@aztec/types/noir';
3
3
 
4
- import { executeCircuit } from '@noir-lang/acvm_js';
4
+ import initACVM, { type ExecutionError, executeCircuit } from '@noir-lang/acvm_js';
5
+ import initAbi from '@noir-lang/noirc_abi';
5
6
  import { type WitnessMap } from '@noir-lang/types';
6
7
 
7
- import { type SimulationProvider } from './simulation_provider.js';
8
+ import { type ACIRCallback, acvm } from '../acvm/acvm.js';
9
+ import { type ACVMWitness } from '../acvm/acvm_types.js';
10
+ import { type SimulationProvider, parseErrorPayload } from '../common/simulation_provider.js';
8
11
 
9
12
  export class WASMSimulator implements SimulationProvider {
10
- async simulateCircuit(input: WitnessMap, compiledCircuit: NoirCompiledCircuit): Promise<WitnessMap> {
13
+ async init(): Promise<void> {
14
+ // If these are available, then we are in the
15
+ // web environment. For the node environment, this
16
+ // is a no-op.
17
+ if (typeof initAbi === 'function') {
18
+ /** @ts-expect-error The node bundle doesn't include these default imports, so TS complains */
19
+ await Promise.all([initAbi(), initACVM()]);
20
+ }
21
+ }
22
+
23
+ async executeProtocolCircuit(input: WitnessMap, compiledCircuit: NoirCompiledCircuit): Promise<WitnessMap> {
24
+ await this.init();
11
25
  // Execute the circuit on those initial witness values
12
26
  //
13
27
  // Decode the bytecode from base64 since the acvm does not know about base64 encoding
14
28
  const decodedBytecode = Buffer.from(compiledCircuit.bytecode, 'base64');
15
29
  //
16
30
  // Execute the circuit
17
- const _witnessMap = await executeCircuit(
18
- decodedBytecode,
19
- input,
20
- foreignCallHandler, // handle calls to debug_log
21
- );
31
+ try {
32
+ const _witnessMap = await executeCircuit(
33
+ decodedBytecode,
34
+ input,
35
+ foreignCallHandler, // handle calls to debug_log
36
+ );
37
+
38
+ return _witnessMap;
39
+ } catch (err) {
40
+ // Typescript types catched errors as unknown or any, so we need to narrow its type to check if it has raw assertion payload.
41
+ if (typeof err === 'object' && err !== null && 'rawAssertionPayload' in err) {
42
+ throw parseErrorPayload(compiledCircuit.abi, err as ExecutionError);
43
+ }
44
+ throw new Error(`Circuit execution failed: ${err}`);
45
+ }
46
+ }
22
47
 
23
- return _witnessMap;
48
+ async executeUserCircuit(acir: Buffer, initialWitness: ACVMWitness, callback: ACIRCallback) {
49
+ await this.init();
50
+ return acvm(acir, initialWitness, callback);
24
51
  }
25
52
  }
@@ -1,25 +1,50 @@
1
1
  import { foreignCallHandler } from '@aztec/noir-protocol-circuits-types/server';
2
2
  import { type NoirCompiledCircuit } from '@aztec/types/noir';
3
3
 
4
- import { executeCircuit } from '@noir-lang/acvm_js';
4
+ import { type ExecutionError, executeCircuit } from '@noir-lang/acvm_js';
5
5
  import { type WitnessMap } from '@noir-lang/types';
6
6
 
7
- import { type SimulationProvider } from './simulation_provider.js';
7
+ import { type ACIRCallback, type ACIRExecutionResult } from '../acvm/acvm.js';
8
+ import { type ACVMWitness } from '../acvm/acvm_types.js';
9
+ import { type SimulationProvider, parseErrorPayload } from '../common/simulation_provider.js';
8
10
 
11
+ /**
12
+ * A simulation provider that uses the WASM simulator with the ability to handle blobs via the foreign call handler.
13
+ * This class is temporary while brillig cannot handle the blob math, and it is kept separate
14
+ * because the zkg commitment library used in the blob code is not browser compatible.
15
+ *
16
+ * It is only used in the context of server-side code executing simulated protocol circuits.
17
+ */
9
18
  export class WASMSimulatorWithBlobs implements SimulationProvider {
10
- async simulateCircuit(input: WitnessMap, compiledCircuit: NoirCompiledCircuit): Promise<WitnessMap> {
19
+ async executeProtocolCircuit(input: WitnessMap, compiledCircuit: NoirCompiledCircuit): Promise<WitnessMap> {
11
20
  // Execute the circuit on those initial witness values
12
21
  //
13
22
  // Decode the bytecode from base64 since the acvm does not know about base64 encoding
14
23
  const decodedBytecode = Buffer.from(compiledCircuit.bytecode, 'base64');
15
24
  //
16
25
  // Execute the circuit
17
- const _witnessMap = await executeCircuit(
18
- decodedBytecode,
19
- input,
20
- foreignCallHandler, // handle calls to debug_log and evaluate_blobs mock
21
- );
26
+ try {
27
+ const _witnessMap = await executeCircuit(
28
+ decodedBytecode,
29
+ input,
30
+ foreignCallHandler, // handle calls to debug_log and evaluate_blobs mock
31
+ );
22
32
 
23
- return _witnessMap;
33
+ return _witnessMap;
34
+ } catch (err) {
35
+ // Typescript types catched errors as unknown or any, so we need to narrow its type to check if it has raw assertion payload.
36
+ if (typeof err === 'object' && err !== null && 'rawAssertionPayload' in err) {
37
+ throw parseErrorPayload(compiledCircuit.abi, err as ExecutionError);
38
+ }
39
+ throw new Error(`Circuit execution failed: ${err}`);
40
+ }
41
+ }
42
+
43
+ executeUserCircuit(
44
+ _acir: Buffer,
45
+ _initialWitness: ACVMWitness,
46
+ _callback: ACIRCallback,
47
+ ): Promise<ACIRExecutionResult> {
48
+ throw new Error('Not implemented');
24
49
  }
25
50
  }
@@ -2,9 +2,9 @@ import { type Logger, createLogger } from '@aztec/foundation/log';
2
2
 
3
3
  import { promises as fs } from 'fs';
4
4
 
5
+ import { type SimulationProvider } from '../common/simulation_provider.js';
5
6
  import { NativeACVMSimulator } from './acvm_native.js';
6
7
  import { WASMSimulator } from './acvm_wasm.js';
7
- import { type SimulationProvider } from './simulation_provider.js';
8
8
 
9
9
  export type SimulationProviderConfig = {
10
10
  acvmBinaryPath?: string;