Skip to content
Draft
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 @@ -1306,7 +1306,6 @@ def base_test_parametrizer_func(
fixture_source_url: str,
gas_benchmark_value: int,
fixed_opcode_count: int | None,
witness_generator: Any,

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is removing the old witness generator thing we included in the specs which use an external Reth tool. Since we generate it from the specs now, we can clean it up.

) -> Any:
"""
Fixture used to instantiate an auto-fillable BaseTest object from
Expand Down Expand Up @@ -1430,11 +1429,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None:
_info_metadata=t8n._info_metadata,
)

# Generate witness data if witness functionality is enabled via
# the witness plugin
if witness_generator is not None:
witness_generator(fixture)

fixture_path = fixture_collector.add_fixture(
node_to_test_info(request.node),
fixture,
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ addopts =
-p execution_testing.cli.pytest_commands.plugins.forks.forks
-p execution_testing.cli.pytest_commands.plugins.concurrency
-p execution_testing.cli.pytest_commands.plugins.filler.pre_alloc
-p execution_testing.cli.pytest_commands.plugins.filler.witness
-p execution_testing.cli.pytest_commands.plugins.filler.ported_tests
-p execution_testing.cli.pytest_commands.plugins.filler.static_filler
-p execution_testing.cli.pytest_commands.plugins.shared.benchmarking
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
TransactionException,
UndefinedException,
)
from execution_testing.fixtures.blockchain import ExecutionWitness
from execution_testing.logging import (
get_logger,
)
Expand Down Expand Up @@ -289,6 +290,7 @@ class Result(CamelModel):
] = None
traces: Traces | None = None
opcode_count: OpcodeCount | None = None
execution_witness: ExecutionWitness | None = None


TRaw = TypeVar("TRaw")
Expand Down
29 changes: 10 additions & 19 deletions packages/testing/src/execution_testing/fixtures/blockchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,14 @@ def from_fixture_header(
]


class ExecutionWitness(CamelModel):
"""Execution witness containing RLP-encoded trie nodes and bytecodes accessed during block execution."""

nodes: List[str]
bytecodes: List[str] = []
ancestors: List[str] = []


class FixtureEngineNewPayload(CamelModel):
"""
Representation of the `engine_newPayloadVX` information to be sent using
Expand All @@ -468,6 +476,7 @@ class FixtureEngineNewPayload(CamelModel):
]
| None
) = None
execution_witness: ExecutionWitness | None = None

def valid(self) -> bool:
"""Return whether the payload is valid."""
Expand Down Expand Up @@ -581,24 +590,6 @@ def from_withdrawal(cls, w: WithdrawalGeneric) -> Self:
return cls(**w.model_dump())


class WitnessChunk(CamelModel):
"""Represents execution witness data for a block."""

state: List[str]
codes: List[str]
keys: List[str]
headers: List[str]

@classmethod
def parse_witness_chunks(cls, s: str) -> List[Self]:
"""
Parse multiple witness chunks from JSON string.

Returns a list of WitnessChunk instances parsed from the JSON array.
"""
return [cls(**obj) for obj in json.loads(s)]


