diff --git a/chia/_tests/wallet/rpc/test_wallet_rpc.py b/chia/_tests/wallet/rpc/test_wallet_rpc.py index b334ffe98e5f..6e0f37d0205c 100644 --- a/chia/_tests/wallet/rpc/test_wallet_rpc.py +++ b/chia/_tests/wallet/rpc/test_wallet_rpc.py @@ -382,7 +382,7 @@ async def test_push_transactions(wallet_rpc_environment: WalletRpcTestEnvironmen full_node_api: FullNodeSimulator = env.full_node.api client: WalletRpcClient = env.wallet_1.rpc_client - await generate_funds(full_node_api, env.wallet_1) + await generate_funds(full_node_api, env.wallet_1, num_blocks=2) outputs = await create_tx_outputs(wallet, [(1234321, None)]) @@ -394,18 +394,29 @@ async def test_push_transactions(wallet_rpc_environment: WalletRpcTestEnvironmen ) ).signed_tx - await client.push_transactions([tx]) - resp = await client.fetch("push_transactions", {"transactions": [tx.to_json_dict_convenience(wallet_node.config)]}) + resp_client = await client.push_transactions([tx], fee=uint64(10)) + resp = await client.fetch( + "push_transactions", {"transactions": [tx.to_json_dict_convenience(wallet_node.config)], "fee": 10} + ) assert resp["success"] - resp = await client.fetch("push_transactions", {"transactions": [tx.to_json_dict()]}) + resp = await client.fetch("push_transactions", {"transactions": [tx.to_json_dict()], "fee": 10}) assert resp["success"] - spend_bundle = tx.spend_bundle + spend_bundle = SpendBundle.aggregate( + [ + # We ARE checking that the spendbundle is not None but mypy can't recognize this + TransactionRecord.from_json_dict_convenience(tx).spend_bundle # type: ignore[misc] + for tx in resp_client["transactions"] + if tx["spend_bundle"] is not None + ] + ) assert spend_bundle is not None await farm_transaction(full_node_api, wallet_node, spend_bundle) - tx = await client.get_transaction(transaction_id=tx.name) - assert tx.confirmed + for tx_json in resp_client["transactions"]: + tx = TransactionRecord.from_json_dict_convenience(tx_json) + tx = await client.get_transaction(transaction_id=tx.name) + assert tx.confirmed @pytest.mark.anyio diff --git a/chia/rpc/wallet_rpc_api.py b/chia/rpc/wallet_rpc_api.py index 39b4598a4744..c532b3e46819 100644 --- a/chia/rpc/wallet_rpc_api.py +++ b/chia/rpc/wallet_rpc_api.py @@ -34,7 +34,7 @@ from chia.server.outbound_message import NodeType from chia.server.ws_connection import WSChiaConnection from chia.types.blockchain_format.coin import Coin, coin_as_list -from chia.types.blockchain_format.program import Program +from chia.types.blockchain_format.program import INFINITE_COST, Program from chia.types.blockchain_format.sized_bytes import bytes32 from chia.types.coin_record import CoinRecord from chia.types.coin_spend import CoinSpend @@ -61,6 +61,7 @@ Condition, CreateCoinAnnouncement, CreatePuzzleAnnouncement, + parse_conditions_non_consensus, ) from chia.wallet.dao_wallet.dao_info import DAORules from chia.wallet.dao_wallet.dao_utils import ( @@ -616,21 +617,68 @@ async def push_tx(self, request: Dict[str, Any]) -> EndpointResult: await self.service.push_tx(SpendBundle.from_bytes(hexstr_to_bytes(request["spend_bundle"]))) return {} - async def push_transactions(self, request: Dict[str, Any]) -> EndpointResult: - txs: List[TransactionRecord] = [] - for transaction_hexstr_or_json in request["transactions"]: - if isinstance(transaction_hexstr_or_json, str): - tx = TransactionRecord.from_bytes(hexstr_to_bytes(transaction_hexstr_or_json)) - txs.append(tx) - else: - try: - tx = TransactionRecord.from_json_dict_convenience(transaction_hexstr_or_json) - except AttributeError: - tx = TransactionRecord.from_json_dict(transaction_hexstr_or_json) - txs.append(tx) + @tx_endpoint(push=True) + async def push_transactions( + self, + request: Dict[str, Any], + action_scope: WalletActionScope, + tx_config: TXConfig = DEFAULT_TX_CONFIG, + extra_conditions: Tuple[Condition, ...] = tuple(), + ) -> EndpointResult: + if not action_scope.config.push: + raise ValueError("Cannot push transactions if push is False") + async with action_scope.use() as interface: + for transaction_hexstr_or_json in request["transactions"]: + if isinstance(transaction_hexstr_or_json, str): + tx = TransactionRecord.from_bytes(hexstr_to_bytes(transaction_hexstr_or_json)) + interface.side_effects.transactions.append(tx) + else: + try: + tx = TransactionRecord.from_json_dict_convenience(transaction_hexstr_or_json) + except AttributeError: + tx = TransactionRecord.from_json_dict(transaction_hexstr_or_json) + interface.side_effects.transactions.append(tx) + + if request.get("fee", 0) != 0: + all_conditions_and_origins = [ + (condition, cs.coin.name()) + for tx in interface.side_effects.transactions + if tx.spend_bundle is not None + for cs in tx.spend_bundle.coin_spends + for condition in cs.puzzle_reveal.run_with_cost(INFINITE_COST, cs.solution)[1].as_iter() + ] + create_coin_announcement = next( + condition + for condition in parse_conditions_non_consensus( + [con for con, coin in all_conditions_and_origins], abstractions=False + ) + if isinstance(condition, CreateCoinAnnouncement) + ) + announcement_origin = next( + coin + for condition, coin in all_conditions_and_origins + if condition == create_coin_announcement.to_program() + ) + async with self.service.wallet_state_manager.new_action_scope(push=False) as inner_action_scope: + await self.service.wallet_state_manager.main_wallet.create_tandem_xch_tx( + uint64(request["fee"]), + dataclasses.replace( + tx_config, + excluded_coin_ids=[ + *tx_config.excluded_coin_ids, + *(c.name() for tx in interface.side_effects.transactions for c in tx.removals), + ], + ), + inner_action_scope, + ( + *extra_conditions, + CreateCoinAnnouncement( + create_coin_announcement.msg, announcement_origin + ).corresponding_assertion(), + ), + ) - async with self.service.wallet_state_manager.lock: - await self.service.wallet_state_manager.add_pending_transactions(txs, sign=request.get("sign", False)) + interface.side_effects.transactions.extend(inner_action_scope.side_effects.transactions) return {} diff --git a/chia/rpc/wallet_rpc_client.py b/chia/rpc/wallet_rpc_client.py index f13aa196eaea..a574ddf080b2 100644 --- a/chia/rpc/wallet_rpc_client.py +++ b/chia/rpc/wallet_rpc_client.py @@ -148,10 +148,12 @@ async def get_height_info(self) -> uint32: async def push_tx(self, spend_bundle: SpendBundle) -> Dict[str, Any]: return await self.fetch("push_tx", {"spend_bundle": bytes(spend_bundle).hex()}) - async def push_transactions(self, txs: List[TransactionRecord], sign: bool = False) -> Dict[str, Any]: + async def push_transactions( + self, txs: List[TransactionRecord], fee: uint64 = uint64(0), sign: bool = False + ) -> Dict[str, Any]: transactions = [bytes(tx).hex() for tx in txs] - return await self.fetch("push_transactions", {"transactions": transactions, "sign": sign}) + return await self.fetch("push_transactions", {"transactions": transactions, "fee": fee, "sign": sign}) async def farm_block(self, address: str) -> Dict[str, Any]: return await self.fetch("farm_block", {"address": address})