示例#1
0
    async def respond_header(self, response: wallet_protocol.RespondHeader):
        """
        The full node responds to our RequestHeader call. We cannot finish this block
        until we have the required additions / removals for our wallets.
        """
        while True:
            if self._shut_down:
                return
            # We loop, to avoid infinite recursion. At the end of each iteration, we might want to
            # process the next block, if it exists.

            block = response.header_block

            # If we already have, return
            if block.header_hash in self.wallet_state_manager.block_records:
                return
            if block.height < 1:
                return

            block_record = BlockRecord(
                block.header_hash,
                block.prev_header_hash,
                block.height,
                block.weight,
                None,
                None,
                response.header_block.header.data.total_iters,
                response.header_block.challenge.get_hash(),
            )

            if self.wallet_state_manager.sync_mode:
                self.potential_blocks_received[uint32(block.height)].set()
                self.potential_header_hashes[block.height] = block.header_hash

            # Caches the block so we can finalize it when additions and removals arrive
            self.cached_blocks[block_record.header_hash] = (
                block_record,
                block,
                response.transactions_filter,
            )

            if block.prev_header_hash not in self.wallet_state_manager.block_records:
                # We do not have the previous block record, so wait for that. When the previous gets added to chain,
                # this method will get called again and we can continue. During sync, the previous blocks are already
                # requested. During normal operation, this might not be the case.
                self.future_block_hashes[
                    block.prev_header_hash] = block.header_hash

                lca = self.wallet_state_manager.block_records[
                    self.wallet_state_manager.lca]
                if (block_record.height - lca.height <
                        self.short_sync_threshold
                        and not self.wallet_state_manager.sync_mode):
                    # Only requests the previous block if we are not in sync mode, close to the new block,
                    # and don't have prev
                    header_request = wallet_protocol.RequestHeader(
                        uint32(block_record.height - 1),
                        block_record.prev_header_hash,
                    )
                    yield OutboundMessage(
                        NodeType.FULL_NODE,
                        Message("request_header", header_request),
                        Delivery.RESPOND,
                    )
                return

            # If the block has transactions that we are interested in, fetch adds/deletes
            if response.transactions_filter is not None:
                (
                    additions,
                    removals,
                ) = await self.wallet_state_manager.get_filter_additions_removals(
                    block_record, response.transactions_filter)
                if len(additions) > 0 or len(removals) > 0:
                    request_a = wallet_protocol.RequestAdditions(
                        block.height, block.header_hash, additions)
                    yield OutboundMessage(
                        NodeType.FULL_NODE,
                        Message("request_additions", request_a),
                        Delivery.RESPOND,
                    )
                    return

            # If we don't have any transactions in filter, don't fetch, and finish the block
            block_record = BlockRecord(
                block_record.header_hash,
                block_record.prev_header_hash,
                block_record.height,
                block_record.weight,
                [],
                [],
                block_record.total_iters,
                block_record.new_challenge_hash,
            )
            respond_header_msg: Optional[
                wallet_protocol.RespondHeader] = await self._block_finished(
                    block_record, block, response.transactions_filter)
            if respond_header_msg is None:
                return
            else:
                response = respond_header_msg
