@aztec/simulator 0.32.1 → 0.34.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/README.md +5 -3
- package/dest/acvm/acvm.js +2 -2
- package/dest/acvm/oracle/index.d.ts +0 -1
- package/dest/acvm/oracle/index.d.ts.map +1 -1
- package/dest/acvm/oracle/index.js +1 -2
- package/dest/acvm/oracle/oracle.d.ts +1 -1
- package/dest/acvm/oracle/oracle.d.ts.map +1 -1
- package/dest/acvm/oracle/oracle.js +4 -5
- package/dest/avm/avm_context.d.ts +4 -14
- package/dest/avm/avm_context.d.ts.map +1 -1
- package/dest/avm/avm_context.js +10 -22
- package/dest/avm/avm_execution_environment.d.ts +4 -3
- package/dest/avm/avm_execution_environment.d.ts.map +1 -1
- package/dest/avm/avm_execution_environment.js +8 -7
- package/dest/avm/avm_gas.d.ts +71 -0
- package/dest/avm/avm_gas.d.ts.map +1 -0
- package/dest/avm/avm_gas.js +161 -0
- package/dest/avm/avm_machine_state.d.ts +4 -2
- package/dest/avm/avm_machine_state.d.ts.map +1 -1
- package/dest/avm/avm_machine_state.js +8 -2
- package/dest/avm/avm_memory_types.d.ts +53 -1
- package/dest/avm/avm_memory_types.d.ts.map +1 -1
- package/dest/avm/avm_memory_types.js +99 -6
- package/dest/avm/avm_simulator.d.ts.map +1 -1
- package/dest/avm/avm_simulator.js +15 -13
- package/dest/avm/fixtures/index.d.ts.map +1 -1
- package/dest/avm/fixtures/index.js +3 -3
- package/dest/avm/journal/journal.d.ts +14 -13
- package/dest/avm/journal/journal.d.ts.map +1 -1
- package/dest/avm/journal/journal.js +5 -5
- package/dest/avm/journal/trace.d.ts +8 -19
- package/dest/avm/journal/trace.d.ts.map +1 -1
- package/dest/avm/journal/trace.js +48 -116
- package/dest/avm/journal/trace_types.d.ts +23 -4
- package/dest/avm/journal/trace_types.d.ts.map +1 -1
- package/dest/avm/opcodes/accrued_substate.d.ts.map +1 -1
- package/dest/avm/opcodes/accrued_substate.js +45 -17
- package/dest/avm/opcodes/addressing_mode.d.ts +5 -3
- package/dest/avm/opcodes/addressing_mode.d.ts.map +1 -1
- package/dest/avm/opcodes/addressing_mode.js +5 -1
- package/dest/avm/opcodes/arithmetic.d.ts +7 -3
- package/dest/avm/opcodes/arithmetic.d.ts.map +1 -1
- package/dest/avm/opcodes/arithmetic.js +27 -16
- package/dest/avm/opcodes/bitwise.d.ts +21 -20
- package/dest/avm/opcodes/bitwise.d.ts.map +1 -1
- package/dest/avm/opcodes/bitwise.js +43 -65
- package/dest/avm/opcodes/comparators.d.ts +12 -9
- package/dest/avm/opcodes/comparators.d.ts.map +1 -1
- package/dest/avm/opcodes/comparators.js +22 -32
- package/dest/avm/opcodes/context_getters.d.ts +20 -0
- package/dest/avm/opcodes/context_getters.d.ts.map +1 -0
- package/dest/avm/opcodes/context_getters.js +26 -0
- package/dest/avm/opcodes/contract.d.ts +14 -0
- package/dest/avm/opcodes/contract.d.ts.map +1 -0
- package/dest/avm/opcodes/contract.js +49 -0
- package/dest/avm/opcodes/control_flow.d.ts.map +1 -1
- package/dest/avm/opcodes/control_flow.js +12 -2
- package/dest/avm/opcodes/environment_getters.d.ts +30 -33
- package/dest/avm/opcodes/environment_getters.d.ts.map +1 -1
- package/dest/avm/opcodes/environment_getters.js +34 -43
- package/dest/avm/opcodes/external_calls.d.ts +13 -19
- package/dest/avm/opcodes/external_calls.d.ts.map +1 -1
- package/dest/avm/opcodes/external_calls.js +69 -72
- package/dest/avm/opcodes/hashing.d.ts +2 -1
- package/dest/avm/opcodes/hashing.d.ts.map +1 -1
- package/dest/avm/opcodes/hashing.js +37 -18
- package/dest/avm/opcodes/index.d.ts +1 -0
- package/dest/avm/opcodes/index.d.ts.map +1 -1
- package/dest/avm/opcodes/index.js +2 -1
- package/dest/avm/opcodes/instruction.d.ts +10 -15
- package/dest/avm/opcodes/instruction.d.ts.map +1 -1
- package/dest/avm/opcodes/instruction.js +12 -22
- package/dest/avm/opcodes/instruction_impl.d.ts +14 -0
- package/dest/avm/opcodes/instruction_impl.d.ts.map +1 -1
- package/dest/avm/opcodes/instruction_impl.js +37 -16
- package/dest/avm/opcodes/memory.d.ts +4 -3
- package/dest/avm/opcodes/memory.d.ts.map +1 -1
- package/dest/avm/opcodes/memory.js +38 -19
- package/dest/avm/opcodes/storage.d.ts +5 -0
- package/dest/avm/opcodes/storage.d.ts.map +1 -1
- package/dest/avm/opcodes/storage.js +21 -7
- package/dest/avm/serialization/bytecode_serialization.d.ts.map +1 -1
- package/dest/avm/serialization/bytecode_serialization.js +7 -5
- package/dest/avm/serialization/instruction_serialization.d.ts +12 -11
- package/dest/avm/serialization/instruction_serialization.d.ts.map +1 -1
- package/dest/avm/serialization/instruction_serialization.js +13 -12
- package/dest/client/client_execution_context.d.ts +2 -2
- package/dest/client/client_execution_context.d.ts.map +1 -1
- package/dest/client/client_execution_context.js +6 -6
- package/dest/client/private_execution.d.ts +1 -1
- package/dest/client/private_execution.d.ts.map +1 -1
- package/dest/client/private_execution.js +8 -4
- package/dest/client/unconstrained_execution.d.ts +1 -1
- package/dest/client/unconstrained_execution.d.ts.map +1 -1
- package/dest/client/unconstrained_execution.js +2 -2
- package/dest/client/view_data_oracle.d.ts +2 -2
- package/dest/client/view_data_oracle.d.ts.map +1 -1
- package/dest/client/view_data_oracle.js +2 -2
- package/dest/public/executor.d.ts +2 -8
- package/dest/public/executor.d.ts.map +1 -1
- package/dest/public/executor.js +101 -69
- package/dest/public/index.d.ts +1 -1
- package/dest/public/index.d.ts.map +1 -1
- package/dest/public/public_execution_context.d.ts +6 -6
- package/dest/public/public_execution_context.d.ts.map +1 -1
- package/dest/public/public_execution_context.js +8 -12
- package/dest/public/transitional_adaptors.d.ts +32 -0
- package/dest/public/transitional_adaptors.d.ts.map +1 -0
- package/dest/public/transitional_adaptors.js +161 -0
- package/package.json +15 -9
- package/src/acvm/acvm.ts +1 -1
- package/src/acvm/oracle/index.ts +0 -1
- package/src/acvm/oracle/oracle.ts +3 -4
- package/src/avm/avm_context.ts +11 -33
- package/src/avm/avm_execution_environment.ts +9 -17
- package/src/avm/{avm_gas_cost.ts → avm_gas.ts} +75 -21
- package/src/avm/avm_machine_state.ts +9 -2
- package/src/avm/avm_memory_types.ts +134 -6
- package/src/avm/avm_simulator.ts +14 -12
- package/src/avm/fixtures/index.ts +2 -1
- package/src/avm/journal/journal.ts +24 -17
- package/src/avm/journal/trace.ts +59 -121
- package/src/avm/journal/trace_types.ts +39 -39
- package/src/avm/opcodes/accrued_substate.ts +58 -23
- package/src/avm/opcodes/addressing_mode.ts +8 -3
- package/src/avm/opcodes/arithmetic.ts +32 -22
- package/src/avm/opcodes/bitwise.ts +49 -83
- package/src/avm/opcodes/comparators.ts +28 -43
- package/src/avm/opcodes/context_getters.ts +32 -0
- package/src/avm/opcodes/contract.ts +58 -0
- package/src/avm/opcodes/control_flow.ts +23 -5
- package/src/avm/opcodes/environment_getters.ts +35 -44
- package/src/avm/opcodes/external_calls.ts +90 -89
- package/src/avm/opcodes/hashing.ts +45 -22
- package/src/avm/opcodes/index.ts +1 -0
- package/src/avm/opcodes/instruction.ts +14 -26
- package/src/avm/opcodes/instruction_impl.ts +45 -15
- package/src/avm/opcodes/memory.ts +48 -28
- package/src/avm/opcodes/storage.ts +26 -12
- package/src/avm/serialization/bytecode_serialization.ts +6 -3
- package/src/avm/serialization/instruction_serialization.ts +1 -0
- package/src/client/client_execution_context.ts +5 -5
- package/src/client/private_execution.ts +10 -4
- package/src/client/unconstrained_execution.ts +1 -1
- package/src/client/view_data_oracle.ts +1 -1
- package/src/public/executor.ts +123 -75
- package/src/public/index.ts +2 -2
- package/src/public/public_execution_context.ts +14 -19
- package/src/public/transitional_adaptors.ts +240 -0
- package/dest/acvm/oracle/debug.d.ts +0 -19
- package/dest/acvm/oracle/debug.d.ts.map +0 -1
- package/dest/acvm/oracle/debug.js +0 -95
- package/dest/avm/avm_gas_cost.d.ts +0 -322
- package/dest/avm/avm_gas_cost.d.ts.map +0 -1
- package/dest/avm/avm_gas_cost.js +0 -118
- package/dest/avm/temporary_executor_migration.d.ts +0 -25
- package/dest/avm/temporary_executor_migration.d.ts.map +0 -1
- package/dest/avm/temporary_executor_migration.js +0 -83
- package/src/acvm/oracle/debug.ts +0 -109
- package/src/avm/temporary_executor_migration.ts +0 -122
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { toBufferBE } from '@aztec/foundation/bigint-buffer';
|
|
2
2
|
import { Fr } from '@aztec/foundation/fields';
|
|
3
3
|
import { type DebugLogger, createDebugLogger } from '@aztec/foundation/log';
|
|
4
|
+
import { type FunctionsOf } from '@aztec/foundation/types';
|
|
4
5
|
|
|
5
6
|
import { strict as assert } from 'assert';
|
|
6
7
|
|
|
7
|
-
import { TagCheckError } from './errors.js';
|
|
8
|
+
import { InstructionExecutionError, TagCheckError } from './errors.js';
|
|
9
|
+
import { Addressing, AddressingMode } from './opcodes/addressing_mode.js';
|
|
8
10
|
|
|
9
11
|
/** MemoryValue gathers the common operations for all memory types. */
|
|
10
12
|
export abstract class MemoryValue {
|
|
@@ -30,6 +32,11 @@ export abstract class MemoryValue {
|
|
|
30
32
|
return new Fr(this.toBigInt());
|
|
31
33
|
}
|
|
32
34
|
|
|
35
|
+
// To number. Throws if exceeds max safe int.
|
|
36
|
+
public toNumber(): number {
|
|
37
|
+
return this.toFr().toNumber();
|
|
38
|
+
}
|
|
39
|
+
|
|
33
40
|
public toString(): string {
|
|
34
41
|
return `${this.constructor.name}(0x${this.toBigInt().toString(16)})`;
|
|
35
42
|
}
|
|
@@ -198,10 +205,16 @@ export enum TypeTag {
|
|
|
198
205
|
INVALID,
|
|
199
206
|
}
|
|
200
207
|
|
|
208
|
+
// Lazy interface definition for tagged memory
|
|
209
|
+
export type TaggedMemoryInterface = FunctionsOf<TaggedMemory>;
|
|
210
|
+
|
|
201
211
|
// TODO: Consider automatic conversion when getting undefined values.
|
|
202
|
-
export class TaggedMemory {
|
|
212
|
+
export class TaggedMemory implements TaggedMemoryInterface {
|
|
203
213
|
static readonly log: DebugLogger = createDebugLogger('aztec:avm_simulator:memory');
|
|
204
214
|
|
|
215
|
+
// Whether to track and validate memory accesses for each instruction.
|
|
216
|
+
static readonly TRACK_MEMORY_ACCESSES = process.env.NODE_ENV === 'test';
|
|
217
|
+
|
|
205
218
|
// FIXME: memory should be 2^32, but TS doesn't allow for arrays that big.
|
|
206
219
|
static readonly MAX_MEMORY_SIZE = Number((1n << 32n) - 2n);
|
|
207
220
|
private _mem: MemoryValue[];
|
|
@@ -211,6 +224,11 @@ export class TaggedMemory {
|
|
|
211
224
|
this._mem = [];
|
|
212
225
|
}
|
|
213
226
|
|
|
227
|
+
/** Returns a MeteredTaggedMemory instance to track the number of reads and writes if TRACK_MEMORY_ACCESSES is set. */
|
|
228
|
+
public track(type: string = 'instruction') {
|
|
229
|
+
return TaggedMemory.TRACK_MEMORY_ACCESSES ? new MeteredTaggedMemory(this, type) : this;
|
|
230
|
+
}
|
|
231
|
+
|
|
214
232
|
public get(offset: number): MemoryValue {
|
|
215
233
|
assert(offset < TaggedMemory.MAX_MEMORY_SIZE);
|
|
216
234
|
const value = this.getAs<MemoryValue>(offset);
|
|
@@ -220,7 +238,10 @@ export class TaggedMemory {
|
|
|
220
238
|
public getAs<T>(offset: number): T {
|
|
221
239
|
assert(offset < TaggedMemory.MAX_MEMORY_SIZE);
|
|
222
240
|
const word = this._mem[offset];
|
|
223
|
-
TaggedMemory.log(`get(${offset}) = ${word}`);
|
|
241
|
+
TaggedMemory.log.debug(`get(${offset}) = ${word}`);
|
|
242
|
+
if (word === undefined) {
|
|
243
|
+
TaggedMemory.log.warn(`Memory at offset ${offset} is undefined! This might be OK if it's stack dumping.`);
|
|
244
|
+
}
|
|
224
245
|
return word as T;
|
|
225
246
|
}
|
|
226
247
|
|
|
@@ -228,7 +249,8 @@ export class TaggedMemory {
|
|
|
228
249
|
assert(offset < TaggedMemory.MAX_MEMORY_SIZE);
|
|
229
250
|
assert(offset + size < TaggedMemory.MAX_MEMORY_SIZE);
|
|
230
251
|
const value = this._mem.slice(offset, offset + size);
|
|
231
|
-
TaggedMemory.log(`getSlice(${offset}, ${size}) = ${value}`);
|
|
252
|
+
TaggedMemory.log.debug(`getSlice(${offset}, ${size}) = ${value}`);
|
|
253
|
+
assert(!value.some(e => e === undefined), 'Memory slice contains undefined values.');
|
|
232
254
|
assert(value.length === size, `Expected slice of size ${size}, got ${value.length}.`);
|
|
233
255
|
return value;
|
|
234
256
|
}
|
|
@@ -248,7 +270,7 @@ export class TaggedMemory {
|
|
|
248
270
|
public set(offset: number, v: MemoryValue) {
|
|
249
271
|
assert(offset < TaggedMemory.MAX_MEMORY_SIZE);
|
|
250
272
|
this._mem[offset] = v;
|
|
251
|
-
TaggedMemory.log(`set(${offset}, ${v})`);
|
|
273
|
+
TaggedMemory.log.debug(`set(${offset}, ${v})`);
|
|
252
274
|
}
|
|
253
275
|
|
|
254
276
|
public setSlice(offset: number, vs: MemoryValue[]) {
|
|
@@ -259,7 +281,7 @@ export class TaggedMemory {
|
|
|
259
281
|
this._mem.length = offset + vs.length;
|
|
260
282
|
}
|
|
261
283
|
this._mem.splice(offset, vs.length, ...vs);
|
|
262
|
-
TaggedMemory.log(`setSlice(${offset}, ${vs})`);
|
|
284
|
+
TaggedMemory.log.debug(`setSlice(${offset}, ${vs})`);
|
|
263
285
|
}
|
|
264
286
|
|
|
265
287
|
public getTag(offset: number): TypeTag {
|
|
@@ -367,4 +389,110 @@ export class TaggedMemory {
|
|
|
367
389
|
throw new Error(`${TypeTag[tag]} is not a valid integral type.`);
|
|
368
390
|
}
|
|
369
391
|
}
|
|
392
|
+
|
|
393
|
+
/** No-op. Implemented here for compatibility with the MeteredTaggedMemory. */
|
|
394
|
+
public assert(_operations: Partial<MemoryOperations & { indirect: number }>) {}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/** Tagged memory wrapper with metering for each memory read and write operation. */
|
|
398
|
+
export class MeteredTaggedMemory implements TaggedMemoryInterface {
|
|
399
|
+
private reads: number = 0;
|
|
400
|
+
private writes: number = 0;
|
|
401
|
+
|
|
402
|
+
constructor(private wrapped: TaggedMemory, private type: string = 'instruction') {}
|
|
403
|
+
|
|
404
|
+
/** Returns the number of reads and writes tracked so far and resets them to zero. */
|
|
405
|
+
public reset(): MemoryOperations {
|
|
406
|
+
const stats = { reads: this.reads, writes: this.writes };
|
|
407
|
+
this.reads = 0;
|
|
408
|
+
this.writes = 0;
|
|
409
|
+
return stats;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Asserts that the exact number of memory operations have been performed.
|
|
414
|
+
* Indirect represents the flags for indirect accesses: each bit set to one counts as an extra read.
|
|
415
|
+
*/
|
|
416
|
+
public assert(operations: Partial<MemoryOperations & { indirect: number }>) {
|
|
417
|
+
const { reads: expectedReads, writes: expectedWrites, indirect } = { reads: 0, writes: 0, ...operations };
|
|
418
|
+
|
|
419
|
+
const totalExpectedReads = expectedReads + Addressing.fromWire(indirect ?? 0).count(AddressingMode.INDIRECT);
|
|
420
|
+
const { reads: actualReads, writes: actualWrites } = this.reset();
|
|
421
|
+
if (actualReads !== totalExpectedReads) {
|
|
422
|
+
throw new InstructionExecutionError(
|
|
423
|
+
`Incorrect number of memory reads for ${this.type}: expected ${totalExpectedReads} but executed ${actualReads}`,
|
|
424
|
+
);
|
|
425
|
+
}
|
|
426
|
+
if (actualWrites !== expectedWrites) {
|
|
427
|
+
throw new InstructionExecutionError(
|
|
428
|
+
`Incorrect number of memory writes for ${this.type}: expected ${expectedWrites} but executed ${actualWrites}`,
|
|
429
|
+
);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
public track(type: string = 'instruction'): MeteredTaggedMemory {
|
|
434
|
+
return new MeteredTaggedMemory(this.wrapped, type);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
public get(offset: number): MemoryValue {
|
|
438
|
+
this.reads++;
|
|
439
|
+
return this.wrapped.get(offset);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
public getSliceAs<T>(offset: number, size: number): T[] {
|
|
443
|
+
this.reads += size;
|
|
444
|
+
return this.wrapped.getSliceAs<T>(offset, size);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
public getAs<T>(offset: number): T {
|
|
448
|
+
this.reads++;
|
|
449
|
+
return this.wrapped.getAs(offset);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
public getSlice(offset: number, size: number): MemoryValue[] {
|
|
453
|
+
this.reads += size;
|
|
454
|
+
return this.wrapped.getSlice(offset, size);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
public set(offset: number, v: MemoryValue): void {
|
|
458
|
+
this.writes++;
|
|
459
|
+
this.wrapped.set(offset, v);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
public setSlice(offset: number, vs: MemoryValue[]): void {
|
|
463
|
+
this.writes += vs.length;
|
|
464
|
+
this.wrapped.setSlice(offset, vs);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
public getSliceTags(offset: number, size: number): TypeTag[] {
|
|
468
|
+
return this.wrapped.getSliceTags(offset, size);
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
public getTag(offset: number): TypeTag {
|
|
472
|
+
return this.wrapped.getTag(offset);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
public checkTag(tag: TypeTag, offset: number): void {
|
|
476
|
+
this.wrapped.checkTag(tag, offset);
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
public checkIsValidMemoryOffsetTag(offset: number): void {
|
|
480
|
+
this.wrapped.checkIsValidMemoryOffsetTag(offset);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
public checkTags(tag: TypeTag, ...offsets: number[]): void {
|
|
484
|
+
this.wrapped.checkTags(tag, ...offsets);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
public checkTagsRange(tag: TypeTag, startOffset: number, size: number): void {
|
|
488
|
+
this.wrapped.checkTagsRange(tag, startOffset, size);
|
|
489
|
+
}
|
|
370
490
|
}
|
|
491
|
+
|
|
492
|
+
/** Tracks number of memory reads and writes. */
|
|
493
|
+
export type MemoryOperations = {
|
|
494
|
+
/** How many total reads are performed. Slice reads are count as one per element. */
|
|
495
|
+
reads: number;
|
|
496
|
+
/** How many total writes are performed. Slice writes are count as one per element. */
|
|
497
|
+
writes: number;
|
|
498
|
+
};
|
package/src/avm/avm_simulator.ts
CHANGED
|
@@ -50,43 +50,45 @@ export class AvmSimulator {
|
|
|
50
50
|
*/
|
|
51
51
|
public async executeInstructions(instructions: Instruction[]): Promise<AvmContractCallResults> {
|
|
52
52
|
assert(instructions.length > 0);
|
|
53
|
+
const { machineState } = this.context;
|
|
53
54
|
try {
|
|
54
55
|
// Execute instruction pointed to by the current program counter
|
|
55
56
|
// continuing until the machine state signifies a halt
|
|
56
|
-
while (!
|
|
57
|
-
const instruction = instructions[
|
|
57
|
+
while (!machineState.halted) {
|
|
58
|
+
const instruction = instructions[machineState.pc];
|
|
58
59
|
assert(
|
|
59
60
|
!!instruction,
|
|
60
61
|
'AVM attempted to execute non-existent instruction. This should never happen (invalid bytecode or AVM simulator bug)!',
|
|
61
62
|
);
|
|
62
63
|
|
|
63
|
-
|
|
64
|
+
const gasLeft = `l1=${machineState.l1GasLeft} l2=${machineState.l2GasLeft} da=${machineState.daGasLeft}`;
|
|
65
|
+
this.log.debug(`@${machineState.pc} (${gasLeft}) ${instruction.toString()}`);
|
|
64
66
|
// Execute the instruction.
|
|
65
67
|
// Normal returns and reverts will return normally here.
|
|
66
68
|
// "Exceptional halts" will throw.
|
|
67
|
-
await instruction.
|
|
69
|
+
await instruction.execute(this.context);
|
|
68
70
|
|
|
69
|
-
if (
|
|
70
|
-
this.log('Passed end of program
|
|
71
|
-
throw new InvalidProgramCounterError(
|
|
71
|
+
if (machineState.pc >= instructions.length) {
|
|
72
|
+
this.log.warn('Passed end of program');
|
|
73
|
+
throw new InvalidProgramCounterError(machineState.pc, /*max=*/ instructions.length);
|
|
72
74
|
}
|
|
73
75
|
}
|
|
74
76
|
|
|
75
77
|
// Return results for processing by calling context
|
|
76
|
-
const results =
|
|
77
|
-
this.log(`Context execution results: ${results.toString()}`);
|
|
78
|
+
const results = machineState.getResults();
|
|
79
|
+
this.log.debug(`Context execution results: ${results.toString()}`);
|
|
78
80
|
return results;
|
|
79
81
|
} catch (e) {
|
|
80
|
-
this.log('Exceptional halt');
|
|
82
|
+
this.log.verbose('Exceptional halt');
|
|
81
83
|
if (!(e instanceof AvmExecutionError)) {
|
|
82
|
-
this.log(`Unknown error thrown by avm: ${e}`);
|
|
84
|
+
this.log.verbose(`Unknown error thrown by avm: ${e}`);
|
|
83
85
|
throw e;
|
|
84
86
|
}
|
|
85
87
|
|
|
86
88
|
// Return results for processing by calling context
|
|
87
89
|
// Note: "exceptional halts" cannot return data
|
|
88
90
|
const results = new AvmContractCallResults(/*reverted=*/ true, /*output=*/ [], /*revertReason=*/ e);
|
|
89
|
-
this.log(`Context execution results: ${results.toString()}`);
|
|
91
|
+
this.log.debug(`Context execution results: ${results.toString()}`);
|
|
90
92
|
return results;
|
|
91
93
|
}
|
|
92
94
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { SiblingPath } from '@aztec/circuit-types';
|
|
2
|
-
import { GlobalVariables, L1_TO_L2_MSG_TREE_HEIGHT } from '@aztec/circuits.js';
|
|
2
|
+
import { GlobalVariables, Header, L1_TO_L2_MSG_TREE_HEIGHT } from '@aztec/circuits.js';
|
|
3
3
|
import { FunctionSelector } from '@aztec/foundation/abi';
|
|
4
4
|
import { AztecAddress } from '@aztec/foundation/aztec-address';
|
|
5
5
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
@@ -67,6 +67,7 @@ export function initExecutionEnvironment(overrides?: Partial<AvmExecutionEnviron
|
|
|
67
67
|
overrides?.feePerL2Gas ?? Fr.zero(),
|
|
68
68
|
overrides?.feePerDaGas ?? Fr.zero(),
|
|
69
69
|
overrides?.contractCallDepth ?? Fr.zero(),
|
|
70
|
+
overrides?.header ?? Header.empty(),
|
|
70
71
|
overrides?.globals ?? GlobalVariables.empty(),
|
|
71
72
|
overrides?.isStaticCall ?? false,
|
|
72
73
|
overrides?.isDelegateCall ?? false,
|
|
@@ -7,16 +7,27 @@ import { type HostStorage } from './host_storage.js';
|
|
|
7
7
|
import { Nullifiers } from './nullifiers.js';
|
|
8
8
|
import { PublicStorage } from './public_storage.js';
|
|
9
9
|
import { WorldStateAccessTrace } from './trace.js';
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
type TracedL1toL2MessageCheck,
|
|
12
|
+
type TracedNoteHash,
|
|
13
|
+
type TracedNoteHashCheck,
|
|
14
|
+
type TracedNullifier,
|
|
15
|
+
type TracedNullifierCheck,
|
|
16
|
+
type TracedPublicStorageRead,
|
|
17
|
+
type TracedPublicStorageWrite,
|
|
18
|
+
} from './trace_types.js';
|
|
11
19
|
|
|
12
20
|
/**
|
|
13
21
|
* Data held within the journal
|
|
14
22
|
*/
|
|
15
23
|
export type JournalData = {
|
|
24
|
+
storageWrites: TracedPublicStorageWrite[];
|
|
25
|
+
storageReads: TracedPublicStorageRead[];
|
|
26
|
+
|
|
16
27
|
noteHashChecks: TracedNoteHashCheck[];
|
|
17
|
-
newNoteHashes:
|
|
28
|
+
newNoteHashes: TracedNoteHash[];
|
|
18
29
|
nullifierChecks: TracedNullifierCheck[];
|
|
19
|
-
newNullifiers:
|
|
30
|
+
newNullifiers: TracedNullifier[];
|
|
20
31
|
l1ToL2MessageChecks: TracedL1toL2MessageCheck[];
|
|
21
32
|
|
|
22
33
|
newL1Messages: L2ToL1Message[];
|
|
@@ -24,11 +35,6 @@ export type JournalData = {
|
|
|
24
35
|
|
|
25
36
|
/** contract address -\> key -\> value */
|
|
26
37
|
currentStorageValue: Map<bigint, Map<bigint, Fr>>;
|
|
27
|
-
|
|
28
|
-
/** contract address -\> key -\> value[] (stored in order of access) */
|
|
29
|
-
storageWrites: Map<bigint, Map<bigint, Fr[]>>;
|
|
30
|
-
/** contract address -\> key -\> value[] (stored in order of access) */
|
|
31
|
-
storageReads: Map<bigint, Map<bigint, Fr[]>>;
|
|
32
38
|
};
|
|
33
39
|
|
|
34
40
|
/**
|
|
@@ -44,18 +50,19 @@ export class AvmPersistableStateManager {
|
|
|
44
50
|
/** Reference to node storage */
|
|
45
51
|
public readonly hostStorage: HostStorage;
|
|
46
52
|
|
|
53
|
+
// TODO: make members private once this is not used in transitional_adaptors.ts.
|
|
47
54
|
/** World State */
|
|
48
55
|
/** Public storage, including cached writes */
|
|
49
|
-
|
|
56
|
+
public publicStorage: PublicStorage;
|
|
50
57
|
/** Nullifier set, including cached/recently-emitted nullifiers */
|
|
51
|
-
|
|
58
|
+
public nullifiers: Nullifiers;
|
|
52
59
|
|
|
53
60
|
/** World State Access Trace */
|
|
54
|
-
|
|
61
|
+
public trace: WorldStateAccessTrace;
|
|
55
62
|
|
|
56
63
|
/** Accrued Substate **/
|
|
57
|
-
|
|
58
|
-
|
|
64
|
+
public newL1Messages: L2ToL1Message[] = [];
|
|
65
|
+
public newLogs: UnencryptedL2Log[] = [];
|
|
59
66
|
|
|
60
67
|
constructor(hostStorage: HostStorage, parent?: AvmPersistableStateManager) {
|
|
61
68
|
this.hostStorage = hostStorage;
|
|
@@ -93,9 +100,9 @@ export class AvmPersistableStateManager {
|
|
|
93
100
|
* @returns the latest value written to slot, or 0 if never written to before
|
|
94
101
|
*/
|
|
95
102
|
public async readStorage(storageAddress: Fr, slot: Fr): Promise<Fr> {
|
|
96
|
-
const [
|
|
103
|
+
const [exists, value] = await this.publicStorage.read(storageAddress, slot);
|
|
97
104
|
// We want to keep track of all performed reads (even reverted ones)
|
|
98
|
-
this.trace.tracePublicStorageRead(storageAddress, slot, value);
|
|
105
|
+
this.trace.tracePublicStorageRead(storageAddress, slot, value, exists);
|
|
99
106
|
return Promise.resolve(value);
|
|
100
107
|
}
|
|
101
108
|
|
|
@@ -119,8 +126,8 @@ export class AvmPersistableStateManager {
|
|
|
119
126
|
* Write a note hash, trace the write.
|
|
120
127
|
* @param noteHash - the unsiloed note hash to write
|
|
121
128
|
*/
|
|
122
|
-
public writeNoteHash(noteHash: Fr) {
|
|
123
|
-
this.trace.traceNewNoteHash(
|
|
129
|
+
public writeNoteHash(storageAddress: Fr, noteHash: Fr) {
|
|
130
|
+
this.trace.traceNewNoteHash(storageAddress, noteHash);
|
|
124
131
|
}
|
|
125
132
|
|
|
126
133
|
/**
|
package/src/avm/journal/trace.ts
CHANGED
|
@@ -1,23 +1,28 @@
|
|
|
1
1
|
import { Fr } from '@aztec/foundation/fields';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
type TracedL1toL2MessageCheck,
|
|
5
|
+
type TracedNoteHash,
|
|
6
|
+
type TracedNoteHashCheck,
|
|
7
|
+
type TracedNullifier,
|
|
8
|
+
type TracedNullifierCheck,
|
|
9
|
+
type TracedPublicStorageRead,
|
|
10
|
+
type TracedPublicStorageWrite,
|
|
11
|
+
} from './trace_types.js';
|
|
4
12
|
|
|
5
13
|
export class WorldStateAccessTrace {
|
|
6
14
|
public accessCounter: number;
|
|
7
|
-
//public contractCalls: Array<TracedContractCall> = [];
|
|
8
15
|
|
|
9
|
-
|
|
10
|
-
public
|
|
11
|
-
//public publicStorageWrites: Array<TracedPublicStorageWrite> = [];
|
|
12
|
-
public publicStorageWrites: Map<bigint, Map<bigint, Fr[]>> = new Map();
|
|
16
|
+
public publicStorageReads: TracedPublicStorageRead[] = [];
|
|
17
|
+
public publicStorageWrites: TracedPublicStorageWrite[] = [];
|
|
13
18
|
|
|
14
19
|
public noteHashChecks: TracedNoteHashCheck[] = [];
|
|
15
|
-
|
|
16
|
-
public newNoteHashes: Fr[] = [];
|
|
20
|
+
public newNoteHashes: TracedNoteHash[] = [];
|
|
17
21
|
public nullifierChecks: TracedNullifierCheck[] = [];
|
|
18
|
-
|
|
19
|
-
public newNullifiers: Fr[] = [];
|
|
22
|
+
public newNullifiers: TracedNullifier[] = [];
|
|
20
23
|
public l1ToL2MessageChecks: TracedL1toL2MessageCheck[] = [];
|
|
24
|
+
|
|
25
|
+
//public contractCalls: TracedContractCall[] = [];
|
|
21
26
|
//public archiveChecks: TracedArchiveLeafCheck[] = [];
|
|
22
27
|
|
|
23
28
|
constructor(parentTrace?: WorldStateAccessTrace) {
|
|
@@ -28,76 +33,73 @@ export class WorldStateAccessTrace {
|
|
|
28
33
|
return this.accessCounter;
|
|
29
34
|
}
|
|
30
35
|
|
|
31
|
-
public tracePublicStorageRead(storageAddress: Fr, slot: Fr, value: Fr
|
|
36
|
+
public tracePublicStorageRead(storageAddress: Fr, slot: Fr, value: Fr, exists: boolean) {
|
|
32
37
|
// TODO(4805): check if some threshold is reached for max storage reads
|
|
33
38
|
// (need access to parent length, or trace needs to be initialized with parent's contents)
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
this.journalRead(storageAddress, slot, value);
|
|
39
|
+
const traced: TracedPublicStorageRead = {
|
|
40
|
+
// callPointer: Fr.ZERO,
|
|
41
|
+
storageAddress,
|
|
42
|
+
slot,
|
|
43
|
+
value,
|
|
44
|
+
exists,
|
|
45
|
+
counter: new Fr(this.accessCounter),
|
|
46
|
+
// endLifetime: Fr.ZERO,
|
|
47
|
+
};
|
|
48
|
+
this.publicStorageReads.push(traced);
|
|
45
49
|
this.incrementAccessCounter();
|
|
46
50
|
}
|
|
47
51
|
|
|
48
52
|
public tracePublicStorageWrite(storageAddress: Fr, slot: Fr, value: Fr) {
|
|
49
53
|
// TODO(4805): check if some threshold is reached for max storage writes
|
|
50
54
|
// (need access to parent length, or trace needs to be initialized with parent's contents)
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
this.journalWrite(storageAddress, slot, value);
|
|
55
|
+
const traced: TracedPublicStorageWrite = {
|
|
56
|
+
// callPointer: Fr.ZERO,
|
|
57
|
+
storageAddress,
|
|
58
|
+
slot,
|
|
59
|
+
value,
|
|
60
|
+
counter: new Fr(this.accessCounter),
|
|
61
|
+
// endLifetime: Fr.ZERO,
|
|
62
|
+
};
|
|
63
|
+
this.publicStorageWrites.push(traced);
|
|
61
64
|
this.incrementAccessCounter();
|
|
62
65
|
}
|
|
63
66
|
|
|
64
67
|
public traceNoteHashCheck(storageAddress: Fr, noteHash: Fr, exists: boolean, leafIndex: Fr) {
|
|
65
68
|
const traced: TracedNoteHashCheck = {
|
|
66
|
-
callPointer: Fr.ZERO,
|
|
69
|
+
// callPointer: Fr.ZERO,
|
|
67
70
|
storageAddress,
|
|
68
71
|
noteHash,
|
|
69
72
|
exists,
|
|
70
73
|
counter: new Fr(this.accessCounter),
|
|
71
|
-
endLifetime: Fr.ZERO,
|
|
74
|
+
// endLifetime: Fr.ZERO,
|
|
72
75
|
leafIndex,
|
|
73
76
|
};
|
|
74
77
|
this.noteHashChecks.push(traced);
|
|
75
78
|
this.incrementAccessCounter();
|
|
76
79
|
}
|
|
77
80
|
|
|
78
|
-
public traceNewNoteHash(
|
|
81
|
+
public traceNewNoteHash(storageAddress: Fr, noteHash: Fr) {
|
|
79
82
|
// TODO(4805): check if some threshold is reached for max new note hash
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
this.newNoteHashes.push(noteHash);
|
|
83
|
+
const traced: TracedNoteHash = {
|
|
84
|
+
// callPointer: Fr.ZERO,
|
|
85
|
+
storageAddress,
|
|
86
|
+
noteHash,
|
|
87
|
+
counter: new Fr(this.accessCounter),
|
|
88
|
+
// endLifetime: Fr.ZERO,
|
|
89
|
+
};
|
|
90
|
+
this.newNoteHashes.push(traced);
|
|
89
91
|
this.incrementAccessCounter();
|
|
90
92
|
}
|
|
91
93
|
|
|
92
94
|
public traceNullifierCheck(storageAddress: Fr, nullifier: Fr, exists: boolean, isPending: boolean, leafIndex: Fr) {
|
|
93
95
|
// TODO(4805): check if some threshold is reached for max new nullifier
|
|
94
96
|
const traced: TracedNullifierCheck = {
|
|
95
|
-
callPointer: Fr.ZERO,
|
|
97
|
+
// callPointer: Fr.ZERO,
|
|
96
98
|
storageAddress,
|
|
97
99
|
nullifier,
|
|
98
100
|
exists,
|
|
99
101
|
counter: new Fr(this.accessCounter),
|
|
100
|
-
endLifetime: Fr.ZERO,
|
|
102
|
+
// endLifetime: Fr.ZERO,
|
|
101
103
|
isPending,
|
|
102
104
|
leafIndex,
|
|
103
105
|
};
|
|
@@ -105,17 +107,16 @@ export class WorldStateAccessTrace {
|
|
|
105
107
|
this.incrementAccessCounter();
|
|
106
108
|
}
|
|
107
109
|
|
|
108
|
-
public traceNewNullifier(
|
|
110
|
+
public traceNewNullifier(storageAddress: Fr, nullifier: Fr) {
|
|
109
111
|
// TODO(4805): check if some threshold is reached for max new nullifier
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
this.newNullifiers.push(nullifier);
|
|
112
|
+
const tracedNullifier: TracedNullifier = {
|
|
113
|
+
// callPointer: Fr.ZERO,
|
|
114
|
+
storageAddress,
|
|
115
|
+
nullifier,
|
|
116
|
+
counter: new Fr(this.accessCounter),
|
|
117
|
+
// endLifetime: Fr.ZERO,
|
|
118
|
+
};
|
|
119
|
+
this.newNullifiers.push(tracedNullifier);
|
|
119
120
|
this.incrementAccessCounter();
|
|
120
121
|
}
|
|
121
122
|
|
|
@@ -146,8 +147,8 @@ export class WorldStateAccessTrace {
|
|
|
146
147
|
*/
|
|
147
148
|
public acceptAndMerge(incomingTrace: WorldStateAccessTrace) {
|
|
148
149
|
// Merge storage read and write journals
|
|
149
|
-
|
|
150
|
-
|
|
150
|
+
this.publicStorageReads = this.publicStorageReads.concat(incomingTrace.publicStorageReads);
|
|
151
|
+
this.publicStorageWrites = this.publicStorageWrites.concat(incomingTrace.publicStorageWrites);
|
|
151
152
|
// Merge new note hashes and nullifiers
|
|
152
153
|
this.noteHashChecks = this.noteHashChecks.concat(incomingTrace.noteHashChecks);
|
|
153
154
|
this.newNoteHashes = this.newNoteHashes.concat(incomingTrace.newNoteHashes);
|
|
@@ -157,67 +158,4 @@ export class WorldStateAccessTrace {
|
|
|
157
158
|
// it is assumed that the incoming trace was initialized with this as parent, so accept counter
|
|
158
159
|
this.accessCounter = incomingTrace.accessCounter;
|
|
159
160
|
}
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* We want to keep track of all performed reads in the journal
|
|
163
|
-
* This information is hinted to the avm circuit
|
|
164
|
-
|
|
165
|
-
* @param contractAddress -
|
|
166
|
-
* @param key -
|
|
167
|
-
* @param value -
|
|
168
|
-
*/
|
|
169
|
-
journalUpdate(map: Map<bigint, Map<bigint, Fr[]>>, contractAddress: Fr, key: Fr, value: Fr): void {
|
|
170
|
-
let contractMap = map.get(contractAddress.toBigInt());
|
|
171
|
-
if (!contractMap) {
|
|
172
|
-
contractMap = new Map<bigint, Array<Fr>>();
|
|
173
|
-
map.set(contractAddress.toBigInt(), contractMap);
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
let accessArray = contractMap.get(key.toBigInt());
|
|
177
|
-
if (!accessArray) {
|
|
178
|
-
accessArray = new Array<Fr>();
|
|
179
|
-
contractMap.set(key.toBigInt(), accessArray);
|
|
180
|
-
}
|
|
181
|
-
accessArray.push(value);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// Create an instance of journalUpdate that appends to the read array
|
|
185
|
-
private journalRead = this.journalUpdate.bind(this, this.publicStorageReads);
|
|
186
|
-
// Create an instance of journalUpdate that appends to the writes array
|
|
187
|
-
private journalWrite = this.journalUpdate.bind(this, this.publicStorageWrites);
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* Merges two contract journalling maps together
|
|
192
|
-
* For read maps, we just append the childMap arrays into the host map arrays, as the order is important
|
|
193
|
-
*
|
|
194
|
-
* @param hostMap - The map to be merged into
|
|
195
|
-
* @param childMap - The map to be merged from
|
|
196
|
-
*/
|
|
197
|
-
function mergeContractJournalMaps(hostMap: Map<bigint, Map<bigint, Fr[]>>, childMap: Map<bigint, Map<bigint, Fr[]>>) {
|
|
198
|
-
for (const [key, value] of childMap) {
|
|
199
|
-
const map1Value = hostMap.get(key);
|
|
200
|
-
if (!map1Value) {
|
|
201
|
-
hostMap.set(key, value);
|
|
202
|
-
} else {
|
|
203
|
-
mergeStorageJournalMaps(map1Value, value);
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
/**
|
|
209
|
-
* Merge two storage journalling maps together (for a particular contract).
|
|
210
|
-
*
|
|
211
|
-
* @param hostMap - The map to be merge into
|
|
212
|
-
* @param childMap - The map to be merged from
|
|
213
|
-
*/
|
|
214
|
-
function mergeStorageJournalMaps(hostMap: Map<bigint, Fr[]>, childMap: Map<bigint, Fr[]>) {
|
|
215
|
-
for (const [key, value] of childMap) {
|
|
216
|
-
const readArr = hostMap.get(key);
|
|
217
|
-
if (!readArr) {
|
|
218
|
-
hostMap.set(key, value);
|
|
219
|
-
} else {
|
|
220
|
-
hostMap.set(key, readArr?.concat(...value));
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
161
|
}
|