Skip to content

Commit

Permalink
Improving error messages
Browse files Browse the repository at this point in the history
Validating keystore file on input
Adding NON_PRATER_CHAIN_KEYS to reduce code duplication
  • Loading branch information
valefar-on-discord committed Apr 20, 2024
1 parent 6ac3ffa commit e15b8a8
Show file tree
Hide file tree
Showing 8 changed files with 54 additions and 31 deletions.
23 changes: 12 additions & 11 deletions staking_deposit/cli/exit_transaction_keystore.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@
from typing import Any
from staking_deposit.exit_transaction import exit_transaction_generation, export_exit_transaction_json
from staking_deposit.key_handling.keystore import Keystore
from staking_deposit.settings import ALL_CHAINS, MAINNET, PRATER, get_chain_setting
from staking_deposit.settings import (
MAINNET,
NON_PRATER_CHAIN_KEYS,
get_chain_setting,
)
from staking_deposit.utils.click import (
captive_prompt_callback,
choice_prompt_func,
Expand All @@ -14,7 +18,7 @@
closest_match,
load_text,
)
from staking_deposit.utils.validation import validate_int_range
from staking_deposit.utils.validation import validate_int_range, validate_keystore_file


FUNC_NAME = 'exit_transaction_keystore'
Expand All @@ -25,24 +29,23 @@
)
@jit_option(
callback=captive_prompt_callback(
lambda x: closest_match(x, list(ALL_CHAINS.keys())),
lambda x: closest_match(x, NON_PRATER_CHAIN_KEYS),
choice_prompt_func(
lambda: load_text(['arg_exit_transaction_keystore_chain', 'prompt'], func=FUNC_NAME),
list(ALL_CHAINS.keys())
NON_PRATER_CHAIN_KEYS
),
),
default=MAINNET,
help=lambda: load_text(['arg_exit_transaction_keystore_chain', 'help'], func=FUNC_NAME),
param_decls='--chain',
prompt=choice_prompt_func(
lambda: load_text(['arg_exit_transaction_keystore_chain', 'prompt'], func=FUNC_NAME),
# Since `prater` is alias of `goerli`, do not show `prater` in the prompt message.
list(key for key in ALL_CHAINS.keys() if key != PRATER)
NON_PRATER_CHAIN_KEYS
),
)
@jit_option(
callback=captive_prompt_callback(
lambda x: x,
lambda file: validate_keystore_file(file),
lambda: load_text(['arg_exit_transaction_keystore_keystore', 'prompt'], func=FUNC_NAME),
),
help=lambda: load_text(['arg_exit_transaction_keystore_keystore', 'help'], func=FUNC_NAME),
Expand Down Expand Up @@ -87,16 +90,14 @@
def exit_transaction_keystore(
ctx: click.Context,
chain: str,
keystore: str,
keystore: Keystore,
keystore_password: str,
validator_index: int,
epoch: int,
output_folder: str,
**kwargs: Any) -> None:
saved_keystore = Keystore.from_file(keystore)

try:
secret_bytes = saved_keystore.decrypt(keystore_password)
secret_bytes = keystore.decrypt(keystore_password)
except ValueError:
click.echo(load_text(['arg_exit_transaction_keystore_keystore_password', 'mismatch']))
exit(1)
Expand Down
13 changes: 8 additions & 5 deletions staking_deposit/cli/exit_transaction_mnemonic.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@
from staking_deposit.cli.existing_mnemonic import load_mnemonic_arguments_decorator
from staking_deposit.credentials import Credential
from staking_deposit.exit_transaction import exit_transaction_generation, export_exit_transaction_json
from staking_deposit.settings import ALL_CHAINS, MAINNET, PRATER, get_chain_setting
from staking_deposit.settings import (
MAINNET,
NON_PRATER_CHAIN_KEYS,
get_chain_setting,
)
from staking_deposit.utils.click import (
captive_prompt_callback,
choice_prompt_func,
Expand All @@ -26,19 +30,18 @@
)
@jit_option(
callback=captive_prompt_callback(
lambda x: closest_match(x, list(ALL_CHAINS.keys())),
lambda x: closest_match(x, NON_PRATER_CHAIN_KEYS),
choice_prompt_func(
lambda: load_text(['arg_exit_transaction_mnemonic_chain', 'prompt'], func=FUNC_NAME),
list(ALL_CHAINS.keys())
NON_PRATER_CHAIN_KEYS
),
),
default=MAINNET,
help=lambda: load_text(['arg_exit_transaction_mnemonic_chain', 'help'], func=FUNC_NAME),
param_decls='--chain',
prompt=choice_prompt_func(
lambda: load_text(['arg_exit_transaction_mnemonic_chain', 'prompt'], func=FUNC_NAME),
# Since `prater` is alias of `goerli`, do not show `prater` in the prompt message.
list(key for key in ALL_CHAINS.keys() if key != PRATER)
NON_PRATER_CHAIN_KEYS
),
)
@load_mnemonic_arguments_decorator
Expand Down
10 changes: 4 additions & 6 deletions staking_deposit/cli/generate_bls_to_execution_change.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,8 @@
load_text,
)
from staking_deposit.settings import (
ALL_CHAINS,
MAINNET,
PRATER,
NON_PRATER_CHAIN_KEYS,
get_chain_setting,
get_devnet_chain_setting,
)
Expand All @@ -63,19 +62,18 @@ def get_password(text: str) -> str:
)
@jit_option(
callback=captive_prompt_callback(
lambda x: closest_match(x, list(ALL_CHAINS.keys())),
lambda x: closest_match(x, NON_PRATER_CHAIN_KEYS),
choice_prompt_func(
lambda: load_text(['arg_chain', 'prompt'], func=FUNC_NAME),
list(ALL_CHAINS.keys())
NON_PRATER_CHAIN_KEYS
),
),
default=MAINNET,
help=lambda: load_text(['arg_chain', 'help'], func=FUNC_NAME),
param_decls='--chain',
prompt=choice_prompt_func(
lambda: load_text(['arg_chain', 'prompt'], func=FUNC_NAME),
# Since `prater` is alias of `goerli`, do not show `prater` in the prompt message.
list(key for key in ALL_CHAINS.keys() if key != PRATER)
NON_PRATER_CHAIN_KEYS
),
)
@load_mnemonic_arguments_decorator
Expand Down
10 changes: 4 additions & 6 deletions staking_deposit/cli/generate_keys.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,8 @@
load_text,
)
from staking_deposit.settings import (
ALL_CHAINS,
MAINNET,
PRATER,
NON_PRATER_CHAIN_KEYS,
get_chain_setting,
)

