mirror of
https://github.com/Chia-Network/chia-blockchain.git
synced 2026-09-05 02:24:21 -05:00
Merge pull request #11740 from Chia-Network/nft1_extend_info
Extend API for NFT1
This commit is contained in:
@@ -23,7 +23,7 @@ from chia.types.spend_bundle import SpendBundle
|
||||
from chia.util.bech32m import decode_puzzle_hash, encode_puzzle_hash
|
||||
from chia.util.byte_types import hexstr_to_bytes
|
||||
from chia.util.config import load_config
|
||||
from chia.util.ints import uint8, uint32, uint64
|
||||
from chia.util.ints import uint8, uint32, uint64, uint16
|
||||
from chia.util.keychain import KeyringIsLocked, bytes_to_mnemonic, generate_mnemonic
|
||||
from chia.util.path import path_from_root
|
||||
from chia.util.ws_message import WsRpcMessage, create_payload_dict
|
||||
@@ -574,18 +574,20 @@ class WalletRpcApi:
|
||||
pass
|
||||
elif request["wallet_type"] == "nft_wallet":
|
||||
for wallet in self.service.wallet_state_manager.wallets.values():
|
||||
if wallet.type() == WalletType.NFT:
|
||||
# TODO Modify this for NFT1
|
||||
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"])
|
||||
if wallet.type() == WalletType.NFT and wallet.get_did() == did_id:
|
||||
log.info("NFT wallet already existed, skipping.")
|
||||
return {
|
||||
"success": True,
|
||||
"type": wallet.type(),
|
||||
"wallet_id": wallet.id(),
|
||||
}
|
||||
|
||||
async with self.service.wallet_state_manager.lock:
|
||||
nft_wallet: NFTWallet = await NFTWallet.create_new_nft_wallet(
|
||||
wallet_state_manager,
|
||||
main_wallet,
|
||||
wallet_state_manager, main_wallet, did_id, request.get("name", None)
|
||||
)
|
||||
return {
|
||||
"success": True,
|
||||
@@ -1363,7 +1365,17 @@ class WalletRpcApi:
|
||||
]
|
||||
)
|
||||
fee = uint64(request.get("fee", 0))
|
||||
spend_bundle = await nft_wallet.generate_new_nft(metadata, royalty_puzhash, target_puzhash, fee=fee)
|
||||
did_id = request.get("did_id", None)
|
||||
if did_id is not None:
|
||||
did_id = bytes.fromhex(did_id)
|
||||
spend_bundle = await nft_wallet.generate_new_nft(
|
||||
metadata,
|
||||
royalty_puzhash,
|
||||
target_puzhash,
|
||||
uint16(request.get("royalty_percentage", 0)),
|
||||
did_id,
|
||||
fee,
|
||||
)
|
||||
return {"wallet_id": wallet_id, "success": True, "spend_bundle": spend_bundle}
|
||||
|
||||
async def nft_get_nfts(self, request) -> Dict:
|
||||
@@ -1421,6 +1433,8 @@ class WalletRpcApi:
|
||||
odd_coin += 1
|
||||
if odd_coin > 1:
|
||||
return {"success": False, "error": "This is not a singleton, multiple children coins found."}
|
||||
if odd_coin == 0:
|
||||
return {"success": False, "error": "Cannot find child coin, please wait then retry."}
|
||||
coin_state = coin_state_list[0]
|
||||
# Get parent coin
|
||||
parent_coin_state_list: List[CoinState] = await self.service.wallet_state_manager.wallet_node.get_coin_state(
|
||||
@@ -1448,8 +1462,8 @@ class WalletRpcApi:
|
||||
break
|
||||
except Exception:
|
||||
log.info(f"Inner solution is not a metadata updater solution: {inner_solution}")
|
||||
uncurried_nft: UncurriedNFT = UncurriedNFT.uncurry(full_puzzle)
|
||||
if update_condition is not None:
|
||||
uncurried_nft: UncurriedNFT = UncurriedNFT.uncurry(full_puzzle)
|
||||
metadata: Program = uncurried_nft.metadata
|
||||
metadata = nft_puzzles.update_metadata(metadata, update_condition)
|
||||
# Note: This is not the actual unspent NFT full puzzle.
|
||||
@@ -1461,7 +1475,18 @@ class WalletRpcApi:
|
||||
uncurried_nft.metadata_updater_hash,
|
||||
uncurried_nft.inner_puzzle,
|
||||
)
|
||||
nft_info: NFTInfo = nft_puzzles.get_nft_info_from_puzzle(NFTCoinInfo(coin_state.coin, None, full_puzzle))
|
||||
# Get launcher coin
|
||||
launcher_coin: List[CoinState] = await self.service.wallet_state_manager.wallet_node.get_coin_state(
|
||||
[uncurried_nft.singleton_launcher_id], peer=peer
|
||||
)
|
||||
if launcher_coin is None or len(launcher_coin) < 1 or launcher_coin[0].spent_height is None:
|
||||
return {
|
||||
"success": False,
|
||||
"error": f"Launcher coin record 0x{uncurried_nft.singleton_launcher_id.hex()} not found",
|
||||
}
|
||||
nft_info: NFTInfo = nft_puzzles.get_nft_info_from_puzzle(
|
||||
NFTCoinInfo(coin_state.coin, None, full_puzzle, launcher_coin[0].spent_height)
|
||||
)
|
||||
except Exception as e:
|
||||
return {"success": False, "error": f"The coin is not a NFT. {e}"}
|
||||
else:
|
||||
|
||||
@@ -575,10 +575,11 @@ class WalletRpcClient(RpcClient):
|
||||
await self.fetch("cancel_offer", {"trade_id": trade_id.hex(), "secure": secure, "fee": fee})
|
||||
|
||||
# NFT wallet
|
||||
async def create_new_nft_wallet(self, did_wallet_id):
|
||||
async def create_new_nft_wallet(self, did_id, name=None):
|
||||
request: Dict[str, Any] = {
|
||||
"wallet_type": "nft_wallet",
|
||||
"did_wallet_id": did_wallet_id,
|
||||
"did_id": did_id,
|
||||
"name": name,
|
||||
}
|
||||
response = await self.fetch("create_new_wallet", request)
|
||||
return response
|
||||
@@ -597,6 +598,8 @@ class WalletRpcClient(RpcClient):
|
||||
series_total=1,
|
||||
series_number=1,
|
||||
fee=0,
|
||||
royalty_percentage=0,
|
||||
did_id=None,
|
||||
):
|
||||
request: Dict[str, Any] = {
|
||||
"wallet_id": wallet_id,
|
||||
@@ -610,6 +613,8 @@ class WalletRpcClient(RpcClient):
|
||||
"license_uris": license_uris,
|
||||
"series_number": series_number,
|
||||
"series_total": series_total,
|
||||
"royalty_percentage": royalty_percentage,
|
||||
"did_id": did_id,
|
||||
"fee": fee,
|
||||
}
|
||||
response = await self.fetch("nft_mint_nft", request)
|
||||
|
||||
@@ -4,7 +4,7 @@ from typing import List, Optional
|
||||
from chia.types.blockchain_format.coin import Coin
|
||||
from chia.types.blockchain_format.program import Program
|
||||
from chia.types.blockchain_format.sized_bytes import bytes32
|
||||
from chia.util.ints import uint16, uint64
|
||||
from chia.util.ints import uint16, uint32, uint64
|
||||
from chia.util.streamable import Streamable, streamable
|
||||
from chia.wallet.lineage_proof import LineageProof
|
||||
from chia.wallet.puzzles.load_clvm import load_clvm
|
||||
@@ -23,9 +23,12 @@ class NFTInfo(Streamable):
|
||||
nft_coin_id: bytes32
|
||||
"""Current NFT coin ID"""
|
||||
|
||||
did_owner: Optional[bytes32]
|
||||
owner_did: Optional[bytes32]
|
||||
"""Owner DID"""
|
||||
|
||||
owner_pubkey: Optional[bytes]
|
||||
"""Pubkey of the NFT owner"""
|
||||
|
||||
royalty: Optional[uint16]
|
||||
"""Percentage of the transaction fee paid to the author, e.g. 1000 = 1%"""
|
||||
|
||||
@@ -59,6 +62,12 @@ class NFTInfo(Streamable):
|
||||
chain_info: str
|
||||
"""Information saved on the chain in hex"""
|
||||
|
||||
mint_height: uint32
|
||||
"""Block height of the NFT minting"""
|
||||
|
||||
supports_did: bool
|
||||
"""If the inner puzzle supports DID"""
|
||||
|
||||
pending_transaction: bool = False
|
||||
"""Indicate if the NFT is pending for a transaction"""
|
||||
|
||||
@@ -72,6 +81,7 @@ class NFTCoinInfo(Streamable):
|
||||
coin: Coin
|
||||
lineage_proof: Optional[LineageProof]
|
||||
full_puzzle: Program
|
||||
mint_height: uint32
|
||||
pending_transaction: bool = False
|
||||
|
||||
|
||||
|
||||
@@ -107,6 +107,7 @@ def get_nft_info_from_puzzle(nft_coin_info: NFTCoinInfo) -> NFTInfo:
|
||||
uncurried_nft.singleton_launcher_id,
|
||||
nft_coin_info.coin.name(),
|
||||
uncurried_nft.owner_did,
|
||||
uncurried_nft.owner_pubkey,
|
||||
uncurried_nft.trade_price_percentage,
|
||||
data_uris,
|
||||
uncurried_nft.data_hash.as_python(),
|
||||
@@ -118,6 +119,8 @@ def get_nft_info_from_puzzle(nft_coin_info: NFTCoinInfo) -> NFTInfo:
|
||||
uint64(uncurried_nft.series_number.as_int()),
|
||||
uncurried_nft.metadata_updater_hash.as_python(),
|
||||
disassemble(uncurried_nft.metadata),
|
||||
nft_coin_info.mint_height,
|
||||
uncurried_nft.supports_did,
|
||||
nft_coin_info.pending_transaction,
|
||||
)
|
||||
return nft_info
|
||||
@@ -179,12 +182,18 @@ def update_metadata(metadata: Program, update_condition: Program) -> Program:
|
||||
|
||||
def create_ownership_layer_puzzle(
|
||||
nft_id: bytes32,
|
||||
did_id: bytes32,
|
||||
did_id: bytes,
|
||||
p2_puzzle: Program,
|
||||
percentage: uint16,
|
||||
royalty_puzzle_hash: Optional[bytes32] = None,
|
||||
) -> Program:
|
||||
log.debug(f"Creating ownership layer puzzle with {nft_id} {did_id} {percentage} {p2_puzzle}")
|
||||
log.debug(
|
||||
"Creating ownership layer puzzle with NFT_ID: %s DID_ID: %s Royalty_Percentage: %d P2_puzzle: %s",
|
||||
nft_id.hex(),
|
||||
did_id.hex(),
|
||||
percentage,
|
||||
p2_puzzle,
|
||||
)
|
||||
singleton_struct = Program.to((SINGLETON_MOD_HASH, (nft_id, LAUNCHER_PUZZLE_HASH)))
|
||||
if not royalty_puzzle_hash:
|
||||
royalty_puzzle_hash = p2_puzzle.get_tree_hash()
|
||||
@@ -205,13 +214,18 @@ def create_ownership_layer_puzzle(
|
||||
|
||||
|
||||
def create_ownership_layer_transfer_solution(
|
||||
new_did: bytes32,
|
||||
new_did: bytes,
|
||||
new_did_inner_hash: bytes32,
|
||||
trade_prices_list: List[List[int]],
|
||||
new_pubkey: G1Element,
|
||||
conditions: List[Any] = [],
|
||||
) -> Program:
|
||||
log.debug(f"Creating a transfer solution with: {new_did} {new_did_inner_hash} {trade_prices_list} {new_pubkey}")
|
||||
log.debug(
|
||||
"Creating a transfer solution with: DID:%s Inner_puzhash:%s trade_price:%s pubkey:%s",
|
||||
new_did.hex(),
|
||||
new_did_inner_hash.hex(),
|
||||
str(trade_prices_list),
|
||||
new_pubkey,
|
||||
)
|
||||
puzhash = STANDARD_PUZZLE_MOD.curry(new_pubkey).get_tree_hash()
|
||||
condition_list = [
|
||||
[
|
||||
|
||||
@@ -136,6 +136,9 @@ class NFTWallet:
|
||||
def id(self) -> uint32:
|
||||
return self.wallet_info.id
|
||||
|
||||
def get_did(self) -> Optional[bytes32]:
|
||||
return self.did_id
|
||||
|
||||
async def get_confirmed_balance(self, record_list=None) -> uint128:
|
||||
"""The NFT wallet doesn't really have a balance."""
|
||||
return uint128(0)
|
||||
@@ -202,7 +205,7 @@ class NFTWallet:
|
||||
self.log.info(
|
||||
f"found the info for NFT coin {coin_name} {uncurried_nft.inner_puzzle} {uncurried_nft.singleton_struct}"
|
||||
)
|
||||
singleton_id = bytes32(uncurried_nft.singleton_launcher_id)
|
||||
singleton_id = uncurried_nft.singleton_launcher_id
|
||||
parent_inner_puzhash = uncurried_nft.nft_state_layer.get_tree_hash()
|
||||
metadata, p2_puzzle_hash = get_metadata_and_phs(
|
||||
uncurried_nft, puzzle, Program.from_bytes(bytes(coin_spend.solution))
|
||||
@@ -247,9 +250,18 @@ class NFTWallet:
|
||||
if new_coin.puzzle_hash == child_puzzle.get_tree_hash():
|
||||
child_coin = new_coin
|
||||
break
|
||||
else:
|
||||
raise ValueError(f"Couldn't regenerate puzzle reveal for NFT: {coin_spend}")
|
||||
|
||||
launcher_coin_states: List[CoinState] = await self.wallet_state_manager.wallet_node.get_coin_state(
|
||||
[singleton_id]
|
||||
)
|
||||
assert (
|
||||
launcher_coin_states is not None
|
||||
and len(launcher_coin_states) == 1
|
||||
and launcher_coin_states[0].spent_height is not None
|
||||
)
|
||||
mint_height: uint32 = launcher_coin_states[0].spent_height
|
||||
self.log.info("Adding a new NFT to wallet: %s", child_coin)
|
||||
|
||||
# all is well, lets add NFT to our local db
|
||||
parent_coin = None
|
||||
coin_record = await self.wallet_state_manager.coin_store.get_coin_record(coin_name)
|
||||
@@ -263,20 +275,24 @@ class NFTWallet:
|
||||
parent_coin = coin_record.coin
|
||||
if parent_coin is None:
|
||||
raise ValueError("Error finding parent")
|
||||
|
||||
await self.add_coin(
|
||||
child_coin,
|
||||
child_puzzle,
|
||||
LineageProof(parent_coin.parent_coin_info, parent_inner_puzhash, parent_coin.amount),
|
||||
mint_height,
|
||||
in_transaction=in_transaction,
|
||||
)
|
||||
|
||||
async def add_coin(self, coin: Coin, puzzle: Program, lineage_proof: LineageProof, in_transaction: bool) -> None:
|
||||
async def add_coin(
|
||||
self, coin: Coin, puzzle: Program, lineage_proof: LineageProof, mint_height: uint32, in_transaction: bool
|
||||
) -> None:
|
||||
my_nft_coins = self.nft_wallet_info.my_nft_coins
|
||||
for coin_info in my_nft_coins:
|
||||
if coin_info.coin == coin:
|
||||
my_nft_coins.remove(coin_info)
|
||||
|
||||
my_nft_coins.append(NFTCoinInfo(coin, lineage_proof, puzzle))
|
||||
my_nft_coins.append(NFTCoinInfo(coin, lineage_proof, puzzle, mint_height))
|
||||
new_nft_wallet_info = NFTWalletInfo(
|
||||
my_nft_coins,
|
||||
self.nft_wallet_info.did_id,
|
||||
@@ -336,11 +352,15 @@ class NFTWallet:
|
||||
target_puzzle_hash: Optional[bytes32] = None,
|
||||
royalty_puzzle_hash: Optional[bytes32] = None,
|
||||
percentage: uint16 = uint16(0),
|
||||
did_id: Optional[bytes] = None,
|
||||
fee: uint64 = uint64(0),
|
||||
) -> Optional[SpendBundle]:
|
||||
"""
|
||||
This must be called under the wallet state manager lock
|
||||
"""
|
||||
if self.did_id is not None and did_id is None:
|
||||
# For a DID enabled NFT wallet it cannot mint NFT0. Mint NFT1 instead.
|
||||
did_id = self.did_id
|
||||
amount = uint64(1)
|
||||
coins = await self.standard_wallet.select_coins(amount)
|
||||
if coins is None:
|
||||
@@ -355,10 +375,10 @@ class NFTWallet:
|
||||
p2_inner_puzzle = await self.standard_wallet.get_new_puzzle()
|
||||
if not target_puzzle_hash:
|
||||
target_puzzle_hash = p2_inner_puzzle.get_tree_hash()
|
||||
if self.did_id:
|
||||
self.log.debug("Creating NFT using DID: %s", self.did_id)
|
||||
if did_id is not None:
|
||||
self.log.debug("Creating NFT using DID: %s", did_id)
|
||||
inner_puzzle = create_ownership_layer_puzzle(
|
||||
launcher_coin.name(), self.did_id, p2_inner_puzzle, percentage, royalty_puzzle_hash=royalty_puzzle_hash
|
||||
launcher_coin.name(), did_id, p2_inner_puzzle, percentage, royalty_puzzle_hash=royalty_puzzle_hash
|
||||
)
|
||||
self.log.debug("Got back ownership inner puzzle: %s", disassemble(inner_puzzle))
|
||||
else:
|
||||
@@ -405,7 +425,7 @@ class NFTWallet:
|
||||
target_puzzle_hash = p2_inner_puzzle.get_tree_hash()
|
||||
record: Optional[DerivationRecord] = None
|
||||
# Create inner solution for eve spend
|
||||
if self.did_id:
|
||||
if did_id is not None:
|
||||
record = await self.wallet_state_manager.puzzle_store.get_derivation_record_for_puzzle_hash(
|
||||
p2_inner_puzzle.get_tree_hash()
|
||||
)
|
||||
@@ -416,7 +436,7 @@ class NFTWallet:
|
||||
did_inner_hash, did_bundle = await self.get_did_approval_info(launcher_coin.name())
|
||||
pubkey = record.pubkey
|
||||
self.log.debug("Going to use this pubkey for NFT mint: %s", pubkey)
|
||||
innersol = create_ownership_layer_transfer_solution(self.did_id, did_inner_hash, [], pubkey)
|
||||
innersol = create_ownership_layer_transfer_solution(did_id, did_inner_hash, [], pubkey)
|
||||
bundles_to_agg.append(did_bundle)
|
||||
|
||||
self.log.debug("Created an inner DID NFT solution: %s", disassemble(innersol))
|
||||
@@ -621,7 +641,13 @@ class NFTWallet:
|
||||
raise ValueError(f"NFT coin {coin_id} doesn't exist.")
|
||||
|
||||
my_nft_coins.append(
|
||||
NFTCoinInfo(target_nft.coin, target_nft.lineage_proof, target_nft.full_puzzle, pending_transaction)
|
||||
NFTCoinInfo(
|
||||
target_nft.coin,
|
||||
target_nft.lineage_proof,
|
||||
target_nft.full_puzzle,
|
||||
target_nft.mint_height,
|
||||
pending_transaction,
|
||||
)
|
||||
)
|
||||
new_nft_wallet_info = NFTWalletInfo(
|
||||
my_nft_coins,
|
||||
|
||||
@@ -64,10 +64,17 @@ class UncurriedNFT:
|
||||
|
||||
p2_puzzle: Program
|
||||
"""p2 puzzle of the owner, either for ownership layer or standard"""
|
||||
|
||||
# ownership layer fields
|
||||
owner_did: Optional[bytes32]
|
||||
"""Owner's DID"""
|
||||
|
||||
supports_did: bool
|
||||
"""If the inner puzzle support the DID"""
|
||||
|
||||
owner_pubkey: Optional[G1Element]
|
||||
"""Owner's Pubkey in the P2 puzzle"""
|
||||
|
||||
nft_inner_puzzle_hash: Optional[bytes32]
|
||||
"""Puzzle hash of the ownership layer inner puzzle """
|
||||
|
||||
@@ -141,7 +148,9 @@ class UncurriedNFT:
|
||||
royalty_percentage = None
|
||||
nft_inner_puzzle_mod = None
|
||||
mod, ol_args = inner_puzzle.uncurry()
|
||||
supports_did = False
|
||||
if mod == NFT_OWNERSHIP_LAYER:
|
||||
supports_did = True
|
||||
log.debug("Parsing ownership layer")
|
||||
_, current_did, transfer_program, p2_puzzle = ol_args.as_iter()
|
||||
_, p2_args = p2_puzzle.uncurry()
|
||||
@@ -150,6 +159,9 @@ class UncurriedNFT:
|
||||
_, _, royalty_address, royalty_percentage, _, _ = transfer_program_args.as_iter()
|
||||
royalty_percentage = uint16(royalty_percentage.as_int())
|
||||
current_did = current_did.atom
|
||||
if current_did == b"":
|
||||
# For unassigned NFT, set owner DID to None
|
||||
current_did = None
|
||||
pubkey = pubkey_sexp.atom
|
||||
else:
|
||||
log.debug("Creating a standard NFT puzzle")
|
||||
@@ -175,8 +187,8 @@ class UncurriedNFT:
|
||||
series_number=series_number,
|
||||
series_total=series_total,
|
||||
inner_puzzle=inner_puzzle,
|
||||
# TODO: Set/Remove following fields after NFT1 implemented
|
||||
owner_did=current_did,
|
||||
supports_did=supports_did,
|
||||
owner_pubkey=pubkey,
|
||||
transfer_program=transfer_program,
|
||||
transfer_program_curry_params=transfer_program_args,
|
||||
|
||||
@@ -40,6 +40,7 @@ from chia.wallet.derive_keys import master_sk_to_wallet_sk, master_sk_to_wallet_
|
||||
from chia.wallet.did_wallet.did_wallet import DIDWallet
|
||||
from chia.wallet.did_wallet.did_wallet_puzzles import DID_INNERPUZ_MOD, create_fullpuz, match_did_puzzle
|
||||
from chia.wallet.key_val_store import KeyValStore
|
||||
from chia.wallet.nft_wallet.nft_info import NFTWalletInfo
|
||||
from chia.wallet.nft_wallet.nft_wallet import NFTWallet
|
||||
from chia.wallet.nft_wallet.uncurry_nft import UncurriedNFT
|
||||
from chia.wallet.outer_puzzles import AssetType, match_puzzle
|
||||
@@ -716,21 +717,27 @@ class WalletStateManager:
|
||||
wallet_id = None
|
||||
wallet_type = None
|
||||
self.log.debug("Handling NFT: %s", coin_spend)
|
||||
did_id = uncurried_nft.owner_did
|
||||
for wallet_info in await self.get_all_wallet_info_entries():
|
||||
if wallet_info.type == WalletType.NFT:
|
||||
self.log.debug(
|
||||
"Checking NFT wallet %r and inner puzzle %s",
|
||||
wallet_info.name,
|
||||
uncurried_nft.inner_puzzle.get_tree_hash(),
|
||||
)
|
||||
wallet_id = wallet_info.id
|
||||
wallet_type = WalletType.NFT
|
||||
nft_wallet_info: NFTWalletInfo = NFTWalletInfo.from_json_dict(json.loads(wallet_info.data))
|
||||
if nft_wallet_info.did_id == did_id:
|
||||
self.log.debug(
|
||||
"Checking NFT wallet %r and inner puzzle %s",
|
||||
wallet_info.name,
|
||||
uncurried_nft.inner_puzzle.get_tree_hash(),
|
||||
)
|
||||
wallet_id = wallet_info.id
|
||||
wallet_type = WalletType.NFT
|
||||
|
||||
if wallet_id is None:
|
||||
# TODO Modify this for NFT1
|
||||
self.log.info("Cannot find a NFT wallet, creating a new one.")
|
||||
self.log.info(
|
||||
"Cannot find a NFT wallet for NFT_ID: %s DID: %s, creating a new one.",
|
||||
uncurried_nft.singleton_launcher_id,
|
||||
did_id,
|
||||
)
|
||||
nft_wallet: NFTWallet = await NFTWallet.create_new_nft_wallet(
|
||||
self, self.main_wallet, name="NFT Wallet", in_transaction=True
|
||||
self, self.main_wallet, did_id=did_id, name="NFT Wallet", in_transaction=True
|
||||
)
|
||||
wallet_id = uint32(nft_wallet.wallet_id)
|
||||
wallet_type = WalletType.NFT
|
||||
|
||||
@@ -513,6 +513,7 @@ class TestDIDWallet:
|
||||
pubkey = (
|
||||
await did_wallet_3.wallet_state_manager.get_unused_derivation_record(did_wallet_3.wallet_info.id)
|
||||
).pubkey
|
||||
await time_out_assert(15, did_wallet.get_confirmed_balance, 101)
|
||||
attest_data = (await did_wallet.create_attestment(coin.name(), new_ph, pubkey))[1]
|
||||
spend_bundle_list = await wallet_node.wallet_state_manager.tx_store.get_unconfirmed_for_wallet(did_wallet.id())
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ from chia.types.peer_info import PeerInfo
|
||||
from chia.util.byte_types import hexstr_to_bytes
|
||||
from chia.util.ints import uint16, uint32, uint64
|
||||
from chia.wallet.did_wallet.did_wallet import DIDWallet
|
||||
from chia.wallet.nft_wallet import uncurry_nft
|
||||
from chia.wallet.nft_wallet.nft_wallet import NFTWallet
|
||||
from chia.wallet.util.compute_memos import compute_memos
|
||||
from chia.wallet.util.wallet_types import WalletType
|
||||
@@ -342,7 +341,8 @@ async def test_nft_wallet_rpc_creation_and_list(two_wallet_nodes: Any, trusted:
|
||||
assert len(coins) == 2
|
||||
uris = []
|
||||
for coin in coins:
|
||||
uris.append(coin.to_json_dict()["data_uris"][0])
|
||||
uris.append(coin.data_uris[0])
|
||||
assert coin.mint_height > 0
|
||||
assert len(uris) == 2
|
||||
assert "https://chialisp.com/img/logo.svg" in uris
|
||||
assert bytes32.fromhex(coins[1].to_json_dict()["nft_coin_id"][2:]) in [x.name() for x in sb.additions()]
|
||||
@@ -431,6 +431,7 @@ async def test_nft_wallet_rpc_update_metadata(two_wallet_nodes: Any, trusted: An
|
||||
assert coins_response.get("success")
|
||||
coins = coins_response["nft_list"]
|
||||
coin = coins[0].to_json_dict()
|
||||
assert coin["mint_height"] > 0
|
||||
assert coin["data_hash"] == "0xd4584ad463139fa8c0d9f68f4b59f185"
|
||||
assert coin["chain_info"] == disassemble(
|
||||
Program.to(
|
||||
@@ -471,6 +472,7 @@ async def test_nft_wallet_rpc_update_metadata(two_wallet_nodes: Any, trusted: An
|
||||
coins = coins_response["nft_list"]
|
||||
assert len(coins) == 1
|
||||
coin = coins[0].to_json_dict()
|
||||
assert coin["mint_height"] > 0
|
||||
uris = coin["data_uris"]
|
||||
assert len(uris) == 1
|
||||
assert "https://www.chia.net/img/branding/chia-logo.svg" in uris
|
||||
@@ -510,6 +512,7 @@ async def test_nft_wallet_rpc_update_metadata(two_wallet_nodes: Any, trusted: An
|
||||
coins = coins_response["nft_list"]
|
||||
assert len(coins) == 1
|
||||
coin = coins[0].to_json_dict()
|
||||
assert coin["mint_height"] > 0
|
||||
uris = coin["data_uris"]
|
||||
assert len(uris) == 2
|
||||
assert len(coin["metadata_uris"]) == 1
|
||||
@@ -523,7 +526,7 @@ async def test_nft_wallet_rpc_update_metadata(two_wallet_nodes: Any, trusted: An
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"trusted",
|
||||
[True],
|
||||
[True, False],
|
||||
)
|
||||
@pytest.mark.asyncio
|
||||
async def test_nft_with_did_wallet_creation(two_wallet_nodes: Any, trusted: Any) -> None:
|
||||
@@ -534,7 +537,7 @@ async def test_nft_with_did_wallet_creation(two_wallet_nodes: Any, trusted: Any)
|
||||
wallet_node_0, server_0 = wallets[0]
|
||||
wallet_node_1, server_1 = wallets[1]
|
||||
wallet_0 = wallet_node_0.wallet_state_manager.main_wallet
|
||||
|
||||
api_0 = WalletRpcApi(wallet_node_0)
|
||||
ph = await wallet_0.get_new_puzzlehash()
|
||||
|
||||
if trusted:
|
||||
@@ -565,7 +568,7 @@ async def test_nft_with_did_wallet_creation(two_wallet_nodes: Any, trusted: Any)
|
||||
|
||||
for _ in range(1, num_blocks):
|
||||
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
||||
|
||||
await asyncio.sleep(5)
|
||||
did_wallet: DIDWallet = await DIDWallet.create_new_did_wallet(
|
||||
wallet_node_0.wallet_state_manager, wallet_0, uint64(1)
|
||||
)
|
||||
@@ -578,20 +581,25 @@ 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()
|
||||
did_id = bytes32.fromhex(hex_did_id)
|
||||
nft_wallet_0 = await NFTWallet.create_new_nft_wallet(
|
||||
wallet_node_0.wallet_state_manager, wallet_0, name="NFT WALLET DID 1", did_id=did_id
|
||||
)
|
||||
metadata = Program.to(
|
||||
[
|
||||
("u", ["https://www.chia.net/img/branding/chia-logo.svg"]),
|
||||
("h", bytes.fromhex("D4584AD463139FA8C0D9F68F4B59F185")),
|
||||
]
|
||||
)
|
||||
|
||||
res = await api_0.create_new_wallet(dict(wallet_type="nft_wallet", name="NFT WALLET 1", did_id=hex_did_id))
|
||||
assert isinstance(res, dict)
|
||||
assert res.get("success")
|
||||
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)
|
||||
sb = await nft_wallet_0.generate_new_nft(metadata)
|
||||
assert sb
|
||||
# Create a NFT with DID
|
||||
resp = await api_0.nft_mint_nft(
|
||||
{
|
||||
"wallet_id": nft_wallet_0_id,
|
||||
"hash": "0xD4584AD463139FA8C0D9F68F4B59F185",
|
||||
"uris": ["https://www.chia.net/img/branding/chia-logo.svg"],
|
||||
}
|
||||
)
|
||||
assert resp.get("success")
|
||||
sb = resp["spend_bundle"]
|
||||
|
||||
# ensure hints are generated
|
||||
assert compute_memos(sb)
|
||||
await time_out_assert_not_none(5, full_node_api.full_node.mempool_manager.get_spendbundle, sb.name())
|
||||
@@ -599,35 +607,61 @@ async def test_nft_with_did_wallet_creation(two_wallet_nodes: Any, trusted: Any)
|
||||
for i in range(1, num_blocks):
|
||||
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
||||
|
||||
await time_out_assert(10, len, 1, nft_wallet_0.nft_wallet_info.my_nft_coins)
|
||||
|
||||
metadata = Program.to(
|
||||
[
|
||||
("u", ["https://url1"]),
|
||||
("h", "0xD4584AD463139FA8C0D9F68F4B59F181"),
|
||||
]
|
||||
)
|
||||
await time_out_assert(10, wallet_0.get_unconfirmed_balance, 7999999999999 - 1)
|
||||
await time_out_assert(10, wallet_0.get_confirmed_balance, 7999999999999 - 1)
|
||||
sb = await nft_wallet_0.generate_new_nft(metadata)
|
||||
assert sb
|
||||
# Create a NFT without DID, this will go the unassigned NFT wallet
|
||||
resp = await api_0.nft_mint_nft(
|
||||
{
|
||||
"wallet_id": nft_wallet_0_id,
|
||||
"did_id": "",
|
||||
"hash": "0xD4584AD463139FA8C0D9F68F4B59F181",
|
||||
"uris": ["https://url1"],
|
||||
}
|
||||
)
|
||||
assert resp.get("success")
|
||||
sb = resp["spend_bundle"]
|
||||
|
||||
# ensure hints are generated
|
||||
assert compute_memos(sb)
|
||||
await time_out_assert_not_none(5, full_node_api.full_node.mempool_manager.get_spendbundle, sb.name())
|
||||
|
||||
for i in range(1, num_blocks):
|
||||
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
||||
|
||||
await time_out_assert(10, len, 2, nft_wallet_0.nft_wallet_info.my_nft_coins)
|
||||
# check if we uncurry well
|
||||
last_nft_coin = nft_wallet_0.nft_wallet_info.my_nft_coins[-1]
|
||||
unft = uncurry_nft.UncurriedNFT.uncurry(last_nft_coin.full_puzzle)
|
||||
assert unft.data_uris == ["https://url1"]
|
||||
assert unft.data_hash.atom == b"0xD4584AD463139FA8C0D9F68F4B59F181"
|
||||
assert unft.owner_did == nft_wallet_0.nft_wallet_info.did_id
|
||||
assert unft.owner_pubkey is not None
|
||||
|
||||
first_nft_coin = nft_wallet_0.nft_wallet_info.my_nft_coins[0]
|
||||
unft = uncurry_nft.UncurriedNFT.uncurry(first_nft_coin.full_puzzle)
|
||||
assert unft.data_uris == ["https://www.chia.net/img/branding/chia-logo.svg"]
|
||||
assert unft.data_hash.atom == bytes.fromhex("D4584AD463139FA8C0D9F68F4B59F185")
|
||||
await time_out_assert(10, wallet_0.get_unconfirmed_balance, 9999999999998 - 1)
|
||||
await time_out_assert(10, wallet_0.get_confirmed_balance, 9999999999998 - 1)
|
||||
# Check DID NFT
|
||||
time_left = 5.0
|
||||
coins_response = {}
|
||||
while time_left > 0:
|
||||
coins_response = await api_0.nft_get_nfts(dict(wallet_id=nft_wallet_0_id))
|
||||
if coins_response.get("nft_list"):
|
||||
break
|
||||
await asyncio.sleep(0.5)
|
||||
time_left -= 0.5
|
||||
assert coins_response["nft_list"], isinstance(coins_response, dict)
|
||||
assert coins_response.get("success")
|
||||
coins = coins_response["nft_list"]
|
||||
assert len(coins) == 1
|
||||
did_nft = coins[0].to_json_dict()
|
||||
assert did_nft["mint_height"] > 0
|
||||
assert did_nft["supports_did"]
|
||||
assert did_nft["data_uris"][0] == "https://www.chia.net/img/branding/chia-logo.svg"
|
||||
assert did_nft["data_hash"] == "0xD4584AD463139FA8C0D9F68F4B59F185".lower()
|
||||
assert did_nft["owner_did"][2:] == hex_did_id
|
||||
assert did_nft["owner_pubkey"] is not None
|
||||
# Check unassigned NFT
|
||||
await asyncio.sleep(5)
|
||||
nft_wallets = await wallet_node_0.wallet_state_manager.get_all_wallet_info_entries(WalletType.NFT)
|
||||
assert len(nft_wallets) == 2
|
||||
coins_response = await api_0.nft_get_nfts(dict(wallet_id=nft_wallets[1].id))
|
||||
assert coins_response["nft_list"], isinstance(coins_response, dict)
|
||||
assert coins_response.get("success")
|
||||
coins = coins_response["nft_list"]
|
||||
assert len(coins) == 1
|
||||
non_did_nft = coins[0].to_json_dict()
|
||||
assert non_did_nft["mint_height"] > 0
|
||||
assert non_did_nft["supports_did"]
|
||||
assert non_did_nft["data_uris"][0] == "https://url1"
|
||||
assert non_did_nft["data_hash"] == "0xD4584AD463139FA8C0D9F68F4B59F181".lower()
|
||||
assert non_did_nft["owner_did"] is None
|
||||
assert non_did_nft["owner_pubkey"] is not None
|
||||
|
||||
Reference in New Issue
Block a user