示例#2
0
    async def test_request_additions(self, two_nodes, wallet_blocks):
        full_node_1, full_node_2, server_1, server_2 = two_nodes
        wallet_a, wallet_receiver, blocks = wallet_blocks

        await server_2.start_client(PeerInfo("localhost", uint16(server_1._port)), None)
        blocks_list = await get_block_path(full_node_1)
        blocks_new = bt.get_consecutive_blocks(
            test_constants, 5, seed=b"test_request_additions"
        )

        # Request additinos for nonexisting block fails
        msgs = [
            _
            async for _ in full_node_1.request_additions(
                wallet_protocol.RequestAdditions(
                    blocks_new[-1].height, blocks_new[-1].header_hash, None
                )
            )
        ]
        assert len(msgs) == 1
        assert isinstance(msgs[0].message.data, wallet_protocol.RejectAdditionsRequest)

        # Request additions for orphaned block fails
        for block in blocks_new:
            async for _ in full_node_1.respond_block(fnp.RespondBlock(block)):
                pass
        msgs = [
            _
            async for _ in full_node_1.request_additions(
                wallet_protocol.RequestAdditions(
                    blocks_new[-1].height, blocks_new[-1].header_hash, None
                )
            )
        ]
        assert len(msgs) == 1
        assert isinstance(msgs[0].message.data, wallet_protocol.RejectAdditionsRequest)

        # If there are no transactions, only cb and fees additions
        blocks_new = bt.get_consecutive_blocks(
            test_constants, 10, block_list=blocks_list,
        )
        for block in blocks_new:
            [_ async for _ in full_node_1.respond_block(fnp.RespondBlock(block))]
        msgs = [
            _
            async for _ in full_node_1.request_additions(
                wallet_protocol.RequestAdditions(
                    blocks_new[-4].height, blocks_new[-4].header_hash, None
                )
            )
        ]
        assert len(msgs) == 1
        assert isinstance(msgs[0].message.data, wallet_protocol.RespondAdditions)
        assert len(msgs[0].message.data.coins) == 2
        assert msgs[0].message.data.proofs is None

        # Add a block with transactions
        spend_bundles = []
        puzzle_hashes = [wallet_a.get_new_puzzlehash(), wallet_a.get_new_puzzlehash()]
        for i in range(5):
            spend_bundles.append(
                wallet_a.generate_signed_transaction(
                    100, puzzle_hashes[i % 2], blocks_new[i - 8].get_coinbase(),
                )
            )
        height_with_transactions = len(blocks_new) + 1
        agg = SpendBundle.aggregate(spend_bundles)
        dic_h = {
            height_with_transactions: (
                best_solution_program(agg),
                agg.aggregated_signature,
            )
        }
        blocks_new = bt.get_consecutive_blocks(
            test_constants, 5, block_list=blocks_new, transaction_data_at_height=dic_h
        )
        for block in blocks_new:
            [_ async for _ in full_node_1.respond_block(fnp.RespondBlock(block))]

        # If no puzzle hashes requested, respond all coins and NO proof
        msgs = [
            _
            async for _ in full_node_1.request_additions(
                wallet_protocol.RequestAdditions(
                    blocks_new[height_with_transactions].height,
                    blocks_new[height_with_transactions].header_hash,
                    None,
                )
            )
        ]
        assert len(msgs) == 1
        assert isinstance(msgs[0].message.data, wallet_protocol.RespondAdditions)
        # One puzzle hash with change and fee (x3) = 9, minus two repeated ph = 7 + coinbase and fees = 9
        assert len(msgs[0].message.data.coins) == 9
        assert msgs[0].message.data.proofs is None

        additions_merkle_set = MerkleSet()
        for sb in spend_bundles:
            for coin in sb.additions():
                if coin is not None:
                    additions_merkle_set.add_already_hashed(coin.name())

        # Ask for one coin and check both PoI
        ph_list = [puzzle_hashes[0]]
        msgs = [
            _
            async for _ in full_node_1.request_additions(
                wallet_protocol.RequestAdditions(
                    blocks_new[height_with_transactions].height,
                    blocks_new[height_with_transactions].header_hash,
                    ph_list,
                )
            )
        ]
        assert len(msgs) == 1
        assert isinstance(msgs[0].message.data, wallet_protocol.RespondAdditions)
        assert len(msgs[0].message.data.coins) == 1
        assert len(msgs[0].message.data.coins[0][1]) == 3
        assert msgs[0].message.data.proofs is not None
        assert len(msgs[0].message.data.proofs) == 1
        assert confirm_included_already_hashed(
            blocks_new[height_with_transactions].header.data.additions_root,
            ph_list[0],
            msgs[0].message.data.proofs[0][1],
        )
        coin_list_for_ph = [
            coin
            for coin in blocks_new[height_with_transactions].additions()
            if coin.puzzle_hash == ph_list[0]
        ]
        assert confirm_included_already_hashed(
            blocks_new[height_with_transactions].header.data.additions_root,
            hash_coin_list(coin_list_for_ph),
            msgs[0].message.data.proofs[0][2],
        )

        # Ask for one ph and check PoE
        ph_list = [token_bytes(32)]
        msgs = [
            _
            async for _ in full_node_1.request_additions(
                wallet_protocol.RequestAdditions(
                    blocks_new[height_with_transactions].height,
                    blocks_new[height_with_transactions].header_hash,
                    ph_list,
                )
            )
        ]
        assert len(msgs) == 1
        assert isinstance(msgs[0].message.data, wallet_protocol.RespondAdditions)
        assert len(msgs[0].message.data.coins) == 1
        assert len(msgs[0].message.data.coins[0][1]) == 0
        assert msgs[0].message.data.proofs is not None
        assert len(msgs[0].message.data.proofs) == 1
        assert confirm_not_included_already_hashed(
            blocks_new[height_with_transactions].header.data.additions_root,
            ph_list[0],
            msgs[0].message.data.proofs[0][1],
        )
        assert msgs[0].message.data.proofs[0][2] is None

        # Ask for two puzzle_hashes
        ph_list = [puzzle_hashes[0], token_bytes(32)]
        msgs = [
            _
            async for _ in full_node_1.request_additions(
                wallet_protocol.RequestAdditions(
                    blocks_new[height_with_transactions].height,
                    blocks_new[height_with_transactions].header_hash,
                    ph_list,
                )
            )
        ]
        assert len(msgs) == 1
        assert isinstance(msgs[0].message.data, wallet_protocol.RespondAdditions)
        assert len(msgs[0].message.data.coins) == 2
        assert len(msgs[0].message.data.coins[0][1]) == 3
        assert msgs[0].message.data.proofs is not None
        assert len(msgs[0].message.data.proofs) == 2
        assert confirm_included_already_hashed(
            blocks_new[height_with_transactions].header.data.additions_root,
            ph_list[0],
            msgs[0].message.data.proofs[0][1],
        )
        assert confirm_included_already_hashed(
            blocks_new[height_with_transactions].header.data.additions_root,
            hash_coin_list(coin_list_for_ph),
            msgs[0].message.data.proofs[0][2],
        )
        assert confirm_not_included_already_hashed(
            blocks_new[height_with_transactions].header.data.additions_root,
            ph_list[1],
            msgs[0].message.data.proofs[1][1],
        )
        assert msgs[0].message.data.proofs[1][2] is None