예제 #1
0
 async def get_locked_coins_in_spend_bundle(self, bundle: SpendBundle) -> Dict[bytes32, WalletCoinRecord]:
     """ Returns a list of coin records that are used in this SpendBundle"""
     result = {}
     removals = bundle.removals()
     for coin in removals:
         coin_record = await self.wallet_state_manager.coin_store.get_coin_record_by_coin_id(coin.name())
         if coin_record is None:
             continue
         result[coin_record.name()] = coin_record
     return result
예제 #2
0
 def update_coin_store_for_spend_bundle(self, spend_bundle: SpendBundle, now: CoinTimestamp):
     err = self.validate_spend_bundle(spend_bundle, now)
     if err != 0:
         raise BadSpendBundleError(f"validation failure {err}")
     for spent_coin in spend_bundle.removals():
         coin_name = spent_coin.name()
         coin_record = self._db[coin_name]
         self._db[coin_name] = replace(coin_record, spent_block_index=now.height, spent=True)
     for new_coin in spend_bundle.additions():
         self._add_coin_entry(new_coin, now)
    async def add_pending_transaction(self, spend_bundle: SpendBundle,
                                      wallet_id):
        """
        Called from wallet_node before new transaction is sent to the full_node
        """
        now = uint64(int(time.time()))
        add_list: List[Coin] = []
        rem_list: List[Coin] = []
        total_removed = 0
        total_added = 0
        outgoing_amount = 0

        for add in spend_bundle.additions():
            total_added += add.amount
            add_list.append(add)
        for rem in spend_bundle.removals():
            total_removed += rem.amount
            rem_list.append(rem)

        fee_amount = total_removed - total_added

        # Figure out if we are sending to ourself or someone else.
        to_puzzle_hash: Optional[bytes32] = None
        for add in add_list:
            if not await self.puzzle_store.puzzle_hash_exists(add.puzzle_hash):
                to_puzzle_hash = add.puzzle_hash
                outgoing_amount += add.amount
                break

        # If there is no addition for outside puzzlehash we are sending tx to ourself
        if to_puzzle_hash is None:
            to_puzzle_hash = add_list[0].puzzle_hash
            outgoing_amount += total_added

        tx_record = TransactionRecord(
            confirmed_at_index=uint32(0),
            created_at_time=now,
            to_puzzle_hash=to_puzzle_hash,
            amount=uint64(outgoing_amount),
            fee_amount=uint64(fee_amount),
            incoming=False,
            confirmed=False,
            sent=uint32(0),
            spend_bundle=spend_bundle,
            additions=add_list,
            removals=rem_list,
            wallet_id=wallet_id,
            sent_to=[],
        )
        # Wallet node will use this queue to retry sending this transaction until full nodes receives it
        await self.tx_store.add_transaction_record(tx_record)
        self.state_changed("pending_transaction")
        self.tx_pending_changed()
예제 #4
0
    async def generate_signed_transaction(
        self,
        amount: uint64,
        to_address: bytes32,
        fee: uint64 = uint64(0),
        origin_id: bytes32 = None,
        coins: Set[Coin] = None,
    ) -> Optional[TransactionRecord]:
        sigs: List[G2Element] = []

        # Get coins and calculate amount of change required
        if coins is None:
            selected_coins: Optional[Set[Coin]] = await self.select_coins(
                amount)
        else:
            selected_coins = coins
        if selected_coins is None:
            return None

        total_amount = sum([x.amount for x in selected_coins])
        change = total_amount - amount

        # first coin becomes the auditor special case
        auditor = selected_coins.pop()
        puzzle_hash = auditor.puzzle_hash
        inner_puzzle: Program = await self.inner_puzzle_for_cc_puzzle(
            puzzle_hash)

        auditor_info = (
            auditor.parent_coin_info,
            inner_puzzle.get_tree_hash(),
            auditor.amount,
        )
        list_of_solutions = []

        # auditees should be (primary_input, innerpuzhash, coin_amount, output_amount)
        auditees = [(
            auditor.parent_coin_info,
            inner_puzzle.get_tree_hash(),
            auditor.amount,
            total_amount,
        )]
        for coin in selected_coins:
            coin_inner_puzzle: Program = await self.inner_puzzle_for_cc_puzzle(
                coin.puzzle_hash)
            auditees.append((
                coin.parent_coin_info,
                coin_inner_puzzle[coin],
                coin.amount,
                0,
            ))

        primaries = [{"puzzlehash": to_address, "amount": amount}]
        if change > 0:
            changepuzzlehash = await self.get_new_inner_hash()
            primaries.append({
                "puzzlehash": changepuzzlehash,
                "amount": change
            })

        innersol = self.standard_wallet.make_solution(primaries=primaries)
        sigs = sigs + await self.get_sigs(inner_puzzle, innersol)
        parent_info = await self.get_parent_for_coin(auditor)
        assert parent_info is not None
        assert self.cc_info.my_core is not None

        solution = cc_wallet_puzzles.cc_make_solution(
            self.cc_info.my_core,
            (
                parent_info.parent_name,
                parent_info.inner_puzzle_hash,
                parent_info.amount,
            ),
            auditor.amount,
            binutils.disassemble(inner_puzzle),
            binutils.disassemble(innersol),
            auditor_info,
            auditees,
            False,
        )

        main_coin_solution = CoinSolution(
            auditor,
            Program.to([
                cc_wallet_puzzles.cc_make_puzzle(
                    inner_puzzle.get_tree_hash(),
                    self.cc_info.my_core,
                ),
                solution,
            ]),
        )
        list_of_solutions.append(main_coin_solution)
        # main = SpendBundle([main_coin_solution], ZERO96)

        ephemeral_coin_solution = create_spend_for_ephemeral(
            auditor, auditor, total_amount)
        list_of_solutions.append(ephemeral_coin_solution)
        # eph = SpendBundle([ephemeral_coin_solution], ZERO96)

        auditor_coin_colution = create_spend_for_auditor(auditor, auditor)
        list_of_solutions.append(auditor_coin_colution)
        # aud = SpendBundle([auditor_coin_colution], ZERO96)

        # loop through remaining spends, treating them as aggregatees
        for coin in selected_coins:
            coin_inner_puzzle = await self.inner_puzzle_for_cc_puzzle(
                coin.puzzle_hash)
            innersol = self.standard_wallet.make_solution()
            parent_info = await self.get_parent_for_coin(coin)
            assert parent_info is not None
            sigs = sigs + await self.get_sigs(coin_inner_puzzle, innersol)

            solution = cc_wallet_puzzles.cc_make_solution(
                self.cc_info.my_core,
                (
                    parent_info.parent_name,
                    parent_info.inner_puzzle_hash,
                    parent_info.amount,
                ),
                coin.amount,
                binutils.disassemble(coin_inner_puzzle),
                binutils.disassemble(innersol),
                auditor_info,
                None,
            )
            list_of_solutions.append(
                CoinSolution(
                    coin,
                    Program.to([
                        cc_wallet_puzzles.cc_make_puzzle(
                            coin_inner_puzzle.get_tree_hash(),
                            self.cc_info.my_core,
                        ),
                        solution,
                    ]),
                ))
            list_of_solutions.append(
                create_spend_for_ephemeral(coin, auditor, 0))
            list_of_solutions.append(create_spend_for_auditor(auditor, coin))

        aggsig = AugSchemeMPL.aggregate(sigs)
        spend_bundle = SpendBundle(list_of_solutions, aggsig)

        tx_record = TransactionRecord(
            confirmed_at_index=uint32(0),
            created_at_time=uint64(int(time.time())),
            to_puzzle_hash=to_address,
            amount=uint64(amount),
            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=self.wallet_info.id,
            sent_to=[],
            trade_id=None,
        )

        return tx_record
