2021-12-05 23:07:54 +00:00
|
|
|
# Optimistic Sync
|
|
|
|
|
|
|
|
## Introduction
|
|
|
|
|
2021-12-13 06:09:09 +00:00
|
|
|
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*.
|
2021-12-05 23:07:54 +00:00
|
|
|
|
2021-12-19 03:44:21 +00:00
|
|
|
## Constants
|
|
|
|
|
|
|
|
|Name|Value|Unit
|
|
|
|
|---|---|---|
|
2021-12-19 23:15:51 +00:00
|
|
|
|`SAFE_SLOTS_TO_IMPORT_OPTIMISTICALLY`| `96` | slots
|
|
|
|
|
|
|
|
*Note: the `SAFE_SLOTS_TO_IMPORT_OPTIMISTICALLY` must be user-configurable. See
|
2021-12-19 23:32:45 +00:00
|
|
|
[Fork Choice Poisoning](#fork-choice-poisoning).*
|
2021-12-19 03:44:21 +00:00
|
|
|
|
|
|
|
## 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`.
|
|
|
|
|
2021-12-19 23:41:50 +00:00
|
|
|
Let `optimistic_roots: Set[Root]` be the set of `hash_tree_root(block)` for all
|
|
|
|
optimistically imported blocks which have yet to receive an `INVALID` or
|
|
|
|
`VALID` designation from an execution engine.
|
|
|
|
|
2021-12-19 03:44:21 +00:00
|
|
|
```python
|
|
|
|
def is_optimistic(block: BeaconBlock) -> bool:
|
|
|
|
hash_tree_root(block) in optimistic_roots
|
|
|
|
```
|
|
|
|
|
|
|
|
```python
|
|
|
|
def latest_valid_ancestor(block: BeaconBlock) -> BeaconBlock:
|
|
|
|
while True:
|
|
|
|
if not is_optimistic(block) or block.parent_root == Root():
|
|
|
|
return block
|
|
|
|
block = get_block(block.parent_root)
|
|
|
|
```
|
|
|
|
|
|
|
|
```python
|
|
|
|
def is_execution_block(block: BeaconBlock) -> BeaconBlock:
|
|
|
|
block.execution_payload != ExecutionPayload()
|
|
|
|
```
|
|
|
|
|
2021-12-19 03:56:46 +00:00
|
|
|
```python
|
|
|
|
def should_optimistically_import_block(current_slot: Slot, block: BeaconBlock) -> bool:
|
|
|
|
block.slot + SAFE_SLOTS_TO_IMPORT_OPTIMISTICALLY <= current_slot
|
|
|
|
```
|
|
|
|
|
2021-12-19 03:44:21 +00:00
|
|
|
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*.
|
|
|
|
|
|
|
|
When this specification only defines behaviour for an optimistic
|
|
|
|
node/validator, but *not* for the non-optimistic case, assume default
|
|
|
|
behaviours without regard for optimistic sync.
|
|
|
|
|
2021-12-05 23:07:54 +00:00
|
|
|
## Mechanisms
|
|
|
|
|
2021-12-19 03:44:21 +00:00
|
|
|
## When to optimistically import blocks
|
|
|
|
|
2021-12-19 03:56:46 +00:00
|
|
|
A block MUST NOT be optimistically imported, unless either of the following
|
|
|
|
conditions are met:
|
|
|
|
|
|
|
|
1. The justified checkpoint has execution enabled. I.e.,
|
2021-12-20 00:12:11 +00:00
|
|
|
`is_execution_block(get_block(get_state(head_block).current_justified_checkpoint.root))`
|
2021-12-19 03:56:46 +00:00
|
|
|
1. The current slot (as per the system clock) is at least `SAFE_SLOTS_TO_IMPORT_OPTIMISTICALLY` ahead of
|
|
|
|
the slot of the block being imported. I.e., `should_optimistically_import_block(current_slot) == True`.
|
2021-12-19 03:44:21 +00:00
|
|
|
|
2021-12-19 23:32:45 +00:00
|
|
|
*See [Fork Choice Poisoning](#fork-choice-poisoning) for the motivations behind
|
|
|
|
these conditions.*
|
|
|
|
|
2021-12-19 03:44:21 +00:00
|
|
|
## How to optimistically import blocks
|
|
|
|
|
|
|
|
To optimistically import a block:
|
2021-12-05 23:07:54 +00:00
|
|
|
|
|
|
|
- The `execute_payload` function MUST return `True` if the execution
|
2021-12-13 06:09:09 +00:00
|
|
|
engine returns `SYNCING` or `VALID`. An `INVALID` response MUST return `False`.
|
2021-12-05 23:07:54 +00:00
|
|
|
|
2021-12-19 23:41:50 +00:00
|
|
|
In addition to this change to validation, the consensus engine MUST be able to
|
2021-12-20 00:12:11 +00:00
|
|
|
ascertain, after import, which blocks returned `SYNCING` and which returned
|
|
|
|
`VALID`.
|
2021-12-05 23:07:54 +00:00
|
|
|
|
2021-12-20 00:12:11 +00:00
|
|
|
Optimistically imported blocks MUST pass all verifications included in
|
|
|
|
`process_block` (withstanding the modifications to `execute_payload`).
|
2021-12-14 05:21:30 +00:00
|
|
|
|
2021-12-05 23:07:54 +00:00
|
|
|
A consensus engine MUST be able to retrospectively (i.e., after import) modify
|
2021-12-13 06:09:09 +00:00
|
|
|
the status of `SYNCING` blocks to be either `VALID` or `INVALID` based upon responses
|
2021-12-05 23:07:54 +00:00
|
|
|
from an execution engine. I.e., perform the following transitions:
|
|
|
|
|
2021-12-13 06:09:09 +00:00
|
|
|
- `SYNCING` -> `VALID`
|
|
|
|
- `SYNCING` -> `INVALID`
|
2021-12-05 23:07:54 +00:00
|
|
|
|
2021-12-20 00:12:11 +00:00
|
|
|
When a block transitions from `SYNCING` -> `VALID`, all *ancestors* of the
|
|
|
|
block MUST also transition from `SYNCING` -> `VALID`. Such a block is no longer
|
|
|
|
considered "optimistically imported".
|
2021-12-05 23:07:54 +00:00
|
|
|
|
2021-12-13 06:09:09 +00:00
|
|
|
When a block transitions from `SYNCING` -> `INVALID`, all *descendants* of the
|
|
|
|
block MUST also transition from `SYNCING` -> `INVALID`.
|
|
|
|
|
2021-12-14 05:22:52 +00:00
|
|
|
When a node transitions from the `SYNCING` state it is removed from the set of
|
2021-12-14 04:51:15 +00:00
|
|
|
`optimistic_roots`.
|
2021-12-13 06:09:09 +00:00
|
|
|
|
2021-12-14 04:51:15 +00:00
|
|
|
### Execution Engine Errors
|
2021-12-19 03:34:43 +00:00
|
|
|
|
2021-12-20 00:12:11 +00:00
|
|
|
When an execution engine returns an error or fails to respond to a payload
|
|
|
|
validity request some block, a consensus engine:
|
|
|
|
|
|
|
|
- MUST NOT optimistically import the block.
|
|
|
|
- MAY queue the block for later processing.
|
2021-12-13 06:09:09 +00:00
|
|
|
|
2021-12-14 04:44:15 +00:00
|
|
|
### Assumptions about Execution Engine Behaviour
|
|
|
|
|
|
|
|
This specification assumes execution engines will only return `SYNCING` when
|
|
|
|
there is insufficient information available to make a `VALID` or `INVALID`
|
|
|
|
determination on the given `ExecutionPayload` (e.g., the parent payload is
|
|
|
|
unknown). Specifically, `SYNCING` responses should be fork-specific; the search
|
|
|
|
for a block on one chain MUST NOT trigger a `SYNCING` response for another
|
|
|
|
chain.
|
|
|
|
|
2021-12-14 05:42:35 +00:00
|
|
|
### Re-Orgs
|
|
|
|
|
|
|
|
The consensus engine MUST support any chain reorganisation which does *not*
|
|
|
|
affect the justified checkpoint. The consensus engine MAY support re-orgs
|
|
|
|
beyond the justified checkpoint.
|
|
|
|
|
|
|
|
If the justified checkpoint transitions from `SYNCING` -> `INVALID`, a
|
|
|
|
consensus engine MAY choose to alert the user and force the application to
|
|
|
|
exit.
|
|
|
|
|
2021-12-19 23:32:45 +00:00
|
|
|
## Fork Choice
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
### Fork Choice Poisoning
|
2021-12-19 23:15:51 +00:00
|
|
|
|
|
|
|
During the merge transition it is possible for an attacker to craft a
|
|
|
|
`BeaconBlock` with an execution payload that references an
|
2021-12-20 00:12:11 +00:00
|
|
|
eternally-unavailable `body.execution_payload.parent_hash` value. In rare
|
2021-12-19 23:15:51 +00:00
|
|
|
circumstances, it is possible that an attacker can build atop such a block to
|
|
|
|
trigger justification. If an optimistic node imports this malicious chain, that
|
|
|
|
node will have a "poisoned" fork choice store, such that the node is unable to
|
|
|
|
produce a child of the head (due to the invalid chain of payloads) and the node
|
|
|
|
is unable to fork around the head (due to the justification of the malicious
|
|
|
|
chain).
|
|
|
|
|
|
|
|
The fork choice poisoning attack is temporary for an individual node, assuming
|
2021-12-20 00:12:11 +00:00
|
|
|
there exists an honest chain which justifies a higher epoch than the malicious
|
|
|
|
chain. Such an honest chain will take precedence and revive any poisoned store.
|
2021-12-19 23:15:51 +00:00
|
|
|
|
|
|
|
The `SAFE_SLOTS_TO_IMPORT_OPTIMISTICALLY` parameter assumes that the network
|
|
|
|
will justify a honest chain within some number of slots. With this assumption,
|
2021-12-20 00:12:11 +00:00
|
|
|
it is acceptable to optimistically import transition blocks during the sync
|
|
|
|
process. Since there is an assumption that an honest chain with a higher
|
2021-12-19 23:15:51 +00:00
|
|
|
justified checkpoint exists, any fork choice poisoning will be short-lived and
|
|
|
|
resolved before that node is required to produce a block.
|
|
|
|
|
|
|
|
However, the assumption that the honest, canonical chain will always justify
|
|
|
|
within `SAFE_SLOTS_TO_IMPORT_OPTIMISTICALLY` slots is dubious. Therefore,
|
|
|
|
clients MUST provide the following command line flag to assist with manual
|
|
|
|
disaster recovery:
|
|
|
|
|
2021-12-20 00:12:11 +00:00
|
|
|
- `--safe-slots-to-import-optimistically`: modifies the
|
2021-12-19 23:15:51 +00:00
|
|
|
`SAFE_SLOTS_TO_IMPORT_OPTIMISTICALLY`.
|
|
|
|
|
2021-12-14 05:10:19 +00:00
|
|
|
## Checkpoint Sync (Weak Subjectivity Sync)
|
|
|
|
|
|
|
|
A consensus engine MAY assume that the `ExecutionPayload` of a block used for
|
2021-12-20 00:12:11 +00:00
|
|
|
checkpoint sync is `VALID` without providing that payload to an execution
|
|
|
|
engine.
|
2021-12-14 05:10:19 +00:00
|
|
|
|
2021-12-13 08:02:45 +00:00
|
|
|
## Validator assignments
|
|
|
|
|
|
|
|
An entirely optimistically synced node is *not* a full node. It is unable to
|
|
|
|
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.
|
|
|
|
|
2021-12-13 08:15:53 +00:00
|
|
|
### Block Production
|
2021-12-05 23:07:54 +00:00
|
|
|
|
|
|
|
A optimistic validator MUST NOT produce a block (i.e., sign across the
|
2021-12-13 08:15:53 +00:00
|
|
|
`DOMAIN_BEACON_PROPOSER` domain), unless one of the following exceptions are
|
|
|
|
met:
|
2021-12-05 23:07:54 +00:00
|
|
|
|
2021-12-13 08:15:53 +00:00
|
|
|
#### Block Production Exception 1.
|
2021-12-05 23:07:54 +00:00
|
|
|
|
|
|
|
If the justified block is fully verified (i.e., `not
|
2021-12-13 06:09:09 +00:00
|
|
|
is_optimistic(justified_block)`, the validator MAY produce a block upon
|
2021-12-05 23:07:54 +00:00
|
|
|
`latest_valid_ancestor(head)`.
|
|
|
|
|
|
|
|
### Attesting
|
|
|
|
|
|
|
|
An optimistic validator MUST NOT participate in attestation (i.e., sign across the
|
|
|
|
`DOMAIN_BEACON_ATTESTER`, `DOMAIN_SELECTION_PROOF` or
|
2021-12-13 08:03:35 +00:00
|
|
|
`DOMAIN_AGGREGATE_AND_PROOF` domains).
|
2021-12-13 06:09:09 +00:00
|
|
|
|
|
|
|
### 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).
|
2021-12-13 08:02:45 +00:00
|
|
|
|
|
|
|
## P2P Networking
|
|
|
|
|
2021-12-13 08:15:53 +00:00
|
|
|
### The Gossip Domain (gossipsub)
|
2021-12-13 08:02:45 +00:00
|
|
|
|
|
|
|
#### `beacon_block`
|
|
|
|
|
2021-12-13 08:15:53 +00:00
|
|
|
An optimistic validator MAY subscribe to the `beacon_block` topic. Propagation
|
2021-12-13 08:02:45 +00:00
|
|
|
validation conditions are modified as such:
|
|
|
|
|
|
|
|
Do not apply the existing condition:
|
|
|
|
|
|
|
|
- [REJECT] The block's parent (defined by block.parent_root) passes validation.
|
|
|
|
|
|
|
|
Instead, apply the new condition:
|
|
|
|
|
|
|
|
- [REJECT] The block's parent (defined by block.parent_root) passes validation,
|
|
|
|
*and* `block.parent root not in optimistic_roots`.
|
|
|
|
|
2021-12-14 04:51:26 +00:00
|
|
|
#### Other Topics
|
2021-12-13 08:02:45 +00:00
|
|
|
|
2021-12-14 04:51:26 +00:00
|
|
|
An optimistic node MUST NOT subscribe to the following topics:
|
2021-12-13 08:02:45 +00:00
|
|
|
|
2021-12-14 04:51:26 +00:00
|
|
|
- `beacon_aggregate_and_proof`
|
|
|
|
- `voluntary_exit`
|
|
|
|
- `proposer_slashing`
|
|
|
|
- `attester_slashing`
|
|
|
|
- `beacon_attestation_{subnet_id}`
|
|
|
|
- `sync_committee_contribution_and_proof`
|
|
|
|
- `sync_committee_{subnet_id}`
|
2021-12-13 08:02:45 +00:00
|
|
|
|
2021-12-14 04:51:26 +00:00
|
|
|
Once the node ceases to be optimistic, it MAY re-subscribe to the aformentioned
|
|
|
|
topics.
|
2021-12-13 08:25:33 +00:00
|
|
|
|
|
|
|
### The Req/Resp Domain
|
|
|
|
|
|
|
|
#### BeaconBlocksByRange (v1, v2)
|
|
|
|
|
|
|
|
Consensus engines MUST NOT include any block in a response where
|
2021-12-14 22:02:39 +00:00
|
|
|
`is_optimistic(block) == True`.
|
2021-12-13 08:25:33 +00:00
|
|
|
|
|
|
|
#### BeaconBlocksByRoot (v1, v2)
|
|
|
|
|
|
|
|
Consensus engines MUST NOT include any block in a response where
|
2021-12-14 03:53:04 +00:00
|
|
|
`is_optimistic(block) == True`.
|
2021-12-13 08:25:33 +00:00
|
|
|
|
|
|
|
#### Status
|
|
|
|
|
|
|
|
An optimistic node MUST use the `latest_valid_ancestor(head)` block to form
|
|
|
|
responses, rather than the head block. Specifically, an optimistic node must
|
|
|
|
form a `Status` message as so:
|
|
|
|
|
|
|
|
The fields are, as seen by the client at the time of sending the message:
|
|
|
|
|
|
|
|
- `fork_digest`: As previously defined.
|
|
|
|
- `finalized_root`: `state.finalized_checkpoint.root` for the state corresponding to the latest valid ancestor block
|
|
|
|
(Note this defaults to `Root(b'\x00' * 32)` for the genesis finalized checkpoint).
|
|
|
|
- `finalized_epoch`: `state.finalized_checkpoint.epoch` for the state corresponding to the latest valid ancestor block.
|
|
|
|
- `head_root`: The `hash_tree_root` root of the current latest valid ancestor block (`BeaconBlock`).
|
|
|
|
- `head_slot`: The slot of the block corresponding to `latest_valid_ancestor(head)`.
|
2021-12-14 05:11:40 +00:00
|
|
|
|
|
|
|
## Ethereum Beacon APIs
|
|
|
|
|
|
|
|
Consensus engines which provide an implementation of the [Ethereum Beacon
|
|
|
|
APIs](https://github.com/ethereum/beacon-APIs) must take care to avoid
|
|
|
|
presenting optimistic blocks as fully-verified blocks.
|
|
|
|
|
|
|
|
When information about an optimistic block is requested, the consensus engine:
|
|
|
|
|
|
|
|
- MUST NOT return a "success"-type response (e.g., 2xx).
|
|
|
|
- MAY return an "empty"-type response (e.g., 404).
|
|
|
|
- MAY return a "beacon node is currently syncing"-type response (e.g., 503).
|
|
|
|
|
|
|
|
When `is_optimistic(head) == True`, the consensus engine:
|
|
|
|
|
|
|
|
- MAY substitute the head block with `latest_valid_ancestor(block)`.
|
|
|
|
- MAY return a "beacon node is currently syncing"-type response (e.g., 503).
|