def skip_candidate_if_on_list_or_fork_mismatch(genesis_hash: Hash32, head: BlockNumber, fork_blocks: Tuple[BlockNumber, ...], skip_list: Container[NodeID], candidate: NodeAPI) -> bool: if skip_candidate_if_on_list(skip_list, candidate): return True # For now we accept candidates which don't specify a ForkID in their ENR, but we may want to # change that if we realize we're getting too many chain-mismatch errors when connecting. candidate_forkid = extract_forkid(candidate.enr) if candidate_forkid is None: p2p_logger.debug("Accepting connection candidate (%s) with no ForkID", candidate) return False try: validate_forkid(candidate_forkid, genesis_hash, head, fork_blocks) except BaseForkIDValidationError as e: p2p_logger.debug( "Skipping forkid-incompatible connection candidate (%s): %s", candidate, e) return True p2p_logger.debug("Accepting forkid-compatible connection candidate (%s)", candidate) return False
def test_forkid_validation(local_head, remote_forkid, expected_error): fork_blocks = extract_fork_blocks(MAINNET_VM_CONFIGURATION) if expected_error: with pytest.raises(expected_error): validate_forkid(remote_forkid, MAINNET_GENESIS_HASH, local_head, fork_blocks) else: validate_forkid(remote_forkid, MAINNET_GENESIS_HASH, local_head, fork_blocks)
def test_skip_candidate_if_on_list_or_fork_mismatch(): mainnet_fork_blocks = forkid.extract_fork_blocks(MAINNET_VM_CONFIGURATION) should_skip_fn = functools.partial( skip_candidate_if_on_list_or_fork_mismatch, MAINNET_GENESIS_HEADER.hash, MAINNET_GENESIS_HEADER.block_number, mainnet_fork_blocks, ) no_forkid_nodes = NodeFactory.create_batch(10) compatible_node = _make_node_with_enr_and_forkid( MAINNET_GENESIS_HEADER.hash, MAINNET_GENESIS_HEADER.block_number, MAINNET_VM_CONFIGURATION) # Ensure compatible_node's forkid is compatible with our current chain state. forkid.validate_forkid( forkid.extract_forkid(compatible_node.enr), MAINNET_GENESIS_HEADER.hash, MAINNET_GENESIS_HEADER.block_number, mainnet_fork_blocks, ) # It returns True for candidates on the skip list, even if they are fork-id compatible. skip_list = [no_forkid_nodes[1].id, compatible_node.id] assert functools.partial(should_skip_fn, skip_list)(compatible_node) is True assert functools.partial(should_skip_fn, skip_list)( no_forkid_nodes[1]) is True # It returns False for candidates with no fork-id that are not on the skip list. with pytest.raises(ENRMissingForkID): forkid.extract_forkid(no_forkid_nodes[0].enr) assert functools.partial(should_skip_fn, skip_list)( no_forkid_nodes[0]) is False # It returns False for candidates with compatible fork-ids that are not on the skip list assert functools.partial(should_skip_fn, [])(compatible_node) is False # It returns True for candidates with incompatible fork-ids incompatible_node = _make_node_with_enr_and_forkid( ROPSTEN_GENESIS_HEADER.hash, ROPSTEN_GENESIS_HEADER.block_number, ROPSTEN_VM_CONFIGURATION) with pytest.raises(BaseForkIDValidationError): forkid.validate_forkid( forkid.extract_forkid(incompatible_node.enr), MAINNET_GENESIS_HEADER.hash, MAINNET_GENESIS_HEADER.block_number, mainnet_fork_blocks, ) assert functools.partial(should_skip_fn, [])(incompatible_node) is True
def skip_candidate_if_on_list_or_fork_mismatch(genesis_hash: Hash32, head: BlockNumber, fork_blocks: Tuple[BlockNumber, ...], skip_list: Container[NodeID], candidate: NodeAPI) -> bool: if skip_candidate_if_on_list(skip_list, candidate): return True # For now we accept candidates which don't specify a ForkID in their ENR, but we may want to # change that if we realize we're getting too many chain-mismatch errors when connecting. try: candidate_forkid = extract_forkid(candidate.enr) except ENRMissingForkID: p2p_logger.debug("Accepting connection candidate (%s) with no ForkID", candidate) return False except MalformedMessage as e: # Logging as a warning just in case there's a bug in our code that fails to deserialize # valid ForkIDs. If this becomes too noisy, we should consider reducing the severity. p2p_logger.warning( "Unable to extract ForkID from ENR of %s (%s), accepting as connection candidate " "anyway", candidate, e, ) return False try: validate_forkid(candidate_forkid, genesis_hash, head, fork_blocks) except BaseForkIDValidationError as e: p2p_logger.debug( "Skipping forkid-incompatible connection candidate (%s): %s", candidate, e) return True p2p_logger.debug("Accepting forkid-compatible connection candidate (%s)", candidate) return False