class FixtureBlockBase(CamelModel):
"""
Representation of an Ethereum block within a test Fixture without RLP
Expand All @@ -625,7 +616,7 @@ def strip_block_number_computed_field(cls, data: Any) -> Any:
default_factory=list, alias="uncleHeaders"
)
withdrawals: List[FixtureWithdrawal] | None = None
execution_witness: WitnessChunk | None = None
execution_witness: ExecutionWitness | None = None
fork: Fork | None = Field(None, exclude=True)

@computed_field(alias="blocknumber") # type: ignore[prop-decorator]
Expand Down
6 changes: 6 additions & 0 deletions packages/testing/src/execution_testing/specs/blockchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ def environment_from_parent_header(parent: "FixtureHeader") -> "Environment":
parent_gas_limit=parent.gas_limit,
parent_ommers_hash=parent.ommers_hash,
block_hashes={parent.number: parent.block_hash},
block_headers={parent.number: parent.rlp},
)


Expand All @@ -115,6 +116,9 @@ def apply_new_parent(
block_hashes = env.block_hashes.copy()
block_hashes[new_parent.number] = new_parent.block_hash
updated["block_hashes"] = block_hashes
block_headers = env.block_headers.copy()
block_headers[new_parent.number] = new_parent.rlp
updated["block_headers"] = block_headers
return env.copy(**updated)


Expand Down Expand Up @@ -380,6 +384,7 @@ def get_fixture_block(self) -> FixtureBlock | InvalidFixtureBlock:
if self.withdrawals is not None
else None
),
execution_witness=self.result.execution_witness,
fork=self.fork,
).with_rlp(txs=self.txs)

Expand Down Expand Up @@ -414,6 +419,7 @@ def get_fixture_engine_new_payload(self) -> FixtureEngineNewPayload:
else None,
validation_error=self.expected_exception,
error_code=self.engine_api_error_code,
execution_witness=self.result.execution_witness,
)

def verify_transactions(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,9 @@ def strip_computed_fields(cls, data: Any) -> Any:
parent_beacon_block_root: Hash | None = Field(None)

block_hashes: Dict[ZeroPaddedHexNumber, Hash] = Field(default_factory=dict)
block_headers: Dict[ZeroPaddedHexNumber, Bytes] = Field(
default_factory=dict
)
ommers: List[Hash] = Field(default_factory=list)
withdrawals: List[Withdrawal] | None = Field(None)
extra_data: Bytes = Field(Bytes(b"\x00"), exclude=True)
Expand Down
45 changes: 45 additions & 0 deletions src/ethereum/forks/osaka/fork.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,10 @@
increment_nonce,
modify_state,
set_account_balance,
set_witness_metadata,
state_root,
track_block_hash_access,
track_bytecode_access,
)
from .transactions import (
AccessListTransaction,
Expand Down Expand Up @@ -191,6 +194,36 @@ def get_last_256_block_hashes(chain: BlockChain) -> List[Hash32]:
return recent_block_hashes


def get_last_256_block_headers(chain: BlockChain) -> List[Bytes]:

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a helper method to feed the witness generator with the info that requires to add the ancestors in the header. In the worst case, it can need the last 256 ancestors -- it will include in the witness the minimal amount of them as required (see next code diff right after).

"""
Obtain the list of RLP-encoded headers of the previous 256 blocks.

This function will return less headers for the first 256 blocks.
The headers are parallel to the hashes from get_last_256_block_hashes.

Parameters
----------
chain :
History and current state.

Returns
-------
recent_block_headers : `List[Bytes]`
RLP-encoded headers of recent 256 blocks in order of increasing number.

"""
recent_blocks = chain.blocks[-256:]
if len(recent_blocks) == 0:
return []

recent_block_headers: List[Bytes] = []
for block in recent_blocks:
header_rlp = rlp.encode(block.header)
recent_block_headers.append(header_rlp)

return recent_block_headers


def state_transition(chain: BlockChain, block: Block) -> None:
"""
Attempts to apply a block to an existing block chain.
Expand Down Expand Up @@ -235,6 +268,13 @@ def state_transition(chain: BlockChain, block: Block) -> None:
parent_beacon_block_root=block.header.parent_beacon_block_root,
)

# Set witness metadata if tracking is enabled
set_witness_metadata(
block_env.state,
block.header.number,
get_last_256_block_headers(chain),
)

block_output = apply_body(
block_env=block_env,
transactions=block.transactions,
Expand Down Expand Up @@ -676,6 +716,7 @@ def process_checked_system_transaction(

"""
system_contract_code = get_account(block_env.state, target_address).code
track_bytecode_access(block_env.state, system_contract_code)

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

process_checked_system_transaction and process_unchecked_system_transaction are the two methods used in the spec to call system contracts.

In both cases, we track that the bytecode is required for the witness.


if len(system_contract_code) == 0:
raise InvalidBlock(
Expand Down Expand Up @@ -724,6 +765,7 @@ def process_unchecked_system_transaction(

"""
system_contract_code = get_account(block_env.state, target_address).code
track_bytecode_access(block_env.state, system_contract_code)
return process_system_transaction(
block_env,
target_address,
Expand Down Expand Up @@ -770,6 +812,9 @@ def apply_body(
data=block_env.parent_beacon_block_root,
)

# Track parent block access for witness (EIP-2935 system call)
track_block_hash_access(block_env.state, block_env.number - Uint(1))

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the 2935 system contract being called right below (L820) -- we track the need of requiring the parent block hash as expected.


process_unchecked_system_transaction(
block_env=block_env,
target_address=HISTORY_STORAGE_ADDRESS,
Expand Down
Loading
Loading