@@ -359,3 +359,163 @@ def test_worst_jumpdests(
359
359
post = {},
360
360
blocks = [Block (txs = txs )],
361
361
)
362
+
363
+
364
+ DEFAULT_BINOP_ARGS = (
365
+ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F ,
366
+ 0x73EDA753299D7D483339D80809A1D80553BDA402FFFE5BFEFFFFFFFF00000001 ,
367
+ )
368
+
369
+
370
+ @pytest .mark .valid_from ("Cancun" )
371
+ @pytest .mark .parametrize (
372
+ "opcode,opcode_args" ,
373
+ [
374
+ (
375
+ Op .ADD ,
376
+ DEFAULT_BINOP_ARGS ,
377
+ ),
378
+ (
379
+ Op .MUL ,
380
+ DEFAULT_BINOP_ARGS ,
381
+ ),
382
+ (
383
+ # This has the cycle of 2, after two SUBs values are back to initials.
384
+ Op .SUB ,
385
+ DEFAULT_BINOP_ARGS ,
386
+ ),
387
+ (
388
+ # This has the cycle of 2:
389
+ # v[0] = a // b
390
+ # v[1] = a // v[0] = a // (a // b) = b
391
+ # v[2] = a // b
392
+ Op .DIV ,
393
+ (
394
+ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F ,
395
+ # We want the first divisor to be slightly bigger than 2**128:
396
+ # this is the worst case for the division algorithm.
397
+ 0x100000000000000000000000000000033 ,
398
+ ),
399
+ ),
400
+ (
401
+ # Same as DIV, but the numerator made positive, and the divisor made negative.
402
+ Op .SDIV ,
403
+ (
404
+ 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F ,
405
+ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCD ,
406
+ ),
407
+ ),
408
+ (
409
+ # This scenario is not suitable for MOD because the values quickly become 0.
410
+ Op .MOD ,
411
+ DEFAULT_BINOP_ARGS ,
412
+ ),
413
+ (
414
+ # This scenario is not suitable for SMOD because the values quickly become 0.
415
+ Op .SMOD ,
416
+ DEFAULT_BINOP_ARGS ,
417
+ ),
418
+ (
419
+ # This keeps the values unchanged, pow(2**256-1, 2**256-1, 2**256) == 2**256-1.
420
+ Op .EXP ,
421
+ (
422
+ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ,
423
+ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ,
424
+ ),
425
+ ),
426
+ (
427
+ # Not great because we always sign-extend the 4 bytes.
428
+ Op .SIGNEXTEND ,
429
+ (
430
+ 3 ,
431
+ 0xFFDADADA , # Negative to have more work.
432
+ ),
433
+ ),
434
+ (
435
+ Op .LT , # Keeps getting result 1.
436
+ (0 , 1 ),
437
+ ),
438
+ (
439
+ Op .GT , # Keeps getting result 0.
440
+ (0 , 1 ),
441
+ ),
442
+ (
443
+ Op .SLT , # Keeps getting result 1.
444
+ (0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF , 1 ),
445
+ ),
446
+ (
447
+ Op .SGT , # Keeps getting result 0.
448
+ (0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF , 1 ),
449
+ ),
450
+ (
451
+ # The worst case is if the arguments are equal (no early return),
452
+ # so let's keep it comparing ones.
453
+ Op .EQ ,
454
+ (1 , 1 ),
455
+ ),
456
+ (
457
+ Op .AND ,
458
+ DEFAULT_BINOP_ARGS ,
459
+ ),
460
+ (
461
+ Op .OR ,
462
+ DEFAULT_BINOP_ARGS ,
463
+ ),
464
+ (
465
+ Op .XOR ,
466
+ DEFAULT_BINOP_ARGS ,
467
+ ),
468
+ (
469
+ Op .BYTE , # Keep extracting the last byte: 0x2F.
470
+ (31 , 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F ),
471
+ ),
472
+ (
473
+ Op .SHL , # Shift by 1 until getting 0.
474
+ (1 , 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F ),
475
+ ),
476
+ (
477
+ Op .SHR , # Shift by 1 until getting 0.
478
+ (1 , 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F ),
479
+ ),
480
+ (
481
+ Op .SAR , # Shift by 1 until getting -1.
482
+ (1 , 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F ),
483
+ ),
484
+ ],
485
+ ids = lambda param : "" if isinstance (param , tuple ) else param ,
486
+ )
487
+ def test_worst_binop_simple (
488
+ blockchain_test : BlockchainTestFiller ,
489
+ pre : Alloc ,
490
+ opcode : Op ,
491
+ opcode_args : tuple [int , int ],
492
+ ):
493
+ """
494
+ Test running a block with as many binary instructions (takes two args, produces one value)
495
+ as possible. The execution starts with two initial values on the stack, and the stack is
496
+ balanced by the DUP2 instruction.
497
+ """
498
+ env = Environment ()
499
+
500
+ tx_data = b"" .join (arg .to_bytes (32 , byteorder = "big" ) for arg in opcode_args )
501
+
502
+ code_prefix = Op .JUMPDEST + Op .CALLDATALOAD (0 ) + Op .CALLDATALOAD (32 )
503
+ code_suffix = Op .POP + Op .POP + Op .PUSH0 + Op .JUMP
504
+ code_body_len = MAX_CODE_SIZE - len (code_prefix ) - len (code_suffix )
505
+ code_body = (Op .DUP2 + opcode ) * (code_body_len // 2 )
506
+ code = code_prefix + code_body + code_suffix
507
+ assert len (code ) == MAX_CODE_SIZE - 1
508
+
509
+ tx = Transaction (
510
+ to = pre .deploy_contract (code = code ),
511
+ data = tx_data ,
512
+ gas_limit = env .gas_limit ,
513
+ sender = pre .fund_eoa (),
514
+ )
515
+
516
+ blockchain_test (
517
+ env = env ,
518
+ pre = pre ,
519
+ post = {},
520
+ blocks = [Block (txs = [tx ])],
521
+ )
0 commit comments