Skip to content

test_pointer_to_precompile()

Documentation for tests/prague/eip7702_set_code_tx/test_set_code_txs_2.py::test_pointer_to_precompile@892e6d1e.

Generate fixtures for these test cases for Amsterdam with:

fill -v tests/prague/eip7702_set_code_tx/test_set_code_txs_2.py::test_pointer_to_precompile --fork Amsterdam

Tx -> call -> pointer A -> precompile contract.

In case a delegation designator points to a precompile address, retrieved code is considered empty and CALL, CALLCODE, STATICCALL, DELEGATECALL instructions targeting this account will execute empty code, i.e. succeed with no execution given enough gas.

So call to a pointer that points to a precompile is like call to an empty account

Source code in tests/prague/eip7702_set_code_tx/test_set_code_txs_2.py
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
@pytest.mark.with_all_precompiles
@pytest.mark.valid_from("Prague")
@pytest.mark.parametrize("sender_delegated", [True, False])
@pytest.mark.parametrize("sender_is_auth_signer", [True, False])
def test_pointer_to_precompile(
    state_test: StateTestFiller,
    pre: Alloc,
    sender_delegated: bool,
    sender_is_auth_signer: bool,
    precompile: int,
) -> None:
    """
    Tx -> call -> pointer A -> precompile contract.

    In case a delegation designator points to a precompile address, retrieved
    code is considered empty and CALL, CALLCODE, STATICCALL, DELEGATECALL
    instructions targeting this account will execute empty code, i.e. succeed
    with no execution given enough gas.

    So call to a pointer that points to a precompile is like call to an empty
    account
    """
    env = Environment()

    storage: Storage = Storage()

    if sender_delegated:
        sender_delegation_target = pre.deploy_contract(Op.STOP)
        sender = pre.fund_eoa(delegation=sender_delegation_target)
    else:
        sender = pre.fund_eoa()

    if sender_is_auth_signer:
        pointer_a = sender
    else:
        pointer_a = pre.fund_eoa()

    contract_test_normal = pre.deploy_contract(
        code=Op.MSTORE(
            0, Op.CALL(gas=0, address=precompile, args_size=Op.CALLDATASIZE())
        )
        + Op.RETURN(0, 32)
    )

    contract_test_pointer = pre.deploy_contract(
        code=Op.MSTORE(
            0, Op.CALL(gas=0, address=pointer_a, args_size=Op.CALLDATASIZE())
        )
        + Op.RETURN(0, 32)
    )

    contract_a = pre.deploy_contract(
        code=Op.CALL(
            gas=1_000_000,
            address=contract_test_normal,
            args_size=Op.CALLDATASIZE(),
            ret_offset=1000,
            ret_size=32,
        )
        # direct call to a precompile with 0 gas always return 0
        + Op.SSTORE(
            storage.store_next(0, "direct_call_result"), Op.MLOAD(1000)
        )
        + Op.CALL(
            gas=1_000_000,
            address=contract_test_pointer,
            args_size=Op.CALLDATASIZE(),
            ret_offset=1000,
            ret_size=32,
        )
        # pointer call to a precompile with 0 gas always return 1 as if calling
        # empty address
        + Op.SSTORE(
            storage.store_next(1, "pointer_call_result"), Op.MLOAD(1000)
        )
    )
    nonce = (
        2
        if sender_delegated and sender_is_auth_signer
        else 1
        if sender_is_auth_signer
        else 0
    )

    tx = Transaction(
        to=contract_a,
        gas_limit=3_000_000,
        data=[0x11] * 256,
        value=0,
        sender=sender,
        authorization_list=[
            AuthorizationTuple(
                address=precompile,
                nonce=nonce,
                signer=pointer_a,
            )
        ],
    )

    post = {contract_a: Account(storage=storage)}
    state_test(
        env=env,
        pre=pre,
        post=post,
        tx=tx,
    )

Parametrized Test Cases

This test generates 72 parametrized test cases across 3 forks.