@aztec/simulator 3.0.0-nightly.20251217 → 3.0.0-nightly.20251219
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/public/avm/avm_memory_types.d.ts +1 -3
- package/dest/public/avm/avm_memory_types.d.ts.map +1 -1
- package/dest/public/avm/avm_memory_types.js +2 -2
- package/dest/public/fixtures/custom_bytecode_tests.d.ts +3 -1
- package/dest/public/fixtures/custom_bytecode_tests.d.ts.map +1 -1
- package/dest/public/fixtures/custom_bytecode_tests.js +45 -1
- package/dest/public/fixtures/opcode_spammer.d.ts +3 -7
- package/dest/public/fixtures/opcode_spammer.d.ts.map +1 -1
- package/dest/public/fixtures/opcode_spammer.js +443 -166
- package/dest/public/fuzzing/avm_simulator_bin.js +1 -0
- package/dest/public/public_tx_simulator/contract_provider_for_cpp.js +11 -11
- package/package.json +16 -16
- package/src/public/avm/avm_memory_types.ts +2 -2
- package/src/public/fixtures/custom_bytecode_tests.ts +61 -1
- package/src/public/fixtures/opcode_spammer.ts +434 -153
- package/src/public/fuzzing/avm_simulator_bin.ts +1 -0
- package/src/public/public_tx_simulator/contract_provider_for_cpp.ts +11 -11
|
@@ -149,14 +149,14 @@
|
|
|
149
149
|
*
|
|
150
150
|
*/ import { FLAT_PUBLIC_LOGS_PAYLOAD_LENGTH, MAX_L2_TO_L1_MSGS_PER_TX, MAX_NOTE_HASHES_PER_TX, MAX_NULLIFIERS_PER_TX, MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, MAX_PUBLIC_LOG_SIZE_IN_FIELDS, PUBLIC_LOG_HEADER_LENGTH } from '@aztec/constants';
|
|
151
151
|
import { Grumpkin } from '@aztec/foundation/crypto/grumpkin';
|
|
152
|
+
import { randomBigInt } from '@aztec/foundation/crypto/random';
|
|
152
153
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
153
154
|
import assert from 'assert';
|
|
154
|
-
import { Field,
|
|
155
|
-
import { Add, And, Call, CalldataCopy, Cast, DebugLog, Div, EcAdd, EmitNoteHash, EmitNullifier, EmitUnencryptedLog, Eq, FieldDiv, GetContractInstance, GetEnvVar, Jump, JumpI, KeccakF1600, L1ToL2MessageExists, Lt, Lte, Mov, Mul, Not, NoteHashExists, NullifierExists, Or, Poseidon2, ReturndataCopy, ReturndataSize, Revert, SLoad, SStore, SendL2ToL1Message, Set, Sha256Compression, Shl, Shr, Sub, SuccessCopy, ToRadixBE, Xor } from '../avm/opcodes/index.js';
|
|
155
|
+
import { Field, TaggedMemory, TypeTag, Uint1, Uint32 } from '../avm/avm_memory_types.js';
|
|
156
|
+
import { Add, And, Call, CalldataCopy, Cast, DebugLog, Div, EcAdd, EmitNoteHash, EmitNullifier, EmitUnencryptedLog, Eq, FieldDiv, GetContractInstance, GetEnvVar, InternalCall, InternalReturn, Jump, JumpI, KeccakF1600, L1ToL2MessageExists, Lt, Lte, Mov, Mul, Not, NoteHashExists, NullifierExists, Or, Poseidon2, Return, ReturndataCopy, ReturndataSize, Revert, SLoad, SStore, SendL2ToL1Message, Set, Sha256Compression, Shl, Shr, StaticCall, Sub, SuccessCopy, ToRadixBE, Xor } from '../avm/opcodes/index.js';
|
|
156
157
|
import { encodeToBytecode } from '../avm/serialization/bytecode_serialization.js';
|
|
157
158
|
import { Opcode } from '../avm/serialization/instruction_serialization.js';
|
|
158
159
|
import { deployCustomBytecode, executeCustomBytecode } from './custom_bytecode_tester.js';
|
|
159
|
-
import { deployAndExecuteCustomBytecode } from './index.js';
|
|
160
160
|
// ============================================================================
|
|
161
161
|
// Constants
|
|
162
162
|
// ============================================================================
|
|
@@ -175,15 +175,131 @@ const MAX_BYTECODE_BYTES = (MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS - 1) * BYT
|
|
|
175
175
|
const JUMP_SIZE = encodeToBytecode([
|
|
176
176
|
new Jump(0)
|
|
177
177
|
]).length; // JUMP_32
|
|
178
|
+
const INTERNALCALL_SIZE = encodeToBytecode([
|
|
179
|
+
new InternalCall(0)
|
|
180
|
+
]).length;
|
|
178
181
|
// ============================================================================
|
|
179
182
|
// Type Variant Helpers (for generating multiple configs per opcode)
|
|
180
183
|
// ============================================================================
|
|
181
|
-
|
|
182
|
-
const
|
|
184
|
+
// Not using these sets directly because we want to control order
|
|
185
|
+
//const ALL_TAGS = Array.from(VALID_TAGS);
|
|
186
|
+
//const INT_TAGS = Array.from(INTEGRAL_TAGS);
|
|
187
|
+
// Ordered so that limiting #configs per opcode still tests max size (Field)
|
|
188
|
+
const ALL_TAGS = [
|
|
189
|
+
TypeTag.FIELD,
|
|
190
|
+
TypeTag.UINT1,
|
|
191
|
+
TypeTag.UINT8,
|
|
192
|
+
TypeTag.UINT16,
|
|
193
|
+
TypeTag.UINT32,
|
|
194
|
+
TypeTag.UINT64,
|
|
195
|
+
TypeTag.UINT128
|
|
196
|
+
];
|
|
197
|
+
// ordered so that limiting #configs per opcode still tests max size
|
|
198
|
+
const INT_TAGS = [
|
|
199
|
+
TypeTag.UINT128,
|
|
200
|
+
TypeTag.UINT1,
|
|
201
|
+
TypeTag.UINT8,
|
|
202
|
+
TypeTag.UINT16,
|
|
203
|
+
TypeTag.UINT32,
|
|
204
|
+
TypeTag.UINT64
|
|
205
|
+
];
|
|
183
206
|
/** Build from tag truncating - shorter name */ function withTag(v, tag) {
|
|
184
207
|
return TaggedMemory.buildFromTagTruncating(v, tag);
|
|
185
208
|
}
|
|
186
209
|
// ============================================================================
|
|
210
|
+
// Random Value Helpers (seeded via SEED env var for reproducibility)
|
|
211
|
+
// ============================================================================
|
|
212
|
+
/** Modulus (really just max+1) for each integer type tag */ const TAG_MODULI = {
|
|
213
|
+
[TypeTag.UINT1]: 2n,
|
|
214
|
+
[TypeTag.UINT8]: 256n,
|
|
215
|
+
[TypeTag.UINT16]: 65536n,
|
|
216
|
+
[TypeTag.UINT32]: 0x1_0000_0000n,
|
|
217
|
+
[TypeTag.UINT64]: 0x1_0000_0000_0000_0000n,
|
|
218
|
+
[TypeTag.UINT128]: 0x1_0000_0000_0000_0000_0000_0000_0000_0000n,
|
|
219
|
+
[TypeTag.FIELD]: Fr.MODULUS
|
|
220
|
+
};
|
|
221
|
+
/** Generate a random value with the given type tag. Uses SEED env var if set. */ function randomWithTag(tag) {
|
|
222
|
+
const modulus = TAG_MODULI[tag];
|
|
223
|
+
if (modulus === undefined) {
|
|
224
|
+
throw new Error(`Unsupported tag for random generation: ${TypeTag[tag]}`);
|
|
225
|
+
}
|
|
226
|
+
const value = randomBigInt(modulus);
|
|
227
|
+
return TaggedMemory.buildFromTagTruncating(value, tag);
|
|
228
|
+
}
|
|
229
|
+
/** Generate a random non-zero value with the given type tag (for division). */ function randomNonZeroWithTag(tag) {
|
|
230
|
+
const modulus = TAG_MODULI[tag];
|
|
231
|
+
if (modulus === undefined) {
|
|
232
|
+
throw new Error(`Unsupported tag for random generation: ${TypeTag[tag]}`);
|
|
233
|
+
}
|
|
234
|
+
// Generate random in range [1, max) by generating [0, max-1) and adding 1
|
|
235
|
+
const value = randomBigInt(modulus - 1n) + 1n;
|
|
236
|
+
return TaggedMemory.buildFromTagTruncating(value, tag);
|
|
237
|
+
}
|
|
238
|
+
/** Generate a random non-zero Field value (for field division). */ function randomNonZeroField() {
|
|
239
|
+
return new Field(randomBigInt(Fr.MODULUS - 1n) + 1n);
|
|
240
|
+
}
|
|
241
|
+
/** Reserved memory offsets for external call loop (used by CALL spam and side-effect opcodes) */ const CONST_0_OFFSET = 0; // Uint32(0)
|
|
242
|
+
const CONST_1_OFFSET = 1; // Uint32(1)
|
|
243
|
+
const CONST_MAX_U32_OFFSET = 2; // Uint32(MAX_U32)
|
|
244
|
+
const CALL_ADDR_OFFSET = 3; // copy addr from calldata to here, and then use this addr for CALL
|
|
245
|
+
const CALL_ARGS_OFFSET = CALL_ADDR_OFFSET; // address is the arg to send to CALL
|
|
246
|
+
const CALL_COPY_SIZE_OFFSET = CONST_1_OFFSET; // copy size = 1 (forward calldata[0])
|
|
247
|
+
const CALL_CALLDATA_INDEX_OFFSET = CONST_0_OFFSET; // calldata[0]
|
|
248
|
+
const CALL_L2_GAS_OFFSET = CONST_MAX_U32_OFFSET; // MAX_U32 gets capped to remaining gas by AVM
|
|
249
|
+
const CALL_DA_GAS_OFFSET = CONST_MAX_U32_OFFSET; // MAX_U32 gets capped to remaining gas by AVM
|
|
250
|
+
const CALL_ARGS_SIZE_OFFSET = CONST_1_OFFSET; // argsSize = 1 (forward calldata[0] - might contain contract address)
|
|
251
|
+
const MAX_U32 = 0xffffffffn;
|
|
252
|
+
/**
|
|
253
|
+
* A SpamConfig for to make external CALLs to an address specified in calldata[0].
|
|
254
|
+
*/ const EXTERNAL_CALL_CONFIG = {
|
|
255
|
+
setup: [
|
|
256
|
+
// calldata will contain 1 item: the external call address
|
|
257
|
+
{
|
|
258
|
+
offset: CONST_0_OFFSET,
|
|
259
|
+
value: new Uint32(0)
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
offset: CONST_1_OFFSET,
|
|
263
|
+
value: new Uint32(1)
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
offset: CONST_MAX_U32_OFFSET,
|
|
267
|
+
value: new Uint32(MAX_U32)
|
|
268
|
+
},
|
|
269
|
+
()=>[
|
|
270
|
+
new CalldataCopy(/*indirect=*/ 0, /*copySizeOffset=*/ CALL_COPY_SIZE_OFFSET, /*cdStartOffset=*/ CALL_CALLDATA_INDEX_OFFSET, /*dstOffset=*/ CALL_ADDR_OFFSET)
|
|
271
|
+
]
|
|
272
|
+
],
|
|
273
|
+
targetInstructions: ()=>[
|
|
274
|
+
new Call(/*indirect=*/ 0, /*l2GasOffset=*/ CALL_L2_GAS_OFFSET, /*daGasOffset=*/ CALL_DA_GAS_OFFSET, /*addrOffset=*/ CALL_ADDR_OFFSET, /*argsSizeOffset=*/ CALL_ARGS_SIZE_OFFSET, /*argsOffset=*/ CALL_ARGS_OFFSET)
|
|
275
|
+
],
|
|
276
|
+
addressAsCalldata: true
|
|
277
|
+
};
|
|
278
|
+
const STATIC_CALL_CONFIG = {
|
|
279
|
+
setup: [
|
|
280
|
+
// calldata will contain 1 item: the external call address
|
|
281
|
+
{
|
|
282
|
+
offset: CONST_0_OFFSET,
|
|
283
|
+
value: new Uint32(0)
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
offset: CONST_1_OFFSET,
|
|
287
|
+
value: new Uint32(1)
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
offset: CONST_MAX_U32_OFFSET,
|
|
291
|
+
value: new Uint32(MAX_U32)
|
|
292
|
+
},
|
|
293
|
+
()=>[
|
|
294
|
+
new CalldataCopy(/*indirect=*/ 0, /*copySizeOffset=*/ CALL_COPY_SIZE_OFFSET, /*cdStartOffset=*/ CALL_CALLDATA_INDEX_OFFSET, /*dstOffset=*/ CALL_ADDR_OFFSET)
|
|
295
|
+
]
|
|
296
|
+
],
|
|
297
|
+
targetInstructions: ()=>[
|
|
298
|
+
new StaticCall(/*indirect=*/ 0, /*l2GasOffset=*/ CALL_L2_GAS_OFFSET, /*daGasOffset=*/ CALL_DA_GAS_OFFSET, /*addrOffset=*/ CALL_ADDR_OFFSET, /*argsSizeOffset=*/ CALL_ARGS_SIZE_OFFSET, /*argsOffset=*/ CALL_ARGS_OFFSET)
|
|
299
|
+
],
|
|
300
|
+
addressAsCalldata: true
|
|
301
|
+
};
|
|
302
|
+
// ============================================================================
|
|
187
303
|
// Configuration Map
|
|
188
304
|
// ============================================================================
|
|
189
305
|
/**
|
|
@@ -192,18 +308,18 @@ const INT_TAGS = Array.from(INTEGRAL_TAGS);
|
|
|
192
308
|
* Uses smallest wire format (_8) for maximum instruction density.
|
|
193
309
|
*/ export const SPAM_CONFIGS = {
|
|
194
310
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
195
|
-
// ARITHMETIC - Test with all type variants
|
|
311
|
+
// ARITHMETIC - Test with all type variants (random values, seeded via SEED env var)
|
|
196
312
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
197
313
|
[Opcode.ADD_8]: ALL_TAGS.map((tag)=>({
|
|
198
314
|
label: TypeTag[tag],
|
|
199
315
|
setup: [
|
|
200
316
|
{
|
|
201
317
|
offset: 0,
|
|
202
|
-
value:
|
|
318
|
+
value: randomWithTag(tag)
|
|
203
319
|
},
|
|
204
320
|
{
|
|
205
321
|
offset: 1,
|
|
206
|
-
value:
|
|
322
|
+
value: randomWithTag(tag)
|
|
207
323
|
}
|
|
208
324
|
],
|
|
209
325
|
targetInstructions: ()=>[
|
|
@@ -215,11 +331,11 @@ const INT_TAGS = Array.from(INTEGRAL_TAGS);
|
|
|
215
331
|
setup: [
|
|
216
332
|
{
|
|
217
333
|
offset: 0,
|
|
218
|
-
value:
|
|
334
|
+
value: randomWithTag(tag)
|
|
219
335
|
},
|
|
220
336
|
{
|
|
221
337
|
offset: 1,
|
|
222
|
-
value:
|
|
338
|
+
value: randomWithTag(tag)
|
|
223
339
|
}
|
|
224
340
|
],
|
|
225
341
|
targetInstructions: ()=>[
|
|
@@ -231,11 +347,11 @@ const INT_TAGS = Array.from(INTEGRAL_TAGS);
|
|
|
231
347
|
setup: [
|
|
232
348
|
{
|
|
233
349
|
offset: 0,
|
|
234
|
-
value:
|
|
350
|
+
value: randomWithTag(tag)
|
|
235
351
|
},
|
|
236
352
|
{
|
|
237
353
|
offset: 1,
|
|
238
|
-
value:
|
|
354
|
+
value: randomWithTag(tag)
|
|
239
355
|
}
|
|
240
356
|
],
|
|
241
357
|
targetInstructions: ()=>[
|
|
@@ -248,11 +364,11 @@ const INT_TAGS = Array.from(INTEGRAL_TAGS);
|
|
|
248
364
|
setup: [
|
|
249
365
|
{
|
|
250
366
|
offset: 0,
|
|
251
|
-
value:
|
|
367
|
+
value: randomWithTag(tag)
|
|
252
368
|
},
|
|
253
369
|
{
|
|
254
370
|
offset: 1,
|
|
255
|
-
value:
|
|
371
|
+
value: randomNonZeroWithTag(tag)
|
|
256
372
|
}
|
|
257
373
|
],
|
|
258
374
|
targetInstructions: ()=>[
|
|
@@ -265,11 +381,11 @@ const INT_TAGS = Array.from(INTEGRAL_TAGS);
|
|
|
265
381
|
setup: [
|
|
266
382
|
{
|
|
267
383
|
offset: 0,
|
|
268
|
-
value: new Field(
|
|
384
|
+
value: new Field(Fr.random())
|
|
269
385
|
},
|
|
270
386
|
{
|
|
271
387
|
offset: 1,
|
|
272
|
-
value:
|
|
388
|
+
value: randomNonZeroField()
|
|
273
389
|
}
|
|
274
390
|
],
|
|
275
391
|
targetInstructions: ()=>[
|
|
@@ -278,18 +394,18 @@ const INT_TAGS = Array.from(INTEGRAL_TAGS);
|
|
|
278
394
|
}
|
|
279
395
|
],
|
|
280
396
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
281
|
-
// COMPARATORS - Test with all type variants
|
|
397
|
+
// COMPARATORS - Test with all type variants (random values)
|
|
282
398
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
283
399
|
[Opcode.EQ_8]: ALL_TAGS.map((tag)=>({
|
|
284
400
|
label: TypeTag[tag],
|
|
285
401
|
setup: [
|
|
286
402
|
{
|
|
287
403
|
offset: 0,
|
|
288
|
-
value:
|
|
404
|
+
value: randomWithTag(tag)
|
|
289
405
|
},
|
|
290
406
|
{
|
|
291
407
|
offset: 1,
|
|
292
|
-
value:
|
|
408
|
+
value: randomWithTag(tag)
|
|
293
409
|
}
|
|
294
410
|
],
|
|
295
411
|
targetInstructions: ()=>[
|
|
@@ -301,11 +417,11 @@ const INT_TAGS = Array.from(INTEGRAL_TAGS);
|
|
|
301
417
|
setup: [
|
|
302
418
|
{
|
|
303
419
|
offset: 0,
|
|
304
|
-
value:
|
|
420
|
+
value: randomWithTag(tag)
|
|
305
421
|
},
|
|
306
422
|
{
|
|
307
423
|
offset: 1,
|
|
308
|
-
value:
|
|
424
|
+
value: randomWithTag(tag)
|
|
309
425
|
}
|
|
310
426
|
],
|
|
311
427
|
targetInstructions: ()=>[
|
|
@@ -317,11 +433,11 @@ const INT_TAGS = Array.from(INTEGRAL_TAGS);
|
|
|
317
433
|
setup: [
|
|
318
434
|
{
|
|
319
435
|
offset: 0,
|
|
320
|
-
value:
|
|
436
|
+
value: randomWithTag(tag)
|
|
321
437
|
},
|
|
322
438
|
{
|
|
323
439
|
offset: 1,
|
|
324
|
-
value:
|
|
440
|
+
value: randomWithTag(tag)
|
|
325
441
|
}
|
|
326
442
|
],
|
|
327
443
|
targetInstructions: ()=>[
|
|
@@ -329,18 +445,18 @@ const INT_TAGS = Array.from(INTEGRAL_TAGS);
|
|
|
329
445
|
]
|
|
330
446
|
})),
|
|
331
447
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
332
|
-
// BITWISE - Integer types only (no FIELD)
|
|
448
|
+
// BITWISE - Integer types only (no FIELD) (random values)
|
|
333
449
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
334
450
|
[Opcode.AND_8]: INT_TAGS.map((tag)=>({
|
|
335
451
|
label: TypeTag[tag],
|
|
336
452
|
setup: [
|
|
337
453
|
{
|
|
338
454
|
offset: 0,
|
|
339
|
-
value:
|
|
455
|
+
value: randomWithTag(tag)
|
|
340
456
|
},
|
|
341
457
|
{
|
|
342
458
|
offset: 1,
|
|
343
|
-
value:
|
|
459
|
+
value: randomWithTag(tag)
|
|
344
460
|
}
|
|
345
461
|
],
|
|
346
462
|
targetInstructions: ()=>[
|
|
@@ -352,11 +468,11 @@ const INT_TAGS = Array.from(INTEGRAL_TAGS);
|
|
|
352
468
|
setup: [
|
|
353
469
|
{
|
|
354
470
|
offset: 0,
|
|
355
|
-
value:
|
|
471
|
+
value: randomWithTag(tag)
|
|
356
472
|
},
|
|
357
473
|
{
|
|
358
474
|
offset: 1,
|
|
359
|
-
value:
|
|
475
|
+
value: randomWithTag(tag)
|
|
360
476
|
}
|
|
361
477
|
],
|
|
362
478
|
targetInstructions: ()=>[
|
|
@@ -368,11 +484,11 @@ const INT_TAGS = Array.from(INTEGRAL_TAGS);
|
|
|
368
484
|
setup: [
|
|
369
485
|
{
|
|
370
486
|
offset: 0,
|
|
371
|
-
value:
|
|
487
|
+
value: randomWithTag(tag)
|
|
372
488
|
},
|
|
373
489
|
{
|
|
374
490
|
offset: 1,
|
|
375
|
-
value:
|
|
491
|
+
value: randomWithTag(tag)
|
|
376
492
|
}
|
|
377
493
|
],
|
|
378
494
|
targetInstructions: ()=>[
|
|
@@ -384,7 +500,7 @@ const INT_TAGS = Array.from(INTEGRAL_TAGS);
|
|
|
384
500
|
setup: [
|
|
385
501
|
{
|
|
386
502
|
offset: 0,
|
|
387
|
-
value:
|
|
503
|
+
value: randomWithTag(tag)
|
|
388
504
|
}
|
|
389
505
|
],
|
|
390
506
|
targetInstructions: ()=>[
|
|
@@ -396,7 +512,7 @@ const INT_TAGS = Array.from(INTEGRAL_TAGS);
|
|
|
396
512
|
setup: [
|
|
397
513
|
{
|
|
398
514
|
offset: 0,
|
|
399
|
-
value:
|
|
515
|
+
value: randomWithTag(tag)
|
|
400
516
|
},
|
|
401
517
|
{
|
|
402
518
|
offset: 1,
|
|
@@ -412,7 +528,7 @@ const INT_TAGS = Array.from(INTEGRAL_TAGS);
|
|
|
412
528
|
setup: [
|
|
413
529
|
{
|
|
414
530
|
offset: 0,
|
|
415
|
-
value:
|
|
531
|
+
value: randomWithTag(tag)
|
|
416
532
|
},
|
|
417
533
|
{
|
|
418
534
|
offset: 1,
|
|
@@ -424,14 +540,14 @@ const INT_TAGS = Array.from(INTEGRAL_TAGS);
|
|
|
424
540
|
]
|
|
425
541
|
})),
|
|
426
542
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
427
|
-
// CAST / MOV - Test with all type variants
|
|
543
|
+
// CAST / MOV - Test with all type variants (random values)
|
|
428
544
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
429
545
|
[Opcode.CAST_8]: ALL_TAGS.map((tag)=>({
|
|
430
546
|
label: TypeTag[tag],
|
|
431
547
|
setup: [
|
|
432
548
|
{
|
|
433
549
|
offset: 0,
|
|
434
|
-
value:
|
|
550
|
+
value: randomWithTag(tag)
|
|
435
551
|
}
|
|
436
552
|
],
|
|
437
553
|
targetInstructions: ()=>[
|
|
@@ -443,7 +559,7 @@ const INT_TAGS = Array.from(INTEGRAL_TAGS);
|
|
|
443
559
|
setup: [
|
|
444
560
|
{
|
|
445
561
|
offset: 0,
|
|
446
|
-
value:
|
|
562
|
+
value: randomWithTag(tag)
|
|
447
563
|
}
|
|
448
564
|
],
|
|
449
565
|
targetInstructions: ()=>[
|
|
@@ -518,6 +634,72 @@ const INT_TAGS = Array.from(INTEGRAL_TAGS);
|
|
|
518
634
|
]
|
|
519
635
|
}
|
|
520
636
|
],
|
|
637
|
+
// INTERNALCALL: calls itself infinitely by jumping to its own PC (PC 0, since no setup)
|
|
638
|
+
// Creates infinite recursion until OOG (internal call stack grows forever)
|
|
639
|
+
[Opcode.INTERNALCALL]: [
|
|
640
|
+
{
|
|
641
|
+
setup: [],
|
|
642
|
+
targetInstructions: ()=>[
|
|
643
|
+
new InternalCall(/*loc=*/ 0)
|
|
644
|
+
]
|
|
645
|
+
}
|
|
646
|
+
],
|
|
647
|
+
// INTERNALRETURN: needs INTERNALCALL to return without error
|
|
648
|
+
// Layout: INTERNALCALL(10) -> JUMP(0) -> INTERNALRETURN
|
|
649
|
+
// INTERNALCALL jumps to INTERNALRETURN, which returns to JUMP, which loops back
|
|
650
|
+
[Opcode.INTERNALRETURN]: [
|
|
651
|
+
{
|
|
652
|
+
setup: [],
|
|
653
|
+
targetInstructions: ()=>[
|
|
654
|
+
new InternalCall(/*loc=*/ INTERNALCALL_SIZE + JUMP_SIZE),
|
|
655
|
+
new Jump(/*jumpOffset=*/ 0),
|
|
656
|
+
new InternalReturn()
|
|
657
|
+
]
|
|
658
|
+
}
|
|
659
|
+
],
|
|
660
|
+
// CALL (EXTERNALCALL): calls the current contract address (self) in a loop
|
|
661
|
+
// Contract address is passed via calldata[0] and propagated to nested calls
|
|
662
|
+
[Opcode.CALL]: [
|
|
663
|
+
EXTERNAL_CALL_CONFIG
|
|
664
|
+
],
|
|
665
|
+
[Opcode.STATICCALL]: [
|
|
666
|
+
STATIC_CALL_CONFIG
|
|
667
|
+
],
|
|
668
|
+
// RETURN: terminates execution, so we need to use the two-contract pattern
|
|
669
|
+
// Outer contract CALLs inner contract in a loop, inner contract does RETURN
|
|
670
|
+
[Opcode.RETURN]: [
|
|
671
|
+
{
|
|
672
|
+
setup: [
|
|
673
|
+
{
|
|
674
|
+
offset: 0,
|
|
675
|
+
value: new Uint32(0)
|
|
676
|
+
}
|
|
677
|
+
],
|
|
678
|
+
targetInstructions: ()=>[
|
|
679
|
+
new Return(/*indirect=*/ 0, /*returnSizeOffset=*/ 0, /*returnOffset=*/ 0)
|
|
680
|
+
],
|
|
681
|
+
// Use the side-effect-limit pattern (even though it's not a side-effect) as it fits
|
|
682
|
+
// this case (we want to CALL, RETURN, then CALL again back in parent). We omit "cleanup"
|
|
683
|
+
// because we don't need to REVERT as we do for real side-effects.
|
|
684
|
+
limit: 1
|
|
685
|
+
}
|
|
686
|
+
],
|
|
687
|
+
// REVERT: terminates execution, so we need to use the two-contract pattern
|
|
688
|
+
// Outer contract CALLs inner contract in a loop, inner contract does REVERT
|
|
689
|
+
[Opcode.REVERT_8]: [
|
|
690
|
+
{
|
|
691
|
+
setup: [
|
|
692
|
+
{
|
|
693
|
+
offset: 0,
|
|
694
|
+
value: new Uint32(0)
|
|
695
|
+
}
|
|
696
|
+
],
|
|
697
|
+
targetInstructions: ()=>[
|
|
698
|
+
new Revert(/*indirect=*/ 0, /*retSizeOffset=*/ 0, /*returnOffset=*/ 1).as(Opcode.REVERT_8, Revert.wireFormat8)
|
|
699
|
+
],
|
|
700
|
+
limit: 1
|
|
701
|
+
}
|
|
702
|
+
],
|
|
521
703
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
522
704
|
// ENVIRONMENT
|
|
523
705
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -529,11 +711,47 @@ const INT_TAGS = Array.from(INTEGRAL_TAGS);
|
|
|
529
711
|
]
|
|
530
712
|
}
|
|
531
713
|
],
|
|
714
|
+
// CALLDATACOPY has dynamic gas scaling with copySize
|
|
532
715
|
[Opcode.CALLDATACOPY]: [
|
|
533
716
|
{
|
|
534
|
-
|
|
535
|
-
//
|
|
536
|
-
|
|
717
|
+
label: 'Min copy size',
|
|
718
|
+
// CalldataCopy with copySize=0 is a no-op but still executes the opcode
|
|
719
|
+
setup: [
|
|
720
|
+
{
|
|
721
|
+
offset: 0,
|
|
722
|
+
value: new Uint32(0n)
|
|
723
|
+
},
|
|
724
|
+
{
|
|
725
|
+
offset: 1,
|
|
726
|
+
value: new Uint32(0n)
|
|
727
|
+
}
|
|
728
|
+
],
|
|
729
|
+
targetInstructions: ()=>[
|
|
730
|
+
new CalldataCopy(/*indirect=*/ 0, /*copySizeOffset=*/ 0, /*cdStartOffset=*/ 1, /*dstOffset=*/ 2)
|
|
731
|
+
]
|
|
732
|
+
},
|
|
733
|
+
{
|
|
734
|
+
label: 'Large copy size',
|
|
735
|
+
// Large copySize with large dynamic gas - will OOG quickly
|
|
736
|
+
// NOTE: we don't want it so large that it exceeds memory bounds (MAX_MEMORY_SIZE = 2^32)
|
|
737
|
+
// and we really want it small enough that we run at least 1 successful target opcode.
|
|
738
|
+
setup: [
|
|
739
|
+
{
|
|
740
|
+
offset: 0,
|
|
741
|
+
value: new Uint32(1000n)
|
|
742
|
+
},
|
|
743
|
+
{
|
|
744
|
+
offset: 1,
|
|
745
|
+
value: new Uint32(0n)
|
|
746
|
+
}
|
|
747
|
+
],
|
|
748
|
+
targetInstructions: ()=>[
|
|
749
|
+
new CalldataCopy(/*indirect=*/ 0, /*copySizeOffset=*/ 0, /*cdStartOffset=*/ 1, /*dstOffset=*/ 2)
|
|
750
|
+
]
|
|
751
|
+
},
|
|
752
|
+
{
|
|
753
|
+
label: 'Near min copy size of 1',
|
|
754
|
+
// Near-min but actually copies data (more meaningful than size=0 no-op)
|
|
537
755
|
setup: [
|
|
538
756
|
{
|
|
539
757
|
offset: 0,
|
|
@@ -565,8 +783,10 @@ const INT_TAGS = Array.from(INTEGRAL_TAGS);
|
|
|
565
783
|
]
|
|
566
784
|
}
|
|
567
785
|
],
|
|
786
|
+
// RETURNDATACOPY has dynamic gas scaling with copySize
|
|
568
787
|
[Opcode.RETURNDATACOPY]: [
|
|
569
788
|
{
|
|
789
|
+
label: 'Min copy size',
|
|
570
790
|
setup: [
|
|
571
791
|
{
|
|
572
792
|
offset: 0,
|
|
@@ -580,6 +800,42 @@ const INT_TAGS = Array.from(INTEGRAL_TAGS);
|
|
|
580
800
|
targetInstructions: ()=>[
|
|
581
801
|
new ReturndataCopy(/*indirect=*/ 0, /*copySizeOffset=*/ 0, /*rdStartOffset=*/ 1, /*dstOffset=*/ 2)
|
|
582
802
|
]
|
|
803
|
+
},
|
|
804
|
+
{
|
|
805
|
+
label: 'Large copy size',
|
|
806
|
+
// Large copySize to maximize dynamic gas - will OOG quickly
|
|
807
|
+
// NOTE: we don't want it so large that it exceeds memory bounds (MAX_MEMORY_SIZE = 2^32)
|
|
808
|
+
// and we really want it small enough that we run at least 1 successful target opcode.
|
|
809
|
+
setup: [
|
|
810
|
+
{
|
|
811
|
+
offset: 0,
|
|
812
|
+
value: new Uint32(1000n)
|
|
813
|
+
},
|
|
814
|
+
{
|
|
815
|
+
offset: 1,
|
|
816
|
+
value: new Uint32(0n)
|
|
817
|
+
}
|
|
818
|
+
],
|
|
819
|
+
targetInstructions: ()=>[
|
|
820
|
+
new ReturndataCopy(/*indirect=*/ 0, /*copySizeOffset=*/ 0, /*rdStartOffset=*/ 1, /*dstOffset=*/ 2)
|
|
821
|
+
]
|
|
822
|
+
},
|
|
823
|
+
{
|
|
824
|
+
label: 'Near min copy size of 1',
|
|
825
|
+
// Near-min but actually copies data (more meaningful than size=0 no-op)
|
|
826
|
+
setup: [
|
|
827
|
+
{
|
|
828
|
+
offset: 0,
|
|
829
|
+
value: new Uint32(1n)
|
|
830
|
+
},
|
|
831
|
+
{
|
|
832
|
+
offset: 1,
|
|
833
|
+
value: new Uint32(0n)
|
|
834
|
+
}
|
|
835
|
+
],
|
|
836
|
+
targetInstructions: ()=>[
|
|
837
|
+
new ReturndataCopy(/*indirect=*/ 0, /*copySizeOffset=*/ 0, /*rdStartOffset=*/ 1, /*dstOffset=*/ 2)
|
|
838
|
+
]
|
|
583
839
|
}
|
|
584
840
|
],
|
|
585
841
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -587,27 +843,61 @@ const INT_TAGS = Array.from(INTEGRAL_TAGS);
|
|
|
587
843
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
588
844
|
[Opcode.SLOAD]: [
|
|
589
845
|
{
|
|
846
|
+
label: 'Cold read (slot not written)',
|
|
590
847
|
setup: [
|
|
591
848
|
{
|
|
592
849
|
offset: 0,
|
|
593
|
-
value: new Field(
|
|
850
|
+
value: new Field(Fr.random())
|
|
594
851
|
}
|
|
595
852
|
],
|
|
596
853
|
targetInstructions: ()=>[
|
|
597
854
|
new SLoad(/*indirect=*/ 0, /*slotOffset=*/ 0, /*dstOffset=*/ 1)
|
|
598
855
|
]
|
|
856
|
+
},
|
|
857
|
+
{
|
|
858
|
+
label: 'Warm read (SSTORE first)',
|
|
859
|
+
// Memory layout: slot (incremented), value, constant 1, revertSize, loaded value
|
|
860
|
+
setup: [
|
|
861
|
+
{
|
|
862
|
+
offset: 0,
|
|
863
|
+
value: new Field(Fr.random())
|
|
864
|
+
},
|
|
865
|
+
{
|
|
866
|
+
offset: 1,
|
|
867
|
+
value: new Field(Fr.random())
|
|
868
|
+
},
|
|
869
|
+
{
|
|
870
|
+
offset: 2,
|
|
871
|
+
value: new Field(1n)
|
|
872
|
+
},
|
|
873
|
+
{
|
|
874
|
+
offset: 3,
|
|
875
|
+
value: new Uint32(0n)
|
|
876
|
+
}
|
|
877
|
+
],
|
|
878
|
+
targetInstructions: ()=>[
|
|
879
|
+
new SStore(/*indirect=*/ 0, /*srcOffset=*/ 1, /*slotOffset=*/ 0),
|
|
880
|
+
new SLoad(/*indirect=*/ 0, /*slotOffset=*/ 0, /*dstOffset=*/ 4),
|
|
881
|
+
new Add(/*indirect=*/ 0, /*aOffset=*/ 0, /*bOffset=*/ 2, /*dstOffset=*/ 0).as(Opcode.ADD_8, Add.wireFormat8)
|
|
882
|
+
],
|
|
883
|
+
cleanupInstructions: ()=>[
|
|
884
|
+
new Revert(/*indirect=*/ 0, /*retSizeOffset=*/ 3, /*returnOffset=*/ 0).as(Opcode.REVERT_8, Revert.wireFormat8)
|
|
885
|
+
],
|
|
886
|
+
limit: MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX
|
|
599
887
|
}
|
|
600
888
|
],
|
|
601
889
|
[Opcode.NOTEHASHEXISTS]: [
|
|
602
890
|
{
|
|
891
|
+
// Note: Can't easily do "write first" version - would need to know the leaf index
|
|
892
|
+
// that EMITNOTEHASH will produce, which depends on tree state
|
|
603
893
|
setup: [
|
|
604
894
|
{
|
|
605
895
|
offset: 0,
|
|
606
|
-
value: new Field(
|
|
896
|
+
value: new Field(Fr.random())
|
|
607
897
|
},
|
|
608
898
|
{
|
|
609
899
|
offset: 1,
|
|
610
|
-
value:
|
|
900
|
+
value: randomWithTag(TypeTag.UINT64)
|
|
611
901
|
}
|
|
612
902
|
],
|
|
613
903
|
targetInstructions: ()=>[
|
|
@@ -617,19 +907,51 @@ const INT_TAGS = Array.from(INTEGRAL_TAGS);
|
|
|
617
907
|
],
|
|
618
908
|
[Opcode.NULLIFIEREXISTS]: [
|
|
619
909
|
{
|
|
910
|
+
label: 'Non-existent nullifier',
|
|
620
911
|
setup: [
|
|
621
912
|
{
|
|
622
913
|
offset: 0,
|
|
623
|
-
value: new Field(
|
|
914
|
+
value: new Field(Fr.random())
|
|
624
915
|
},
|
|
625
916
|
{
|
|
626
917
|
offset: 1,
|
|
627
|
-
value: new Field(
|
|
918
|
+
value: new Field(Fr.random())
|
|
628
919
|
}
|
|
629
920
|
],
|
|
630
921
|
targetInstructions: ()=>[
|
|
631
922
|
new NullifierExists(/*indirect=*/ 0, /*nullifierOffset=*/ 0, /*addressOffset=*/ 1, /*existsOffset=*/ 2)
|
|
632
923
|
]
|
|
924
|
+
},
|
|
925
|
+
{
|
|
926
|
+
label: 'Existing nullifier (EMITNULLIFIER first)',
|
|
927
|
+
// Memory layout: nullifier (incremented), constant 1, current address (from GETENVVAR), revertSize, exists result
|
|
928
|
+
setup: [
|
|
929
|
+
{
|
|
930
|
+
offset: 0,
|
|
931
|
+
value: new Field(Fr.random())
|
|
932
|
+
},
|
|
933
|
+
{
|
|
934
|
+
offset: 1,
|
|
935
|
+
value: new Field(1n)
|
|
936
|
+
},
|
|
937
|
+
()=>[
|
|
938
|
+
// Get current contract address into offset 2
|
|
939
|
+
new GetEnvVar(/*indirect=*/ 0, /*dstOffset=*/ 2, /*varEnum=*/ 0).as(Opcode.GETENVVAR_16, GetEnvVar.wireFormat16)
|
|
940
|
+
],
|
|
941
|
+
{
|
|
942
|
+
offset: 3,
|
|
943
|
+
value: new Uint32(0n)
|
|
944
|
+
}
|
|
945
|
+
],
|
|
946
|
+
targetInstructions: ()=>[
|
|
947
|
+
new EmitNullifier(/*indirect=*/ 0, /*nullifierOffset=*/ 0),
|
|
948
|
+
new NullifierExists(/*indirect=*/ 0, /*nullifierOffset=*/ 0, /*addressOffset=*/ 2, /*existsOffset=*/ 4),
|
|
949
|
+
new Add(/*indirect=*/ 0, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 0).as(Opcode.ADD_8, Add.wireFormat8)
|
|
950
|
+
],
|
|
951
|
+
cleanupInstructions: ()=>[
|
|
952
|
+
new Revert(/*indirect=*/ 0, /*retSizeOffset=*/ 3, /*returnOffset=*/ 0).as(Opcode.REVERT_8, Revert.wireFormat8)
|
|
953
|
+
],
|
|
954
|
+
limit: MAX_NULLIFIERS_PER_TX - 1
|
|
633
955
|
}
|
|
634
956
|
],
|
|
635
957
|
[Opcode.L1TOL2MSGEXISTS]: [
|
|
@@ -637,11 +959,11 @@ const INT_TAGS = Array.from(INTEGRAL_TAGS);
|
|
|
637
959
|
setup: [
|
|
638
960
|
{
|
|
639
961
|
offset: 0,
|
|
640
|
-
value: new Field(
|
|
962
|
+
value: new Field(Fr.random())
|
|
641
963
|
},
|
|
642
964
|
{
|
|
643
965
|
offset: 1,
|
|
644
|
-
value:
|
|
966
|
+
value: randomWithTag(TypeTag.UINT64)
|
|
645
967
|
}
|
|
646
968
|
],
|
|
647
969
|
targetInstructions: ()=>[
|
|
@@ -651,11 +973,12 @@ const INT_TAGS = Array.from(INTEGRAL_TAGS);
|
|
|
651
973
|
],
|
|
652
974
|
[Opcode.GETCONTRACTINSTANCE]: [
|
|
653
975
|
{
|
|
976
|
+
// Use GETENVVAR to get current contract address (varEnum 0 = ADDRESS)
|
|
977
|
+
// This ensures we're querying a valid deployed contract
|
|
654
978
|
setup: [
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
}
|
|
979
|
+
()=>[
|
|
980
|
+
new GetEnvVar(/*indirect=*/ 0, /*dstOffset=*/ 0, /*varEnum=*/ 0).as(Opcode.GETENVVAR_16, GetEnvVar.wireFormat16)
|
|
981
|
+
]
|
|
659
982
|
],
|
|
660
983
|
// memberEnum 0 = DEPLOYER
|
|
661
984
|
targetInstructions: ()=>[
|
|
@@ -671,7 +994,7 @@ const INT_TAGS = Array.from(INTEGRAL_TAGS);
|
|
|
671
994
|
setup: [
|
|
672
995
|
{
|
|
673
996
|
offset: 0,
|
|
674
|
-
value: new Field(
|
|
997
|
+
value: new Field(Fr.random())
|
|
675
998
|
},
|
|
676
999
|
{
|
|
677
1000
|
offset: 1,
|
|
@@ -694,7 +1017,7 @@ const INT_TAGS = Array.from(INTEGRAL_TAGS);
|
|
|
694
1017
|
setup: [
|
|
695
1018
|
{
|
|
696
1019
|
offset: 0,
|
|
697
|
-
value: new Field(
|
|
1020
|
+
value: new Field(Fr.random())
|
|
698
1021
|
},
|
|
699
1022
|
{
|
|
700
1023
|
offset: 1,
|
|
@@ -720,11 +1043,11 @@ const INT_TAGS = Array.from(INTEGRAL_TAGS);
|
|
|
720
1043
|
setup: [
|
|
721
1044
|
{
|
|
722
1045
|
offset: 0,
|
|
723
|
-
value: new Field(
|
|
1046
|
+
value: new Field(Fr.random())
|
|
724
1047
|
},
|
|
725
1048
|
{
|
|
726
1049
|
offset: 1,
|
|
727
|
-
value: new Field(
|
|
1050
|
+
value: new Field(Fr.random())
|
|
728
1051
|
},
|
|
729
1052
|
{
|
|
730
1053
|
offset: 2,
|
|
@@ -749,11 +1072,11 @@ const INT_TAGS = Array.from(INTEGRAL_TAGS);
|
|
|
749
1072
|
setup: [
|
|
750
1073
|
{
|
|
751
1074
|
offset: 0,
|
|
752
|
-
value: new Field(
|
|
1075
|
+
value: new Field(Fr.random())
|
|
753
1076
|
},
|
|
754
1077
|
{
|
|
755
1078
|
offset: 1,
|
|
756
|
-
value: new Field(
|
|
1079
|
+
value: new Field(Fr.random())
|
|
757
1080
|
},
|
|
758
1081
|
{
|
|
759
1082
|
offset: 2,
|
|
@@ -772,11 +1095,11 @@ const INT_TAGS = Array.from(INTEGRAL_TAGS);
|
|
|
772
1095
|
setup: [
|
|
773
1096
|
{
|
|
774
1097
|
offset: 0,
|
|
775
|
-
value: new Field(
|
|
1098
|
+
value: new Field(Fr.random())
|
|
776
1099
|
},
|
|
777
1100
|
{
|
|
778
1101
|
offset: 1,
|
|
779
|
-
value: new Field(
|
|
1102
|
+
value: new Field(Fr.random())
|
|
780
1103
|
},
|
|
781
1104
|
{
|
|
782
1105
|
offset: 2,
|
|
@@ -843,7 +1166,7 @@ const INT_TAGS = Array.from(INTEGRAL_TAGS);
|
|
|
843
1166
|
}
|
|
844
1167
|
],
|
|
845
1168
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
846
|
-
// GADGETS -
|
|
1169
|
+
// GADGETS - Random inputs (seeded via SEED env var)
|
|
847
1170
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
848
1171
|
[Opcode.POSEIDON2]: [
|
|
849
1172
|
{
|
|
@@ -852,7 +1175,7 @@ const INT_TAGS = Array.from(INTEGRAL_TAGS);
|
|
|
852
1175
|
length: 4
|
|
853
1176
|
}, (_, i)=>({
|
|
854
1177
|
offset: i,
|
|
855
|
-
value: new Field(
|
|
1178
|
+
value: new Field(Fr.random())
|
|
856
1179
|
})),
|
|
857
1180
|
// Poseidon hash data at M[0..3], write result to M[0:3] (reuse results as next inputs)
|
|
858
1181
|
targetInstructions: ()=>[
|
|
@@ -863,45 +1186,19 @@ const INT_TAGS = Array.from(INTEGRAL_TAGS);
|
|
|
863
1186
|
[Opcode.SHA256COMPRESSION]: [
|
|
864
1187
|
{
|
|
865
1188
|
setup: [
|
|
866
|
-
// State: 8 x UINT32 at offsets 0-7 (
|
|
867
|
-
{
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
},
|
|
875
|
-
{
|
|
876
|
-
offset: 2,
|
|
877
|
-
value: new Uint32(0x3c6ef372n)
|
|
878
|
-
},
|
|
879
|
-
{
|
|
880
|
-
offset: 3,
|
|
881
|
-
value: new Uint32(0xa54ff53an)
|
|
882
|
-
},
|
|
883
|
-
{
|
|
884
|
-
offset: 4,
|
|
885
|
-
value: new Uint32(0x510e527fn)
|
|
886
|
-
},
|
|
887
|
-
{
|
|
888
|
-
offset: 5,
|
|
889
|
-
value: new Uint32(0x9b05688cn)
|
|
890
|
-
},
|
|
891
|
-
{
|
|
892
|
-
offset: 6,
|
|
893
|
-
value: new Uint32(0x1f83d9abn)
|
|
894
|
-
},
|
|
895
|
-
{
|
|
896
|
-
offset: 7,
|
|
897
|
-
value: new Uint32(0x5be0cd19n)
|
|
898
|
-
},
|
|
899
|
-
// Inputs: 16 x UINT32 at offsets 8-23 (non-trivial message block)
|
|
1189
|
+
// State: 8 x UINT32 at offsets 0-7 (random initial state)
|
|
1190
|
+
...Array.from({
|
|
1191
|
+
length: 8
|
|
1192
|
+
}, (_, i)=>({
|
|
1193
|
+
offset: i,
|
|
1194
|
+
value: randomWithTag(TypeTag.UINT32)
|
|
1195
|
+
})),
|
|
1196
|
+
// Inputs: 16 x UINT32 at offsets 8-23 (random message block)
|
|
900
1197
|
...Array.from({
|
|
901
1198
|
length: 16
|
|
902
1199
|
}, (_, i)=>({
|
|
903
1200
|
offset: 8 + i,
|
|
904
|
-
value:
|
|
1201
|
+
value: randomWithTag(TypeTag.UINT32)
|
|
905
1202
|
}))
|
|
906
1203
|
],
|
|
907
1204
|
targetInstructions: ()=>[
|
|
@@ -911,12 +1208,12 @@ const INT_TAGS = Array.from(INTEGRAL_TAGS);
|
|
|
911
1208
|
],
|
|
912
1209
|
[Opcode.KECCAKF1600]: [
|
|
913
1210
|
{
|
|
914
|
-
// Keccak state: 25 x UINT64 (5x5 lane array)
|
|
1211
|
+
// Keccak state: 25 x UINT64 (5x5 lane array) with random values
|
|
915
1212
|
setup: Array.from({
|
|
916
1213
|
length: 25
|
|
917
1214
|
}, (_, i)=>({
|
|
918
1215
|
offset: i,
|
|
919
|
-
value:
|
|
1216
|
+
value: randomWithTag(TypeTag.UINT64)
|
|
920
1217
|
})),
|
|
921
1218
|
targetInstructions: ()=>[
|
|
922
1219
|
new KeccakF1600(/*indirect=*/ 0, /*dstOffset=*/ 0, /*inputOffset=*/ 0)
|
|
@@ -957,20 +1254,46 @@ const INT_TAGS = Array.from(INTEGRAL_TAGS);
|
|
|
957
1254
|
]
|
|
958
1255
|
}
|
|
959
1256
|
],
|
|
1257
|
+
// TORADIXBE has dynamic gas scaling with numLimbs
|
|
960
1258
|
[Opcode.TORADIXBE]: [
|
|
961
1259
|
{
|
|
1260
|
+
label: 'Min limbs',
|
|
962
1261
|
setup: [
|
|
963
1262
|
{
|
|
964
1263
|
offset: 0,
|
|
965
|
-
value: new Field(
|
|
1264
|
+
value: new Field(1n)
|
|
966
1265
|
},
|
|
967
1266
|
{
|
|
968
1267
|
offset: 1,
|
|
969
|
-
value: new Uint32(
|
|
1268
|
+
value: new Uint32(2n)
|
|
970
1269
|
},
|
|
971
1270
|
{
|
|
972
1271
|
offset: 2,
|
|
973
|
-
value: new Uint32(
|
|
1272
|
+
value: new Uint32(1n)
|
|
1273
|
+
},
|
|
1274
|
+
{
|
|
1275
|
+
offset: 3,
|
|
1276
|
+
value: new Uint1(0n)
|
|
1277
|
+
}
|
|
1278
|
+
],
|
|
1279
|
+
targetInstructions: ()=>[
|
|
1280
|
+
new ToRadixBE(/*indirect=*/ 0, /*srcOffset=*/ 0, /*radixOffset=*/ 1, /*numLimbsOffset=*/ 2, /*outputBitsOffset=*/ 3, /*dstOffset=*/ 4)
|
|
1281
|
+
]
|
|
1282
|
+
},
|
|
1283
|
+
{
|
|
1284
|
+
label: 'Max limbs',
|
|
1285
|
+
setup: [
|
|
1286
|
+
{
|
|
1287
|
+
offset: 0,
|
|
1288
|
+
value: new Field(Fr.random())
|
|
1289
|
+
},
|
|
1290
|
+
{
|
|
1291
|
+
offset: 1,
|
|
1292
|
+
value: new Uint32(2n)
|
|
1293
|
+
},
|
|
1294
|
+
{
|
|
1295
|
+
offset: 2,
|
|
1296
|
+
value: new Uint32(256n)
|
|
974
1297
|
},
|
|
975
1298
|
{
|
|
976
1299
|
offset: 3,
|
|
@@ -985,6 +1308,8 @@ const INT_TAGS = Array.from(INTEGRAL_TAGS);
|
|
|
985
1308
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
986
1309
|
// MISC
|
|
987
1310
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
1311
|
+
// DEBUGLOG only has base gas (no dynamic gas scaling) - memory reads only happen
|
|
1312
|
+
// when collectDebugLogs config is enabled
|
|
988
1313
|
[Opcode.DEBUGLOG]: [
|
|
989
1314
|
{
|
|
990
1315
|
setup: [
|
|
@@ -1128,7 +1453,7 @@ const INT_TAGS = Array.from(INTEGRAL_TAGS);
|
|
|
1128
1453
|
const instructions = [];
|
|
1129
1454
|
// 1. Setup memory
|
|
1130
1455
|
appendSetupInstructions(instructions, config.setup);
|
|
1131
|
-
// 2. Infinite loop - maximize
|
|
1456
|
+
// 2. Infinite loop - maximize calls to target until out-of-gas
|
|
1132
1457
|
appendInfiniteLoop(instructions, config);
|
|
1133
1458
|
return encodeToBytecode(instructions);
|
|
1134
1459
|
}
|
|
@@ -1151,66 +1476,14 @@ const INT_TAGS = Array.from(INTEGRAL_TAGS);
|
|
|
1151
1476
|
}
|
|
1152
1477
|
return encodeToBytecode(instructions);
|
|
1153
1478
|
}
|
|
1154
|
-
/** Reserved memory offsets for outer call loop */ const CONST_1_OFFSET = 0;
|
|
1155
|
-
const CALL_L2_GAS_OFFSET = 1;
|
|
1156
|
-
const CALL_DA_GAS_OFFSET = 2;
|
|
1157
|
-
const CALL_ADDR_OFFSET = 3;
|
|
1158
|
-
const CALL_ARGS_SIZE = 4;
|
|
1159
|
-
const CALL_ARGS_OFFSET = 5;
|
|
1160
|
-
const CALLDATA_INDEX_OFFSET = 6; // calldata index as in calldata[index]
|
|
1161
|
-
/**
|
|
1162
|
-
* A SpamConfig for an external call loop.
|
|
1163
|
-
*/ const EXTERNAL_CALL_LOOP_CONFIG = {
|
|
1164
|
-
setup: [
|
|
1165
|
-
// calldata will contain 1 item: the external call address
|
|
1166
|
-
{
|
|
1167
|
-
offset: CONST_1_OFFSET,
|
|
1168
|
-
value: new Uint32(1)
|
|
1169
|
-
},
|
|
1170
|
-
{
|
|
1171
|
-
offset: CALLDATA_INDEX_OFFSET,
|
|
1172
|
-
value: new Uint32(0)
|
|
1173
|
-
},
|
|
1174
|
-
{
|
|
1175
|
-
offset: CALL_L2_GAS_OFFSET,
|
|
1176
|
-
value: new Uint32(0xffffffffn)
|
|
1177
|
-
},
|
|
1178
|
-
{
|
|
1179
|
-
offset: CALL_DA_GAS_OFFSET,
|
|
1180
|
-
value: new Uint32(0xffffffffn)
|
|
1181
|
-
},
|
|
1182
|
-
()=>[
|
|
1183
|
-
new CalldataCopy(/*indirect=*/ 0, /*copySizeOffset=*/ CONST_1_OFFSET, /*cdStartOffset=*/ CALLDATA_INDEX_OFFSET, /*dstOffset=*/ CALL_ADDR_OFFSET)
|
|
1184
|
-
],
|
|
1185
|
-
{
|
|
1186
|
-
offset: CALL_ARGS_SIZE,
|
|
1187
|
-
value: new Uint32(0)
|
|
1188
|
-
},
|
|
1189
|
-
{
|
|
1190
|
-
offset: CALL_ARGS_SIZE,
|
|
1191
|
-
value: new Uint32(0)
|
|
1192
|
-
}
|
|
1193
|
-
],
|
|
1194
|
-
targetInstructions: ()=>[
|
|
1195
|
-
new Call(/*indirect=*/ 0, /*l2GasOffset=*/ CALL_L2_GAS_OFFSET, /*daGasOffset=*/ CALL_DA_GAS_OFFSET, /*addrOffset=*/ CALL_ADDR_OFFSET, /*argsSizeOffset=*/ CALL_ARGS_SIZE, /*argsOffset=*/ CALL_ARGS_OFFSET)
|
|
1196
|
-
]
|
|
1197
|
-
};
|
|
1198
|
-
/**
|
|
1199
|
-
* Create bytecode that makes an external call in a loop.
|
|
1200
|
-
*
|
|
1201
|
-
* @returns the bytecode for the external call loop
|
|
1202
|
-
*/ export function createExternalCallLoopBytecode() {
|
|
1203
|
-
const config = EXTERNAL_CALL_LOOP_CONFIG;
|
|
1204
|
-
const instructions = [];
|
|
1205
|
-
// 1. Setup memory
|
|
1206
|
-
appendSetupInstructions(instructions, config.setup);
|
|
1207
|
-
// 2. Infinite loop of external calls - maximize iterations until out-of-gas
|
|
1208
|
-
appendInfiniteLoop(instructions, config);
|
|
1209
|
-
return encodeToBytecode(instructions);
|
|
1210
|
-
}
|
|
1211
1479
|
async function testStandardOpcodeSpam(tester, config, expectToBeTrue) {
|
|
1212
1480
|
const bytecode = createOpcodeSpamBytecode(config);
|
|
1213
|
-
const
|
|
1481
|
+
const contract = await deployCustomBytecode(bytecode, tester, config.label);
|
|
1482
|
+
// Should we pass the contract address as calldata?
|
|
1483
|
+
const calldata = config.addressAsCalldata ? [
|
|
1484
|
+
contract.address.toField()
|
|
1485
|
+
] : [];
|
|
1486
|
+
const result = await executeCustomBytecode(contract, tester, config.label, calldata);
|
|
1214
1487
|
// should have halted with out of gas
|
|
1215
1488
|
expectToBeTrue(!result.revertCode.isOK());
|
|
1216
1489
|
const revertReason = result.findRevertReason()?.message.toLowerCase();
|
|
@@ -1219,12 +1492,14 @@ async function testStandardOpcodeSpam(tester, config, expectToBeTrue) {
|
|
|
1219
1492
|
'not enough l2gas'
|
|
1220
1493
|
];
|
|
1221
1494
|
// expect the reason to match ONE of the allowed reasons
|
|
1222
|
-
expectToBeTrue(allowedReasons.some((allowedReason)=>revertReason
|
|
1495
|
+
expectToBeTrue(allowedReasons.some((allowedReason)=>revertReason?.includes(allowedReason)));
|
|
1223
1496
|
return result;
|
|
1224
1497
|
}
|
|
1225
1498
|
async function testSideEffectOpcodeSpam(tester, config, expectToBeTrue) {
|
|
1499
|
+
// Inner contract will spam the side-effect limited opcode up to its limit, then REVERT
|
|
1226
1500
|
const innerBytecode = createSideEffectSpamBytecode(config);
|
|
1227
|
-
|
|
1501
|
+
// Outer contract will CALL to inner contract in a loop
|
|
1502
|
+
const outerBytecode = createOpcodeSpamBytecode(EXTERNAL_CALL_CONFIG);
|
|
1228
1503
|
const innerContract = await deployCustomBytecode(innerBytecode, tester, `${config.label}_Inner`);
|
|
1229
1504
|
const outerContract = await deployCustomBytecode(outerBytecode, tester, `${config.label}_Outer`);
|
|
1230
1505
|
// Outer contract reads calldata[0] as inner contract address to CALL to
|
|
@@ -1240,7 +1515,7 @@ async function testSideEffectOpcodeSpam(tester, config, expectToBeTrue) {
|
|
|
1240
1515
|
'not enough l2gas'
|
|
1241
1516
|
];
|
|
1242
1517
|
// expect the reason to match ONE of the allowed reasons
|
|
1243
|
-
expectToBeTrue(allowedReasons.some((allowedReason)=>revertReason
|
|
1518
|
+
expectToBeTrue(allowedReasons.some((allowedReason)=>revertReason?.includes(allowedReason)));
|
|
1244
1519
|
// Top-level should _always_ run out of gas for these tests
|
|
1245
1520
|
// Check top-level halting message
|
|
1246
1521
|
// WARNING: only the C++ simulator (or TsVsCpp) will have haltingMessage
|
|
@@ -1248,10 +1523,12 @@ async function testSideEffectOpcodeSpam(tester, config, expectToBeTrue) {
|
|
|
1248
1523
|
'out of gas',
|
|
1249
1524
|
'not enough l2gas'
|
|
1250
1525
|
];
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1526
|
+
if (result.callStackMetadata && result.callStackMetadata.length > 0) {
|
|
1527
|
+
const outerCallMetadata = result.callStackMetadata[0];
|
|
1528
|
+
const outerReason = outerCallMetadata.haltingMessage?.toLowerCase();
|
|
1529
|
+
// expect the reason to match ONE of the allowed reasons
|
|
1530
|
+
expectToBeTrue(allowedOuterReasons.some((allowedReason)=>outerReason?.includes(allowedReason)));
|
|
1531
|
+
}
|
|
1255
1532
|
return result;
|
|
1256
1533
|
}
|
|
1257
1534
|
export async function testOpcodeSpamCase(tester, config, expectToBeTrue = ()=>{}) {
|