Expand Down Expand Up @@ -65,19 +64,18 @@ def generate_keys_arguments_decorator(function: Callable[..., Any]) -> Callable[
),
jit_option(
callback=captive_prompt_callback(
lambda x: closest_match(x, list(ALL_CHAINS.keys())),
lambda x: closest_match(x, NON_PRATER_CHAIN_KEYS),
choice_prompt_func(
lambda: load_text(['chain', 'prompt'], func='generate_keys_arguments_decorator'),
list(ALL_CHAINS.keys())
NON_PRATER_CHAIN_KEYS
),
),
default=MAINNET,
help=lambda: load_text(['chain', 'help'], func='generate_keys_arguments_decorator'),
param_decls='--chain',
prompt=choice_prompt_func(
lambda: load_text(['chain', 'prompt'], func='generate_keys_arguments_decorator'),
# Since `prater` is alias of `goerli`, do not show `prater` in the prompt message.
list(key for key in ALL_CHAINS.keys() if key != PRATER)
NON_PRATER_CHAIN_KEYS
),
),
jit_option(
Expand Down
2 changes: 1 addition & 1 deletion staking_deposit/intl/en/cli/exit_transaction_keystore.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"arg_exit_transaction_keystore_keystore_password": {
"help": "The password that is used to encrypt the provided keystore. Note: It's not your mnemonic password. (It is recommended not to use this argument, and wait for the CLI to ask you for your password as otherwise it will appear in your shell history.)",
"prompt": "Enter the password that is used to encrypt the provided keystore.",
"mismatch": "Error: The password does not match the provided keystore. Please try again."
"mismatch": "Error: The provided keystore password was unable to decrypt this keystore file. Make sure you have the correct password and try again."
},
"arg_exit_transaction_keystore_output_folder": {
"help": "The folder path where the exit transactions will be saved to. Pointing to `./exit_transactions` by default."
Expand Down
7 changes: 5 additions & 2 deletions staking_deposit/intl/en/utils/validation.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"msg_ECDSA_hex_addr_withdrawal": "**[Warning] you are setting an Eth1 address as your withdrawal address. Please ensure that you have control over this address.**"
},
"validate_bls_withdrawal_credentials": {
"err_is_already_eth1_form": "The given withdrawal credentials is already in ETH1_ADDRESS_WITHDRAWAL_PREFIX form. Have you already set the EL (eth1) withdrawal addresss?",
"err_is_already_eth1_form": "The given withdrawal credentials is already in ETH1_ADDRESS_WITHDRAWAL_PREFIX form. Have you already set the EL (eth1) withdrawal address?",
"err_not_bls_form": "The given withdrawal credentials is not in BLS_WITHDRAWAL_PREFIX form."
},
"validate_bls_withdrawal_credentials_matching": {
Expand All @@ -28,9 +28,12 @@
},
"normalize_bls_withdrawal_credentials_to_bytes" :{
"err_incorrect_hex_form": "The given input is not in hexadecimal encoded form."

},
"normalize_input_list": {
"err_incorrect_list": "The given input should be a list of the old BLS withdrawal credentials of your validator(s). Split multiple items with whitespaces or commas."
},
"validate_keystore_file": {
"err_file_not_found": "No file was found. Please verify the provided path and try again.",
"err_invalid_keystore_file": "The discovered file is not the correct keystore file format. Please verify the provided path is to a keystore file and try again."
}
}
2 changes: 2 additions & 0 deletions staking_deposit/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ class BaseChainSetting(NamedTuple):
HOLESKY: HoleskySetting,
}

