Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(tests): Add blob gas subtraction order tests #407

Merged
merged 4 commits into from
Jan 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ Test fixtures for use by clients are available for each release on the [Github r

### 🧪 Test Cases

- ✨ [EIP-4844](https://eips.ethereum.org/EIPS/eip-4844): Adds `test_blob_gas_subtraction_tx()` verifying the blob gas fee is subtracted from the sender before executing the blob tx ([#407](https://github.com/ethereum/execution-spec-tests/pull/407)).

### 🛠️ Framework

- 🐞 State tests generated with transition forks no longer use the transition fork name in the fixture output, instead they use the actual enabled fork according to the state test's block number and timestamp ([#406](https://github.com/ethereum/execution-spec-tests/pull/406)).
Expand Down
113 changes: 91 additions & 22 deletions tests/cancun/eip4844_blobs/test_blob_txs.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,10 +207,33 @@ def total_account_minimum_balance( # noqa: D103
Calculates the minimum balance required for the account to be able to send
the transactions in the block of the test.
"""
minimum_cost = 0
for tx_blob_count in [len(x) for x in blob_hashes_per_tx]:
blob_cost = tx_max_fee_per_blob_gas * Spec.GAS_PER_BLOB * tx_blob_count
minimum_cost += (tx_gas * tx_max_fee_per_gas) + tx_value + blob_cost
return minimum_cost


@pytest.fixture
def total_account_transactions_fee( # noqa: D103
tx_gas: int,
tx_value: int,
blob_gasprice: int,
block_fee_per_gas: int,
tx_max_fee_per_gas: int,
tx_max_priority_fee_per_gas: int,
blob_hashes_per_tx: List[List[bytes]],
) -> int:
"""
Calculates the actual fee for the blob transactions in the block of the test.
"""
total_cost = 0
for tx_blob_count in [len(x) for x in blob_hashes_per_tx]:
data_cost = tx_max_fee_per_blob_gas * Spec.GAS_PER_BLOB * tx_blob_count
total_cost += (tx_gas * tx_max_fee_per_gas) + tx_value + data_cost
blob_cost = blob_gasprice * Spec.GAS_PER_BLOB * tx_blob_count
block_producer_fee = (
tx_max_fee_per_gas - block_fee_per_gas if tx_max_priority_fee_per_gas else 0
)
total_cost += (tx_gas * (block_fee_per_gas + block_producer_fee)) + tx_value + blob_cost
return total_cost


Expand Down Expand Up @@ -463,34 +486,17 @@ def header_verify(
return header_verify


@pytest.fixture
def all_blob_gas_used(
fork: Fork,
txs: List[Transaction],
block_number: int,
block_timestamp: int,
) -> Optional[int | Removable]:
"""
Calculates the blob gas used by the test block taking into account failed transactions.
"""
if not fork.header_blob_gas_used_required(
block_number=block_number, timestamp=block_timestamp
):
return Header.EMPTY_FIELD
return sum([Spec.get_total_blob_gas(tx) for tx in txs])


@pytest.fixture
def rlp_modifier(
all_blob_gas_used: Optional[int | Removable],
expected_blob_gas_used: Optional[int | Removable],
) -> Optional[Header]:
"""
Header fields to modify on the output block in the BlockchainTest.
"""
if all_blob_gas_used == Header.EMPTY_FIELD:
if expected_blob_gas_used == Header.EMPTY_FIELD:
return None
return Header(
blob_gas_used=all_blob_gas_used,
blob_gas_used=expected_blob_gas_used,
)


Expand Down Expand Up @@ -896,6 +902,69 @@ def test_sufficient_balance_blob_tx_pre_fund_tx(
)


@pytest.mark.parametrize(
"tx_access_list",
[[], [AccessList(address=100, storage_keys=[100, 200])]],
ids=["no_access_list", "access_list"],
)
@pytest.mark.parametrize("tx_max_fee_per_gas", [7, 14])
@pytest.mark.parametrize("tx_max_priority_fee_per_gas", [0, 7])
@pytest.mark.parametrize("tx_value", [0, 1])
@pytest.mark.parametrize(
"tx_calldata",
[b"", b"\x01"],
ids=["no_calldata", "single_non_zero_byte_calldata"],
)
@pytest.mark.parametrize("tx_max_fee_per_blob_gas", [1, 100])
@pytest.mark.parametrize(
"tx_gas", [500_000], ids=[""]
) # Increase gas to account for contract code
@pytest.mark.parametrize(
"mid_tx_send_amount", [100]
) # Amount sent by the contract to the sender mid execution
@pytest.mark.valid_from("Cancun")
def test_blob_gas_subtraction_tx(
state_test: StateTestFiller,
state_env: Environment,
pre: Dict,
txs: List[Transaction],
destination_account: str,
mid_tx_send_amount: int,
total_account_transactions_fee: int,
):
"""
Check that the blob gas fee for a transaction is subtracted from the sender balance before the
transaction is executed, including:

- Transactions with max fee equal or higher than current block base fee
- Transactions with and without value
- Transactions with and without calldata
- Transactions with max fee per blob gas lower or higher than the priority fee
- Transactions where an externally owned account sends funds to the sender mid execution
"""
assert len(txs) == 1
pre[destination_account] = Account(
balance=mid_tx_send_amount,
code=Op.SSTORE(0, Op.BALANCE(Op.ORIGIN))
+ Op.CALL(Op.GAS, Op.ORIGIN, mid_tx_send_amount, 0, 0, 0, 0)
+ Op.SSTORE(1, Op.BALANCE(Op.ORIGIN)),
)
post = {
destination_account: Account(
storage={
0: pre[TestAddress].balance - total_account_transactions_fee,
1: pre[TestAddress].balance - total_account_transactions_fee + mid_tx_send_amount,
}
)
}
state_test(
pre=pre,
post=post,
tx=txs[0],
env=state_env,
)


@pytest.mark.parametrize(
"blobs_per_tx",
all_valid_blob_combinations(),
Expand Down
Loading