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:
Jesús Espino 2021-02-23 11:23:56 +01:00 committed by GitHub
parent dd18113d4d
commit d8410eff83
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 660 additions and 915 deletions

View file

@ -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 = [

View file

@ -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__":

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

@ -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))

View file

@ -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"])

View file

@ -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))

View file

@ -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,
)
)

View file

@ -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))

View file

@ -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)))

View file

@ -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

View file

@ -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))

View file

@ -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():

View file

@ -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")

View file

@ -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.")