Ejemplo n.º 1
0
    async def coin_added(self, coin: Coin, _: uint32):
        """Notification from wallet state manager that wallet has been received."""
        self.log.info("DID wallet has been notified that coin was added")
        inner_puzzle = await self.inner_puzzle_for_did_puzzle(coin.puzzle_hash)
        if self.did_info.temp_coin is not None:
            self.wallet_state_manager.state_changed("did_coin_added",
                                                    self.wallet_info.id)
        new_info = DIDInfo(
            self.did_info.origin_coin,
            self.did_info.backup_ids,
            self.did_info.num_of_backup_ids_needed,
            self.did_info.parent_info,
            inner_puzzle,
            None,
            None,
            None,
            False,
        )
        await self.save_info(new_info, True)

        future_parent = LineageProof(
            coin.parent_coin_info,
            inner_puzzle.get_tree_hash(),
            coin.amount,
        )

        await self.add_parent(coin.name(), future_parent, True)
Ejemplo n.º 2
0
    async def coin_added(self, coin: Coin, _: uint32):
        """Notification from wallet state manager that wallet has been received."""
        self.log.info(f"DID wallet has been notified that coin was added: {coin.name()}:{coin}")
        inner_puzzle = await self.inner_puzzle_for_did_puzzle(coin.puzzle_hash)
        if self.did_info.temp_coin is not None:
            self.wallet_state_manager.state_changed("did_coin_added", self.wallet_info.id)
        new_info = DIDInfo(
            self.did_info.origin_coin,
            self.did_info.backup_ids,
            self.did_info.num_of_backup_ids_needed,
            self.did_info.parent_info,
            inner_puzzle,
            None,
            None,
            None,
            False,
        )
        await self.save_info(new_info, True)

        future_parent = LineageProof(
            coin.parent_coin_info,
            inner_puzzle.get_tree_hash(),
            coin.amount,
        )

        await self.add_parent(coin.name(), future_parent, True)
        parent = self.get_parent_for_coin(coin)
        if parent is None:
            parent_state: CoinState = (
                await self.wallet_state_manager.wallet_node.get_coin_state([coin.parent_coin_info])
            )[0]
            node = self.wallet_state_manager.wallet_node.get_full_node_peer()
            assert parent_state.spent_height is not None
            puzzle_solution_request = wallet_protocol.RequestPuzzleSolution(
                coin.parent_coin_info, parent_state.spent_height
            )
            response = await node.request_puzzle_solution(puzzle_solution_request)
            req_puz_sol = response.response
            assert req_puz_sol.puzzle is not None
            parent_innerpuz = did_wallet_puzzles.get_innerpuzzle_from_puzzle(req_puz_sol.puzzle)
            assert parent_innerpuz is not None
            parent_info = LineageProof(
                parent_state.coin.parent_coin_info,
                parent_innerpuz.get_tree_hash(),
                parent_state.coin.amount,
            )
            await self.add_parent(coin.parent_coin_info, parent_info, False)
Ejemplo n.º 3
0
class SpendableCAT:
    coin: Coin
    limitations_program_hash: bytes32
    inner_puzzle: Program
    inner_solution: Program
    limitations_solution: Program = Program.to([])
    lineage_proof: LineageProof = LineageProof()
    extra_delta: int = 0
    limitations_program_reveal: Program = Program.to([])
