def batch_pre_validate_blocks( constants_dict: Dict, blocks_pickled: Dict[bytes, bytes], header_blocks_pickled: List[bytes], transaction_generators: List[Optional[bytes]], check_filter: bool, expected_difficulty: List[uint64], expected_sub_slot_iters: List[uint64], validate_transactions: bool, ) -> List[bytes]: assert len(header_blocks_pickled) == len(transaction_generators) blocks = {} for k, v in blocks_pickled.items(): blocks[k] = BlockRecord.from_bytes(v) results: List[PreValidationResult] = [] constants: ConsensusConstants = dataclass_from_dict( ConsensusConstants, constants_dict) for i in range(len(header_blocks_pickled)): try: header_block: HeaderBlock = HeaderBlock.from_bytes( header_blocks_pickled[i]) generator: Optional[bytes] = transaction_generators[i] required_iters, error = validate_finished_header_block( constants, BlockCache(blocks), header_block, check_filter, expected_difficulty[i], expected_sub_slot_iters[i], ) cost_result: Optional[CostResult] = None error_int: Optional[uint16] = None if error is not None: error_int = uint16(error.code.value) if constants_dict["NETWORK_TYPE"] == NetworkType.MAINNET.value: cost_result = None else: if not error and generator is not None and validate_transactions: cost_result = calculate_cost_of_program( SerializedProgram.from_bytes(generator), constants.CLVM_COST_RATIO_CONSTANT) results.append( PreValidationResult(error_int, required_iters, cost_result)) except Exception: error_stack = traceback.format_exc() log.error(f"Exception: {error_stack}") results.append( PreValidationResult(uint16(Err.UNKNOWN.value), None, None)) return [bytes(r) for r in results]
async def receive_block( self, 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 = block_record.header genesis: bool = block.height == 0 if self.contains_sub_block(block.header_hash): return ReceiveBlockResult.ALREADY_HAVE_BLOCK, None, None if not self.contains_sub_block(block.prev_header_hash) and not genesis: return ( ReceiveBlockResult.DISCONNECTED_BLOCK, Err.INVALID_PREV_BLOCK_HASH, None, ) if block.height == 0: prev_sb: Optional[SubBlockRecord] = None else: prev_sb = self.sub_block_record(block.prev_header_hash) sub_slot_iters, difficulty = get_sub_slot_iters_and_difficulty( self.constants, block, prev_sb, 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_sub_block.get_unfinished(), block.challenge_chain_sp_proof, block.reward_chain_sp_proof, block.foliage_sub_block, block.foliage_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 sub_block = block_to_sub_block_record( self.constants, self, required_iters, None, block, ) # Always add the block to the database await self.block_store.add_block_record(block_record, sub_block) self.add_sub_block(sub_block) self.clean_sub_block_record(sub_block.height - self.constants.SUB_BLOCKS_CACHE_SIZE) fork_height: Optional[uint32] = await self._reconsider_peak( sub_block, genesis, fork_point_with_peak) if fork_height is not None: self.log.info( f"💰 Updated wallet peak to sub height {sub_block.height}, weight {sub_block.weight}, " ) return ReceiveBlockResult.NEW_PEAK, None, fork_height else: return ReceiveBlockResult.ADDED_AS_ORPHAN, None, None
async def receive_block( self, block: FullBlock, pre_validation_result: Optional[PreValidationResult] = None, ) -> Tuple[ReceiveBlockResult, Optional[Err], Optional[uint32]]: """ 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. """ genesis: bool = block.sub_block_height == 0 if block.header_hash in self.sub_blocks: return ReceiveBlockResult.ALREADY_HAVE_BLOCK, None, None if block.prev_header_hash not in self.sub_blocks and not genesis: return ( ReceiveBlockResult.DISCONNECTED_BLOCK, Err.INVALID_PREV_BLOCK_HASH, None, ) if pre_validation_result is None: if block.sub_block_height == 0: prev_sb: Optional[SubBlockRecord] = None else: prev_sb = self.sub_blocks[block.prev_header_hash] sub_slot_iters, difficulty = get_sub_slot_iters_and_difficulty( self.constants, block, self.sub_height_to_hash, prev_sb, self.sub_blocks) required_iters, error = validate_finished_header_block( self.constants, self.sub_blocks, block.get_block_header(), False, difficulty, sub_slot_iters, ) if error is not None: return ReceiveBlockResult.INVALID_BLOCK, error.code, None else: 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.sub_blocks, self.sub_height_to_hash, self.block_store, self.coin_store, self.get_peak(), block, block.sub_block_height, block.height if block.is_block() else None, pre_validation_result.cost_result if pre_validation_result is not None else None, ) if error_code is not None: return ReceiveBlockResult.INVALID_BLOCK, error_code, None sub_block = block_to_sub_block_record( self.constants, self.sub_blocks, self.sub_height_to_hash, required_iters, block, None, ) # Always add the block to the database await self.block_store.add_full_block(block, sub_block) self.sub_blocks[sub_block.header_hash] = sub_block fork_height: Optional[uint32] = await self._reconsider_peak( sub_block, genesis) if fork_height is not None: return ReceiveBlockResult.NEW_PEAK, None, fork_height else: return ReceiveBlockResult.ADDED_AS_ORPHAN, None, None