async def fetch_blocks_and_validate( self, peer: WSChiaConnection, height_start: uint32, height_end: uint32, fork_point_with_peak: Optional[uint32], ) -> Tuple[bool, bool]: """ Returns whether the blocks validated, and whether the peak was advanced """ if self.wallet_state_manager is None: return False, False self.log.info(f"Requesting blocks {height_start}-{height_end}") request = RequestHeaderBlocks(uint32(height_start), uint32(height_end)) res: Optional[RespondHeaderBlocks] = await peer.request_header_blocks( request) if res is None or not isinstance(res, RespondHeaderBlocks): raise ValueError("Peer returned no response") header_blocks: List[HeaderBlock] = res.header_blocks advanced_peak = False if header_blocks is None: raise ValueError(f"No response from peer {peer}") if (self.full_node_peer is not None and peer.peer_host == self.full_node_peer.host or peer.peer_host == "127.0.0.1"): trusted = True pre_validation_results: Optional[List[PreValidationResult]] = None else: trusted = False pre_validation_results = await self.wallet_state_manager.blockchain.pre_validate_blocks_multiprocessing( header_blocks) if pre_validation_results is None: return False, advanced_peak assert len(header_blocks) == len(pre_validation_results) for i in range(len(header_blocks)): header_block = header_blocks[i] if not trusted and pre_validation_results is not None and pre_validation_results[ i].error is not None: raise ValidationError(Err(pre_validation_results[i].error)) fork_point_with_old_peak = None if advanced_peak else fork_point_with_peak if header_block.is_transaction_block: # Find additions and removals ( additions, removals, ) = await self.wallet_state_manager.get_filter_additions_removals( header_block, header_block.transactions_filter, fork_point_with_old_peak) # Get Additions added_coins = await self.get_additions(peer, header_block, additions) if added_coins is None: raise ValueError("Failed to fetch additions") # Get removals removed_coins = await self.get_removals( peer, header_block, added_coins, removals) if removed_coins is None: raise ValueError("Failed to fetch removals") header_block_record = HeaderBlockRecord( header_block, added_coins, removed_coins) else: header_block_record = HeaderBlockRecord(header_block, [], []) start_t = time.time() if trusted: ( result, error, fork_h, ) = await self.wallet_state_manager.blockchain.receive_block( header_block_record, None, trusted, fork_point_with_old_peak) else: assert pre_validation_results is not None ( result, error, fork_h, ) = await self.wallet_state_manager.blockchain.receive_block( header_block_record, pre_validation_results[i], trusted, fork_point_with_old_peak) self.log.debug( f"Time taken to validate {header_block.height} with fork " f"{fork_point_with_old_peak}: {time.time() - start_t}") if result == ReceiveBlockResult.NEW_PEAK: advanced_peak = True self.wallet_state_manager.state_changed("new_block") elif result == ReceiveBlockResult.INVALID_BLOCK: raise ValueError("Value error peer sent us invalid block") if advanced_peak: await self.wallet_state_manager.create_more_puzzle_hashes() return True, advanced_peak
async def receive_block( self, header_block_record: HeaderBlockRecord, pre_validation_result: Optional[PreValidationResult] = None, trusted: bool = False, fork_point_with_peak: Optional[uint32] = None, ) -> Tuple[ReceiveBlockResult, Optional[Err], Optional[uint32]]: """ 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. """ block = header_block_record.header 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 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 trusted is False and pre_validation_result is None: required_iters, error = validate_finished_header_block( self.constants, self, block, False, difficulty, sub_slot_iters) elif trusted: unfinished_header_block = UnfinishedHeaderBlock( block.finished_sub_slots, block.reward_chain_block.get_unfinished(), block.challenge_chain_sp_proof, block.reward_chain_sp_proof, block.foliage, block.foliage_transaction_block, block.transactions_filter, ) required_iters, val_error = validate_unfinished_header_block( self.constants, self, unfinished_header_block, False, difficulty, sub_slot_iters, False, True) error = ValidationError( Err(val_error)) if val_error is not None else None else: assert pre_validation_result is not None required_iters = pre_validation_result.required_iters error = (ValidationError(Err(pre_validation_result.error)) if pre_validation_result.error is not None else None) if error is not None: return ReceiveBlockResult.INVALID_BLOCK, error.code, None assert required_iters is not None block_record = block_to_block_record( self.constants, self, required_iters, None, block, ) # Always add the block to the database async with self.wallet_state_manager_lock: async with self.block_store.db_wrapper.lock: try: await self.block_store.db_wrapper.begin_transaction() await self.block_store.add_block_record( header_block_record, block_record) self.add_block_record(block_record) self.clean_block_record(block_record.height - self.constants.BLOCKS_CACHE_SIZE) fork_height: Optional[ uint32] = await self._reconsider_peak( block_record, genesis, fork_point_with_peak) await self.block_store.db_wrapper.commit_transaction() except BaseException as e: self.log.error(f"Error during db transaction: {e}") if self.block_store.db_wrapper.db._connection is not None: await self.block_store.db_wrapper.rollback_transaction( ) await self.coin_store.rebuild_wallet_cache() await self.tx_store.rebuild_tx_cache() raise if fork_height is not None: self.log.info( f"💰 Updated wallet peak to height {block_record.height}, weight {block_record.weight}, " ) return ReceiveBlockResult.NEW_PEAK, None, fork_height else: return ReceiveBlockResult.ADDED_AS_ORPHAN, None, None