Skip to content
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
27 changes: 27 additions & 0 deletions src/ethereum_test_execution/transaction_post.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ class TransactionPost(BaseExecute):

blocks: List[List[Transaction]]
post: Alloc
# Gas validation fields for benchmark tests
expected_benchmark_gas_used: int | None = None # Expected total gas to be consumed
skip_gas_used_validation: bool = False # Skip gas validation even if expected is set

format_name: ClassVar[str] = "transaction_post_test"
description: ClassVar[str] = (
Expand All @@ -33,6 +36,10 @@ def execute(
assert not any(tx.ty == 3 for block in self.blocks for tx in block), (
"Transaction type 3 is not supported in execute mode."
)

# Track transaction hashes for gas validation (benchmarking)
all_tx_hashes = []

for block in self.blocks:
signed_txs = []
for tx_index, tx in enumerate(block):
Expand All @@ -51,11 +58,31 @@ def execute(
for transaction in signed_txs:
if transaction.error is None:
eth_rpc.send_wait_transaction(transaction)
all_tx_hashes.append(transaction.hash)
else:
with pytest.raises(SendTransactionExceptionError):
eth_rpc.send_transaction(transaction)
else:
eth_rpc.send_wait_transactions(signed_txs)
all_tx_hashes.extend([tx.hash for tx in signed_txs])

# Perform gas validation if required for benchmarking
# Ensures benchmark tests consume exactly the expected gas
if not self.skip_gas_used_validation and self.expected_benchmark_gas_used is not None:
total_gas_used = 0
# Fetch transaction receipts to get actual gas used
for tx_hash in all_tx_hashes:
receipt = eth_rpc.get_transaction_receipt(tx_hash)
assert receipt is not None, f"Failed to get receipt for transaction {tx_hash}"
gas_used = int(receipt["gasUsed"], 16)
total_gas_used += gas_used

# Verify that the total gas consumed matches expectations
assert total_gas_used == self.expected_benchmark_gas_used, (
f"Total gas used ({total_gas_used}) does not match "
f"expected benchmark gas ({self.expected_benchmark_gas_used}), "
f"difference: {total_gas_used - self.expected_benchmark_gas_used}"
)

for address, account in self.post.root.items():
balance = eth_rpc.get_balance(address)
Expand Down
12 changes: 12 additions & 0 deletions src/ethereum_test_rpc/rpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,18 @@ def get_transaction_by_hash(self, transaction_hash: Hash) -> TransactionByHashRe
pprint(e.errors())
raise e

def get_transaction_receipt(self, transaction_hash: Hash) -> dict | None:
"""
`eth_getTransactionReceipt`: Returns transaction receipt.

Used to get the actual gas used by a transaction for gas validation
in benchmark tests.
"""
response = self.post_request(
method="getTransactionReceipt", params=[f"{transaction_hash}"]
)
return response

def get_storage_at(
self, address: Address, position: Hash, block_number: BlockNumberType = "latest"
) -> Hash:
Expand Down
3 changes: 3 additions & 0 deletions src/ethereum_test_specs/blockchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -899,9 +899,12 @@ def execute(
blocks: List[List[Transaction]] = []
for block in self.blocks:
blocks += [block.txs]
# Pass gas validation params for benchmark tests
return TransactionPost(
blocks=blocks,
post=self.post,
expected_benchmark_gas_used=self.expected_benchmark_gas_used,
skip_gas_used_validation=self.skip_gas_used_validation,
)
raise Exception(f"Unsupported execute format: {execute_format}")

Expand Down
3 changes: 3 additions & 0 deletions src/ethereum_test_specs/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -444,9 +444,12 @@ def execute(
) -> BaseExecute:
"""Generate the list of test fixtures."""
if execute_format == TransactionPost:
# Pass gas validation params for benchmark tests
return TransactionPost(
blocks=[[self.tx]],
post=self.post,
expected_benchmark_gas_used=self.expected_benchmark_gas_used,
skip_gas_used_validation=self.skip_gas_used_validation,
)
raise Exception(f"Unsupported execute format: {execute_format}")

Expand Down
5 changes: 5 additions & 0 deletions src/pytest_plugins/execute/execute.py
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,11 @@ def __init__(self, *args, **kwargs):
kwargs["pre"] = pre
elif kwargs["pre"] != pre:
raise ValueError("The pre-alloc object was modified by the test.")
# Set default for expected_benchmark_gas_used
if "expected_benchmark_gas_used" not in kwargs:
kwargs["expected_benchmark_gas_used"] = request.getfixturevalue(
"gas_benchmark_value"
)
kwargs |= {
p: request.getfixturevalue(p)
for p in cls_fixture_parameters
Expand Down
Loading