Ejemplo n.º 4
0
    async def generate_issuance_bundle(
            cls, wallet, _: Dict,
            amount: uint64) -> Tuple[TransactionRecord, SpendBundle]:
        coins = await wallet.standard_wallet.select_coins(amount)

        origin = coins.copy().pop()
        origin_id = origin.name()

        cc_inner: Program = await wallet.get_new_inner_puzzle()
        await wallet.add_lineage(origin_id, LineageProof())
        genesis_coin_checker: Program = cls.construct([Program.to(origin_id)])

        minted_cc_puzzle_hash: bytes32 = construct_cat_puzzle(
            CAT_MOD, genesis_coin_checker.get_tree_hash(),
            cc_inner).get_tree_hash()

        tx_record: TransactionRecord = await wallet.standard_wallet.generate_signed_transaction(
            amount, minted_cc_puzzle_hash, uint64(0), origin_id, coins)
        assert tx_record.spend_bundle is not None

        inner_solution = wallet.standard_wallet.add_condition_to_solution(
            Program.to([51, 0, -113, genesis_coin_checker, []]),
            wallet.standard_wallet.make_solution(primaries=[{
                "puzzlehash":
                cc_inner.get_tree_hash(),
                "amount":
                amount
            }], ),
        )
        eve_spend = unsigned_spend_bundle_for_spendable_cats(
            CAT_MOD,
            [
                SpendableCAT(
                    list(
                        filter(lambda a: a.amount == amount,
                               tx_record.additions))[0],
                    genesis_coin_checker.get_tree_hash(),
                    cc_inner,
                    inner_solution,
                    limitations_program_reveal=genesis_coin_checker,
                )
            ],
        )
        signed_eve_spend = await wallet.sign(eve_spend)

        if wallet.cat_info.my_tail is None:
            await wallet.save_info(
                CATInfo(genesis_coin_checker.get_tree_hash(),
                        genesis_coin_checker, wallet.cat_info.lineage_proofs),
                False,
            )

        return tx_record, SpendBundle.aggregate(
            [tx_record.spend_bundle, signed_eve_spend])
Ejemplo n.º 5
0
def lineage_proof_for_coinsol(coin_spend: CoinSpend) -> LineageProof:
    parent_name: bytes32 = coin_spend.coin.parent_coin_info

    inner_puzzle_hash: Optional[bytes32] = None
    if coin_spend.coin.puzzle_hash != SINGLETON_LAUNCHER_HASH:
        full_puzzle = Program.from_bytes(bytes(coin_spend.puzzle_reveal))
        r = full_puzzle.uncurry()
        if r is not None:
            _, args = r
            _, inner_puzzle = list(args.as_iter())
            inner_puzzle_hash = inner_puzzle.get_tree_hash()

    amount: uint64 = coin_spend.coin.amount

    return LineageProof(
        parent_name,
        inner_puzzle_hash,
        amount,
    )
Ejemplo n.º 6
0
 async def puzzle_solution_received(self, response: PuzzleSolutionResponse,
                                    action_id: int):
     coin_name = response.coin_name
     puzzle: Program = response.puzzle
     matched, curried_args = match_cat_puzzle(puzzle)
     if matched:
         mod_hash, genesis_coin_checker_hash, inner_puzzle = curried_args
         self.log.info(
             f"parent: {coin_name} inner_puzzle for parent is {inner_puzzle}"
         )
         parent_coin = None
         coin_record = await self.wallet_state_manager.coin_store.get_coin_record(
             coin_name)
         if coin_record is None:
             coin_states: Optional[List[
                 CoinState]] = await self.wallet_state_manager.wallet_node.get_coin_state(
                     [coin_name])
             if coin_states is not None:
                 parent_coin = coin_states[0].coin
         if coin_record is not None:
             parent_coin = coin_record.coin
         if parent_coin is None:
             raise ValueError("Error in finding parent")
         await self.add_lineage(
             coin_name,
             LineageProof(parent_coin.parent_coin_info,
                          inner_puzzle.get_tree_hash(), parent_coin.amount))
         await self.wallet_state_manager.action_store.action_done(action_id)
     else:
         # The parent is not a CAT which means we need to scrub all of its children from our DB
         child_coin_records = await self.wallet_state_manager.coin_store.get_coin_records_by_parent_id(
             coin_name)
         if len(child_coin_records) > 0:
             for record in child_coin_records:
                 if record.wallet_id == self.id():
                     await self.wallet_state_manager.coin_store.delete_coin_record(
                         record.coin.name())
                     await self.remove_lineage(record.coin.name())
                     # We also need to make sure there's no record of the transaction
                     await self.wallet_state_manager.tx_store.delete_transaction_record(
                         record.coin.name())
