async def test_invalid_max_height(self, initial_blockchain): blocks, b = initial_blockchain print(blocks[9].header) pool_target = PoolTarget( blocks[9].header.data.pool_target.puzzle_hash, uint32(8) ) agg_sig = bt.get_pool_key_signature( pool_target, blocks[9].proof_of_space.pool_public_key ) assert agg_sig is not None new_header_data = HeaderData( blocks[9].header.data.height, blocks[9].header.data.prev_header_hash, blocks[9].header.data.timestamp, blocks[9].header.data.filter_hash, blocks[9].header.data.proof_of_space_hash, blocks[9].header.data.weight, blocks[9].header.data.total_iters, blocks[9].header.data.additions_root, blocks[9].header.data.removals_root, blocks[9].header.data.farmer_rewards_puzzle_hash, blocks[9].header.data.total_transaction_fees, pool_target, agg_sig, blocks[9].header.data.cost, blocks[9].header.data.extension_data, blocks[9].header.data.generator_hash, ) block_bad = FullBlock( blocks[9].proof_of_space, blocks[9].proof_of_time, Header( new_header_data, bt.get_plot_signature( new_header_data, blocks[9].proof_of_space.plot_public_key ), ), blocks[9].transactions_generator, blocks[9].transactions_filter, ) result, removed, error_code = await b.receive_block(block_bad) assert result == ReceiveBlockResult.INVALID_BLOCK assert error_code == Err.INVALID_POOL_TARGET
def _create_block( self, test_constants: ConsensusConstants, challenge_hash: bytes32, height: uint32, prev_header_hash: bytes32, prev_iters: uint64, prev_weight: uint128, timestamp: uint64, difficulty: int, min_iters: int, seed: bytes, genesis: bool = False, reward_puzzlehash: bytes32 = None, transactions: Program = None, aggsig: G2Element = None, fees: uint64 = uint64(0), ) -> FullBlock: """ Creates a block with the specified details. Uses the stored plots to create a proof of space, and also evaluates the VDF for the proof of time. """ selected_plot_info = None selected_proof_index = 0 selected_quality: Optional[bytes] = None best_quality = 0 plots = [ pinfo for _, pinfo in sorted(list(self.plots.items()), key=lambda x: str(x[0])) ] if self.use_any_pos: random.seed(seed) for i in range(len(plots) * 3): # Allow passing in seed, to create reorgs and different chains seeded_pn = random.randint(0, len(plots) - 1) plot_info = plots[seeded_pn] plot_id = plot_info.prover.get_id() ccp = ProofOfSpace.can_create_proof( plot_id, challenge_hash, test_constants.NUMBER_ZERO_BITS_CHALLENGE_SIG, ) if not ccp: continue qualities = plot_info.prover.get_qualities_for_challenge( challenge_hash) if len(qualities) > 0: selected_plot_info = plot_info selected_quality = qualities[0] break else: for i in range(len(plots)): plot_info = plots[i] j = 0 plot_id = plot_info.prover.get_id() ccp = ProofOfSpace.can_create_proof( plot_id, challenge_hash, test_constants.NUMBER_ZERO_BITS_CHALLENGE_SIG, ) if not ccp: continue qualities = plot_info.prover.get_qualities_for_challenge( challenge_hash) for quality in qualities: qual_int = int.from_bytes(quality, "big", signed=False) if qual_int > best_quality: best_quality = qual_int selected_quality = quality selected_plot_info = plot_info selected_proof_index = j j += 1 assert selected_plot_info is not None if selected_quality is None: raise RuntimeError("No proofs for this challenge") proof_xs: bytes = selected_plot_info.prover.get_full_proof( challenge_hash, selected_proof_index) plot_pk = ProofOfSpace.generate_plot_public_key( selected_plot_info.local_sk.get_g1(), selected_plot_info.farmer_public_key, ) proof_of_space: ProofOfSpace = ProofOfSpace( challenge_hash, selected_plot_info.pool_public_key, plot_pk, selected_plot_info.prover.get_size(), proof_xs, ) number_iters: uint64 = pot_iterations.calculate_iterations( proof_of_space, difficulty, min_iters, test_constants.NUMBER_ZERO_BITS_CHALLENGE_SIG, ) if self.real_plots: print(f"Performing {number_iters} VDF iterations") int_size = (test_constants.DISCRIMINANT_SIZE_BITS + 16) >> 4 result = prove(challenge_hash, test_constants.DISCRIMINANT_SIZE_BITS, number_iters) output = ClassgroupElement( int512(int.from_bytes( result[0:int_size], "big", signed=True, )), int512( int.from_bytes( result[int_size:2 * int_size], "big", signed=True, )), ) proof_bytes = result[2 * int_size:4 * int_size] proof_of_time = ProofOfTime( challenge_hash, number_iters, output, uint8(0), proof_bytes, ) # Use the extension data to create different blocks based on header hash extension_data: bytes32 = bytes32( [random.randint(0, 255) for _ in range(32)]) cost = uint64(0) fee_reward = uint64(block_rewards.calculate_base_fee(height) + fees) std_hash(std_hash(height)) # Create filter byte_array_tx: List[bytes32] = [] tx_additions: List[Coin] = [] tx_removals: List[bytes32] = [] if transactions: error, npc_list, _ = get_name_puzzle_conditions(transactions) additions: List[Coin] = additions_for_npc(npc_list) for coin in additions: tx_additions.append(coin) byte_array_tx.append(bytearray(coin.puzzle_hash)) for npc in npc_list: tx_removals.append(npc.coin_name) byte_array_tx.append(bytearray(npc.coin_name)) farmer_ph = self.farmer_ph pool_ph = self.pool_ph if reward_puzzlehash is not None: farmer_ph = reward_puzzlehash pool_ph = reward_puzzlehash byte_array_tx.append(bytearray(farmer_ph)) byte_array_tx.append(bytearray(pool_ph)) bip158: PyBIP158 = PyBIP158(byte_array_tx) encoded = bytes(bip158.GetEncoded()) removal_merkle_set = MerkleSet() addition_merkle_set = MerkleSet() # Create removal Merkle set for coin_name in tx_removals: removal_merkle_set.add_already_hashed(coin_name) # Create addition Merkle set puzzlehash_coin_map: Dict[bytes32, List[Coin]] = {} cb_reward = calculate_block_reward(height) cb_coin = create_coinbase_coin(height, pool_ph, cb_reward) fees_coin = create_fees_coin(height, farmer_ph, fee_reward) for coin in tx_additions + [cb_coin, fees_coin]: if coin.puzzle_hash in puzzlehash_coin_map: puzzlehash_coin_map[coin.puzzle_hash].append(coin) else: puzzlehash_coin_map[coin.puzzle_hash] = [coin] # Addition Merkle set contains puzzlehash and hash of all coins with that puzzlehash for puzzle, coins in puzzlehash_coin_map.items(): addition_merkle_set.add_already_hashed(puzzle) addition_merkle_set.add_already_hashed(hash_coin_list(coins)) additions_root = addition_merkle_set.get_root() removal_root = removal_merkle_set.get_root() generator_hash = (transactions.get_tree_hash() if transactions is not None else bytes32([0] * 32)) filter_hash = std_hash(encoded) pool_target = PoolTarget(pool_ph, uint32(height)) pool_target_signature = self.get_pool_key_signature( pool_target, proof_of_space.pool_public_key) assert pool_target_signature is not None final_aggsig: G2Element = pool_target_signature if aggsig is not None: final_aggsig = AugSchemeMPL.aggregate([final_aggsig, aggsig]) header_data: HeaderData = HeaderData( height, prev_header_hash, timestamp, filter_hash, proof_of_space.get_hash(), uint128(prev_weight + difficulty), uint64(prev_iters + number_iters), additions_root, removal_root, farmer_ph, fee_reward, pool_target, final_aggsig, cost, extension_data, generator_hash, ) header_hash_sig: G2Element = self.get_plot_signature( header_data, plot_pk) header: Header = Header(header_data, header_hash_sig) full_block: FullBlock = FullBlock(proof_of_space, proof_of_time, header, transactions, encoded) return full_block
def create_genesis_block( self, constants: ConsensusConstants, seed: bytes32 = b"", timestamp: Optional[uint64] = None, farmer_reward_puzzle_hash: Optional[bytes32] = None, force_overflow: bool = False, skip_slots: int = 0, ) -> FullBlock: if timestamp is None: timestamp = uint64(int(time.time())) if farmer_reward_puzzle_hash is None: farmer_reward_puzzle_hash = self.farmer_ph finished_sub_slots: List[EndOfSubSlotBundle] = [] unfinished_block: Optional[UnfinishedBlock] = None ip_iters: uint64 = uint64(0) sub_slot_total_iters: uint128 = uint128(0) # Keep trying until we get a good proof of space that also passes sp filter while True: cc_challenge, rc_challenge = get_challenges(constants, {}, finished_sub_slots, None) for signage_point_index in range(0, constants.NUM_SPS_SUB_SLOT): signage_point: SignagePoint = get_signage_point( constants, {}, None, sub_slot_total_iters, uint8(signage_point_index), finished_sub_slots, constants.SUB_SLOT_ITERS_STARTING, ) if signage_point_index == 0: cc_sp_output_hash: bytes32 = cc_challenge else: assert signage_point is not None assert signage_point.cc_vdf is not None cc_sp_output_hash = signage_point.cc_vdf.output.get_hash() # If did not reach the target slots to skip, don't make any proofs for this sub-slot qualified_proofs: List[Tuple[uint64, ProofOfSpace]] = self.get_pospaces_for_challenge( constants, cc_challenge, cc_sp_output_hash, seed, constants.DIFFICULTY_STARTING, constants.SUB_SLOT_ITERS_STARTING, ) # Try each of the proofs of space for required_iters, proof_of_space in qualified_proofs: sp_iters: uint64 = calculate_sp_iters( constants, uint64(constants.SUB_SLOT_ITERS_STARTING), uint8(signage_point_index), ) ip_iters = calculate_ip_iters( constants, uint64(constants.SUB_SLOT_ITERS_STARTING), uint8(signage_point_index), required_iters, ) is_overflow_block = is_overflow_sub_block(constants, uint8(signage_point_index)) if force_overflow and not is_overflow_block: continue if len(finished_sub_slots) < skip_slots: continue unfinished_block = create_unfinished_block( constants, sub_slot_total_iters, constants.SUB_SLOT_ITERS_STARTING, uint8(signage_point_index), sp_iters, ip_iters, proof_of_space, cc_challenge, farmer_reward_puzzle_hash, PoolTarget(constants.GENESIS_PRE_FARM_POOL_PUZZLE_HASH, uint32(0)), self.get_plot_signature, self.get_pool_key_signature, signage_point, timestamp, seed, finished_sub_slots_input=finished_sub_slots, ) assert unfinished_block is not None if not is_overflow_block: cc_ip_vdf, cc_ip_proof = get_vdf_info_and_proof( constants, ClassgroupElement.get_default_element(), cc_challenge, ip_iters, ) cc_ip_vdf = replace(cc_ip_vdf, number_of_iterations=ip_iters) rc_ip_vdf, rc_ip_proof = get_vdf_info_and_proof( constants, ClassgroupElement.get_default_element(), rc_challenge, ip_iters, ) assert unfinished_block is not None total_iters_sp = uint128(sub_slot_total_iters + sp_iters) return unfinished_block_to_full_block( unfinished_block, cc_ip_vdf, cc_ip_proof, rc_ip_vdf, rc_ip_proof, None, None, finished_sub_slots, None, {}, total_iters_sp, constants.DIFFICULTY_STARTING, ) if signage_point_index == constants.NUM_SPS_SUB_SLOT - constants.NUM_SP_INTERVALS_EXTRA - 1: # Finish the end of sub-slot and try again next sub-slot cc_vdf, cc_proof = get_vdf_info_and_proof( constants, ClassgroupElement.get_default_element(), cc_challenge, constants.SUB_SLOT_ITERS_STARTING, ) rc_vdf, rc_proof = get_vdf_info_and_proof( constants, ClassgroupElement.get_default_element(), rc_challenge, constants.SUB_SLOT_ITERS_STARTING, ) cc_slot = ChallengeChainSubSlot(cc_vdf, None, None, None, None) finished_sub_slots.append( EndOfSubSlotBundle( cc_slot, None, RewardChainSubSlot( rc_vdf, cc_slot.get_hash(), None, uint8(constants.MIN_SUB_BLOCKS_PER_CHALLENGE_BLOCK), ), SubSlotProofs(cc_proof, None, rc_proof), ) ) if unfinished_block is not None: cc_ip_vdf, cc_ip_proof = get_vdf_info_and_proof( constants, ClassgroupElement.get_default_element(), finished_sub_slots[-1].challenge_chain.get_hash(), ip_iters, ) rc_ip_vdf, rc_ip_proof = get_vdf_info_and_proof( constants, ClassgroupElement.get_default_element(), finished_sub_slots[-1].reward_chain.get_hash(), ip_iters, ) total_iters_sp = uint128( sub_slot_total_iters + calculate_sp_iters( self.constants, self.constants.SUB_SLOT_ITERS_STARTING, unfinished_block.reward_chain_sub_block.signage_point_index, ) ) return unfinished_block_to_full_block( unfinished_block, cc_ip_vdf, cc_ip_proof, rc_ip_vdf, rc_ip_proof, None, None, finished_sub_slots, None, {}, total_iters_sp, constants.DIFFICULTY_STARTING, ) sub_slot_total_iters = uint128(sub_slot_total_iters + constants.SUB_SLOT_ITERS_STARTING)
def get_consecutive_blocks( self, num_blocks: int, block_list_input: List[FullBlock] = None, farmer_reward_puzzle_hash: Optional[bytes32] = None, pool_reward_puzzle_hash: Optional[bytes32] = None, transaction_data: Optional[SpendBundle] = None, seed: bytes = b"", time_per_sub_block: Optional[float] = None, force_overflow: bool = False, skip_slots: int = 0, # Force at least this number of empty slots before the first SB guarantee_block: bool = False, # Force that this sub-block must be a block ) -> List[FullBlock]: assert num_blocks > 0 if block_list_input is not None: block_list = block_list_input.copy() else: block_list = [] constants = self.constants transaction_data_included = False if time_per_sub_block is None: time_per_sub_block = float(constants.SUB_SLOT_TIME_TARGET) / float(constants.SLOT_SUB_BLOCKS_TARGET) if farmer_reward_puzzle_hash is None: farmer_reward_puzzle_hash = self.farmer_ph if pool_reward_puzzle_hash is None: pool_reward_puzzle_hash = self.pool_ph pool_target = PoolTarget(pool_reward_puzzle_hash, uint32(0)) if len(block_list) == 0: initial_block_list_len = 0 genesis = self.create_genesis_block( constants, seed, force_overflow=force_overflow, skip_slots=skip_slots, timestamp=uint64(int(time.time())), farmer_reward_puzzle_hash=farmer_reward_puzzle_hash, ) log.info(f"Created block 0 iters: {genesis.total_iters}") num_empty_slots_added = skip_slots block_list = [genesis] num_blocks -= 1 else: initial_block_list_len = len(block_list) num_empty_slots_added = uint32(0) # Allows forcing empty slots in the beginning, for testing purposes if num_blocks == 0: return block_list height_to_hash, difficulty, sub_blocks = load_block_list(block_list, constants) latest_sub_block: SubBlockRecord = sub_blocks[block_list[-1].header_hash] curr = latest_sub_block while not curr.is_block: curr = sub_blocks[curr.prev_hash] start_timestamp = curr.timestamp start_height = curr.sub_block_height curr = latest_sub_block sub_blocks_added_this_sub_slot = 1 while not curr.first_in_sub_slot: curr = sub_blocks[curr.prev_hash] sub_blocks_added_this_sub_slot += 1 finished_sub_slots_at_sp: List[EndOfSubSlotBundle] = [] # Sub-slots since last sub block, up to signage point finished_sub_slots_at_ip: List[EndOfSubSlotBundle] = [] # Sub-slots since last sub block, up to infusion point sub_slot_iters: uint64 = latest_sub_block.sub_slot_iters # The number of iterations in one sub-slot same_slot_as_last = True # Only applies to first slot, to prevent old blocks from being added sub_slot_start_total_iters: uint128 = latest_sub_block.ip_sub_slot_total_iters(constants) sub_slots_finished = 0 pending_ses: bool = False # Start at the last block in block list # Get the challenge for that slot while True: slot_cc_challenge, slot_rc_challenge = get_challenges( constants, sub_blocks, finished_sub_slots_at_sp, latest_sub_block.header_hash, ) prev_num_of_blocks = num_blocks if num_empty_slots_added < skip_slots: # If did not reach the target slots to skip, don't make any proofs for this sub-slot num_empty_slots_added += 1 else: # Loop over every signage point (Except for the last ones, which are used for overflows) for signage_point_index in range(0, constants.NUM_SPS_SUB_SLOT - constants.NUM_SP_INTERVALS_EXTRA): curr = latest_sub_block while curr.total_iters > sub_slot_start_total_iters + calculate_sp_iters( constants, sub_slot_iters, uint8(signage_point_index) ): if curr.sub_block_height == 0: break curr = sub_blocks[curr.prev_hash] if curr.total_iters > sub_slot_start_total_iters: finished_sub_slots_at_sp = [] if same_slot_as_last: if signage_point_index < latest_sub_block.signage_point_index: # Ignore this signage_point because it's in the past continue signage_point: SignagePoint = get_signage_point( constants, sub_blocks, latest_sub_block, sub_slot_start_total_iters, uint8(signage_point_index), finished_sub_slots_at_sp, sub_slot_iters, ) if signage_point_index == 0: cc_sp_output_hash: bytes32 = slot_cc_challenge else: assert signage_point.cc_vdf is not None cc_sp_output_hash = signage_point.cc_vdf.output.get_hash() qualified_proofs: List[Tuple[uint64, ProofOfSpace]] = self.get_pospaces_for_challenge( constants, slot_cc_challenge, cc_sp_output_hash, seed, difficulty, sub_slot_iters, ) for required_iters, proof_of_space in sorted(qualified_proofs, key=lambda t: t[0]): if sub_blocks_added_this_sub_slot == constants.MAX_SUB_SLOT_SUB_BLOCKS or force_overflow: break if same_slot_as_last: if signage_point_index == latest_sub_block.signage_point_index: # Ignore this sub-block because it's in the past if required_iters <= latest_sub_block.required_iters: continue assert latest_sub_block.header_hash in sub_blocks if transaction_data_included: transaction_data = None assert start_timestamp is not None full_block, sub_block_record = get_full_block_and_sub_record( constants, sub_blocks, sub_slot_start_total_iters, uint8(signage_point_index), proof_of_space, slot_cc_challenge, slot_rc_challenge, farmer_reward_puzzle_hash, pool_target, start_timestamp, start_height, time_per_sub_block, transaction_data, height_to_hash, difficulty, required_iters, sub_slot_iters, self.get_plot_signature, self.get_pool_key_signature, finished_sub_slots_at_ip, signage_point, latest_sub_block, seed, ) if sub_block_record.is_block: transaction_data_included = True else: if guarantee_block: continue if pending_ses: pending_ses = False block_list.append(full_block) sub_blocks_added_this_sub_slot += 1 sub_blocks[full_block.header_hash] = sub_block_record log.info( f"Created block {sub_block_record.sub_block_height} ove=False, iters " f"{sub_block_record.total_iters}" ) height_to_hash[uint32(full_block.sub_block_height)] = full_block.header_hash latest_sub_block = sub_blocks[full_block.header_hash] finished_sub_slots_at_ip = [] num_blocks -= 1 if num_blocks == 0: return block_list # Finish the end of sub-slot and try again next sub-slot # End of sub-slot logic if len(finished_sub_slots_at_ip) == 0: # Sub block has been created within this sub-slot eos_iters: uint64 = uint64(sub_slot_iters - (latest_sub_block.total_iters - sub_slot_start_total_iters)) cc_input: ClassgroupElement = latest_sub_block.challenge_vdf_output rc_challenge: bytes32 = latest_sub_block.reward_infusion_new_challenge else: # No sub-blocks were successfully created within this sub-slot eos_iters = sub_slot_iters cc_input = ClassgroupElement.get_default_element() rc_challenge = slot_rc_challenge cc_vdf, cc_proof = get_vdf_info_and_proof( constants, cc_input, slot_cc_challenge, eos_iters, ) rc_vdf, rc_proof = get_vdf_info_and_proof( constants, ClassgroupElement.get_default_element(), rc_challenge, eos_iters, ) eos_deficit: uint8 = ( latest_sub_block.deficit if latest_sub_block.deficit > 0 else constants.MIN_SUB_BLOCKS_PER_CHALLENGE_BLOCK ) icc_ip_vdf, icc_ip_proof = get_icc( constants, uint128(sub_slot_start_total_iters + sub_slot_iters), finished_sub_slots_at_ip, latest_sub_block, sub_blocks, sub_slot_start_total_iters, eos_deficit, ) # End of slot vdf info for icc and cc have to be from challenge block or start of slot, respectively, # in order for light clients to validate. cc_vdf = VDFInfo(cc_vdf.challenge, sub_slot_iters, cc_vdf.output) if pending_ses: sub_epoch_summary: Optional[SubEpochSummary] = None else: sub_epoch_summary = next_sub_epoch_summary( constants, sub_blocks, height_to_hash, latest_sub_block.required_iters, block_list[-1], False, ) pending_ses = True if sub_epoch_summary is not None: ses_hash = sub_epoch_summary.get_hash() new_sub_slot_iters: Optional[uint64] = sub_epoch_summary.new_sub_slot_iters new_difficulty: Optional[uint64] = sub_epoch_summary.new_difficulty log.info(f"Sub epoch summary: {sub_epoch_summary}") else: ses_hash = None new_sub_slot_iters = None new_difficulty = None if icc_ip_vdf is not None: # Icc vdf (Deficit of latest sub-block is <= 4) if len(finished_sub_slots_at_ip) == 0: # This means there are sub-blocks in this sub-slot curr = latest_sub_block while not curr.is_challenge_sub_block(constants) and not curr.first_in_sub_slot: curr = sub_blocks[curr.prev_hash] if curr.is_challenge_sub_block(constants): icc_eos_iters = uint64(sub_slot_start_total_iters + sub_slot_iters - curr.total_iters) else: icc_eos_iters = sub_slot_iters else: # This means there are no sub-blocks in this sub-slot icc_eos_iters = sub_slot_iters icc_ip_vdf = VDFInfo( icc_ip_vdf.challenge, icc_eos_iters, icc_ip_vdf.output, ) icc_sub_slot: Optional[InfusedChallengeChainSubSlot] = InfusedChallengeChainSubSlot(icc_ip_vdf) assert icc_sub_slot is not None icc_sub_slot_hash = icc_sub_slot.get_hash() if latest_sub_block.deficit == 0 else None cc_sub_slot = ChallengeChainSubSlot( cc_vdf, icc_sub_slot_hash, ses_hash, new_sub_slot_iters, new_difficulty, ) else: # No icc icc_sub_slot = None cc_sub_slot = ChallengeChainSubSlot(cc_vdf, None, ses_hash, new_sub_slot_iters, new_difficulty) finished_sub_slots_at_ip.append( EndOfSubSlotBundle( cc_sub_slot, icc_sub_slot, RewardChainSubSlot( rc_vdf, cc_sub_slot.get_hash(), icc_sub_slot.get_hash() if icc_sub_slot is not None else None, eos_deficit, ), SubSlotProofs(cc_proof, icc_ip_proof, rc_proof), ) ) finished_sub_slots_eos = finished_sub_slots_at_ip.copy() latest_sub_block_eos = latest_sub_block overflow_cc_challenge = finished_sub_slots_at_ip[-1].challenge_chain.get_hash() overflow_rc_challenge = finished_sub_slots_at_ip[-1].reward_chain.get_hash() if transaction_data_included: transaction_data = None sub_slots_finished += 1 log.info( f"Sub slot finished. Sub-blocks included: {sub_blocks_added_this_sub_slot} sub_blocks_per_slot: " f"{(len(block_list) - initial_block_list_len)/sub_slots_finished}" ) sub_blocks_added_this_sub_slot = 0 # Sub slot ended, overflows are in next sub slot # Handle overflows: No overflows on new epoch if new_sub_slot_iters is None and num_empty_slots_added >= skip_slots and new_difficulty is None: for signage_point_index in range( constants.NUM_SPS_SUB_SLOT - constants.NUM_SP_INTERVALS_EXTRA, constants.NUM_SPS_SUB_SLOT, ): # note that we are passing in the finished slots which include the last slot signage_point = get_signage_point( constants, sub_blocks, latest_sub_block_eos, sub_slot_start_total_iters, uint8(signage_point_index), finished_sub_slots_eos, sub_slot_iters, ) if signage_point_index == 0: cc_sp_output_hash = slot_cc_challenge else: assert signage_point is not None assert signage_point.cc_vdf is not None cc_sp_output_hash = signage_point.cc_vdf.output.get_hash() # If did not reach the target slots to skip, don't make any proofs for this sub-slot qualified_proofs = self.get_pospaces_for_challenge( constants, slot_cc_challenge, cc_sp_output_hash, seed, difficulty, sub_slot_iters, ) for required_iters, proof_of_space in sorted(qualified_proofs, key=lambda t: t[0]): if sub_blocks_added_this_sub_slot == constants.MAX_SUB_SLOT_SUB_BLOCKS: break assert start_timestamp is not None full_block, sub_block_record = get_full_block_and_sub_record( constants, sub_blocks, sub_slot_start_total_iters, uint8(signage_point_index), proof_of_space, slot_cc_challenge, slot_rc_challenge, farmer_reward_puzzle_hash, pool_target, start_timestamp, start_height, time_per_sub_block, transaction_data, height_to_hash, difficulty, required_iters, sub_slot_iters, self.get_plot_signature, self.get_pool_key_signature, finished_sub_slots_at_ip, signage_point, latest_sub_block, seed, overflow_cc_challenge=overflow_cc_challenge, overflow_rc_challenge=overflow_rc_challenge, ) if sub_block_record.is_block: transaction_data_included = True elif guarantee_block: continue if pending_ses: pending_ses = False block_list.append(full_block) sub_blocks_added_this_sub_slot += 1 log.info( f"Created block {sub_block_record.sub_block_height } ov=True, iters " f"{sub_block_record.total_iters}" ) num_blocks -= 1 if num_blocks == 0: return block_list sub_blocks[full_block.header_hash] = sub_block_record height_to_hash[uint32(full_block.sub_block_height)] = full_block.header_hash latest_sub_block = sub_blocks[full_block.header_hash] finished_sub_slots_at_ip = [] finished_sub_slots_at_sp = finished_sub_slots_eos.copy() same_slot_as_last = False sub_slot_start_total_iters = uint128(sub_slot_start_total_iters + sub_slot_iters) if num_blocks < prev_num_of_blocks: num_empty_slots_added += 1 if new_sub_slot_iters is not None: assert new_difficulty is not None sub_slot_iters = new_sub_slot_iters difficulty = new_difficulty
async def respond_signatures( self, response: harvester_protocol.RespondSignatures): """ There are two cases: receiving signatures for sps, or receiving signatures for the block. """ if response.sp_hash not in self.farmer.sps: self.farmer.log.warning( f"Do not have challenge hash {response.challenge_hash}") return is_sp_signatures: bool = False sps = self.farmer.sps[response.sp_hash] signage_point_index = sps[0].signage_point_index found_sp_hash_debug = False for sp_candidate in sps: if response.sp_hash == response.message_signatures[0][0]: found_sp_hash_debug = True if sp_candidate.reward_chain_sp == response.message_signatures[ 1][0]: is_sp_signatures = True if found_sp_hash_debug: assert is_sp_signatures pospace = None for plot_identifier, candidate_pospace in self.farmer.proofs_of_space[ response.sp_hash]: if plot_identifier == response.plot_identifier: pospace = candidate_pospace assert pospace is not None computed_quality_string = pospace.verify_and_get_quality_string( self.farmer.constants, response.challenge_hash, response.sp_hash) if computed_quality_string is None: self.farmer.log.warning(f"Have invalid PoSpace {pospace}") return if is_sp_signatures: ( challenge_chain_sp, challenge_chain_sp_harv_sig, ) = response.message_signatures[0] reward_chain_sp, reward_chain_sp_harv_sig = response.message_signatures[ 1] for sk in self.farmer.get_private_keys(): pk = sk.get_g1() if pk == response.farmer_pk: agg_pk = ProofOfSpace.generate_plot_public_key( response.local_pk, pk) assert agg_pk == pospace.plot_public_key farmer_share_cc_sp = AugSchemeMPL.sign( sk, challenge_chain_sp, agg_pk) agg_sig_cc_sp = AugSchemeMPL.aggregate( [challenge_chain_sp_harv_sig, farmer_share_cc_sp]) assert AugSchemeMPL.verify(agg_pk, challenge_chain_sp, agg_sig_cc_sp) # This means it passes the sp filter farmer_share_rc_sp = AugSchemeMPL.sign( sk, reward_chain_sp, agg_pk) agg_sig_rc_sp = AugSchemeMPL.aggregate( [reward_chain_sp_harv_sig, farmer_share_rc_sp]) assert AugSchemeMPL.verify(agg_pk, reward_chain_sp, agg_sig_rc_sp) assert pospace.pool_public_key is not None pool_pk = bytes(pospace.pool_public_key) if pool_pk not in self.farmer.pool_sks_map: self.farmer.log.error( f"Don't have the private key for the pool key used by harvester: {pool_pk.hex()}" ) return pool_target: PoolTarget = PoolTarget( self.farmer.pool_target, uint32(0)) pool_target_signature: G2Element = AugSchemeMPL.sign( self.farmer.pool_sks_map[pool_pk], bytes(pool_target)) request = farmer_protocol.DeclareProofOfSpace( response.challenge_hash, challenge_chain_sp, signage_point_index, reward_chain_sp, pospace, agg_sig_cc_sp, agg_sig_rc_sp, self.farmer.wallet_target, pool_target, pool_target_signature, ) msg = Message("declare_proof_of_space", request) await self.farmer.server.send_to_all([msg], NodeType.FULL_NODE) return else: # This is a response with block signatures for sk in self.farmer.get_private_keys(): ( foliage_sub_block_hash, foliage_sub_block_sig_harvester, ) = response.message_signatures[0] ( foliage_block_hash, foliage_block_sig_harvester, ) = response.message_signatures[1] pk = sk.get_g1() if pk == response.farmer_pk: agg_pk = ProofOfSpace.generate_plot_public_key( response.local_pk, pk) assert agg_pk == pospace.plot_public_key foliage_sub_block_sig_farmer = AugSchemeMPL.sign( sk, foliage_sub_block_hash, agg_pk) foliage_block_sig_farmer = AugSchemeMPL.sign( sk, foliage_block_hash, agg_pk) foliage_sub_block_agg_sig = AugSchemeMPL.aggregate([ foliage_sub_block_sig_harvester, foliage_sub_block_sig_farmer ]) foliage_block_agg_sig = AugSchemeMPL.aggregate([ foliage_block_sig_harvester, foliage_block_sig_farmer ]) assert AugSchemeMPL.verify(agg_pk, foliage_sub_block_hash, foliage_sub_block_agg_sig) assert AugSchemeMPL.verify(agg_pk, foliage_block_hash, foliage_block_agg_sig) request_to_nodes = farmer_protocol.SignedValues( computed_quality_string, foliage_sub_block_agg_sig, foliage_block_agg_sig, ) msg = Message("signed_values", request_to_nodes) await self.farmer.server.send_to_all([msg], NodeType.FULL_NODE)
async def respond_proof_of_space( self, response: harvester_protocol.RespondProofOfSpace ): """ This is a response from the harvester with a proof of space. We check it's validity, and request a pool partial, a header signature, or both, if the proof is good enough. """ challenge_hash: bytes32 = response.proof.challenge_hash challenge_weight: uint128 = self.challenge_to_weight[challenge_hash] difficulty: uint64 = uint64(0) for posf in self.challenges[challenge_weight]: if posf.challenge_hash == challenge_hash: difficulty = posf.difficulty if difficulty == 0: raise RuntimeError("Did not find challenge") computed_quality_string = response.proof.verify_and_get_quality_string( self.constants.NUMBER_ZERO_BITS_CHALLENGE_SIG ) if computed_quality_string is None: raise RuntimeError("Invalid proof of space") self.harvester_responses_proofs[ (response.proof.challenge_hash, response.plot_id, response.response_number) ] = response.proof self.harvester_responses_proof_hash_to_info[response.proof.get_hash()] = ( response.proof.challenge_hash, response.plot_id, response.response_number, ) estimate_min = ( self.proof_of_time_estimate_ips * self.constants.BLOCK_TIME_TARGET / self.constants.MIN_ITERS_PROPORTION ) estimate_min = uint64(int(estimate_min)) number_iters: uint64 = calculate_iterations_quality( computed_quality_string, response.proof.size, difficulty, estimate_min, ) estimate_secs: float = number_iters / self.proof_of_time_estimate_ips if estimate_secs < self.config["pool_share_threshold"]: # TODO: implement pooling pass if estimate_secs < self.config["propagate_threshold"]: pool_pk = bytes(response.proof.pool_public_key) if pool_pk not in self.pool_sks_map: log.error( f"Don't have the private key for the pool key used by harvester: {pool_pk.hex()}" ) return pool_target: PoolTarget = PoolTarget(self.pool_target, uint32(0)) pool_target_signature: G2Element = AugSchemeMPL.sign( self.pool_sks_map[pool_pk], bytes(pool_target) ) request2 = farmer_protocol.RequestHeaderHash( challenge_hash, response.proof, pool_target, pool_target_signature, self.wallet_target, ) yield OutboundMessage( NodeType.FULL_NODE, Message("request_header_hash", request2), Delivery.BROADCAST, )