예제 #5
0
def debug_spend_bundle(spend_bundle: SpendBundle) -> None:
    """
    Print a lot of useful information about a `SpendBundle` that might help with debugging
    its clvm.
    """

    assert_consumed_set = set()

    print("=" * 80)
    for coin_solution in spend_bundle.coin_solutions:
        coin, solution_pair = coin_solution.coin, coin_solution.solution
        puzzle_reveal = solution_pair.first()
        solution = solution_pair.rest().first()

        print(f"consuming coin {dump_coin(coin)}")
        print(f"  with id {coin.name()}")
        print()
        print(
            f"\nbrun -y main.sym '{bu_disassemble(puzzle_reveal)}' '{bu_disassemble(solution)}'"
        )
        error, conditions, cost = conditions_dict_for_solution(
            Program.to([puzzle_reveal, solution]))
        if error:
            print(f"*** error {error}")
        else:
            print()
            cost, r = run_program(puzzle_reveal, solution)
            print(disassemble(r))
            print()
            if conditions and len(conditions) > 0:
                print("grouped conditions:")
                for condition_programs in conditions.values():
                    print()
                    for c in condition_programs:
                        as_prog = Program.to([c.opcode, c.var1, c.var2])
                        print(f"  {disassemble(as_prog)}")
                print()
                for _ in conditions.get(ConditionOpcode.ASSERT_COIN_CONSUMED,
                                        []):
                    assert_consumed_set.add(bytes32(_.var1))
            else:
                print("(no output conditions generated)")
        print()
        print("-------")

    created = set(spend_bundle.additions())
    spent = set(spend_bundle.removals())

    zero_coin_set = set(coin.name() for coin in created if coin.amount == 0)

    ephemeral = created.intersection(spent)
    created.difference_update(ephemeral)
    spent.difference_update(ephemeral)
    print()
    print("spent coins")
    for coin in sorted(spent, key=lambda _: _.name()):
        print(f"  {dump_coin(coin)}")
        print(f"      => spent coin id {coin.name()}")
    print()
    print("created coins")
    for coin in sorted(created, key=lambda _: _.name()):
        print(f"  {dump_coin(coin)}")
        print(f"      => created coin id {coin.name()}")

    if ephemeral:
        print()
        print("ephemeral coins")
        for coin in sorted(ephemeral, key=lambda _: _.name()):
            print(f"  {dump_coin(coin)}")
            print(f"      => created coin id {coin.name()}")

    print()
    print(f"assert_consumed_set = {sorted(assert_consumed_set)}")
    print()
    print(f"zero_coin_set = {sorted(zero_coin_set)}")
    print()
    set_difference = zero_coin_set ^ assert_consumed_set
    print(f"zero_coin_set ^ assert_consumed_set = {sorted(set_difference)}")
    if len(set_difference):
        print(
            "not all zero coins asserted consumed or vice versa, entering debugger"
        )
        breakpoint()

    print()
    print("=" * 80)
