def enodeToMultiAddress(_node): u = urllib.parse.urlparse(_node) pubkey = bytearray.fromhex(u.username) xpub = keys.PublicKey(pubkey) nn = Node.from_pubkey_and_addr(xpub, Address(u.hostname, u.port, u.port)) nodeid = base58.b58encode(nn.id) return multiaddr.Multiaddr("/ip4/" + u.hostname + "/tcp/" + str(u.port) + "/p2p/" + nodeid.decode("utf-8"))
async def test_request_enr(nursery, manually_driven_discovery_pair): alice, bob = manually_driven_discovery_pair # Pretend that bob and alice have already bonded, otherwise bob will ignore alice's ENR # request. bob._last_pong_at[alice.this_node.id] = int(time.monotonic()) # Add a copy of Bob's node with a stub ENR to alice's RT as later we're going to check that it # gets updated with the received ENR. bobs_node_with_stub_enr = Node.from_pubkey_and_addr( bob.this_node.pubkey, bob.this_node.address) alice._last_pong_at[bob.this_node.id] = int(time.monotonic()) alice.enr_db.delete_enr(bobs_node_with_stub_enr.id) alice.enr_db.set_enr(bobs_node_with_stub_enr.enr) assert alice.enr_db.get_enr(bobs_node_with_stub_enr.id).sequence_number == 0 received_enr = None got_enr = trio.Event() async def fetch_enr(event): nonlocal received_enr received_enr = await alice.request_enr(bobs_node_with_stub_enr) event.set() # Start a task in the background that requests an ENR to bob and then waits for it. nursery.start_soon(fetch_enr, got_enr) # Bob will now consume one datagram containing the ENR_REQUEST from alice, and as part of that # will send an ENR_RESPONSE, which will then be consumed by alice, and as part of that it will # be fed into the request_enr() task we're running the background. with trio.fail_after(0.5): await bob.consume_datagram() await alice.consume_datagram() with trio.fail_after(1): await got_enr.wait() validate_node_enr(bob.this_node, received_enr, sequence_number=1) assert alice.enr_db.get_enr(bob.this_node.id) == received_enr # Now, if Bob later sends us a new ENR with no endpoint information, we'll evict him from both # our DB and RT. sequence_number = bob.this_node.enr.sequence_number + 1 new_unsigned_enr = UnsignedENR( sequence_number, kv_pairs={ IDENTITY_SCHEME_ENR_KEY: V4IdentityScheme.id, V4IdentityScheme.public_key_enr_key: bob.pubkey.to_compressed_bytes(), } ) bob.this_node = Node(new_unsigned_enr.to_signed_enr(bob.privkey.to_bytes())) received_enr = None got_new_enr = trio.Event() nursery.start_soon(fetch_enr, got_new_enr) with trio.fail_after(0.5): await bob.consume_datagram() await alice.consume_datagram() with trio.fail_after(1): await got_new_enr.wait() assert Node(received_enr).address is None assert not alice.routing._contains(bob.this_node.id, include_replacement_cache=True) with pytest.raises(KeyError): alice.enr_db.get_enr(bob.this_node.id)
NETWORKING_EVENTBUS_ENDPOINT = 'networking' UPNP_EVENTBUS_ENDPOINT = 'upnp' TO_NETWORKING_BROADCAST_CONFIG = BroadcastConfig( filter_endpoint=NETWORKING_EVENTBUS_ENDPOINT) # Network IDs: https://ethereum.stackexchange.com/questions/17051/how-to-select-a-network-id-or-is-there-a-list-of-network-ids/17101#17101 # noqa: E501 MAINNET_NETWORK_ID = 1 ROPSTEN_NETWORK_ID = 3 GOERLI_NETWORK_ID = 5 # Default preferred enodes DEFAULT_PREFERRED_NODES: Dict[int, Tuple[Node, ...]] = { MAINNET_NETWORK_ID: ( Node.from_pubkey_and_addr( keys.PublicKey( decode_hex( "1118980bf48b0a3640bdba04e0fe78b1add18e1cd99bf22d53daac1fd9972ad650df52176e7c7d89d1114cfef2bc23a2959aa54998a46afcf7d91809f0855082" )), # noqa: E501 Address("52.74.57.123", 30303, 30303)), Node.from_pubkey_and_addr( keys.PublicKey( decode_hex( "78de8a0916848093c73790ead81d1928bec737d565119932b98c6b100d944b7a95e94f847f689fc723399d2e31129d182f7ef3863f2b4c820abbf3ab2722344d" )), # noqa: E501 Address("191.235.84.50", 30303, 30303)), Node.from_pubkey_and_addr( keys.PublicKey( decode_hex( "ddd81193df80128880232fc1deb45f72746019839589eeb642d3d44efbb8b2dda2c1a46a348349964a6066f8afb016eb2a8c0f3c66f32fadf4370a236a4b5286" )), # noqa: E501 Address("52.231.202.145", 30303, 30303)), Node.from_pubkey_and_addr(
async def receive_connection( cls, reader: asyncio.StreamReader, writer: asyncio.StreamWriter, private_key: datatypes.PrivateKey) -> TransportAPI: try: msg = await asyncio.wait_for( reader.readexactly(ENCRYPTED_AUTH_MSG_LEN), timeout=REPLY_TIMEOUT, ) except asyncio.IncompleteReadError as err: raise HandshakeFailure(*err.args) from err try: ephem_pubkey, initiator_nonce, initiator_pubkey = decode_authentication( msg, private_key, ) except DecryptionError as non_eip8_err: # Try to decode as EIP8 msg_size = big_endian_to_int(msg[:2]) remaining_bytes = msg_size - ENCRYPTED_AUTH_MSG_LEN + 2 try: msg += await asyncio.wait_for( reader.readexactly(remaining_bytes), timeout=REPLY_TIMEOUT, ) except asyncio.IncompleteReadError as err: raise HandshakeFailure(*err.args) from err try: ephem_pubkey, initiator_nonce, initiator_pubkey = decode_authentication( msg, private_key, ) except DecryptionError as eip8_err: raise HandshakeFailure( f"Failed to decrypt both EIP8 handshake: {eip8_err} and " f"non-EIP8 handshake: {non_eip8_err}") else: got_eip8 = True else: got_eip8 = False peername = writer.get_extra_info("peername") if peername is None: socket = writer.get_extra_info("socket") sockname = writer.get_extra_info("sockname") raise HandshakeFailure( "Received incoming connection with no remote information:" f"socket={repr(socket)} sockname={sockname}") ip, socket, *_ = peername remote_address = Address(ip, socket, socket) cls.logger.debug("Receiving auth handshake from %s", remote_address) initiator_remote = Node.from_pubkey_and_addr(initiator_pubkey, remote_address) responder = HandshakeResponder(initiator_remote, private_key, got_eip8) responder_nonce = secrets.token_bytes(HASH_LEN) auth_ack_msg = responder.create_auth_ack_message(responder_nonce) auth_ack_ciphertext = responder.encrypt_auth_ack_message(auth_ack_msg) if writer.transport.is_closing() or reader.at_eof(): raise HandshakeFailure( f"Connection to {initiator_remote} is closing") # Use the `writer` to send the reply to the remote writer.write(auth_ack_ciphertext) await writer.drain() # Call `HandshakeResponder.derive_shared_secrets()` and use return values to create `Peer` aes_secret, mac_secret, egress_mac, ingress_mac = responder.derive_secrets( initiator_nonce=initiator_nonce, responder_nonce=responder_nonce, remote_ephemeral_pubkey=ephem_pubkey, auth_init_ciphertext=msg, auth_ack_ciphertext=auth_ack_ciphertext) transport = cls( remote=initiator_remote, private_key=private_key, reader=reader, writer=writer, aes_secret=aes_secret, mac_secret=mac_secret, egress_mac=egress_mac, ingress_mac=ingress_mac, ) return transport