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
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,7 @@ ethereum-spec-sync = "ethereum_spec_tools.sync:main"
ethereum-spec-new-fork = "ethereum_spec_tools.new_fork.cli:main"
ethereum-spec-patch = "ethereum_spec_tools.patch_tool:main"
ethereum-spec-evm = "ethereum_spec_tools.evm_tools:main"
ethereum-spec-engine = "ethereum_spec_tools.engine_server:main"
whitelist = "ethereum_spec_tools.whitelist:main"

[project.entry-points."docc.plugins"]
Expand Down
14 changes: 14 additions & 0 deletions src/ethereum/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,17 @@ class NonceOverflowError(InvalidTransaction):
"""
Thrown when a transaction's nonce is greater than `2**64 - 2`.
"""


class UnsupportedForkError(EthereumException):
"""
Thrown when an Engine API method version does not serve the fork
that the request belongs to.
"""


class InvalidEngineParamsError(EthereumException):
"""
Thrown when an Engine API request carries structurally invalid
parameters, such as malformed execution requests.
"""
108 changes: 108 additions & 0 deletions src/ethereum/forks/amsterdam/execution_engine/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
"""
The [Engine API] under the Amsterdam fork.

The consensus layer drives the execution layer through versioned
methods — `engine_newPayloadV1`…`V5`, `engine_forkchoiceUpdatedV1`…`V4`, `engine_getPayloadV1`…`V6` — each a thin
wrapper over the shared validation and forkchoice cores. Versions are
additive: every version up to the newest exists here, and versions
superseded by Amsterdam answer with an unsupported-fork error, so this
module is the complete engine surface a Amsterdam client presents.

[Engine API]: https://github.com/ethereum/execution-apis/blob/main/src/engine/amsterdam.md
""" # noqa: E501

from .forkchoice_update import (
forkchoice_updated_v1,
forkchoice_updated_v2,
forkchoice_updated_v3,
forkchoice_updated_v4,
notify_forkchoice_updated,
)
from .get_payload import (
get_payload_v1,
get_payload_v2,
get_payload_v3,
get_payload_v4,
get_payload_v5,
get_payload_v6,
)
from .new_payload import (
is_valid_block_hash,
is_valid_versioned_hashes,
new_payload_v1,
new_payload_v2,
new_payload_v3,
new_payload_v4,
new_payload_v5,
validate_execution_requests,
verify_and_notify_new_payload,
)
from .types import (
BlobsBundleV1,
BlobsBundleV2,
ExecutionEngine,
ExecutionPayloadV1,
ExecutionPayloadV2,
ExecutionPayloadV3,
ExecutionPayloadV4,
ForkchoiceStateV1,
ForkchoiceUpdatedResponse,
GetPayloadResponseV2,
GetPayloadResponseV3,
GetPayloadResponseV4,
GetPayloadResponseV5,
GetPayloadResponseV6,
PayloadAttributesV1,
PayloadAttributesV2,
PayloadAttributesV3,
PayloadAttributesV4,
PayloadId,
PayloadStatus,
PayloadStatusV1,
create_execution_engine,
)

__all__ = [
"BlobsBundleV1",
"BlobsBundleV2",
"ExecutionEngine",
"ExecutionPayloadV1",
"ExecutionPayloadV2",
"ExecutionPayloadV3",
"ExecutionPayloadV4",
"ForkchoiceStateV1",
"ForkchoiceUpdatedResponse",
"GetPayloadResponseV2",
"GetPayloadResponseV3",
"GetPayloadResponseV4",
"GetPayloadResponseV5",
"GetPayloadResponseV6",
"PayloadAttributesV1",
"PayloadAttributesV2",
"PayloadAttributesV3",
"PayloadAttributesV4",
"PayloadId",
"PayloadStatus",
"PayloadStatusV1",
"create_execution_engine",
"forkchoice_updated_v1",
"forkchoice_updated_v2",
"forkchoice_updated_v3",
"forkchoice_updated_v4",
"get_payload_v1",
"get_payload_v2",
"get_payload_v3",
"get_payload_v4",
"get_payload_v5",
"get_payload_v6",
"is_valid_block_hash",
"is_valid_versioned_hashes",
"new_payload_v1",
"new_payload_v2",
"new_payload_v3",
"new_payload_v4",
"new_payload_v5",
"notify_forkchoice_updated",
"validate_execution_requests",
"verify_and_notify_new_payload",
]
143 changes: 143 additions & 0 deletions src/ethereum/forks/amsterdam/execution_engine/forkchoice_update.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
"""
The `engine_forkchoiceUpdated` family of methods.

The consensus layer selects the canonical head among the validated
blocks; the safe and finalized hashes carry no execution semantics in
this model and clients use them for pruning and reorg limits.
"""

from typing import Optional

from ethereum_rlp import rlp
from ethereum_types.bytes import Bytes

from ethereum.crypto.hash import Hash32, keccak256

from .types import (
ExecutionEngine,
ForkchoiceStateV1,
ForkchoiceUpdatedResponse,
PayloadAttributesV1,
PayloadAttributesV2,
PayloadAttributesV3,
PayloadAttributesV4,
PayloadStatus,
PayloadStatusV1,
)
from .validation_helpers import chain_of


def notify_forkchoice_updated(
engine: ExecutionEngine, head_block_hash: Hash32
) -> PayloadStatusV1:
"""
Make the validated block `head_block_hash` the canonical head.

The canonical chain becomes the ancestry of the chosen head,
re-executed from genesis. A head that never passed payload
validation cannot be adopted and reports `SYNCING`.
"""
if head_block_hash not in engine.validated_blocks:
return PayloadStatusV1(
status=PayloadStatus.SYNCING,
latest_valid_hash=None,
validation_error=None,
)

