async def get_total_miniters(rpc_client, old_block, new_block): """ Calculates the sum of min_iters from all blocks starting from old and up to and including new_block, but not including old_block. """ old_block_parent = await rpc_client.get_header(old_block.prev_header_hash) old_diff = old_block.weight - old_block_parent.weight curr_mi = calculate_min_iters_from_iterations( old_block.proof_of_space, old_diff, old_block.proof_of_time.number_of_iterations ) # We do not count the min iters in the old block, since it's not included in the range total_mi: uint64 = uint64(0) for curr_h in range(old_block.height + 1, new_block.height + 1): if (curr_h % constants["DIFFICULTY_EPOCH"]) == constants["DIFFICULTY_DELAY"]: curr_b_header = await rpc_client.get_header_by_height(curr_h) curr_b_block = await rpc_client.get_block(curr_b_header.header_hash) curr_parent = await rpc_client.get_header(curr_b_block.prev_header_hash) curr_diff = curr_b_block.weight - curr_parent.weight curr_mi = calculate_min_iters_from_iterations( curr_b_block.proof_of_space, curr_diff, curr_b_block.proof_of_time.number_of_iterations, ) total_mi = uint64(total_mi + curr_mi) print("Minimum iterations:", total_mi) return total_mi
async def get_total_miniters(self, newer_block, older_block) -> Optional[uint64]: """ Calculates the sum of min_iters from all blocks starting from old and up to and including new_block, but not including old_block. """ older_block_parent = await self.service.block_store.get_block( older_block.prev_header_hash ) if older_block_parent is None: return None older_diff = older_block.weight - older_block_parent.weight curr_mi = calculate_min_iters_from_iterations( older_block.proof_of_space, older_diff, older_block.proof_of_time.number_of_iterations, self.service.constants["NUMBER_ZERO_BITS_CHALLENGE_SIG"], ) # We do not count the min iters in the old block, since it's not included in the range total_mi: uint64 = uint64(0) for curr_h in range(older_block.height + 1, newer_block.height + 1): if ( curr_h % self.service.constants["DIFFICULTY_EPOCH"] ) == self.service.constants["DIFFICULTY_DELAY"]: curr_b_header_hash = self.service.blockchain.height_to_hash.get( uint32(int(curr_h)) ) if curr_b_header_hash is None: return None curr_b_block = await self.service.block_store.get_block( curr_b_header_hash ) if curr_b_block is None or curr_b_block.proof_of_time is None: return None curr_parent = await self.service.block_store.get_block( curr_b_block.prev_header_hash ) if curr_parent is None: return None curr_diff = curr_b_block.weight - curr_parent.weight curr_mi = calculate_min_iters_from_iterations( curr_b_block.proof_of_space, uint64(curr_diff), curr_b_block.proof_of_time.number_of_iterations, self.service.constants["NUMBER_ZERO_BITS_CHALLENGE_SIG"], ) if curr_mi is None: raise web.HTTPBadRequest() total_mi = uint64(total_mi + curr_mi) return total_mi
def get_consecutive_blocks( self, test_constants: ConsensusConstants, num_blocks: int, block_list: List[FullBlock] = [], seconds_per_block=None, seed: bytes = b"", reward_puzzlehash: bytes32 = None, transaction_data_at_height: Dict[int, Tuple[Program, G2Element]] = None, fees: uint64 = uint64(0), ) -> List[FullBlock]: if transaction_data_at_height is None: transaction_data_at_height = {} if seconds_per_block is None: seconds_per_block = test_constants.BLOCK_TIME_TARGET if len(block_list) == 0: block_list.append( FullBlock.from_bytes(test_constants.GENESIS_BLOCK)) prev_difficulty = test_constants.DIFFICULTY_STARTING curr_difficulty = prev_difficulty curr_min_iters = test_constants.MIN_ITERS_STARTING elif len(block_list) < (test_constants.DIFFICULTY_EPOCH + test_constants.DIFFICULTY_DELAY): # First epoch (+delay), so just get first difficulty prev_difficulty = block_list[0].weight curr_difficulty = block_list[0].weight assert test_constants.DIFFICULTY_STARTING == prev_difficulty curr_min_iters = test_constants.MIN_ITERS_STARTING else: curr_difficulty = block_list[-1].weight - block_list[-2].weight prev_difficulty = ( block_list[-1 - test_constants.DIFFICULTY_EPOCH].weight - block_list[-2 - test_constants.DIFFICULTY_EPOCH].weight) assert block_list[-1].proof_of_time is not None curr_min_iters = calculate_min_iters_from_iterations( block_list[-1].proof_of_space, curr_difficulty, block_list[-1].proof_of_time.number_of_iterations, test_constants.NUMBER_ZERO_BITS_CHALLENGE_SIG, ) starting_height = block_list[-1].height + 1 timestamp = block_list[-1].header.data.timestamp for next_height in range(starting_height, starting_height + num_blocks): if (next_height > test_constants.DIFFICULTY_EPOCH and next_height % test_constants.DIFFICULTY_EPOCH == test_constants.DIFFICULTY_DELAY): # Calculates new difficulty height1 = uint64(next_height - (test_constants.DIFFICULTY_EPOCH + test_constants.DIFFICULTY_DELAY) - 1) height2 = uint64(next_height - (test_constants.DIFFICULTY_EPOCH) - 1) height3 = uint64(next_height - (test_constants.DIFFICULTY_DELAY) - 1) if height1 >= 0: block1 = block_list[height1] iters1 = block1.header.data.total_iters timestamp1 = block1.header.data.timestamp else: block1 = block_list[0] timestamp1 = uint64(block1.header.data.timestamp - test_constants.BLOCK_TIME_TARGET) iters1 = uint64(0) timestamp2 = block_list[height2].header.data.timestamp timestamp3 = block_list[height3].header.data.timestamp block3 = block_list[height3] iters3 = block3.header.data.total_iters term1 = (test_constants.DIFFICULTY_DELAY * prev_difficulty * (timestamp3 - timestamp2) * test_constants.BLOCK_TIME_TARGET) term2 = ((test_constants.DIFFICULTY_WARP_FACTOR - 1) * (test_constants.DIFFICULTY_EPOCH - test_constants.DIFFICULTY_DELAY) * curr_difficulty * (timestamp2 - timestamp1) * test_constants.BLOCK_TIME_TARGET) # Round down after the division new_difficulty_precise: uint64 = uint64( (term1 + term2) // (test_constants.DIFFICULTY_WARP_FACTOR * (timestamp3 - timestamp2) * (timestamp2 - timestamp1))) new_difficulty = uint64( truncate_to_significant_bits( new_difficulty_precise, test_constants.SIGNIFICANT_BITS)) max_diff = uint64( truncate_to_significant_bits( test_constants.DIFFICULTY_FACTOR * curr_difficulty, test_constants.SIGNIFICANT_BITS, )) min_diff = uint64( truncate_to_significant_bits( curr_difficulty // test_constants.DIFFICULTY_FACTOR, test_constants.SIGNIFICANT_BITS, )) if new_difficulty >= curr_difficulty: new_difficulty = min( new_difficulty, max_diff, ) else: new_difficulty = max([uint64(1), new_difficulty, min_diff]) min_iters_precise = uint64( (iters3 - iters1) // (test_constants.DIFFICULTY_EPOCH * test_constants.MIN_ITERS_PROPORTION)) curr_min_iters = uint64( truncate_to_significant_bits( min_iters_precise, test_constants.SIGNIFICANT_BITS)) prev_difficulty = curr_difficulty curr_difficulty = new_difficulty time_taken = seconds_per_block timestamp += time_taken transactions: Optional[Program] = None aggsig: Optional[G2Element] = None if next_height in transaction_data_at_height: transactions, aggsig = transaction_data_at_height[next_height] update_difficulty = (next_height % test_constants.DIFFICULTY_EPOCH == test_constants.DIFFICULTY_DELAY) block_list.append( self.create_next_block( test_constants, block_list[-1], timestamp, update_difficulty, curr_difficulty, curr_min_iters, seed, reward_puzzlehash, transactions, aggsig, fees, )) return block_list
def get_next_min_iters( constants: Dict, headers: Dict[bytes32, Header], height_to_hash: Dict[uint32, bytes32], block: Union[FullBlock, HeaderBlock], ) -> uint64: """ Returns the VDF speed in iterations per seconds, to be used for the next block. This depends on the number of iterations of the last epoch, and changes at the same block as the difficulty. """ next_height: uint32 = uint32(block.height + 1) if next_height < constants["DIFFICULTY_EPOCH"]: # First epoch has a hardcoded vdf speed return constants["MIN_ITERS_STARTING"] prev_block_header: Header = headers[block.prev_header_hash] proof_of_space = block.proof_of_space difficulty = get_next_difficulty( constants, headers, height_to_hash, prev_block_header ) iterations = uint64( block.header.data.total_iters - prev_block_header.data.total_iters ) prev_min_iters = calculate_min_iters_from_iterations( proof_of_space, difficulty, iterations ) if next_height % constants["DIFFICULTY_EPOCH"] != constants["DIFFICULTY_DELAY"]: # Not at a point where ips would change, so return the previous ips # TODO: cache this for efficiency return prev_min_iters # min iters (along with difficulty) will change in this block, so we need to calculate the new one. # The calculation is (iters_2 - iters_1) // epoch size # 1 and 2 correspond to height_1 and height_2, being the last block of the second to last, and last # block of the last epochs. Basically, it's total iterations per block on average. # Height1 is the last block 2 epochs ago, so we can include the iterations taken for mining first block in epoch height1 = uint32( next_height - constants["DIFFICULTY_EPOCH"] - constants["DIFFICULTY_DELAY"] - 1 ) # Height2 is the last block in the previous epoch height2 = uint32(next_height - constants["DIFFICULTY_DELAY"] - 1) block1: Optional[Header] = None block2: Optional[Header] = None # We need to backtrack until we merge with the LCA chain, so we can use the height_to_hash dict. # This is important if we are on a fork, or beyond the LCA. curr: Optional[Header] = block.header assert curr is not None while ( curr.height not in height_to_hash or height_to_hash[curr.height] != curr.header_hash ): if curr.height == height1: block1 = curr elif curr.height == height2: block2 = curr curr = headers.get(curr.prev_header_hash, None) assert curr is not None # Once we are before the fork point (and before the LCA), we can use the height_to_hash map if block1 is None and height1 >= 0: # height1 could be -1, for the first difficulty calculation block1 = headers.get(height_to_hash[height1], None) if block2 is None: block2 = headers.get(height_to_hash[height2], None) assert block2 is not None if block1 is not None: iters1 = block1.data.total_iters else: # In the case of height == -1, iters = 0 iters1 = uint64(0) iters2 = block2.data.total_iters min_iters_precise = uint64( (iters2 - iters1) // (constants["DIFFICULTY_EPOCH"] * constants["MIN_ITERS_PROPORTION"]) ) min_iters = uint64( truncate_to_significant_bits(min_iters_precise, constants["SIGNIFICANT_BITS"]) ) assert count_significant_bits(min_iters) <= constants["SIGNIFICANT_BITS"] return min_iters
def get_consecutive_blocks( self, input_constants: Dict, num_blocks: int, block_list: List[FullBlock] = [], seconds_per_block=None, seed: bytes = b"", reward_puzzlehash: bytes32 = None, transaction_data_at_height: Dict[int, Tuple[Program, BLSSignature]] = None, fees: uint64 = uint64(0), ) -> List[FullBlock]: if transaction_data_at_height is None: transaction_data_at_height = {} test_constants: Dict[str, Any] = constants.copy() for key, value in input_constants.items(): test_constants[key] = value if seconds_per_block is None: seconds_per_block = test_constants["BLOCK_TIME_TARGET"] if len(block_list) == 0: if "GENESIS_BLOCK" in test_constants: block_list.append(FullBlock.from_bytes(test_constants["GENESIS_BLOCK"])) else: block_list.append( self.create_genesis_block(test_constants, std_hash(seed), seed) ) prev_difficulty = test_constants["DIFFICULTY_STARTING"] curr_difficulty = prev_difficulty curr_min_iters = test_constants["MIN_ITERS_STARTING"] elif len(block_list) < ( test_constants["DIFFICULTY_EPOCH"] + test_constants["DIFFICULTY_DELAY"] ): # First epoch (+delay), so just get first difficulty prev_difficulty = block_list[0].weight curr_difficulty = block_list[0].weight assert test_constants["DIFFICULTY_STARTING"] == prev_difficulty curr_min_iters = test_constants["MIN_ITERS_STARTING"] else: curr_difficulty = block_list[-1].weight - block_list[-2].weight prev_difficulty = ( block_list[-1 - test_constants["DIFFICULTY_EPOCH"]].weight - block_list[-2 - test_constants["DIFFICULTY_EPOCH"]].weight ) assert block_list[-1].proof_of_time is not None curr_min_iters = calculate_min_iters_from_iterations( block_list[-1].proof_of_space, curr_difficulty, block_list[-1].proof_of_time.number_of_iterations, ) starting_height = block_list[-1].height + 1 timestamp = block_list[-1].header.data.timestamp for next_height in range(starting_height, starting_height + num_blocks): if ( next_height > test_constants["DIFFICULTY_EPOCH"] and next_height % test_constants["DIFFICULTY_EPOCH"] == test_constants["DIFFICULTY_DELAY"] ): # Calculates new difficulty height1 = uint64( next_height - ( test_constants["DIFFICULTY_EPOCH"] + test_constants["DIFFICULTY_DELAY"] ) - 1 ) height2 = uint64(next_height - (test_constants["DIFFICULTY_EPOCH"]) - 1) height3 = uint64(next_height - (test_constants["DIFFICULTY_DELAY"]) - 1) if height1 >= 0: block1 = block_list[height1] iters1 = block1.header.data.total_iters timestamp1 = block1.header.data.timestamp else: block1 = block_list[0] timestamp1 = ( block1.header.data.timestamp - test_constants["BLOCK_TIME_TARGET"] ) iters1 = uint64(0) timestamp2 = block_list[height2].header.data.timestamp timestamp3 = block_list[height3].header.data.timestamp block3 = block_list[height3] iters3 = block3.header.data.total_iters term1 = ( test_constants["DIFFICULTY_DELAY"] * prev_difficulty * (timestamp3 - timestamp2) * test_constants["BLOCK_TIME_TARGET"] ) term2 = ( (test_constants["DIFFICULTY_WARP_FACTOR"] - 1) * ( test_constants["DIFFICULTY_EPOCH"] - test_constants["DIFFICULTY_DELAY"] ) * curr_difficulty * (timestamp2 - timestamp1) * test_constants["BLOCK_TIME_TARGET"] ) # Round down after the division new_difficulty_precise: uint64 = uint64( (term1 + term2) // ( test_constants["DIFFICULTY_WARP_FACTOR"] * (timestamp3 - timestamp2) * (timestamp2 - timestamp1) ) ) new_difficulty = uint64( truncate_to_significant_bits( new_difficulty_precise, test_constants["SIGNIFICANT_BITS"] ) ) max_diff = uint64( truncate_to_significant_bits( test_constants["DIFFICULTY_FACTOR"] * curr_difficulty, test_constants["SIGNIFICANT_BITS"], ) ) min_diff = uint64( truncate_to_significant_bits( curr_difficulty // test_constants["DIFFICULTY_FACTOR"], test_constants["SIGNIFICANT_BITS"], ) ) if new_difficulty >= curr_difficulty: new_difficulty = min(new_difficulty, max_diff,) else: new_difficulty = max([uint64(1), new_difficulty, min_diff]) min_iters_precise = uint64( (iters3 - iters1) // ( test_constants["DIFFICULTY_EPOCH"] * test_constants["MIN_ITERS_PROPORTION"] ) ) curr_min_iters = uint64( truncate_to_significant_bits( min_iters_precise, test_constants["SIGNIFICANT_BITS"] ) ) prev_difficulty = curr_difficulty curr_difficulty = new_difficulty time_taken = seconds_per_block timestamp += time_taken transactions: Optional[Program] = None aggsig: Optional[BLSSignature] = None if next_height in transaction_data_at_height: transactions, aggsig = transaction_data_at_height[next_height] update_difficulty = ( next_height % test_constants["DIFFICULTY_EPOCH"] == test_constants["DIFFICULTY_DELAY"] ) block_list.append( self.create_next_block( test_constants, block_list[-1], timestamp, update_difficulty, curr_difficulty, curr_min_iters, seed, reward_puzzlehash, transactions, aggsig, fees, ) ) return block_list