async def validate_unfinished_block( self, block: UnfinishedBlock, skip_overflow_ss_validation=True) -> PreValidationResult: if (not self.contains_block(block.prev_header_hash) and not block.prev_header_hash == self.constants.GENESIS_CHALLENGE): return PreValidationResult( uint16(Err.INVALID_PREV_BLOCK_HASH.value), None, None) unfinished_header_block = UnfinishedHeaderBlock( block.finished_sub_slots, block.reward_chain_block, block.challenge_chain_sp_proof, block.reward_chain_sp_proof, block.foliage, block.foliage_transaction_block, b"", ) prev_b = self.try_block_record( unfinished_header_block.prev_header_hash) sub_slot_iters, difficulty = get_next_sub_slot_iters_and_difficulty( self.constants, len(unfinished_header_block.finished_sub_slots) > 0, prev_b, self) required_iters, error = validate_unfinished_header_block( self.constants, self, unfinished_header_block, False, difficulty, sub_slot_iters, skip_overflow_ss_validation, ) if error is not None: return PreValidationResult(uint16(error.code.value), None, None) prev_height = (-1 if block.prev_header_hash == self.constants.GENESIS_CHALLENGE else self.block_record(block.prev_header_hash).height) error_code, cost_result = await validate_block_body( self.constants, self, self.block_store, self.coin_store, self.get_peak(), block, uint32(prev_height + 1), None, ) if error_code is not None: return PreValidationResult(uint16(error_code.value), None, None) return PreValidationResult(None, required_iters, cost_result)
async def validate_unfinished_block( self, block: UnfinishedBlock, skip_overflow_ss_validation=True ) -> Tuple[Optional[uint64], Optional[Err]]: if ( not self.contains_sub_block(block.prev_header_hash) and not block.prev_header_hash == self.constants.GENESIS_CHALLENGE ): return None, Err.INVALID_PREV_BLOCK_HASH unfinished_header_block = UnfinishedHeaderBlock( block.finished_sub_slots, block.reward_chain_sub_block, block.challenge_chain_sp_proof, block.reward_chain_sp_proof, block.foliage_sub_block, block.foliage_block, b"", ) prev_sb = self.try_sub_block(unfinished_header_block.prev_header_hash) sub_slot_iters, difficulty = get_sub_slot_iters_and_difficulty( self.constants, unfinished_header_block, prev_sb, self ) required_iters, error = validate_unfinished_header_block( self.constants, self, unfinished_header_block, False, difficulty, sub_slot_iters, skip_overflow_ss_validation, ) if error is not None: return None, error.code prev_height = ( -1 if block.prev_header_hash == self.constants.GENESIS_CHALLENGE else self.sub_block_record(block.prev_header_hash).height ) error_code = await validate_block_body( self.constants, self, self.block_store, self.coin_store, self.get_peak(), block, uint32(prev_height + 1), None, ) if error_code is not None: return None, error_code return required_iters, None
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 validate_unfinished_block( self, block: UnfinishedBlock, skip_overflow_ss_validation=True ) -> Tuple[Optional[uint64], Optional[Err]]: if (block.prev_header_hash not in self.sub_blocks and not block.prev_header_hash == self.constants.GENESIS_PREV_HASH): return None, Err.INVALID_PREV_BLOCK_HASH unfinished_header_block = UnfinishedHeaderBlock( block.finished_sub_slots, block.reward_chain_sub_block, block.challenge_chain_sp_proof, block.reward_chain_sp_proof, block.foliage_sub_block, block.foliage_block, b"", ) prev_sb = self.sub_blocks.get(unfinished_header_block.prev_header_hash, None) sub_slot_iters, difficulty = get_sub_slot_iters_and_difficulty( self.constants, unfinished_header_block, self.sub_height_to_hash, prev_sb, self.sub_blocks) required_iters, error = validate_unfinished_header_block( self.constants, self.sub_blocks, unfinished_header_block, False, difficulty, sub_slot_iters, skip_overflow_ss_validation, ) if error is not None: return None, error.code prev_sub_height = ( -1 if block.prev_header_hash == self.constants.GENESIS_PREV_HASH else self.sub_blocks[block.prev_header_hash].sub_block_height) if block.is_block(): assert block.foliage_block is not None height: Optional[uint32] = block.foliage_block.height else: height = 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, uint32(prev_sub_height + 1), height, ) if error_code is not None: return None, error_code return required_iters, None