current_head = keccak256(rlp.encode(engine.chain.blocks[-1].header))
if head_block_hash != current_head:
engine.chain = chain_of(engine, head_block_hash)

return PayloadStatusV1(
status=PayloadStatus.VALID,
latest_valid_hash=head_block_hash,
validation_error=None,
)


def forkchoice_updated_v1(
engine: ExecutionEngine,
forkchoice_state: ForkchoiceStateV1,
payload_attributes: Optional[PayloadAttributesV1],
) -> ForkchoiceUpdatedResponse:
"""
`engine_forkchoiceUpdatedV1`: adopt the given head.

Payload building (non-`None` attributes) is not implemented.
"""
if payload_attributes is not None:
raise NotImplementedError

return ForkchoiceUpdatedResponse(
payload_status=notify_forkchoice_updated(
engine, forkchoice_state.head_block_hash
),
payload_id=None,
)


def forkchoice_updated_v2(
engine: ExecutionEngine,
forkchoice_state: ForkchoiceStateV1,
payload_attributes: Optional[PayloadAttributesV2],
) -> ForkchoiceUpdatedResponse:
"""
`engine_forkchoiceUpdatedV2`: adopt the given head.

Payload building (non-`None` attributes) is not implemented.
"""
if payload_attributes is not None:
raise NotImplementedError

return ForkchoiceUpdatedResponse(
payload_status=notify_forkchoice_updated(
engine, forkchoice_state.head_block_hash
),
payload_id=None,
)


def forkchoice_updated_v3(
engine: ExecutionEngine,
forkchoice_state: ForkchoiceStateV1,
payload_attributes: Optional[PayloadAttributesV3],
) -> ForkchoiceUpdatedResponse:
"""
`engine_forkchoiceUpdatedV3`: adopt the given head.

Payload building (non-`None` attributes) is not implemented.
"""
if payload_attributes is not None:
raise NotImplementedError

return ForkchoiceUpdatedResponse(
payload_status=notify_forkchoice_updated(
engine, forkchoice_state.head_block_hash
),
payload_id=None,
)


def forkchoice_updated_v4(
engine: ExecutionEngine,
forkchoice_state: ForkchoiceStateV1,
payload_attributes: Optional[PayloadAttributesV4],
_custody_columns: Optional[Bytes],
) -> ForkchoiceUpdatedResponse:
"""
`engine_forkchoiceUpdatedV4`: adopt the given head.

The custody-column bitmap carries no execution
semantics and is accepted as-is.

Payload building (non-`None` attributes) is not implemented.
"""
if payload_attributes is not None:
raise NotImplementedError

return ForkchoiceUpdatedResponse(
payload_status=notify_forkchoice_updated(
engine, forkchoice_state.head_block_hash
),
payload_id=None,
)
82 changes: 82 additions & 0 deletions src/ethereum/forks/amsterdam/execution_engine/get_payload.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
"""
The `engine_getPayload` family of methods.

Payload building is outside this specification; the method signatures
and response structures document the interface.
"""

from .types import (
ExecutionPayloadV1,
GetPayloadResponseV2,
GetPayloadResponseV3,
GetPayloadResponseV4,
GetPayloadResponseV5,
GetPayloadResponseV6,
PayloadId,
)


def get_payload_v1(_payload_id: PayloadId) -> ExecutionPayloadV1:
"""
`engine_getPayloadV1`: return a payload built for a previously
returned [`PayloadId`]. Payload building is not implemented.

[`PayloadId`]:
ref:ethereum.forks.amsterdam.execution_engine.types.PayloadId
"""
raise NotImplementedError


def get_payload_v2(_payload_id: PayloadId) -> GetPayloadResponseV2:
"""
`engine_getPayloadV2`: return a payload built for a previously
returned [`PayloadId`]. Payload building is not implemented.

[`PayloadId`]:
ref:ethereum.forks.amsterdam.execution_engine.types.PayloadId
"""
raise NotImplementedError


def get_payload_v3(_payload_id: PayloadId) -> GetPayloadResponseV3:
"""
`engine_getPayloadV3`: return a payload built for a previously
returned [`PayloadId`]. Payload building is not implemented.

[`PayloadId`]:
ref:ethereum.forks.amsterdam.execution_engine.types.PayloadId
"""
raise NotImplementedError


def get_payload_v4(_payload_id: PayloadId) -> GetPayloadResponseV4:
"""
`engine_getPayloadV4`: return a payload built for a previously
returned [`PayloadId`]. Payload building is not implemented.

[`PayloadId`]:
ref:ethereum.forks.amsterdam.execution_engine.types.PayloadId
"""
raise NotImplementedError


def get_payload_v5(_payload_id: PayloadId) -> GetPayloadResponseV5:
"""
`engine_getPayloadV5`: return a payload built for a previously
returned [`PayloadId`]. Payload building is not implemented.

[`PayloadId`]:
ref:ethereum.forks.amsterdam.execution_engine.types.PayloadId
"""
raise NotImplementedError


def get_payload_v6(_payload_id: PayloadId) -> GetPayloadResponseV6:
"""
`engine_getPayloadV6`: return a payload built for a previously
returned [`PayloadId`]. Payload building is not implemented.

[`PayloadId`]:
ref:ethereum.forks.amsterdam.execution_engine.types.PayloadId
"""
raise NotImplementedError
Loading
Loading