Skip to content

Commit 2cfd028

Browse files
authored
Merge pull request #712 from serokell/krendelhoff/chore-refactor-baking
[Chore] Factor out code from `wizard_structure`
2 parents 55640e6 + d7eda94 commit 2cfd028

File tree

6 files changed

+576
-511
lines changed

6 files changed

+576
-511
lines changed

baking/src/tezos_baking/steps.py

+218
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
# SPDX-FileCopyrightText: 2023 Oxhead Alpha
2+
# SPDX-License-Identifier: LicenseRef-MIT-OA
3+
4+
"""
5+
Contains step class definition along with the common steps shared between wizards
6+
"""
7+
8+
from dataclasses import dataclass, field
9+
import textwrap
10+
import sys
11+
12+
from tezos_baking.util import *
13+
from tezos_baking.validators import Validator
14+
import tezos_baking.validators as validators
15+
16+
17+
class Step:
18+
def __init__(
19+
self,
20+
id: str,
21+
prompt: str,
22+
help: str,
23+
default: str = "1",
24+
options=None,
25+
validator=None,
26+
):
27+
self.id = id
28+
self.prompt = prompt
29+
self.help = help
30+
self.default = default
31+
self.options = options
32+
self.validator = validator
33+
34+
def pprint_options(self):
35+
i = 1
36+
def_i = None
37+
try:
38+
def_i = int(self.default)
39+
except:
40+
pass
41+
42+
if self.options and isinstance(self.options, list):
43+
options_count = 0
44+
for o in self.options:
45+
if isinstance(o, dict):
46+
for values in o.values():
47+
if not isinstance(values, list):
48+
options_count += 1
49+
else:
50+
options_count += len(values)
51+
else:
52+
options_count += 1
53+
index_len = len(str(options_count))
54+
str_format = f"{{:{index_len}}}. {{}}"
55+
for o in self.options:
56+
if isinstance(o, dict):
57+
for k, values in o.items():
58+
print()
59+
print(f"'{k}':")
60+
print()
61+
if not isinstance(values, list):
62+
values = [values]
63+
for v in values:
64+
if def_i is not None and i == def_i:
65+
print(str_format.format(i, "(default) " + v))
66+
else:
67+
print(str_format.format(i, v))
68+
i += 1
69+
print()
70+
else:
71+
if def_i is not None and i == def_i:
72+
print(str_format.format(i, "(default) " + o))
73+
else:
74+
print(str_format.format(i, o))
75+
i += 1
76+
elif self.options and isinstance(self.options, dict):
77+
index_len = len(str(len(self.options)))
78+
max_option_len = max(map(len, self.options.keys()))
79+
padding = max(26, max_option_len + 2)
80+
indent_size = index_len + 4 + padding
81+
str_format = f"{{:{index_len}}}. {{:<{padding}}} {{}}"
82+
for o in self.options:
83+
description = textwrap.indent(
84+
textwrap.fill(self.options[o], 60),
85+
" " * indent_size,
86+
).lstrip()
87+
if def_i is not None and i == def_i:
88+
print(str_format.format(i, o + " (default)", description))
89+
else:
90+
print(str_format.format(i, o, description))
91+
i += 1
92+
elif not self.options and self.default is not None:
93+
print("Default:", self.default)
94+
95+
96+
# Global options
97+
98+
key_import_modes = {
99+
"ledger": "From a ledger",
100+
"secret-key": "Either the unencrypted or password-encrypted secret key for your address",
101+
"remote": "Remote key governed by a signer running on a different machine",
102+
"generate-fresh-key": "Generate fresh key that should be filled manually later",
103+
"json": "Faucet JSON file",
104+
}
105+
106+
networks = {
107+
"mainnet": "Main Tezos network",
108+
"ghostnet": "Long running test network, currently using the Nairobi Tezos protocol",
109+
"nairobinet": "Test network using the Nairobi Tezos protocol",
110+
"oxfordnet": "Test network using the Oxford Tezos protocol",
111+
}
112+
113+
# Steps
114+
115+
secret_key_query = Step(
116+
id="secret_key",
117+
prompt="Provide either the unencrypted or password-encrypted secret key for your address.",
118+
help="The format is 'unencrypted:edsk...' for the unencrypted key, or 'encrypted:edesk...'"
119+
"for the encrypted key.",
120+
default=None,
121+
validator=Validator([validators.required_field, validators.secret_key]),
122+
)
123+
124+
remote_signer_uri_query = Step(
125+
id="remote_signer_uri",
126+
prompt="Provide your remote key with the address of the signer.",
127+
help="The format is the address of your remote signer host, followed by a public key,\n"
128+
"i.e. something like http://127.0.0.1:6732/tz1V8fDHpHzN8RrZqiYCHaJM9EocsYZch5Cy\n"
129+
"The supported schemes are https, http, tcp, and unix.",
130+
default=None,
131+
validator=Validator([validators.required_field, validators.signer_uri]),
132+
)
133+
134+
derivation_path_query = Step(
135+
id="derivation_path",
136+
prompt="Provide derivation path for the key stored on the ledger.",
137+
help="The format is '[0-9]+h/[0-9]+h'",
138+
default=None,
139+
validator=Validator([validators.required_field, validators.derivation_path]),
140+
)
141+
142+
143+
json_filepath_query = Step(
144+
id="json_filepath",
145+
prompt="Provide the path to your downloaded faucet JSON file.",
146+
help="The file should contain the 'mnemonic' and 'secret' fields.",
147+
default=None,
148+
validator=Validator([validators.required_field, validators.filepath]),
149+
)
150+
151+
152+
def get_ledger_url_query(ledgers):
153+
return Step(
154+
id="ledger_url",
155+
prompt="Choose a ledger to get the new derivation from.",
156+
options=ledgers,
157+
default=None,
158+
validator=Validator(
159+
[validators.required_field, validators.enum_range(ledgers)]
160+
),
161+
help="In order to specify new derivation path, you need to specify a ledger to get the derivation from.",
162+
)
163+
164+
165+
def ledger_urls_info(ledgers_derivations, node_endpoint, client_dir):
166+
ledgers_info = {}
167+
max_derivation_len = 0
168+
for derivations_paths in ledgers_derivations.values():
169+
max_derivation_len = max(max_derivation_len, max(map(len, derivations_paths)))
170+
for ledger_url, derivations_paths in ledgers_derivations.items():
171+
for derivation_path in derivations_paths:
172+
output = get_proc_output(
173+
f"sudo -u tezos {suppress_warning_text} octez-client --base-dir {client_dir} "
174+
f"show ledger {ledger_url + derivation_path}"
175+
).stdout
176+
addr = re.search(address_regex, output).group(0).decode()
177+
balance = (
178+
get_proc_output(
179+
f"sudo -u tezos {suppress_warning_text} octez-client --base-dir {client_dir} "
180+
f"--endpoint {node_endpoint} get balance for {addr}"
181+
)
182+
.stdout.decode()
183+
.strip()
184+
)
185+
ledgers_info.setdefault(ledger_url, []).append(
186+
(
187+
"{:" + str(max_derivation_len + 1) + "} address: {}, balance: {}"
188+
).format(derivation_path + ",", addr, balance)
189+
)
190+
return ledgers_info
191+
192+
193+
# We define this step as a function since the corresponding step requires
194+
# tezos-node to be running and bootstrapped in order to gather the data
195+
# about the ledger-stored addresses, so it's called right before invoking
196+
# after the node was boostrapped
197+
def get_ledger_derivation_query(ledgers_derivations, node_endpoint, client_dir):
198+
extra_options = ["Specify derivation path", "Go back"]
199+
full_ledger_urls = []
200+
for ledger_url, derivations_paths in ledgers_derivations.items():
201+
for derivation_path in derivations_paths:
202+
full_ledger_urls.append(ledger_url + derivation_path)
203+
return Step(
204+
id="ledger_derivation",
205+
prompt="Select a key to import from the ledger.\n"
206+
"You can choose one of the suggested derivations or provide your own:",
207+
help="'Specify derivation path' will ask a derivation path from you."
208+
"'Go back' will return you back to the key type choice.",
209+
default=None,
210+
options=[ledger_urls_info(ledgers_derivations, node_endpoint, client_dir)]
211+
+ extra_options,
212+
validator=Validator(
213+
[
214+
validators.required_field,
215+
validators.enum_range(full_ledger_urls + extra_options),
216+
]
217+
),
218+
)

baking/src/tezos_baking/tezos_setup_wizard.py

+16-12
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,11 @@
1616
import json
1717
from typing import List
1818

19-
from .wizard_structure import *
19+
from tezos_baking.wizard_structure import *
20+
from tezos_baking.util import *
21+
from tezos_baking.steps import *
22+
from tezos_baking.validators import Validator
23+
import tezos_baking.validators as validators
2024

2125
# Global options
2226

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

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

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

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

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

268272

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

278282
provider_url_query = Step(
279283
id="provider_url",
280284
prompt="Provide the url of the snapshot provider.",
281285
help="You have indicated wanting to fetch the snapshot from a custom provider.\n",
282286
default=None,
283-
validator=Validator([required_field_validator, reachable_url_validator()]),
287+
validator=Validator([validators.required_field, validators.reachable_url()]),
284288
)
285289

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

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

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

328332

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

342346

0 commit comments

Comments
 (0)