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
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ class GethTransitionTool(GethEvm, TransitionTool):
subcommand: Optional[str] = "t8n"
trace: bool
t8n_use_stream = True
supports_opcode_count: ClassVar[bool] = True

def __init__(
self,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -643,6 +643,23 @@ def _evaluate_stream(
},
)

if self.supports_opcode_count:
opcode_count_file_path = Path(temp_dir.name) / "opcodes.json"
if opcode_count_file_path.exists():
opcode_count = OpcodeCount.model_validate_json(
opcode_count_file_path.read_text()
)
output.result.opcode_count = opcode_count

if debug_output_path:
with profiler.pause():
dump_files_to_directory(
debug_output_path,
{
"opcodes.json": opcode_count.model_dump(),
},
)

if self.trace:
output.result.traces = self.collect_traces(
output.result.receipts, temp_dir, debug_output_path
Expand Down Expand Up @@ -694,8 +711,12 @@ def safe_t8n_args(
f"--state.reward={reward}",
]

if self.trace and temp_dir:
args.extend([trace_flag, f"--output.basedir={temp_dir.name}"])
if temp_dir and (self.trace or self.supports_opcode_count):
args.append(f"--output.basedir={temp_dir.name}")
if self.trace:
args.append(trace_flag)
if self.supports_opcode_count and temp_dir:
args.extend(["--opcode.count", "opcodes.json"])

return args

Expand Down
9 changes: 8 additions & 1 deletion packages/testing/src/execution_testing/fixtures/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@

from typing import Any, ClassVar, Dict, List

from pydantic import AliasChoices, Field, computed_field, model_validator
from pydantic import (
AliasChoices,
Field,
computed_field,
model_validator,
)

from execution_testing.base_types import (
BlobSchedule,
Expand Down Expand Up @@ -102,6 +107,8 @@ def sign(self) -> None:
class FixtureTransactionLog(CamelModel, RLPSerializable):
"""Fixture variant of the TransactionLog type."""

model_config = CamelModel.model_config | {"extra": "ignore"}

address: Address | None = None
topics: List[Hash] | None = None
data: Bytes | None = None
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class TransactionLog(CamelModel):
block_hash: Hash | None = None
log_index: HexNumber | None = None
removed: bool | None = None
block_timestamp: HexNumber | None = None


class ReceiptDelegation(CamelModel):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,12 @@ def __new__(
res += (
Op.SWAP1
+ Op.SUB
+ Op.PUSH1(overhead_cost + 2)
+ Op.PUSH1[overhead_cost]
+ Op.GAS
+ Op.GAS
+ Op.SWAP1
+ Op.SUB
+ Op.ADD
+ Op.SWAP1
+ Op.SSTORE(sstore_key, Op.SUB)
)
Expand Down
30 changes: 30 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,36 @@ packages = [
"ethereum.forks.osaka.vm.instructions",
"ethereum.forks.osaka.vm.precompiled_contracts",
"ethereum.forks.osaka.vm.precompiled_contracts.bls12_381",
"ethereum.forks.bpo1",
"ethereum.forks.bpo1.utils",
"ethereum.forks.bpo1.vm",
"ethereum.forks.bpo1.vm.instructions",
"ethereum.forks.bpo1.vm.precompiled_contracts",
"ethereum.forks.bpo1.vm.precompiled_contracts.bls12_381",
"ethereum.forks.bpo2",
"ethereum.forks.bpo2.utils",
"ethereum.forks.bpo2.vm",
"ethereum.forks.bpo2.vm.instructions",
"ethereum.forks.bpo2.vm.precompiled_contracts",
"ethereum.forks.bpo2.vm.precompiled_contracts.bls12_381",
"ethereum.forks.bpo3",
"ethereum.forks.bpo3.utils",
"ethereum.forks.bpo3.vm",
"ethereum.forks.bpo3.vm.instructions",
"ethereum.forks.bpo3.vm.precompiled_contracts",
"ethereum.forks.bpo3.vm.precompiled_contracts.bls12_381",
"ethereum.forks.bpo4",
"ethereum.forks.bpo4.utils",
"ethereum.forks.bpo4.vm",
"ethereum.forks.bpo4.vm.instructions",
"ethereum.forks.bpo4.vm.precompiled_contracts",
"ethereum.forks.bpo4.vm.precompiled_contracts.bls12_381",
"ethereum.forks.bpo5",
"ethereum.forks.bpo5.utils",
"ethereum.forks.bpo5.vm",
"ethereum.forks.bpo5.vm.instructions",
"ethereum.forks.bpo5.vm.precompiled_contracts",
"ethereum.forks.bpo5.vm.precompiled_contracts.bls12_381",
"ethereum.forks.amsterdam",
"ethereum.forks.amsterdam.block_access_lists",
"ethereum.forks.amsterdam.utils",
Expand Down
95 changes: 35 additions & 60 deletions tests/shanghai/eip3860_initcode/test_initcode.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
Transaction,
TransactionException,
TransactionReceipt,
ceiling_division,
compute_create_address,
)

Expand Down Expand Up @@ -141,11 +140,9 @@ def test_contract_creating_tx(
)

tx = Transaction(
nonce=0,
to=None,
data=initcode,
gas_limit=10000000,
gas_price=10,
gas_limit=10_000_000,
sender=sender,
)

Expand Down Expand Up @@ -320,12 +317,10 @@ def tx(
pytest.fail("Invalid gas test case provided.")

return Transaction(
nonce=0,
to=None,
access_list=tx_access_list,
data=initcode,
gas_limit=gas_limit,
gas_price=10,
error=tx_error,
sender=sender,
# The entire gas limit is expected to be consumed.
Expand Down Expand Up @@ -415,29 +410,52 @@ def create2_salt(self) -> int:
return 0xDEADBEEF

@pytest.fixture
def creator_code(self, opcode: Op, create2_salt: int) -> Bytecode:
def create_code(
self, opcode: Op, create2_salt: int, initcode: Initcode
) -> Bytecode:
"""
Generate the CREATE/CREATE2 bytecode.
"""
return (
opcode(
size=Op.CALLDATASIZE,
salt=create2_salt,
init_code_size=len(initcode),
)
if opcode == Op.CREATE2
else opcode(size=Op.CALLDATASIZE, init_code_size=len(initcode))
)

@pytest.fixture
def creator_code(self, fork: Fork, create_code: Bytecode) -> Bytecode:
"""
Generate code for the creator contract which calls CREATE/CREATE2.
"""
return (
Op.CALLDATACOPY(0, 0, Op.CALLDATASIZE)
+ Op.GAS
+ (
opcode(size=Op.CALLDATASIZE, salt=create2_salt)
if opcode == Op.CREATE2
else opcode(size=Op.CALLDATASIZE)
)
+ create_code
+ Op.GAS
# stack: [Gas 2, Call Result, Gas 1]
+ Op.SWAP1
# stack: [Call Result, Gas 2, Gas 1]
+ Op.SSTORE(0, unchecked=True)
+ Op.PUSH1[0]
# stack: [0, Call Result, Gas 2, Gas 1]
+ Op.SSTORE
# stack: [Gas 2, Gas 1]
+ Op.SWAP1
# stack: [Gas 1, Gas 2]
+ Op.SUB
# stack: [Gas 1 - Gas 2]
+ Op.SSTORE(1, unchecked=True)
+ Op.PUSH1[Op.GAS.gas_cost(fork)]
# stack: [Op.GAS cost, Gas 1 - Gas 2]
+ Op.SWAP1
# stack: [Gas 1 - Gas 2, Op.GAS cost]
+ Op.SUB
# stack: [Gas 1 - Gas 2 - Op.GAS cost]
+ Op.PUSH1[1]
# stack: [1, Gas 1 - Gas 2 - Op.GAS cost]
+ Op.SSTORE
)

@pytest.fixture
Expand Down Expand Up @@ -491,45 +509,12 @@ def tx(
) -> Transaction:
"""Generate transaction that executes the caller contract."""
return Transaction(
nonce=0,
to=caller_contract_address,
data=initcode,
gas_limit=10000000,
gas_price=10,
gas_limit=10_000_000,
sender=sender,
)

@pytest.fixture
def contract_creation_gas_cost(
self, fork: Fork, opcode: Op, create2_salt: int
) -> int:
"""Calculate gas cost of the contract creation operation."""
create_code = (
opcode(size=Op.CALLDATASIZE, salt=create2_salt)
if opcode == Op.CREATE2
else opcode(size=Op.CALLDATASIZE)
)
return (create_code + Op.GAS).gas_cost(fork)

@pytest.fixture
def initcode_word_cost(self, fork: Fork, initcode: Initcode) -> int:
"""Calculate gas cost charged for the initcode length."""
gas_costs = fork.gas_costs()
return ceiling_division(len(initcode), 32) * gas_costs.G_INITCODE_WORD

@pytest.fixture
def create2_word_cost(
self, opcode: Op, fork: Fork, initcode: Initcode
) -> int:
"""Calculate gas cost charged for the initcode length."""
if opcode == Op.CREATE:
return 0

gas_costs = fork.gas_costs()
return (
ceiling_division(len(initcode), 32) * gas_costs.G_KECCAK_256_WORD
)

@pytest.mark.xdist_group(name="bigmem")
@pytest.mark.slow()
def test_create_opcode_initcode(
Expand All @@ -543,9 +528,7 @@ def test_create_opcode_initcode(
caller_contract_address: Address,
creator_contract_address: Address,
created_contract_address: Address,
contract_creation_gas_cost: int,
initcode_word_cost: int,
create2_word_cost: int,
create_code: Bytecode,
fork: Fork,
) -> None:
"""
Expand Down Expand Up @@ -574,18 +557,10 @@ def test_create_opcode_initcode(
)

else:
expected_gas_usage = contract_creation_gas_cost
expected_gas_usage = create_code.gas_cost(fork)
# The initcode is only executed if the length check succeeds
expected_gas_usage += initcode.gas_cost(fork)

# CREATE2 hashing cost should only be deducted if the initcode
# does not exceed the max length
expected_gas_usage += create2_word_cost

# Initcode word cost is only deducted if the length check
# succeeds
expected_gas_usage += initcode_word_cost

# Call returns 1 as valid initcode length s[0]==1 && s[1]==1
post[caller_contract_address] = Account(
nonce=1,
Expand Down
Loading