def debug_spend_bundle(spend_bundle: SpendBundle) -> None:
    """
    Print a lot of useful information about a `SpendBundle` that might help with debugging
    its clvm.
    """

    assert_consumed_set = set()

    pks = []
    msgs = []

    print("=" * 80)
    for coin_solution in spend_bundle.coin_solutions:
        coin, solution_pair = coin_solution.coin, Program.to(
            coin_solution.solution)
        puzzle_reveal = solution_pair.first()
        solution = solution_pair.rest().first()

        print(f"consuming coin {dump_coin(coin)}")
        print(f"  with id {coin.name()}")
        print()
        print(
            f"\nbrun -y main.sym '{bu_disassemble(puzzle_reveal)}' '{bu_disassemble(solution)}'"
        )
        error, conditions, cost = conditions_dict_for_solution(
            Program.to([puzzle_reveal, solution]))
        if error:
            print(f"*** error {error}")
        elif conditions is not None:
            for pk, m in pkm_pairs_for_conditions_dict(conditions,
                                                       coin.name()):
                pks.append(pk)
                msgs.append(m)
            print()
            r = puzzle_reveal.run(solution)
            print(disassemble(r))
            print()
            if conditions and len(conditions) > 0:
                print("grouped conditions:")
                for condition_programs in conditions.values():
                    print()
                    for c in condition_programs:
                        as_prog = Program.to([c.opcode, c.vars[0], c.vars[1]])
                        print(f"  {disassemble(as_prog)}")
                print()
                for _ in conditions.get(ConditionOpcode.ASSERT_COIN_CONSUMED,
                                        []):
                    assert_consumed_set.add(bytes32(c.vars[0]))
            else:
                print("(no output conditions generated)")
        print()
        print("-------")

    created = set(spend_bundle.additions())
    spent = set(spend_bundle.removals())

    zero_coin_set = set(coin.name() for coin in created if coin.amount == 0)

    ephemeral = created.intersection(spent)
    created.difference_update(ephemeral)
    spent.difference_update(ephemeral)
    print()
    print("spent coins")
    for coin in sorted(spent, key=lambda _: _.name()):
        print(f"  {dump_coin(coin)}")
        print(f"      => spent coin id {coin.name()}")
    print()
    print("created coins")
    for coin in sorted(created, key=lambda _: _.name()):
        print(f"  {dump_coin(coin)}")
        print(f"      => created coin id {coin.name()}")

    if ephemeral:
        print()
        print("ephemeral coins")
        for coin in sorted(ephemeral, key=lambda _: _.name()):
            print(f"  {dump_coin(coin)}")
            print(f"      => created coin id {coin.name()}")

    print()
    print(f"assert_consumed_set = {sorted(assert_consumed_set)}")
    print()
    print(f"zero_coin_set = {sorted(zero_coin_set)}")
    print()
    set_difference = zero_coin_set ^ assert_consumed_set
    print(f"zero_coin_set ^ assert_consumed_set = {sorted(set_difference)}")
    if len(set_difference):
        print("not all zero coins asserted consumed or vice versa")

    print()
    print("=" * 80)
    print()
    if len(msgs) > 0:
        validates = AugSchemeMPL.aggregate_verify(
            pks, msgs, spend_bundle.aggregated_signature)
        print(f"aggregated signature check pass: {validates}")
