def puzzle_announcements_names_for_npc(npc_list) -> Set[bytes32]:
    output_announcements: Set[bytes32] = set()
    for npc in npc_list:
        for condition, cvp_list in npc.conditions:
            if condition == ConditionOpcode.CREATE_PUZZLE_ANNOUNCEMENT:
                for cvp in cvp_list:
                    message = cvp.vars[0]
                    announcement = Announcement(npc.puzzle_hash, message)
                    output_announcements.add(announcement.name())
    return output_announcements
Пример #2
0
        def test_fun(coin_1: Coin, coin_2: Coin) -> SpendBundle:
            announce = Announcement(coin_2.name(), b"test")
            cvp = ConditionWithArgs(ConditionOpcode.ASSERT_COIN_ANNOUNCEMENT, [announce.name()])
            dic = {cvp.opcode: [cvp]}

            cvp2 = ConditionWithArgs(ConditionOpcode.CREATE_COIN_ANNOUNCEMENT, [b"test"])
            dic2 = {cvp.opcode: [cvp2]}
            spend_bundle1 = generate_test_spend_bundle(coin_1, dic)
            spend_bundle2 = generate_test_spend_bundle(coin_2, dic2)
            bundle = SpendBundle.aggregate([spend_bundle1, spend_bundle2])
            return bundle
Пример #3
0
def coin_announcements_names_for_npc(npc_list) -> Set[bytes32]:
    output_announcements: Set[bytes32] = set()
    for npc in npc_list:
        for condition, cvp_list in npc.conditions:
            if condition == ConditionOpcode.CREATE_COIN_ANNOUNCEMENT:
                for cvp in cvp_list:
                    message = cvp.vars[0]
                    assert len(message) <= 1024
                    announcement = Announcement(npc.coin_name, message)
                    output_announcements.add(announcement.name())
    return output_announcements
Пример #4
0
        def test_fun(coin_1: Coin, coin_2: Coin):
            announce = Announcement(coin_2.puzzle_hash, bytes(0x80))

            cvp = ConditionWithArgs(ConditionOpcode.ASSERT_PUZZLE_ANNOUNCEMENT, [announce.name()])

            dic = {cvp.opcode: [cvp]}

            cvp2 = ConditionWithArgs(ConditionOpcode.CREATE_PUZZLE_ANNOUNCEMENT, [bytes(0x80)])
            dic2 = {cvp.opcode: [cvp2]}
            spend_bundle1 = generate_test_spend_bundle(coin_1, dic)

            spend_bundle2 = generate_test_spend_bundle(coin_2, dic2)

            return SpendBundle.aggregate([spend_bundle1, spend_bundle2])
Пример #5
0
    async def test_invalid_announcement_consumed_two(self, two_nodes):
        reward_ph = WALLET_A.get_new_puzzlehash()
        full_node_1, full_node_2, server_1, server_2 = two_nodes
        blocks = await full_node_1.get_all_full_blocks()
        start_height = blocks[-1].height if len(blocks) > 0 else -1
        blocks = bt.get_consecutive_blocks(
            3,
            block_list_input=blocks,
            guarantee_transaction_block=True,
            farmer_reward_puzzle_hash=reward_ph,
            pool_reward_puzzle_hash=reward_ph,
        )
        peer = await connect_and_get_peer(server_1, server_2)

        for block in blocks:
            await full_node_1.full_node.respond_block(
                full_node_protocol.RespondBlock(block))

        await time_out_assert(60, node_height_at_least, True, full_node_1,
                              start_height + 3)

        coin_1 = list(blocks[-2].get_included_reward_coins())[0]
        coin_2 = list(blocks[-1].get_included_reward_coins())[0]

        announce = Announcement(coin_1.name(), bytes("test", "utf-8"))

        cvp = ConditionVarPair(ConditionOpcode.ASSERT_ANNOUNCEMENT,
                               [announce.name()])

        dic = {cvp.opcode: [cvp]}

        cvp2 = ConditionVarPair(
            ConditionOpcode.CREATE_ANNOUNCEMENT,
            [bytes("test", "utf-8")],
        )
        dic2 = {cvp.opcode: [cvp2]}
        spend_bundle1 = generate_test_spend_bundle(coin_1, dic)

        spend_bundle2 = generate_test_spend_bundle(coin_2, dic2)

        bundle = SpendBundle.aggregate([spend_bundle1, spend_bundle2])

        tx1: full_node_protocol.RespondTransaction = full_node_protocol.RespondTransaction(
            spend_bundle1)
        await full_node_1.respond_transaction(tx1, peer)

        mempool_bundle = full_node_1.full_node.mempool_manager.get_spendbundle(
            bundle.name())

        assert mempool_bundle is None
Пример #6
0
def launcher_conditions_and_spend_bundle(
    parent_coin_id: bytes32,
    launcher_amount: uint64,
    initial_singleton_inner_puzzle: Program,
    metadata: List[Tuple[str, str]],
    launcher_puzzle: Program = LAUNCHER_PUZZLE,
) -> Tuple[Program, bytes32, List[Program], SpendBundle]:
    launcher_puzzle_hash = launcher_puzzle.get_tree_hash()
    launcher_coin = Coin(parent_coin_id, launcher_puzzle_hash, launcher_amount)
    singleton_full_puzzle = SINGLETON_MOD.curry(
        SINGLETON_MOD_HASH, launcher_coin.name(), launcher_puzzle_hash,
        initial_singleton_inner_puzzle)
    singleton_full_puzzle_hash = singleton_full_puzzle.get_tree_hash()
    message_program = Program.to(
        [singleton_full_puzzle_hash, launcher_amount, metadata])
    expected_announcement = Announcement(launcher_coin.name(),
                                         message_program.get_tree_hash())
    expected_conditions = []
    expected_conditions.append(
        Program.to(
            binutils.assemble(
                f"(0x{ConditionOpcode.ASSERT_COIN_ANNOUNCEMENT.hex()} 0x{expected_announcement.name()})"
            )))
    expected_conditions.append(
        Program.to(
            binutils.assemble(
                f"(0x{ConditionOpcode.CREATE_COIN.hex()} 0x{launcher_puzzle_hash} {launcher_amount})"
            )))
    launcher_solution = Program.to(
        [singleton_full_puzzle_hash, launcher_amount, metadata])
    coin_spend = CoinSpend(launcher_coin, launcher_puzzle, launcher_solution)
    spend_bundle = SpendBundle([coin_spend], G2Element())
    lineage_proof = Program.to([parent_coin_id, launcher_amount])
    return lineage_proof, launcher_coin.name(
    ), expected_conditions, spend_bundle
def claim_p2_singleton(
        puzzle_db: PuzzleDB, singleton_wallet: SingletonWallet,
        p2_singleton_coin: Coin) -> Tuple[CoinSpend, List[Program]]:
    inner_puzzle = singleton_wallet.inner_puzzle(puzzle_db)
    assert inner_puzzle
    inner_puzzle_hash = inner_puzzle.get_tree_hash()
    p2_singleton_puzzle = puzzle_db.puzzle_for_hash(
        p2_singleton_coin.puzzle_hash)
    assert p2_singleton_puzzle is not None
    p2_singleton_coin_name = p2_singleton_coin.name()
    p2_singleton_solution = solve_puzzle(
        puzzle_db,
        p2_singleton_puzzle,
        p2_singleton_spend_type="claim-p2-nft",
        singleton_inner_puzzle_hash=inner_puzzle_hash,
        p2_singleton_coin_name=p2_singleton_coin_name,
    )
    p2_singleton_coin_spend = CoinSpend(
        p2_singleton_coin,
        p2_singleton_puzzle.to_serialized_program(),
        p2_singleton_solution,
    )
    expected_p2_singleton_announcement = Announcement(p2_singleton_coin_name,
                                                      bytes(b"$")).name()
    singleton_conditions = [
        Program.to([
            ConditionOpcode.CREATE_PUZZLE_ANNOUNCEMENT, p2_singleton_coin_name
        ]),
        Program.to([ConditionOpcode.CREATE_COIN, inner_puzzle_hash, 1]),
        Program.to([
            ConditionOpcode.ASSERT_COIN_ANNOUNCEMENT,
            expected_p2_singleton_announcement
        ]),
    ]
    return p2_singleton_coin_spend, singleton_conditions
