Merge pull request #11834 from Chia-Network/1.4.0_hide_wallet_info

Create stores for NFT
This commit is contained in:
William Allen
2022-06-15 13:39:09 -05:00
committed by GitHub
10 changed files with 336 additions and 99 deletions
@@ -95,7 +95,7 @@ jobs:
- name: Test wallet code with pytest
run: |
. ./activate
venv/bin/coverage run --rcfile=.coveragerc --module pytest --durations=10 -n 4 -m "not benchmark" tests/wallet/test_bech32m.py tests/wallet/test_chialisp.py tests/wallet/test_coin_selection.py tests/wallet/test_puzzle_store.py tests/wallet/test_singleton.py tests/wallet/test_singleton_lifecycle.py tests/wallet/test_singleton_lifecycle_fast.py tests/wallet/test_taproot.py tests/wallet/test_wallet.py tests/wallet/test_wallet_blockchain.py tests/wallet/test_wallet_interested_store.py tests/wallet/test_wallet_key_val_store.py tests/wallet/test_wallet_retry.py tests/wallet/test_wallet_store.py tests/wallet/test_wallet_user_store.py
venv/bin/coverage run --rcfile=.coveragerc --module pytest --durations=10 -n 4 -m "not benchmark" tests/wallet/test_bech32m.py tests/wallet/test_chialisp.py tests/wallet/test_coin_selection.py tests/wallet/test_nft_store.py tests/wallet/test_puzzle_store.py tests/wallet/test_singleton.py tests/wallet/test_singleton_lifecycle.py tests/wallet/test_singleton_lifecycle_fast.py tests/wallet/test_taproot.py tests/wallet/test_wallet.py tests/wallet/test_wallet_blockchain.py tests/wallet/test_wallet_interested_store.py tests/wallet/test_wallet_key_val_store.py tests/wallet/test_wallet_retry.py tests/wallet/test_wallet_store.py tests/wallet/test_wallet_user_store.py
- name: Process coverage data
run: |
@@ -94,7 +94,7 @@ jobs:
- name: Test wallet code with pytest
run: |
. ./activate
venv/bin/coverage run --rcfile=.coveragerc --module pytest --durations=10 -n 4 -m "not benchmark" tests/wallet/test_bech32m.py tests/wallet/test_chialisp.py tests/wallet/test_coin_selection.py tests/wallet/test_puzzle_store.py tests/wallet/test_singleton.py tests/wallet/test_singleton_lifecycle.py tests/wallet/test_singleton_lifecycle_fast.py tests/wallet/test_taproot.py tests/wallet/test_wallet.py tests/wallet/test_wallet_blockchain.py tests/wallet/test_wallet_interested_store.py tests/wallet/test_wallet_key_val_store.py tests/wallet/test_wallet_retry.py tests/wallet/test_wallet_store.py tests/wallet/test_wallet_user_store.py
venv/bin/coverage run --rcfile=.coveragerc --module pytest --durations=10 -n 4 -m "not benchmark" tests/wallet/test_bech32m.py tests/wallet/test_chialisp.py tests/wallet/test_coin_selection.py tests/wallet/test_nft_store.py tests/wallet/test_puzzle_store.py tests/wallet/test_singleton.py tests/wallet/test_singleton_lifecycle.py tests/wallet/test_singleton_lifecycle_fast.py tests/wallet/test_taproot.py tests/wallet/test_wallet.py tests/wallet/test_wallet_blockchain.py tests/wallet/test_wallet_interested_store.py tests/wallet/test_wallet_key_val_store.py tests/wallet/test_wallet_retry.py tests/wallet/test_wallet_store.py tests/wallet/test_wallet_user_store.py
- name: Process coverage data
run: |
+13 -3
View File
@@ -436,13 +436,17 @@ class WalletRpcApi:
async def get_wallets(self, request: Dict):
assert self.service.wallet_state_manager is not None
include_data: bool = request.get("include_data", True)
wallet_type: Optional[WalletType] = None
if "type" in request:
wallet_type = WalletType(request["type"])
wallets: List[WalletInfo] = await self.service.wallet_state_manager.get_all_wallet_info_entries(wallet_type)
if not include_data:
result: List[WalletInfo] = []
for wallet in wallets:
result.append(WalletInfo(wallet.id, wallet.name, wallet.type, ""))
wallets = result
return {"wallets": wallets}
async def create_new_wallet(self, request: Dict):
@@ -1561,7 +1565,13 @@ class WalletRpcApi:
"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)
NFTCoinInfo(
uncurried_nft.singleton_launcher_id,
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}"}
+3 -1
View File
@@ -10,6 +10,8 @@ from chia.wallet.lineage_proof import LineageProof
from chia.wallet.puzzles.load_clvm import load_clvm
LAUNCHER_PUZZLE = load_clvm("singleton_launcher.clvm")
IN_TRANSACTION_STATUS = "IN_TRANSACTION"
DEFAULT_STATUS = "DEFAULT"
NFT_HRP = "nft"
@@ -79,6 +81,7 @@ class NFTInfo(Streamable):
@streamable
@dataclass(frozen=True)
class NFTCoinInfo(Streamable):
nft_id: bytes32
coin: Coin
lineage_proof: Optional[LineageProof]
full_puzzle: Program
@@ -89,5 +92,4 @@ class NFTCoinInfo(Streamable):
@streamable
@dataclass(frozen=True)
class NFTWalletInfo(Streamable):
my_nft_coins: List[NFTCoinInfo]
did_id: Optional[bytes32] = None
+41 -50
View File
@@ -32,7 +32,6 @@ from chia.wallet.nft_wallet.uncurry_nft import UncurriedNFT
from chia.wallet.outer_puzzles import AssetType, match_puzzle
from chia.wallet.payment import Payment
from chia.wallet.puzzle_drivers import PuzzleInfo
from chia.wallet.puzzles.load_clvm import load_clvm
from chia.wallet.puzzles.p2_delegated_puzzle_or_hidden_puzzle import (
DEFAULT_HIDDEN_PUZZLE_HASH,
calculate_synthetic_secret_key,
@@ -40,7 +39,6 @@ from chia.wallet.puzzles.p2_delegated_puzzle_or_hidden_puzzle import (
solution_for_conditions,
)
from chia.wallet.puzzles.puzzle_utils import make_create_coin_condition
from chia.wallet.puzzles.singleton_top_layer_v1_1 import match_singleton_puzzle
from chia.wallet.transaction_record import TransactionRecord
from chia.wallet.util.compute_memos import compute_memos
from chia.wallet.util.debug_spend_bundle import disassemble
@@ -49,18 +47,15 @@ from chia.wallet.util.wallet_types import WalletType
from chia.wallet.wallet import Wallet
from chia.wallet.wallet_info import WalletInfo
STANDARD_PUZZLE_MOD = load_clvm("p2_delegated_puzzle_or_hidden_puzzle.clvm")
_T_NFTWallet = TypeVar("_T_NFTWallet", bound="NFTWallet")
OFFER_MOD = load_clvm("settlement_payments.clvm")
class NFTWallet:
wallet_state_manager: Any
log: logging.Logger
wallet_info: WalletInfo
nft_wallet_info: NFTWalletInfo
my_nft_coins: List[NFTCoinInfo]
standard_wallet: Wallet
wallet_id: int
@@ -86,7 +81,8 @@ class NFTWallet:
name = "NFT Wallet"
self.log = logging.getLogger(name if name else __name__)
self.wallet_state_manager = wallet_state_manager
self.nft_wallet_info = NFTWalletInfo([], did_id)
self.nft_wallet_info = NFTWalletInfo(did_id)
self.my_nft_coins = []
info_as_string = json.dumps(self.nft_wallet_info.to_json_dict())
wallet_info = await wallet_state_manager.user_store.create_wallet(
name,
@@ -120,6 +116,7 @@ class NFTWallet:
self.wallet_id = wallet_info.id
self.standard_wallet = wallet
self.wallet_info = wallet_info
self.my_nft_coins = await self.wallet_state_manager.nft_store.get_nft_list(wallet_id=self.wallet_id)
self.nft_wallet_info = NFTWalletInfo.from_json_dict(json.loads(wallet_info.data))
return self
@@ -157,7 +154,7 @@ class NFTWallet:
return uint128(0)
def get_nft_coin_by_id(self, nft_coin_id: bytes32) -> NFTCoinInfo:
for nft_coin in self.nft_wallet_info.my_nft_coins:
for nft_coin in self.my_nft_coins:
if nft_coin.coin.name() == nft_coin_id:
return nft_coin
raise KeyError(f"Couldn't find coin with id: {nft_coin_id}")
@@ -168,7 +165,7 @@ class NFTWallet:
async def coin_added(self, coin: Coin, height: uint32, in_transaction: bool) -> None:
"""Notification from wallet state manager that wallet has been received."""
self.log.info(f"NFT wallet %s has been notified that {coin} was added", self.wallet_info.name)
for coin_info in self.nft_wallet_info.my_nft_coins:
for coin_info in self.my_nft_coins:
if coin_info.coin == coin:
return
wallet_node = self.wallet_state_manager.wallet_node
@@ -271,6 +268,7 @@ class NFTWallet:
await self.add_coin(
child_coin,
singleton_id,
child_puzzle,
LineageProof(parent_coin.parent_coin_info, parent_inner_puzhash, parent_coin.amount),
mint_height,
@@ -278,33 +276,33 @@ class NFTWallet:
)
async def add_coin(
self, coin: Coin, puzzle: Program, lineage_proof: LineageProof, mint_height: uint32, in_transaction: bool
self,
coin: Coin,
nft_id: bytes32,
puzzle: Program,
lineage_proof: LineageProof,
mint_height: uint32,
in_transaction: bool,
) -> None:
my_nft_coins = self.nft_wallet_info.my_nft_coins
my_nft_coins = self.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, mint_height))
new_nft_wallet_info = NFTWalletInfo(
my_nft_coins,
self.nft_wallet_info.did_id,
new_nft = NFTCoinInfo(nft_id, coin, lineage_proof, puzzle, mint_height)
my_nft_coins.append(new_nft)
await self.wallet_state_manager.nft_store.save_nft(
self.id(), self.get_did(), new_nft, in_transaction=in_transaction
)
await self.save_info(new_nft_wallet_info, in_transaction=in_transaction)
await self.wallet_state_manager.add_interested_coin_ids([coin.name()], in_transaction=in_transaction)
self.wallet_state_manager.state_changed("nft_coin_added", self.wallet_info.id)
return
async def remove_coin(self, coin: Coin, in_transaction: bool) -> None:
my_nft_coins = self.nft_wallet_info.my_nft_coins
my_nft_coins = self.my_nft_coins
for coin_info in my_nft_coins:
if coin_info.coin == coin:
my_nft_coins.remove(coin_info)
new_nft_wallet_info = NFTWalletInfo(
my_nft_coins,
self.nft_wallet_info.did_id,
)
await self.save_info(new_nft_wallet_info, in_transaction=in_transaction)
await self.wallet_state_manager.nft_store.delete_nft(coin_info.nft_id, in_transaction=in_transaction)
self.wallet_state_manager.state_changed("nft_coin_removed", self.wallet_info.id)
return
@@ -590,12 +588,12 @@ class NFTWallet:
return nft_tx_record.spend_bundle
def get_current_nfts(self) -> List[NFTCoinInfo]:
return self.nft_wallet_info.my_nft_coins
return self.my_nft_coins
async def update_coin_status(
self, coin_id: bytes32, pending_transaction: bool, in_transaction: bool = False
) -> None:
my_nft_coins = self.nft_wallet_info.my_nft_coins
my_nft_coins = self.my_nft_coins
target_nft: Optional[NFTCoinInfo] = None
for coin_info in my_nft_coins:
if coin_info.coin.name() == coin_id:
@@ -603,21 +601,18 @@ class NFTWallet:
my_nft_coins.remove(coin_info)
if target_nft is None:
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,
target_nft.mint_height,
pending_transaction,
)
new_nft = NFTCoinInfo(
target_nft.nft_id,
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,
self.nft_wallet_info.did_id,
my_nft_coins.append(new_nft)
await self.wallet_state_manager.nft_store.save_nft(
self.id(), self.get_did(), new_nft, in_transaction=in_transaction
)
await self.save_info(new_nft_wallet_info, in_transaction=in_transaction)
async def save_info(self, nft_info: NFTWalletInfo, in_transaction: bool) -> None:
self.nft_wallet_info = nft_info
@@ -631,17 +626,13 @@ class NFTWallet:
return puzhash
def get_nft(self, launcher_id: bytes32) -> Optional[NFTCoinInfo]:
for coin in self.nft_wallet_info.my_nft_coins:
matched, curried_args = match_singleton_puzzle(coin.full_puzzle)
if matched:
singleton_struct, inner_puzzle = curried_args
launcher: bytes32 = singleton_struct.as_python()[1]
if launcher == launcher_id:
return coin
for coin in self.my_nft_coins:
if coin.nft_id == launcher_id:
return coin
return None
def get_puzzle_info(self, asset_id: bytes32) -> PuzzleInfo:
nft_coin: Optional[NFTCoinInfo] = self.get_nft(asset_id)
def get_puzzle_info(self, nft_id: bytes32) -> PuzzleInfo:
nft_coin: Optional[NFTCoinInfo] = self.get_nft(nft_id)
if nft_coin is None:
raise ValueError("An asset ID was specified that this wallet doesn't track")
puzzle_info: Optional[PuzzleInfo] = match_puzzle(nft_coin.full_puzzle)
@@ -650,8 +641,8 @@ class NFTWallet:
else:
return puzzle_info
async def get_coins_to_offer(self, asset_id: bytes32, amount: uint64) -> Set[Coin]:
nft_coin: Optional[NFTCoinInfo] = self.get_nft(asset_id)
async def get_coins_to_offer(self, nft_id: bytes32, amount: uint64) -> Set[Coin]:
nft_coin: Optional[NFTCoinInfo] = self.get_nft(nft_id)
if nft_coin is None:
raise ValueError("An asset ID was specified that this wallet doesn't track")
return set([nft_coin.coin])
@@ -801,7 +792,7 @@ class NFTWallet:
elif len(payments) > 1:
raise ValueError("NFTs can only be sent to one party")
else:
nft_coin = [c for c in self.nft_wallet_info.my_nft_coins if c.coin in coins][0]
nft_coin = [c for c in self.my_nft_coins if c.coin in coins][0]
if coin_announcements_to_consume is not None:
coin_announcements_bytes: Optional[Set[bytes32]] = {a.name() for a in coin_announcements_to_consume}
+143
View File
@@ -0,0 +1,143 @@
import json
from typing import List, Optional, Type, TypeVar
import aiosqlite
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.db_wrapper import DBWrapper
from chia.util.ints import uint32
from chia.wallet.lineage_proof import LineageProof
from chia.wallet.nft_wallet.nft_info import DEFAULT_STATUS, IN_TRANSACTION_STATUS, NFTCoinInfo
_T_WalletNftStore = TypeVar("_T_WalletNftStore", bound="WalletNftStore")
class WalletNftStore:
"""
WalletNftStore keeps track of all user created NFTs and necessary smart-contract data
"""
db_connection: aiosqlite.Connection
db_wrapper: DBWrapper
@classmethod
async def create(cls: Type[_T_WalletNftStore], db_wrapper: DBWrapper) -> _T_WalletNftStore:
self = cls()
self.db_wrapper = db_wrapper
self.db_connection = db_wrapper.db
await self.db_connection.execute(
(
"CREATE TABLE IF NOT EXISTS users_nfts("
" nft_id text PRIMARY KEY,"
" nft_coin_id text,"
" wallet_id int,"
" did_id text,"
" coin text,"
" lineage_proof text,"
" mint_height bigint,"
" status text,"
" full_puzzle blob)"
)
)
await self.db_connection.execute("CREATE INDEX IF NOT EXISTS nft_coin_id on users_nfts(nft_coin_id)")
await self.db_connection.execute("CREATE INDEX IF NOT EXISTS nft_wallet_id on users_nfts(wallet_id)")
await self.db_connection.execute("CREATE INDEX IF NOT EXISTS nft_did_id on users_nfts(did_id)")
await self.db_connection.commit()
return self
async def _clear_database(self) -> None:
cursor = await self.db_connection.execute("DELETE FROM users_nfts")
await cursor.close()
await self.db_connection.commit()
async def delete_nft(self, nft_id: bytes32, in_transaction: bool = False) -> None:
if not in_transaction:
await self.db_wrapper.lock.acquire()
try:
cursor = await self.db_connection.execute(f"DELETE FROM users_nfts where nft_id='{nft_id.hex()}'")
await cursor.close()
finally:
if not in_transaction:
await self.db_connection.commit()
self.db_wrapper.lock.release()
async def save_nft(
self, wallet_id: uint32, did_id: Optional[bytes32], nft_coin_info: NFTCoinInfo, in_transaction: bool = False
) -> None:
if not in_transaction:
await self.db_wrapper.lock.acquire()
try:
cursor = await self.db_connection.execute(
"INSERT or REPLACE INTO users_nfts VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)",
(
nft_coin_info.nft_id.hex(),
nft_coin_info.coin.name().hex(),
int(wallet_id),
did_id.hex() if did_id else None,
json.dumps(nft_coin_info.coin.to_json_dict()),
json.dumps(nft_coin_info.lineage_proof.to_json_dict())
if nft_coin_info.lineage_proof is not None
else None,
int(nft_coin_info.mint_height),
IN_TRANSACTION_STATUS if nft_coin_info.pending_transaction else DEFAULT_STATUS,
bytes(nft_coin_info.full_puzzle),
),
)
await cursor.close()
finally:
if not in_transaction:
await self.db_connection.commit()
self.db_wrapper.lock.release()
async def get_nft_list(
self, wallet_id: Optional[uint32] = None, did_id: Optional[bytes32] = None
) -> List[NFTCoinInfo]:
sql: str = "SELECT nft_id, coin, lineage_proof, mint_height, status, full_puzzle from users_nfts"
if wallet_id is not None and did_id is None:
sql += f" where wallet_id={wallet_id}"
if wallet_id is None and did_id is not None:
sql += f" where did_id='{did_id.hex()}'"
if wallet_id is not None and did_id is not None:
sql += f" where did_id='{did_id.hex()}' and wallet_id={wallet_id}"
cursor = await self.db_connection.execute(sql)
rows = await cursor.fetchall()
await cursor.close()
result = []
for row in rows:
result.append(
NFTCoinInfo(
bytes32.from_hexstr(row[0]),
Coin.from_json_dict(json.loads(row[1])),
None if row[2] is None else LineageProof.from_json_dict(json.loads(row[2])),
Program.from_bytes(row[5]),
uint32(row[3]),
row[4] == IN_TRANSACTION_STATUS,
)
)
return result
async def get_nft_by_id(self, nft_id: bytes32) -> Optional[NFTCoinInfo]:
cursor = await self.db_connection.execute(
"SELECT nft_id, coin, lineage_proof, mint_height, status, full_puzzle from users_nfts WHERE nft_id=?",
(nft_id.hex(),),
)
row = await cursor.fetchone()
await cursor.close()
if row is None:
return None
return NFTCoinInfo(
bytes32.from_hexstr(row[0]),
Coin.from_json_dict(json.loads(row[1])),
None if row[2] is None else LineageProof.from_json_dict(json.loads(row[2])),
Program.from_bytes(row[5]),
uint32(row[3]),
row[4] == IN_TRANSACTION_STATUS,
)
async def close(self) -> None:
await self.db_connection.close()
+36 -4
View File
@@ -37,13 +37,15 @@ 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
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_puzzles import get_metadata_and_phs
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
from chia.wallet.outer_puzzles import AssetType
from chia.wallet.puzzle_drivers import PuzzleInfo
from chia.wallet.puzzles.cat_loader import CAT_MOD
from chia.wallet.rl_wallet.rl_wallet import RLWallet
@@ -62,6 +64,7 @@ from chia.wallet.wallet_coin_record import WalletCoinRecord
from chia.wallet.wallet_coin_store import WalletCoinStore
from chia.wallet.wallet_info import WalletInfo
from chia.wallet.wallet_interested_store import WalletInterestedStore
from chia.wallet.wallet_nft_store import WalletNftStore
from chia.wallet.wallet_pool_store import WalletPoolStore
from chia.wallet.wallet_puzzle_store import WalletPuzzleStore
from chia.wallet.wallet_sync_store import WalletSyncStore
@@ -76,6 +79,7 @@ class WalletStateManager:
tx_store: WalletTransactionStore
puzzle_store: WalletPuzzleStore
user_store: WalletUserStore
nft_store: WalletNftStore
action_store: WalletActionStore
basic_store: KeyValStore
@@ -151,6 +155,7 @@ class WalletStateManager:
self.tx_store = await WalletTransactionStore.create(self.db_wrapper)
self.puzzle_store = await WalletPuzzleStore.create(self.db_wrapper)
self.user_store = await WalletUserStore.create(self.db_wrapper)
self.nft_store = await WalletNftStore.create(self.db_wrapper)
self.action_store = await WalletActionStore.create(self.db_wrapper)
self.basic_store = await KeyValStore.create(self.db_wrapper)
self.trade_manager = await TradeManager.create(self, self.db_wrapper)
@@ -730,6 +735,34 @@ class WalletStateManager:
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,
Program.from_bytes(bytes(coin_spend.puzzle_reveal)),
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
self.log.info(
"Cannot find a NFT wallet for NFT_ID: %s DID: %s, creating a new one.",
uncurried_nft.singleton_launcher_id,
@@ -1300,9 +1333,8 @@ class WalletStateManager:
if bytes(wallet.cat_info.limitations_program_hash).hex() == asset_id:
return wallet
elif wallet.type() == WalletType.NFT:
for nft_coin in wallet.nft_wallet_info.my_nft_coins:
nft_info = match_puzzle(nft_coin.full_puzzle)
if nft_info.info["launcher_id"] == "0x" + asset_id: # type: ignore
for nft_coin in wallet.my_nft_coins:
if nft_coin.nft_id.hex() == asset_id:
return wallet
return None
+24 -24
View File
@@ -109,9 +109,9 @@ async def test_nft_offer_with_fee(two_wallet_nodes: Any, trusted: Any) -> None:
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(token_ph))
await asyncio.sleep(5)
coins_maker = nft_wallet_maker.nft_wallet_info.my_nft_coins
coins_maker = nft_wallet_maker.my_nft_coins
assert len(coins_maker) == 1
coins_taker = nft_wallet_taker.nft_wallet_info.my_nft_coins
coins_taker = nft_wallet_taker.my_nft_coins
assert len(coins_taker) == 0
# MAKE FIRST TRADE: 1 NFT for 100 xch
@@ -154,8 +154,8 @@ async def test_nft_offer_with_fee(two_wallet_nodes: Any, trusted: Any) -> None:
await time_out_assert(15, wallet_maker.get_confirmed_balance, maker_balance_pre + xch_request - maker_fee)
await time_out_assert(15, wallet_taker.get_confirmed_balance, taker_balance_pre - xch_request - taker_fee)
coins_maker = nft_wallet_maker.nft_wallet_info.my_nft_coins
coins_taker = nft_wallet_taker.nft_wallet_info.my_nft_coins
coins_maker = nft_wallet_maker.my_nft_coins
coins_taker = nft_wallet_taker.my_nft_coins
assert len(coins_maker) == 0
assert len(coins_taker) == 1
@@ -199,8 +199,8 @@ async def test_nft_offer_with_fee(two_wallet_nodes: Any, trusted: Any) -> None:
await time_out_assert(15, wallet_maker.get_confirmed_balance, maker_balance_pre - xch_offered - maker_fee)
await time_out_assert(15, wallet_taker.get_confirmed_balance, taker_balance_pre + xch_offered - taker_fee)
coins_maker = nft_wallet_maker.nft_wallet_info.my_nft_coins
coins_taker = nft_wallet_taker.nft_wallet_info.my_nft_coins
coins_maker = nft_wallet_maker.my_nft_coins
coins_taker = nft_wallet_taker.my_nft_coins
assert len(coins_maker) == 1
assert len(coins_taker) == 0
@@ -280,9 +280,9 @@ async def test_nft_offer_cancellations(two_wallet_nodes: Any, trusted: Any) -> N
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(token_ph))
await asyncio.sleep(5)
coins_maker = nft_wallet_maker.nft_wallet_info.my_nft_coins
coins_maker = nft_wallet_maker.my_nft_coins
assert len(coins_maker) == 1
coins_taker = nft_wallet_taker.nft_wallet_info.my_nft_coins
coins_taker = nft_wallet_taker.my_nft_coins
assert len(coins_taker) == 0
# maker creates offer and cancels
@@ -326,7 +326,7 @@ async def test_nft_offer_cancellations(two_wallet_nodes: Any, trusted: Any) -> N
maker_balance = await wallet_maker.get_confirmed_balance()
assert maker_balance == maker_balance_pre - cancel_fee
coins_maker = nft_wallet_maker.nft_wallet_info.my_nft_coins
coins_maker = nft_wallet_maker.my_nft_coins
assert len(coins_maker) == 1
@@ -411,9 +411,9 @@ async def test_nft_offer_with_metadata_update(two_wallet_nodes: Any, trusted: An
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(token_ph))
await asyncio.sleep(5)
coins_maker = nft_wallet_maker.nft_wallet_info.my_nft_coins
coins_maker = nft_wallet_maker.my_nft_coins
assert len(coins_maker) == 1
coins_taker = nft_wallet_taker.nft_wallet_info.my_nft_coins
coins_taker = nft_wallet_taker.my_nft_coins
assert len(coins_taker) == 0
# Maker updates metadata:
@@ -429,7 +429,7 @@ async def test_nft_offer_with_metadata_update(two_wallet_nodes: Any, trusted: An
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(token_ph))
await asyncio.sleep(5)
coins_maker = nft_wallet_maker.nft_wallet_info.my_nft_coins
coins_maker = nft_wallet_maker.my_nft_coins
updated_nft = coins_maker[0]
updated_nft_info = match_puzzle(updated_nft.full_puzzle)
@@ -475,8 +475,8 @@ async def test_nft_offer_with_metadata_update(two_wallet_nodes: Any, trusted: An
await time_out_assert(15, wallet_maker.get_confirmed_balance, maker_balance_pre + xch_request - maker_fee)
await time_out_assert(15, wallet_taker.get_confirmed_balance, taker_balance_pre - xch_request - taker_fee)
coins_maker = nft_wallet_maker.nft_wallet_info.my_nft_coins
coins_taker = nft_wallet_taker.nft_wallet_info.my_nft_coins
coins_maker = nft_wallet_maker.my_nft_coins
coins_taker = nft_wallet_taker.my_nft_coins
assert len(coins_maker) == 0
assert len(coins_taker) == 1
@@ -557,9 +557,9 @@ async def test_nft_offer_nft_for_cat(two_wallet_nodes: Any, trusted: Any) -> Non
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(token_ph))
await asyncio.sleep(5)
coins_maker = nft_wallet_maker.nft_wallet_info.my_nft_coins
coins_maker = nft_wallet_maker.my_nft_coins
assert len(coins_maker) == 1
coins_taker = nft_wallet_taker.nft_wallet_info.my_nft_coins
coins_taker = nft_wallet_taker.my_nft_coins
assert len(coins_taker) == 0
# Create two new CATs and wallets for maker and taker
@@ -642,8 +642,8 @@ async def test_nft_offer_nft_for_cat(two_wallet_nodes: Any, trusted: Any) -> Non
taker_balance_post = await wallet_taker.get_confirmed_balance()
assert maker_balance_post == maker_balance_pre - maker_fee
assert taker_balance_post == taker_balance_pre - taker_fee
coins_maker = nft_wallet_maker.nft_wallet_info.my_nft_coins
coins_taker = nft_wallet_taker.nft_wallet_info.my_nft_coins
coins_maker = nft_wallet_maker.my_nft_coins
coins_taker = nft_wallet_taker.my_nft_coins
assert len(coins_maker) == 0
assert len(coins_taker) == 1
@@ -700,8 +700,8 @@ async def test_nft_offer_nft_for_cat(two_wallet_nodes: Any, trusted: Any) -> Non
taker_balance_post_2 = await wallet_taker.get_confirmed_balance()
assert maker_balance_post_2 == maker_balance_post - maker_fee
assert taker_balance_post_2 == taker_balance_post - taker_fee
coins_maker = nft_wallet_maker.nft_wallet_info.my_nft_coins
coins_taker = nft_wallet_taker.nft_wallet_info.my_nft_coins
coins_maker = nft_wallet_maker.my_nft_coins
coins_taker = nft_wallet_taker.my_nft_coins
assert len(coins_maker) == 1
assert len(coins_taker) == 0
@@ -792,9 +792,9 @@ async def test_nft_offer_nft_for_nft(two_wallet_nodes: Any, trusted: Any) -> Non
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(token_ph))
await asyncio.sleep(5)
coins_maker = nft_wallet_maker.nft_wallet_info.my_nft_coins
coins_maker = nft_wallet_maker.my_nft_coins
assert len(coins_maker) == 1
coins_taker = nft_wallet_taker.nft_wallet_info.my_nft_coins
coins_taker = nft_wallet_taker.my_nft_coins
assert len(coins_taker) == 1
maker_balance_pre = await wallet_maker.get_confirmed_balance()
@@ -843,7 +843,7 @@ async def test_nft_offer_nft_for_nft(two_wallet_nodes: Any, trusted: Any) -> Non
await time_out_assert(15, wallet_maker.get_confirmed_balance, maker_balance_pre - maker_fee)
await time_out_assert(15, wallet_taker.get_confirmed_balance, taker_balance_pre - taker_fee)
coins_maker = nft_wallet_maker.nft_wallet_info.my_nft_coins
coins_taker = nft_wallet_taker.nft_wallet_info.my_nft_coins
coins_maker = nft_wallet_maker.my_nft_coins
coins_taker = nft_wallet_taker.my_nft_coins
assert len(coins_maker) == 1
assert len(coins_taker) == 1
+16 -15
View File
@@ -98,8 +98,8 @@ async def test_nft_wallet_creation_automatically(two_wallet_nodes: Any, trusted:
for i in range(1, num_blocks):
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph1))
await time_out_assert(15, len, 1, nft_wallet_0.nft_wallet_info.my_nft_coins)
coins = nft_wallet_0.nft_wallet_info.my_nft_coins
await time_out_assert(15, len, 1, nft_wallet_0.my_nft_coins)
coins = nft_wallet_0.my_nft_coins
assert len(coins) == 1, "nft not generated"
txs = await nft_wallet_0.generate_signed_transaction([coins[0].coin.amount], [ph1], coins=set([coins[0].coin]))
@@ -116,11 +116,12 @@ async def test_nft_wallet_creation_automatically(two_wallet_nodes: Any, trusted:
nft_wallets = await wallet_node_1.wallet_state_manager.get_all_wallet_info_entries(WalletType.NFT)
assert len(nft_wallets) == 1
nft_wallet_1: NFTWallet = wallet_node_1.wallet_state_manager.wallets[nft_wallets[0].id]
await time_out_assert(15, len, 0, nft_wallet_0.nft_wallet_info.my_nft_coins)
await time_out_assert(15, len, 1, nft_wallet_1.nft_wallet_info.my_nft_coins)
coins = nft_wallet_0.nft_wallet_info.my_nft_coins
await time_out_assert(15, len, 0, nft_wallet_0.my_nft_coins)
await time_out_assert(15, len, 1, nft_wallet_1.my_nft_coins)
coins = nft_wallet_0.my_nft_coins
assert len(coins) == 0
coins = nft_wallet_1.nft_wallet_info.my_nft_coins
coins = nft_wallet_1.my_nft_coins
assert len(coins) == 1
@@ -193,7 +194,7 @@ async def test_nft_wallet_creation_and_transfer(two_wallet_nodes: Any, trusted:
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)
await time_out_assert(10, len, 1, nft_wallet_0.my_nft_coins)
metadata = Program.to(
[
("u", ["https://www.test.net/logo.svg"]),
@@ -212,8 +213,8 @@ async def test_nft_wallet_creation_and_transfer(two_wallet_nodes: Any, trusted:
await time_out_assert(30, wallet_node_0.wallet_state_manager.lock.locked, False)
for i in range(1, num_blocks):
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph1))
await time_out_assert(15, len, 2, nft_wallet_0.nft_wallet_info.my_nft_coins)
coins = nft_wallet_0.nft_wallet_info.my_nft_coins
await time_out_assert(15, len, 2, nft_wallet_0.my_nft_coins)
coins = nft_wallet_0.my_nft_coins
assert len(coins) == 2, "nft not generated"
await time_out_assert(15, wallet_0.get_pending_change_balance, 0)
@@ -231,9 +232,9 @@ async def test_nft_wallet_creation_and_transfer(two_wallet_nodes: Any, trusted:
for i in range(1, num_blocks):
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph1))
await time_out_assert(15, len, 1, nft_wallet_0.nft_wallet_info.my_nft_coins)
await time_out_assert(15, len, 1, nft_wallet_1.nft_wallet_info.my_nft_coins)
coins = nft_wallet_1.nft_wallet_info.my_nft_coins
await time_out_assert(15, len, 1, nft_wallet_0.my_nft_coins)
await time_out_assert(15, len, 1, nft_wallet_1.my_nft_coins)
coins = nft_wallet_1.my_nft_coins
assert len(coins) == 1
await time_out_assert(15, wallet_1.get_pending_change_balance, 0)
@@ -253,8 +254,8 @@ async def test_nft_wallet_creation_and_transfer(two_wallet_nodes: Any, trusted:
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
await time_out_assert(30, wallet_node_0.wallet_state_manager.lock.locked, False)
await time_out_assert(15, len, 2, nft_wallet_0.nft_wallet_info.my_nft_coins)
await time_out_assert(15, len, 0, nft_wallet_1.nft_wallet_info.my_nft_coins)
await time_out_assert(15, len, 2, nft_wallet_0.my_nft_coins)
await time_out_assert(15, len, 0, nft_wallet_1.my_nft_coins)
@pytest.mark.parametrize(
@@ -962,7 +963,7 @@ async def test_nft_transfer_nft_with_did(two_wallet_nodes: Any, trusted: Any) ->
raise AssertionError("NFT not transferred")
nft_wallet_1 = wallet_1.wallet_state_manager.wallets[2]
await time_out_assert(15, len, 1, nft_wallet_1.nft_wallet_info.my_nft_coins)
await time_out_assert(15, len, 1, nft_wallet_1.my_nft_coins)
@pytest.mark.parametrize(
+58
View File
@@ -0,0 +1,58 @@
from pathlib import Path
import aiosqlite
import pytest
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.db_wrapper import DBWrapper
from chia.util.ints import uint32, uint64
from chia.wallet.lineage_proof import LineageProof
from chia.wallet.nft_wallet.nft_info import NFTCoinInfo
from chia.wallet.wallet_nft_store import WalletNftStore
class TestNftStore:
@pytest.mark.asyncio
async def test_nft_store(self) -> None:
db_filename = Path("nft_store_test.db")
if db_filename.exists():
db_filename.unlink()
con = await aiosqlite.connect(db_filename)
wrapper = DBWrapper(con)
db = await WalletNftStore.create(wrapper)
try:
a_bytes32 = bytes32.fromhex("09287c75377c63fd6a3a4d6658abed03e9a521e0436b1f83cdf4af99341ce8f1")
puzzle = Program.to(["A Test puzzle"])
nft = NFTCoinInfo(
a_bytes32,
Coin(a_bytes32, a_bytes32, uint64(1)),
LineageProof(a_bytes32, a_bytes32, uint64(1)),
puzzle,
uint32(10),
)
# Test save
await db.save_nft(uint32(1), a_bytes32, nft)
# Test get nft
assert nft == (await db.get_nft_list(wallet_id=uint32(1)))[0]
assert nft == (await db.get_nft_list())[0]
assert nft == (await db.get_nft_list(did_id=a_bytes32))[0]
assert nft == (await db.get_nft_list(wallet_id=uint32(1), did_id=a_bytes32))[0]
assert nft == await db.get_nft_by_id(a_bytes32)
# Test delete
await db.delete_nft(a_bytes32)
assert await db.get_nft_by_id(a_bytes32) is None
except Exception as e:
print(e, type(e))
await db._clear_database()
await db.close()
db_filename.unlink()
raise e
await db._clear_database()
await db.close()
db_filename.unlink()