Merge BeaconBlock gossip validation (#3165)

* Merge BeaconBlock gossip validation

* figure/ground inversion

* revert cosmetic cleanups to reduce merge conflicts
This commit is contained in:
tersec 2021-12-08 17:29:22 +00:00 committed by GitHub
parent 6f077ce82c
commit 2ca28fb861
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 78 additions and 10 deletions

View File

@ -177,6 +177,10 @@ type
eth1_deposit_index*: uint64
beacon_proposers*: array[SLOTS_PER_EPOCH, Option[ValidatorIndex]]
shuffled_active_validator_indices*: seq[ValidatorIndex]
# enables more efficient merge block validation
merge_transition_complete*: bool
# balances, as used in fork choice
effective_balances_bytes*: seq[byte]

View File

@ -153,7 +153,15 @@ func init*(
getStateField(state.data, current_justified_checkpoint),
finalized_checkpoint: getStateField(state.data, finalized_checkpoint),
shuffled_active_validator_indices:
cache.get_shuffled_active_validator_indices(state.data, epoch)
cache.get_shuffled_active_validator_indices(state.data, epoch),
merge_transition_complete:
case state.data.kind:
of BeaconStateFork.Phase0: false
of BeaconStateFork.Altair: false
of BeaconStateFork.Merge:
# https://github.com/ethereum/consensus-specs/blob/v1.1.6/specs/merge/beacon-chain.md#is_merge_transition_complete
state.data.mergeData.data.latest_execution_payload_header !=
ExecutionPayloadHeader()
)
for i in 0'u64..<SLOTS_PER_EPOCH:

View File

@ -355,7 +355,7 @@ proc addBlock*(chain: var Eth1Chain, newBlock: Eth1Block) =
chain.blocksByHash[newBlock.voteData.block_hash.asBlockHash] = newBlock
eth1_chain_len.set chain.blocks.len.int64
func hash(x: Eth1Data): Hash =
func hash*(x: Eth1Data): Hash =
hash(x.block_hash)
template awaitWithRetries*[T](lazyFutExpr: Future[T],

View File

@ -12,7 +12,7 @@ import
stew/results, bearssl,
chronicles, chronos, metrics, taskpools,
../spec/[helpers, forks],
../spec/datatypes/[altair, phase0],
../spec/datatypes/[altair, merge, phase0],
../consensus_object_pools/[
block_clearance, block_quarantine, blockchain_dag, exit_pool, attestation_pool,
sync_committee_msg_pool],
@ -172,7 +172,8 @@ proc new*(T: type Eth2Processor,
proc blockValidator*(
self: var Eth2Processor,
signedBlock: phase0.SignedBeaconBlock | altair.SignedBeaconBlock): ValidationRes =
signedBlock: phase0.SignedBeaconBlock | altair.SignedBeaconBlock |
merge.SignedBeaconBlock): ValidationRes =
let
wallTime = self.getCurrentBeaconTime()
(afterGenesis, wallSlot) = wallTime.toSlot()

View File

@ -12,7 +12,7 @@ import
chronicles, chronos, metrics,
stew/results,
# Internals
../spec/datatypes/[phase0, altair],
../spec/datatypes/[phase0, altair, merge],
../spec/[
beaconstate, state_transition_block, forks, helpers, network, signatures],
../consensus_object_pools/[
@ -141,10 +141,9 @@ func check_attestation_subnet(
epochRef: EpochRef, attestation: Attestation,
subnet_id: SubnetId): Result[void, ValidationError] =
let
expectedSubnet =
compute_subnet_for_attestation(
get_committee_count_per_slot(epochRef),
attestation.data.slot, attestation.data.index.CommitteeIndex)
expectedSubnet = compute_subnet_for_attestation(
get_committee_count_per_slot(epochRef),
attestation.data.slot, attestation.data.index.CommitteeIndex)
if expectedSubnet != subnet_id:
return errReject("Attestation not on the correct subnet")
@ -171,10 +170,59 @@ template checkedReject(error: ValidationError): untyped =
raiseAssert $error[1]
err(error)
template validateBeaconBlockMerge(
signed_beacon_block: phase0.SignedBeaconBlock |
altair.SignedBeaconBlock): untyped =
discard
# https://github.com/ethereum/consensus-specs/blob/v1.1.6/specs/merge/p2p-interface.md#beacon_block
template validateBeaconBlockMerge(
signed_beacon_block: merge.SignedBeaconBlock): untyped =
# If the execution is enabled for the block -- i.e.
# is_execution_enabled(state, block.body) then validate the following:
let executionEnabled =
if signed_beacon_block.message.body.execution_payload !=
default(ExecutionPayload):
true
elif dag.getEpochRef(parent_ref, parent_ref.slot.epoch).merge_transition_complete:
# Should usually be inexpensive, but could require cache refilling
true
else:
# Somewhat more expensive fallback, with database I/O, but should be
# mostly relevant around merge transition epochs. It's possible that
# the previous block is phase 0 or Altair, if this is the transition
# block itself.
let blockData = dag.get(parent_ref)
case blockData.data.kind:
of BeaconBlockFork.Phase0:
false
of BeaconBlockFork.Altair:
false
of BeaconBlockFork.Merge:
# https://github.com/ethereum/consensus-specs/blob/v1.1.6/specs/merge/beacon-chain.md#process_execution_payload
# shows how this gets folded into the state each block; checking this
# is equivalent, without ever requiring state replay or any similarly
# expensive computation.
blockData.data.mergeData.message.body.execution_payload !=
default(ExecutionPayload)
if executionEnabled:
# [REJECT] The block's execution payload timestamp is correct with respect
# to the slot -- i.e. execution_payload.timestamp ==
# compute_timestamp_at_slot(state, block.slot).
let timestampAtSlot =
withState(dag.headState.data):
compute_timestamp_at_slot(state.data, signed_beacon_block.message.slot)
if not (signed_beacon_block.message.body.execution_payload.timestamp ==
timestampAtSlot):
return errReject("BeaconBlock: Mismatched execution payload timestamp")
# https://github.com/ethereum/consensus-specs/blob/v1.0.1/specs/phase0/p2p-interface.md#beacon_block
# https://github.com/ethereum/consensus-specs/blob/v1.1.6/specs/merge/p2p-interface.md#beacon_block
proc validateBeaconBlock*(
dag: ChainDAGRef, quarantine: ref Quarantine,
signed_beacon_block: phase0.SignedBeaconBlock | altair.SignedBeaconBlock,
signed_beacon_block: phase0.SignedBeaconBlock | altair.SignedBeaconBlock |
merge.SignedBeaconBlock,
wallTime: BeaconTime, flags: UpdateFlags): Result[void, ValidationError] =
# In general, checks are ordered from cheap to expensive. Especially, crypto
# verification could be quite a bit more expensive than the rest. This is an
@ -296,6 +344,8 @@ proc validateBeaconBlock*(
signed_beacon_block.signature):
return errReject("Invalid proposer signature")
validateBeaconBlockMerge(signed_beacon_block)
ok()
# https://github.com/ethereum/consensus-specs/blob/v1.0.1/specs/phase0/p2p-interface.md#beacon_attestation_subnet_id

View File

@ -1047,6 +1047,11 @@ proc installMessageValidators(node: BeaconNode) =
proc (signedBlock: altair.SignedBeaconBlock): ValidationResult =
toValidationResult(node.processor[].blockValidator(signedBlock)))
node.network.addValidator(
getBeaconBlocksTopic(node.dag.forkDigests.merge),
proc (signedBlock: merge.SignedBeaconBlock): ValidationResult =
toValidationResult(node.processor[].blockValidator(signedBlock)))
template installSyncCommitteeeValidators(digest: auto) =
for committeeIdx in allSyncSubcommittees():
closureScope: