def test_very_deep_tree(self): blob = b"a" for depth in [10, 100, 1000, 10000, 100000]: s = to_sexp_f(blob) for _ in range(depth): s = to_sexp_f((s, blob)) self.check_serde(s)
def disassemble_to_ir(sexp, keyword_from_atom, allow_keyword=None): if is_ir(sexp) and allow_keyword is not False: return ir_cons(ir_symbol("ir"), sexp) if sexp.nullp(): return ir_null() if sexp.listp(): if sexp.first().listp() or allow_keyword is None: allow_keyword = True v0 = disassemble_to_ir(sexp.first(), keyword_from_atom, allow_keyword=allow_keyword) v1 = disassemble_to_ir(sexp.rest(), keyword_from_atom, allow_keyword=False) return to_sexp_f((Type.CONS, (v0, v1))) as_atom = sexp.as_atom() if allow_keyword: v = keyword_from_atom.get(as_atom) if v is not None and v != '.': return ir_symbol(v) return to_sexp_f((type_for_atom(as_atom), as_atom))
def assemble_from_ir(ir_sexp): keyword = ir_as_symbol(ir_sexp) if keyword: if keyword[:1] == "#": keyword = keyword[1:] atom = KEYWORD_TO_ATOM.get(keyword) if atom: return to_sexp_f(atom) if True: return ir_val(ir_sexp) raise SyntaxError("can't parse %s at %s" % (keyword, ir_sexp._offset)) if not ir_listp(ir_sexp): return ir_val(ir_sexp) if ir_nullp(ir_sexp): return to_sexp_f([]) # handle "q" first = ir_first(ir_sexp) keyword = ir_as_symbol(first) if keyword == "q": pass # TODO: note that any symbol is legal after this point sexp_1 = assemble_from_ir(first) sexp_2 = assemble_from_ir(ir_rest(ir_sexp)) return sexp_1.cons(sexp_2)
def ap_generate_signed_aggregation_transaction(self): list_of_coinsolutions = [] if self.aggregation_coins is False: # empty sets evaluate to false in python return consolidating_coin = self.aggregation_coins.pop() pubkey, secretkey = self.get_keys( self.temp_coin.puzzle_hash, self.a_pubkey) # Spend wallet coin puzzle = ap_make_puzzle(self.a_pubkey, bytes(pubkey)) solution = self.ap_make_solution_mode_2(self.temp_coin.puzzle_hash, consolidating_coin.parent_coin_info, consolidating_coin.puzzle_hash, consolidating_coin.amount, self.temp_coin.parent_coin_info, self.temp_coin.amount) signature = secretkey.sign(ProgramHash(solution)) list_of_coinsolutions.append(CoinSolution( self.temp_coin, clvm.to_sexp_f([puzzle, solution]))) # Spend consolidating coin puzzle = ap_make_aggregation_puzzle(self.temp_coin.puzzle_hash) solution = self.ac_make_aggregation_solution(consolidating_coin.name( ), self.temp_coin.parent_coin_info, self.temp_coin.amount) list_of_coinsolutions.append(CoinSolution( consolidating_coin, clvm.to_sexp_f([puzzle, solution]))) # Spend lock puzstring = f"(r (c (q 0x{consolidating_coin.name().hex()}) (q ())))" puzzle = Program(binutils.assemble(puzstring)) solution = Program(binutils.assemble("()")) list_of_coinsolutions.append(CoinSolution(Coin(self.temp_coin, ProgramHash( puzzle), 0), clvm.to_sexp_f([puzzle, solution]))) self.temp_coin = Coin(self.temp_coin, self.temp_coin.puzzle_hash, self.temp_coin.amount + consolidating_coin.amount) aggsig = BLSSignature.aggregate([signature]) solution_list = CoinSolutionList(list_of_coinsolutions) return SpendBundle(solution_list, aggsig)
def rl_generate_signed_aggregation_transaction(self): list_of_coinsolutions = [] if self.aggregation_coins is False: # empty sets evaluate to false in python return consolidating_coin = self.aggregation_coins.pop() pubkey, secretkey = self.get_keys(self.rl_coin.puzzle_hash) # Spend wallet coin puzzle = self.rl_puzzle_for_pk(pubkey.serialize(), self.limit, self.interval, self.rl_origin, self.rl_clawback_pk) if isinstance(self.rl_parent, Coin): solution = self.rl_make_solution_mode_2( self.rl_coin.puzzle_hash, consolidating_coin.parent_coin_info, consolidating_coin.puzzle_hash, consolidating_coin.amount, self.rl_coin.parent_coin_info, self.rl_coin.amount, self.rl_parent.amount, self.rl_parent.parent_coin_info) else: solution = self.rl_make_solution_mode_2( self.rl_coin.puzzle_hash, consolidating_coin.parent_coin_info, consolidating_coin.puzzle_hash, consolidating_coin.amount, self.rl_coin.parent_coin_info, self.rl_coin.amount, self.rl_parent["amount"], self.rl_parent["parent_coin_info"]) signature = BLSPrivateKey(secretkey).sign(ProgramHash(solution)) list_of_coinsolutions.append( CoinSolution(self.rl_coin, clvm.to_sexp_f([puzzle, solution]))) # Spend consolidating coin puzzle = self.rl_make_aggregation_puzzle(self.rl_coin.puzzle_hash) solution = self.rl_make_aggregation_solution( consolidating_coin.name(), self.rl_coin.parent_coin_info, self.rl_coin.amount) list_of_coinsolutions.append( CoinSolution(consolidating_coin, clvm.to_sexp_f([puzzle, solution]))) # Spend lock puzstring = "(r (c (q 0x" + hexlify( consolidating_coin.name()).decode('ascii') + ") (q ())))" puzzle = Program(binutils.assemble(puzstring)) solution = Program(binutils.assemble("()")) list_of_coinsolutions.append( CoinSolution(Coin(self.rl_coin, ProgramHash(puzzle), 0), clvm.to_sexp_f([puzzle, solution]))) aggsig = BLSSignature.aggregate([signature]) solution_list = CoinSolutionList(list_of_coinsolutions) return SpendBundle(solution_list, aggsig)
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 clvm.to_sexp_f([puzzle_for_conditions(ret), []])
def generate_recovery_transaction(self, coins, root_public_key, secret_key, escrow_duration): recovery_pubkey = root_public_key.public_child( 0).get_public_key().serialize() signatures = [] coin_solutions = [] secret_key = BLSPrivateKey(secret_key) for coin in coins: pubkey = self.find_pubkey_for_escrow_puzzle( coin, root_public_key, escrow_duration) puzzle = self.get_escrow_puzzle_with_params( recovery_pubkey, pubkey.serialize(), escrow_duration) op_create_coin = ConditionOpcode.CREATE_COIN[0] puzzlehash = f'0x' + str(hexbytes(self.get_new_puzzlehash())) solution_src = sexp( quote(sexp(sexp(op_create_coin, puzzlehash, coin.amount))), sexp(), 1) solution = Program(binutils.assemble(solution_src)) puzzle_solution_list = clvm.to_sexp_f([puzzle, solution]) coin_solution = CoinSolution(coin, puzzle_solution_list) coin_solutions.append(coin_solution) conditions_dict = conditions_by_opcode( conditions_for_solution(puzzle_solution_list)) for _ in hash_key_pairs_for_conditions_dict(conditions_dict): signature = secret_key.sign(_.message_hash) signatures.append(signature) coin_solution_list = CoinSolutionList(coin_solutions) aggsig = BLSSignature.aggregate(signatures) spend_bundle = SpendBundle(coin_solution_list, aggsig) return spend_bundle
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, clvm.to_sexp_f([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) 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 sign_transaction(self, spends: (Program, [CoinSolution])): sigs = [] for puzzle, solution in spends: pubkey, secretkey = self.get_keys(solution.coin.puzzle_hash) code_ = [puzzle, solution.solution] sexp = clvm.to_sexp_f(code_) conditions_dict = conditions_by_opcode( conditions_for_solution(sexp)) for _ in hash_key_pairs_for_conditions_dict(conditions_dict): signature = secretkey.sign(_.message_hash) sigs.append(signature) aggsig = BLSSignature.aggregate(sigs) solution_list = CoinSolutionList( [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
async def sign_transaction( self, spends: List[Tuple[Program, CoinSolution]] ) -> Optional[SpendBundle]: signatures = [] for puzzle, solution in spends: # Get keys keys = await self.wallet_state_manager.get_keys(solution.coin.puzzle_hash) if not keys: self.log.error( f"Sign transaction failed, No Keys for puzzlehash {solution.coin.puzzle_hash}" ) return None pubkey, secretkey = keys secretkey = BLSPrivateKey(secretkey) code_ = [puzzle, solution.solution] sexp = clvm.to_sexp_f(code_) # Get AGGSIG conditions err, con, cost = conditions_for_solution(sexp) if err or not con: self.log.error(f"Sign transcation failed, con:{con}, error: {err}") return None conditions_dict = conditions_by_opcode(con) # Create signature for pk_message in hash_key_pairs_for_conditions_dict( conditions_dict, bytes(solution.coin) ): signature = secretkey.sign(pk_message.message_hash) signatures.append(signature) # Aggregate signatures aggsig = BLSSignature.aggregate(signatures) 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 check_serde(self, s): v = to_sexp_f(s) b = v.as_bin() v1 = sexp_from_stream(io.BytesIO(b), to_sexp_f) if v != v1: print("%s: %d %s %s" % (v, len(b), b, v1)) breakpoint() b = v.as_bin() v1 = sexp_from_stream(io.BytesIO(b), to_sexp_f) self.assertEqual(v, v1)
def cc_generate_eve_spend(coin: Coin, full_puzzle: Program): solution = cc_make_eve_solution(coin.parent_coin_info, coin.puzzle_hash, coin.amount) list_of_solutions = [ CoinSolution( coin, clvm.to_sexp_f([full_puzzle, solution]), ) ] aggsig = BLSSignature.aggregate([]) spend_bundle = SpendBundle(list_of_solutions, aggsig) return spend_bundle
def make_solution(self, primaries=[], min_time=0, me={}, consumed=[]): ret = [] for primary in primaries: ret.append(make_create_coin_condition( primary['puzzlehash'], primary['amount'])) for coin in consumed: ret.append(make_assert_coin_consumed_condition(coin)) if min_time > 0: ret.append(make_assert_min_time_condition(min_time)) if me: ret.append(make_assert_my_coin_id_condition(me['id'])) return clvm.to_sexp_f([puzzle_for_conditions(ret), []])
def sign_clawback_transaction(self, spends: (Program, [CoinSolution]), clawback_pubkey): sigs = [] for puzzle, solution in spends: pubkey, secretkey = self.get_keys_pk(clawback_pubkey) signature = secretkey.sign( ProgramHash(Program(solution.solution))) sigs.append(signature) aggsig = BLSSignature.aggregate(sigs) solution_list = CoinSolutionList( [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 check_serde(self, s): v = to_sexp_f(s) b = v.as_bin() v1 = sexp_from_stream(io.BytesIO(b), to_sexp_f) if v != v1: print("%s: %d %s %s" % (v, len(b), b, v1)) breakpoint() b = v.as_bin() v1 = sexp_from_stream(io.BytesIO(b), to_sexp_f) self.assertEqual(v, v1) # this copies the bytes that represent a single s-expression, just to # know where the message ends. It doesn't build a python representaion # of it buf = sexp_buffer_from_stream(io.BytesIO(b)) self.assertEqual(buf, b)
def ap_sign_transaction(self, spends: (Program, [CoinSolution]), signatures_from_a): sigs = [] for puzzle, solution in spends: pubkey, secretkey = self.get_keys( solution.coin.puzzle_hash, self.a_pubkey) signature = secretkey.sign( ProgramHash(Program(solution.solution))) sigs.append(signature) for s in signatures_from_a: sigs.append(s) aggsig = BLSSignature.aggregate(sigs) solution_list = CoinSolutionList( [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 make_solution(parent, puzzlehash, value, stake_factor, primaries=[], recovery=False): conditions = [] for primary in primaries: conditions.append( make_create_coin_condition(primary['puzzlehash'], primary['amount'])) conditions = [binutils.assemble("#q"), conditions] solution = [ conditions, [], 1 if recovery else 0, parent, puzzlehash, value, math.floor(value * stake_factor) ] program = Program(to_sexp_f(solution)) return program
def generate_recovery_to_escrow_transaction(self, coin, recovery_pubkey, pubkey, stake_factor, escrow_duration): solution = make_solution(coin.parent_coin_info, coin.puzzle_hash, coin.amount, stake_factor, recovery=True) puzzle = self.get_new_puzzle_with_params_and_root( recovery_pubkey, pubkey, stake_factor, escrow_duration) sexp = clvm.to_sexp_f([puzzle, solution]) destination_puzzle_hash = get_destination_puzzle_hash(sexp) staked_amount = math.ceil(coin.amount * (stake_factor - 1)) spends = self.generate_unsigned_transaction_without_recipient( staked_amount) spends.append((puzzle, CoinSolution(coin, solution))) return spends, destination_puzzle_hash, coin.amount + staked_amount
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 = BLSSignature.aggregate(sigs) solution_list = [] for puzzle, coin_solution in spends: solution_list.append( CoinSolution(coin_solution.coin, clvm.to_sexp_f([puzzle, coin_solution.solution]))) spend_bundle = SpendBundle(solution_list, aggsig) return spend_bundle
async def create_spend_bundle_relative_chia( self, chia_amount: int, exclude: List[Coin] ): 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) if utxos is None: return None # 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 sigs: List[BLSSignature] = [] for coin in utxos: pubkey, secretkey = await self.wallet_state_manager.get_keys( coin.puzzle_hash ) puzzle = self.puzzle_for_pk(bytes(pubkey)) 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 else: solution = self.make_solution(consumed=[output_created.name()]) list_of_solutions.append( CoinSolution(coin, clvm.to_sexp_f([puzzle, solution])) ) new_sigs = await self.get_sigs_for_innerpuz_with_innersol(puzzle, solution) sigs = sigs + new_sigs aggsig = BLSSignature.aggregate(sigs) spend_bundle = SpendBundle(list_of_solutions, aggsig) return spend_bundle
def make_solution( self, primaries=None, min_time=0, me=None, consumed=None, fee=None ): condition_list = [] if primaries: for primary in primaries: condition_list.append( make_create_coin_condition(primary["puzzlehash"], primary["amount"]) ) if consumed: for coin in consumed: condition_list.append(make_assert_coin_consumed_condition(coin)) if min_time > 0: condition_list.append(make_assert_time_exceeds_condition(min_time)) if me: condition_list.append(make_assert_my_coin_id_condition(me["id"])) if fee: condition_list.append(make_assert_fee_condition(fee)) return clvm.to_sexp_f([puzzle_for_conditions(condition_list), []])
def cp_sign_transaction(self, spends: (Program, [CoinSolution]), approval=None): sigs = [] for puzzle, solution in spends: pubkey, secretkey = self.get_keys(solution.coin.puzzle_hash) signature = secretkey.sign(ProgramHash(Program(solution.solution))) sigs.append(signature) if approval is not None: app = BLSSignature(approval) sigs.append(app) aggsig = BLSSignature.aggregate(sigs) solution_list = CoinSolutionList([ 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 test_zero(self): v = to_sexp_f(b"\x00") self.assertEqual(v.as_bin(), b"\x00")
from clvm import run_program as default_run_program, to_sexp_f, KEYWORD_TO_ATOM # noqa from clvm.operators import OPERATOR_LOOKUP # noqa from clvm.EvalError import EvalError # noqa from clvm.casts import int_from_bytes, int_to_bytes # noqa from clvm.serialize import sexp_from_stream, sexp_to_stream # noqa SExp = to_sexp_f(1).__class__ BaseSExp = SExp def run_program( program, args, quote_kw=KEYWORD_TO_ATOM["q"], args_kw=KEYWORD_TO_ATOM["a"], operator_lookup=OPERATOR_LOOKUP, max_cost=None, pre_eval_f=None, ): return default_run_program( program, args, quote_kw, args_kw, operator_lookup, max_cost, pre_eval_f=pre_eval_f, )
def launch_tool(args, tool_name, default_stage=0): parser = argparse.ArgumentParser(description='Execute a clvm script.') parser.add_argument("-s", "--stage", type=stage_import, help="stage number to include", default=stage_import(default_stage)) parser.add_argument( "-v", "--verbose", action="store_true", help="Display resolve of all reductions, for debugging") parser.add_argument("-c", "--cost", action="store_true", help="Show cost") parser.add_argument("-m", "--max-cost", type=int, help="Maximum cost") parser.add_argument("-d", "--dump", action="store_true", help="dump hex version of final output") parser.add_argument("-y", "--symbol-table", type=pathlib.Path, help=".SYM file generated by compiler") parser.add_argument( "-i", "--include", type=pathlib.Path, help="add a search path for included files", action="append", default=[], ) parser.add_argument("path_or_code", type=path_or_code, help="path to clvm script, or literal script") parser.add_argument("args", type=reader.read_ir, help="arguments", nargs="?", default=reader.read_ir("()")) args = parser.parse_args(args=args[1:]) if hasattr(args.stage, "run_program_for_search_paths"): run_program = args.stage.run_program_for_search_paths(args.include) else: run_program = args.stage.run_program src_text = args.path_or_code src_sexp = reader.read_ir(src_text) assembled_sexp = binutils.assemble_from_ir(src_sexp) pre_eval_f = None symbol_table = None log_entries = [] if args.symbol_table: with open(args.symbol_table) as f: symbol_table = json.load(f) pre_eval_f = make_trace_pre_eval(log_entries, symbol_table) elif args.verbose: pre_eval_f = make_trace_pre_eval(log_entries) run_script = getattr(args.stage, tool_name) cost = 0 try: output = "(didn't finish)" env = binutils.assemble_from_ir(args.args) input_sexp = to_sexp_f((assembled_sexp, env)) cost, result = run_program(run_script, input_sexp, max_cost=args.max_cost, pre_eval_f=pre_eval_f) if args.cost: print("cost = %d" % cost) if args.dump: blob = as_bin(lambda f: sexp_to_stream(result, f)) output = blob.hex() else: output = binutils.disassemble(result) except EvalError as ex: output = "FAIL: %s %s" % (ex, binutils.disassemble(ex._sexp)) result = ex._sexp return -1 except Exception as ex: result = src_sexp output = str(ex) raise finally: print(output) if args.verbose or symbol_table: print() trace_to_text(log_entries, binutils.disassemble, symbol_table)
from clvm import to_sexp_f from . import binutils # monkey-patch SExp SExp = to_sexp_f([]).__class__ SExp.__str__ = SExp.__repr__ = binutils.disassemble
async def respond_to_offer(self, file_path: Path) -> Tuple[bool, Optional[str]]: has_wallets = await self.maybe_create_wallets_for_offer(file_path) if not has_wallets: return False, "Unknown Error" trade_offer_hex = file_path.read_text() trade_offer = SpendBundle.from_bytes(bytes.fromhex(trade_offer_hex)) coinsols = [] # [] of CoinSolutions cc_coinsol_outamounts: Dict[bytes32, List[Tuple[Any, int]]] = dict() # Used for generating auditor solution, key is colour auditees: Dict[bytes32, List[Tuple[bytes32, bytes32, Any, int]]] = dict() aggsig = trade_offer.aggregated_signature cc_discrepancies: Dict[bytes32, int] = dict() chia_discrepancy = None wallets: Dict[bytes32, Any] = dict() # colour to wallet dict for coinsol in trade_offer.coin_solutions: puzzle = coinsol.solution.first() solution = coinsol.solution.rest().first() # work out the deficits between coin amount and expected output for each if cc_wallet_puzzles.check_is_cc_puzzle(puzzle): parent_info = binutils.disassemble( solution.rest().first()).split(" ") if len(parent_info) > 1: # Calculate output amounts colour = cc_wallet_puzzles.get_genesis_from_puzzle( binutils.disassemble(puzzle)) if colour not in wallets: wallets[ colour] = await self.wallet_state_manager.get_wallet_for_colour( colour) unspent = await self.wallet_state_manager.get_spendable_coins_for_wallet( wallets[colour].wallet_info.id) if coinsol.coin in [record.coin for record in unspent]: return False, "can't respond to own offer" innerpuzzlereveal = solution.rest().rest().rest().first() innersol = solution.rest().rest().rest().rest().first() out_amount = cc_wallet_puzzles.get_output_amount_for_puzzle_and_solution( innerpuzzlereveal, innersol) if colour in cc_discrepancies: cc_discrepancies[ colour] += coinsol.coin.amount - out_amount else: cc_discrepancies[ colour] = coinsol.coin.amount - out_amount # Store coinsol and output amount for later if colour in cc_coinsol_outamounts: cc_coinsol_outamounts[colour].append( (coinsol, out_amount)) else: cc_coinsol_outamounts[colour] = [(coinsol, out_amount)] # auditees should be (primary_input, innerpuzhash, coin_amount, output_amount) if colour in auditees: auditees[colour].append(( coinsol.coin.parent_coin_info, Program(innerpuzzlereveal).get_tree_hash(), coinsol.coin.amount, out_amount, )) else: auditees[colour] = [( coinsol.coin.parent_coin_info, Program(innerpuzzlereveal).get_tree_hash(), coinsol.coin.amount, out_amount, )] else: coinsols.append(coinsol) else: # standard chia coin unspent = await self.wallet_state_manager.get_spendable_coins_for_wallet( 1) if coinsol.coin in [record.coin for record in unspent]: return False, "can't respond to own offer" if chia_discrepancy is None: chia_discrepancy = cc_wallet_puzzles.get_output_discrepancy_for_puzzle_and_solution( coinsol.coin, puzzle, solution) else: chia_discrepancy += cc_wallet_puzzles.get_output_discrepancy_for_puzzle_and_solution( coinsol.coin, puzzle, solution) coinsols.append(coinsol) chia_spend_bundle: Optional[SpendBundle] = None if chia_discrepancy is not None: chia_spend_bundle = await self.wallet_state_manager.main_wallet.create_spend_bundle_relative_chia( chia_discrepancy, []) zero_spend_list: List[SpendBundle] = [] # create coloured coin self.log.info(cc_discrepancies) for colour in cc_discrepancies.keys(): if cc_discrepancies[colour] < 0: my_cc_spends = await wallets[colour].select_coins( abs(cc_discrepancies[colour])) else: if chia_spend_bundle is None: to_exclude: List = [] else: to_exclude = chia_spend_bundle.removals() my_cc_spends = await wallets[colour].select_coins(0) if my_cc_spends is None or my_cc_spends == set(): zero_spend_bundle: SpendBundle = await wallets[ colour].generate_zero_val_coin(False, to_exclude) if zero_spend_bundle is None: return ( False, "unable to generate zero value coin, check you have chia available", ) zero_spend_list.append(zero_spend_bundle) additions = zero_spend_bundle.additions() removals = zero_spend_bundle.removals() my_cc_spends = set() for add in additions: if add not in removals and add.amount == 0: my_cc_spends.add(add) if my_cc_spends == set() or my_cc_spends is None: return False, "insufficient funds" auditor = my_cc_spends.pop() auditor_inner_puzzle = await self.get_inner_puzzle_for_puzzle_hash( auditor.puzzle_hash) assert auditor_inner_puzzle is not None inner_hash = auditor_inner_puzzle.get_tree_hash() auditor_info = ( auditor.parent_coin_info, inner_hash, auditor.amount, ) auditor_formatted = ( f"(0x{auditor.parent_coin_info} 0x{inner_hash} {auditor.amount})" ) core = cc_wallet_puzzles.cc_make_core(colour) parent_info = await wallets[colour].get_parent_for_coin(auditor) for coloured_coin in my_cc_spends: inner_solution = self.wallet_state_manager.main_wallet.make_solution( consumed=[auditor.name()]) sig = await wallets[ colour].get_sigs_for_innerpuz_with_innersol( await self.get_inner_puzzle_for_puzzle_hash( coloured_coin.puzzle_hash), inner_solution, ) aggsig = BLSSignature.aggregate( [BLSSignature.aggregate(sig), aggsig]) inner_puzzle = await self.get_inner_puzzle_for_puzzle_hash( coloured_coin.puzzle_hash) assert inner_puzzle is not None # auditees should be (primary_input, innerpuzhash, coin_amount, output_amount) auditees[colour].append(( coloured_coin.parent_coin_info, inner_puzzle.get_tree_hash(), coloured_coin.amount, 0, )) solution = cc_wallet_puzzles.cc_make_solution( core, ( parent_info.parent_name, parent_info.inner_puzzle_hash, parent_info.amount, ), coloured_coin.amount, binutils.disassemble(inner_puzzle), binutils.disassemble(inner_solution), auditor_info, None, ) coin_spend = CoinSolution( coloured_coin, clvm.to_sexp_f([ cc_wallet_puzzles.cc_make_puzzle( inner_puzzle.get_tree_hash(), core, ), solution, ]), ) coinsols.append(coin_spend) ephemeral = cc_wallet_puzzles.create_spend_for_ephemeral( coloured_coin, auditor, 0) coinsols.append(ephemeral) auditor = cc_wallet_puzzles.create_spend_for_auditor( auditor, coloured_coin) coinsols.append(auditor) # Tweak the offer's solution to include the new auditor for cc_coinsol_out in cc_coinsol_outamounts[colour]: cc_coinsol = cc_coinsol_out[0] offer_sol = binutils.disassemble(cc_coinsol.solution) # auditor is (primary_input, innerpuzzlehash, amount) offer_sol = offer_sol.replace( "))) ()) () ()))", f"))) ()) {auditor_formatted} ()))") new_coinsol = CoinSolution(cc_coinsol.coin, binutils.assemble(offer_sol)) coinsols.append(new_coinsol) eph = cc_wallet_puzzles.create_spend_for_ephemeral( cc_coinsol.coin, auditor, cc_coinsol_out[1]) coinsols.append(eph) aud = cc_wallet_puzzles.create_spend_for_auditor( auditor, cc_coinsol.coin) coinsols.append(aud) # Finish the auditor CoinSolution with new information newinnerpuzhash = await wallets[colour].get_new_inner_hash() outputamount = (sum([c.amount for c in my_cc_spends]) + cc_discrepancies[colour] + auditor.amount) innersol = self.wallet_state_manager.main_wallet.make_solution( primaries=[{ "puzzlehash": newinnerpuzhash, "amount": outputamount }]) parent_info = await wallets[colour].get_parent_for_coin(auditor) auditees[colour].append(( auditor.parent_coin_info, auditor_inner_puzzle.get_tree_hash(), auditor.amount, outputamount, )) sig = await wallets[colour].get_sigs(auditor_inner_puzzle, innersol) aggsig = BLSSignature.aggregate( [BLSSignature.aggregate(sig), aggsig]) solution = cc_wallet_puzzles.cc_make_solution( core, ( parent_info.parent_name, parent_info.inner_puzzle_hash, parent_info.amount, ), auditor.amount, binutils.disassemble(auditor_inner_puzzle), binutils.disassemble(innersol), auditor_info, auditees[colour], ) cs = CoinSolution( auditor, clvm.to_sexp_f([ cc_wallet_puzzles.cc_make_puzzle( auditor_inner_puzzle.get_tree_hash(), core), solution, ]), ) coinsols.append(cs) cs_eph = create_spend_for_ephemeral(auditor, auditor, outputamount) coinsols.append(cs_eph) cs_aud = create_spend_for_auditor(auditor, auditor) coinsols.append(cs_aud) spend_bundle = SpendBundle(coinsols, aggsig) my_tx_records = [] if zero_spend_list is not None: zero_spend_list.append(spend_bundle) spend_bundle = SpendBundle.aggregate(zero_spend_list) # Add transaction history hor this trade if chia_spend_bundle is not None: spend_bundle = SpendBundle.aggregate( [spend_bundle, chia_spend_bundle]) if chia_discrepancy < 0: tx_record = TransactionRecord( confirmed_at_index=uint32(0), created_at_time=uint64(int(time.time())), to_puzzle_hash=token_bytes(), amount=uint64(abs(chia_discrepancy)), fee_amount=uint64(0), incoming=False, confirmed=False, sent=uint32(10), spend_bundle=chia_spend_bundle, additions=chia_spend_bundle.additions(), removals=chia_spend_bundle.removals(), wallet_id=uint32(1), sent_to=[], ) else: tx_record = TransactionRecord( confirmed_at_index=uint32(0), created_at_time=uint64(int(time.time())), to_puzzle_hash=token_bytes(), amount=uint64(abs(chia_discrepancy)), fee_amount=uint64(0), incoming=True, confirmed=False, sent=uint32(10), spend_bundle=chia_spend_bundle, additions=chia_spend_bundle.additions(), removals=chia_spend_bundle.removals(), wallet_id=uint32(1), sent_to=[], ) my_tx_records.append(tx_record) for colour, amount in cc_discrepancies.items(): wallet = wallets[colour] if chia_discrepancy > 0: tx_record = TransactionRecord( confirmed_at_index=uint32(0), created_at_time=uint64(int(time.time())), to_puzzle_hash=token_bytes(), amount=uint64(abs(amount)), fee_amount=uint64(0), incoming=False, confirmed=False, sent=uint32(10), spend_bundle=spend_bundle, additions=spend_bundle.additions(), removals=spend_bundle.removals(), wallet_id=wallet.wallet_info.id, sent_to=[], ) else: tx_record = TransactionRecord( confirmed_at_index=uint32(0), created_at_time=uint64(int(time.time())), to_puzzle_hash=token_bytes(), amount=uint64(abs(amount)), fee_amount=uint64(0), incoming=True, confirmed=False, sent=uint32(10), spend_bundle=spend_bundle, additions=spend_bundle.additions(), removals=spend_bundle.removals(), wallet_id=wallet.wallet_info.id, sent_to=[], ) my_tx_records.append(tx_record) tx_record = TransactionRecord( confirmed_at_index=uint32(0), created_at_time=uint64(int(time.time())), to_puzzle_hash=token_bytes(), amount=uint64(0), fee_amount=uint64(0), incoming=False, confirmed=False, sent=uint32(0), spend_bundle=spend_bundle, additions=spend_bundle.additions(), removals=spend_bundle.removals(), wallet_id=uint32(0), sent_to=[], ) await self.wallet_state_manager.add_pending_transaction(tx_record) for tx in my_tx_records: await self.wallet_state_manager.add_transaction(tx) return True, None
def default_macro_lookup(eval): global DEFAULT_MACRO_LOOKUP if DEFAULT_MACRO_LOOKUP is None: DEFAULT_MACRO_LOOKUP = to_sexp_f([]) DEFAULT_MACRO_LOOKUP = build_default_macro_lookup(eval) return DEFAULT_MACRO_LOOKUP
def test_empty(self): v = to_sexp_f(b"") self.assertEqual(v.as_bin(), b"\x80")