diff --git a/build_scripts/daemon.spec b/build_scripts/daemon.spec index cff4d45f..03a1253c 100644 --- a/build_scripts/daemon.spec +++ b/build_scripts/daemon.spec @@ -34,6 +34,7 @@ SUBCOMMANDS = [ "version", "netspace", "run_daemon", + "wallet", ] block_cipher = None subcommand_modules = [f"{root}/src.cmds.%s" % _ for _ in SUBCOMMANDS] diff --git a/build_scripts/daemon_windows.spec b/build_scripts/daemon_windows.spec index 22a21175..5c36596e 100644 --- a/build_scripts/daemon_windows.spec +++ b/build_scripts/daemon_windows.spec @@ -31,6 +31,7 @@ SUBCOMMANDS = [ "version", "netspace", "run_daemon", + "wallet", ] block_cipher = None subcommand_modules = [f"../src.cmds.%s" % _ for _ in SUBCOMMANDS] diff --git a/src/cmds/chia.py b/src/cmds/chia.py index 10fa1936..08646add 100644 --- a/src/cmds/chia.py +++ b/src/cmds/chia.py @@ -16,6 +16,7 @@ SUBCOMMANDS = [ "plots", "netspace", "run_daemon", + "wallet", ] diff --git a/src/cmds/show.py b/src/cmds/show.py index 58bf8312..1bc28de2 100644 --- a/src/cmds/show.py +++ b/src/cmds/show.py @@ -101,15 +101,6 @@ def make_parser(parser): default=9256, ) - parser.add_argument( - "-w", - "--wallet-balances", - help="Display wallet balances.", - type=str2bool, - nargs="?", - const=True, - default=False, - ) parser.set_defaults(function=show) @@ -313,145 +304,6 @@ async def show_async(args, parser): else: print("Block with header hash", args.block_by_header_hash, "not found.") - if args.wallet_balances: - if "wallet_rpc_port" not in args or args.wallet_rpc_port is None: - wallet_rpc_port = config["wallet"]["rpc_port"] - else: - wallet_rpc_port = args.wallet_rpc_port - wallet_client = await WalletRpcClient.create(self_hostname, wallet_rpc_port) - fingerprints = await wallet_client.get_public_keys() - if len(fingerprints) == 0: - print("Error, no keys loaded") - wallet_client.close() - await wallet_client.await_closed() - client.close() - await client.await_closed() - return - fingerprint = None - if len(fingerprints) == 1: - fingerprint = fingerprints[0] - log_in_response = await wallet_client.log_in(fingerprint) - else: - print("Choose wallet key:") - for i, fp in enumerate(fingerprints): - print(f"{i+1}) {fp}") - val = None - while val is None: - val = input("Enter a number to pick or q to quit: ") - if val == "q": - return - if not val.isdigit(): - val = None - else: - index = int(val) - 1 - if index >= len(fingerprints): - print("Invalid value") - val = None - continue - else: - fingerprint = fingerprints[index][0] - log_in_response = await wallet_client.log_in(fingerprint) - - if log_in_response["success"] is False: - if log_in_response["error"] == "not_initialized": - use_cloud = True - if "backup_path" in log_in_response: - path = log_in_response["backup_path"] - print( - f"Backup file from backup.chia.net downloaded and written to: {path}" - ) - val = input( - "Do you want to use this file to restore from backup? (Y/N) " - ) - if val.lower() == "y": - log_in_response = await wallet_client.log_in_and_restore( - fingerprint, path - ) - else: - use_cloud = False - - if "backup_path" not in log_in_response or use_cloud is False: - if use_cloud is True: - val = input( - "No online backup file found, \n Press S to skip restore from backup" - " \n Press F to use your own backup file: " - ) - else: - val = input( - "Cloud backup declined, \n Press S to skip restore from backup" - " \n Press F to use your own backup file: " - ) - - if val.lower() == "s": - log_in_response = await wallet_client.log_in_and_skip( - fingerprint - ) - elif val.lower() == "f": - val = input( - "Please provide the full path to your backup file: " - ) - log_in_response = await wallet_client.log_in_and_restore( - fingerprint, val - ) - - if "success" not in log_in_response or log_in_response["success"] is False: - if "error" in log_in_response: - error = log_in_response["error"] - print(f"Error: {log_in_response[error]}") - - summaries_response = await wallet_client.get_wallets() - if "wallet_summaries" not in summaries_response: - print("Wallet summary cannot be displayed") - else: - print("Balances") - for wallet_id, summary in summaries_response[ - "wallet_summaries" - ].items(): - balances_response = await wallet_client.get_wallet_balance( - wallet_id - ) - if "wallet_balance" not in balances_response: - print("Balances cannot be displayed") - continue - balances = balances_response["wallet_balance"] - typ = WalletType(int(summary["type"])).name - if "name" in summary: - print(f"Wallet ID {wallet_id} type {typ} {summary['name']}") - print( - f" -Confirmed: {balances['confirmed_wallet_balance']/units['colouredcoin']}" - ) - print( - f" -Unconfirmed: {balances['unconfirmed_wallet_balance']/units['colouredcoin']}" - ) - print( - f" -Spendable: {balances['spendable_balance']/units['colouredcoin']}" - ) - print( - f" -Frozen: {balances['frozen_balance']/units['colouredcoin']}" - ) - print( - f" -Pending change: {balances['pending_change']/units['colouredcoin']}" - ) - else: - print(f"Wallet ID {wallet_id} type {typ}") - print( - f" -Confirmed: {balances['confirmed_wallet_balance']/units['chia']} TXCH" - ) - print( - f" -Unconfirmed: {balances['unconfirmed_wallet_balance']/units['chia']} TXCH" - ) - print( - f" -Spendable: {balances['spendable_balance']/units['chia']} TXCH" - ) - print( - f" -Frozen: {balances['frozen_balance']/units['chia']} TXCH" - ) - print( - f" -Pending change: {balances['pending_change']/units['chia']} TXCH" - ) - wallet_client.close() - await wallet_client.await_closed() - except Exception as e: if isinstance(e, aiohttp.client_exceptions.ClientConnectorError): print(f"Connection error. Check if full node is running at {args.rpc_port}") diff --git a/src/cmds/wallet.py b/src/cmds/wallet.py new file mode 100644 index 00000000..712a7403 --- /dev/null +++ b/src/cmds/wallet.py @@ -0,0 +1,189 @@ +import aiohttp +import asyncio +import time +from time import struct_time, localtime + +from typing import List, Optional + +from src.server.connection import NodeType +from src.types.header_block import HeaderBlock +from src.rpc.full_node_rpc_client import FullNodeRpcClient +from src.rpc.wallet_rpc_client import WalletRpcClient +from src.util.byte_types import hexstr_to_bytes +from src.util.config import str2bool +from src.util.config import load_config +from src.util.default_root import DEFAULT_ROOT_PATH +from src.wallet.util.wallet_types import WalletType +from src.cmds.units import units +from src.util.chech32 import encode_puzzle_hash + + +def make_parser(parser): + parser.add_argument( + "-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." + + "Defaults to 9256", + type=int, + default=9256, + ) + parser.set_defaults(function=show) + +async def print_balances(wallet_client): + summaries_response = await wallet_client.get_wallets() + if "wallet_summaries" not in summaries_response: + print("Wallet summary cannot be displayed") + else: + print("Balances") + for wallet_id, summary in summaries_response[ + "wallet_summaries" + ].items(): + balances_response = await wallet_client.get_wallet_balance( + wallet_id + ) + if "wallet_balance" not in balances_response: + print("Balances cannot be displayed") + continue + balances = balances_response["wallet_balance"] + typ = WalletType(int(summary["type"])).name + if "name" in summary: + print(f"Wallet ID {wallet_id} type {typ} {summary['name']}") + print( + f" -Confirmed: {balances['confirmed_wallet_balance']/units['colouredcoin']}" + ) + print( + f" -Unconfirmed: {balances['unconfirmed_wallet_balance']/units['colouredcoin']}" + ) + print( + f" -Spendable: {balances['spendable_balance']/units['colouredcoin']}" + ) + print( + f" -Frozen: {balances['frozen_balance']/units['colouredcoin']}" + ) + print( + f" -Pending change: {balances['pending_change']/units['colouredcoin']}" + ) + else: + print(f"Wallet ID {wallet_id} type {typ}") + print( + f" -Confirmed: {balances['confirmed_wallet_balance']/units['chia']} TXCH" + ) + print( + f" -Unconfirmed: {balances['unconfirmed_wallet_balance']/units['chia']} TXCH" + ) + print( + f" -Spendable: {balances['spendable_balance']/units['chia']} TXCH" + ) + print( + f" -Frozen: {balances['frozen_balance']/units['chia']} TXCH" + ) + print( + f" -Pending change: {balances['pending_change']/units['chia']} TXCH" + ) + +async def wallet_loop(wallet_client): + fingerprint = None + while True: + # if fingerprint is None: + fingerprints = await wallet_client.get_public_keys() + if len(fingerprints) == 0: + print("No keys loaded. Run 'chia keys generate' or import a key.") + return + if len(fingerprints) == 1: + fingerprint = fingerprints[0] + log_in_response = await wallet_client.log_in(fingerprint) + else: + print("Choose wallet key:") + for i, fp in enumerate(fingerprints): + print(f"{i+1}) {fp}") + val = None + while val is None: + val = input("Enter number to pick press q to quite: ") + if val == "q": + return + if not val.isdigit(): + val = None + else: + index = int(val) - 1 + if index >= len(fingerprints): + print("Invalid value") + val = None + continue + else: + fingerprint = fingerprints[index][0] + log_in_response = await wallet_client.log_in(fingerprint) + + if log_in_response["success"] is False: + if log_in_response["error"] == "not_initialized": + use_cloud = True + if "backup_path" in log_in_response: + path = log_in_response["backup_path"] + print( + f"Backup file from backup.chia.net downloaded and written to: {path}" + ) + val = input( + "Do you want to use this file to restore from backup? (Y/N) " + ) + if val.lower() == "y": + log_in_response = await wallet_client.log_in_and_restore( + fingerprint, path + ) + else: + use_cloud = False + + if "backup_path" not in log_in_response or use_cloud is False: + if use_cloud is True: + val = input( + "No online backup file found, \n Press S to skip restore from backup" + " \n Press F to use your own backup file: " + ) + else: + val = input( + "Cloud backup declined, \n Press S to skip restore from backup" + " \n Press F to use your own backup file: " + ) + + if val.lower() == "s": + log_in_response = await wallet_client.log_in_and_skip( + fingerprint + ) + elif val.lower() == "f": + val = input( + "Please provide the full path to your backup file: " + ) + log_in_response = await wallet_client.log_in_and_restore( + fingerprint, val + ) + + if "success" not in log_in_response or log_in_response["success"] is False: + if "error" in log_in_response: + error = log_in_response["error"] + print(f"Error: {log_in_response[error]}") + return + print_balances(wallet_client) + + +async def show_async(args, parser): + try: + config = load_config(DEFAULT_ROOT_PATH, "config.yaml") + self_hostname = config["self_hostname"] + if "wallet_rpc_port" not in args or args.wallet_rpc_port is None: + wallet_rpc_port = config["wallet"]["rpc_port"] + else: + wallet_rpc_port = args.wallet_rpc_port + wallet_client = await WalletRpcClient.create(self_hostname, wallet_rpc_port) + await wallet_loop(wallet_client) + + except Exception as e: + if isinstance(e, aiohttp.client_exceptions.ClientConnectorError): + print(f"Connection error. Check if wallet is running at {args.wallet_rpc_port}") + else: + print(f"Exception from 'wallet' {e}") + + wallet_client.close() + await wallet_client.await_closed() + + +def show(args, parser): + return asyncio.run(show_async(args, parser)) diff --git a/tests/wallet/test_backup.py b/tests/wallet/test_backup.py index 758530a1..ced13075 100644 --- a/tests/wallet/test_backup.py +++ b/tests/wallet/test_backup.py @@ -66,10 +66,11 @@ class TestCCWalletBackup: await wallet_node.wallet_state_manager.create_wallet_backup(file_path) # Close wallet and restart + db_path = wallet_node.wallet_state_manager.db_path wallet_node._close() await wallet_node._await_closed() - wallet_node.wallet_state_manager.db_path.unlink() + db_path.unlink() started = await wallet_node._start() assert started is False