async def generate_signed_transaction( self, amounts: List[uint64], puzzle_hashes: List[bytes32], fee: uint64 = uint64(0), origin_id: bytes32 = None, coins: Set[Coin] = None, ) -> TransactionRecord: sigs: List[G2Element] = [] # Get coins and calculate amount of change required outgoing_amount = uint64(sum(amounts)) if coins is None: selected_coins: Set[Coin] = await self.select_coins( uint64(outgoing_amount + fee)) else: selected_coins = coins total_amount = sum([x.amount for x in selected_coins]) change = total_amount - outgoing_amount - fee primaries = [] for amount, puzzle_hash in zip(amounts, puzzle_hashes): primaries.append({"puzzlehash": puzzle_hash, "amount": amount}) if change > 0: changepuzzlehash = await self.get_new_inner_hash() primaries.append({ "puzzlehash": changepuzzlehash, "amount": change }) if fee > 0: innersol = self.standard_wallet.make_solution(primaries=primaries, fee=fee) else: innersol = self.standard_wallet.make_solution(primaries=primaries) coin = selected_coins.pop() inner_puzzle = await self.inner_puzzle_for_cc_puzhash(coin.puzzle_hash) if self.cc_info.my_genesis_checker is None: raise ValueError("My genesis checker is None") genesis_id = genesis_coin_id_for_genesis_coin_checker( self.cc_info.my_genesis_checker) innersol_list = [innersol] sigs = sigs + await self.get_sigs(inner_puzzle, innersol, coin.name()) lineage_proof = await self.get_lineage_proof_for_coin(coin) assert lineage_proof is not None spendable_cc_list = [ SpendableCC(coin, genesis_id, inner_puzzle, lineage_proof) ] assert self.cc_info.my_genesis_checker is not None for coin in selected_coins: coin_inner_puzzle = await self.inner_puzzle_for_cc_puzhash( coin.puzzle_hash) innersol = self.standard_wallet.make_solution() innersol_list.append(innersol) sigs = sigs + await self.get_sigs(coin_inner_puzzle, innersol, coin.name()) spend_bundle = spend_bundle_for_spendable_ccs( CC_MOD, self.cc_info.my_genesis_checker, spendable_cc_list, innersol_list, sigs, ) # TODO add support for array in stored records return TransactionRecord( confirmed_at_sub_height=uint32(0), confirmed_at_height=uint32(0), created_at_time=uint64(int(time.time())), to_puzzle_hash=puzzle_hashes[0], amount=uint64(outgoing_amount), fee_amount=uint64(0), confirmed=False, sent=uint32(0), spend_bundle=spend_bundle, additions=spend_bundle.additions(), removals=spend_bundle.removals(), wallet_id=self.id(), sent_to=[], trade_id=None, type=uint32(TransactionType.OUTGOING_TX.value), name=spend_bundle.name(), )
async def generate_zero_val_coin(self, send=True, exclude: List[Coin] = None ) -> SpendBundle: if self.cc_info.my_genesis_checker is None: raise ValueError("My genesis checker is None") if exclude is None: exclude = [] coins = await self.standard_wallet.select_coins(0, exclude) assert coins != set() origin = coins.copy().pop() origin_id = origin.name() cc_inner = await self.get_new_inner_hash() cc_puzzle_hash: Program = cc_puzzle_hash_for_inner_puzzle_hash( CC_MOD, self.cc_info.my_genesis_checker, cc_inner) tx: TransactionRecord = await self.standard_wallet.generate_signed_transaction( uint64(0), cc_puzzle_hash, uint64(0), origin_id, coins) assert tx.spend_bundle is not None full_spend: SpendBundle = tx.spend_bundle self.log.info( f"Generate zero val coin: cc_puzzle_hash is {cc_puzzle_hash}") # generate eve coin so we can add future lineage_proofs even if we don't eve spend eve_coin = Coin(origin_id, cc_puzzle_hash, uint64(0)) await self.add_lineage( eve_coin.name(), Program.to(( 1, [eve_coin.parent_coin_info, cc_inner, eve_coin.amount], )), ) await self.add_lineage(eve_coin.parent_coin_info, Program.to((0, [origin.as_list(), 1]))) if send: regular_record = TransactionRecord( confirmed_at_sub_height=uint32(0), confirmed_at_height=uint32(0), created_at_time=uint64(int(time.time())), to_puzzle_hash=cc_puzzle_hash, amount=uint64(0), fee_amount=uint64(0), confirmed=False, sent=uint32(10), spend_bundle=full_spend, additions=full_spend.additions(), removals=full_spend.removals(), wallet_id=uint32(1), sent_to=[], trade_id=None, type=uint32(TransactionType.INCOMING_TX.value), name=token_bytes(), ) cc_record = TransactionRecord( confirmed_at_sub_height=uint32(0), confirmed_at_height=uint32(0), created_at_time=uint64(int(time.time())), to_puzzle_hash=cc_puzzle_hash, amount=uint64(0), fee_amount=uint64(0), confirmed=False, sent=uint32(0), spend_bundle=full_spend, additions=full_spend.additions(), removals=full_spend.removals(), wallet_id=self.id(), sent_to=[], trade_id=None, type=uint32(TransactionType.INCOMING_TX.value), name=full_spend.name(), ) await self.wallet_state_manager.add_transaction(regular_record) await self.wallet_state_manager.add_pending_transaction(cc_record) return full_spend
async def respond_to_offer( self, file_path: Path ) -> Tuple[bool, Optional[TradeRecord], Optional[str]]: has_wallets = await self.maybe_create_wallets_for_offer(file_path) if not has_wallets: return False, None, "Unknown Error" trade_offer = None try: trade_offer_hex = file_path.read_text() trade_offer = TradeRecord.from_bytes(hexstr_to_bytes(trade_offer_hex)) except Exception as e: return False, None, f"Error: {e}" if trade_offer is not None: offer_spend_bundle: SpendBundle = trade_offer.spend_bundle coinsols = [] # [] of CoinSolutions cc_coinsol_outamounts: Dict[bytes32, List[Tuple[Any, int]]] = dict() aggsig = offer_spend_bundle.aggregated_signature cc_discrepancies: Dict[bytes32, int] = dict() chia_discrepancy = None wallets: Dict[bytes32, Any] = dict() # colour to wallet dict for coinsol in offer_spend_bundle.coin_solutions: puzzle: Program = coinsol.solution.first() solution: Program = coinsol.solution.rest().first() # work out the deficits between coin amount and expected output for each r = cc_utils.uncurry_cc(puzzle) if r: # Calculate output amounts mod_hash, genesis_checker, inner_puzzle = r colour = bytes(genesis_checker).hex() 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].id() ) ) if coinsol.coin in [record.coin for record in unspent]: return False, None, "can't respond to own offer" innersol = solution.first() total = get_output_amount_for_puzzle_and_solution( inner_puzzle, innersol ) if colour in cc_discrepancies: cc_discrepancies[colour] += coinsol.coin.amount - total else: cc_discrepancies[colour] = coinsol.coin.amount - total # Store coinsol and output amount for later if colour in cc_coinsol_outamounts: cc_coinsol_outamounts[colour].append((coinsol, total)) else: cc_coinsol_outamounts[colour] = [(coinsol, total)] 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, None, "can't respond to own offer" if chia_discrepancy is None: chia_discrepancy = get_output_discrepancy_for_puzzle_and_solution( coinsol.coin, puzzle, solution ) else: chia_discrepancy += 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, [] ) if chia_spend_bundle is not None: for coinsol in coinsols: chia_spend_bundle.coin_solutions.append(coinsol) zero_spend_list: List[SpendBundle] = [] spend_bundle = None # 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, None, "Unable to generate zero value coin. Confirm that 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, None, "insufficient funds" # Create SpendableCC list and innersol_list with both my coins and the offered coins # Firstly get the output coin my_output_coin = my_cc_spends.pop() spendable_cc_list = [] innersol_list = [] genesis_id = genesis_coin_id_for_genesis_coin_checker( Program.from_bytes(bytes.fromhex(colour)) ) # Make the rest of the coins assert the output coin is consumed for coloured_coin in my_cc_spends: inner_solution = self.wallet_state_manager.main_wallet.make_solution( consumed=[my_output_coin.name()] ) inner_puzzle = await self.get_inner_puzzle_for_puzzle_hash( coloured_coin.puzzle_hash ) assert inner_puzzle is not None sigs = await wallets[colour].get_sigs( inner_puzzle, inner_solution, ) sigs.append(aggsig) aggsig = AugSchemeMPL.aggregate(sigs) lineage_proof = await wallets[colour].get_lineage_proof_for_coin( coloured_coin ) spendable_cc_list.append( SpendableCC(coloured_coin, genesis_id, inner_puzzle, lineage_proof) ) innersol_list.append(inner_solution) # Create SpendableCC for each of the coloured coins received for cc_coinsol_out in cc_coinsol_outamounts[colour]: cc_coinsol = cc_coinsol_out[0] puzzle = cc_coinsol.solution.first() solution = cc_coinsol.solution.rest().first() r = uncurry_cc(puzzle) if r: mod_hash, genesis_coin_checker, inner_puzzle = r inner_solution = solution.first() lineage_proof = solution.rest().rest().first() spendable_cc_list.append( SpendableCC( cc_coinsol.coin, genesis_id, inner_puzzle, lineage_proof ) ) innersol_list.append(inner_solution) # Finish the output coin SpendableCC with new information newinnerpuzhash = await wallets[colour].get_new_inner_hash() outputamount = ( sum([c.amount for c in my_cc_spends]) + cc_discrepancies[colour] + my_output_coin.amount ) inner_solution = self.wallet_state_manager.main_wallet.make_solution( primaries=[{"puzzlehash": newinnerpuzhash, "amount": outputamount}] ) inner_puzzle = await self.get_inner_puzzle_for_puzzle_hash( my_output_coin.puzzle_hash ) assert inner_puzzle is not None lineage_proof = await wallets[colour].get_lineage_proof_for_coin( my_output_coin ) spendable_cc_list.append( SpendableCC(my_output_coin, genesis_id, inner_puzzle, lineage_proof) ) innersol_list.append(inner_solution) sigs = await wallets[colour].get_sigs( inner_puzzle, inner_solution, ) sigs.append(aggsig) aggsig = AugSchemeMPL.aggregate(sigs) if spend_bundle is None: spend_bundle = spend_bundle_for_spendable_ccs( CC_MOD, Program.from_bytes(bytes.fromhex(colour)), spendable_cc_list, innersol_list, [aggsig], ) else: new_spend_bundle = spend_bundle_for_spendable_ccs( CC_MOD, Program.from_bytes(bytes.fromhex(colour)), spendable_cc_list, innersol_list, [aggsig], ) spend_bundle = SpendBundle.aggregate([spend_bundle, new_spend_bundle]) # reset sigs and aggsig so that they aren't included next time around sigs = [] aggsig = AugSchemeMPL.aggregate(sigs) my_tx_records = [] if zero_spend_list is not None and spend_bundle is not None: zero_spend_list.append(spend_bundle) spend_bundle = SpendBundle.aggregate(zero_spend_list) if spend_bundle is None: return False, None, "spend_bundle missing" # Add transaction history for this trade now = uint64(int(time.time())) if chia_spend_bundle is not None: spend_bundle = SpendBundle.aggregate([spend_bundle, chia_spend_bundle]) # debug_spend_bundle(spend_bundle) if chia_discrepancy < 0: tx_record = TransactionRecord( confirmed_at_index=uint32(0), created_at_time=now, 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=[], trade_id=std_hash(spend_bundle.name() + bytes(now)), ) 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=[], trade_id=std_hash(spend_bundle.name() + bytes(now)), ) 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.id(), sent_to=[], trade_id=std_hash(spend_bundle.name() + bytes(now)), ) 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.id(), sent_to=[], trade_id=std_hash(spend_bundle.name() + bytes(now)), ) 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=[], trade_id=std_hash(spend_bundle.name() + bytes(now)), ) now = uint64(int(time.time())) trade_record: TradeRecord = TradeRecord( confirmed_at_index=uint32(0), accepted_at_time=now, created_at_time=now, my_offer=False, sent=uint32(0), spend_bundle=offer_spend_bundle, tx_spend_bundle=spend_bundle, additions=spend_bundle.additions(), removals=spend_bundle.removals(), trade_id=std_hash(spend_bundle.name() + bytes(now)), status=uint32(TradeStatus.PENDING_CONFIRM.value), sent_to=[], ) await self.save_trade(trade_record) 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, trade_record, None
async def coin_added( self, coin: Coin, height: uint32, coinbase: bool, fee_reward: bool, wallet_id: uint32, wallet_type: WalletType, sub_height: uint32, ): """ Adding coin to DB """ self.log.info(f"Adding coin: {coin} at {sub_height}") farm_reward = False if coinbase or fee_reward: farm_reward = True now = uint64(int(time.time())) if coinbase: type = TransactionType.COINBASE_REWARD.value else: type = TransactionType.FEE_REWARD.value tx_record = TransactionRecord( confirmed_at_sub_height=uint32(sub_height), confirmed_at_height=uint32(height), created_at_time=now, to_puzzle_hash=coin.puzzle_hash, amount=coin.amount, fee_amount=uint64(0), confirmed=True, sent=uint32(0), spend_bundle=None, additions=[coin], removals=[], wallet_id=wallet_id, sent_to=[], trade_id=None, type=uint32(type), name=coin.name(), ) await self.tx_store.add_transaction_record(tx_record) else: records = await self.tx_store.tx_with_addition_coin(coin.name(), wallet_id) if len(records) > 0: # This is the change from this transaction for record in records: if record.confirmed is False: await self.tx_store.set_confirmed(record.name, sub_height, height) else: now = uint64(int(time.time())) tx_record = TransactionRecord( confirmed_at_sub_height=uint32(sub_height), confirmed_at_height=uint32(height), created_at_time=now, to_puzzle_hash=coin.puzzle_hash, amount=coin.amount, fee_amount=uint64(0), confirmed=True, sent=uint32(0), spend_bundle=None, additions=[coin], removals=[], wallet_id=wallet_id, sent_to=[], trade_id=None, type=uint32(TransactionType.INCOMING_TX.value), name=coin.name(), ) if coin.amount > 0: await self.tx_store.add_transaction_record(tx_record) coin_record: WalletCoinRecord = WalletCoinRecord( coin, sub_height, height, uint32(0), uint32(0), False, farm_reward, wallet_type, wallet_id ) await self.coin_store.add_coin_record(coin_record) if wallet_type == WalletType.COLOURED_COIN: wallet: CCWallet = self.wallets[wallet_id] # TODO(straya): should this use height to hash instead of sub_height to hash header_hash: bytes32 = self.blockchain.sub_height_to_hash[height] block: Optional[HeaderBlockRecord] = await self.block_store.get_header_block_record(header_hash) assert block is not None assert block.removals is not None await wallet.coin_added(coin, height, header_hash, block.removals, sub_height) self.state_changed("coin_added", wallet_id)
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. Confirm that 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
async def get_transaction(self, wallet_id: str, transaction_id: bytes32) -> TransactionRecord: res = await self.fetch( "get_transaction", {"walled_id": wallet_id, "transaction_id": transaction_id.hex()}, ) return TransactionRecord.from_json_dict(res["transaction"])