async def get_max_send_amount(self, records=None): spendable: List[WalletCoinRecord] = list( await self.wallet_state_manager.get_spendable_coins_for_wallet(self.id(), records) ) if len(spendable) == 0: return 0 spendable.sort(reverse=True, key=lambda record: record.coin.amount) if self.cost_of_single_tx is None: coin = spendable[0].coin tx = await self.generate_signed_transaction( coin.amount, coin.puzzle_hash, coins={coin}, ignore_max_send_amount=True ) program = best_solution_program(tx.spend_bundle) # npc contains names of the coins removed, puzzle_hashes and their spend conditions cost_result: CostResult = calculate_cost_of_program( program, self.wallet_state_manager.constants.CLVM_COST_RATIO_CONSTANT, True ) self.cost_of_single_tx = cost_result.cost self.log.info(f"Cost of a single tx for standard wallet: {self.cost_of_single_tx}") max_cost = self.wallet_state_manager.constants.MAX_BLOCK_COST_CLVM / 2 # avoid full block TXs current_cost = 0 total_amount = 0 total_coin_count = 0 for record in spendable: current_cost += self.cost_of_single_tx total_amount += record.coin.amount total_coin_count += 1 if current_cost + self.cost_of_single_tx > max_cost: break return total_amount
def validate_transaction_multiprocess( constants_dict: Dict, spend_bundle_bytes: bytes, ) -> bytes: constants: ConsensusConstants = dataclass_from_dict(ConsensusConstants, constants_dict) # Calculate the cost and fees program = best_solution_program(SpendBundle.from_bytes(spend_bundle_bytes)) # npc contains names of the coins removed, puzzle_hashes and their spend conditions return bytes(calculate_cost_of_program(program, constants.CLVM_COST_RATIO_CONSTANT, True))
def make_block_generator(count: int) -> SerializedProgram: puzzle_hash_db: Dict = dict() coins = [make_fake_coin(_, puzzle_hash_db) for _ in range(count)] coin_solutions = [] for coin in coins: puzzle_reveal = puzzle_hash_db[coin.puzzle_hash] conditions = conditions_for_payment(coin) solution = solution_for_conditions(conditions) coin_solution = CoinSolution(coin, puzzle_reveal, solution) coin_solutions.append(coin_solution) spend_bundle = SpendBundle(coin_solutions, blspy.G2Element()) return best_solution_program(spend_bundle)
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) result: CostResult = calculate_cost_of_program( program, test_constants.COST_PER_BYTE) 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 == ConditionCost.CREATE_COIN.value + ConditionCost.AGG_SIG.value + len(bytes(program)) * test_constants.COST_PER_BYTE + cost)
def create_foliage( constants: ConsensusConstants, reward_block_unfinished: RewardChainBlockUnfinished, spend_bundle: Optional[SpendBundle], additions: List[Coin], removals: List[Coin], prev_block: Optional[BlockRecord], blocks: BlockchainInterface, 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, Optional[G1Element]], Optional[G2Element]], seed: bytes32 = b"", ) -> Tuple[Foliage, Optional[FoliageTransactionBlock], Optional[TransactionsInfo], Optional[SerializedProgram]]: """ Creates a foliage for a given reward chain block. This may or may not be a tx block. In the case of a tx 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_block_unfinished: the reward block to look at, potentially at the signage point spend_bundle: the spend bundle including all transactions prev_block: the previous block at the signage point blocks: dict from header hash to blocks, of all ancestor 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_block is not None: res = get_prev_transaction_block(prev_block, blocks, total_iters_sp) is_transaction_block: bool = res[0] prev_transaction_block: Optional[BlockRecord] = res[1] else: # Genesis is a transaction block prev_transaction_block = None is_transaction_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_block is None: height: uint32 = uint32(0) else: height = uint32(prev_block.height + 1) # 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_block_unfinished.proof_of_space.pool_public_key) foliage_data = FoliageBlockData( reward_block_unfinished.get_hash(), pool_target, pool_target_signature, farmer_reward_puzzlehash, extension_data, ) foliage_block_data_signature: G2Element = get_plot_signature( foliage_data.get_hash(), reward_block_unfinished.proof_of_space.plot_public_key, ) prev_block_hash: bytes32 = constants.GENESIS_CHALLENGE if height != 0: assert prev_block is not None prev_block_hash = prev_block.header_hash solution_program: Optional[SerializedProgram] = None if is_transaction_block: aggregate_sig: G2Element = G2Element() cost = uint64(0) if spend_bundle is not None: solution_program = best_solution_program(spend_bundle) 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 removal_amount = 0 addition_amount = 0 for coin in removals: removal_amount += coin.amount for coin in additions: addition_amount += coin.amount spend_bundle_fees = removal_amount - addition_amount else: spend_bundle_fees = 0 # TODO: prev generators root reward_claims_incorporated = [] if height > 0: assert prev_transaction_block is not None assert prev_block is not None curr: BlockRecord = prev_block while not curr.is_transaction_block: curr = blocks.block_record(curr.prev_hash) assert curr.fees is not None pool_coin = create_pool_coin(curr.height, curr.pool_puzzle_hash, calculate_pool_reward(curr.height), constants.GENESIS_CHALLENGE) farmer_coin = create_farmer_coin( curr.height, curr.farmer_puzzle_hash, uint64(calculate_base_farmer_reward(curr.height) + curr.fees), constants.GENESIS_CHALLENGE, ) assert curr.header_hash == prev_transaction_block.header_hash reward_claims_incorporated += [pool_coin, farmer_coin] if curr.height > 0: curr = blocks.block_record(curr.prev_hash) # Prev block is not genesis while not curr.is_transaction_block: pool_coin = create_pool_coin( curr.height, curr.pool_puzzle_hash, calculate_pool_reward(curr.height), constants.GENESIS_CHALLENGE, ) farmer_coin = create_farmer_coin( curr.height, curr.farmer_puzzle_hash, calculate_base_farmer_reward(curr.height), constants.GENESIS_CHALLENGE, ) reward_claims_incorporated += [pool_coin, farmer_coin] curr = blocks.block_record(curr.prev_hash) additions.extend(reward_claims_incorporated.copy()) for coin in additions: tx_additions.append(coin) byte_array_tx.append(bytearray(coin.puzzle_hash)) for coin in removals: tx_removals.append(coin.name()) byte_array_tx.append(bytearray(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) generator_refs_hash = bytes32([1] * 32) filter_hash: bytes32 = std_hash(encoded) transactions_info: Optional[TransactionsInfo] = TransactionsInfo( generator_hash, generator_refs_hash, aggregate_sig, uint64(spend_bundle_fees), cost, reward_claims_incorporated, ) if prev_transaction_block is None: prev_transaction_block_hash: bytes32 = constants.GENESIS_CHALLENGE else: prev_transaction_block_hash = prev_transaction_block.header_hash assert transactions_info is not None foliage_transaction_block: Optional[ FoliageTransactionBlock] = FoliageTransactionBlock( prev_transaction_block_hash, timestamp, filter_hash, additions_root, removals_root, transactions_info.get_hash(), ) assert foliage_transaction_block is not None foliage_transaction_block_hash: Optional[ bytes32] = foliage_transaction_block.get_hash() foliage_transaction_block_signature: Optional[ G2Element] = get_plot_signature( foliage_transaction_block_hash, reward_block_unfinished.proof_of_space.plot_public_key) assert foliage_transaction_block_signature is not None else: foliage_transaction_block_hash = None foliage_transaction_block_signature = None foliage_transaction_block = None transactions_info = None assert (foliage_transaction_block_hash is None) == (foliage_transaction_block_signature is None) foliage = Foliage( prev_block_hash, reward_block_unfinished.get_hash(), foliage_data, foliage_block_data_signature, foliage_transaction_block_hash, foliage_transaction_block_signature, ) return foliage, foliage_transaction_block, transactions_info, solution_program