From 3a16a0ec10cd5a0b06a091ff052acdde6c3319bb Mon Sep 17 00:00:00 2001 From: terence tsao Date: Wed, 11 Sep 2024 13:38:01 -0700 Subject: [PATCH 01/23] Add focil containers --- specs/_features/focil/beacon-chain.md | 172 ++++++++++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 specs/_features/focil/beacon-chain.md diff --git a/specs/_features/focil/beacon-chain.md b/specs/_features/focil/beacon-chain.md new file mode 100644 index 0000000000..5518038a49 --- /dev/null +++ b/specs/_features/focil/beacon-chain.md @@ -0,0 +1,172 @@ +# FOCIL -- The Beacon Chain + +## Table of contents + + + + + +- [Introduction](#introduction) +- [Constants](#constants) + - [Domain types](#domain-types) +- [Preset](#preset) + - [Inclusion List Committee](#inclusion-list-committee) + - [Execution](#execution) +- [Containers](#containers) + - [New containers](#new-containers) + - [`InclusionListSummary`](#inclusionlistsummary) + - [`InclusionList`](#inclusionlist) + - [`SignedInclusionList`](#signedinclusionlist) + - [`InclusionListAggregate`](#inclusionlistaggregate) + - [`IndexedInclusionListAggregate`](#indexedinclusionlistaggregate) + - [Modified containers](#modified-containers) + - [`BeaconBlockBody`](#beaconblockbody) + - [Beacon State accessors](#beacon-state-accessors) + - [`get_inclusion_list_committee`](#get_inclusion_list_committee) +- [Beacon chain state transition function](#beacon-chain-state-transition-function) + - [Execution engine](#execution-engine) + - [Engine APIs](#engine-apis) + - [New `verify_and_notify_new_inclusion_list`](#new-verify_and_notify_new_inclusion_list) + + + + +## Introduction + +This is the beacon chain specification to add a committee-based inclusion list mechanism to allow forced transaction inclusion. Refers to [Ethresearch](https://ethresear.ch/t/fork-choice-enforced-inclusion-lists-focil-a-simple-committee-based-inclusion-list-proposal/19870/1) + +*Note:* This specification is built upon [Electra](../../electra/beacon_chain.md) and is under active development. + +## Constants + +### Domain types + +| Name | Value | +| - | - | +| `DOMAIN_IL_COMMITTEE` | `DomainType('0x0C000000')` # (New in FOCIL)| + +## Preset + +### Inclusion List Committee + +| Name | Value | +| - | - | +| `IL_COMMITTEE_SIZE` | `uint64(2**9)` (=512) # (New in FOCIL) | + +### Execution + +| Name | Value | +| - | - | +| `MAX_TRANSACTIONS_PER_INCLUSION_LIST` | `uint64(1)` # (New in FOCIL) | #TODO: Fill this value + +## Containers + +### New containers + +#### `InclusionListSummary` + +```python +class InclusionListSummary(Container): + address: ExecutionAddress + nonce: uint64 + gas_limit: uint64 +``` + +#### `InclusionList` + +```python +class InclusionList(Container): + slot: Slot + validator_index: ValidatorIndex + parent_hash: Hash32 + summaries: List[InclusionListSummary, MAX_TRANSACTIONS_PER_INCLUSION_LIST] + transactions: List[Transaction, MAX_TRANSACTIONS_PER_INCLUSION_LIST] +``` + +#### `SignedInclusionList` + +```python +class SignedInclusionList(Container): + message: InclusionList + signature: BLSSignature +``` + +#### `InclusionListAggregate` + +```python +class InclusionListAggregate(Container): + aggregation_bits: Bitvector[IL_COMMITTEE_SIZE] + summary: InclusionListSummary + signature: BLSSignature +``` + +#### `IndexedInclusionListAggregate` + +```python +class IndexedInclusionListAggregate(Container): + validator_indices: List[ValidatorIndex, IL_COMMITTEE_SIZE] + message: InclusionList + signature: BLSSignature +``` + +### Modified containers + +#### `BeaconBlockBody` + +**Note:** The Beacon Block body is modified to contain a new `inclusion_list_aggregate` field. + +```python +class BeaconBlockBody(Container): + randao_reveal: BLSSignature + eth1_data: Eth1Data # Eth1 data vote + graffiti: Bytes32 # Arbitrary data + # Operations + proposer_slashings: List[ProposerSlashing, MAX_PROPOSER_SLASHINGS] + attester_slashings: List[AttesterSlashing, MAX_ATTESTER_SLASHINGS_ELECTRA] + attestations: List[Attestation, MAX_ATTESTATIONS_ELECTRA] + deposits: List[Deposit, MAX_DEPOSITS] + voluntary_exits: List[SignedVoluntaryExit, MAX_VOLUNTARY_EXITS] + sync_aggregate: SyncAggregate + bls_to_execution_changes: List[SignedBLSToExecutionChange, MAX_BLS_TO_EXECUTION_CHANGES] + # FOCIL + inclusion_list_aggregate: List[InclusionListAggregate, MAX_TRANSACTIONS_PER_INCLUSION_LIST] # [New in FOCIL] +``` + +### Beacon State accessors + +#### `get_inclusion_list_committee` + +```python +def get_inclusion_list_committee(state: BeaconState, slot: Slot) -> Vector[ValidatorIndex, IL_COMMITTEE_SIZE]: + """ + Get the inclusion list committee for the given ``slot`` + """ + epoch = compute_epoch_at_slot(slot) + committees_per_slot = bit_floor(min(get_committee_count_per_slot(state, epoch), IL_COMMITTEE_SIZE)) + members_per_committee = IL_COMMITTEE_SIZE // committees_per_slot + + validator_indices: List[ValidatorIndex] = [] + for idx in range(committees_per_slot): + beacon_committee = get_beacon_committee(state, slot, CommitteeIndex(idx)) + validator_indices += beacon_committee[:members_per_committee] + return validator_indices +``` + +## Beacon chain state transition function + +### Execution engine + +#### Engine APIs + +##### New `verify_and_notify_new_inclusion_list` + +```python +def verify_and_notify_new_inclusion_list(self: ExecutionEngine, + inclusion_list: InclusionList) -> bool: + """ + Return ``True`` if and only if the transactions in the inclusion list can be successfully executed + starting from the execution state corresponding to the `parent_hash` in the inclusion list + summary. + """ + ... +``` \ No newline at end of file From fa27c8fd8d84d6c174252cf08e8d2e46d607d1bc Mon Sep 17 00:00:00 2001 From: terence tsao Date: Thu, 12 Sep 2024 10:41:11 -0700 Subject: [PATCH 02/23] Add process inclusion list aggregate --- specs/_features/focil/beacon-chain.md | 106 +++++++++++++++++++++++++- 1 file changed, 102 insertions(+), 4 deletions(-) diff --git a/specs/_features/focil/beacon-chain.md b/specs/_features/focil/beacon-chain.md index 5518038a49..dc1a1db105 100644 --- a/specs/_features/focil/beacon-chain.md +++ b/specs/_features/focil/beacon-chain.md @@ -23,10 +23,18 @@ - [`BeaconBlockBody`](#beaconblockbody) - [Beacon State accessors](#beacon-state-accessors) - [`get_inclusion_list_committee`](#get_inclusion_list_committee) + - [Beacon State accessors](#beacon-state-accessors-1) + - [New `get_inclusion_list_aggregate_indices`](#new-get_inclusion_list_aggregate_indices) + - [New `get_indexed_inclusion_list_aggregate`](#new-get_indexed_inclusion_list_aggregate) - [Beacon chain state transition function](#beacon-chain-state-transition-function) - [Execution engine](#execution-engine) - [Engine APIs](#engine-apis) - [New `verify_and_notify_new_inclusion_list`](#new-verify_and_notify_new_inclusion_list) + - [Block processing](#block-processing) + - [Operations](#operations) + - [Modified `process_operations`](#modified-process_operations) + - [Inclusion list aggregate](#inclusion-list-aggregate) + - [New `process_inclusion_list_aggregate`](#new-process_inclusion_list_aggregate) @@ -105,7 +113,7 @@ class InclusionListAggregate(Container): ```python class IndexedInclusionListAggregate(Container): validator_indices: List[ValidatorIndex, IL_COMMITTEE_SIZE] - message: InclusionList + summary: InclusionListSummary signature: BLSSignature ``` @@ -137,20 +145,50 @@ class BeaconBlockBody(Container): #### `get_inclusion_list_committee` ```python -def get_inclusion_list_committee(state: BeaconState, slot: Slot) -> Vector[ValidatorIndex, IL_COMMITTEE_SIZE]: +def get_inclusion_list_committee(state: BeaconState) -> Vector[ValidatorIndex, IL_COMMITTEE_SIZE]: """ Get the inclusion list committee for the given ``slot`` """ - epoch = compute_epoch_at_slot(slot) + epoch = compute_epoch_at_slot(state.slot) committees_per_slot = bit_floor(min(get_committee_count_per_slot(state, epoch), IL_COMMITTEE_SIZE)) members_per_committee = IL_COMMITTEE_SIZE // committees_per_slot validator_indices: List[ValidatorIndex] = [] for idx in range(committees_per_slot): - beacon_committee = get_beacon_committee(state, slot, CommitteeIndex(idx)) + beacon_committee = get_beacon_committee(state, state.slot, CommitteeIndex(idx)) validator_indices += beacon_committee[:members_per_committee] return validator_indices ``` +### Beacon State accessors + +#### New `get_inclusion_list_aggregate_indices` + +```python +def get_inclusion_list_aggregate_indices(state: BeaconState, + inclusion_list_aggregate: InclusionListAggregate) -> Set[ValidatorIndex]: + """ + Return the set of indices corresponding to ``inclusion_list_aggregate``. + """ + il_committee = get_inclusion_list_committee(state) + return set(index for i, index in enumerate(il_committee) if inclusion_list_aggregate.aggregation_bits[i]) +``` + +#### New `get_indexed_inclusion_list_aggregate` + +```python +def get_indexed_inclusion_list_aggregate(state: BeaconState, + inclusion_list_aggregate: InclusionListAggregate) -> IndexedInclusionListAggregate: + """ + Return the indexed inclusion list aggregate corresponding to ``inclusion_list_aggregate``. + """ + indices = get_inclusion_list_aggregate_indices(state) + + return IndexedInclusionListAggregate( + validator_indices=sorted(indices), + summary=inclusion_list_aggregate.summary, + signature=inclusion_list_aggregate.signature, + ) +``` ## Beacon chain state transition function @@ -169,4 +207,64 @@ def verify_and_notify_new_inclusion_list(self: ExecutionEngine, summary. """ ... +``` + +### Block processing + +```python +def process_block(state: BeaconState, block: BeaconBlock) -> None: + process_block_header(state, block) + process_withdrawals(state, block.body.execution_payload) + process_execution_payload(state, block.body, EXECUTION_ENGINE) + process_randao(state, block.body) + process_eth1_data(state, block.body) + process_operations(state, block.body) # [Modified in FOCIL] + process_sync_aggregate(state, block.body.sync_aggregate) +``` + +#### Operations + +##### Modified `process_operations` + +*Note*: The function `process_operations` is modified to support all of the new functionality in FOCIL. + +```python +def process_operations(state: BeaconState, body: BeaconBlockBody) -> None: + eth1_deposit_index_limit = min(state.eth1_data.deposit_count, state.deposit_requests_start_index) + if state.eth1_deposit_index < eth1_deposit_index_limit: + assert len(body.deposits) == min(MAX_DEPOSITS, eth1_deposit_index_limit - state.eth1_deposit_index) + else: + assert len(body.deposits) == 0 + + def for_ops(operations: Sequence[Any], fn: Callable[[BeaconState, Any], None]) -> None: + for operation in operations: + fn(state, operation) + + for_ops(body.proposer_slashings, process_proposer_slashing) + for_ops(body.attester_slashings, process_attester_slashing) + for_ops(body.attestations, process_attestation) + for_ops(body.deposits, process_deposit) + for_ops(body.voluntary_exits, process_voluntary_exit) + for_ops(body.bls_to_execution_changes, process_bls_to_execution_change) + for_ops(body.execution_payload.deposit_requests, process_deposit_request) + for_ops(body.execution_payload.withdrawal_requests, process_withdrawal_request) + for_ops(body.execution_payload.consolidation_requests, process_consolidation_request) + for_ops(body.inclusion_list_aggregate, process_inclusion_list_aggregate) # [New in FOCIL] +``` + +##### Inclusion list aggregate + +###### New `process_inclusion_list_aggregate` + +```python +def process_inclusion_list_aggregate( + state: BeaconState, + inclusion_list_aggregate: InclusionListAggregate +) -> None: + + # Verify signature + indexed_inclusion_list_aggregate = get_indexed_inclusion_list_aggregate(state, inclusion_list_aggregate) + assert is_valid_indexed_inclusion_list_aggregate(state, indexed_inclusion_list_aggregate) + + # TODO: Reward inclusion list aggregate participants ``` \ No newline at end of file From 3c86963ba506a26f47831fdef37657bf61f6387c Mon Sep 17 00:00:00 2001 From: terence tsao Date: Fri, 13 Sep 2024 20:03:08 -0700 Subject: [PATCH 03/23] Add inclusion list gossip conditions --- specs/_features/focil/fork-choice.md | 10 ++++ specs/_features/focil/p2p-interface.md | 70 ++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 specs/_features/focil/fork-choice.md create mode 100644 specs/_features/focil/p2p-interface.md diff --git a/specs/_features/focil/fork-choice.md b/specs/_features/focil/fork-choice.md new file mode 100644 index 0000000000..f53575d168 --- /dev/null +++ b/specs/_features/focil/fork-choice.md @@ -0,0 +1,10 @@ + + +**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* + +- [FOCIL -- Fork Choice](#focil----fork-choice) + + + +# FOCIL -- Fork Choice + diff --git a/specs/_features/focil/p2p-interface.md b/specs/_features/focil/p2p-interface.md new file mode 100644 index 0000000000..502ea0e691 --- /dev/null +++ b/specs/_features/focil/p2p-interface.md @@ -0,0 +1,70 @@ +# FOCIL -- Networking + +This document contains the consensus-layer networking specification for FOCIL. + + + + + - [Containers](#containers) + - [`SignedInclusionListAggregate`](#signedinclusionlistaggregate) + - [The gossip domain: gossipsub](#the-gossip-domain-gossipsub) + - [Topics and messages](#topics-and-messages) + - [Topics and messages](#topics-and-messages-1) + - [Global topics](#global-topics) + - [`inclusion_list`](#inclusion_list) + - [`inclusion_list_aggregate`](#inclusion_list_aggregate) +- [TODO](#todo) + + + +### Containers + +#### `SignedInclusionListAggregate` + +```python +class SignedInclusionListAggregate(Container): + message: InclusionListAggregate + signature: BLSSignature +``` + +### The gossip domain: gossipsub + +Some gossip meshes are upgraded in the fork of FOCIL to support upgraded types. + +#### Topics and messages + +Topics follow the same specification as in prior upgrades. + +The `beacon_block` topic is updated to support the modified type +| Name | Message Type | +| --- | --- | +| `beacon_block` | `SignedBeaconBlock` [modified in FOCIL] | + +#### Topics and messages + +The new topics along with the type of the `data` field of a gossipsub message are given in this table: + +| Name | Message Type | +|-------------------------------|------------------------------------------------------| +| `inclusion_list` | `SignedInclusionList` [New in FOCIL] | +| `inclusion_list_aggregate` | `SignedInclusionListAggregate` [New in FOCIL] | + +##### Global topics + +FOCIL introduces new global topics for inclusion list and inclusion list aggregate. + +###### `inclusion_list` + +This topic is used to propagate execution payload messages as `SignedInclusionList`. +The following validations MUST pass before forwarding the `inclusion_list` on the network, assuming the alias `message = signed_inclusion_list.message`: + +- _[REJECT]_ The slot `message.slot` is equal to current slot + 1 +- _[REJECT]_ The transactions `message.transactions` length is within upperbound `MAX_TRANSACTIONS_PER_INCLUSION_LIST`. +- _[REJECT]_ The summaries `message.summaries` length is within upperbound `MAX_TRANSACTIONS_PER_INCLUSION_LIST`. +- _[IGNORE]_ The block hash `message.parent_block_hash` is a known execution payload in fork choice. +- _[REJECT]_ The signature of `inclusion_list.signature` is valid with respect to the validator index. +- _[REJECT]_ The validator index is within the inclusion list committee in `get_inclusion_list_committee(state)`. The `state` is the head state corresponding to processing the block up to the current slot as determined by the fork choice. + +###### `inclusion_list_aggregate` + +# TODO From 8a1e9409f5f83ce43e32a167f7e8e8513151989f Mon Sep 17 00:00:00 2001 From: terence tsao Date: Sun, 15 Sep 2024 12:15:06 -0700 Subject: [PATCH 04/23] Add execution api --- specs/_features/focil/beacon-chain.md | 2 +- specs/_features/focil/engine-api.md | 131 +++++++++++++++++++++++++ specs/_features/focil/p2p-interface.md | 2 +- 3 files changed, 133 insertions(+), 2 deletions(-) create mode 100644 specs/_features/focil/engine-api.md diff --git a/specs/_features/focil/beacon-chain.md b/specs/_features/focil/beacon-chain.md index dc1a1db105..0285fab3b5 100644 --- a/specs/_features/focil/beacon-chain.md +++ b/specs/_features/focil/beacon-chain.md @@ -262,7 +262,7 @@ def process_inclusion_list_aggregate( inclusion_list_aggregate: InclusionListAggregate ) -> None: - # Verify signature + # Verify inclusion list aggregate signature indexed_inclusion_list_aggregate = get_indexed_inclusion_list_aggregate(state, inclusion_list_aggregate) assert is_valid_indexed_inclusion_list_aggregate(state, indexed_inclusion_list_aggregate) diff --git a/specs/_features/focil/engine-api.md b/specs/_features/focil/engine-api.md new file mode 100644 index 0000000000..c1cfd0537e --- /dev/null +++ b/specs/_features/focil/engine-api.md @@ -0,0 +1,131 @@ +# Engine API -- FOCIL + +Engine API changes introduced in FOCIL. + +This specification is based on and extends [Engine API - Prague](./prague.md) specification. + +Warning: This file should be placed in https://github.com/ethereum/execution-apis but I'm placing it here for convenience of reviewing. + +## Table of contents + + + + +- [Constants](#constants) +- [Structures](#structures) + - [InclusionListSummaryV1](#inclusionlistsummaryv1) + - [InclusionListSummaryListV1](#inclusionlistsummarylistv1) + - [InclusionListStatusV1](#inclusionliststatusv1) +- [Methods](#methods) + - [engine_newPayloadV5](#engine_newpayloadv5) + - [Request](#request) + - [Response](#response) + - [Specification](#specification) + - [engine_newInclusionListV1](#engine_newinclusionlistv1) + - [Request](#request-1) + - [Response](#response-1) + - [Specification](#specification-1) + - [engine_getInclusionListV1](#engine_getinclusionlistv1) + - [Request](#request-2) + - [Response](#response-2) + - [Specification](#specification-2) + + + +## Constants + +| Name | Value | +| - | - | +| `INCLUSION_LIST_MAX_GAS` | `uint64(4194304) = 2**22` | + +## Structures + +### InclusionListSummaryV1 + +This structure contains the details of each inclusion list summary. + +- `address` : `DATA`, 20 Bytes +- `nonce` : `QUANTITY`, 64 Bits +- `gas_limit` : `QUANTITY`, 64 Bits + +### InclusionListSummaryListV1 + +This structure contains the inclusion list summary. + +- `summary`: `Array of InclusionListSummaryV1`, Array of summaries that must be satisfied. + +### InclusionListStatusV1 + +This structure contains the result of processing an inclusion list. The fields are encoded as follows: + +- `status`: `enum` - `"VALID" | "INVALID" | "SYNCING" | "ACCEPTED"` +- `validationError`: `String|null` - a message providing additional details on the validation error if the payload is classified as `INVALID`. + +## Methods + +### engine_newPayloadV5 + +The request of this method is updated with [`ExecutionPayloadV5`](#ExecutionPayloadV5). + +#### Request + +* method: `engine_newPayloadV5` +* params: + 1. `executionPayload`: [`ExecutionPayloadV4`](#ExecutionPayloadV4). + 2. `expectedBlobVersionedHashes`: `Array of DATA`, 32 Bytes - Array of expected blob versioned hashes to validate. + 3. `parentBeaconBlockRoot`: `DATA`, 32 Bytes - Root of the parent beacon block. + 4. `inclusionListSummaryList`: [`InclusionListSummaryListV1`](#InclusionListSummaryListV1). + +#### Response + +Refer to the response for [`engine_newPayloadV3`](./cancun.md#engine_newpayloadv3). + +#### Specification + +This method follows the same specification as [`engine_newPayloadV3`](./cancun.md#engine_newpayloadv3) with the following changes: + +1. Client software **MUST** return `-38005: Unsupported fork` error if the `timestamp` of the payload does not fall within the time frame of the Prague fork. + +### engine_newInclusionListV1 + +#### Request + +* method: `engine_newInclusionListV1` +* params: + 1. `parent_hash`: `DATA`, 32 Bytes - parent hash which this inclusion list is built upon. + 2. `inclusionListSummaryList`: `InclusionListSummaryListV1` - Summary. + 3. `inclusionListTransactions`: `Array of DATA` - Array of transaction objects, each object is a byte list (`DATA`) representing `TransactionType || TransactionPayload` or `LegacyTransaction` as defined in [EIP-2718](https://eips.ethereum.org/EIPS/eip-2718) +* timeout: 1s + +#### Response + +* result: [`InclusionListStatusV1`](./#inclusionliststatusv1). +* error: code and message set in case an exception happens while processing the inclusion list. + +#### Specification + +1. Client software **MUST** validate the inclusion list transactions are valid (correct nonce and sufficient base fee) given the parent block hash specified. If the parent block is not available, return false. +2. Client software **MUST** validate that the sum of inclusion list transactions gas does not exceed `INCLUSION_LIST_MAX_GAS`. +3. Client software **MUST** validate that the summary and transactions are the same length. +4. Client software **MUST** validate that the order of the summary entries matches the order of the transactions. + +### engine_getInclusionListV1 + +#### Request + +* method: `engine_getInclusionListV1` +* params: + 1. `parent_hash`: `DATA`, 32 Bytes - parent hash which returned inclusion list should be built upon. +* timeout: 1s + +#### Response + +* result: `object` + - `inclusionListSummaryList`: `InclusionListSummaryListV1` - Summary. + - `inclusionListTransactions`: `Array of DATA` - Array of transaction objects, each object is a byte list (`DATA`) representing `TransactionType || TransactionPayload` or `LegacyTransaction` as defined in [EIP-2718](https://eips.ethereum.org/EIPS/eip-2718) +* error: code and message set in case an exception happens while getting the inclusion list. + +#### Specification + +1. Client software **MUST** provide a list of transactions for the inclusion list based on local view of the mempool and according to the config specifications. +2. Client software **MUST** broadcast their inclusion list in addition to the full beacon block during their block proposal. \ No newline at end of file diff --git a/specs/_features/focil/p2p-interface.md b/specs/_features/focil/p2p-interface.md index 502ea0e691..66d25c22ec 100644 --- a/specs/_features/focil/p2p-interface.md +++ b/specs/_features/focil/p2p-interface.md @@ -55,7 +55,7 @@ FOCIL introduces new global topics for inclusion list and inclusion list aggrega ###### `inclusion_list` -This topic is used to propagate execution payload messages as `SignedInclusionList`. +This topic is used to propagate execution inclusion list as `SignedInclusionList`. The following validations MUST pass before forwarding the `inclusion_list` on the network, assuming the alias `message = signed_inclusion_list.message`: - _[REJECT]_ The slot `message.slot` is equal to current slot + 1 From 833be940d31f4b4273ecb0970f478f8b1657cf53 Mon Sep 17 00:00:00 2001 From: terence tsao Date: Mon, 16 Sep 2024 15:25:39 -0700 Subject: [PATCH 05/23] Add validator spec --- specs/_features/focil/beacon-chain.md | 75 +++++++------- specs/_features/focil/engine-api.md | 4 +- specs/_features/focil/p2p-interface.md | 45 +++++---- specs/_features/focil/validator.md | 129 +++++++++++++++++++++++++ 4 files changed, 198 insertions(+), 55 deletions(-) create mode 100644 specs/_features/focil/validator.md diff --git a/specs/_features/focil/beacon-chain.md b/specs/_features/focil/beacon-chain.md index 0285fab3b5..0c518533da 100644 --- a/specs/_features/focil/beacon-chain.md +++ b/specs/_features/focil/beacon-chain.md @@ -14,10 +14,10 @@ - [Execution](#execution) - [Containers](#containers) - [New containers](#new-containers) - - [`InclusionListSummary`](#inclusionlistsummary) - - [`InclusionList`](#inclusionlist) - - [`SignedInclusionList`](#signedinclusionlist) - - [`InclusionListAggregate`](#inclusionlistaggregate) + - [`InclusionSummary`](#inclusionsummary) + - [`LocalInclusionList`](#localinclusionlist) + - [`SignedLocalInclusionList`](#signedlocalinclusionlist) + - [`InclusionSummaryAggregate`](#inclusionsummaryaggregate) - [`IndexedInclusionListAggregate`](#indexedinclusionlistaggregate) - [Modified containers](#modified-containers) - [`BeaconBlockBody`](#beaconblockbody) @@ -65,46 +65,46 @@ This is the beacon chain specification to add a committee-based inclusion list m | Name | Value | | - | - | -| `MAX_TRANSACTIONS_PER_INCLUSION_LIST` | `uint64(1)` # (New in FOCIL) | #TODO: Fill this value +| `MAX_TRANSACTIONS_PER_INCLUSION_LIST` | `uint64(1)` # (New in FOCIL) TODO: Placeholder | ## Containers ### New containers -#### `InclusionListSummary` +#### `InclusionSummary` ```python -class InclusionListSummary(Container): +class InclusionSummary(Container): address: ExecutionAddress nonce: uint64 gas_limit: uint64 ``` -#### `InclusionList` +#### `LocalInclusionList` ```python -class InclusionList(Container): +class LocalInclusionList(Container): slot: Slot validator_index: ValidatorIndex parent_hash: Hash32 - summaries: List[InclusionListSummary, MAX_TRANSACTIONS_PER_INCLUSION_LIST] + summaries: List[InclusionSummary, MAX_TRANSACTIONS_PER_INCLUSION_LIST] transactions: List[Transaction, MAX_TRANSACTIONS_PER_INCLUSION_LIST] ``` -#### `SignedInclusionList` +#### `SignedLocalInclusionList` ```python -class SignedInclusionList(Container): - message: InclusionList +class SignedLocalInclusionList(Container): + message: LocalInclusionList signature: BLSSignature ``` -#### `InclusionListAggregate` +#### `InclusionSummaryAggregate` ```python -class InclusionListAggregate(Container): +class InclusionSummaryAggregate(Container): aggregation_bits: Bitvector[IL_COMMITTEE_SIZE] - summary: InclusionListSummary + summary: InclusionSummary signature: BLSSignature ``` @@ -113,15 +113,22 @@ class InclusionListAggregate(Container): ```python class IndexedInclusionListAggregate(Container): validator_indices: List[ValidatorIndex, IL_COMMITTEE_SIZE] - summary: InclusionListSummary + summary: InclusionSummary signature: BLSSignature ``` +#### `InclusionSummaryAggregates` + +```python +class InclusionSummaryAggregates(Container): + List[InclusionSummaryAggregate, MAX_TRANSACTIONS_PER_INCLUSION_LIST] +``` + ### Modified containers #### `BeaconBlockBody` -**Note:** The Beacon Block body is modified to contain a new `inclusion_list_aggregate` field. +**Note:** The Beacon Block body is modified to contain a new `inclusion_summary_aggregate` field. ```python class BeaconBlockBody(Container): @@ -137,7 +144,7 @@ class BeaconBlockBody(Container): sync_aggregate: SyncAggregate bls_to_execution_changes: List[SignedBLSToExecutionChange, MAX_BLS_TO_EXECUTION_CHANGES] # FOCIL - inclusion_list_aggregate: List[InclusionListAggregate, MAX_TRANSACTIONS_PER_INCLUSION_LIST] # [New in FOCIL] + inclusion_summary_aggregates: InclusionSummaryAggregates # [New in FOCIL] ``` ### Beacon State accessors @@ -145,17 +152,17 @@ class BeaconBlockBody(Container): #### `get_inclusion_list_committee` ```python -def get_inclusion_list_committee(state: BeaconState) -> Vector[ValidatorIndex, IL_COMMITTEE_SIZE]: +def get_inclusion_list_committee(state: BeaconState, slot: Slot) -> Vector[ValidatorIndex, IL_COMMITTEE_SIZE]: """ Get the inclusion list committee for the given ``slot`` """ - epoch = compute_epoch_at_slot(state.slot) + epoch = compute_epoch_at_slot(slot) committees_per_slot = bit_floor(min(get_committee_count_per_slot(state, epoch), IL_COMMITTEE_SIZE)) members_per_committee = IL_COMMITTEE_SIZE // committees_per_slot validator_indices: List[ValidatorIndex] = [] for idx in range(committees_per_slot): - beacon_committee = get_beacon_committee(state, state.slot, CommitteeIndex(idx)) + beacon_committee = get_beacon_committee(state, slot, CommitteeIndex(idx)) validator_indices += beacon_committee[:members_per_committee] return validator_indices ``` @@ -165,28 +172,28 @@ def get_inclusion_list_committee(state: BeaconState) -> Vector[ValidatorIndex, I ```python def get_inclusion_list_aggregate_indices(state: BeaconState, - inclusion_list_aggregate: InclusionListAggregate) -> Set[ValidatorIndex]: + inclusion_summary_aggregate: InclusionSummaryAggregate) -> Set[ValidatorIndex]: """ - Return the set of indices corresponding to ``inclusion_list_aggregate``. + Return the set of indices corresponding to ``inclusion_summary_aggregate``. """ - il_committee = get_inclusion_list_committee(state) - return set(index for i, index in enumerate(il_committee) if inclusion_list_aggregate.aggregation_bits[i]) + il_committee = get_inclusion_list_committee(state, state.slot) + return set(index for i, index in enumerate(il_committee) if inclusion_summary_aggregate.aggregation_bits[i]) ``` #### New `get_indexed_inclusion_list_aggregate` ```python def get_indexed_inclusion_list_aggregate(state: BeaconState, - inclusion_list_aggregate: InclusionListAggregate) -> IndexedInclusionListAggregate: + inclusion_summary_aggregate: InclusionSummaryAggregate) -> IndexedInclusionListAggregate: """ - Return the indexed inclusion list aggregate corresponding to ``inclusion_list_aggregate``. + Return the indexed inclusion list aggregate corresponding to ``inclusion_summary_aggregate``. """ indices = get_inclusion_list_aggregate_indices(state) return IndexedInclusionListAggregate( validator_indices=sorted(indices), - summary=inclusion_list_aggregate.summary, - signature=inclusion_list_aggregate.signature, + summary=inclusion_summary_aggregate.summary, + signature=inclusion_summary_aggregate.signature, ) ``` @@ -200,7 +207,7 @@ def get_indexed_inclusion_list_aggregate(state: BeaconState, ```python def verify_and_notify_new_inclusion_list(self: ExecutionEngine, - inclusion_list: InclusionList) -> bool: + inclusion_list: LocalInclusionList) -> bool: """ Return ``True`` if and only if the transactions in the inclusion list can be successfully executed starting from the execution state corresponding to the `parent_hash` in the inclusion list @@ -249,7 +256,7 @@ def process_operations(state: BeaconState, body: BeaconBlockBody) -> None: for_ops(body.execution_payload.deposit_requests, process_deposit_request) for_ops(body.execution_payload.withdrawal_requests, process_withdrawal_request) for_ops(body.execution_payload.consolidation_requests, process_consolidation_request) - for_ops(body.inclusion_list_aggregate, process_inclusion_list_aggregate) # [New in FOCIL] + for_ops(body.inclusion_summary_aggregate, process_inclusion_list_aggregate) # [New in FOCIL] ``` ##### Inclusion list aggregate @@ -259,11 +266,11 @@ def process_operations(state: BeaconState, body: BeaconBlockBody) -> None: ```python def process_inclusion_list_aggregate( state: BeaconState, - inclusion_list_aggregate: InclusionListAggregate + inclusion_summary_aggregate: InclusionSummaryAggregate ) -> None: # Verify inclusion list aggregate signature - indexed_inclusion_list_aggregate = get_indexed_inclusion_list_aggregate(state, inclusion_list_aggregate) + indexed_inclusion_list_aggregate = get_indexed_inclusion_list_aggregate(state, inclusion_summary_aggregate) assert is_valid_indexed_inclusion_list_aggregate(state, indexed_inclusion_list_aggregate) # TODO: Reward inclusion list aggregate participants diff --git a/specs/_features/focil/engine-api.md b/specs/_features/focil/engine-api.md index c1cfd0537e..a8e6394588 100644 --- a/specs/_features/focil/engine-api.md +++ b/specs/_features/focil/engine-api.md @@ -42,7 +42,7 @@ Warning: This file should be placed in https://github.com/ethereum/execution-api ### InclusionListSummaryV1 -This structure contains the details of each inclusion list summary. +This structure contains the details of each inclusion summary. - `address` : `DATA`, 20 Bytes - `nonce` : `QUANTITY`, 64 Bits @@ -50,7 +50,7 @@ This structure contains the details of each inclusion list summary. ### InclusionListSummaryListV1 -This structure contains the inclusion list summary. +This structure contains the inclusion summary. - `summary`: `Array of InclusionListSummaryV1`, Array of summaries that must be satisfied. diff --git a/specs/_features/focil/p2p-interface.md b/specs/_features/focil/p2p-interface.md index 66d25c22ec..e1fc5adc7f 100644 --- a/specs/_features/focil/p2p-interface.md +++ b/specs/_features/focil/p2p-interface.md @@ -5,15 +5,14 @@ This document contains the consensus-layer networking specification for FOCIL. - - [Containers](#containers) - - [`SignedInclusionListAggregate`](#signedinclusionlistaggregate) - - [The gossip domain: gossipsub](#the-gossip-domain-gossipsub) - - [Topics and messages](#topics-and-messages) - - [Topics and messages](#topics-and-messages-1) - - [Global topics](#global-topics) - - [`inclusion_list`](#inclusion_list) - - [`inclusion_list_aggregate`](#inclusion_list_aggregate) -- [TODO](#todo) +- [Containers](#containers) + - [`SignedInclusionListAggregate`](#signedinclusionlistaggregate) +- [The gossip domain: gossipsub](#the-gossip-domain-gossipsub) + - [Topics and messages](#topics-and-messages) + - [Topics and messages](#topics-and-messages-1) + - [Global topics](#global-topics) + - [`local_inclusion_list`](#local_inclusion_list) + - [`inclusion_summary_aggregate`](#inclusion_summary_aggregate) @@ -22,8 +21,10 @@ This document contains the consensus-layer networking specification for FOCIL. #### `SignedInclusionListAggregate` ```python -class SignedInclusionListAggregate(Container): - message: InclusionListAggregate +class SignedInclusionListAggregates(Container): + slot: Slot + proposer_index: ValidatorIndex + message: InclusionSummaryAggregates signature: BLSSignature ``` @@ -46,25 +47,31 @@ The new topics along with the type of the `data` field of a gossipsub message ar | Name | Message Type | |-------------------------------|------------------------------------------------------| -| `inclusion_list` | `SignedInclusionList` [New in FOCIL] | -| `inclusion_list_aggregate` | `SignedInclusionListAggregate` [New in FOCIL] | +| `local_inclusion_list` | `SignedLocalInclusionList` [New in FOCIL] | +| `inclusion_summary_aggregates` | `SignedInclusionListAggregates` [New in FOCIL] | ##### Global topics FOCIL introduces new global topics for inclusion list and inclusion list aggregate. -###### `inclusion_list` +###### `local_inclusion_list` -This topic is used to propagate execution inclusion list as `SignedInclusionList`. -The following validations MUST pass before forwarding the `inclusion_list` on the network, assuming the alias `message = signed_inclusion_list.message`: +This topic is used to propagate signed local inclusion list as `SignedLocalInclusionList`. +The following validations MUST pass before forwarding the `local_inclusion_list` on the network, assuming the alias `message = signed_local_inclusion_list.message`: -- _[REJECT]_ The slot `message.slot` is equal to current slot + 1 +- _[REJECT]_ The slot `message.slot` is equal to current slot. - _[REJECT]_ The transactions `message.transactions` length is within upperbound `MAX_TRANSACTIONS_PER_INCLUSION_LIST`. - _[REJECT]_ The summaries `message.summaries` length is within upperbound `MAX_TRANSACTIONS_PER_INCLUSION_LIST`. +- _[IGNORE]_ The `message` is the first valid message received from the validator with index `message.validate_index`. - _[IGNORE]_ The block hash `message.parent_block_hash` is a known execution payload in fork choice. - _[REJECT]_ The signature of `inclusion_list.signature` is valid with respect to the validator index. - _[REJECT]_ The validator index is within the inclusion list committee in `get_inclusion_list_committee(state)`. The `state` is the head state corresponding to processing the block up to the current slot as determined by the fork choice. -###### `inclusion_list_aggregate` +###### `inclusion_summary_aggregate` -# TODO +This topic is used to propagate signed inclusion list aggregate as `SignedInclusionListAggregates`. +The following validations MUST pass before forwarding the `inclusion_summary_aggregates` on the network: + +- _[REJECT]_ The slot `signed_inclusion_list_aggregates.slot` is equal to current slot + 1. +- _[REJECT]_ The signature of `signed_inclusion_list_aggregates.signature` is valid with respect to the proposer index. +- _[REJECT]_ The proposer index `signed_inclusion_list_aggregates.proposer_index` is the proposer for slot. \ No newline at end of file diff --git a/specs/_features/focil/validator.md b/specs/_features/focil/validator.md new file mode 100644 index 0000000000..d07d888298 --- /dev/null +++ b/specs/_features/focil/validator.md @@ -0,0 +1,129 @@ +# FOCIL -- Honest Validator + +## Table of contents + + + + + +- [Introduction](#introduction) +- [Prerequisites](#prerequisites) +- [Protocol](#protocol) + - [`ExecutionEngine`](#executionengine) +- [New inclusion list committee assignment](#new-inclusion-list-committee-assignment) + - [Lookahead](#lookahead) +- [New proposer duty](#new-proposer-duty) + - [Inclusion summary aggregate release](#inclusion-summary-aggregate-release) + - [Block proposal](#block-proposal) + - [Constructing the new `InclusionSummaryAggregate` field in `BeaconBlockBody`](#constructing-the-new-inclusionsummaryaggregate-field-in--beaconblockbody) +- [New inclusion list committee duty](#new-inclusion-list-committee-duty) + - [Constructing a local inclusion list](#constructing-a-local-inclusion-list) + + + + +## Introduction + +This document represents the changes to be made in the code of an "honest validator" to implement FOCIL. + +## Prerequisites + +This document is an extension of the [Electra -- Honest Validator](../../electra/validator.md) guide. +All behaviors and definitions defined in this document, and documents it extends, carry over unless explicitly noted or overridden. + +All terminology, constants, functions, and protocol mechanics defined in the updated Beacon Chain doc of [FOCIL](./beacon-chain.md) are requisite for this document and used throughout. +Please see related Beacon Chain doc before continuing and use them as a reference throughout. + +## Protocol + +### `ExecutionEngine` + +*Note*: `engine_getInclusionListV1` and `engine_newInclusionListV1` functions are added to the `ExecutionEngine` protocol for use as a validator. + +The body of these function is implementation dependent. The Engine API may be used to implement it with an external execution engine. + +## New inclusion list committee assignment + +A validator may be a member of the new Inclusion List Committee (ILC) for a given slot. To check for ILC assignments the validator uses the helper `get_ilc_assignment(state, epoch, validator_index)` where `epoch <= next_epoch`. + +ILC selection is only stable within the context of the current and next epoch. + +```python +def get_ilc_assignment( + state: BeaconState, + epoch: Epoch, + validator_index: ValidatorIndex) -> Optional[Slot]: + """ + Returns the slot during the requested epoch in which the validator with index `validator_index` + is a member of the ILC. Returns None if no assignment is found. + """ + next_epoch = Epoch(get_current_epoch(state) + 1) + assert epoch <= next_epoch + + start_slot = compute_start_slot_at_epoch(epoch) + for slot in range(start_slot, start_slot + SLOTS_PER_EPOCH): + if validator_index in get_inclusion_list_committee(state, Slot(slot)): + return Slot(slot) + return None +``` + +### Lookahead + +`get_ilc_assignment` should be called at the start of each epoch to get the assignment for the next epoch (`current_epoch + 1`). A validator should plan for future assignments by noting their assigned ILC slot. + +## New proposer duty + +### Inclusion summary aggregates release + +Proposer has to release `signed_inclusion_summary_aggregates` at `3 * SECONDS_PER_SLOT // 4` seconds into the slot. The proposer will have to: +- Listen to the `inclusion_list` gossip global topic until `3 * SECONDS_PER_SLOT // 4` seconds into the slot. +- Gather all observed local inclusion lists, ensuring they meet the verification criteria specified in the local inclusion list gossip validation and `on_local_inclusion_list` sections. This requires: + - The `message.parent_hash` must match the local fork choice head view. + - The `message.slot` must be exactly one slot before the current proposing slot. + - The `message.summaries` and `message.transactions` must pass `engine_newPayloadV4` validation. +- The proposer aggregates all `local_inclusion_list` data into an `inclusion_summary_aggregates`, focusing only on the `InclusionSummary` field from the `LocalInclusionList`. + - To aggregate, the proposer fills the `aggregation_bits` field by using the relative position of the validator indices with respect to the ILC obtained from `get_inclusion_list_committee(state, proposing_slot - 1)`. +- The proposer signs the `inclusion_summary_aggregates` using helper `get_inclusion_summary_aggregates` and constructs a `signed_inclusion_aggregates_summary`. + +```python +def get_inclusion_summary_aggregate( + state: BeaconState, inclusion_summary_aggregates: InclusionSummaryAggregates, privkey: int) -> BLSSignature: + domain = get_domain(state, DOMAIN_BEACON_PROPOSER), compute_epoch_at_slot(proposer_slot)) + signing_root = compute_signing_root(inclusion_summary_aggregates, domain) + return bls.Sign(privkey, signing_root) +``` + +### Block proposal + +Validators are still expected to propose `SignedBeaconBlock` at the beginning of any slot during which `is_proposer(state, validator_index)` returns `true`. The mechanism to prepare this beacon block and related sidecars differs from previous forks as follows: + +#### Constructing the new `InclusionSummaryAggregate` field in `BeaconBlockBody` + +Proposer has to include a valid `inclusion_summary_aggregate` into the block body. The proposer will have to +* Proposer uses perviously constructed `InclusionSummaryAggregate` and include it in the beacon block body. + +## New inclusion list committee duty + +Some validators are selected to submit local inclusion list. Validators should call `get_ilc_assignment` at the beginning of an epoch to be prepared to submit their local inclusion list during the next epoch. + +A validator should create and broadcast the `signed_inclusion_list` to the global `inclusion_list` subnet at the `SECONDS_PER_SLOT * 2 // 2` seconds of `slot`. + +#### Constructing a local inclusion list + +The validator creates the `signed_local_inclusion_list` as follows: +- First, the validator creates the `local_inclusion_list`. +- Set `local_inclusion_list.slot` to the assigned slot returned by `get_ilc_assignment`. +- Set `local_inclusion_list.validator_index` to the validator's index. +- Set `local_inclusion_list.parent_hash` to the block hash of the fork choice head. +- Set `local_inclusion_list.summaries` and `local_inclusion_list.transactions` using the response from `engine_getInclusionListV1` from the execution layer client. +- Sign the `local_inclusion_list` using the helper `get_inclusion_list_signature` and obtain the `signature`. +- Set `signed_local_inclusion_list.message` to `local_inclusion_list`. +- Set `signed_local_inclusion_list.signature` to `signature`. + +```python +def get_inclusion_list_signature( + state: BeaconState, inclusion_list: LocalInclusionList, privkey: int) -> BLSSignature: + domain = get_domain(state, DOMAIN_IL_COMMITTEE, compute_epoch_at_slot(inclusion_list.slot)) + signing_root = compute_signing_root(inclusion_list, domain) + return bls.Sign(privkey, signing_root) +``` \ No newline at end of file From a2b68a764303e9ea842c18bde5d1db70a3042b84 Mon Sep 17 00:00:00 2001 From: terence tsao Date: Tue, 17 Sep 2024 10:03:20 -0700 Subject: [PATCH 06/23] Review feedback --- specs/_features/focil/beacon-chain.md | 41 +++++++++++++------------- specs/_features/focil/engine-api.md | 8 ++--- specs/_features/focil/p2p-interface.md | 10 +++---- specs/_features/focil/validator.md | 10 +++---- 4 files changed, 35 insertions(+), 34 deletions(-) diff --git a/specs/_features/focil/beacon-chain.md b/specs/_features/focil/beacon-chain.md index 0c518533da..cee4b2b67f 100644 --- a/specs/_features/focil/beacon-chain.md +++ b/specs/_features/focil/beacon-chain.md @@ -18,18 +18,19 @@ - [`LocalInclusionList`](#localinclusionlist) - [`SignedLocalInclusionList`](#signedlocalinclusionlist) - [`InclusionSummaryAggregate`](#inclusionsummaryaggregate) - - [`IndexedInclusionListAggregate`](#indexedinclusionlistaggregate) + - [`IndexedInclusionSummaryAggregate`](#indexedinclusionsummaryaggregate) + - [`InclusionSummaryAggregates`](#inclusionsummaryaggregates) - [Modified containers](#modified-containers) - [`BeaconBlockBody`](#beaconblockbody) - [Beacon State accessors](#beacon-state-accessors) - [`get_inclusion_list_committee`](#get_inclusion_list_committee) - [Beacon State accessors](#beacon-state-accessors-1) - - [New `get_inclusion_list_aggregate_indices`](#new-get_inclusion_list_aggregate_indices) - - [New `get_indexed_inclusion_list_aggregate`](#new-get_indexed_inclusion_list_aggregate) + - [New `get_inclusion_summary_aggregates_signature_indices`](#new-get_inclusion_summary_aggregates_signature_indices) + - [New `get_indexed_inclusion_summary_aggregate`](#new-get_indexed_inclusion_summary_aggregate) - [Beacon chain state transition function](#beacon-chain-state-transition-function) - [Execution engine](#execution-engine) - [Engine APIs](#engine-apis) - - [New `verify_and_notify_new_inclusion_list`](#new-verify_and_notify_new_inclusion_list) + - [New `verify_and_notify_local_inclusion_list`](#new-verify_and_notify_local_inclusion_list) - [Block processing](#block-processing) - [Operations](#operations) - [Modified `process_operations`](#modified-process_operations) @@ -41,7 +42,7 @@ ## Introduction -This is the beacon chain specification to add a committee-based inclusion list mechanism to allow forced transaction inclusion. Refers to [Ethresearch](https://ethresear.ch/t/fork-choice-enforced-inclusion-lists-focil-a-simple-committee-based-inclusion-list-proposal/19870/1) +This is the beacon chain specification to add a fork-choice enforced, committee-based inclusion list (FOCIL) mechanism to allow forced transaction inclusion. Refers to [Ethresearch](https://ethresear.ch/t/fork-choice-enforced-inclusion-lists-focil-a-simple-committee-based-inclusion-list-proposal/19870/1) *Note:* This specification is built upon [Electra](../../electra/beacon_chain.md) and is under active development. @@ -59,7 +60,7 @@ This is the beacon chain specification to add a committee-based inclusion list m | Name | Value | | - | - | -| `IL_COMMITTEE_SIZE` | `uint64(2**9)` (=512) # (New in FOCIL) | +| `IL_COMMITTEE_SIZE` | `uint64(2**9)` (=256) # (New in FOCIL) | ### Execution @@ -108,10 +109,10 @@ class InclusionSummaryAggregate(Container): signature: BLSSignature ``` -#### `IndexedInclusionListAggregate` +#### `IndexedInclusionSummaryAggregate` ```python -class IndexedInclusionListAggregate(Container): +class IndexedInclusionSummaryAggregate(Container): validator_indices: List[ValidatorIndex, IL_COMMITTEE_SIZE] summary: InclusionSummary signature: BLSSignature @@ -121,14 +122,14 @@ class IndexedInclusionListAggregate(Container): ```python class InclusionSummaryAggregates(Container): - List[InclusionSummaryAggregate, MAX_TRANSACTIONS_PER_INCLUSION_LIST] + List[InclusionSummaryAggregate, MAX_TRANSACTIONS_PER_INCLUSION_LIST * IL_COMMITTEE_SIZE] ``` ### Modified containers #### `BeaconBlockBody` -**Note:** The Beacon Block body is modified to contain a new `inclusion_summary_aggregate` field. +**Note:** The Beacon Block body is modified to contain a new `inclusion_summary_aggregates` field. ```python class BeaconBlockBody(Container): @@ -168,10 +169,10 @@ def get_inclusion_list_committee(state: BeaconState, slot: Slot) -> Vector[Valid ``` ### Beacon State accessors -#### New `get_inclusion_list_aggregate_indices` +#### New `get_inclusion_summary_aggregates_signature_indices` ```python -def get_inclusion_list_aggregate_indices(state: BeaconState, +def get_inclusion_summary_aggregates_signature_indices(state: BeaconState, inclusion_summary_aggregate: InclusionSummaryAggregate) -> Set[ValidatorIndex]: """ Return the set of indices corresponding to ``inclusion_summary_aggregate``. @@ -180,17 +181,17 @@ def get_inclusion_list_aggregate_indices(state: BeaconState, return set(index for i, index in enumerate(il_committee) if inclusion_summary_aggregate.aggregation_bits[i]) ``` -#### New `get_indexed_inclusion_list_aggregate` +#### New `get_indexed_inclusion_summary_aggregate` ```python -def get_indexed_inclusion_list_aggregate(state: BeaconState, - inclusion_summary_aggregate: InclusionSummaryAggregate) -> IndexedInclusionListAggregate: +def get_indexed_inclusion_summary_aggregate(state: BeaconState, + inclusion_summary_aggregate: InclusionSummaryAggregate) -> IndexedInclusionSummaryAggregate: """ Return the indexed inclusion list aggregate corresponding to ``inclusion_summary_aggregate``. """ - indices = get_inclusion_list_aggregate_indices(state) + indices = get_inclusion_summary_aggregates_signature_indices(state, inclusion_summary_aggregate) - return IndexedInclusionListAggregate( + return IndexedInclusionSummaryAggregate( validator_indices=sorted(indices), summary=inclusion_summary_aggregate.summary, signature=inclusion_summary_aggregate.signature, @@ -203,10 +204,10 @@ def get_indexed_inclusion_list_aggregate(state: BeaconState, #### Engine APIs -##### New `verify_and_notify_new_inclusion_list` +##### New `verify_and_notify_local_inclusion_list` ```python -def verify_and_notify_new_inclusion_list(self: ExecutionEngine, +def verify_and_notify_local_inclusion_list(self: ExecutionEngine, inclusion_list: LocalInclusionList) -> bool: """ Return ``True`` if and only if the transactions in the inclusion list can be successfully executed @@ -270,7 +271,7 @@ def process_inclusion_list_aggregate( ) -> None: # Verify inclusion list aggregate signature - indexed_inclusion_list_aggregate = get_indexed_inclusion_list_aggregate(state, inclusion_summary_aggregate) + indexed_inclusion_list_aggregate = get_indexed_inclusion_summary_aggregate(state, inclusion_summary_aggregate) assert is_valid_indexed_inclusion_list_aggregate(state, indexed_inclusion_list_aggregate) # TODO: Reward inclusion list aggregate participants diff --git a/specs/_features/focil/engine-api.md b/specs/_features/focil/engine-api.md index a8e6394588..a29e96cfc6 100644 --- a/specs/_features/focil/engine-api.md +++ b/specs/_features/focil/engine-api.md @@ -21,7 +21,7 @@ Warning: This file should be placed in https://github.com/ethereum/execution-api - [Request](#request) - [Response](#response) - [Specification](#specification) - - [engine_newInclusionListV1](#engine_newinclusionlistv1) + - [engine_newLocalInclusionListV1](#engine_newlocalinclusionlistv1) - [Request](#request-1) - [Response](#response-1) - [Specification](#specification-1) @@ -36,7 +36,7 @@ Warning: This file should be placed in https://github.com/ethereum/execution-api | Name | Value | | - | - | -| `INCLUSION_LIST_MAX_GAS` | `uint64(4194304) = 2**22` | +| `LOCAL_INCLUSION_LIST_MAX_GAS` | `uint64(4194304) = 2**22` | ## Structures @@ -86,11 +86,11 @@ This method follows the same specification as [`engine_newPayloadV3`](./cancun.m 1. Client software **MUST** return `-38005: Unsupported fork` error if the `timestamp` of the payload does not fall within the time frame of the Prague fork. -### engine_newInclusionListV1 +### engine_newLocalInclusionListV1 #### Request -* method: `engine_newInclusionListV1` +* method: `engine_newLocalInclusionListV1` * params: 1. `parent_hash`: `DATA`, 32 Bytes - parent hash which this inclusion list is built upon. 2. `inclusionListSummaryList`: `InclusionListSummaryListV1` - Summary. diff --git a/specs/_features/focil/p2p-interface.md b/specs/_features/focil/p2p-interface.md index e1fc5adc7f..da156a8d5a 100644 --- a/specs/_features/focil/p2p-interface.md +++ b/specs/_features/focil/p2p-interface.md @@ -12,7 +12,7 @@ This document contains the consensus-layer networking specification for FOCIL. - [Topics and messages](#topics-and-messages-1) - [Global topics](#global-topics) - [`local_inclusion_list`](#local_inclusion_list) - - [`inclusion_summary_aggregate`](#inclusion_summary_aggregate) + - [`inclusion_summary_aggregates`](#inclusion_summary_aggregates) @@ -21,7 +21,7 @@ This document contains the consensus-layer networking specification for FOCIL. #### `SignedInclusionListAggregate` ```python -class SignedInclusionListAggregates(Container): +class SignedInclusionSummaryAggregates(Container): slot: Slot proposer_index: ValidatorIndex message: InclusionSummaryAggregates @@ -48,7 +48,7 @@ The new topics along with the type of the `data` field of a gossipsub message ar | Name | Message Type | |-------------------------------|------------------------------------------------------| | `local_inclusion_list` | `SignedLocalInclusionList` [New in FOCIL] | -| `inclusion_summary_aggregates` | `SignedInclusionListAggregates` [New in FOCIL] | +| `inclusion_summary_aggregates` | `SignedInclusionSummaryAggregates` [New in FOCIL] | ##### Global topics @@ -67,9 +67,9 @@ The following validations MUST pass before forwarding the `local_inclusion_list` - _[REJECT]_ The signature of `inclusion_list.signature` is valid with respect to the validator index. - _[REJECT]_ The validator index is within the inclusion list committee in `get_inclusion_list_committee(state)`. The `state` is the head state corresponding to processing the block up to the current slot as determined by the fork choice. -###### `inclusion_summary_aggregate` +###### `inclusion_summary_aggregates` -This topic is used to propagate signed inclusion list aggregate as `SignedInclusionListAggregates`. +This topic is used to propagate signed inclusion list aggregate as `SignedInclusionSummaryAggregates`. The following validations MUST pass before forwarding the `inclusion_summary_aggregates` on the network: - _[REJECT]_ The slot `signed_inclusion_list_aggregates.slot` is equal to current slot + 1. diff --git a/specs/_features/focil/validator.md b/specs/_features/focil/validator.md index d07d888298..bbe37ecf07 100644 --- a/specs/_features/focil/validator.md +++ b/specs/_features/focil/validator.md @@ -13,7 +13,7 @@ - [New inclusion list committee assignment](#new-inclusion-list-committee-assignment) - [Lookahead](#lookahead) - [New proposer duty](#new-proposer-duty) - - [Inclusion summary aggregate release](#inclusion-summary-aggregate-release) + - [Inclusion summary aggregates release](#inclusion-summary-aggregates-release) - [Block proposal](#block-proposal) - [Constructing the new `InclusionSummaryAggregate` field in `BeaconBlockBody`](#constructing-the-new-inclusionsummaryaggregate-field-in--beaconblockbody) - [New inclusion list committee duty](#new-inclusion-list-committee-duty) @@ -83,10 +83,10 @@ Proposer has to release `signed_inclusion_summary_aggregates` at `3 * SECONDS_PE - The `message.summaries` and `message.transactions` must pass `engine_newPayloadV4` validation. - The proposer aggregates all `local_inclusion_list` data into an `inclusion_summary_aggregates`, focusing only on the `InclusionSummary` field from the `LocalInclusionList`. - To aggregate, the proposer fills the `aggregation_bits` field by using the relative position of the validator indices with respect to the ILC obtained from `get_inclusion_list_committee(state, proposing_slot - 1)`. -- The proposer signs the `inclusion_summary_aggregates` using helper `get_inclusion_summary_aggregates` and constructs a `signed_inclusion_aggregates_summary`. +- The proposer signs the `inclusion_summary_aggregates` using helper `get_inclusion_summary_aggregates_signatures` and constructs a `signed_inclusion_aggregates_summary`. ```python -def get_inclusion_summary_aggregate( +def get_inclusion_summary_aggregates_signature( state: BeaconState, inclusion_summary_aggregates: InclusionSummaryAggregates, privkey: int) -> BLSSignature: domain = get_domain(state, DOMAIN_BEACON_PROPOSER), compute_epoch_at_slot(proposer_slot)) signing_root = compute_signing_root(inclusion_summary_aggregates, domain) @@ -99,8 +99,8 @@ Validators are still expected to propose `SignedBeaconBlock` at the beginning of #### Constructing the new `InclusionSummaryAggregate` field in `BeaconBlockBody` -Proposer has to include a valid `inclusion_summary_aggregate` into the block body. The proposer will have to -* Proposer uses perviously constructed `InclusionSummaryAggregate` and include it in the beacon block body. +Proposer has to include a valid `inclusion_summary_aggregates` into the block body. The proposer will have to +* Proposer uses perviously constructed `InclusionSummaryAggregates` and include it in the beacon block body. ## New inclusion list committee duty From fb4a3c31c7ce7eea52bf9ef6861e4c0602b93529 Mon Sep 17 00:00:00 2001 From: terence tsao Date: Tue, 17 Sep 2024 14:29:26 -0700 Subject: [PATCH 07/23] Update forkchoice --- specs/_features/focil/fork-choice.md | 93 +++++++++++++++++++++++++- specs/_features/focil/p2p-interface.md | 8 +++ 2 files changed, 98 insertions(+), 3 deletions(-) diff --git a/specs/_features/focil/fork-choice.md b/specs/_features/focil/fork-choice.md index f53575d168..f5a0defdce 100644 --- a/specs/_features/focil/fork-choice.md +++ b/specs/_features/focil/fork-choice.md @@ -1,10 +1,97 @@ +# FOCIL -- Fork Choice + +## Table of contents + -**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* -- [FOCIL -- Fork Choice](#focil----fork-choice) +- [Introduction](#introduction) +- [Helpers](#helpers) + - [New `evaluate_inclusion_summary_aggregates`](#new-evaluate_inclusion_summary_aggregates) + - [Modified `Store`](#modified-store) + - [New `on_local_inclusion_list`](#new-on_local_inclusion_list) + -# FOCIL -- Fork Choice +## Introduction + +This is the modification of the fork choice accompanying the FOCIL upgrade. + +## Helpers + +### New `evaluate_inclusion_summary_aggregates` + +```python +def evaluate_inclusion_summary_aggregates(store: Store, inclusion_summary_aggregates: InclusionSummaryAggregates) -> bool: + """ + Return ``True`` if and only if the input ``inclusion_summary_aggregates`` satifies evaluation. + """ + ... +``` + +TODO: Apply `evaluate_inclusion_summary_aggregates`, which can be part of the import rule for head filtering. Where it should be applied requires further analysis, and we won't specify the design at this moment. + +### Modified `Store` +**Note:** `Store` is modified to track the seen local inclusion lists before inclusion list aggregate cutoff interval. + +```python +@dataclass +class Store(object): + time: uint64 + genesis_time: uint64 + justified_checkpoint: Checkpoint + finalized_checkpoint: Checkpoint + unrealized_justified_checkpoint: Checkpoint + unrealized_finalized_checkpoint: Checkpoint + proposer_boost_root: Root + equivocating_indices: Set[ValidatorIndex] + blocks: Dict[Root, BeaconBlock] = field(default_factory=dict) + block_states: Dict[Root, BeaconState] = field(default_factory=dict) + block_timeliness: Dict[Root, boolean] = field(default_factory=dict) + checkpoint_states: Dict[Checkpoint, BeaconState] = field(default_factory=dict) + latest_messages: Dict[ValidatorIndex, LatestMessage] = field(default_factory=dict) + unrealized_justifications: Dict[Root, Checkpoint] = field(default_factory=dict) + inclusion_summary_aggregates: Dict[Root, InclusionSummaryAggregates] = field(default_factory=dict) # [New in FOCIL] + +### New `notify_local_inclusion_list` + +```python +def notify_local_inclusion_list(store: Store, signed_inclusion_list: SignedLocalInclusionList) -> None: + """ + ``notify_local_inclusion_list`` sends inclusion list to EL to verify and import it to fork choice store. + """ + assert self.verify_and_notify_local_inclusion_list(inclusion_list) + + on_local_inclusion_list(store, signed_inclusion_list) +``` + + +### New `on_local_inclusion_list` + +`on_local_inclusion_list` is called to import `signed_local_inclusion_list` to the fork choice store. + +```python +def on_local_inclusion_list( + store: Store, signed_inclusion_list: SignedLocalInclusionList) -> None: + """ + ``on_local_inclusion_list`` verify the inclusion list before import it to fork choice store. + """ + message = signed_inclusion_list.message + # Verify inclusion list slot is bouded to the current slot + assert get_current_slot(store) != message.slot + + state = store.block_states[message.beacon_block_root] + ilc = get_inclusion_list_committee(state, message.slot) + # Verify inclusion list validator is part of the committee + assert message.validator_index in ilc + + # Verify inclusion list signature + assert is_valid_local_inclusion_list_signature(state, signed_inclusion_list) + + parent_hash = message.parent_hash + # TODO: Convert inclusion list to aggregate format and properly aggregate them given existing entries + aggregates = InclusionSummaryAggregate() + store.inclusion_summary_aggregates[parent_hash] = aggregate +``` \ No newline at end of file diff --git a/specs/_features/focil/p2p-interface.md b/specs/_features/focil/p2p-interface.md index da156a8d5a..b14dc1a8ad 100644 --- a/specs/_features/focil/p2p-interface.md +++ b/specs/_features/focil/p2p-interface.md @@ -5,6 +5,7 @@ This document contains the consensus-layer networking specification for FOCIL. +- [Time parameters](#time-parameters) - [Containers](#containers) - [`SignedInclusionListAggregate`](#signedinclusionlistaggregate) - [The gossip domain: gossipsub](#the-gossip-domain-gossipsub) @@ -16,6 +17,12 @@ This document contains the consensus-layer networking specification for FOCIL. +### Time parameters + +| Name | Value | Unit | Duration | +| - | - | :-: | :-: | +| `LOCAL_INCLUSION_CUT_OFF` | `uint64(9)` | seconds | 9 seconds | + ### Containers #### `SignedInclusionListAggregate` @@ -60,6 +67,7 @@ This topic is used to propagate signed local inclusion list as `SignedLocalInclu The following validations MUST pass before forwarding the `local_inclusion_list` on the network, assuming the alias `message = signed_local_inclusion_list.message`: - _[REJECT]_ The slot `message.slot` is equal to current slot. +- _[IGNORE]_ The current time is `LOCAL_INCLUSION_CUT_OFF` seconds into the slot. - _[REJECT]_ The transactions `message.transactions` length is within upperbound `MAX_TRANSACTIONS_PER_INCLUSION_LIST`. - _[REJECT]_ The summaries `message.summaries` length is within upperbound `MAX_TRANSACTIONS_PER_INCLUSION_LIST`. - _[IGNORE]_ The `message` is the first valid message received from the validator with index `message.validate_index`. From c7a056bc3d3a67bd7ccc7261d77d795da1effbb3 Mon Sep 17 00:00:00 2001 From: terence tsao Date: Fri, 20 Sep 2024 19:45:02 -0700 Subject: [PATCH 08/23] Update based on latest discussion. No more IL aggregate --- specs/_features/focil/beacon-chain.md | 186 +++---------------------- specs/_features/focil/engine-api.md | 32 +---- specs/_features/focil/fork-choice.md | 14 -- specs/_features/focil/p2p-interface.md | 23 --- 4 files changed, 20 insertions(+), 235 deletions(-) diff --git a/specs/_features/focil/beacon-chain.md b/specs/_features/focil/beacon-chain.md index cee4b2b67f..a36743120e 100644 --- a/specs/_features/focil/beacon-chain.md +++ b/specs/_features/focil/beacon-chain.md @@ -17,25 +17,10 @@ - [`InclusionSummary`](#inclusionsummary) - [`LocalInclusionList`](#localinclusionlist) - [`SignedLocalInclusionList`](#signedlocalinclusionlist) - - [`InclusionSummaryAggregate`](#inclusionsummaryaggregate) - - [`IndexedInclusionSummaryAggregate`](#indexedinclusionsummaryaggregate) - - [`InclusionSummaryAggregates`](#inclusionsummaryaggregates) - - [Modified containers](#modified-containers) - - [`BeaconBlockBody`](#beaconblockbody) + - [Predicates](#predicates) + - [New `is_valid_local_inclusion_list_signature`](#new-is_valid_local_inclusion_list_signature) - [Beacon State accessors](#beacon-state-accessors) - [`get_inclusion_list_committee`](#get_inclusion_list_committee) - - [Beacon State accessors](#beacon-state-accessors-1) - - [New `get_inclusion_summary_aggregates_signature_indices`](#new-get_inclusion_summary_aggregates_signature_indices) - - [New `get_indexed_inclusion_summary_aggregate`](#new-get_indexed_inclusion_summary_aggregate) -- [Beacon chain state transition function](#beacon-chain-state-transition-function) - - [Execution engine](#execution-engine) - - [Engine APIs](#engine-apis) - - [New `verify_and_notify_local_inclusion_list`](#new-verify_and_notify_local_inclusion_list) - - [Block processing](#block-processing) - - [Operations](#operations) - - [Modified `process_operations`](#modified-process_operations) - - [Inclusion list aggregate](#inclusion-list-aggregate) - - [New `process_inclusion_list_aggregate`](#new-process_inclusion_list_aggregate) @@ -87,6 +72,7 @@ class InclusionSummary(Container): class LocalInclusionList(Container): slot: Slot validator_index: ValidatorIndex + parent_root: Root parent_hash: Hash32 summaries: List[InclusionSummary, MAX_TRANSACTIONS_PER_INCLUSION_LIST] transactions: List[Transaction, MAX_TRANSACTIONS_PER_INCLUSION_LIST] @@ -100,52 +86,23 @@ class SignedLocalInclusionList(Container): signature: BLSSignature ``` -#### `InclusionSummaryAggregate` +### Predicates -```python -class InclusionSummaryAggregate(Container): - aggregation_bits: Bitvector[IL_COMMITTEE_SIZE] - summary: InclusionSummary - signature: BLSSignature -``` - -#### `IndexedInclusionSummaryAggregate` - -```python -class IndexedInclusionSummaryAggregate(Container): - validator_indices: List[ValidatorIndex, IL_COMMITTEE_SIZE] - summary: InclusionSummary - signature: BLSSignature -``` - -#### `InclusionSummaryAggregates` - -```python -class InclusionSummaryAggregates(Container): - List[InclusionSummaryAggregate, MAX_TRANSACTIONS_PER_INCLUSION_LIST * IL_COMMITTEE_SIZE] -``` - -### Modified containers - -#### `BeaconBlockBody` - -**Note:** The Beacon Block body is modified to contain a new `inclusion_summary_aggregates` field. +#### New `is_valid_local_inclusion_list_signature` ```python -class BeaconBlockBody(Container): - randao_reveal: BLSSignature - eth1_data: Eth1Data # Eth1 data vote - graffiti: Bytes32 # Arbitrary data - # Operations - proposer_slashings: List[ProposerSlashing, MAX_PROPOSER_SLASHINGS] - attester_slashings: List[AttesterSlashing, MAX_ATTESTER_SLASHINGS_ELECTRA] - attestations: List[Attestation, MAX_ATTESTATIONS_ELECTRA] - deposits: List[Deposit, MAX_DEPOSITS] - voluntary_exits: List[SignedVoluntaryExit, MAX_VOLUNTARY_EXITS] - sync_aggregate: SyncAggregate - bls_to_execution_changes: List[SignedBLSToExecutionChange, MAX_BLS_TO_EXECUTION_CHANGES] - # FOCIL - inclusion_summary_aggregates: InclusionSummaryAggregates # [New in FOCIL] +def is_valid_local_inclusion_list_signature( + state: BeaconState, + signed_local_inclusion_list: SignedLocalInclusionList) -> bool: + """ + Check if ``signed_local_inclusion_list`` has a valid signature + """ + message = signed_local_inclusion_list.message + index = message.validator_index + pubkey = state.validators[index].pubkey + domain = get_domain(state, IL_COMMITTEE_SIZE, compute_epoch_at_slot(message.slot)) + signing_root = compute_signing_root(message, domain) + return bls.FastAggregateVerify(pubkey, signing_root, signed_local_inclusion_list.signature) ``` ### Beacon State accessors @@ -167,112 +124,3 @@ def get_inclusion_list_committee(state: BeaconState, slot: Slot) -> Vector[Valid validator_indices += beacon_committee[:members_per_committee] return validator_indices ``` -### Beacon State accessors - -#### New `get_inclusion_summary_aggregates_signature_indices` - -```python -def get_inclusion_summary_aggregates_signature_indices(state: BeaconState, - inclusion_summary_aggregate: InclusionSummaryAggregate) -> Set[ValidatorIndex]: - """ - Return the set of indices corresponding to ``inclusion_summary_aggregate``. - """ - il_committee = get_inclusion_list_committee(state, state.slot) - return set(index for i, index in enumerate(il_committee) if inclusion_summary_aggregate.aggregation_bits[i]) -``` - -#### New `get_indexed_inclusion_summary_aggregate` - -```python -def get_indexed_inclusion_summary_aggregate(state: BeaconState, - inclusion_summary_aggregate: InclusionSummaryAggregate) -> IndexedInclusionSummaryAggregate: - """ - Return the indexed inclusion list aggregate corresponding to ``inclusion_summary_aggregate``. - """ - indices = get_inclusion_summary_aggregates_signature_indices(state, inclusion_summary_aggregate) - - return IndexedInclusionSummaryAggregate( - validator_indices=sorted(indices), - summary=inclusion_summary_aggregate.summary, - signature=inclusion_summary_aggregate.signature, - ) -``` - -## Beacon chain state transition function - -### Execution engine - -#### Engine APIs - -##### New `verify_and_notify_local_inclusion_list` - -```python -def verify_and_notify_local_inclusion_list(self: ExecutionEngine, - inclusion_list: LocalInclusionList) -> bool: - """ - Return ``True`` if and only if the transactions in the inclusion list can be successfully executed - starting from the execution state corresponding to the `parent_hash` in the inclusion list - summary. - """ - ... -``` - -### Block processing - -```python -def process_block(state: BeaconState, block: BeaconBlock) -> None: - process_block_header(state, block) - process_withdrawals(state, block.body.execution_payload) - process_execution_payload(state, block.body, EXECUTION_ENGINE) - process_randao(state, block.body) - process_eth1_data(state, block.body) - process_operations(state, block.body) # [Modified in FOCIL] - process_sync_aggregate(state, block.body.sync_aggregate) -``` - -#### Operations - -##### Modified `process_operations` - -*Note*: The function `process_operations` is modified to support all of the new functionality in FOCIL. - -```python -def process_operations(state: BeaconState, body: BeaconBlockBody) -> None: - eth1_deposit_index_limit = min(state.eth1_data.deposit_count, state.deposit_requests_start_index) - if state.eth1_deposit_index < eth1_deposit_index_limit: - assert len(body.deposits) == min(MAX_DEPOSITS, eth1_deposit_index_limit - state.eth1_deposit_index) - else: - assert len(body.deposits) == 0 - - def for_ops(operations: Sequence[Any], fn: Callable[[BeaconState, Any], None]) -> None: - for operation in operations: - fn(state, operation) - - for_ops(body.proposer_slashings, process_proposer_slashing) - for_ops(body.attester_slashings, process_attester_slashing) - for_ops(body.attestations, process_attestation) - for_ops(body.deposits, process_deposit) - for_ops(body.voluntary_exits, process_voluntary_exit) - for_ops(body.bls_to_execution_changes, process_bls_to_execution_change) - for_ops(body.execution_payload.deposit_requests, process_deposit_request) - for_ops(body.execution_payload.withdrawal_requests, process_withdrawal_request) - for_ops(body.execution_payload.consolidation_requests, process_consolidation_request) - for_ops(body.inclusion_summary_aggregate, process_inclusion_list_aggregate) # [New in FOCIL] -``` - -##### Inclusion list aggregate - -###### New `process_inclusion_list_aggregate` - -```python -def process_inclusion_list_aggregate( - state: BeaconState, - inclusion_summary_aggregate: InclusionSummaryAggregate -) -> None: - - # Verify inclusion list aggregate signature - indexed_inclusion_list_aggregate = get_indexed_inclusion_summary_aggregate(state, inclusion_summary_aggregate) - assert is_valid_indexed_inclusion_list_aggregate(state, indexed_inclusion_list_aggregate) - - # TODO: Reward inclusion list aggregate participants -``` \ No newline at end of file diff --git a/specs/_features/focil/engine-api.md b/specs/_features/focil/engine-api.md index a29e96cfc6..506680c820 100644 --- a/specs/_features/focil/engine-api.md +++ b/specs/_features/focil/engine-api.md @@ -21,14 +21,10 @@ Warning: This file should be placed in https://github.com/ethereum/execution-api - [Request](#request) - [Response](#response) - [Specification](#specification) - - [engine_newLocalInclusionListV1](#engine_newlocalinclusionlistv1) + - [engine_getInclusionListV1](#engine_getinclusionlistv1) - [Request](#request-1) - [Response](#response-1) - [Specification](#specification-1) - - [engine_getInclusionListV1](#engine_getinclusionlistv1) - - [Request](#request-2) - - [Response](#response-2) - - [Specification](#specification-2) @@ -84,30 +80,8 @@ Refer to the response for [`engine_newPayloadV3`](./cancun.md#engine_newpayloadv This method follows the same specification as [`engine_newPayloadV3`](./cancun.md#engine_newpayloadv3) with the following changes: -1. Client software **MUST** return `-38005: Unsupported fork` error if the `timestamp` of the payload does not fall within the time frame of the Prague fork. - -### engine_newLocalInclusionListV1 - -#### Request - -* method: `engine_newLocalInclusionListV1` -* params: - 1. `parent_hash`: `DATA`, 32 Bytes - parent hash which this inclusion list is built upon. - 2. `inclusionListSummaryList`: `InclusionListSummaryListV1` - Summary. - 3. `inclusionListTransactions`: `Array of DATA` - Array of transaction objects, each object is a byte list (`DATA`) representing `TransactionType || TransactionPayload` or `LegacyTransaction` as defined in [EIP-2718](https://eips.ethereum.org/EIPS/eip-2718) -* timeout: 1s - -#### Response - -* result: [`InclusionListStatusV1`](./#inclusionliststatusv1). -* error: code and message set in case an exception happens while processing the inclusion list. - -#### Specification - -1. Client software **MUST** validate the inclusion list transactions are valid (correct nonce and sufficient base fee) given the parent block hash specified. If the parent block is not available, return false. -2. Client software **MUST** validate that the sum of inclusion list transactions gas does not exceed `INCLUSION_LIST_MAX_GAS`. -3. Client software **MUST** validate that the summary and transactions are the same length. -4. Client software **MUST** validate that the order of the summary entries matches the order of the transactions. +1. Client software **MUST** return `-38005: Unsupported fork` error if the `timestamp` of the payload does not fall within the time frame of the fork number. +2. Client software **MUST** return `TBD` if the payload does not satisfy inclusion list evaluation. ### engine_getInclusionListV1 diff --git a/specs/_features/focil/fork-choice.md b/specs/_features/focil/fork-choice.md index f5a0defdce..c2fa88f3ab 100644 --- a/specs/_features/focil/fork-choice.md +++ b/specs/_features/focil/fork-choice.md @@ -9,7 +9,6 @@ - [Helpers](#helpers) - [New `evaluate_inclusion_summary_aggregates`](#new-evaluate_inclusion_summary_aggregates) - [Modified `Store`](#modified-store) - - [New `on_local_inclusion_list`](#new-on_local_inclusion_list) @@ -53,18 +52,6 @@ class Store(object): latest_messages: Dict[ValidatorIndex, LatestMessage] = field(default_factory=dict) unrealized_justifications: Dict[Root, Checkpoint] = field(default_factory=dict) inclusion_summary_aggregates: Dict[Root, InclusionSummaryAggregates] = field(default_factory=dict) # [New in FOCIL] - -### New `notify_local_inclusion_list` - -```python -def notify_local_inclusion_list(store: Store, signed_inclusion_list: SignedLocalInclusionList) -> None: - """ - ``notify_local_inclusion_list`` sends inclusion list to EL to verify and import it to fork choice store. - """ - assert self.verify_and_notify_local_inclusion_list(inclusion_list) - - on_local_inclusion_list(store, signed_inclusion_list) -``` ### New `on_local_inclusion_list` @@ -91,7 +78,6 @@ def on_local_inclusion_list( parent_hash = message.parent_hash - # TODO: Convert inclusion list to aggregate format and properly aggregate them given existing entries aggregates = InclusionSummaryAggregate() store.inclusion_summary_aggregates[parent_hash] = aggregate ``` \ No newline at end of file diff --git a/specs/_features/focil/p2p-interface.md b/specs/_features/focil/p2p-interface.md index b14dc1a8ad..ec2a44557e 100644 --- a/specs/_features/focil/p2p-interface.md +++ b/specs/_features/focil/p2p-interface.md @@ -10,10 +10,8 @@ This document contains the consensus-layer networking specification for FOCIL. - [`SignedInclusionListAggregate`](#signedinclusionlistaggregate) - [The gossip domain: gossipsub](#the-gossip-domain-gossipsub) - [Topics and messages](#topics-and-messages) - - [Topics and messages](#topics-and-messages-1) - [Global topics](#global-topics) - [`local_inclusion_list`](#local_inclusion_list) - - [`inclusion_summary_aggregates`](#inclusion_summary_aggregates) @@ -37,17 +35,6 @@ class SignedInclusionSummaryAggregates(Container): ### The gossip domain: gossipsub -Some gossip meshes are upgraded in the fork of FOCIL to support upgraded types. - -#### Topics and messages - -Topics follow the same specification as in prior upgrades. - -The `beacon_block` topic is updated to support the modified type -| Name | Message Type | -| --- | --- | -| `beacon_block` | `SignedBeaconBlock` [modified in FOCIL] | - #### Topics and messages The new topics along with the type of the `data` field of a gossipsub message are given in this table: @@ -55,7 +42,6 @@ The new topics along with the type of the `data` field of a gossipsub message ar | Name | Message Type | |-------------------------------|------------------------------------------------------| | `local_inclusion_list` | `SignedLocalInclusionList` [New in FOCIL] | -| `inclusion_summary_aggregates` | `SignedInclusionSummaryAggregates` [New in FOCIL] | ##### Global topics @@ -74,12 +60,3 @@ The following validations MUST pass before forwarding the `local_inclusion_list` - _[IGNORE]_ The block hash `message.parent_block_hash` is a known execution payload in fork choice. - _[REJECT]_ The signature of `inclusion_list.signature` is valid with respect to the validator index. - _[REJECT]_ The validator index is within the inclusion list committee in `get_inclusion_list_committee(state)`. The `state` is the head state corresponding to processing the block up to the current slot as determined by the fork choice. - -###### `inclusion_summary_aggregates` - -This topic is used to propagate signed inclusion list aggregate as `SignedInclusionSummaryAggregates`. -The following validations MUST pass before forwarding the `inclusion_summary_aggregates` on the network: - -- _[REJECT]_ The slot `signed_inclusion_list_aggregates.slot` is equal to current slot + 1. -- _[REJECT]_ The signature of `signed_inclusion_list_aggregates.signature` is valid with respect to the proposer index. -- _[REJECT]_ The proposer index `signed_inclusion_list_aggregates.proposer_index` is the proposer for slot. \ No newline at end of file From f84556cd459395510465f35afb3d15438dd146e4 Mon Sep 17 00:00:00 2001 From: terence tsao Date: Thu, 26 Sep 2024 14:30:05 -0700 Subject: [PATCH 09/23] Update fork-choice.md --- specs/_features/focil/fork-choice.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/specs/_features/focil/fork-choice.md b/specs/_features/focil/fork-choice.md index c2fa88f3ab..d296438229 100644 --- a/specs/_features/focil/fork-choice.md +++ b/specs/_features/focil/fork-choice.md @@ -9,6 +9,7 @@ - [Helpers](#helpers) - [New `evaluate_inclusion_summary_aggregates`](#new-evaluate_inclusion_summary_aggregates) - [Modified `Store`](#modified-store) + - [New `on_local_inclusion_list`](#new-on_local_inclusion_list) @@ -17,6 +18,16 @@ This is the modification of the fork choice accompanying the FOCIL upgrade. +## Containers + +### New `inclusion_list_aggregate` + +```python +class InclusionSummaryAggregate(Container): + aggregation_bits: Bitvector[IL_COMMITTEE_SIZE] + summary: InclusionSummary +``` + ## Helpers ### New `evaluate_inclusion_summary_aggregates` @@ -80,4 +91,21 @@ def on_local_inclusion_list( aggregates = InclusionSummaryAggregate() store.inclusion_summary_aggregates[parent_hash] = aggregate + ilc_index = ilc.index(message.validator_index) + for summary in message.summaries: + matching_aggregate = None + for aggregate in store.inclusion_summary_aggregates[parent_hash]: + if aggregate.summary == summary: + matching_aggregate = aggregate + break + + if matching_aggregate: + matching_aggregate.aggregation_bits.flip(ilc_index_to_i) + else: + new_aggregate = InclusionSummaryAggregate( + aggregation_bits=Bitvector[IL_COMMITTEE_SIZE](), + summary=summary + ) + new_aggregate.aggregation_bits.flip(ilc_index_to_i) + store.inclusion_summary_aggregates[parent_hash].append(new_aggregate) ``` \ No newline at end of file From 2787b0d9d9d4e4a814d66e04e9f201558160217b Mon Sep 17 00:00:00 2001 From: terence tsao Date: Mon, 30 Sep 2024 10:11:30 -1000 Subject: [PATCH 10/23] Update per https://ethresear.ch/t/focil-cl-el-workflow/20526 --- specs/_features/focil/beacon-chain.md | 92 ++++++++++++++++++++++---- specs/_features/focil/engine-api.md | 66 +++++++++--------- specs/_features/focil/fork-choice.md | 73 ++++++++++---------- specs/_features/focil/p2p-interface.md | 18 +---- specs/_features/focil/validator.md | 45 +++++-------- 5 files changed, 164 insertions(+), 130 deletions(-) diff --git a/specs/_features/focil/beacon-chain.md b/specs/_features/focil/beacon-chain.md index a36743120e..c523fcdd6a 100644 --- a/specs/_features/focil/beacon-chain.md +++ b/specs/_features/focil/beacon-chain.md @@ -14,13 +14,19 @@ - [Execution](#execution) - [Containers](#containers) - [New containers](#new-containers) - - [`InclusionSummary`](#inclusionsummary) - [`LocalInclusionList`](#localinclusionlist) - [`SignedLocalInclusionList`](#signedlocalinclusionlist) - [Predicates](#predicates) - [New `is_valid_local_inclusion_list_signature`](#new-is_valid_local_inclusion_list_signature) - [Beacon State accessors](#beacon-state-accessors) - [`get_inclusion_list_committee`](#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 `notify_new_payload`](#modified-notify_new_payload) + - [Modified `verify_and_notify_new_payload`](#modified-verify_and_notify_new_payload) @@ -45,7 +51,7 @@ This is the beacon chain specification to add a fork-choice enforced, committee- | Name | Value | | - | - | -| `IL_COMMITTEE_SIZE` | `uint64(2**9)` (=256) # (New in FOCIL) | +| `IL_COMMITTEE_SIZE` | `uint64(2**4)` (=16) # (New in FOCIL) | ### Execution @@ -57,15 +63,6 @@ This is the beacon chain specification to add a fork-choice enforced, committee- ### New containers -#### `InclusionSummary` - -```python -class InclusionSummary(Container): - address: ExecutionAddress - nonce: uint64 - gas_limit: uint64 -``` - #### `LocalInclusionList` ```python @@ -74,7 +71,6 @@ class LocalInclusionList(Container): validator_index: ValidatorIndex parent_root: Root parent_hash: Hash32 - summaries: List[InclusionSummary, MAX_TRANSACTIONS_PER_INCLUSION_LIST] transactions: List[Transaction, MAX_TRANSACTIONS_PER_INCLUSION_LIST] ``` @@ -100,7 +96,7 @@ def is_valid_local_inclusion_list_signature( message = signed_local_inclusion_list.message index = message.validator_index pubkey = state.validators[index].pubkey - domain = get_domain(state, IL_COMMITTEE_SIZE, compute_epoch_at_slot(message.slot)) + domain = get_domain(state, DOMAIN_IL_COMMITTEE, compute_epoch_at_slot(message.slot)) signing_root = compute_signing_root(message, domain) return bls.FastAggregateVerify(pubkey, signing_root, signed_local_inclusion_list.signature) ``` @@ -124,3 +120,73 @@ def get_inclusion_list_committee(state: BeaconState, slot: Slot) -> Vector[Valid validator_indices += beacon_committee[:members_per_committee] return validator_indices ``` + +## 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 + il_transactions: List[Transaction, MAX_TRANSACTIONS_PER_INCLUSION_LIST] # [New in FOCIL] +``` + +#### Engine APIs + +##### Modified `notify_new_payload` + +*Note*: The function `notify_new_payload` is modified to include the additional `il_transactions` parameter in FOCIL. + +```python +def notify_new_payload(self: ExecutionEngine, + execution_payload: ExecutionPayload, + execution_requests: ExecutionRequests, + parent_beacon_block_root: Root, + il_transactions: List[Transaction, MAX_TRANSACTIONS_PER_INCLUSION_LIST] ) -> bool: + """ + Return ``True`` if and only if ``execution_payload`` and ``execution_requests`` + are valid with respect to ``self.execution_state``. + """ + ... +``` + +##### Modified `verify_and_notify_new_payload` + +*Note*: The function `verify_and_notify_new_payload` is modified to pass the additional parameter `il_transactions` +when calling `notify_new_payload` in FOCIL. + +```python +def verify_and_notify_new_payload(self: ExecutionEngine, + il_transactions: List[Transaction, MAX_TRANSACTIONS_PER_INCLUSION_LIST]) -> 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 + execution_requests = new_payload_request.execution_requests + parent_beacon_block_root = new_payload_request.parent_beacon_block_root + il_transactions = new_payload_request.il_transactions + + if not self.is_valid_block_hash(execution_payload, parent_beacon_block_root): + return False + + if not self.is_valid_versioned_hashes(new_payload_request): + return False + + # [Modified in FOCIL] + if not self.notify_new_payload( + execution_payload, + execution_requests, + parent_beacon_block_root, + il_transactions): + return False + + return True +``` \ No newline at end of file diff --git a/specs/_features/focil/engine-api.md b/specs/_features/focil/engine-api.md index 506680c820..14fadcf555 100644 --- a/specs/_features/focil/engine-api.md +++ b/specs/_features/focil/engine-api.md @@ -12,19 +12,20 @@ Warning: This file should be placed in https://github.com/ethereum/execution-api - [Constants](#constants) -- [Structures](#structures) - - [InclusionListSummaryV1](#inclusionlistsummaryv1) - - [InclusionListSummaryListV1](#inclusionlistsummarylistv1) - - [InclusionListStatusV1](#inclusionliststatusv1) + - [PayloadStatusV2](#payloadstatusv2) - [Methods](#methods) - [engine_newPayloadV5](#engine_newpayloadv5) - [Request](#request) - [Response](#response) - [Specification](#specification) - - [engine_getInclusionListV1](#engine_getinclusionlistv1) + - [engine_updateBlockWithInclusionListV1](#engine_updateblockwithinclusionlistv1) - [Request](#request-1) - [Response](#response-1) - [Specification](#specification-1) + - [engine_getInclusionListV1](#engine_getinclusionlistv1) + - [Request](#request-2) + - [Response](#response-2) + - [Specification](#specification-2) @@ -34,28 +35,13 @@ Warning: This file should be placed in https://github.com/ethereum/execution-api | - | - | | `LOCAL_INCLUSION_LIST_MAX_GAS` | `uint64(4194304) = 2**22` | -## Structures - -### InclusionListSummaryV1 - -This structure contains the details of each inclusion summary. - -- `address` : `DATA`, 20 Bytes -- `nonce` : `QUANTITY`, 64 Bits -- `gas_limit` : `QUANTITY`, 64 Bits +### PayloadStatusV2 -### InclusionListSummaryListV1 +This structure contains the result of processing a payload. The fields are encoded as follows: -This structure contains the inclusion summary. - -- `summary`: `Array of InclusionListSummaryV1`, Array of summaries that must be satisfied. - -### InclusionListStatusV1 - -This structure contains the result of processing an inclusion list. The fields are encoded as follows: - -- `status`: `enum` - `"VALID" | "INVALID" | "SYNCING" | "ACCEPTED"` -- `validationError`: `String|null` - a message providing additional details on the validation error if the payload is classified as `INVALID`. +- `status`: `enum` - `"VALID" | "INVALID" | "SYNCING" | "ACCEPTED" | "INVALID_BLOCK_HASH" | "INVALID_INCLUSION_LIST"` +- `latestValidHash`: `DATA|null`, 32 Bytes - the hash of the most recent *valid* block in the branch defined by payload and its ancestors +- `validationError`: `String|null` - a message providing additional details on the validation error if the payload is classified as `INVALID` or `INVALID_BLOCK_HASH`. ## Methods @@ -70,18 +56,34 @@ The request of this method is updated with [`ExecutionPayloadV5`](#ExecutionPayl 1. `executionPayload`: [`ExecutionPayloadV4`](#ExecutionPayloadV4). 2. `expectedBlobVersionedHashes`: `Array of DATA`, 32 Bytes - Array of expected blob versioned hashes to validate. 3. `parentBeaconBlockRoot`: `DATA`, 32 Bytes - Root of the parent beacon block. - 4. `inclusionListSummaryList`: [`InclusionListSummaryListV1`](#InclusionListSummaryListV1). + 4. `inclusionListTransactions`: `Array of DATA` - Array of transaction objects, each object is a byte list (`DATA`) representing `TransactionType || TransactionPayload` or `LegacyTransaction` as defined in [EIP-2718](https://eips.ethereum.org/EIPS/eip-2718) #### Response -Refer to the response for [`engine_newPayloadV3`](./cancun.md#engine_newpayloadv3). +* result: [`PayloadStatusV2`](#PayloadStatusV2) + +#### Specification + +1. Client software **MUST** respond to this method call in the following way: + * `{status: INVALID_INCLUSION_LISTING, latestValidHash: null, validationError: null}` if there are any leftover `inclusionListTransactions` that are not part of the `executionPayload`, they can be processed at the end of the `executionPayload`. + +### engine_updateBlockWithInclusionListV1 + +#### Request + +* method: `engine_updateBlockWithInclusionListV1` +* params: + 1. `payloadId`: `DATA`, 8 Bytes - Identifier of the payload build process + 2. `inclusionListTransactions`: `Array of DATA` - Array of transaction objects, each object is a byte list (`DATA`) representing `TransactionType || TransactionPayload` or `LegacyTransaction` as defined in [EIP-2718](https://eips.ethereum.org/EIPS/eip-2718) +* timeout: 1s + +#### Response #### Specification -This method follows the same specification as [`engine_newPayloadV3`](./cancun.md#engine_newpayloadv3) with the following changes: +1. Given the `payloadId` client software **MUST** update payload build process building with a list of `inclusionListTransactions`. The transactions must be part of the execution payload unless it fails to be included at the end of the execution block. -1. Client software **MUST** return `-38005: Unsupported fork` error if the `timestamp` of the payload does not fall within the time frame of the fork number. -2. Client software **MUST** return `TBD` if the payload does not satisfy inclusion list evaluation. +* error: code and message set in case an exception happens while getting the payload. ### engine_getInclusionListV1 @@ -95,11 +97,9 @@ This method follows the same specification as [`engine_newPayloadV3`](./cancun.m #### Response * result: `object` - - `inclusionListSummaryList`: `InclusionListSummaryListV1` - Summary. - `inclusionListTransactions`: `Array of DATA` - Array of transaction objects, each object is a byte list (`DATA`) representing `TransactionType || TransactionPayload` or `LegacyTransaction` as defined in [EIP-2718](https://eips.ethereum.org/EIPS/eip-2718) * error: code and message set in case an exception happens while getting the inclusion list. #### Specification -1. Client software **MUST** provide a list of transactions for the inclusion list based on local view of the mempool and according to the config specifications. -2. Client software **MUST** broadcast their inclusion list in addition to the full beacon block during their block proposal. \ No newline at end of file +1. Client software **MUST** provide a list of transactions for the inclusion list based on local view of the mempool and according to the config specifications. \ No newline at end of file diff --git a/specs/_features/focil/fork-choice.md b/specs/_features/focil/fork-choice.md index d296438229..e9dc454f38 100644 --- a/specs/_features/focil/fork-choice.md +++ b/specs/_features/focil/fork-choice.md @@ -7,9 +7,8 @@ - [Introduction](#introduction) - [Helpers](#helpers) - - [New `evaluate_inclusion_summary_aggregates`](#new-evaluate_inclusion_summary_aggregates) + - [New `validate_inclusion_lists`](#new-validate_inclusion_lists) - [Modified `Store`](#modified-store) - - [New `on_local_inclusion_list`](#new-on_local_inclusion_list) @@ -18,32 +17,20 @@ This is the modification of the fork choice accompanying the FOCIL upgrade. -## Containers - -### New `inclusion_list_aggregate` - -```python -class InclusionSummaryAggregate(Container): - aggregation_bits: Bitvector[IL_COMMITTEE_SIZE] - summary: InclusionSummary -``` - ## Helpers -### New `evaluate_inclusion_summary_aggregates` +### New `validate_inclusion_lists` ```python -def evaluate_inclusion_summary_aggregates(store: Store, inclusion_summary_aggregates: InclusionSummaryAggregates) -> bool: +def validate_inclusion_lists(store: Store, inclusion_list_transactions: List[Transaction, MAX_TRANSACTIONS_PER_INCLUSION_LIST], execution_payload: ExecutionPayload) -> bool: """ - Return ``True`` if and only if the input ``inclusion_summary_aggregates`` satifies evaluation. + Return ``True`` if and only if the input ``inclusion_list_transactions`` satifies validation, that to verify if the `execution_payload` satisfies `inclusion_list_transactions` validity conditions either when all transactions are present in payload or when any missing transactions are found to be invalid when appended to the end of the payload. """ ... ``` -TODO: Apply `evaluate_inclusion_summary_aggregates`, which can be part of the import rule for head filtering. Where it should be applied requires further analysis, and we won't specify the design at this moment. - ### Modified `Store` -**Note:** `Store` is modified to track the seen local inclusion lists before inclusion list aggregate cutoff interval. +**Note:** `Store` is modified to track the seen local inclusion lists. ```python @dataclass @@ -62,15 +49,40 @@ class Store(object): checkpoint_states: Dict[Checkpoint, BeaconState] = field(default_factory=dict) latest_messages: Dict[ValidatorIndex, LatestMessage] = field(default_factory=dict) unrealized_justifications: Dict[Root, Checkpoint] = field(default_factory=dict) - inclusion_summary_aggregates: Dict[Root, InclusionSummaryAggregates] = field(default_factory=dict) # [New in FOCIL] + inclusion_lists: List[Transaction] # [New in FOCIL] +#### Modified `get_head` +**Note:** `get_head` is modified to use `validate_inclusion_lists` as filter for head. + +```python +def get_head(store: Store) -> Root: + # Get filtered block tree that only includes viable branches + blocks = get_filtered_block_tree(store) + # Execute the LMD-GHOST fork choice + head = store.justified_checkpoint.root + while True: + children = [ + root for root in blocks.keys() + if blocks[root].parent_root == head + ] + if len(children) == 0: + return head + # Sort by latest attesting balance with ties broken lexicographically + # Ties broken by favoring block with lexicographically higher root + head = max( + children, + key=lambda root: (get_weight(store, root), root) + if validate_inclusion_lists(store, store.inclusion_list_transactions, blocks[root].body.execution_payload) # [New in FOCIL] + else (0, root) +)``` + ### New `on_local_inclusion_list` `on_local_inclusion_list` is called to import `signed_local_inclusion_list` to the fork choice store. ```python -def on_local_inclusion_list( +def on_inclusion_list( store: Store, signed_inclusion_list: SignedLocalInclusionList) -> None: """ ``on_local_inclusion_list`` verify the inclusion list before import it to fork choice store. @@ -89,23 +101,6 @@ def on_local_inclusion_list( parent_hash = message.parent_hash - aggregates = InclusionSummaryAggregate() - store.inclusion_summary_aggregates[parent_hash] = aggregate - ilc_index = ilc.index(message.validator_index) - for summary in message.summaries: - matching_aggregate = None - for aggregate in store.inclusion_summary_aggregates[parent_hash]: - if aggregate.summary == summary: - matching_aggregate = aggregate - break - - if matching_aggregate: - matching_aggregate.aggregation_bits.flip(ilc_index_to_i) - else: - new_aggregate = InclusionSummaryAggregate( - aggregation_bits=Bitvector[IL_COMMITTEE_SIZE](), - summary=summary - ) - new_aggregate.aggregation_bits.flip(ilc_index_to_i) - store.inclusion_summary_aggregates[parent_hash].append(new_aggregate) + if message.transaction not in store.inclusion_lists: + store.inclusion_lists.append(message.transaction) ``` \ No newline at end of file diff --git a/specs/_features/focil/p2p-interface.md b/specs/_features/focil/p2p-interface.md index ec2a44557e..00a639e237 100644 --- a/specs/_features/focil/p2p-interface.md +++ b/specs/_features/focil/p2p-interface.md @@ -6,8 +6,6 @@ This document contains the consensus-layer networking specification for FOCIL. - [Time parameters](#time-parameters) -- [Containers](#containers) - - [`SignedInclusionListAggregate`](#signedinclusionlistaggregate) - [The gossip domain: gossipsub](#the-gossip-domain-gossipsub) - [Topics and messages](#topics-and-messages) - [Global topics](#global-topics) @@ -19,19 +17,8 @@ This document contains the consensus-layer networking specification for FOCIL. | Name | Value | Unit | Duration | | - | - | :-: | :-: | -| `LOCAL_INCLUSION_CUT_OFF` | `uint64(9)` | seconds | 9 seconds | +| `LOCAL_INCLUSION_LIST_CUT_OFF` | `uint64(9)` | seconds | 9 seconds | -### Containers - -#### `SignedInclusionListAggregate` - -```python -class SignedInclusionSummaryAggregates(Container): - slot: Slot - proposer_index: ValidatorIndex - message: InclusionSummaryAggregates - signature: BLSSignature -``` ### The gossip domain: gossipsub @@ -55,8 +42,7 @@ The following validations MUST pass before forwarding the `local_inclusion_list` - _[REJECT]_ The slot `message.slot` is equal to current slot. - _[IGNORE]_ The current time is `LOCAL_INCLUSION_CUT_OFF` seconds into the slot. - _[REJECT]_ The transactions `message.transactions` length is within upperbound `MAX_TRANSACTIONS_PER_INCLUSION_LIST`. -- _[REJECT]_ The summaries `message.summaries` length is within upperbound `MAX_TRANSACTIONS_PER_INCLUSION_LIST`. -- _[IGNORE]_ The `message` is the first valid message received from the validator with index `message.validate_index`. +- _[IGNORE]_ The `message` is the first valid message received from the validator with index `message.validate_index` and can be received no more than twice from the same validator index. - _[IGNORE]_ The block hash `message.parent_block_hash` is a known execution payload in fork choice. - _[REJECT]_ The signature of `inclusion_list.signature` is valid with respect to the validator index. - _[REJECT]_ The validator index is within the inclusion list committee in `get_inclusion_list_committee(state)`. The `state` is the head state corresponding to processing the block up to the current slot as determined by the fork choice. diff --git a/specs/_features/focil/validator.md b/specs/_features/focil/validator.md index bbe37ecf07..416873aadb 100644 --- a/specs/_features/focil/validator.md +++ b/specs/_features/focil/validator.md @@ -8,14 +8,14 @@ - [Introduction](#introduction) - [Prerequisites](#prerequisites) + - [Time parameters](#time-parameters) - [Protocol](#protocol) - [`ExecutionEngine`](#executionengine) - [New inclusion list committee assignment](#new-inclusion-list-committee-assignment) - [Lookahead](#lookahead) - [New proposer duty](#new-proposer-duty) - - [Inclusion summary aggregates release](#inclusion-summary-aggregates-release) - [Block proposal](#block-proposal) - - [Constructing the new `InclusionSummaryAggregate` field in `BeaconBlockBody`](#constructing-the-new-inclusionsummaryaggregate-field-in--beaconblockbody) + - [Update execution client with inclusion lists](#update-execution-client-with-inclusion-lists) - [New inclusion list committee duty](#new-inclusion-list-committee-duty) - [Constructing a local inclusion list](#constructing-a-local-inclusion-list) @@ -34,11 +34,17 @@ All behaviors and definitions defined in this document, and documents it extends All terminology, constants, functions, and protocol mechanics defined in the updated Beacon Chain doc of [FOCIL](./beacon-chain.md) are requisite for this document and used throughout. Please see related Beacon Chain doc before continuing and use them as a reference throughout. +### Time parameters + +| Name | Value | Unit | Duration | +| - | - | :-: | :-: | +| `PROPOSER_INCLUSION_LIST_CUT_OFF` | `uint64(11)` | seconds | 11 seconds | + ## Protocol ### `ExecutionEngine` -*Note*: `engine_getInclusionListV1` and `engine_newInclusionListV1` functions are added to the `ExecutionEngine` protocol for use as a validator. +*Note*: `engine_getInclusionListV1` and `engine_updateInclusionListV1` functions are added to the `ExecutionEngine` protocol for use as a validator. The body of these function is implementation dependent. The Engine API may be used to implement it with an external execution engine. @@ -73,40 +79,20 @@ def get_ilc_assignment( ## New proposer duty -### Inclusion summary aggregates release - -Proposer has to release `signed_inclusion_summary_aggregates` at `3 * SECONDS_PER_SLOT // 4` seconds into the slot. The proposer will have to: -- Listen to the `inclusion_list` gossip global topic until `3 * SECONDS_PER_SLOT // 4` seconds into the slot. -- Gather all observed local inclusion lists, ensuring they meet the verification criteria specified in the local inclusion list gossip validation and `on_local_inclusion_list` sections. This requires: - - The `message.parent_hash` must match the local fork choice head view. - - The `message.slot` must be exactly one slot before the current proposing slot. - - The `message.summaries` and `message.transactions` must pass `engine_newPayloadV4` validation. -- The proposer aggregates all `local_inclusion_list` data into an `inclusion_summary_aggregates`, focusing only on the `InclusionSummary` field from the `LocalInclusionList`. - - To aggregate, the proposer fills the `aggregation_bits` field by using the relative position of the validator indices with respect to the ILC obtained from `get_inclusion_list_committee(state, proposing_slot - 1)`. -- The proposer signs the `inclusion_summary_aggregates` using helper `get_inclusion_summary_aggregates_signatures` and constructs a `signed_inclusion_aggregates_summary`. - -```python -def get_inclusion_summary_aggregates_signature( - state: BeaconState, inclusion_summary_aggregates: InclusionSummaryAggregates, privkey: int) -> BLSSignature: - domain = get_domain(state, DOMAIN_BEACON_PROPOSER), compute_epoch_at_slot(proposer_slot)) - signing_root = compute_signing_root(inclusion_summary_aggregates, domain) - return bls.Sign(privkey, signing_root) -``` - ### Block proposal -Validators are still expected to propose `SignedBeaconBlock` at the beginning of any slot during which `is_proposer(state, validator_index)` returns `true`. The mechanism to prepare this beacon block and related sidecars differs from previous forks as follows: +Proposers are still expected to propose `SignedBeaconBlock` at the beginning of any slot during which `is_proposer(state, validator_index)` returns `true`. The mechanism to prepare this beacon block and related sidecars differs from previous forks as follows: + +#### Update execution client with inclusion lists -#### Constructing the new `InclusionSummaryAggregate` field in `BeaconBlockBody` +The proposer should call `engine_updateInclusionListV1` at `PROPOSER_INCLUSION_LIST_CUT_OFF` into the slot with the list of the inclusion lists that gathered since `LOCAL_INCLUSION_LIST_CUT_OFF` -Proposer has to include a valid `inclusion_summary_aggregates` into the block body. The proposer will have to -* Proposer uses perviously constructed `InclusionSummaryAggregates` and include it in the beacon block body. ## New inclusion list committee duty Some validators are selected to submit local inclusion list. Validators should call `get_ilc_assignment` at the beginning of an epoch to be prepared to submit their local inclusion list during the next epoch. -A validator should create and broadcast the `signed_inclusion_list` to the global `inclusion_list` subnet at the `SECONDS_PER_SLOT * 2 // 2` seconds of `slot`. +A validator should create and broadcast the `signed_inclusion_list` to the global `inclusion_list` subnet by the `LOCAL_INCLUSION_LIST_CUT_OFF` in the slot, unless a block for the current slot has been processed and is the head of the chain and broadcast to the network. #### Constructing a local inclusion list @@ -115,7 +101,8 @@ The validator creates the `signed_local_inclusion_list` as follows: - Set `local_inclusion_list.slot` to the assigned slot returned by `get_ilc_assignment`. - Set `local_inclusion_list.validator_index` to the validator's index. - Set `local_inclusion_list.parent_hash` to the block hash of the fork choice head. -- Set `local_inclusion_list.summaries` and `local_inclusion_list.transactions` using the response from `engine_getInclusionListV1` from the execution layer client. +- Set `local_inclusion_list.parent_root` to the block hash of the fork choice head. +- Set `local_inclusion_list.transactions` using the response from `engine_getInclusionListV1` from the execution layer client. - Sign the `local_inclusion_list` using the helper `get_inclusion_list_signature` and obtain the `signature`. - Set `signed_local_inclusion_list.message` to `local_inclusion_list`. - Set `signed_local_inclusion_list.signature` to `signature`. From 0b09f964a55eef3732ed33f9246e196d95f51ace Mon Sep 17 00:00:00 2001 From: terence tsao Date: Wed, 2 Oct 2024 08:26:24 -1000 Subject: [PATCH 11/23] Address feedback --- specs/_features/focil/beacon-chain.md | 9 +++++---- specs/_features/focil/engine-api.md | 2 +- specs/_features/focil/fork-choice.md | 9 +++------ specs/_features/focil/p2p-interface.md | 4 ++-- specs/_features/focil/validator.md | 6 +++--- 5 files changed, 14 insertions(+), 16 deletions(-) diff --git a/specs/_features/focil/beacon-chain.md b/specs/_features/focil/beacon-chain.md index c523fcdd6a..959941fd53 100644 --- a/specs/_features/focil/beacon-chain.md +++ b/specs/_features/focil/beacon-chain.md @@ -33,8 +33,9 @@ ## Introduction -This is the beacon chain specification to add a fork-choice enforced, committee-based inclusion list (FOCIL) mechanism to allow forced transaction inclusion. Refers to [Ethresearch](https://ethresear.ch/t/fork-choice-enforced-inclusion-lists-focil-a-simple-committee-based-inclusion-list-proposal/19870/1) - +This is the beacon chain specification to add a 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. ## Constants @@ -165,14 +166,14 @@ when calling `notify_new_payload` in FOCIL. ```python def verify_and_notify_new_payload(self: ExecutionEngine, - il_transactions: List[Transaction, MAX_TRANSACTIONS_PER_INCLUSION_LIST]) -> bool: + 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 execution_requests = new_payload_request.execution_requests parent_beacon_block_root = new_payload_request.parent_beacon_block_root - il_transactions = new_payload_request.il_transactions + il_transactions = new_payload_request.il_transactions # [New in FOCIL] if not self.is_valid_block_hash(execution_payload, parent_beacon_block_root): return False diff --git a/specs/_features/focil/engine-api.md b/specs/_features/focil/engine-api.md index 14fadcf555..074b5dd5a4 100644 --- a/specs/_features/focil/engine-api.md +++ b/specs/_features/focil/engine-api.md @@ -65,7 +65,7 @@ The request of this method is updated with [`ExecutionPayloadV5`](#ExecutionPayl #### Specification 1. Client software **MUST** respond to this method call in the following way: - * `{status: INVALID_INCLUSION_LISTING, latestValidHash: null, validationError: null}` if there are any leftover `inclusionListTransactions` that are not part of the `executionPayload`, they can be processed at the end of the `executionPayload`. + * `{status: INVALID_INCLUSION_LIST, latestValidHash: null, validationError: null}` if there are any leftover `inclusionListTransactions` that are not part of the `executionPayload`, they can be processed at the end of the `executionPayload` or the block is full. ### engine_updateBlockWithInclusionListV1 diff --git a/specs/_features/focil/fork-choice.md b/specs/_features/focil/fork-choice.md index e9dc454f38..e9bcba26f1 100644 --- a/specs/_features/focil/fork-choice.md +++ b/specs/_features/focil/fork-choice.md @@ -22,9 +22,9 @@ This is the modification of the fork choice accompanying the FOCIL upgrade. ### New `validate_inclusion_lists` ```python -def validate_inclusion_lists(store: Store, inclusion_list_transactions: List[Transaction, MAX_TRANSACTIONS_PER_INCLUSION_LIST], execution_payload: ExecutionPayload) -> bool: +def validate_inclusion_lists(store: Store, inclusion_list_transactions: List[Transaction, MAX_TRANSACTIONS_PER_INCLUSION_LIST * IL_COMMITTEE_SIZE], execution_payload: ExecutionPayload) -> bool: """ - Return ``True`` if and only if the input ``inclusion_list_transactions`` satifies validation, that to verify if the `execution_payload` satisfies `inclusion_list_transactions` validity conditions either when all transactions are present in payload or when any missing transactions are found to be invalid when appended to the end of the payload. + Return ``True`` if and only if the input ``inclusion_list_transactions`` satifies validation, that to verify if the `execution_payload` satisfies `inclusion_list_transactions` validity conditions either when all transactions are present in payload or when any missing transactions are found to be invalid when appended to the end of the payload unless the block is full. """ ... ``` @@ -72,8 +72,7 @@ def get_head(store: Store) -> Root: head = max( children, key=lambda root: (get_weight(store, root), root) - if validate_inclusion_lists(store, store.inclusion_list_transactions, blocks[root].body.execution_payload) # [New in FOCIL] - else (0, root) + 0 if validate_inclusion_lists(store, store.inclusion_list_transactions, blocks[root].body.execution_payload) else root # [New in FOCIL] )``` @@ -99,8 +98,6 @@ def on_inclusion_list( # Verify inclusion list signature assert is_valid_local_inclusion_list_signature(state, signed_inclusion_list) - parent_hash = message.parent_hash - if message.transaction not in store.inclusion_lists: store.inclusion_lists.append(message.transaction) ``` \ No newline at end of file diff --git a/specs/_features/focil/p2p-interface.md b/specs/_features/focil/p2p-interface.md index 00a639e237..be6a8d8465 100644 --- a/specs/_features/focil/p2p-interface.md +++ b/specs/_features/focil/p2p-interface.md @@ -32,7 +32,7 @@ The new topics along with the type of the `data` field of a gossipsub message ar ##### Global topics -FOCIL introduces new global topics for inclusion list and inclusion list aggregate. +FOCIL introduces new global topics for inclusion list. ###### `local_inclusion_list` @@ -45,4 +45,4 @@ The following validations MUST pass before forwarding the `local_inclusion_list` - _[IGNORE]_ The `message` is the first valid message received from the validator with index `message.validate_index` and can be received no more than twice from the same validator index. - _[IGNORE]_ The block hash `message.parent_block_hash` is a known execution payload in fork choice. - _[REJECT]_ The signature of `inclusion_list.signature` is valid with respect to the validator index. -- _[REJECT]_ The validator index is within the inclusion list committee in `get_inclusion_list_committee(state)`. The `state` is the head state corresponding to processing the block up to the current slot as determined by the fork choice. +- _[REJECT]_ The validator index is within the inclusion list committee in `get_inclusion_list_committee(state)`. The `state` is based from `message.parent_block_root` to processing the block up to the current slot as determined by the fork choice. diff --git a/specs/_features/focil/validator.md b/specs/_features/focil/validator.md index 416873aadb..b26b3b3a05 100644 --- a/specs/_features/focil/validator.md +++ b/specs/_features/focil/validator.md @@ -44,7 +44,7 @@ Please see related Beacon Chain doc before continuing and use them as a referenc ### `ExecutionEngine` -*Note*: `engine_getInclusionListV1` and `engine_updateInclusionListV1` functions are added to the `ExecutionEngine` protocol for use as a validator. +*Note*: `engine_getInclusionListV1` and `engine_updateBlockWithInclusionListV1` functions are added to the `ExecutionEngine` protocol for use as a validator. The body of these function is implementation dependent. The Engine API may be used to implement it with an external execution engine. @@ -92,7 +92,7 @@ The proposer should call `engine_updateInclusionListV1` at `PROPOSER_INCLUSION_L Some validators are selected to submit local inclusion list. Validators should call `get_ilc_assignment` at the beginning of an epoch to be prepared to submit their local inclusion list during the next epoch. -A validator should create and broadcast the `signed_inclusion_list` to the global `inclusion_list` subnet by the `LOCAL_INCLUSION_LIST_CUT_OFF` in the slot, unless a block for the current slot has been processed and is the head of the chain and broadcast to the network. +A validator should create and broadcast the `signed_inclusion_list` to the global `local_inclusion_list` subnet by the `LOCAL_INCLUSION_LIST_CUT_OFF` in the slot, unless a block for the current slot has been processed and is the head of the chain and broadcast to the network. #### Constructing a local inclusion list @@ -101,7 +101,7 @@ The validator creates the `signed_local_inclusion_list` as follows: - Set `local_inclusion_list.slot` to the assigned slot returned by `get_ilc_assignment`. - Set `local_inclusion_list.validator_index` to the validator's index. - Set `local_inclusion_list.parent_hash` to the block hash of the fork choice head. -- Set `local_inclusion_list.parent_root` to the block hash of the fork choice head. +- Set `local_inclusion_list.parent_root` to the block root of the fork choice head. - Set `local_inclusion_list.transactions` using the response from `engine_getInclusionListV1` from the execution layer client. - Sign the `local_inclusion_list` using the helper `get_inclusion_list_signature` and obtain the `signature`. - Set `signed_local_inclusion_list.message` to `local_inclusion_list`. From 6056b69ea1215c3dff6042da2b0a8563347be645 Mon Sep 17 00:00:00 2001 From: terence tsao Date: Sat, 12 Oct 2024 15:37:15 -0700 Subject: [PATCH 12/23] Clarify forkchoice and p2p --- specs/_features/focil/beacon-chain.md | 4 +- specs/_features/focil/fork-choice.md | 53 ++++++++++---------------- specs/_features/focil/p2p-interface.md | 34 +++++++++++++++++ specs/_features/focil/validator.md | 6 ++- 4 files changed, 60 insertions(+), 37 deletions(-) diff --git a/specs/_features/focil/beacon-chain.md b/specs/_features/focil/beacon-chain.md index 959941fd53..9121271a23 100644 --- a/specs/_features/focil/beacon-chain.md +++ b/specs/_features/focil/beacon-chain.md @@ -38,7 +38,7 @@ This is the beacon chain specification to add a fork-choice enforced, committee- - [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. -## Constants +## Preset ### Domain types @@ -46,8 +46,6 @@ This is the beacon chain specification to add a fork-choice enforced, committee- | - | - | | `DOMAIN_IL_COMMITTEE` | `DomainType('0x0C000000')` # (New in FOCIL)| -## Preset - ### Inclusion List Committee | Name | Value | diff --git a/specs/_features/focil/fork-choice.md b/specs/_features/focil/fork-choice.md index e9bcba26f1..5852782e28 100644 --- a/specs/_features/focil/fork-choice.md +++ b/specs/_features/focil/fork-choice.md @@ -49,46 +49,22 @@ class Store(object): checkpoint_states: Dict[Checkpoint, BeaconState] = field(default_factory=dict) latest_messages: Dict[ValidatorIndex, LatestMessage] = field(default_factory=dict) unrealized_justifications: Dict[Root, Checkpoint] = field(default_factory=dict) - inclusion_lists: List[Transaction] # [New in FOCIL] - -#### Modified `get_head` -**Note:** `get_head` is modified to use `validate_inclusion_lists` as filter for head. - -```python -def get_head(store: Store) -> Root: - # Get filtered block tree that only includes viable branches - blocks = get_filtered_block_tree(store) - # Execute the LMD-GHOST fork choice - head = store.justified_checkpoint.root - while True: - children = [ - root for root in blocks.keys() - if blocks[root].parent_root == head - ] - if len(children) == 0: - return head - # Sort by latest attesting balance with ties broken lexicographically - # Ties broken by favoring block with lexicographically higher root - head = max( - children, - key=lambda root: (get_weight(store, root), root) - 0 if validate_inclusion_lists(store, store.inclusion_list_transactions, blocks[root].body.execution_payload) else root # [New in FOCIL] -)``` + inclusion_lists: List[Transaction] # [New in FOCIL ### New `on_local_inclusion_list` `on_local_inclusion_list` is called to import `signed_local_inclusion_list` to the fork choice store. - ```python def on_inclusion_list( store: Store, signed_inclusion_list: SignedLocalInclusionList) -> None: """ - ``on_local_inclusion_list`` verify the inclusion list before import it to fork choice store. + ``on_local_inclusion_list`` verify the inclusion list before importing it to fork choice store. + If there exists more than 1 inclusion list in store with the same slot and validator index, remove the original one. """ message = signed_inclusion_list.message - # Verify inclusion list slot is bouded to the current slot - assert get_current_slot(store) != message.slot + # Verify inclusion list slot is bounded to the current slot + assert get_current_slot(store) == message.slot state = store.block_states[message.beacon_block_root] ilc = get_inclusion_list_committee(state, message.slot) @@ -96,8 +72,19 @@ def on_inclusion_list( assert message.validator_index in ilc # Verify inclusion list signature - assert is_valid_local_inclusion_list_signature(state, signed_inclusion_list) - - if message.transaction not in store.inclusion_lists: - store.inclusion_lists.append(message.transaction) + assert is_valid_local_inclusion_list_signature(state, signed_inclusion_list) + + # Check if an inclusion list with the same slot and validator index exists + existing_inclusion_list = next( + (il for il in store.inclusion_lists + if il.slot == message.slot and il.validator_index == message.validator_index), + None + ) + + # If such an inclusion list exists, remove it + if existing_inclusion_list: + store.inclusion_lists.remove(existing_inclusion_list) + else: + # If no such inclusion list exists, add the new one + store.inclusion_lists.append(message) ``` \ No newline at end of file diff --git a/specs/_features/focil/p2p-interface.md b/specs/_features/focil/p2p-interface.md index be6a8d8465..f527288b7e 100644 --- a/specs/_features/focil/p2p-interface.md +++ b/specs/_features/focil/p2p-interface.md @@ -19,6 +19,9 @@ This document contains the consensus-layer networking specification for FOCIL. | - | - | :-: | :-: | | `LOCAL_INCLUSION_LIST_CUT_OFF` | `uint64(9)` | seconds | 9 seconds | +### Configuration + +| `MAX_REQUEST_INCLUSION_LIST` | `2**4` (= 16) | Maximum number of inclusion list in a single request | ### The gossip domain: gossipsub @@ -46,3 +49,34 @@ The following validations MUST pass before forwarding the `local_inclusion_list` - _[IGNORE]_ The block hash `message.parent_block_hash` is a known execution payload in fork choice. - _[REJECT]_ The signature of `inclusion_list.signature` is valid with respect to the validator index. - _[REJECT]_ The validator index is within the inclusion list committee in `get_inclusion_list_committee(state)`. The `state` is based from `message.parent_block_root` to processing the block up to the current slot as determined by the fork choice. + +### The Req/Resp domain + +#### Messages + +##### InclusionListByCommitteeIndices v1 + +**Protocol ID:** `/eth2/beacon_chain/req/inclusion_list_by_committee_indices/1/` + +The `` field is calculated as `context = compute_fork_digest(fork_version, genesis_validators_root)`: + +[1]: # (eth2spec: skip) + +| `fork_version` | Chunk SSZ type | +|------------------------|------------------------------------------| +| `FOCIL_FORK_VERSION` | `focil.SignedInclusionList` | + +Request Content: +``` +( + slot: Slot + committee_indices: Bitvector[IL_COMMITTEE_SIZE] +) +``` + +Response Content: +``` +( + List[SignedInclusionList, MAX_REQUEST_INCLUSION_LIST] +) +``` \ No newline at end of file diff --git a/specs/_features/focil/validator.md b/specs/_features/focil/validator.md index b26b3b3a05..39f4e5b5b8 100644 --- a/specs/_features/focil/validator.md +++ b/specs/_features/focil/validator.md @@ -113,4 +113,8 @@ def get_inclusion_list_signature( domain = get_domain(state, DOMAIN_IL_COMMITTEE, compute_epoch_at_slot(inclusion_list.slot)) signing_root = compute_signing_root(inclusion_list, domain) return bls.Sign(privkey, signing_root) -``` \ No newline at end of file +``` + +## Modified attester duty + +Attesters should not vote for the head block if `validate_inclusion_lists` of the head block returns false. \ No newline at end of file From a8aec7c1d12ed912968dc9a4d9fdd1f727826f54 Mon Sep 17 00:00:00 2001 From: terence tsao Date: Thu, 17 Oct 2024 07:26:40 -0700 Subject: [PATCH 13/23] Remove local --- specs/_features/focil/beacon-chain.md | 31 ++++++++++++------------ specs/_features/focil/engine-api.md | 2 +- specs/_features/focil/fork-choice.md | 12 +++++----- specs/_features/focil/p2p-interface.md | 18 ++++++++------ specs/_features/focil/validator.md | 33 +++++++++++++------------- 5 files changed, 50 insertions(+), 46 deletions(-) diff --git a/specs/_features/focil/beacon-chain.md b/specs/_features/focil/beacon-chain.md index 9121271a23..bc98a3de1b 100644 --- a/specs/_features/focil/beacon-chain.md +++ b/specs/_features/focil/beacon-chain.md @@ -7,17 +7,16 @@ - [Introduction](#introduction) -- [Constants](#constants) - - [Domain types](#domain-types) - [Preset](#preset) + - [Domain types](#domain-types) - [Inclusion List Committee](#inclusion-list-committee) - [Execution](#execution) - [Containers](#containers) - [New containers](#new-containers) - - [`LocalInclusionList`](#localinclusionlist) - - [`SignedLocalInclusionList`](#signedlocalinclusionlist) + - [`InclusionList`](#inclusionlist) + - [`SignedInclusionList`](#signedinclusionlist) - [Predicates](#predicates) - - [New `is_valid_local_inclusion_list_signature`](#new-is_valid_local_inclusion_list_signature) + - [New `is_valid_inclusion_list_signature`](#new-is_valid_inclusion_list_signature) - [Beacon State accessors](#beacon-state-accessors) - [`get_inclusion_list_committee`](#get_inclusion_list_committee) - [Beacon chain state transition function](#beacon-chain-state-transition-function) @@ -62,10 +61,10 @@ This is the beacon chain specification to add a fork-choice enforced, committee- ### New containers -#### `LocalInclusionList` +#### `InclusionList` ```python -class LocalInclusionList(Container): +class InclusionList(Container): slot: Slot validator_index: ValidatorIndex parent_root: Root @@ -73,31 +72,31 @@ class LocalInclusionList(Container): transactions: List[Transaction, MAX_TRANSACTIONS_PER_INCLUSION_LIST] ``` -#### `SignedLocalInclusionList` +#### `SignedInclusionList` ```python -class SignedLocalInclusionList(Container): - message: LocalInclusionList +class SignedInclusionList(Container): + message: InclusionList signature: BLSSignature ``` ### Predicates -#### New `is_valid_local_inclusion_list_signature` +#### New `is_valid_inclusion_list_signature` ```python -def is_valid_local_inclusion_list_signature( +def is_valid_inclusion_list_signature( state: BeaconState, - signed_local_inclusion_list: SignedLocalInclusionList) -> bool: + signed_inclusion_list: SignedInclusionList) -> bool: """ - Check if ``signed_local_inclusion_list`` has a valid signature + Check if ``signed_inclusion_list`` has a valid signature """ - message = signed_local_inclusion_list.message + message = signed_inclusion_list.message index = message.validator_index pubkey = state.validators[index].pubkey domain = get_domain(state, DOMAIN_IL_COMMITTEE, compute_epoch_at_slot(message.slot)) signing_root = compute_signing_root(message, domain) - return bls.FastAggregateVerify(pubkey, signing_root, signed_local_inclusion_list.signature) + return bls.FastAggregateVerify(pubkey, signing_root, signed_inclusion_list.signature) ``` ### Beacon State accessors diff --git a/specs/_features/focil/engine-api.md b/specs/_features/focil/engine-api.md index 074b5dd5a4..b69e059bdb 100644 --- a/specs/_features/focil/engine-api.md +++ b/specs/_features/focil/engine-api.md @@ -33,7 +33,7 @@ Warning: This file should be placed in https://github.com/ethereum/execution-api | Name | Value | | - | - | -| `LOCAL_INCLUSION_LIST_MAX_GAS` | `uint64(4194304) = 2**22` | +| `INCLUSION_LIST_MAX_GAS` | `uint64(4194304) = 2**22` | ### PayloadStatusV2 diff --git a/specs/_features/focil/fork-choice.md b/specs/_features/focil/fork-choice.md index 5852782e28..3b2af62a42 100644 --- a/specs/_features/focil/fork-choice.md +++ b/specs/_features/focil/fork-choice.md @@ -30,7 +30,7 @@ def validate_inclusion_lists(store: Store, inclusion_list_transactions: List[Tra ``` ### Modified `Store` -**Note:** `Store` is modified to track the seen local inclusion lists. +**Note:** `Store` is modified to track the seen inclusion lists. ```python @dataclass @@ -52,14 +52,14 @@ class Store(object): inclusion_lists: List[Transaction] # [New in FOCIL -### New `on_local_inclusion_list` +### New `on_inclusion_list` -`on_local_inclusion_list` is called to import `signed_local_inclusion_list` to the fork choice store. +`on_inclusion_list` is called to import `signed_inclusion_list` to the fork choice store. ```python def on_inclusion_list( - store: Store, signed_inclusion_list: SignedLocalInclusionList) -> None: + store: Store, signed_inclusion_list: SignedInclusionList) -> None: """ - ``on_local_inclusion_list`` verify the inclusion list before importing it to fork choice store. + ``on_inclusion_list`` verify the inclusion list before importing it to fork choice store. If there exists more than 1 inclusion list in store with the same slot and validator index, remove the original one. """ message = signed_inclusion_list.message @@ -72,7 +72,7 @@ def on_inclusion_list( assert message.validator_index in ilc # Verify inclusion list signature - assert is_valid_local_inclusion_list_signature(state, signed_inclusion_list) + assert is_valid_inclusion_list_signature(state, signed_inclusion_list) # Check if an inclusion list with the same slot and validator index exists existing_inclusion_list = next( diff --git a/specs/_features/focil/p2p-interface.md b/specs/_features/focil/p2p-interface.md index f527288b7e..ce2633f4e3 100644 --- a/specs/_features/focil/p2p-interface.md +++ b/specs/_features/focil/p2p-interface.md @@ -6,10 +6,14 @@ This document contains the consensus-layer networking specification for FOCIL. - [Time parameters](#time-parameters) +- [Configuration](#configuration) - [The gossip domain: gossipsub](#the-gossip-domain-gossipsub) - [Topics and messages](#topics-and-messages) - [Global topics](#global-topics) - - [`local_inclusion_list`](#local_inclusion_list) + - [`inclusion_list`](#inclusion_list) +- [The Req/Resp domain](#the-reqresp-domain) + - [Messages](#messages) + - [InclusionListByCommitteeIndices v1](#inclusionlistbycommitteeindices-v1) @@ -17,7 +21,7 @@ This document contains the consensus-layer networking specification for FOCIL. | Name | Value | Unit | Duration | | - | - | :-: | :-: | -| `LOCAL_INCLUSION_LIST_CUT_OFF` | `uint64(9)` | seconds | 9 seconds | +| `inclusion_list_CUT_OFF` | `uint64(9)` | seconds | 9 seconds | ### Configuration @@ -31,19 +35,19 @@ The new topics along with the type of the `data` field of a gossipsub message ar | Name | Message Type | |-------------------------------|------------------------------------------------------| -| `local_inclusion_list` | `SignedLocalInclusionList` [New in FOCIL] | +| `inclusion_list` | `SignedInclusionList` [New in FOCIL] | ##### Global topics FOCIL introduces new global topics for inclusion list. -###### `local_inclusion_list` +###### `inclusion_list` -This topic is used to propagate signed local inclusion list as `SignedLocalInclusionList`. -The following validations MUST pass before forwarding the `local_inclusion_list` on the network, assuming the alias `message = signed_local_inclusion_list.message`: +This topic is used to propagate signed inclusion list as `SignedInclusionList`. +The following validations MUST pass before forwarding the `inclusion_list` on the network, assuming the alias `message = signed_inclusion_list.message`: - _[REJECT]_ The slot `message.slot` is equal to current slot. -- _[IGNORE]_ The current time is `LOCAL_INCLUSION_CUT_OFF` seconds into the slot. +- _[IGNORE]_ The current time is `INCLUSION_LIST_CUT_OFF` seconds into the slot. - _[REJECT]_ The transactions `message.transactions` length is within upperbound `MAX_TRANSACTIONS_PER_INCLUSION_LIST`. - _[IGNORE]_ The `message` is the first valid message received from the validator with index `message.validate_index` and can be received no more than twice from the same validator index. - _[IGNORE]_ The block hash `message.parent_block_hash` is a known execution payload in fork choice. diff --git a/specs/_features/focil/validator.md b/specs/_features/focil/validator.md index 39f4e5b5b8..bc99f15de5 100644 --- a/specs/_features/focil/validator.md +++ b/specs/_features/focil/validator.md @@ -17,7 +17,8 @@ - [Block proposal](#block-proposal) - [Update execution client with inclusion lists](#update-execution-client-with-inclusion-lists) - [New inclusion list committee duty](#new-inclusion-list-committee-duty) - - [Constructing a local inclusion list](#constructing-a-local-inclusion-list) + - [Constructing a signed inclusion list](#constructing-a-signed-inclusion-list) +- [Modified attester duty](#modified-attester-duty) @@ -85,31 +86,31 @@ Proposers are still expected to propose `SignedBeaconBlock` at the beginning of #### Update execution client with inclusion lists -The proposer should call `engine_updateInclusionListV1` at `PROPOSER_INCLUSION_LIST_CUT_OFF` into the slot with the list of the inclusion lists that gathered since `LOCAL_INCLUSION_LIST_CUT_OFF` +The proposer should call `engine_updateInclusionListV1` at `PROPOSER_INCLUSION_LIST_CUT_OFF` into the slot with the list of the inclusion lists that gathered since `inclusion_list_CUT_OFF` ## New inclusion list committee duty -Some validators are selected to submit local inclusion list. Validators should call `get_ilc_assignment` at the beginning of an epoch to be prepared to submit their local inclusion list during the next epoch. +Some validators are selected to submit signed inclusion list. Validators should call `get_ilc_assignment` at the beginning of an epoch to be prepared to submit their inclusion list during the next epoch. -A validator should create and broadcast the `signed_inclusion_list` to the global `local_inclusion_list` subnet by the `LOCAL_INCLUSION_LIST_CUT_OFF` in the slot, unless a block for the current slot has been processed and is the head of the chain and broadcast to the network. +A validator should create and broadcast the `signed_inclusion_list` to the global `inclusion_list` subnet by the `inclusion_list_CUT_OFF` in the slot, unless a block for the current slot has been processed and is the head of the chain and broadcast to the network. -#### Constructing a local inclusion list +#### Constructing a signed inclusion list -The validator creates the `signed_local_inclusion_list` as follows: -- First, the validator creates the `local_inclusion_list`. -- Set `local_inclusion_list.slot` to the assigned slot returned by `get_ilc_assignment`. -- Set `local_inclusion_list.validator_index` to the validator's index. -- Set `local_inclusion_list.parent_hash` to the block hash of the fork choice head. -- Set `local_inclusion_list.parent_root` to the block root of the fork choice head. -- Set `local_inclusion_list.transactions` using the response from `engine_getInclusionListV1` from the execution layer client. -- Sign the `local_inclusion_list` using the helper `get_inclusion_list_signature` and obtain the `signature`. -- Set `signed_local_inclusion_list.message` to `local_inclusion_list`. -- Set `signed_local_inclusion_list.signature` to `signature`. +The validator creates the `signed_inclusion_list` as follows: +- First, the validator creates the `inclusion_list`. +- Set `inclusion_list.slot` to the assigned slot returned by `get_ilc_assignment`. +- Set `inclusion_list.validator_index` to the validator's index. +- Set `inclusion_list.parent_hash` to the block hash of the fork choice head. +- Set `inclusion_list.parent_root` to the block root of the fork choice head. +- Set `inclusion_list.transactions` using the response from `engine_getInclusionListV1` from the execution layer client. +- Sign the `inclusion_list` using the helper `get_inclusion_list_signature` and obtain the `signature`. +- Set `signed_inclusion_list.message` to `inclusion_list`. +- Set `signed_inclusion_list.signature` to `signature`. ```python def get_inclusion_list_signature( - state: BeaconState, inclusion_list: LocalInclusionList, privkey: int) -> BLSSignature: + state: BeaconState, inclusion_list: InclusionList, privkey: int) -> BLSSignature: domain = get_domain(state, DOMAIN_IL_COMMITTEE, compute_epoch_at_slot(inclusion_list.slot)) signing_root = compute_signing_root(inclusion_list, domain) return bls.Sign(privkey, signing_root) From cbe6129ba8230cdba89f0625c8545337c7d7581f Mon Sep 17 00:00:00 2001 From: fradamt <104826920+fradamt@users.noreply.github.com> Date: Tue, 22 Oct 2024 15:50:25 +0200 Subject: [PATCH 14/23] suggested changes --- specs/_features/focil/beacon-chain.md | 17 ++++------- specs/_features/focil/fork-choice.md | 40 ++++++++++++++------------ specs/_features/focil/p2p-interface.md | 6 ++-- 3 files changed, 29 insertions(+), 34 deletions(-) diff --git a/specs/_features/focil/beacon-chain.md b/specs/_features/focil/beacon-chain.md index bc98a3de1b..b02b6c4f3b 100644 --- a/specs/_features/focil/beacon-chain.md +++ b/specs/_features/focil/beacon-chain.md @@ -68,7 +68,6 @@ class InclusionList(Container): slot: Slot validator_index: ValidatorIndex parent_root: Root - parent_hash: Hash32 transactions: List[Transaction, MAX_TRANSACTIONS_PER_INCLUSION_LIST] ``` @@ -105,18 +104,12 @@ def is_valid_inclusion_list_signature( ```python def get_inclusion_list_committee(state: BeaconState, slot: Slot) -> Vector[ValidatorIndex, IL_COMMITTEE_SIZE]: - """ - Get the inclusion list committee for the given ``slot`` - """ epoch = compute_epoch_at_slot(slot) - committees_per_slot = bit_floor(min(get_committee_count_per_slot(state, epoch), IL_COMMITTEE_SIZE)) - members_per_committee = IL_COMMITTEE_SIZE // committees_per_slot - - validator_indices: List[ValidatorIndex] = [] - for idx in range(committees_per_slot): - beacon_committee = get_beacon_committee(state, slot, CommitteeIndex(idx)) - validator_indices += beacon_committee[:members_per_committee] - return validator_indices + seed = get_seed(state, epoch, DOMAIN_IL_COMMITTEE) + indices = get_active_validator_indices(state, epoch) + start = (slot % SLOTS_PER_EPOCH) * IL_COMMITTEE_SIZE + end = start + IL_COMMITTEE_SIZE + return [indices[compute_shuffled_index(uint64(i), uint64(len(indices)), seed)] for i in range(start, end)] ``` ## Beacon chain state transition function diff --git a/specs/_features/focil/fork-choice.md b/specs/_features/focil/fork-choice.md index 3b2af62a42..9bee312d63 100644 --- a/specs/_features/focil/fork-choice.md +++ b/specs/_features/focil/fork-choice.md @@ -49,7 +49,8 @@ class Store(object): checkpoint_states: Dict[Checkpoint, BeaconState] = field(default_factory=dict) latest_messages: Dict[ValidatorIndex, LatestMessage] = field(default_factory=dict) unrealized_justifications: Dict[Root, Checkpoint] = field(default_factory=dict) - inclusion_lists: List[Transaction] # [New in FOCIL + inclusion_lists: Dict[Tuple[Slot, Root], List[InclusionList]] = field(default_factory=dict) # [New in FOCIL] + inclusion_list_equivocators: Dict[Tuple[Slot, Root], Set[ValidatorIndex]] = field(default_factory=dict)# [New in FOCIL] ### New `on_inclusion_list` @@ -66,25 +67,28 @@ def on_inclusion_list( # Verify inclusion list slot is bounded to the current slot assert get_current_slot(store) == message.slot - state = store.block_states[message.beacon_block_root] - ilc = get_inclusion_list_committee(state, message.slot) + assert message.beacon_block_root in store.block_states + # Get the inclusion list committee for this slot + state = copy(store.block_states[message.beacon_block_root]) + if state.slot < message.slot: + process_slots(state, message.slot) + inclusion_list_committee = get_inclusion_list_committee(state, message.slot) + # Verify inclusion list validator is part of the committee - assert message.validator_index in ilc + validator_index = message.validator_index + assert validator_index.validator_index in inclusion_list_committee # Verify inclusion list signature assert is_valid_inclusion_list_signature(state, signed_inclusion_list) - # Check if an inclusion list with the same slot and validator index exists - existing_inclusion_list = next( - (il for il in store.inclusion_lists - if il.slot == message.slot and il.validator_index == message.validator_index), - None - ) - - # If such an inclusion list exists, remove it - if existing_inclusion_list: - store.inclusion_lists.remove(existing_inclusion_list) - else: - # If no such inclusion list exists, add the new one - store.inclusion_lists.append(message) -``` \ No newline at end of file + root = hash_tree_root(inclusion_list_committee) + if validator_index not in inclusion_list_equivocators[(message.slot, root)]: + if validator_index in [il.validator_index for il in inclusion_lists[(message.slot, root)]] + il = [il for il in inclusion_lists[(message.slot, root)] if il.validator_index == validator_index][0] + if not il == message: + inclusion_list_equivocators[(message.slot, root)].add(validator_index) + else: + inclusion_lists[(message.slot, root)].append(message) +``` + + diff --git a/specs/_features/focil/p2p-interface.md b/specs/_features/focil/p2p-interface.md index ce2633f4e3..4c937c76ab 100644 --- a/specs/_features/focil/p2p-interface.md +++ b/specs/_features/focil/p2p-interface.md @@ -47,12 +47,10 @@ This topic is used to propagate signed inclusion list as `SignedInclusionList`. The following validations MUST pass before forwarding the `inclusion_list` on the network, assuming the alias `message = signed_inclusion_list.message`: - _[REJECT]_ The slot `message.slot` is equal to current slot. -- _[IGNORE]_ The current time is `INCLUSION_LIST_CUT_OFF` seconds into the slot. - _[REJECT]_ The transactions `message.transactions` length is within upperbound `MAX_TRANSACTIONS_PER_INCLUSION_LIST`. -- _[IGNORE]_ The `message` is the first valid message received from the validator with index `message.validate_index` and can be received no more than twice from the same validator index. -- _[IGNORE]_ The block hash `message.parent_block_hash` is a known execution payload in fork choice. +- _[IGNORE]_ The `message` is either the first or second valid message received from the validator with index `message.validator_index`. - _[REJECT]_ The signature of `inclusion_list.signature` is valid with respect to the validator index. -- _[REJECT]_ The validator index is within the inclusion list committee in `get_inclusion_list_committee(state)`. The `state` is based from `message.parent_block_root` to processing the block up to the current slot as determined by the fork choice. +- _[REJECT]_ The validator index `message.validator_index` is within the inclusion list committee given by `get_inclusion_list_committee(state, message.slot)`, where the `state` is based on `message.parent_block_root` and processed up to `message.slot`. If the validator index cannot immediately be verified against the expected committee, the inclusion list MAY be queued for later processing while the committee for the branch of `message.parent_block_root` is calculated -- in such a case do not REJECT, instead IGNORE this message. ### The Req/Resp domain From f151c7c671dab5cd5271f9944c1fcde529eb47ff Mon Sep 17 00:00:00 2001 From: fradamt <104826920+fradamt@users.noreply.github.com> Date: Tue, 22 Oct 2024 17:48:05 +0200 Subject: [PATCH 15/23] replace parent_root with il_committee_root --- specs/_features/focil/beacon-chain.md | 2 +- specs/_features/focil/fork-choice.md | 19 ++++++++----------- specs/_features/focil/p2p-interface.md | 13 ++++++++----- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/specs/_features/focil/beacon-chain.md b/specs/_features/focil/beacon-chain.md index b02b6c4f3b..c6f9bd8a4d 100644 --- a/specs/_features/focil/beacon-chain.md +++ b/specs/_features/focil/beacon-chain.md @@ -67,7 +67,7 @@ This is the beacon chain specification to add a fork-choice enforced, committee- class InclusionList(Container): slot: Slot validator_index: ValidatorIndex - parent_root: Root + inclusion_list_committee_root: Root transactions: List[Transaction, MAX_TRANSACTIONS_PER_INCLUSION_LIST] ``` diff --git a/specs/_features/focil/fork-choice.md b/specs/_features/focil/fork-choice.md index 9bee312d63..9bc85a59be 100644 --- a/specs/_features/focil/fork-choice.md +++ b/specs/_features/focil/fork-choice.md @@ -58,30 +58,27 @@ class Store(object): `on_inclusion_list` is called to import `signed_inclusion_list` to the fork choice store. ```python def on_inclusion_list( - store: Store, signed_inclusion_list: SignedInclusionList) -> None: + store: Store, + signed_inclusion_list: SignedInclusionList, + inclusion_list_committee: Vector[ValidatorIndex, IL_COMMITTEE_SIZE]]) -> None: """ ``on_inclusion_list`` verify the inclusion list before importing it to fork choice store. If there exists more than 1 inclusion list in store with the same slot and validator index, remove the original one. """ message = signed_inclusion_list.message - # Verify inclusion list slot is bounded to the current slot - assert get_current_slot(store) == message.slot + # Verify inclusion list slot is either from the current or previous slot + assert get_current_slot(store) in [message.slot, message.slot + 1] - assert message.beacon_block_root in store.block_states - # Get the inclusion list committee for this slot - state = copy(store.block_states[message.beacon_block_root]) - if state.slot < message.slot: - process_slots(state, message.slot) - inclusion_list_committee = get_inclusion_list_committee(state, message.slot) + root = message.inclusion_list_committee_root + assert hash_tree_root(inclusion_list_committee) == root # Verify inclusion list validator is part of the committee validator_index = message.validator_index - assert validator_index.validator_index in inclusion_list_committee + assert validator_index in inclusion_list_committee # Verify inclusion list signature assert is_valid_inclusion_list_signature(state, signed_inclusion_list) - root = hash_tree_root(inclusion_list_committee) if validator_index not in inclusion_list_equivocators[(message.slot, root)]: if validator_index in [il.validator_index for il in inclusion_lists[(message.slot, root)]] il = [il for il in inclusion_lists[(message.slot, root)] if il.validator_index == validator_index][0] diff --git a/specs/_features/focil/p2p-interface.md b/specs/_features/focil/p2p-interface.md index 4c937c76ab..83b4a86008 100644 --- a/specs/_features/focil/p2p-interface.md +++ b/specs/_features/focil/p2p-interface.md @@ -21,7 +21,7 @@ This document contains the consensus-layer networking specification for FOCIL. | Name | Value | Unit | Duration | | - | - | :-: | :-: | -| `inclusion_list_CUT_OFF` | `uint64(9)` | seconds | 9 seconds | +| `attestation_deadline` | `uint64(4)` | seconds | 4 seconds | ### Configuration @@ -39,18 +39,21 @@ The new topics along with the type of the `data` field of a gossipsub message ar ##### Global topics -FOCIL introduces new global topics for inclusion list. +FOCIL introduces a new global topic for inclusion lists. ###### `inclusion_list` This topic is used to propagate signed inclusion list as `SignedInclusionList`. The following validations MUST pass before forwarding the `inclusion_list` on the network, assuming the alias `message = signed_inclusion_list.message`: -- _[REJECT]_ The slot `message.slot` is equal to current slot. +- _[REJECT]_ The slot `message.slot` is equal to the previous or current slot. +- _[IGNORE]_ The slot `message.slot` is equal to the current slot, or it is equal to the previous slot and the current time is less than `attestation_deadline` seconds into the slot. +- _[IGNORE]_ The `inclusion_list_committee` for slot `message.slot` on the current branch corresponds to `message.inclusion_list_committee_root`, as determined by `hash_tree_root(inclusion_list_committee) == message.inclusion_list_committee_root`. +- _[REJECT]_ The validator index `message.validator_index` is within the `inclusion_list_committee` corresponding to `message.inclusion_list_committee_root`. - _[REJECT]_ The transactions `message.transactions` length is within upperbound `MAX_TRANSACTIONS_PER_INCLUSION_LIST`. - _[IGNORE]_ The `message` is either the first or second valid message received from the validator with index `message.validator_index`. -- _[REJECT]_ The signature of `inclusion_list.signature` is valid with respect to the validator index. -- _[REJECT]_ The validator index `message.validator_index` is within the inclusion list committee given by `get_inclusion_list_committee(state, message.slot)`, where the `state` is based on `message.parent_block_root` and processed up to `message.slot`. If the validator index cannot immediately be verified against the expected committee, the inclusion list MAY be queued for later processing while the committee for the branch of `message.parent_block_root` is calculated -- in such a case do not REJECT, instead IGNORE this message. +- _[REJECT]_ The signature of `inclusion_list.signature` is valid with respect to the validator index. + ### The Req/Resp domain From b18165518b367fa0b7104f47f2c0d1a2d5cd064f Mon Sep 17 00:00:00 2001 From: fradamt <104826920+fradamt@users.noreply.github.com> Date: Wed, 23 Oct 2024 12:40:37 +0200 Subject: [PATCH 16/23] add timing logic (deaadlines) to IL processing --- specs/_features/focil/fork-choice.md | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/specs/_features/focil/fork-choice.md b/specs/_features/focil/fork-choice.md index 9bc85a59be..8c85219a22 100644 --- a/specs/_features/focil/fork-choice.md +++ b/specs/_features/focil/fork-choice.md @@ -17,6 +17,12 @@ This is the modification of the fork choice accompanying the FOCIL upgrade. +## Configuration + +| Name | Value | Unit | Duration | +| - | - | :-: | :-: | +| `VIEW_FREEZE_DEADLINE` | `uint64(9)` | seconds | 9 seconds | # [New in FOCIL] + ## Helpers ### New `validate_inclusion_lists` @@ -50,7 +56,7 @@ class Store(object): latest_messages: Dict[ValidatorIndex, LatestMessage] = field(default_factory=dict) unrealized_justifications: Dict[Root, Checkpoint] = field(default_factory=dict) inclusion_lists: Dict[Tuple[Slot, Root], List[InclusionList]] = field(default_factory=dict) # [New in FOCIL] - inclusion_list_equivocators: Dict[Tuple[Slot, Root], Set[ValidatorIndex]] = field(default_factory=dict)# [New in FOCIL] + inclusion_list_equivocators: Dict[Tuple[Slot, Root], Set[ValidatorIndex]] = field(default_factory=dict) # [New in FOCIL] ### New `on_inclusion_list` @@ -69,6 +75,13 @@ def on_inclusion_list( # Verify inclusion list slot is either from the current or previous slot assert get_current_slot(store) in [message.slot, message.slot + 1] + time_into_slot = (store.time - store.genesis_time) % SECONDS_PER_SLOT + is_before_attesting_interval = time_into_slot < SECONDS_PER_SLOT // INTERVALS_PER_SLOT + # If the inclusion list is from the previous slot, ignore it if already past the attestation deadline + if get_current_slot(store) == message.slot + 1: + assert is_before_attesting_interval + + # Sanity check that the given `inclusion_list_committee` matches the root in the inclusion list root = message.inclusion_list_committee_root assert hash_tree_root(inclusion_list_committee) == root @@ -79,12 +92,17 @@ def on_inclusion_list( # Verify inclusion list signature assert is_valid_inclusion_list_signature(state, signed_inclusion_list) + is_before_freeze_deadline = get_current_slot(store) == message.slot and time_into_slot < VIEW_FREEZE_DEADLINE + + # Do not process inclusion lists from known equivocators if validator_index not in inclusion_list_equivocators[(message.slot, root)]: if validator_index in [il.validator_index for il in inclusion_lists[(message.slot, root)]] il = [il for il in inclusion_lists[(message.slot, root)] if il.validator_index == validator_index][0] if not il == message: + # We have equivocation evidence for `validator_index`, record it as equivocator inclusion_list_equivocators[(message.slot, root)].add(validator_index) - else: + # This inclusion list is not an equivocation. Store it if prior to the view freeze deadline + elif is_before_freeze_deadline: inclusion_lists[(message.slot, root)].append(message) ``` From 0681f8c08d77432e837592e5a16850783e194ece Mon Sep 17 00:00:00 2001 From: fradamt <104826920+fradamt@users.noreply.github.com> Date: Fri, 25 Oct 2024 08:34:37 +0200 Subject: [PATCH 17/23] missing : Co-authored-by: terence --- specs/_features/focil/fork-choice.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/_features/focil/fork-choice.md b/specs/_features/focil/fork-choice.md index 8c85219a22..5044598eed 100644 --- a/specs/_features/focil/fork-choice.md +++ b/specs/_features/focil/fork-choice.md @@ -96,7 +96,7 @@ def on_inclusion_list( # Do not process inclusion lists from known equivocators if validator_index not in inclusion_list_equivocators[(message.slot, root)]: - if validator_index in [il.validator_index for il in inclusion_lists[(message.slot, root)]] + if validator_index in [il.validator_index for il in inclusion_lists[(message.slot, root)]]: il = [il for il in inclusion_lists[(message.slot, root)] if il.validator_index == validator_index][0] if not il == message: # We have equivocation evidence for `validator_index`, record it as equivocator From e678deb772fe83edd1ea54cb6d2c1e4b1e45cec6 Mon Sep 17 00:00:00 2001 From: terence tsao Date: Mon, 4 Nov 2024 21:15:18 +0800 Subject: [PATCH 18/23] Use eip7805 --- .../{focil => eip7805}/beacon-chain.md | 20 ++-- .../{focil => eip7805}/fork-choice.md | 12 +- .../{focil => eip7805}/p2p-interface.md | 10 +- .../_features/{focil => eip7805}/validator.md | 6 +- specs/_features/focil/engine-api.md | 105 ------------------ 5 files changed, 24 insertions(+), 129 deletions(-) rename specs/_features/{focil => eip7805}/beacon-chain.md (91%) rename specs/_features/{focil => eip7805}/fork-choice.md (95%) rename specs/_features/{focil => eip7805}/p2p-interface.md (93%) rename specs/_features/{focil => eip7805}/validator.md (96%) delete mode 100644 specs/_features/focil/engine-api.md diff --git a/specs/_features/focil/beacon-chain.md b/specs/_features/eip7805/beacon-chain.md similarity index 91% rename from specs/_features/focil/beacon-chain.md rename to specs/_features/eip7805/beacon-chain.md index c6f9bd8a4d..cf59ee1ca3 100644 --- a/specs/_features/focil/beacon-chain.md +++ b/specs/_features/eip7805/beacon-chain.md @@ -1,4 +1,4 @@ -# FOCIL -- The Beacon Chain +# EIP-7805 -- The Beacon Chain ## Table of contents @@ -32,7 +32,7 @@ ## Introduction -This is the beacon chain specification to add a fork-choice enforced, committee-based inclusion list (FOCIL) mechanism to allow forced transaction inclusion. Refers to the following posts: +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. @@ -43,19 +43,19 @@ This is the beacon chain specification to add a fork-choice enforced, committee- | Name | Value | | - | - | -| `DOMAIN_IL_COMMITTEE` | `DomainType('0x0C000000')` # (New in FOCIL)| +| `DOMAIN_IL_COMMITTEE` | `DomainType('0x0C000000')` # (New in EIP-7805)| ### Inclusion List Committee | Name | Value | | - | - | -| `IL_COMMITTEE_SIZE` | `uint64(2**4)` (=16) # (New in FOCIL) | +| `IL_COMMITTEE_SIZE` | `uint64(2**4)` (=16) # (New in EIP-7805) | ### Execution | Name | Value | | - | - | -| `MAX_TRANSACTIONS_PER_INCLUSION_LIST` | `uint64(1)` # (New in FOCIL) TODO: Placeholder | +| `MAX_TRANSACTIONS_PER_INCLUSION_LIST` | `uint64(1)` # (New in EIP-7805) TODO: Placeholder | ## Containers @@ -127,14 +127,14 @@ class NewPayloadRequest(object): versioned_hashes: Sequence[VersionedHash] parent_beacon_block_root: Root execution_requests: ExecutionRequests - il_transactions: List[Transaction, MAX_TRANSACTIONS_PER_INCLUSION_LIST] # [New in FOCIL] + il_transactions: List[Transaction, MAX_TRANSACTIONS_PER_INCLUSION_LIST] # [New in EIP-7805] ``` #### Engine APIs ##### Modified `notify_new_payload` -*Note*: The function `notify_new_payload` is modified to include the additional `il_transactions` parameter in FOCIL. +*Note*: The function `notify_new_payload` is modified to include the additional `il_transactions` parameter in EIP-7805. ```python def notify_new_payload(self: ExecutionEngine, @@ -152,7 +152,7 @@ def notify_new_payload(self: ExecutionEngine, ##### Modified `verify_and_notify_new_payload` *Note*: The function `verify_and_notify_new_payload` is modified to pass the additional parameter `il_transactions` -when calling `notify_new_payload` in FOCIL. +when calling `notify_new_payload` in EIP-7805. ```python def verify_and_notify_new_payload(self: ExecutionEngine, @@ -163,7 +163,7 @@ def verify_and_notify_new_payload(self: ExecutionEngine, execution_payload = new_payload_request.execution_payload execution_requests = new_payload_request.execution_requests parent_beacon_block_root = new_payload_request.parent_beacon_block_root - il_transactions = new_payload_request.il_transactions # [New in FOCIL] + il_transactions = new_payload_request.il_transactions # [New in EIP-7805] if not self.is_valid_block_hash(execution_payload, parent_beacon_block_root): return False @@ -171,7 +171,7 @@ def verify_and_notify_new_payload(self: ExecutionEngine, if not self.is_valid_versioned_hashes(new_payload_request): return False - # [Modified in FOCIL] + # [Modified in EIP-7805] if not self.notify_new_payload( execution_payload, execution_requests, diff --git a/specs/_features/focil/fork-choice.md b/specs/_features/eip7805/fork-choice.md similarity index 95% rename from specs/_features/focil/fork-choice.md rename to specs/_features/eip7805/fork-choice.md index 5044598eed..fc4db346eb 100644 --- a/specs/_features/focil/fork-choice.md +++ b/specs/_features/eip7805/fork-choice.md @@ -1,4 +1,4 @@ -# FOCIL -- Fork Choice +# EIP-7805 -- Fork Choice ## Table of contents @@ -15,13 +15,13 @@ ## Introduction -This is the modification of the fork choice accompanying the FOCIL upgrade. +This is the modification of the fork choice accompanying the EIP-7805 upgrade. ## Configuration | Name | Value | Unit | Duration | | - | - | :-: | :-: | -| `VIEW_FREEZE_DEADLINE` | `uint64(9)` | seconds | 9 seconds | # [New in FOCIL] +| `VIEW_FREEZE_DEADLINE` | `uint64(9)` | seconds | 9 seconds | # [New in EIP-7805] ## Helpers @@ -55,9 +55,9 @@ class Store(object): checkpoint_states: Dict[Checkpoint, BeaconState] = field(default_factory=dict) latest_messages: Dict[ValidatorIndex, LatestMessage] = field(default_factory=dict) unrealized_justifications: Dict[Root, Checkpoint] = field(default_factory=dict) - inclusion_lists: Dict[Tuple[Slot, Root], List[InclusionList]] = field(default_factory=dict) # [New in FOCIL] - inclusion_list_equivocators: Dict[Tuple[Slot, Root], Set[ValidatorIndex]] = field(default_factory=dict) # [New in FOCIL] - + inclusion_lists: Dict[Tuple[Slot, Root], List[InclusionList]] = field(default_factory=dict) # [New in EIP-7805] + inclusion_list_equivocators: Dict[Tuple[Slot, Root], Set[ValidatorIndex]] = field(default_factory=dict) # [New in EIP-7805] +``` ### New `on_inclusion_list` diff --git a/specs/_features/focil/p2p-interface.md b/specs/_features/eip7805/p2p-interface.md similarity index 93% rename from specs/_features/focil/p2p-interface.md rename to specs/_features/eip7805/p2p-interface.md index 83b4a86008..d2c0e0ed0e 100644 --- a/specs/_features/focil/p2p-interface.md +++ b/specs/_features/eip7805/p2p-interface.md @@ -1,6 +1,6 @@ -# FOCIL -- Networking +# EIP-7805 -- Networking -This document contains the consensus-layer networking specification for FOCIL. +This document contains the consensus-layer networking specification for EIP-7805. @@ -35,11 +35,11 @@ The new topics along with the type of the `data` field of a gossipsub message ar | Name | Message Type | |-------------------------------|------------------------------------------------------| -| `inclusion_list` | `SignedInclusionList` [New in FOCIL] | +| `inclusion_list` | `SignedInclusionList` [New in EIP-7805] | ##### Global topics -FOCIL introduces a new global topic for inclusion lists. +EIP-7805 introduces a new global topic for inclusion lists. ###### `inclusion_list` @@ -69,7 +69,7 @@ The `` field is calculated as `context = compute_fork_digest(fork | `fork_version` | Chunk SSZ type | |------------------------|------------------------------------------| -| `FOCIL_FORK_VERSION` | `focil.SignedInclusionList` | +| `EIP-7805_FORK_VERSION` | `EIP-7805.SignedInclusionList` | Request Content: ``` diff --git a/specs/_features/focil/validator.md b/specs/_features/eip7805/validator.md similarity index 96% rename from specs/_features/focil/validator.md rename to specs/_features/eip7805/validator.md index bc99f15de5..af27256c1b 100644 --- a/specs/_features/focil/validator.md +++ b/specs/_features/eip7805/validator.md @@ -1,4 +1,4 @@ -# FOCIL -- Honest Validator +# EIP-7805 -- Honest Validator ## Table of contents @@ -25,14 +25,14 @@ ## Introduction -This document represents the changes to be made in the code of an "honest validator" to implement FOCIL. +This document represents the changes to be made in the code of an "honest validator" to implement EIP-7805. ## Prerequisites This document is an extension of the [Electra -- Honest Validator](../../electra/validator.md) guide. All behaviors and definitions defined in this document, and documents it extends, carry over unless explicitly noted or overridden. -All terminology, constants, functions, and protocol mechanics defined in the updated Beacon Chain doc of [FOCIL](./beacon-chain.md) are requisite for this document and used throughout. +All terminology, constants, functions, and protocol mechanics defined in the updated Beacon Chain doc of [EIP-7805](./beacon-chain.md) are requisite for this document and used throughout. Please see related Beacon Chain doc before continuing and use them as a reference throughout. ### Time parameters diff --git a/specs/_features/focil/engine-api.md b/specs/_features/focil/engine-api.md deleted file mode 100644 index b69e059bdb..0000000000 --- a/specs/_features/focil/engine-api.md +++ /dev/null @@ -1,105 +0,0 @@ -# Engine API -- FOCIL - -Engine API changes introduced in FOCIL. - -This specification is based on and extends [Engine API - Prague](./prague.md) specification. - -Warning: This file should be placed in https://github.com/ethereum/execution-apis but I'm placing it here for convenience of reviewing. - -## Table of contents - - - - -- [Constants](#constants) - - [PayloadStatusV2](#payloadstatusv2) -- [Methods](#methods) - - [engine_newPayloadV5](#engine_newpayloadv5) - - [Request](#request) - - [Response](#response) - - [Specification](#specification) - - [engine_updateBlockWithInclusionListV1](#engine_updateblockwithinclusionlistv1) - - [Request](#request-1) - - [Response](#response-1) - - [Specification](#specification-1) - - [engine_getInclusionListV1](#engine_getinclusionlistv1) - - [Request](#request-2) - - [Response](#response-2) - - [Specification](#specification-2) - - - -## Constants - -| Name | Value | -| - | - | -| `INCLUSION_LIST_MAX_GAS` | `uint64(4194304) = 2**22` | - -### PayloadStatusV2 - -This structure contains the result of processing a payload. The fields are encoded as follows: - -- `status`: `enum` - `"VALID" | "INVALID" | "SYNCING" | "ACCEPTED" | "INVALID_BLOCK_HASH" | "INVALID_INCLUSION_LIST"` -- `latestValidHash`: `DATA|null`, 32 Bytes - the hash of the most recent *valid* block in the branch defined by payload and its ancestors -- `validationError`: `String|null` - a message providing additional details on the validation error if the payload is classified as `INVALID` or `INVALID_BLOCK_HASH`. - -## Methods - -### engine_newPayloadV5 - -The request of this method is updated with [`ExecutionPayloadV5`](#ExecutionPayloadV5). - -#### Request - -* method: `engine_newPayloadV5` -* params: - 1. `executionPayload`: [`ExecutionPayloadV4`](#ExecutionPayloadV4). - 2. `expectedBlobVersionedHashes`: `Array of DATA`, 32 Bytes - Array of expected blob versioned hashes to validate. - 3. `parentBeaconBlockRoot`: `DATA`, 32 Bytes - Root of the parent beacon block. - 4. `inclusionListTransactions`: `Array of DATA` - Array of transaction objects, each object is a byte list (`DATA`) representing `TransactionType || TransactionPayload` or `LegacyTransaction` as defined in [EIP-2718](https://eips.ethereum.org/EIPS/eip-2718) - -#### Response - -* result: [`PayloadStatusV2`](#PayloadStatusV2) - -#### Specification - -1. Client software **MUST** respond to this method call in the following way: - * `{status: INVALID_INCLUSION_LIST, latestValidHash: null, validationError: null}` if there are any leftover `inclusionListTransactions` that are not part of the `executionPayload`, they can be processed at the end of the `executionPayload` or the block is full. - -### engine_updateBlockWithInclusionListV1 - -#### Request - -* method: `engine_updateBlockWithInclusionListV1` -* params: - 1. `payloadId`: `DATA`, 8 Bytes - Identifier of the payload build process - 2. `inclusionListTransactions`: `Array of DATA` - Array of transaction objects, each object is a byte list (`DATA`) representing `TransactionType || TransactionPayload` or `LegacyTransaction` as defined in [EIP-2718](https://eips.ethereum.org/EIPS/eip-2718) -* timeout: 1s - -#### Response - -#### Specification - -1. Given the `payloadId` client software **MUST** update payload build process building with a list of `inclusionListTransactions`. The transactions must be part of the execution payload unless it fails to be included at the end of the execution block. - -* error: code and message set in case an exception happens while getting the payload. - -### engine_getInclusionListV1 - -#### Request - -* method: `engine_getInclusionListV1` -* params: - 1. `parent_hash`: `DATA`, 32 Bytes - parent hash which returned inclusion list should be built upon. -* timeout: 1s - -#### Response - -* result: `object` - - `inclusionListTransactions`: `Array of DATA` - Array of transaction objects, each object is a byte list (`DATA`) representing `TransactionType || TransactionPayload` or `LegacyTransaction` as defined in [EIP-2718](https://eips.ethereum.org/EIPS/eip-2718) -* error: code and message set in case an exception happens while getting the inclusion list. - -#### Specification - -1. Client software **MUST** provide a list of transactions for the inclusion list based on local view of the mempool and according to the config specifications. \ No newline at end of file From e9fdfc68854ddf5ea16c27f74343e6cdee77ca1d Mon Sep 17 00:00:00 2001 From: terence Date: Fri, 8 Nov 2024 01:35:59 -0800 Subject: [PATCH 19/23] @jtraglia's suggestions Co-authored-by: Justin Traglia <95511699+jtraglia@users.noreply.github.com> --- specs/_features/eip7805/beacon-chain.md | 8 ++++---- specs/_features/eip7805/fork-choice.md | 14 +++++++++----- specs/_features/eip7805/p2p-interface.md | 1 - specs/_features/eip7805/validator.md | 10 +++++----- 4 files changed, 18 insertions(+), 15 deletions(-) diff --git a/specs/_features/eip7805/beacon-chain.md b/specs/_features/eip7805/beacon-chain.md index cf59ee1ca3..f128bab7b2 100644 --- a/specs/_features/eip7805/beacon-chain.md +++ b/specs/_features/eip7805/beacon-chain.md @@ -43,7 +43,7 @@ This is the beacon chain specification to add EIP-7805 / fork-choice enforced, c | Name | Value | | - | - | -| `DOMAIN_IL_COMMITTEE` | `DomainType('0x0C000000')` # (New in EIP-7805)| +| `DOMAIN_IL_COMMITTEE` | `DomainType('0x0C000000')` # (New in EIP7805) | ### Inclusion List Committee @@ -55,7 +55,7 @@ This is the beacon chain specification to add EIP-7805 / fork-choice enforced, c | Name | Value | | - | - | -| `MAX_TRANSACTIONS_PER_INCLUSION_LIST` | `uint64(1)` # (New in EIP-7805) TODO: Placeholder | +| `MAX_TRANSACTIONS_PER_INCLUSION_LIST` | `uint64(1)` # (New in EIP-7805) TODO: Placeholder | ## Containers @@ -88,14 +88,14 @@ def is_valid_inclusion_list_signature( state: BeaconState, signed_inclusion_list: SignedInclusionList) -> bool: """ - Check if ``signed_inclusion_list`` has a valid signature + 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_IL_COMMITTEE, compute_epoch_at_slot(message.slot)) signing_root = compute_signing_root(message, domain) - return bls.FastAggregateVerify(pubkey, signing_root, signed_inclusion_list.signature) + return bls.Verify(pubkey, signing_root, signed_inclusion_list.signature) ``` ### Beacon State accessors diff --git a/specs/_features/eip7805/fork-choice.md b/specs/_features/eip7805/fork-choice.md index fc4db346eb..8cdc87d680 100644 --- a/specs/_features/eip7805/fork-choice.md +++ b/specs/_features/eip7805/fork-choice.md @@ -21,7 +21,7 @@ This is the modification of the fork choice accompanying the EIP-7805 upgrade. | Name | Value | Unit | Duration | | - | - | :-: | :-: | -| `VIEW_FREEZE_DEADLINE` | `uint64(9)` | seconds | 9 seconds | # [New in EIP-7805] +| `VIEW_FREEZE_DEADLINE` | `uint64(9)` | seconds | 9 seconds # (New in EIP7805) | ## Helpers @@ -30,13 +30,16 @@ This is the modification of the fork choice accompanying the EIP-7805 upgrade. ```python def validate_inclusion_lists(store: Store, inclusion_list_transactions: List[Transaction, MAX_TRANSACTIONS_PER_INCLUSION_LIST * IL_COMMITTEE_SIZE], execution_payload: ExecutionPayload) -> bool: """ - Return ``True`` if and only if the input ``inclusion_list_transactions`` satifies validation, that to verify if the `execution_payload` satisfies `inclusion_list_transactions` validity conditions either when all transactions are present in payload or when any missing transactions are found to be invalid when appended to the end of the payload unless the block is full. + Return ``True`` if and only if the input ``inclusion_list_transactions`` satifies validation, + that to verify if the ``execution_payload`` satisfies ``inclusion_list_transactions`` validity conditions either when all transactions are present in payload or + when any missing transactions are found to be invalid when appended to the end of the payload unless the block is full. """ ... ``` ### Modified `Store` -**Note:** `Store` is modified to track the seen inclusion lists. + +**Note:** `Store` is modified to track the seen inclusion lists and inclusion list equivocators. ```python @dataclass @@ -62,13 +65,14 @@ class Store(object): ### New `on_inclusion_list` `on_inclusion_list` is called to import `signed_inclusion_list` to the fork choice store. + ```python def on_inclusion_list( store: Store, signed_inclusion_list: SignedInclusionList, inclusion_list_committee: Vector[ValidatorIndex, IL_COMMITTEE_SIZE]]) -> None: """ - ``on_inclusion_list`` verify the inclusion list before importing it to fork choice store. + Verify the inclusion list and import it into the fork choice store. If there exists more than 1 inclusion list in store with the same slot and validator index, remove the original one. """ message = signed_inclusion_list.message @@ -98,7 +102,7 @@ def on_inclusion_list( if validator_index not in inclusion_list_equivocators[(message.slot, root)]: if validator_index in [il.validator_index for il in inclusion_lists[(message.slot, root)]]: il = [il for il in inclusion_lists[(message.slot, root)] if il.validator_index == validator_index][0] - if not il == message: + if validator_il != message: # We have equivocation evidence for `validator_index`, record it as equivocator inclusion_list_equivocators[(message.slot, root)].add(validator_index) # This inclusion list is not an equivocation. Store it if prior to the view freeze deadline diff --git a/specs/_features/eip7805/p2p-interface.md b/specs/_features/eip7805/p2p-interface.md index d2c0e0ed0e..1ca8155698 100644 --- a/specs/_features/eip7805/p2p-interface.md +++ b/specs/_features/eip7805/p2p-interface.md @@ -54,7 +54,6 @@ The following validations MUST pass before forwarding the `inclusion_list` on th - _[IGNORE]_ The `message` is either the first or second valid message received from the validator with index `message.validator_index`. - _[REJECT]_ The signature of `inclusion_list.signature` is valid with respect to the validator index. - ### The Req/Resp domain #### Messages diff --git a/specs/_features/eip7805/validator.md b/specs/_features/eip7805/validator.md index af27256c1b..c857936ee0 100644 --- a/specs/_features/eip7805/validator.md +++ b/specs/_features/eip7805/validator.md @@ -51,7 +51,7 @@ The body of these function is implementation dependent. The Engine API may be us ## New inclusion list committee assignment -A validator may be a member of the new Inclusion List Committee (ILC) for a given slot. To check for ILC assignments the validator uses the helper `get_ilc_assignment(state, epoch, validator_index)` where `epoch <= next_epoch`. +A validator may be a member of the new Inclusion List Committee (ILC) for a given slot. To check for ILC assignments the validator uses the helper `get_ilc_assignment(state, epoch, validator_index)` where `epoch <= next_epoch`. ILC selection is only stable within the context of the current and next epoch. @@ -61,7 +61,7 @@ def get_ilc_assignment( epoch: Epoch, validator_index: ValidatorIndex) -> Optional[Slot]: """ - Returns the slot during the requested epoch in which the validator with index `validator_index` + Returns the slot during the requested epoch in which the validator with index ``validator_index`` is a member of the ILC. Returns None if no assignment is found. """ next_epoch = Epoch(get_current_epoch(state) + 1) @@ -82,18 +82,18 @@ def get_ilc_assignment( ### Block proposal -Proposers are still expected to propose `SignedBeaconBlock` at the beginning of any slot during which `is_proposer(state, validator_index)` returns `true`. The mechanism to prepare this beacon block and related sidecars differs from previous forks as follows: +Proposers are still expected to propose `SignedBeaconBlock` at the beginning of any slot during which `is_proposer(state, validator_index)` returns true. The mechanism to prepare this beacon block and related sidecars differs from previous forks as follows: #### Update execution client with inclusion lists -The proposer should call `engine_updateInclusionListV1` at `PROPOSER_INCLUSION_LIST_CUT_OFF` into the slot with the list of the inclusion lists that gathered since `inclusion_list_CUT_OFF` +The proposer should call `engine_updateInclusionListV1` at `PROPOSER_INCLUSION_LIST_CUT_OFF` into the slot with the list of the inclusion lists that gathered since `inclusion_list_CUT_OFF`. ## New inclusion list committee duty Some validators are selected to submit signed inclusion list. Validators should call `get_ilc_assignment` at the beginning of an epoch to be prepared to submit their inclusion list during the next epoch. -A validator should create and broadcast the `signed_inclusion_list` to the global `inclusion_list` subnet by the `inclusion_list_CUT_OFF` in the slot, unless a block for the current slot has been processed and is the head of the chain and broadcast to the network. +A validator should create and broadcast the `signed_inclusion_list` to the global `inclusion_list` subnet by `PROPOSER_INCLUSION_LIST_CUT_OFF` seconds into the slot, unless a block for the current slot has been processed and is the head of the chain and broadcast to the network. #### Constructing a signed inclusion list From 6ce0077f91b83a1115aad65e7823ca284318f799 Mon Sep 17 00:00:00 2001 From: terence Date: Tue, 19 Nov 2024 07:12:57 -0800 Subject: [PATCH 20/23] Update new inclusion list comments --- specs/_features/eip7805/fork-choice.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/specs/_features/eip7805/fork-choice.md b/specs/_features/eip7805/fork-choice.md index 8cdc87d680..8ffb738849 100644 --- a/specs/_features/eip7805/fork-choice.md +++ b/specs/_features/eip7805/fork-choice.md @@ -73,7 +73,8 @@ def on_inclusion_list( inclusion_list_committee: Vector[ValidatorIndex, IL_COMMITTEE_SIZE]]) -> None: """ Verify the inclusion list and import it into the fork choice store. - If there exists more than 1 inclusion list in store with the same slot and validator index, remove the original one. + If there exists more than 1 inclusion list in store with the same slot and validator index, add the equivocator to the ``inclusion_list_equivocators`` cache. + Otherwise, add inclusion list to the ``inclusion_lists` cache. """ message = signed_inclusion_list.message # Verify inclusion list slot is either from the current or previous slot From be471b8d5be137274ba5c72d2e0f6e44f4d58b68 Mon Sep 17 00:00:00 2001 From: terence Date: Tue, 19 Nov 2024 07:19:14 -0800 Subject: [PATCH 21/23] P2p spec feedbacks --- specs/_features/eip7805/p2p-interface.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/specs/_features/eip7805/p2p-interface.md b/specs/_features/eip7805/p2p-interface.md index 1ca8155698..a5c9086a65 100644 --- a/specs/_features/eip7805/p2p-interface.md +++ b/specs/_features/eip7805/p2p-interface.md @@ -46,6 +46,7 @@ EIP-7805 introduces a new global topic for inclusion lists. This topic is used to propagate signed inclusion list as `SignedInclusionList`. The following validations MUST pass before forwarding the `inclusion_list` on the network, assuming the alias `message = signed_inclusion_list.message`: +- _[REJECT]_ The transactions `message.transactions` is not exceeding `MAX_BYTES_PER_INCLUSION_LIST`. - _[REJECT]_ The slot `message.slot` is equal to the previous or current slot. - _[IGNORE]_ The slot `message.slot` is equal to the current slot, or it is equal to the previous slot and the current time is less than `attestation_deadline` seconds into the slot. - _[IGNORE]_ The `inclusion_list_committee` for slot `message.slot` on the current branch corresponds to `message.inclusion_list_committee_root`, as determined by `hash_tree_root(inclusion_list_committee) == message.inclusion_list_committee_root`. @@ -68,7 +69,7 @@ The `` field is calculated as `context = compute_fork_digest(fork | `fork_version` | Chunk SSZ type | |------------------------|------------------------------------------| -| `EIP-7805_FORK_VERSION` | `EIP-7805.SignedInclusionList` | +| `EIP7805_FORK_VERSION` | `EIP-7805.SignedInclusionList` | Request Content: ``` @@ -83,4 +84,4 @@ Response Content: ( List[SignedInclusionList, MAX_REQUEST_INCLUSION_LIST] ) -``` \ No newline at end of file +``` From c443db01bf43ef733e409ed9edb646e790d9e7db Mon Sep 17 00:00:00 2001 From: terence tsao Date: Tue, 19 Nov 2024 07:46:32 -0800 Subject: [PATCH 22/23] Add get attester head --- specs/_features/eip7805/beacon-chain.md | 18 ---------- specs/_features/eip7805/fork-choice.md | 38 +++++++++++++++++++++ specs/_features/eip7805/validator.md | 44 ++++++++++++++++++++----- 3 files changed, 73 insertions(+), 27 deletions(-) diff --git a/specs/_features/eip7805/beacon-chain.md b/specs/_features/eip7805/beacon-chain.md index f128bab7b2..a6b4c3a915 100644 --- a/specs/_features/eip7805/beacon-chain.md +++ b/specs/_features/eip7805/beacon-chain.md @@ -24,7 +24,6 @@ - [Request data](#request-data) - [Modified `NewPayloadRequest`](#modified-newpayloadrequest) - [Engine APIs](#engine-apis) - - [Modified `notify_new_payload`](#modified-notify_new_payload) - [Modified `verify_and_notify_new_payload`](#modified-verify_and_notify_new_payload) @@ -132,23 +131,6 @@ class NewPayloadRequest(object): #### Engine APIs -##### Modified `notify_new_payload` - -*Note*: The function `notify_new_payload` is modified to include the additional `il_transactions` parameter in EIP-7805. - -```python -def notify_new_payload(self: ExecutionEngine, - execution_payload: ExecutionPayload, - execution_requests: ExecutionRequests, - parent_beacon_block_root: Root, - il_transactions: List[Transaction, MAX_TRANSACTIONS_PER_INCLUSION_LIST] ) -> bool: - """ - Return ``True`` if and only if ``execution_payload`` and ``execution_requests`` - are valid with respect to ``self.execution_state``. - """ - ... -``` - ##### Modified `verify_and_notify_new_payload` *Note*: The function `verify_and_notify_new_payload` is modified to pass the additional parameter `il_transactions` diff --git a/specs/_features/eip7805/fork-choice.md b/specs/_features/eip7805/fork-choice.md index 8ffb738849..065a84e886 100644 --- a/specs/_features/eip7805/fork-choice.md +++ b/specs/_features/eip7805/fork-choice.md @@ -6,9 +6,13 @@ - [Introduction](#introduction) +- [Configuration](#configuration) - [Helpers](#helpers) - [New `validate_inclusion_lists`](#new-validate_inclusion_lists) - [Modified `Store`](#modified-store) + - [Modified `notify_new_payload`](#modified-notify_new_payload) + - [`get_attester_head`](#get_attester_head) + - [New `on_inclusion_list`](#new-on_inclusion_list) @@ -60,6 +64,40 @@ class Store(object): unrealized_justifications: Dict[Root, Checkpoint] = field(default_factory=dict) inclusion_lists: Dict[Tuple[Slot, Root], List[InclusionList]] = field(default_factory=dict) # [New in EIP-7805] inclusion_list_equivocators: Dict[Tuple[Slot, Root], Set[ValidatorIndex]] = field(default_factory=dict) # [New in EIP-7805] + unsatisfied_inclusion_list_blocks: Set[Root] # [New in EIP-7805] +``` + +##### Modified `notify_new_payload` + +*Note*: The function `notify_new_payload` is modified to include the additional `il_transactions` parameter in EIP-7805. + +```python +def notify_new_payload(self: ExecutionEngine, + execution_payload: ExecutionPayload, + execution_requests: ExecutionRequests, + parent_beacon_block_root: Root, + il_transactions: List[Transaction, MAX_TRANSACTIONS_PER_INCLUSION_LIST], + store: Store) -> bool: + """ + Return ``True`` if and only if ``execution_payload`` and ``execution_requests`` + are valid with respect to ``self.execution_state``. + """ + + # If execution client returns block does not satisfy inclusion list transactions, cache the block + store.unsatisfied_inclusion_list_blocks.add(execution_payload.block_root) + ... +``` + +##### `get_attester_head` + +```python +def get_attester_head(store: Store, head_root: Root) -> Root: + head_block = store.blocks[head_root] + + if head_root in store.unsatisfied_inclusion_list_blocks: + return head_block.parent_root + return head_root + ``` ### New `on_inclusion_list` diff --git a/specs/_features/eip7805/validator.md b/specs/_features/eip7805/validator.md index c857936ee0..b71d3e44ee 100644 --- a/specs/_features/eip7805/validator.md +++ b/specs/_features/eip7805/validator.md @@ -19,6 +19,9 @@ - [New inclusion list committee duty](#new-inclusion-list-committee-duty) - [Constructing a signed inclusion list](#constructing-a-signed-inclusion-list) - [Modified attester duty](#modified-attester-duty) + - [Modified LMD GHOST vote](#modified-lmd-ghost-vote) +- [Modified sync committee duty](#modified-sync-committee-duty) + - [Modified beacon block root](#modified-beacon-block-root) @@ -51,12 +54,12 @@ The body of these function is implementation dependent. The Engine API may be us ## New inclusion list committee assignment -A validator may be a member of the new Inclusion List Committee (ILC) for a given slot. To check for ILC assignments the validator uses the helper `get_ilc_assignment(state, epoch, validator_index)` where `epoch <= next_epoch`. +A validator may be a member of the new Inclusion List Committee (ILC) for a given slot. To check for ILC assignments the validator uses the helper `get_inclusion_committee_assignment(state, epoch, validator_index)` where `epoch <= next_epoch`. -ILC selection is only stable within the context of the current and next epoch. +Inclusion list committee selection is only stable within the context of the current and next epoch. ```python -def get_ilc_assignment( +def get_inclusion_committee_assignment( state: BeaconState, epoch: Epoch, validator_index: ValidatorIndex) -> Optional[Slot]: @@ -76,7 +79,7 @@ def get_ilc_assignment( ### Lookahead -`get_ilc_assignment` should be called at the start of each epoch to get the assignment for the next epoch (`current_epoch + 1`). A validator should plan for future assignments by noting their assigned ILC slot. +`get_inclusion_committee_assignment` should be called at the start of each epoch to get the assignment for the next epoch (`current_epoch + 1`). A validator should plan for future assignments by noting their assigned ILC slot. ## New proposer duty @@ -91,7 +94,7 @@ The proposer should call `engine_updateInclusionListV1` at `PROPOSER_INCLUSION_L ## New inclusion list committee duty -Some validators are selected to submit signed inclusion list. Validators should call `get_ilc_assignment` at the beginning of an epoch to be prepared to submit their inclusion list during the next epoch. +Some validators are selected to submit signed inclusion list. Validators should call `get_inclusion_committee_assignment` at the beginning of an epoch to be prepared to submit their inclusion list during the next epoch. A validator should create and broadcast the `signed_inclusion_list` to the global `inclusion_list` subnet by `PROPOSER_INCLUSION_LIST_CUT_OFF` seconds into the slot, unless a block for the current slot has been processed and is the head of the chain and broadcast to the network. @@ -99,10 +102,9 @@ A validator should create and broadcast the `signed_inclusion_list` to the globa The validator creates the `signed_inclusion_list` as follows: - First, the validator creates the `inclusion_list`. -- Set `inclusion_list.slot` to the assigned slot returned by `get_ilc_assignment`. +- Set `inclusion_list.slot` to the assigned slot returned by `get_inclusion_committee_assignment`. - Set `inclusion_list.validator_index` to the validator's index. -- Set `inclusion_list.parent_hash` to the block hash of the fork choice head. -- Set `inclusion_list.parent_root` to the block root of the fork choice head. +- Set `inclusion_list.inclusion_list_committee_root` to the hash tree root of the committee that the validator is a member of. - Set `inclusion_list.transactions` using the response from `engine_getInclusionListV1` from the execution layer client. - Sign the `inclusion_list` using the helper `get_inclusion_list_signature` and obtain the `signature`. - Set `signed_inclusion_list.message` to `inclusion_list`. @@ -118,4 +120,28 @@ def get_inclusion_list_signature( ## Modified attester duty -Attesters should not vote for the head block if `validate_inclusion_lists` of the head block returns false. \ No newline at end of file +#### Modified LMD GHOST vote + +Set `attestation_data.beacon_block_root = get_attester_head(store, head_root)`. + +## Modified sync committee duty + +#### Modified beacon block root + +```python +def get_sync_committee_message(state: BeaconState, + block_root: Root, + validator_index: ValidatorIndex, + privkey: int) -> SyncCommitteeMessage: + epoch = get_current_epoch(state) + domain = get_domain(state, DOMAIN_SYNC_COMMITTEE, epoch) + signing_root = compute_signing_root(block_root, domain) + signature = bls.Sign(privkey, signing_root) + + return SyncCommitteeMessage( + slot=state.slot, + beacon_block_root=get_attester_head(store, block_root), + validator_index=validator_index, + signature=signature, + ) +``` From c8b6296f4750d21dd68f57260298e25991d2c93b Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Mon, 2 Dec 2024 09:57:52 -0500 Subject: [PATCH 23/23] Fix typo and make CI happy --- specs/_features/eip7805/fork-choice.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/_features/eip7805/fork-choice.md b/specs/_features/eip7805/fork-choice.md index 065a84e886..f17077dc7f 100644 --- a/specs/_features/eip7805/fork-choice.md +++ b/specs/_features/eip7805/fork-choice.md @@ -34,7 +34,7 @@ This is the modification of the fork choice accompanying the EIP-7805 upgrade. ```python def validate_inclusion_lists(store: Store, inclusion_list_transactions: List[Transaction, MAX_TRANSACTIONS_PER_INCLUSION_LIST * IL_COMMITTEE_SIZE], execution_payload: ExecutionPayload) -> bool: """ - Return ``True`` if and only if the input ``inclusion_list_transactions`` satifies validation, + Return ``True`` if and only if the input ``inclusion_list_transactions`` satisfies validation, that to verify if the ``execution_payload`` satisfies ``inclusion_list_transactions`` validity conditions either when all transactions are present in payload or when any missing transactions are found to be invalid when appended to the end of the payload unless the block is full. """