Skip to content

Commit a4e0375

Browse files
committed
Port create_signed_transaction to @marshal
1 parent d9451ef commit a4e0375

File tree

5 files changed

+157
-133
lines changed

5 files changed

+157
-133
lines changed

chia/_tests/wallet/rpc/test_wallet_rpc.py

Lines changed: 46 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@
105105
from chia.wallet.wallet_node import WalletNode
106106
from chia.wallet.wallet_protocol import WalletProtocol
107107
from chia.wallet.wallet_request_types import (
108+
Addition,
108109
AddKey,
109110
CancelOffer,
110111
CancelOffers,
@@ -118,6 +119,7 @@
118119
ClawbackPuzzleDecoratorOverride,
119120
CombineCoins,
120121
CreateOfferForIDs,
122+
CreateSignedTransaction,
121123
DefaultCAT,
122124
DeleteKey,
123125
DeleteNotifications,
@@ -294,21 +296,18 @@ async def wallet_rpc_environment(
294296
yield WalletRpcTestEnvironment(wallet_bundle_1, wallet_bundle_2, node_bundle)
295297

296298

297-
async def create_tx_outputs(wallet: Wallet, output_args: list[tuple[int, Optional[list[str]]]]) -> list[dict[str, Any]]:
298-
outputs = []
299+
async def create_tx_outputs(wallet: Wallet, output_args: list[tuple[int, Optional[list[str]]]]) -> list[Addition]:
299300
async with wallet.wallet_state_manager.new_action_scope(DEFAULT_TX_CONFIG, push=True) as action_scope:
300-
for args in output_args:
301-
output = {
302-
"amount": uint64(args[0]),
303-
"puzzle_hash": await action_scope.get_puzzle_hash(
301+
return [
302+
Addition(
303+
amount=uint64(args[0]),
304+
puzzle_hash=await action_scope.get_puzzle_hash(
304305
wallet.wallet_state_manager, override_reuse_puzhash_with=False
305306
),
306-
}
307-
if args[1] is not None:
308-
assert len(args[1]) > 0
309-
output["memos"] = args[1]
310-
outputs.append(output)
311-
return outputs
307+
memos=None if args[1] is None or len(args[1]) == 0 else args[1],
308+
)
309+
for args in output_args
310+
]
312311

313312

314313
async def assert_wallet_types(client: WalletRpcClient, expected: dict[WalletType, int]) -> None:
@@ -323,20 +322,20 @@ async def assert_wallet_types(client: WalletRpcClient, expected: dict[WalletType
323322

324323
def assert_tx_amounts(
325324
tx: TransactionRecord,
326-
outputs: list[dict[str, Any]],
325+
outputs: list[Addition],
327326
*,
328327
amount_fee: uint64,
329328
change_expected: bool,
330329
is_cat: bool = False,
331330
) -> None:
332331
assert tx.fee_amount == amount_fee
333-
assert tx.amount == sum(output["amount"] for output in outputs)
332+
assert tx.amount == sum(output.amount for output in outputs)
334333
expected_additions = len(outputs) + 1 if change_expected else len(outputs)
335334
assert len(tx.additions) == expected_additions
336335
addition_amounts = [addition.amount for addition in tx.additions]
337336
removal_amounts = [removal.amount for removal in tx.removals]
338337
for output in outputs:
339-
assert output["amount"] in addition_amounts
338+
assert output.amount in addition_amounts
340339
if is_cat:
341340
assert (sum(removal_amounts) - sum(addition_amounts)) == 0
342341
else:
@@ -486,9 +485,8 @@ async def test_push_transactions(wallet_rpc_environment: WalletRpcTestEnvironmen
486485

487486
tx = (
488487
await client.create_signed_transactions(
489-
outputs,
488+
CreateSignedTransaction(additions=outputs, fee=uint64(100)),
490489
tx_config=DEFAULT_TX_CONFIG,
491-
fee=uint64(100),
492490
)
493491
).signed_tx
494492

@@ -654,35 +652,39 @@ async def test_create_signed_transaction(
654652
await farm_transaction_block(full_node_api, wallet_1_node)
655653

656654
outputs = await create_tx_outputs(wallet_2, output_args)
657-
amount_outputs = sum(output["amount"] for output in outputs)
655+
amount_outputs = sum(output.amount for output in outputs)
658656
amount_fee = uint64(fee)
659657

660658
if is_cat:
661-
amount_total = amount_outputs
659+
amount_total: int = amount_outputs
662660
else:
663661
amount_total = amount_outputs + amount_fee
664662

665663
selected_coin = None
666664
if select_coin:
667665
select_coins_response = await wallet_1_rpc.select_coins(
668666
SelectCoins.from_coin_selection_config(
669-
amount=amount_total, wallet_id=uint32(wallet_id), coin_selection_config=DEFAULT_COIN_SELECTION_CONFIG
667+
amount=uint64(amount_total),
668+
wallet_id=uint32(wallet_id),
669+
coin_selection_config=DEFAULT_COIN_SELECTION_CONFIG,
670670
)
671671
)
672672
assert len(select_coins_response.coins) == 1
673673
selected_coin = select_coins_response.coins[0]
674674

675675
txs = (
676676
await wallet_1_rpc.create_signed_transactions(
677-
outputs,
678-
coins=[selected_coin] if selected_coin is not None else [],
679-
fee=amount_fee,
680-
wallet_id=wallet_id,
677+
CreateSignedTransaction(
678+
additions=outputs,
679+
coins=[selected_coin] if selected_coin is not None else None,
680+
fee=amount_fee,
681+
wallet_id=uint32(wallet_id),
682+
push=True,
683+
),
681684
# shouldn't actually block it
682685
tx_config=DEFAULT_TX_CONFIG.override(
683686
excluded_coin_amounts=[uint64(selected_coin.amount)] if selected_coin is not None else [],
684687
),
685-
push=True,
686688
)
687689
).transactions
688690
change_expected = not selected_coin or selected_coin.amount - amount_total > 0
@@ -711,18 +713,18 @@ async def test_create_signed_transaction(
711713
addition_dict: dict[bytes32, Coin] = {addition.name(): addition for addition in additions}
712714
memo_dictionary: dict[bytes32, list[bytes]] = compute_memos(spend_bundle)
713715
for output in outputs:
714-
if "memos" in output:
716+
if output.memos is not None:
715717
found: bool = False
716718
for addition_id, addition in addition_dict.items():
717719
if (
718720
is_cat
719-
and addition.amount == output["amount"]
720-
and memo_dictionary[addition_id][0] == output["puzzle_hash"]
721-
and memo_dictionary[addition_id][1:] == [memo.encode() for memo in output["memos"]]
721+
and addition.amount == output.amount
722+
and memo_dictionary[addition_id][0] == output.puzzle_hash
723+
and memo_dictionary[addition_id][1:] == [memo.encode() for memo in output.memos]
722724
) or (
723-
addition.amount == output["amount"]
724-
and addition.puzzle_hash == output["puzzle_hash"]
725-
and memo_dictionary[addition_id] == [memo.encode() for memo in output["memos"]]
725+
addition.amount == output.amount
726+
and addition.puzzle_hash == output.puzzle_hash
727+
and memo_dictionary[addition_id] == [memo.encode() for memo in output.memos]
726728
):
727729
found = True
728730
assert found
@@ -755,7 +757,9 @@ async def test_create_signed_transaction_with_coin_announcement(
755757
outputs = await create_tx_outputs(wallet_2, [(signed_tx_amount, None)])
756758
tx_res: TransactionRecord = (
757759
await client.create_signed_transactions(
758-
outputs, tx_config=DEFAULT_TX_CONFIG, extra_conditions=(*tx_coin_announcements,)
760+
CreateSignedTransaction(additions=outputs),
761+
tx_config=DEFAULT_TX_CONFIG,
762+
extra_conditions=(*tx_coin_announcements,),
759763
)
760764
).signed_tx
761765
assert_tx_amounts(tx_res, outputs, amount_fee=uint64(0), change_expected=True)
@@ -789,7 +793,9 @@ async def test_create_signed_transaction_with_puzzle_announcement(
789793
outputs = await create_tx_outputs(wallet_2, [(signed_tx_amount, None)])
790794
tx_res = (
791795
await client.create_signed_transactions(
792-
outputs, tx_config=DEFAULT_TX_CONFIG, extra_conditions=(*tx_puzzle_announcements,)
796+
CreateSignedTransaction(additions=outputs),
797+
tx_config=DEFAULT_TX_CONFIG,
798+
extra_conditions=(*tx_puzzle_announcements,),
793799
)
794800
).signed_tx
795801
assert_tx_amounts(tx_res, outputs, amount_fee=uint64(0), change_expected=True)
@@ -816,7 +822,7 @@ async def it_does_not_include_the_excluded_coins() -> None:
816822

817823
tx = (
818824
await wallet_1_rpc.create_signed_transactions(
819-
outputs,
825+
CreateSignedTransaction(additions=outputs),
820826
DEFAULT_TX_CONFIG.override(
821827
excluded_coin_ids=[c.name() for c in select_coins_response.coins],
822828
),
@@ -839,7 +845,7 @@ async def it_throws_an_error_when_all_spendable_coins_are_excluded() -> None:
839845

840846
with pytest.raises(ValueError):
841847
await wallet_1_rpc.create_signed_transactions(
842-
outputs,
848+
CreateSignedTransaction(additions=outputs),
843849
DEFAULT_TX_CONFIG.override(
844850
excluded_coin_ids=[c.name() for c in select_coins_response.coins],
845851
),
@@ -992,13 +998,13 @@ async def test_send_transaction_multi(wallet_rpc_environment: WalletRpcTestEnvir
992998
)
993999
) # we want a coin that won't be selected by default
9941000
outputs = await create_tx_outputs(wallet_2, [(uint64(1), ["memo_1"]), (uint64(2), ["memo_2"])])
995-
amount_outputs = sum(output["amount"] for output in outputs)
1001+
amount_outputs = sum(output.amount for output in outputs)
9961002
amount_fee = uint64(amount_outputs + 1)
9971003

9981004
send_tx_res: TransactionRecord = (
9991005
await client.send_transaction_multi(
10001006
1,
1001-
outputs,
1007+
[output.to_json_dict() for output in outputs],
10021008
DEFAULT_TX_CONFIG,
10031009
coins=select_coins_response.coins,
10041010
fee=amount_fee,
@@ -1021,7 +1027,8 @@ async def test_send_transaction_multi(wallet_rpc_environment: WalletRpcTestEnvir
10211027
memos = tx_confirmed.memos
10221028
assert len(memos) == len(outputs)
10231029
for output in outputs:
1024-
assert [output["memos"][0].encode()] in memos.values()
1030+
assert output.memos is not None
1031+
assert [output.memos[0].encode()] in memos.values()
10251032
spend_bundle = send_tx_res.spend_bundle
10261033
assert spend_bundle is not None
10271034
for key in memos.keys():

chia/_tests/wallet/vc_wallet/test_vc_wallet.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@
3131
from chia.wallet.wallet import Wallet
3232
from chia.wallet.wallet_node import WalletNode
3333
from chia.wallet.wallet_request_types import (
34+
Addition,
3435
CATSpend,
36+
CreateSignedTransaction,
3537
GetTransactions,
3638
GetWallets,
3739
VCAddProofs,
@@ -70,14 +72,16 @@ async def mint_cr_cat(
7072
await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node_0, timeout=20)
7173
tx = (
7274
await client_0.create_signed_transactions(
73-
[
74-
{
75-
"puzzle_hash": cat_puzzle.get_tree_hash(),
76-
"amount": CAT_AMOUNT_0,
77-
}
78-
],
75+
CreateSignedTransaction(
76+
wallet_id=uint32(1),
77+
additions=[
78+
Addition(
79+
puzzle_hash=cat_puzzle.get_tree_hash(),
80+
amount=CAT_AMOUNT_0,
81+
)
82+
],
83+
),
7984
tx_config,
80-
wallet_id=1,
8185
)
8286
).signed_tx
8387
spend_bundle = tx.spend_bundle

chia/wallet/wallet_request_types.py

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,15 @@
1616
from chia.types.blockchain_format.program import Program
1717
from chia.types.coin_record import CoinRecord
1818
from chia.util.byte_types import hexstr_to_bytes
19+
from chia.util.hash import std_hash
1920
from chia.util.streamable import Streamable, streamable
20-
from chia.wallet.conditions import Condition, ConditionValidTimes, conditions_to_json_dicts
21+
from chia.wallet.conditions import (
22+
AssertCoinAnnouncement,
23+
AssertPuzzleAnnouncement,
24+
Condition,
25+
ConditionValidTimes,
26+
conditions_to_json_dicts,
27+
)
2128
from chia.wallet.nft_wallet.nft_info import NFTInfo
2229
from chia.wallet.notification_store import Notification
2330
from chia.wallet.puzzle_drivers import PuzzleInfo, Solver
@@ -1434,7 +1441,7 @@ class CombineCoinsResponse(TransactionEndpointResponse):
14341441
pass
14351442

14361443

1437-
# utility for CATSpend
1444+
# utility for CATSpend/CreateSignedTransaction
14381445
# unfortunate that we can't use CreateCoin but the memos are taken as strings not bytes
14391446
@streamable
14401447
@dataclass(frozen=True)
@@ -1874,6 +1881,63 @@ class SendTransactionMultiResponse(TransactionEndpointResponse):
18741881
transaction_id: bytes32
18751882

18761883

1884+
@streamable
1885+
@dataclass(frozen=True)
1886+
class CSTCoinAnnouncement(Streamable):
1887+
coin_id: bytes32
1888+
message: bytes
1889+
1890+
1891+
@streamable
1892+
@dataclass(frozen=True)
1893+
class CSTPuzzleAnnouncement(Streamable):
1894+
puzzle_hash: bytes32
1895+
message: bytes
1896+
1897+
1898+
@streamable
1899+
@dataclass(frozen=True)
1900+
class CreateSignedTransaction(TransactionEndpointRequest):
1901+
additions: list[Addition] = field(default_factory=default_raise)
1902+
wallet_id: Optional[uint32] = None
1903+
coins: Optional[list[Coin]] = None
1904+
morph_bytes: Optional[bytes] = None
1905+
coin_announcements: list[CSTCoinAnnouncement] = field(default_factory=list)
1906+
puzzle_announcements: list[CSTPuzzleAnnouncement] = field(default_factory=list)
1907+
1908+
def __post_init__(self) -> None:
1909+
if len(self.additions) < 1:
1910+
raise ValueError("Must have at least one addition")
1911+
super().__post_init__()
1912+
1913+
@property
1914+
def coin_set(self) -> Optional[set[Coin]]:
1915+
if self.coins is None:
1916+
return None
1917+
else:
1918+
return set(self.coins)
1919+
1920+
@property
1921+
def asserted_coin_announcements(self) -> tuple[AssertCoinAnnouncement, ...]:
1922+
return tuple(
1923+
AssertCoinAnnouncement(
1924+
asserted_id=ca.coin_id,
1925+
asserted_msg=(ca.message if self.morph_bytes is None else std_hash(self.morph_bytes + ca.message)),
1926+
)
1927+
for ca in self.coin_announcements
1928+
)
1929+
1930+
@property
1931+
def asserted_puzzle_announcements(self) -> tuple[AssertPuzzleAnnouncement, ...]:
1932+
return tuple(
1933+
AssertPuzzleAnnouncement(
1934+
asserted_ph=pa.puzzle_hash,
1935+
asserted_msg=(pa.message if self.morph_bytes is None else std_hash(self.morph_bytes + pa.message)),
1936+
)
1937+
for pa in self.puzzle_announcements
1938+
)
1939+
1940+
18771941
@streamable
18781942
@dataclass(frozen=True)
18791943
class CreateSignedTransactionsResponse(TransactionEndpointResponse):

0 commit comments

Comments
 (0)