Ejemplo n.º 7
0
    async def coin_added(self, coin: Coin, height: uint32):
        """Notification from wallet state manager that wallet has been received."""
        self.log.info(f"CC wallet has been notified that {coin} was added")
        search_for_parent: bool = True

        inner_puzzle = await self.inner_puzzle_for_cc_puzhash(coin.puzzle_hash)
        lineage_proof = LineageProof(coin.parent_coin_info,
                                     inner_puzzle.get_tree_hash(), coin.amount)
        await self.add_lineage(coin.name(), lineage_proof, True)

        for name, lineage_proofs in self.cat_info.lineage_proofs:
            if coin.parent_coin_info == name:
                search_for_parent = False
                break

        if search_for_parent:
            data: Dict[str, Any] = {
                "data": {
                    "action_data": {
                        "api_name": "request_puzzle_solution",
                        "height": height,
                        "coin_name": coin.parent_coin_info,
                        "received_coin": coin.name(),
                    }
                }
            }

            data_str = dict_to_json_str(data)
            await self.wallet_state_manager.create_action(
                name="request_puzzle_solution",
                wallet_id=self.id(),
                wallet_type=self.type(),
                callback="puzzle_solution_received",
                done=False,
                data=data_str,
                in_transaction=True,
            )
Ejemplo n.º 8
0
    def to_valid_spend(self,
                       arbitrage_ph: Optional[bytes32] = None) -> SpendBundle:
        if not self.is_valid():
            raise ValueError("Offer is currently incomplete")

        completion_spends: List[CoinSpend] = []
        for tail_hash, payments in self.requested_payments.items():
            offered_coins: List[Coin] = self.get_offered_coins()[tail_hash]

            # Because of CAT supply laws, we must specify a place for the leftovers to go
            arbitrage_amount: int = self.arbitrage()[tail_hash]
            all_payments: List[NotarizedPayment] = payments.copy()
            if arbitrage_amount > 0:
                assert arbitrage_amount is not None
                assert arbitrage_ph is not None
                all_payments.append(
                    NotarizedPayment(arbitrage_ph, uint64(arbitrage_amount),
                                     []))

            for coin in offered_coins:
                inner_solutions = []
                if coin == offered_coins[0]:
                    nonces: List[bytes32] = [p.nonce for p in all_payments]
                    for nonce in list(dict.fromkeys(
                            nonces)):  # dedup without messing with order
                        nonce_payments: List[NotarizedPayment] = list(
                            filter(lambda p: p.nonce == nonce, all_payments))
                        inner_solutions.append(
                            (nonce,
                             [np.as_condition_args()
                              for np in nonce_payments]))

                if tail_hash:
                    # CATs have a special way to be solved so we have to do some calculation before getting the solution
                    parent_spend: CoinSpend = list(
                        filter(
                            lambda cs: cs.coin.name() == coin.parent_coin_info,
                            self.bundle.coin_spends))[0]
                    parent_coin: Coin = parent_spend.coin
                    matched, curried_args = match_cat_puzzle(
                        parent_spend.puzzle_reveal.to_program())
                    assert matched
                    _, _, inner_puzzle = curried_args
                    spendable_cat = SpendableCAT(
                        coin,
                        tail_hash,
                        OFFER_MOD,
                        Program.to(inner_solutions),
                        lineage_proof=LineageProof(
                            parent_coin.parent_coin_info,
                            inner_puzzle.get_tree_hash(), parent_coin.amount),
                    )
                    solution: Program = (
                        unsigned_spend_bundle_for_spendable_cats(
                            CAT_MOD, [spendable_cat
                                      ]).coin_spends[0].solution.to_program())
                else:
                    solution = Program.to(inner_solutions)

                completion_spends.append(
                    CoinSpend(
                        coin,
                        construct_cat_puzzle(CAT_MOD, tail_hash, OFFER_MOD)
                        if tail_hash else OFFER_MOD,
                        solution,
                    ))

        return SpendBundle.aggregate(
            [SpendBundle(completion_spends, G2Element()), self.bundle])
