|
5 | 5 | Cap](https://eips.ethereum.org/EIPS/eip-7825). |
6 | 6 | """ |
7 | 7 |
|
8 | | -from typing import List |
| 8 | +from typing import Callable, List |
9 | 9 |
|
10 | 10 | import pytest |
11 | 11 | from execution_testing import ( |
|
36 | 36 | REFERENCE_SPEC_VERSION = ref_spec_7825.version |
37 | 37 |
|
38 | 38 |
|
| 39 | +def max_count_with_intrinsic_cost_at_most( |
| 40 | + cost_fn: Callable[[int], int], gas_limit: int |
| 41 | +) -> int: |
| 42 | + """Return the largest count where cost_fn(count) <= gas_limit.""" |
| 43 | + low = 0 |
| 44 | + high = 1 |
| 45 | + |
| 46 | + while cost_fn(high) <= gas_limit: |
| 47 | + low = high |
| 48 | + high *= 2 |
| 49 | + |
| 50 | + while low < high: |
| 51 | + mid = (low + high + 1) // 2 |
| 52 | + if cost_fn(mid) <= gas_limit: |
| 53 | + low = mid |
| 54 | + else: |
| 55 | + high = mid - 1 |
| 56 | + |
| 57 | + return low |
| 58 | + |
| 59 | + |
39 | 60 | def tx_gas_limit_cap_tests(fork: Fork) -> List[ParameterSet]: |
40 | 61 | """ |
41 | 62 | Return a list of tests for transaction gas limit cap parametrized for each |
@@ -483,21 +504,22 @@ def test_tx_gas_limit_cap_access_list_with_diff_keys( |
483 | 504 | assert tx_gas_limit_cap is not None, ( |
484 | 505 | "Fork does not have a transaction gas limit cap" |
485 | 506 | ) |
486 | | - gas_available = tx_gas_limit_cap - intrinsic_cost() |
487 | | - |
488 | | - gas_costs = fork.gas_costs() |
489 | | - gas_per_address = gas_costs.G_ACCESS_LIST_ADDRESS |
490 | | - gas_per_storage_key = gas_costs.G_ACCESS_LIST_STORAGE |
| 507 | + access_address = Address("0x1234567890123456789012345678901234567890") |
491 | 508 |
|
492 | | - gas_after_address = gas_available - gas_per_address |
493 | | - num_storage_keys = gas_after_address // gas_per_storage_key + int( |
494 | | - exceed_tx_gas_limit |
495 | | - ) |
| 509 | + def intrinsic_cost_for_num_storage_keys(storage_key_count: int) -> int: |
| 510 | + return intrinsic_cost( |
| 511 | + access_list=[ |
| 512 | + AccessList( |
| 513 | + address=access_address, |
| 514 | + storage_keys=[Hash(i) for i in range(storage_key_count)], |
| 515 | + ) |
| 516 | + ] |
| 517 | + ) |
496 | 518 |
|
497 | | - access_address = Address("0x1234567890123456789012345678901234567890") |
498 | | - storage_keys = [] |
499 | | - for i in range(num_storage_keys): |
500 | | - storage_keys.append(Hash(i)) |
| 519 | + num_storage_keys = max_count_with_intrinsic_cost_at_most( |
| 520 | + intrinsic_cost_for_num_storage_keys, tx_gas_limit_cap |
| 521 | + ) + int(exceed_tx_gas_limit) |
| 522 | + storage_keys = [Hash(i) for i in range(num_storage_keys)] |
501 | 523 |
|
502 | 524 | access_list = [ |
503 | 525 | AccessList( |
@@ -568,23 +590,23 @@ def test_tx_gas_limit_cap_access_list_with_diff_addr( |
568 | 590 | assert tx_gas_limit_cap is not None, ( |
569 | 591 | "Fork does not have a transaction gas limit cap" |
570 | 592 | ) |
571 | | - gas_available = tx_gas_limit_cap - intrinsic_cost() |
572 | 593 |
|
573 | | - gas_costs = fork.gas_costs() |
574 | | - gas_per_address = gas_costs.G_ACCESS_LIST_ADDRESS |
575 | | - gas_per_storage_key = gas_costs.G_ACCESS_LIST_STORAGE |
| 594 | + def make_access_list(account_count: int) -> List[AccessList]: |
| 595 | + return [ |
| 596 | + AccessList( |
| 597 | + address=Address(i + 1), |
| 598 | + storage_keys=[Hash(i)], |
| 599 | + ) |
| 600 | + for i in range(account_count) |
| 601 | + ] |
576 | 602 |
|
577 | | - account_num = gas_available // ( |
578 | | - gas_per_address + gas_per_storage_key |
579 | | - ) + int(exceed_tx_gas_limit) |
| 603 | + def intrinsic_cost_for_num_accounts(account_count: int) -> int: |
| 604 | + return intrinsic_cost(access_list=make_access_list(account_count)) |
580 | 605 |
|
581 | | - access_list = [ |
582 | | - AccessList( |
583 | | - address=pre.fund_eoa(), |
584 | | - storage_keys=[Hash(i)], |
585 | | - ) |
586 | | - for i in range(account_num) |
587 | | - ] |
| 606 | + account_num = max_count_with_intrinsic_cost_at_most( |
| 607 | + intrinsic_cost_for_num_accounts, tx_gas_limit_cap |
| 608 | + ) + int(exceed_tx_gas_limit) |
| 609 | + access_list = make_access_list(account_num) |
588 | 610 |
|
589 | 611 | correct_intrinsic_cost = intrinsic_cost(access_list=access_list) |
590 | 612 | if exceed_tx_gas_limit: |
@@ -645,36 +667,40 @@ def test_tx_gas_limit_cap_authorized_tx( |
645 | 667 | assert tx_gas_limit_cap is not None, ( |
646 | 668 | "Fork does not have a transaction gas limit cap" |
647 | 669 | ) |
648 | | - gas_available = tx_gas_limit_cap - intrinsic_cost() |
649 | 670 |
|
650 | | - gas_costs = fork.gas_costs() |
651 | | - gas_per_address = gas_costs.G_ACCESS_LIST_ADDRESS |
| 671 | + def make_access_list(auth_count: int) -> List[AccessList]: |
| 672 | + return [ |
| 673 | + AccessList( |
| 674 | + address=Address(i + 1), |
| 675 | + storage_keys=[], |
| 676 | + ) |
| 677 | + for i in range(auth_count) |
| 678 | + ] |
| 679 | + |
| 680 | + def intrinsic_cost_for_auth_list_length(auth_count: int) -> int: |
| 681 | + return intrinsic_cost( |
| 682 | + access_list=make_access_list(auth_count), |
| 683 | + authorization_list_or_count=auth_count, |
| 684 | + ) |
652 | 685 |
|
653 | | - per_empty_account_cost = 25_000 |
654 | | - auth_list_length = gas_available // ( |
655 | | - gas_per_address + per_empty_account_cost |
| 686 | + auth_list_length = max_count_with_intrinsic_cost_at_most( |
| 687 | + intrinsic_cost_for_auth_list_length, tx_gas_limit_cap |
656 | 688 | ) + int(exceed_tx_gas_limit) |
657 | 689 |
|
658 | 690 | # EIP-7702 authorization transaction cost: |
659 | 691 | # 21000 + 16 * non-zero calldata bytes + 4 * zero calldata bytes + 1900 * |
660 | | - # access list storage key count + 2400 * access list address count + |
661 | | - # PER_EMPTY_ACCOUNT_COST * authorization list length |
| 692 | + # access list storage key count + 2400 * access list address count + access |
| 693 | + # list data cost + PER_EMPTY_ACCOUNT_COST * authorization list length |
662 | 694 | # |
663 | | - # There is no calldata and no storage keys in this test case and the access |
664 | | - # address list count is equal to the authorization list length |
665 | | - # |
666 | | - # total cost = 21000 + (2400 + 25_000) * auth_list_length |
| 695 | + # There is no calldata and no storage keys in this test case. |
| 696 | + # However, each access-list address includes data bytes that may contribute |
| 697 | + # additional cost depending on fork repricing. |
667 | 698 |
|
668 | 699 | auth_address = pre.deploy_contract(code=Op.STOP) |
669 | 700 |
|
670 | 701 | auth_signers = [pre.fund_eoa() for _ in range(auth_list_length)] |
671 | | - |
672 | 702 | access_list = [ |
673 | | - AccessList( |
674 | | - address=addr, |
675 | | - storage_keys=[], |
676 | | - ) |
677 | | - for addr in auth_signers |
| 703 | + AccessList(address=addr, storage_keys=[]) for addr in auth_signers |
678 | 704 | ] |
679 | 705 |
|
680 | 706 | auth_tuples = [ |
|
0 commit comments