Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add EIP-7805 (FOCIL) specs #4003

Open
wants to merge 45 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 41 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
3a16a0e
Add focil containers
terencechain Sep 11, 2024
fa27c8f
Add process inclusion list aggregate
terencechain Sep 12, 2024
3c86963
Add inclusion list gossip conditions
terencechain Sep 14, 2024
8a1e940
Add execution api
terencechain Sep 15, 2024
833be94
Add validator spec
terencechain Sep 16, 2024
a2b68a7
Review feedback
terencechain Sep 17, 2024
fb4a3c3
Update forkchoice
terencechain Sep 17, 2024
c7a056b
Update based on latest discussion. No more IL aggregate
terencechain Sep 21, 2024
f84556c
Update fork-choice.md
terencechain Sep 26, 2024
2787b0d
Update per https://ethresear.ch/t/focil-cl-el-workflow/20526
terencechain Sep 30, 2024
0b09f96
Address feedback
terencechain Oct 2, 2024
6056b69
Clarify forkchoice and p2p
terencechain Oct 12, 2024
a8aec7c
Remove local
terencechain Oct 17, 2024
cbe6129
suggested changes
fradamt Oct 22, 2024
0efcf13
Merge pull request #5 from fradamt/focil-patch
terencechain Oct 22, 2024
f151c7c
replace parent_root with il_committee_root
fradamt Oct 22, 2024
b181655
add timing logic (deaadlines) to IL processing
fradamt Oct 23, 2024
0681f8c
missing :
fradamt Oct 25, 2024
2861684
Merge pull request #6 from fradamt/focil-patch
terencechain Oct 29, 2024
e678deb
Use eip7805
terencechain Nov 4, 2024
e9fdfc6
@jtraglia's suggestions
terencechain Nov 8, 2024
6ce0077
Update new inclusion list comments
terencechain Nov 19, 2024
be471b8
P2p spec feedbacks
terencechain Nov 19, 2024
c443db0
Add get attester head
terencechain Nov 19, 2024
c8b6296
Fix typo and make CI happy
hwwhww Dec 2, 2024
7fa44f4
fix byte limit check
fradamt Dec 16, 2024
84acfbb
ensure valid committee with <16 validators
nerolation Dec 30, 2024
702b9e5
Merge pull request #7 from nerolation/patch-1
terencechain Jan 2, 2025
561aed7
Merge branch 'dev' into focil-consensus
jtraglia Jan 14, 2025
c762a33
Add eip7805 to list of executable specs
jtraglia Jan 14, 2025
20b0c50
Fix simple linter warnings
jtraglia Jan 14, 2025
aca38ea
Fix some more linter issues
jtraglia Jan 14, 2025
1c6761a
Add execution_requests_list to notify_new_payload
jtraglia Jan 14, 2025
d63042e
Fix things regarding engine API
jtraglia Jan 14, 2025
86c86d0
Update eth2spec before linting
jtraglia Jan 14, 2025
7d8e6fb
Expand acronym in DOMAIN_IL_COMMITTEE and IL_COMMITTEE_SIZE
jtraglia Jan 14, 2025
19b50a6
Rename il_transactions to inclusion_list_transactions
jtraglia Jan 14, 2025
f25efbf
Change engine API param type to Sequence[Transaction]
jtraglia Jan 14, 2025
5a37c91
Fix various nits
jtraglia Jan 14, 2025
c411d45
Replace "since" with "up to"
jtraglia Jan 14, 2025
dcd31d6
Pull in electra's changes to verify_and_notify_new_payload
jtraglia Jan 14, 2025
3652904
Change inclusion_list_transactions to a Sequence
jtraglia Jan 15, 2025
2781459
Make some improvements to fork-choice doc
jtraglia Jan 15, 2025
7a2cb7a
Make time params work with minimal
jtraglia Jan 24, 2025
91054e6
Fix networking toc
jtraglia Jan 24, 2025
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 .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ tests/core/pyspec/eth2spec/fulu/
tests/core/pyspec/eth2spec/whisk/
tests/core/pyspec/eth2spec/eip6800/
tests/core/pyspec/eth2spec/eip7732/
tests/core/pyspec/eth2spec/eip7805/

# coverage reports
.htmlcov
Expand Down
7 changes: 4 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ ALL_EXECUTABLE_SPEC_NAMES = \
fulu \
whisk \
eip6800 \
eip7732
eip7732 \
eip7805

