From 38fffd3e2f5d7f32d85e0b317fc5a0202534b2e9 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Mon, 13 Dec 2021 17:09:09 +1100 Subject: [PATCH] Tidy, finish duties --- sync/optimistic.md | 105 ++++++++++++++++++++++++++++++++------------- 1 file changed, 74 insertions(+), 31 deletions(-) diff --git a/sync/optimistic.md b/sync/optimistic.md index b3220b96f..5837a1349 100644 --- a/sync/optimistic.md +++ b/sync/optimistic.md @@ -2,52 +2,71 @@ ## Introduction -In order to provide a syncing execution engine with a (partially-verified) view -of the head of the chain, it may be desirable for a consensus engine to import -beacon blocks without verifying the execution payloads. This partial sync is -called an *optimistic sync*. +In order to provide a syncing execution engine with a partial view of the head +of the chain, it may be desirable for a consensus engine to import beacon +blocks without verifying the execution payloads. This partial sync is called an +*optimistic sync*. ## Mechanisms To perform an optimistic sync: - The `execute_payload` function MUST return `True` if the execution - engine returns SYNCING or VALID. An INVALID response MUST return `False`. + engine returns `SYNCING` or `VALID`. An `INVALID` response MUST return `False`. - The `validate_merge_block` function MUST NOT raise an assertion if both the `pow_block` and `pow_parent` are unknown to the execution engine. In addition to these changes to validation, the consensus engine MUST be able -to ascertain, after import, which blocks returned SYNCING and which returned -VALID. This document will assume consensus engines store the following sets: +to ascertain, after import, which blocks returned `SYNCING` and which returned +`VALID`. This document will assume that consensus engines store the following +sets: - `valid_roots: Set[Root]`: `hash_tree_root(block)` where - `block.body.execution_payload` is known to be VALID. + `block.body.execution_payload` is known to be `VALID`. - `optimistic_roots: Set[Root]`: `hash_tree_root(block)` where - `block.body.execution_payload` is known to be SYNCING. + `block.body.execution_payload` is known to be `SYNCING`. -Notably, `optimistic_roots` only includes blocks which have execution enabled -whilst `valid_roots` contains blocks *with or without* execution enabled (i.e., -all blocks). +Notably, `optimistic_roots` only includes blocks which have execution enabled. +On the other hand, `valid_roots` contains blocks *with or without* execution +enabled (i.e., all blocks). A consensus engine MUST be able to retrospectively (i.e., after import) modify -the status of SYNCING blocks to be either VALID or INVALID based upon responses +the status of `SYNCING` blocks to be either `VALID` or `INVALID` based upon responses from an execution engine. I.e., perform the following transitions: -- SYNCING -> VALID -- SYNCING -> INVALID +- `SYNCING` -> `VALID` +- `SYNCING` -> `INVALID` -When a block transitions from SYNCING -> VALID, all *ancestors* of the block MUST -also transition from SYNCING -> VALID. +When a block transitions from `SYNCING` -> `VALID`, all *ancestors* of the block MUST +also transition from `SYNCING` -> `VALID`. -When a block transitions from SYNCING -> INVALID, all *descendants* of the -block MUST also transition from SYNCING -> INVALID. +When a block transitions from `SYNCING` -> `INVALID`, all *descendants* of the +block MUST also transition from `SYNCING` -> `INVALID`. + +### Execution Engine Errors + +A consensus engine MUST NOT interpret an error or failure to respond to a +message as a `SYNCING`, `VALID` or `INVALID` response. A consensus engine MAY +queue such a message for later processing. + +## Merge Transition + +To protect against attacks during the transition from empty `ExecutionPayload` +values to those which include the terminal PoW block, a consensus engine MUST +NOT perform an optimistic sync unless the `finalized_checkpoint.root` of the head +block references a block for which +`is_execution_enabled(head_state, head_block.body) == True`. + +TODO: this restriction is very onerous, however it is the best known remedy for +the attack described in https://hackmd.io/S5ZEVhsNTqqfJirTAkBPlg I hope we can +do better. ## Fork Choice -Consensus engines MUST support removing blocks that transition from SYNCING to -INVALID. Specifically, an INVALID block MUST NOT be included in the canonical -chain and the weights from INVALID blocks MUST NOT be applied to any VALID or -SYNCING ancestors. +Consensus engines MUST support removing from fork choice blocks that transition +from `SYNCING` to `INVALID`. Specifically, a block deemed `INVALID` at any +point MUST NOT be included in the canonical chain and the weights from those +`INVALID` blocks MUST NOT be applied to any `VALID` or `SYNCING` ancestors. ## Validator assignments @@ -56,6 +75,8 @@ produce blocks, since an execution engine cannot produce a payload upon an unknown parent. It cannot faithfully attest to the head block of the chain, since it has not fully verified that block. +### Helpers + Let `head_block: BeaconBlock` be the result of calling of the fork choice algorithm at the time of block production. Let `justified_block: BeaconBlock` be the latest current justified ancestor ancestor of the `head_block`. @@ -75,8 +96,18 @@ def latest_valid_ancestor(block: BeaconBlock) -> Optional[BeaconBlock]: return None ``` -Let a node which returns `is_optimistic(head) == True` be an *optimistic -node*. Let a validator on an optimistic node be an *optimistic validator*. +```python +def recent_valid_ancestor(block: BeaconBlock) -> Optional[BeaconBlock]: + ancestor = latest_valid_ancestor(block) + + if ancestor is None or ancestor.slot + SLOTS_PER_EPOCH >= block.slot: + return None + + return ancestor +``` + +Let only a node which returns `is_optimistic(head) == True` be an *optimistic +node*. Let only a validator on an optimistic node be an *optimistic validator*. ### Block production @@ -86,9 +117,11 @@ A optimistic validator MUST NOT produce a block (i.e., sign across the #### Exception 1. If the justified block is fully verified (i.e., `not -is_optimistic(justified_block)`, the validator MUST produce a block upon +is_optimistic(justified_block)`, the validator MAY produce a block upon `latest_valid_ancestor(head)`. +If the latest valid ancestor is `None`, the validator MUST NOT produce a block. + ### Attesting An optimistic validator MUST NOT participate in attestation (i.e., sign across the @@ -96,10 +129,20 @@ An optimistic validator MUST NOT participate in attestation (i.e., sign across t `DOMAIN_AGGREGATE_AND_PROOF` domains), unless one of the follow exceptions are met: -#### Exception - #### Exception 1. -If a validator *does not* have an optimistic head (i.e., `not -is_optimistic(head_block)`), the node is *fully synced*. -The validator may produce an attestation. +If the justified block is fully verified (i.e., `not +is_optimistic(justified_block)`, the validator MAY sign across the following +domains: + +- `DOMAIN_BEACON_ATTESTER`: where `attestation.data.beacon_block_root == hash_tree_root(recent_valid_ancestor(head))`. +- `DOMAIN_AGGREGATE_AND_PROOF` and `DOMAIN_SELECTION_PROOF`: where `aggregate.message.aggregate.data.beacon_block_root == hash_tree_root(recent_valid_ancestor(head))` + +If the recent valid ancestor is `None`, the validator MUST NOT participate in +attestation. + +### Participating in Sync Committees + +An optimistic validator MUST NOT participate in sync committees (i.e., sign across the +`DOMAIN_SYNC_COMMITTEE`, `DOMAIN_SYNC_COMMITTEE_SELECTION_PROOF` or +`DOMAIN_CONTRIBUTION_AND_PROOF` domains).