Rewrite ownership logic

This commit is contained in:
ytx1991 2022-06-17 19:34:32 -07:00
parent ccec59ce25
commit e5dfc0825f
No known key found for this signature in database
GPG Key ID: 1B767F8CA343E501
5 changed files with 87 additions and 76 deletions

View File

@ -1406,10 +1406,14 @@ class WalletRpcApi:
assert self.service.wallet_state_manager is not None
wallet_id = uint32(request["wallet_id"])
nft_wallet: NFTWallet = self.service.wallet_state_manager.wallets[wallet_id]
did_id: Optional[bytes32] = None
if "did_id" in request:
did_id = decode_puzzle_hash(request["did_id"])
did_id = request.get("did_id", "")
if did_id == "":
did_id = b""
else:
did_id = decode_puzzle_hash(did_id)
nft_coin_info = nft_wallet.get_nft_coin_by_id(bytes32.from_hexstr(request["nft_coin_id"]))
if not nft_puzzles.get_nft_info_from_puzzle(nft_coin_info).supports_did:
return {"success": False, "error": "The NFT doesn't support setting a DID."}
fee = uint64(request.get("fee", 0))
spend_bundle = await nft_wallet.set_nft_did(nft_coin_info, did_id, fee=fee)
return {"wallet_id": wallet_id, "success": True, "spend_bundle": spend_bundle}

View File

@ -210,10 +210,7 @@ class NFTWallet:
] = await self.wallet_state_manager.puzzle_store.get_derivation_record_for_puzzle_hash(p2_puzzle_hash)
self.log.debug("Record for %s is: %s", p2_puzzle_hash, derivation_record)
if derivation_record is None:
self.log.info("Received a puzzle hash that is not ours, returning")
# we transferred it to another wallet, remove the coin from our wallet
await self.remove_coin(coin_spend.coin, in_transaction=in_transaction)
return
raise ValueError(f"Cannot find the DerivationRecord for {p2_puzzle_hash}")
p2_puzzle = puzzle_for_pk(derivation_record.pubkey)
if uncurried_nft.supports_did:
inner_puzzle = nft_puzzles.recurry_nft_puzzle(uncurried_nft, delegated_puz_solution, p2_puzzle)
@ -850,26 +847,24 @@ class NFTWallet:
return unsigned_spend_bundle, chia_tx
async def set_nft_did(
self, nft_coin_info: NFTCoinInfo, did_id: Optional[bytes32], fee: uint64 = uint64(0)
) -> SpendBundle:
async def set_nft_did(self, nft_coin_info: NFTCoinInfo, did_id: bytes, fee: uint64 = uint64(0)) -> SpendBundle:
self.log.debug("Setting NFT DID with parameters: nft=%s did=%s", nft_coin_info, did_id)
unft = UncurriedNFT.uncurry(nft_coin_info.full_puzzle)
nft_id = unft.singleton_launcher_id
puzzle_hashes_to_sign = [unft.p2_puzzle.get_tree_hash()]
if did_id:
did_inner_hash, did_bundle = await self.get_did_approval_info(nft_id, did_id)
additional_bundles = [did_bundle]
else:
did_inner_hash = None
additional_bundles = []
did_inner_hash = b""
additional_bundles = []
if did_id != b"":
did_inner_hash, did_bundle = await self.get_did_approval_info(nft_id, bytes32(did_id))
additional_bundles.append(did_bundle)
nft_tx_record = await self.generate_signed_transaction(
[nft_coin_info.coin.amount],
puzzle_hashes_to_sign,
fee,
{nft_coin_info.coin},
new_owner=did_id if did_id else b"",
new_did_inner_hash=did_inner_hash if did_inner_hash else b"",
new_owner=did_id,
new_did_inner_hash=did_inner_hash,
additional_bundles=additional_bundles,
)
spend_bundle: Optional[SpendBundle] = None

View File

