def create_spend_for_auditor(parent_of_a, auditee): puzstring = f"(r (c (q 0x{auditee.name()}) (q ())))" puzzle = Program(binutils.assemble(puzstring)) coin = Coin(parent_of_a.name(), puzzle.get_tree_hash(), uint64(0)) solution = Program(binutils.assemble("()")) coinsol = CoinSolution(coin, Program.to([puzzle, solution])) return coinsol
def create_spend_for_ephemeral(parent_of_e, auditor_coin, spend_amount): puzstring = f"(r (r (c (q 0x{auditor_coin.name()}) (c (q {spend_amount}) (q ())))))" puzzle = Program(binutils.assemble(puzstring)) coin = Coin(parent_of_e.name(), puzzle.get_tree_hash(), uint64(0)) solution = Program(binutils.assemble("()")) coinsol = CoinSolution(coin, Program.to([puzzle, solution])) return coinsol
def sign_transaction(self, spends: List[Tuple[Program, CoinSolution]]): sigs = [] solution: Program puzzle: Program for puzzle, solution in spends: # type: ignore # noqa pubkey, secretkey = self.get_keys(solution.coin.puzzle_hash) code_ = [puzzle, solution.solution] sexp = Program.to(code_) err, con, cost = conditions_for_solution(sexp) if not con: return conditions_dict = conditions_by_opcode(con) for _, msg in pkm_pairs_for_conditions_dict( conditions_dict, bytes(solution.coin)): signature = AugSchemeMPL.sign(secretkey, msg) sigs.append(signature) aggsig = AugSchemeMPL.aggregate(sigs) solution_list: List[CoinSolution] = [ CoinSolution(coin_solution.coin, Program.to([puzzle, coin_solution.solution])) for (puzzle, coin_solution) in spends ] spend_bundle = SpendBundle(solution_list, aggsig) return spend_bundle
def get_output_amount_for_puzzle_and_solution(puzzle, solution): error, conditions, cost = conditions_dict_for_solution( Program.to([puzzle, solution])) total = 0 if conditions: for _ in conditions.get(ConditionOpcode.CREATE_COIN, []): total += Program.to(_.vars[1]).as_int() return total
async def rl_generate_signed_aggregation_transaction( self, rl_info, consolidating_coin, rl_parent, rl_coin): if (rl_info.limit is None or rl_info.interval is None or rl_info.user_pubkey is None or rl_info.admin_pubkey is None): raise ValueError("One or more of the elements of rl_info is None") if self.rl_coin_record is None: raise ValueError("Rl coin record is None") list_of_coinsolutions = [] self.rl_coin_record = await self._get_rl_coin_record() pubkey, secretkey = await self.get_keys( self.rl_coin_record.coin.puzzle_hash) # Spend wallet coin puzzle = rl_puzzle_for_pk( rl_info.user_pubkey, rl_info.limit, rl_info.interval, rl_info.rl_origin_id, rl_info.admin_pubkey, ) solution = rl_make_solution_mode_2( rl_coin.puzzle_hash, consolidating_coin.parent_coin_info, consolidating_coin.puzzle_hash, consolidating_coin.amount, rl_coin.parent_coin_info, rl_coin.amount, rl_parent.amount, rl_parent.parent_coin_info, ) signature = AugSchemeMPL.sign(secretkey, solution.get_tree_hash()) rl_spend = CoinSolution(self.rl_coin_record.coin, Program.to([puzzle, solution])) list_of_coinsolutions.append(rl_spend) # Spend consolidating coin puzzle = rl_make_aggregation_puzzle( self.rl_coin_record.coin.puzzle_hash) solution = rl_make_aggregation_solution( consolidating_coin.name(), self.rl_coin_record.coin.parent_coin_info, self.rl_coin_record.coin.amount, ) agg_spend = CoinSolution(consolidating_coin, Program.to([puzzle, solution])) list_of_coinsolutions.append(agg_spend) aggsig = AugSchemeMPL.aggregate([signature]) return SpendBundle(list_of_coinsolutions, aggsig)
def puzzle_for_m_of_public_key_list(m, public_key_list): format_tuple = tuple([ binutils.disassemble(Program.to(_)) for _ in (puzzle_prog_template, m, public_key_list) ]) puzzle_src = "((c (q %s) (c (q %s) (c (q %s) (a)))))" % ( format_tuple[0], format_tuple[1], format_tuple[2], ) puzzle_prog = binutils.assemble(puzzle_src) return Program.to(puzzle_prog)
async def get_sigs(self, innerpuz: Program, innersol: Program) -> List[G2Element]: puzzle_hash = innerpuz.get_tree_hash() pubkey, private = await self.wallet_state_manager.get_keys(puzzle_hash) sigs: List[G2Element] = [] code_ = [innerpuz, innersol] sexp = Program.to(code_) error, conditions, cost = conditions_dict_for_solution(sexp) if conditions is not None: for _, msg in pkm_pairs_for_conditions_dict(conditions): signature = AugSchemeMPL.sign(private, msg) sigs.append(signature) return sigs
async def get_sigs(self, innerpuz: Program, innersol: Program, coin_name) -> List[G2Element]: puzzle_hash = innerpuz.get_tree_hash() pubkey, private = await self.wallet_state_manager.get_keys(puzzle_hash) synthetic_secret_key = calculate_synthetic_secret_key(private, DEFAULT_HIDDEN_PUZZLE_HASH) sigs: List[G2Element] = [] code_ = [innerpuz, innersol] sexp = Program.to(code_) error, conditions, cost = conditions_dict_for_solution(sexp) if conditions is not None: for _, msg in pkm_pairs_for_conditions_dict(conditions, coin_name): signature = AugSchemeMPL.sign(synthetic_secret_key, msg) sigs.append(signature) return sigs
def run_and_return_cost_time(chialisp): start = time.time() clvm_loop = f"((c (q ((c (f (a)) (c (f (a)) (c (f (r (a))) (c (f (r (r (a)))) (q ()))))))) (c (q ((c (i (f (r (a))) (q (i (q 1) ((c (f (a)) (c (f (a)) (c (- (f (r (a))) (q 1)) (c (f (r (r (a)))) (q ())))))) ((c (f (r (r (a)))) (q ()))))) (q (q ()))) (a)))) (a))))" loop_program = Program(binutils.assemble(clvm_loop)) clvm_loop_solution = f"(1000 {chialisp})" solution_program = Program(binutils.assemble(clvm_loop_solution)) cost, sexp = run_program(loop_program, solution_program) end = time.time() total_time = end - start return cost, total_time
async def get_sigs_for_innerpuz_with_innersol( self, innerpuz: Program, innersol: Program) -> List[BLSSignature]: puzzle_hash = innerpuz.get_tree_hash() pubkey, private = await self.wallet_state_manager.get_keys(puzzle_hash) private = BLSPrivateKey(private) sigs: List[BLSSignature] = [] code_ = [innerpuz, innersol] sexp = Program.to(code_) error, conditions, cost = conditions_dict_for_solution(sexp) if conditions is not None: for _ in hash_key_pairs_for_conditions_dict(conditions): signature = private.sign(_.message_hash) sigs.append(signature) return sigs
async def generate_new_coloured_coin( self, amount: uint64) -> Optional[SpendBundle]: coins = await self.standard_wallet.select_coins(amount) if coins is None: return None origin = coins.copy().pop() origin_id = origin.name() cc_inner_hash = await self.get_new_inner_hash() await self.add_lineage(origin_id, Program.to((0, [origin.as_list(), 0]))) genesis_coin_checker = create_genesis_or_zero_coin_checker(origin_id) minted_cc_puzzle_hash = cc_puzzle_hash_for_inner_puzzle_hash( CC_MOD, genesis_coin_checker, cc_inner_hash) tx_record = await self.standard_wallet.generate_signed_transaction( amount, minted_cc_puzzle_hash, uint64(0), origin_id, coins) if tx_record is None: return None lineage_proof: Optional[Program] = lineage_proof_for_genesis(origin) lineage_proofs = [(origin_id, lineage_proof)] cc_info: CCInfo = CCInfo(genesis_coin_checker, lineage_proofs) await self.save_info(cc_info) return tx_record.spend_bundle
def curry(*args, **kwargs): """ The clvm_tools version of curry returns `cost, program` for now. Eventually it will just return `program`. This placeholder awaits that day. """ cost, prog = ct_curry(*args, **kwargs) return Program.to(prog)
def generate_unsigned_transaction( self, amount: uint64, new_puzzle_hash: bytes32, coin: Coin, condition_dic: Dict[ConditionOpcode, List[ConditionVarPair]], fee: int = 0, secret_key=None, ) -> List[CoinSolution]: spends = [] spend_value = coin.amount puzzle_hash = coin.puzzle_hash if secret_key is None: secret_key = self.get_private_key_for_puzzle_hash(puzzle_hash) pubkey = secret_key.get_g1() puzzle = puzzle_for_pk(bytes(pubkey)) if ConditionOpcode.CREATE_COIN not in condition_dic: condition_dic[ConditionOpcode.CREATE_COIN] = [] output = ConditionVarPair(ConditionOpcode.CREATE_COIN, [new_puzzle_hash, int_to_bytes(amount)]) condition_dic[output.opcode].append(output) amount_total = sum(int_from_bytes(cvp.vars[1]) for cvp in condition_dic[ConditionOpcode.CREATE_COIN]) change = spend_value - amount_total - fee if change > 0: change_puzzle_hash = self.get_new_puzzlehash() change_output = ConditionVarPair(ConditionOpcode.CREATE_COIN, [change_puzzle_hash, int_to_bytes(change)]) condition_dic[output.opcode].append(change_output) solution = self.make_solution(condition_dic) else: solution = self.make_solution(condition_dic) puzzle_solution_pair = Program.to([puzzle, solution]) spends.append(CoinSolution(coin, puzzle_solution_pair)) return spends
async def create_spend_bundle_relative_amount(self, cc_amount, zero_coin: Coin = None ) -> Optional[SpendBundle]: # If we're losing value then get coloured coins with at least that much value # If we're gaining value then our amount doesn't matter if cc_amount < 0: cc_spends = await self.select_coins(abs(cc_amount)) else: if zero_coin is None: return None cc_spends = set() cc_spends.add(zero_coin) if cc_spends is None: return None # Calculate output amount given relative difference and sum of actual values spend_value = sum([coin.amount for coin in cc_spends]) cc_amount = spend_value + cc_amount # Loop through coins and create solution for innerpuzzle list_of_solutions = [] output_created = None sigs: List[G2Element] = [] for coin in cc_spends: if output_created is None: newinnerpuzhash = await self.get_new_inner_hash() innersol = self.standard_wallet.make_solution( primaries=[{ "puzzlehash": newinnerpuzhash, "amount": cc_amount }]) output_created = coin else: innersol = self.standard_wallet.make_solution( consumed=[output_created.name()]) innerpuz: Program = await self.inner_puzzle_for_cc_puzhash( coin.puzzle_hash) sigs = sigs + await self.get_sigs(innerpuz, innersol) lineage_proof = await self.get_lineage_proof_for_coin(coin) puzzle_reveal = cc_puzzle_for_inner_puzzle( CC_MOD, self.cc_info.my_genesis_checker, innerpuz) # Use coin info to create solution and add coin and solution to list of CoinSolutions solution = [ innersol, coin.as_list(), lineage_proof, None, None, None, None, None, ] full_solution = Program.to([puzzle_reveal, solution]) list_of_solutions.append(CoinSolution(coin, full_solution)) aggsig = AugSchemeMPL.aggregate(sigs) return SpendBundle(list_of_solutions, aggsig)
def solution_for_rl( my_parent_id: bytes32, my_puzzlehash: bytes32, my_amount: uint64, out_puzzlehash: bytes32, out_amount: uint64, my_parent_parent_id: bytes32, parent_amount: uint64, interval, limit, fee, ): """ Solution is (1 my_parent_id, my_puzzlehash, my_amount, outgoing_puzzle_hash, outgoing_amount, min_block_time, parent_parent_id, parent_amount, fee) min block time = Math.ceil((new_amount * self.interval) / self.limit) """ min_block_count = math.ceil((out_amount * interval) / limit) solution = sexp( 1, "0x" + my_parent_id.hex(), "0x" + my_puzzlehash.hex(), my_amount, "0x" + out_puzzlehash.hex(), out_amount, min_block_count, "0x" + my_parent_parent_id.hex(), parent_amount, fee, ) return Program.to(binutils.assemble(solution))
def rl_make_solution_mode_2( my_puzzle_hash, consolidating_primary_input, consolidating_coin_puzzle_hash, outgoing_amount, my_primary_input, incoming_amount, parent_amount, my_parent_parent_id, ): my_puzzle_hash = hexlify(my_puzzle_hash).decode("ascii") consolidating_primary_input = hexlify(consolidating_primary_input).decode("ascii") consolidating_coin_puzzle_hash = hexlify(consolidating_coin_puzzle_hash).decode("ascii") primary_input = hexlify(my_primary_input).decode("ascii") sol = sexp( 2, "0x" + my_puzzle_hash, "0x" + consolidating_primary_input, "0x" + consolidating_coin_puzzle_hash, outgoing_amount, "0x" + primary_input, incoming_amount, parent_amount, "0x" + str(my_parent_parent_id), ) return Program.to(binutils.assemble(sol))
def load_clvm(clvm_filename, package_or_requirement=__name__) -> Program: """ This function takes a .clvm file in the given package and compiles it to a .clvm.hex file if the .hex file is missing or older than the .clvm file, then returns the contents of the .hex file as a `Program`. clvm_filename: file name package_or_requirement: usually `__name__` if the clvm file is in the same package """ hex_filename = f"{clvm_filename}.hex" try: if pkg_resources.resource_exists(package_or_requirement, clvm_filename): full_path = pathlib.Path( pkg_resources.resource_filename(package_or_requirement, clvm_filename)) output = full_path.parent / hex_filename compile_clvm(full_path, output) except NotImplementedError: # pyinstaller doesn't support `pkg_resources.resource_exists` # so we just fall through to loading the hex clvm pass clvm_hex = pkg_resources.resource_string(package_or_requirement, hex_filename).decode("utf8") clvm_blob = bytes.fromhex(clvm_hex) return Program.from_bytes(clvm_blob)
async def create_wallet_for_cc( wallet_state_manager: Any, wallet: Wallet, genesis_checker_hex: str, name: str = None, ): self = CCWallet() self.base_puzzle_program = None self.base_inner_puzzle_hash = None self.standard_wallet = wallet if name: self.log = logging.getLogger(name) else: self.log = logging.getLogger(__name__) self.wallet_state_manager = wallet_state_manager self.cc_info = CCInfo( Program.from_bytes(bytes.fromhex(genesis_checker_hex)), []) info_as_string = bytes(self.cc_info).hex() self.wallet_info = await wallet_state_manager.user_store.create_wallet( "CC Wallet", WalletType.COLOURED_COIN.value, info_as_string) if self.wallet_info is None: raise Exception("wallet_info is None") await self.wallet_state_manager.add_new_wallet(self, self.wallet_info.id) return self
def puzzle_for_synthetic_public_key(synthetic_public_key): puzzle_src = "((c (q %s) (c (q 0x%s) (a))))" % ( binutils.disassemble(puzzle_prog_template), synthetic_public_key.hex(), ) puzzle_prog = binutils.assemble(puzzle_src) return Program(puzzle_prog)
def make_solution(self, condition_dic: Dict[ConditionOpcode, List[ConditionVarPair]]): ret = [] for con_list in condition_dic.values(): for cvp in con_list: if cvp.opcode == ConditionOpcode.CREATE_COIN: ret.append(make_create_coin_condition(cvp.var1, cvp.var2)) if cvp.opcode == ConditionOpcode.AGG_SIG: ret.append(make_assert_aggsig_condition(cvp.var1)) if cvp.opcode == ConditionOpcode.ASSERT_COIN_CONSUMED: ret.append(make_assert_coin_consumed_condition(cvp.var1)) if cvp.opcode == ConditionOpcode.ASSERT_TIME_EXCEEDS: ret.append(make_assert_time_exceeds_condition(cvp.var1)) if cvp.opcode == ConditionOpcode.ASSERT_MY_COIN_ID: ret.append(make_assert_my_coin_id_condition(cvp.var1)) if cvp.opcode == ConditionOpcode.ASSERT_BLOCK_INDEX_EXCEEDS: ret.append( make_assert_block_index_exceeds_condition(cvp.var1)) if cvp.opcode == ConditionOpcode.ASSERT_BLOCK_AGE_EXCEEDS: ret.append( make_assert_block_age_exceeds_condition(cvp.var1)) if cvp.opcode == ConditionOpcode.ASSERT_FEE: ret.append(make_assert_fee_condition(cvp.var1)) return Program.to([puzzle_for_conditions(ret), []])
def sign_transaction(self, spends: List[Tuple[Program, CoinSolution]]): sigs = [] solution: Program puzzle: Program for puzzle, solution in spends: # type: ignore # noqa pubkey, secretkey = self.get_keys(solution.coin.puzzle_hash) secretkey = BLSPrivateKey(secretkey) code_ = [puzzle, solution.solution] sexp = Program.to(code_) err, con, cost = conditions_for_solution(sexp) if not con: return conditions_dict = conditions_by_opcode(con) for _ in hash_key_pairs_for_conditions_dict( conditions_dict, bytes(solution.coin)): signature = secretkey.sign(_.message_hash) sigs.append(signature) aggsig = BLSSignature.aggregate(sigs) solution_list: List[CoinSolution] = [ CoinSolution(coin_solution.coin, clvm.to_sexp_f([puzzle, coin_solution.solution])) for (puzzle, coin_solution) in spends ] spend_bundle = SpendBundle(solution_list, aggsig) return spend_bundle
def disassemble(sexp): """ This version of `disassemble` also disassembles condition opcodes like `ASSERT_COIN_CONSUMED`. """ kfa = dict(KEYWORD_FROM_ATOM) kfa.update((Program.to(k).as_atom(), v) for k, v in KFA.items()) return bu_disassemble(sexp, kfa)
async def create_spend_bundle_relative_chia(self, chia_amount: int, exclude: List[Coin]) -> SpendBundle: list_of_solutions = [] utxos = None # If we're losing value then get coins with at least that much value # If we're gaining value then our amount doesn't matter if chia_amount < 0: utxos = await self.select_coins(abs(chia_amount), exclude) else: utxos = await self.select_coins(0, exclude) assert len(utxos) > 0 # Calculate output amount given sum of utxos spend_value = sum([coin.amount for coin in utxos]) chia_amount = spend_value + chia_amount # Create coin solutions for each utxo output_created = None for coin in utxos: puzzle = await self.puzzle_for_puzzle_hash(coin.puzzle_hash) if output_created is None: newpuzhash = await self.get_new_puzzlehash() primaries = [{"puzzlehash": newpuzhash, "amount": chia_amount}] solution = self.make_solution(primaries=primaries) output_created = coin list_of_solutions.append(CoinSolution(coin, Program.to([puzzle, solution]))) await self.hack_populate_secret_keys_for_coin_solutions(list_of_solutions) spend_bundle = await sign_coin_solutions(list_of_solutions, self.secret_key_store.secret_key_for_public_key) return spend_bundle
def lineage_proof_for_cc_parent(parent_coin: Coin, parent_inner_puzzle_hash: bytes32) -> Program: return Program.to( ( 1, [parent_coin.parent_coin_info, parent_inner_puzzle_hash, parent_coin.amount], ) )
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 solution_with_hidden_puzzle(hidden_public_key, hidden_puzzle, solution_to_hidden_puzzle): synthetic_public_key = calculate_synthetic_public_key( hidden_public_key, hidden_puzzle) puzzle = puzzle_for_synthetic_public_key(synthetic_public_key) return Program.to([ puzzle, [hidden_public_key, hidden_puzzle, solution_to_hidden_puzzle] ])
def solution_for_pay_to_any( puzzle_hash_amount_pairs: List[Tuple[bytes32, int]] ) -> Program: output_conditions = [ [ConditionOpcode.CREATE_COIN, puzzle_hash, amount] for puzzle_hash, amount in puzzle_hash_amount_pairs ] return Program.to(output_conditions)
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 parse_sexp_to_condition( sexp: Program, ) -> Tuple[Optional[Err], Optional[ConditionVarPair]]: """ Takes a ChiaLisp sexp and returns a ConditionVarPair. If it fails, returns an Error """ if not sexp.listp(): return Err.SEXP_ERROR, None items = sexp.as_python() if not isinstance(items[0], bytes): return Err.INVALID_CONDITION, None try: opcode = ConditionOpcode(items[0]) except ValueError: opcode = ConditionOpcode.UNKNOWN if len(items) == 3: return None, ConditionVarPair(opcode, [items[1], items[2]]) return None, ConditionVarPair(opcode, [items[1]])
async def sign_clawback_transaction( self, spends: List[Tuple[Program, CoinSolution]], clawback_pubkey ): sigs = [] for puzzle, solution in spends: pubkey, secretkey = await self.get_keys_pk(clawback_pubkey) signature = secretkey.sign(Program(solution.solution).get_hash()) sigs.append(signature) aggsig = AugSchemeMPL.aggregate(sigs) solution_list = [] for puzzle, coin_solution in spends: solution_list.append( CoinSolution( coin_solution.coin, Program.to([puzzle, coin_solution.solution]) ) ) spend_bundle = SpendBundle(solution_list, aggsig) return spend_bundle