async def get_receipts(self, block_hash: bytes, cancel_token: CancelToken) -> List[Receipt]: request_id = gen_request_id() self.sub_proto.send_get_receipts(block_hash, request_id) reply = await self._wait_for_reply(request_id, cancel_token) if not reply['receipts']: raise BlockNotFound("No block with hash {} found".format(block_hash)) return reply['receipts'][0]
def request_block_headers(self, block_number_or_hash: Union[int, bytes], max_headers: int, reverse: bool = True) -> None: request_id = gen_request_id() self.sub_proto.send_get_block_headers(block_number_or_hash, max_headers, request_id, reverse)
async def _get_headers_at_chain_split( self, block_number: BlockNumber) -> Tuple[BlockHeader, BlockHeader]: start_block = block_number - 1 max_headers = 2 reverse = False request_id = gen_request_id() self.sub_proto.send_get_block_headers(start_block, max_headers, request_id, reverse) while True: cmd, msg = await self.read_msg() msg = cast(Dict[str, Any], msg) if not isinstance(cmd, les.BlockHeaders): self.logger.debug( "Ignoring %s msg from %s as we haven't performed the chain-split check yet", cmd, self) continue elif msg['request_id'] != request_id: self.logger.debug( "Got a BlockHeaders msg with the wrong request_id (%d) from %s, ignoring", msg['request_id'], self) continue headers = msg['headers'] validate_header_response(start_block, max_headers, reverse, headers) parent, header = headers return header, parent
async def get_block_by_hash( self, block_hash: bytes, cancel_token: CancelToken) -> les.LESBlockBody: request_id = gen_request_id() self.sub_proto.send_get_block_bodies([block_hash], request_id) reply = await self._wait_for_reply(request_id, cancel_token) if not reply['bodies']: raise BlockNotFound("Peer {} has no block with hash {}".format(self, block_hash)) return reply['bodies'][0]
async def get_contract_code( self, block_hash: bytes, key: bytes, cancel_token: CancelToken) -> bytes: request_id = gen_request_id() self.sub_proto.send_get_contract_code(block_hash, key, request_id) reply = await self._wait_for_reply(request_id, cancel_token) if not reply['codes']: return b'' return reply['codes'][0]
async def get_contract_code(self, block_hash: Hash32, key: bytes) -> bytes: peer = cast(LESPeer, self.peer_pool.highest_td_peer) request_id = gen_request_id() peer.sub_proto.send_get_contract_code(block_hash, key, request_id) reply = await self._wait_for_reply(request_id) if not reply['codes']: return b'' return reply['codes'][0]
async def get_contract_code(self, block_hash: Hash32, key: bytes) -> bytes: peer = await self.get_best_peer() request_id = gen_request_id() peer.sub_proto.send_get_contract_code(block_hash, key, request_id) reply = await self._wait_for_reply(request_id) if not reply['codes']: return b'' return reply['codes'][0]
async def get_block_by_hash( self, block_hash: bytes, cancel_token: CancelToken) -> BlockBody: request_id = gen_request_id() self.sub_proto.send_get_block_bodies([block_hash], request_id) reply = await self._wait_for_reply(request_id, cancel_token) if not reply['bodies']: raise BlockNotFound("Peer {} has no block with hash {}".format(self, block_hash)) return reply['bodies'][0]
async def get_block_header_by_hash( self, block_hash: bytes, cancel_token: CancelToken) -> BlockHeader: request_id = gen_request_id() max_headers = 1 self.sub_proto.send_get_block_headers(block_hash, max_headers, request_id) reply = await self._wait_for_reply(request_id, cancel_token) if not reply['headers']: raise BlockNotFound("Peer {} has no block with hash {}".format(self, block_hash)) return reply['headers'][0]
async def get_receipts(self, block_hash: Hash32) -> List[Receipt]: peer = cast(LESPeer, self.peer_pool.highest_td_peer) self.logger.debug("Fetching %s receipts from %s", encode_hex(block_hash), peer) request_id = gen_request_id() peer.sub_proto.send_get_receipts(block_hash, request_id) reply = await self._wait_for_reply(request_id) if not reply['receipts']: raise BlockNotFound("No block with hash {} found".format(block_hash)) return reply['receipts'][0]
async def get_block_body_by_hash(self, block_hash: Hash32) -> BlockBody: peer = cast(LESPeer, self.peer_pool.highest_td_peer) self.logger.debug("Fetching block %s from %s", encode_hex(block_hash), peer) request_id = gen_request_id() peer.sub_proto.send_get_block_bodies([block_hash], request_id) reply = await self._wait_for_reply(request_id) if not reply['bodies']: raise BlockNotFound("Peer {} has no block with hash {}".format(peer, block_hash)) return reply['bodies'][0]
async def _get_proof(self, cancel_token: CancelToken, block_hash: bytes, account_key: bytes, key: bytes, from_level: int = 0) -> List[bytes]: request_id = gen_request_id() self.sub_proto.send_get_proof(block_hash, account_key, key, from_level, request_id) reply = await self._wait_for_reply(request_id, cancel_token) return reply['proof']
async def _get_proof(self, peer: LESPeer, block_hash: bytes, account_key: bytes, key: bytes, from_level: int = 0) -> List[bytes]: request_id = gen_request_id() peer.sub_proto.send_get_proof(block_hash, account_key, key, from_level, request_id) reply = await self._wait_for_reply(request_id) return reply['proof']
async def _handle_new_collation_hashes(self, peer, msg): """Request those collations.""" # Request all collations for now, no matter if we now about them or not, as there's no way # to header existence at the moment. In the future we won't transfer collations anyway but # only collation bodies (headers are retrieved from the main chain), so there's no need to # add this at the moment. collation_hashes = set( collation_hash for collation_hash, _ in msg["collation_hashes_and_periods"] ) if collation_hashes: peer.sub_proto.send_get_collations(gen_request_id(), list(collation_hashes))
async def _get_block_header_by_hash(self, peer: LESPeer, block_hash: Hash32) -> BlockHeader: self.logger.debug("Fetching header %s from %s", encode_hex(block_hash), peer) request_id = gen_request_id() max_headers = 1 peer.sub_proto.send_get_block_headers(block_hash, max_headers, request_id) reply = await self._wait_for_reply(request_id) if not reply['headers']: raise HeaderNotFound("Peer {} has no block with hash {}".format(peer, block_hash)) header = reply['headers'][0] if header.hash != block_hash: raise BadLESResponse( "Received header hash (%s) does not match what we requested (%s)", header.hex_hash, encode_hex(block_hash)) return header
async def fetch_headers_starting_at( self, start_block: int, cancel_token: CancelToken) -> List[BlockHeader]: """Fetches up to self.max_headers_fetch starting at start_block. Returns a list containing those headers in ascending order of block number. """ request_id = gen_request_id() self.sub_proto.send_get_block_headers( start_block, self.max_headers_fetch, request_id, reverse=False) reply = await self._wait_for_reply(request_id, cancel_token) if not reply['headers']: raise EmptyGetBlockHeadersReply( "No headers in reply. start_block=={}".format(start_block)) self.logger.info( "fetched headers from %s to %s", reply['headers'][0].block_number, reply['headers'][-1].block_number) return reply['headers']
async def _fetch_headers_starting_at( self, peer: LESPeer, start_block: int) -> List[BlockHeader]: """Fetches up to self.max_headers_fetch starting at start_block. :return: a list containing those headers in ascending order of block number. :raise EmptyGetBlockHeadersReply: if no headers are returned """ request_id = gen_request_id() peer.sub_proto.send_get_block_headers( start_block, peer.max_headers_fetch, request_id, reverse=False) reply = await self._wait_for_reply(request_id) if not reply['headers']: raise EmptyGetBlockHeadersReply( "No headers in reply. start_block=={}".format(start_block)) self.logger.debug( "fetched headers from %s to %s", reply['headers'][0].block_number, reply['headers'][-1].block_number) return reply['headers']
async def get_collations(self, collation_hashes: List[Hash32], cancel_token: CancelToken) -> Set[Collation]: # Don't send empty request if len(collation_hashes) == 0: return set() request_id = gen_request_id() pending_reply: asyncio.Future[Tuple[ Command, protocol._DecodedMsgType]] = asyncio.Future() self._pending_replies[request_id] = pending_reply cast(ShardingProtocol, self.sub_proto).send_get_collations(request_id, collation_hashes) cmd, msg = await wait_with_token(pending_reply, token=cancel_token) if not isinstance(cmd, Collations): raise UnexpectedMessage( "Expected Collations as response to GetCollations, but got %s", cmd.__class__.__name__) return set(msg["collations"])
async def _get_contract_code_from_peer(self, block_hash: Hash32, address: Address, code_hash: Hash32, peer: LESPeer) -> bytes: """ A single attempt to get the contract code from the given peer :raise BadLESResponse: if the peer replies with contract code that does not match the account's code hash """ # request contract code request_id = gen_request_id() peer.sub_proto.send_get_contract_code(block_hash, keccak(address), request_id) reply = await self._wait_for_reply(request_id) if not reply['codes']: bytecode = b'' else: bytecode = reply['codes'][0] # validate bytecode against a proven account if code_hash == keccak(bytecode): return bytecode elif bytecode == b'': await self._raise_for_empty_code(block_hash, address, code_hash, peer) # The following is added for mypy linting: raise RuntimeError( "Unreachable, _raise_for_empty_code must raise its own exception" ) else: # a bad-acting peer sent an invalid non-empty bytecode raise BadLESResponse( "Peer %s sent code %s that did not match hash %s in account %s" % ( peer, encode_hex(bytecode), encode_hex(code_hash), encode_hex(address), ))