Migrating CLI to Click (#1002)
* Migrating CLI to Click * Adding more type annotations * Adding some required to flags * Some extra small fixes * Add click dependency to the setup.py file * Making callable from outside * Manually applying changes from commit f23c45 * Improving type annotations * Adding -h as --help option * Added feedback to add/remove commands for plots * Properly exiting with error code
This commit is contained in:
parent
dd18113d4d
commit
d8410eff83
16 changed files with 660 additions and 915 deletions
1
setup.py
1
setup.py
|
@ -23,6 +23,7 @@ dependencies = [
|
||||||
"setproctitle==1.2.2", # Gives the chia processes readable names
|
"setproctitle==1.2.2", # Gives the chia processes readable names
|
||||||
"sortedcontainers==2.3.0", # For maintaining sorted mempools
|
"sortedcontainers==2.3.0", # For maintaining sorted mempools
|
||||||
"websockets==8.1.0", # For use in wallet RPC and electron UI
|
"websockets==8.1.0", # For use in wallet RPC and electron UI
|
||||||
|
"click==7.1.2", # For the CLI
|
||||||
]
|
]
|
||||||
|
|
||||||
upnp_dependencies = [
|
upnp_dependencies = [
|
||||||
|
|
|
@ -1,62 +1,61 @@
|
||||||
import importlib
|
import asyncio
|
||||||
import pathlib
|
import click
|
||||||
from argparse import Namespace, ArgumentParser
|
from pathlib import Path
|
||||||
|
|
||||||
from src import __version__
|
from src import __version__
|
||||||
from src.util.default_root import DEFAULT_ROOT_PATH
|
from src.util.default_root import DEFAULT_ROOT_PATH
|
||||||
|
from src.daemon.server import async_run_daemon
|
||||||
|
|
||||||
|
from src.cmds.init import init_cmd
|
||||||
|
from src.cmds.keys import keys_cmd
|
||||||
|
from src.cmds.plots import plots_cmd
|
||||||
|
from src.cmds.wallet import wallet_cmd
|
||||||
|
from src.cmds.configure import configure_cmd
|
||||||
|
from src.cmds.show import show_cmd
|
||||||
|
from src.cmds.start import start_cmd
|
||||||
|
from src.cmds.stop import stop_cmd
|
||||||
|
from src.cmds.netspace import netspace_cmd
|
||||||
|
|
||||||
|
|
||||||
SUBCOMMANDS = [
|
CONTEXT_SETTINGS = dict(help_option_names=["-h", "--help"])
|
||||||
"init",
|
|
||||||
"keys",
|
|
||||||
"show",
|
|
||||||
"start",
|
|
||||||
"stop",
|
|
||||||
"version",
|
|
||||||
"plots",
|
|
||||||
"netspace",
|
|
||||||
"run_daemon",
|
|
||||||
"wallet",
|
|
||||||
"configure",
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def create_parser() -> ArgumentParser:
|
@click.group(
|
||||||
parser: ArgumentParser = ArgumentParser(
|
help=f"\n Manage chia blockchain infrastructure ({__version__})\n",
|
||||||
description="Manage chia blockchain infrastructure (%s)." % __version__,
|
epilog="Try 'chia start node', 'chia netspace -d 192', or 'chia show -s'.",
|
||||||
epilog="Try 'chia start node', 'chia netspace -d 192', or 'chia show -s'.",
|
context_settings=CONTEXT_SETTINGS,
|
||||||
)
|
)
|
||||||
|
@click.option("--root-path", default=DEFAULT_ROOT_PATH, help="Config file root.", type=click.Path(), show_default=True)
|
||||||
parser.add_argument(
|
@click.pass_context
|
||||||
"--root-path",
|
def cli(ctx: click.Context, root_path: str) -> None:
|
||||||
help="Config file root (defaults to %s)." % DEFAULT_ROOT_PATH,
|
ctx.ensure_object(dict)
|
||||||
type=pathlib.Path,
|
ctx.obj["root_path"] = Path(root_path)
|
||||||
default=DEFAULT_ROOT_PATH,
|
|
||||||
)
|
|
||||||
|
|
||||||
subparsers = parser.add_subparsers()
|
|
||||||
|
|
||||||
# this magic metaprogramming generalizes:
|
|
||||||
# from src.cmds import version
|
|
||||||
# new_parser = subparsers.add_parser(version)
|
|
||||||
# version.version_parser(new_parser)
|
|
||||||
|
|
||||||
for subcommand in SUBCOMMANDS:
|
|
||||||
mod = importlib.import_module("src.cmds.%s" % subcommand)
|
|
||||||
mod.make_parser(subparsers.add_parser(subcommand)) # type: ignore
|
|
||||||
|
|
||||||
parser.set_defaults(function=lambda args, parser: parser.print_help())
|
|
||||||
return parser
|
|
||||||
|
|
||||||
|
|
||||||
def chia(args: Namespace, parser: ArgumentParser):
|
@cli.command("version", short_help="show version")
|
||||||
return args.function(args, parser)
|
def version_cmd() -> None:
|
||||||
|
print(__version__)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
@cli.command("run_daemon", short_help="runs chia daemon")
|
||||||
parser = create_parser()
|
@click.pass_context
|
||||||
args = parser.parse_args()
|
def run_daemon_cmd(ctx: click.Context) -> None:
|
||||||
return chia(args, parser)
|
asyncio.get_event_loop().run_until_complete(async_run_daemon(ctx.obj["root_path"]))
|
||||||
|
|
||||||
|
|
||||||
|
cli.add_command(keys_cmd)
|
||||||
|
cli.add_command(plots_cmd)
|
||||||
|
cli.add_command(wallet_cmd)
|
||||||
|
cli.add_command(configure_cmd)
|
||||||
|
cli.add_command(init_cmd)
|
||||||
|
cli.add_command(show_cmd)
|
||||||
|
cli.add_command(start_cmd)
|
||||||
|
cli.add_command(stop_cmd)
|
||||||
|
cli.add_command(netspace_cmd)
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
cli() # pylint: disable=no-value-for-parameter
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
@ -1,76 +1,24 @@
|
||||||
|
import click
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
from src.util.config import (
|
from src.util.config import (
|
||||||
load_config,
|
load_config,
|
||||||
save_config,
|
save_config,
|
||||||
)
|
)
|
||||||
from argparse import ArgumentParser
|
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
from src.util.default_root import DEFAULT_ROOT_PATH
|
from src.util.default_root import DEFAULT_ROOT_PATH
|
||||||
from src.util.config import str2bool
|
from src.util.config import str2bool
|
||||||
|
|
||||||
|
|
||||||
def make_parser(parser: ArgumentParser):
|
def configure(root_path: Path, set_node_introducer: str, set_fullnode_port: str, set_log_level: str, enable_upnp: str):
|
||||||
|
|
||||||
parser.add_argument(
|
|
||||||
"--set-node-introducer",
|
|
||||||
help="Set the introducer for node - IP:Port",
|
|
||||||
type=str,
|
|
||||||
nargs="?",
|
|
||||||
default="",
|
|
||||||
)
|
|
||||||
|
|
||||||
parser.add_argument(
|
|
||||||
"--set-fullnode-port",
|
|
||||||
help="Set the port to use for the fullnode",
|
|
||||||
type=str,
|
|
||||||
nargs="?",
|
|
||||||
default="",
|
|
||||||
)
|
|
||||||
|
|
||||||
parser.add_argument(
|
|
||||||
"--set-log-level",
|
|
||||||
"--log-level",
|
|
||||||
"-log-level",
|
|
||||||
help="Set the instance log level, Can be CRITICAL, ERROR, WARNING, INFO, DEBUG, NOTSET",
|
|
||||||
type=str,
|
|
||||||
nargs="?",
|
|
||||||
default="",
|
|
||||||
)
|
|
||||||
|
|
||||||
parser.add_argument(
|
|
||||||
"--enable-upnp",
|
|
||||||
"--upnp",
|
|
||||||
"-upnp",
|
|
||||||
help="Enable or disable uPnP. Can be True or False",
|
|
||||||
type=str,
|
|
||||||
nargs="?",
|
|
||||||
)
|
|
||||||
|
|
||||||
parser.set_defaults(function=configure)
|
|
||||||
|
|
||||||
|
|
||||||
def help_message():
|
|
||||||
print("usage: chia configure -flag")
|
|
||||||
print(
|
|
||||||
"""
|
|
||||||
chia configure [arguments] [inputs]
|
|
||||||
--set-node-introducer [IP:Port] (Set the introducer for node),
|
|
||||||
--set-fullnode-port [Port] (Set the full node default port, useful for beta testing),
|
|
||||||
--set-log-level [LogLevel] (Can be CRITICAL, ERROR, WARNING, INFO, DEBUG, NOTSET),
|
|
||||||
--enable-upnp,
|
|
||||||
--upnp {True,False} (Enable or disable uPnP. Can be True or False)
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def configure(args, parser):
|
|
||||||
config: Dict = load_config(DEFAULT_ROOT_PATH, "config.yaml")
|
config: Dict = load_config(DEFAULT_ROOT_PATH, "config.yaml")
|
||||||
change_made = False
|
change_made = False
|
||||||
if args.set_node_introducer:
|
if set_node_introducer:
|
||||||
try:
|
try:
|
||||||
if args.set_node_introducer.index(":"):
|
if set_node_introducer.index(":"):
|
||||||
host, port = (
|
host, port = (
|
||||||
":".join(args.set_node_introducer.split(":")[:-1]),
|
":".join(set_node_introducer.split(":")[:-1]),
|
||||||
args.set_node_introducer.split(":")[-1],
|
set_node_introducer.split(":")[-1],
|
||||||
)
|
)
|
||||||
config["full_node"]["introducer_peer"]["host"] = host
|
config["full_node"]["introducer_peer"]["host"] = host
|
||||||
config["full_node"]["introducer_peer"]["port"] = int(port)
|
config["full_node"]["introducer_peer"]["port"] = int(port)
|
||||||
|
@ -79,34 +27,52 @@ def configure(args, parser):
|
||||||
change_made = True
|
change_made = True
|
||||||
except ValueError:
|
except ValueError:
|
||||||
print("Node introducer address must be in format [IP:Port]")
|
print("Node introducer address must be in format [IP:Port]")
|
||||||
if args.set_fullnode_port:
|
if set_fullnode_port:
|
||||||
config["full_node"]["port"] = int(args.set_fullnode_port)
|
config["full_node"]["port"] = int(set_fullnode_port)
|
||||||
config["full_node"]["introducer_peer"]["port"] = int(args.set_fullnode_port)
|
config["full_node"]["introducer_peer"]["port"] = int(set_fullnode_port)
|
||||||
config["farmer"]["full_node_peer"]["port"] = int(args.set_fullnode_port)
|
config["farmer"]["full_node_peer"]["port"] = int(set_fullnode_port)
|
||||||
config["timelord"]["full_node_peer"]["port"] = int(args.set_fullnode_port)
|
config["timelord"]["full_node_peer"]["port"] = int(set_fullnode_port)
|
||||||
config["wallet"]["full_node_peer"]["port"] = int(args.set_fullnode_port)
|
config["wallet"]["full_node_peer"]["port"] = int(set_fullnode_port)
|
||||||
config["wallet"]["introducer_peer"]["port"] = int(args.set_fullnode_port)
|
config["wallet"]["introducer_peer"]["port"] = int(set_fullnode_port)
|
||||||
config["introducer"]["port"] = int(args.set_fullnode_port)
|
config["introducer"]["port"] = int(set_fullnode_port)
|
||||||
print("Default full node port updated.")
|
print("Default full node port updated.")
|
||||||
change_made = True
|
change_made = True
|
||||||
if args.set_log_level:
|
if set_log_level:
|
||||||
levels = ["CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG", "NOTSET"]
|
levels = ["CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG", "NOTSET"]
|
||||||
if args.set_log_level in levels:
|
if set_log_level in levels:
|
||||||
config["logging"]["log_level"] = args.set_log_level
|
config["logging"]["log_level"] = set_log_level
|
||||||
print(f"Logging level updated. Check {DEFAULT_ROOT_PATH}/log/debug.log")
|
print(f"Logging level updated. Check {DEFAULT_ROOT_PATH}/log/debug.log")
|
||||||
change_made = True
|
change_made = True
|
||||||
else:
|
else:
|
||||||
print(f"Logging level not updated. Use one of: {levels}")
|
print(f"Logging level not updated. Use one of: {levels}")
|
||||||
if args.enable_upnp is not None:
|
if enable_upnp is not None:
|
||||||
config["full_node"]["enable_upnp"] = str2bool(args.enable_upnp)
|
config["full_node"]["enable_upnp"] = str2bool(enable_upnp)
|
||||||
if str2bool(args.enable_upnp):
|
if str2bool(enable_upnp):
|
||||||
print("uPnP enabled.")
|
print("uPnP enabled.")
|
||||||
else:
|
else:
|
||||||
print("uPnP disabled.")
|
print("uPnP disabled.")
|
||||||
change_made = True
|
change_made = True
|
||||||
if change_made:
|
if change_made:
|
||||||
print("Restart any running chia services for changes to take effect.")
|
print("Restart any running chia services for changes to take effect.")
|
||||||
save_config(args.root_path, "config.yaml", config)
|
save_config(root_path, "config.yaml", config)
|
||||||
else:
|
|
||||||
help_message()
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
@click.command("configure", short_help="modify configuration")
|
||||||
|
@click.option("--set-node-introducer", help="Set the introducer for node - IP:Port.", type=str)
|
||||||
|
@click.option(
|
||||||
|
"--set-fullnode-port",
|
||||||
|
help="Set the port to use for the fullnode, useful for beta testing.",
|
||||||
|
type=str,
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
"--set-log-level",
|
||||||
|
"--log-level",
|
||||||
|
"-log-level",
|
||||||
|
help="Set the instance log level.",
|
||||||
|
type=click.Choice(["CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG", "NOTSET"]),
|
||||||
|
)
|
||||||
|
@click.option("--enable-upnp", "--upnp", "-upnp", help="Enable or disable uPnP.", type=click.Choice(["true", "false"]))
|
||||||
|
@click.pass_context
|
||||||
|
def configure_cmd(ctx, set_node_introducer, set_fullnode_port, set_log_level, enable_upnp):
|
||||||
|
configure(ctx.obj["root_path"], set_node_introducer, set_fullnode_port, set_log_level, enable_upnp)
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
|
import click
|
||||||
from src import __version__
|
from src import __version__
|
||||||
|
from pathlib import Path
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
from argparse import Namespace, ArgumentParser
|
|
||||||
from typing import List, Dict, Any, Tuple
|
from typing import List, Dict, Any, Tuple
|
||||||
|
|
||||||
from src.util.default_root import DEFAULT_ROOT_PATH
|
from src.util.default_root import DEFAULT_ROOT_PATH
|
||||||
from src.util.keychain import Keychain
|
from src.util.keychain import Keychain
|
||||||
|
|
||||||
from src.util.config import unflatten_properties
|
from src.util.config import unflatten_properties
|
||||||
from pathlib import Path
|
|
||||||
from src.consensus.coinbase import create_puzzlehash_for_pk
|
from src.consensus.coinbase import create_puzzlehash_for_pk
|
||||||
from src.util.ints import uint32
|
from src.util.ints import uint32
|
||||||
|
|
||||||
|
@ -31,35 +31,6 @@ private_node_names = {"full_node", "wallet", "farmer", "harvester", "timelord",
|
||||||
public_node_names = {"full_node", "wallet", "farmer", "introducer", "timelord"}
|
public_node_names = {"full_node", "wallet", "farmer", "introducer", "timelord"}
|
||||||
|
|
||||||
|
|
||||||
def help_message():
|
|
||||||
print("usage: chia init")
|
|
||||||
print(
|
|
||||||
"""
|
|
||||||
chia init (migrate previous version configuration to current)
|
|
||||||
chia init -c [directory] (creates new TLS certificates signed by your CA in [directory])
|
|
||||||
Follow these steps to create new certifcates for a remote harvester:
|
|
||||||
- Make a copy of your Farming Machine CA directory: ~/.chia/[version]/config/ssl/ca
|
|
||||||
- Shut down all chia daemon processes with `chia stop all -d`
|
|
||||||
- Run `chia init -c [directory]` on your remote harvester,
|
|
||||||
where [directory] is the the copy of your Farming Machine CA directory
|
|
||||||
- Get more details on remote harvester on Chia wiki:
|
|
||||||
https://github.com/Chia-Network/chia-blockchain/wiki/Farming-on-many-machines
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def make_parser(parser):
|
|
||||||
parser.add_argument(
|
|
||||||
"-c",
|
|
||||||
"--create_certs",
|
|
||||||
help="Create new SSL certificates based on CA in [directory]",
|
|
||||||
type=Path,
|
|
||||||
default=None,
|
|
||||||
)
|
|
||||||
parser.set_defaults(function=init)
|
|
||||||
parser.print_help = lambda self=parser: help_message()
|
|
||||||
|
|
||||||
|
|
||||||
def dict_add_new_default(updated: Dict, default: Dict, do_not_migrate_keys: Dict[str, Any]):
|
def dict_add_new_default(updated: Dict, default: Dict, do_not_migrate_keys: Dict[str, Any]):
|
||||||
for k in do_not_migrate_keys:
|
for k in do_not_migrate_keys:
|
||||||
if k in updated and do_not_migrate_keys[k] == "":
|
if k in updated and do_not_migrate_keys[k] == "":
|
||||||
|
@ -255,24 +226,24 @@ def generate_ssl_for_nodes(ssl_dir: Path, ca_crt: bytes, ca_key: bytes, private:
|
||||||
generate_ca_signed_cert(ca_crt, ca_key, crt_path, key_path)
|
generate_ca_signed_cert(ca_crt, ca_key, crt_path, key_path)
|
||||||
|
|
||||||
|
|
||||||
def init(args: Namespace, parser: ArgumentParser):
|
def init(create_certs: Path, root_path: Path):
|
||||||
if args.create_certs is not None:
|
if create_certs is not None:
|
||||||
if args.root_path.exists():
|
if root_path.exists():
|
||||||
if os.path.isdir(args.create_certs):
|
if os.path.isdir(create_certs):
|
||||||
ca_dir: Path = args.root_path / "config/ssl/ca"
|
ca_dir: Path = root_path / "config/ssl/ca"
|
||||||
if ca_dir.exists():
|
if ca_dir.exists():
|
||||||
print(f"Deleting your OLD CA in {ca_dir}")
|
print(f"Deleting your OLD CA in {ca_dir}")
|
||||||
shutil.rmtree(ca_dir)
|
shutil.rmtree(ca_dir)
|
||||||
print(f"Copying your CA from {args.create_certs} to {ca_dir}")
|
print(f"Copying your CA from {create_certs} to {ca_dir}")
|
||||||
copy_files_rec(args.create_certs, ca_dir)
|
copy_files_rec(create_certs, ca_dir)
|
||||||
create_all_ssl(args.root_path)
|
create_all_ssl(root_path)
|
||||||
else:
|
else:
|
||||||
print(f"** Directory {args.create_certs} does not exist **")
|
print(f"** Directory {create_certs} does not exist **")
|
||||||
else:
|
else:
|
||||||
print(f"** {args.root_path} does not exist **")
|
print(f"** {root_path} does not exist **")
|
||||||
print("** please run `chia init` to migrate or create new config files **")
|
print("** please run `chia init` to migrate or create new config files **")
|
||||||
else:
|
else:
|
||||||
return chia_init(args.root_path)
|
return chia_init(root_path)
|
||||||
|
|
||||||
|
|
||||||
def chia_version_number() -> Tuple[str, str, str, str]:
|
def chia_version_number() -> Tuple[str, str, str, str]:
|
||||||
|
@ -442,5 +413,30 @@ def chia_init(root_path: Path):
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
@click.command("init", short_help="create or migrate to current")
|
||||||
|
@click.option(
|
||||||
|
"--create-certs",
|
||||||
|
"-c",
|
||||||
|
default=None,
|
||||||
|
help="Create new SSL certificates based on CA in [directory]",
|
||||||
|
type=click.Path(),
|
||||||
|
)
|
||||||
|
@click.pass_context
|
||||||
|
def init_cmd(ctx: click.Context, create_certs: str):
|
||||||
|
"""
|
||||||
|
Create a new configuration or migrate from previous versions to current
|
||||||
|
|
||||||
|
\b
|
||||||
|
Follow these steps to create new certifcates for a remote harvester:
|
||||||
|
- Make a copy of your Farming Machine CA directory: ~/.chia/[version]/config/ssl/ca
|
||||||
|
- Shut down all chia daemon processes with `chia stop all -d`
|
||||||
|
- Run `chia init -c [directory]` on your remote harvester,
|
||||||
|
where [directory] is the the copy of your Farming Machine CA directory
|
||||||
|
- Get more details on remote harvester on Chia wiki:
|
||||||
|
https://github.com/Chia-Network/chia-blockchain/wiki/Farming-on-many-machines
|
||||||
|
"""
|
||||||
|
init(Path(create_certs) if create_certs is not None else None, ctx.obj["root_path"])
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
chia_init(DEFAULT_ROOT_PATH)
|
chia_init(DEFAULT_ROOT_PATH)
|
||||||
|
|
242
src/cmds/keys.py
242
src/cmds/keys.py
|
@ -1,3 +1,5 @@
|
||||||
|
import click
|
||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
|
@ -18,97 +20,6 @@ from src.wallet.derive_keys import (
|
||||||
from src.util.ints import uint32
|
from src.util.ints import uint32
|
||||||
from src.consensus.coinbase import create_puzzlehash_for_pk
|
from src.consensus.coinbase import create_puzzlehash_for_pk
|
||||||
|
|
||||||
command_list = [
|
|
||||||
"generate",
|
|
||||||
"generate_and_print",
|
|
||||||
"show",
|
|
||||||
"add",
|
|
||||||
"delete",
|
|
||||||
"delete_all",
|
|
||||||
"sign",
|
|
||||||
"verify",
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def help_message():
|
|
||||||
print("usage: chia keys command")
|
|
||||||
print(f"command can be any of {command_list}")
|
|
||||||
print("")
|
|
||||||
print("chia keys generate (generates and adds a key to keychain)")
|
|
||||||
print("chia keys generate_and_print (generates but does NOT add to keychain)")
|
|
||||||
print("chia keys show (displays all the keys in keychain)")
|
|
||||||
print("chia keys add -m [24 words] (add a private key through the mnemonic)")
|
|
||||||
print("chia keys delete -f [fingerprint] (delete a key by it's pk fingerprint in hex form)")
|
|
||||||
print("chia keys delete_all (delete all private keys in keychain)")
|
|
||||||
print("chia keys sign -f [fingerprint] -t [hd_path] -d [message] (sign a message with a private key)")
|
|
||||||
print("chia keys verify -p [public_key] -d [message] -s [signature] (verify a signature with a pk)")
|
|
||||||
|
|
||||||
|
|
||||||
def make_parser(parser):
|
|
||||||
parser.add_argument(
|
|
||||||
"-m",
|
|
||||||
"--mnemonic",
|
|
||||||
type=str,
|
|
||||||
nargs=24,
|
|
||||||
default=None,
|
|
||||||
help="Enter mnemonic you want to use",
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"-k",
|
|
||||||
"--key",
|
|
||||||
type=str,
|
|
||||||
default=None,
|
|
||||||
help="Enter the raw private key in hex",
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"-f",
|
|
||||||
"--fingerprint",
|
|
||||||
type=int,
|
|
||||||
default=None,
|
|
||||||
help="Enter the fingerprint of the key you want to use",
|
|
||||||
)
|
|
||||||
|
|
||||||
parser.add_argument(
|
|
||||||
"-t",
|
|
||||||
"--hd_path",
|
|
||||||
type=str,
|
|
||||||
default=None,
|
|
||||||
help="Enter the HD path in the form 'm/12381/8444/n/n'",
|
|
||||||
)
|
|
||||||
|
|
||||||
parser.add_argument(
|
|
||||||
"-d",
|
|
||||||
"--message",
|
|
||||||
type=str,
|
|
||||||
default=None,
|
|
||||||
help="Enter the message to sign in UTF-8",
|
|
||||||
)
|
|
||||||
|
|
||||||
parser.add_argument(
|
|
||||||
"-p",
|
|
||||||
"--public_key",
|
|
||||||
type=str,
|
|
||||||
default=None,
|
|
||||||
help="Enter the pk in hex",
|
|
||||||
)
|
|
||||||
|
|
||||||
parser.add_argument(
|
|
||||||
"-s",
|
|
||||||
"--signature",
|
|
||||||
type=str,
|
|
||||||
default=None,
|
|
||||||
help="Enter the signature in hex",
|
|
||||||
)
|
|
||||||
|
|
||||||
parser.add_argument(
|
|
||||||
"command",
|
|
||||||
help=f"Command can be any one of {command_list}",
|
|
||||||
type=str,
|
|
||||||
nargs="?",
|
|
||||||
)
|
|
||||||
parser.set_defaults(function=handler)
|
|
||||||
parser.print_help = lambda self=parser: help_message()
|
|
||||||
|
|
||||||
|
|
||||||
keychain: Keychain = Keychain()
|
keychain: Keychain = Keychain()
|
||||||
|
|
||||||
|
@ -135,7 +46,7 @@ def generate_and_add():
|
||||||
add_private_key_seed(mnemonic)
|
add_private_key_seed(mnemonic)
|
||||||
|
|
||||||
|
|
||||||
def add_private_key_seed(mnemonic):
|
def add_private_key_seed(mnemonic: str):
|
||||||
"""
|
"""
|
||||||
Add a private key seed to the keyring, with the given mnemonic.
|
Add a private key seed to the keyring, with the given mnemonic.
|
||||||
"""
|
"""
|
||||||
|
@ -186,39 +97,18 @@ def show_all_keys():
|
||||||
print(mnemonic)
|
print(mnemonic)
|
||||||
|
|
||||||
|
|
||||||
def delete(args):
|
def delete(fingerprint: int):
|
||||||
"""
|
"""
|
||||||
Delete a key by it's public key fingerprint (which is an int).
|
Delete a key by it's public key fingerprint (which is an int).
|
||||||
"""
|
"""
|
||||||
if args.fingerprint is None:
|
|
||||||
print("Please specify the fingerprint argument -f")
|
|
||||||
quit()
|
|
||||||
|
|
||||||
fingerprint = args.fingerprint
|
|
||||||
assert fingerprint is not None
|
|
||||||
print(f"Deleting private_key with fingerprint {fingerprint}")
|
print(f"Deleting private_key with fingerprint {fingerprint}")
|
||||||
keychain.delete_key_by_fingerprint(fingerprint)
|
keychain.delete_key_by_fingerprint(fingerprint)
|
||||||
|
|
||||||
|
|
||||||
def sign(args):
|
def sign(message: str, fingerprint: int, hd_path: str):
|
||||||
if args.message is None:
|
|
||||||
print("Please specify the message argument -d")
|
|
||||||
quit()
|
|
||||||
|
|
||||||
if args.fingerprint is None or args.hd_path is None:
|
|
||||||
print("Please specify the fingerprint argument -f and hd_path argument -t")
|
|
||||||
quit()
|
|
||||||
|
|
||||||
message = args.message
|
|
||||||
assert message is not None
|
|
||||||
|
|
||||||
k = Keychain()
|
k = Keychain()
|
||||||
private_keys = k.get_all_private_keys()
|
private_keys = k.get_all_private_keys()
|
||||||
|
|
||||||
fingerprint = args.fingerprint
|
|
||||||
assert fingerprint is not None
|
|
||||||
hd_path = args.hd_path
|
|
||||||
assert hd_path is not None
|
|
||||||
path: List[uint32] = [uint32(int(i)) for i in hd_path.split("/") if i != "m"]
|
path: List[uint32] = [uint32(int(i)) for i in hd_path.split("/") if i != "m"]
|
||||||
for sk, _ in private_keys:
|
for sk, _ in private_keys:
|
||||||
if sk.get_g1().get_fingerprint() == fingerprint:
|
if sk.get_g1().get_fingerprint() == fingerprint:
|
||||||
|
@ -230,55 +120,85 @@ def sign(args):
|
||||||
print(f"Fingerprint {fingerprint} not found in keychain")
|
print(f"Fingerprint {fingerprint} not found in keychain")
|
||||||
|
|
||||||
|
|
||||||
def verify(args):
|
def verify(message: str, public_key: str, signature: str):
|
||||||
if args.message is None:
|
messageBytes = bytes(message, "utf-8")
|
||||||
print("Please specify the message argument -d")
|
public_key = G1Element.from_bytes(bytes.fromhex(public_key))
|
||||||
quit()
|
signature = G2Element.from_bytes(bytes.fromhex(signature))
|
||||||
if args.public_key is None:
|
print(AugSchemeMPL.verify(public_key, messageBytes, signature))
|
||||||
print("Please specify the public_key argument -p")
|
|
||||||
quit()
|
|
||||||
if args.signature is None:
|
|
||||||
print("Please specify the signature argument -s")
|
|
||||||
quit()
|
|
||||||
assert args.message is not None
|
|
||||||
assert args.public_key is not None
|
|
||||||
assert args.signature is not None
|
|
||||||
message = bytes(args.message, "utf-8")
|
|
||||||
public_key = G1Element.from_bytes(bytes.fromhex(args.public_key))
|
|
||||||
signature = G2Element.from_bytes(bytes.fromhex(args.signature))
|
|
||||||
print(AugSchemeMPL.verify(public_key, message, signature))
|
|
||||||
|
|
||||||
|
|
||||||
def handler(args, parser):
|
@click.group("keys", short_help="manage your keys")
|
||||||
if args.command is None or len(args.command) < 1:
|
@click.pass_context
|
||||||
help_message()
|
def keys_cmd(ctx: click.Context):
|
||||||
parser.exit(1)
|
"""Create, delete, view and use your key pairs"""
|
||||||
|
root_path: Path = ctx.obj["root_path"]
|
||||||
root_path: Path = args.root_path
|
|
||||||
if not root_path.is_dir():
|
if not root_path.is_dir():
|
||||||
raise RuntimeError("Please initialize (or migrate) your config directory with chia init.")
|
raise RuntimeError("Please initialize (or migrate) your config directory with chia init.")
|
||||||
|
|
||||||
command = args.command
|
|
||||||
if command not in command_list:
|
|
||||||
help_message()
|
|
||||||
parser.exit(1)
|
|
||||||
|
|
||||||
if command == "generate":
|
@keys_cmd.command("generate", short_help="generates and adds a key to keychain")
|
||||||
generate_and_add()
|
@click.pass_context
|
||||||
check_keys(root_path)
|
def generate_cmd(ctx: click.Context):
|
||||||
elif command == "show":
|
generate_and_add()
|
||||||
show_all_keys()
|
check_keys(ctx.obj["root_path"])
|
||||||
elif command == "add":
|
|
||||||
add_private_key_seed(" ".join(args.mnemonic))
|
|
||||||
check_keys(root_path)
|
@keys_cmd.command("show", short_help="displays all the keys in keychain")
|
||||||
elif command == "delete":
|
def show_cmd():
|
||||||
delete(args)
|
show_all_keys()
|
||||||
check_keys(root_path)
|
|
||||||
elif command == "delete_all":
|
|
||||||
keychain.delete_all_keys()
|
@keys_cmd.command("add", short_help="add a private key through the mnemonic")
|
||||||
if command == "generate_and_print":
|
@click.option("--mnemonic", "-m", help="Enter mnemonic you want to use", type=str)
|
||||||
generate_and_print()
|
@click.pass_context
|
||||||
if command == "sign":
|
def add_cmd(ctx: click.Context, mnemonic: str):
|
||||||
sign(args)
|
add_private_key_seed(mnemonic)
|
||||||
if command == "verify":
|
check_keys(ctx.obj["root_path"])
|
||||||
verify(args)
|
|
||||||
|
|
||||||
|
@keys_cmd.command("delete", short_help="delete a key by it's pk fingerprint in hex form")
|
||||||
|
@click.option(
|
||||||
|
"--fingerprint",
|
||||||
|
"-f",
|
||||||
|
default=None,
|
||||||
|
help="Enter the fingerprint of the key you want to use",
|
||||||
|
type=int,
|
||||||
|
required=True,
|
||||||
|
)
|
||||||
|
@click.pass_context
|
||||||
|
def delete_cmd(ctx: click.Context, fingerprint: int):
|
||||||
|
delete(fingerprint)
|
||||||
|
check_keys(ctx.obj["root_path"])
|
||||||
|
|
||||||
|
|
||||||
|
@keys_cmd.command("delete_all", short_help="delete all private keys in keychain")
|
||||||
|
def delete_all_cmd():
|
||||||
|
keychain.delete_all_keys()
|
||||||
|
|
||||||
|
|
||||||
|
@keys_cmd.command("generate_and_print", short_help="generates but does NOT add to keychain")
|
||||||
|
def generate_and_print_cmd():
|
||||||
|
generate_and_print()
|
||||||
|
|
||||||
|
|
||||||
|
@keys_cmd.command("sign", short_help="sign a message with a private key")
|
||||||
|
@click.option("--message", "-d", default=None, help="Enter the message to sign in UTF-8", type=str, required=True)
|
||||||
|
@click.option(
|
||||||
|
"--fingerprint",
|
||||||
|
"-f",
|
||||||
|
default=None,
|
||||||
|
help="Enter the fingerprint of the key you want to use",
|
||||||
|
type=int,
|
||||||
|
required=True,
|
||||||
|
)
|
||||||
|
@click.option("--hd_path", "-t", help="Enter the HD path in the form 'm/12381/8444/n/n'", type=str, required=True)
|
||||||
|
def sing_cmd(message: str, fingerprint: int, hd_path: str):
|
||||||
|
sign(message, fingerprint, hd_path)
|
||||||
|
|
||||||
|
|
||||||
|
@keys_cmd.command("verify", short_help="verify a signature with a pk")
|
||||||
|
@click.option("--message", "-d", default=None, help="Enter the message to sign in UTF-8", type=str, required=True)
|
||||||
|
@click.option("--public_key", "-p", default=None, help="Enter the pk in hex", type=str, required=True)
|
||||||
|
@click.option("--signature", "-s", default=None, help="Enter the signature in hex", type=str, required=True)
|
||||||
|
def verify_cmd(message: str, public_key: str, signature: str):
|
||||||
|
verify(message, public_key, signature)
|
||||||
|
|
|
@ -1,64 +1,27 @@
|
||||||
|
import click
|
||||||
import aiohttp
|
import aiohttp
|
||||||
import asyncio
|
import asyncio
|
||||||
import time
|
|
||||||
from time import struct_time, localtime
|
|
||||||
from src.util.config import load_config
|
from src.util.config import load_config
|
||||||
from src.util.default_root import DEFAULT_ROOT_PATH
|
from src.util.default_root import DEFAULT_ROOT_PATH
|
||||||
from src.util.byte_types import hexstr_to_bytes
|
from src.util.byte_types import hexstr_to_bytes
|
||||||
|
from src.util.ints import uint16
|
||||||
|
|
||||||
from src.rpc.full_node_rpc_client import FullNodeRpcClient
|
from src.rpc.full_node_rpc_client import FullNodeRpcClient
|
||||||
|
|
||||||
|
|
||||||
def make_parser(parser):
|
async def netstorge_async(rpc_port: int, delta_block_height: str, start: str) -> None:
|
||||||
|
|
||||||
parser.add_argument(
|
|
||||||
"-d",
|
|
||||||
"--delta-block-height",
|
|
||||||
help="Compare a block X blocks older to estimate total network space. "
|
|
||||||
+ "Defaults to 192 blocks (~1 hour) and Peak block as the starting block. "
|
|
||||||
+ "Use --start BLOCK_HEIGHT to specify starting block. "
|
|
||||||
+ "Use 1000 blocks to replicate the GUI estimate.",
|
|
||||||
type=str,
|
|
||||||
default="192",
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"-s",
|
|
||||||
"--start",
|
|
||||||
help="Newest block used to calculate estimated total network space. Defaults to Peak block.",
|
|
||||||
type=str,
|
|
||||||
default="",
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"-p",
|
|
||||||
"--rpc-port",
|
|
||||||
help="Set the port where the Full Node is hosting the RPC interface."
|
|
||||||
+ "See the rpc_port under full_node in config.yaml. Defaults to 8555.",
|
|
||||||
type=int,
|
|
||||||
)
|
|
||||||
parser.set_defaults(function=netspace)
|
|
||||||
|
|
||||||
|
|
||||||
def human_local_time(timestamp):
|
|
||||||
time_local = struct_time(localtime(timestamp))
|
|
||||||
return time.strftime("%a %b %d %Y %T %Z", time_local)
|
|
||||||
|
|
||||||
|
|
||||||
async def netstorge_async(args, parser):
|
|
||||||
"""
|
"""
|
||||||
Calculates the estimated space on the network given two block header hases
|
Calculates the estimated space on the network given two block header hases
|
||||||
# TODO: add help on failure/no args
|
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
config = load_config(DEFAULT_ROOT_PATH, "config.yaml")
|
config = load_config(DEFAULT_ROOT_PATH, "config.yaml")
|
||||||
self_hostname = config["self_hostname"]
|
self_hostname = config["self_hostname"]
|
||||||
if "rpc_port" not in args or args.rpc_port is None:
|
if rpc_port is None:
|
||||||
rpc_port = config["full_node"]["rpc_port"]
|
rpc_port = config["full_node"]["rpc_port"]
|
||||||
else:
|
client = await FullNodeRpcClient.create(self_hostname, uint16(rpc_port), DEFAULT_ROOT_PATH, config)
|
||||||
rpc_port = args.rpc_port
|
|
||||||
client = await FullNodeRpcClient.create(self_hostname, rpc_port, DEFAULT_ROOT_PATH, config)
|
|
||||||
|
|
||||||
if args.delta_block_height:
|
if delta_block_height:
|
||||||
if args.start == "":
|
if start == "":
|
||||||
blockchain_state = await client.get_blockchain_state()
|
blockchain_state = await client.get_blockchain_state()
|
||||||
if blockchain_state["peak"] is None:
|
if blockchain_state["peak"] is None:
|
||||||
print("No blocks in blockchain")
|
print("No blocks in blockchain")
|
||||||
|
@ -68,9 +31,9 @@ async def netstorge_async(args, parser):
|
||||||
|
|
||||||
newer_block_height = blockchain_state["peak"].height
|
newer_block_height = blockchain_state["peak"].height
|
||||||
else:
|
else:
|
||||||
newer_block = await client.get_block_record(hexstr_to_bytes(args.start))
|
newer_block = await client.get_block_record(hexstr_to_bytes(start))
|
||||||
if newer_block is None:
|
if newer_block is None:
|
||||||
print("Block header hash", args.start, "not found.")
|
print("Block header hash", start, "not found.")
|
||||||
client.close()
|
client.close()
|
||||||
await client.await_closed()
|
await client.await_closed()
|
||||||
return None
|
return None
|
||||||
|
@ -79,7 +42,7 @@ async def netstorge_async(args, parser):
|
||||||
newer_block_height = newer_block.height
|
newer_block_height = newer_block.height
|
||||||
|
|
||||||
newer_block_header = await client.get_block_record_by_height(newer_block_height)
|
newer_block_header = await client.get_block_record_by_height(newer_block_height)
|
||||||
older_block_height = max(0, newer_block_height - int(args.delta_block_height))
|
older_block_height = max(0, newer_block_height - int(delta_block_height))
|
||||||
older_block_header = await client.get_block_record_by_height(older_block_height)
|
older_block_header = await client.get_block_record_by_height(older_block_height)
|
||||||
network_space_bytes_estimate = await client.get_network_space(
|
network_space_bytes_estimate = await client.get_network_space(
|
||||||
newer_block_header.header_hash, older_block_header.header_hash
|
newer_block_header.header_hash, older_block_header.header_hash
|
||||||
|
@ -106,7 +69,7 @@ async def netstorge_async(args, parser):
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if isinstance(e, aiohttp.client_exceptions.ClientConnectorError):
|
if isinstance(e, aiohttp.client_exceptions.ClientConnectorError):
|
||||||
print(f"Connection error. Check if full node rpc is running at {args.rpc_port}")
|
print(f"Connection error. Check if full node rpc is running at {rpc_port}")
|
||||||
else:
|
else:
|
||||||
print(f"Exception {e}")
|
print(f"Exception {e}")
|
||||||
|
|
||||||
|
@ -114,5 +77,39 @@ async def netstorge_async(args, parser):
|
||||||
await client.await_closed()
|
await client.await_closed()
|
||||||
|
|
||||||
|
|
||||||
def netspace(args, parser):
|
@click.command("netspace", short_help="estimate space on the network")
|
||||||
return asyncio.run(netstorge_async(args, parser))
|
@click.option(
|
||||||
|
"-p",
|
||||||
|
"--rpc-port",
|
||||||
|
help=(
|
||||||
|
"Set the port where the Full Node is hosting the RPC interface. "
|
||||||
|
"See the rpc_port under full_node in config.yaml. "
|
||||||
|
"[default: 8555]"
|
||||||
|
),
|
||||||
|
type=int,
|
||||||
|
show_default=True,
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
"-d",
|
||||||
|
"--delta-block-height",
|
||||||
|
help=(
|
||||||
|
"Compare a block X blocks older to estimate total network space. "
|
||||||
|
"Defaults to 192 blocks (~1 hour) and Peak block as the starting block. "
|
||||||
|
"Use --start BLOCK_HEIGHT to specify starting block. "
|
||||||
|
"Use 1000 blocks to replicate the GUI estimate."
|
||||||
|
),
|
||||||
|
type=str,
|
||||||
|
default="192",
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
"-s",
|
||||||
|
"--start",
|
||||||
|
help="Newest block used to calculate estimated total network space. Defaults to Peak block.",
|
||||||
|
type=str,
|
||||||
|
default="",
|
||||||
|
)
|
||||||
|
def netspace_cmd(rpc_port: int, delta_block_height: str, start: str) -> None:
|
||||||
|
"""
|
||||||
|
Calculates the estimated space on the network given two block header hases.
|
||||||
|
"""
|
||||||
|
asyncio.run(netstorge_async(rpc_port, delta_block_height, start))
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import click
|
||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import logging
|
import logging
|
||||||
from src.plotting.plot_tools import (
|
from src.plotting.plot_tools import (
|
||||||
|
@ -13,154 +15,7 @@ from src.util.logging import initialize_logging
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
command_list = ["create", "check", "add", "remove", "show"]
|
def show_plots(root_path: Path):
|
||||||
|
|
||||||
|
|
||||||
def help_message():
|
|
||||||
print("usage: chia plots command")
|
|
||||||
print(f"command can be any of {command_list}")
|
|
||||||
print("")
|
|
||||||
print(
|
|
||||||
"chia plots create -k [size] -n [number of plots] -b [memory buffer size MiB]"
|
|
||||||
+ " -r [number of threads] -u [number of buckets] -s [stripe size]"
|
|
||||||
+ " -a [fingerprint] -f [farmer public key] -p [pool public key]"
|
|
||||||
+ " -t [tmp dir] -2 [tmp dir 2] -d [final dir] (creates plots)"
|
|
||||||
)
|
|
||||||
print("-e disables bitfield plotting")
|
|
||||||
print("-x skips adding [final dir] to harvester for farming")
|
|
||||||
print("-i [plotid] -m [memo] are available for debugging")
|
|
||||||
print("chia plots check -n [challenges] -g [string] -l (checks plots)")
|
|
||||||
print(" Default: check all plots in every directory with 30 challenges")
|
|
||||||
print(" -n: number of challenges; 0 = skip opening plot files; can be used with -l")
|
|
||||||
print(" -g: checks plots with file or directory name containing [string]")
|
|
||||||
print(" -l: list plots with duplicate IDs")
|
|
||||||
print(" Debugging options for chia plots check")
|
|
||||||
print(" --debug-show-memo: shows memo to recreate the same exact plot")
|
|
||||||
print(" --challenge-start [start]: begins at a different [start] for -n [challenges]")
|
|
||||||
print("chia plots add -d [directory] (adds a directory of plots)")
|
|
||||||
print("chia plots remove -d [directory] (removes a directory of plots from config)")
|
|
||||||
print("chia plots show (shows the directory of current plots)")
|
|
||||||
|
|
||||||
|
|
||||||
def make_parser(parser):
|
|
||||||
parser.add_argument("-k", "--size", help="Plot size", type=int, default=32)
|
|
||||||
parser.add_argument("-n", "--num", help="Number of plots or challenges", type=int, default=None)
|
|
||||||
parser.add_argument("-b", "--buffer", help="Mebibytes for sort/plot buffer", type=int, default=4608)
|
|
||||||
parser.add_argument("-r", "--num_threads", help="Number of threads to use", type=int, default=2)
|
|
||||||
parser.add_argument("-u", "--buckets", help="Number of buckets", type=int, default=0)
|
|
||||||
parser.add_argument("-s", "--stripe_size", help="Stripe size", type=int, default=0)
|
|
||||||
parser.add_argument(
|
|
||||||
"-a",
|
|
||||||
"--alt_fingerprint",
|
|
||||||
type=int,
|
|
||||||
default=None,
|
|
||||||
help="Enter the alternative fingerprint of the key you want to use",
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"-c",
|
|
||||||
"--pool_contract_address",
|
|
||||||
type=str,
|
|
||||||
default=None,
|
|
||||||
help=(
|
|
||||||
"Address of where the pool reward will be sent to. Only used "
|
|
||||||
"if alt_fingerprint and pool public key are None"
|
|
||||||
),
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"-f",
|
|
||||||
"--farmer_public_key",
|
|
||||||
help="Hex farmer public key",
|
|
||||||
type=str,
|
|
||||||
default=None,
|
|
||||||
)
|
|
||||||
parser.add_argument("-p", "--pool_public_key", help="Hex public key of pool", type=str, default=None)
|
|
||||||
parser.add_argument(
|
|
||||||
"-g",
|
|
||||||
"--grep_string",
|
|
||||||
help="Shows only plots that contain the string in the filename or directory name",
|
|
||||||
type=str,
|
|
||||||
default=None,
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"-t",
|
|
||||||
"--tmp_dir",
|
|
||||||
help="Temporary directory for plotting files",
|
|
||||||
type=Path,
|
|
||||||
default=Path("."),
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"-2",
|
|
||||||
"--tmp2_dir",
|
|
||||||
help="Second temporary directory for plotting files",
|
|
||||||
type=Path,
|
|
||||||
default=None,
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"-d",
|
|
||||||
"--final_dir",
|
|
||||||
help="Final directory for plots (relative or absolute)",
|
|
||||||
type=Path,
|
|
||||||
default=Path("."),
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"-i",
|
|
||||||
"--plotid",
|
|
||||||
help="PlotID in hex for reproducing plots (debugging only)",
|
|
||||||
type=str,
|
|
||||||
default=None,
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"-m",
|
|
||||||
"--memo",
|
|
||||||
help="Memo in hex for reproducing plots (debugging only)",
|
|
||||||
type=str,
|
|
||||||
default=None,
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"-e",
|
|
||||||
"--nobitfield",
|
|
||||||
help="Disable bitfield",
|
|
||||||
default=False,
|
|
||||||
action="store_true",
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"-x",
|
|
||||||
"--exclude_final_dir",
|
|
||||||
help="Skips adding [final dir] to harvester for farming",
|
|
||||||
default=False,
|
|
||||||
action="store_true",
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"-l",
|
|
||||||
"--list_duplicates",
|
|
||||||
help="List plots with duplicate IDs",
|
|
||||||
default=False,
|
|
||||||
action="store_true",
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--debug-show-memo",
|
|
||||||
help="Shows memo to recreate the same exact plot",
|
|
||||||
default=False,
|
|
||||||
action="store_true",
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--challenge-start",
|
|
||||||
help="Begins at a different [start] for -n [challenges]",
|
|
||||||
type=int,
|
|
||||||
default=None,
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"command",
|
|
||||||
help=f"Command can be any one of {command_list}",
|
|
||||||
type=str,
|
|
||||||
nargs="?",
|
|
||||||
)
|
|
||||||
|
|
||||||
parser.set_defaults(function=handler)
|
|
||||||
parser.print_help = lambda self=parser: help_message()
|
|
||||||
|
|
||||||
|
|
||||||
def show(root_path):
|
|
||||||
print("Directories where plots are being searched for:")
|
print("Directories where plots are being searched for:")
|
||||||
print("Note that subdirectories must be added manually.")
|
print("Note that subdirectories must be added manually.")
|
||||||
print(
|
print(
|
||||||
|
@ -173,30 +28,156 @@ def show(root_path):
|
||||||
print(f"{str_path}")
|
print(f"{str_path}")
|
||||||
|
|
||||||
|
|
||||||
def handler(args, parser):
|
@click.group("plots", short_help="manage your plots")
|
||||||
if args.command is None or len(args.command) < 1:
|
@click.pass_context
|
||||||
help_message()
|
def plots_cmd(ctx: click.Context):
|
||||||
parser.exit(1)
|
"""Create, add, remove and check your plots"""
|
||||||
|
root_path: Path = ctx.obj["root_path"]
|
||||||
root_path: Path = args.root_path
|
|
||||||
if not root_path.is_dir():
|
if not root_path.is_dir():
|
||||||
raise RuntimeError("Please initialize (or migrate) your config directory with chia init.")
|
raise RuntimeError("Please initialize (or migrate) your config directory with chia init.")
|
||||||
|
|
||||||
initialize_logging("", {"log_stdout": True}, root_path)
|
initialize_logging("", {"log_stdout": True}, root_path)
|
||||||
command = args.command
|
|
||||||
if command not in command_list:
|
|
||||||
help_message()
|
|
||||||
parser.exit(1)
|
|
||||||
|
|
||||||
if command == "create":
|
|
||||||
create_plots(args, root_path)
|
@plots_cmd.command("create", short_help="creates plots")
|
||||||
elif command == "check":
|
@click.option("-k", "--size", help="Plot size", type=int, default=32, show_default=True)
|
||||||
check_plots(args, root_path)
|
@click.option("-n", "--num", help="Number of plots or challenges", type=int, default=1, show_default=True)
|
||||||
elif command == "add":
|
@click.option("-b", "--buffer", help="Megabytes for sort/plot buffer", type=int, default=4608, show_default=True)
|
||||||
str_path = args.final_dir
|
@click.option("-r", "--num_threads", help="Number of threads to use", type=int, default=2, show_default=True)
|
||||||
add_plot_directory(str_path, root_path)
|
@click.option("-u", "--buckets", help="Number of buckets", type=int, default=0)
|
||||||
elif command == "remove":
|
@click.option("-s", "--stripe_size", help="Stripe size", type=int, default=0)
|
||||||
str_path = args.final_dir
|
@click.option(
|
||||||
remove_plot_directory(str_path, root_path)
|
"-a",
|
||||||
elif command == "show":
|
"--alt_fingerprint",
|
||||||
show(root_path)
|
type=int,
|
||||||
|
default=None,
|
||||||
|
help="Enter the alternative fingerprint of the key you want to use",
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
"-c",
|
||||||
|
"--pool_contract_address",
|
||||||
|
type=str,
|
||||||
|
default=None,
|
||||||
|
help="Address of where the pool reward will be sent to. Only used if alt_fingerprint and pool public key are None",
|
||||||
|
)
|
||||||
|
@click.option("-f", "--farmer_public_key", help="Hex farmer public key", type=str, default=None)
|
||||||
|
@click.option("-p", "--pool_public_key", help="Hex public key of pool", type=str, default=None)
|
||||||
|
@click.option(
|
||||||
|
"-t",
|
||||||
|
"--tmp_dir",
|
||||||
|
help="Temporary directory for plotting files",
|
||||||
|
type=click.Path(),
|
||||||
|
default=Path("."),
|
||||||
|
show_default=True,
|
||||||
|
)
|
||||||
|
@click.option("-2", "--tmp2_dir", help="Second temporary directory for plotting files", type=click.Path(), default=None)
|
||||||
|
@click.option(
|
||||||
|
"-d",
|
||||||
|
"--final_dir",
|
||||||
|
help="Final directory for plots (relative or absolute)",
|
||||||
|
type=click.Path(),
|
||||||
|
default=Path("."),
|
||||||
|
show_default=True,
|
||||||
|
)
|
||||||
|
@click.option("-i", "--plotid", help="PlotID in hex for reproducing plots (debugging only)", type=str, default=None)
|
||||||
|
@click.option("-m", "--memo", help="Memo in hex for reproducing plots (debugging only)", type=str, default=None)
|
||||||
|
@click.option("-e", "--nobitfield", help="Disable bitfield", default=False, is_flag=True)
|
||||||
|
@click.option(
|
||||||
|
"-x", "--exclude_final_dir", help="Skips adding [final dir] to harvester for farming", default=False, is_flag=True
|
||||||
|
)
|
||||||
|
@click.pass_context
|
||||||
|
def create_cmd(
|
||||||
|
ctx: click.Context,
|
||||||
|
size: int,
|
||||||
|
num: int,
|
||||||
|
buffer: int,
|
||||||
|
num_threads: int,
|
||||||
|
buckets: int,
|
||||||
|
stripe_size: int,
|
||||||
|
alt_fingerprint: int,
|
||||||
|
pool_contract_address: str,
|
||||||
|
farmer_public_key: str,
|
||||||
|
pool_public_key: str,
|
||||||
|
tmp_dir: str,
|
||||||
|
tmp2_dir: str,
|
||||||
|
final_dir: str,
|
||||||
|
plotid: str,
|
||||||
|
memo: str,
|
||||||
|
nobitfield: bool,
|
||||||
|
exclude_final_dir: bool,
|
||||||
|
):
|
||||||
|
class Params(object):
|
||||||
|
def __init__(self):
|
||||||
|
self.size = size
|
||||||
|
self.num = num
|
||||||
|
self.buffer = buffer
|
||||||
|
self.num_threads = num_threads
|
||||||
|
self.buckets = buckets
|
||||||
|
self.stripe_size = stripe_size
|
||||||
|
self.alt_fingerprint = alt_fingerprint
|
||||||
|
self.pool_contract_address = pool_contract_address
|
||||||
|
self.farmer_public_key = farmer_public_key
|
||||||
|
self.pool_public_key = pool_public_key
|
||||||
|
self.tmp_dir = Path(tmp_dir)
|
||||||
|
self.tmp2_dir = Path(tmp2_dir) if tmp2_dir else None
|
||||||
|
self.final_dir = Path(final_dir)
|
||||||
|
self.plotid = plotid
|
||||||
|
self.memo = memo
|
||||||
|
self.nobitfield = nobitfield
|
||||||
|
self.exclude_final_dir = exclude_final_dir
|
||||||
|
|
||||||
|
create_plots(Params(), ctx.obj["root_path"])
|
||||||
|
|
||||||
|
|
||||||
|
@plots_cmd.command("check", short_help="checks plots")
|
||||||
|
@click.option("-n", "--num", help="Number of plots or challenges", type=int, default=None)
|
||||||
|
@click.option(
|
||||||
|
"-g",
|
||||||
|
"--grep_string",
|
||||||
|
help="Shows only plots that contain the string in the filename or directory name",
|
||||||
|
type=str,
|
||||||
|
default=None,
|
||||||
|
)
|
||||||
|
@click.option("-l", "--list_duplicates", help="List plots with duplicate IDs", default=False, is_flag=True)
|
||||||
|
@click.option("--debug-show-memo", help="Shows memo to recreate the same exact plot", default=False, is_flag=True)
|
||||||
|
@click.option("--challenge-start", help="Begins at a different [start] for -n [challenges]", type=int, default=None)
|
||||||
|
@click.pass_context
|
||||||
|
def check_cmd(
|
||||||
|
ctx: click.Context, num: int, grep_string: str, list_duplicates: bool, debug_show_memo: bool, challenge_start: int
|
||||||
|
):
|
||||||
|
check_plots(ctx.obj["root_path"], num, challenge_start, grep_string, list_duplicates, debug_show_memo)
|
||||||
|
|
||||||
|
|
||||||
|
@plots_cmd.command("add", short_help="adds a directory of plots")
|
||||||
|
@click.option(
|
||||||
|
"-d",
|
||||||
|
"--final_dir",
|
||||||
|
help="Final directory for plots (relative or absolute)",
|
||||||
|
type=click.Path(),
|
||||||
|
default=".",
|
||||||
|
show_default=True,
|
||||||
|
)
|
||||||
|
@click.pass_context
|
||||||
|
def add_cmd(ctx: click.Context, final_dir: str):
|
||||||
|
add_plot_directory(Path(final_dir), ctx.obj["root_path"])
|
||||||
|
print(f'Added plot directory "{final_dir}".')
|
||||||
|
|
||||||
|
|
||||||
|
@plots_cmd.command("remove", short_help="removes a directory of plots from config")
|
||||||
|
@click.option(
|
||||||
|
"-d",
|
||||||
|
"--final_dir",
|
||||||
|
help="Final directory for plots (relative or absolute)",
|
||||||
|
type=click.Path(),
|
||||||
|
default=".",
|
||||||
|
show_default=True,
|
||||||
|
)
|
||||||
|
@click.pass_context
|
||||||
|
def remove_cmd(ctx: click.Context, final_dir: str):
|
||||||
|
remove_plot_directory(Path(final_dir), ctx.obj["root_path"])
|
||||||
|
print(f'Removed plot directory "{final_dir}".')
|
||||||
|
|
||||||
|
|
||||||
|
@plots_cmd.command("show", short_help="shows the directory of current plots")
|
||||||
|
@click.pass_context
|
||||||
|
def show_cmd(ctx: click.Context):
|
||||||
|
show_plots(ctx.obj["root_path"])
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
import asyncio
|
|
||||||
|
|
||||||
from src.daemon.server import async_run_daemon
|
|
||||||
|
|
||||||
|
|
||||||
def make_parser(parser):
|
|
||||||
parser.set_defaults(function=run_daemon)
|
|
||||||
|
|
||||||
|
|
||||||
def run_daemon(args, parser):
|
|
||||||
return asyncio.get_event_loop().run_until_complete(async_run_daemon(args.root_path))
|
|
239
src/cmds/show.py
239
src/cmds/show.py
|
@ -1,3 +1,4 @@
|
||||||
|
import click
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
|
@ -12,115 +13,37 @@ from src.server.outbound_message import NodeType
|
||||||
from src.types.full_block import FullBlock
|
from src.types.full_block import FullBlock
|
||||||
from src.rpc.full_node_rpc_client import FullNodeRpcClient
|
from src.rpc.full_node_rpc_client import FullNodeRpcClient
|
||||||
from src.util.byte_types import hexstr_to_bytes
|
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.config import load_config
|
||||||
from src.util.default_root import DEFAULT_ROOT_PATH
|
from src.util.default_root import DEFAULT_ROOT_PATH
|
||||||
from src.util.bech32m import encode_puzzle_hash
|
from src.util.bech32m import encode_puzzle_hash
|
||||||
|
from src.util.ints import uint16
|
||||||
|
|
||||||
|
|
||||||
def make_parser(parser):
|
async def show_async(
|
||||||
|
rpc_port: int,
|
||||||
parser.add_argument(
|
state: bool,
|
||||||
"-s",
|
show_connections: bool,
|
||||||
"--state",
|
exit_node: bool,
|
||||||
help="Show the current state of the blockchain.",
|
add_connection: str,
|
||||||
type=str2bool,
|
remove_connection: str,
|
||||||
nargs="?",
|
block_header_hash_by_height: str,
|
||||||
const=True,
|
block_by_header_hash: str,
|
||||||
default=False,
|
) -> None:
|
||||||
)
|
|
||||||
|
|
||||||
parser.add_argument(
|
|
||||||
"-c",
|
|
||||||
"--connections",
|
|
||||||
help="List nodes connected to this Full Node.",
|
|
||||||
type=str2bool,
|
|
||||||
nargs="?",
|
|
||||||
const=True,
|
|
||||||
default=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
parser.add_argument(
|
|
||||||
"-b",
|
|
||||||
"--block-by-header-hash",
|
|
||||||
help="Look up a block by block header hash.",
|
|
||||||
type=str,
|
|
||||||
default="",
|
|
||||||
)
|
|
||||||
|
|
||||||
parser.add_argument(
|
|
||||||
"-bh",
|
|
||||||
"--block-header-hash-by-height",
|
|
||||||
help="Look up a block header hash by block height.",
|
|
||||||
type=str,
|
|
||||||
default="",
|
|
||||||
)
|
|
||||||
|
|
||||||
parser.add_argument(
|
|
||||||
"-a",
|
|
||||||
"--add-connection",
|
|
||||||
help="Connect to another Full Node by ip:port",
|
|
||||||
type=str,
|
|
||||||
default="",
|
|
||||||
)
|
|
||||||
|
|
||||||
parser.add_argument(
|
|
||||||
"-r",
|
|
||||||
"--remove-connection",
|
|
||||||
help="Remove a Node by the first 8 characters of NodeID",
|
|
||||||
type=str,
|
|
||||||
default="",
|
|
||||||
)
|
|
||||||
|
|
||||||
parser.add_argument(
|
|
||||||
"-e",
|
|
||||||
"--exit-node",
|
|
||||||
help="Shut down the running Full Node",
|
|
||||||
nargs="?",
|
|
||||||
const=True,
|
|
||||||
default=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
parser.add_argument(
|
|
||||||
"-p",
|
|
||||||
"--rpc-port",
|
|
||||||
help="Set the port where the Full Node is hosting the RPC interface."
|
|
||||||
+ " See the rpc_port under full_node in config.yaml."
|
|
||||||
+ "Defaults to 8555",
|
|
||||||
type=int,
|
|
||||||
default=8555,
|
|
||||||
)
|
|
||||||
|
|
||||||
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 show_async(args, parser):
|
|
||||||
|
|
||||||
# TODO read configuration for rpc_port instead of assuming default
|
# TODO read configuration for rpc_port instead of assuming default
|
||||||
|
|
||||||
try:
|
try:
|
||||||
config = load_config(DEFAULT_ROOT_PATH, "config.yaml")
|
config = load_config(DEFAULT_ROOT_PATH, "config.yaml")
|
||||||
self_hostname = config["self_hostname"]
|
self_hostname = config["self_hostname"]
|
||||||
if "rpc_port" not in args or args.rpc_port is None:
|
if rpc_port is None:
|
||||||
rpc_port = config["full_node"]["rpc_port"]
|
rpc_port = config["full_node"]["rpc_port"]
|
||||||
else:
|
client = await FullNodeRpcClient.create(self_hostname, uint16(rpc_port), DEFAULT_ROOT_PATH, config)
|
||||||
rpc_port = args.rpc_port
|
|
||||||
client = await FullNodeRpcClient.create(self_hostname, rpc_port, DEFAULT_ROOT_PATH, config)
|
|
||||||
|
|
||||||
if args.state:
|
if state:
|
||||||
blockchain_state = await client.get_blockchain_state()
|
blockchain_state = await client.get_blockchain_state()
|
||||||
if blockchain_state is None:
|
if blockchain_state is None:
|
||||||
return "There is no blockchain found yet. Try again shortly."
|
print("There is no blockchain found yet. Try again shortly.")
|
||||||
|
return
|
||||||
peak: Optional[BlockRecord] = blockchain_state["peak"]
|
peak: Optional[BlockRecord] = blockchain_state["peak"]
|
||||||
difficulty = blockchain_state["difficulty"]
|
difficulty = blockchain_state["difficulty"]
|
||||||
sub_slot_iters = blockchain_state["sub_slot_iters"]
|
sub_slot_iters = blockchain_state["sub_slot_iters"]
|
||||||
|
@ -140,7 +63,7 @@ async def show_async(args, parser):
|
||||||
)
|
)
|
||||||
if synced:
|
if synced:
|
||||||
print("Current Blockchain Status: Full Node Synced")
|
print("Current Blockchain Status: Full Node Synced")
|
||||||
print("\nPeak: Hash:", peak.header_hash)
|
print("\nPeak: Hash:", peak.header_hash if peak is not None else "")
|
||||||
elif peak is not None:
|
elif peak is not None:
|
||||||
print(f"Current Blockchain Status: Not Synced. Peak height: {peak.height}")
|
print(f"Current Blockchain Status: Not Synced. Peak height: {peak.height}")
|
||||||
else:
|
else:
|
||||||
|
@ -156,11 +79,11 @@ async def show_async(args, parser):
|
||||||
while curr is not None and not curr.is_transaction_block:
|
while curr is not None and not curr.is_transaction_block:
|
||||||
curr = await client.get_block_record(curr.prev_hash)
|
curr = await client.get_block_record(curr.prev_hash)
|
||||||
peak_time = curr.timestamp
|
peak_time = curr.timestamp
|
||||||
peak_time = struct_time(localtime(peak_time))
|
peak_time_struct = struct_time(localtime(peak_time))
|
||||||
|
|
||||||
print(
|
print(
|
||||||
" Time:",
|
" Time:",
|
||||||
f"{time.strftime('%a %b %d %Y %T %Z', peak_time)}",
|
f"{time.strftime('%a %b %d %Y %T %Z', peak_time_struct)}",
|
||||||
f" Height: {peak.height:>10}\n",
|
f" Height: {peak.height:>10}\n",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -188,10 +111,10 @@ async def show_async(args, parser):
|
||||||
else:
|
else:
|
||||||
print("Blockchain has no blocks yet")
|
print("Blockchain has no blocks yet")
|
||||||
|
|
||||||
# if called together with connections, leave a blank line
|
# if called together with show_connections, leave a blank line
|
||||||
if args.connections:
|
if show_connections:
|
||||||
print("")
|
print("")
|
||||||
if args.connections:
|
if show_connections:
|
||||||
connections = await client.get_connections()
|
connections = await client.get_connections()
|
||||||
print("Connections:")
|
print("Connections:")
|
||||||
print(
|
print(
|
||||||
|
@ -235,18 +158,18 @@ async def show_async(args, parser):
|
||||||
)
|
)
|
||||||
print(con_str)
|
print(con_str)
|
||||||
# if called together with state, leave a blank line
|
# if called together with state, leave a blank line
|
||||||
if args.state:
|
if state:
|
||||||
print("")
|
print("")
|
||||||
if args.exit_node:
|
if exit_node:
|
||||||
node_stop = await client.stop_node()
|
node_stop = await client.stop_node()
|
||||||
print(node_stop, "Node stopped.")
|
print(node_stop, "Node stopped.")
|
||||||
if args.add_connection:
|
if add_connection:
|
||||||
if ":" not in args.add_connection:
|
if ":" not in add_connection:
|
||||||
print("Enter a valid IP and port in the following format: 10.5.4.3:8000")
|
print("Enter a valid IP and port in the following format: 10.5.4.3:8000")
|
||||||
else:
|
else:
|
||||||
ip, port = (
|
ip, port = (
|
||||||
":".join(args.add_connection.split(":")[:-1]),
|
":".join(add_connection.split(":")[:-1]),
|
||||||
args.add_connection.split(":")[-1],
|
add_connection.split(":")[-1],
|
||||||
)
|
)
|
||||||
print(f"Connecting to {ip}, {port}")
|
print(f"Connecting to {ip}, {port}")
|
||||||
try:
|
try:
|
||||||
|
@ -254,34 +177,34 @@ async def show_async(args, parser):
|
||||||
except Exception:
|
except Exception:
|
||||||
# TODO: catch right exception
|
# TODO: catch right exception
|
||||||
print(f"Failed to connect to {ip}:{port}")
|
print(f"Failed to connect to {ip}:{port}")
|
||||||
if args.remove_connection:
|
if remove_connection:
|
||||||
result_txt = ""
|
result_txt = ""
|
||||||
if len(args.remove_connection) != 8:
|
if len(remove_connection) != 8:
|
||||||
result_txt = "Invalid NodeID. Do not include '.'."
|
result_txt = "Invalid NodeID. Do not include '.'."
|
||||||
else:
|
else:
|
||||||
connections = await client.get_connections()
|
connections = await client.get_connections()
|
||||||
for con in connections:
|
for con in connections:
|
||||||
if args.remove_connection == con["node_id"].hex()[:8]:
|
if remove_connection == con["node_id"].hex()[:8]:
|
||||||
print("Attempting to disconnect", "NodeID", args.remove_connection)
|
print("Attempting to disconnect", "NodeID", remove_connection)
|
||||||
try:
|
try:
|
||||||
await client.close_connection(con["node_id"])
|
await client.close_connection(con["node_id"])
|
||||||
except Exception:
|
except Exception:
|
||||||
result_txt = f"Failed to disconnect NodeID {args.remove_connection}"
|
result_txt = f"Failed to disconnect NodeID {remove_connection}"
|
||||||
else:
|
else:
|
||||||
result_txt = f"NodeID {args.remove_connection}... {NodeType(con['type']).name} "
|
result_txt = f"NodeID {remove_connection}... {NodeType(con['type']).name} "
|
||||||
f"{con['peer_host']} disconnected."
|
f"{con['peer_host']} disconnected."
|
||||||
elif result_txt == "":
|
elif result_txt == "":
|
||||||
result_txt = f"NodeID {args.remove_connection}... not found."
|
result_txt = f"NodeID {remove_connection}... not found."
|
||||||
print(result_txt)
|
print(result_txt)
|
||||||
if args.block_header_hash_by_height != "":
|
if block_header_hash_by_height != "":
|
||||||
block_header = await client.get_block_record_by_height(args.block_header_hash_by_height)
|
block_header = await client.get_block_record_by_height(block_header_hash_by_height)
|
||||||
if block_header is not None:
|
if block_header is not None:
|
||||||
print(f"Header hash of block {args.block_header_hash_by_height}: " f"{block_header.header_hash.hex()}")
|
print(f"Header hash of block {block_header_hash_by_height}: " f"{block_header.header_hash.hex()}")
|
||||||
else:
|
else:
|
||||||
print("Block height", args.block_header_hash_by_height, "not found.")
|
print("Block height", block_header_hash_by_height, "not found.")
|
||||||
if args.block_by_header_hash != "":
|
if block_by_header_hash != "":
|
||||||
block: Optional[BlockRecord] = await client.get_block_record(hexstr_to_bytes(args.block_by_header_hash))
|
block: Optional[BlockRecord] = await client.get_block_record(hexstr_to_bytes(block_by_header_hash))
|
||||||
full_block: Optional[FullBlock] = await client.get_block(hexstr_to_bytes(args.block_by_header_hash))
|
full_block: Optional[FullBlock] = await client.get_block(hexstr_to_bytes(block_by_header_hash))
|
||||||
# Would like to have a verbose flag for this
|
# Would like to have a verbose flag for this
|
||||||
if block is not None:
|
if block is not None:
|
||||||
assert full_block is not None
|
assert full_block is not None
|
||||||
|
@ -292,10 +215,18 @@ async def show_async(args, parser):
|
||||||
difficulty = block.weight
|
difficulty = block.weight
|
||||||
if block.is_transaction_block:
|
if block.is_transaction_block:
|
||||||
assert full_block.transactions_info is not None
|
assert full_block.transactions_info is not None
|
||||||
block_time = struct_time(localtime(full_block.foliage_transaction_block.timestamp))
|
block_time = struct_time(
|
||||||
|
localtime(
|
||||||
|
full_block.foliage_transaction_block.timestamp
|
||||||
|
if full_block.foliage_transaction_block
|
||||||
|
else None
|
||||||
|
)
|
||||||
|
)
|
||||||
block_time_string = time.strftime("%a %b %d %Y %T %Z", block_time)
|
block_time_string = time.strftime("%a %b %d %Y %T %Z", block_time)
|
||||||
cost = full_block.transactions_info.cost
|
cost = str(full_block.transactions_info.cost)
|
||||||
tx_filter_hash = full_block.foliage_transaction_block.filter_hash
|
tx_filter_hash = "Not a transaction block"
|
||||||
|
if full_block.foliage_transaction_block:
|
||||||
|
tx_filter_hash = full_block.foliage_transaction_block.filter_hash
|
||||||
else:
|
else:
|
||||||
block_time_string = "Not a transaction block"
|
block_time_string = "Not a transaction block"
|
||||||
cost = "Not a transaction block"
|
cost = "Not a transaction block"
|
||||||
|
@ -325,11 +256,11 @@ async def show_async(args, parser):
|
||||||
f"Fees Amount {block.fees}\n"
|
f"Fees Amount {block.fees}\n"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
print("Block with header hash", args.block_header_hash_by_height, "not found.")
|
print("Block with header hash", block_header_hash_by_height, "not found.")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if isinstance(e, aiohttp.client_exceptions.ClientConnectorError):
|
if isinstance(e, aiohttp.client_exceptions.ClientConnectorError):
|
||||||
print(f"Connection error. Check if full node rpc is running at {args.rpc_port}")
|
print(f"Connection error. Check if full node rpc is running at {rpc_port}")
|
||||||
print("This is normal if full node is still starting up.")
|
print("This is normal if full node is still starting up.")
|
||||||
else:
|
else:
|
||||||
tb = traceback.format_exc()
|
tb = traceback.format_exc()
|
||||||
|
@ -339,5 +270,59 @@ async def show_async(args, parser):
|
||||||
await client.await_closed()
|
await client.await_closed()
|
||||||
|
|
||||||
|
|
||||||
def show(args, parser):
|
@click.command("show", short_help="show node information")
|
||||||
return asyncio.run(show_async(args, parser))
|
@click.option(
|
||||||
|
"-p",
|
||||||
|
"--rpc-port",
|
||||||
|
help=(
|
||||||
|
"Set the port where the Full Node is hosting the RPC interface. "
|
||||||
|
"See the rpc_port under full_node in config.yaml."
|
||||||
|
),
|
||||||
|
type=int,
|
||||||
|
default=8555,
|
||||||
|
show_default=True,
|
||||||
|
)
|
||||||
|
@click.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=9256,
|
||||||
|
show_default=True,
|
||||||
|
)
|
||||||
|
@click.option("-s", "--state", help="Show the current state of the blockchain.", is_flag=True, type=bool, default=False)
|
||||||
|
@click.option(
|
||||||
|
"-c", "--connections", help="List nodes connected to this Full Node.", is_flag=True, type=bool, default=False
|
||||||
|
)
|
||||||
|
@click.option("-e", "--exit-node", help="Shut down the running Full Node", is_flag=True, default=False)
|
||||||
|
@click.option("-a", "--add-connection", help="Connect to another Full Node by ip:port", type=str, default="")
|
||||||
|
@click.option(
|
||||||
|
"-r", "--remove-connection", help="Remove a Node by the first 8 characters of NodeID", type=str, default=""
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
"-bh", "--block-header-hash-by-height", help="Look up a block header hash by block height.", type=str, default=""
|
||||||
|
)
|
||||||
|
@click.option("-b", "--block-by-header-hash", help="Look up a block by block header hash.", type=str, default="")
|
||||||
|
def show_cmd(
|
||||||
|
rpc_port: int,
|
||||||
|
wallet_rpc_port: int,
|
||||||
|
state: bool,
|
||||||
|
connections: bool,
|
||||||
|
exit_node: bool,
|
||||||
|
add_connection: str,
|
||||||
|
remove_connection: str,
|
||||||
|
block_header_hash_by_height: str,
|
||||||
|
block_by_header_hash: str,
|
||||||
|
) -> None:
|
||||||
|
asyncio.run(
|
||||||
|
show_async(
|
||||||
|
rpc_port,
|
||||||
|
state,
|
||||||
|
connections,
|
||||||
|
exit_node,
|
||||||
|
add_connection,
|
||||||
|
remove_connection,
|
||||||
|
block_header_hash_by_height,
|
||||||
|
block_by_header_hash,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
|
@ -1,43 +1,30 @@
|
||||||
|
import click
|
||||||
import asyncio
|
import asyncio
|
||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
from src.daemon.client import connect_to_daemon_and_validate
|
from src.daemon.client import connect_to_daemon_and_validate, DaemonProxy
|
||||||
from src.util.service_groups import all_groups, services_for_groups
|
from src.util.service_groups import all_groups, services_for_groups
|
||||||
|
|
||||||
|
|
||||||
def make_parser(parser):
|
def launch_start_daemon(root_path: Path) -> subprocess.Popen:
|
||||||
|
|
||||||
parser.add_argument(
|
|
||||||
"-r",
|
|
||||||
"--restart",
|
|
||||||
action="store_true",
|
|
||||||
help="Restart of running processes",
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"group",
|
|
||||||
choices=all_groups(),
|
|
||||||
type=str,
|
|
||||||
nargs="+",
|
|
||||||
)
|
|
||||||
parser.set_defaults(function=start)
|
|
||||||
|
|
||||||
|
|
||||||
def launch_start_daemon(root_path):
|
|
||||||
os.environ["CHIA_ROOT"] = str(root_path)
|
os.environ["CHIA_ROOT"] = str(root_path)
|
||||||
# TODO: use startupinfo=subprocess.DETACHED_PROCESS on windows
|
# TODO: use startupinfo=subprocess.DETACHED_PROCESS on windows
|
||||||
process = subprocess.Popen("chia run_daemon".split(), stdout=subprocess.PIPE)
|
process = subprocess.Popen("chia run_daemon".split(), stdout=subprocess.PIPE)
|
||||||
return process
|
return process
|
||||||
|
|
||||||
|
|
||||||
async def create_start_daemon_connection(root_path):
|
async def create_start_daemon_connection(root_path: Path) -> Optional[DaemonProxy]:
|
||||||
connection = await connect_to_daemon_and_validate(root_path)
|
connection = await connect_to_daemon_and_validate(root_path)
|
||||||
if connection is None:
|
if connection is None:
|
||||||
print("Starting daemon")
|
print("Starting daemon")
|
||||||
# launch a daemon
|
# launch a daemon
|
||||||
process = launch_start_daemon(root_path)
|
process = launch_start_daemon(root_path)
|
||||||
# give the daemon a chance to start up
|
# give the daemon a chance to start up
|
||||||
process.stdout.readline()
|
if process.stdout:
|
||||||
|
process.stdout.readline()
|
||||||
await asyncio.sleep(1)
|
await asyncio.sleep(1)
|
||||||
# it prints "daemon: listening"
|
# it prints "daemon: listening"
|
||||||
connection = await connect_to_daemon_and_validate(root_path)
|
connection = await connect_to_daemon_and_validate(root_path)
|
||||||
|
@ -46,16 +33,16 @@ async def create_start_daemon_connection(root_path):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
async def async_start(args, parser):
|
async def async_start(root_path: Path, group: str, restart: bool) -> None:
|
||||||
daemon = await create_start_daemon_connection(args.root_path)
|
daemon = await create_start_daemon_connection(root_path)
|
||||||
if daemon is None:
|
if daemon is None:
|
||||||
print("failed to create the chia start daemon")
|
print("failed to create the chia start daemon")
|
||||||
return 1
|
return
|
||||||
|
|
||||||
for service in services_for_groups(args.group):
|
for service in services_for_groups(group):
|
||||||
if await daemon.is_running(service_name=service):
|
if await daemon.is_running(service_name=service):
|
||||||
print(f"{service}: ", end="", flush=True)
|
print(f"{service}: ", end="", flush=True)
|
||||||
if args.restart:
|
if restart:
|
||||||
if not await daemon.is_running(service_name=service):
|
if not await daemon.is_running(service_name=service):
|
||||||
print("not running")
|
print("not running")
|
||||||
elif await daemon.stop_service(service_name=service):
|
elif await daemon.stop_service(service_name=service):
|
||||||
|
@ -77,5 +64,9 @@ async def async_start(args, parser):
|
||||||
await daemon.close()
|
await daemon.close()
|
||||||
|
|
||||||
|
|
||||||
def start(args, parser):
|
@click.command("start", short_help="start service groups")
|
||||||
return asyncio.get_event_loop().run_until_complete(async_start(args, parser))
|
@click.option("-r", "--restart", is_flag=True, type=bool, help="Restart of running processes")
|
||||||
|
@click.argument("group", type=click.Choice(all_groups()), nargs=-1, required=True)
|
||||||
|
@click.pass_context
|
||||||
|
def start_cmd(ctx: click.Context, restart: bool, group: str) -> None:
|
||||||
|
asyncio.get_event_loop().run_until_complete(async_start(ctx.obj["root_path"], group, restart))
|
||||||
|
|
|
@ -1,50 +1,19 @@
|
||||||
|
import click
|
||||||
|
import sys
|
||||||
import asyncio
|
import asyncio
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
from src.daemon.client import connect_to_daemon_and_validate
|
from src.daemon.client import connect_to_daemon_and_validate
|
||||||
from src.util.service_groups import all_groups, services_for_groups
|
from src.util.service_groups import all_groups, services_for_groups
|
||||||
|
|
||||||
|
|
||||||
def make_parser(parser):
|
async def async_stop(root_path: Path, group: str, stop_daemon: bool) -> int:
|
||||||
|
daemon = await connect_to_daemon_and_validate(root_path)
|
||||||
parser.add_argument(
|
|
||||||
"-d",
|
|
||||||
"--daemon",
|
|
||||||
action="store_true",
|
|
||||||
help="Stop daemon",
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"group",
|
|
||||||
choices=all_groups(),
|
|
||||||
type=str,
|
|
||||||
nargs="+",
|
|
||||||
default=None,
|
|
||||||
)
|
|
||||||
|
|
||||||
parser.set_defaults(function=stop)
|
|
||||||
|
|
||||||
|
|
||||||
async def stop_daemon(daemon):
|
|
||||||
service = "daemon"
|
|
||||||
print(f"{service}: ", end="", flush=True)
|
|
||||||
if daemon is None:
|
|
||||||
print("not running")
|
|
||||||
return
|
|
||||||
r = await daemon.exit()
|
|
||||||
exited = r["data"]["success"]
|
|
||||||
if exited:
|
|
||||||
print("Daemon stopped")
|
|
||||||
else:
|
|
||||||
error = r["data"]["error"]
|
|
||||||
print(f"error: {error}")
|
|
||||||
|
|
||||||
|
|
||||||
async def async_stop(args, parser):
|
|
||||||
daemon = await connect_to_daemon_and_validate(args.root_path)
|
|
||||||
if daemon is None:
|
if daemon is None:
|
||||||
print("couldn't connect to chia daemon")
|
print("couldn't connect to chia daemon")
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
if args.daemon:
|
if stop_daemon:
|
||||||
r = await daemon.exit()
|
r = await daemon.exit()
|
||||||
await daemon.close()
|
await daemon.close()
|
||||||
print(f"daemon: {r}")
|
print(f"daemon: {r}")
|
||||||
|
@ -52,7 +21,7 @@ async def async_stop(args, parser):
|
||||||
|
|
||||||
return_val = 0
|
return_val = 0
|
||||||
|
|
||||||
for service in services_for_groups(args.group):
|
for service in services_for_groups(group):
|
||||||
print(f"{service}: ", end="", flush=True)
|
print(f"{service}: ", end="", flush=True)
|
||||||
if not await daemon.is_running(service_name=service):
|
if not await daemon.is_running(service_name=service):
|
||||||
print("not running")
|
print("not running")
|
||||||
|
@ -66,5 +35,9 @@ async def async_stop(args, parser):
|
||||||
return return_val
|
return return_val
|
||||||
|
|
||||||
|
|
||||||
def stop(args, parser):
|
@click.command("stop", short_help="stop service groups")
|
||||||
return asyncio.get_event_loop().run_until_complete(async_stop(args, parser))
|
@click.option("-d", "--daemon", is_flag=True, type=bool, help="Stop daemon")
|
||||||
|
@click.argument("group", type=click.Choice(all_groups()), nargs=-1, required=True)
|
||||||
|
@click.pass_context
|
||||||
|
def stop_cmd(ctx: click.Context, daemon: bool, group: str) -> None:
|
||||||
|
sys.exit(asyncio.get_event_loop().run_until_complete(async_stop(ctx.obj["root_path"], group, daemon)))
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
from src import __version__
|
|
||||||
|
|
||||||
|
|
||||||
def version(args, parser):
|
|
||||||
print(__version__)
|
|
||||||
|
|
||||||
|
|
||||||
def make_parser(parser):
|
|
||||||
parser.set_defaults(function=version)
|
|
||||||
return parser
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import click
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
@ -11,77 +12,14 @@ from src.util.bech32m import encode_puzzle_hash
|
||||||
from src.util.byte_types import hexstr_to_bytes
|
from src.util.byte_types import hexstr_to_bytes
|
||||||
from src.util.config import load_config
|
from src.util.config import load_config
|
||||||
from src.util.default_root import DEFAULT_ROOT_PATH
|
from src.util.default_root import DEFAULT_ROOT_PATH
|
||||||
from src.util.ints import uint64
|
from src.util.ints import uint64, uint16
|
||||||
from src.wallet.transaction_record import TransactionRecord
|
from src.wallet.transaction_record import TransactionRecord
|
||||||
from src.wallet.util.wallet_types import WalletType
|
from src.wallet.util.wallet_types import WalletType
|
||||||
from src.cmds.units import units
|
from src.cmds.units import units
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
|
|
||||||
command_list = ["send", "show", "get_transaction", "get_transactions"]
|
def print_transaction(tx: TransactionRecord, verbose: bool) -> None:
|
||||||
|
|
||||||
|
|
||||||
def help_message():
|
|
||||||
print("usage: chia wallet command")
|
|
||||||
print(f"command can be any of {command_list}")
|
|
||||||
print("")
|
|
||||||
print(
|
|
||||||
"chia wallet send -f [optional fingerprint] -i [optional wallet_id] -a [(T)XCH amount] -m [(T)XCH fee] "
|
|
||||||
"-t [target address]"
|
|
||||||
)
|
|
||||||
print("chia wallet show -f [optional fingerprint] -i [optional wallet_id]")
|
|
||||||
print("chia wallet get_transaction -f [optional fingerprint] -i [optional wallet_id] -tx [transaction id]")
|
|
||||||
print("chia wallet get_transactions -f [optional fingerprint] -i [optional wallet_id]")
|
|
||||||
|
|
||||||
|
|
||||||
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.add_argument(
|
|
||||||
"-f",
|
|
||||||
"--fingerprint",
|
|
||||||
help="Set the fingerprint to specify which wallet to use.",
|
|
||||||
type=int,
|
|
||||||
)
|
|
||||||
parser.add_argument("-i", "--id", help="Id of the wallet to use.", type=int, default=1)
|
|
||||||
parser.add_argument(
|
|
||||||
"-a",
|
|
||||||
"--amount",
|
|
||||||
help="How much chia to send, in TXCH/XCH",
|
|
||||||
type=str,
|
|
||||||
)
|
|
||||||
parser.add_argument("-m", "--fee", help="Set the fees for the transaction.", type=str, default="0")
|
|
||||||
parser.add_argument(
|
|
||||||
"-t",
|
|
||||||
"--address",
|
|
||||||
help="Address to send the TXCH/XCH",
|
|
||||||
type=str,
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"-tx",
|
|
||||||
"--tx_id",
|
|
||||||
help="transaction id to search for",
|
|
||||||
type=str,
|
|
||||||
)
|
|
||||||
parser.add_argument("--verbose", "-v", action="count", default=0)
|
|
||||||
parser.add_argument(
|
|
||||||
"command",
|
|
||||||
help=f"Command can be any one of {command_list}",
|
|
||||||
type=str,
|
|
||||||
nargs="?",
|
|
||||||
)
|
|
||||||
parser.set_defaults(function=handler)
|
|
||||||
parser.print_help = lambda self=parser: help_message()
|
|
||||||
|
|
||||||
|
|
||||||
def print_transaction(tx: TransactionRecord, verbose: bool):
|
|
||||||
if verbose:
|
if verbose:
|
||||||
print(tx)
|
print(tx)
|
||||||
else:
|
else:
|
||||||
|
@ -95,33 +33,21 @@ def print_transaction(tx: TransactionRecord, verbose: bool):
|
||||||
print("")
|
print("")
|
||||||
|
|
||||||
|
|
||||||
async def get_transaction(args, wallet_client, fingerprint: int):
|
async def get_transaction(args: dict, wallet_client: WalletRpcClient, fingerprint: int) -> None:
|
||||||
if args.id is None:
|
wallet_id = args["id"]
|
||||||
print("Please specify a wallet id with -i")
|
transaction_id = hexstr_to_bytes(args["tx_id"])
|
||||||
return
|
|
||||||
else:
|
|
||||||
wallet_id = args.id
|
|
||||||
if args.tx_id is None:
|
|
||||||
print("Please specify a transaction id -tx")
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
transaction_id = hexstr_to_bytes(args.tx_id)
|
|
||||||
tx: TransactionRecord = await wallet_client.get_transaction(wallet_id, transaction_id=transaction_id)
|
tx: TransactionRecord = await wallet_client.get_transaction(wallet_id, transaction_id=transaction_id)
|
||||||
print_transaction(tx, verbose=(args.verbose > 0))
|
print_transaction(tx, verbose=(args["verbose"] > 0))
|
||||||
|
|
||||||
|
|
||||||
async def get_transactions(args, wallet_client, fingerprint: int):
|
async def get_transactions(args: dict, wallet_client: WalletRpcClient, fingerprint: int) -> None:
|
||||||
if args.id is None:
|
wallet_id = args["id"]
|
||||||
print("Please specify a wallet id with -i")
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
wallet_id = args.id
|
|
||||||
txs: List[TransactionRecord] = await wallet_client.get_transactions(wallet_id)
|
txs: List[TransactionRecord] = await wallet_client.get_transactions(wallet_id)
|
||||||
if len(txs) == 0:
|
if len(txs) == 0:
|
||||||
print("There are no transactions to this address")
|
print("There are no transactions to this address")
|
||||||
for i in range(0, len(txs), 5):
|
for i in range(0, len(txs), 5):
|
||||||
for j in range(0, 5):
|
for j in range(0, 5):
|
||||||
print_transaction(txs[i + j], verbose=(args.verbose > 0))
|
print_transaction(txs[i + j], verbose=(args["verbose"] > 0))
|
||||||
print("Press q to quit, or c to continue")
|
print("Press q to quit, or c to continue")
|
||||||
while True:
|
while True:
|
||||||
entered_key = sys.stdin.read(1)
|
entered_key = sys.stdin.read(1)
|
||||||
|
@ -131,27 +57,11 @@ async def get_transactions(args, wallet_client, fingerprint: int):
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
||||||
async def send(args, wallet_client, fingerprint: int):
|
async def send(args: dict, wallet_client: WalletRpcClient, fingerprint: int) -> None:
|
||||||
if args.id is None:
|
wallet_id = args["id"]
|
||||||
print("Please specify a wallet id with -i")
|
amount = Decimal(args["amount"])
|
||||||
return
|
fee = Decimal(args["fee"])
|
||||||
else:
|
address = args["address"]
|
||||||
wallet_id = args.id
|
|
||||||
if args.amount is None:
|
|
||||||
print("Please specify an amount with -a")
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
amount = Decimal(args.amount)
|
|
||||||
if args.amount is None:
|
|
||||||
print("Please specify the transaction fees with -m")
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
fee = Decimal(args.fee)
|
|
||||||
if args.address is None:
|
|
||||||
print("Please specify a target address with -t")
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
address = args.address
|
|
||||||
|
|
||||||
print("Submitting transaction...")
|
print("Submitting transaction...")
|
||||||
final_amount = uint64(int(amount * units["chia"]))
|
final_amount = uint64(int(amount * units["chia"]))
|
||||||
|
@ -171,7 +81,7 @@ async def send(args, wallet_client, fingerprint: int):
|
||||||
print(f"Do 'chia wallet get_transaction -f {fingerprint} -tx 0x{tx_id}' to get status")
|
print(f"Do 'chia wallet get_transaction -f {fingerprint} -tx 0x{tx_id}' to get status")
|
||||||
|
|
||||||
|
|
||||||
async def print_balances(args, wallet_client, fingerprint: int):
|
async def print_balances(args: dict, wallet_client: WalletRpcClient, fingerprint: int) -> None:
|
||||||
summaries_response = await wallet_client.get_wallets()
|
summaries_response = await wallet_client.get_wallets()
|
||||||
|
|
||||||
print(f"Wallet height: {await wallet_client.get_height_info()}")
|
print(f"Wallet height: {await wallet_client.get_height_info()}")
|
||||||
|
@ -206,7 +116,7 @@ async def print_balances(args, wallet_client, fingerprint: int):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def get_wallet(wallet_client, fingerprint=None) -> Optional[Tuple[WalletRpcClient, int]]:
|
async def get_wallet(wallet_client: WalletRpcClient, fingerprint: int = None) -> Optional[Tuple[WalletRpcClient, int]]:
|
||||||
fingerprints = await wallet_client.get_public_keys()
|
fingerprints = await wallet_client.get_public_keys()
|
||||||
if len(fingerprints) == 0:
|
if len(fingerprints) == 0:
|
||||||
print("No keys loaded. Run 'chia keys generate' or import a key.")
|
print("No keys loaded. Run 'chia keys generate' or import a key.")
|
||||||
|
@ -238,7 +148,9 @@ async def get_wallet(wallet_client, fingerprint=None) -> Optional[Tuple[WalletRp
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
fingerprint = fingerprints[index]
|
fingerprint = fingerprints[index]
|
||||||
|
assert fingerprint is not None
|
||||||
log_in_response = await wallet_client.log_in(fingerprint)
|
log_in_response = await wallet_client.log_in(fingerprint)
|
||||||
|
|
||||||
if log_in_response["success"] is False:
|
if log_in_response["success"] is False:
|
||||||
if log_in_response["error"] == "not_initialized":
|
if log_in_response["error"] == "not_initialized":
|
||||||
use_cloud = True
|
use_cloud = True
|
||||||
|
@ -277,31 +189,24 @@ async def get_wallet(wallet_client, fingerprint=None) -> Optional[Tuple[WalletRp
|
||||||
return wallet_client, fingerprint
|
return wallet_client, fingerprint
|
||||||
|
|
||||||
|
|
||||||
async def execute_with_wallet(args, parser, function: Callable):
|
async def execute_with_wallet(wallet_rpc_port: int, fingerprint: int, extra_params: dict, function: Callable) -> None:
|
||||||
if args.fingerprint is None:
|
|
||||||
fingerprint = None
|
|
||||||
else:
|
|
||||||
fingerprint = args.fingerprint
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
config = load_config(DEFAULT_ROOT_PATH, "config.yaml")
|
config = load_config(DEFAULT_ROOT_PATH, "config.yaml")
|
||||||
self_hostname = config["self_hostname"]
|
self_hostname = config["self_hostname"]
|
||||||
if "wallet_rpc_port" not in args or args.wallet_rpc_port is None:
|
if wallet_rpc_port is None:
|
||||||
wallet_rpc_port = config["wallet"]["rpc_port"]
|
wallet_rpc_port = config["wallet"]["rpc_port"]
|
||||||
else:
|
wallet_client = await WalletRpcClient.create(self_hostname, uint16(wallet_rpc_port), DEFAULT_ROOT_PATH, config)
|
||||||
wallet_rpc_port = args.wallet_rpc_port
|
|
||||||
wallet_client = await WalletRpcClient.create(self_hostname, wallet_rpc_port, DEFAULT_ROOT_PATH, config)
|
|
||||||
wallet_client_f = await get_wallet(wallet_client, fingerprint=fingerprint)
|
wallet_client_f = await get_wallet(wallet_client, fingerprint=fingerprint)
|
||||||
if wallet_client_f is None:
|
if wallet_client_f is None:
|
||||||
wallet_client.close()
|
wallet_client.close()
|
||||||
await wallet_client.await_closed()
|
await wallet_client.await_closed()
|
||||||
return
|
return
|
||||||
wallet_client, fingerprint = wallet_client_f
|
wallet_client, fingerprint = wallet_client_f
|
||||||
await function(args, wallet_client, fingerprint)
|
await function(extra_params, wallet_client, fingerprint)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if isinstance(e, aiohttp.client_exceptions.ClientConnectorError):
|
if isinstance(e, aiohttp.client_exceptions.ClientConnectorError):
|
||||||
print(f"Connection error. Check if wallet is running at {args.wallet_rpc_port}")
|
print(f"Connection error. Check if wallet is running at {wallet_rpc_port}")
|
||||||
else:
|
else:
|
||||||
print(f"Exception from 'wallet' {e}")
|
print(f"Exception from 'wallet' {e}")
|
||||||
|
|
||||||
|
@ -309,20 +214,76 @@ async def execute_with_wallet(args, parser, function: Callable):
|
||||||
await wallet_client.await_closed()
|
await wallet_client.await_closed()
|
||||||
|
|
||||||
|
|
||||||
def handler(args, parser):
|
@click.group("wallet", short_help="manage your wallet")
|
||||||
if args.command is None or len(args.command) < 1:
|
def wallet_cmd() -> None:
|
||||||
help_message()
|
pass
|
||||||
parser.exit(1)
|
|
||||||
command = args.command
|
|
||||||
if command not in command_list:
|
|
||||||
help_message()
|
|
||||||
parser.exit(1)
|
|
||||||
|
|
||||||
if command == "get_transaction":
|
|
||||||
return asyncio.run(execute_with_wallet(args, parser, get_transaction))
|
@wallet_cmd.command("get_transaction", short_help="get transaction")
|
||||||
if command == "get_transactions":
|
@click.option(
|
||||||
return asyncio.run(execute_with_wallet(args, parser, get_transactions))
|
"-wp",
|
||||||
if command == "send":
|
"--wallet-rpc-port",
|
||||||
return asyncio.run(execute_with_wallet(args, parser, send))
|
help="Set the port where the Wallet is hosting the RPC interface. See the rpc_port under wallet in config.yaml.",
|
||||||
elif command == "show":
|
type=int,
|
||||||
return asyncio.run(execute_with_wallet(args, parser, print_balances))
|
default=9256,
|
||||||
|
show_default=True,
|
||||||
|
)
|
||||||
|
@click.option("-f", "--fingerprint", help="Set the fingerprint to specify which wallet to use.", type=int)
|
||||||
|
@click.option("-i", "--id", help="Id of the wallet to use.", type=int, default=1, show_default=True, required=True)
|
||||||
|
@click.option("-tx", "--tx_id", help="transaction id to search for", type=str, required=True)
|
||||||
|
@click.option("--verbose", "-v", count=True, type=int)
|
||||||
|
def get_transaction_cmd(wallet_rpc_port: int, fingerprint: int, id: int, tx_id: str, verbose: int) -> None:
|
||||||
|
extra_params = {"id": id, "tx_id": tx_id, "verbose": verbose}
|
||||||
|
asyncio.run(execute_with_wallet(wallet_rpc_port, fingerprint, extra_params, get_transaction))
|
||||||
|
|
||||||
|
|
||||||
|
@wallet_cmd.command("get_transactions", short_help="get all transactions")
|
||||||
|
@click.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=9256,
|
||||||
|
show_default=True,
|
||||||
|
)
|
||||||
|
@click.option("-f", "--fingerprint", help="Set the fingerprint to specify which wallet to use.", type=int)
|
||||||
|
@click.option("-i", "--id", help="Id of the wallet to use.", type=int, default=1, show_default=True, required=True)
|
||||||
|
@click.option("--verbose", "-v", count=True, type=int)
|
||||||
|
def get_transactions_cmd(wallet_rpc_port: int, fingerprint: int, id: int, verbose: bool) -> None:
|
||||||
|
extra_params = {"id": id, "verbose": verbose}
|
||||||
|
asyncio.run(execute_with_wallet(wallet_rpc_port, fingerprint, extra_params, get_transactions))
|
||||||
|
|
||||||
|
|
||||||
|
@wallet_cmd.command("send", short_help="send chia to other wallet")
|
||||||
|
@click.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=9256,
|
||||||
|
show_default=True,
|
||||||
|
)
|
||||||
|
@click.option("-f", "--fingerprint", help="Set the fingerprint to specify which wallet to use.", type=int)
|
||||||
|
@click.option("-i", "--id", help="Id of the wallet to use.", type=int, default=1, show_default=True, required=True)
|
||||||
|
@click.option("-a", "--amount", help="How much chia to send, in TXCH/XCH", type=str, required=True)
|
||||||
|
@click.option(
|
||||||
|
"-m", "--fee", help="Set the fees for the transaction.", type=str, default="0", show_default=True, required=True
|
||||||
|
)
|
||||||
|
@click.option("-t", "--address", help="Address to send the TXCH/XCH", type=str, required=True)
|
||||||
|
def send_cmd(wallet_rpc_port: int, fingerprint: int, id: int, amount: str, fee: str, address: str) -> None:
|
||||||
|
extra_params = {"id": id, "amount": amount, "fee": fee, "address": address}
|
||||||
|
asyncio.run(execute_with_wallet(wallet_rpc_port, fingerprint, extra_params, send))
|
||||||
|
|
||||||
|
|
||||||
|
@wallet_cmd.command("show", short_help="show wallet information")
|
||||||
|
@click.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=9256,
|
||||||
|
show_default=True,
|
||||||
|
)
|
||||||
|
@click.option("-f", "--fingerprint", help="Set the fingerprint to specify which wallet to use.", type=int)
|
||||||
|
def show_cmd(wallet_rpc_port: int, fingerprint: int) -> None:
|
||||||
|
asyncio.run(execute_with_wallet(wallet_rpc_port, fingerprint, {}, print_balances))
|
||||||
|
|
|
@ -13,10 +13,9 @@ from src.wallet.derive_keys import master_sk_to_farmer_sk
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def check_plots(args, root_path):
|
def check_plots(root_path, num, challenge_start, grep_string, list_duplicates, debug_show_memo):
|
||||||
config = load_config(root_path, "config.yaml")
|
config = load_config(root_path, "config.yaml")
|
||||||
if args.num is not None:
|
if num is not None:
|
||||||
num = args.num
|
|
||||||
if num == 0:
|
if num == 0:
|
||||||
log.warning("Not opening plot files")
|
log.warning("Not opening plot files")
|
||||||
else:
|
else:
|
||||||
|
@ -28,25 +27,25 @@ def check_plots(args, root_path):
|
||||||
else:
|
else:
|
||||||
num = 30
|
num = 30
|
||||||
|
|
||||||
if args.challenge_start is not None:
|
if challenge_start is not None:
|
||||||
num_start = args.challenge_start
|
num_start = challenge_start
|
||||||
num_end = num_start + num
|
num_end = num_start + num
|
||||||
else:
|
else:
|
||||||
num_start = 0
|
num_start = 0
|
||||||
num_end = num
|
num_end = num
|
||||||
challenges = num_end - num_start
|
challenges = num_end - num_start
|
||||||
|
|
||||||
if args.grep_string is not None:
|
if grep_string is not None:
|
||||||
match_str = args.grep_string
|
match_str = grep_string
|
||||||
else:
|
else:
|
||||||
match_str = None
|
match_str = None
|
||||||
if args.list_duplicates:
|
if list_duplicates:
|
||||||
log.warning("Checking for duplicate Plot IDs")
|
log.warning("Checking for duplicate Plot IDs")
|
||||||
log.info("Plot filenames expected to end with -[64 char plot ID].plot")
|
log.info("Plot filenames expected to end with -[64 char plot ID].plot")
|
||||||
|
|
||||||
show_memo: bool = args.debug_show_memo
|
show_memo: bool = debug_show_memo
|
||||||
|
|
||||||
if args.list_duplicates:
|
if list_duplicates:
|
||||||
plot_filenames: Dict[Path, List[Path]] = get_plot_filenames(config["harvester"])
|
plot_filenames: Dict[Path, List[Path]] = get_plot_filenames(config["harvester"])
|
||||||
all_filenames: List[Path] = []
|
all_filenames: List[Path] = []
|
||||||
for paths in plot_filenames.values():
|
for paths in plot_filenames.values():
|
||||||
|
|
|
@ -78,10 +78,7 @@ def create_plots(args, root_path, use_datetime=True, test_private_keys: Optional
|
||||||
pool_contract_puzzle_hash = decode_puzzle_hash(args.pool_contract_address)
|
pool_contract_puzzle_hash = decode_puzzle_hash(args.pool_contract_address)
|
||||||
|
|
||||||
assert (pool_public_key is None) != (pool_contract_puzzle_hash is None)
|
assert (pool_public_key is None) != (pool_contract_puzzle_hash is None)
|
||||||
if args.num is not None:
|
num = args.num
|
||||||
num = args.num
|
|
||||||
else:
|
|
||||||
num = 1
|
|
||||||
|
|
||||||
if args.size < config["min_mainnet_k_size"] and test_private_keys is None:
|
if args.size < config["min_mainnet_k_size"] and test_private_keys is None:
|
||||||
log.warning(f"Creating plots with size k={args.size}, which is less than the minimum required for mainnet")
|
log.warning(f"Creating plots with size k={args.size}, which is less than the minimum required for mainnet")
|
||||||
|
|
|
@ -112,7 +112,7 @@ class WalletNode:
|
||||||
self.logged_in_fingerprint: Optional[int] = None
|
self.logged_in_fingerprint: Optional[int] = None
|
||||||
self.peer_task = None
|
self.peer_task = None
|
||||||
|
|
||||||
def get_key_for_fingerprint(self, fingerprint):
|
def get_key_for_fingerprint(self, fingerprint: Optional[int]):
|
||||||
private_keys = self.keychain.get_all_private_keys()
|
private_keys = self.keychain.get_all_private_keys()
|
||||||
if len(private_keys) == 0:
|
if len(private_keys) == 0:
|
||||||
self.log.warning("No keys present. Create keys with the UI, or with the 'chia keys' program.")
|
self.log.warning("No keys present. Create keys with the UI, or with the 'chia keys' program.")
|
||||||
|
|
Loading…
Reference in a new issue