class MinGasParameters(Command): _cmd_id = 32 structure = [('hist_net_tpc_capability', sedes.CountableList( sedes.List([sedes.big_endian_int, sedes.big_endian_int]))), ('hist_min_allowed_gas_price', sedes.CountableList( sedes.List([sedes.big_endian_int, sedes.big_endian_int])))]
class Hello(Command): _cmd_id = 0 decode_strict = False structure = [ ('version', sedes.big_endian_int), ('client_version_string', sedes.text), ('capabilities', sedes.CountableList(sedes.List([sedes.text, sedes.big_endian_int]))), ('listen_port', sedes.big_endian_int), ('remote_pubkey', sedes.binary) ]
class Announce(Command): _cmd_id = 1 structure = [ ('head_hash', sedes.binary), ('head_number', sedes.big_endian_int), ('head_td', sedes.big_endian_int), ('reorg_depth', sedes.big_endian_int), # TODO: The params CountableList may contain any of the values from the # Status msg. Need to extend this command to process that too. ('params', sedes.CountableList(sedes.List([sedes.text, sedes.raw]))), ]
def decode_payload(self, rlp_data: bytes) -> PayloadType: if isinstance(self.structure, sedes.CountableList): decoder = self.structure else: decoder = sedes.List([type_ for _, type_ in self.structure], strict=self.decode_strict) try: data = rlp.decode(rlp_data, sedes=decoder, recursive_cache=True) except rlp.DecodingError as err: raise MalformedMessage( f"Malformed {type(self).__name__} message: {err!r}") from err if isinstance(self.structure, sedes.CountableList): return data return { field_name: value for ((field_name, _), value) in zip(self.structure, data) }
def encode_payload(self, data: Union[PayloadType, sedes.CountableList]) -> bytes: if isinstance(data, dict): # convert dict to ordered list if not isinstance(self.structure, list): raise ValueError( "Command.structure must be a list when data is a dict") expected_keys = sorted(name for name, _ in self.structure) data_keys = sorted(data.keys()) if data_keys != expected_keys: raise ValueError( f"Keys in data dict ({data_keys}) do not match expected keys ({expected_keys})" ) data = [data[name] for name, _ in self.structure] if isinstance(self.structure, sedes.CountableList): encoder = self.structure else: encoder = sedes.List([type_ for _, type_ in self.structure]) return rlp.encode(data, sedes=encoder)
token_flag = b'\x00' msg = self.ephemeral_pubkey.to_bytes() + nonce + token_flag return msg def encrypt_auth_ack_message(self, ack_message: bytes) -> bytes: if self.use_eip8: auth_ack = encrypt_eip8_msg(ack_message, self.remote.pubkey) else: auth_ack = ecies.encrypt(ack_message, self.remote.pubkey) return auth_ack eip8_ack_sedes = sedes.List( [ sedes.Binary(min_length=64, max_length=64), # ephemeral pubkey sedes.Binary(min_length=32, max_length=32), # nonce sedes.BigEndianInt() # version ], strict=False) eip8_auth_sedes = sedes.List( [ sedes.Binary(min_length=65, max_length=65), # sig sedes.Binary(min_length=64, max_length=64), # pubkey sedes.Binary(min_length=32, max_length=32), # nonce sedes.BigEndianInt() # version ], strict=False) def _pad_eip8_data(data: bytes) -> bytes: # Pad with random amount of data, as per
class NewBlockHashes(Command): _cmd_id = 1 structure = sedes.CountableList( sedes.List([sedes.binary, sedes.big_endian_int]))
class StakeForAddresses(Command): _cmd_id = 26 structure = [ ('stakes', sedes.CountableList(sedes.List([address, sedes.big_endian_int]))) ]
class ChainHeadRootHashTimestamps(Command): _cmd_id = 20 # this way is actually almost twice as fast as using a key... structure is [timestamp, root_hash] structure = sedes.CountableList( sedes.List([sedes.big_endian_int, sedes.binary]))
class Status(Command): _cmd_id = 0 decode_strict = False # A list of (key, value) pairs is all a Status msg contains, but since the values can be of # any type, we need to use the raw sedes here and do the actual deserialization in # decode_payload(). structure = sedes.CountableList(sedes.List([sedes.text, sedes.raw])) # The sedes used for each key in the list above. Keys that use None as their sedes are # optional and have no value -- IOW, they just need to be present in the msg when appropriate. items_sedes = { 'protocolVersion': sedes.big_endian_int, 'networkId': sedes.big_endian_int, 'headTd': sedes.big_endian_int, 'headHash': sedes.binary, 'headNum': sedes.big_endian_int, 'genesisHash': sedes.binary, 'serveHeaders': None, 'serveChainSince': sedes.big_endian_int, 'serveStateSince': sedes.big_endian_int, 'txRelay': None, 'flowControl/BL': sedes.big_endian_int, 'flowControl/MRC': sedes.CountableList( sedes.List([ sedes.big_endian_int, sedes.big_endian_int, sedes.big_endian_int ])), 'flowControl/MRR': sedes.big_endian_int, } @to_dict def decode_payload(self, rlp_data: bytes) -> Iterator[Tuple[str, Any]]: data = cast(List[Tuple[str, bytes]], super().decode_payload(rlp_data)) # The LES/Status msg contains an arbitrary list of (key, value) pairs, where values can # have different types and unknown keys should be ignored for forward compatibility # reasons, so here we need an extra pass to deserialize each of the key/value pairs we # know about. for key, value in data: if key not in self.items_sedes: continue yield key, self._deserialize_item(key, value) def encode_payload( self, data: Union[_DecodedMsgType, sedes.CountableList]) -> bytes: response = [ (key, self._serialize_item(key, value)) for key, value in sorted(cast(Dict[str, Any], data).items()) ] return super().encode_payload(response) def _deserialize_item(self, key: str, value: bytes) -> Any: sedes = self.items_sedes[key] if sedes is not None: return sedes.deserialize(value) else: # See comment in the definition of item_sedes as to why we do this. return b'' def _serialize_item(self, key: str, value: bytes) -> bytes: sedes = self.items_sedes[key] if sedes is not None: return sedes.serialize(value) else: # See comment in the definition of item_sedes as to why we do this. return b''