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 eth1_deposit_index*: uint64
beacon_proposers*: array[SLOTS_PER_EPOCH, Option[ValidatorIndex]] beacon_proposers*: array[SLOTS_PER_EPOCH, Option[ValidatorIndex]]
shuffled_active_validator_indices*: seq[ValidatorIndex] shuffled_active_validator_indices*: seq[ValidatorIndex]
# enables more efficient merge block validation
merge_transition_complete*: bool
# balances, as used in fork choice # balances, as used in fork choice
effective_balances_bytes*: seq[byte] effective_balances_bytes*: seq[byte]

View File

@ -153,7 +153,15 @@ func init*(
getStateField(state.data, current_justified_checkpoint), getStateField(state.data, current_justified_checkpoint),
finalized_checkpoint: getStateField(state.data, finalized_checkpoint), finalized_checkpoint: getStateField(state.data, finalized_checkpoint),
shuffled_active_validator_indices: 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: 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 chain.blocksByHash[newBlock.voteData.block_hash.asBlockHash] = newBlock
eth1_chain_len.set chain.blocks.len.int64 eth1_chain_len.set chain.blocks.len.int64
func hash(x: Eth1Data): Hash = func hash*(x: Eth1Data): Hash =
hash(x.block_hash) hash(x.block_hash)
template awaitWithRetries*[T](lazyFutExpr: Future[T], template awaitWithRetries*[T](lazyFutExpr: Future[T],

View File

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

View File

@ -12,7 +12,7 @@ import
chronicles, chronos, metrics, chronicles, chronos, metrics,
stew/results, stew/results,
# Internals # Internals
../spec/datatypes/[phase0, altair], ../spec/datatypes/[phase0, altair, merge],
../spec/[ ../spec/[
beaconstate, state_transition_block, forks, helpers, network, signatures], beaconstate, state_transition_block, forks, helpers, network, signatures],
../consensus_object_pools/[ ../consensus_object_pools/[
@ -141,10 +141,9 @@ func check_attestation_subnet(
epochRef: EpochRef, attestation: Attestation, epochRef: EpochRef, attestation: Attestation,
subnet_id: SubnetId): Result[void, ValidationError] = subnet_id: SubnetId): Result[void, ValidationError] =
let let
expectedSubnet = expectedSubnet = compute_subnet_for_attestation(
compute_subnet_for_attestation( get_committee_count_per_slot(epochRef),
get_committee_count_per_slot(epochRef), attestation.data.slot, attestation.data.index.CommitteeIndex)
attestation.data.slot, attestation.data.index.CommitteeIndex)
if expectedSubnet != subnet_id: if expectedSubnet != subnet_id:
return errReject("Attestation not on the correct subnet") return errReject("Attestation not on the correct subnet")
@ -171,10 +170,59 @@ template checkedReject(error: ValidationError): untyped =
raiseAssert $error[1] raiseAssert $error[1]
err(error) 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.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*( proc validateBeaconBlock*(
dag: ChainDAGRef, quarantine: ref Quarantine, 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] = wallTime: BeaconTime, flags: UpdateFlags): Result[void, ValidationError] =
# In general, checks are ordered from cheap to expensive. Especially, crypto # 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 # verification could be quite a bit more expensive than the rest. This is an
@ -296,6 +344,8 @@ proc validateBeaconBlock*(
signed_beacon_block.signature): signed_beacon_block.signature):
return errReject("Invalid proposer signature") return errReject("Invalid proposer signature")
validateBeaconBlockMerge(signed_beacon_block)
ok() ok()
# https://github.com/ethereum/consensus-specs/blob/v1.0.1/specs/phase0/p2p-interface.md#beacon_attestation_subnet_id # 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 = proc (signedBlock: altair.SignedBeaconBlock): ValidationResult =
toValidationResult(node.processor[].blockValidator(signedBlock))) 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) = template installSyncCommitteeeValidators(digest: auto) =
for committeeIdx in allSyncSubcommittees(): for committeeIdx in allSyncSubcommittees():
closureScope: closureScope: