async def receive_block(self, block: FullBlock, pre_validation_result: Optional[PreValidationResult]=None, fork_point_with_peak: Optional[uint32]=None) -> Tuple[(ReceiveBlockResult, Optional[Err], Optional[uint32], Tuple[(List[CoinRecord], Dict[(bytes, Dict[(bytes32, CoinRecord)])])])]:
    "\n        This method must be called under the blockchain lock\n        Adds a new block into the blockchain, if it's valid and connected to the current\n        blockchain, regardless of whether it is the child of a head, or another block.\n        Returns a header if block is added to head. Returns an error if the block is\n        invalid. Also returns the fork height, in the case of a new peak.\n        "
    genesis: bool = (block.height == 0)
    if self.contains_block(block.header_hash):
        return (ReceiveBlockResult.ALREADY_HAVE_BLOCK, None, None, ([], {}))
    if ((not self.contains_block(block.prev_header_hash)) and (not genesis)):
        return (ReceiveBlockResult.DISCONNECTED_BLOCK, Err.INVALID_PREV_BLOCK_HASH, None, ([], {}))
    if ((not genesis) and ((self.block_record(block.prev_header_hash).height + 1) != block.height)):
        return (ReceiveBlockResult.INVALID_BLOCK, Err.INVALID_HEIGHT, None, ([], {}))
    npc_result: Optional[NPCResult] = None
    if (pre_validation_result is None):
        if (block.height == 0):
            prev_b: Optional[BlockRecord] = None
        else:
            prev_b = self.block_record(block.prev_header_hash)
        (sub_slot_iters, difficulty) = get_next_sub_slot_iters_and_difficulty(self.constants, (len(block.finished_sub_slots) > 0), prev_b, self)
        if block.is_transaction_block():
            if (block.transactions_generator is not None):
                try:
                    block_generator: Optional[BlockGenerator] = (await self.get_block_generator(block))
                except ValueError:
                    return (ReceiveBlockResult.INVALID_BLOCK, Err.GENERATOR_REF_HAS_NO_GENERATOR, None, ([], {}))
                assert ((block_generator is not None) and (block.transactions_info is not None))
                npc_result = get_name_puzzle_conditions(block_generator, min(self.constants.MAX_BLOCK_COST_CLVM, block.transactions_info.cost), cost_per_byte=self.constants.COST_PER_BYTE, safe_mode=False)
                (removals, tx_additions) = tx_removals_and_additions(npc_result.npc_list)
            else:
                (removals, tx_additions) = ([], [])
            header_block = get_block_header(block, tx_additions, removals)
        else:
            npc_result = None
            header_block = get_block_header(block, [], [])
        (required_iters, error) = validate_finished_header_block(self.constants, self, header_block, False, difficulty, sub_slot_iters)
        if (error is not None):
            return (ReceiveBlockResult.INVALID_BLOCK, error.code, None, ([], {}))
    else:
        npc_result = pre_validation_result.npc_result
        required_iters = pre_validation_result.required_iters
        assert (pre_validation_result.error is None)
    assert (required_iters is not None)
    (error_code, _) = (await validate_block_body(self.constants, self, self.block_store, self.coin_store, self.get_peak(), block, block.height, npc_result, fork_point_with_peak, self.get_block_generator))
    if (error_code is not None):
        return (ReceiveBlockResult.INVALID_BLOCK, error_code, None, ([], {}))
    block_record = block_to_block_record(self.constants, self, required_iters, block, None)
    async with self.block_store.db_wrapper.lock:
        try:
            header_hash: bytes32 = block.header_hash
            (await self.block_store.db_wrapper.begin_transaction())
            (await self.block_store.add_full_block(header_hash, block, block_record))
            (fork_height, peak_height, records, (coin_record_change, hint_changes)) = (await self._reconsider_peak(block_record, genesis, fork_point_with_peak, npc_result))
            (await self.block_store.db_wrapper.commit_transaction())
            self.add_block_record(block_record)
            for fetched_block_record in records:
                self.__height_to_hash[fetched_block_record.height] = fetched_block_record.header_hash
                if (fetched_block_record.sub_epoch_summary_included is not None):
                    self.__sub_epoch_summaries[fetched_block_record.height] = fetched_block_record.sub_epoch_summary_included
            if (peak_height is not None):
                self._peak_height = peak_height
        except BaseException:
            self.block_store.rollback_cache_block(header_hash)
            (await self.block_store.db_wrapper.rollback_transaction())
            raise
    if (fork_height is not None):
        assert (coin_record_change is not None)
        return (ReceiveBlockResult.NEW_PEAK, None, fork_height, (coin_record_change, hint_changes))
    else:
        return (ReceiveBlockResult.ADDED_AS_ORPHAN, None, None, ([], {}))
This method must be called under the blockchain lock
Adds a new block into the blockchain, if it's valid and connected to the current
blockchain, regardless of whether it is the child of a head, or another block.
Returns a header if block is added to head. Returns an error if the block is
invalid. Also returns the fork height, in the case of a new peak.
async def receive_block(self, block: FullBlock, pre_validation_result: Optional[PreValidationResult]=None, fork_point_with_peak: Optional[uint32]=None) -> Tuple[(ReceiveBlockResult, Optional[Err], Optional[uint32], Tuple[(List[CoinRecord], Dict[(bytes, Dict[(bytes32, CoinRecord)])])])]:
    "\n        This method must be called under the blockchain lock\n        Adds a new block into the blockchain, if it's valid and connected to the current\n        blockchain, regardless of whether it is the child of a head, or another block.\n        Returns a header if block is added to head. Returns an error if the block is\n        invalid. Also returns the fork height, in the case of a new peak.\n        "
    genesis: bool = (block.height == 0)
    if self.contains_block(block.header_hash):
        return (ReceiveBlockResult.ALREADY_HAVE_BLOCK, None, None, ([], {}))
    if ((not self.contains_block(block.prev_header_hash)) and (not genesis)):
        return (ReceiveBlockResult.DISCONNECTED_BLOCK, Err.INVALID_PREV_BLOCK_HASH, None, ([], {}))
    if ((not genesis) and ((self.block_record(block.prev_header_hash).height + 1) != block.height)):
        return (ReceiveBlockResult.INVALID_BLOCK, Err.INVALID_HEIGHT, None, ([], {}))
    npc_result: Optional[NPCResult] = None
    if (pre_validation_result is None):
        if (block.height == 0):
            prev_b: Optional[BlockRecord] = None
        else:
            prev_b = self.block_record(block.prev_header_hash)
        (sub_slot_iters, difficulty) = get_next_sub_slot_iters_and_difficulty(self.constants, (len(block.finished_sub_slots) > 0), prev_b, self)
        if block.is_transaction_block():
            if (block.transactions_generator is not None):
                try:
                    block_generator: Optional[BlockGenerator] = (await self.get_block_generator(block))
                except ValueError:
                    return (ReceiveBlockResult.INVALID_BLOCK, Err.GENERATOR_REF_HAS_NO_GENERATOR, None, ([], {}))
                assert ((block_generator is not None) and (block.transactions_info is not None))
                npc_result = get_name_puzzle_conditions(block_generator, min(self.constants.MAX_BLOCK_COST_CLVM, block.transactions_info.cost), cost_per_byte=self.constants.COST_PER_BYTE, safe_mode=False)
                (removals, tx_additions) = tx_removals_and_additions(npc_result.npc_list)
            else:
                (removals, tx_additions) = ([], [])
            header_block = get_block_header(block, tx_additions, removals)
        else:
            npc_result = None
            header_block = get_block_header(block, [], [])
        (required_iters, error) = validate_finished_header_block(self.constants, self, header_block, False, difficulty, sub_slot_iters)
        if (error is not None):
            return (ReceiveBlockResult.INVALID_BLOCK, error.code, None, ([], {}))
    else:
        npc_result = pre_validation_result.npc_result
        required_iters = pre_validation_result.required_iters
        assert (pre_validation_result.error is None)
    assert (required_iters is not None)
    (error_code, _) = (await validate_block_body(self.constants, self, self.block_store, self.coin_store, self.get_peak(), block, block.height, npc_result, fork_point_with_peak, self.get_block_generator))
    if (error_code is not None):
        return (ReceiveBlockResult.INVALID_BLOCK, error_code, None, ([], {}))
    block_record = block_to_block_record(self.constants, self, required_iters, block, None)
    async with self.block_store.db_wrapper.lock:
        try:
            header_hash: bytes32 = block.header_hash
            (await self.block_store.db_wrapper.begin_transaction())
            (await self.block_store.add_full_block(header_hash, block, block_record))
            (fork_height, peak_height, records, (coin_record_change, hint_changes)) = (await self._reconsider_peak(block_record, genesis, fork_point_with_peak, npc_result))
            (await self.block_store.db_wrapper.commit_transaction())
            self.add_block_record(block_record)
            for fetched_block_record in records:
                self.__height_to_hash[fetched_block_record.height] = fetched_block_record.header_hash
                if (fetched_block_record.sub_epoch_summary_included is not None):
                    self.__sub_epoch_summaries[fetched_block_record.height] = fetched_block_record.sub_epoch_summary_included
            if (peak_height is not None):
                self._peak_height = peak_height
        except BaseException:
            self.block_store.rollback_cache_block(header_hash)
            (await self.block_store.db_wrapper.rollback_transaction())
            raise
    if (fork_height is not None):
        assert (coin_record_change is not None)
        return (ReceiveBlockResult.NEW_PEAK, None, fork_height, (coin_record_change, hint_changes))
    else:
        return (ReceiveBlockResult.ADDED_AS_ORPHAN, None, None, ([], {}))