Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

FOCIL #2

Draft
wants to merge 13 commits into
base: dev
Choose a base branch
from
Draft

FOCIL #2

wants to merge 13 commits into from

Conversation

terencechain
Copy link
Owner

No description provided.

Copy link

@soispoke soispoke left a comment

Choose a reason for hiding this comment

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

General comment/question:

Just to make sure I understand: I couldn't find the Agg/Eval functions attesters have to run before casting their votes, is this expected?

- [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)

Choose a reason for hiding this comment

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

should it be verify_and_notify_new_inclusion_list_aggregate?


## 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)

Choose a reason for hiding this comment

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

better to say "to add a fork-choice enforced, committee-based inclusion list (FOCIL)" here


| Name | Value |
| - | - |
| `IL_COMMITTEE_SIZE` | `uint64(2**9)` (=512) # (New in FOCIL) |

Choose a reason for hiding this comment

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

this is still very much to be defined, but in the post it's 256, not 512

#### `InclusionSummary`

```python
class InclusionSummary(Container):

Choose a reason for hiding this comment

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

LocalInclusionSummary?

sync_aggregate: SyncAggregate
bls_to_execution_changes: List[SignedBLSToExecutionChange, MAX_BLS_TO_EXECUTION_CHANGES]
# FOCIL
inclusion_summary_aggregates: InclusionSummaryAggregates # [New in FOCIL]

Choose a reason for hiding this comment

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

the note mentions inclusion_summary_aggregate but here you added an "s" at the end

Copy link
Owner Author

@terencechain terencechain Sep 17, 2024

Choose a reason for hiding this comment

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

I think the note is wrong, inclusion_summary_aggregates is correct here. Aligns with bls_to_execution_changes and others


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

Choose a reason for hiding this comment

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

Should probably be LocalInclusionListSummary here too (throughout the whole section)


| Name | Value |
| - | - |
| `INCLUSION_LIST_MAX_GAS` | `uint64(4194304) = 2**22` |

Choose a reason for hiding this comment

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

LOCAL_INCLUSION_LIST_MAX_GAS

- [Topics and messages](#topics-and-messages-1)
- [Global topics](#global-topics)
- [`local_inclusion_list`](#local_inclusion_list)
- [`inclusion_summary_aggregate`](#inclusion_summary_aggregate)

Choose a reason for hiding this comment

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

did you mean inclusion_list_summary_aggregate?

Copy link
Owner Author

Choose a reason for hiding this comment

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

I renamed everything from inclusion_list_summary -> inclusion_summary. It feels cleaner, happy to change but I will leave naming stuff to the last. Focusing on functionality first

class SignedInclusionListAggregates(Container):
slot: Slot
proposer_index: ValidatorIndex
message: InclusionSummaryAggregates

Choose a reason for hiding this comment

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

same q here

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]

Choose a reason for hiding this comment

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

What happens if multiple committees assemble their members from the beginning of beacon committee at the same time? Does a validator with multiple committee roles just fulfill its duties? If so, wouldn't this be a burden at some point?

Copy link
Owner Author

Choose a reason for hiding this comment

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

It's just one committee, size 256 or 512, if you get duplicated entries then just do it twice. I don't think it's worth optimizing this edge case at this point

Choose a reason for hiding this comment

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

Alright, it was a genuine question. Thanks for the answer!

"""
Return the indexed inclusion list aggregate corresponding to ``inclusion_summary_aggregate``.
"""
indices = get_inclusion_list_aggregate_indices(state)

Choose a reason for hiding this comment

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

You need to pass inclusion_summary_aggregate.

#### `IndexedInclusionListAggregate`

```python
class IndexedInclusionListAggregate(Container):

Choose a reason for hiding this comment

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

Suggested change
class IndexedInclusionListAggregate(Container):
class IndexedInclusionSummaryAggregate(Container):

#### New `get_indexed_inclusion_list_aggregate`

