Skip to content

Commit

Permalink
move some non-framework items out of chia.cmds.cmd_classes (#19047)
Browse files Browse the repository at this point in the history
* move some non-framework items out of `chia.cmds.cmd_classes`

* move the other new stuff too
  • Loading branch information
altendky authored Dec 17, 2024
1 parent 61207d2 commit 45465b4
Show file tree
Hide file tree
Showing 13 changed files with 261 additions and 252 deletions.
7 changes: 2 additions & 5 deletions chia/_tests/cmds/test_cmd_framework.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,14 @@

from chia._tests.environments.wallet import STANDARD_TX_ENDPOINT_ARGS, WalletTestFramework
from chia._tests.wallet.conftest import * # noqa
from chia.cmds.cmd_classes import (
from chia.cmds.cmd_classes import ChiaCommand, Context, chia_command, option
from chia.cmds.cmd_helpers import (
_TRANSACTION_ENDPOINT_DECORATOR_APPLIED,
ChiaCommand,
Context,
NeedsCoinSelectionConfig,
NeedsTXConfig,
NeedsWalletRPC,
TransactionEndpoint,
TransactionEndpointWithTimelocks,
chia_command,
option,
transaction_endpoint_runner,
)
from chia.cmds.cmds_util import coin_selection_args, tx_config_args, tx_out_cmd
Expand Down
2 changes: 1 addition & 1 deletion chia/_tests/environments/wallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from typing import TYPE_CHECKING, Any, ClassVar, Union, cast

from chia._tests.environments.common import ServiceEnvironment
from chia.cmds.cmd_classes import NeedsTXConfig, NeedsWalletRPC, TransactionEndpoint, TransactionsOut, WalletClientInfo
from chia.cmds.cmd_helpers import NeedsTXConfig, NeedsWalletRPC, TransactionEndpoint, TransactionsOut, WalletClientInfo
from chia.cmds.param_types import CliAmount, cli_amount_none
from chia.rpc.full_node_rpc_client import FullNodeRpcClient
from chia.rpc.rpc_server import RpcServer
Expand Down
2 changes: 1 addition & 1 deletion chia/_tests/pools/test_pool_cli_parsing.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from __future__ import annotations

from chia._tests.cmds.test_cmd_framework import check_click_parsing
from chia.cmds.cmd_classes import NeedsWalletRPC
from chia.cmds.cmd_helpers import NeedsWalletRPC
from chia.cmds.param_types import CliAddress
from chia.cmds.plotnft import (
ChangePayoutInstructionsPlotNFTCMD,
Expand Down
2 changes: 1 addition & 1 deletion chia/_tests/pools/test_pool_cmdline.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
verify_pool_state,
)
from chia._tests.util.misc import Marks, boolean_datacases, datacases
from chia.cmds.cmd_classes import NeedsWalletRPC, WalletClientInfo
from chia.cmds.cmd_helpers import NeedsWalletRPC, WalletClientInfo
from chia.cmds.param_types import CliAddress
from chia.cmds.plotnft import (
ChangePayoutInstructionsPlotNFTCMD,
Expand Down
2 changes: 1 addition & 1 deletion chia/_tests/wallet/test_coin_management.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

from chia._tests.cmds.test_cmd_framework import check_click_parsing
from chia._tests.environments.wallet import STANDARD_TX_ENDPOINT_ARGS, WalletStateTransition, WalletTestFramework
from chia.cmds.cmd_classes import NeedsCoinSelectionConfig, NeedsWalletRPC, WalletClientInfo
from chia.cmds.cmd_helpers import NeedsCoinSelectionConfig, NeedsWalletRPC, WalletClientInfo
from chia.cmds.coins import CombineCMD, ListCMD, SplitCMD
from chia.cmds.param_types import CliAmount, cli_amount_none
from chia.rpc.wallet_request_types import GetSyncStatusResponse
Expand Down
3 changes: 2 additions & 1 deletion chia/_tests/wallet/test_signer_protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
from chia._tests.cmds.test_cmd_framework import check_click_parsing
from chia._tests.cmds.wallet.test_consts import STD_TX
from chia._tests.environments.wallet import WalletStateTransition, WalletTestFramework
from chia.cmds.cmd_classes import NeedsWalletRPC, TransactionsIn, TransactionsOut, WalletClientInfo, chia_command
from chia.cmds.cmd_classes import chia_command
from chia.cmds.cmd_helpers import NeedsWalletRPC, TransactionsIn, TransactionsOut, WalletClientInfo
from chia.cmds.cmds_util import TransactionBundle
from chia.cmds.signer import (
ApplySignaturesCMD,
Expand Down
237 changes: 1 addition & 236 deletions chia/cmds/cmd_classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,15 @@
import collections
import inspect
import sys
from collections.abc import AsyncIterator, Coroutine, Sequence
from contextlib import asynccontextmanager
from dataclasses import MISSING, dataclass, field, fields
from functools import cached_property
from pathlib import Path
from typing import Any, Callable, Optional, Protocol, TypeVar, Union, get_args, get_origin, get_type_hints
from typing import Any, Callable, Optional, Protocol, Union, get_args, get_origin, get_type_hints

import click
from typing_extensions import dataclass_transform

from chia.cmds.cmds_util import CMDCoinSelectionConfigLoader, CMDTXConfigLoader, TransactionBundle, get_wallet_client
from chia.cmds.param_types import AmountParamType, Bytes32ParamType, CliAmount, TransactionFeeParamType, cli_amount_none
from chia.rpc.wallet_rpc_client import WalletRpcClient
from chia.types.blockchain_format.sized_bytes import bytes32
from chia.util.byte_types import hexstr_to_bytes
from chia.util.ints import uint64
from chia.util.streamable import is_type_SpecificOptional
from chia.wallet.conditions import ConditionValidTimes
from chia.wallet.transaction_record import TransactionRecord
from chia.wallet.util.tx_config import CoinSelectionConfig, TXConfig

SyncCmd = Callable[..., None]

Expand Down Expand Up @@ -298,227 +287,3 @@ def command_helper(cls: type[Any]) -> type[Any]:


Context = dict[str, Any]


@dataclass(frozen=True)
class WalletClientInfo:
client: WalletRpcClient
fingerprint: int
config: dict[str, Any]


@command_helper
class NeedsWalletRPC:
context: Context = field(default_factory=dict)
client_info: Optional[WalletClientInfo] = None
wallet_rpc_port: Optional[int] = option(
"-wp",
"--wallet-rpc_port",
help=(
"Set the port where the Wallet is hosting the RPC interface."
"See the rpc_port under wallet in config.yaml."
),
type=int,
default=None,
)
fingerprint: Optional[int] = option(
"-f",
"--fingerprint",
help="Fingerprint of the wallet to use",
type=int,
default=None,
)

@asynccontextmanager
async def wallet_rpc(self, **kwargs: Any) -> AsyncIterator[WalletClientInfo]:
if self.client_info is not None:
yield self.client_info
else:
root_path = kwargs.get("root_path", self.context["root_path"])
async with get_wallet_client(root_path, self.wallet_rpc_port, self.fingerprint, **kwargs) as (
wallet_client,
fp,
config,
):
yield WalletClientInfo(wallet_client, fp, config)


@command_helper
class TransactionsIn:
transaction_file_in: str = option(
"--transaction-file-in",
type=str,
help="Transaction file to use as input",
required=True,
)

@cached_property
def transaction_bundle(self) -> TransactionBundle:
with open(Path(self.transaction_file_in), "rb") as file:
return TransactionBundle.from_bytes(file.read())


@command_helper
class TransactionsOut:
transaction_file_out: Optional[str] = option(
"--transaction-file-out",
type=str,
default=None,
help="A file to write relevant transactions to",
required=False,
)

def handle_transaction_output(self, output: list[TransactionRecord]) -> None:
if self.transaction_file_out is None:
return
else:
with open(Path(self.transaction_file_out), "wb") as file:
file.write(bytes(TransactionBundle(output)))


@command_helper
class NeedsCoinSelectionConfig:
min_coin_amount: CliAmount = option(
"-ma",
"--min-coin-amount",
"--min-amount",
help="Ignore coins worth less then this much XCH or CAT units",
type=AmountParamType(),
required=False,
default=cli_amount_none,
)
max_coin_amount: CliAmount = option(
"-l",
"--max-coin-amount",
"--max-amount",
help="Ignore coins worth more then this much XCH or CAT units",
type=AmountParamType(),
required=False,
default=cli_amount_none,
)
coins_to_exclude: Sequence[bytes32] = option(
"--exclude-coin",
multiple=True,
type=Bytes32ParamType(),
help="Exclude this coin from being spent.",
)
amounts_to_exclude: Sequence[CliAmount] = option(
"--exclude-amount",
multiple=True,
type=AmountParamType(),
help="Exclude any coins with this XCH or CAT amount from being included.",
)

def load_coin_selection_config(self, mojo_per_unit: int) -> CoinSelectionConfig:
return CMDCoinSelectionConfigLoader(
min_coin_amount=self.min_coin_amount,
max_coin_amount=self.max_coin_amount,
excluded_coin_amounts=list(_ for _ in self.amounts_to_exclude),
excluded_coin_ids=list(_ for _ in self.coins_to_exclude),
).to_coin_selection_config(mojo_per_unit)


@command_helper
class NeedsTXConfig(NeedsCoinSelectionConfig):
reuse: Optional[bool] = option(
"--reuse/--new-address",
"--reuse-puzhash/--generate-new-puzhash",
help="Reuse existing address for the change.",
is_flag=True,
default=None,
)

def load_tx_config(self, mojo_per_unit: int, config: dict[str, Any], fingerprint: int) -> TXConfig:
return CMDTXConfigLoader(
min_coin_amount=self.min_coin_amount,
max_coin_amount=self.max_coin_amount,
excluded_coin_amounts=list(_ for _ in self.amounts_to_exclude),
excluded_coin_ids=list(_ for _ in self.coins_to_exclude),
reuse_puzhash=self.reuse,
).to_tx_config(mojo_per_unit, config, fingerprint)


def transaction_endpoint_runner(
func: Callable[[_T_TransactionEndpoint], Coroutine[Any, Any, list[TransactionRecord]]],
) -> Callable[[_T_TransactionEndpoint], Coroutine[Any, Any, None]]:
async def wrapped_func(self: _T_TransactionEndpoint) -> None:
txs = await func(self)
self.transaction_writer.handle_transaction_output(txs)

setattr(wrapped_func, _TRANSACTION_ENDPOINT_DECORATOR_APPLIED, True)
return wrapped_func


_TRANSACTION_ENDPOINT_DECORATOR_APPLIED = (
f"_{__name__.replace('.', '_')}_{transaction_endpoint_runner.__qualname__}_applied"
)


@dataclass(frozen=True)
class TransactionEndpoint:
rpc_info: NeedsWalletRPC
tx_config_loader: NeedsTXConfig
transaction_writer: TransactionsOut
fee: uint64 = option(
"-m",
"--fee",
help="Set the fees for the transaction, in XCH",
type=TransactionFeeParamType(),
default="0",
show_default=True,
required=True,
)
push: bool = option(
"--push/--no-push", help="Push the transaction to the network", type=bool, is_flag=True, default=True
)
valid_at: Optional[int] = option(
"--valid-at",
help="UNIX timestamp at which the associated transactions become valid",
type=int,
required=False,
default=None,
hidden=True,
)
expires_at: Optional[int] = option(
"--expires-at",
help="UNIX timestamp at which the associated transactions expire",
type=int,
required=False,
default=None,
hidden=True,
)

def __post_init__(self) -> None:
if not hasattr(self.run, _TRANSACTION_ENDPOINT_DECORATOR_APPLIED):
raise TypeError("TransactionEndpoints must utilize @transaction_endpoint_runner on their `run` method")

def load_condition_valid_times(self) -> ConditionValidTimes:
return ConditionValidTimes(
min_time=uint64.construct_optional(self.valid_at),
max_time=uint64.construct_optional(self.expires_at),
)

@transaction_endpoint_runner
async def run(self) -> list[TransactionRecord]:
raise NotImplementedError("Must implement `.run()` on a TransactionEndpoint subclass") # pragma: no cover


@dataclass(frozen=True)
class TransactionEndpointWithTimelocks(TransactionEndpoint):
valid_at: Optional[int] = option(
"--valid-at",
help="UNIX timestamp at which the associated transactions become valid",
type=int,
required=False,
default=None,
)
expires_at: Optional[int] = option(
"--expires-at",
help="UNIX timestamp at which the associated transactions expire",
type=int,
required=False,
default=None,
)


_T_TransactionEndpoint = TypeVar("_T_TransactionEndpoint", bound=TransactionEndpoint)
Loading

0 comments on commit 45465b4

Please sign in to comment.