예제 #7
0
def debug_spend_bundle(spend_bundle: SpendBundle) -> None:
    """
    Print a lot of useful information about a `SpendBundle` that might help with debugging
    its clvm.
    """

    pks = []
    msgs = []

    created_announcements: List[List[bytes]] = []
    asserted_annoucements = []

    print("=" * 80)
    for coin_solution in spend_bundle.coin_solutions:
        coin = coin_solution.coin
        puzzle_reveal = coin_solution.puzzle_reveal
        solution = coin_solution.solution
        coin_name = coin.name()

        print(f"consuming coin {dump_coin(coin)}")
        print(f"  with id {coin_name}")
        print()
        print(f"\nbrun -y main.sym '{bu_disassemble(puzzle_reveal)}' '{bu_disassemble(solution)}'")
        error, conditions, cost = conditions_dict_for_solution(puzzle_reveal, solution)
        if error:
            print(f"*** error {error}")
        elif conditions is not None:
            for pk, m in pkm_pairs_for_conditions_dict(conditions, coin_name):
                pks.append(pk)
                msgs.append(m)
            print()
            r = puzzle_reveal.run(solution)
            print(disassemble(r))
            print()
            if conditions and len(conditions) > 0:
                print("grouped conditions:")
                for condition_programs in conditions.values():
                    print()
                    for c in condition_programs:
                        if len(c.vars) == 1:
                            as_prog = Program.to([c.opcode, c.vars[0]])
                        if len(c.vars) == 2:
                            as_prog = Program.to([c.opcode, c.vars[0], c.vars[1]])
                        print(f"  {disassemble(as_prog)}")
                created_announcements.extend(
                    [coin_name] + _.vars for _ in conditions.get(ConditionOpcode.CREATE_ANNOUNCEMENT, [])
                )
                asserted_annoucements.extend(
                    [_.vars[0].hex() for _ in conditions.get(ConditionOpcode.ASSERT_ANNOUNCEMENT, [])]
                )
                print()
            else:
                print("(no output conditions generated)")
        print()
        print("-------")

    created = set(spend_bundle.additions())
    spent = set(spend_bundle.removals())

    zero_coin_set = set(coin.name() for coin in created if coin.amount == 0)

    ephemeral = created.intersection(spent)
    created.difference_update(ephemeral)
    spent.difference_update(ephemeral)
    print()
    print("spent coins")
    for coin in sorted(spent, key=lambda _: _.name()):
        print(f"  {dump_coin(coin)}")
        print(f"      => spent coin id {coin.name()}")
    print()
    print("created coins")
    for coin in sorted(created, key=lambda _: _.name()):
        print(f"  {dump_coin(coin)}")
        print(f"      => created coin id {coin.name()}")

    if ephemeral:
        print()
        print("ephemeral coins")
        for coin in sorted(ephemeral, key=lambda _: _.name()):
            print(f"  {dump_coin(coin)}")
            print(f"      => created coin id {coin.name()}")

    created_announcement_pairs = [(_, std_hash(b"".join(_)).hex()) for _ in created_announcements]
    if created_announcements:
        print("created announcements")
        for announcement, hashed in sorted(created_announcement_pairs, key=lambda _: _[-1]):
            as_hex = [f"0x{_.hex()}" for _ in announcement]
            print(f"  {as_hex} =>\n      {hashed}")

    eor_announcements = sorted(set(_[-1] for _ in created_announcement_pairs) ^ set(asserted_annoucements))

    print()
    print()
    print(f"zero_coin_set = {sorted(zero_coin_set)}")
    print()
    print(f"created announcements = {sorted([_[-1] for _ in created_announcement_pairs])}")
    print()
    print(f"asserted announcements = {sorted(asserted_annoucements)}")
    print()
    print(f"symdiff of announcements = {sorted(eor_announcements)}")
    print()
    print()
    print("=" * 80)
    print()
    validates = AugSchemeMPL.aggregate_verify(pks, msgs, spend_bundle.aggregated_signature)
    print(f"aggregated signature check pass: {validates}")
예제 #8
0
    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_hex = file_path.read_text()
        trade_offer: TradeRecord = TradeRecord.from_bytes(
            hexstr_to_bytes(trade_offer_hex))
        offer_spend_bundle = trade_offer.spend_bundle

        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 = 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 = 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):

                if not cc_wallet_puzzles.is_ephemeral_solution(solution):
                    # Calculate output amounts
                    colour = cc_wallet_puzzles.get_genesis_from_puzzle(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, None, "can't respond to own offer"
                    innerpuzzlereveal = cc_wallet_puzzles.inner_puzzle(
                        solution)
                    innersol = cc_wallet_puzzles.inner_puzzle_solution(
                        solution)
                    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, None, "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,
                            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"

            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,
            )
            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 = AugSchemeMPL.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,
                    Program.to([
                        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]
                new_solution = cc_wallet_puzzles.update_auditors_in_solution(
                    cc_coinsol.solution, auditor_info)
                new_coinsol = CoinSolution(cc_coinsol.coin, new_solution)
                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,
            ))

            sigs: List[G2Element] = await wallets[colour].get_sigs(
                auditor_inner_puzzle, innersol)
            aggsig = AugSchemeMPL.aggregate(sigs + [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,
                Program.to([
                    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
        now = uint64(int(time.time()))
        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=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.wallet_info.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.wallet_info.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