eth2.0-specs/sync/optimistic.md

5.5 KiB

Optimistic Sync

Introduction

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.
  • 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 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.
  • optimistic_roots: Set[Root]: hash_tree_root(block) where block.body.execution_payload is known to be SYNCING.

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 from an execution engine. I.e., perform the following transitions:

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

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.

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.

def is_optimistic(block: BeaconBlock) -> bool:
	hash_tree_root(block) in optimistic_roots
def latest_valid_ancestor(block: BeaconBlock) -> Optional[BeaconBlock]:
	while block.parent_root != Root():
		if not is_optimistic(block):
			return block
		block = get_block(block.parent_root)

	return None
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

A optimistic validator MUST NOT produce a block (i.e., sign across the DOMAIN_BEACON_PROPOSER domain), unless one of the follow exceptions are met:

Exception 1.

If the justified block is fully verified (i.e., not 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 DOMAIN_BEACON_ATTESTER, DOMAIN_SELECTION_PROOF or DOMAIN_AGGREGATE_AND_PROOF domains), unless one of the follow exceptions are met:

Exception 1.

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