Ejemplo n.º 9
0
    async def generate_new_decentralised_id(
            self, amount: uint64) -> Optional[SpendBundle]:
        """
        This must be called under the wallet state manager lock
        """

        coins = await self.standard_wallet.select_coins(amount)
        if coins is None:
            return None

        origin = coins.copy().pop()
        genesis_launcher_puz = did_wallet_puzzles.SINGLETON_LAUNCHER
        launcher_coin = Coin(origin.name(),
                             genesis_launcher_puz.get_tree_hash(), amount)

        did_inner: Program = await self.get_new_innerpuz()
        did_inner_hash = did_inner.get_tree_hash()
        did_full_puz = did_wallet_puzzles.create_fullpuz(
            did_inner, launcher_coin.name())
        did_puzzle_hash = did_full_puz.get_tree_hash()

        announcement_set: Set[Announcement] = set()
        announcement_message = Program.to(
            [did_puzzle_hash, amount, bytes(0x80)]).get_tree_hash()
        announcement_set.add(
            Announcement(launcher_coin.name(), announcement_message).name())

        tx_record: Optional[
            TransactionRecord] = await self.standard_wallet.generate_signed_transaction(
                amount, genesis_launcher_puz.get_tree_hash(), uint64(0),
                origin.name(), coins, None, False, announcement_set)

        genesis_launcher_solution = Program.to(
            [did_puzzle_hash, amount, bytes(0x80)])

        launcher_cs = CoinSolution(launcher_coin, genesis_launcher_puz,
                                   genesis_launcher_solution)
        launcher_sb = SpendBundle([launcher_cs], AugSchemeMPL.aggregate([]))
        eve_coin = Coin(launcher_coin.name(), did_puzzle_hash, amount)
        future_parent = LineageProof(
            eve_coin.parent_coin_info,
            did_inner_hash,
            eve_coin.amount,
        )
        eve_parent = LineageProof(
            launcher_coin.parent_coin_info,
            launcher_coin.puzzle_hash,
            launcher_coin.amount,
        )
        await self.add_parent(eve_coin.parent_coin_info, eve_parent, False)
        await self.add_parent(eve_coin.name(), future_parent, False)

        if tx_record is None or tx_record.spend_bundle is None:
            return None

        # Only want to save this information if the transaction is valid
        did_info: DIDInfo = DIDInfo(
            launcher_coin,
            self.did_info.backup_ids,
            self.did_info.num_of_backup_ids_needed,
            self.did_info.parent_info,
            did_inner,
            None,
            None,
            None,
        )
        await self.save_info(did_info, False)
        eve_spend = await self.generate_eve_spend(eve_coin, did_full_puz,
                                                  did_inner)
        full_spend = SpendBundle.aggregate(
            [tx_record.spend_bundle, eve_spend, launcher_sb])
        return full_spend
