@aztec/simulator 0.67.1 → 0.68.1
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 +1 -1
- package/dest/acvm/oracle/oracle.d.ts.map +1 -1
- package/dest/acvm/oracle/oracle.js +3 -3
- package/dest/acvm/oracle/typed_oracle.d.ts +1 -1
- package/dest/acvm/oracle/typed_oracle.d.ts.map +1 -1
- package/dest/acvm/oracle/typed_oracle.js +3 -3
- 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_memory_types.js +45 -38
- package/dest/avm/avm_simulator.d.ts +1 -0
- package/dest/avm/avm_simulator.d.ts.map +1 -1
- package/dest/avm/avm_simulator.js +38 -18
- package/dest/avm/avm_tree.d.ts +2 -23
- package/dest/avm/avm_tree.d.ts.map +1 -1
- package/dest/avm/avm_tree.js +27 -82
- package/dest/avm/errors.d.ts +8 -1
- package/dest/avm/errors.d.ts.map +1 -1
- package/dest/avm/errors.js +13 -3
- package/dest/avm/fixtures/index.d.ts +2 -0
- package/dest/avm/fixtures/index.d.ts.map +1 -1
- package/dest/avm/fixtures/index.js +4 -4
- package/dest/avm/journal/journal.d.ts +15 -7
- package/dest/avm/journal/journal.d.ts.map +1 -1
- package/dest/avm/journal/journal.js +30 -22
- package/dest/avm/journal/nullifiers.d.ts +0 -4
- package/dest/avm/journal/nullifiers.d.ts.map +1 -1
- package/dest/avm/journal/nullifiers.js +1 -11
- package/dest/avm/journal/public_storage.d.ts +1 -49
- package/dest/avm/journal/public_storage.d.ts.map +1 -1
- package/dest/avm/journal/public_storage.js +1 -19
- package/dest/avm/opcodes/addressing_mode.js +3 -3
- package/dest/avm/opcodes/conversion.d.ts +4 -4
- package/dest/avm/opcodes/conversion.d.ts.map +1 -1
- package/dest/avm/opcodes/conversion.js +22 -18
- package/dest/avm/opcodes/ec_add.d.ts.map +1 -1
- package/dest/avm/opcodes/ec_add.js +5 -4
- package/dest/avm/opcodes/external_calls.js +2 -2
- package/dest/avm/opcodes/hashing.d.ts.map +1 -1
- package/dest/avm/opcodes/hashing.js +5 -5
- package/dest/avm/opcodes/misc.d.ts.map +1 -1
- package/dest/avm/opcodes/misc.js +3 -3
- package/dest/avm/opcodes/multi_scalar_mul.d.ts.map +1 -1
- package/dest/avm/opcodes/multi_scalar_mul.js +9 -6
- package/dest/avm/test_utils.d.ts +1 -0
- package/dest/avm/test_utils.d.ts.map +1 -1
- package/dest/avm/test_utils.js +4 -1
- package/dest/client/client_execution_context.d.ts.map +1 -1
- package/dest/client/client_execution_context.js +2 -1
- package/dest/client/db_oracle.d.ts +7 -3
- package/dest/client/db_oracle.d.ts.map +1 -1
- package/dest/client/index.d.ts +1 -0
- package/dest/client/index.d.ts.map +1 -1
- package/dest/client/index.js +2 -1
- 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 +5 -4
- package/dest/providers/acvm_wasm.js +2 -2
- package/dest/providers/acvm_wasm_with_blobs.d.ts +7 -0
- package/dest/providers/acvm_wasm_with_blobs.d.ts.map +1 -0
- package/dest/providers/acvm_wasm_with_blobs.js +15 -0
- package/dest/providers/index.d.ts +1 -1
- package/dest/providers/index.d.ts.map +1 -1
- package/dest/providers/index.js +2 -2
- package/dest/public/bytecode_errors.d.ts +4 -0
- package/dest/public/bytecode_errors.d.ts.map +1 -0
- package/dest/public/bytecode_errors.js +7 -0
- package/dest/public/enqueued_call_side_effect_trace.d.ts +10 -4
- package/dest/public/enqueued_call_side_effect_trace.d.ts.map +1 -1
- package/dest/public/enqueued_call_side_effect_trace.js +63 -13
- package/dest/public/execution.d.ts +2 -2
- package/dest/public/execution.d.ts.map +1 -1
- package/dest/public/execution.js +1 -1
- package/dest/public/executor_metrics.d.ts +2 -0
- package/dest/public/executor_metrics.d.ts.map +1 -1
- package/dest/public/executor_metrics.js +11 -1
- package/dest/public/fee_payment.d.ts.map +1 -1
- package/dest/public/fee_payment.js +4 -3
- package/dest/public/fixtures/index.d.ts +17 -11
- package/dest/public/fixtures/index.d.ts.map +1 -1
- package/dest/public/fixtures/index.js +103 -35
- package/dest/public/public_db_sources.d.ts.map +1 -1
- package/dest/public/public_db_sources.js +7 -6
- package/dest/public/public_processor.d.ts +15 -7
- package/dest/public/public_processor.d.ts.map +1 -1
- package/dest/public/public_processor.js +119 -75
- package/dest/public/public_processor_metrics.d.ts +10 -2
- package/dest/public/public_processor_metrics.d.ts.map +1 -1
- package/dest/public/public_processor_metrics.js +49 -2
- package/dest/public/public_tx_context.d.ts +5 -0
- package/dest/public/public_tx_context.d.ts.map +1 -1
- package/dest/public/public_tx_context.js +40 -20
- package/dest/public/public_tx_simulator.d.ts +2 -1
- package/dest/public/public_tx_simulator.d.ts.map +1 -1
- package/dest/public/public_tx_simulator.js +35 -6
- package/dest/public/side_effect_errors.js +2 -2
- package/dest/public/side_effect_trace_interface.d.ts +2 -1
- package/dest/public/side_effect_trace_interface.d.ts.map +1 -1
- package/dest/public/transitional_adapters.d.ts.map +1 -1
- package/dest/public/transitional_adapters.js +2 -35
- package/dest/public/unique_class_ids.d.ts +37 -0
- package/dest/public/unique_class_ids.d.ts.map +1 -0
- package/dest/public/unique_class_ids.js +66 -0
- package/package.json +12 -12
- package/src/acvm/oracle/oracle.ts +2 -2
- package/src/acvm/oracle/typed_oracle.ts +2 -2
- package/src/avm/avm_memory_types.ts +56 -38
- package/src/avm/avm_simulator.ts +48 -20
- package/src/avm/avm_tree.ts +35 -92
- package/src/avm/errors.ts +13 -2
- package/src/avm/fixtures/index.ts +4 -2
- package/src/avm/journal/journal.ts +39 -29
- package/src/avm/journal/nullifiers.ts +0 -11
- package/src/avm/journal/public_storage.ts +2 -21
- package/src/avm/opcodes/addressing_mode.ts +2 -2
- package/src/avm/opcodes/conversion.ts +21 -16
- package/src/avm/opcodes/ec_add.ts +4 -3
- package/src/avm/opcodes/external_calls.ts +1 -1
- package/src/avm/opcodes/hashing.ts +6 -4
- package/src/avm/opcodes/misc.ts +4 -3
- package/src/avm/opcodes/multi_scalar_mul.ts +10 -5
- package/src/avm/test_utils.ts +4 -0
- package/src/client/client_execution_context.ts +2 -0
- package/src/client/db_oracle.ts +8 -3
- package/src/client/index.ts +1 -0
- package/src/client/view_data_oracle.ts +6 -3
- package/src/providers/acvm_wasm.ts +1 -1
- package/src/providers/acvm_wasm_with_blobs.ts +25 -0
- package/src/providers/index.ts +1 -1
- package/src/public/bytecode_errors.ts +6 -0
- package/src/public/enqueued_call_side_effect_trace.ts +83 -19
- package/src/public/execution.ts +1 -2
- package/src/public/executor_metrics.ts +13 -0
- package/src/public/fee_payment.ts +3 -2
- package/src/public/fixtures/index.ts +152 -46
- package/src/public/public_db_sources.ts +6 -5
- package/src/public/public_processor.ts +171 -88
- package/src/public/public_processor_metrics.ts +64 -2
- package/src/public/public_tx_context.ts +57 -21
- package/src/public/public_tx_simulator.ts +34 -6
- package/src/public/side_effect_errors.ts +1 -1
- package/src/public/side_effect_trace_interface.ts +2 -1
- package/src/public/transitional_adapters.ts +0 -51
- package/src/public/unique_class_ids.ts +80 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aztec/simulator",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.68.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./dest/index.js",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"clean": "rm -rf ./dest .tsbuildinfo",
|
|
23
23
|
"formatting": "run -T prettier --check ./src && run -T eslint ./src",
|
|
24
24
|
"formatting:fix": "run -T eslint --fix ./src && run -T prettier -w ./src",
|
|
25
|
-
"test": "NODE_NO_WARNINGS=1 node --experimental-vm-modules ../node_modules/.bin/jest --passWithNoTests"
|
|
25
|
+
"test": "HARDWARE_CONCURRENCY=${HARDWARE_CONCURRENCY:-16} RAYON_NUM_THREADS=${RAYON_NUM_THREADS:-4} NODE_NO_WARNINGS=1 node --experimental-vm-modules ../node_modules/.bin/jest --passWithNoTests --maxWorkers=${JEST_MAX_WORKERS:-8}"
|
|
26
26
|
},
|
|
27
27
|
"inherits": [
|
|
28
28
|
"../package.common.json"
|
|
@@ -54,9 +54,9 @@
|
|
|
54
54
|
],
|
|
55
55
|
"reporters": [
|
|
56
56
|
[
|
|
57
|
-
"
|
|
57
|
+
"jest-silent-reporter",
|
|
58
58
|
{
|
|
59
|
-
"
|
|
59
|
+
"useDots": true
|
|
60
60
|
}
|
|
61
61
|
]
|
|
62
62
|
],
|
|
@@ -66,14 +66,14 @@
|
|
|
66
66
|
]
|
|
67
67
|
},
|
|
68
68
|
"dependencies": {
|
|
69
|
-
"@aztec/circuit-types": "0.
|
|
70
|
-
"@aztec/circuits.js": "0.
|
|
71
|
-
"@aztec/foundation": "0.
|
|
72
|
-
"@aztec/noir-protocol-circuits-types": "0.
|
|
73
|
-
"@aztec/protocol-contracts": "0.
|
|
74
|
-
"@aztec/telemetry-client": "0.
|
|
75
|
-
"@aztec/types": "0.
|
|
76
|
-
"@aztec/world-state": "0.
|
|
69
|
+
"@aztec/circuit-types": "0.68.1",
|
|
70
|
+
"@aztec/circuits.js": "0.68.1",
|
|
71
|
+
"@aztec/foundation": "0.68.1",
|
|
72
|
+
"@aztec/noir-protocol-circuits-types": "0.68.1",
|
|
73
|
+
"@aztec/protocol-contracts": "0.68.1",
|
|
74
|
+
"@aztec/telemetry-client": "0.68.1",
|
|
75
|
+
"@aztec/types": "0.68.1",
|
|
76
|
+
"@aztec/world-state": "0.68.1",
|
|
77
77
|
"@noir-lang/acvm_js": "portal:../../noir/packages/acvm_js",
|
|
78
78
|
"@noir-lang/noirc_abi": "portal:../../noir/packages/noirc_abi",
|
|
79
79
|
"@noir-lang/types": "portal:../../noir/packages/types",
|
|
@@ -375,8 +375,8 @@ export class Oracle {
|
|
|
375
375
|
this.typedOracle.notifySetMinRevertibleSideEffectCounter(frToNumber(fromACVMField(minRevertibleSideEffectCounter)));
|
|
376
376
|
}
|
|
377
377
|
|
|
378
|
-
async
|
|
379
|
-
const taggingSecret = await this.typedOracle.
|
|
378
|
+
async getIndexedTaggingSecretAsSender([sender]: ACVMField[], [recipient]: ACVMField[]): Promise<ACVMField[]> {
|
|
379
|
+
const taggingSecret = await this.typedOracle.getIndexedTaggingSecretAsSender(
|
|
380
380
|
AztecAddress.fromString(sender),
|
|
381
381
|
AztecAddress.fromString(recipient),
|
|
382
382
|
);
|
|
@@ -237,8 +237,8 @@ export abstract class TypedOracle {
|
|
|
237
237
|
throw new OracleMethodNotAvailableError('debugLog');
|
|
238
238
|
}
|
|
239
239
|
|
|
240
|
-
|
|
241
|
-
throw new OracleMethodNotAvailableError('
|
|
240
|
+
getIndexedTaggingSecretAsSender(_sender: AztecAddress, _recipient: AztecAddress): Promise<IndexedTaggingSecret> {
|
|
241
|
+
throw new OracleMethodNotAvailableError('getIndexedTaggingSecretAsSender');
|
|
242
242
|
}
|
|
243
243
|
|
|
244
244
|
incrementAppTaggingSecretIndexAsSender(_sender: AztecAddress, _recipient: AztecAddress): Promise<void> {
|
|
@@ -15,7 +15,12 @@ import { type FunctionsOf } from '@aztec/foundation/types';
|
|
|
15
15
|
|
|
16
16
|
import { strict as assert } from 'assert';
|
|
17
17
|
|
|
18
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
InstructionExecutionError,
|
|
20
|
+
InvalidTagValueError,
|
|
21
|
+
MemorySliceOutOfRangeError,
|
|
22
|
+
TagCheckError,
|
|
23
|
+
} from './errors.js';
|
|
19
24
|
import { Addressing, AddressingMode } from './opcodes/addressing_mode.js';
|
|
20
25
|
|
|
21
26
|
/** MemoryValue gathers the common operations for all memory types. */
|
|
@@ -232,13 +237,14 @@ export class TaggedMemory implements TaggedMemoryInterface {
|
|
|
232
237
|
// Whether to track and validate memory accesses for each instruction.
|
|
233
238
|
static readonly TRACK_MEMORY_ACCESSES = process.env.NODE_ENV === 'test';
|
|
234
239
|
|
|
235
|
-
//
|
|
236
|
-
|
|
237
|
-
|
|
240
|
+
// Memory is modelled by a map with key type being number.
|
|
241
|
+
// We however restrict the keys to be non-negative integers smaller than
|
|
242
|
+
// MAX_MEMORY_SIZE.
|
|
243
|
+
static readonly MAX_MEMORY_SIZE = Number(1n << 32n);
|
|
244
|
+
private _mem: Map<number, MemoryValue>;
|
|
238
245
|
|
|
239
246
|
constructor() {
|
|
240
|
-
|
|
241
|
-
this._mem = [];
|
|
247
|
+
this._mem = new Map<number, MemoryValue>();
|
|
242
248
|
}
|
|
243
249
|
|
|
244
250
|
public getMaxMemorySize(): number {
|
|
@@ -257,8 +263,9 @@ export class TaggedMemory implements TaggedMemoryInterface {
|
|
|
257
263
|
}
|
|
258
264
|
|
|
259
265
|
public getAs<T>(offset: number): T {
|
|
266
|
+
assert(Number.isInteger(offset));
|
|
260
267
|
assert(offset < TaggedMemory.MAX_MEMORY_SIZE);
|
|
261
|
-
const word = this._mem
|
|
268
|
+
const word = this._mem.get(offset);
|
|
262
269
|
TaggedMemory.log.trace(`get(${offset}) = ${word}`);
|
|
263
270
|
if (word === undefined) {
|
|
264
271
|
TaggedMemory.log.debug(`WARNING: Memory at offset ${offset} is undefined!`);
|
|
@@ -268,54 +275,63 @@ export class TaggedMemory implements TaggedMemoryInterface {
|
|
|
268
275
|
}
|
|
269
276
|
|
|
270
277
|
public getSlice(offset: number, size: number): MemoryValue[] {
|
|
271
|
-
assert(offset
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
if (value[i] === undefined) {
|
|
276
|
-
value[i] = new Field(0);
|
|
277
|
-
}
|
|
278
|
+
assert(Number.isInteger(offset) && Number.isInteger(size));
|
|
279
|
+
|
|
280
|
+
if (offset + size > TaggedMemory.MAX_MEMORY_SIZE) {
|
|
281
|
+
throw new MemorySliceOutOfRangeError(offset, size);
|
|
278
282
|
}
|
|
279
|
-
|
|
280
|
-
|
|
283
|
+
|
|
284
|
+
const slice = new Array<MemoryValue>(size);
|
|
285
|
+
|
|
286
|
+
for (let i = 0; i < size; i++) {
|
|
287
|
+
slice[i] = this._mem.get(offset + i) ?? new Field(0);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
TaggedMemory.log.trace(`getSlice(${offset}, ${size}) = ${slice}`);
|
|
291
|
+
return slice;
|
|
281
292
|
}
|
|
282
293
|
|
|
283
294
|
public getSliceAs<T>(offset: number, size: number): T[] {
|
|
284
|
-
assert(offset + size <= TaggedMemory.MAX_MEMORY_SIZE);
|
|
285
295
|
return this.getSlice(offset, size) as T[];
|
|
286
296
|
}
|
|
287
297
|
|
|
288
298
|
public getSliceTags(offset: number, size: number): TypeTag[] {
|
|
289
|
-
|
|
290
|
-
return this._mem.slice(offset, offset + size).map(TaggedMemory.getTag);
|
|
299
|
+
return this.getSlice(offset, size).map(TaggedMemory.getTag);
|
|
291
300
|
}
|
|
292
301
|
|
|
293
302
|
public set(offset: number, v: MemoryValue) {
|
|
303
|
+
assert(Number.isInteger(offset));
|
|
294
304
|
assert(offset < TaggedMemory.MAX_MEMORY_SIZE);
|
|
295
|
-
this._mem
|
|
305
|
+
this._mem.set(offset, v);
|
|
296
306
|
TaggedMemory.log.trace(`set(${offset}, ${v})`);
|
|
297
307
|
}
|
|
298
308
|
|
|
299
|
-
public setSlice(offset: number,
|
|
300
|
-
assert(offset
|
|
301
|
-
|
|
302
|
-
if (offset +
|
|
303
|
-
|
|
309
|
+
public setSlice(offset: number, slice: MemoryValue[]) {
|
|
310
|
+
assert(Number.isInteger(offset));
|
|
311
|
+
|
|
312
|
+
if (offset + slice.length > TaggedMemory.MAX_MEMORY_SIZE) {
|
|
313
|
+
throw new MemorySliceOutOfRangeError(offset, slice.length);
|
|
304
314
|
}
|
|
305
|
-
|
|
306
|
-
|
|
315
|
+
|
|
316
|
+
slice.forEach((element, idx) => {
|
|
317
|
+
this._mem.set(offset + idx, element);
|
|
318
|
+
});
|
|
319
|
+
TaggedMemory.log.trace(`setSlice(${offset}, ${slice})`);
|
|
307
320
|
}
|
|
308
321
|
|
|
309
322
|
public getTag(offset: number): TypeTag {
|
|
310
|
-
|
|
323
|
+
assert(Number.isInteger(offset));
|
|
324
|
+
assert(offset < TaggedMemory.MAX_MEMORY_SIZE);
|
|
325
|
+
return TaggedMemory.getTag(this._mem.get(offset));
|
|
311
326
|
}
|
|
312
327
|
|
|
313
328
|
/**
|
|
314
329
|
* Check that the memory at the given offset matches the specified tag.
|
|
315
330
|
*/
|
|
316
331
|
public checkTag(tag: TypeTag, offset: number) {
|
|
317
|
-
|
|
318
|
-
|
|
332
|
+
const gotTag = this.getTag(offset);
|
|
333
|
+
if (gotTag !== tag) {
|
|
334
|
+
throw TagCheckError.forOffset(offset, TypeTag[gotTag], TypeTag[tag]);
|
|
319
335
|
}
|
|
320
336
|
}
|
|
321
337
|
|
|
@@ -334,13 +350,13 @@ export class TaggedMemory implements TaggedMemoryInterface {
|
|
|
334
350
|
public static checkIsValidTag(tagNumber: number) {
|
|
335
351
|
if (
|
|
336
352
|
![
|
|
353
|
+
TypeTag.FIELD,
|
|
337
354
|
TypeTag.UINT1,
|
|
338
355
|
TypeTag.UINT8,
|
|
339
356
|
TypeTag.UINT16,
|
|
340
357
|
TypeTag.UINT32,
|
|
341
358
|
TypeTag.UINT64,
|
|
342
359
|
TypeTag.UINT128,
|
|
343
|
-
TypeTag.FIELD,
|
|
344
360
|
].includes(tagNumber)
|
|
345
361
|
) {
|
|
346
362
|
throw new InvalidTagValueError(tagNumber);
|
|
@@ -380,21 +396,23 @@ export class TaggedMemory implements TaggedMemoryInterface {
|
|
|
380
396
|
public static getTag(v: MemoryValue | undefined): TypeTag {
|
|
381
397
|
let tag = TypeTag.INVALID;
|
|
382
398
|
|
|
399
|
+
// Not sure why, but using instanceof here doesn't work and leads odd behavior,
|
|
400
|
+
// but using constructor.name does the job...
|
|
383
401
|
if (v === undefined) {
|
|
384
402
|
tag = TypeTag.FIELD; // uninitialized memory is Field(0)
|
|
385
|
-
} else if (v
|
|
403
|
+
} else if (v.constructor.name == 'Field') {
|
|
386
404
|
tag = TypeTag.FIELD;
|
|
387
|
-
} else if (v
|
|
405
|
+
} else if (v.constructor.name == 'Uint1') {
|
|
388
406
|
tag = TypeTag.UINT1;
|
|
389
|
-
} else if (v
|
|
407
|
+
} else if (v.constructor.name == 'Uint8') {
|
|
390
408
|
tag = TypeTag.UINT8;
|
|
391
|
-
} else if (v
|
|
409
|
+
} else if (v.constructor.name == 'Uint16') {
|
|
392
410
|
tag = TypeTag.UINT16;
|
|
393
|
-
} else if (v
|
|
411
|
+
} else if (v.constructor.name == 'Uint32') {
|
|
394
412
|
tag = TypeTag.UINT32;
|
|
395
|
-
} else if (v
|
|
413
|
+
} else if (v.constructor.name == 'Uint64') {
|
|
396
414
|
tag = TypeTag.UINT64;
|
|
397
|
-
} else if (v
|
|
415
|
+
} else if (v.constructor.name == 'Uint128') {
|
|
398
416
|
tag = TypeTag.UINT128;
|
|
399
417
|
}
|
|
400
418
|
|
package/src/avm/avm_simulator.ts
CHANGED
|
@@ -97,25 +97,22 @@ export class AvmSimulator {
|
|
|
97
97
|
* Fetch the bytecode and execute it in the current context.
|
|
98
98
|
*/
|
|
99
99
|
public async execute(): Promise<AvmContractCallResult> {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
},
|
|
111
|
-
/*noirCallStack=*/ [],
|
|
100
|
+
let bytecode: Buffer | undefined;
|
|
101
|
+
try {
|
|
102
|
+
bytecode = await this.context.persistableState.getBytecode(this.context.environment.address);
|
|
103
|
+
} catch (err: any) {
|
|
104
|
+
if (!(err instanceof AvmExecutionError || err instanceof SideEffectLimitReachedError)) {
|
|
105
|
+
this.log.error(`Unknown error thrown by AVM during bytecode retrieval: ${err}`);
|
|
106
|
+
throw err;
|
|
107
|
+
}
|
|
108
|
+
return await this.handleFailureToRetrieveBytecode(
|
|
109
|
+
`Bytecode retrieval for contract '${this.context.environment.address}' failed with ${err}. Reverting...`,
|
|
112
110
|
);
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
revertReason,
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (!bytecode) {
|
|
114
|
+
return await this.handleFailureToRetrieveBytecode(
|
|
115
|
+
`No bytecode found at: ${this.context.environment.address}. Reverting...`,
|
|
119
116
|
);
|
|
120
117
|
}
|
|
121
118
|
|
|
@@ -189,14 +186,25 @@ export class AvmSimulator {
|
|
|
189
186
|
return results;
|
|
190
187
|
} catch (err: any) {
|
|
191
188
|
this.log.verbose('Exceptional halt (revert by something other than REVERT opcode)');
|
|
192
|
-
|
|
189
|
+
// FIXME: weird that we have to do this OutOfGasError check because:
|
|
190
|
+
// 1. OutOfGasError is an AvmExecutionError, so that check should cover both
|
|
191
|
+
// 2. We should at least be able to do instanceof OutOfGasError instead of checking the constructor name
|
|
192
|
+
if (
|
|
193
|
+
!(
|
|
194
|
+
err.constructor.name == 'OutOfGasError' ||
|
|
195
|
+
err instanceof AvmExecutionError ||
|
|
196
|
+
err instanceof SideEffectLimitReachedError
|
|
197
|
+
)
|
|
198
|
+
) {
|
|
193
199
|
this.log.error(`Unknown error thrown by AVM: ${err}`);
|
|
194
200
|
throw err;
|
|
195
201
|
}
|
|
196
202
|
|
|
197
203
|
const revertReason = await revertReasonFromExceptionalHalt(err, this.context);
|
|
204
|
+
// Exceptional halts consume all allocated gas
|
|
205
|
+
const noGasLeft = { l2Gas: 0, daGas: 0 };
|
|
198
206
|
// Note: "exceptional halts" cannot return data, hence [].
|
|
199
|
-
const results = new AvmContractCallResult(/*reverted=*/ true, /*output=*/ [],
|
|
207
|
+
const results = new AvmContractCallResult(/*reverted=*/ true, /*output=*/ [], noGasLeft, revertReason);
|
|
200
208
|
this.log.debug(`Context execution results: ${results.toString()}`);
|
|
201
209
|
|
|
202
210
|
this.tallyPrintFunction();
|
|
@@ -205,6 +213,26 @@ export class AvmSimulator {
|
|
|
205
213
|
}
|
|
206
214
|
}
|
|
207
215
|
|
|
216
|
+
private async handleFailureToRetrieveBytecode(message: string): Promise<AvmContractCallResult> {
|
|
217
|
+
// revert, consuming all gas
|
|
218
|
+
const fnName = await this.context.persistableState.getPublicFunctionDebugName(this.context.environment);
|
|
219
|
+
const revertReason = new AvmRevertReason(
|
|
220
|
+
message,
|
|
221
|
+
/*failingFunction=*/ {
|
|
222
|
+
contractAddress: this.context.environment.address,
|
|
223
|
+
functionName: fnName,
|
|
224
|
+
},
|
|
225
|
+
/*noirCallStack=*/ [],
|
|
226
|
+
);
|
|
227
|
+
this.log.warn(message);
|
|
228
|
+
return new AvmContractCallResult(
|
|
229
|
+
/*reverted=*/ true,
|
|
230
|
+
/*output=*/ [],
|
|
231
|
+
/*gasLeft=*/ { l2Gas: 0, daGas: 0 }, // consumes all allocated gas
|
|
232
|
+
revertReason,
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
|
|
208
236
|
private tallyInstruction(pc: number, opcode: string, gasUsed: Gas) {
|
|
209
237
|
const opcodeTally = this.opcodeTallies.get(opcode) || ({ count: 0, gas: { l2Gas: 0, daGas: 0 } } as OpcodeTally);
|
|
210
238
|
opcodeTally.count++;
|
package/src/avm/avm_tree.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { type IndexedTreeId, MerkleTreeId, type MerkleTreeReadOperations
|
|
2
|
-
import { NullifierLeafPreimage, PublicDataTreeLeafPreimage } from '@aztec/circuits.js';
|
|
1
|
+
import { type IndexedTreeId, MerkleTreeId, type MerkleTreeReadOperations } from '@aztec/circuit-types';
|
|
2
|
+
import { AppendOnlyTreeSnapshot, NullifierLeafPreimage, PublicDataTreeLeafPreimage } from '@aztec/circuits.js';
|
|
3
3
|
import { poseidon2Hash } from '@aztec/foundation/crypto';
|
|
4
4
|
import { Fr } from '@aztec/foundation/fields';
|
|
5
5
|
import { type IndexedTreeLeafPreimage, type TreeLeafPreimage } from '@aztec/foundation/trees';
|
|
@@ -398,35 +398,36 @@ export class AvmEphemeralForest {
|
|
|
398
398
|
// First, search the indexed updates (no DB fallback) to find
|
|
399
399
|
// the leafIndex of the leaf with the largest key <= the specified key.
|
|
400
400
|
const minIndexedLeafIndex = this._getLeafIndexOrNextLowestInIndexedUpdates(treeId, key);
|
|
401
|
+
|
|
402
|
+
// Then we search on the external DB
|
|
403
|
+
const leafOrLowLeafWitnessFromExternalDb: GetLeafResult<T> = await this._getLeafOrLowLeafWitnessInExternalDb(
|
|
404
|
+
treeId,
|
|
405
|
+
bigIntKey,
|
|
406
|
+
);
|
|
407
|
+
|
|
408
|
+
// If the indexed updates are empty, we can return the leaf from the DB
|
|
401
409
|
if (minIndexedLeafIndex === -1n) {
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
410
|
+
return [leafOrLowLeafWitnessFromExternalDb, /*pathAbsentInEphemeralTree=*/ true];
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// Otherwise, we return the closest one. First fetch the leaf from the indexed updates.
|
|
414
|
+
const minIndexedUpdate: T = this.getIndexedUpdate(treeId, minIndexedLeafIndex);
|
|
415
|
+
|
|
416
|
+
// And get both keys
|
|
417
|
+
const keyFromIndexed = minIndexedUpdate.getKey();
|
|
418
|
+
const keyFromExternal = leafOrLowLeafWitnessFromExternalDb.preimage.getKey();
|
|
419
|
+
|
|
420
|
+
if (keyFromExternal > keyFromIndexed) {
|
|
421
|
+
// this.log.debug(`Using leaf from external DB for ${MerkleTreeId[treeId]}`);
|
|
422
|
+
return [leafOrLowLeafWitnessFromExternalDb, /*pathAbsentInEphemeralTree=*/ true];
|
|
409
423
|
} else {
|
|
410
|
-
//
|
|
411
|
-
const
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
// We are starting with the leaf with largest key <= the specified key
|
|
418
|
-
// Starting at that "min leaf", search for specified key in both the indexed updates
|
|
419
|
-
// and the underlying DB. If not found, return its low leaf.
|
|
420
|
-
const [leafOrLowLeafInfo, pathAbsentInEphemeralTree] = await this._searchForLeafOrLowLeaf<ID, T>(
|
|
421
|
-
treeId,
|
|
422
|
-
bigIntKey,
|
|
423
|
-
minPreimage,
|
|
424
|
-
minIndexedLeafIndex,
|
|
425
|
-
);
|
|
426
|
-
// We did not find it - this is unexpected... the leaf OR low leaf should always be present
|
|
427
|
-
assert(leafOrLowLeafInfo !== undefined, 'Could not find leaf or low leaf. This should not happen!');
|
|
428
|
-
return [leafOrLowLeafInfo, pathAbsentInEphemeralTree];
|
|
429
|
-
}
|
|
424
|
+
// this.log.debug(`Using leaf from indexed DB for ${MerkleTreeId[treeId]}`);
|
|
425
|
+
const leafInfo = {
|
|
426
|
+
preimage: minIndexedUpdate,
|
|
427
|
+
index: minIndexedLeafIndex,
|
|
428
|
+
alreadyPresent: keyFromIndexed === bigIntKey,
|
|
429
|
+
};
|
|
430
|
+
return [leafInfo, /*pathAbsentInEphemeralTree=*/ false];
|
|
430
431
|
}
|
|
431
432
|
}
|
|
432
433
|
|
|
@@ -480,69 +481,6 @@ export class AvmEphemeralForest {
|
|
|
480
481
|
return { preimage: leafPreimage as T, index: leafIndex, alreadyPresent };
|
|
481
482
|
}
|
|
482
483
|
|
|
483
|
-
/**
|
|
484
|
-
* Search for the leaf for the specified key.
|
|
485
|
-
* Some leaf with key <= the specified key is expected to be present in the ephemeral tree's "indexed updates".
|
|
486
|
-
* While searching, this function bounces between our local indexedUpdates and the external DB.
|
|
487
|
-
*
|
|
488
|
-
* @param key - The key for which we are look up the leaf or low leaf for.
|
|
489
|
-
* @param minPreimage - The leaf with the largest key <= the specified key. Expected to be present in local indexedUpdates.
|
|
490
|
-
* @param minIndex - The index of the leaf with the largest key <= the specified key.
|
|
491
|
-
* @param T - The type of the preimage (PublicData or Nullifier)
|
|
492
|
-
* @returns [
|
|
493
|
-
* GetLeafResult | undefined - The leaf or low leaf info (preimage & leaf index),
|
|
494
|
-
* pathAbsentInEphemeralTree - whether its sibling path is absent in the ephemeral tree (useful during insertions)
|
|
495
|
-
* ]
|
|
496
|
-
*
|
|
497
|
-
* @details We look for the low element by bouncing between our local indexedUpdates map or the external DB
|
|
498
|
-
* The conditions we are looking for are:
|
|
499
|
-
* (1) Exact Match: curr.nextKey == key (this is only valid for public data tree)
|
|
500
|
-
* (2) Sandwich Match: curr.nextKey > key and curr.key < key
|
|
501
|
-
* (3) Max Condition: curr.next_index == 0 and curr.key < key
|
|
502
|
-
* Note the min condition does not need to be handled since indexed trees are prefilled with at least the 0 element
|
|
503
|
-
*/
|
|
504
|
-
private async _searchForLeafOrLowLeaf<ID extends IndexedTreeId, T extends IndexedTreeLeafPreimage>(
|
|
505
|
-
treeId: ID,
|
|
506
|
-
key: bigint,
|
|
507
|
-
minPreimage: T,
|
|
508
|
-
minIndex: bigint,
|
|
509
|
-
): Promise<[GetLeafResult<T> | undefined, /*pathAbsentInEphemeralTree=*/ boolean]> {
|
|
510
|
-
let found = false;
|
|
511
|
-
let curr = minPreimage as T;
|
|
512
|
-
let result: GetLeafResult<T> | undefined = undefined;
|
|
513
|
-
// Temp to avoid infinite loops - the limit is the number of leaves we may have to read
|
|
514
|
-
const LIMIT = 2n ** BigInt(getTreeHeight(treeId)) - 1n;
|
|
515
|
-
let counter = 0n;
|
|
516
|
-
let lowPublicDataIndex = minIndex;
|
|
517
|
-
let pathAbsentInEphemeralTree = false;
|
|
518
|
-
while (!found && counter < LIMIT) {
|
|
519
|
-
const bigIntKey = key;
|
|
520
|
-
if (curr.getKey() === bigIntKey) {
|
|
521
|
-
// We found an exact match - therefore this is an update
|
|
522
|
-
found = true;
|
|
523
|
-
result = { preimage: curr, index: lowPublicDataIndex, alreadyPresent: true };
|
|
524
|
-
} else if (curr.getKey() < bigIntKey && (curr.getNextIndex() === 0n || curr.getNextKey() > bigIntKey)) {
|
|
525
|
-
// We found it via sandwich or max condition, this is a low nullifier
|
|
526
|
-
found = true;
|
|
527
|
-
result = { preimage: curr, index: lowPublicDataIndex, alreadyPresent: false };
|
|
528
|
-
}
|
|
529
|
-
// Update the the values for the next iteration
|
|
530
|
-
else {
|
|
531
|
-
lowPublicDataIndex = curr.getNextIndex();
|
|
532
|
-
if (this.hasLocalUpdates(treeId, lowPublicDataIndex)) {
|
|
533
|
-
curr = this.getIndexedUpdate(treeId, lowPublicDataIndex)!;
|
|
534
|
-
pathAbsentInEphemeralTree = false;
|
|
535
|
-
} else {
|
|
536
|
-
const preimage: IndexedTreeLeafPreimage = (await this.treeDb.getLeafPreimage(treeId, lowPublicDataIndex))!;
|
|
537
|
-
curr = preimage as T;
|
|
538
|
-
pathAbsentInEphemeralTree = true;
|
|
539
|
-
}
|
|
540
|
-
}
|
|
541
|
-
counter++;
|
|
542
|
-
}
|
|
543
|
-
return [result, pathAbsentInEphemeralTree];
|
|
544
|
-
}
|
|
545
|
-
|
|
546
484
|
/**
|
|
547
485
|
* This hashes the preimage to a field element
|
|
548
486
|
*/
|
|
@@ -550,6 +488,11 @@ export class AvmEphemeralForest {
|
|
|
550
488
|
const input = preimage.toHashInputs().map(x => Fr.fromBuffer(x));
|
|
551
489
|
return poseidon2Hash(input);
|
|
552
490
|
}
|
|
491
|
+
|
|
492
|
+
getTreeSnapshot(id: MerkleTreeId): AppendOnlyTreeSnapshot {
|
|
493
|
+
const tree = this.treeMap.get(id)!;
|
|
494
|
+
return new AppendOnlyTreeSnapshot(tree.getRoot(), Number(tree.leafCount));
|
|
495
|
+
}
|
|
553
496
|
}
|
|
554
497
|
|
|
555
498
|
/****************************************************/
|
package/src/avm/errors.ts
CHANGED
|
@@ -102,10 +102,21 @@ export class TagCheckError extends AvmExecutionError {
|
|
|
102
102
|
* Error is thrown when a relative memory address resolved to an offset which
|
|
103
103
|
* is out of range, i.e, greater than maxUint32.
|
|
104
104
|
*/
|
|
105
|
-
export class
|
|
105
|
+
export class RelativeAddressOutOfRangeError extends AvmExecutionError {
|
|
106
106
|
constructor(baseAddr: number, relOffset: number) {
|
|
107
107
|
super(`Address out of range. Base address ${baseAddr}, relative offset ${relOffset}`);
|
|
108
|
-
this.name = '
|
|
108
|
+
this.name = 'RelativeAddressOutOfRangeError';
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Error is thrown when a memory slice contains addresses which are
|
|
114
|
+
* out of range, i.e, greater than maxUint32.
|
|
115
|
+
*/
|
|
116
|
+
export class MemorySliceOutOfRangeError extends AvmExecutionError {
|
|
117
|
+
constructor(baseAddr: number, size: number) {
|
|
118
|
+
super(`Memory slice is out of range. Base address ${baseAddr}, size ${size}`);
|
|
119
|
+
this.name = 'MemorySliceOutOfRangeError';
|
|
109
120
|
}
|
|
110
121
|
}
|
|
111
122
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { isNoirCallStackUnresolved } from '@aztec/circuit-types';
|
|
1
|
+
import { TxHash, isNoirCallStackUnresolved } from '@aztec/circuit-types';
|
|
2
2
|
import { GasFees, GlobalVariables, MAX_L2_GAS_PER_TX_PUBLIC_PORTION } from '@aztec/circuits.js';
|
|
3
3
|
import { type FunctionArtifact, FunctionSelector } from '@aztec/foundation/abi';
|
|
4
4
|
import { AztecAddress } from '@aztec/foundation/aztec-address';
|
|
5
5
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
6
6
|
import { Fr } from '@aztec/foundation/fields';
|
|
7
|
-
import { AvmTestContractArtifact } from '@aztec/noir-contracts.js';
|
|
7
|
+
import { AvmTestContractArtifact } from '@aztec/noir-contracts.js/AvmTest';
|
|
8
8
|
|
|
9
9
|
import { strict as assert } from 'assert';
|
|
10
10
|
import { mock } from 'jest-mock-extended';
|
|
@@ -45,6 +45,7 @@ export function initPersistableStateManager(overrides?: {
|
|
|
45
45
|
nullifiers?: NullifierManager;
|
|
46
46
|
doMerkleOperations?: boolean;
|
|
47
47
|
merkleTrees?: AvmEphemeralForest;
|
|
48
|
+
txHash?: TxHash;
|
|
48
49
|
}): AvmPersistableStateManager {
|
|
49
50
|
const worldStateDB = overrides?.worldStateDB || mock<WorldStateDB>();
|
|
50
51
|
return new AvmPersistableStateManager(
|
|
@@ -54,6 +55,7 @@ export function initPersistableStateManager(overrides?: {
|
|
|
54
55
|
overrides?.nullifiers || new NullifierManager(worldStateDB),
|
|
55
56
|
overrides?.doMerkleOperations || false,
|
|
56
57
|
overrides?.merkleTrees || mock<AvmEphemeralForest>(),
|
|
58
|
+
overrides?.txHash || new TxHash(new Fr(27).toBuffer()),
|
|
57
59
|
);
|
|
58
60
|
}
|
|
59
61
|
|