mirror of
https://github.com/Chia-Network/chia-blockchain.git
synced 2024-09-20 08:05:33 +03:00
Add /cat_get_unacknowledged API for accessing unknown CATs (#10382)
* Add /cat_get_unacknowledged API for accessing unknown CATs * Reformat & fix cast issue * Integration tested & add unit test * Handle optional uint32 * Reformat * Reformat * Reformat * Merge PR 10308 * Reformat * Fix concurrent issue * Add state change notification * rename API * Fix failing tests * Updated state_change name Co-authored-by: Jeff Cruikshank <jeff@chia.net>
This commit is contained in:
parent
c8468bea07
commit
ffd3b19315
@ -91,6 +91,7 @@ class WalletRpcApi:
|
||||
"/cat_set_name": self.cat_set_name,
|
||||
"/cat_asset_id_to_name": self.cat_asset_id_to_name,
|
||||
"/cat_get_name": self.cat_get_name,
|
||||
"/get_stray_cats": self.get_stray_cats,
|
||||
"/cat_spend": self.cat_spend,
|
||||
"/cat_get_asset_id": self.cat_get_asset_id,
|
||||
"/create_offer_for_ids": self.create_offer_for_ids,
|
||||
@ -857,6 +858,16 @@ class WalletRpcApi:
|
||||
name: str = await wallet.get_name()
|
||||
return {"wallet_id": wallet_id, "name": name}
|
||||
|
||||
async def get_stray_cats(self, request):
|
||||
"""
|
||||
Get a list of all unacknowledged CATs
|
||||
:param request: RPC request
|
||||
:return: A list of unacknowledged CATs
|
||||
"""
|
||||
assert self.service.wallet_state_manager is not None
|
||||
cats = await self.service.wallet_state_manager.interested_store.get_unacknowledged_tokens()
|
||||
return {"stray_cats": cats}
|
||||
|
||||
async def cat_spend(self, request):
|
||||
assert self.service.wallet_state_manager is not None
|
||||
|
||||
|
@ -363,6 +363,10 @@ class WalletRpcClient(RpcClient):
|
||||
}
|
||||
return bytes.fromhex((await self.fetch("cat_get_asset_id", request))["asset_id"])
|
||||
|
||||
async def get_stray_cats(self) -> Dict:
|
||||
response = await self.fetch("get_stray_cats", {})
|
||||
return response["stray_cats"]
|
||||
|
||||
async def cat_asset_id_to_name(self, asset_id: bytes32) -> Optional[Tuple[Optional[uint32], str]]:
|
||||
request: Dict[str, Any] = {
|
||||
"asset_id": asset_id.hex(),
|
||||
|
@ -4,6 +4,7 @@ import aiosqlite
|
||||
|
||||
from chia.types.blockchain_format.sized_bytes import bytes32
|
||||
from chia.util.db_wrapper import DBWrapper
|
||||
from chia.util.ints import uint32
|
||||
|
||||
|
||||
class WalletInterestedStore:
|
||||
@ -26,14 +27,21 @@ class WalletInterestedStore:
|
||||
await self.db_connection.execute(
|
||||
"CREATE TABLE IF NOT EXISTS interested_puzzle_hashes(puzzle_hash text PRIMARY KEY, wallet_id integer)"
|
||||
)
|
||||
|
||||
# Table for unknown CATs
|
||||
fields = "asset_id text PRIMARY KEY, name text, first_seen_height integer, sender_puzzle_hash text"
|
||||
await self.db_connection.execute(f"CREATE TABLE IF NOT EXISTS unacknowledged_asset_tokens({fields})")
|
||||
|
||||
await self.db_connection.commit()
|
||||
return self
|
||||
|
||||
async def _clear_database(self):
|
||||
cursor = await self.db_connection.execute("DELETE FROM puzzle_hashes")
|
||||
cursor = await self.db_connection.execute("DELETE FROM interested_puzzle_hashes")
|
||||
await cursor.close()
|
||||
cursor = await self.db_connection.execute("DELETE FROM interested_coins")
|
||||
await cursor.close()
|
||||
cursor = await self.db_connection.execute("DELETE FROM unacknowledged_asset_tokens")
|
||||
await cursor.close()
|
||||
await self.db_connection.commit()
|
||||
|
||||
async def get_interested_coin_ids(self) -> List[bytes32]:
|
||||
@ -97,3 +105,52 @@ class WalletInterestedStore:
|
||||
if not in_transaction:
|
||||
await self.db_connection.commit()
|
||||
self.db_wrapper.lock.release()
|
||||
|
||||
async def add_unacknowledged_token(
|
||||
self,
|
||||
asset_id: bytes32,
|
||||
name: str,
|
||||
first_seen_height: Optional[uint32],
|
||||
sender_puzzle_hash: bytes32,
|
||||
in_transaction: bool = True,
|
||||
) -> None:
|
||||
"""
|
||||
Add an unacknowledged CAT to the database. It will only be inserted once at the first time.
|
||||
:param asset_id: CAT asset ID
|
||||
:param name: Name of the CAT, for now it will be unknown until we integrate the CAT name service
|
||||
:param first_seen_height: The block height of the wallet received this CAT in the first time
|
||||
:param sender_puzzle_hash: The puzzle hash of the sender
|
||||
:param in_transaction: In transaction or not
|
||||
:return: None
|
||||
"""
|
||||
if not in_transaction:
|
||||
await self.db_wrapper.lock.acquire()
|
||||
try:
|
||||
cursor = await self.db_connection.execute(
|
||||
"INSERT OR IGNORE INTO unacknowledged_asset_tokens VALUES (?, ?, ?, ?)",
|
||||
(
|
||||
asset_id.hex(),
|
||||
name,
|
||||
first_seen_height if first_seen_height is not None else 0,
|
||||
sender_puzzle_hash.hex(),
|
||||
),
|
||||
)
|
||||
await cursor.close()
|
||||
finally:
|
||||
if not in_transaction:
|
||||
await self.db_connection.commit()
|
||||
self.db_wrapper.lock.release()
|
||||
|
||||
async def get_unacknowledged_tokens(self) -> List:
|
||||
"""
|
||||
Get a list of all unacknowledged CATs
|
||||
:return: A json style list of unacknowledged CATs
|
||||
"""
|
||||
cursor = await self.db_connection.execute(
|
||||
"SELECT asset_id, name, first_seen_height, sender_puzzle_hash FROM unacknowledged_asset_tokens"
|
||||
)
|
||||
cats = await cursor.fetchall()
|
||||
return [
|
||||
{"asset_id": cat[0], "name": cat[1], "first_seen_height": cat[2], "sender_puzzle_hash": cat[3]}
|
||||
for cat in cats
|
||||
]
|
||||
|
@ -572,7 +572,8 @@ class WalletStateManager:
|
||||
self.log.info(f"Received state for the coin that doesn't belong to us {coin_state}")
|
||||
else:
|
||||
our_inner_puzzle: Program = self.main_wallet.puzzle_for_pk(bytes(derivation_record.pubkey))
|
||||
cat_puzzle = construct_cat_puzzle(CAT_MOD, bytes32(bytes(tail_hash)[1:]), our_inner_puzzle)
|
||||
asset_id: bytes32 = bytes32(bytes(tail_hash)[1:])
|
||||
cat_puzzle = construct_cat_puzzle(CAT_MOD, asset_id, our_inner_puzzle)
|
||||
if cat_puzzle.get_tree_hash() != coin_state.coin.puzzle_hash:
|
||||
return None, None
|
||||
if bytes(tail_hash).hex()[2:] in self.default_cats or self.config.get(
|
||||
@ -584,6 +585,15 @@ class WalletStateManager:
|
||||
wallet_id = cat_wallet.id()
|
||||
wallet_type = WalletType(cat_wallet.type())
|
||||
self.state_changed("wallet_created")
|
||||
else:
|
||||
# Found unacknowledged CAT, save it in the database.
|
||||
await self.interested_store.add_unacknowledged_token(
|
||||
asset_id,
|
||||
CATWallet.default_wallet_name_for_unknown_cat(asset_id.hex()),
|
||||
parent_coin_state.spent_height,
|
||||
parent_coin_state.coin.puzzle_hash,
|
||||
)
|
||||
self.state_changed("added_stray_cat")
|
||||
|
||||
return wallet_id, wallet_type
|
||||
|
||||
|
@ -477,6 +477,12 @@ class TestWalletRpc:
|
||||
for i in range(0, 5):
|
||||
await client.farm_block(encode_puzzle_hash(ph_2, "txch"))
|
||||
await asyncio.sleep(0.5)
|
||||
# Test unacknowledged CAT
|
||||
await wallet_node.wallet_state_manager.interested_store.add_unacknowledged_token(
|
||||
asset_id, "Unknown", uint32(10000), bytes.fromhex("ABCD")
|
||||
)
|
||||
cats = await client.get_stray_cats()
|
||||
assert len(cats) == 1
|
||||
|
||||
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)
|
||||
|
Loading…
Reference in New Issue
Block a user