async def test_clvm_strict_mode(self): block = Program.from_bytes(bytes(SMALL_BLOCK_GENERATOR)) disassembly = binutils.disassemble(block) # this is a valid generator program except the first clvm # if-condition, that depends on executing an unknown operator # ("0xfe"). In strict mode, this should fail, but in non-strict # mode, the unknown operator should be treated as if it returns (). program = SerializedProgram.from_bytes( binutils.assemble( f"(i (0xfe (q . 0)) (q . ()) {disassembly})").as_bin()) error, npc_list, cost = get_name_puzzle_conditions(program, True) assert error is not None error, npc_list, cost = get_name_puzzle_conditions(program, False) assert error is None
async def test_basics(self): wallet_tool = bt.get_pool_wallet_tool() num_blocks = 2 blocks = bt.get_consecutive_blocks( test_constants, num_blocks, [], 10, ) spend_bundle = wallet_tool.generate_signed_transaction( blocks[1].get_coinbase().amount, BURN_PUZZLE_HASH, blocks[1].get_coinbase(), ) assert spend_bundle is not None program = best_solution_program(spend_bundle) ratio = test_constants.CLVM_COST_RATIO_CONSTANT error, npc_list, clvm_cost = calculate_cost_of_program(program, ratio) error, npc_list, cost = get_name_puzzle_conditions(program) # Create condition + agg_sig_condition + length + cpu_cost assert (clvm_cost == 200 * ratio + 20 * ratio + len(bytes(program)) * ratio + cost)
async def test_strict_mode(self): wallet_tool = bt.get_pool_wallet_tool() ph = wallet_tool.get_new_puzzlehash() num_blocks = 3 blocks = bt.get_consecutive_blocks(num_blocks, [], guarantee_transaction_block=True, pool_reward_puzzle_hash=ph, farmer_reward_puzzle_hash=ph) coinbase = None for coin in blocks[2].get_included_reward_coins(): if coin.puzzle_hash == ph: coinbase = coin break assert coinbase is not None spend_bundle = wallet_tool.generate_signed_transaction( coinbase.amount, BURN_PUZZLE_HASH, coinbase, ) assert spend_bundle is not None program = SerializedProgram.from_bytes( binutils.assemble( "(q . ((0x3d2331635a58c0d49912bc1427d7db51afe3f20a7b4bcaffa17ee250dcbcbfaa" " (((c (q . ((c (q . ((c (i 11 (q . ((c (i (= 5 (point_add 11" " (pubkey_for_exp (sha256 11 ((c 6 (c 2 (c 23 (q . ())))))))))" " (q . ((c 23 47))) (q . (x))) 1))) (q . (c (c 4 (c 5 (c ((c 6 (c 2" " (c 23 (q . ()))))) (q . ())))) ((c 23 47))))) 1))) (c (q . (57 (c" " (i (l 5) (q . (sha256 (q . 2) ((c 6 (c 2 (c 9 (q . ()))))) ((c 6 (c" " 2 (c 13 (q . ()))))))) (q . (sha256 (q . 1) 5))) 1))) 1)))) (c" " (q . 0x88bc9360319e7c54ab42e19e974288a2d7a817976f7633f4b43" "f36ce72074e59c4ab8ddac362202f3e366f0aebbb6280)" ' 1))) (() (q . ((65 "00000000000000000000000000000000" 0x0cbba106e000))) ())))))' ).as_bin()) error, npc_list, cost = get_name_puzzle_conditions(program, True) assert error is not None error, npc_list, cost = get_name_puzzle_conditions(program, False) assert error is None coin_name = npc_list[0].coin_name error, puzzle, solution = get_puzzle_and_solution_for_coin( program, coin_name) assert error is None
async def test_strict_mode(self): wallet_tool = bt.get_pool_wallet_tool() ph = wallet_tool.get_new_puzzlehash() num_blocks = 3 blocks = bt.get_consecutive_blocks(num_blocks, [], guarantee_transaction_block=True, pool_reward_puzzle_hash=ph, farmer_reward_puzzle_hash=ph) coinbase = None for coin in blocks[2].get_included_reward_coins(): if coin.puzzle_hash == ph: coinbase = coin break assert coinbase is not None spend_bundle = wallet_tool.generate_signed_transaction( coinbase.amount, BURN_PUZZLE_HASH, coinbase, ) assert spend_bundle is not None pk = bytes.fromhex( "88bc9360319e7c54ab42e19e974288a2d7a817976f7633f4b43f36ce72074e59c4ab8ddac362202f3e366f0aebbb6280" ) puzzle = p2_delegated_puzzle_or_hidden_puzzle.puzzle_for_pk(pk) disassembly = binutils.disassemble(puzzle) program = SerializedProgram.from_bytes( binutils.assemble( f"(q . (((0x3d2331635a58c0d49912bc1427d7db51afe3f20a7b4bcaffa17ee250dcbcbfaa 300)" f" ({disassembly} (() (q . ((65 '00000000000000000000000000000000' 0x0cbba106e000))) ())))))" ).as_bin()) error, npc_list, cost = get_name_puzzle_conditions(program, True) assert error is not None error, npc_list, cost = get_name_puzzle_conditions(program, False) assert error is None coin_name = npc_list[0].coin_name error, puzzle, solution = get_puzzle_and_solution_for_coin( program, coin_name) assert error is None
def calculate_cost_of_program(program: SerializedProgram, clvm_cost_ratio_constant: int, strict_mode: bool = False) -> CostResult: """ This function calculates the total cost of either a block or a spendbundle """ total_clvm_cost = 0 error, npc_list, cost = get_name_puzzle_conditions(program, strict_mode) if error: raise Exception("get_name_puzzle_conditions raised error:" + str(error)) total_clvm_cost += cost # Add cost of conditions npc: NPC total_vbyte_cost = 0 for npc in npc_list: for condition, cvp_list in npc.condition_dict.items(): if condition is ConditionOpcode.AGG_SIG or condition is ConditionOpcode.AGG_SIG_ME: total_vbyte_cost += len(cvp_list) * ConditionCost.AGG_SIG.value elif condition is ConditionOpcode.CREATE_COIN: total_vbyte_cost += len( cvp_list) * ConditionCost.CREATE_COIN.value elif condition is ConditionOpcode.ASSERT_TIME_EXCEEDS: total_vbyte_cost += len( cvp_list) * ConditionCost.ASSERT_TIME_EXCEEDS.value elif condition is ConditionOpcode.ASSERT_BLOCK_AGE_EXCEEDS: total_vbyte_cost += len( cvp_list) * ConditionCost.ASSERT_BLOCK_AGE_EXCEEDS.value elif condition is ConditionOpcode.ASSERT_BLOCK_INDEX_EXCEEDS: total_vbyte_cost += len( cvp_list) * ConditionCost.ASSERT_BLOCK_INDEX_EXCEEDS.value elif condition is ConditionOpcode.ASSERT_MY_COIN_ID: total_vbyte_cost += len( cvp_list) * ConditionCost.ASSERT_MY_COIN_ID.value elif condition is ConditionOpcode.ASSERT_FEE: total_vbyte_cost += len( cvp_list) * ConditionCost.ASSERT_FEE.value elif condition is ConditionOpcode.CREATE_ANNOUNCEMENT: total_vbyte_cost += len( cvp_list) * ConditionCost.CREATE_ANNOUNCEMENT.value elif condition is ConditionOpcode.ASSERT_ANNOUNCEMENT: total_vbyte_cost += len( cvp_list) * ConditionCost.ASSERT_ANNOUNCEMENT.value else: # We ignore unknown conditions in order to allow for future soft forks pass # Add raw size of the program total_vbyte_cost += len(bytes(program)) total_clvm_cost += total_vbyte_cost * clvm_cost_ratio_constant return CostResult(error, npc_list, uint64(total_clvm_cost))
def calculate_cost_of_program( program: Program, clvm_cost_ratio_constant: int, ) -> Tuple[Optional[Err], List[NPC], uint64]: """ This function calculates the total cost of either block or a spendbundle """ total_clvm_cost = 0 error, npc_list, cost = get_name_puzzle_conditions(program) if error: raise Exception("get_name_puzzle_conditions raised error" + str(error)) total_clvm_cost += cost # Add cost of conditions npc: NPC total_vbyte_cost = 0 for npc in npc_list: for condition, cvp_list in npc.condition_dict.items(): if condition is ConditionOpcode.AGG_SIG: total_vbyte_cost += len(cvp_list) * ConditionCost.AGG_SIG.value elif condition is ConditionOpcode.CREATE_COIN: total_vbyte_cost += len( cvp_list) * ConditionCost.CREATE_COIN.value elif condition is ConditionOpcode.ASSERT_TIME_EXCEEDS: total_vbyte_cost += (len(cvp_list) * ConditionCost.ASSERT_TIME_EXCEEDS.value) elif condition is ConditionOpcode.ASSERT_BLOCK_AGE_EXCEEDS: total_vbyte_cost += ( len(cvp_list) * ConditionCost.ASSERT_BLOCK_AGE_EXCEEDS.value) elif condition is ConditionOpcode.ASSERT_BLOCK_INDEX_EXCEEDS: total_vbyte_cost += ( len(cvp_list) * ConditionCost.ASSERT_BLOCK_INDEX_EXCEEDS.value) elif condition is ConditionOpcode.ASSERT_MY_COIN_ID: total_vbyte_cost += (len(cvp_list) * ConditionCost.ASSERT_MY_COIN_ID.value) elif condition is ConditionOpcode.ASSERT_COIN_CONSUMED: total_vbyte_cost += (len(cvp_list) * ConditionCost.ASSERT_COIN_CONSUMED.value) elif condition is ConditionOpcode.ASSERT_FEE: total_vbyte_cost += len( cvp_list) * ConditionCost.ASSERT_FEE.value else: # We ignore unknown conditions in order to allow for future soft forks pass # Add raw size of the program total_vbyte_cost += len(bytes(program)) total_clvm_cost += total_vbyte_cost * clvm_cost_ratio_constant return error, npc_list, uint64(total_clvm_cost)
async def test_tx_generator_speed(self, large_txn_hex): generator = hexstr_to_bytes(large_txn_hex) program = SerializedProgram.from_bytes(generator) start_time = time.time() err, npc, cost = get_name_puzzle_conditions(program, False) end_time = time.time() duration = end_time - start_time assert err is None assert len(npc) == 687 log.info(f"Time spent: {duration}") assert duration < 3
def additions(self) -> List[Coin]: additions: List[Coin] = [] if self.transactions_generator is not None: # This should never throw here, block must be valid if it comes to here err, npc_list, cost = get_name_puzzle_conditions(self.transactions_generator, False) # created coins if npc_list is not None: additions.extend(additions_for_npc(npc_list)) additions.extend(self.get_included_reward_coins()) return additions
async def test_tx_generator_speed(self): LARGE_BLOCK_COIN_CONSUMED_COUNT = 687 generator = large_block_generator(LARGE_BLOCK_COIN_CONSUMED_COUNT) program = SerializedProgram.from_bytes(generator) start_time = time.time() err, npc, cost = get_name_puzzle_conditions(program, False) end_time = time.time() duration = end_time - start_time assert err is None assert len(npc) == LARGE_BLOCK_COIN_CONSUMED_COUNT log.info(f"Time spent: {duration}") assert duration < 3
async def test_clvm_strict_mode(self): program = SerializedProgram.from_bytes( # this is a valid generator program except the first clvm # if-condition, that depends on executing an unknown operator # ("0xfe"). In strict mode, this should fail, but in non-strict # mode, the unknown operator should be treated as if it returns (). binutils.assemble( "(i (a (q . 0xfe) (q . ())) (q . ()) " "(q . ((0x3d2331635a58c0d49912bc1427d7db51afe3f20a7b4bcaffa17ee250dcbcbfaa" " (((c (q . ((c (q . ((c (i 11 (q . ((c (i (= 5 (point_add 11" " (pubkey_for_exp (sha256 11 ((c 6 (c 2 (c 23 (q . ())))))))))" " (q . ((c 23 47))) (q . (x))) 1))) (q . (c (c 4 (c 5 (c ((c 6 (c 2" " (c 23 (q . ()))))) (q . ())))) ((c 23 47))))) 1))) (c (q . (57 (c" " (i (l 5) (q . (sha256 (q . 2) ((c 6 (c 2 (c 9 (q . ()))))) ((c 6 (c" " 2 (c 13 (q . ()))))))) (q . (sha256 (q . 1) 5))) 1))) 1)))) (c" " (q . 0x88bc9360319e7c54ab42e19e974288a2d7a817976f7633f4b43" "f36ce72074e59c4ab8ddac362202f3e366f0aebbb6280)" ' 1))) (() (q . ((51 "00000000000000000000000000000000" 0x0cbba106e000))) ())))))' ")").as_bin()) error, npc_list, cost = get_name_puzzle_conditions(program, True) assert error is not None error, npc_list, cost = get_name_puzzle_conditions(program, False) assert error is None
def tx_removals_and_additions(self) -> Tuple[List[bytes32], List[Coin]]: """ Doesn't return farmer and pool reward. This call assumes that this block has been validated already, get_name_puzzle_conditions should not return error here """ removals: List[bytes32] = [] additions: List[Coin] = [] if self.transactions_generator is not None: # This should never throw here, block must be valid if it comes to here err, npc_list, cost = get_name_puzzle_conditions(self.transactions_generator, False) # build removals list if npc_list is None: return [], [] for npc in npc_list: removals.append(npc.coin_name) additions.extend(additions_for_npc(npc_list)) return removals, additions
async def test_basics(self): wallet_tool = bt.get_pool_wallet_tool() ph = wallet_tool.get_new_puzzlehash() num_blocks = 3 blocks = bt.get_consecutive_blocks(num_blocks, [], guarantee_transaction_block=True, pool_reward_puzzle_hash=ph, farmer_reward_puzzle_hash=ph) coinbase = None for coin in blocks[2].get_included_reward_coins(): if coin.puzzle_hash == ph: coinbase = coin break assert coinbase is not None spend_bundle = wallet_tool.generate_signed_transaction( coinbase.amount, BURN_PUZZLE_HASH, coinbase, ) assert spend_bundle is not None program = best_solution_program(spend_bundle) ratio = test_constants.CLVM_COST_RATIO_CONSTANT result: CostResult = calculate_cost_of_program(program, ratio) clvm_cost = result.cost error, npc_list, cost = get_name_puzzle_conditions(program, False) assert error is None coin_name = npc_list[0].coin_name error, puzzle, solution = get_puzzle_and_solution_for_coin( program, coin_name) assert error is None # Create condition + agg_sig_condition + length + cpu_cost assert clvm_cost == 200 * ratio + 92 * ratio + len( bytes(program)) * ratio + cost
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_foliage( constants: ConsensusConstants, reward_sub_block: RewardChainSubBlockUnfinished, spend_bundle: Optional[SpendBundle], prev_sub_block: Optional[SubBlockRecord], sub_blocks: Dict[bytes32, SubBlockRecord], total_iters_sp: uint128, timestamp: uint64, farmer_reward_puzzlehash: bytes32, pool_target: PoolTarget, get_plot_signature: Callable[[bytes32, G1Element], G2Element], get_pool_signature: Callable[[PoolTarget, G1Element], G2Element], seed: bytes32 = b"", ) -> Tuple[FoliageSubBlock, Optional[FoliageBlock], Optional[TransactionsInfo], Optional[Program]]: """ Creates a foliage for a given reward chain sub block. This may or may not be a block. In the case of a block, the return values are not None. This is called at the signage point, so some of this information may be tweaked at the infusion point. Args: constants: consensus constants being used for this chain reward_sub_block: the reward sub block to look at, potentially at the signage point spend_bundle: the spend bundle including all transactions prev_sub_block: the previous sub-block at the signage point sub_blocks: dict from header hash to sub-blocks, of all ancestor sub-blocks total_iters_sp: total iters at the signage point timestamp: timestamp to put into the foliage block farmer_reward_puzzlehash: where to pay out farming reward pool_target: where to pay out pool reward get_plot_signature: retrieve the signature corresponding to the plot public key get_pool_signature: retrieve the signature corresponding to the pool public key seed: seed to randomize block """ if prev_sub_block is not None: res = get_prev_block(prev_sub_block, sub_blocks, total_iters_sp) is_block: bool = res[0] prev_block: Optional[SubBlockRecord] = res[1] else: # Genesis is a block prev_block = None is_block = True random.seed(seed) # Use the extension data to create different blocks based on header hash extension_data: bytes32 = random.randint(0, 100000000).to_bytes(32, "big") if prev_sub_block is None: sub_block_height: uint32 = uint32(0) else: sub_block_height = uint32(prev_sub_block.sub_block_height + 1) if prev_block is None: sub_height: uint32 = uint32(0) height: uint32 = uint32(0) else: sub_height = uint32(prev_block.sub_block_height + 1) prev_is_block = prev_block.is_block if prev_is_block: height = uint32(prev_block.height + 1) else: height = uint32(prev_block.height) # Create filter byte_array_tx: List[bytes32] = [] tx_additions: List[Coin] = [] tx_removals: List[bytes32] = [] pool_target_signature: Optional[G2Element] = get_pool_signature( pool_target, reward_sub_block.proof_of_space.pool_public_key) assert pool_target_signature is not None foliage_sub_block_data = FoliageSubBlockData( reward_sub_block.get_hash(), pool_target, pool_target_signature, farmer_reward_puzzlehash, extension_data, ) foliage_sub_block_signature: G2Element = get_plot_signature( foliage_sub_block_data.get_hash(), reward_sub_block.proof_of_space.plot_public_key, ) prev_sub_block_hash: bytes32 = constants.GENESIS_PREV_HASH if sub_block_height != 0: assert prev_sub_block is not None prev_sub_block_hash = prev_sub_block.header_hash solution_program: Optional[Program] = None if is_block: spend_bundle_fees: int = 0 aggregate_sig: G2Element = G2Element.infinity() cost = uint64(0) if spend_bundle is not None: solution_program = best_solution_program(spend_bundle) spend_bundle_fees = spend_bundle.fees() aggregate_sig = spend_bundle.aggregated_signature # Calculate the cost of transactions if solution_program is not None: result: CostResult = calculate_cost_of_program( solution_program, constants.CLVM_COST_RATIO_CONSTANT) cost = result.cost # TODO: prev generators root reward_claims_incorporated = [] if sub_height > 0: assert prev_block is not None assert prev_sub_block is not None curr: SubBlockRecord = prev_sub_block while not curr.is_block: curr = sub_blocks[curr.prev_hash] assert curr.fees is not None pool_coin = create_pool_coin( curr.sub_block_height, curr.pool_puzzle_hash, calculate_pool_reward(curr.height), ) farmer_coin = create_farmer_coin( curr.sub_block_height, curr.farmer_puzzle_hash, uint64(calculate_base_farmer_reward(curr.height) + curr.fees), ) assert curr.header_hash == prev_block.header_hash reward_claims_incorporated += [pool_coin, farmer_coin] if curr.sub_block_height > 0: curr = sub_blocks[curr.prev_hash] # Prev block is not genesis while not curr.is_block: pool_coin = create_pool_coin( curr.sub_block_height, curr.pool_puzzle_hash, calculate_pool_reward(curr.height), ) farmer_coin = create_farmer_coin( curr.sub_block_height, curr.farmer_puzzle_hash, calculate_base_farmer_reward(curr.height), ) reward_claims_incorporated += [pool_coin, farmer_coin] curr = sub_blocks[curr.prev_hash] additions: List[Coin] = reward_claims_incorporated.copy() npc_list = [] if solution_program is not None: error, npc_list, _ = get_name_puzzle_conditions( solution_program, False) additions += 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)) 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]] = {} for coin in tx_additions: 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() removals_root = removal_merkle_set.get_root() generator_hash = solution_program.get_tree_hash( ) if solution_program is not None else bytes32([0] * 32) filter_hash: bytes32 = std_hash(encoded) transactions_info: Optional[TransactionsInfo] = TransactionsInfo( bytes([0] * 32), generator_hash, aggregate_sig, uint64(spend_bundle_fees), cost, reward_claims_incorporated, ) if prev_block is None: prev_block_hash: bytes32 = constants.GENESIS_PREV_HASH else: prev_block_hash = prev_block.header_hash assert transactions_info is not None foliage_block: Optional[FoliageBlock] = FoliageBlock( prev_block_hash, timestamp, filter_hash, additions_root, removals_root, transactions_info.get_hash(), height, ) assert foliage_block is not None foliage_block_hash: Optional[bytes32] = foliage_block.get_hash() foliage_block_signature: Optional[G2Element] = get_plot_signature( foliage_block_hash, reward_sub_block.proof_of_space.plot_public_key) assert foliage_block_signature is not None else: foliage_block_hash = None foliage_block_signature = None foliage_block = None transactions_info = None assert (foliage_block_hash is None) == (foliage_block_signature is None) foliage_sub_block = FoliageSubBlock( prev_sub_block_hash, reward_sub_block.get_hash(), foliage_sub_block_data, foliage_sub_block_signature, foliage_block_hash, foliage_block_signature, ) return foliage_sub_block, foliage_block, transactions_info, solution_program