Skip to content

Commit

Permalink
Port send_transaction
Browse files Browse the repository at this point in the history
  • Loading branch information
Quexington committed Nov 1, 2024
1 parent 80b96fd commit 66443fd
Show file tree
Hide file tree
Showing 7 changed files with 177 additions and 92 deletions.
9 changes: 7 additions & 2 deletions chia/_tests/pools/test_pool_rpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
from chia.consensus.constants import ConsensusConstants
from chia.pools.pool_puzzles import SINGLETON_LAUNCHER_HASH
from chia.pools.pool_wallet_info import PoolSingletonState, PoolWalletInfo
from chia.rpc.wallet_request_types import GetTransactions, GetWalletBalance, GetWallets
from chia.rpc.wallet_request_types import GetTransactions, GetWalletBalance, GetWallets, SendTransaction
from chia.rpc.wallet_rpc_client import WalletRpcClient
from chia.simulator.block_tools import BlockTools, get_plot_dir
from chia.simulator.full_node_simulator import FullNodeSimulator
Expand Down Expand Up @@ -469,7 +469,12 @@ async def test_absorb_self(

tr: TransactionRecord = (
await client.send_transaction(
1, uint64(100), encode_puzzle_hash(status.p2_singleton_puzzle_hash, "txch"), DEFAULT_TX_CONFIG
SendTransaction(
wallet_id=uint32(1),
amount=uint64(100),
address=encode_puzzle_hash(status.p2_singleton_puzzle_hash, "txch"),
),
DEFAULT_TX_CONFIG,
)
).transaction

Expand Down
8 changes: 6 additions & 2 deletions chia/_tests/wallet/cat_wallet/test_cat_wallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from chia._tests.environments.wallet import WalletEnvironment, WalletStateTransition, WalletTestFramework
from chia._tests.util.time_out_assert import time_out_assert, time_out_assert_not_none
from chia.protocols.wallet_protocol import CoinState
from chia.rpc.wallet_request_types import GetTransactionMemo, PushTX
from chia.rpc.wallet_request_types import GetTransactionMemo, PushTX, SendTransaction
from chia.simulator.simulator_protocol import ReorgProtocol
from chia.types.blockchain_format.coin import Coin, coin_as_list
from chia.types.blockchain_format.program import Program
Expand Down Expand Up @@ -1478,7 +1478,11 @@ async def test_cat_change_detection(wallet_environments: WalletTestFramework) ->
cat_amount_0 = uint64(100)
cat_amount_1 = uint64(5)

tx = (await env.rpc_client.send_transaction(1, cat_amount_0, addr, wallet_environments.tx_config)).transaction
tx = (
await env.rpc_client.send_transaction(
SendTransaction(wallet_id=uint32(1), amount=cat_amount_0, address=addr), wallet_environments.tx_config
)
).transaction
spend_bundle = tx.spend_bundle
assert spend_bundle is not None

Expand Down
91 changes: 63 additions & 28 deletions chia/_tests/wallet/rpc/test_wallet_rpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@
LogIn,
PushTransactions,
PushTX,
PuzzleDecoratorData,
SendTransaction,
SetWalletResyncOnStartup,
SplitCoins,
SplitCoinsResponse,
Expand Down Expand Up @@ -114,6 +116,7 @@
from chia.wallet.util.blind_signer_tl import BLIND_SIGNER_TRANSLATION
from chia.wallet.util.clvm_streamable import byte_deserialize_clvm_streamable
from chia.wallet.util.compute_memos import compute_memos
from chia.wallet.util.puzzle_decorator_type import PuzzleDecoratorType
from chia.wallet.util.query_filter import AmountFilter, HashFilter, TransactionTypeFilter
from chia.wallet.util.transaction_type import TransactionType
from chia.wallet.util.tx_config import DEFAULT_COIN_SELECTION_CONFIG, DEFAULT_TX_CONFIG
Expand Down Expand Up @@ -346,24 +349,28 @@ async def test_send_transaction(wallet_rpc_environment: WalletRpcTestEnvironment
addr = encode_puzzle_hash(await wallet_2.get_new_puzzlehash(), "txch")
tx_amount = uint64(15600000)
with pytest.raises(ValueError):
await client.send_transaction(1, uint64(100000000000000001), addr, DEFAULT_TX_CONFIG)
await client.send_transaction(
SendTransaction(wallet_id=uint32(1), amount=uint64(100000000000000001), address=addr), DEFAULT_TX_CONFIG
)

# Tests sending a basic transaction
extra_conditions = (Remark(Program.to(("test", None))),)
non_existent_coin = Coin(bytes32.zeros, bytes32.zeros, uint64(0))
tx_no_push = (
await client.send_transaction(
1,
tx_amount,
addr,
memos=["this is a basic tx"],
SendTransaction(
wallet_id=uint32(1),
amount=tx_amount,
address=addr,
memos=["this is a basic tx"],
push=False,
),
tx_config=DEFAULT_TX_CONFIG.override(
excluded_coin_amounts=[uint64(250000000000)],
excluded_coin_ids=[non_existent_coin.name()],
reuse_puzhash=True,
),
extra_conditions=extra_conditions,
push=False,
)
).transaction
response = await client.fetch(
Expand Down Expand Up @@ -801,12 +808,14 @@ async def test_spend_clawback_coins(wallet_rpc_environment: WalletRpcTestEnviron
wallet_2_puzhash = await wallet_2.get_new_puzzlehash()
tx = (
await wallet_1_rpc.send_transaction(
wallet_id=1,
amount=uint64(500),
address=encode_puzzle_hash(wallet_2_puzhash, "txch"),
SendTransaction(
wallet_id=uint32(1),
amount=uint64(500),
address=encode_puzzle_hash(wallet_2_puzhash, "txch"),
fee=uint64(0),
puzzle_decorator=[PuzzleDecoratorData(PuzzleDecoratorType.CLAWBACK, {"clawback_timelock": 5})],
),
tx_config=DEFAULT_TX_CONFIG,
fee=uint64(0),
puzzle_decorator_override=[{"decorator": "CLAWBACK", "clawback_timelock": 5}],
)
).transaction
clawback_coin_id_1 = tx.additions[0].name()
Expand All @@ -815,12 +824,14 @@ async def test_spend_clawback_coins(wallet_rpc_environment: WalletRpcTestEnviron
await full_node_api.wait_for_wallet_synced(wallet_node=wallet_2_node, timeout=20)
tx = (
await wallet_2_rpc.send_transaction(
wallet_id=1,
amount=uint64(500),
address=encode_puzzle_hash(wallet_1_puzhash, "txch"),
SendTransaction(
wallet_id=uint32(1),
amount=uint64(500),
address=encode_puzzle_hash(wallet_1_puzhash, "txch"),
fee=uint64(0),
puzzle_decorator=[PuzzleDecoratorData(PuzzleDecoratorType.CLAWBACK, {"clawback_timelock": 5})],
),
tx_config=DEFAULT_TX_CONFIG,
fee=uint64(0),
puzzle_decorator_override=[{"decorator": "CLAWBACK", "clawback_timelock": 5}],
)
).transaction
assert tx.spend_bundle is not None
Expand Down Expand Up @@ -982,7 +993,8 @@ async def test_get_transactions(wallet_rpc_environment: WalletRpcTestEnvironment
puzhash = await wallet.get_new_puzzlehash()
await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20)
await client.send_transaction(
1, uint64(1), encode_puzzle_hash(puzhash, "txch"), DEFAULT_TX_CONFIG
SendTransaction(wallet_id=uint32(1), amount=uint64(1), address=encode_puzzle_hash(puzhash, "txch")),
DEFAULT_TX_CONFIG,
) # Create a pending tx

all_transactions = (
Expand All @@ -1004,7 +1016,10 @@ async def test_get_transactions(wallet_rpc_environment: WalletRpcTestEnvironment
# Test get_transactions to address
ph_by_addr = await wallet.get_new_puzzlehash()
await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20)
await client.send_transaction(1, uint64(1), encode_puzzle_hash(ph_by_addr, "txch"), DEFAULT_TX_CONFIG)
await client.send_transaction(
SendTransaction(wallet_id=uint32(1), amount=uint64(1), address=encode_puzzle_hash(ph_by_addr, "txch")),
DEFAULT_TX_CONFIG,
)
await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20)
tx_for_address = (
await client.get_transactions(GetTransactions(uint32(1), to_address=encode_puzzle_hash(ph_by_addr, "txch")))
Expand Down Expand Up @@ -1493,7 +1508,11 @@ async def test_get_coin_records_by_names(wallet_rpc_environment: WalletRpcTestEn
await full_node_api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20)

# Spend half of it back to the same wallet get some spent coins in the wallet
tx = (await client.send_transaction(1, uint64(generated_funds / 2), address, DEFAULT_TX_CONFIG)).transaction
tx = (
await client.send_transaction(
SendTransaction(wallet_id=uint32(1), amount=uint64(generated_funds / 2), address=address), DEFAULT_TX_CONFIG
)
).transaction
assert tx.spend_bundle is not None
await time_out_assert(20, tx_in_mempool, True, client, tx.name)
await farm_transaction(full_node_api, wallet_node, tx.spend_bundle)
Expand Down Expand Up @@ -1807,7 +1826,11 @@ async def test_key_and_address_endpoints(wallet_rpc_environment: WalletRpcTestEn
addr = encode_puzzle_hash(ph, "txch")
tx_amount = uint64(15600000)
await env.full_node.api.wait_for_wallet_synced(wallet_node=wallet_node, timeout=20)
created_tx = (await client.send_transaction(1, tx_amount, addr, DEFAULT_TX_CONFIG)).transaction
created_tx = (
await client.send_transaction(
SendTransaction(wallet_id=uint32(1), amount=tx_amount, address=addr), DEFAULT_TX_CONFIG
)
).transaction

await time_out_assert(20, tx_in_mempool, True, client, created_tx.name)
assert len(await wallet.wallet_state_manager.tx_store.get_unconfirmed_for_wallet(1)) == 1
Expand Down Expand Up @@ -1870,7 +1893,9 @@ async def test_key_and_address_endpoints(wallet_rpc_environment: WalletRpcTestEn
assert await get_unconfirmed_balance(client, int(wallets[0].id)) == 0

with pytest.raises(ValueError):
await client.send_transaction(wallets[0].id, uint64(100), addr, DEFAULT_TX_CONFIG)
await client.send_transaction(
SendTransaction(wallet_id=uint32(wallets[0].id), amount=uint64(100), address=addr), DEFAULT_TX_CONFIG
)

# Delete all keys
await client.delete_all_keys()
Expand All @@ -1895,7 +1920,11 @@ async def test_select_coins_rpc(wallet_rpc_environment: WalletRpcTestEnvironment
for tx_amount in tx_amounts:
funds -= tx_amount
# create coins for tests
tx = (await client.send_transaction(1, tx_amount, addr, DEFAULT_TX_CONFIG)).transaction
tx = (
await client.send_transaction(
SendTransaction(wallet_id=uint32(1), amount=tx_amount, address=addr), DEFAULT_TX_CONFIG
)
).transaction
spend_bundle = tx.spend_bundle
assert spend_bundle is not None
for coin in spend_bundle.additions():
Expand Down Expand Up @@ -2461,12 +2490,14 @@ async def test_set_wallet_resync_on_startup(wallet_rpc_environment: WalletRpcTes
# Test Clawback resync
tx = (
await wc.send_transaction(
wallet_id=1,
amount=uint64(500),
address=address,
SendTransaction(
wallet_id=uint32(1),
amount=uint64(500),
address=address,
fee=uint64(0),
puzzle_decorator=[PuzzleDecoratorData(PuzzleDecoratorType.CLAWBACK, {"clawback_timelock": 5})],
),
tx_config=DEFAULT_TX_CONFIG,
fee=uint64(0),
puzzle_decorator_override=[{"decorator": "CLAWBACK", "clawback_timelock": 5}],
)
).transaction
clawback_coin_id = tx.additions[0].name()
Expand Down Expand Up @@ -2608,7 +2639,11 @@ async def test_cat_spend_run_tail(wallet_rpc_environment: WalletRpcTestEnvironme
)
tx_amount = uint64(100)

tx = (await client.send_transaction(1, tx_amount, addr, DEFAULT_TX_CONFIG)).transaction
tx = (
await client.send_transaction(
SendTransaction(wallet_id=uint32(1), amount=tx_amount, address=addr), DEFAULT_TX_CONFIG
)
).transaction
transaction_id = tx.name
spend_bundle = tx.spend_bundle
assert spend_bundle is not None
Expand Down
28 changes: 16 additions & 12 deletions chia/cmds/wallet_funcs.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
GetTransactions,
GetWalletBalance,
GetWallets,
PuzzleDecoratorData,
SendTransaction,
SendTransactionResponse,
)
from chia.rpc.wallet_rpc_client import WalletRpcClient
Expand Down Expand Up @@ -328,23 +330,25 @@ async def send(
if typ == WalletType.STANDARD_WALLET:
print("Submitting transaction...")
res: Union[CATSpendResponse, SendTransactionResponse] = await wallet_client.send_transaction(
wallet_id,
final_amount,
address.original_address,
CMDTXConfigLoader(
SendTransaction(
wallet_id=uint32(wallet_id),
amount=final_amount,
address=address.original_address,
fee=fee,
memos=memos,
puzzle_decorator=(
[PuzzleDecoratorData(PuzzleDecoratorType.CLAWBACK, {"clawback_timelock": clawback_time_lock})]
if clawback_time_lock > 0
else None
),
push=push,
),
tx_config=CMDTXConfigLoader(
min_coin_amount=min_coin_amount,
max_coin_amount=max_coin_amount,
excluded_coin_ids=list(excluded_coin_ids),
reuse_puzhash=reuse_puzhash,
).to_tx_config(mojo_per_unit, config, fingerprint),
fee,
memos,
puzzle_decorator_override=(
[{"decorator": PuzzleDecoratorType.CLAWBACK.name, "clawback_timelock": clawback_time_lock}]
if clawback_time_lock > 0
else None
),
push=push,
timelock_info=condition_valid_times,
)
elif typ in {WalletType.CAT, WalletType.CRCAT}:
Expand Down
61 changes: 57 additions & 4 deletions chia/rpc/wallet_request_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from chia.util.byte_types import hexstr_to_bytes
from chia.util.ints import uint16, uint32, uint64
from chia.util.streamable import Streamable, streamable
from chia.wallet.conditions import Condition, ConditionValidTimes
from chia.wallet.conditions import Condition, ConditionValidTimes, conditions_to_json_dicts
from chia.wallet.notification_store import Notification
from chia.wallet.signer_protocol import (
SignedTransaction,
Expand All @@ -26,6 +26,7 @@
from chia.wallet.transaction_record import TransactionRecord
from chia.wallet.transaction_sorting import SortKey
from chia.wallet.util.clvm_streamable import json_deserialize_with_clvm_streamable
from chia.wallet.util.puzzle_decorator_type import PuzzleDecoratorType
from chia.wallet.util.query_filter import TransactionTypeFilter
from chia.wallet.util.tx_config import TXConfig
from chia.wallet.vc_wallet.vc_store import VCRecord
Expand Down Expand Up @@ -704,7 +705,7 @@ def json_serialize_for_transport(
return {
**tx_config.to_json_dict(),
**timelock_info.to_json_dict(),
"extra_conditions": [condition.to_json_dict() for condition in extra_conditions],
"extra_conditions": conditions_to_json_dicts(extra_conditions),
**self.to_json_dict(_avoid_ban=True),
}

Expand Down Expand Up @@ -808,15 +809,67 @@ class NFTTransferBulkResponse(TransactionEndpointResponse):
spend_bundle: WalletSpendBundle


# TODO: The section below needs corresponding request types
# TODO: The section below should be added to the API (currently only for client)
# utility for SendTransaction
class PuzzleDecoratorData:
decorator_name: PuzzleDecoratorType
decorator_information: dict[str, Any]

def __init__(self, decorator_name: PuzzleDecoratorType, decorator_information: dict[str, Any]) -> None:
self.decorator_name = decorator_name
self.decorator_information = decorator_information

def __eq__(self, other: Any) -> bool:
if (
isinstance(other, PuzzleDecoratorData)
and other.decorator_name == self.decorator_name
and other.decorator_information == self.decorator_information
):
return True
else:
return False

def __bytes__(self) -> bytes:
raise NotImplementedError("Should not be serializing this object as bytes, it's only for RPC")

@classmethod
def parse(cls, f: BinaryIO) -> TransactionRecordMetadata:
raise NotImplementedError("Should not be deserializing this object from a stream, it's only for RPC")

def to_json_dict(self) -> dict[str, Any]:
return {
**self.decorator_information,
"decorator_name": self.decorator_name.name,
}

@classmethod
def from_json_dict(cls, json_dict: dict[str, Any]) -> PuzzleDecoratorData:
return PuzzleDecoratorData(
decorator_name=PuzzleDecoratorType.__members__[json_dict["decorator_name"]],
decorator_information={k: v for k, v in json_dict.items() if k != "decorator_name"},
)


@streamable
@kw_only_dataclass
class SendTransaction(TransactionEndpointRequest):
wallet_id: uint32 = field(default_factory=default_raise)
amount: uint64 = field(default_factory=default_raise)
address: str = field(default_factory=default_raise)
memos: Optional[list[str]] = None
puzzle_decorator: Optional[list[PuzzleDecoratorData]] = None


@streamable
@dataclass(frozen=True)
class SendTransactionResponse(TransactionEndpointResponse):
transaction: TransactionRecord
transaction_id: bytes32


# TODO: The section below needs corresponding request types
# TODO: The section below should be added to the API (currently only for client)


@streamable
@dataclass(frozen=True)
class SendTransactionMultiResponse(TransactionEndpointResponse):
Expand Down
Loading

0 comments on commit 66443fd

Please sign in to comment.