Skip to content

Commit

Permalink
Merge pull request #712 from serokell/krendelhoff/chore-refactor-baking
Browse files Browse the repository at this point in the history
[Chore] Factor out code from `wizard_structure`
  • Loading branch information
krendelhoff2 authored Sep 22, 2023
2 parents 55640e6 + d7eda94 commit 2cfd028
Show file tree
Hide file tree
Showing 6 changed files with 576 additions and 511 deletions.
218 changes: 218 additions & 0 deletions baking/src/tezos_baking/steps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
# SPDX-FileCopyrightText: 2023 Oxhead Alpha
# SPDX-License-Identifier: LicenseRef-MIT-OA

"""
Contains step class definition along with the common steps shared between wizards
"""

from dataclasses import dataclass, field
import textwrap
import sys

from tezos_baking.util import *
from tezos_baking.validators import Validator
import tezos_baking.validators as validators


class Step:
def __init__(
self,
id: str,
prompt: str,
help: str,
default: str = "1",
options=None,
validator=None,
):
self.id = id
self.prompt = prompt
self.help = help
self.default = default
self.options = options
self.validator = validator

def pprint_options(self):
i = 1
def_i = None
try:
def_i = int(self.default)
except:
pass

if self.options and isinstance(self.options, list):
options_count = 0
for o in self.options:
if isinstance(o, dict):
for values in o.values():
if not isinstance(values, list):
options_count += 1
else:
options_count += len(values)
else:
options_count += 1
index_len = len(str(options_count))
str_format = f"{{:{index_len}}}. {{}}"
for o in self.options:
if isinstance(o, dict):
for k, values in o.items():
print()
print(f"'{k}':")
print()
if not isinstance(values, list):
values = [values]
for v in values:
if def_i is not None and i == def_i:
print(str_format.format(i, "(default) " + v))
else:
print(str_format.format(i, v))
i += 1
print()
else:
if def_i is not None and i == def_i:
print(str_format.format(i, "(default) " + o))
else:
print(str_format.format(i, o))
i += 1
elif self.options and isinstance(self.options, dict):
index_len = len(str(len(self.options)))
max_option_len = max(map(len, self.options.keys()))
padding = max(26, max_option_len + 2)
indent_size = index_len + 4 + padding
str_format = f"{{:{index_len}}}. {{:<{padding}}} {{}}"
for o in self.options:
description = textwrap.indent(
textwrap.fill(self.options[o], 60),
" " * indent_size,
).lstrip()
if def_i is not None and i == def_i:
print(str_format.format(i, o + " (default)", description))
else:
print(str_format.format(i, o, description))
i += 1
elif not self.options and self.default is not None:
print("Default:", self.default)


# Global options

key_import_modes = {
"ledger": "From a ledger",
"secret-key": "Either the unencrypted or password-encrypted secret key for your address",
"remote": "Remote key governed by a signer running on a different machine",
"generate-fresh-key": "Generate fresh key that should be filled manually later",
"json": "Faucet JSON file",
}

networks = {
"mainnet": "Main Tezos network",
"ghostnet": "Long running test network, currently using the Nairobi Tezos protocol",
"nairobinet": "Test network using the Nairobi Tezos protocol",
"oxfordnet": "Test network using the Oxford Tezos protocol",
}

# Steps

secret_key_query = Step(
id="secret_key",
prompt="Provide either the unencrypted or password-encrypted secret key for your address.",
help="The format is 'unencrypted:edsk...' for the unencrypted key, or 'encrypted:edesk...'"
"for the encrypted key.",
default=None,
validator=Validator([validators.required_field, validators.secret_key]),
)

remote_signer_uri_query = Step(
id="remote_signer_uri",
prompt="Provide your remote key with the address of the signer.",
help="The format is the address of your remote signer host, followed by a public key,\n"
"i.e. something like http://127.0.0.1:6732/tz1V8fDHpHzN8RrZqiYCHaJM9EocsYZch5Cy\n"
"The supported schemes are https, http, tcp, and unix.",
default=None,
validator=Validator([validators.required_field, validators.signer_uri]),
)

