async def validate_peer_status(chain: BaseBeaconChain, peer_status: Status) -> None: state_machine = chain.get_state_machine() state = chain.get_head_state() config = state_machine.config if peer_status.head_fork_version != state.fork.current_version: raise IrrelevantNetwork( "`fork_version` mismatches: " f"peer_status.head_fork_version={peer_status.head_fork_version}, " f"state.fork.current_version={state.fork.current_version}") # Can not validate the checkpoint with `finalized_epoch` higher than ours if peer_status.finalized_epoch > state.finalized_checkpoint.epoch: return # Edge case where nothing is finalized yet if (peer_status.finalized_epoch == 0 and peer_status.finalized_root == ZERO_ROOT): return finalized_epoch_start_slot = compute_start_slot_at_epoch( peer_status.finalized_epoch, config.SLOTS_PER_EPOCH, ) finalized_root = chain.get_canonical_block_root(finalized_epoch_start_slot) if peer_status.finalized_root != finalized_root: raise IrrelevantNetwork( "`finalized_root` mismatches: " f"peer_status.finalized_root={peer_status.finalized_root.hex()}, " f"peer_status.finalized_epoch={peer_status.finalized_epoch}, " f"our `finalized_root` at the same `finalized_epoch`={finalized_root.hex()}" )
async def process_metrics(chain: BaseBeaconChain, event_bus: EndpointAPI) -> None: # Networking libp2p_peers = await event_bus.request(Libp2pPeersRequest()) metrics.libp2p_peers.set(len(libp2p_peers.result)) # Per slot info beacon_slot = chain.get_head_state_slot() metrics.beacon_slot.set(beacon_slot) # Per block info head_block = chain.get_canonical_head() metrics.beacon_head_slot.set(head_block.slot) metrics.beacon_head_root.set(root_to_int(head_block.hash_tree_root)) # Per epoch info epoch_info = chain.get_canonical_epoch_info() metrics.beacon_previous_justified_epoch.set( epoch_info.previous_justified_checkpoint.epoch) metrics.beacon_previous_justified_root.set( root_to_int(epoch_info.previous_justified_checkpoint.root), ) metrics.beacon_current_justified_epoch.set( epoch_info.current_justified_checkpoint.epoch) metrics.beacon_current_justified_root.set( root_to_int(epoch_info.current_justified_checkpoint.root), ) metrics.beacon_finalized_epoch.set(epoch_info.finalized_checkpoint.epoch) metrics.beacon_finalized_root.set( root_to_int(epoch_info.finalized_checkpoint.root))
def peer_is_ahead(chain: BaseBeaconChain, peer_status: Status) -> bool: checkpoint = chain.get_head_state().finalized_checkpoint head_block = chain.get_canonical_head() peer_has_higher_finalized_epoch = peer_status.finalized_epoch > checkpoint.epoch peer_has_equal_finalized_epoch = peer_status.finalized_epoch == checkpoint.epoch peer_has_higher_head_slot = peer_status.head_slot > head_block.slot return (peer_has_higher_finalized_epoch or (peer_has_equal_finalized_epoch and peer_has_higher_head_slot))
def validate_voting_beacon_block(chain: BaseBeaconChain, attestation: Attestation) -> None: # Check that beacon blocks attested to by the attestation are validated try: chain.get_block_by_root(attestation.data.beacon_block_root) except BlockNotFound: raise InvalidGossipMessage( f"Failed to validate attestation={attestation}," f" attested block={encode_hex(attestation.data.beacon_block_root)}" " has not been not validated yet")
def validate_start_slot(chain: BaseBeaconChain, start_slot: Slot) -> None: config = chain.get_state_machine().config state = chain.get_head_state() finalized_epoch_start_slot = compute_start_slot_at_epoch( epoch=state.finalized_checkpoint.epoch, slots_per_epoch=config.SLOTS_PER_EPOCH, ) if start_slot < finalized_epoch_start_slot: raise ValidationError( f"`start_slot`({start_slot}) lower than our" f" latest finalized slot({finalized_epoch_start_slot})")
def get_my_status(chain: BaseBeaconChain) -> Status: state = chain.get_head_state() head = chain.get_canonical_head() finalized_checkpoint = state.finalized_checkpoint return Status.create( head_fork_version=state.fork.current_version, finalized_root=finalized_checkpoint.root, finalized_epoch=finalized_checkpoint.epoch, head_root=head.message.hash_tree_root, head_slot=head.slot, )
def get_blocks( chain: BaseBeaconChain, parent_block: SerenityBeaconBlock = None, num_blocks: int = 3, ) -> Tuple[SerenityBeaconBlock, ...]: if parent_block is None: parent_block = chain.get_canonical_head() blocks = [] for _ in range(num_blocks): block = chain.create_block_from_parent(parent_block=parent_block, block_params=FromBlockParams()) blocks.append(block) parent_block = block return tuple(blocks)
def compare_chain_tip_and_finalized_epoch(chain: BaseBeaconChain, peer_status: Status) -> None: checkpoint = chain.get_head_state().finalized_checkpoint head_block = chain.get_canonical_head() peer_has_higher_finalized_epoch = peer_status.finalized_epoch > checkpoint.epoch peer_has_equal_finalized_epoch = peer_status.finalized_epoch == checkpoint.epoch peer_has_higher_head_slot = peer_status.head_slot > head_block.slot if (peer_has_higher_finalized_epoch or (peer_has_equal_finalized_epoch and peer_has_higher_head_slot)): # TODO: kickoff syncing process with this peer logger.debug( "Peer's chain is ahead of us, start syncing with the peer.") pass
def get_blocks_from_fork_chain_by_root( chain: BaseBeaconChain, start_slot: Slot, peer_head_block: BaseBeaconBlock, slot_of_requested_blocks: Sequence[Slot], ) -> Iterable[BaseBeaconBlock]: # Peer's head block is on a fork chain, # start getting the requested blocks by # traversing the history from the head. # `slot_of_requested_blocks` starts with earliest slot # and end with most recent slot, so we start traversing # from the most recent slot. cur_index = len(slot_of_requested_blocks) - 1 block = peer_head_block if block.slot == slot_of_requested_blocks[cur_index]: yield block cur_index -= 1 while block.slot > start_slot and cur_index >= 0: try: block = chain.get_block_by_root(block.parent_root) except (BlockNotFound, ValidationError): # This should not happen as we only persist block if its # ancestors are also in the database. break else: while block.slot < slot_of_requested_blocks[cur_index]: if cur_index > 0: cur_index -= 1 else: break if block.slot == slot_of_requested_blocks[cur_index]: yield block
def get_requested_beacon_blocks( chain: BaseBeaconChain, request: BeaconBlocksByRangeRequest ) -> Tuple[BaseBeaconBlock, ...]: try: requested_head = chain.get_block_by_root( request.head_block_root ) except (BlockNotFound, ValidationError) as error: logger.info("Sending empty blocks, reason: %s", error) return tuple() # Check if slot of specified head block is greater than specified start slot if requested_head.slot < request.start_slot: raise InvalidRequest( f"head block slot({requested_head.slot}) lower than `start_slot`({request.start_slot})" ) try: requested_beacon_blocks = _get_requested_beacon_blocks( chain, request, requested_head ) return requested_beacon_blocks except ValidationError as val_error: raise InvalidRequest(str(val_error))
def __init__(self, chain: BaseBeaconChain, event_bus: EndpointAPI, token: CancelToken = None) -> None: super().__init__(token) self.genesis_time = chain.get_head_state().genesis_time self.chain = chain self.event_bus = event_bus config = self.chain.get_state_machine().config self.slots_per_epoch = config.SLOTS_PER_EPOCH
def get_beacon_blocks_by_root( chain: BaseBeaconChain, request: BeaconBlocksByRootRequest, ) -> Iterable[BaseBeaconBlock]: for block_root in request.block_roots: try: block = chain.get_block_by_root(block_root) except (BlockNotFound, ValidationError): pass else: yield block
def __init__( self, chain: BaseBeaconChain, p2p_node: Node, event_bus: EndpointAPI, get_ready_attestations_fn: GetReadyAttestationsFn, get_aggregatable_attestations_fn: GetAggregatableAttestationsFn, import_attestation_fn: ImportAttestationFn, token: CancelToken = None) -> None: super().__init__(token) self.genesis_time = chain.get_head_state().genesis_time self.chain = chain self.p2p_node = p2p_node self.event_bus = event_bus self.get_ready_attestations: GetReadyAttestationsFn = get_ready_attestations_fn self.get_aggregatable_attestations: GetAggregatableAttestationsFn = get_aggregatable_attestations_fn # noqa: E501 self.import_attestation: ImportAttestationFn = import_attestation_fn # `state.eth1_data` can be updated in the middle of voting period and thus # the starting `eth1_data.block_hash` must be stored separately. self.starting_eth1_block_hash = chain.get_head_state( ).eth1_data.block_hash
def get_blocks_from_canonical_chain_by_slot( chain: BaseBeaconChain, slot_of_requested_blocks: Sequence[Slot], ) -> Iterable[BaseBeaconBlock]: # If peer's head block is on our canonical chain, # start getting the requested blocks by slots. for slot in slot_of_requested_blocks: try: block = chain.get_canonical_block_by_slot(slot) except BlockNotFound: pass else: yield block
def __init__( self, chain: BaseBeaconChain, p2p_node: Node, validator_privkeys: Dict[ValidatorIndex, int], event_bus: EndpointAPI, get_ready_attestations_fn: GetReadyAttestationsFn, get_aggregatable_attestations_fn: GetAggregatableAttestationsFn, import_attestation_fn: ImportAttestationFn, token: CancelToken = None) -> None: super().__init__(token) self.genesis_time = chain.get_head_state().genesis_time self.chain = chain self.p2p_node = p2p_node self.validator_privkeys = validator_privkeys self.event_bus = event_bus config = self.chain.get_state_machine().config self.slots_per_epoch = config.SLOTS_PER_EPOCH # TODO: `latest_proposed_epoch` and `latest_attested_epoch` should be written # into/read from validator's own db. self.latest_proposed_epoch = {} self.latest_attested_epoch = {} self.local_validator_epoch_assignment = {} for validator_index in validator_privkeys: self.latest_proposed_epoch[validator_index] = Epoch(-1) self.latest_attested_epoch[validator_index] = Epoch(-1) self.local_validator_epoch_assignment[validator_index] = ( Epoch(-1), CommitteeAssignment((), CommitteeIndex(-1), Slot(-1)), ) self.get_ready_attestations: GetReadyAttestationsFn = get_ready_attestations_fn self.get_aggregatable_attestations: GetAggregatableAttestationsFn = get_aggregatable_attestations_fn # noqa: E501 self.import_attestation: ImportAttestationFn = import_attestation_fn # `state.eth1_data` can be updated in the middle of voting period and thus # the starting `eth1_data.block_hash` must be stored separately. self.starting_eth1_block_hash = chain.get_head_state( ).eth1_data.block_hash
def _get_requested_beacon_blocks( chain: BaseBeaconChain, beacon_blocks_request: BeaconBlocksByRangeRequest, requested_head_block: BaseBeaconBlock, ) -> Tuple[BaseBeaconBlock, ...]: slot_of_requested_blocks = tuple( beacon_blocks_request.start_slot + i * beacon_blocks_request.step for i in range(beacon_blocks_request.count) ) logger.info("slot_of_requested_blocks: %s", slot_of_requested_blocks) slot_of_requested_blocks = tuple( filter(lambda slot: slot <= requested_head_block.slot, slot_of_requested_blocks) ) if len(slot_of_requested_blocks) == 0: return tuple() # We have the peer's head block in our database, # next check if the head block is on our canonical chain. try: canonical_block_at_slot = chain.get_canonical_block_by_slot( requested_head_block.slot ) block_match = canonical_block_at_slot == requested_head_block except BlockNotFound: logger.debug( ( "The requested head block is not on our canonical chain " "requested_head_block: %s" ), requested_head_block, ) block_match = False finally: if block_match: # Peer's head block is on our canonical chain return get_blocks_from_canonical_chain_by_slot( chain, slot_of_requested_blocks, ) else: # Peer's head block is not on our canonical chain # Validate `start_slot` is greater than our latest finalized slot validate_start_slot(chain, beacon_blocks_request.start_slot) return get_blocks_from_fork_chain_by_root( chain, beacon_blocks_request.start_slot, requested_head_block, slot_of_requested_blocks, )