Пример #8
0
 async def test_valid_puzzle_announcement(self):
     announce = Announcement(EASY_PUZZLE_HASH, b"test")
     conditions = Program.to(
         assemble(
             f"(({ConditionOpcode.CREATE_PUZZLE_ANNOUNCEMENT[0]} 'test')"
             f"({ConditionOpcode.ASSERT_PUZZLE_ANNOUNCEMENT[0]} 0x{announce.name().hex()}))"
         ))
     await check_conditions(conditions)
Пример #9
0
 async def test_invalid_puzzle_announcement(self):
     announce = Announcement(EASY_PUZZLE_HASH, b"test_bad")
     conditions = Program.to(
         assemble(
             f"(({ConditionOpcode.CREATE_PUZZLE_ANNOUNCEMENT[0]} 'test')"
             f"({ConditionOpcode.ASSERT_PUZZLE_ANNOUNCEMENT[0]} 0x{announce.name().hex()}))"
         ))
     await check_conditions(
         conditions, expected_err=Err.ASSERT_ANNOUNCE_CONSUMED_FAILED)
Пример #10
0
        def test_fun(coin_1: Coin, coin_2: Coin):
            announce = Announcement(coin_1.name(), b"test")

            cvp = ConditionWithArgs(ConditionOpcode.ASSERT_COIN_ANNOUNCEMENT, [announce.name()])

            dic = {cvp.opcode: [cvp]}

            cvp2 = ConditionWithArgs(
                ConditionOpcode.CREATE_COIN_ANNOUNCEMENT,
                [b"test"],
            )
            dic2 = {cvp.opcode: [cvp2]}
            spend_bundle1 = generate_test_spend_bundle(coin_1, dic)

            # coin 2 is making the announcement, right message wrong coin
            spend_bundle2 = generate_test_spend_bundle(coin_2, dic2)

            return SpendBundle.aggregate([spend_bundle1, spend_bundle2])
Пример #11
0
 async def test_valid_coin_announcement(self):
     blocks = initial_blocks()
     coin = list(blocks[-2].get_included_reward_coins())[0]
     announce = Announcement(coin.name(), b"test")
     conditions = Program.to(
         assemble(
             f"(({ConditionOpcode.CREATE_COIN_ANNOUNCEMENT[0]} 'test')"
             f"({ConditionOpcode.ASSERT_COIN_ANNOUNCEMENT[0]} 0x{announce.name().hex()}))"
         ))
     await check_conditions(conditions)
Пример #12
0
    async def create_tandem_xch_tx(
        self,
        fee: uint64,
        amount_to_claim: uint64,
        announcement_to_assert: Optional[Announcement] = None,
    ) -> Tuple[TransactionRecord, Optional[Announcement]]:
        """
        This function creates a non-CAT transaction to pay fees, contribute funds for issuance, and absorb melt value.
        It is meant to be called in `generate_unsigned_spendbundle` and as such should be called under the
        wallet_state_manager lock
        """
        announcement = None
        if fee > amount_to_claim:
            chia_coins = await self.standard_wallet.select_coins(fee)
            origin_id = list(chia_coins)[0].name()
            chia_tx = await self.standard_wallet.generate_signed_transaction(
                uint64(0),
                (await self.standard_wallet.get_new_puzzlehash()),
                fee=uint64(fee - amount_to_claim),
                coins=chia_coins,
                origin_id=
                origin_id,  # We specify this so that we know the coin that is making the announcement
                negative_change_allowed=False,
                coin_announcements_to_consume={announcement_to_assert}
                if announcement_to_assert is not None else None,
            )
            assert chia_tx.spend_bundle is not None

            message = None
            for spend in chia_tx.spend_bundle.coin_spends:
                if spend.coin.name() == origin_id:
                    conditions = spend.puzzle_reveal.to_program().run(
                        spend.solution.to_program()).as_python()
                    for condition in conditions:
                        if condition[
                                0] == ConditionOpcode.CREATE_COIN_ANNOUNCEMENT:
                            message = condition[1]

            assert message is not None
            announcement = Announcement(origin_id, message)
        else:
            chia_coins = await self.standard_wallet.select_coins(fee)
            selected_amount = sum([c.amount for c in chia_coins])
            chia_tx = await self.standard_wallet.generate_signed_transaction(
                uint64(selected_amount + amount_to_claim - fee),
                (await self.standard_wallet.get_new_puzzlehash()),
                coins=chia_coins,
                negative_change_allowed=True,
                coin_announcements_to_consume={announcement_to_assert}
                if announcement_to_assert is not None else None,
            )
            assert chia_tx.spend_bundle is not None

        return chia_tx, announcement
Пример #13
0
def coin_announcements_for_conditions_dict(
    conditions_dict: Dict[ConditionOpcode, List[ConditionWithArgs]],
    input_coin: Coin,
) -> Set[Announcement]:
    output_announcements: Set[Announcement] = set()
    for cvp in conditions_dict.get(ConditionOpcode.CREATE_COIN_ANNOUNCEMENT, []):
        message = cvp.vars[0]
        assert len(message) <= 1024
        announcement = Announcement(input_coin.name(), message)
        output_announcements.add(announcement)
    return output_announcements
Пример #14
0
def puzzle_announcements_for_conditions_dict(
    conditions_dict: Dict[ConditionOpcode, List[ConditionWithArgs]],
    input_coin: Coin,
) -> Set[Announcement]:
    output_announcements: Set[Announcement] = set()
    for cvp in conditions_dict.get(ConditionOpcode.CREATE_PUZZLE_ANNOUNCEMENT,
                                   []):
        message = cvp.vars[0]
        announcement = Announcement(input_coin.puzzle_hash, message)
        output_announcements.add(announcement)
    return output_announcements
Пример #15
0
def created_announcements_for_conditions_dict(
    conditions_dict: Dict[ConditionOpcode, List[ConditionWithArgs]],
    input_coin_name: bytes32,
) -> List[Announcement]:
    output_announcements = []
    for cvp in conditions_dict.get(ConditionOpcode.CREATE_ANNOUNCEMENT, []):
        # TODO: check condition very carefully
        # (ensure there are the correct number and type of parameters)
        # maybe write a type-checking framework for conditions
        # and don't just fail with asserts
        message = cvp.vars[0]
        announcement = Announcement(input_coin_name, message)
        output_announcements.append(announcement)
    return output_announcements
def launcher_conditions_and_spend_bundle(
    puzzle_db: PuzzleDB,
    parent_coin_id: bytes32,
    launcher_amount: uint64,
    initial_singleton_inner_puzzle: Program,
    metadata: List[Tuple[str, str]],
    launcher_puzzle: Program,
) -> Tuple[bytes32, List[Program], SpendBundle]:
    puzzle_db.add_puzzle(launcher_puzzle)
    launcher_puzzle_hash = launcher_puzzle.get_tree_hash()
    launcher_coin = Coin(parent_coin_id, launcher_puzzle_hash, launcher_amount)
    # TODO: address hint error and remove ignore
    #       error: Argument 1 to "singleton_puzzle" has incompatible type "bytes32"; expected "Program"  [arg-type]
    singleton_full_puzzle = singleton_puzzle(
        launcher_coin.name(),  # type: ignore[arg-type]
        launcher_puzzle_hash,
        initial_singleton_inner_puzzle,
    )
    puzzle_db.add_puzzle(singleton_full_puzzle)
    singleton_full_puzzle_hash = singleton_full_puzzle.get_tree_hash()
    message_program = Program.to(
        [singleton_full_puzzle_hash, launcher_amount, metadata])
    expected_announcement = Announcement(launcher_coin.name(),
                                         message_program.get_tree_hash())
    expected_conditions = []
    expected_conditions.append(
        Program.to(
            binutils.assemble(
                f"(0x{ConditionOpcode.ASSERT_COIN_ANNOUNCEMENT.hex()} 0x{expected_announcement.name()})"
            )))
    expected_conditions.append(
        Program.to(
            binutils.assemble(
                f"(0x{ConditionOpcode.CREATE_COIN.hex()} 0x{launcher_puzzle_hash} {launcher_amount})"
            )))
    solution = solve_puzzle(
        puzzle_db,
        launcher_puzzle,
        destination_puzzle_hash=singleton_full_puzzle_hash,
        launcher_amount=launcher_amount,
        metadata=metadata,
    )
    coin_spend = CoinSpend(launcher_coin,
                           SerializedProgram.from_program(launcher_puzzle),
                           solution)
    spend_bundle = SpendBundle([coin_spend], G2Element())
    return launcher_coin.name(), expected_conditions, spend_bundle
Пример #17
0
    def calculate_announcements(
        notarized_payments: Dict[Optional[bytes32], List[NotarizedPayment]],
    ) -> List[Announcement]:
        announcements: List[Announcement] = []
        for tail, payments in notarized_payments.items():
            if tail is not None:
                settlement_ph: bytes32 = construct_cat_puzzle(
                    CAT_MOD, tail, OFFER_MOD).get_tree_hash()
            else:
                settlement_ph = OFFER_MOD.get_tree_hash()

            msg: bytes32 = Program.to(
                (payments[0].nonce, [p.as_condition_args()
                                     for p in payments])).get_tree_hash()
            announcements.append(Announcement(settlement_ph, msg))

        return announcements
Пример #18
0
def test_p2_singleton():
    # create a singleton. This should call driver code.
    launcher_id = LAUNCHER_ID
    innerpuz = Program.to(1)
    singleton_full_puzzle = singleton_puzzle(launcher_id, LAUNCHER_PUZZLE_HASH,
                                             innerpuz)

    # create a fake coin id for the `p2_singleton`
    p2_singleton_coin_id = Program.to(["test_hash"]).get_tree_hash()
    expected_announcement = Announcement(singleton_full_puzzle.get_tree_hash(),
                                         p2_singleton_coin_id).name()

    # create a `p2_singleton` puzzle. This should call driver code.
    p2_singleton_full = p2_singleton_puzzle(launcher_id, LAUNCHER_PUZZLE_HASH)
    solution = Program.to([innerpuz.get_tree_hash(), p2_singleton_coin_id])
    cost, result = p2_singleton_full.run_with_cost(INFINITE_COST, solution)
    err, conditions = parse_sexp_to_conditions(result)
    assert err is None

    p2_singleton_full = p2_singleton_puzzle(launcher_id, LAUNCHER_PUZZLE_HASH)
    solution = Program.to([innerpuz.get_tree_hash(), p2_singleton_coin_id])
    cost, result = p2_singleton_full.run_with_cost(INFINITE_COST, solution)
    assert result.first().rest().first().as_atom() == expected_announcement
    assert conditions[0].vars[0] == expected_announcement
Пример #19
0
    async def test_wallet_rpc(self, two_wallet_nodes, trusted):
        test_rpc_port = uint16(21529)
        test_rpc_port_2 = uint16(21536)
        test_rpc_port_node = uint16(21530)
        num_blocks = 5
        full_nodes, wallets = two_wallet_nodes
        full_node_api = full_nodes[0]
        full_node_server = full_node_api.full_node.server
        wallet_node, server_2 = wallets[0]
        wallet_node_2, server_3 = wallets[1]
        wallet = wallet_node.wallet_state_manager.main_wallet
        wallet_2 = wallet_node_2.wallet_state_manager.main_wallet
        ph = await wallet.get_new_puzzlehash()
        ph_2 = await wallet_2.get_new_puzzlehash()

        await server_2.start_client(PeerInfo("localhost", uint16(full_node_server._port)), None)
        await server_3.start_client(PeerInfo("localhost", uint16(full_node_server._port)), None)

        if trusted:
            wallet_node.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
            wallet_node_2.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()}
        else:
            wallet_node.config["trusted_peers"] = {}
            wallet_node_2.config["trusted_peers"] = {}

        for i in range(0, num_blocks):
            await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))

        initial_funds = sum(
            [calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i)) for i in range(1, num_blocks)]
        )
        initial_funds_eventually = sum(
            [
                calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i))
                for i in range(1, num_blocks + 1)
            ]
        )

        wallet_rpc_api = WalletRpcApi(wallet_node)
        wallet_rpc_api_2 = WalletRpcApi(wallet_node_2)

        config = bt.config
        hostname = config["self_hostname"]
        daemon_port = config["daemon_port"]

        def stop_node_cb():
            pass

        full_node_rpc_api = FullNodeRpcApi(full_node_api.full_node)

        rpc_cleanup_node = await start_rpc_server(
            full_node_rpc_api,
            hostname,
            daemon_port,
            test_rpc_port_node,
            stop_node_cb,
            bt.root_path,
            config,
            connect_to_daemon=False,
        )
        rpc_cleanup = await start_rpc_server(
            wallet_rpc_api,
            hostname,
            daemon_port,
            test_rpc_port,
            stop_node_cb,
            bt.root_path,
            config,
            connect_to_daemon=False,
        )
        rpc_cleanup_2 = await start_rpc_server(
            wallet_rpc_api_2,
            hostname,
            daemon_port,
            test_rpc_port_2,
            stop_node_cb,
            bt.root_path,
            config,
            connect_to_daemon=False,
        )

        await time_out_assert(5, wallet.get_confirmed_balance, initial_funds)
        await time_out_assert(5, wallet.get_unconfirmed_balance, initial_funds)

        client = await WalletRpcClient.create(self_hostname, test_rpc_port, bt.root_path, config)
        client_2 = await WalletRpcClient.create(self_hostname, test_rpc_port_2, bt.root_path, config)
        client_node = await FullNodeRpcClient.create(self_hostname, test_rpc_port_node, bt.root_path, config)
        try:
            await time_out_assert(5, client.get_synced)
            addr = encode_puzzle_hash(await wallet_node_2.wallet_state_manager.main_wallet.get_new_puzzlehash(), "xch")
            tx_amount = 15600000
            try:
                await client.send_transaction("1", 100000000000000001, addr)
                raise Exception("Should not create high value tx")
            except ValueError:
                pass

            # Tests sending a basic transaction
            tx = await client.send_transaction("1", tx_amount, addr, memos=["this is a basic tx"])
            transaction_id = tx.name

            async def tx_in_mempool():
                tx = await client.get_transaction("1", transaction_id)
                return tx.is_in_mempool()

            await time_out_assert(5, tx_in_mempool, True)
            await time_out_assert(5, wallet.get_unconfirmed_balance, initial_funds - tx_amount)
            assert (await client.get_wallet_balance("1"))["unconfirmed_wallet_balance"] == initial_funds - tx_amount
            assert (await client.get_wallet_balance("1"))["confirmed_wallet_balance"] == initial_funds

            for i in range(0, 5):
                await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph_2))

            async def eventual_balance():
                return (await client.get_wallet_balance("1"))["confirmed_wallet_balance"]

            async def eventual_balance_det(c, wallet_id: str):
                return (await c.get_wallet_balance(wallet_id))["confirmed_wallet_balance"]

            # Checks that the memo can be retrieved
            tx_confirmed = await client.get_transaction("1", transaction_id)
            assert tx_confirmed.confirmed
            assert len(tx_confirmed.get_memos()) == 1
            assert [b"this is a basic tx"] in tx_confirmed.get_memos().values()
            assert list(tx_confirmed.get_memos().keys())[0] in [a.name() for a in tx.spend_bundle.additions()]

            await time_out_assert(5, eventual_balance, initial_funds_eventually - tx_amount)

            # Tests offline signing
            ph_3 = await wallet_node_2.wallet_state_manager.main_wallet.get_new_puzzlehash()
            ph_4 = await wallet_node_2.wallet_state_manager.main_wallet.get_new_puzzlehash()
            ph_5 = await wallet_node_2.wallet_state_manager.main_wallet.get_new_puzzlehash()

            # Test basic transaction to one output and coin announcement
            signed_tx_amount = 888000
            tx_coin_announcements = [
                Announcement(
                    std_hash(b"coin_id_1"),
                    std_hash(b"message"),
                    b"\xca",
                ),
                Announcement(
                    std_hash(b"coin_id_2"),
                    bytes(Program.to("a string")),
                ),
            ]
            tx_res: TransactionRecord = await client.create_signed_transaction(
                [{"amount": signed_tx_amount, "puzzle_hash": ph_3}], coin_announcements=tx_coin_announcements
            )

            assert tx_res.fee_amount == 0
            assert tx_res.amount == signed_tx_amount
            assert len(tx_res.additions) == 2  # The output and the change
            assert any([addition.amount == signed_tx_amount for addition in tx_res.additions])
            # check error for a ASSERT_ANNOUNCE_CONSUMED_FAILED and if the error is not there throw a value error
            try:
                push_res = await client_node.push_tx(tx_res.spend_bundle)
            except ValueError as error:
                error_string = error.args[0]["error"]  # noqa:  # pylint: disable=E1126
                if error_string.find("ASSERT_ANNOUNCE_CONSUMED_FAILED") == -1:
                    raise ValueError from error

            # # Test basic transaction to one output and puzzle announcement
            signed_tx_amount = 888000
            tx_puzzle_announcements = [
                Announcement(
                    std_hash(b"puzzle_hash_1"),
                    b"message",
                    b"\xca",
                ),
                Announcement(
                    std_hash(b"puzzle_hash_2"),
                    bytes(Program.to("a string")),
                ),
            ]
            tx_res: TransactionRecord = await client.create_signed_transaction(
                [{"amount": signed_tx_amount, "puzzle_hash": ph_3}], puzzle_announcements=tx_puzzle_announcements
            )

            assert tx_res.fee_amount == 0
            assert tx_res.amount == signed_tx_amount
            assert len(tx_res.additions) == 2  # The output and the change
            assert any([addition.amount == signed_tx_amount for addition in tx_res.additions])
            # check error for a ASSERT_ANNOUNCE_CONSUMED_FAILED and if the error is not there throw a value error
            try:
                push_res = await client_node.push_tx(tx_res.spend_bundle)
            except ValueError as error:
                error_string = error.args[0]["error"]  # noqa:  # pylint: disable=E1126
                if error_string.find("ASSERT_ANNOUNCE_CONSUMED_FAILED") == -1:
                    raise ValueError from error

            # Test basic transaction to one output
            signed_tx_amount = 888000
            tx_res: TransactionRecord = await client.create_signed_transaction(
                [{"amount": signed_tx_amount, "puzzle_hash": ph_3, "memos": ["My memo"]}]
            )

            assert tx_res.fee_amount == 0
            assert tx_res.amount == signed_tx_amount
            assert len(tx_res.additions) == 2  # The output and the change
            assert any([addition.amount == signed_tx_amount for addition in tx_res.additions])

            push_res = await client.push_tx(tx_res.spend_bundle)
            assert push_res["success"]
            assert (await client.get_wallet_balance("1"))[
                "confirmed_wallet_balance"
            ] == initial_funds_eventually - tx_amount

            for i in range(0, 5):
                await client.farm_block(encode_puzzle_hash(ph_2, "xch"))
                await asyncio.sleep(0.5)

            await time_out_assert(5, eventual_balance, initial_funds_eventually - tx_amount - signed_tx_amount)

            # Test transaction to two outputs, from a specified coin, with a fee
            coin_to_spend = None
            for addition in tx_res.additions:
                if addition.amount != signed_tx_amount:
                    coin_to_spend = addition
            assert coin_to_spend is not None

            tx_res = await client.create_signed_transaction(
                [{"amount": 444, "puzzle_hash": ph_4, "memos": ["hhh"]}, {"amount": 999, "puzzle_hash": ph_5}],
                coins=[coin_to_spend],
                fee=100,
            )
            assert tx_res.fee_amount == 100
            assert tx_res.amount == 444 + 999
            assert len(tx_res.additions) == 3  # The outputs and the change
            assert any([addition.amount == 444 for addition in tx_res.additions])
            assert any([addition.amount == 999 for addition in tx_res.additions])
            assert sum([rem.amount for rem in tx_res.removals]) - sum([ad.amount for ad in tx_res.additions]) == 100

            push_res = await client_node.push_tx(tx_res.spend_bundle)
            assert push_res["success"]
            for i in range(0, 5):
                await client.farm_block(encode_puzzle_hash(ph_2, "xch"))
                await asyncio.sleep(0.5)

            found: bool = False
            for addition in tx_res.spend_bundle.additions():
                if addition.amount == 444:
                    cr: Optional[CoinRecord] = await client_node.get_coin_record_by_name(addition.name())
                    assert cr is not None
                    spend: CoinSpend = await client_node.get_puzzle_and_solution(
                        addition.parent_coin_info, cr.confirmed_block_index
                    )
                    sb: SpendBundle = SpendBundle([spend], G2Element())
                    assert compute_memos(sb) == {addition.name(): [b"hhh"]}
                    found = True
            assert found

            new_balance = initial_funds_eventually - tx_amount - signed_tx_amount - 444 - 999 - 100
            await time_out_assert(5, eventual_balance, new_balance)

            send_tx_res: TransactionRecord = await client.send_transaction_multi(
                "1",
                [
                    {"amount": 555, "puzzle_hash": ph_4, "memos": ["FiMemo"]},
                    {"amount": 666, "puzzle_hash": ph_5, "memos": ["SeMemo"]},
                ],
                fee=200,
            )
            assert send_tx_res is not None
            assert send_tx_res.fee_amount == 200
            assert send_tx_res.amount == 555 + 666
            assert len(send_tx_res.additions) == 3  # The outputs and the change
            assert any([addition.amount == 555 for addition in send_tx_res.additions])
            assert any([addition.amount == 666 for addition in send_tx_res.additions])
            assert (
                sum([rem.amount for rem in send_tx_res.removals]) - sum([ad.amount for ad in send_tx_res.additions])
                == 200
            )

            await asyncio.sleep(3)
            for i in range(0, 5):
                await client.farm_block(encode_puzzle_hash(ph_2, "xch"))
                await asyncio.sleep(0.5)

            new_balance = new_balance - 555 - 666 - 200
            await time_out_assert(5, eventual_balance, new_balance)

            address = await client.get_next_address("1", True)
            assert len(address) > 10

            transactions = await client.get_transactions("1")
            assert len(transactions) > 1

            all_transactions = await client.get_transactions("1")
            # Test transaction pagination
            some_transactions = await client.get_transactions("1", 0, 5)
            some_transactions_2 = await client.get_transactions("1", 5, 10)
            assert some_transactions == all_transactions[0:5]
            assert some_transactions_2 == all_transactions[5:10]

            # Testing sorts
            # Test the default sort (CONFIRMED_AT_HEIGHT)
            assert all_transactions == sorted(all_transactions, key=attrgetter("confirmed_at_height"))
            all_transactions = await client.get_transactions("1", reverse=True)
            assert all_transactions == sorted(all_transactions, key=attrgetter("confirmed_at_height"), reverse=True)

            # Test RELEVANCE
            await client.send_transaction("1", 1, encode_puzzle_hash(ph_2, "xch"))  # Create a pending tx

            all_transactions = await client.get_transactions("1", sort_key=SortKey.RELEVANCE)
            sorted_transactions = sorted(all_transactions, key=attrgetter("created_at_time"), reverse=True)
            sorted_transactions = sorted(sorted_transactions, key=attrgetter("confirmed_at_height"), reverse=True)
            sorted_transactions = sorted(sorted_transactions, key=attrgetter("confirmed"))
            assert all_transactions == sorted_transactions

            all_transactions = await client.get_transactions("1", sort_key=SortKey.RELEVANCE, reverse=True)
            sorted_transactions = sorted(all_transactions, key=attrgetter("created_at_time"))
            sorted_transactions = sorted(sorted_transactions, key=attrgetter("confirmed_at_height"))
            sorted_transactions = sorted(sorted_transactions, key=attrgetter("confirmed"), reverse=True)
            assert all_transactions == sorted_transactions

            # Checks that the memo can be retrieved
            tx_confirmed = await client.get_transaction("1", send_tx_res.name)
            assert tx_confirmed.confirmed
            if isinstance(tx_confirmed, SpendBundle):
                memos = compute_memos(tx_confirmed)
            else:
                memos = tx_confirmed.get_memos()
            assert len(memos) == 2
            print(memos)
            assert [b"FiMemo"] in memos.values()
            assert [b"SeMemo"] in memos.values()
            assert list(memos.keys())[0] in [a.name() for a in send_tx_res.spend_bundle.additions()]
            assert list(memos.keys())[1] in [a.name() for a in send_tx_res.spend_bundle.additions()]

            ##############
            # CATS       #
            ##############

            # Creates a wallet and a CAT with 20 mojos
            res = await client.create_new_cat_and_wallet(20)
            assert res["success"]
            cat_0_id = res["wallet_id"]
            asset_id = bytes.fromhex(res["asset_id"])
            assert len(asset_id) > 0

            bal_0 = await client.get_wallet_balance(cat_0_id)
            assert bal_0["confirmed_wallet_balance"] == 0
            assert bal_0["pending_coin_removal_count"] == 1
            col = await client.get_cat_asset_id(cat_0_id)
            assert col == asset_id
            assert (await client.get_cat_name(cat_0_id)) == "CAT Wallet"
            await client.set_cat_name(cat_0_id, "My cat")
            assert (await client.get_cat_name(cat_0_id)) == "My cat"
            wid, name = await client.cat_asset_id_to_name(col)
            assert wid == cat_0_id
            assert name == "My cat"
            should_be_none = await client.cat_asset_id_to_name(bytes([0] * 32))
            assert should_be_none is None
            verified_asset_id = next(iter(DEFAULT_CATS.items()))[1]["asset_id"]
            should_be_none, name = await client.cat_asset_id_to_name(bytes.fromhex(verified_asset_id))
            assert should_be_none is None
            assert name == next(iter(DEFAULT_CATS.items()))[1]["name"]

            await asyncio.sleep(1)
            for i in range(0, 5):
                await client.farm_block(encode_puzzle_hash(ph_2, "xch"))
                await asyncio.sleep(0.5)

            await time_out_assert(10, eventual_balance_det, 20, client, cat_0_id)
            bal_0 = await client.get_wallet_balance(cat_0_id)
            assert bal_0["pending_coin_removal_count"] == 0
            assert bal_0["unspent_coin_count"] == 1

            # Creates a second wallet with the same CAT
            res = await client_2.create_wallet_for_existing_cat(asset_id)
            assert res["success"]
            cat_1_id = res["wallet_id"]
            colour_1 = bytes.fromhex(res["asset_id"])
            assert colour_1 == asset_id

            await asyncio.sleep(1)
            for i in range(0, 5):
                await client.farm_block(encode_puzzle_hash(ph_2, "xch"))
                await asyncio.sleep(0.5)
            bal_1 = await client_2.get_wallet_balance(cat_1_id)
            assert bal_1["confirmed_wallet_balance"] == 0

            addr_0 = await client.get_next_address(cat_0_id, False)
            addr_1 = await client_2.get_next_address(cat_1_id, False)

            assert addr_0 != addr_1

            await client.cat_spend(cat_0_id, 4, addr_1, 0, ["the cat memo"])

            await asyncio.sleep(1)
            for i in range(0, 5):
                await client.farm_block(encode_puzzle_hash(ph_2, "xch"))
                await asyncio.sleep(0.5)

            await time_out_assert(10, eventual_balance_det, 16, client, cat_0_id)
            await time_out_assert(10, eventual_balance_det, 4, client_2, cat_1_id)

            ##########
            # Offers #
            ##########

            # Create an offer of 5 chia for one CAT
            offer, trade_record = await client.create_offer_for_ids({uint32(1): -5, cat_0_id: 1}, validate_only=True)
            all_offers = await client.get_all_offers()
            assert len(all_offers) == 0
            assert offer is None

            offer, trade_record = await client.create_offer_for_ids({uint32(1): -5, cat_0_id: 1}, fee=uint64(1))

            summary = await client.get_offer_summary(offer)
            assert summary == {"offered": {"xch": 5}, "requested": {col.hex(): 1}}

            assert await client.check_offer_validity(offer)

            all_offers = await client.get_all_offers(file_contents=True)
            assert len(all_offers) == 1
            assert TradeStatus(all_offers[0].status) == TradeStatus.PENDING_ACCEPT
            assert all_offers[0].offer == bytes(offer)

            trade_record = await client_2.take_offer(offer, fee=uint64(1))
            assert TradeStatus(trade_record.status) == TradeStatus.PENDING_CONFIRM

            await client.cancel_offer(offer.name(), secure=False)

            trade_record = await client.get_offer(offer.name(), file_contents=True)
            assert trade_record.offer == bytes(offer)
            assert TradeStatus(trade_record.status) == TradeStatus.CANCELLED

            await client.cancel_offer(offer.name(), fee=uint64(1), secure=True)

            trade_record = await client.get_offer(offer.name())
            assert TradeStatus(trade_record.status) == TradeStatus.PENDING_CANCEL

            new_offer, new_trade_record = await client.create_offer_for_ids({uint32(1): -5, cat_0_id: 1}, fee=uint64(1))
            all_offers = await client.get_all_offers()
            assert len(all_offers) == 2

            await asyncio.sleep(1)
            for i in range(0, 5):
                await client.farm_block(encode_puzzle_hash(ph_2, "xch"))
                await asyncio.sleep(0.5)

            async def is_trade_confirmed(client, trade) -> bool:
                trade_record = await client.get_offer(trade.name())
                return TradeStatus(trade_record.status) == TradeStatus.CONFIRMED

            time_out_assert(15, is_trade_confirmed, True, client, offer)

            # Test trade sorting
            def only_ids(trades):
                return [t.trade_id for t in trades]

            trade_record = await client.get_offer(offer.name())
            all_offers = await client.get_all_offers(include_completed=True)  # confirmed at index descending
            assert len(all_offers) == 2
            assert only_ids(all_offers) == only_ids([trade_record, new_trade_record])
            all_offers = await client.get_all_offers(
                include_completed=True, reverse=True
            )  # confirmed at index ascending
            assert only_ids(all_offers) == only_ids([new_trade_record, trade_record])
            all_offers = await client.get_all_offers(include_completed=True, sort_key="RELEVANCE")  # most relevant
            assert only_ids(all_offers) == only_ids([new_trade_record, trade_record])
            all_offers = await client.get_all_offers(
                include_completed=True, sort_key="RELEVANCE", reverse=True
            )  # least relevant
            assert only_ids(all_offers) == only_ids([trade_record, new_trade_record])
            # Test pagination
            all_offers = await client.get_all_offers(include_completed=True, start=0, end=1)
            assert len(all_offers) == 1
            all_offers = await client.get_all_offers(include_completed=True, start=50)
            assert len(all_offers) == 0
            all_offers = await client.get_all_offers(include_completed=True, start=0, end=50)
            assert len(all_offers) == 2

            # Keys and addresses

            address = await client.get_next_address("1", True)
            assert len(address) > 10

            all_transactions = await client.get_transactions("1")

            some_transactions = await client.get_transactions("1", 0, 5)
            some_transactions_2 = await client.get_transactions("1", 5, 10)
            assert len(all_transactions) > 1
            assert some_transactions == all_transactions[0:5]
            assert some_transactions_2 == all_transactions[5:10]

            transaction_count = await client.get_transaction_count("1")
            assert transaction_count == len(all_transactions)

            pks = await client.get_public_keys()
            assert len(pks) == 1

            assert (await client.get_height_info()) > 0

            created_tx = await client.send_transaction("1", tx_amount, addr)

            async def tx_in_mempool_2():
                tx = await client.get_transaction("1", created_tx.name)
                return tx.is_in_mempool()

            await time_out_assert(5, tx_in_mempool_2, True)
            assert len(await wallet.wallet_state_manager.tx_store.get_unconfirmed_for_wallet(1)) == 1
            await client.delete_unconfirmed_transactions("1")
            assert len(await wallet.wallet_state_manager.tx_store.get_unconfirmed_for_wallet(1)) == 0

            sk_dict = await client.get_private_key(pks[0])
            assert sk_dict["fingerprint"] == pks[0]
            assert sk_dict["sk"] is not None
            assert sk_dict["pk"] is not None
            assert sk_dict["seed"] is not None

            mnemonic = await client.generate_mnemonic()
            assert len(mnemonic) == 24

            await client.add_key(mnemonic)

            pks = await client.get_public_keys()
            assert len(pks) == 2

            await client.log_in_and_skip(pks[1])
            sk_dict = await client.get_private_key(pks[1])
            assert sk_dict["fingerprint"] == pks[1]

            # Add in reward addresses into farmer and pool for testing delete key checks
            # set farmer to first private key
            sk = await wallet_node.get_key_for_fingerprint(pks[0])
            test_ph = create_puzzlehash_for_pk(master_sk_to_wallet_sk(sk, uint32(0)).get_g1())
            test_config = load_config(wallet_node.root_path, "config.yaml")
            test_config["farmer"]["xch_target_address"] = encode_puzzle_hash(test_ph, "txch")
            # set pool to second private key
            sk = await wallet_node.get_key_for_fingerprint(pks[1])
            test_ph = create_puzzlehash_for_pk(master_sk_to_wallet_sk(sk, uint32(0)).get_g1())
            test_config["pool"]["xch_target_address"] = encode_puzzle_hash(test_ph, "txch")
            save_config(wallet_node.root_path, "config.yaml", test_config)

            # Check first key
            sk_dict = await client.check_delete_key(pks[0])
            assert sk_dict["fingerprint"] == pks[0]
            assert sk_dict["used_for_farmer_rewards"] is True
            assert sk_dict["used_for_pool_rewards"] is False

            # Check second key
            sk_dict = await client.check_delete_key(pks[1])
            assert sk_dict["fingerprint"] == pks[1]
            assert sk_dict["used_for_farmer_rewards"] is False
            assert sk_dict["used_for_pool_rewards"] is True

            # Check unknown key
            sk_dict = await client.check_delete_key(123456)
            assert sk_dict["fingerprint"] == 123456
            assert sk_dict["used_for_farmer_rewards"] is False
            assert sk_dict["used_for_pool_rewards"] is False

            await client.delete_key(pks[0])
            await client.log_in_and_skip(pks[1])
            assert len(await client.get_public_keys()) == 1

            assert not (await client.get_sync_status())

            wallets = await client.get_wallets()
            assert len(wallets) == 1
            balance = await client.get_wallet_balance(wallets[0]["id"])
            assert balance["unconfirmed_wallet_balance"] == 0

            try:
                await client.send_transaction(wallets[0]["id"], 100, addr)
                raise Exception("Should not create tx if no balance")
            except ValueError:
                pass
            # Delete all keys
            await client.delete_all_keys()
            assert len(await client.get_public_keys()) == 0
        finally:
            # Checks that the RPC manages to stop the node
            client.close()
            client_2.close()
            client_node.close()
            await client.await_closed()
            await client_2.await_closed()
            await client_node.await_closed()
            await rpc_cleanup()
            await rpc_cleanup_2()
            await rpc_cleanup_node()
Пример #20
0
    async def test_assert_announcement_consumed(self, two_nodes):

        num_blocks = 10
        wallet_a = WALLET_A
        coinbase_puzzlehash = WALLET_A_PUZZLE_HASHES[0]
        receiver_puzzlehash = BURN_PUZZLE_HASH

        # Farm blocks
        blocks = bt.get_consecutive_blocks(
            num_blocks,
            farmer_reward_puzzle_hash=coinbase_puzzlehash,
            guarantee_transaction_block=True)
        full_node_api_1, full_node_api_2, server_1, server_2 = two_nodes
        full_node_1 = full_node_api_1.full_node

        for block in blocks:
            await full_node_api_1.full_node.respond_block(
                full_node_protocol.RespondBlock(block))

        # Coinbase that gets spent
        block1 = blocks[2]
        block2 = blocks[3]

        spend_coin_block_1 = None
        spend_coin_block_2 = None
        for coin in list(block1.get_included_reward_coins()):
            if coin.puzzle_hash == coinbase_puzzlehash:
                spend_coin_block_1 = coin
        for coin in list(block2.get_included_reward_coins()):
            if coin.puzzle_hash == coinbase_puzzlehash:
                spend_coin_block_2 = coin

        # This condition requires block2 coinbase to be spent
        block1_cvp = ConditionWithArgs(
            ConditionOpcode.ASSERT_ANNOUNCEMENT,
            [
                Announcement(spend_coin_block_2.name(), bytes("test",
                                                              "utf-8")).name()
            ],
        )
        block1_dic = {block1_cvp.opcode: [block1_cvp]}
        block1_spend_bundle = wallet_a.generate_signed_transaction(
            1000, receiver_puzzlehash, spend_coin_block_1, block1_dic)

        # This condition requires block1 coinbase to be spent
        block2_cvp = ConditionWithArgs(
            ConditionOpcode.CREATE_ANNOUNCEMENT,
            [bytes("test", "utf-8")],
        )
        block2_dic = {block2_cvp.opcode: [block2_cvp]}
        block2_spend_bundle = wallet_a.generate_signed_transaction(
            1000, receiver_puzzlehash, spend_coin_block_2, block2_dic)

        # Invalid block bundle
        assert block1_spend_bundle is not None
        # Create another block that includes our transaction
        invalid_new_blocks = bt.get_consecutive_blocks(
            1,
            blocks,
            farmer_reward_puzzle_hash=coinbase_puzzlehash,
            transaction_data=block1_spend_bundle,
            guarantee_transaction_block=True,
        )

        # Try to validate that block
        res, err, _ = await full_node_1.blockchain.receive_block(
            invalid_new_blocks[-1])
        assert res == ReceiveBlockResult.INVALID_BLOCK
        assert err == Err.ASSERT_ANNOUNCE_CONSUMED_FAILED

        # bundle_together contains both transactions
        bundle_together = SpendBundle.aggregate(
            [block1_spend_bundle, block2_spend_bundle])

        # Create another block that includes our transaction
        new_blocks = bt.get_consecutive_blocks(
            1,
            blocks,
            farmer_reward_puzzle_hash=coinbase_puzzlehash,
            transaction_data=bundle_together,
            guarantee_transaction_block=True,
        )

        # Try to validate newly created block
        res, err, _ = await full_node_1.blockchain.receive_block(new_blocks[-1]
                                                                 )
        assert res == ReceiveBlockResult.NEW_PEAK
        assert err is None
Пример #21
0
    async def generate_launcher_spend(
        standard_wallet: Wallet,
        amount: uint64,
        initial_target_state: PoolState,
        genesis_challenge: bytes32,
        delay_time: uint64,
        delay_ph: bytes32,
    ) -> Tuple[SpendBundle, bytes32, bytes32]:
        """
        Creates the initial singleton, which includes spending an origin coin, the launcher, and creating a singleton
        with the "pooling" inner state, which can be either self pooling or using a pool
        """
        coins: Set[Coin] = await standard_wallet.select_coins(amount)
        if coins is None:
            raise ValueError("Not enough coins to create pool wallet")

        assert len(coins) == 1

        launcher_parent: Coin = coins.copy().pop()
        genesis_launcher_puz: Program = SINGLETON_LAUNCHER
        launcher_coin: Coin = Coin(launcher_parent.name(), genesis_launcher_puz.get_tree_hash(), amount)

        escaping_inner_puzzle: Program = create_waiting_room_inner_puzzle(
            initial_target_state.target_puzzle_hash,
            initial_target_state.relative_lock_height,
            initial_target_state.owner_pubkey,
            launcher_coin.name(),
            genesis_challenge,
            delay_time,
            delay_ph,
        )
        escaping_inner_puzzle_hash = escaping_inner_puzzle.get_tree_hash()

        self_pooling_inner_puzzle: Program = create_pooling_inner_puzzle(
            initial_target_state.target_puzzle_hash,
            escaping_inner_puzzle_hash,
            initial_target_state.owner_pubkey,
            launcher_coin.name(),
            genesis_challenge,
            delay_time,
            delay_ph,
        )

        if initial_target_state.state == SELF_POOLING:
            puzzle = escaping_inner_puzzle
        elif initial_target_state.state == FARMING_TO_POOL:
            puzzle = self_pooling_inner_puzzle
        else:
            raise ValueError("Invalid initial state")
        full_pooling_puzzle: Program = create_full_puzzle(puzzle, launcher_id=launcher_coin.name())

        puzzle_hash: bytes32 = full_pooling_puzzle.get_tree_hash()
        pool_state_bytes = Program.to([("p", bytes(initial_target_state)), ("t", delay_time), ("h", delay_ph)])
        announcement_set: Set[Announcement] = set()
        announcement_message = Program.to([puzzle_hash, amount, pool_state_bytes]).get_tree_hash()
        announcement_set.add(Announcement(launcher_coin.name(), announcement_message))

        create_launcher_tx_record: Optional[TransactionRecord] = await standard_wallet.generate_signed_transaction(
            amount,
            genesis_launcher_puz.get_tree_hash(),
            uint64(0),
            None,
            coins,
            None,
            False,
            announcement_set,
        )
        assert create_launcher_tx_record is not None and create_launcher_tx_record.spend_bundle is not None

        genesis_launcher_solution: Program = Program.to([puzzle_hash, amount, pool_state_bytes])

        launcher_cs: CoinSpend = CoinSpend(
            launcher_coin,
            SerializedProgram.from_program(genesis_launcher_puz),
            SerializedProgram.from_program(genesis_launcher_solution),
        )
        launcher_sb: SpendBundle = SpendBundle([launcher_cs], G2Element())

        # Current inner will be updated when state is verified on the blockchain
        full_spend: SpendBundle = SpendBundle.aggregate([create_launcher_tx_record.spend_bundle, launcher_sb])
        return full_spend, puzzle_hash, launcher_coin.name()
