@aztec/simulator 0.22.0 → 0.24.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 +2 -2
- package/dest/acvm/oracle/oracle.d.ts.map +1 -1
- package/dest/acvm/oracle/oracle.js +5 -5
- package/dest/acvm/oracle/typed_oracle.d.ts +2 -2
- package/dest/acvm/oracle/typed_oracle.d.ts.map +1 -1
- package/dest/acvm/oracle/typed_oracle.js +3 -3
- package/dest/avm/avm_execution_environment.d.ts +3 -2
- package/dest/avm/avm_execution_environment.d.ts.map +1 -1
- package/dest/avm/avm_execution_environment.js +6 -5
- package/dest/avm/avm_memory_types.d.ts +120 -39
- package/dest/avm/avm_memory_types.d.ts.map +1 -1
- package/dest/avm/avm_memory_types.js +91 -109
- package/dest/avm/avm_simulator.d.ts.map +1 -1
- package/dest/avm/avm_simulator.js +2 -3
- package/dest/avm/errors.d.ts +3 -1
- package/dest/avm/errors.d.ts.map +1 -1
- package/dest/avm/errors.js +9 -3
- package/dest/avm/fixtures/index.d.ts +4 -0
- package/dest/avm/fixtures/index.d.ts.map +1 -1
- package/dest/avm/fixtures/index.js +11 -3
- package/dest/avm/journal/host_storage.d.ts +1 -1
- package/dest/avm/journal/host_storage.d.ts.map +1 -1
- package/dest/avm/opcodes/addressing_mode.d.ts +24 -0
- package/dest/avm/opcodes/addressing_mode.d.ts.map +1 -0
- package/dest/avm/opcodes/addressing_mode.js +62 -0
- package/dest/avm/opcodes/comparators.d.ts.map +1 -1
- package/dest/avm/opcodes/comparators.js +8 -5
- package/dest/avm/opcodes/instruction.d.ts +6 -4
- package/dest/avm/opcodes/instruction.d.ts.map +1 -1
- package/dest/avm/opcodes/instruction.js +4 -3
- package/dest/avm/opcodes/memory.d.ts +9 -2
- package/dest/avm/opcodes/memory.d.ts.map +1 -1
- package/dest/avm/opcodes/memory.js +46 -8
- package/dest/avm/serialization/bytecode_serialization.d.ts.map +1 -1
- package/dest/avm/serialization/bytecode_serialization.js +24 -22
- package/dest/avm/serialization/instruction_serialization.d.ts +17 -15
- package/dest/avm/serialization/instruction_serialization.d.ts.map +1 -1
- package/dest/avm/serialization/instruction_serialization.js +22 -17
- package/dest/avm/temporary_executor_migration.d.ts +25 -0
- package/dest/avm/temporary_executor_migration.d.ts.map +1 -0
- package/dest/avm/temporary_executor_migration.js +71 -0
- package/dest/client/client_execution_context.d.ts +4 -2
- package/dest/client/client_execution_context.d.ts.map +1 -1
- package/dest/client/client_execution_context.js +7 -4
- package/dest/client/execution_result.d.ts +2 -0
- package/dest/client/execution_result.d.ts.map +1 -1
- package/dest/client/execution_result.js +1 -1
- package/dest/client/simulator.d.ts +10 -5
- package/dest/client/simulator.d.ts.map +1 -1
- package/dest/client/simulator.js +19 -11
- package/dest/client/unconstrained_execution.js +2 -2
- package/dest/public/execution.js +2 -2
- package/dest/public/executor.d.ts +7 -0
- package/dest/public/executor.d.ts.map +1 -1
- package/dest/public/executor.js +26 -1
- package/dest/public/public_execution_context.js +2 -2
- package/dest/public/state_actions.d.ts +1 -1
- package/dest/public/state_actions.d.ts.map +1 -1
- package/dest/public/state_actions.js +5 -6
- package/dest/utils.d.ts +5 -20
- package/dest/utils.d.ts.map +1 -1
- package/dest/utils.js +4 -20
- package/package.json +7 -5
- package/src/acvm/acvm.ts +156 -0
- package/src/acvm/acvm_types.ts +11 -0
- package/src/acvm/deserialize.ts +36 -0
- package/src/acvm/index.ts +5 -0
- package/src/acvm/oracle/debug.ts +109 -0
- package/src/acvm/oracle/index.ts +17 -0
- package/src/acvm/oracle/oracle.ts +332 -0
- package/src/acvm/oracle/typed_oracle.ts +217 -0
- package/src/acvm/serialize.ts +75 -0
- package/src/avm/avm_context.ts +63 -0
- package/src/avm/avm_execution_environment.ts +98 -0
- package/src/avm/avm_machine_state.ts +93 -0
- package/src/avm/avm_memory_types.ts +309 -0
- package/src/avm/avm_message_call_result.ts +29 -0
- package/src/avm/avm_simulator.ts +89 -0
- package/src/avm/errors.ts +57 -0
- package/src/avm/fixtures/index.ts +90 -0
- package/src/avm/journal/host_storage.ts +20 -0
- package/src/avm/journal/index.ts +2 -0
- package/src/avm/journal/journal.ts +266 -0
- package/src/avm/opcodes/.eslintrc.cjs +8 -0
- package/src/avm/opcodes/accrued_substate.ts +92 -0
- package/src/avm/opcodes/addressing_mode.ts +66 -0
- package/src/avm/opcodes/arithmetic.ts +79 -0
- package/src/avm/opcodes/bitwise.ts +129 -0
- package/src/avm/opcodes/comparators.ts +72 -0
- package/src/avm/opcodes/control_flow.ts +129 -0
- package/src/avm/opcodes/environment_getters.ts +199 -0
- package/src/avm/opcodes/external_calls.ts +122 -0
- package/src/avm/opcodes/index.ts +10 -0
- package/src/avm/opcodes/instruction.ts +64 -0
- package/src/avm/opcodes/instruction_impl.ts +52 -0
- package/src/avm/opcodes/memory.ts +193 -0
- package/src/avm/opcodes/storage.ts +76 -0
- package/src/avm/serialization/buffer_cursor.ts +109 -0
- package/src/avm/serialization/bytecode_serialization.ts +172 -0
- package/src/avm/serialization/instruction_serialization.ts +167 -0
- package/src/avm/temporary_executor_migration.ts +108 -0
- package/src/client/client_execution_context.ts +472 -0
- package/src/client/db_oracle.ts +184 -0
- package/src/client/execution_note_cache.ts +90 -0
- package/src/client/execution_result.ts +89 -0
- package/src/client/index.ts +3 -0
- package/src/client/pick_notes.ts +125 -0
- package/src/client/private_execution.ts +78 -0
- package/src/client/simulator.ts +316 -0
- package/src/client/unconstrained_execution.ts +49 -0
- package/src/client/view_data_oracle.ts +243 -0
- package/src/common/errors.ts +61 -0
- package/src/common/index.ts +3 -0
- package/src/common/packed_args_cache.ts +55 -0
- package/src/common/side_effect_counter.ts +12 -0
- package/src/index.ts +3 -0
- package/src/public/db.ts +85 -0
- package/src/public/execution.ts +137 -0
- package/src/public/executor.ts +158 -0
- package/src/public/index.ts +9 -0
- package/src/public/public_execution_context.ts +217 -0
- package/src/public/state_actions.ts +100 -0
- package/src/test/utils.ts +38 -0
- package/src/utils.ts +18 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { siloNullifier } from '@aztec/circuits.js/abis';
|
|
2
|
+
import { AztecAddress } from '@aztec/foundation/aztec-address';
|
|
3
|
+
import { Fr } from '@aztec/foundation/fields';
|
|
4
|
+
|
|
5
|
+
import { NoteData } from '../acvm/index.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Data that's accessible by all the function calls in an execution.
|
|
9
|
+
*/
|
|
10
|
+
export class ExecutionNoteCache {
|
|
11
|
+
/**
|
|
12
|
+
* New notes created in this transaction.
|
|
13
|
+
* This mapping maps from a contract address to the notes in the contract.
|
|
14
|
+
*/
|
|
15
|
+
private newNotes: Map<bigint, NoteData[]> = new Map();
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* The list of nullifiers created in this transaction.
|
|
19
|
+
* This mapping maps from a contract address to the nullifiers emitted from the contract.
|
|
20
|
+
* The note which is nullified might be new or not (i.e., was generated in a previous transaction).
|
|
21
|
+
* Note that their value (bigint representation) is used because Frs cannot be looked up in Sets.
|
|
22
|
+
*/
|
|
23
|
+
private nullifiers: Map<bigint, Set<bigint>> = new Map();
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Add a new note to cache.
|
|
27
|
+
* @param note - New note created during execution.
|
|
28
|
+
*/
|
|
29
|
+
public addNewNote(note: NoteData) {
|
|
30
|
+
const notes = this.newNotes.get(note.contractAddress.toBigInt()) ?? [];
|
|
31
|
+
notes.push(note);
|
|
32
|
+
this.newNotes.set(note.contractAddress.toBigInt(), notes);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Add a nullifier to cache. It could be for a db note or a new note created during execution.
|
|
37
|
+
* @param contractAddress - Contract address of the note.
|
|
38
|
+
* @param storageSlot - Storage slot of the note.
|
|
39
|
+
* @param innerNullifier - Inner nullifier of the note.
|
|
40
|
+
* @param innerNoteHash - Inner note hash of the note. If this value equals 0, it means the
|
|
41
|
+
* note being nullified is from a previous transaction (and thus not a new note).
|
|
42
|
+
*/
|
|
43
|
+
public nullifyNote(contractAddress: AztecAddress, innerNullifier: Fr, innerNoteHash: Fr) {
|
|
44
|
+
const siloedNullifier = siloNullifier(contractAddress, innerNullifier);
|
|
45
|
+
const nullifiers = this.getNullifiers(contractAddress);
|
|
46
|
+
nullifiers.add(siloedNullifier.value);
|
|
47
|
+
this.nullifiers.set(contractAddress.toBigInt(), nullifiers);
|
|
48
|
+
|
|
49
|
+
// Find and remove the matching new note if the emitted innerNoteHash is not empty.
|
|
50
|
+
if (!innerNoteHash.equals(Fr.ZERO)) {
|
|
51
|
+
const notes = this.newNotes.get(contractAddress.toBigInt()) ?? [];
|
|
52
|
+
const noteIndexToRemove = notes.findIndex(n => n.innerNoteHash.equals(innerNoteHash));
|
|
53
|
+
if (noteIndexToRemove === -1) {
|
|
54
|
+
throw new Error('Attempt to remove a pending note that does not exist.');
|
|
55
|
+
}
|
|
56
|
+
notes.splice(noteIndexToRemove, 1);
|
|
57
|
+
this.newNotes.set(contractAddress.toBigInt(), notes);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Return notes created up to current point in execution.
|
|
63
|
+
* If a nullifier for a note in this list is emitted, the note will be deleted.
|
|
64
|
+
* @param contractAddress - Contract address of the notes.
|
|
65
|
+
* @param storageSlot - Storage slot of the notes.
|
|
66
|
+
**/
|
|
67
|
+
public getNotes(contractAddress: AztecAddress, storageSlot: Fr) {
|
|
68
|
+
const notes = this.newNotes.get(contractAddress.toBigInt()) ?? [];
|
|
69
|
+
return notes.filter(n => n.storageSlot.equals(storageSlot));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Check if a note exists in the newNotes array.
|
|
74
|
+
* @param contractAddress - Contract address of the note.
|
|
75
|
+
* @param storageSlot - Storage slot of the note.
|
|
76
|
+
* @param innerNoteHash - Inner note hash of the note.
|
|
77
|
+
**/
|
|
78
|
+
public checkNoteExists(contractAddress: AztecAddress, innerNoteHash: Fr) {
|
|
79
|
+
const notes = this.newNotes.get(contractAddress.toBigInt()) ?? [];
|
|
80
|
+
return notes.some(n => n.innerNoteHash.equals(innerNoteHash));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Return all nullifiers emitted from a contract.
|
|
85
|
+
* @param contractAddress - Address of the contract.
|
|
86
|
+
*/
|
|
87
|
+
public getNullifiers(contractAddress: AztecAddress): Set<bigint> {
|
|
88
|
+
return this.nullifiers.get(contractAddress.toBigInt()) ?? new Set();
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { FunctionL2Logs, Note } from '@aztec/circuit-types';
|
|
2
|
+
import { PrivateCallStackItem, PublicCallRequest, ReadRequestMembershipWitness } from '@aztec/circuits.js';
|
|
3
|
+
import { DecodedReturn } from '@aztec/foundation/abi';
|
|
4
|
+
import { Fr } from '@aztec/foundation/fields';
|
|
5
|
+
|
|
6
|
+
import { ACVMField } from '../acvm/index.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The contents of a new note.
|
|
10
|
+
*/
|
|
11
|
+
export interface NoteAndSlot {
|
|
12
|
+
/** The note. */
|
|
13
|
+
note: Note;
|
|
14
|
+
/** The storage slot of the note. */
|
|
15
|
+
storageSlot: Fr;
|
|
16
|
+
/** The note type identifier. */
|
|
17
|
+
noteTypeId: Fr;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The result of executing a private function.
|
|
22
|
+
*/
|
|
23
|
+
export interface ExecutionResult {
|
|
24
|
+
// Needed for prover
|
|
25
|
+
/** The ACIR bytecode. */
|
|
26
|
+
acir: Buffer;
|
|
27
|
+
/** The verification key. */
|
|
28
|
+
vk: Buffer;
|
|
29
|
+
/** The partial witness. */
|
|
30
|
+
partialWitness: Map<number, ACVMField>;
|
|
31
|
+
// Needed for the verifier (kernel)
|
|
32
|
+
/** The call stack item. */
|
|
33
|
+
callStackItem: PrivateCallStackItem;
|
|
34
|
+
/** The partially filled-in read request membership witnesses for commitments being read. */
|
|
35
|
+
readRequestPartialWitnesses: ReadRequestMembershipWitness[];
|
|
36
|
+
// Needed when we enable chained txs. The new notes can be cached and used in a later transaction.
|
|
37
|
+
/** The notes created in the executed function. */
|
|
38
|
+
newNotes: NoteAndSlot[];
|
|
39
|
+
/** The decoded return values of the executed function. */
|
|
40
|
+
returnValues: DecodedReturn;
|
|
41
|
+
/** The nested executions. */
|
|
42
|
+
nestedExecutions: this[];
|
|
43
|
+
/** Enqueued public function execution requests to be picked up by the sequencer. */
|
|
44
|
+
enqueuedPublicFunctionCalls: PublicCallRequest[];
|
|
45
|
+
/**
|
|
46
|
+
* Encrypted logs emitted during execution of this function call.
|
|
47
|
+
* Note: These are preimages to `encryptedLogsHash`.
|
|
48
|
+
*/
|
|
49
|
+
encryptedLogs: FunctionL2Logs;
|
|
50
|
+
/**
|
|
51
|
+
* Unencrypted logs emitted during execution of this function call.
|
|
52
|
+
* Note: These are preimages to `unencryptedLogsHash`.
|
|
53
|
+
*/
|
|
54
|
+
unencryptedLogs: FunctionL2Logs;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Collect all encrypted logs across all nested executions.
|
|
59
|
+
* @param execResult - The topmost execution result.
|
|
60
|
+
* @returns All encrypted logs.
|
|
61
|
+
*/
|
|
62
|
+
export function collectEncryptedLogs(execResult: ExecutionResult): FunctionL2Logs[] {
|
|
63
|
+
// without the .reverse(), the logs will be in a queue like fashion which is wrong as the kernel processes it like a stack.
|
|
64
|
+
return [execResult.encryptedLogs, ...[...execResult.nestedExecutions].reverse().flatMap(collectEncryptedLogs)];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Collect all unencrypted logs across all nested executions.
|
|
69
|
+
* @param execResult - The topmost execution result.
|
|
70
|
+
* @returns All unencrypted logs.
|
|
71
|
+
*/
|
|
72
|
+
export function collectUnencryptedLogs(execResult: ExecutionResult): FunctionL2Logs[] {
|
|
73
|
+
// without the .reverse(), the logs will be in a queue like fashion which is wrong as the kernel processes it like a stack.
|
|
74
|
+
return [execResult.unencryptedLogs, ...[...execResult.nestedExecutions].reverse().flatMap(collectUnencryptedLogs)];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Collect all enqueued public function calls across all nested executions.
|
|
79
|
+
* @param execResult - The topmost execution result.
|
|
80
|
+
* @returns All enqueued public function calls.
|
|
81
|
+
*/
|
|
82
|
+
export function collectEnqueuedPublicFunctionCalls(execResult: ExecutionResult): PublicCallRequest[] {
|
|
83
|
+
// without the reverse sort, the logs will be in a queue like fashion which is wrong
|
|
84
|
+
// as the kernel processes it like a stack, popping items off and pushing them to output
|
|
85
|
+
return [
|
|
86
|
+
...execResult.enqueuedPublicFunctionCalls,
|
|
87
|
+
...[...execResult.nestedExecutions].flatMap(collectEnqueuedPublicFunctionCalls),
|
|
88
|
+
].sort((a, b) => b.callContext.startSideEffectCounter - a.callContext.startSideEffectCounter);
|
|
89
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { Comparator, Note } from '@aztec/circuit-types';
|
|
2
|
+
import { Fr } from '@aztec/foundation/fields';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Configuration for selecting values.
|
|
6
|
+
*/
|
|
7
|
+
export interface Select {
|
|
8
|
+
/**
|
|
9
|
+
* Index of the field to select and match.
|
|
10
|
+
*/
|
|
11
|
+
index: number;
|
|
12
|
+
/**
|
|
13
|
+
* Required value of the field.
|
|
14
|
+
*/
|
|
15
|
+
value: Fr;
|
|
16
|
+
/**
|
|
17
|
+
* The comparator to use
|
|
18
|
+
*/
|
|
19
|
+
comparator: Comparator;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* The order to sort an array.
|
|
24
|
+
*/
|
|
25
|
+
export enum SortOrder {
|
|
26
|
+
NADA = 0,
|
|
27
|
+
DESC = 1,
|
|
28
|
+
ASC = 2,
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Configuration for sorting values.
|
|
33
|
+
*/
|
|
34
|
+
export interface Sort {
|
|
35
|
+
/**
|
|
36
|
+
* Index of the field to sort.
|
|
37
|
+
*/
|
|
38
|
+
index: number;
|
|
39
|
+
/**
|
|
40
|
+
* Order to sort the field.
|
|
41
|
+
*/
|
|
42
|
+
order: SortOrder;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Options for picking items from an array of BasicNoteData.
|
|
47
|
+
*/
|
|
48
|
+
interface GetOptions {
|
|
49
|
+
/**
|
|
50
|
+
* Configurations for selecting items.
|
|
51
|
+
* Default: empty array.
|
|
52
|
+
*/
|
|
53
|
+
selects?: Select[];
|
|
54
|
+
/**
|
|
55
|
+
* Configurations for sorting items.
|
|
56
|
+
* Default: empty array.
|
|
57
|
+
*/
|
|
58
|
+
sorts?: Sort[];
|
|
59
|
+
/**
|
|
60
|
+
* The number of items to retrieve per query.
|
|
61
|
+
* Default: 0. No limit.
|
|
62
|
+
*/
|
|
63
|
+
limit?: number;
|
|
64
|
+
/**
|
|
65
|
+
* The starting index for pagination.
|
|
66
|
+
* Default: 0.
|
|
67
|
+
*/
|
|
68
|
+
offset?: number;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Data needed from to perform sort.
|
|
73
|
+
*/
|
|
74
|
+
interface ContainsNote {
|
|
75
|
+
/**
|
|
76
|
+
* The note.
|
|
77
|
+
*/
|
|
78
|
+
note: Note;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const selectNotes = <T extends ContainsNote>(noteDatas: T[], selects: Select[]): T[] =>
|
|
82
|
+
noteDatas.filter(noteData =>
|
|
83
|
+
selects.every(({ index, value, comparator }) => {
|
|
84
|
+
const comparatorSelector = {
|
|
85
|
+
[Comparator.EQ]: () => noteData.note.items[index].equals(value),
|
|
86
|
+
[Comparator.NEQ]: () => !noteData.note.items[index].equals(value),
|
|
87
|
+
[Comparator.LT]: () => noteData.note.items[index].lt(value),
|
|
88
|
+
[Comparator.LTE]: () => noteData.note.items[index].lt(value) || noteData.note.items[index].equals(value),
|
|
89
|
+
[Comparator.GT]: () => !noteData.note.items[index].lt(value) && !noteData.note.items[index].equals(value),
|
|
90
|
+
[Comparator.GTE]: () => !noteData.note.items[index].lt(value),
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
return comparatorSelector[comparator]();
|
|
94
|
+
}),
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
const sortNotes = (a: Fr[], b: Fr[], sorts: Sort[], level = 0): number => {
|
|
98
|
+
if (sorts[level] === undefined) {
|
|
99
|
+
return 0;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const { index, order } = sorts[level];
|
|
103
|
+
if (order === 0) {
|
|
104
|
+
return 0;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const dir = order === 1 ? [-1, 1] : [1, -1];
|
|
108
|
+
return a[index].value === b[index].value
|
|
109
|
+
? sortNotes(a, b, sorts, level + 1)
|
|
110
|
+
: a[index].value > b[index].value
|
|
111
|
+
? dir[0]
|
|
112
|
+
: dir[1];
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Pick from a note array a number of notes that meet the criteria.
|
|
117
|
+
*/
|
|
118
|
+
export function pickNotes<T extends ContainsNote>(
|
|
119
|
+
noteDatas: T[],
|
|
120
|
+
{ selects = [], sorts = [], limit = 0, offset = 0 }: GetOptions,
|
|
121
|
+
): T[] {
|
|
122
|
+
return selectNotes(noteDatas, selects)
|
|
123
|
+
.sort((a, b) => sortNotes(a.note.items, b.note.items, sorts))
|
|
124
|
+
.slice(offset, limit ? offset + limit : undefined);
|
|
125
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { FunctionData, PrivateCallStackItem, PrivateCircuitPublicInputs } from '@aztec/circuits.js';
|
|
2
|
+
import { FunctionArtifactWithDebugMetadata, decodeReturnValues } from '@aztec/foundation/abi';
|
|
3
|
+
import { AztecAddress } from '@aztec/foundation/aztec-address';
|
|
4
|
+
import { Fr } from '@aztec/foundation/fields';
|
|
5
|
+
import { createDebugLogger } from '@aztec/foundation/log';
|
|
6
|
+
import { to2Fields } from '@aztec/foundation/serialize';
|
|
7
|
+
|
|
8
|
+
import { extractReturnWitness } from '../acvm/deserialize.js';
|
|
9
|
+
import { Oracle, acvm, extractCallStack } from '../acvm/index.js';
|
|
10
|
+
import { ExecutionError } from '../common/errors.js';
|
|
11
|
+
import { ClientExecutionContext } from './client_execution_context.js';
|
|
12
|
+
import { ExecutionResult } from './execution_result.js';
|
|
13
|
+
import { AcirSimulator } from './simulator.js';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Execute a private function and return the execution result.
|
|
17
|
+
*/
|
|
18
|
+
export async function executePrivateFunction(
|
|
19
|
+
context: ClientExecutionContext,
|
|
20
|
+
artifact: FunctionArtifactWithDebugMetadata,
|
|
21
|
+
contractAddress: AztecAddress,
|
|
22
|
+
functionData: FunctionData,
|
|
23
|
+
log = createDebugLogger('aztec:simulator:secret_execution'),
|
|
24
|
+
): Promise<ExecutionResult> {
|
|
25
|
+
const functionSelector = functionData.selector;
|
|
26
|
+
log(`Executing external function ${contractAddress}:${functionSelector}`);
|
|
27
|
+
|
|
28
|
+
const acir = Buffer.from(artifact.bytecode, 'base64');
|
|
29
|
+
const initialWitness = context.getInitialWitness(artifact);
|
|
30
|
+
const acvmCallback = new Oracle(context);
|
|
31
|
+
const { partialWitness } = await acvm(await AcirSimulator.getSolver(), acir, initialWitness, acvmCallback).catch(
|
|
32
|
+
(err: Error) => {
|
|
33
|
+
throw new ExecutionError(
|
|
34
|
+
err.message,
|
|
35
|
+
{
|
|
36
|
+
contractAddress,
|
|
37
|
+
functionSelector,
|
|
38
|
+
},
|
|
39
|
+
extractCallStack(err, artifact.debug),
|
|
40
|
+
{ cause: err },
|
|
41
|
+
);
|
|
42
|
+
},
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
const returnWitness = extractReturnWitness(acir, partialWitness);
|
|
46
|
+
const publicInputs = PrivateCircuitPublicInputs.fromFields(returnWitness);
|
|
47
|
+
|
|
48
|
+
const encryptedLogs = context.getEncryptedLogs();
|
|
49
|
+
const unencryptedLogs = context.getUnencryptedLogs();
|
|
50
|
+
// TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165) --> set this in Noir
|
|
51
|
+
publicInputs.encryptedLogsHash = to2Fields(encryptedLogs.hash());
|
|
52
|
+
publicInputs.encryptedLogPreimagesLength = new Fr(encryptedLogs.getSerializedLength());
|
|
53
|
+
publicInputs.unencryptedLogsHash = to2Fields(unencryptedLogs.hash());
|
|
54
|
+
publicInputs.unencryptedLogPreimagesLength = new Fr(unencryptedLogs.getSerializedLength());
|
|
55
|
+
|
|
56
|
+
const callStackItem = new PrivateCallStackItem(contractAddress, functionData, publicInputs);
|
|
57
|
+
const returnValues = decodeReturnValues(artifact, publicInputs.returnValues);
|
|
58
|
+
const readRequestPartialWitnesses = context.getReadRequestPartialWitnesses(publicInputs.readRequests);
|
|
59
|
+
const newNotes = context.getNewNotes();
|
|
60
|
+
const nestedExecutions = context.getNestedExecutions();
|
|
61
|
+
const enqueuedPublicFunctionCalls = context.getEnqueuedPublicFunctionCalls();
|
|
62
|
+
|
|
63
|
+
log(`Returning from call to ${contractAddress.toString()}:${functionSelector}`);
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
acir,
|
|
67
|
+
partialWitness,
|
|
68
|
+
callStackItem,
|
|
69
|
+
returnValues,
|
|
70
|
+
readRequestPartialWitnesses,
|
|
71
|
+
newNotes,
|
|
72
|
+
vk: Buffer.from(artifact.verificationKey!, 'hex'),
|
|
73
|
+
nestedExecutions,
|
|
74
|
+
enqueuedPublicFunctionCalls,
|
|
75
|
+
encryptedLogs,
|
|
76
|
+
unencryptedLogs,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
import { AztecNode, FunctionCall, Note, TxExecutionRequest } from '@aztec/circuit-types';
|
|
2
|
+
import { CallContext, FunctionData } from '@aztec/circuits.js';
|
|
3
|
+
import { Grumpkin } from '@aztec/circuits.js/barretenberg';
|
|
4
|
+
import {
|
|
5
|
+
ArrayType,
|
|
6
|
+
FunctionArtifactWithDebugMetadata,
|
|
7
|
+
FunctionSelector,
|
|
8
|
+
FunctionType,
|
|
9
|
+
encodeArguments,
|
|
10
|
+
} from '@aztec/foundation/abi';
|
|
11
|
+
import { AztecAddress } from '@aztec/foundation/aztec-address';
|
|
12
|
+
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
13
|
+
import { Fr } from '@aztec/foundation/fields';
|
|
14
|
+
import { DebugLogger, createDebugLogger } from '@aztec/foundation/log';
|
|
15
|
+
|
|
16
|
+
import { WasmBlackBoxFunctionSolver, createBlackBoxSolver } from '@noir-lang/acvm_js';
|
|
17
|
+
|
|
18
|
+
import { createSimulationError } from '../common/errors.js';
|
|
19
|
+
import { PackedArgsCache } from '../common/packed_args_cache.js';
|
|
20
|
+
import { ClientExecutionContext } from './client_execution_context.js';
|
|
21
|
+
import { DBOracle } from './db_oracle.js';
|
|
22
|
+
import { ExecutionNoteCache } from './execution_note_cache.js';
|
|
23
|
+
import { ExecutionResult } from './execution_result.js';
|
|
24
|
+
import { executePrivateFunction } from './private_execution.js';
|
|
25
|
+
import { executeUnconstrainedFunction } from './unconstrained_execution.js';
|
|
26
|
+
import { ViewDataOracle } from './view_data_oracle.js';
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The ACIR simulator.
|
|
30
|
+
*/
|
|
31
|
+
export class AcirSimulator {
|
|
32
|
+
private static solver: Promise<WasmBlackBoxFunctionSolver>; // ACVM's backend
|
|
33
|
+
private log: DebugLogger;
|
|
34
|
+
|
|
35
|
+
constructor(private db: DBOracle, private node: AztecNode) {
|
|
36
|
+
this.log = createDebugLogger('aztec:simulator');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Gets or initializes the ACVM WasmBlackBoxFunctionSolver.
|
|
41
|
+
*
|
|
42
|
+
* @remarks
|
|
43
|
+
*
|
|
44
|
+
* Occurs only once across all instances of AcirSimulator.
|
|
45
|
+
* Speeds up execution by only performing setup tasks (like pedersen
|
|
46
|
+
* generator initialization) one time.
|
|
47
|
+
* TODO(https://github.com/AztecProtocol/aztec-packages/issues/1627):
|
|
48
|
+
* determine whether this requires a lock
|
|
49
|
+
*
|
|
50
|
+
* @returns ACVM WasmBlackBoxFunctionSolver
|
|
51
|
+
*/
|
|
52
|
+
public static getSolver(): Promise<WasmBlackBoxFunctionSolver> {
|
|
53
|
+
if (!this.solver) {
|
|
54
|
+
this.solver = createBlackBoxSolver();
|
|
55
|
+
}
|
|
56
|
+
return this.solver;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Runs a private function.
|
|
61
|
+
* @param request - The transaction request.
|
|
62
|
+
* @param entryPointArtifact - The artifact of the entry point function.
|
|
63
|
+
* @param contractAddress - The address of the contract (should match request.origin)
|
|
64
|
+
* @param portalContractAddress - The address of the portal contract.
|
|
65
|
+
* @param msgSender - The address calling the function. This can be replaced to simulate a call from another contract or a specific account.
|
|
66
|
+
* @returns The result of the execution.
|
|
67
|
+
*/
|
|
68
|
+
public async run(
|
|
69
|
+
request: TxExecutionRequest,
|
|
70
|
+
entryPointArtifact: FunctionArtifactWithDebugMetadata,
|
|
71
|
+
contractAddress: AztecAddress,
|
|
72
|
+
portalContractAddress: EthAddress,
|
|
73
|
+
msgSender = AztecAddress.ZERO,
|
|
74
|
+
): Promise<ExecutionResult> {
|
|
75
|
+
if (entryPointArtifact.functionType !== FunctionType.SECRET) {
|
|
76
|
+
throw new Error(`Cannot run ${entryPointArtifact.functionType} function as secret`);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (request.origin !== contractAddress) {
|
|
80
|
+
this.log.warn(
|
|
81
|
+
`Request origin does not match contract address in simulation. Request origin: ${request.origin}, contract address: ${contractAddress}`,
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const curve = new Grumpkin();
|
|
86
|
+
|
|
87
|
+
const header = await this.db.getHeader();
|
|
88
|
+
const callContext = new CallContext(
|
|
89
|
+
msgSender,
|
|
90
|
+
contractAddress,
|
|
91
|
+
portalContractAddress,
|
|
92
|
+
FunctionSelector.fromNameAndParameters(entryPointArtifact.name, entryPointArtifact.parameters),
|
|
93
|
+
false,
|
|
94
|
+
false,
|
|
95
|
+
request.functionData.isConstructor,
|
|
96
|
+
// TODO: when contract deployment is done in-app, we should only reserve one counter for the tx hash
|
|
97
|
+
2, // 2 counters are reserved for tx hash and contract deployment nullifier
|
|
98
|
+
);
|
|
99
|
+
const context = new ClientExecutionContext(
|
|
100
|
+
contractAddress,
|
|
101
|
+
request.argsHash,
|
|
102
|
+
request.txContext,
|
|
103
|
+
callContext,
|
|
104
|
+
header,
|
|
105
|
+
request.authWitnesses,
|
|
106
|
+
PackedArgsCache.create(request.packedArguments),
|
|
107
|
+
new ExecutionNoteCache(),
|
|
108
|
+
this.db,
|
|
109
|
+
curve,
|
|
110
|
+
this.node,
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
try {
|
|
114
|
+
const executionResult = await executePrivateFunction(
|
|
115
|
+
context,
|
|
116
|
+
entryPointArtifact,
|
|
117
|
+
contractAddress,
|
|
118
|
+
request.functionData,
|
|
119
|
+
);
|
|
120
|
+
return executionResult;
|
|
121
|
+
} catch (err) {
|
|
122
|
+
throw createSimulationError(err instanceof Error ? err : new Error('Unknown error during private execution'));
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Runs an unconstrained function.
|
|
128
|
+
* @param request - The transaction request.
|
|
129
|
+
* @param entryPointArtifact - The artifact of the entry point function.
|
|
130
|
+
* @param contractAddress - The address of the contract.
|
|
131
|
+
* @param aztecNode - The AztecNode instance.
|
|
132
|
+
*/
|
|
133
|
+
public async runUnconstrained(
|
|
134
|
+
request: FunctionCall,
|
|
135
|
+
entryPointArtifact: FunctionArtifactWithDebugMetadata,
|
|
136
|
+
contractAddress: AztecAddress,
|
|
137
|
+
) {
|
|
138
|
+
if (entryPointArtifact.functionType !== FunctionType.UNCONSTRAINED) {
|
|
139
|
+
throw new Error(`Cannot run ${entryPointArtifact.functionType} function as constrained`);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const context = new ViewDataOracle(contractAddress, [], this.db, this.node);
|
|
143
|
+
|
|
144
|
+
try {
|
|
145
|
+
return await executeUnconstrainedFunction(
|
|
146
|
+
context,
|
|
147
|
+
entryPointArtifact,
|
|
148
|
+
contractAddress,
|
|
149
|
+
request.functionData,
|
|
150
|
+
request.args,
|
|
151
|
+
);
|
|
152
|
+
} catch (err) {
|
|
153
|
+
throw createSimulationError(err instanceof Error ? err : new Error('Unknown error during private execution'));
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Computes the inner nullifier of a note.
|
|
159
|
+
* @param contractAddress - The address of the contract.
|
|
160
|
+
* @param nonce - The nonce of the note hash.
|
|
161
|
+
* @param storageSlot - The storage slot.
|
|
162
|
+
* @param noteTypeId - The note type identifier.
|
|
163
|
+
* @param note - The note.
|
|
164
|
+
* @returns The nullifier.
|
|
165
|
+
*/
|
|
166
|
+
public async computeNoteHashAndNullifier(
|
|
167
|
+
contractAddress: AztecAddress,
|
|
168
|
+
nonce: Fr,
|
|
169
|
+
storageSlot: Fr,
|
|
170
|
+
noteTypeId: Fr,
|
|
171
|
+
note: Note,
|
|
172
|
+
) {
|
|
173
|
+
const artifact: FunctionArtifactWithDebugMetadata | undefined = await this.db.getFunctionArtifactByName(
|
|
174
|
+
contractAddress,
|
|
175
|
+
'compute_note_hash_and_nullifier',
|
|
176
|
+
);
|
|
177
|
+
if (!artifact) {
|
|
178
|
+
throw new Error(
|
|
179
|
+
`Mandatory implementation of "compute_note_hash_and_nullifier" missing in noir contract ${contractAddress.toString()}.`,
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (artifact.parameters.length != 5) {
|
|
184
|
+
throw new Error(
|
|
185
|
+
`Expected 5 parameters in mandatory implementation of "compute_note_hash_and_nullifier", but found ${
|
|
186
|
+
artifact.parameters.length
|
|
187
|
+
} in noir contract ${contractAddress.toString()}.`,
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const maxNoteFields = (artifact.parameters[artifact.parameters.length - 1].type as ArrayType).length;
|
|
192
|
+
if (maxNoteFields < note.items.length) {
|
|
193
|
+
throw new Error(
|
|
194
|
+
`The note being processed has ${note.items.length} fields, while "compute_note_hash_and_nullifier" can only handle a maximum of ${maxNoteFields} fields. Please consider increasing the allowed field size to accommodate all notes generated from the contract.`,
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const extendedNoteItems = note.items.concat(Array(maxNoteFields - note.items.length).fill(Fr.ZERO));
|
|
199
|
+
|
|
200
|
+
const execRequest: FunctionCall = {
|
|
201
|
+
to: contractAddress,
|
|
202
|
+
functionData: FunctionData.empty(),
|
|
203
|
+
args: encodeArguments(artifact, [contractAddress, nonce, storageSlot, noteTypeId, extendedNoteItems]),
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
const [innerNoteHash, siloedNoteHash, uniqueSiloedNoteHash, innerNullifier] = (await this.runUnconstrained(
|
|
207
|
+
execRequest,
|
|
208
|
+
artifact,
|
|
209
|
+
contractAddress,
|
|
210
|
+
)) as bigint[];
|
|
211
|
+
|
|
212
|
+
return {
|
|
213
|
+
innerNoteHash: new Fr(innerNoteHash),
|
|
214
|
+
siloedNoteHash: new Fr(siloedNoteHash),
|
|
215
|
+
uniqueSiloedNoteHash: new Fr(uniqueSiloedNoteHash),
|
|
216
|
+
innerNullifier: new Fr(innerNullifier),
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Computes the inner note hash of a note, which contains storage slot and the custom note hash.
|
|
222
|
+
* @param contractAddress - The address of the contract.
|
|
223
|
+
* @param storageSlot - The storage slot.
|
|
224
|
+
* @param noteTypeId - The note type identifier.
|
|
225
|
+
* @param note - The note.
|
|
226
|
+
* @returns The note hash.
|
|
227
|
+
*/
|
|
228
|
+
public async computeInnerNoteHash(contractAddress: AztecAddress, storageSlot: Fr, noteTypeId: Fr, note: Note) {
|
|
229
|
+
const { innerNoteHash } = await this.computeNoteHashAndNullifier(
|
|
230
|
+
contractAddress,
|
|
231
|
+
Fr.ZERO,
|
|
232
|
+
storageSlot,
|
|
233
|
+
noteTypeId,
|
|
234
|
+
note,
|
|
235
|
+
);
|
|
236
|
+
return innerNoteHash;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Computes the unique note hash of a note.
|
|
241
|
+
* @param contractAddress - The address of the contract.
|
|
242
|
+
* @param nonce - The nonce of the note hash.
|
|
243
|
+
* @param storageSlot - The storage slot.
|
|
244
|
+
* @param noteTypeId - The note type identifier.
|
|
245
|
+
* @param note - The note.
|
|
246
|
+
* @returns The note hash.
|
|
247
|
+
*/
|
|
248
|
+
public async computeUniqueSiloedNoteHash(
|
|
249
|
+
contractAddress: AztecAddress,
|
|
250
|
+
nonce: Fr,
|
|
251
|
+
storageSlot: Fr,
|
|
252
|
+
noteTypeId: Fr,
|
|
253
|
+
note: Note,
|
|
254
|
+
) {
|
|
255
|
+
const { uniqueSiloedNoteHash } = await this.computeNoteHashAndNullifier(
|
|
256
|
+
contractAddress,
|
|
257
|
+
nonce,
|
|
258
|
+
storageSlot,
|
|
259
|
+
noteTypeId,
|
|
260
|
+
note,
|
|
261
|
+
);
|
|
262
|
+
return uniqueSiloedNoteHash;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Computes the siloed note hash of a note.
|
|
267
|
+
* @param contractAddress - The address of the contract.
|
|
268
|
+
* @param nonce - The nonce of the note hash.
|
|
269
|
+
* @param storageSlot - The storage slot.
|
|
270
|
+
* @param noteTypeId - The note type identifier.
|
|
271
|
+
* @param note - The note.
|
|
272
|
+
* @returns The note hash.
|
|
273
|
+
*/
|
|
274
|
+
public async computeSiloedNoteHash(
|
|
275
|
+
contractAddress: AztecAddress,
|
|
276
|
+
nonce: Fr,
|
|
277
|
+
storageSlot: Fr,
|
|
278
|
+
noteTypeId: Fr,
|
|
279
|
+
note: Note,
|
|
280
|
+
) {
|
|
281
|
+
const { siloedNoteHash } = await this.computeNoteHashAndNullifier(
|
|
282
|
+
contractAddress,
|
|
283
|
+
nonce,
|
|
284
|
+
storageSlot,
|
|
285
|
+
noteTypeId,
|
|
286
|
+
note,
|
|
287
|
+
);
|
|
288
|
+
return siloedNoteHash;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Computes the inner note hash of a note, which contains storage slot and the custom note hash.
|
|
293
|
+
* @param contractAddress - The address of the contract.
|
|
294
|
+
* @param nonce - The nonce of the unique note hash.
|
|
295
|
+
* @param storageSlot - The storage slot.
|
|
296
|
+
* @param noteTypeId - The note type identifier.
|
|
297
|
+
* @param note - The note.
|
|
298
|
+
* @returns The note hash.
|
|
299
|
+
*/
|
|
300
|
+
public async computeInnerNullifier(
|
|
301
|
+
contractAddress: AztecAddress,
|
|
302
|
+
nonce: Fr,
|
|
303
|
+
storageSlot: Fr,
|
|
304
|
+
noteTypeId: Fr,
|
|
305
|
+
note: Note,
|
|
306
|
+
) {
|
|
307
|
+
const { innerNullifier } = await this.computeNoteHashAndNullifier(
|
|
308
|
+
contractAddress,
|
|
309
|
+
nonce,
|
|
310
|
+
storageSlot,
|
|
311
|
+
noteTypeId,
|
|
312
|
+
note,
|
|
313
|
+
);
|
|
314
|
+
return innerNullifier;
|
|
315
|
+
}
|
|
316
|
+
}
|