derivation_path_query = Step(
id="derivation_path",
prompt="Provide derivation path for the key stored on the ledger.",
help="The format is '[0-9]+h/[0-9]+h'",
default=None,
validator=Validator([validators.required_field, validators.derivation_path]),
)


json_filepath_query = Step(
id="json_filepath",
prompt="Provide the path to your downloaded faucet JSON file.",
help="The file should contain the 'mnemonic' and 'secret' fields.",
default=None,
validator=Validator([validators.required_field, validators.filepath]),
)


def get_ledger_url_query(ledgers):
return Step(
id="ledger_url",
prompt="Choose a ledger to get the new derivation from.",
options=ledgers,
default=None,
validator=Validator(
[validators.required_field, validators.enum_range(ledgers)]
),
help="In order to specify new derivation path, you need to specify a ledger to get the derivation from.",
)


def ledger_urls_info(ledgers_derivations, node_endpoint, client_dir):
ledgers_info = {}
max_derivation_len = 0
for derivations_paths in ledgers_derivations.values():
max_derivation_len = max(max_derivation_len, max(map(len, derivations_paths)))
for ledger_url, derivations_paths in ledgers_derivations.items():
for derivation_path in derivations_paths:
output = get_proc_output(
f"sudo -u tezos {suppress_warning_text} octez-client --base-dir {client_dir} "
f"show ledger {ledger_url + derivation_path}"
).stdout
addr = re.search(address_regex, output).group(0).decode()
balance = (
get_proc_output(
f"sudo -u tezos {suppress_warning_text} octez-client --base-dir {client_dir} "
f"--endpoint {node_endpoint} get balance for {addr}"
)
.stdout.decode()
.strip()
)
ledgers_info.setdefault(ledger_url, []).append(
(
"{:" + str(max_derivation_len + 1) + "} address: {}, balance: {}"
).format(derivation_path + ",", addr, balance)
)
return ledgers_info


# We define this step as a function since the corresponding step requires
# tezos-node to be running and bootstrapped in order to gather the data
# about the ledger-stored addresses, so it's called right before invoking
# after the node was boostrapped
def get_ledger_derivation_query(ledgers_derivations, node_endpoint, client_dir):
extra_options = ["Specify derivation path", "Go back"]
full_ledger_urls = []
for ledger_url, derivations_paths in ledgers_derivations.items():
for derivation_path in derivations_paths:
full_ledger_urls.append(ledger_url + derivation_path)
return Step(
id="ledger_derivation",
prompt="Select a key to import from the ledger.\n"
"You can choose one of the suggested derivations or provide your own:",
help="'Specify derivation path' will ask a derivation path from you."
"'Go back' will return you back to the key type choice.",
default=None,
options=[ledger_urls_info(ledgers_derivations, node_endpoint, client_dir)]
+ extra_options,
validator=Validator(
[
validators.required_field,
validators.enum_range(full_ledger_urls + extra_options),
]
),
)
28 changes: 16 additions & 12 deletions baking/src/tezos_baking/tezos_setup_wizard.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@
import json
from typing import List

from .wizard_structure import *
from tezos_baking.wizard_structure import *
from tezos_baking.util import *
from tezos_baking.steps import *
from tezos_baking.validators import Validator
import tezos_baking.validators as validators

# Global options

Expand Down Expand Up @@ -188,7 +192,7 @@ def get_node_version_hash():
"Keep in mind that you must select the test network (e.g. ghostnet)\n"
"if you plan on baking with a faucet JSON file.\n",
options=networks,
validator=Validator(enum_range_validator(networks)),
validator=Validator(validators.enum_range(networks)),
)

