chia keys show --json (display all keys as json) (#13637)

* chia keys show --json (display all keys as json)

* black (linting) chia keys show --json (display all keys as json)

* output a root object containing the keys array

* support key with empty label

* add test_show_json and test_show_mnemonic_json to test_keys

* chia keys show --json -- do not format json output

* tests/core/cmds/test_keys.py -- result.output.find(<value>) != -1

* tests/core/cmds/test_keys.py -- result.output.find(<value>) != -1 -- black

* Pass root_path in show_all_keys and fix tests

Co-authored-by: Jeff Cruikshank <jeff@chia.net>
This commit is contained in:
Karlkim Suwanmongkol 2022-10-20 05:41:03 +07:00 committed by GitHub
parent 769d9e2e2f
commit a3c4c2367a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 153 additions and 39 deletions

View File

@ -54,10 +54,19 @@ def generate_cmd(ctx: click.Context, label: Optional[str]):
show_default=True,
is_flag=True,
)
def show_cmd(show_mnemonic_seed, non_observer_derivation):
@click.option(
"--json",
"-j",
help=("Displays all the keys in keychain as JSON"),
default=False,
show_default=True,
is_flag=True,
)
@click.pass_context
def show_cmd(ctx: click.Context, show_mnemonic_seed, non_observer_derivation, json):
from .keys_funcs import show_all_keys
show_all_keys(show_mnemonic_seed, non_observer_derivation)
show_all_keys(ctx.obj["root_path"], show_mnemonic_seed, non_observer_derivation, json)
@keys_cmd.command("add", short_help="Add a private key by mnemonic")

View File

@ -1,3 +1,4 @@
import json
import logging
import os
import sys
@ -14,7 +15,6 @@ from chia.daemon.keychain_proxy import KeychainProxy, connect_to_keychain_and_va
from chia.util.bech32m import encode_puzzle_hash
from chia.util.errors import KeychainNotSet
from chia.util.config import load_config
from chia.util.default_root import DEFAULT_ROOT_PATH
from chia.util.errors import KeychainException
from chia.util.file_keyring import MAX_LABEL_LENGTH
from chia.util.ints import uint32
@ -126,51 +126,73 @@ def delete_key_label(fingerprint: int) -> None:
sys.exit(f"Error: {e}")
def show_all_keys(show_mnemonic: bool, non_observer_derivation: bool):
def show_all_keys(root_path: Path, show_mnemonic: bool, non_observer_derivation: bool, json_output: bool):
"""
Prints all keys and mnemonics (if available).
"""
unlock_keyring()
root_path = DEFAULT_ROOT_PATH
config = load_config(root_path, "config.yaml")
all_keys = Keychain().get_keys(True)
selected = config["selected_network"]
prefix = config["network_overrides"]["config"][selected]["address_prefix"]
if len(all_keys) == 0:
print("There are no saved private keys")
if json_output:
print(json.dumps({"keys": []}))
else:
print("There are no saved private keys")
return None
msg = "Showing all public keys derived from your master seed and private key:"
if show_mnemonic:
msg = "Showing all public and private keys"
print(msg)
for key_data in all_keys:
if not json_output:
msg = "Showing all public keys derived from your master seed and private key:"
if show_mnemonic:
msg = "Showing all public and private keys"
print(msg)
def process_key_data(key_data):
key = {}
sk = key_data.private_key
print("")
if key_data.label is not None:
print("Label:", key_data.label)
print("Fingerprint:", key_data.fingerprint)
print("Master public key (m):", key_data.public_key)
print(
"Farmer public key (m/12381/8444/0/0):",
master_sk_to_farmer_sk(sk).get_g1(),
)
print("Pool public key (m/12381/8444/1/0):", master_sk_to_pool_sk(sk).get_g1())
key["label"] = key_data.label
key["fingerprint"] = key_data.fingerprint
key["master_pk"] = bytes(key_data.public_key).hex()
key["farmer_pk"] = bytes(master_sk_to_farmer_sk(sk).get_g1()).hex()
key["pool_pk"] = bytes(master_sk_to_pool_sk(sk).get_g1()).hex()
first_wallet_sk: PrivateKey = (
master_sk_to_wallet_sk(sk, uint32(0))
if non_observer_derivation
else master_sk_to_wallet_sk_unhardened(sk, uint32(0))
)
wallet_address: str = encode_puzzle_hash(create_puzzlehash_for_pk(first_wallet_sk.get_g1()), prefix)
print(f"First wallet address{' (non-observer)' if non_observer_derivation else ''}: {wallet_address}")
key["wallet_address"] = wallet_address
key["non_observer"] = non_observer_derivation
if show_mnemonic:
print("Master private key (m):", bytes(sk).hex())
print(
"First wallet secret key (m/12381/8444/2/0):",
master_sk_to_wallet_sk(sk, uint32(0)),
)
mnemonic = bytes_to_mnemonic(key_data.entropy)
print(" Mnemonic seed (24 secret words):")
print(mnemonic)
key["master_sk"] = bytes(sk).hex()
key["wallet_sk"] = bytes(master_sk_to_wallet_sk(sk, uint32(0))).hex()
key["mnemonic"] = bytes_to_mnemonic(key_data.entropy)
return key
keys = map(process_key_data, all_keys)
if json_output:
print(json.dumps({"keys": list(keys)}))
else:
for key in keys:
print("")
if "label" in key:
print("Label:", key["label"])
print("Fingerprint:", key["fingerprint"])
print("Master public key (m):", key["master_pk"])
print("Farmer public key (m/12381/8444/0/0):", key["farmer_pk"])
print("Pool public key (m/12381/8444/1/0):", key["pool_pk"])
print(f"First wallet address{' (non-observer)' if key['non_observer'] else ''}: {key['wallet_address']}")
if show_mnemonic:
print("Master private key (m):", key["master_sk"])
print("First wallet secret key (m/12381/8444/2/0):", key["wallet_sk"])
print(" Mnemonic seed (24 secret words):")
print(key["mnemonic"])
def delete(fingerprint: int):

