@aztec/simulator 0.47.1 → 0.48.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/oracle/oracle.d.ts +3 -3
- package/dest/acvm/oracle/oracle.d.ts.map +1 -1
- package/dest/acvm/oracle/oracle.js +8 -9
- package/dest/acvm/oracle/typed_oracle.d.ts +5 -5
- package/dest/acvm/oracle/typed_oracle.d.ts.map +1 -1
- package/dest/acvm/oracle/typed_oracle.js +6 -6
- package/dest/avm/fixtures/index.d.ts.map +1 -1
- package/dest/avm/fixtures/index.js +2 -2
- package/dest/avm/opcodes/commitment.js +4 -4
- package/dest/avm/serialization/instruction_serialization.js +2 -2
- package/dest/client/client_execution_context.d.ts +8 -8
- package/dest/client/client_execution_context.d.ts.map +1 -1
- package/dest/client/client_execution_context.js +22 -23
- package/dest/client/db_oracle.d.ts +4 -3
- package/dest/client/db_oracle.d.ts.map +1 -1
- package/dest/client/execution_note_cache.d.ts +17 -13
- package/dest/client/execution_note_cache.d.ts.map +1 -1
- package/dest/client/execution_note_cache.js +65 -26
- package/dest/client/execution_result.d.ts +3 -2
- package/dest/client/execution_result.d.ts.map +1 -1
- package/dest/client/execution_result.js +20 -9
- package/dest/client/private_execution.js +3 -3
- package/dest/client/simulator.d.ts +7 -6
- package/dest/client/simulator.d.ts.map +1 -1
- package/dest/client/simulator.js +14 -12
- package/dest/client/test_utils.d.ts +9 -0
- package/dest/client/test_utils.d.ts.map +1 -0
- package/dest/client/test_utils.js +21 -0
- package/dest/client/view_data_oracle.d.ts +2 -1
- package/dest/client/view_data_oracle.d.ts.map +1 -1
- package/dest/client/view_data_oracle.js +4 -3
- package/dest/index.d.ts +0 -1
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +1 -2
- package/dest/public/abstract_phase_manager.d.ts.map +1 -1
- package/dest/public/abstract_phase_manager.js +3 -3
- package/dest/public/executor.d.ts +4 -1
- package/dest/public/executor.d.ts.map +1 -1
- package/dest/public/executor.js +10 -2
- package/dest/public/executor_metrics.d.ts +10 -0
- package/dest/public/executor_metrics.d.ts.map +1 -0
- package/dest/public/executor_metrics.js +32 -0
- package/dest/public/fee_payment.d.ts +2 -2
- package/dest/public/fee_payment.d.ts.map +1 -1
- package/dest/public/fee_payment.js +9 -10
- package/dest/public/hints_builder.d.ts.map +1 -1
- package/dest/public/hints_builder.js +1 -1
- package/dest/public/public_processor.d.ts +3 -3
- package/dest/public/public_processor.d.ts.map +1 -1
- package/dest/public/public_processor.js +33 -14
- package/dest/public/public_processor_metrics.d.ts +19 -0
- package/dest/public/public_processor_metrics.d.ts.map +1 -0
- package/dest/public/public_processor_metrics.js +57 -0
- package/package.json +9 -9
- package/src/acvm/oracle/oracle.ts +8 -23
- package/src/acvm/oracle/typed_oracle.ts +8 -21
- package/src/avm/fixtures/index.ts +1 -0
- package/src/avm/opcodes/commitment.ts +3 -3
- package/src/avm/serialization/instruction_serialization.ts +1 -1
- package/src/client/client_execution_context.ts +23 -20
- package/src/client/db_oracle.ts +9 -3
- package/src/client/execution_note_cache.ts +76 -25
- package/src/client/execution_result.ts +29 -9
- package/src/client/private_execution.ts +2 -2
- package/src/client/simulator.ts +18 -14
- package/src/client/test_utils.ts +30 -0
- package/src/client/view_data_oracle.ts +2 -1
- package/src/index.ts +0 -1
- package/src/public/abstract_phase_manager.ts +1 -2
- package/src/public/executor.ts +14 -1
- package/src/public/executor_metrics.ts +48 -0
- package/src/public/fee_payment.ts +8 -10
- package/src/public/hints_builder.ts +2 -2
- package/src/public/public_processor.ts +46 -15
- package/src/public/public_processor_metrics.ts +90 -0
- package/dest/utils.d.ts +0 -12
- package/dest/utils.d.ts.map +0 -1
- package/dest/utils.js +0 -11
- package/src/utils.ts +0 -18
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
countArgumentsSize,
|
|
31
31
|
} from '@aztec/foundation/abi';
|
|
32
32
|
import { type AztecAddress } from '@aztec/foundation/aztec-address';
|
|
33
|
-
import {
|
|
33
|
+
import { poseidon2HashWithSeparator } from '@aztec/foundation/crypto';
|
|
34
34
|
import { Fr, GrumpkinScalar, type Point } from '@aztec/foundation/fields';
|
|
35
35
|
import { applyStringFormatting, createDebugLogger } from '@aztec/foundation/log';
|
|
36
36
|
|
|
@@ -71,7 +71,7 @@ export class ClientExecutionContext extends ViewDataOracle {
|
|
|
71
71
|
* They should act as references for the read requests output by an app circuit via public inputs.
|
|
72
72
|
*/
|
|
73
73
|
private noteHashLeafIndexMap: Map<bigint, bigint> = new Map();
|
|
74
|
-
private
|
|
74
|
+
private noteHashNullifierCounterMap: Map<number, number> = new Map();
|
|
75
75
|
private noteEncryptedLogs: CountedNoteLog[] = [];
|
|
76
76
|
private encryptedLogs: CountedLog<EncryptedL2Log>[] = [];
|
|
77
77
|
private unencryptedLogs: CountedLog<UnencryptedL2Log>[] = [];
|
|
@@ -94,8 +94,9 @@ export class ClientExecutionContext extends ViewDataOracle {
|
|
|
94
94
|
private node: AztecNode,
|
|
95
95
|
protected sideEffectCounter: number = 0,
|
|
96
96
|
log = createDebugLogger('aztec:simulator:client_execution_context'),
|
|
97
|
+
scopes?: AztecAddress[],
|
|
97
98
|
) {
|
|
98
|
-
super(contractAddress, authWitnesses, db, node, log);
|
|
99
|
+
super(contractAddress, authWitnesses, db, node, log, scopes);
|
|
99
100
|
}
|
|
100
101
|
|
|
101
102
|
// We still need this function until we can get user-defined ordering of structs for fn arguments
|
|
@@ -135,14 +136,13 @@ export class ClientExecutionContext extends ViewDataOracle {
|
|
|
135
136
|
|
|
136
137
|
/**
|
|
137
138
|
* Get the data for the newly created notes.
|
|
138
|
-
* @param innerNoteHashes - Inner note hashes for the notes.
|
|
139
139
|
*/
|
|
140
140
|
public getNewNotes(): NoteAndSlot[] {
|
|
141
141
|
return this.newNotes;
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
-
public
|
|
145
|
-
return this.
|
|
144
|
+
public getNoteHashNullifierCounterMap() {
|
|
145
|
+
return this.noteHashNullifierCounterMap;
|
|
146
146
|
}
|
|
147
147
|
|
|
148
148
|
/**
|
|
@@ -251,7 +251,7 @@ export class ClientExecutionContext extends ViewDataOracle {
|
|
|
251
251
|
const pendingNotes = this.noteCache.getNotes(this.callContext.storageContractAddress, storageSlot);
|
|
252
252
|
|
|
253
253
|
const pendingNullifiers = this.noteCache.getNullifiers(this.callContext.storageContractAddress);
|
|
254
|
-
const dbNotes = await this.db.getNotes(this.callContext.storageContractAddress, storageSlot, status);
|
|
254
|
+
const dbNotes = await this.db.getNotes(this.callContext.storageContractAddress, storageSlot, status, this.scopes);
|
|
255
255
|
const dbNotesFiltered = dbNotes.filter(n => !pendingNullifiers.has((n.siloedNullifier as Fr).value));
|
|
256
256
|
|
|
257
257
|
const notes = pickNotes<NoteData>([...dbNotesFiltered, ...pendingNotes], {
|
|
@@ -276,12 +276,9 @@ export class ClientExecutionContext extends ViewDataOracle {
|
|
|
276
276
|
|
|
277
277
|
notes.forEach(n => {
|
|
278
278
|
if (n.index !== undefined) {
|
|
279
|
-
|
|
280
|
-
// Should always call computeUniqueNoteHash when publicly created notes include nonces.
|
|
281
|
-
const uniqueNoteHash = n.nonce.isZero() ? n.innerNoteHash : computeUniqueNoteHash(n.nonce, n.innerNoteHash);
|
|
279
|
+
const uniqueNoteHash = computeUniqueNoteHash(n.nonce, n.noteHash);
|
|
282
280
|
const siloedNoteHash = siloNoteHash(n.contractAddress, uniqueNoteHash);
|
|
283
|
-
|
|
284
|
-
this.noteHashLeafIndexMap.set(noteHashForReadRequest.toBigInt(), n.index);
|
|
281
|
+
this.noteHashLeafIndexMap.set(siloedNoteHash.toBigInt(), n.index);
|
|
285
282
|
}
|
|
286
283
|
});
|
|
287
284
|
|
|
@@ -295,14 +292,14 @@ export class ClientExecutionContext extends ViewDataOracle {
|
|
|
295
292
|
* @param storageSlot - The storage slot.
|
|
296
293
|
* @param noteTypeId - The type ID of the note.
|
|
297
294
|
* @param noteItems - The items to be included in a Note.
|
|
298
|
-
* @param
|
|
295
|
+
* @param noteHash - A hash of the new note.
|
|
299
296
|
* @returns
|
|
300
297
|
*/
|
|
301
298
|
public override notifyCreatedNote(
|
|
302
299
|
storageSlot: Fr,
|
|
303
300
|
noteTypeId: NoteSelector,
|
|
304
301
|
noteItems: Fr[],
|
|
305
|
-
|
|
302
|
+
noteHash: Fr,
|
|
306
303
|
counter: number,
|
|
307
304
|
) {
|
|
308
305
|
const note = new Note(noteItems);
|
|
@@ -313,7 +310,7 @@ export class ClientExecutionContext extends ViewDataOracle {
|
|
|
313
310
|
nonce: Fr.ZERO, // Nonce cannot be known during private execution.
|
|
314
311
|
note,
|
|
315
312
|
siloedNullifier: undefined, // Siloed nullifier cannot be known for newly created note.
|
|
316
|
-
|
|
313
|
+
noteHash,
|
|
317
314
|
},
|
|
318
315
|
counter,
|
|
319
316
|
);
|
|
@@ -328,16 +325,16 @@ export class ClientExecutionContext extends ViewDataOracle {
|
|
|
328
325
|
* Adding a siloed nullifier into the current set of all pending nullifiers created
|
|
329
326
|
* within the current transaction/execution.
|
|
330
327
|
* @param innerNullifier - The pending nullifier to add in the list (not yet siloed by contract address).
|
|
331
|
-
* @param
|
|
328
|
+
* @param noteHash - A hash of the new note.
|
|
332
329
|
*/
|
|
333
|
-
public override notifyNullifiedNote(innerNullifier: Fr,
|
|
330
|
+
public override notifyNullifiedNote(innerNullifier: Fr, noteHash: Fr, counter: number) {
|
|
334
331
|
const nullifiedNoteHashCounter = this.noteCache.nullifyNote(
|
|
335
332
|
this.callContext.storageContractAddress,
|
|
336
333
|
innerNullifier,
|
|
337
|
-
|
|
334
|
+
noteHash,
|
|
338
335
|
);
|
|
339
336
|
if (nullifiedNoteHashCounter !== undefined) {
|
|
340
|
-
this.
|
|
337
|
+
this.noteHashNullifierCounterMap.set(nullifiedNoteHashCounter, counter);
|
|
341
338
|
}
|
|
342
339
|
return Promise.resolve();
|
|
343
340
|
}
|
|
@@ -360,7 +357,7 @@ export class ClientExecutionContext extends ViewDataOracle {
|
|
|
360
357
|
// An app providing randomness = 0 signals to not mask the address.
|
|
361
358
|
const maskedContractAddress = randomness.isZero()
|
|
362
359
|
? contractAddress.toField()
|
|
363
|
-
:
|
|
360
|
+
: poseidon2HashWithSeparator([contractAddress, randomness], 0);
|
|
364
361
|
const encryptedLog = new CountedLog(new EncryptedL2Log(encryptedEvent, maskedContractAddress), counter);
|
|
365
362
|
this.encryptedLogs.push(encryptedLog);
|
|
366
363
|
}
|
|
@@ -519,6 +516,8 @@ export class ClientExecutionContext extends ViewDataOracle {
|
|
|
519
516
|
this.db,
|
|
520
517
|
this.node,
|
|
521
518
|
sideEffectCounter,
|
|
519
|
+
this.log,
|
|
520
|
+
this.scopes,
|
|
522
521
|
);
|
|
523
522
|
|
|
524
523
|
const childExecutionResult = await executePrivateFunction(
|
|
@@ -645,6 +644,10 @@ export class ClientExecutionContext extends ViewDataOracle {
|
|
|
645
644
|
);
|
|
646
645
|
}
|
|
647
646
|
|
|
647
|
+
public override notifySetMinRevertibleSideEffectCounter(minRevertibleSideEffectCounter: number): void {
|
|
648
|
+
this.noteCache.setMinRevertibleSideEffectCounter(minRevertibleSideEffectCounter);
|
|
649
|
+
}
|
|
650
|
+
|
|
648
651
|
/**
|
|
649
652
|
* Derives the call context for a nested execution.
|
|
650
653
|
* @param targetContractAddress - The address of the contract being called.
|
package/src/client/db_oracle.ts
CHANGED
|
@@ -78,12 +78,18 @@ export interface DBOracle extends CommitmentsDB {
|
|
|
78
78
|
* The query result is paginated using 'limit' and 'offset' values.
|
|
79
79
|
* Returns an object containing an array of note data.
|
|
80
80
|
*
|
|
81
|
-
* @param contractAddress - The
|
|
82
|
-
* @param storageSlot - The
|
|
81
|
+
* @param contractAddress - The contract address of the notes.
|
|
82
|
+
* @param storageSlot - The storage slot of the notes.
|
|
83
83
|
* @param status - The status of notes to fetch.
|
|
84
|
+
* @param scopes - The accounts whose notes we can access in this call. Currently optional and will default to all.
|
|
84
85
|
* @returns A Promise that resolves to an array of note data.
|
|
85
86
|
*/
|
|
86
|
-
getNotes(
|
|
87
|
+
getNotes(
|
|
88
|
+
contractAddress: AztecAddress,
|
|
89
|
+
storageSlot: Fr,
|
|
90
|
+
status: NoteStatus,
|
|
91
|
+
scopes?: AztecAddress[],
|
|
92
|
+
): Promise<NoteData[]>;
|
|
87
93
|
|
|
88
94
|
/**
|
|
89
95
|
* Retrieve the artifact information of a specific function within a contract.
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import { siloNullifier } from '@aztec/circuits.js/hash';
|
|
1
|
+
import { computeNoteHashNonce, computeUniqueNoteHash, siloNoteHash, siloNullifier } from '@aztec/circuits.js/hash';
|
|
2
2
|
import { type AztecAddress } from '@aztec/foundation/aztec-address';
|
|
3
|
-
import { Fr } from '@aztec/foundation/fields';
|
|
3
|
+
import { type Fr } from '@aztec/foundation/fields';
|
|
4
4
|
|
|
5
5
|
import { type NoteData } from '../acvm/index.js';
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
interface PendingNote {
|
|
8
8
|
note: NoteData;
|
|
9
9
|
counter: number;
|
|
10
|
+
noteHashForConsumption: Fr;
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
/**
|
|
@@ -15,9 +16,13 @@ export interface PendingNote {
|
|
|
15
16
|
export class ExecutionNoteCache {
|
|
16
17
|
/**
|
|
17
18
|
* New notes created in this transaction.
|
|
19
|
+
* They are pushed to this array in the same order as they are emitted.
|
|
20
|
+
*/
|
|
21
|
+
private notes: PendingNote[] = [];
|
|
22
|
+
/**
|
|
18
23
|
* This mapping maps from a contract address to the notes in the contract.
|
|
19
24
|
*/
|
|
20
|
-
private
|
|
25
|
+
private noteMap: Map<bigint, PendingNote[]> = new Map();
|
|
21
26
|
|
|
22
27
|
/**
|
|
23
28
|
* The list of nullifiers created in this transaction.
|
|
@@ -25,43 +30,81 @@ export class ExecutionNoteCache {
|
|
|
25
30
|
* The note which is nullified might be new or not (i.e., was generated in a previous transaction).
|
|
26
31
|
* Note that their value (bigint representation) is used because Frs cannot be looked up in Sets.
|
|
27
32
|
*/
|
|
28
|
-
private
|
|
33
|
+
private nullifierMap: Map<bigint, Set<bigint>> = new Map();
|
|
34
|
+
|
|
35
|
+
private minRevertibleSideEffectCounter = 0;
|
|
36
|
+
|
|
37
|
+
constructor(private readonly txHash: Fr) {}
|
|
38
|
+
|
|
39
|
+
public setMinRevertibleSideEffectCounter(minRevertibleSideEffectCounter: number) {
|
|
40
|
+
if (this.minRevertibleSideEffectCounter && this.minRevertibleSideEffectCounter !== minRevertibleSideEffectCounter) {
|
|
41
|
+
throw new Error(
|
|
42
|
+
`Cannot override minRevertibleSideEffectCounter. Current value: ${minRevertibleSideEffectCounter}. Previous value: ${this.minRevertibleSideEffectCounter}`,
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
this.minRevertibleSideEffectCounter = minRevertibleSideEffectCounter;
|
|
47
|
+
|
|
48
|
+
// The existing pending notes are all non-revertible.
|
|
49
|
+
// They cannot be squashed by nullifiers emitted after minRevertibleSideEffectCounter is set.
|
|
50
|
+
// 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
|
+
// The nonces will be used to create the "complete" nullifier.
|
|
52
|
+
const updatedNotes = this.notes.map(({ note, counter }, i) => {
|
|
53
|
+
const nonce = computeNoteHashNonce(this.txHash, i);
|
|
54
|
+
const uniqueNoteHash = computeUniqueNoteHash(nonce, note.noteHash);
|
|
55
|
+
return {
|
|
56
|
+
counter,
|
|
57
|
+
note: { ...note, nonce },
|
|
58
|
+
noteHashForConsumption: siloNoteHash(note.contractAddress, uniqueNoteHash),
|
|
59
|
+
};
|
|
60
|
+
});
|
|
61
|
+
// Rebuild the data.
|
|
62
|
+
this.notes = [];
|
|
63
|
+
this.noteMap = new Map();
|
|
64
|
+
updatedNotes.forEach(n => this.#addNote(n));
|
|
65
|
+
}
|
|
29
66
|
|
|
30
67
|
/**
|
|
31
68
|
* Add a new note to cache.
|
|
32
69
|
* @param note - New note created during execution.
|
|
33
70
|
*/
|
|
34
71
|
public addNewNote(note: NoteData, counter: number) {
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
72
|
+
const previousNote = this.notes[this.notes.length - 1];
|
|
73
|
+
if (previousNote && previousNote.counter >= counter) {
|
|
74
|
+
throw new Error(
|
|
75
|
+
`Note hash counters must be strictly increasing. Current counter: ${counter}. Previous counter: ${previousNote.counter}.`,
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
this.#addNote({ note, counter, noteHashForConsumption: note.noteHash });
|
|
38
80
|
}
|
|
39
81
|
|
|
40
82
|
/**
|
|
41
83
|
* Add a nullifier to cache. It could be for a db note or a new note created during execution.
|
|
42
84
|
* @param contractAddress - Contract address of the note.
|
|
43
|
-
* @param storageSlot - Storage slot of the note.
|
|
44
85
|
* @param innerNullifier - Inner nullifier of the note.
|
|
45
|
-
* @param
|
|
46
|
-
*
|
|
86
|
+
* @param noteHash - A hash of the note. If this value equals 0, it means the note being nullified is from a previous
|
|
87
|
+
* transaction (and thus not a new note).
|
|
47
88
|
*/
|
|
48
|
-
public nullifyNote(contractAddress: AztecAddress, innerNullifier: Fr,
|
|
89
|
+
public nullifyNote(contractAddress: AztecAddress, innerNullifier: Fr, noteHash: Fr) {
|
|
49
90
|
const siloedNullifier = siloNullifier(contractAddress, innerNullifier);
|
|
50
91
|
const nullifiers = this.getNullifiers(contractAddress);
|
|
51
92
|
nullifiers.add(siloedNullifier.value);
|
|
52
|
-
this.
|
|
93
|
+
this.nullifierMap.set(contractAddress.toBigInt(), nullifiers);
|
|
53
94
|
|
|
54
95
|
let nullifiedNoteHashCounter: number | undefined = undefined;
|
|
55
|
-
// Find and remove the matching new note and log(s) if the emitted
|
|
56
|
-
if (!
|
|
57
|
-
const
|
|
58
|
-
const noteIndexToRemove =
|
|
96
|
+
// Find and remove the matching new note and log(s) if the emitted noteHash is not empty.
|
|
97
|
+
if (!noteHash.isEmpty()) {
|
|
98
|
+
const notesInContract = this.noteMap.get(contractAddress.toBigInt()) ?? [];
|
|
99
|
+
const noteIndexToRemove = notesInContract.findIndex(n => n.noteHashForConsumption.equals(noteHash));
|
|
59
100
|
if (noteIndexToRemove === -1) {
|
|
60
101
|
throw new Error('Attempt to remove a pending note that does not exist.');
|
|
61
102
|
}
|
|
62
|
-
|
|
103
|
+
|
|
104
|
+
const note = notesInContract.splice(noteIndexToRemove, 1)[0];
|
|
63
105
|
nullifiedNoteHashCounter = note.counter;
|
|
64
|
-
this.
|
|
106
|
+
this.noteMap.set(contractAddress.toBigInt(), notesInContract);
|
|
107
|
+
this.notes = this.notes.filter(n => n.counter !== note.counter);
|
|
65
108
|
}
|
|
66
109
|
return nullifiedNoteHashCounter;
|
|
67
110
|
}
|
|
@@ -73,7 +116,7 @@ export class ExecutionNoteCache {
|
|
|
73
116
|
* @param storageSlot - Storage slot of the notes.
|
|
74
117
|
**/
|
|
75
118
|
public getNotes(contractAddress: AztecAddress, storageSlot: Fr) {
|
|
76
|
-
const notes = this.
|
|
119
|
+
const notes = this.noteMap.get(contractAddress.toBigInt()) ?? [];
|
|
77
120
|
return notes.filter(n => n.note.storageSlot.equals(storageSlot)).map(n => n.note);
|
|
78
121
|
}
|
|
79
122
|
|
|
@@ -81,11 +124,11 @@ export class ExecutionNoteCache {
|
|
|
81
124
|
* Check if a note exists in the newNotes array.
|
|
82
125
|
* @param contractAddress - Contract address of the note.
|
|
83
126
|
* @param storageSlot - Storage slot of the note.
|
|
84
|
-
* @param
|
|
127
|
+
* @param noteHash - A hash of the note.
|
|
85
128
|
**/
|
|
86
|
-
public checkNoteExists(contractAddress: AztecAddress,
|
|
87
|
-
const notes = this.
|
|
88
|
-
return notes.some(n => n.note.
|
|
129
|
+
public checkNoteExists(contractAddress: AztecAddress, noteHash: Fr) {
|
|
130
|
+
const notes = this.noteMap.get(contractAddress.toBigInt()) ?? [];
|
|
131
|
+
return notes.some(n => n.note.noteHash.equals(noteHash));
|
|
89
132
|
}
|
|
90
133
|
|
|
91
134
|
/**
|
|
@@ -93,6 +136,14 @@ export class ExecutionNoteCache {
|
|
|
93
136
|
* @param contractAddress - Address of the contract.
|
|
94
137
|
*/
|
|
95
138
|
public getNullifiers(contractAddress: AztecAddress): Set<bigint> {
|
|
96
|
-
return this.
|
|
139
|
+
return this.nullifierMap.get(contractAddress.toBigInt()) ?? new Set();
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
#addNote(note: PendingNote) {
|
|
143
|
+
this.notes.push(note);
|
|
144
|
+
|
|
145
|
+
const notes = this.noteMap.get(note.note.contractAddress.toBigInt()) ?? [];
|
|
146
|
+
notes.push(note);
|
|
147
|
+
this.noteMap.set(note.note.contractAddress.toBigInt(), notes);
|
|
97
148
|
}
|
|
98
149
|
}
|
|
@@ -67,7 +67,7 @@ export interface ExecutionResult {
|
|
|
67
67
|
/** The notes created in the executed function. */
|
|
68
68
|
newNotes: NoteAndSlot[];
|
|
69
69
|
/** Mapping of note hash counter to the counter of its nullifier. */
|
|
70
|
-
|
|
70
|
+
noteHashNullifierCounterMap: Map<number, number>;
|
|
71
71
|
/** The raw return values of the executed function. */
|
|
72
72
|
returnValues: Fr[];
|
|
73
73
|
/** The nested executions. */
|
|
@@ -99,9 +99,12 @@ export function collectNoteHashLeafIndexMap(execResult: ExecutionResult, accum:
|
|
|
99
99
|
return accum;
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
-
export function
|
|
103
|
-
execResult
|
|
104
|
-
|
|
102
|
+
export function collectNoteHashNullifierCounterMap(
|
|
103
|
+
execResult: ExecutionResult,
|
|
104
|
+
accum: Map<number, number> = new Map(),
|
|
105
|
+
) {
|
|
106
|
+
execResult.noteHashNullifierCounterMap.forEach((value, key) => accum.set(key, value));
|
|
107
|
+
execResult.nestedExecutions.forEach(nested => collectNoteHashNullifierCounterMap(nested, accum));
|
|
105
108
|
return accum;
|
|
106
109
|
}
|
|
107
110
|
|
|
@@ -112,11 +115,20 @@ export function collectNullifiedNoteHashCounters(execResult: ExecutionResult, ac
|
|
|
112
115
|
*/
|
|
113
116
|
function collectNoteEncryptedLogs(
|
|
114
117
|
execResult: ExecutionResult,
|
|
115
|
-
|
|
118
|
+
noteHashNullifierCounterMap: Map<number, number>,
|
|
119
|
+
minRevertibleSideEffectCounter: number,
|
|
116
120
|
): CountedLog<EncryptedL2NoteLog>[] {
|
|
117
121
|
return [
|
|
118
|
-
execResult.noteEncryptedLogs.filter(noteLog =>
|
|
119
|
-
|
|
122
|
+
execResult.noteEncryptedLogs.filter(noteLog => {
|
|
123
|
+
const nullifierCounter = noteHashNullifierCounterMap.get(noteLog.noteHashCounter);
|
|
124
|
+
return (
|
|
125
|
+
nullifierCounter === undefined ||
|
|
126
|
+
(noteLog.noteHashCounter < minRevertibleSideEffectCounter && nullifierCounter >= minRevertibleSideEffectCounter)
|
|
127
|
+
);
|
|
128
|
+
}),
|
|
129
|
+
...execResult.nestedExecutions.flatMap(res =>
|
|
130
|
+
collectNoteEncryptedLogs(res, noteHashNullifierCounterMap, minRevertibleSideEffectCounter),
|
|
131
|
+
),
|
|
120
132
|
].flat();
|
|
121
133
|
}
|
|
122
134
|
|
|
@@ -126,8 +138,9 @@ function collectNoteEncryptedLogs(
|
|
|
126
138
|
* @returns All encrypted logs.
|
|
127
139
|
*/
|
|
128
140
|
export function collectSortedNoteEncryptedLogs(execResult: ExecutionResult): EncryptedNoteFunctionL2Logs {
|
|
129
|
-
const
|
|
130
|
-
const
|
|
141
|
+
const noteHashNullifierCounterMap = collectNoteHashNullifierCounterMap(execResult);
|
|
142
|
+
const minRevertibleSideEffectCounter = getFinalMinRevertibleSideEffectCounter(execResult);
|
|
143
|
+
const allLogs = collectNoteEncryptedLogs(execResult, noteHashNullifierCounterMap, minRevertibleSideEffectCounter);
|
|
131
144
|
const sortedLogs = sortByCounter(allLogs);
|
|
132
145
|
return new EncryptedNoteFunctionL2Logs(sortedLogs.map(l => l.log));
|
|
133
146
|
}
|
|
@@ -206,3 +219,10 @@ export function collectPublicTeardownFunctionCall(execResult: ExecutionResult):
|
|
|
206
219
|
|
|
207
220
|
return PublicExecutionRequest.empty();
|
|
208
221
|
}
|
|
222
|
+
|
|
223
|
+
export function getFinalMinRevertibleSideEffectCounter(execResult: ExecutionResult): number {
|
|
224
|
+
return execResult.nestedExecutions.reduce((counter, exec) => {
|
|
225
|
+
const nestedCounter = getFinalMinRevertibleSideEffectCounter(exec);
|
|
226
|
+
return nestedCounter ? nestedCounter : counter;
|
|
227
|
+
}, execResult.callStackItem.publicInputs.minRevertibleSideEffectCounter.toNumber());
|
|
228
|
+
}
|
|
@@ -68,7 +68,7 @@ export async function executePrivateFunction(
|
|
|
68
68
|
|
|
69
69
|
const noteHashLeafIndexMap = context.getNoteHashLeafIndexMap();
|
|
70
70
|
const newNotes = context.getNewNotes();
|
|
71
|
-
const
|
|
71
|
+
const noteHashNullifierCounterMap = context.getNoteHashNullifierCounterMap();
|
|
72
72
|
const nestedExecutions = context.getNestedExecutions();
|
|
73
73
|
const enqueuedPublicFunctionCalls = context.getEnqueuedPublicFunctionCalls();
|
|
74
74
|
const publicTeardownFunctionCall = context.getPublicTeardownFunctionCall();
|
|
@@ -82,7 +82,7 @@ export async function executePrivateFunction(
|
|
|
82
82
|
returnValues: rawReturnValues,
|
|
83
83
|
noteHashLeafIndexMap,
|
|
84
84
|
newNotes,
|
|
85
|
-
|
|
85
|
+
noteHashNullifierCounterMap,
|
|
86
86
|
vk: Buffer.from(artifact.verificationKey!, 'hex'),
|
|
87
87
|
nestedExecutions,
|
|
88
88
|
enqueuedPublicFunctionCalls,
|
package/src/client/simulator.ts
CHANGED
|
@@ -38,6 +38,7 @@ export class AcirSimulator {
|
|
|
38
38
|
* @param entryPointArtifact - The artifact of the entry point function.
|
|
39
39
|
* @param contractAddress - The address of the contract (should match request.origin)
|
|
40
40
|
* @param msgSender - The address calling the function. This can be replaced to simulate a call from another contract or a specific account.
|
|
41
|
+
* @param scopes - The accounts whose notes we can access in this call. Currently optional and will default to all.
|
|
41
42
|
* @returns The result of the execution.
|
|
42
43
|
*/
|
|
43
44
|
public async run(
|
|
@@ -45,6 +46,7 @@ export class AcirSimulator {
|
|
|
45
46
|
entryPointArtifact: FunctionArtifact,
|
|
46
47
|
contractAddress: AztecAddress,
|
|
47
48
|
msgSender = AztecAddress.fromField(Fr.MAX_FIELD_VALUE),
|
|
49
|
+
scopes?: AztecAddress[],
|
|
48
50
|
): Promise<ExecutionResult> {
|
|
49
51
|
if (entryPointArtifact.functionType !== FunctionType.PRIVATE) {
|
|
50
52
|
throw new Error(`Cannot run ${entryPointArtifact.functionType} function as private`);
|
|
@@ -68,6 +70,9 @@ export class AcirSimulator {
|
|
|
68
70
|
false,
|
|
69
71
|
entryPointArtifact.isStatic,
|
|
70
72
|
);
|
|
73
|
+
|
|
74
|
+
const txHash = request.toTxRequest().hash();
|
|
75
|
+
|
|
71
76
|
const context = new ClientExecutionContext(
|
|
72
77
|
contractAddress,
|
|
73
78
|
request.firstCallArgsHash,
|
|
@@ -76,10 +81,12 @@ export class AcirSimulator {
|
|
|
76
81
|
header,
|
|
77
82
|
request.authWitnesses,
|
|
78
83
|
PackedValuesCache.create(request.argsOfCalls),
|
|
79
|
-
new ExecutionNoteCache(),
|
|
84
|
+
new ExecutionNoteCache(txHash),
|
|
80
85
|
this.db,
|
|
81
86
|
this.node,
|
|
82
87
|
startSideEffectCounter,
|
|
88
|
+
undefined,
|
|
89
|
+
scopes,
|
|
83
90
|
);
|
|
84
91
|
|
|
85
92
|
try {
|
|
@@ -100,18 +107,19 @@ export class AcirSimulator {
|
|
|
100
107
|
* @param request - The transaction request.
|
|
101
108
|
* @param entryPointArtifact - The artifact of the entry point function.
|
|
102
109
|
* @param contractAddress - The address of the contract.
|
|
103
|
-
* @param
|
|
110
|
+
* @param scopes - The accounts whose notes we can access in this call. Currently optional and will default to all.
|
|
104
111
|
*/
|
|
105
112
|
public async runUnconstrained(
|
|
106
113
|
request: FunctionCall,
|
|
107
114
|
entryPointArtifact: FunctionArtifact,
|
|
108
115
|
contractAddress: AztecAddress,
|
|
116
|
+
scopes?: AztecAddress[],
|
|
109
117
|
) {
|
|
110
118
|
if (entryPointArtifact.functionType !== FunctionType.UNCONSTRAINED) {
|
|
111
119
|
throw new Error(`Cannot run ${entryPointArtifact.functionType} function as unconstrained`);
|
|
112
120
|
}
|
|
113
121
|
|
|
114
|
-
const context = new ViewDataOracle(contractAddress, [], this.db, this.node);
|
|
122
|
+
const context = new ViewDataOracle(contractAddress, [], this.db, this.node, undefined, scopes);
|
|
115
123
|
|
|
116
124
|
try {
|
|
117
125
|
return await executeUnconstrainedFunction(
|
|
@@ -188,14 +196,15 @@ export class AcirSimulator {
|
|
|
188
196
|
returnTypes: artifact.returnTypes,
|
|
189
197
|
};
|
|
190
198
|
|
|
191
|
-
const [
|
|
199
|
+
const [noteHash, uniqueNoteHash, siloedNoteHash, innerNullifier] = (await this.runUnconstrained(
|
|
192
200
|
execRequest,
|
|
193
201
|
artifact,
|
|
194
202
|
contractAddress,
|
|
203
|
+
// We can omit scopes here, because "compute_note_hash_and_optionally_a_nullifier" does not need access to any notes.
|
|
195
204
|
)) as bigint[];
|
|
196
205
|
|
|
197
206
|
return {
|
|
198
|
-
|
|
207
|
+
noteHash: new Fr(noteHash),
|
|
199
208
|
uniqueNoteHash: new Fr(uniqueNoteHash),
|
|
200
209
|
siloedNoteHash: new Fr(siloedNoteHash),
|
|
201
210
|
innerNullifier: new Fr(innerNullifier),
|
|
@@ -203,20 +212,15 @@ export class AcirSimulator {
|
|
|
203
212
|
}
|
|
204
213
|
|
|
205
214
|
/**
|
|
206
|
-
* Computes
|
|
215
|
+
* Computes a hash of the note.
|
|
207
216
|
* @param contractAddress - The address of the contract.
|
|
208
217
|
* @param storageSlot - The storage slot.
|
|
209
218
|
* @param noteTypeId - The note type identifier.
|
|
210
219
|
* @param note - The note.
|
|
211
220
|
* @returns The note hash.
|
|
212
221
|
*/
|
|
213
|
-
public async
|
|
214
|
-
|
|
215
|
-
storageSlot: Fr,
|
|
216
|
-
noteTypeId: NoteSelector,
|
|
217
|
-
note: Note,
|
|
218
|
-
) {
|
|
219
|
-
const { innerNoteHash } = await this.computeNoteHashAndOptionallyANullifier(
|
|
222
|
+
public async computeNoteHash(contractAddress: AztecAddress, storageSlot: Fr, noteTypeId: NoteSelector, note: Note) {
|
|
223
|
+
const { noteHash } = await this.computeNoteHashAndOptionallyANullifier(
|
|
220
224
|
contractAddress,
|
|
221
225
|
Fr.ZERO,
|
|
222
226
|
storageSlot,
|
|
@@ -224,6 +228,6 @@ export class AcirSimulator {
|
|
|
224
228
|
false,
|
|
225
229
|
note,
|
|
226
230
|
);
|
|
227
|
-
return
|
|
231
|
+
return noteHash;
|
|
228
232
|
}
|
|
229
233
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Fq, Fr, GeneratorIndex, Point } from '@aztec/circuits.js';
|
|
2
|
+
import { Grumpkin } from '@aztec/circuits.js/barretenberg';
|
|
3
|
+
import { pedersenCommit } from '@aztec/foundation/crypto';
|
|
4
|
+
|
|
5
|
+
// Copied over from `noir-projects/aztec-nr/aztec/src/generators.nr`
|
|
6
|
+
const G_SLOT = new Point(
|
|
7
|
+
new Fr(0x041223147b680850dc82e8a55a952d4df20256fe0593d949a9541ca00f0abf15n),
|
|
8
|
+
new Fr(0x0a8c72e60d0e60f5d804549d48f3044d06140b98ed717a9b532af630c1530791n),
|
|
9
|
+
false,
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Computes a note hiding point as is done by the default implementation injected by macros.
|
|
14
|
+
* @param storageSlot - The slot to which the note was inserted.
|
|
15
|
+
* @param noteContent - The note content (e.g. note.items).
|
|
16
|
+
* @returns A note hash.
|
|
17
|
+
*/
|
|
18
|
+
export function computeNoteHash(storageSlot: Fr, noteContent: Fr[]): Fr {
|
|
19
|
+
// TODO(#7771): update this to do only 1 MSM call
|
|
20
|
+
const c = pedersenCommit(
|
|
21
|
+
noteContent.map(f => f.toBuffer()),
|
|
22
|
+
GeneratorIndex.NOTE_HIDING_POINT,
|
|
23
|
+
);
|
|
24
|
+
const noteHidingPointBeforeSlotting = new Point(new Fr(c[0]), new Fr(c[1]), false);
|
|
25
|
+
|
|
26
|
+
const grumpkin = new Grumpkin();
|
|
27
|
+
const slotPoint = grumpkin.mul(G_SLOT, new Fq(storageSlot.toBigInt()));
|
|
28
|
+
const noteHidingPoint = grumpkin.add(noteHidingPointBeforeSlotting, slotPoint);
|
|
29
|
+
return noteHidingPoint.x;
|
|
30
|
+
}
|
|
@@ -30,6 +30,7 @@ export class ViewDataOracle extends TypedOracle {
|
|
|
30
30
|
protected readonly db: DBOracle,
|
|
31
31
|
protected readonly aztecNode: AztecNode,
|
|
32
32
|
protected log = createDebugLogger('aztec:simulator:client_view_context'),
|
|
33
|
+
protected readonly scopes?: AztecAddress[],
|
|
33
34
|
) {
|
|
34
35
|
super();
|
|
35
36
|
}
|
|
@@ -219,7 +220,7 @@ export class ViewDataOracle extends TypedOracle {
|
|
|
219
220
|
offset: number,
|
|
220
221
|
status: NoteStatus,
|
|
221
222
|
): Promise<NoteData[]> {
|
|
222
|
-
const dbNotes = await this.db.getNotes(this.contractAddress, storageSlot, status);
|
|
223
|
+
const dbNotes = await this.db.getNotes(this.contractAddress, storageSlot, status, this.scopes);
|
|
223
224
|
return pickNotes<NoteData>(dbNotes, {
|
|
224
225
|
selects: selectByIndexes.slice(0, numSelects).map((index, i) => ({
|
|
225
226
|
selector: { index, offset: selectByOffsets[i], length: selectByLengths[i] },
|
package/src/index.ts
CHANGED
|
@@ -14,7 +14,6 @@ import {
|
|
|
14
14
|
import {
|
|
15
15
|
type AvmExecutionHints,
|
|
16
16
|
AztecAddress,
|
|
17
|
-
ClientIvcProof,
|
|
18
17
|
ContractStorageRead,
|
|
19
18
|
ContractStorageUpdateRequest,
|
|
20
19
|
Fr,
|
|
@@ -361,7 +360,7 @@ export abstract class AbstractPhaseManager {
|
|
|
361
360
|
const previousKernel = this.getPreviousKernelData(previousOutput, previousCircuit);
|
|
362
361
|
|
|
363
362
|
// We take a deep copy (clone) of these inputs to be passed to the prover
|
|
364
|
-
const inputs = new PublicKernelCircuitPrivateInputs(previousKernel,
|
|
363
|
+
const inputs = new PublicKernelCircuitPrivateInputs(previousKernel, callData);
|
|
365
364
|
switch (this.phase) {
|
|
366
365
|
case PublicKernelType.SETUP:
|
|
367
366
|
return [inputs.clone(), await this.publicKernel.publicKernelCircuitSetup(inputs), 'PublicKernelSetupArtifact'];
|
package/src/public/executor.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { type AvmSimulationStats } from '@aztec/circuit-types/stats';
|
|
|
3
3
|
import { Fr, Gas, type GlobalVariables, type Header, type Nullifier, type TxContext } from '@aztec/circuits.js';
|
|
4
4
|
import { createDebugLogger } from '@aztec/foundation/log';
|
|
5
5
|
import { Timer } from '@aztec/foundation/timer';
|
|
6
|
+
import { type TelemetryClient } from '@aztec/telemetry-client';
|
|
6
7
|
|
|
7
8
|
import { AvmContext } from '../avm/avm_context.js';
|
|
8
9
|
import { AvmExecutionEnvironment } from '../avm/avm_execution_environment.js';
|
|
@@ -12,18 +13,24 @@ import { HostStorage } from '../avm/journal/host_storage.js';
|
|
|
12
13
|
import { AvmPersistableStateManager } from '../avm/journal/index.js';
|
|
13
14
|
import { type CommitmentsDB, type PublicContractsDB, type PublicStateDB } from './db_interfaces.js';
|
|
14
15
|
import { type PublicExecutionResult } from './execution.js';
|
|
16
|
+
import { ExecutorMetrics } from './executor_metrics.js';
|
|
15
17
|
import { PublicSideEffectTrace } from './side_effect_trace.js';
|
|
16
18
|
|
|
17
19
|
/**
|
|
18
20
|
* Handles execution of public functions.
|
|
19
21
|
*/
|
|
20
22
|
export class PublicExecutor {
|
|
23
|
+
metrics: ExecutorMetrics;
|
|
24
|
+
|
|
21
25
|
constructor(
|
|
22
26
|
private readonly publicStorageDB: PublicStateDB,
|
|
23
27
|
private readonly contractsDb: PublicContractsDB,
|
|
24
28
|
private readonly commitmentsDb: CommitmentsDB,
|
|
25
29
|
private readonly header: Header,
|
|
26
|
-
|
|
30
|
+
client: TelemetryClient,
|
|
31
|
+
) {
|
|
32
|
+
this.metrics = new ExecutorMetrics(client, 'PublicExecutor');
|
|
33
|
+
}
|
|
27
34
|
|
|
28
35
|
static readonly log = createDebugLogger('aztec:simulator:public_executor');
|
|
29
36
|
|
|
@@ -102,6 +109,12 @@ export class PublicExecutor {
|
|
|
102
109
|
fnName,
|
|
103
110
|
);
|
|
104
111
|
|
|
112
|
+
if (publicExecutionResult.reverted) {
|
|
113
|
+
this.metrics.recordFunctionSimulationFailure();
|
|
114
|
+
} else {
|
|
115
|
+
this.metrics.recordFunctionSimulation(bytecode.length, timer.ms());
|
|
116
|
+
}
|
|
117
|
+
|
|
105
118
|
return publicExecutionResult;
|
|
106
119
|
}
|
|
107
120
|
}
|