async def process_sub_proto_handshake(self, cmd: Command, msg: _DecodedMsgType) -> None: if not isinstance(cmd, Status): await self.disconnect(DisconnectReason.other) raise HandshakeFailure( "Expected a HLS Status msg, got {}, disconnecting".format(cmd)) msg = cast(Dict[str, Any], msg) if msg['network_id'] != self.network_id: await self.disconnect(DisconnectReason.useless_peer) raise HandshakeFailure( "{} network ({}) does not match ours ({}), disconnecting". format(self, msg['network_id'], self.network_id)) chain_info = await self._local_chain_info genesis_block_hash = chain_info.genesis_block_hash if msg['genesis_block_hash'] != genesis_block_hash: await self.disconnect(DisconnectReason.useless_peer) raise HandshakeFailure( "{} genesis ({}) does not match ours ({}), disconnecting". format(self, encode_hex(msg['genesis_block_hash']), encode_hex(genesis_block_hash))) self.node_type = msg['node_type'] self.send_wallet_address_verification(self.local_salt, msg['salt']) # After the sub_proto handshake, the peer will send back a signed message containing the wallet address cmd, msg = await self.read_msg() if isinstance(cmd, Disconnect): # Peers sometimes send a disconnect msg before they send the sub-proto handshake. raise HandshakeFailure( "{} disconnected before completing wallet address verification: {}" .format(self, msg['reason_name'])) await self.process_sub_proto_wallet_address_verification(cmd, msg)
async def process_sub_proto_handshake(self, cmd: Command, msg: _DecodedMsgType) -> None: if not isinstance(cmd, (Status, StatusV2)): await self.disconnect(DisconnectReason.subprotocol_error) raise HandshakeFailure( "Expected a LES Status msg, got {}, disconnecting".format(cmd)) msg = cast(Dict[str, Any], msg) if msg['networkId'] != self.network_id: await self.disconnect(DisconnectReason.useless_peer) raise HandshakeFailure( "{} network ({}) does not match ours ({}), disconnecting". format(self, msg['networkId'], self.network_id)) genesis = await self.genesis if msg['genesisHash'] != genesis.hash: await self.disconnect(DisconnectReason.useless_peer) raise HandshakeFailure( "{} genesis ({}) does not match ours ({}), disconnecting". format(self, encode_hex(msg['genesisHash']), genesis.hex_hash)) # Eventually we might want to keep connections to peers where we are the only side serving # data, but right now both our chain syncer and the Peer.boot() method expect the remote # to reply to header requests, so if they don't we simply disconnect here. if 'serveHeaders' not in msg: await self.disconnect(DisconnectReason.useless_peer) raise HandshakeFailure( f"{self} doesn't serve headers, disconnecting") self.head_td = msg['headTd'] self.head_hash = msg['headHash'] self.head_number = msg['headNum']
async def _handshake( initiator: 'HandshakeInitiator', reader: asyncio.StreamReader, writer: asyncio.StreamWriter, token: CancelToken, ) -> Tuple[bytes, bytes, sha3.keccak_256, sha3.keccak_256]: """See the handshake() function above. This code was factored out into this helper so that we can create Peers with directly connected readers/writers for our tests. """ initiator_nonce = keccak(os.urandom(HASH_LEN)) auth_msg = initiator.create_auth_message(initiator_nonce) auth_init = initiator.encrypt_auth_message(auth_msg) writer.write(auth_init) auth_ack = await token.cancellable_wait( reader.read(ENCRYPTED_AUTH_ACK_LEN), timeout=REPLY_TIMEOUT) if reader.at_eof(): # This is what happens when Parity nodes have blacklisted us # (https://github.com/ethereum/py-evm/issues/901). raise HandshakeFailure("%s disconnected before sending auth ack", repr(initiator.remote)) ephemeral_pubkey, responder_nonce = initiator.decode_auth_ack_message( auth_ack) aes_secret, mac_secret, egress_mac, ingress_mac = initiator.derive_secrets( initiator_nonce, responder_nonce, ephemeral_pubkey, auth_init, auth_ack) return aes_secret, mac_secret, egress_mac, ingress_mac
async def process_p2p_handshake(self, cmd: protocol.Command, msg: protocol.PayloadType) -> None: msg = cast(Dict[str, Any], msg) if not isinstance(cmd, Hello): await self.disconnect(DisconnectReason.bad_protocol) raise HandshakeFailure( f"Expected a Hello msg, got {cmd}, disconnecting") remote_capabilities = msg['capabilities'] try: self.sub_proto = self.select_sub_protocol(remote_capabilities) except NoMatchingPeerCapabilities: await self.disconnect(DisconnectReason.useless_peer) raise HandshakeFailure( f"No matching capabilities between us ({self.capabilities}) and {self.remote} " f"({remote_capabilities}), disconnecting") self.logger.debug( "Finished P2P handshake with %s, using sub-protocol %s", self.remote, self.sub_proto)
async def process_sub_proto_wallet_address_verification( self, cmd: Command, msg: _DecodedMsgType) -> None: if not isinstance(cmd, WalletAddressVerification): await self.disconnect(DisconnectReason.other) raise HandshakeFailure( "Expected a HLS WalletAddressVerification msg, got {}, disconnecting" .format(cmd)) msg = cast(Dict[str, Any], msg) # make sure the salt they replied with is the salt we sent: if msg['peer_salt'] != self.local_salt: raise HandshakeFailure( "The peer replied with a signed message using the wrong salt") salt = msg['local_salt'] + msg['peer_salt'] validate_transaction_signature(salt, msg['v'], msg['r'], msg['s']) self.wallet_address = extract_wallet_verification_sender( salt, msg['v'], msg['r'], msg['s'])
async def do_p2p_handshake(self) -> None: """Perform the handshake for the P2P base protocol. Raises HandshakeFailure if the handshake is not successful. """ self.base_protocol.send_handshake() try: cmd, msg = await self.read_msg() except rlp.DecodingError: raise HandshakeFailure( "Got invalid rlp_templates data during handshake") except MalformedMessage as e: raise HandshakeFailure( "Got malformed message during handshake") from e if isinstance(cmd, Disconnect): msg = cast(Dict[str, Any], msg) # Peers sometimes send a disconnect msg before they send the initial P2P handshake. raise HandshakeFailure( f"{self} disconnected before completing sub-proto handshake: {msg['reason_name']}" ) await self.process_p2p_handshake(cmd, msg)
async def do_sub_proto_handshake(self) -> None: """Perform the handshake for the sub-protocol agreed with the remote peer. Raises HandshakeFailure if the handshake is not successful. """ await self.send_sub_proto_handshake() cmd, msg = await self.read_msg() if isinstance(cmd, Ping): # Parity sends a Ping before the sub-proto handshake, so respond to that and read the # next one, which hopefully will be the actual handshake. self.base_protocol.send_pong() cmd, msg = await self.read_msg() if isinstance(cmd, Disconnect): msg = cast(Dict[str, Any], msg) # Peers sometimes send a disconnect msg before they send the sub-proto handshake. raise HandshakeFailure( f"{self} disconnected before completing sub-proto handshake: {msg['reason_name']}" ) await self.process_sub_proto_handshake(cmd, msg) self.logger.debug("Finished %s handshake with %s", self.sub_proto, self.remote)