```python
def get_indexed_inclusion_list_aggregate(state: BeaconState,

Choose a reason for hiding this comment

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

Suggested change
def get_indexed_inclusion_list_aggregate(state: BeaconState,
def get_indexed_inclusion_summary_aggregate(state: BeaconState,

#### New `get_inclusion_list_aggregate_indices`

```python
def get_inclusion_list_aggregate_indices(state: BeaconState,

Choose a reason for hiding this comment

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

Suggested change
def get_inclusion_list_aggregate_indices(state: BeaconState,
def get_inclusion_summary_aggregate_indices(state: BeaconState,


```python
class InclusionSummaryAggregates(Container):
List[InclusionSummaryAggregate, MAX_TRANSACTIONS_PER_INCLUSION_LIST]

Choose a reason for hiding this comment

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

If I understood correctly, the maximum number of InclusionSummaryAggregate is MAX_TRANSACTIONS_PER_INCLUSION_LIST * IL_COMMITTEE_SIZE.

Comment on lines 78 to 80
address: ExecutionAddress
nonce: uint64
gas_limit: uint64

Choose a reason for hiding this comment

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

Do these combination always correspond to one transaction? What if an EOA sends a transaction and then sends another one with the same nonce and gas_limit but with higher priority fee in the hope that it can effectively cancel the first one?

Copy link
Owner Author

Choose a reason for hiding this comment

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

Execution layer will filter that

validator_index: ValidatorIndex
parent_hash: Hash32
summaries: List[InclusionSummary, MAX_TRANSACTIONS_PER_INCLUSION_LIST]
transactions: List[Transaction, MAX_TRANSACTIONS_PER_INCLUSION_LIST]

Choose a reason for hiding this comment

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

Shouldn't LocalInclusionList contain any raw transaction to prevent free DA? I think raw transactions need to be cached somewhere else.

Copy link
Owner Author

Choose a reason for hiding this comment

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

LocalInclusionList can't be proven on chain, there's no free DA. Yea it will be cached short term, which wont be specified in the spec

Choose a reason for hiding this comment

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

You're right, I was mistaken. Only the summary is part of a block.

inclusion_summary_aggregates: InclusionSummaryAggregates # [New in FOCIL]
```

### Beacon State accessors

Choose a reason for hiding this comment

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

I think we also need a function that takes a list of SignedLocalInclusionList and outputs InclusionSummaryAggregates.

Copy link
Owner Author

Choose a reason for hiding this comment

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

I don't think that's part of the state transition function though. I briefly mentioned this in the validator spec. I think this is an implementation detail. Nodes will cache signed local inclusion lists, aggregate them, and release them

Copy link

@jihoonsong jihoonsong Sep 17, 2024

Choose a reason for hiding this comment

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

I don't think it belongs to beacon state transition function either. I think it belongs to beacon state accessors or at least a helper. Currently, all beacon state accessors are using InclusionSummaryAggregate so I thought they need some function to derive it from the received a list of SignedLocalInclusionList. It's for attesters rather than IL committee members.

Copy link
Owner Author

Choose a reason for hiding this comment

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

Let's add that to the fork choice spec or validator spec. Fork choice spec will use it to lock views

@terencechain
Copy link
Owner Author

Just to make sure I understand: I couldn't find the Agg/Eval functions attesters have to run before casting their votes, is this expected?

That's part of fork choice spec, which I've been procrastinating on : )

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

Choose a reason for hiding this comment

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

Suggested change
def get_inclusion_summary_aggregate(
def get_inclusion_summary_aggregates_signature(

Comment on lines 100 to 103
#### 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.

Choose a reason for hiding this comment

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

InclusionSummaryAggregate -> InclusionSummaryAggregates

#### `SignedInclusionListAggregate`

```python
class SignedInclusionListAggregates(Container):

Choose a reason for hiding this comment

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

Suggested change
class SignedInclusionListAggregates(Container):
class SignedInclusionSummaryAggregates(Container):

- _[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`

Choose a reason for hiding this comment

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

Suggested change
###### `inclusion_summary_aggregate`
###### `inclusion_summary_aggregates`

sync_aggregate: SyncAggregate
bls_to_execution_changes: List[SignedBLSToExecutionChange, MAX_BLS_TO_EXECUTION_CHANGES]
# FOCIL
inclusion_summary_aggregates: InclusionSummaryAggregates # [New in FOCIL]

Choose a reason for hiding this comment

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

I just realize this could add quite amount of bytes to beacon block. I'm not aware of what is the acceptable range, but here is my back-of-the-napkin math.

InclusionSummary: address + uint64 + uint64 = (20 + 8 + 8) bytes = 36 bytes.

InclusionSummaryAggregate: Bitvector[IL_COMMITTEE_SIZE] + InclusionSummary + BLSSignature = (256 / 8 + 36 + 96) bytes = 164 bytes.

InclusionSummaryAggregates: InclusionSummaryAggregate * MAX_TRANSACTIONS_PER_INCLUSION_LIST * IL_COMMITTEE_SIZE = (164 * MAX_TRANSACTIONS_PER_INCLUSION_LIST * 256) bytes >= 41,984 bytes.

Of course it depends on constants values, which are not fixed yet.

The ideal size of the new field would be (128 + 36 * MAX_TRANSACTIONS_PER_INCLUSION_LIST) bytes, which comes from:

aggregation_bits: BitVector[IL_COMMITTEE_SIZE]
summaries: List[InclusionSummary, MAX_TRANSACTIONS_PER_INCLUSION_LIST]
signature: BLSSignature

But this requires at least one more round from a proposer to IL committee members. I don't have a good solution at the moment but wanted to share what I've found.


## 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)

Choose a reason for hiding this comment

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

Might be worth to add this one also? Could help the draft reader to follow up the concepts.. but it doesn't really matter as we can add a link to EIP once we have it.

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

Choose a reason for hiding this comment

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

Suggested change
il_transactions = new_payload_request.il_transactions
il_transactions = new_payload_request.il_transactions # [New in FOCIL]

when calling `notify_new_payload` in FOCIL.

```python
def verify_and_notify_new_payload(self: ExecutionEngine,

Choose a reason for hiding this comment

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

il_transactions is in NewPayloadRequest anyway so there is no need to change the signature of verify_and_notify_new_payload of Electra.

```python
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_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.

Choose a reason for hiding this comment

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

Would it be better to explicitly describe a condition when a block is full?

# Verify inclusion list signature
assert is_valid_local_inclusion_list_signature(state, signed_inclusion_list)

parent_hash = message.parent_hash

Choose a reason for hiding this comment

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

Where is parent_hash used?

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.

Choose a reason for hiding this comment

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

Suggested change
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 [FOCIL](./beacon-chain.md) are prerequisite for this document and used throughout.

Copy link
Owner Author

Choose a reason for hiding this comment

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

Sticking with requisite because that's what's used in all the documents, not really sure which is better

#### 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`.

Choose a reason for hiding this comment

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

Suggested change
* `{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`.

Choose a reason for hiding this comment

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

Do we need to mention the condition that execution payload has leftover gas?


### `ExecutionEngine`

*Note*: `engine_getInclusionListV1` and `engine_updateInclusionListV1` functions are added to the `ExecutionEngine` protocol for use as a validator.

Choose a reason for hiding this comment

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

engine_updateInclusionListV1 seems missing in the engine-api.md.


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.

Choose a reason for hiding this comment

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

Suggested change
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.

Comment on lines 103 to 104
- 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.

Choose a reason for hiding this comment

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

What's the difference between these two?

Copy link
Owner Author

Choose a reason for hiding this comment

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

one is consensus layer construct, one is execution layer construct, fixing the sentences to clarify

"""
message = signed_inclusion_list.message
# Verify inclusion list slot is bounded to the current slot
assert get_current_slot(store) != message.slot

Choose a reason for hiding this comment

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

Suggested change
assert get_current_slot(store) != message.slot
assert get_current_slot(store) == message.slot

store.inclusion_lists.remove(existing_inclusion_list)
else:
# If no such inclusion list exists, add the new one
store.inclusion_lists.append(message.transaction)

Choose a reason for hiding this comment

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

Suggested change
store.inclusion_lists.append(message.transaction)
store.inclusion_lists.append(message)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants