diff --git a/chia/cmds/wallet_funcs.py b/chia/cmds/wallet_funcs.py index e27e122c06ae..42145419ba47 100644 --- a/chia/cmds/wallet_funcs.py +++ b/chia/cmds/wallet_funcs.py @@ -760,7 +760,8 @@ async def list_nfts(args: Dict, wallet_client: WalletRpcClient, fingerprint: int response = await wallet_client.list_nfts(wallet_id) nft_list = response["nft_list"] if len(nft_list) > 0: - from chia.wallet.nft_wallet.nft_info import NFTInfo + from chia.wallet.did_wallet.did_info import DID_HRP + from chia.wallet.nft_wallet.nft_info import NFT_HRP, NFTInfo indent: str = " " @@ -771,11 +772,12 @@ async def list_nfts(args: Dict, wallet_client: WalletRpcClient, fingerprint: int else: owner_pubkey = nft.owner_pubkey.hex() print() + print(f"{'NFT identifier:'.ljust(26)} {encode_puzzle_hash(nft.launcher_id, NFT_HRP)}") print(f"{'Launcher coin ID:'.ljust(26)} {nft.launcher_id}") print(f"{'Launcher puzhash:'.ljust(26)} {nft.launcher_puzhash}") print(f"{'Current NFT coin ID:'.ljust(26)} {nft.nft_coin_id}") print(f"{'On-chain data/info:'.ljust(26)} {nft.chain_info}") - print(f"{'Owner DID:'.ljust(26)} {nft.owner_did}") + print(f"{'Owner DID:'.ljust(26)} {encode_puzzle_hash(nft.owner_did, DID_HRP)}") print(f"{'Owner pubkey:'.ljust(26)} {owner_pubkey}") print(f"{'Royalty percentage:'.ljust(26)} {nft.royalty_percentage}") print(f"{'Royalty puzhash:'.ljust(26)} {nft.royalty_puzzle_hash}") diff --git a/chia/rpc/wallet_rpc_api.py b/chia/rpc/wallet_rpc_api.py index b9257bebeef8..8fa964e90bc2 100644 --- a/chia/rpc/wallet_rpc_api.py +++ b/chia/rpc/wallet_rpc_api.py @@ -36,9 +36,10 @@ from chia.wallet.derive_keys import ( master_sk_to_singleton_owner_sk, match_address_to_sk, ) +from chia.wallet.did_wallet.did_info import DID_HRP from chia.wallet.did_wallet.did_wallet import DIDWallet from chia.wallet.nft_wallet import nft_puzzles -from chia.wallet.nft_wallet.nft_info import NFTInfo +from chia.wallet.nft_wallet.nft_info import NFT_HRP, NFTInfo from chia.wallet.nft_wallet.nft_wallet import NFTWallet, NFTCoinInfo from chia.wallet.nft_wallet.uncurry_nft import UncurriedNFT from chia.wallet.outer_puzzles import AssetType @@ -519,7 +520,7 @@ class WalletRpcApi: backup_dids = [] num_needed = 0 for d in request["backup_dids"]: - backup_dids.append(hexstr_to_bytes(d)) + backup_dids.append(decode_puzzle_hash(d)) if len(backup_dids) > 0: num_needed = uint64(request["num_of_backup_ids_needed"]) metadata: Dict[str, str] = {} @@ -539,7 +540,7 @@ class WalletRpcApi: uint64(request.get("fee", 0)), ) - my_did = did_wallet.get_my_DID() + my_did = encode_puzzle_hash(bytes32.fromhex(did_wallet.get_my_DID()), DID_HRP) return { "success": True, "type": did_wallet.type(), @@ -578,7 +579,7 @@ class WalletRpcApi: for wallet in self.service.wallet_state_manager.wallets.values(): did_id: Optional[bytes32] = None if "did_id" in request and request["did_id"] is not None: - did_id = bytes32.from_hexstr(request["did_id"]) + did_id = decode_puzzle_hash(request["did_id"]) if wallet.type() == WalletType.NFT and wallet.get_did() == did_id: log.info("NFT wallet already existed, skipping.") return { @@ -1140,7 +1141,7 @@ class WalletRpcApi: recovery_list = [] success: bool = False for _ in request["new_list"]: - recovery_list.append(hexstr_to_bytes(_)) + recovery_list.append(decode_puzzle_hash(_)) if "num_verifications_required" in request: new_amount_verifications_required = uint64(request["num_verifications_required"]) else: @@ -1174,7 +1175,7 @@ class WalletRpcApi: async def did_get_did(self, request): wallet_id = int(request["wallet_id"]) wallet: DIDWallet = self.service.wallet_state_manager.wallets[wallet_id] - my_did: str = wallet.get_my_DID() + my_did: str = encode_puzzle_hash(bytes32.fromhex(wallet.get_my_DID()), DID_HRP) async with self.service.wallet_state_manager.lock: coins = await wallet.select_coins(1) if coins is None or coins == set(): @@ -1187,13 +1188,13 @@ class WalletRpcApi: wallet_id = int(request["wallet_id"]) wallet: DIDWallet = self.service.wallet_state_manager.wallets[wallet_id] recovery_list = wallet.did_info.backup_ids - recover_hex_list = [] - for _ in recovery_list: - recover_hex_list.append(_.hex()) + recovery_dids = [] + for backup_id in recovery_list: + recovery_dids.append(encode_puzzle_hash(backup_id, DID_HRP)) return { "success": True, "wallet_id": wallet_id, - "recovery_list": recover_hex_list, + "recovery_list": recovery_dids, "num_required": wallet.did_info.num_of_backup_ids_needed, } @@ -1271,7 +1272,7 @@ class WalletRpcApi: async def did_get_information_needed_for_recovery(self, request): wallet_id = int(request["wallet_id"]) did_wallet: DIDWallet = self.service.wallet_state_manager.wallets[wallet_id] - my_did = did_wallet.get_my_DID() + my_did = encode_puzzle_hash(bytes32.from_hexstr(did_wallet.get_my_DID()), DID_HRP) coin_name = did_wallet.did_info.temp_coin.name().hex() return { "success": True, @@ -1286,7 +1287,7 @@ class WalletRpcApi: async def did_get_current_coin_info(self, request): wallet_id = int(request["wallet_id"]) did_wallet: DIDWallet = self.service.wallet_state_manager.wallets[wallet_id] - my_did = did_wallet.get_my_DID() + my_did = encode_puzzle_hash(bytes32.from_hexstr(did_wallet.get_my_DID()), DID_HRP) did_coin_threeple = await did_wallet.get_info_for_recovery() assert my_did is not None assert did_coin_threeple is not None @@ -1369,7 +1370,10 @@ class WalletRpcApi: fee = uint64(request.get("fee", 0)) did_id = request.get("did_id", None) if did_id is not None: - did_id = bytes.fromhex(did_id) + if did_id == "": + did_id = bytes() + else: + did_id = decode_puzzle_hash(did_id) spend_bundle = await nft_wallet.generate_new_nft( metadata, target_puzhash, @@ -1393,7 +1397,7 @@ class WalletRpcApi: async def nft_get_by_did(self, request) -> Dict: did_id: Optional[bytes32] = None if "did_id" in request: - did_id = bytes32.from_hexstr(request["did_id"]) + did_id = decode_puzzle_hash(request["did_id"]) assert self.service.wallet_state_manager is not None for wallet in self.service.wallet_state_manager.wallets.values(): if isinstance(wallet, NFTWallet) and wallet.get_did() == did_id: @@ -1436,7 +1440,12 @@ class WalletRpcApi: return dict(success=False, error="target_address parameter missing") nft_wallet: NFTWallet = self.service.wallet_state_manager.wallets[wallet_id] try: - nft_coin_info = nft_wallet.get_nft_coin_by_id(bytes32.from_hexstr(request["nft_coin_id"])) + nft_coin_id = request["nft_coin_id"] + if nft_coin_id.startswith(NFT_HRP): + nft_coin_id = decode_puzzle_hash(nft_coin_id) + else: + nft_coin_id = bytes32.from_hexstr(nft_coin_id) + nft_coin_info = nft_wallet.get_nft_coin_by_id(nft_coin_id) fee = uint64(request.get("fee", 0)) spend_bundle = await nft_wallet.transfer_nft(nft_coin_info, puzzle_hash, fee=fee) return {"wallet_id": wallet_id, "success": True, "spend_bundle": spend_bundle} @@ -1448,7 +1457,11 @@ class WalletRpcApi: assert self.service.wallet_state_manager is not None if "coin_id" not in request: return {"success": False, "error": "Coin ID is required."} - coin_id = bytes32.from_hexstr(request["coin_id"]) + coin_id = request["coin_id"] + if coin_id.startswith(NFT_HRP): + coin_id = decode_puzzle_hash(coin_id) + else: + coin_id = bytes32.from_hexstr(coin_id) peer = self.service.wallet_state_manager.wallet_node.get_full_node_peer() if peer is None: return {"success": False, "error": "Cannot find a full node peer."} @@ -1539,7 +1552,12 @@ class WalletRpcApi: try: uri = request["uri"] key = request["key"] - nft_coin_info = nft_wallet.get_nft_coin_by_id(bytes32.from_hexstr(request["nft_coin_id"])) + nft_coin_id = request["nft_coin_id"] + if nft_coin_id.startswith(NFT_HRP): + nft_coin_id = decode_puzzle_hash(nft_coin_id) + else: + nft_coin_id = bytes32.from_hexstr(nft_coin_id) + nft_coin_info = nft_wallet.get_nft_coin_by_id(nft_coin_id) fee = uint64(request.get("fee", 0)) spend_bundle = await nft_wallet.update_metadata(nft_coin_info, key, uri, fee=fee) return {"wallet_id": wallet_id, "success": True, "spend_bundle": spend_bundle} diff --git a/chia/rpc/wallet_rpc_client.py b/chia/rpc/wallet_rpc_client.py index fa826dab5330..8a9048382570 100644 --- a/chia/rpc/wallet_rpc_client.py +++ b/chia/rpc/wallet_rpc_client.py @@ -631,8 +631,8 @@ class WalletRpcClient(RpcClient): response = await self.fetch("nft_add_uri", request) return response - async def get_nft_info(self, coin_id: bytes32, latest: bool = True): - request: Dict[str, Any] = {"coin_id": coin_id.hex(), "latest": latest} + async def get_nft_info(self, coin_id: str, latest: bool = True): + request: Dict[str, Any] = {"coin_id": coin_id, "latest": latest} response = await self.fetch("nft_get_info", request) return response diff --git a/chia/wallet/did_wallet/did_info.py b/chia/wallet/did_wallet/did_info.py index cd84153016fc..9bfdc5058c67 100644 --- a/chia/wallet/did_wallet/did_info.py +++ b/chia/wallet/did_wallet/did_info.py @@ -8,6 +8,8 @@ from chia.wallet.lineage_proof import LineageProof from chia.types.blockchain_format.program import Program from chia.types.blockchain_format.coin import Coin +DID_HRP = "did:chia:" + @streamable @dataclass(frozen=True) diff --git a/chia/wallet/nft_wallet/nft_info.py b/chia/wallet/nft_wallet/nft_info.py index bade90808b13..cb699f25ac1d 100644 --- a/chia/wallet/nft_wallet/nft_info.py +++ b/chia/wallet/nft_wallet/nft_info.py @@ -11,6 +11,8 @@ from chia.wallet.puzzles.load_clvm import load_clvm LAUNCHER_PUZZLE = load_clvm("singleton_launcher.clvm") +NFT_HRP = "nft" + @streamable @dataclass(frozen=True) diff --git a/tests/wallet/nft_wallet/test_nft_wallet.py b/tests/wallet/nft_wallet/test_nft_wallet.py index ade30bd0c9ca..1b828e0fd7f7 100644 --- a/tests/wallet/nft_wallet/test_nft_wallet.py +++ b/tests/wallet/nft_wallet/test_nft_wallet.py @@ -364,6 +364,9 @@ async def test_nft_wallet_rpc_creation_and_list(two_wallet_nodes: Any, trusted: ) @pytest.mark.asyncio async def test_nft_wallet_rpc_update_metadata(two_wallet_nodes: Any, trusted: Any) -> None: + from chia.types.blockchain_format.sized_bytes import bytes32 + from chia.wallet.nft_wallet.nft_info import NFT_HRP + num_blocks = 3 full_nodes, wallets = two_wallet_nodes full_node_api = full_nodes[0] @@ -452,9 +455,9 @@ async def test_nft_wallet_rpc_update_metadata(two_wallet_nodes: Any, trusted: An ] ) ) - nft_coin_id = coin["nft_coin_id"] + # add another URI using a bech32m nft_coin_id await time_out_assert(15, wallet_0.get_pending_change_balance, 0) - # add another URI + nft_coin_id = encode_puzzle_hash(bytes32.from_hexstr(coin["nft_coin_id"]), NFT_HRP) tr1 = await api_0.nft_add_uri( {"wallet_id": nft_wallet_0_id, "nft_coin_id": nft_coin_id, "uri": "http://metadata", "key": "mu"} ) @@ -490,7 +493,7 @@ async def test_nft_wallet_rpc_update_metadata(two_wallet_nodes: Any, trusted: An await asyncio.sleep(0.5) time_left -= 0.5 - # add yet another URI + # add yet another URI, this time using a hex nft_coin_id await time_out_assert(15, wallet_0.get_pending_change_balance, 0) nft_coin_id = coin["nft_coin_id"] tr1 = await api_0.nft_add_uri( @@ -535,6 +538,8 @@ async def test_nft_wallet_rpc_update_metadata(two_wallet_nodes: Any, trusted: An ) @pytest.mark.asyncio async def test_nft_with_did_wallet_creation(two_wallet_nodes: Any, trusted: Any) -> None: + from chia.wallet.did_wallet.did_info import DID_HRP + num_blocks = 3 full_nodes, wallets = two_wallet_nodes full_node_api: FullNodeSimulator = full_nodes[0] @@ -580,14 +585,15 @@ async def test_nft_with_did_wallet_creation(two_wallet_nodes: Any, trusted: Any) await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph)) await time_out_assert(15, wallet_0.get_pending_change_balance, 0) hex_did_id = did_wallet.get_my_DID() + hmr_did_id = encode_puzzle_hash(bytes32.from_hexstr(hex_did_id), DID_HRP) - res = await api_0.create_new_wallet(dict(wallet_type="nft_wallet", name="NFT WALLET 1", did_id=hex_did_id)) + res = await api_0.create_new_wallet(dict(wallet_type="nft_wallet", name="NFT WALLET 1", did_id=hmr_did_id)) assert isinstance(res, dict) assert res.get("success") nft_wallet_0_id = res["wallet_id"] # this shouldn't work - res = await api_0.create_new_wallet(dict(wallet_type="nft_wallet", name="NFT WALLET 1", did_id=hex_did_id)) + res = await api_0.create_new_wallet(dict(wallet_type="nft_wallet", name="NFT WALLET 1", did_id=hmr_did_id)) assert isinstance(res, dict) assert res.get("success") assert res["wallet_id"] == nft_wallet_0_id @@ -598,7 +604,7 @@ async def test_nft_with_did_wallet_creation(two_wallet_nodes: Any, trusted: Any) nft_wallet_p2_puzzle = res["wallet_id"] assert nft_wallet_p2_puzzle != nft_wallet_0_id - res = await api_0.nft_get_by_did({"did_id": hex_did_id}) + res = await api_0.nft_get_by_did({"did_id": hmr_did_id}) assert nft_wallet_0_id == res["wallet_id"] await time_out_assert(10, wallet_0.get_unconfirmed_balance, 5999999999999) await time_out_assert(10, wallet_0.get_confirmed_balance, 5999999999999) @@ -702,6 +708,8 @@ async def test_nft_with_did_wallet_creation(two_wallet_nodes: Any, trusted: Any) ) @pytest.mark.asyncio async def test_nft_rpc_mint(two_wallet_nodes: Any, trusted: Any) -> None: + from chia.wallet.did_wallet.did_info import DID_HRP + num_blocks = 3 full_nodes, wallets = two_wallet_nodes full_node_api: FullNodeSimulator = full_nodes[0] @@ -748,9 +756,9 @@ async def test_nft_rpc_mint(two_wallet_nodes: Any, trusted: Any) -> None: for _ in range(1, num_blocks): await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph)) await time_out_assert(15, wallet_0.get_pending_change_balance, 0) - hex_did_id = did_wallet.get_my_DID() + did_id = encode_puzzle_hash(bytes32.from_hexstr(did_wallet.get_my_DID()), DID_HRP) - res = await api_0.create_new_wallet(dict(wallet_type="nft_wallet", name="NFT WALLET 1", did_id=hex_did_id)) + res = await api_0.create_new_wallet(dict(wallet_type="nft_wallet", name="NFT WALLET 1", did_id=did_id)) assert isinstance(res, dict) assert res.get("success") nft_wallet_0_id = res["wallet_id"] diff --git a/tests/wallet/rpc/test_wallet_rpc.py b/tests/wallet/rpc/test_wallet_rpc.py index 46cbd4e3e17d..d288ed642ed1 100644 --- a/tests/wallet/rpc/test_wallet_rpc.py +++ b/tests/wallet/rpc/test_wallet_rpc.py @@ -735,6 +735,8 @@ async def test_offer_endpoints(wallet_rpc_environment: WalletRpcTestEnvironment) @pytest.mark.asyncio async def test_did_endpoints(wallet_rpc_environment: WalletRpcTestEnvironment): + from chia.wallet.did_wallet.did_info import DID_HRP + env: WalletRpcTestEnvironment = wallet_rpc_environment wallet_1: Wallet = env.wallet_1.wallet @@ -818,13 +820,16 @@ async def test_did_endpoints(wallet_rpc_environment: WalletRpcTestEnvironment): ) ) did_wallet_2: DIDWallet = wallet_2_node.wallet_state_manager.wallets[did_wallets[0].id] - assert did_wallet_2.get_my_DID() == did_id_0 + assert encode_puzzle_hash(bytes32.from_hexstr(did_wallet_2.get_my_DID()), DID_HRP) == did_id_0 metadata = json.loads(did_wallet_2.did_info.metadata) assert metadata["Twitter"] == "Https://test" @pytest.mark.asyncio async def test_nft_endpoints(wallet_rpc_environment: WalletRpcTestEnvironment): + + from chia.wallet.nft_wallet.nft_info import NFT_HRP + env: WalletRpcTestEnvironment = wallet_rpc_environment wallet_1_node: WalletNode = env.wallet_1.node wallet_1_rpc: WalletRpcClient = env.wallet_1.rpc_client @@ -852,12 +857,17 @@ async def test_nft_endpoints(wallet_rpc_environment: WalletRpcTestEnvironment): assert wallet_1_node.wallet_state_manager is not None nft_wallet: NFTWallet = wallet_1_node.wallet_state_manager.wallets[nft_wallet_id] - nft_id = nft_wallet.get_current_nfts()[0].coin.name() + # Test with the hex version of nft_id + nft_id = nft_wallet.get_current_nfts()[0].coin.name().hex() nft_info = (await wallet_1_rpc.get_nft_info(nft_id))["nft_info"] assert nft_info["nft_coin_id"][2:] == nft_wallet.get_current_nfts()[0].coin.name().hex() + # Test with the bech32m version of nft_id + hmr_nft_id = encode_puzzle_hash(nft_wallet.get_current_nfts()[0].coin.name(), NFT_HRP) + nft_info = (await wallet_1_rpc.get_nft_info(hmr_nft_id))["nft_info"] + assert nft_info["nft_coin_id"][2:] == nft_wallet.get_current_nfts()[0].coin.name().hex() addr = encode_puzzle_hash(await wallet_2.get_new_puzzlehash(), "txch") - res = await wallet_1_rpc.transfer_nft(nft_wallet_id, nft_id.hex(), addr, 0) + res = await wallet_1_rpc.transfer_nft(nft_wallet_id, nft_id, addr, 0) assert res["success"] for _ in range(3):