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
|
||||
"sortedcontainers==2.3.0", # For maintaining sorted mempools
|
||||
"websockets==8.1.0", # For use in wallet RPC and electron UI
|
||||
"click==7.1.2", # For the CLI
|
||||
]
|
||||
|
||||
upnp_dependencies = [
|
||||
|
|
|
@ -1,62 +1,61 @@
|
|||
import importlib
|
||||
import pathlib
|
||||
from argparse import Namespace, ArgumentParser
|
||||
import asyncio
|
||||
import click
|
||||
from pathlib import Path
|
||||
|
||||
from src import __version__
|
||||
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 = [
|
||||
"init",
|
||||
"keys",
|
||||
"show",
|
||||
"start",
|
||||
"stop",
|
||||
"version",
|
||||
"plots",
|
||||
"netspace",
|
||||
"run_daemon",
|
||||
"wallet",
|
||||
"configure",
|
||||
]
|
||||
CONTEXT_SETTINGS = dict(help_option_names=["-h", "--help"])
|
||||
|
||||
|
||||
def create_parser() -> ArgumentParser:
|
||||
parser: ArgumentParser = ArgumentParser(
|
||||
description="Manage chia blockchain infrastructure (%s)." % __version__,
|
||||
@click.group(
|
||||
help=f"\n Manage chia blockchain infrastructure ({__version__})\n",
|
||||
epilog="Try 'chia start node', 'chia netspace -d 192', or 'chia show -s'.",
|
||||
context_settings=CONTEXT_SETTINGS,
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--root-path",
|
||||
help="Config file root (defaults to %s)." % DEFAULT_ROOT_PATH,
|
||||
type=pathlib.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
|
||||
@click.option("--root-path", default=DEFAULT_ROOT_PATH, help="Config file root.", type=click.Path(), show_default=True)
|
||||
@click.pass_context
|
||||
def cli(ctx: click.Context, root_path: str) -> None:
|
||||
ctx.ensure_object(dict)
|
||||
ctx.obj["root_path"] = Path(root_path)
|
||||
|
||||
|
||||
def chia(args: Namespace, parser: ArgumentParser):
|
||||
return args.function(args, parser)
|
||||
@cli.command("version", short_help="show version")
|
||||
def version_cmd() -> None:
|
||||
print(__version__)
|
||||
|
||||
|
||||
def main():
|
||||
parser = create_parser()
|
||||
args = parser.parse_args()
|
||||
return chia(args, parser)
|
||||
@cli.command("run_daemon", short_help="runs chia daemon")
|
||||
@click.pass_context
|
||||
def run_daemon_cmd(ctx: click.Context) -> None:
|
||||
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__":
|
||||
|
|
|
@ -1,76 +1,24 @@
|
|||
import click
|
||||
from pathlib import Path
|
||||
|
||||
from src.util.config import (
|
||||
load_config,
|
||||
save_config,
|
||||
)
|
||||
from argparse import ArgumentParser
|
||||
from typing import Dict
|
||||
from src.util.default_root import DEFAULT_ROOT_PATH
|
||||
from src.util.config import str2bool
|
||||
|
||||
|
||||
def make_parser(parser: ArgumentParser):
|
||||
|
||||
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):
|
||||
def configure(root_path: Path, set_node_introducer: str, set_fullnode_port: str, set_log_level: str, enable_upnp: str):
|
||||
config: Dict = load_config(DEFAULT_ROOT_PATH, "config.yaml")
|
||||
change_made = False
|
||||
if args.set_node_introducer:
|
||||
if set_node_introducer:
|
||||
try:
|
||||
if args.set_node_introducer.index(":"):
|
||||
if set_node_introducer.index(":"):
|
||||
host, port = (
|
||||
":".join(args.set_node_introducer.split(":")[:-1]),
|
||||
args.set_node_introducer.split(":")[-1],
|
||||
":".join(set_node_introducer.split(":")[:-1]),
|
||||
set_node_introducer.split(":")[-1],
|
||||
)
|
||||
config["full_node"]["introducer_peer"]["host"] = host
|
||||
config["full_node"]["introducer_peer"]["port"] = int(port)
|
||||
|
@ -79,34 +27,52 @@ def configure(args, parser):
|
|||
change_made = True
|
||||
except ValueError:
|
||||
print("Node introducer address must be in format [IP:Port]")
|
||||
if args.set_fullnode_port:
|
||||
config["full_node"]["port"] = int(args.set_fullnode_port)
|
||||
config["full_node"]["introducer_peer"]["port"] = int(args.set_fullnode_port)
|
||||
config["farmer"]["full_node_peer"]["port"] = int(args.set_fullnode_port)
|
||||
config["timelord"]["full_node_peer"]["port"] = int(args.set_fullnode_port)
|
||||
config["wallet"]["full_node_peer"]["port"] = int(args.set_fullnode_port)
|
||||
config["wallet"]["introducer_peer"]["port"] = int(args.set_fullnode_port)
|
||||
config["introducer"]["port"] = int(args.set_fullnode_port)
|
||||
if set_fullnode_port:
|
||||
config["full_node"]["port"] = int(set_fullnode_port)
|
||||
config["full_node"]["introducer_peer"]["port"] = int(set_fullnode_port)
|
||||
config["farmer"]["full_node_peer"]["port"] = int(set_fullnode_port)
|
||||
config["timelord"]["full_node_peer"]["port"] = int(set_fullnode_port)
|
||||
config["wallet"]["full_node_peer"]["port"] = int(set_fullnode_port)
|
||||
config["wallet"]["introducer_peer"]["port"] = int(set_fullnode_port)
|
||||
config["introducer"]["port"] = int(set_fullnode_port)
|
||||
print("Default full node port updated.")
|
||||
change_made = True
|
||||
if args.set_log_level:
|
||||
if set_log_level:
|
||||
levels = ["CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG", "NOTSET"]
|
||||
if args.set_log_level in levels:
|
||||
config["logging"]["log_level"] = args.set_log_level
|
||||
if set_log_level in levels:
|
||||
config["logging"]["log_level"] = set_log_level
|
||||
print(f"Logging level updated. Check {DEFAULT_ROOT_PATH}/log/debug.log")
|
||||
change_made = True
|
||||
else:
|
||||
print(f"Logging level not updated. Use one of: {levels}")
|
||||
if args.enable_upnp is not None:
|
||||
config["full_node"]["enable_upnp"] = str2bool(args.enable_upnp)
|
||||
if str2bool(args.enable_upnp):
|
||||
if enable_upnp is not None:
|
||||
config["full_node"]["enable_upnp"] = str2bool(enable_upnp)
|
||||
if str2bool(enable_upnp):
|
||||
print("uPnP enabled.")
|
||||
else:
|
||||
print("uPnP disabled.")
|
||||
change_made = True
|
||||
if change_made:
|
||||
print("Restart any running chia services for changes to take effect.")
|
||||
save_config(args.root_path, "config.yaml", config)
|
||||
else:
|
||||
help_message()
|
||||
save_config(root_path, "config.yaml", config)
|
||||
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 pathlib import Path
|
||||
import os
|
||||
import shutil
|
||||
|
||||
from argparse import Namespace, ArgumentParser
|
||||
from typing import List, Dict, Any, Tuple
|
||||
|
||||
from src.util.default_root import DEFAULT_ROOT_PATH
|
||||
from src.util.keychain import Keychain
|
||||
|
||||
from src.util.config import unflatten_properties
|
||||
from pathlib import Path
|
||||
from src.consensus.coinbase import create_puzzlehash_for_pk
|
||||
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"}
|
||||
|
||||
|
||||
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]):
|
||||
for k in do_not_migrate_keys:
|
||||
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)
|
||||
|
||||
|
||||
def init(args: Namespace, parser: ArgumentParser):
|
||||
if args.create_certs is not None:
|
||||
if args.root_path.exists():
|
||||
if os.path.isdir(args.create_certs):
|
||||
ca_dir: Path = args.root_path / "config/ssl/ca"
|
||||
def init(create_certs: Path, root_path: Path):
|
||||
if create_certs is not None:
|
||||
if root_path.exists():
|
||||
if os.path.isdir(create_certs):
|
||||
ca_dir: Path = root_path / "config/ssl/ca"
|
||||
if ca_dir.exists():
|
||||
print(f"Deleting your OLD CA in {ca_dir}")
|
||||
shutil.rmtree(ca_dir)
|
||||
print(f"Copying your CA from {args.create_certs} to {ca_dir}")
|
||||
copy_files_rec(args.create_certs, ca_dir)
|
||||
create_all_ssl(args.root_path)
|
||||
print(f"Copying your CA from {create_certs} to {ca_dir}")
|
||||
copy_files_rec(create_certs, ca_dir)
|
||||
create_all_ssl(root_path)
|
||||
else:
|
||||
print(f"** Directory {args.create_certs} does not exist **")
|
||||
print(f"** Directory {create_certs} does not exist **")
|
||||
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 **")
|
||||
else:
|
||||
return chia_init(args.root_path)
|
||||
return chia_init(root_path)
|
||||
|
||||
|
||||
def chia_version_number() -> Tuple[str, str, str, str]:
|
||||
|
@ -442,5 +413,30 @@ def chia_init(root_path: Path):
|
|||
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__":
|
||||
chia_init(DEFAULT_ROOT_PATH)
|
||||
|
|
234
src/cmds/keys.py
234
src/cmds/keys.py
|
@ -1,3 +1,5 @@
|
|||
import click
|
||||
|
||||
from pathlib import Path
|
||||
from typing import List
|
||||
|
||||
|
@ -18,97 +20,6 @@ from src.wallet.derive_keys import (
|
|||
from src.util.ints import uint32
|
||||
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()
|
||||
|
||||
|
@ -135,7 +46,7 @@ def generate_and_add():
|
|||
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.
|
||||
"""
|
||||
|
@ -186,39 +97,18 @@ def show_all_keys():
|
|||
print(mnemonic)
|
||||
|
||||
|
||||
def delete(args):
|
||||
def delete(fingerprint: 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}")
|
||||
keychain.delete_key_by_fingerprint(fingerprint)
|
||||
|
||||
|
||||
def sign(args):
|
||||
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
|
||||
|
||||
def sign(message: str, fingerprint: int, hd_path: str):
|
||||
k = Keychain()
|
||||
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"]
|
||||
for sk, _ in private_keys:
|
||||
if sk.get_g1().get_fingerprint() == fingerprint:
|
||||
|
@ -230,55 +120,85 @@ def sign(args):
|
|||
print(f"Fingerprint {fingerprint} not found in keychain")
|
||||
|
||||
|
||||
def verify(args):
|
||||
if args.message is None:
|
||||
print("Please specify the message argument -d")
|
||||
quit()
|
||||
if args.public_key is None:
|
||||
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 verify(message: str, public_key: str, signature: str):
|
||||
messageBytes = bytes(message, "utf-8")
|
||||
public_key = G1Element.from_bytes(bytes.fromhex(public_key))
|
||||
signature = G2Element.from_bytes(bytes.fromhex(signature))
|
||||
print(AugSchemeMPL.verify(public_key, messageBytes, signature))
|
||||
|
||||
|
||||
def handler(args, parser):
|
||||
if args.command is None or len(args.command) < 1:
|
||||
help_message()
|
||||
parser.exit(1)
|
||||
|
||||
root_path: Path = args.root_path
|
||||
@click.group("keys", short_help="manage your keys")
|
||||
@click.pass_context
|
||||
def keys_cmd(ctx: click.Context):
|
||||
"""Create, delete, view and use your key pairs"""
|
||||
root_path: Path = ctx.obj["root_path"]
|
||||
if not root_path.is_dir():
|
||||
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")
|
||||
@click.pass_context
|
||||
def generate_cmd(ctx: click.Context):
|
||||
generate_and_add()
|
||||
check_keys(root_path)
|
||||
elif command == "show":
|
||||
check_keys(ctx.obj["root_path"])
|
||||
|
||||
|
||||
@keys_cmd.command("show", short_help="displays all the keys in keychain")
|
||||
def show_cmd():
|
||||
show_all_keys()
|
||||
elif command == "add":
|
||||
add_private_key_seed(" ".join(args.mnemonic))
|
||||
check_keys(root_path)
|
||||
elif command == "delete":
|
||||
delete(args)
|
||||
check_keys(root_path)
|
||||
elif command == "delete_all":
|
||||
|
||||
|
||||
@keys_cmd.command("add", short_help="add a private key through the mnemonic")
|
||||
@click.option("--mnemonic", "-m", help="Enter mnemonic you want to use", type=str)
|
||||
@click.pass_context
|
||||
def add_cmd(ctx: click.Context, mnemonic: str):
|
||||
add_private_key_seed(mnemonic)
|
||||
check_keys(ctx.obj["root_path"])
|
||||
|
||||
|
||||
@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()
|
||||
if command == "generate_and_print":
|
||||
|
||||
|
||||
@keys_cmd.command("generate_and_print", short_help="generates but does NOT add to keychain")
|
||||
def generate_and_print_cmd():
|
||||
generate_and_print()
|
||||
if command == "sign":
|
||||
sign(args)
|
||||
if command == "verify":
|
||||
verify(args)
|
||||
|
||||
|
||||
@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 asyncio
|
||||
import time
|
||||
from time import struct_time, localtime
|
||||
from src.util.config import load_config
|
||||
from src.util.default_root import DEFAULT_ROOT_PATH
|
||||
from src.util.byte_types import hexstr_to_bytes
|
||||
from src.util.ints import uint16
|
||||
|
||||
from src.rpc.full_node_rpc_client import FullNodeRpcClient
|
||||
|
||||
|
||||
def make_parser(parser):
|
||||
|
||||
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):
|
||||
async def netstorge_async(rpc_port: int, delta_block_height: str, start: str) -> None:
|
||||
"""
|
||||
Calculates the estimated space on the network given two block header hases
|
||||
# TODO: add help on failure/no args
|
||||
"""
|
||||
try:
|
||||
config = load_config(DEFAULT_ROOT_PATH, "config.yaml")
|
||||
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"]
|
||||
else:
|
||||
rpc_port = args.rpc_port
|
||||
client = await FullNodeRpcClient.create(self_hostname, rpc_port, DEFAULT_ROOT_PATH, config)
|
||||
client = await FullNodeRpcClient.create(self_hostname, uint16(rpc_port), DEFAULT_ROOT_PATH, config)
|
||||
|
||||
if args.delta_block_height:
|
||||
if args.start == "":
|
||||
if delta_block_height:
|
||||
if start == "":
|
||||
blockchain_state = await client.get_blockchain_state()
|
||||
if blockchain_state["peak"] is None:
|
||||
print("No blocks in blockchain")
|
||||
|
@ -68,9 +31,9 @@ async def netstorge_async(args, parser):
|
|||
|
||||
newer_block_height = blockchain_state["peak"].height
|
||||
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:
|
||||
print("Block header hash", args.start, "not found.")
|
||||
print("Block header hash", start, "not found.")
|
||||
client.close()
|
||||
await client.await_closed()
|
||||
return None
|
||||
|
@ -79,7 +42,7 @@ async def netstorge_async(args, parser):
|
|||
newer_block_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)
|
||||
network_space_bytes_estimate = await client.get_network_space(
|
||||
newer_block_header.header_hash, older_block_header.header_hash
|
||||
|
@ -106,7 +69,7 @@ async def netstorge_async(args, parser):
|
|||
|
||||
except Exception as e:
|
||||
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:
|
||||
print(f"Exception {e}")
|
||||
|
||||
|
@ -114,5 +77,39 @@ async def netstorge_async(args, parser):
|
|||
await client.await_closed()
|
||||
|
||||
|
||||
def netspace(args, parser):
|
||||
return asyncio.run(netstorge_async(args, parser))
|
||||
@click.command("netspace", short_help="estimate space on the network")
|
||||
@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
|
||||
import logging
|
||||
from src.plotting.plot_tools import (
|
||||
|
@ -13,154 +15,7 @@ from src.util.logging import initialize_logging
|
|||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
command_list = ["create", "check", "add", "remove", "show"]
|
||||
|
||||
|
||||
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):
|
||||
def show_plots(root_path: Path):
|
||||
print("Directories where plots are being searched for:")
|
||||
print("Note that subdirectories must be added manually.")
|
||||
print(
|
||||
|
@ -173,30 +28,156 @@ def show(root_path):
|
|||
print(f"{str_path}")
|
||||
|
||||
|
||||
def handler(args, parser):
|
||||
if args.command is None or len(args.command) < 1:
|
||||
help_message()
|
||||
parser.exit(1)
|
||||
|
||||
root_path: Path = args.root_path
|
||||
@click.group("plots", short_help="manage your plots")
|
||||
@click.pass_context
|
||||
def plots_cmd(ctx: click.Context):
|
||||
"""Create, add, remove and check your plots"""
|
||||
root_path: Path = ctx.obj["root_path"]
|
||||
if not root_path.is_dir():
|
||||
raise RuntimeError("Please initialize (or migrate) your config directory with chia init.")
|
||||
|
||||
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)
|
||||
elif command == "check":
|
||||
check_plots(args, root_path)
|
||||
elif command == "add":
|
||||
str_path = args.final_dir
|
||||
add_plot_directory(str_path, root_path)
|
||||
elif command == "remove":
|
||||
str_path = args.final_dir
|
||||
remove_plot_directory(str_path, root_path)
|
||||
elif command == "show":
|
||||
show(root_path)
|
||||
|
||||
@plots_cmd.command("create", short_help="creates plots")
|
||||
@click.option("-k", "--size", help="Plot size", type=int, default=32, show_default=True)
|
||||
@click.option("-n", "--num", help="Number of plots or challenges", type=int, default=1, show_default=True)
|
||||
@click.option("-b", "--buffer", help="Megabytes for sort/plot buffer", type=int, default=4608, show_default=True)
|
||||
@click.option("-r", "--num_threads", help="Number of threads to use", type=int, default=2, show_default=True)
|
||||
@click.option("-u", "--buckets", help="Number of buckets", type=int, default=0)
|
||||
@click.option("-s", "--stripe_size", help="Stripe size", type=int, default=0)
|
||||
@click.option(
|
||||
"-a",
|
||||
"--alt_fingerprint",
|
||||
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))
|
237
src/cmds/show.py
237
src/cmds/show.py
|
@ -1,3 +1,4 @@
|
|||
import click
|
||||
import traceback
|
||||
|
||||
import aiohttp
|
||||
|
@ -12,115 +13,37 @@ from src.server.outbound_message import NodeType
|
|||
from src.types.full_block import FullBlock
|
||||
from src.rpc.full_node_rpc_client import FullNodeRpcClient
|
||||
from src.util.byte_types import hexstr_to_bytes
|
||||
from src.util.config import str2bool
|
||||
from src.util.config import load_config
|
||||
from src.util.default_root import DEFAULT_ROOT_PATH
|
||||
from src.util.bech32m import encode_puzzle_hash
|
||||
from src.util.ints import uint16
|
||||
|
||||
|
||||
def make_parser(parser):
|
||||
|
||||
parser.add_argument(
|
||||
"-s",
|
||||
"--state",
|
||||
help="Show the current state of the blockchain.",
|
||||
type=str2bool,
|
||||
nargs="?",
|
||||
const=True,
|
||||
default=False,
|
||||
)
|
||||
|
||||
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):
|
||||
async def show_async(
|
||||
rpc_port: int,
|
||||
state: bool,
|
||||
show_connections: bool,
|
||||
exit_node: bool,
|
||||
add_connection: str,
|
||||
remove_connection: str,
|
||||
block_header_hash_by_height: str,
|
||||
block_by_header_hash: str,
|
||||
) -> None:
|
||||
|
||||
# TODO read configuration for rpc_port instead of assuming default
|
||||
|
||||
try:
|
||||
config = load_config(DEFAULT_ROOT_PATH, "config.yaml")
|
||||
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"]
|
||||
else:
|
||||
rpc_port = args.rpc_port
|
||||
client = await FullNodeRpcClient.create(self_hostname, rpc_port, DEFAULT_ROOT_PATH, config)
|
||||
client = await FullNodeRpcClient.create(self_hostname, uint16(rpc_port), DEFAULT_ROOT_PATH, config)
|
||||
|
||||
if args.state:
|
||||
if state:
|
||||
blockchain_state = await client.get_blockchain_state()
|
||||
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"]
|
||||
difficulty = blockchain_state["difficulty"]
|
||||
sub_slot_iters = blockchain_state["sub_slot_iters"]
|
||||
|
@ -140,7 +63,7 @@ async def show_async(args, parser):
|
|||
)
|
||||
if 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:
|
||||
print(f"Current Blockchain Status: Not Synced. Peak height: {peak.height}")
|
||||
else:
|
||||
|
@ -156,11 +79,11 @@ async def show_async(args, parser):
|
|||
while curr is not None and not curr.is_transaction_block:
|
||||
curr = await client.get_block_record(curr.prev_hash)
|
||||
peak_time = curr.timestamp
|
||||
peak_time = struct_time(localtime(peak_time))
|
||||
peak_time_struct = struct_time(localtime(peak_time))
|
||||
|
||||
print(
|
||||
" 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",
|
||||
)
|
||||
|
||||
|
@ -188,10 +111,10 @@ async def show_async(args, parser):
|
|||
else:
|
||||
print("Blockchain has no blocks yet")
|
||||
|
||||
# if called together with connections, leave a blank line
|
||||
if args.connections:
|
||||
# if called together with show_connections, leave a blank line
|
||||
if show_connections:
|
||||
print("")
|
||||
if args.connections:
|
||||
if show_connections:
|
||||
connections = await client.get_connections()
|
||||
print("Connections:")
|
||||
print(
|
||||
|
@ -235,18 +158,18 @@ async def show_async(args, parser):
|
|||
)
|
||||
print(con_str)
|
||||
# if called together with state, leave a blank line
|
||||
if args.state:
|
||||
if state:
|
||||
print("")
|
||||
if args.exit_node:
|
||||
if exit_node:
|
||||
node_stop = await client.stop_node()
|
||||
print(node_stop, "Node stopped.")
|
||||
if args.add_connection:
|
||||
if ":" not in args.add_connection:
|
||||
if add_connection:
|
||||
if ":" not in add_connection:
|
||||
print("Enter a valid IP and port in the following format: 10.5.4.3:8000")
|
||||
else:
|
||||
ip, port = (
|
||||
":".join(args.add_connection.split(":")[:-1]),
|
||||
args.add_connection.split(":")[-1],
|
||||
":".join(add_connection.split(":")[:-1]),
|
||||
add_connection.split(":")[-1],
|
||||
)
|
||||
print(f"Connecting to {ip}, {port}")
|
||||
try:
|
||||
|
@ -254,34 +177,34 @@ async def show_async(args, parser):
|
|||
except Exception:
|
||||
# TODO: catch right exception
|
||||
print(f"Failed to connect to {ip}:{port}")
|
||||
if args.remove_connection:
|
||||
if remove_connection:
|
||||
result_txt = ""
|
||||
if len(args.remove_connection) != 8:
|
||||
if len(remove_connection) != 8:
|
||||
result_txt = "Invalid NodeID. Do not include '.'."
|
||||
else:
|
||||
connections = await client.get_connections()
|
||||
for con in connections:
|
||||
if args.remove_connection == con["node_id"].hex()[:8]:
|
||||
print("Attempting to disconnect", "NodeID", args.remove_connection)
|
||||
if remove_connection == con["node_id"].hex()[:8]:
|
||||
print("Attempting to disconnect", "NodeID", remove_connection)
|
||||
try:
|
||||
await client.close_connection(con["node_id"])
|
||||
except Exception:
|
||||
result_txt = f"Failed to disconnect NodeID {args.remove_connection}"
|
||||
result_txt = f"Failed to disconnect NodeID {remove_connection}"
|
||||
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."
|
||||
elif result_txt == "":
|
||||
result_txt = f"NodeID {args.remove_connection}... not found."
|
||||
result_txt = f"NodeID {remove_connection}... not found."
|
||||
print(result_txt)
|
||||
if args.block_header_hash_by_height != "":
|
||||
block_header = await client.get_block_record_by_height(args.block_header_hash_by_height)
|
||||
if 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:
|
||||
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:
|
||||
print("Block height", args.block_header_hash_by_height, "not found.")
|
||||
if args.block_by_header_hash != "":
|
||||
block: Optional[BlockRecord] = await client.get_block_record(hexstr_to_bytes(args.block_by_header_hash))
|
||||
full_block: Optional[FullBlock] = await client.get_block(hexstr_to_bytes(args.block_by_header_hash))
|
||||
print("Block height", block_header_hash_by_height, "not found.")
|
||||
if 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(block_by_header_hash))
|
||||
# Would like to have a verbose flag for this
|
||||
if block is not None:
|
||||
assert full_block is not None
|
||||
|
@ -292,9 +215,17 @@ async def show_async(args, parser):
|
|||
difficulty = block.weight
|
||||
if block.is_transaction_block:
|
||||
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)
|
||||
cost = full_block.transactions_info.cost
|
||||
cost = str(full_block.transactions_info.cost)
|
||||
tx_filter_hash = "Not a transaction block"
|
||||
if full_block.foliage_transaction_block:
|
||||
tx_filter_hash = full_block.foliage_transaction_block.filter_hash
|
||||
else:
|
||||
block_time_string = "Not a transaction block"
|
||||
|
@ -325,11 +256,11 @@ async def show_async(args, parser):
|
|||
f"Fees Amount {block.fees}\n"
|
||||
)
|
||||
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:
|
||||
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.")
|
||||
else:
|
||||
tb = traceback.format_exc()
|
||||
|
@ -339,5 +270,59 @@ async def show_async(args, parser):
|
|||
await client.await_closed()
|
||||
|
||||
|
||||
def show(args, parser):
|
||||
return asyncio.run(show_async(args, parser))
|
||||
@click.command("show", short_help="show node information")
|
||||
@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,42 +1,29 @@
|
|||
import click
|
||||
import asyncio
|
||||
import os
|
||||
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
|
||||
|
||||
|
||||
def make_parser(parser):
|
||||
|
||||
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):
|
||||
def launch_start_daemon(root_path: Path) -> subprocess.Popen:
|
||||
os.environ["CHIA_ROOT"] = str(root_path)
|
||||
# TODO: use startupinfo=subprocess.DETACHED_PROCESS on windows
|
||||
process = subprocess.Popen("chia run_daemon".split(), stdout=subprocess.PIPE)
|
||||
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)
|
||||
if connection is None:
|
||||
print("Starting daemon")
|
||||
# launch a daemon
|
||||
process = launch_start_daemon(root_path)
|
||||
# give the daemon a chance to start up
|
||||
if process.stdout:
|
||||
process.stdout.readline()
|
||||
await asyncio.sleep(1)
|
||||
# it prints "daemon: listening"
|
||||
|
@ -46,16 +33,16 @@ async def create_start_daemon_connection(root_path):
|
|||
return None
|
||||
|
||||
|
||||
async def async_start(args, parser):
|
||||
daemon = await create_start_daemon_connection(args.root_path)
|
||||
async def async_start(root_path: Path, group: str, restart: bool) -> None:
|
||||
daemon = await create_start_daemon_connection(root_path)
|
||||
if daemon is None:
|
||||
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):
|
||||
print(f"{service}: ", end="", flush=True)
|
||||
if args.restart:
|
||||
if restart:
|
||||
if not await daemon.is_running(service_name=service):
|
||||
print("not running")
|
||||
elif await daemon.stop_service(service_name=service):
|
||||
|
@ -77,5 +64,9 @@ async def async_start(args, parser):
|
|||
await daemon.close()
|
||||
|
||||
|
||||
def start(args, parser):
|
||||
return asyncio.get_event_loop().run_until_complete(async_start(args, parser))
|
||||
@click.command("start", short_help="start service groups")
|
||||
@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
|
||||
from pathlib import Path
|
||||
|
||||
from src.daemon.client import connect_to_daemon_and_validate
|
||||
from src.util.service_groups import all_groups, services_for_groups
|
||||
|
||||
|
||||
def make_parser(parser):
|
||||
|
||||
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)
|
||||
async def async_stop(root_path: Path, group: str, stop_daemon: bool) -> int:
|
||||
daemon = await connect_to_daemon_and_validate(root_path)
|
||||
if daemon is None:
|
||||
print("couldn't connect to chia daemon")
|
||||
return 1
|
||||
|
||||
if args.daemon:
|
||||
if stop_daemon:
|
||||
r = await daemon.exit()
|
||||
await daemon.close()
|
||||
print(f"daemon: {r}")
|
||||
|
@ -52,7 +21,7 @@ async def async_stop(args, parser):
|
|||
|
||||
return_val = 0
|
||||
|
||||
for service in services_for_groups(args.group):
|
||||
for service in services_for_groups(group):
|
||||
print(f"{service}: ", end="", flush=True)
|
||||
if not await daemon.is_running(service_name=service):
|
||||
print("not running")
|
||||
|
@ -66,5 +35,9 @@ async def async_stop(args, parser):
|
|||
return return_val
|
||||
|
||||
|
||||
def stop(args, parser):
|
||||
return asyncio.get_event_loop().run_until_complete(async_stop(args, parser))
|
||||
@click.command("stop", short_help="stop service groups")
|
||||
@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 time
|
||||
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.config import load_config
|
||||
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.util.wallet_types import WalletType
|
||||
from src.cmds.units import units
|
||||
from decimal import Decimal
|
||||
|
||||
|
||||
command_list = ["send", "show", "get_transaction", "get_transactions"]
|
||||
|
||||
|
||||
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):
|
||||
def print_transaction(tx: TransactionRecord, verbose: bool) -> None:
|
||||
if verbose:
|
||||
print(tx)
|
||||
else:
|
||||
|
@ -95,33 +33,21 @@ def print_transaction(tx: TransactionRecord, verbose: bool):
|
|||
print("")
|
||||
|
||||
|
||||
async def get_transaction(args, wallet_client, fingerprint: int):
|
||||
if args.id is None:
|
||||
print("Please specify a wallet id with -i")
|
||||
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)
|
||||
async def get_transaction(args: dict, wallet_client: WalletRpcClient, fingerprint: int) -> None:
|
||||
wallet_id = args["id"]
|
||||
transaction_id = hexstr_to_bytes(args["tx_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):
|
||||
if args.id is None:
|
||||
print("Please specify a wallet id with -i")
|
||||
return
|
||||
else:
|
||||
wallet_id = args.id
|
||||
async def get_transactions(args: dict, wallet_client: WalletRpcClient, fingerprint: int) -> None:
|
||||
wallet_id = args["id"]
|
||||
txs: List[TransactionRecord] = await wallet_client.get_transactions(wallet_id)
|
||||
if len(txs) == 0:
|
||||
print("There are no transactions to this address")
|
||||
for i in range(0, len(txs), 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")
|
||||
while True:
|
||||
entered_key = sys.stdin.read(1)
|
||||
|
@ -131,27 +57,11 @@ async def get_transactions(args, wallet_client, fingerprint: int):
|
|||
break
|
||||
|
||||
|
||||
async def send(args, wallet_client, fingerprint: int):
|
||||
if args.id is None:
|
||||
print("Please specify a wallet id with -i")
|
||||
return
|
||||
else:
|
||||
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
|
||||
async def send(args: dict, wallet_client: WalletRpcClient, fingerprint: int) -> None:
|
||||
wallet_id = args["id"]
|
||||
amount = Decimal(args["amount"])
|
||||
fee = Decimal(args["fee"])
|
||||
address = args["address"]
|
||||
|
||||
print("Submitting transaction...")
|
||||
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")
|
||||
|
||||
|
||||
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()
|
||||
|
||||
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()
|
||||
if len(fingerprints) == 0:
|
||||
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
|
||||
else:
|
||||
fingerprint = fingerprints[index]
|
||||
assert fingerprint is not None
|
||||
log_in_response = await wallet_client.log_in(fingerprint)
|
||||
|
||||
if log_in_response["success"] is False:
|
||||
if log_in_response["error"] == "not_initialized":
|
||||
use_cloud = True
|
||||
|
@ -277,31 +189,24 @@ async def get_wallet(wallet_client, fingerprint=None) -> Optional[Tuple[WalletRp
|
|||
return wallet_client, fingerprint
|
||||
|
||||
|
||||
async def execute_with_wallet(args, parser, function: Callable):
|
||||
if args.fingerprint is None:
|
||||
fingerprint = None
|
||||
else:
|
||||
fingerprint = args.fingerprint
|
||||
|
||||
async def execute_with_wallet(wallet_rpc_port: int, fingerprint: int, extra_params: dict, function: Callable) -> None:
|
||||
try:
|
||||
config = load_config(DEFAULT_ROOT_PATH, "config.yaml")
|
||||
self_hostname = config["self_hostname"]
|
||||
if "wallet_rpc_port" not in args or args.wallet_rpc_port is None:
|
||||
if wallet_rpc_port is None:
|
||||
wallet_rpc_port = config["wallet"]["rpc_port"]
|
||||
else:
|
||||
wallet_rpc_port = args.wallet_rpc_port
|
||||
wallet_client = await WalletRpcClient.create(self_hostname, wallet_rpc_port, DEFAULT_ROOT_PATH, config)
|
||||
wallet_client = await WalletRpcClient.create(self_hostname, uint16(wallet_rpc_port), DEFAULT_ROOT_PATH, config)
|
||||
wallet_client_f = await get_wallet(wallet_client, fingerprint=fingerprint)
|
||||
if wallet_client_f is None:
|
||||
wallet_client.close()
|
||||
await wallet_client.await_closed()
|
||||
return
|
||||
wallet_client, fingerprint = wallet_client_f
|
||||
await function(args, wallet_client, fingerprint)
|
||||
await function(extra_params, wallet_client, fingerprint)
|
||||
|
||||
except Exception as e:
|
||||
if isinstance(e, aiohttp.client_exceptions.ClientConnectorError):
|
||||
print(f"Connection error. Check if wallet is running at {args.wallet_rpc_port}")
|
||||
print(f"Connection error. Check if wallet is running at {wallet_rpc_port}")
|
||||
else:
|
||||
print(f"Exception from 'wallet' {e}")
|
||||
|
||||
|
@ -309,20 +214,76 @@ async def execute_with_wallet(args, parser, function: Callable):
|
|||
await wallet_client.await_closed()
|
||||
|
||||
|
||||
def handler(args, parser):
|
||||
if args.command is None or len(args.command) < 1:
|
||||
help_message()
|
||||
parser.exit(1)
|
||||
command = args.command
|
||||
if command not in command_list:
|
||||
help_message()
|
||||
parser.exit(1)
|
||||
@click.group("wallet", short_help="manage your wallet")
|
||||
def wallet_cmd() -> None:
|
||||
pass
|
||||
|
||||
if command == "get_transaction":
|
||||
return asyncio.run(execute_with_wallet(args, parser, get_transaction))
|
||||
if command == "get_transactions":
|
||||
return asyncio.run(execute_with_wallet(args, parser, get_transactions))
|
||||
if command == "send":
|
||||
return asyncio.run(execute_with_wallet(args, parser, send))
|
||||
elif command == "show":
|
||||
return asyncio.run(execute_with_wallet(args, parser, print_balances))
|
||||
|
||||
@wallet_cmd.command("get_transaction", short_help="get transaction")
|
||||
@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("-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__)
|
||||
|
||||
|
||||
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")
|
||||
if args.num is not None:
|
||||
num = args.num
|
||||
if num is not None:
|
||||
if num == 0:
|
||||
log.warning("Not opening plot files")
|
||||
else:
|
||||
|
@ -28,25 +27,25 @@ def check_plots(args, root_path):
|
|||
else:
|
||||
num = 30
|
||||
|
||||
if args.challenge_start is not None:
|
||||
num_start = args.challenge_start
|
||||
if challenge_start is not None:
|
||||
num_start = challenge_start
|
||||
num_end = num_start + num
|
||||
else:
|
||||
num_start = 0
|
||||
num_end = num
|
||||
challenges = num_end - num_start
|
||||
|
||||
if args.grep_string is not None:
|
||||
match_str = args.grep_string
|
||||
if grep_string is not None:
|
||||
match_str = grep_string
|
||||
else:
|
||||
match_str = None
|
||||
if args.list_duplicates:
|
||||
if list_duplicates:
|
||||
log.warning("Checking for duplicate Plot IDs")
|
||||
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"])
|
||||
all_filenames: List[Path] = []
|
||||
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)
|
||||
|
||||
assert (pool_public_key is None) != (pool_contract_puzzle_hash is None)
|
||||
if args.num is not None:
|
||||
num = args.num
|
||||
else:
|
||||
num = 1
|
||||
|
||||
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")
|
||||
|
|
|
@ -112,7 +112,7 @@ class WalletNode:
|
|||
self.logged_in_fingerprint: Optional[int] = 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()
|
||||
if len(private_keys) == 0:
|
||||
self.log.warning("No keys present. Create keys with the UI, or with the 'chia keys' program.")
|
||||
|
|
Loading…
Reference in a new issue