# A list of fake targets.
.PHONY: \
Expand Down Expand Up @@ -211,7 +212,7 @@ _check_toc: $(MARKDOWN_FILES:=.toc)
@[ "$$(find . -name '*.md.tmp' -print -quit)" ] && exit 1 || exit 0

# Check for mistakes.
lint: $(ETH2SPEC) pyspec _check_toc
lint: eth2spec pyspec _check_toc
@$(CODESPELL_VENV) . --skip "./.git,$(VENV),$(PYSPEC_DIR)/.mypy_cache" -I .codespell-whitelist
@$(PYTHON_VENV) -m flake8 --config $(FLAKE8_CONFIG) $(PYSPEC_DIR)/eth2spec
@$(PYTHON_VENV) -m flake8 --config $(FLAKE8_CONFIG) $(TEST_GENERATORS_DIR)
Expand Down Expand Up @@ -275,4 +276,4 @@ kzg_setups: $(ETH2SPEC)

# Delete all untracked files.
clean:
@git clean -fdx
@git clean -fdx
1 change: 1 addition & 0 deletions pysetup/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
EIP6800 = 'eip6800'
WHISK = 'whisk'
EIP7732 = 'eip7732'
EIP7805 = 'eip7805'


# The helper functions that are used when defining constants
Expand Down
2 changes: 1 addition & 1 deletion pysetup/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ def combine_dicts(old_dict: Dict[str, T], new_dict: Dict[str, T]) -> Dict[str, T
'uint8', 'uint16', 'uint32', 'uint64', 'uint128', 'uint256',
'bytes', 'byte', 'ByteList', 'ByteVector',
'Dict', 'dict', 'field', 'ceillog2', 'floorlog2', 'Set',
'Optional', 'Sequence',
'Optional', 'Sequence', 'Tuple',
]


Expand Down
2 changes: 2 additions & 0 deletions pysetup/md_doc_paths.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
WHISK,
EIP6800,
EIP7732,
EIP7805,
)


Expand All @@ -25,6 +26,7 @@
WHISK: CAPELLA,
EIP6800: DENEB,
EIP7732: ELECTRA,
EIP7805: ELECTRA,
}

ALL_FORKS = list(PREVIOUS_FORK_OF.keys())
Expand Down
2 changes: 2 additions & 0 deletions pysetup/spec_builders/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@
from .whisk import WhiskSpecBuilder
from .eip6800 import EIP6800SpecBuilder
from .eip7732 import EIP7732SpecBuilder
from .eip7805 import EIP7805SpecBuilder


spec_builders = {
builder.fork: builder
for builder in (
Phase0SpecBuilder, AltairSpecBuilder, BellatrixSpecBuilder, CapellaSpecBuilder, DenebSpecBuilder,
ElectraSpecBuilder, FuluSpecBuilder, WhiskSpecBuilder, EIP6800SpecBuilder, EIP7732SpecBuilder,
EIP7805SpecBuilder,
)
}
49 changes: 49 additions & 0 deletions pysetup/spec_builders/eip7805.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
from .base import BaseSpecBuilder
from ..constants import EIP7805


class EIP7805SpecBuilder(BaseSpecBuilder):
fork: str = EIP7805