Ejemplo n.º 10
0
    async def load_backup(self, filename: str):
        try:
            f = open(filename, "r")
            details = f.readline().split(":")
            f.close()
            origin = Coin(bytes.fromhex(details[0]), bytes.fromhex(details[1]),
                          uint64(int(details[2])))
            backup_ids = []
            for d in details[3].split(","):
                backup_ids.append(bytes.fromhex(d))
            num_of_backup_ids_needed = uint64(int(details[5]))
            if num_of_backup_ids_needed > len(backup_ids):
                raise Exception
            innerpuz = Program.from_bytes(bytes.fromhex(details[4]))
            did_info = DIDInfo(
                origin,
                backup_ids,
                num_of_backup_ids_needed,
                self.did_info.parent_info,
                innerpuz,
                None,
                None,
                None,
            )
            await self.save_info(did_info, False)
            await self.wallet_state_manager.update_wallet_puzzle_hashes(
                self.wallet_info.id)

            full_puz = did_wallet_puzzles.create_fullpuz(
                innerpuz, origin.name())
            full_puzzle_hash = full_puz.get_tree_hash()
            (
                sub_height,
                header_hash,
            ) = await self.wallet_state_manager.search_blockrecords_for_puzzlehash(
                full_puzzle_hash)
            assert sub_height is not None
            assert header_hash is not None
            full_nodes = self.wallet_state_manager.server.connection_by_type[
                NodeType.FULL_NODE]
            additions: Union[RespondAdditions, RejectAdditionsRequest,
                             None] = None
            for id, node in full_nodes.items():
                request = wallet_protocol.RequestAdditions(
                    sub_height, header_hash, None)
                additions = await node.request_additions(request)
                if additions is not None:
                    break
                if isinstance(additions, RejectAdditionsRequest):
                    continue

            assert additions is not None
            assert isinstance(additions, RespondAdditions)
            # All additions in this block here:
            new_puzhash = (await self.get_new_puzzle()).get_tree_hash()
            new_pubkey = bytes(
                (await self.wallet_state_manager.get_unused_derivation_record(
                    self.wallet_info.id)).pubkey)

            all_parents: bytes32 = set()
            for puzzle_list_coin in additions.coins:
                puzzle_hash, coins = puzzle_list_coin
                for coin in coins:
                    all_parents.add(coin.parent_coin_info)
            for puzzle_list_coin in additions.coins:
                puzzle_hash, coins = puzzle_list_coin
                if puzzle_hash == full_puzzle_hash:
                    # our coin
                    for coin in coins:
                        future_parent = LineageProof(
                            coin.parent_coin_info,
                            innerpuz.get_tree_hash(),
                            coin.amount,
                        )
                        await self.add_parent(coin.name(), future_parent,
                                              False)
                        if coin.name() in all_parents:
                            continue
                        did_info = DIDInfo(
                            origin,
                            backup_ids,
                            num_of_backup_ids_needed,
                            self.did_info.parent_info,
                            innerpuz,
                            coin,
                            new_puzhash,
                            new_pubkey,
                        )
                        await self.save_info(did_info, False)

            return None
        except Exception as e:
            raise e
Ejemplo n.º 11
0
    async def load_backup(self, filename: str):
        try:
            f = open(filename, "r")
            details = f.readline().split(":")
            f.close()
            origin = Coin(
                bytes32.fromhex(details[0]),
                bytes32.fromhex(details[1]),
                uint64(int(details[2])),
            )
            backup_ids = []
            for d in details[3].split(","):
                backup_ids.append(bytes.fromhex(d))
            num_of_backup_ids_needed = uint64(int(details[5]))
            if num_of_backup_ids_needed > len(backup_ids):
                raise Exception
            innerpuz: Program = Program.from_bytes(bytes.fromhex(details[4]))
            did_info: DIDInfo = DIDInfo(
                origin,
                backup_ids,
                num_of_backup_ids_needed,
                self.did_info.parent_info,
                innerpuz,
                None,
                None,
                None,
                False,
            )
            await self.save_info(did_info, False)
            await self.wallet_state_manager.update_wallet_puzzle_hashes(self.wallet_info.id)

            # full_puz = did_wallet_puzzles.create_fullpuz(innerpuz, origin.name())
            # All additions in this block here:
            new_puzhash = await self.get_new_inner_hash()
            new_pubkey = bytes(
                (await self.wallet_state_manager.get_unused_derivation_record(self.wallet_info.id)).pubkey
            )
            parent_info = None

            node = self.wallet_state_manager.wallet_node.get_full_node_peer()
            children = await self.wallet_state_manager.wallet_node.fetch_children(node, origin.name())
            while True:
                if len(children) == 0:
                    break

                children_state: CoinState = children[0]
                coin = children_state.coin
                name = coin.name()
                children = await self.wallet_state_manager.wallet_node.fetch_children(node, name)
                future_parent = LineageProof(
                    coin.parent_coin_info,
                    innerpuz.get_tree_hash(),
                    coin.amount,
                )
                await self.add_parent(coin.name(), future_parent, False)
                if children_state.spent_height != children_state.created_height:
                    did_info = DIDInfo(
                        origin,
                        backup_ids,
                        num_of_backup_ids_needed,
                        self.did_info.parent_info,
                        innerpuz,
                        coin,
                        new_puzhash,
                        new_pubkey,
                        False,
                    )
                    await self.save_info(did_info, False)
                    assert children_state.created_height
                    puzzle_solution_request = wallet_protocol.RequestPuzzleSolution(
                        coin.parent_coin_info, children_state.created_height
                    )
                    parent_state: CoinState = (
                        await self.wallet_state_manager.wallet_node.get_coin_state([coin.parent_coin_info])
                    )[0]
                    response = await node.request_puzzle_solution(puzzle_solution_request)
                    req_puz_sol = response.response
                    assert req_puz_sol.puzzle is not None
                    parent_innerpuz = did_wallet_puzzles.get_innerpuzzle_from_puzzle(req_puz_sol.puzzle)
                    assert parent_innerpuz is not None
                    parent_info = LineageProof(
                        parent_state.coin.parent_coin_info,
                        parent_innerpuz.get_tree_hash(),
                        parent_state.coin.amount,
                    )
                    await self.add_parent(coin.parent_coin_info, parent_info, False)
            assert parent_info is not None
            return None
        except Exception as e:
            raise e
