Skip to content

Commit 3fa1f5c

Browse files
committed
Port cat_spend to @marshal
1 parent 89b6919 commit 3fa1f5c

File tree

7 files changed

+183
-157
lines changed

7 files changed

+183
-157
lines changed

chia/_tests/cmds/wallet/test_wallet.py

Lines changed: 12 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
)
2727
from chia.cmds.cmds_util import TransactionBundle
2828
from chia.protocols.outbound_message import NodeType
29-
from chia.types.blockchain_format.program import Program
3029
from chia.types.signing_mode import SigningMode
3130
from chia.util.bech32m import encode_puzzle_hash
3231
from chia.wallet.conditions import Condition, ConditionValidTimes
@@ -47,6 +46,7 @@
4746
CATAssetIDToNameResponse,
4847
CATSetName,
4948
CATSetNameResponse,
49+
CATSpend,
5050
CATSpendResponse,
5151
ClawbackPuzzleDecoratorOverride,
5252
CreateOfferForIDsResponse,
@@ -385,31 +385,24 @@ async def send_transaction(
385385

386386
async def cat_spend(
387387
self,
388-
wallet_id: int,
388+
request: CATSpend,
389389
tx_config: TXConfig,
390-
amount: Optional[uint64] = None,
391-
inner_address: Optional[str] = None,
392-
fee: uint64 = uint64(0),
393-
memos: Optional[list[str]] = None,
394-
additions: Optional[list[dict[str, Any]]] = None,
395-
removals: Optional[list[Coin]] = None,
396-
cat_discrepancy: Optional[tuple[int, Program, Program]] = None, # (extra_delta, tail_reveal, tail_solution)
397-
push: bool = True,
390+
extra_conditions: tuple[Condition, ...] = tuple(),
398391
timelock_info: ConditionValidTimes = ConditionValidTimes(),
399392
) -> CATSpendResponse:
400393
self.add_to_log(
401394
"cat_spend",
402395
(
403-
wallet_id,
396+
request.wallet_id,
404397
tx_config,
405-
amount,
406-
inner_address,
407-
fee,
408-
memos,
409-
additions,
410-
removals,
411-
cat_discrepancy,
412-
push,
398+
request.amount,
399+
request.inner_address,
400+
request.fee,
401+
request.memos,
402+
request.additions,
403+
request.coins,
404+
request.cat_discrepancy,
405+
request.push,
413406
timelock_info,
414407
),
415408
)

chia/_tests/wallet/rpc/test_wallet_rpc.py

Lines changed: 47 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@
108108
CATGetAssetID,
109109
CATGetName,
110110
CATSetName,
111+
CATSpend,
111112
CheckDeleteKey,
112113
CheckOfferValidity,
113114
ClawbackPuzzleDecoratorOverride,
@@ -1255,18 +1256,30 @@ async def test_cat_endpoints(wallet_environments: WalletTestFramework, wallet_ty
12551256
# Test CAT spend without a fee
12561257
with pytest.raises(ValueError):
12571258
await env_0.rpc_client.cat_spend(
1258-
cat_0_id,
1259-
DEFAULT_TX_CONFIG.override(
1259+
CATSpend(
1260+
wallet_id=cat_0_id,
1261+
amount=uint64(4),
1262+
inner_address=addr_1,
1263+
fee=uint64(0),
1264+
memos=["the cat memo"],
1265+
push=False,
1266+
),
1267+
tx_config=wallet_environments.tx_config.override(
12601268
excluded_coin_amounts=[uint64(100)],
12611269
excluded_coin_ids=[bytes32.zeros],
12621270
),
1263-
uint64(4),
1264-
addr_1,
1265-
uint64(0),
1266-
["the cat memo"],
12671271
)
1272+
12681273
tx_res = await env_0.rpc_client.cat_spend(
1269-
cat_0_id, wallet_environments.tx_config, uint64(4), addr_1, uint64(0), ["the cat memo"]
1274+
CATSpend(
1275+
wallet_id=cat_0_id,
1276+
amount=uint64(4),
1277+
inner_address=addr_1,
1278+
fee=uint64(0),
1279+
memos=["the cat memo"],
1280+
push=True,
1281+
),
1282+
tx_config=wallet_environments.tx_config,
12701283
)
12711284

12721285
spend_bundle = tx_res.transaction.spend_bundle
@@ -1312,7 +1325,15 @@ async def test_cat_endpoints(wallet_environments: WalletTestFramework, wallet_ty
13121325

13131326
# Test CAT spend with a fee
13141327
tx_res = await env_0.rpc_client.cat_spend(
1315-
cat_0_id, wallet_environments.tx_config, uint64(1), addr_1, uint64(5_000_000), ["the cat memo"]
1328+
CATSpend(
1329+
wallet_id=cat_0_id,
1330+
amount=uint64(1),
1331+
inner_address=addr_1,
1332+
fee=uint64(5_000_000),
1333+
memos=["the cat memo"],
1334+
push=True,
1335+
),
1336+
wallet_environments.tx_config,
13161337
)
13171338

13181339
spend_bundle = tx_res.transaction.spend_bundle
@@ -1379,13 +1400,16 @@ async def test_cat_endpoints(wallet_environments: WalletTestFramework, wallet_ty
13791400
)
13801401
)
13811402
tx_res = await env_0.rpc_client.cat_spend(
1382-
cat_0_id,
1403+
CATSpend(
1404+
wallet_id=cat_0_id,
1405+
amount=uint64(1),
1406+
inner_address=addr_1,
1407+
fee=uint64(5_000_000),
1408+
memos=["the cat memo"],
1409+
coins=select_coins_response.coins,
1410+
push=True,
1411+
),
13831412
wallet_environments.tx_config,
1384-
uint64(1),
1385-
addr_1,
1386-
uint64(5_000_000),
1387-
["the cat memo"],
1388-
removals=select_coins_response.coins,
13891413
)
13901414

13911415
spend_bundle = tx_res.transaction.spend_bundle
@@ -3109,11 +3133,16 @@ async def test_cat_spend_run_tail(wallet_rpc_environment: WalletRpcTestEnvironme
31093133
# Attempt to melt it fully
31103134
tx = (
31113135
await client.cat_spend(
3112-
cat_wallet_id,
3113-
amount=uint64(0),
3136+
CATSpend(
3137+
wallet_id=cat_wallet_id,
3138+
amount=uint64(0),
3139+
inner_address=encode_puzzle_hash(our_ph, "txch"),
3140+
extra_delta=str(tx_amount * -1),
3141+
tail_reveal=b"",
3142+
tail_solution=b"",
3143+
push=True,
3144+
),
31143145
tx_config=DEFAULT_TX_CONFIG,
3115-
inner_address=encode_puzzle_hash(our_ph, "txch"),
3116-
cat_discrepancy=(tx_amount * -1, Program.to(None), Program.to(None)),
31173146
)
31183147
).transaction
31193148
transaction_id = tx.name

chia/_tests/wallet/vc_wallet/test_vc_wallet.py

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
from chia.wallet.wallet import Wallet
3232
from chia.wallet.wallet_node import WalletNode
3333
from chia.wallet.wallet_request_types import (
34+
CATSpend,
3435
GetTransactions,
3536
GetWallets,
3637
VCAddProofs,
@@ -383,12 +384,15 @@ async def test_vc_lifecycle(wallet_environments: WalletTestFramework) -> None:
383384
wallet_1_addr = encode_puzzle_hash(wallet_1_ph, "txch")
384385
txs = (
385386
await client_0.cat_spend(
386-
cr_cat_wallet_0.id(),
387+
CATSpend(
388+
wallet_id=cr_cat_wallet_0.id(),
389+
amount=uint64(90),
390+
inner_address=wallet_1_addr,
391+
fee=uint64(2000000000),
392+
memos=["hey"],
393+
push=True,
394+
),
387395
wallet_environments.tx_config,
388-
uint64(90),
389-
wallet_1_addr,
390-
uint64(2000000000),
391-
memos=["hey"],
392396
)
393397
).transactions
394398
await wallet_environments.process_pending_states(
@@ -557,23 +561,30 @@ async def test_vc_lifecycle(wallet_environments: WalletTestFramework) -> None:
557561
# (Negative test) Try to spend a CR-CAT that we don't have a valid VC for
558562
with pytest.raises(ValueError):
559563
await client_0.cat_spend(
560-
cr_cat_wallet_0.id(),
561-
wallet_environments.tx_config,
562-
uint64(10),
563-
wallet_1_addr,
564+
CATSpend(
565+
wallet_id=cr_cat_wallet_0.id(),
566+
amount=uint64(10),
567+
inner_address=wallet_1_addr,
568+
),
569+
tx_config=wallet_environments.tx_config,
564570
)
565571

566572
# Test melting a CRCAT
567573
# This is intended to trigger an edge case where the output and change are the same forcing a new puzhash
568574
with wallet_environments.new_puzzle_hashes_allowed():
569575
tx = (
570576
await client_1.cat_spend(
571-
env_1.dealias_wallet_id("crcat"),
572-
wallet_environments.tx_config,
573-
uint64(20),
574-
wallet_1_addr,
575-
uint64(0),
576-
cat_discrepancy=(-50, Program.to(None), Program.to(None)),
577+
CATSpend(
578+
wallet_id=env_1.dealias_wallet_id("crcat"),
579+
amount=uint64(20),
580+
inner_address=wallet_1_addr,
581+
fee=uint64(0),
582+
extra_delta=str(-50),
583+
tail_reveal=b"",
584+
tail_solution=b"",
585+
push=True,
586+
),
587+
tx_config=wallet_environments.tx_config,
577588
)
578589
).transaction
579590
[tx] = await wallet_node_1.wallet_state_manager.add_pending_transactions([tx])

chia/cmds/wallet_funcs.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
CATAssetIDToNameResponse,
5050
CATGetName,
5151
CATSetName,
52+
CATSpend,
5253
CATSpendResponse,
5354
ClawbackPuzzleDecoratorOverride,
5455
DeleteNotifications,
@@ -401,18 +402,20 @@ async def send(
401402
elif typ in {WalletType.CAT, WalletType.CRCAT, WalletType.RCAT}:
402403
print("Submitting transaction...")
403404
res = await wallet_client.cat_spend(
404-
wallet_id,
405-
CMDTXConfigLoader(
405+
CATSpend(
406+
wallet_id=uint32(wallet_id),
407+
amount=final_amount,
408+
inner_address=address.original_address,
409+
fee=fee,
410+
memos=memos,
411+
push=push,
412+
),
413+
tx_config=CMDTXConfigLoader(
406414
min_coin_amount=min_coin_amount,
407415
max_coin_amount=max_coin_amount,
408416
excluded_coin_ids=list(excluded_coin_ids),
409417
reuse_puzhash=reuse_puzhash,
410418
).to_tx_config(mojo_per_unit, config, fingerprint),
411-
final_amount,
412-
address.original_address,
413-
fee,
414-
memos,
415-
push=push,
416419
timelock_info=condition_valid_times,
417420
)
418421
else:

chia/wallet/wallet_request_types.py

Lines changed: 55 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1387,6 +1387,61 @@ class CombineCoinsResponse(TransactionEndpointResponse):
13871387
pass
13881388

13891389

1390+
# utility for CATSpend
1391+
# unfortunate that we can't use CreateCoin but the memos are taken as strings not bytes
1392+
@streamable
1393+
@dataclass(frozen=True)
1394+
class Addition(Streamable):
1395+
amount: uint64
1396+
puzzle_hash: bytes32
1397+
memos: Optional[list[str]] = None
1398+
1399+
1400+
@streamable
1401+
@kw_only_dataclass
1402+
class CATSpend(TransactionEndpointRequest):
1403+
wallet_id: uint32 = field(default_factory=default_raise)
1404+
additions: Optional[list[Addition]] = None
1405+
amount: Optional[uint64] = None
1406+
inner_address: Optional[str] = None
1407+
memos: Optional[list[str]] = None
1408+
coins: Optional[list[Coin]] = None
1409+
extra_delta: Optional[str] = None # str to support negative ints :(
1410+
tail_reveal: Optional[bytes] = None
1411+
tail_solution: Optional[bytes] = None
1412+
1413+
def __post_init__(self) -> None:
1414+
if (
1415+
self.additions is not None
1416+
and (self.amount is not None or self.inner_address is not None or self.memos is not None)
1417+
) or (self.additions is None and self.amount is None and self.inner_address is None and self.memos is None):
1418+
raise ValueError('Must specify "additions" or "amount"+"inner_address"+"memos", but not both.')
1419+
elif self.additions is None and None in {self.amount, self.inner_address}:
1420+
raise ValueError('Must specify "amount" and "inner_address" together.')
1421+
super().__post_init__()
1422+
1423+
@property
1424+
def cat_discrepancy(self) -> Optional[tuple[int, Program, Program]]:
1425+
if self.extra_delta is None and self.tail_reveal is None and self.tail_solution is None:
1426+
return None
1427+
elif None in {self.extra_delta, self.tail_reveal, self.tail_solution}:
1428+
raise ValueError('Must specify "extra_delta", "tail_reveal" and "tail_solution" together.')
1429+
else:
1430+
# Curious that mypy doesn't see the elif and know that none of these are None
1431+
return (
1432+
int(self.extra_delta), # type: ignore[arg-type]
1433+
Program.from_bytes(self.tail_reveal), # type: ignore[arg-type]
1434+
Program.from_bytes(self.tail_solution), # type: ignore[arg-type]
1435+
)
1436+
1437+
1438+
@streamable
1439+
@dataclass(frozen=True)
1440+
class CATSpendResponse(TransactionEndpointResponse):
1441+
transaction: TransactionRecord
1442+
transaction_id: bytes32
1443+
1444+
13901445
@streamable
13911446
@kw_only_dataclass
13921447
class DIDMessageSpend(TransactionEndpointRequest):
@@ -1779,13 +1834,6 @@ class CreateSignedTransactionsResponse(TransactionEndpointResponse):
17791834
signed_tx: TransactionRecord
17801835

17811836

1782-
@streamable
1783-
@dataclass(frozen=True)
1784-
class CATSpendResponse(TransactionEndpointResponse):
1785-
transaction: TransactionRecord
1786-
transaction_id: bytes32
1787-
1788-
17891837
@streamable
17901838
@dataclass(frozen=True)
17911839
class _OfferEndpointResponse(TransactionEndpointResponse):

0 commit comments

Comments
 (0)