@classmethod
def execution_engine_cls(cls) -> str:
return """
class NoopExecutionEngine(ExecutionEngine):

def notify_new_payload(self: ExecutionEngine,
execution_payload: ExecutionPayload,
parent_beacon_block_root: Root,
execution_requests_list: Sequence[bytes],
inclusion_list_transactions:
List[Transaction, MAX_TRANSACTIONS_PER_INCLUSION_LIST]) -> bool:
return True

def notify_forkchoice_updated(self: ExecutionEngine,
head_block_hash: Hash32,
safe_block_hash: Hash32,
finalized_block_hash: Hash32,
payload_attributes: Optional[PayloadAttributes]) -> Optional[PayloadId]:
pass

def get_payload(self: ExecutionEngine, payload_id: PayloadId) -> GetPayloadResponse:
# pylint: disable=unused-argument
raise NotImplementedError("no default block production")

def is_valid_block_hash(self: ExecutionEngine,
execution_payload: ExecutionPayload,
parent_beacon_block_root: Root,
execution_requests_list: Sequence[bytes],
inclusion_list_transactions:
List[Transaction, MAX_TRANSACTIONS_PER_INCLUSION_LIST]) -> bool:
return True

def is_valid_versioned_hashes(self: ExecutionEngine, new_payload_request: NewPayloadRequest) -> bool:
return True

def verify_and_notify_new_payload(self: ExecutionEngine,
new_payload_request: NewPayloadRequest) -> bool:
return True


EXECUTION_ENGINE = NoopExecutionEngine()"""
265 changes: 265 additions & 0 deletions specs/_features/eip7805/beacon-chain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,265 @@
# EIP-7805 -- 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)
- [Preset](#preset)
- [Domain types](#domain-types)
- [Inclusion List Committee](#inclusion-list-committee)
- [Execution](#execution)
- [Containers](#containers)
- [New containers](#new-containers)
- [`InclusionList`](#inclusionlist)
- [`SignedInclusionList`](#signedinclusionlist)
- [Predicates](#predicates)
- [New `is_valid_inclusion_list_signature`](#new-is_valid_inclusion_list_signature)
- [Beacon State accessors](#beacon-state-accessors)
- [New `get_inclusion_list_committee`](#new-get_inclusion_list_committee)
- [Beacon chain state transition function](#beacon-chain-state-transition-function)
- [Execution engine](#execution-engine)
- [Request data](#request-data)
- [Modified `NewPayloadRequest`](#modified-newpayloadrequest)
- [Engine APIs](#engine-apis)
- [Modified `is_valid_block_hash`](#modified-is_valid_block_hash)
- [Modified `notify_new_payload`](#modified-notify_new_payload)
- [Modified `verify_and_notify_new_payload`](#modified-verify_and_notify_new_payload)
- [Modified `process_execution_payload`](#modified-process_execution_payload)

<!-- END doctoc generated TOC please keep comment here to allow auto update -->
<!-- /TOC -->

## Introduction

This is the beacon chain specification to add EIP-7805 / fork-choice enforced, committee-based inclusion list (FOCIL) mechanism to allow forced transaction inclusion. Refers to the following posts:
- [Fork-Choice enforced Inclusion Lists (FOCIL): A simple committee-based inclusion list proposal](https://ethresear.ch/t/fork-choice-enforced-inclusion-lists-focil-a-simple-committee-based-inclusion-list-proposal/19870/1)
- [FOCIL CL & EL workflow](https://ethresear.ch/t/focil-cl-el-workflow/20526)
*Note:* This specification is built upon [Electra](../../electra/beacon_chain.md) and is under active development.

## Preset

### Domain types

| Name | Value |
| - | - |
| `DOMAIN_INCLUSION_LIST_COMMITTEE` | `DomainType('0x0C000000')` |

### Inclusion List Committee

| Name | Value |
| - | - |
| `INCLUSION_LIST_COMMITTEE_SIZE` | `uint64(2**4)` (=16) |

### Execution

| Name | Value |
| - | - |
| `MAX_TRANSACTIONS_PER_INCLUSION_LIST` | `uint64(1)` **TBD** |

## Containers

### New containers

#### `InclusionList`

```python
class InclusionList(Container):
slot: Slot
validator_index: ValidatorIndex
inclusion_list_committee_root: Root
transactions: List[Transaction, MAX_TRANSACTIONS_PER_INCLUSION_LIST]
```

#### `SignedInclusionList`

```python
class SignedInclusionList(Container):
Copy link

Choose a reason for hiding this comment

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

Is there a reason why we introduce a SignedInclusionList container instead of just adding the signature field directly onto InclusionList?

Copy link
Member

@nflaig nflaig Jan 12, 2025

Choose a reason for hiding this comment

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

When the beacon node initially retrieves the IL transactions from the execution client wouldn't it create the (unsigned) InclusionList and pass it through the beacon-api to the validator client to sign over it? only after that the validator client would submit the SignedInclusionList to be broadcast

message: InclusionList
signature: BLSSignature
```

### Predicates

#### New `is_valid_inclusion_list_signature`

```python
def is_valid_inclusion_list_signature(
state: BeaconState,
signed_inclusion_list: SignedInclusionList) -> bool:
"""
Check if ``signed_inclusion_list`` has a valid signature.
"""
message = signed_inclusion_list.message
index = message.validator_index
pubkey = state.validators[index].pubkey
domain = get_domain(state, DOMAIN_INCLUSION_LIST_COMMITTEE, compute_epoch_at_slot(message.slot))
signing_root = compute_signing_root(message, domain)
return bls.Verify(pubkey, signing_root, signed_inclusion_list.signature)
```

### Beacon State accessors

#### New `get_inclusion_list_committee`

```python
def get_inclusion_list_committee(state: BeaconState,
slot: Slot) -> Vector[ValidatorIndex, INCLUSION_LIST_COMMITTEE_SIZE]:
epoch = compute_epoch_at_slot(slot)
seed = get_seed(state, epoch, DOMAIN_INCLUSION_LIST_COMMITTEE)
indices = get_active_validator_indices(state, epoch)
start = (slot % SLOTS_PER_EPOCH) * INCLUSION_LIST_COMMITTEE_SIZE
end = start + INCLUSION_LIST_COMMITTEE_SIZE
return [
indices[compute_shuffled_index(uint64(i % len(indices)), uint64(len(indices)), seed)]
for i in range(start, end)
]
```

## Beacon chain state transition function

### Execution engine

#### Request data

##### Modified `NewPayloadRequest`

```python
@dataclass
class NewPayloadRequest(object):
execution_payload: ExecutionPayload
versioned_hashes: Sequence[VersionedHash]
parent_beacon_block_root: Root
execution_requests: ExecutionRequests
inclusion_list_transactions: Sequence[Transaction] # [New in EIP-7805]
```

#### Engine APIs

##### Modified `is_valid_block_hash`

*Note*: The function `is_valid_block_hash` is modified to include the additional `inclusion_list_transactions`.

```python
def is_valid_block_hash(self: ExecutionEngine,
execution_payload: ExecutionPayload,
parent_beacon_block_root: Root,
execution_requests_list: Sequence[bytes],
inclusion_list_transactions: Sequence[Transaction]) -> bool:
"""
Return ``True`` if and only if ``execution_payload.block_hash`` is computed correctly.
"""
...
```

##### Modified `notify_new_payload`

*Note*: The function `notify_new_payload` is modified to include the additional `inclusion_list_transactions`.

```python
def notify_new_payload(self: ExecutionEngine,
execution_payload: ExecutionPayload,
parent_beacon_block_root: Root,
execution_requests_list: Sequence[bytes],
inclusion_list_transactions: Sequence[Transaction]) -> bool:
"""
Return ``True`` if and only if ``execution_payload`` and ``execution_requests_list``
are valid with respect to ``self.execution_state``.
"""
# TODO: move this outside of notify_new_payload.
# If execution client returns block does not satisfy inclusion list transactions, cache the block
# store.unsatisfied_inclusion_list_blocks.add(execution_payload.block_root)
...
```

##### Modified `verify_and_notify_new_payload`

*Note*: The function `verify_and_notify_new_payload` is modified to pass the additional parameter
`inclusion_list_transactions` when calling `notify_new_payload` in EIP-7805.

```python
def verify_and_notify_new_payload(self: ExecutionEngine,
new_payload_request: NewPayloadRequest) -> bool:
"""
Return ``True`` if and only if ``new_payload_request`` is valid with respect to ``self.execution_state``.
"""
execution_payload = new_payload_request.execution_payload
parent_beacon_block_root = new_payload_request.parent_beacon_block_root
execution_requests_list = get_execution_requests_list(new_payload_request.execution_requests)
inclusion_list_transactions = new_payload_request.inclusion_list_transactions # [New in EIP-7805]

if b'' in execution_payload.transactions:
return False

if not self.is_valid_block_hash(
execution_payload,
parent_beacon_block_root,
execution_requests_list):
return False

if not self.is_valid_versioned_hashes(new_payload_request):
return False

# [Modified in EIP-7805]
if not self.notify_new_payload(
execution_payload,
parent_beacon_block_root,
execution_requests_list,
inclusion_list_transactions):
return False

return True
```

##### Modified `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_ELECTRA
# Verify the execution payload is valid
versioned_hashes = [kzg_commitment_to_versioned_hash(commitment) for commitment in body.blob_kzg_commitments]
# Verify inclusion list transactions
inclusion_list_transactions: Sequence[Transaction] = [] # TODO: where do we get this?
assert len(inclusion_list_transactions) <= MAX_TRANSACTIONS_PER_INCLUSION_LIST
# Verify the payload with the execution engine
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,
execution_requests=body.execution_requests,
inclusion_list_transactions=inclusion_list_transactions,
)
)
# 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),
blob_gas_used=payload.blob_gas_used,
excess_blob_gas=payload.excess_blob_gas,
)
```
Loading