Пример #22
0
    async def _generate_unsigned_transaction(
        self,
        amount: uint64,
        newpuzzlehash: bytes32,
        fee: uint64 = uint64(0),
        origin_id: bytes32 = None,
        coins: Set[Coin] = None,
        primaries_input: Optional[List[Dict[str, Any]]] = None,
        ignore_max_send_amount: bool = False,
        announcements_to_consume: Set[Announcement] = None,
    ) -> List[CoinSpend]:
        """
        Generates a unsigned transaction in form of List(Puzzle, Solutions)
        Note: this must be called under a wallet state manager lock
        """
        if primaries_input is None:
            primaries: Optional[List[Dict]] = None
            total_amount = amount + fee
        else:
            primaries = primaries_input.copy()
            primaries_amount = 0
            for prim in primaries:
                primaries_amount += prim["amount"]
            total_amount = amount + fee + primaries_amount

        if not ignore_max_send_amount:
            max_send = await self.get_max_send_amount()
            if total_amount > max_send:
                raise ValueError(
                    f"Can't send more than {max_send} in a single transaction")

        if coins is None:
            coins = await self.select_coins(total_amount)
        assert len(coins) > 0

        self.log.info(f"coins is not None {coins}")
        spend_value = sum([coin.amount for coin in coins])
        change = spend_value - total_amount
        assert change >= 0

        spends: List[CoinSpend] = []
        primary_announcement_hash: Optional[bytes32] = None

        # Check for duplicates
        if primaries is not None:
            all_primaries_list = [
                (p["puzzlehash"], p["amount"]) for p in primaries
            ] + [(newpuzzlehash, amount)]
            if len(set(all_primaries_list)) != len(all_primaries_list):
                raise ValueError("Cannot create two identical coins")

        for coin in coins:
            self.log.info(f"coin from coins {coin}")
            puzzle: Program = await self.puzzle_for_puzzle_hash(
                coin.puzzle_hash)

            # Only one coin creates outputs
            if primary_announcement_hash is None and origin_id in (
                    None, coin.name()):
                if primaries is None:
                    primaries = [{
                        "puzzlehash": newpuzzlehash,
                        "amount": amount
                    }]
                else:
                    primaries.append({
                        "puzzlehash": newpuzzlehash,
                        "amount": amount
                    })
                if change > 0:
                    change_puzzle_hash: bytes32 = await self.get_new_puzzlehash(
                    )
                    primaries.append({
                        "puzzlehash": change_puzzle_hash,
                        "amount": change
                    })
                message_list: List[bytes32] = [c.name() for c in coins]
                for primary in primaries:
                    message_list.append(
                        Coin(coin.name(), primary["puzzlehash"],
                             primary["amount"]).name())
                message: bytes32 = std_hash(b"".join(message_list))
                solution: Program = self.make_solution(
                    primaries=primaries,
                    fee=fee,
                    coin_announcements={message},
                    coin_announcements_to_assert=announcements_to_consume,
                )
                primary_announcement_hash = Announcement(coin.name(),
                                                         message).name()
            else:
                solution = self.make_solution(
                    coin_announcements_to_assert={primary_announcement_hash})

            spends.append(
                CoinSpend(coin, SerializedProgram.from_bytes(bytes(puzzle)),
                          SerializedProgram.from_bytes(bytes(solution))))

        self.log.info(f"Spends is {spends}")
        return spends
Пример #23
0
    def generate_unsigned_transaction(
        self,
        amount: uint64,
        new_puzzle_hash: bytes32,
        coins: List[Coin],
        condition_dic: Dict[ConditionOpcode, List[ConditionWithArgs]],
        fee: int = 0,
        secret_key: Optional[PrivateKey] = None,
        additional_outputs: Optional[List[Tuple[bytes32, int]]] = None,
    ) -> List[CoinSpend]:
        spends = []

        spend_value = sum([c.amount for c in coins])

        if ConditionOpcode.CREATE_COIN not in condition_dic:
            condition_dic[ConditionOpcode.CREATE_COIN] = []
        if ConditionOpcode.CREATE_COIN_ANNOUNCEMENT not in condition_dic:
            condition_dic[ConditionOpcode.CREATE_COIN_ANNOUNCEMENT] = []

        output = ConditionWithArgs(
            ConditionOpcode.CREATE_COIN,
            [new_puzzle_hash, int_to_bytes(amount)])
        condition_dic[output.opcode].append(output)
        if additional_outputs is not None:
            for o in additional_outputs:
                out = ConditionWithArgs(ConditionOpcode.CREATE_COIN,
                                        [o[0], int_to_bytes(o[1])])
                condition_dic[out.opcode].append(out)

        amount_total = sum(
            int_from_bytes(cvp.vars[1])
            for cvp in condition_dic[ConditionOpcode.CREATE_COIN])
        change = spend_value - amount_total - fee
        if change > 0:
            change_puzzle_hash = self.get_new_puzzlehash()
            change_output = ConditionWithArgs(
                ConditionOpcode.CREATE_COIN,
                [change_puzzle_hash, int_to_bytes(change)])
            condition_dic[output.opcode].append(change_output)

        secondary_coins_cond_dic: Dict[ConditionOpcode,
                                       List[ConditionWithArgs]] = dict()
        secondary_coins_cond_dic[ConditionOpcode.ASSERT_COIN_ANNOUNCEMENT] = []
        for n, coin in enumerate(coins):
            puzzle_hash = coin.puzzle_hash
            if secret_key is None:
                secret_key = self.get_private_key_for_puzzle_hash(puzzle_hash)
            pubkey = secret_key.get_g1()
            puzzle = puzzle_for_pk(bytes(pubkey))
            if n == 0:
                message_list = [c.name() for c in coins]
                for outputs in condition_dic[ConditionOpcode.CREATE_COIN]:
                    message_list.append(
                        Coin(coin.name(), outputs.vars[0],
                             int_from_bytes(outputs.vars[1])).name())
                message = std_hash(b"".join(message_list))
                condition_dic[ConditionOpcode.CREATE_COIN_ANNOUNCEMENT].append(
                    ConditionWithArgs(ConditionOpcode.CREATE_COIN_ANNOUNCEMENT,
                                      [message]))
                primary_announcement_hash = Announcement(coin.name(),
                                                         message).name()
                secondary_coins_cond_dic[
                    ConditionOpcode.ASSERT_COIN_ANNOUNCEMENT].append(
                        ConditionWithArgs(
                            ConditionOpcode.ASSERT_COIN_ANNOUNCEMENT,
                            [primary_announcement_hash]))
                main_solution = self.make_solution(condition_dic)
                spends.append(CoinSpend(coin, puzzle, main_solution))
            else:
                spends.append(
                    CoinSpend(coin, puzzle,
                              self.make_solution(secondary_coins_cond_dic)))
        return spends