Ejemplo n.º 12
0
    async def test_complex_spend(self, setup_sim):
        sim, sim_client = setup_sim

        try:
            tail = Program.to([])
            checker_solution = Program.to([])
            cat_puzzle: Program = construct_cat_puzzle(CAT_MOD, tail.get_tree_hash(), acs)
            cat_ph: bytes32 = cat_puzzle.get_tree_hash()
            await sim.farm_block(cat_ph)
            await sim.farm_block(cat_ph)

            cat_records = await sim_client.get_coin_records_by_puzzle_hash(cat_ph, include_spent_coins=False)
            parent_of_mint = cat_records[0].coin
            parent_of_melt = cat_records[1].coin
            eve_to_mint = cat_records[2].coin
            eve_to_melt = cat_records[3].coin

            # Spend two of them to make them non-eve
            await self.do_spend(
                sim,
                sim_client,
                tail,
                [parent_of_mint, parent_of_melt],
                [NO_LINEAGE_PROOF, NO_LINEAGE_PROOF],
                [
                    Program.to(
                        [
                            [51, acs.get_tree_hash(), parent_of_mint.amount],
                            [51, 0, -113, tail, checker_solution],
                        ]
                    ),
                    Program.to(
                        [
                            [51, acs.get_tree_hash(), parent_of_melt.amount],
                            [51, 0, -113, tail, checker_solution],
                        ]
                    ),
                ],
                (MempoolInclusionStatus.SUCCESS, None),
                limitations_solutions=[checker_solution] * 2,
                cost_str="Spend two eves",
            )

            # Make the lineage proofs for the non-eves
            mint_lineage = LineageProof(parent_of_mint.parent_coin_info, acs_ph, parent_of_mint.amount)
            melt_lineage = LineageProof(parent_of_melt.parent_coin_info, acs_ph, parent_of_melt.amount)

            # Find the two new coins
            all_cats = await sim_client.get_coin_records_by_puzzle_hash(cat_ph, include_spent_coins=False)
            all_cat_coins = [cr.coin for cr in all_cats]
            standard_to_mint = list(filter(lambda cr: cr.parent_coin_info == parent_of_mint.name(), all_cat_coins))[0]
            standard_to_melt = list(filter(lambda cr: cr.parent_coin_info == parent_of_melt.name(), all_cat_coins))[0]

            # Do the complex spend
            # We have both and eve and non-eve doing both minting and melting
            await self.do_spend(
                sim,
                sim_client,
                tail,
                [eve_to_mint, eve_to_melt, standard_to_mint, standard_to_melt],
                [NO_LINEAGE_PROOF, NO_LINEAGE_PROOF, mint_lineage, melt_lineage],
                [
                    Program.to(
                        [
                            [51, acs.get_tree_hash(), eve_to_mint.amount + 13],
                            [51, 0, -113, tail, checker_solution],
                        ]
                    ),
                    Program.to(
                        [
                            [51, acs.get_tree_hash(), eve_to_melt.amount - 21],
                            [51, 0, -113, tail, checker_solution],
                        ]
                    ),
                    Program.to(
                        [
                            [51, acs.get_tree_hash(), standard_to_mint.amount + 21],
                            [51, 0, -113, tail, checker_solution],
                        ]
                    ),
                    Program.to(
                        [
                            [51, acs.get_tree_hash(), standard_to_melt.amount - 13],
                            [51, 0, -113, tail, checker_solution],
                        ]
                    ),
                ],
                (MempoolInclusionStatus.SUCCESS, None),
                limitations_solutions=[checker_solution] * 4,
                extra_deltas=[13, -21, 21, -13],
                cost_str="Complex Spend",
            )
        finally:
            await sim.close()
