@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.
- package/dest/acvm/acvm.d.ts +1 -2
- package/dest/acvm/acvm.d.ts.map +1 -1
- package/dest/acvm/index.d.ts +1 -1
- package/dest/acvm/index.d.ts.map +1 -1
- package/dest/acvm/index.js +2 -2
- package/dest/acvm/oracle/oracle.d.ts +4 -4
- package/dest/acvm/oracle/oracle.d.ts.map +1 -1
- package/dest/acvm/oracle/oracle.js +14 -14
- package/dest/acvm/oracle/typed_oracle.d.ts +6 -16
- package/dest/acvm/oracle/typed_oracle.d.ts.map +1 -1
- package/dest/acvm/oracle/typed_oracle.js +10 -20
- package/dest/avm/avm_simulator.d.ts.map +1 -1
- package/dest/avm/avm_simulator.js +7 -2
- package/dest/avm/fixtures/index.d.ts +1 -1
- package/dest/avm/fixtures/index.d.ts.map +1 -1
- package/dest/avm/fixtures/index.js +2 -2
- package/dest/avm/journal/nullifiers.d.ts +1 -1
- package/dest/avm/journal/nullifiers.d.ts.map +1 -1
- package/dest/avm/journal/public_storage.d.ts +1 -1
- package/dest/avm/journal/public_storage.d.ts.map +1 -1
- package/dest/client/client_execution_context.d.ts +29 -18
- package/dest/client/client_execution_context.d.ts.map +1 -1
- package/dest/client/client_execution_context.js +42 -29
- package/dest/client/execution_note_cache.d.ts +27 -4
- package/dest/client/execution_note_cache.d.ts.map +1 -1
- package/dest/client/execution_note_cache.js +62 -12
- package/dest/client/index.d.ts +11 -3
- package/dest/client/index.d.ts.map +1 -1
- package/dest/client/index.js +11 -4
- package/dest/client/private_execution.d.ts +3 -2
- package/dest/client/private_execution.d.ts.map +1 -1
- package/dest/client/private_execution.js +9 -7
- package/dest/client/simulator.d.ts +4 -2
- package/dest/client/simulator.d.ts.map +1 -1
- package/dest/client/simulator.js +13 -8
- package/dest/client/unconstrained_execution.d.ts +2 -1
- package/dest/client/unconstrained_execution.d.ts.map +1 -1
- package/dest/client/unconstrained_execution.js +6 -4
- package/dest/client/view_data_oracle.d.ts +1 -1
- package/dest/common/hashed_values_cache.d.ts +28 -0
- package/dest/common/hashed_values_cache.d.ts.map +1 -0
- package/dest/common/{packed_values_cache.js → hashed_values_cache.js} +22 -22
- package/dest/common/index.d.ts +1 -1
- package/dest/common/index.js +1 -1
- package/dest/common/message_load_oracle_inputs.d.ts +15 -0
- package/dest/common/message_load_oracle_inputs.d.ts.map +1 -0
- package/dest/common/message_load_oracle_inputs.js +15 -0
- package/dest/common/simulation_provider.d.ts +19 -0
- package/dest/common/simulation_provider.d.ts.map +1 -0
- package/dest/common/simulation_provider.js +27 -0
- package/dest/common.d.ts +2 -0
- package/dest/common.d.ts.map +1 -0
- package/dest/common.js +2 -0
- package/dest/providers/acvm_native.d.ts +5 -2
- package/dest/providers/acvm_native.d.ts.map +1 -1
- package/dest/providers/acvm_native.js +5 -2
- package/dest/providers/acvm_wasm.d.ts +8 -2
- package/dest/providers/acvm_wasm.d.ts.map +1 -1
- package/dest/providers/acvm_wasm.js +31 -5
- package/dest/providers/acvm_wasm_with_blobs.d.ts +14 -2
- package/dest/providers/acvm_wasm_with_blobs.d.ts.map +1 -1
- package/dest/providers/acvm_wasm_with_blobs.js +24 -4
- package/dest/providers/factory.d.ts +1 -1
- package/dest/providers/factory.d.ts.map +1 -1
- package/dest/providers/factory.js +1 -1
- package/dest/providers/index.d.ts +1 -1
- package/dest/providers/index.d.ts.map +1 -1
- package/dest/providers/index.js +2 -2
- package/dest/public/db_interfaces.d.ts +1 -1
- package/dest/public/db_interfaces.d.ts.map +1 -1
- package/dest/public/fixtures/index.d.ts +3 -3
- package/dest/public/fixtures/index.d.ts.map +1 -1
- package/dest/public/fixtures/index.js +48 -18
- package/dest/public/public_db_sources.d.ts +2 -1
- package/dest/public/public_db_sources.d.ts.map +1 -1
- package/dest/public/public_db_sources.js +2 -2
- package/dest/public/public_tx_simulator.js +2 -2
- package/{src/index.ts → dest/server.d.ts} +1 -2
- package/dest/server.d.ts.map +1 -0
- package/dest/server.js +6 -0
- package/package.json +11 -12
- package/src/acvm/acvm.ts +1 -1
- package/src/acvm/index.ts +1 -1
- package/src/acvm/oracle/oracle.ts +14 -14
- package/src/acvm/oracle/typed_oracle.ts +12 -20
- package/src/avm/avm_simulator.ts +6 -1
- package/src/avm/fixtures/index.ts +2 -1
- package/src/avm/journal/nullifiers.ts +1 -1
- package/src/avm/journal/public_storage.ts +1 -1
- package/src/client/client_execution_context.ts +47 -30
- package/src/client/execution_note_cache.ts +67 -14
- package/src/client/index.ts +11 -3
- package/src/client/private_execution.ts +21 -17
- package/src/client/simulator.ts +20 -7
- package/src/client/unconstrained_execution.ts +17 -13
- package/src/common/hashed_values_cache.ts +55 -0
- package/src/common/index.ts +1 -1
- package/src/common/message_load_oracle_inputs.ts +15 -0
- package/src/common/simulation_provider.ts +45 -0
- package/src/common.ts +1 -0
- package/src/providers/acvm_native.ts +12 -2
- package/src/providers/acvm_wasm.ts +36 -9
- package/src/providers/acvm_wasm_with_blobs.ts +34 -9
- package/src/providers/factory.ts +1 -1
- package/src/providers/index.ts +1 -1
- package/src/public/db_interfaces.ts +1 -1
- package/src/public/fixtures/index.ts +62 -26
- package/src/public/public_db_sources.ts +3 -6
- package/src/public/public_tx_simulator.ts +1 -1
- package/{dest/index.d.ts → src/server.ts} +0 -3
- package/dest/common/packed_values_cache.d.ts +0 -28
- package/dest/common/packed_values_cache.d.ts.map +0 -1
- package/dest/index.d.ts.map +0 -1
- package/dest/index.js +0 -8
- package/dest/providers/simulation_provider.d.ts +0 -9
- package/dest/providers/simulation_provider.d.ts.map +0 -1
- package/dest/providers/simulation_provider.js +0 -2
- package/src/common/packed_values_cache.ts +0 -55
- 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
|
-
*
|
|
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
|
-
|
|
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.
|
|
56
|
+
if (this.inRevertiblePhase) {
|
|
41
57
|
throw new Error(
|
|
42
|
-
`Cannot
|
|
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(
|
|
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.
|
|
156
|
-
|
|
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
|
}
|
package/src/client/index.ts
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
|
-
export
|
|
2
|
-
export
|
|
1
|
+
export { AcirSimulator } from './simulator.js';
|
|
2
|
+
export { DBOracle, ContractClassNotFoundError, ContractNotFoundError } from './db_oracle.js';
|
|
3
3
|
export * from './pick_notes.js';
|
|
4
|
-
export
|
|
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 {
|
|
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,
|
|
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<
|
|
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
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
err.message,
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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.
|
|
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
|
|
79
|
+
return new PrivateCallExecutionResult(
|
|
76
80
|
acir,
|
|
77
81
|
Buffer.from(artifact.verificationKey!, 'base64'),
|
|
78
82
|
partialWitness,
|
package/src/client/simulator.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import
|
|
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 {
|
|
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
|
|
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
|
-
|
|
81
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
err.message,
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
+
}
|
package/src/common/index.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export * from './
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
|
|
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
|
}
|
package/src/providers/factory.ts
CHANGED
|
@@ -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;
|