diff --git a/chia/_tests/cmds/wallet/test_notifications.py b/chia/_tests/cmds/wallet/test_notifications.py index cde0ca62b0c1..65e6e773da59 100644 --- a/chia/_tests/cmds/wallet/test_notifications.py +++ b/chia/_tests/cmds/wallet/test_notifications.py @@ -1,18 +1,23 @@ from __future__ import annotations from pathlib import Path -from typing import cast from chia_rs.sized_bytes import bytes32 from chia_rs.sized_ints import uint32, uint64 from chia._tests.cmds.cmd_test_utils import TestRpcClients, TestWalletRpcClient, logType, run_cli_command_and_assert -from chia._tests.cmds.wallet.test_consts import FINGERPRINT, FINGERPRINT_ARG, get_bytes32 +from chia._tests.cmds.wallet.test_consts import FINGERPRINT, FINGERPRINT_ARG, STD_TX, STD_UTX, get_bytes32 from chia.util.bech32m import encode_puzzle_hash -from chia.wallet.conditions import ConditionValidTimes +from chia.wallet.conditions import Condition, ConditionValidTimes from chia.wallet.notification_store import Notification -from chia.wallet.transaction_record import TransactionRecord -from chia.wallet.wallet_request_types import DeleteNotifications, GetNotifications, GetNotificationsResponse +from chia.wallet.util.tx_config import TXConfig +from chia.wallet.wallet_request_types import ( + DeleteNotifications, + GetNotifications, + GetNotificationsResponse, + SendNotification, + SendNotificationResponse, +) test_condition_valid_times: ConditionValidTimes = ConditionValidTimes(min_time=uint64(100), max_time=uint64(150)) @@ -26,20 +31,17 @@ def test_notifications_send(capsys: object, get_test_cli_clients: tuple[TestRpcC class NotificationsSendRpcClient(TestWalletRpcClient): async def send_notification( self, - target: bytes32, - msg: bytes, - amount: uint64, - fee: uint64 = uint64(0), - push: bool = True, + request: SendNotification, + tx_config: TXConfig, + extra_conditions: tuple[Condition, ...] = tuple(), timelock_info: ConditionValidTimes = ConditionValidTimes(), - ) -> TransactionRecord: - self.add_to_log("send_notification", (target, msg, amount, fee, push, timelock_info)) - - class FakeTransactionRecord: - def __init__(self, name: str) -> None: - self.name = name + ) -> SendNotificationResponse: + self.add_to_log( + "send_notification", + (request.target, request.message, request.amount, request.fee, request.push, timelock_info), + ) - return cast(TransactionRecord, FakeTransactionRecord(get_bytes32(2).hex())) + return SendNotificationResponse([STD_UTX], [STD_TX], tx=STD_TX) inst_rpc_client = NotificationsSendRpcClient() test_rpc_clients.wallet_rpc_client = inst_rpc_client diff --git a/chia/_tests/wallet/rpc/test_wallet_rpc.py b/chia/_tests/wallet/rpc/test_wallet_rpc.py index 8c5082d3b5df..779ff8a3ed88 100644 --- a/chia/_tests/wallet/rpc/test_wallet_rpc.py +++ b/chia/_tests/wallet/rpc/test_wallet_rpc.py @@ -144,6 +144,7 @@ PushTX, RoyaltyAsset, SelectCoins, + SendNotification, SendTransaction, SetWalletResyncOnStartup, SpendClawbackCoins, @@ -2621,21 +2622,25 @@ async def test_notification_rpcs(wallet_rpc_environment: WalletRpcTestEnvironmen env.wallet_2.node.config["enable_notifications"] = True env.wallet_2.node.config["required_notification_amount"] = 100000000000 async with wallet_2.wallet_state_manager.new_action_scope(DEFAULT_TX_CONFIG, push=True) as action_scope: - tx = await client.send_notification( - await action_scope.get_puzzle_hash(wallet_2.wallet_state_manager), - b"hello", - uint64(100000000000), - fee=uint64(100000000000), + response = await client.send_notification( + SendNotification( + target=(await action_scope.get_puzzle_hash(wallet_2.wallet_state_manager)), + message=b"hello", + amount=uint64(100000000000), + fee=uint64(100000000000), + push=True, + ), + tx_config=DEFAULT_TX_CONFIG, ) - assert tx.spend_bundle is not None + assert response.tx.spend_bundle is not None await time_out_assert( 5, full_node_api.full_node.mempool_manager.get_spendbundle, - tx.spend_bundle, - tx.spend_bundle.name(), + response.tx.spend_bundle, + response.tx.spend_bundle.name(), ) - await farm_transaction(full_node_api, wallet_node, tx.spend_bundle) + await farm_transaction(full_node_api, wallet_node, response.tx.spend_bundle) await time_out_assert(20, env.wallet_2.wallet.get_confirmed_balance, uint64(100000000000)) notification = (await client_2.get_notifications(GetNotifications())).notifications[0] @@ -2648,21 +2653,25 @@ async def test_notification_rpcs(wallet_rpc_environment: WalletRpcTestEnvironmen assert [] == (await client_2.get_notifications(GetNotifications([notification.id]))).notifications async with wallet_2.wallet_state_manager.new_action_scope(DEFAULT_TX_CONFIG, push=True) as action_scope: - tx = await client.send_notification( - await action_scope.get_puzzle_hash(wallet_2.wallet_state_manager), - b"hello", - uint64(100000000000), - fee=uint64(100000000000), + response = await client.send_notification( + SendNotification( + target=(await action_scope.get_puzzle_hash(wallet_2.wallet_state_manager)), + message=b"hello", + amount=uint64(100000000000), + fee=uint64(100000000000), + push=True, + ), + tx_config=DEFAULT_TX_CONFIG, ) - assert tx.spend_bundle is not None + assert response.tx.spend_bundle is not None await time_out_assert( 5, full_node_api.full_node.mempool_manager.get_spendbundle, - tx.spend_bundle, - tx.spend_bundle.name(), + response.tx.spend_bundle, + response.tx.spend_bundle.name(), ) - await farm_transaction(full_node_api, wallet_node, tx.spend_bundle) + await farm_transaction(full_node_api, wallet_node, response.tx.spend_bundle) await time_out_assert(20, env.wallet_2.wallet.get_confirmed_balance, uint64(200000000000)) notification = (await client_2.get_notifications(GetNotifications())).notifications[0] diff --git a/chia/cmds/wallet_funcs.py b/chia/cmds/wallet_funcs.py index f111500df6e3..373f960c3961 100644 --- a/chia/cmds/wallet_funcs.py +++ b/chia/cmds/wallet_funcs.py @@ -74,6 +74,7 @@ NFTSetNFTDID, NFTTransferNFT, RoyaltyAsset, + SendNotification, SendTransaction, SendTransactionResponse, SignMessageByAddress, @@ -1576,19 +1577,25 @@ async def send_notification( async with get_wallet_client(root_path, wallet_rpc_port, fp) as (wallet_client, fingerprint, _): amount: uint64 = cli_amount.convert_amount(units["chia"]) - tx = await wallet_client.send_notification( - address.puzzle_hash, - message, - amount, - fee, - push=push, + response = await wallet_client.send_notification( + SendNotification( + address.puzzle_hash, + message, + amount, + fee=fee, + push=push, + ), + tx_config=DEFAULT_TX_CONFIG, timelock_info=condition_valid_times, ) if push: print("Notification sent successfully.") - print(f"To get status, use command: chia wallet get_transaction -f {fingerprint} -tx 0x{tx.name}") - return [tx] + print( + "To get status, use command: chia wallet get_transaction" + f" -f {fingerprint} -tx 0x{response.transactions[0].name}" + ) + return response.transactions async def get_notifications( diff --git a/chia/wallet/wallet_request_types.py b/chia/wallet/wallet_request_types.py index d40fcfc8df75..326a9fa11d0d 100644 --- a/chia/wallet/wallet_request_types.py +++ b/chia/wallet/wallet_request_types.py @@ -1234,6 +1234,20 @@ class SpendClawbackCoinsResponse(TransactionEndpointResponse): transaction_ids: list[bytes32] +@streamable +@dataclass(frozen=True) +class SendNotification(TransactionEndpointRequest): + target: bytes32 = field(default_factory=default_raise) + message: bytes = field(default_factory=default_raise) + amount: uint64 = uint64(0) + + +@streamable +@dataclass(frozen=True) +class SendNotificationResponse(TransactionEndpointResponse): + tx: TransactionRecord + + @streamable @dataclass(frozen=True) class PushTransactions(TransactionEndpointRequest): diff --git a/chia/wallet/wallet_rpc_api.py b/chia/wallet/wallet_rpc_api.py index 0d4a97610ff2..aaacbaba29ae 100644 --- a/chia/wallet/wallet_rpc_api.py +++ b/chia/wallet/wallet_rpc_api.py @@ -248,6 +248,8 @@ PWStatusResponse, SelectCoins, SelectCoinsResponse, + SendNotification, + SendNotificationResponse, SendTransaction, SendTransactionResponse, SetWalletResyncOnStartup, @@ -1937,22 +1939,24 @@ async def delete_notifications(self, request: DeleteNotifications) -> Empty: return Empty() @tx_endpoint(push=True) + @marshal async def send_notification( self, - request: dict[str, Any], + request: SendNotification, action_scope: WalletActionScope, extra_conditions: tuple[Condition, ...] = tuple(), - ) -> EndpointResult: + ) -> SendNotificationResponse: await self.service.wallet_state_manager.notification_manager.send_new_notification( - bytes32.from_hexstr(request["target"]), - bytes.fromhex(request["message"]), - uint64(request["amount"]), + request.target, + request.message, + request.amount, action_scope, - request.get("fee", uint64(0)), + request.fee, extra_conditions=extra_conditions, ) - return {"tx": None, "transactions": None} # tx_endpoint wrapper will take care of this + # tx_endpoint will take care of these default values + return SendNotificationResponse([], [], tx=REPLACEABLE_TRANSACTION_RECORD) @marshal async def verify_signature(self, request: VerifySignature) -> VerifySignatureResponse: diff --git a/chia/wallet/wallet_rpc_client.py b/chia/wallet/wallet_rpc_client.py index 14d2a2fcd515..75a578fcae3b 100644 --- a/chia/wallet/wallet_rpc_client.py +++ b/chia/wallet/wallet_rpc_client.py @@ -162,6 +162,8 @@ PWStatusResponse, SelectCoins, SelectCoinsResponse, + SendNotification, + SendNotificationResponse, SendTransaction, SendTransactionMultiResponse, SendTransactionResponse, @@ -1088,27 +1090,16 @@ async def delete_notifications(self, request: DeleteNotifications) -> None: async def send_notification( self, - target: bytes32, - msg: bytes, - amount: uint64, - fee: uint64 = uint64(0), + request: SendNotification, + tx_config: TXConfig, extra_conditions: tuple[Condition, ...] = tuple(), timelock_info: ConditionValidTimes = ConditionValidTimes(), - push: bool = True, - ) -> TransactionRecord: - response = await self.fetch( - "send_notification", - { - "target": target.hex(), - "message": msg.hex(), - "amount": amount, - "fee": fee, - "extra_conditions": conditions_to_json_dicts(extra_conditions), - "push": push, - **timelock_info.to_json_dict(), - }, + ) -> SendNotificationResponse: + return SendNotificationResponse.from_json_dict( + await self.fetch( + "send_notification", request.json_serialize_for_transport(tx_config, extra_conditions, timelock_info) + ) ) - return TransactionRecord.from_json_dict(response["tx"]) async def sign_message_by_address(self, request: SignMessageByAddress) -> SignMessageByAddressResponse: return SignMessageByAddressResponse.from_json_dict(