@ -38,7 +38,7 @@ from chia.wallet.cat_wallet.cat_utils import construct_cat_puzzle, match_cat_puz
from chia.wallet.cat_wallet.cat_wallet import CATWallet
from chia.wallet.derivation_record import DerivationRecord
from chia.wallet.derive_keys import master_sk_to_wallet_sk, master_sk_to_wallet_sk_unhardened
from chia.wallet.did_wallet.did_info import DIDInfo, DID_HRP
from chia.wallet.did_wallet.did_info import DID_HRP
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
@ -727,67 +727,74 @@ class WalletStateManager:
"""
wallet_id = None
wallet_type = None
did_id = None
# DID ID determines which NFT wallet should process the NFT
new_did_id = None
old_did_id = None
# P2 puzzle hash determines if we should ignore the NFT
old_p2_puzhash = uncurried_nft.p2_puzzle.get_tree_hash()
metadata, new_p2_puzhash = get_metadata_and_phs(
uncurried_nft,
coin_spend.solution,
)
if uncurried_nft.supports_did:
# Try to get the latest owner DID
did_id = get_new_owner_did(coin_spend.solution.to_program())
if did_id is None:
# No DID owner update, use the original DID
did_id = uncurried_nft.owner_did
if did_id == b"":
# Owner DID is updated to None
did_id = None
self.log.debug("Handling NFT: %s DID: %s", coin_spend, did_id)
new_did_id = get_new_owner_did(coin_spend.solution.to_program())
old_did_id = uncurried_nft.owner_did
if new_did_id is None:
new_did_id = old_did_id
if new_did_id == b"":
new_did_id = None
self.log.debug(
"Handling NFT: %s old DID:%s, new DID:%s, old P2:%s, new P2:%s",
coin_spend,
old_did_id,
new_did_id,
old_p2_puzhash,
new_p2_puzhash,
)
new_derivation_record: Optional[
DerivationRecord
] = await self.puzzle_store.get_derivation_record_for_puzzle_hash(new_p2_puzhash)
old_derivation_record: Optional[
DerivationRecord
] = await self.puzzle_store.get_derivation_record_for_puzzle_hash(old_p2_puzhash)
if new_derivation_record is None and old_derivation_record is None:
self.log.debug(
"Cannot find a P2 puzzle hash for NFT:%s, this NFT belongs to others.",
uncurried_nft.singleton_launcher_id.hex(),
)
return wallet_id, wallet_type
for wallet_info in await self.get_all_wallet_info_entries(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(),
if nft_wallet_info.did_id == old_did_id:
self.log.info(
"Removing old NFT, NFT_ID:%s, DID_ID:%s",
uncurried_nft.singleton_launcher_id.hex(),
old_did_id,
)
nft_wallet: NFTWallet = self.wallets[wallet_info.id]
await nft_wallet.remove_coin(coin_spend.coin, in_transaction=True)
if nft_wallet_info.did_id == new_did_id:
self.log.info(
"Adding new NFT, NFT_ID:%s, DID_ID:%s",
uncurried_nft.singleton_launcher_id.hex(),
new_did_id,
)
wallet_id = wallet_info.id
wallet_type = WalletType.NFT
if wallet_id is None:
if did_id is not None:
found_did: bool = False
for wallet_info in await self.get_all_wallet_info_entries(wallet_type=WalletType.DISTRIBUTED_ID):
did_info: DIDInfo = DIDInfo.from_json_dict(json.loads(wallet_info.data))
if did_info.origin_coin is not None and did_info.origin_coin.name() == did_id:
found_did = True
break
if not found_did:
self.log.info(
"Cannot find a profile for DID:%s NFT:%s, checking the inner puzzle ...",
did_id.hex(),
uncurried_nft.singleton_launcher_id.hex(),
)
metadata, p2_puzzle_hash = get_metadata_and_phs(
uncurried_nft,
coin_spend.solution,
)
derivation_record: Optional[
DerivationRecord
] = await self.puzzle_store.get_derivation_record_for_puzzle_hash(p2_puzzle_hash)
if derivation_record is None:
self.log.info(
"Cannot find a P2 puzzle hash for DID:%s NFT:%s, this NFT belongs to others.",
did_id.hex(),
uncurried_nft.singleton_launcher_id.hex(),
)
return wallet_id, wallet_type
if wallet_id is None and new_derivation_record:
# Cannot find an existed NFT wallet for the new NFT
self.log.info(
"Cannot find a NFT wallet for NFT_ID: %s DID: %s, creating a new one.",
"Cannot find a NFT wallet for NFT_ID: %s DID_ID: %s, creating a new one.",
uncurried_nft.singleton_launcher_id,
did_id,
new_did_id,
)
nft_wallet: NFTWallet = await NFTWallet.create_new_nft_wallet(
self, self.main_wallet, did_id=did_id, name="NFT Wallet", in_transaction=True
new_nft_wallet: NFTWallet = await NFTWallet.create_new_nft_wallet(
self, self.main_wallet, did_id=new_did_id, name="NFT Wallet", in_transaction=True
)
wallet_id = uint32(nft_wallet.wallet_id)
wallet_id = uint32(new_nft_wallet.wallet_id)
wallet_type = WalletType.NFT
return wallet_id, wallet_type
async def new_coin_state(

View File

@ -652,7 +652,6 @@ class TestDIDWallet:
# Transfer DID
new_puzhash = await wallet2.get_new_puzzlehash()
await did_wallet_1.transfer_did(new_puzhash, uint64(0), with_recovery)
print(f"Original launch_id {did_wallet_1.did_info.origin_coin.name()}")
spend_bundle_list = await wallet_node.wallet_state_manager.tx_store.get_unconfirmed_for_wallet(
did_wallet_1.id()
)
@ -675,7 +674,7 @@ class TestDIDWallet:
if with_recovery:
assert did_wallet_1.did_info.backup_ids[0] == did_wallet_2.did_info.backup_ids[0]
assert did_wallet_1.did_info.num_of_backup_ids_needed == did_wallet_2.did_info.num_of_backup_ids_needed
metadata = json.loads(did_wallet_1.did_info.metadata)
metadata = json.loads(did_wallet_2.did_info.metadata)
assert metadata["Twitter"] == "Test"
assert metadata["GitHub"] == "测试"

View File

@ -965,6 +965,7 @@ async def test_nft_transfer_nft_with_did(two_wallet_nodes: Any, trusted: Any) ->
time_left -= 0.5
else:
raise AssertionError("NFT not transferred")
assert len(wallet_node_0.wallet_state_manager.wallets[nft_wallet_0_id].my_nft_coins) == 0
# Check if the NFT owner DID is reset
resp = await api_1.nft_get_by_did(dict())
assert resp.get("success")
@ -1094,7 +1095,7 @@ async def test_update_metadata_for_nft_did(two_wallet_nodes: Any, trusted: Any)
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
# Check DID NFT
time_left = 5.0
time_left = 10.0
coins_response = {}
while time_left > 0:
coins_response = await api_0.nft_get_nfts(dict(wallet_id=nft_wallet_0_id))
@ -1122,7 +1123,7 @@ async def test_update_metadata_for_nft_did(two_wallet_nodes: Any, trusted: Any)
for i in range(1, num_blocks):
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
# check that new URI was added
time_left = 10.0
time_left = 15.0
while time_left > 0:
coins_response = await api_0.nft_get_nfts(dict(wallet_id=nft_wallet_0_id))
try:
@ -1226,7 +1227,7 @@ async def test_nft_set_did(two_wallet_nodes: Any, trusted: Any) -> None:
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
# Check DID NFT
time_left = 5.0
time_left = 10.0
coins_response = {}
while time_left > 0:
coins_response = await api_0.nft_get_nfts(dict(wallet_id=nft_wallet_0_id))
@ -1251,7 +1252,7 @@ async def test_nft_set_did(two_wallet_nodes: Any, trusted: Any) -> None:
for i in range(1, num_blocks):
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
time_left = 5.0
time_left = 10.0
coins_response = {}
while time_left > 0:
coins_response = await api_0.nft_get_by_did(dict(did_id=hmr_did_id))
@ -1261,6 +1262,8 @@ async def test_nft_set_did(two_wallet_nodes: Any, trusted: Any) -> None:
time_left -= 0.5
nft_wallet_1_id = coins_response.get("wallet_id")
assert nft_wallet_1_id
assert len(wallet_node_0.wallet_state_manager.wallets[nft_wallet_0_id].my_nft_coins) == 0
# Check NFT DID
time_left = 10.0
while time_left > 0:
@ -1284,7 +1287,7 @@ async def test_nft_set_did(two_wallet_nodes: Any, trusted: Any) -> None:
for i in range(1, num_blocks):
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
time_left = 5.0
time_left = 10.0
coins_response = {}
while time_left > 0:
coins_response = await api_0.nft_get_by_did(dict(did_id=hmr_did_id))
@ -1294,6 +1297,8 @@ async def test_nft_set_did(two_wallet_nodes: Any, trusted: Any) -> None:
time_left -= 0.5
nft_wallet_2_id = coins_response.get("wallet_id")
assert nft_wallet_2_id
assert len(wallet_node_0.wallet_state_manager.wallets[nft_wallet_1_id].my_nft_coins) == 0
# Check NFT DID
time_left = 10.0
while time_left > 0:
@ -1325,6 +1330,7 @@ async def test_nft_set_did(two_wallet_nodes: Any, trusted: Any) -> None:
coins = resp["nft_list"]
assert len(coins) == 1
assert coins[0].owner_did is None
assert len(wallet_node_0.wallet_state_manager.wallets[nft_wallet_2_id].my_nft_coins) == 0
@pytest.mark.parametrize(
@ -1391,7 +1397,7 @@ async def test_set_nft_status(two_wallet_nodes: Any, trusted: Any) -> None:
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
# Check DID NFT
time_left = 5.0
time_left = 10.0
coins_response = {}
while time_left > 0:
coins_response = await api_0.nft_get_nfts(dict(wallet_id=nft_wallet_0_id))