View File

@ -1,9 +1,10 @@
import json
import os
import pytest
import re
from chia.cmds.chia import cli
from chia.cmds.keys import delete_all_cmd, generate_and_print_cmd, show_cmd, sign_cmd, verify_cmd
from chia.cmds.keys import delete_all_cmd, generate_and_print_cmd, sign_cmd, verify_cmd
from chia.util.config import load_config
from chia.util.file_keyring import FileKeyring
from chia.util.keychain import KeyData, DEFAULT_USER, DEFAULT_SERVICE, Keychain, generate_mnemonic
@ -358,7 +359,7 @@ class TestKeysCommands:
else:
assert label == key.label
def test_show(self, keyring_with_one_key):
def test_show(self, keyring_with_one_key, tmp_path):
"""
Test that the `chia keys show` command shows the correct key.
"""
@ -367,13 +368,54 @@ class TestKeysCommands:
assert len(keychain.get_all_private_keys()) == 1
keys_root_path = keychain.keyring_wrapper.keys_root_path
base_params = [
"--no-force-legacy-keyring-migration",
"--root-path",
os.fspath(tmp_path),
"--keys-root-path",
os.fspath(keys_root_path),
]
runner = CliRunner()
result: Result = runner.invoke(show_cmd, [])
cmd_params = ["keys", "show"]
# Generate a new config
assert runner.invoke(cli, [*base_params, "init"]).exit_code == 0
# Run the command
result: Result = runner.invoke(cli, [*base_params, *cmd_params])
# assert result.exit_code == 0
assert result.output.find(f"Fingerprint: {TEST_FINGERPRINT}") != 0
assert result.output.find(f"Fingerprint: {TEST_FINGERPRINT}") != -1
def test_show_mnemonic(self, keyring_with_one_key):
def test_show_json(self, keyring_with_one_key, tmp_path):
"""
Test that the `chia keys show --json` command shows the correct key.
"""
keychain = keyring_with_one_key
assert len(keychain.get_all_private_keys()) == 1
keys_root_path = keychain.keyring_wrapper.keys_root_path
base_params = [
"--no-force-legacy-keyring-migration",
"--root-path",
os.fspath(tmp_path),
"--keys-root-path",
os.fspath(keys_root_path),
]
runner = CliRunner()
cmd_params = ["keys", "show", "--json"]
# Generate a new config
assert runner.invoke(cli, [*base_params, "init"]).exit_code == 0
# Run the command
result: Result = runner.invoke(cli, [*base_params, *cmd_params])
json_result = json.loads(result.output)
# assert result.exit_code == 0
assert json_result["keys"][0]["fingerprint"] == TEST_FINGERPRINT
def test_show_mnemonic(self, keyring_with_one_key, tmp_path):
"""
Test that the `chia keys show --show-mnemonic-seed` command shows the key's mnemonic seed.
"""
@ -382,13 +424,54 @@ class TestKeysCommands:
assert len(keychain.get_all_private_keys()) == 1
keys_root_path = keychain.keyring_wrapper.keys_root_path
base_params = [
"--no-force-legacy-keyring-migration",
"--root-path",
os.fspath(tmp_path),
"--keys-root-path",
os.fspath(keys_root_path),
]
runner = CliRunner()
result: Result = runner.invoke(show_cmd, ["--show-mnemonic-seed"])
cmd_params = ["keys", "show", "--show-mnemonic-seed"]
# Generate a new config
assert runner.invoke(cli, [*base_params, "init"]).exit_code == 0
# Run the command
result: Result = runner.invoke(cli, [*base_params, *cmd_params])
# assert result.exit_code == 0
assert result.output.find(f"Fingerprint: {TEST_FINGERPRINT}") != 0
assert result.output.find("Mnemonic: seed (24 secret words):") != 0
assert result.output.find(TEST_MNEMONIC_SEED) != 0
assert result.output.find(f"Fingerprint: {TEST_FINGERPRINT}") != -1
assert result.output.find("Mnemonic seed (24 secret words):") != -1
assert result.output.find(TEST_MNEMONIC_SEED) != -1
def test_show_mnemonic_json(self, keyring_with_one_key, tmp_path):
"""
Test that the `chia keys show --show-mnemonic-seed --json` command shows the key's mnemonic seed.
"""
keychain = keyring_with_one_key
assert len(keychain.get_all_private_keys()) == 1
keys_root_path = keychain.keyring_wrapper.keys_root_path
base_params = [
"--no-force-legacy-keyring-migration",
"--root-path",
os.fspath(tmp_path),
"--keys-root-path",
os.fspath(keys_root_path),
]
runner = CliRunner()
cmd_params = ["keys", "show", "--show-mnemonic-seed", "--json"]
# Generate a new config
assert runner.invoke(cli, [*base_params, "init"]).exit_code == 0
# Run the command
result: Result = runner.invoke(cli, [*base_params, *cmd_params])
json_result = json.loads(result.output)
# assert result.exit_code == 0
assert json_result["keys"][0]["fingerprint"] == TEST_FINGERPRINT
assert json_result["keys"][0]["mnemonic"] == TEST_MNEMONIC_SEED
def test_add_interactive(self, tmp_path, empty_keyring):
"""
@ -545,7 +628,7 @@ class TestKeysCommands:
result: Result = runner.invoke(generate_and_print_cmd, [])
assert result.exit_code == 0
assert result.output.find("Mnemonic (24 secret words):") != 0
assert result.output.find("Mnemonic (24 secret words):") != -1
def test_sign(self, keyring_with_one_key):
"""