Ejemplo n.º 13
0
    async def test_cat_mod(self, setup_sim):
        sim, sim_client = setup_sim

        try:
            tail = Program.to([])
            checker_solution = Program.to([])
            cat_puzzle: Program = construct_cat_puzzle(CAT_MOD, tail.get_tree_hash(), acs)
            cat_ph: bytes32 = cat_puzzle.get_tree_hash()
            await sim.farm_block(cat_ph)
            starting_coin: Coin = (await sim_client.get_coin_records_by_puzzle_hash(cat_ph))[0].coin

            # Testing the eve spend
            await self.do_spend(
                sim,
                sim_client,
                tail,
                [starting_coin],
                [NO_LINEAGE_PROOF],
                [
                    Program.to(
                        [
                            [51, acs.get_tree_hash(), starting_coin.amount - 3, [b"memo"]],
                            [51, acs.get_tree_hash(), 1],
                            [51, acs.get_tree_hash(), 2],
                            [51, 0, -113, tail, checker_solution],
                        ]
                    )
                ],
                (MempoolInclusionStatus.SUCCESS, None),
                limitations_solutions=[checker_solution],
                cost_str="Eve Spend",
            )

            # There's 4 total coins at this point. A farming reward and the three children of the spend above.

            # Testing a combination of two
            coins: List[Coin] = [
                record.coin
                for record in (await sim_client.get_coin_records_by_puzzle_hash(cat_ph, include_spent_coins=False))
            ]
            coins = [coins[0], coins[1]]
            await self.do_spend(
                sim,
                sim_client,
                tail,
                coins,
                [NO_LINEAGE_PROOF] * 2,
                [
                    Program.to(
                        [
                            [51, acs.get_tree_hash(), coins[0].amount + coins[1].amount],
                            [51, 0, -113, tail, checker_solution],
                        ]
                    ),
                    Program.to([[51, 0, -113, tail, checker_solution]]),
                ],
                (MempoolInclusionStatus.SUCCESS, None),
                limitations_solutions=[checker_solution] * 2,
                cost_str="Two CATs",
            )

            # Testing a combination of three
            coins = [
                record.coin
                for record in (await sim_client.get_coin_records_by_puzzle_hash(cat_ph, include_spent_coins=False))
            ]
            total_amount: uint64 = uint64(sum([c.amount for c in coins]))
            await self.do_spend(
                sim,
                sim_client,
                tail,
                coins,
                [NO_LINEAGE_PROOF] * 3,
                [
                    Program.to(
                        [
                            [51, acs.get_tree_hash(), total_amount],
                            [51, 0, -113, tail, checker_solution],
                        ]
                    ),
                    Program.to([[51, 0, -113, tail, checker_solution]]),
                    Program.to([[51, 0, -113, tail, checker_solution]]),
                ],
                (MempoolInclusionStatus.SUCCESS, None),
                limitations_solutions=[checker_solution] * 3,
                cost_str="Three CATs",
            )

            # Spend with a standard lineage proof
            parent_coin: Coin = coins[0]  # The first one is the one we didn't light on fire
            _, curried_args = cat_puzzle.uncurry()
            _, _, innerpuzzle = curried_args.as_iter()
            lineage_proof = LineageProof(parent_coin.parent_coin_info, innerpuzzle.get_tree_hash(), parent_coin.amount)
            await self.do_spend(
                sim,
                sim_client,
                tail,
                [(await sim_client.get_coin_records_by_puzzle_hash(cat_ph, include_spent_coins=False))[0].coin],
                [lineage_proof],
                [Program.to([[51, acs.get_tree_hash(), total_amount]])],
                (MempoolInclusionStatus.SUCCESS, None),
                reveal_limitations_program=False,
                cost_str="Standard Lineage Check",
            )

            # Melt some value
            await self.do_spend(
                sim,
                sim_client,
                tail,
                [(await sim_client.get_coin_records_by_puzzle_hash(cat_ph, include_spent_coins=False))[0].coin],
                [NO_LINEAGE_PROOF],
                [
                    Program.to(
                        [
                            [51, acs.get_tree_hash(), total_amount - 1],
                            [51, 0, -113, tail, checker_solution],
                        ]
                    )
                ],
                (MempoolInclusionStatus.SUCCESS, None),
                extra_deltas=[-1],
                limitations_solutions=[checker_solution],
                cost_str="Melting Value",
            )

            # Mint some value
            temp_p = Program.to(1)
            temp_ph: bytes32 = temp_p.get_tree_hash()
            await sim.farm_block(temp_ph)
            acs_coin: Coin = (await sim_client.get_coin_records_by_puzzle_hash(temp_ph, include_spent_coins=False))[
                0
            ].coin
            acs_bundle = SpendBundle(
                [
                    CoinSpend(
                        acs_coin,
                        temp_p,
                        Program.to([]),
                    )
                ],
                G2Element(),
            )
            await self.do_spend(
                sim,
                sim_client,
                tail,
                [(await sim_client.get_coin_records_by_puzzle_hash(cat_ph, include_spent_coins=False))[0].coin],
                [NO_LINEAGE_PROOF],
                [
                    Program.to(
                        [
                            [51, acs.get_tree_hash(), total_amount],
                            [51, 0, -113, tail, checker_solution],
                        ]
                    )
                ],  # We subtracted 1 last time so it's normal now
                (MempoolInclusionStatus.SUCCESS, None),
                extra_deltas=[1],
                additional_spends=[acs_bundle],
                limitations_solutions=[checker_solution],
                cost_str="Mint Value",
            )

        finally:
            await sim.close()
Ejemplo n.º 14
0
    construct_cat_puzzle,
    unsigned_spend_bundle_for_spendable_cats,
)
from chia.wallet.puzzles.tails import (
    GenesisById,
    GenesisByPuzhash,
    EverythingWithSig,
    DelegatedLimitations,
)

from tests.clvm.test_puzzles import secret_exponent_for_index
from tests.clvm.benchmark_costs import cost_of_spend_bundle

acs = Program.to(1)
acs_ph = acs.get_tree_hash()
NO_LINEAGE_PROOF = LineageProof()


class TestCATLifecycle:
    cost: Dict[str, int] = {}

    @pytest.fixture(scope="function")
    async def setup_sim(self):
        sim = await SpendSim.create()
        sim_client = SimClient(sim)
        await sim.farm_block()
        return sim, sim_client

    async def do_spend(
        self,
        sim: SpendSim,