|
| 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 | + ) |
0 commit comments