@aztec/simulator 0.36.0 → 0.38.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 -1
- package/dest/acvm/acvm.d.ts.map +1 -1
- package/dest/acvm/acvm.js +2 -2
- package/dest/acvm/oracle/oracle.d.ts +6 -4
- package/dest/acvm/oracle/oracle.d.ts.map +1 -1
- package/dest/acvm/oracle/oracle.js +39 -8
- package/dest/acvm/oracle/typed_oracle.d.ts +5 -3
- package/dest/acvm/oracle/typed_oracle.d.ts.map +1 -1
- package/dest/acvm/oracle/typed_oracle.js +9 -3
- package/dest/avm/avm_gas.d.ts.map +1 -1
- package/dest/avm/avm_gas.js +2 -1
- package/dest/avm/avm_memory_types.d.ts +1 -1
- package/dest/avm/avm_memory_types.d.ts.map +1 -1
- package/dest/avm/avm_simulator.d.ts.map +1 -1
- package/dest/avm/avm_simulator.js +3 -1
- package/dest/avm/journal/journal.d.ts +20 -1
- package/dest/avm/journal/journal.d.ts.map +1 -1
- package/dest/avm/journal/journal.js +69 -9
- package/dest/avm/journal/nullifiers.d.ts +3 -1
- package/dest/avm/journal/nullifiers.d.ts.map +1 -1
- package/dest/avm/journal/nullifiers.js +14 -6
- package/dest/avm/journal/public_storage.d.ts +10 -1
- package/dest/avm/journal/public_storage.d.ts.map +1 -1
- package/dest/avm/journal/public_storage.js +17 -2
- package/dest/avm/journal/trace.d.ts +1 -4
- package/dest/avm/journal/trace.d.ts.map +1 -1
- package/dest/avm/journal/trace.js +4 -5
- package/dest/avm/journal/trace_types.d.ts +1 -0
- package/dest/avm/journal/trace_types.d.ts.map +1 -1
- package/dest/avm/journal/trace_types.js +1 -1
- package/dest/avm/opcodes/bitwise.d.ts +4 -1
- package/dest/avm/opcodes/bitwise.d.ts.map +1 -1
- package/dest/avm/opcodes/bitwise.js +14 -2
- package/dest/avm/opcodes/environment_getters.d.ts +5 -0
- package/dest/avm/opcodes/environment_getters.d.ts.map +1 -1
- package/dest/avm/opcodes/environment_getters.js +8 -1
- package/dest/avm/opcodes/external_calls.d.ts.map +1 -1
- package/dest/avm/opcodes/external_calls.js +14 -13
- package/dest/avm/serialization/bytecode_serialization.d.ts.map +1 -1
- package/dest/avm/serialization/bytecode_serialization.js +3 -2
- package/dest/avm/serialization/instruction_serialization.d.ts +39 -38
- package/dest/avm/serialization/instruction_serialization.d.ts.map +1 -1
- package/dest/avm/serialization/instruction_serialization.js +40 -39
- package/dest/client/client_execution_context.d.ts +31 -18
- package/dest/client/client_execution_context.d.ts.map +1 -1
- package/dest/client/client_execution_context.js +48 -31
- package/dest/client/db_oracle.d.ts +11 -4
- package/dest/client/db_oracle.d.ts.map +1 -1
- package/dest/client/execution_result.d.ts +19 -15
- package/dest/client/execution_result.d.ts.map +1 -1
- package/dest/client/execution_result.js +45 -12
- package/dest/client/logs_cache.d.ts +33 -0
- package/dest/client/logs_cache.d.ts.map +1 -0
- package/dest/client/logs_cache.js +59 -0
- package/dest/client/private_execution.d.ts +2 -2
- package/dest/client/private_execution.d.ts.map +1 -1
- package/dest/client/private_execution.js +3 -7
- package/dest/client/simulator.d.ts +3 -3
- package/dest/client/simulator.d.ts.map +1 -1
- package/dest/client/simulator.js +3 -2
- package/dest/client/unconstrained_execution.d.ts +2 -2
- package/dest/client/unconstrained_execution.d.ts.map +1 -1
- package/dest/client/unconstrained_execution.js +1 -1
- package/dest/client/view_data_oracle.d.ts +7 -0
- package/dest/client/view_data_oracle.d.ts.map +1 -1
- package/dest/client/view_data_oracle.js +10 -1
- package/dest/mocks/fixtures.d.ts.map +1 -1
- package/dest/mocks/fixtures.js +3 -1
- package/dest/public/abstract_phase_manager.d.ts +2 -0
- package/dest/public/abstract_phase_manager.d.ts.map +1 -1
- package/dest/public/abstract_phase_manager.js +13 -6
- package/dest/public/execution.d.ts +9 -0
- package/dest/public/execution.d.ts.map +1 -1
- package/dest/public/execution.js +1 -1
- package/dest/public/executor.d.ts +2 -2
- package/dest/public/executor.d.ts.map +1 -1
- package/dest/public/executor.js +34 -14
- package/dest/public/public_execution_context.d.ts +10 -4
- package/dest/public/public_execution_context.d.ts.map +1 -1
- package/dest/public/public_execution_context.js +19 -6
- package/dest/public/tail_phase_manager.d.ts +0 -1
- package/dest/public/tail_phase_manager.d.ts.map +1 -1
- package/dest/public/tail_phase_manager.js +3 -26
- package/dest/public/transitional_adaptors.d.ts +4 -17
- package/dest/public/transitional_adaptors.d.ts.map +1 -1
- package/dest/public/transitional_adaptors.js +27 -119
- package/package.json +8 -8
- package/src/acvm/acvm.ts +2 -2
- package/src/acvm/oracle/oracle.ts +63 -10
- package/src/acvm/oracle/typed_oracle.ts +12 -3
- package/src/avm/avm_gas.ts +1 -0
- package/src/avm/avm_memory_types.ts +1 -1
- package/src/avm/avm_simulator.ts +2 -0
- package/src/avm/journal/journal.ts +133 -9
- package/src/avm/journal/nullifiers.ts +19 -8
- package/src/avm/journal/public_storage.ts +23 -2
- package/src/avm/journal/trace.ts +3 -4
- package/src/avm/journal/trace_types.ts +1 -0
- package/src/avm/opcodes/bitwise.ts +18 -7
- package/src/avm/opcodes/environment_getters.ts +9 -0
- package/src/avm/opcodes/external_calls.ts +21 -16
- package/src/avm/serialization/bytecode_serialization.ts +2 -0
- package/src/avm/serialization/instruction_serialization.ts +1 -0
- package/src/client/client_execution_context.ts +55 -31
- package/src/client/db_oracle.ts +12 -10
- package/src/client/execution_result.ts +55 -24
- package/src/client/logs_cache.ts +65 -0
- package/src/client/private_execution.ts +4 -10
- package/src/client/simulator.ts +6 -4
- package/src/client/unconstrained_execution.ts +2 -2
- package/src/client/view_data_oracle.ts +10 -0
- package/src/mocks/fixtures.ts +2 -0
- package/src/public/abstract_phase_manager.ts +13 -5
- package/src/public/execution.ts +9 -0
- package/src/public/executor.ts +47 -10
- package/src/public/public_execution_context.ts +18 -4
- package/src/public/tail_phase_manager.ts +2 -34
- package/src/public/transitional_adaptors.ts +39 -178
|
@@ -1,9 +1,21 @@
|
|
|
1
|
+
// TODO(5818): Rename file and all uses of "journal"
|
|
1
2
|
import { UnencryptedL2Log } from '@aztec/circuit-types';
|
|
2
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
AztecAddress,
|
|
5
|
+
ContractStorageRead,
|
|
6
|
+
ContractStorageUpdateRequest,
|
|
7
|
+
EthAddress,
|
|
8
|
+
L2ToL1Message,
|
|
9
|
+
NoteHash,
|
|
10
|
+
Nullifier,
|
|
11
|
+
ReadRequest,
|
|
12
|
+
SideEffect,
|
|
13
|
+
} from '@aztec/circuits.js';
|
|
3
14
|
import { EventSelector } from '@aztec/foundation/abi';
|
|
4
15
|
import { Fr } from '@aztec/foundation/fields';
|
|
5
16
|
import { type DebugLogger, createDebugLogger } from '@aztec/foundation/log';
|
|
6
17
|
|
|
18
|
+
import { type PublicExecutionResult } from '../../index.js';
|
|
7
19
|
import { type HostStorage } from './host_storage.js';
|
|
8
20
|
import { Nullifiers } from './nullifiers.js';
|
|
9
21
|
import { PublicStorage } from './public_storage.js';
|
|
@@ -19,6 +31,7 @@ import {
|
|
|
19
31
|
type TracedUnencryptedL2Log,
|
|
20
32
|
} from './trace_types.js';
|
|
21
33
|
|
|
34
|
+
// TODO:(5818): do we need this type anymore?
|
|
22
35
|
/**
|
|
23
36
|
* Data held within the journal
|
|
24
37
|
*/
|
|
@@ -37,6 +50,25 @@ export type JournalData = {
|
|
|
37
50
|
newLogsHashes: TracedUnencryptedL2Log[];
|
|
38
51
|
/** contract address -\> key -\> value */
|
|
39
52
|
currentStorageValue: Map<bigint, Map<bigint, Fr>>;
|
|
53
|
+
|
|
54
|
+
sideEffectCounter: number;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// TRANSITIONAL: This should be removed once the kernel handles and entire enqueued call per circuit
|
|
58
|
+
type PartialPublicExecutionResult = {
|
|
59
|
+
nullifierReadRequests: ReadRequest[];
|
|
60
|
+
nullifierNonExistentReadRequests: ReadRequest[];
|
|
61
|
+
newNoteHashes: NoteHash[];
|
|
62
|
+
newL2ToL1Messages: L2ToL1Message[];
|
|
63
|
+
startSideEffectCounter: number;
|
|
64
|
+
newNullifiers: Nullifier[];
|
|
65
|
+
contractStorageReads: ContractStorageRead[];
|
|
66
|
+
contractStorageUpdateRequests: ContractStorageUpdateRequest[];
|
|
67
|
+
unencryptedLogsHashes: SideEffect[];
|
|
68
|
+
unencryptedLogs: UnencryptedL2Log[];
|
|
69
|
+
unencryptedLogPreimagesLength: Fr;
|
|
70
|
+
allUnencryptedLogs: UnencryptedL2Log[];
|
|
71
|
+
nestedExecutions: PublicExecutionResult[];
|
|
40
72
|
};
|
|
41
73
|
|
|
42
74
|
/**
|
|
@@ -53,7 +85,7 @@ export class AvmPersistableStateManager {
|
|
|
53
85
|
/** Reference to node storage */
|
|
54
86
|
public readonly hostStorage: HostStorage;
|
|
55
87
|
|
|
56
|
-
// TODO: make members private once this is not used in transitional_adaptors.ts.
|
|
88
|
+
// TODO(5818): make members private once this is not used in transitional_adaptors.ts.
|
|
57
89
|
/** World State */
|
|
58
90
|
/** Public storage, including cached writes */
|
|
59
91
|
public publicStorage: PublicStorage;
|
|
@@ -67,11 +99,30 @@ export class AvmPersistableStateManager {
|
|
|
67
99
|
public newL1Messages: L2ToL1Message[] = [];
|
|
68
100
|
public newLogs: UnencryptedL2Log[] = [];
|
|
69
101
|
|
|
102
|
+
// TRANSITIONAL: This should be removed once the kernel handles and entire enqueued call per circuit
|
|
103
|
+
public transitionalExecutionResult: PartialPublicExecutionResult;
|
|
104
|
+
|
|
70
105
|
constructor(hostStorage: HostStorage, parent?: AvmPersistableStateManager) {
|
|
71
106
|
this.hostStorage = hostStorage;
|
|
72
107
|
this.publicStorage = new PublicStorage(hostStorage.publicStateDb, parent?.publicStorage);
|
|
73
108
|
this.nullifiers = new Nullifiers(hostStorage.commitmentsDb, parent?.nullifiers);
|
|
74
109
|
this.trace = new WorldStateAccessTrace(parent?.trace);
|
|
110
|
+
|
|
111
|
+
this.transitionalExecutionResult = {
|
|
112
|
+
nullifierReadRequests: [],
|
|
113
|
+
nullifierNonExistentReadRequests: [],
|
|
114
|
+
newNoteHashes: [],
|
|
115
|
+
newL2ToL1Messages: [],
|
|
116
|
+
startSideEffectCounter: this.trace.accessCounter,
|
|
117
|
+
newNullifiers: [],
|
|
118
|
+
contractStorageReads: [],
|
|
119
|
+
contractStorageUpdateRequests: [],
|
|
120
|
+
unencryptedLogsHashes: [],
|
|
121
|
+
unencryptedLogs: [],
|
|
122
|
+
unencryptedLogPreimagesLength: new Fr(0),
|
|
123
|
+
allUnencryptedLogs: [],
|
|
124
|
+
nestedExecutions: [],
|
|
125
|
+
};
|
|
75
126
|
}
|
|
76
127
|
|
|
77
128
|
/**
|
|
@@ -92,6 +143,21 @@ export class AvmPersistableStateManager {
|
|
|
92
143
|
this.log.debug(`storage(${storageAddress})@${slot} <- ${value}`);
|
|
93
144
|
// Cache storage writes for later reference/reads
|
|
94
145
|
this.publicStorage.write(storageAddress, slot, value);
|
|
146
|
+
|
|
147
|
+
// TRANSITIONAL: This should be removed once the kernel handles and entire enqueued call per circuit
|
|
148
|
+
// The current info to the kernel clears any previous read or write request.
|
|
149
|
+
this.transitionalExecutionResult.contractStorageReads =
|
|
150
|
+
this.transitionalExecutionResult.contractStorageReads.filter(
|
|
151
|
+
read => !read.storageSlot.equals(slot) || !read.contractAddress!.equals(storageAddress),
|
|
152
|
+
);
|
|
153
|
+
this.transitionalExecutionResult.contractStorageUpdateRequests =
|
|
154
|
+
this.transitionalExecutionResult.contractStorageUpdateRequests.filter(
|
|
155
|
+
update => !update.storageSlot.equals(slot) || !update.contractAddress!.equals(storageAddress),
|
|
156
|
+
);
|
|
157
|
+
this.transitionalExecutionResult.contractStorageUpdateRequests.push(
|
|
158
|
+
new ContractStorageUpdateRequest(slot, value, this.trace.accessCounter, storageAddress),
|
|
159
|
+
);
|
|
160
|
+
|
|
95
161
|
// Trace all storage writes (even reverted ones)
|
|
96
162
|
this.trace.tracePublicStorageWrite(storageAddress, slot, value);
|
|
97
163
|
}
|
|
@@ -104,10 +170,24 @@ export class AvmPersistableStateManager {
|
|
|
104
170
|
* @returns the latest value written to slot, or 0 if never written to before
|
|
105
171
|
*/
|
|
106
172
|
public async readStorage(storageAddress: Fr, slot: Fr): Promise<Fr> {
|
|
107
|
-
const
|
|
108
|
-
this.log.debug(`storage(${storageAddress})@${slot} ?? value: ${value}, exists: ${exists}.`);
|
|
173
|
+
const { value, exists, cached } = await this.publicStorage.read(storageAddress, slot);
|
|
174
|
+
this.log.debug(`storage(${storageAddress})@${slot} ?? value: ${value}, exists: ${exists}, cached: ${cached}.`);
|
|
175
|
+
|
|
176
|
+
// TRANSITIONAL: This should be removed once the kernel handles and entire enqueued call per circuit
|
|
177
|
+
// The current info to the kernel kernel does not consider cached reads.
|
|
178
|
+
if (!cached) {
|
|
179
|
+
// The current info to the kernel removes any previous reads to the same slot.
|
|
180
|
+
this.transitionalExecutionResult.contractStorageReads =
|
|
181
|
+
this.transitionalExecutionResult.contractStorageReads.filter(
|
|
182
|
+
read => !read.storageSlot.equals(slot) || !read.contractAddress!.equals(storageAddress),
|
|
183
|
+
);
|
|
184
|
+
this.transitionalExecutionResult.contractStorageReads.push(
|
|
185
|
+
new ContractStorageRead(slot, value, this.trace.accessCounter, storageAddress),
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
|
|
109
189
|
// We want to keep track of all performed reads (even reverted ones)
|
|
110
|
-
this.trace.tracePublicStorageRead(storageAddress, slot, value, exists);
|
|
190
|
+
this.trace.tracePublicStorageRead(storageAddress, slot, value, exists, cached);
|
|
111
191
|
return Promise.resolve(value);
|
|
112
192
|
}
|
|
113
193
|
|
|
@@ -133,6 +213,9 @@ export class AvmPersistableStateManager {
|
|
|
133
213
|
* @param noteHash - the unsiloed note hash to write
|
|
134
214
|
*/
|
|
135
215
|
public writeNoteHash(storageAddress: Fr, noteHash: Fr) {
|
|
216
|
+
// TRANSITIONAL: This should be removed once the kernel handles and entire enqueued call per circuit
|
|
217
|
+
this.transitionalExecutionResult.newNoteHashes.push(new NoteHash(noteHash, this.trace.accessCounter));
|
|
218
|
+
|
|
136
219
|
this.log.debug(`noteHashes(${storageAddress}) += @${noteHash}.`);
|
|
137
220
|
this.trace.traceNewNoteHash(storageAddress, noteHash);
|
|
138
221
|
}
|
|
@@ -148,6 +231,16 @@ export class AvmPersistableStateManager {
|
|
|
148
231
|
this.log.debug(
|
|
149
232
|
`nullifiers(${storageAddress})@${nullifier} ?? leafIndex: ${leafIndex}, pending: ${isPending}, exists: ${exists}.`,
|
|
150
233
|
);
|
|
234
|
+
|
|
235
|
+
// TRANSITIONAL: This should be removed once the kernel handles and entire enqueued call per circuit
|
|
236
|
+
if (exists) {
|
|
237
|
+
this.transitionalExecutionResult.nullifierReadRequests.push(new ReadRequest(nullifier, this.trace.accessCounter));
|
|
238
|
+
} else {
|
|
239
|
+
this.transitionalExecutionResult.nullifierNonExistentReadRequests.push(
|
|
240
|
+
new ReadRequest(nullifier, this.trace.accessCounter),
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
|
|
151
244
|
this.trace.traceNullifierCheck(storageAddress, nullifier, exists, isPending, leafIndex);
|
|
152
245
|
return Promise.resolve(exists);
|
|
153
246
|
}
|
|
@@ -158,6 +251,9 @@ export class AvmPersistableStateManager {
|
|
|
158
251
|
* @param nullifier - the unsiloed nullifier to write
|
|
159
252
|
*/
|
|
160
253
|
public async writeNullifier(storageAddress: Fr, nullifier: Fr) {
|
|
254
|
+
// TRANSITIONAL: This should be removed once the kernel handles and entire enqueued call per circuit
|
|
255
|
+
this.transitionalExecutionResult.newNullifiers.push(new Nullifier(nullifier, this.trace.accessCounter, Fr.ZERO));
|
|
256
|
+
|
|
161
257
|
this.log.debug(`nullifiers(${storageAddress}) += ${nullifier}.`);
|
|
162
258
|
// Cache pending nullifiers for later access
|
|
163
259
|
await this.nullifiers.append(storageAddress, nullifier);
|
|
@@ -189,18 +285,39 @@ export class AvmPersistableStateManager {
|
|
|
189
285
|
public writeL1Message(recipient: EthAddress | Fr, content: Fr) {
|
|
190
286
|
this.log.debug(`L1Messages(${recipient}) += ${content}.`);
|
|
191
287
|
const recipientAddress = recipient instanceof EthAddress ? recipient : EthAddress.fromField(recipient);
|
|
192
|
-
|
|
288
|
+
const message = new L2ToL1Message(recipientAddress, content);
|
|
289
|
+
this.newL1Messages.push(message);
|
|
290
|
+
|
|
291
|
+
// TRANSITIONAL: This should be removed once the kernel handles and entire enqueued call per circuit
|
|
292
|
+
this.transitionalExecutionResult.newL2ToL1Messages.push(message);
|
|
193
293
|
}
|
|
194
294
|
|
|
195
295
|
public writeLog(contractAddress: Fr, event: Fr, log: Fr[]) {
|
|
196
296
|
this.log.debug(`UnencryptedL2Log(${contractAddress}) += event ${event} with ${log.length} fields.`);
|
|
197
|
-
const
|
|
297
|
+
const ulog = new UnencryptedL2Log(
|
|
198
298
|
AztecAddress.fromField(contractAddress),
|
|
199
299
|
EventSelector.fromField(event),
|
|
200
300
|
Buffer.concat(log.map(f => f.toBuffer())),
|
|
201
301
|
);
|
|
202
|
-
|
|
203
|
-
|
|
302
|
+
const logHash = Fr.fromBuffer(ulog.hash());
|
|
303
|
+
|
|
304
|
+
// TRANSITIONAL: This should be removed once the kernel handles and entire enqueued call per circuit
|
|
305
|
+
this.transitionalExecutionResult.unencryptedLogs.push(ulog);
|
|
306
|
+
this.transitionalExecutionResult.allUnencryptedLogs.push(ulog);
|
|
307
|
+
// this duplicates exactly what happens in the trace just for the purpose of transitional integration with the kernel
|
|
308
|
+
this.transitionalExecutionResult.unencryptedLogsHashes.push(
|
|
309
|
+
new SideEffect(logHash, new Fr(this.trace.accessCounter)),
|
|
310
|
+
);
|
|
311
|
+
// Duplicates computation performed in public_context.nr::emit_unencrypted_log
|
|
312
|
+
// 44 = addr (32) + selector (4) + raw log len (4) + processed log len (4).
|
|
313
|
+
this.transitionalExecutionResult.unencryptedLogPreimagesLength = new Fr(
|
|
314
|
+
this.transitionalExecutionResult.unencryptedLogPreimagesLength.toNumber() + 44 + log.length * Fr.SIZE_IN_BYTES,
|
|
315
|
+
);
|
|
316
|
+
// TODO(6206): likely need to track this here and not just in the transitional logic.
|
|
317
|
+
|
|
318
|
+
// TODO(6205): why are logs pushed here but logs hashes are traced?
|
|
319
|
+
this.newLogs.push(ulog);
|
|
320
|
+
this.trace.traceNewLog(logHash);
|
|
204
321
|
}
|
|
205
322
|
|
|
206
323
|
/**
|
|
@@ -216,6 +333,11 @@ export class AvmPersistableStateManager {
|
|
|
216
333
|
// Accrued Substate
|
|
217
334
|
this.newL1Messages = this.newL1Messages.concat(nestedJournal.newL1Messages);
|
|
218
335
|
this.newLogs = this.newLogs.concat(nestedJournal.newLogs);
|
|
336
|
+
|
|
337
|
+
// TRANSITIONAL: This should be removed once the kernel handles and entire enqueued call per circuit
|
|
338
|
+
this.transitionalExecutionResult.allUnencryptedLogs.concat(
|
|
339
|
+
nestedJournal.transitionalExecutionResult.allUnencryptedLogs,
|
|
340
|
+
);
|
|
219
341
|
}
|
|
220
342
|
|
|
221
343
|
/**
|
|
@@ -226,6 +348,7 @@ export class AvmPersistableStateManager {
|
|
|
226
348
|
this.trace.acceptAndMerge(nestedJournal.trace);
|
|
227
349
|
}
|
|
228
350
|
|
|
351
|
+
// TODO:(5818): do we need this type anymore?
|
|
229
352
|
/**
|
|
230
353
|
* Access the current state of the journal
|
|
231
354
|
*
|
|
@@ -244,6 +367,7 @@ export class AvmPersistableStateManager {
|
|
|
244
367
|
currentStorageValue: this.publicStorage.getCache().cachePerContract,
|
|
245
368
|
storageReads: this.trace.publicStorageReads,
|
|
246
369
|
storageWrites: this.trace.publicStorageWrites,
|
|
370
|
+
sideEffectCounter: this.trace.accessCounter,
|
|
247
371
|
};
|
|
248
372
|
}
|
|
249
373
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { AztecAddress } from '@aztec/circuits.js';
|
|
1
2
|
import { siloNullifier } from '@aztec/circuits.js/hash';
|
|
2
3
|
import { Fr } from '@aztec/foundation/fields';
|
|
3
4
|
|
|
@@ -10,7 +11,7 @@ import type { CommitmentsDB } from '../../index.js';
|
|
|
10
11
|
*/
|
|
11
12
|
export class Nullifiers {
|
|
12
13
|
/** Cached nullifiers. */
|
|
13
|
-
|
|
14
|
+
public cache: NullifierCache;
|
|
14
15
|
/** Parent's nullifier cache. Checked on cache-miss. */
|
|
15
16
|
private readonly parentCache: NullifierCache | undefined;
|
|
16
17
|
/** Reference to node storage. Checked on parent cache-miss. */
|
|
@@ -95,6 +96,7 @@ export class NullifierCache {
|
|
|
95
96
|
* each entry being a nullifier.
|
|
96
97
|
*/
|
|
97
98
|
private cachePerContract: Map<bigint, Set<bigint>> = new Map();
|
|
99
|
+
private siloedNullifiers: Set<bigint> = new Set();
|
|
98
100
|
|
|
99
101
|
/**
|
|
100
102
|
* Check whether a nullifier exists in the cache.
|
|
@@ -104,8 +106,10 @@ export class NullifierCache {
|
|
|
104
106
|
* @returns whether the nullifier is found in the cache
|
|
105
107
|
*/
|
|
106
108
|
public exists(storageAddress: Fr, nullifier: Fr): boolean {
|
|
107
|
-
const exists =
|
|
108
|
-
|
|
109
|
+
const exists =
|
|
110
|
+
this.cachePerContract.get(storageAddress.toBigInt())?.has(nullifier.toBigInt()) ||
|
|
111
|
+
this.siloedNullifiers.has(siloNullifier(AztecAddress.fromField(storageAddress), nullifier).toBigInt());
|
|
112
|
+
return !!exists;
|
|
109
113
|
}
|
|
110
114
|
|
|
111
115
|
/**
|
|
@@ -115,20 +119,25 @@ export class NullifierCache {
|
|
|
115
119
|
* @param nullifier - the nullifier to stage
|
|
116
120
|
*/
|
|
117
121
|
public append(storageAddress: Fr, nullifier: Fr) {
|
|
122
|
+
if (this.exists(storageAddress, nullifier)) {
|
|
123
|
+
throw new NullifierCollisionError(
|
|
124
|
+
`Nullifier ${nullifier} at contract ${storageAddress} already exists in cache.`,
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
118
128
|
let nullifiersForContract = this.cachePerContract.get(storageAddress.toBigInt());
|
|
119
129
|
// If this contract's nullifier set has no cached nullifiers, create a new Set to store them
|
|
120
130
|
if (!nullifiersForContract) {
|
|
121
131
|
nullifiersForContract = new Set();
|
|
122
132
|
this.cachePerContract.set(storageAddress.toBigInt(), nullifiersForContract);
|
|
123
133
|
}
|
|
124
|
-
if (nullifiersForContract.has(nullifier.toBigInt())) {
|
|
125
|
-
throw new NullifierCollisionError(
|
|
126
|
-
`Nullifier ${nullifier} at contract ${storageAddress} already exists in cache.`,
|
|
127
|
-
);
|
|
128
|
-
}
|
|
129
134
|
nullifiersForContract.add(nullifier.toBigInt());
|
|
130
135
|
}
|
|
131
136
|
|
|
137
|
+
public appendSiloed(siloedNullifier: Fr) {
|
|
138
|
+
this.siloedNullifiers.add(siloedNullifier.toBigInt());
|
|
139
|
+
}
|
|
140
|
+
|
|
132
141
|
/**
|
|
133
142
|
* Merge another cache's nullifiers into this instance's.
|
|
134
143
|
*
|
|
@@ -139,6 +148,8 @@ export class NullifierCache {
|
|
|
139
148
|
* @param incomingNullifiers - the incoming cached nullifiers to merge into this instance's
|
|
140
149
|
*/
|
|
141
150
|
public acceptAndMerge(incomingNullifiers: NullifierCache) {
|
|
151
|
+
// Merge siloed nullifiers.
|
|
152
|
+
this.siloedNullifiers = new Set([...this.siloedNullifiers, ...incomingNullifiers.siloedNullifiers]);
|
|
142
153
|
// Iterate over all contracts with staged writes in the child.
|
|
143
154
|
for (const [incomingAddress, incomingCacheAtContract] of incomingNullifiers.cachePerContract) {
|
|
144
155
|
const thisCacheAtContract = this.cachePerContract.get(incomingAddress);
|
|
@@ -1,7 +1,14 @@
|
|
|
1
|
+
import { AztecAddress } from '@aztec/circuits.js';
|
|
1
2
|
import { Fr } from '@aztec/foundation/fields';
|
|
2
3
|
|
|
3
4
|
import type { PublicStateDB } from '../../index.js';
|
|
4
5
|
|
|
6
|
+
type PublicStorageReadResult = {
|
|
7
|
+
value: Fr;
|
|
8
|
+
exists: boolean;
|
|
9
|
+
cached: boolean;
|
|
10
|
+
};
|
|
11
|
+
|
|
5
12
|
/**
|
|
6
13
|
* A class to manage public storage reads and writes during a contract call's AVM simulation.
|
|
7
14
|
* Maintains a storage write cache, and ensures that reads fall back to the correct source.
|
|
@@ -39,7 +46,8 @@ export class PublicStorage {
|
|
|
39
46
|
* @param slot - the slot in the contract's storage being read from
|
|
40
47
|
* @returns exists: whether the slot has EVER been written to before, value: the latest value written to slot, or 0 if never written to before
|
|
41
48
|
*/
|
|
42
|
-
public async read(storageAddress: Fr, slot: Fr): Promise<
|
|
49
|
+
public async read(storageAddress: Fr, slot: Fr): Promise<PublicStorageReadResult> {
|
|
50
|
+
let cached = false;
|
|
43
51
|
// First try check this storage cache
|
|
44
52
|
let value = this.cache.read(storageAddress, slot);
|
|
45
53
|
// Then try parent's storage cache (if it exists / written to earlier in this TX)
|
|
@@ -49,11 +57,13 @@ export class PublicStorage {
|
|
|
49
57
|
// Finally try the host's Aztec state (a trip to the database)
|
|
50
58
|
if (!value) {
|
|
51
59
|
value = await this.hostPublicStorage.storageRead(storageAddress, slot);
|
|
60
|
+
} else {
|
|
61
|
+
cached = true;
|
|
52
62
|
}
|
|
53
63
|
// if value is undefined, that means this slot has never been written to!
|
|
54
64
|
const exists = value !== undefined;
|
|
55
65
|
const valueOrZero = exists ? value : Fr.ZERO;
|
|
56
|
-
return Promise.resolve(
|
|
66
|
+
return Promise.resolve({ value: valueOrZero, exists, cached });
|
|
57
67
|
}
|
|
58
68
|
|
|
59
69
|
/**
|
|
@@ -75,6 +85,17 @@ export class PublicStorage {
|
|
|
75
85
|
public acceptAndMerge(incomingPublicStorage: PublicStorage) {
|
|
76
86
|
this.cache.acceptAndMerge(incomingPublicStorage.cache);
|
|
77
87
|
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Commits ALL staged writes to the host's state.
|
|
91
|
+
*/
|
|
92
|
+
public async commitToDB() {
|
|
93
|
+
for (const [storageAddress, cacheAtContract] of this.cache.cachePerContract) {
|
|
94
|
+
for (const [slot, value] of cacheAtContract) {
|
|
95
|
+
await this.hostPublicStorage.storageWrite(AztecAddress.fromBigInt(storageAddress), new Fr(slot), value);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
78
99
|
}
|
|
79
100
|
|
|
80
101
|
/**
|
package/src/avm/journal/trace.ts
CHANGED
|
@@ -29,13 +29,14 @@ export class WorldStateAccessTrace {
|
|
|
29
29
|
|
|
30
30
|
constructor(parentTrace?: WorldStateAccessTrace) {
|
|
31
31
|
this.accessCounter = parentTrace ? parentTrace.accessCounter : 0;
|
|
32
|
+
// TODO(4805): consider tracking the parent's trace vector lengths so we can enforce limits
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
public getAccessCounter() {
|
|
35
36
|
return this.accessCounter;
|
|
36
37
|
}
|
|
37
38
|
|
|
38
|
-
public tracePublicStorageRead(storageAddress: Fr, slot: Fr, value: Fr, exists: boolean) {
|
|
39
|
+
public tracePublicStorageRead(storageAddress: Fr, slot: Fr, value: Fr, exists: boolean, cached: boolean) {
|
|
39
40
|
// TODO(4805): check if some threshold is reached for max storage reads
|
|
40
41
|
// (need access to parent length, or trace needs to be initialized with parent's contents)
|
|
41
42
|
const traced: TracedPublicStorageRead = {
|
|
@@ -44,6 +45,7 @@ export class WorldStateAccessTrace {
|
|
|
44
45
|
slot,
|
|
45
46
|
value,
|
|
46
47
|
exists,
|
|
48
|
+
cached,
|
|
47
49
|
counter: new Fr(this.accessCounter),
|
|
48
50
|
// endLifetime: Fr.ZERO,
|
|
49
51
|
};
|
|
@@ -151,9 +153,6 @@ export class WorldStateAccessTrace {
|
|
|
151
153
|
/**
|
|
152
154
|
* Merges another trace into this one
|
|
153
155
|
*
|
|
154
|
-
* - Public state journals (r/w logs), with the accessing being appended in chronological order
|
|
155
|
-
* - Utxo objects are concatenated
|
|
156
|
-
*
|
|
157
156
|
* @param incomingTrace - the incoming trace to merge into this instance
|
|
158
157
|
*/
|
|
159
158
|
public acceptAndMerge(incomingTrace: WorldStateAccessTrace) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AvmContext } from '../avm_context.js';
|
|
2
|
-
import { type IntegralValue } from '../avm_memory_types.js';
|
|
2
|
+
import { type IntegralValue, type TaggedMemoryInterface, TypeTag } from '../avm_memory_types.js';
|
|
3
3
|
import { Opcode } from '../serialization/instruction_serialization.js';
|
|
4
4
|
import { ThreeOperandInstruction, TwoOperandInstruction } from './instruction_impl.js';
|
|
5
5
|
|
|
@@ -9,7 +9,7 @@ abstract class ThreeOperandBitwiseInstruction extends ThreeOperandInstruction {
|
|
|
9
9
|
const memory = context.machineState.memory.track(this.type);
|
|
10
10
|
context.machineState.consumeGas(this.gasCost(memoryOperations));
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
this.checkTags(memory, this.inTag, this.aOffset, this.bOffset);
|
|
13
13
|
|
|
14
14
|
const a = memory.getAs<IntegralValue>(this.aOffset);
|
|
15
15
|
const b = memory.getAs<IntegralValue>(this.bOffset);
|
|
@@ -22,13 +22,16 @@ abstract class ThreeOperandBitwiseInstruction extends ThreeOperandInstruction {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
protected abstract compute(a: IntegralValue, b: IntegralValue): IntegralValue;
|
|
25
|
+
protected checkTags(memory: TaggedMemoryInterface, inTag: number, aOffset: number, bOffset: number) {
|
|
26
|
+
memory.checkTags(inTag, aOffset, bOffset);
|
|
27
|
+
}
|
|
25
28
|
}
|
|
26
29
|
|
|
27
30
|
export class And extends ThreeOperandBitwiseInstruction {
|
|
28
31
|
static readonly type: string = 'AND';
|
|
29
32
|
static readonly opcode = Opcode.AND;
|
|
30
33
|
|
|
31
|
-
protected compute(a: IntegralValue, b: IntegralValue): IntegralValue {
|
|
34
|
+
protected override compute(a: IntegralValue, b: IntegralValue): IntegralValue {
|
|
32
35
|
return a.and(b);
|
|
33
36
|
}
|
|
34
37
|
}
|
|
@@ -37,7 +40,7 @@ export class Or extends ThreeOperandBitwiseInstruction {
|
|
|
37
40
|
static readonly type: string = 'OR';
|
|
38
41
|
static readonly opcode = Opcode.OR;
|
|
39
42
|
|
|
40
|
-
protected compute(a: IntegralValue, b: IntegralValue): IntegralValue {
|
|
43
|
+
protected override compute(a: IntegralValue, b: IntegralValue): IntegralValue {
|
|
41
44
|
return a.or(b);
|
|
42
45
|
}
|
|
43
46
|
}
|
|
@@ -46,7 +49,7 @@ export class Xor extends ThreeOperandBitwiseInstruction {
|
|
|
46
49
|
static readonly type: string = 'XOR';
|
|
47
50
|
static readonly opcode = Opcode.XOR;
|
|
48
51
|
|
|
49
|
-
protected compute(a: IntegralValue, b: IntegralValue): IntegralValue {
|
|
52
|
+
protected override compute(a: IntegralValue, b: IntegralValue): IntegralValue {
|
|
50
53
|
return a.xor(b);
|
|
51
54
|
}
|
|
52
55
|
}
|
|
@@ -55,18 +58,26 @@ export class Shl extends ThreeOperandBitwiseInstruction {
|
|
|
55
58
|
static readonly type: string = 'SHL';
|
|
56
59
|
static readonly opcode = Opcode.SHL;
|
|
57
60
|
|
|
58
|
-
protected compute(a: IntegralValue, b: IntegralValue): IntegralValue {
|
|
61
|
+
protected override compute(a: IntegralValue, b: IntegralValue): IntegralValue {
|
|
59
62
|
return a.shl(b);
|
|
60
63
|
}
|
|
64
|
+
protected override checkTags(memory: TaggedMemoryInterface, inTag: number, aOffset: number, bOffset: number) {
|
|
65
|
+
memory.checkTag(inTag, aOffset);
|
|
66
|
+
memory.checkTag(TypeTag.UINT8, bOffset);
|
|
67
|
+
}
|
|
61
68
|
}
|
|
62
69
|
|
|
63
70
|
export class Shr extends ThreeOperandBitwiseInstruction {
|
|
64
71
|
static readonly type: string = 'SHR';
|
|
65
72
|
static readonly opcode = Opcode.SHR;
|
|
66
73
|
|
|
67
|
-
protected compute(a: IntegralValue, b: IntegralValue): IntegralValue {
|
|
74
|
+
protected override compute(a: IntegralValue, b: IntegralValue): IntegralValue {
|
|
68
75
|
return a.shr(b);
|
|
69
76
|
}
|
|
77
|
+
protected override checkTags(memory: TaggedMemoryInterface, inTag: number, aOffset: number, bOffset: number) {
|
|
78
|
+
memory.checkTag(inTag, aOffset);
|
|
79
|
+
memory.checkTag(TypeTag.UINT8, bOffset);
|
|
80
|
+
}
|
|
70
81
|
}
|
|
71
82
|
|
|
72
83
|
export class Not extends TwoOperandInstruction {
|
|
@@ -59,6 +59,15 @@ export class FeePerDAGas extends EnvironmentGetterInstruction {
|
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
export class TransactionFee extends EnvironmentGetterInstruction {
|
|
63
|
+
static type: string = 'TRANSACTIONFEE';
|
|
64
|
+
static readonly opcode: Opcode = Opcode.TRANSACTIONFEE;
|
|
65
|
+
|
|
66
|
+
protected getEnvironmentValue(env: AvmExecutionEnvironment) {
|
|
67
|
+
return env.transactionFee;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
62
71
|
export class ChainId extends EnvironmentGetterInstruction {
|
|
63
72
|
static type: string = 'CHAINID';
|
|
64
73
|
static readonly opcode: Opcode = Opcode.CHAINID;
|
|
@@ -1,16 +1,12 @@
|
|
|
1
|
-
import { FunctionSelector } from '@aztec/circuits.js';
|
|
1
|
+
import { FunctionSelector, Gas } from '@aztec/circuits.js';
|
|
2
2
|
import { padArrayEnd } from '@aztec/foundation/collection';
|
|
3
3
|
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
convertPublicExecutionResult,
|
|
7
|
-
createPublicExecutionContext,
|
|
8
|
-
updateAvmContextFromPublicExecutionResult,
|
|
9
|
-
} from '../../public/transitional_adaptors.js';
|
|
4
|
+
import { convertAvmResultsToPxResult, createPublicExecution } from '../../public/transitional_adaptors.js';
|
|
10
5
|
import type { AvmContext } from '../avm_context.js';
|
|
11
6
|
import { gasLeftToGas, sumGas } from '../avm_gas.js';
|
|
12
7
|
import { Field, Uint8 } from '../avm_memory_types.js';
|
|
13
8
|
import { type AvmContractCallResults } from '../avm_message_call_result.js';
|
|
9
|
+
import { AvmSimulator } from '../avm_simulator.js';
|
|
14
10
|
import { Opcode, OperandType } from '../serialization/instruction_serialization.js';
|
|
15
11
|
import { Addressing } from './addressing_mode.js';
|
|
16
12
|
import { Instruction } from './instruction.js';
|
|
@@ -69,7 +65,7 @@ abstract class ExternalCall extends Instruction {
|
|
|
69
65
|
const totalGas = sumGas(this.gasCost(memoryOperations), allocatedGas);
|
|
70
66
|
context.machineState.consumeGas(totalGas);
|
|
71
67
|
|
|
72
|
-
// TRANSITIONAL: This should be removed once the
|
|
68
|
+
// TRANSITIONAL: This should be removed once the kernel handles and entire enqueued call per circuit
|
|
73
69
|
const nestedContext = context.createNestedContractCallContext(
|
|
74
70
|
callAddress.toFr(),
|
|
75
71
|
calldata,
|
|
@@ -77,11 +73,21 @@ abstract class ExternalCall extends Instruction {
|
|
|
77
73
|
this.type,
|
|
78
74
|
FunctionSelector.fromField(functionSelector),
|
|
79
75
|
);
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
const
|
|
76
|
+
const startSideEffectCounter = nestedContext.persistableState.trace.accessCounter;
|
|
77
|
+
|
|
78
|
+
const oldStyleExecution = createPublicExecution(startSideEffectCounter, nestedContext.environment, calldata);
|
|
79
|
+
const nestedCallResults: AvmContractCallResults = await new AvmSimulator(nestedContext).execute();
|
|
80
|
+
const pxResults = convertAvmResultsToPxResult(
|
|
81
|
+
nestedCallResults,
|
|
82
|
+
startSideEffectCounter,
|
|
83
|
+
oldStyleExecution,
|
|
84
|
+
Gas.from(allocatedGas),
|
|
85
|
+
nestedContext,
|
|
86
|
+
);
|
|
87
|
+
// store the old PublicExecutionResult object to maintain a recursive data structure for the old kernel
|
|
88
|
+
context.persistableState.transitionalExecutionResult.nestedExecutions.push(pxResults);
|
|
89
|
+
// END TRANSITIONAL
|
|
90
|
+
|
|
85
91
|
// const nestedContext = context.createNestedContractCallContext(
|
|
86
92
|
// callAddress.toFr(),
|
|
87
93
|
// calldata,
|
|
@@ -90,7 +96,6 @@ abstract class ExternalCall extends Instruction {
|
|
|
90
96
|
// FunctionSelector.fromField(functionSelector),
|
|
91
97
|
// );
|
|
92
98
|
// const nestedCallResults: AvmContractCallResults = await new AvmSimulator(nestedContext).execute();
|
|
93
|
-
// const nestedPersistableState = nestedContext.persistableState;
|
|
94
99
|
|
|
95
100
|
const success = !nestedCallResults.reverted;
|
|
96
101
|
|
|
@@ -112,9 +117,9 @@ abstract class ExternalCall extends Instruction {
|
|
|
112
117
|
|
|
113
118
|
// TODO: Should we merge the changes from a nested call in the case of a STATIC call?
|
|
114
119
|
if (success) {
|
|
115
|
-
context.persistableState.acceptNestedCallState(
|
|
120
|
+
context.persistableState.acceptNestedCallState(nestedContext.persistableState);
|
|
116
121
|
} else {
|
|
117
|
-
context.persistableState.rejectNestedCallState(
|
|
122
|
+
context.persistableState.rejectNestedCallState(nestedContext.persistableState);
|
|
118
123
|
}
|
|
119
124
|
|
|
120
125
|
memory.assert(memoryOperations);
|
|
@@ -46,6 +46,7 @@ import {
|
|
|
46
46
|
StorageAddress,
|
|
47
47
|
Sub,
|
|
48
48
|
Timestamp,
|
|
49
|
+
TransactionFee,
|
|
49
50
|
Version,
|
|
50
51
|
Xor,
|
|
51
52
|
} from '../opcodes/index.js';
|
|
@@ -82,6 +83,7 @@ const INSTRUCTION_SET = () =>
|
|
|
82
83
|
[Sender.opcode, Sender],
|
|
83
84
|
[FeePerL2Gas.opcode, FeePerL2Gas],
|
|
84
85
|
[FeePerDAGas.opcode, FeePerDAGas],
|
|
86
|
+
[TransactionFee.opcode, TransactionFee],
|
|
85
87
|
//[Contractcalldepth.opcode, Contractcalldepth],
|
|
86
88
|
// Execution Environment - Globals
|
|
87
89
|
[ChainId.opcode, ChainId],
|