def get_protocol_command_for(self, msg: bytes) -> protocol.Command: """Return the Command corresponding to the cmd_id encoded in the given msg.""" cmd_id = get_devp2p_cmd_id(msg) self.logger.debug("Got msg with cmd_id: %s", cmd_id) if cmd_id < self.base_protocol.cmd_length: proto = self.base_protocol elif cmd_id < self.sub_proto.cmd_id_offset + self.sub_proto.cmd_length: proto = self.sub_proto # type: ignore else: raise UnknownProtocolCommand("No protocol found for cmd_id {}".format(cmd_id)) return proto.cmd_by_id[cmd_id]
def process_msg(self, msg: bytes) -> protocol._DecodedMsgType: cmd_id = get_devp2p_cmd_id(msg) self.logger.debug("Got msg with cmd_id: {}".format(cmd_id)) proto = self.get_protocol_for(cmd_id) if proto is None: raise UnknownProtocolCommand( "No protocol found for cmd_id {}".format(cmd_id)) decoded = proto.process(cmd_id, msg) if decoded is None: return None # Check if this is a reply we're waiting for and, if so, call the callback for this # request_id. request_id = decoded.get('request_id') if request_id is not None and request_id in self._pending_replies: callback = self._pending_replies.pop(request_id) callback(decoded) return decoded
async def handshake(remote: Node, privkey: datatypes.PrivateKey, peer_class: 'Type[BasePeer]', chaindb: BaseChainDB, network_id: int) -> 'BasePeer': """Perform the auth and P2P handshakes with the given remote. Return an instance of the given peer_class (must be a subclass of BasePeer) connected to that remote in case both handshakes are successful and at least one of the sub-protocols supported by peer_class is also supported by the remote. Raises UnreachablePeer if we cannot connect to the peer or UselessPeer if none of the sub-protocols supported by us is also supported by the remote. """ try: (aes_secret, mac_secret, egress_mac, ingress_mac, reader, writer) = await auth.handshake(remote, privkey) except (ConnectionRefusedError, OSError) as e: raise UnreachablePeer(e) peer = peer_class(remote=remote, privkey=privkey, reader=reader, writer=writer, aes_secret=aes_secret, mac_secret=mac_secret, egress_mac=egress_mac, ingress_mac=ingress_mac, chaindb=chaindb, network_id=network_id) peer.base_protocol.send_handshake() msg = await peer.read_msg() decoded = peer.process_msg(msg) if get_devp2p_cmd_id(msg) == Disconnect._cmd_id: # Peers may send a disconnect msg before they send the initial P2P handshake (e.g. when # they're not accepting more peers), so we special case that here because it's important # to distinguish this from a failed handshake (e.g. no matching protocols, etc). raise PeerDisconnected("{} disconnected before handshake: {}".format( peer, decoded['reason_name'])) if len(peer.enabled_sub_protocols) == 0: raise UselessPeer("No matching sub-protocols with {}".format(peer)) for proto in peer.enabled_sub_protocols: proto.send_handshake(peer.head_info) return peer
def process_msg( self, msg: bytes) -> Tuple[protocol.Command, protocol._DecodedMsgType]: cmd_id = get_devp2p_cmd_id(msg) self.logger.debug("Got msg with cmd_id: %s", cmd_id) proto = self.get_protocol_for(cmd_id) if proto is None: raise UnknownProtocolCommand( "No protocol found for cmd_id {}".format(cmd_id)) cmd, decoded = proto.process(cmd_id, msg) if isinstance(decoded, dict): # Check if this is a reply we're waiting for and, if so, call the callback for this # request_id. request_id = decoded.get('request_id') if request_id is not None and request_id in self._pending_replies: callback = self._pending_replies.pop(request_id) callback(decoded) if self.received_msg_callback is not None: self.received_msg_callback(self, cmd, decoded) return cmd, decoded
def decode(self, data: bytes) -> _DecodedMsgType: packet_type = get_devp2p_cmd_id(data) if packet_type != self.cmd_id: raise ValueError("Wrong packet type: {}".format(packet_type)) return self.decode_payload(data[1:])