コード例 #1
0
    async def _get_block_header_by_hash(self, block_hash: Hash32,
                                        peer: LESPeer) -> BlockHeader:
        """
        A single attempt to get the block header from the given peer.

        :raise BadLESResponse: if the peer replies with a header that has a different hash
        """
        self.logger.debug("Fetching header %s from %s", encode_hex(block_hash),
                          peer)
        max_headers = 1

        # TODO: Figure out why mypy thinks the first parameter to `get_block_headers`
        # should be of type `int`
        headers = await peer.chain_api.get_block_headers(
            block_hash,
            max_headers,
            skip=0,
            reverse=False,
        )
        if not headers:
            raise HeaderNotFound(
                f"Peer {peer} has no block with hash {block_hash}")
        header = headers[0]
        if header.hash != block_hash:
            raise BadLESResponse(
                f"Received header hash ({header.hex_hash}) does not "
                f"match what we requested ({encode_hex(block_hash)})")
        return header
コード例 #2
0
ファイル: service.py プロジェクト: skylenet/trinity
    async def _get_contract_code_from_peer(
            self,
            block_hash: Hash32,
            address: ETHAddress,
            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 = peer.sub_proto.send_get_contract_code(block_hash, keccak(address))
        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 {peer} sent code {encode_hex(bytecode)} that did not match "
                f"hash {encode_hex(code_hash)} in account {encode_hex(address)}"
            )
コード例 #3
0
    async def _get_block_header_by_hash(self, block_hash: Hash32,
                                        peer: LESPeer) -> BlockHeader:
        """
        A single attempt to get the block header from the given peer.

        :raise BadLESResponse: if the peer replies with a header that has a different hash
        """
        self.logger.debug("Fetching header %s from %s", encode_hex(block_hash),
                          peer)
        max_headers = 1
        headers = await peer.requests.get_block_headers(
            block_hash,
            max_headers,
            skip=0,
            reverse=False,
        )
        if not headers:
            raise HeaderNotFound("Peer {} has no block with hash {}".format(
                peer, block_hash))
        header = 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
コード例 #4
0
ファイル: service.py プロジェクト: akhila-raju/py-evm
    async def _raise_for_empty_code(
            self,
            block_hash: Hash32,
            address: Address,
            code_hash: Hash32,
            peer: LESPeer) -> None:
        """
        A peer might return b'' if it doesn't have the block at the requested header,
        or it might maliciously return b'' when the code is non-empty. This method tries to tell the
        difference.

        This method MUST raise an exception, it's trying to determine the appropriate one.

        :raise BadLESResponse: if peer seems to be maliciously responding with invalid empty code
        :raise NoEligiblePeers: if peer might simply not have the code available
        """
        try:
            header = await self._get_block_header_by_hash(block_hash, peer)
        except HeaderNotFound:
            # We presume that the current peer is the best peer. Because
            # our best peer doesn't have the header we want, there are no eligible peers.
            raise NoEligiblePeers("Our best peer does not have the header %s" % block_hash)

        head_number = peer.head_info.block_number
        if head_number - header.block_number > MAX_REORG_DEPTH:
            # The peer claims to be far ahead of the header we requested
            if self.headerdb.get_canonical_block_hash(header.block_number) == block_hash:
                # Our node believes that the header at the reference hash is canonical,
                # so treat the peer as malicious
                raise BadLESResponse(
                    "Peer %s sent empty code that did not match hash %s in account %s" % (
                        peer,
                        encode_hex(code_hash),
                        encode_hex(address),
                    )
                )
            else:
                # our header isn't canonical, so treat the empty response as missing data
                raise NoEligiblePeers(
                    "Our best peer does not have the non-canonical header %s" % block_hash
                )
        elif head_number - header.block_number < 0:
            # The peer claims to be behind the header we requested, but somehow served it to us.
            # Odd, it might be a race condition. Treat as if there are no eligible peers for now.
            raise NoEligiblePeers("Our best peer's head does include header %s" % block_hash)
        else:
            # The peer is ahead of the current block header, but only by a bit. It might be on
            # an uncle, or we might be. So we can't tell the difference between missing and
            # malicious. We don't want to aggressively drop this peer, so treat the code as missing.
            raise NoEligiblePeers(
                "Peer %s claims to be ahead of %s, but returned empty code with hash %s. "
                "It is on number %d, maybe an uncle. Retry with an older block hash." % (
                    peer,
                    header,
                    code_hash,
                    head_number,
                )
            )
コード例 #5
0
ファイル: lightchain.py プロジェクト: tuxxy/py-evm
 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
コード例 #6
0
ファイル: service.py プロジェクト: skylenet/trinity
 async def _get_account_from_peer(
         self,
         block_hash: Hash32,
         address: ETHAddress,
         peer: LESPeer) -> Account:
     key = keccak(address)
     proof = await self._get_proof(peer, block_hash, account_key=b'', key=key)
     header = await self._get_block_header_by_hash(block_hash, peer)
     try:
         rlp_account = HexaryTrie.get_from_proof(header.state_root, key, proof)
     except BadTrieProof as exc:
         raise BadLESResponse(
             f"Peer {peer} returned an invalid proof for account {encode_hex(address)} "
             f"at block {encode_hex(block_hash)}"
         ) from exc
     return rlp.decode(rlp_account, sedes=Account)
コード例 #7
0
    async def _get_block_header_by_hash(self, block_hash: Hash32,
                                        peer: LESPeer) -> BlockHeader:
        """
        A single attempt to get the block header from the given peer.

        :raise BadLESResponse: if the peer replies with a header that has a different hash
        """
        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, 0,
                                              False, 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