Merge pull request #3230 from gballet/gballet/add-proof-verge

TheVerge: spec draft
This commit is contained in:
Hsiao-Wei Wang 2024-05-28 21:43:27 +08:00 committed by GitHub
commit 50972f9bf9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 427 additions and 5 deletions

1
.gitignore vendored
View File

@ -24,6 +24,7 @@ tests/core/pyspec/eth2spec/deneb/
tests/core/pyspec/eth2spec/electra/ tests/core/pyspec/eth2spec/electra/
tests/core/pyspec/eth2spec/whisk/ tests/core/pyspec/eth2spec/whisk/
tests/core/pyspec/eth2spec/eip7594/ tests/core/pyspec/eth2spec/eip7594/
tests/core/pyspec/eth2spec/eip6800/
# coverage reports # coverage reports
.htmlcov .htmlcov

View File

@ -35,7 +35,7 @@ MARKDOWN_FILES = $(wildcard $(SPEC_DIR)/*/*.md) \
$(wildcard $(SPEC_DIR)/_features/*/*/*.md) \ $(wildcard $(SPEC_DIR)/_features/*/*/*.md) \
$(wildcard $(SSZ_DIR)/*.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. # 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)) COVERAGE_SCOPE := $(foreach S,$(ALL_EXECUTABLE_SPEC_NAMES), --cov=eth2spec.$S.$(TEST_PRESET_TYPE))
PYLINT_SCOPE := $(foreach S,$(ALL_EXECUTABLE_SPEC_NAMES), ./eth2spec/$S) PYLINT_SCOPE := $(foreach S,$(ALL_EXECUTABLE_SPEC_NAMES), ./eth2spec/$S)

View File

@ -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

View File

@ -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

View File

@ -6,10 +6,10 @@ CAPELLA = 'capella'
DENEB = 'deneb' DENEB = 'deneb'
ELECTRA = 'electra' ELECTRA = 'electra'
EIP7594 = 'eip7594' EIP7594 = 'eip7594'
EIP6800 = 'eip6800'
WHISK = 'whisk' WHISK = 'whisk'
# The helper functions that are used when defining constants # The helper functions that are used when defining constants
CONSTANT_DEP_SUNDRY_CONSTANTS_FUNCTIONS = ''' CONSTANT_DEP_SUNDRY_CONSTANTS_FUNCTIONS = '''
def ceillog2(x: int) -> uint64: def ceillog2(x: int) -> uint64:

View File

@ -178,7 +178,7 @@ def combine_dicts(old_dict: Dict[str, T], new_dict: Dict[str, T]) -> Dict[str, T
ignored_dependencies = [ ignored_dependencies = [
'bit', 'boolean', 'Vector', 'List', 'Container', 'BLSPubkey', 'BLSSignature', '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', 'uint8', 'uint16', 'uint32', 'uint64', 'uint128', 'uint256',
'bytes', 'byte', 'ByteList', 'ByteVector', 'bytes', 'byte', 'ByteList', 'ByteVector',
'Dict', 'dict', 'field', 'ceillog2', 'floorlog2', 'Set', 'Dict', 'dict', 'field', 'ceillog2', 'floorlog2', 'Set',

View File

@ -9,6 +9,7 @@ from .constants import (
ELECTRA, ELECTRA,
WHISK, WHISK,
EIP7594, EIP7594,
EIP6800,
) )
@ -21,6 +22,7 @@ PREVIOUS_FORK_OF = {
ELECTRA: DENEB, ELECTRA: DENEB,
WHISK: CAPELLA, WHISK: CAPELLA,
EIP7594: DENEB, EIP7594: DENEB,
EIP6800: DENEB,
} }
ALL_FORKS = list(PREVIOUS_FORK_OF.keys()) ALL_FORKS = list(PREVIOUS_FORK_OF.keys())

View File

@ -6,12 +6,13 @@ from .deneb import DenebSpecBuilder
from .electra import ElectraSpecBuilder from .electra import ElectraSpecBuilder
from .whisk import WhiskSpecBuilder from .whisk import WhiskSpecBuilder
from .eip7594 import EIP7594SpecBuilder from .eip7594 import EIP7594SpecBuilder
from .eip6800 import EIP6800SpecBuilder
spec_builders = { spec_builders = {
builder.fork: builder builder.fork: builder
for builder in ( for builder in (
Phase0SpecBuilder, AltairSpecBuilder, BellatrixSpecBuilder, CapellaSpecBuilder, DenebSpecBuilder, Phase0SpecBuilder, AltairSpecBuilder, BellatrixSpecBuilder, CapellaSpecBuilder, DenebSpecBuilder,
ElectraSpecBuilder, WhiskSpecBuilder, EIP7594SpecBuilder, ElectraSpecBuilder, WhiskSpecBuilder, EIP7594SpecBuilder, EIP6800SpecBuilder,
) )
} }

View File

@ -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,
}

View File

@ -219,7 +219,13 @@ def get_spec(file_name: Path, preset: Dict[str, str], config: Dict[str, str], pr
elif source.startswith("class"): elif source.startswith("class"):
class_name, parent_class = _get_class_info_from_source(source) class_name, parent_class = _get_class_info_from_source(source)
# check consistency with spec # 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: if parent_class:
assert parent_class == "Container" assert parent_class == "Container"
# NOTE: trim whitespace from spec # NOTE: trim whitespace from spec

View File

@ -0,0 +1,221 @@
# EIP6800 -- The Beacon Chain
## Table of contents
<!-- TOC -->
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
- [Introduction](#introduction)
- [Custom types](#custom-types)
- [Preset](#preset)
- [Execution](#execution)
- [Containers](#containers)
- [Extended containers](#extended-containers)
- [`ExecutionPayload`](#executionpayload)
- [`ExecutionPayloadHeader`](#executionpayloadheader)
- [New containers](#new-containers)
- [`SuffixStateDiff`](#suffixstatediff)
- [`StemStateDiff`](#stemstatediff)
- [`IPAProof`](#ipaproof)
- [`VerkleProof`](#verkleproof)
- [`ExecutionWitness`](#executionwitness)
- [Beacon chain state transition function](#beacon-chain-state-transition-function)
- [Block processing](#block-processing)
- [Execution payload](#execution-payload)
- [`process_execution_payload`](#process_execution_payload)
- [Testing](#testing)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
<!-- /TOC -->
## Introduction
This upgrade adds transaction execution to the beacon chain as part of the eip6800 upgrade.
## Custom types
| Name | SSZ equivalent | Description |
| - | - | - |
| `BanderwagonGroupElement` | `Bytes32` | |
| `BanderwagonFieldElement` | `Bytes32` | |
| `Stem` | `Bytes31` | |
## Preset
### Execution
| Name | Value |
| - | - |
| `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
### Extended containers
#### `ExecutionPayload`
```python
class ExecutionPayload(Container):
# Execution block header fields
parent_hash: Hash32
fee_recipient: ExecutionAddress # 'beneficiary' in the yellow paper
state_root: Bytes32
receipts_root: Bytes32
logs_bloom: ByteVector[BYTES_PER_LOGS_BLOOM]
prev_randao: Bytes32 # 'difficulty' in the yellow paper
block_number: uint64 # 'number' in the yellow paper
gas_limit: uint64
gas_used: uint64
timestamp: uint64
extra_data: ByteList[MAX_EXTRA_DATA_BYTES]
base_fee_per_gas: uint256
# Extra payload fields
block_hash: Hash32 # Hash of execution block
transactions: List[Transaction, MAX_TRANSACTIONS_PER_PAYLOAD]
withdrawals: List[Withdrawal, MAX_WITHDRAWALS_PER_PAYLOAD]
blob_gas_used: uint64
excess_blob_gas: uint64
execution_witness: ExecutionWitness # [New in EIP6800]
```
#### `ExecutionPayloadHeader`
```python
class ExecutionPayloadHeader(Container):
# Execution block header fields
parent_hash: Hash32
fee_recipient: ExecutionAddress
state_root: Bytes32
receipts_root: Bytes32
logs_bloom: ByteVector[BYTES_PER_LOGS_BLOOM]
prev_randao: Bytes32
block_number: uint64
gas_limit: uint64
gas_used: uint64
timestamp: uint64
extra_data: ByteList[MAX_EXTRA_DATA_BYTES]
base_fee_per_gas: uint256
# Extra payload fields
block_hash: Hash32 # Hash of execution block
transactions_root: Root
withdrawals_root: Root
blob_gas_used: uint64
excess_data_gas: uint64
execution_witness_root: Root # [New in EIP6800]
```
### New containers
#### `SuffixStateDiff`
```python
class SuffixStateDiff(Container):
suffix: Bytes1
# Null means not currently present
current_value: Optional[Bytes32]
# Null means value not updated
new_value: Optional[Bytes32]
```
*Note*: on the Kaustinen testnet, `new_value` is omitted from the container.
#### `StemStateDiff`
```python
class StemStateDiff(Container):
stem: Stem
# Valid only if list is sorted by suffixes
suffix_diffs: List[SuffixStateDiff, VERKLE_WIDTH]
```
#### `IPAProof`
```python
class IPAProof(Container):
cl: Vector[BanderwagonGroupElement, IPA_PROOF_DEPTH]
cr: Vector[BanderwagonGroupElement, IPA_PROOF_DEPTH]
final_evaluation = BanderwagonFieldElement
```
#### `VerkleProof`
```python
class VerkleProof(Container):
other_stems: List[Bytes31, MAX_STEMS]
depth_extension_present: ByteList[MAX_STEMS]
commitments_by_path: List[BanderwagonGroupElement, MAX_STEMS * MAX_COMMITMENTS_PER_STEM]
d: BanderwagonGroupElement
ipa_proof: IPAProof
```
#### `ExecutionWitness`
```python
class ExecutionWitness(Container):
state_diff: List[StemStateDiff, MAX_STEMS]
verkle_proof: VerkleProof
```
## Beacon chain state transition function
### Block processing
#### Execution payload
##### `process_execution_payload`
```python
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
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
# 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(
parent_hash=payload.parent_hash,
fee_recipient=payload.fee_recipient,
state_root=payload.state_root,
receipts_root=payload.receipts_root,
logs_bloom=payload.logs_bloom,
prev_randao=payload.prev_randao,
block_number=payload.block_number,
gas_limit=payload.gas_limit,
gas_used=payload.gas_used,
timestamp=payload.timestamp,
extra_data=payload.extra_data,
base_fee_per_gas=payload.base_fee_per_gas,
block_hash=payload.block_hash,
transactions_root=hash_tree_root(payload.transactions),
withdrawals_root=hash_tree_root(payload.withdrawals),
excess_data_gas=payload.excess_data_gas,
execution_witness_root=hash_tree_root(payload.execution_witness), # [New in EIP6800]
)
```
## Testing
TBD

View File

@ -0,0 +1,145 @@
# EIP-6800 -- Fork Logic
## Table of contents
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
- [Introduction](#introduction)
- [Configuration](#configuration)
- [Helper functions](#helper-functions)
- [Misc](#misc)
- [Modified `compute_fork_version`](#modified-compute_fork_version)
- [Fork to eip6800](#fork-to-eip6800)
- [Fork trigger](#fork-trigger)
- [Upgrading the state](#upgrading-the-state)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
## Introduction
This document describes the process of the eip6800 upgrade.
## Configuration
Warning: this configuration is not definitive.
| Name | Value |
| - | - |
| `EIP6800_FORK_VERSION` | `Version('0x05000000')` |
| `EIP6800_FORK_EPOCH` | `Epoch(18446744073709551615)` **TBD** |
## Helper functions
### Misc
#### Modified `compute_fork_version`
```python
def compute_fork_version(epoch: Epoch) -> Version:
"""
Return the fork version at the given ``epoch``.
"""
if epoch >= EIP6800_FORK_EPOCH:
return EIP6800_FORK_VERSION
if epoch >= DENEB_FORK_EPOCH:
return DENEB_FORK_VERSION
if epoch >= CAPELLA_FORK_EPOCH:
return CAPELLA_FORK_VERSION
if epoch >= BELLATRIX_FORK_EPOCH:
return BELLATRIX_FORK_VERSION
if epoch >= ALTAIR_FORK_EPOCH:
return ALTAIR_FORK_VERSION
return GENESIS_FORK_VERSION
```
## Fork to eip6800
### Fork trigger
The fork is triggered at epoch `EIP6800_FORK_EPOCH`.
Note that for the pure eip6800 networks, we don't apply `upgrade_to_eip6800` since it starts with the eip6800 version logic.
### Upgrading the state
If `state.slot % SLOTS_PER_EPOCH == 0` and `compute_epoch_at_slot(state.slot) == EIP6800_FORK_EPOCH`,
an irregular state change is made to upgrade to eip6800.
The upgrade occurs after the completion of the inner loop of `process_slots` that sets `state.slot` equal to `EIP6800_FORK_EPOCH * SLOTS_PER_EPOCH`.
Care must be taken when transitioning through the fork boundary as implementations will need a modified [state transition function](../phase0/beacon-chain.md#beacon-chain-state-transition-function) that deviates from the Phase 0 document.
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: deneb.BeaconState) -> BeaconState:
epoch = capella.get_current_epoch(pre)
latest_execution_payload_header = ExecutionPayloadHeader(
parent_hash=pre.latest_execution_payload_header.parent_hash,
fee_recipient=pre.latest_execution_payload_header.fee_recipient,
state_root=pre.latest_execution_payload_header.state_root,
receipts_root=pre.latest_execution_payload_header.receipts_root,
logs_bloom=pre.latest_execution_payload_header.logs_bloom,
prev_randao=pre.latest_execution_payload_header.prev_randao,
block_number=pre.latest_execution_payload_header.block_number,
gas_limit=pre.latest_execution_payload_header.gas_limit,
gas_used=pre.latest_execution_payload_header.gas_used,
timestamp=pre.latest_execution_payload_header.timestamp,
extra_data=pre.latest_execution_payload_header.extra_data,
base_fee_per_gas=pre.latest_execution_payload_header.base_fee_per_gas,
excess_data_gas=uint256(0),
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_root=hash_tree_root(ExecutionWitness([], [])) # New in eip6800
)
post = BeaconState(
# Versioning
genesis_time=pre.genesis_time,
genesis_validators_root=pre.genesis_validators_root,
slot=pre.slot,
fork=Fork(
previous_version=pre.fork.current_version,
current_version=EIP6800_FORK_VERSION, # [Modified in eip6800]
epoch=epoch,
),
# History
latest_block_header=pre.latest_block_header,
block_roots=pre.block_roots,
state_roots=pre.state_roots,
historical_roots=pre.historical_roots,
# Eth1
eth1_data=pre.eth1_data,
eth1_data_votes=pre.eth1_data_votes,
eth1_deposit_index=pre.eth1_deposit_index,
# Registry
validators=pre.validators,
balances=pre.balances,
# Randomness
randao_mixes=pre.randao_mixes,
# Slashings
slashings=pre.slashings,
# Participation
previous_epoch_participation=pre.previous_epoch_participation,
current_epoch_participation=pre.current_epoch_participation,
# Finality
justification_bits=pre.justification_bits,
previous_justified_checkpoint=pre.previous_justified_checkpoint,
current_justified_checkpoint=pre.current_justified_checkpoint,
finalized_checkpoint=pre.finalized_checkpoint,
# Inactivity
inactivity_scores=pre.inactivity_scores,
# Sync
current_sync_committee=pre.current_sync_committee,
next_sync_committee=pre.next_sync_committee,
# Execution-layer
latest_execution_payload_header=latest_execution_payload_header,
# Withdrawals
next_withdrawal_index=pre.next_withdrawal_index,
next_withdrawal_validator_index=pre.next_withdrawal_validator_index,
# Deep history valid from Capella onwards
historical_summaries=pre.historical_summaries,
)
return post
```

View File

@ -10,3 +10,4 @@ from remerkleable.core import BasicView, View, Path
Bytes20 = ByteVector[20] # type: ignore Bytes20 = ByteVector[20] # type: ignore
Bytes31 = ByteVector[31] # type: ignore