Merge BeaconBlock gossip validation (#3165)
* Merge BeaconBlock gossip validation * figure/ground inversion * revert cosmetic cleanups to reduce merge conflicts
This commit is contained in:
parent
6f077ce82c
commit
2ca28fb861
|
@ -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]
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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],
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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:
|
||||||
|
|
Loading…
Reference in New Issue