コード例 #1
0
    async def _load_chain_from_store(self, ) -> None:
        """
        Initializes the state of the Blockchain class from the database. Sets the LCA, tips,
        headers, height_to_hash, and block_store DiffStores.
        """
        lca_db: Optional[Header] = await self.block_store.get_lca()
        tips_db: List[Header] = await self.block_store.get_tips()
        headers_db: Dict[bytes32,
                         Header] = await self.block_store.get_headers()

        assert (lca_db is None) == (len(tips_db) == 0) == (len(headers_db)
                                                           == 0)
        if lca_db is None:
            result, removed, error_code = await self.receive_block(
                self.genesis, sync_mode=False)
            if result != ReceiveBlockResult.ADDED_TO_HEAD:
                if error_code is not None:
                    raise ConsensusError(error_code)
                else:
                    raise RuntimeError(f"Invalid genesis block {self.genesis}")
            return

        await self.block_store.init_challenge_hashes()

        # Set the state (lca block and tips)
        self.lca_block = lca_db
        self.tips = tips_db

        # Find the common ancestor of the tips, and add intermediate blocks to headers
        cur: List[Header] = self.tips[:]
        while any(b.header_hash != cur[0].header_hash for b in cur):
            heights = [b.height for b in cur]
            i = heights.index(max(heights))
            self.headers[cur[i].header_hash] = cur[i]
            prev: Header = headers_db[cur[i].prev_header_hash]
            challenge_hash = self.block_store.get_challenge_hash(
                cur[i].header_hash)
            self.block_store.add_proof_of_time(
                challenge_hash,
                uint64(cur[i].data.total_iters - prev.data.total_iters),
                cur[i].data.height,
            )
            cur[i] = prev

        # Consistency check, tips should have an LCA equal to the DB LCA
        assert cur[0] == self.lca_block

        # Sets the header for remaining blocks, and height_to_hash dict
        cur_b: Header = self.lca_block
        while True:
            self.headers[cur_b.header_hash] = cur_b
            self.height_to_hash[cur_b.height] = cur_b.header_hash
            if cur_b.height == 0:
                break
            prev_b: Header = headers_db[cur_b.prev_header_hash]
            challenge_hash = self.block_store.get_challenge_hash(
                cur_b.header_hash)
            self.block_store.add_proof_of_time(
                challenge_hash,
                uint64(cur_b.data.total_iters - prev_b.data.total_iters),
                cur_b.data.height,
            )
            cur_b = prev_b

        # Asserts that the DB genesis block is correct
        assert cur_b == self.genesis.header

        # Adds the blocks to the db between LCA and tip
        await self.recreate_diff_stores()
コード例 #2
0
    async def process(self) -> None:
        header_hashes = self.sync_store.get_potential_hashes()

        # TODO: run this in a new process so it doesn't have to share CPU time with other things
        for batch_start_height in range(self.fork_height + 1,
                                        self.tip_height + 1, self.BATCH_SIZE):
            total_time_slept = 0
            batch_end_height = min(batch_start_height + self.BATCH_SIZE - 1,
                                   self.tip_height)
            for height in range(batch_start_height, batch_end_height + 1):
                # If we have already added this block to the chain, skip it
                if header_hashes[height] in self.blockchain.headers:
                    batch_start_height = height + 1

            while True:
                if self._shut_down:
                    return
                if total_time_slept > self.TOTAL_TIMEOUT:
                    raise TimeoutError("Took too long to fetch blocks")
                awaitables = [
                    (self.sync_store.potential_blocks_received[uint32(height)]
                     ).wait()
                    for height in range(batch_start_height, batch_end_height +
                                        1)
                ]
                future = asyncio.gather(*awaitables, return_exceptions=True)
                try:
                    await asyncio.wait_for(future, timeout=self.SLEEP_INTERVAL)
                    break
                except concurrent.futures.TimeoutError:
                    try:
                        await future
                    except asyncio.CancelledError:
                        pass
                    total_time_slept += self.SLEEP_INTERVAL
                    log.info(
                        f"Did not receive desired blocks ({batch_start_height}, {batch_end_height})"
                    )

            # Verifies this batch, which we are guaranteed to have (since we broke from the above loop)
            blocks = []
            for height in range(batch_start_height, batch_end_height + 1):
                b: Optional[FullBlock] = self.sync_store.potential_blocks[
                    uint32(height)]
                assert b is not None
                blocks.append(b)

            validation_start_time = time.time()

            prevalidate_results = await self.blockchain.pre_validate_blocks_multiprocessing(
                blocks)

            if self._shut_down:
                return

            for index, block in enumerate(blocks):
                assert block is not None

                # The block gets permanantly added to the blockchain
                validated, pos = prevalidate_results[index]

                async with self.blockchain.lock:
                    (
                        result,
                        header_block,
                        error_code,
                    ) = await self.blockchain.receive_block(block,
                                                            validated,
                                                            pos,
                                                            sync_mode=True)
                    if (result == ReceiveBlockResult.INVALID_BLOCK or result
                            == ReceiveBlockResult.DISCONNECTED_BLOCK):
                        if error_code is not None:
                            raise ConsensusError(error_code, block.header_hash)
                        raise RuntimeError(
                            f"Invalid block {block.header_hash}")
                assert (max([
                    h.height for h in self.blockchain.get_current_tips()
                ]) >= block.height)
                del self.sync_store.potential_blocks[block.height]

            log.info(
                f"Took {time.time() - validation_start_time} seconds to validate and add blocks "
                f"{batch_start_height} to {batch_end_height + 1}.")