service_mode_query = Step(
Expand All @@ -198,7 +202,7 @@ def get_node_version_hash():
"on different networks.\nSometimes, however, you might want to only run the Tezos node.\n"
"When this option is chosen, this wizard will help you bootstrap the Tezos node only.",
options=modes,
validator=Validator(enum_range_validator(modes)),
validator=Validator(validators.enum_range(modes)),
)

systemd_mode_query = Step(
Expand All @@ -207,7 +211,7 @@ def get_node_version_hash():
help="Starting the service will make it available just for this session, great\n"
"if you want to experiment. Enabling it will make it start on every boot.",
options=systemd_enable,
validator=Validator(enum_range_validator(systemd_enable)),
validator=Validator(validators.enum_range(systemd_enable)),
)

liquidity_toggle_vote_query = Step(
Expand All @@ -218,7 +222,7 @@ def get_node_version_hash():
"\nYou can read more about this in the here:\n"
"https://tezos.gitlab.io/active/liquidity_baking.html",
options=toggle_vote_modes,
validator=Validator(enum_range_validator(toggle_vote_modes)),
validator=Validator(validators.enum_range(toggle_vote_modes)),
)

# We define this step as a function to better tailor snapshot options to the chosen history mode
Expand Down Expand Up @@ -262,7 +266,7 @@ def get_snapshot_mode_query(config):
"that is sufficient for baking. You can read more about other Tezos node history modes here:\n"
"https://tezos.gitlab.io/user/history_modes.html#history-modes",
options=import_modes,
validator=Validator(enum_range_validator(import_modes)),
validator=Validator(validators.enum_range(import_modes)),
)


Expand All @@ -272,15 +276,15 @@ def get_snapshot_mode_query(config):
help="You have indicated wanting to import the snapshot from a file.\n"
"You can download the snapshot yourself e.g. from XTZ-Shots or Tezos Giganode Snapshots.",
default=None,
validator=Validator([required_field_validator, filepath_validator]),
validator=Validator([validators.required_field, validators.filepath]),
)

provider_url_query = Step(
id="provider_url",
prompt="Provide the url of the snapshot provider.",
help="You have indicated wanting to fetch the snapshot from a custom provider.\n",
default=None,
validator=Validator([required_field_validator, reachable_url_validator()]),
validator=Validator([validators.required_field, validators.reachable_url()]),
)

snapshot_url_query = Step(
Expand All @@ -289,7 +293,7 @@ def get_snapshot_mode_query(config):
help="You have indicated wanting to import the snapshot from a custom url.\n"
"You can use e.g. links to XTZ-Shots or Marigold resources.",
default=None,
validator=Validator([required_field_validator, reachable_url_validator()]),
validator=Validator([validators.required_field, validators.reachable_url()]),
)

snapshot_sha256_query = Step(
Expand All @@ -308,7 +312,7 @@ def get_snapshot_mode_query(config):
"You can read more about different nodes history modes here:\n"
"https://tezos.gitlab.io/user/history_modes.html",
options=history_modes,
validator=Validator(enum_range_validator(history_modes)),
validator=Validator(validators.enum_range(history_modes)),
)

# We define the step as a function to disallow choosing json baking on mainnet
Expand All @@ -322,7 +326,7 @@ def get_key_mode_query(modes):
"If you want to test baking with a faucet file, "
"make sure you have chosen a test network like " + list(networks.keys())[1],
options=modes,
validator=Validator(enum_range_validator(modes)),
validator=Validator(validators.enum_range(modes)),
)


Expand All @@ -336,7 +340,7 @@ def get_key_mode_query(modes):
prompt="Do you want to proceed with this snapshot anyway?",
help="It's possible, but not recommended, to ignore the sha256 mismatch and use this snapshot anyway.",
options=ignore_hash_mismatch_options,
validator=Validator(enum_range_validator(ignore_hash_mismatch_options)),
validator=Validator(validators.enum_range(ignore_hash_mismatch_options)),
)


Expand Down
Loading

0 comments on commit 2cfd028

Please sign in to comment.