NON_PRATER_CHAIN_KEYS: list[str] = list(key for key in ALL_CHAINS.keys() if key != PRATER)


def get_chain_setting(chain_name: str = MAINNET) -> BaseChainSetting:
return ALL_CHAINS[chain_name]
Expand Down
18 changes: 18 additions & 0 deletions staking_deposit/utils/validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from py_ecc.bls import G2ProofOfPossession as bls

from staking_deposit.exceptions import ValidationError
from staking_deposit.key_handling.keystore import Keystore
from staking_deposit.utils.intl import load_text
from staking_deposit.utils.ssz import (
BLSToExecutionChange,
Expand Down Expand Up @@ -136,6 +137,7 @@ def validate_eth1_withdrawal_address(cts: click.Context, param: Any, address: st
click.echo('\n%s\n' % load_text(['msg_ECDSA_hex_addr_withdrawal']))
return normalized_address


#
# BLSToExecutionChange
#
Expand Down Expand Up @@ -264,3 +266,19 @@ def validate_validator_indices(input_validator_indices: str) -> Sequence[int]:
def validate_bls_withdrawal_credentials_matching(bls_withdrawal_credentials: bytes, credential: Credential) -> None:
if bls_withdrawal_credentials[1:] != SHA256(credential.withdrawal_pk)[1:]:
raise ValidationError(load_text(['err_not_matching']) + '\n')


#
# Exit Message Generation
#


def validate_keystore_file(file_path: str) -> Keystore:
try:
saved_keystore = Keystore.from_file(file_path)
except FileNotFoundError:
# Required as captive_prompt_callback does not utilize click type argument for validation
raise ValidationError(load_text(['err_file_not_found']) + '\n')
except Exception:
raise ValidationError(load_text(['err_invalid_keystore_file']) + '\n')
return saved_keystore

0 comments on commit e15b8a8

Please sign in to comment.