Пример #24
0
    async def generate_unsigned_spendbundle(
        self,
        payments: List[Payment],
        fee: uint64 = uint64(0),
        cat_discrepancy: Optional[Tuple[
            int, Program]] = None,  # (extra_delta, limitations_solution)
        coins: Set[Coin] = None,
        coin_announcements_to_consume: Optional[Set[Announcement]] = None,
        puzzle_announcements_to_consume: Optional[Set[Announcement]] = None,
    ) -> Tuple[SpendBundle, Optional[TransactionRecord]]:
        if coin_announcements_to_consume is not None:
            coin_announcements_bytes: Optional[Set[bytes32]] = {
                a.name()
                for a in coin_announcements_to_consume
            }
        else:
            coin_announcements_bytes = None

        if puzzle_announcements_to_consume is not None:
            puzzle_announcements_bytes: Optional[Set[bytes32]] = {
                a.name()
                for a in puzzle_announcements_to_consume
            }
        else:
            puzzle_announcements_bytes = None

        if cat_discrepancy is not None:
            extra_delta, limitations_solution = cat_discrepancy
        else:
            extra_delta, limitations_solution = 0, Program.to([])
        payment_amount: int = sum([p.amount for p in payments])
        starting_amount: int = payment_amount - extra_delta

        if coins is None:
            cat_coins = await self.select_coins(uint64(starting_amount))
        else:
            cat_coins = coins

        selected_cat_amount = sum([c.amount for c in cat_coins])
        assert selected_cat_amount >= starting_amount

        # Figure out if we need to absorb/melt some XCH as part of this
        regular_chia_to_claim: int = 0
        if payment_amount > starting_amount:
            fee = uint64(fee + payment_amount - starting_amount)
        elif payment_amount < starting_amount:
            regular_chia_to_claim = payment_amount

        need_chia_transaction = (fee > 0 or regular_chia_to_claim > 0) and (
            fee - regular_chia_to_claim != 0)

        # Calculate standard puzzle solutions
        change = selected_cat_amount - starting_amount
        primaries: List[AmountWithPuzzlehash] = []
        for payment in payments:
            primaries.append({
                "puzzlehash": payment.puzzle_hash,
                "amount": payment.amount,
                "memos": payment.memos
            })

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

        limitations_program_reveal = Program.to([])
        if self.cat_info.my_tail is None:
            assert cat_discrepancy is None
        elif cat_discrepancy is not None:
            limitations_program_reveal = self.cat_info.my_tail

        # Loop through the coins we've selected and gather the information we need to spend them
        spendable_cc_list = []
        chia_tx = None
        first = True
        for coin in cat_coins:
            if first:
                first = False
                if need_chia_transaction:
                    if fee > regular_chia_to_claim:
                        announcement = Announcement(coin.name(), b"$", b"\xca")
                        chia_tx, _ = await self.create_tandem_xch_tx(
                            fee,
                            uint64(regular_chia_to_claim),
                            announcement_to_assert=announcement)
                        innersol = self.standard_wallet.make_solution(
                            primaries=primaries,
                            coin_announcements={announcement.message},
                            coin_announcements_to_assert=
                            coin_announcements_bytes,
                            puzzle_announcements_to_assert=
                            puzzle_announcements_bytes,
                        )
                    elif regular_chia_to_claim > fee:
                        chia_tx, _ = await self.create_tandem_xch_tx(
                            fee, uint64(regular_chia_to_claim))
                        innersol = self.standard_wallet.make_solution(
                            primaries=primaries,
                            coin_announcements_to_assert={announcement.name()})
                else:
                    innersol = self.standard_wallet.make_solution(
                        primaries=primaries,
                        coin_announcements_to_assert=coin_announcements_bytes,
                        puzzle_announcements_to_assert=
                        puzzle_announcements_bytes,
                    )
            else:
                innersol = self.standard_wallet.make_solution(primaries=[])
            inner_puzzle = await self.inner_puzzle_for_cc_puzhash(
                coin.puzzle_hash)
            lineage_proof = await self.get_lineage_proof_for_coin(coin)
            assert lineage_proof is not None
            new_spendable_cc = SpendableCAT(
                coin,
                self.cat_info.limitations_program_hash,
                inner_puzzle,
                innersol,
                limitations_solution=limitations_solution,
                extra_delta=extra_delta,
                lineage_proof=lineage_proof,
                limitations_program_reveal=limitations_program_reveal,
            )
            spendable_cc_list.append(new_spendable_cc)

        cat_spend_bundle = unsigned_spend_bundle_for_spendable_cats(
            CAT_MOD, spendable_cc_list)
        chia_spend_bundle = SpendBundle([], G2Element())
        if chia_tx is not None and chia_tx.spend_bundle is not None:
            chia_spend_bundle = chia_tx.spend_bundle

        return (
            SpendBundle.aggregate([
                cat_spend_bundle,
                chia_spend_bundle,
            ]),
            chia_tx,
        )
Пример #25
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
Пример #26
0
async def create_absorb_transaction(
    node_rpc_client: FullNodeRpcClient,
    farmer_record: FarmerRecord,
    peak_height: uint32,
    reward_coin_records: List[CoinRecord],
    genesis_challenge: bytes32,
    fee_amount: Optional[uint64] = None,
    wallet_rpc_client: Optional[WalletRpcClient] = None,
    fee_target_puzzle_hash: Optional[bytes32] = None,
) -> Optional[SpendBundle]:
    singleton_state_tuple: Optional[Tuple[
        CoinSpend, PoolState,
        PoolState]] = await get_singleton_state(node_rpc_client,
                                                farmer_record.launcher_id,
                                                farmer_record, peak_height, 0,
                                                genesis_challenge)
    if singleton_state_tuple is None:
        log.info(f"Invalid singleton {farmer_record.launcher_id}.")
        return None
    last_spend, last_state, last_state_2 = singleton_state_tuple
    # Here the buried state is equivalent to the latest state, because we use 0 as the security_threshold
    assert last_state == last_state_2

    if last_state.state == PoolSingletonState.SELF_POOLING:
        log.info(
            f"Don't try to absorb from former farmer {farmer_record.launcher_id}."
        )
        return None

    launcher_coin_record: Optional[
        CoinRecord] = await node_rpc_client.get_coin_record_by_name(
            farmer_record.launcher_id)
    assert launcher_coin_record is not None
    coin_announcements: List[Announcement] = []

    all_spends: List[CoinSpend] = []
    for reward_coin_record in reward_coin_records:
        found_block_index: Optional[uint32] = get_farmed_height(
            reward_coin_record, genesis_challenge)
        if not found_block_index:
            # The puzzle does not allow spending coins that are not a coinbase reward
            log.info(
                f"Received reward {reward_coin_record.coin} that is not a pool reward."
            )
            continue
        absorb_spend: List[CoinSpend] = create_absorb_spend(
            last_spend,
            last_state,
            launcher_coin_record.coin,
            found_block_index,
            genesis_challenge,
            farmer_record.delay_time,
            farmer_record.delay_puzzle_hash,
        )
        if fee_amount > 0:
            coin_announcements.append(
                Announcement(reward_coin_record.coin.name(), b"$"))
        last_spend = absorb_spend[0]
        all_spends += absorb_spend
        # TODO(pool): handle the case where the cost exceeds the size of the block

    if len(coin_announcements) > 0:
        # address can be anything
        signed_transaction: TransactionRecord = await wallet_rpc_client.create_signed_transaction(
            additions=[{
                "amount": uint64(1),
                "puzzle_hash": fee_target_puzzle_hash
            }],
            fee=uint64(fee_amount * len(coin_announcements)),
            coin_announcements=coin_announcements,
        )
        fee_spend_bundle: Optional[
            SpendBundle] = signed_transaction.spend_bundle
    else:
        fee_spend_bundle = None

    if len(all_spends) == 0:
        return None
    spend_bundle: SpendBundle = SpendBundle(all_spends, G2Element())
    if fee_spend_bundle is not None:
        spend_bundle = SpendBundle.aggregate([spend_bundle, fee_spend_bundle])

    return spend_bundle