def test_spend_zero_coin(mod_code: Program, coin_checker_for_farmed_coin): """ Test to spend ccs from a farmed coin to a cc genesis coin, then to N outputs, then joining back down to two outputs. """ eve_inner_puzzle = ANYONE_CAN_SPEND_PUZZLE eve_inner_puzzle_hash = eve_inner_puzzle.get_tree_hash() total_minted = 0x111 genesis_coin_checker, spend_bundle = issue_cc_from_farmed_coin( mod_code, coin_checker_for_farmed_coin, 1, eve_inner_puzzle_hash, total_minted) puzzles_for_db = [ cc_puzzle_for_inner_puzzle(mod_code, genesis_coin_checker, eve_inner_puzzle) ] add_puzzles_to_puzzle_preimage_db(puzzles_for_db) eve_cc_list = [] for _ in spend_bundle.coin_solutions: eve_cc_list.extend( spendable_cc_list_from_coin_solution(_, hash_to_puzzle_f)) assert len(eve_cc_list) == 1 eve_cc_spendable = eve_cc_list[0] # farm regular chia farmed_coin = generate_farmed_coin(2, eve_inner_puzzle_hash, amount=500) # create a zero cc from this farmed coin wrapped_cc_puzzle_hash = cc_puzzle_hash_for_inner_puzzle_hash( mod_code, genesis_coin_checker, eve_inner_puzzle_hash) solution = solution_for_pay_to_any([(wrapped_cc_puzzle_hash, 0)]) reveal_w_solution = Program.to([ANYONE_CAN_SPEND_PUZZLE, solution]) coin_solution = CoinSolution(farmed_coin, reveal_w_solution) spendable_cc_list = spendable_cc_list_from_coin_solution( coin_solution, hash_to_puzzle_f) assert len(spendable_cc_list) == 1 zero_cc_spendable = spendable_cc_list[0] # we have our zero coin # now try to spend it spendable_cc_list = [eve_cc_spendable, zero_cc_spendable] inner_solutions = [ solution_for_pay_to_any([]), solution_for_pay_to_any([(wrapped_cc_puzzle_hash, eve_cc_spendable.coin.amount)]), ] spend_bundle = spend_bundle_for_spendable_ccs(mod_code, genesis_coin_checker, spendable_cc_list, inner_solutions) debug_spend_bundle(spend_bundle)
def coin_solution_for_lock_coin( prev_coin: Coin, subtotal: int, coin: Coin, ) -> CoinSolution: puzzle_reveal = LOCK_INNER_PUZZLE.curry(prev_coin.as_list(), subtotal) coin = Coin(coin.name(), puzzle_reveal.get_tree_hash(), uint64(0)) coin_solution = CoinSolution(coin, Program.to([puzzle_reveal, 0])) return coin_solution
def spendable_cc_list_from_coin_solution( coin_solution: CoinSolution, hash_to_puzzle_f ) -> List[SpendableCC]: """ Given a `CoinSolution`, extract out a list of `SpendableCC` objects. Since `SpendableCC` needs to track the inner puzzles and a `Coin` only includes puzzle hash, we also need a `hash_to_puzzle_f` function that turns puzzle hashes into the corresponding puzzles. This is generally either a `dict` or some kind of DB (if it's large or persistent). """ spendable_cc_list = [] coin = coin_solution.coin puzzle = coin_solution.solution.first() r = uncurry_cc(puzzle) if r: mod_hash, genesis_coin_checker, inner_puzzle = r lineage_proof = lineage_proof_for_cc_parent(coin, inner_puzzle.get_tree_hash()) else: lineage_proof = lineage_proof_for_coin(coin) for new_coin in coin_solution.additions(): puzzle = hash_to_puzzle_f(new_coin.puzzle_hash) if puzzle is None: # we don't recognize this puzzle hash, skip it continue r = uncurry_cc(puzzle) if r is None: # this isn't a cc puzzle continue mod_hash, genesis_coin_checker, inner_puzzle = r genesis_coin_id = genesis_coin_id_for_genesis_coin_checker(genesis_coin_checker) cc_spend_info = SpendableCC( new_coin, genesis_coin_id, inner_puzzle, lineage_proof ) spendable_cc_list.append(cc_spend_info) return spendable_cc_list
def issue_cc_from_farmed_coin( mod_code: Program, coin_checker_for_farmed_coin, block_id: int, inner_puzzle_hash: bytes32, amount: int, ) -> Tuple[Program, SpendBundle]: """ This is an example of how to issue a cc. """ # get a farmed coin farmed_puzzle = ANYONE_CAN_SPEND_PUZZLE farmed_puzzle_hash = farmed_puzzle.get_tree_hash() # mint a cc farmed_coin = generate_farmed_coin(block_id, farmed_puzzle_hash, amount=uint64(amount)) genesis_coin_checker = coin_checker_for_farmed_coin(farmed_coin) minted_cc_puzzle_hash = cc_puzzle_hash_for_inner_puzzle_hash( mod_code, genesis_coin_checker, inner_puzzle_hash) output_conditions = [[ ConditionOpcode.CREATE_COIN, minted_cc_puzzle_hash, farmed_coin.amount ]] # for this very simple puzzle, the solution is simply the output conditions # this is just a coincidence... for more complicated puzzles, you'll likely have to do some real work solution = Program.to(output_conditions) coin_solution = CoinSolution(farmed_coin, Program.to([farmed_puzzle, solution])) spend_bundle = SpendBundle([coin_solution], NULL_SIGNATURE) return genesis_coin_checker, spend_bundle
def spend_bundle_for_spendable_ccs( mod_code: Program, genesis_coin_checker: Program, spendable_cc_list: List[SpendableCC], inner_solutions: List[Program], sigs: Optional[List[G2Element]] = [], ) -> SpendBundle: """ Given a list of `SpendableCC` objects and inner solutions for those objects, create a `SpendBundle` that spends all those coins. Note that it the signature is not calculated it, so the caller is responsible for fixing it. """ N = len(spendable_cc_list) if len(inner_solutions) != N: raise ValueError("spendable_cc_list and inner_solutions are different lengths") input_coins = [_.coin for _ in spendable_cc_list] # figure out what the output amounts are by running the inner puzzles & solutions output_amounts = [] for cc_spend_info, inner_solution in zip(spendable_cc_list, inner_solutions): error, conditions, cost = conditions_dict_for_solution(Program.to([cc_spend_info.inner_puzzle, inner_solution])) total = 0 if conditions: for _ in conditions.get(ConditionOpcode.CREATE_COIN, []): total += Program.to(_.vars[1]).as_int() output_amounts.append(total) coin_solutions = [] deltas = [input_coins[_].amount - output_amounts[_] for _ in range(N)] subtotals = subtotals_for_deltas(deltas) if sum(deltas) != 0: raise ValueError("input and output amounts don't match") bundles = [bundle_for_spendable_cc_list(_) for _ in spendable_cc_list] for index in range(N): cc_spend_info = spendable_cc_list[index] puzzle_reveal = cc_puzzle_for_inner_puzzle(mod_code, genesis_coin_checker, cc_spend_info.inner_puzzle) prev_index = (index - 1) % N next_index = (index + 1) % N prev_bundle = bundles[prev_index] my_bundle = bundles[index] next_bundle = bundles[next_index] solution = [ inner_solutions[index], prev_bundle, my_bundle, next_bundle, subtotals[index], ] full_solution = Program.to([puzzle_reveal, solution]) coin_solution = CoinSolution(input_coins[index], full_solution) coin_solutions.append(coin_solution) # now add solutions to consume the lock coins for _ in range(N): prev_index = (_ - 1) % N prev_coin = spendable_cc_list[prev_index].coin this_coin = spendable_cc_list[_].coin subtotal = subtotals[_] coin_solution = coin_solution_for_lock_coin(prev_coin, subtotal, this_coin) coin_solutions.append(coin_solution) if sigs is None or sigs == []: return SpendBundle(coin_solutions, NULL_SIGNATURE) else: return SpendBundle(coin_solutions, AugSchemeMPL.aggregate(sigs))