diff --git a/.gitignore b/.gitignore index 3e4413e97..3586b356c 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,7 @@ tests/core/pyspec/eth2spec/deneb/ tests/core/pyspec/eth2spec/electra/ tests/core/pyspec/eth2spec/whisk/ tests/core/pyspec/eth2spec/eip7594/ +tests/core/pyspec/eth2spec/eip6800/ # coverage reports .htmlcov diff --git a/Makefile b/Makefile index 8fbedf251..bdf4bdde7 100644 --- a/Makefile +++ b/Makefile @@ -35,7 +35,7 @@ MARKDOWN_FILES = $(wildcard $(SPEC_DIR)/*/*.md) \ $(wildcard $(SPEC_DIR)/_features/*/*/*.md) \ $(wildcard $(SSZ_DIR)/*.md) -ALL_EXECUTABLE_SPEC_NAMES = phase0 altair bellatrix capella deneb electra whisk +ALL_EXECUTABLE_SPEC_NAMES = phase0 altair bellatrix capella deneb electra whisk eip6800 # The parameters for commands. Use `foreach` to avoid listing specs again. COVERAGE_SCOPE := $(foreach S,$(ALL_EXECUTABLE_SPEC_NAMES), --cov=eth2spec.$S.$(TEST_PRESET_TYPE)) PYLINT_SCOPE := $(foreach S,$(ALL_EXECUTABLE_SPEC_NAMES), ./eth2spec/$S) diff --git a/presets/mainnet/eip6800.yaml b/presets/mainnet/eip6800.yaml new file mode 100644 index 000000000..d74ee6212 --- /dev/null +++ b/presets/mainnet/eip6800.yaml @@ -0,0 +1,12 @@ +# Mainnet preset - EIP6800 + +# Misc +# --------------------------------------------------------------- +# `uint64(2**16)` (= 65,536) +MAX_STEMS: 65536 +# `uint64(33)` +MAX_COMMITMENTS_PER_STEM: 33 +# `uint64(2**8)` (= 256) +VERKLE_WIDTH: 256 +# `uint64(2**3)` (= 8) +IPA_PROOF_DEPTH: 8 diff --git a/presets/minimal/eip6800.yaml b/presets/minimal/eip6800.yaml new file mode 100644 index 000000000..499721e4a --- /dev/null +++ b/presets/minimal/eip6800.yaml @@ -0,0 +1,12 @@ +# Minimal preset - EIP6800 + +# Execution +# --------------------------------------------------------------- +# `uint64(2**16)` (= 65,536) +MAX_STEMS: 65536 +# `uint64(33)` +MAX_COMMITMENTS_PER_STEM: 33 +# `uint64(2**8)` (= 256) +VERKLE_WIDTH: 256 +# `uint64(2**3)` (= 8) +IPA_PROOF_DEPTH: 8 diff --git a/pysetup/constants.py b/pysetup/constants.py index 0078b24dc..e26efb8e0 100644 --- a/pysetup/constants.py +++ b/pysetup/constants.py @@ -6,10 +6,10 @@ CAPELLA = 'capella' DENEB = 'deneb' ELECTRA = 'electra' EIP7594 = 'eip7594' +EIP6800 = 'eip6800' WHISK = 'whisk' - # The helper functions that are used when defining constants CONSTANT_DEP_SUNDRY_CONSTANTS_FUNCTIONS = ''' def ceillog2(x: int) -> uint64: diff --git a/pysetup/helpers.py b/pysetup/helpers.py index 49c0fcafc..589ae6ab5 100644 --- a/pysetup/helpers.py +++ b/pysetup/helpers.py @@ -178,7 +178,7 @@ def combine_dicts(old_dict: Dict[str, T], new_dict: Dict[str, T]) -> Dict[str, T ignored_dependencies = [ 'bit', 'boolean', 'Vector', 'List', 'Container', 'BLSPubkey', 'BLSSignature', - 'Bytes1', 'Bytes4', 'Bytes8', 'Bytes20', 'Bytes32', 'Bytes48', 'Bytes96', 'Bitlist', 'Bitvector', + 'Bytes1', 'Bytes4', 'Bytes8', 'Bytes20', 'Bytes31', 'Bytes32', 'Bytes48', 'Bytes96', 'Bitlist', 'Bitvector', 'uint8', 'uint16', 'uint32', 'uint64', 'uint128', 'uint256', 'bytes', 'byte', 'ByteList', 'ByteVector', 'Dict', 'dict', 'field', 'ceillog2', 'floorlog2', 'Set', diff --git a/pysetup/md_doc_paths.py b/pysetup/md_doc_paths.py index a4a730e5a..28ebc7137 100644 --- a/pysetup/md_doc_paths.py +++ b/pysetup/md_doc_paths.py @@ -9,6 +9,7 @@ from .constants import ( ELECTRA, WHISK, EIP7594, + EIP6800, ) @@ -21,6 +22,7 @@ PREVIOUS_FORK_OF = { ELECTRA: DENEB, WHISK: CAPELLA, EIP7594: DENEB, + EIP6800: DENEB, } ALL_FORKS = list(PREVIOUS_FORK_OF.keys()) diff --git a/pysetup/spec_builders/__init__.py b/pysetup/spec_builders/__init__.py index ea74b50b7..922cee18b 100644 --- a/pysetup/spec_builders/__init__.py +++ b/pysetup/spec_builders/__init__.py @@ -6,12 +6,13 @@ from .deneb import DenebSpecBuilder from .electra import ElectraSpecBuilder from .whisk import WhiskSpecBuilder from .eip7594 import EIP7594SpecBuilder +from .eip6800 import EIP6800SpecBuilder spec_builders = { builder.fork: builder for builder in ( Phase0SpecBuilder, AltairSpecBuilder, BellatrixSpecBuilder, CapellaSpecBuilder, DenebSpecBuilder, - ElectraSpecBuilder, WhiskSpecBuilder, EIP7594SpecBuilder, + ElectraSpecBuilder, WhiskSpecBuilder, EIP7594SpecBuilder, EIP6800SpecBuilder, ) } diff --git a/pysetup/spec_builders/eip6800.py b/pysetup/spec_builders/eip6800.py new file mode 100644 index 000000000..4ea76d6a2 --- /dev/null +++ b/pysetup/spec_builders/eip6800.py @@ -0,0 +1,21 @@ +from typing import Dict + +from .base import BaseSpecBuilder +from ..constants import EIP6800 + + +class EIP6800SpecBuilder(BaseSpecBuilder): + fork: str = EIP6800 + + @classmethod + def imports(cls, preset_name: str): + return f''' +from eth2spec.deneb import {preset_name} as deneb +from eth2spec.utils.ssz.ssz_typing import Bytes31 +''' + + @classmethod + def hardcoded_custom_type_dep_constants(cls, spec_object) -> str: + return { + 'MAX_STEMS': spec_object.preset_vars['MAX_STEMS'].value, + } diff --git a/setup.py b/setup.py index fe2250f7c..e5c348ada 100644 --- a/setup.py +++ b/setup.py @@ -219,7 +219,13 @@ def get_spec(file_name: Path, preset: Dict[str, str], config: Dict[str, str], pr elif source.startswith("class"): class_name, parent_class = _get_class_info_from_source(source) # check consistency with spec - assert class_name == current_name + try: + assert class_name == current_name + except Exception: + print('class_name', class_name) + print('current_name', current_name) + raise + if parent_class: assert parent_class == "Container" # NOTE: trim whitespace from spec diff --git a/specs/_features/eip6800/beacon-chain.md b/specs/_features/eip6800/beacon-chain.md index dd821e66b..8c93729b8 100644 --- a/specs/_features/eip6800/beacon-chain.md +++ b/specs/_features/eip6800/beacon-chain.md @@ -1,4 +1,4 @@ -# eip6800 -- The Beacon Chain +# EIP6800 -- The Beacon Chain ## Table of contents @@ -21,8 +21,6 @@ - [`VerkleProof`](#verkleproof) - [`ExecutionWitness`](#executionwitness) - [Beacon chain state transition function](#beacon-chain-state-transition-function) - - [Execution engine](#execution-engine) - - [`notify_new_payload`](#notify_new_payload) - [Block processing](#block-processing) - [Execution payload](#execution-payload) - [`process_execution_payload`](#process_execution_payload) @@ -39,7 +37,6 @@ This upgrade adds transaction execution to the beacon chain as part of the eip68 | Name | SSZ equivalent | Description | | - | - | - | -| `StateDiff` | `List[StemStateDiff, MAX_STEMS]` | Only valid if list is sorted by stems | | `BanderwagonGroupElement` | `Bytes32` | | | `BanderwagonFieldElement` | `Bytes32` | | | `Stem` | `Bytes31` | | @@ -50,10 +47,10 @@ This upgrade adds transaction execution to the beacon chain as part of the eip68 | Name | Value | | - | - | -| `MAX_STEMS` | `2**16` | -| `MAX_COMMITMENTS_PER_STEM` | `33` | -| `VERKLE_WIDTH` | `256` | -| `IPA_PROOF_DEPTH` | `8` | +| `MAX_STEMS` | `uint64(2**16)` (= 65,536) | +| `MAX_COMMITMENTS_PER_STEM` | `uint64(33)` | +| `VERKLE_WIDTH` | `uint64(2**8)` (= 256) | +| `IPA_PROOF_DEPTH` | `uint64(2**3)` (= 8) | ## Containers @@ -80,7 +77,9 @@ class ExecutionPayload(Container): block_hash: Hash32 # Hash of execution block transactions: List[Transaction, MAX_TRANSACTIONS_PER_PAYLOAD] withdrawals: List[Withdrawal, MAX_WITHDRAWALS_PER_PAYLOAD] - execution_witness: ExecutionWitness # [New in eip6800] + blob_gas_used: uint64 + excess_blob_gas: uint64 + execution_witness: ExecutionWitness # [New in EIP6800] ``` #### `ExecutionPayloadHeader` @@ -104,8 +103,9 @@ class ExecutionPayloadHeader(Container): block_hash: Hash32 # Hash of execution block transactions_root: Root withdrawals_root: Root - excess_data_gas: uint256 - execution_witness_root: Root # [New in eip6800] + blob_gas_used: uint64 + excess_data_gas: uint64 + execution_witness_root: Root # [New in EIP6800] ``` ### New containers @@ -114,7 +114,7 @@ class ExecutionPayloadHeader(Container): ```python class SuffixStateDiff(Container): - suffix: Byte + suffix: Bytes1 # Null means not currently present current_value: Optional[Bytes32] # Null means value not updated @@ -132,15 +132,10 @@ class StemStateDiff(Container): suffix_diffs: List[SuffixStateDiff, VERKLE_WIDTH] ``` -```python -# Valid only if list is sorted by stems -StateDiff = List[StemStateDiff, MAX_STEMS] -``` - #### `IPAProof` ```python -class IpaProof(Container): +class IPAProof(Container): cl: Vector[BanderwagonGroupElement, IPA_PROOF_DEPTH] cr: Vector[BanderwagonGroupElement, IPA_PROOF_DEPTH] final_evaluation = BanderwagonFieldElement @@ -154,14 +149,14 @@ class VerkleProof(Container): depth_extension_present: ByteList[MAX_STEMS] commitments_by_path: List[BanderwagonGroupElement, MAX_STEMS * MAX_COMMITMENTS_PER_STEM] d: BanderwagonGroupElement - ipa_proof: IpaProof + ipa_proof: IPAProof ``` #### `ExecutionWitness` ```python -class ExecutionWitness(container): - state_diff: StateDiff +class ExecutionWitness(Container): + state_diff: List[StemStateDiff, MAX_STEMS] verkle_proof: VerkleProof ``` @@ -174,16 +169,30 @@ class ExecutionWitness(container): ##### `process_execution_payload` ```python -def process_execution_payload(state: BeaconState, payload: ExecutionPayload, execution_engine: ExecutionEngine) -> None: +def process_execution_payload(state: BeaconState, body: BeaconBlockBody, execution_engine: ExecutionEngine) -> None: + payload = body.execution_payload + # Verify consistency of the parent hash with respect to the previous execution payload header - if is_merge_transition_complete(state): - assert payload.parent_hash == state.latest_execution_payload_header.block_hash + assert payload.parent_hash == state.latest_execution_payload_header.block_hash # Verify prev_randao assert payload.prev_randao == get_randao_mix(state, get_current_epoch(state)) # Verify timestamp assert payload.timestamp == compute_timestamp_at_slot(state, state.slot) + + # Verify commitments are under limit + assert len(body.blob_kzg_commitments) <= MAX_BLOBS_PER_BLOCK + # Verify the execution payload is valid - assert execution_engine.notify_new_payload(payload) + # Pass `versioned_hashes` to Execution Engine + # Pass `parent_beacon_block_root` to Execution Engine + versioned_hashes = [kzg_commitment_to_versioned_hash(commitment) for commitment in body.blob_kzg_commitments] + assert execution_engine.verify_and_notify_new_payload( + NewPayloadRequest( + execution_payload=payload, + versioned_hashes=versioned_hashes, + parent_beacon_block_root=state.latest_block_header.parent_root, + ) + ) # Cache execution payload header state.latest_execution_payload_header = ExecutionPayloadHeader( @@ -203,7 +212,7 @@ def process_execution_payload(state: BeaconState, payload: ExecutionPayload, exe transactions_root=hash_tree_root(payload.transactions), withdrawals_root=hash_tree_root(payload.withdrawals), excess_data_gas=payload.excess_data_gas, - execution_witness=payload.execution_witness, # [New in eip6800] + execution_witness=payload.execution_witness, # [New in EIP6800] ) ``` diff --git a/specs/_features/eip6800/fork.md b/specs/_features/eip6800/fork.md index 1edbe4d60..9c6df2f16 100644 --- a/specs/_features/eip6800/fork.md +++ b/specs/_features/eip6800/fork.md @@ -1,4 +1,4 @@ -# eip6800 -- Fork Logic +# EIP-6800 -- Fork Logic ## Table of contents @@ -72,7 +72,7 @@ Care must be taken when transitioning through the fork boundary as implementatio In particular, the outer `state_transition` function defined in the Phase 0 document will not expose the precise fork slot to execute the upgrade in the presence of skipped slots at the fork boundary. Instead, the logic must be within `process_slots`. ```python -def upgrade_to_eip6800(pre: capella.BeaconState) -> BeaconState: +def upgrade_to_eip6800(pre: deneb.BeaconState) -> BeaconState: epoch = capella.get_current_epoch(pre) latest_execution_payload_header = ExecutionPayloadHeader( parent_hash=pre.latest_execution_payload_header.parent_hash, @@ -91,7 +91,7 @@ def upgrade_to_eip6800(pre: capella.BeaconState) -> BeaconState: block_hash=pre.latest_execution_payload_header.block_hash, transactions_root=pre.latest_execution_payload_header.transactions_root, withdrawals_root=pre.latest_execution_payload_header.withdrawals_root, - execution_witness=ExecutionWitness([], []) # New in eip6800 + execution_witness=ExecutionWitness([], []) # New in eip6800 ) post = BeaconState( # Versioning diff --git a/tests/core/pyspec/eth2spec/utils/ssz/ssz_typing.py b/tests/core/pyspec/eth2spec/utils/ssz/ssz_typing.py index 5a1b61d0b..1f3db2fe0 100644 --- a/tests/core/pyspec/eth2spec/utils/ssz/ssz_typing.py +++ b/tests/core/pyspec/eth2spec/utils/ssz/ssz_typing.py @@ -10,3 +10,4 @@ from remerkleable.core import BasicView, View, Path Bytes20 = ByteVector[20] # type: ignore +Bytes31 = ByteVector[31] # type: ignore