Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unify cli user input code where limited range of answers are allowed #1674

Merged
merged 1 commit into from
Mar 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions scripts/add-utxo.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
PoDLE, get_podle_commitments, get_utxo_info, validate_utxo_data, quit,\
get_wallet_path, add_base_options, BTCEngine
from jmbase.support import EXIT_SUCCESS, EXIT_FAILURE, \
jmprint
jmprint, cli_prompt_user_yesno


def add_ext_commitments(utxo_datas):
Expand Down Expand Up @@ -152,14 +152,15 @@ def main():
if options.delete_ext:
other = options.in_file or options.in_json or options.loadwallet
if len(args) > 0 or other:
if input("You have chosen to delete commitments, other arguments "
"will be ignored; continue? (y/n)") != 'y':
if not cli_prompt_user_yesno(
"You have chosen to delete commitments, other arguments "
"will be ignored; continue?"):
jmprint("Quitting", "warning")
sys.exit(EXIT_SUCCESS)
c, e = get_podle_commitments()
jmprint(pformat(e), "info")
if input(
"You will remove the above commitments; are you sure? (y/n): ") != 'y':
if not cli_prompt_user_yesno(
"You will remove the above commitments; are you sure?"):
jmprint("Quitting", "warning")
sys.exit(EXIT_SUCCESS)
update_commitments(external_to_remove=e)
Expand Down
5 changes: 3 additions & 2 deletions scripts/bumpfee.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from decimal import Decimal
from jmbase import get_log, hextobin, bintohex
from jmbase.support import EXIT_SUCCESS, EXIT_FAILURE, EXIT_ARGERROR, jmprint
from jmbase.support import EXIT_SUCCESS, EXIT_FAILURE, EXIT_ARGERROR, jmprint, cli_prompt_user_yesno
from jmclient import jm_single, load_program_config, open_test_wallet_maybe, get_wallet_path, WalletService
from jmclient.cli_options import OptionParser, add_base_options
import jmbitcoin as btc
Expand Down Expand Up @@ -272,7 +272,8 @@ def create_bumped_tx(tx, fee_per_kb, wallet, output_index=-1):
jlog.info(btc.human_readable_transaction(bumped_tx))

if not options.answeryes:
if input('Would you like to push to the network? (y/n):')[0] != 'y':
if not cli_prompt_user_yesno(
'Would you like to push to the network?'):
jlog.info("You chose not to broadcast the transaction, quitting.")
sys.exit(EXIT_SUCCESS)

Expand Down
18 changes: 9 additions & 9 deletions scripts/sendpayment.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
EngineError, check_and_start_tor
from twisted.python.log import startLogging
from jmbase.support import get_log, jmprint, \
EXIT_FAILURE, EXIT_ARGERROR
EXIT_FAILURE, EXIT_ARGERROR, cli_prompt_user_yesno

import jmbitcoin as btc

Expand Down Expand Up @@ -174,7 +174,7 @@ def main():
"above absurd value "
f"{btc.fee_per_kb_to_str(absurd_fee)}.",
"warning")
if input("Still continue? (y/n):")[0] != "y":
if not cli_prompt_user_yesno("Still continue?"):
sys.exit("Aborted by user.")
jm_single().config.set("POLICY", "absurd_fee_per_kb",
str(max_potential_txfee))
Expand Down Expand Up @@ -227,8 +227,8 @@ def main():
if exp_tx_fees_ratio > 0.05:
jmprint('WARNING: Expected bitcoin network miner fees for this coinjoin'
' amount are roughly {:.1%}'.format(exp_tx_fees_ratio), "warning")
if input('You might want to modify your tx_fee'
' settings in joinmarket.cfg. Still continue? (y/n):')[0] != 'y':
print('You might want to modify your tx_fee settings in joinmarket.cfg.')
if not cli_prompt_user_yesno('Still continue?'):
sys.exit('Aborted by user.')
else:
log.info("Estimated miner/tx fees for this coinjoin amount: {:.1%}"
Expand All @@ -255,8 +255,8 @@ def main():
"with Payjoin. Please retry without a custom change address.")
sys.exit(EXIT_ARGERROR)
if options.makercount > 0:
if not options.answeryes and input(
general_custom_change_warning + " (y/n):")[0] != "y":
if not options.answeryes and \
not cli_prompt_user_yesno(general_custom_change_warning):
sys.exit(EXIT_ARGERROR)
engine_recognized = True
try:
Expand All @@ -265,8 +265,8 @@ def main():
engine_recognized = False
if (not engine_recognized) or (
change_addr_type != wallet_service.get_txtype()):
if not options.answeryes and input(
nonwallet_custom_change_warning + " (y/n):")[0] != "y":
if not options.answeryes and \
not cli_prompt_user_yesno(nonwallet_custom_change_warning):
sys.exit(EXIT_ARGERROR)

if options.makercount == 0 and not bip78url:
Expand Down Expand Up @@ -304,7 +304,7 @@ def filter_orders_callback(orders_fees, cjamount):
log.info('WARNING ' * 6)
log.info('\n'.join(['=' * 60] * 3))
if not options.answeryes:
if input('send with these orders? (y/n):')[0] != 'y':
if not cli_prompt_user_yesno('Send with these orders?'):
return False
return True

Expand Down
4 changes: 2 additions & 2 deletions scripts/sendtomany.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from optparse import OptionParser
import jmbitcoin as btc
from jmbase import (get_log, jmprint, bintohex, utxostr_to_utxo,
IndentedHelpFormatterWithNL)
IndentedHelpFormatterWithNL, cli_prompt_user_yesno)
from jmclient import load_program_config, estimate_tx_fee, jm_single,\
validate_address, get_utxo_info, add_base_options,\
validate_utxo_data, quit, BTCEngine, compute_tx_locktime
Expand Down Expand Up @@ -112,7 +112,7 @@ def main():
return
log.info("Got signed transaction:\n" + bintohex(txsigned.serialize()))
log.info(btc.human_readable_transaction(txsigned))
if input('Would you like to push to the network? (y/n):')[0] != 'y':
if not cli_prompt_user_yesno('Would you like to push to the network?'):
log.info("You chose not to broadcast the transaction, quitting.")
return
jm_single().bc_interface.pushtx(txsigned.serialize())
Expand Down
4 changes: 2 additions & 2 deletions scripts/tumbler.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@


from jmbase.support import get_log, jmprint, EXIT_SUCCESS, \
EXIT_FAILURE, EXIT_ARGERROR
EXIT_FAILURE, EXIT_ARGERROR, cli_prompt_user_yesno

log = get_log()

Expand Down Expand Up @@ -95,7 +95,7 @@ def main():
jmprint("For restarts, destinations are taken from schedule file,"
" so passed destinations on the command line were ignored.",
"important")
if input("OK? (y/n)") != "y":
if not cli_prompt_user_yesno("OK?"):
sys.exit(EXIT_SUCCESS)
destaddrs = [s[3] for s in schedule if s[3] not in ["INTERNAL", "addrask"]]
jmprint("Remaining destination addresses in restart: " + ",".join(destaddrs),
Expand Down
3 changes: 2 additions & 1 deletion src/jmbase/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
EXIT_SUCCESS, hexbin, dictchanger, listchanger,
JM_WALLET_NAME_PREFIX, JM_APP_NAME,
IndentedHelpFormatterWithNL, wrapped_urlparse,
bdict_sdict_convert, random_insert, dict_factory)
bdict_sdict_convert, random_insert, dict_factory,
cli_prompt_user_value, cli_prompt_user_yesno)
from .proof_of_work import get_pow, verify_pow
from .twisted_utils import (stop_reactor, is_hs_uri, get_tor_agent,
get_nontor_agent, JMHiddenService,
Expand Down
30 changes: 29 additions & 1 deletion src/jmbase/support.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from functools import wraps
from optparse import IndentedHelpFormatter
from sqlite3 import Cursor, Row
from typing import List
from typing import Callable, List, Optional
import urllib.parse as urlparse

# JoinMarket version
Expand Down Expand Up @@ -361,3 +361,31 @@ def get_free_tcp_ports(num_ports: int) -> List[int]:
def dict_factory(cursor: Cursor, row: Row) -> dict:
fields = [column[0] for column in cursor.description]
return {key: value for key, value in zip(fields, row)}

def cli_prompt_user_value(message: str,
input_check_fn: Callable[[str], bool],
input_for_default: Optional[str] = None,
default_value: Optional[str] = None) -> str:
while True:
data = input(message)
if input_for_default is not None and data == input_for_default:
return default_value
if not input_check_fn(data):
continue
return data

def cli_prompt_user_yesno(message: str) -> bool:

def cli_prompt_yesno_check(value: str) -> bool:
if len(value) > 0:
value = value.upper()
res = value[0] == "Y" or value[0] == "N"
else:
res = False
if not res:
print("Bad answer, try again.")
return res

data = cli_prompt_user_value(f"{message} (y/n): ",
cli_prompt_yesno_check)
return data[0] == "Y" or data[0] == "y"
35 changes: 14 additions & 21 deletions src/jmclient/cli_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import jmclient.support
from jmbase import JM_APP_NAME
from jmclient import jm_single, RegtestBitcoinCoreInterface, cryptoengine
from jmbase.support import print_jm_version
from jmbase.support import print_jm_version, cli_prompt_user_value

"""This exists as a separate module for two reasons:
to reduce clutter in main scripts, and refactor out
Expand Down Expand Up @@ -157,20 +157,6 @@ def prompt_user_for_cj_fee(rel_val, abs_val):
significantly less; perhaps half that amount, depending on which
counterparties are selected."""

def prompt_user_value(m, val, check):
while True:
data = input(m)
if data == 'y':
return val
try:
val_user = float(data)
except ValueError:
print("Bad answer, try again.")
continue
if not check(val_user):
continue
return val_user

rel_prompt = False
if rel_val is None:
rel_prompt = True
Expand All @@ -186,27 +172,34 @@ def prompt_user_value(m, val, check):
msg = ("\nIf you want to keep this relative limit, enter 'y';"
"\notherwise choose your own fraction (between 1 and 0): ")

def rel_check(val):
if val >= 1:
def rel_check(val: str) -> bool:
try:
val_float = float(val)
except ValueError:
print("Bad answer, try again.")
return False
if val_float >= 1:
print("Choose a number below 1! Else you will spend all your "
"bitcoins for fees!")
return False
return True

rel_val = prompt_user_value(msg, rel_val, rel_check)
rel_val = float(cli_prompt_user_value(msg, rel_check, "y", rel_val))
print("Success! Using relative fee limit of {:%}".format(rel_val))

if abs_prompt:
msg = ("\nIf you want to keep this absolute limit, enter 'y';"
"\notherwise choose your own limit in satoshi: ")

def abs_check(val):
if val % 1 != 0:
def abs_check(val: str) -> bool:
try:
val_int = int(val)
except ValueError:
print("You must choose a full number!")
return False
return True

abs_val = int(prompt_user_value(msg, abs_val, abs_check))
abs_val = int(cli_prompt_user_value(msg, abs_check, "y", abs_val))
print("Success! Using absolute fee limit of {}".format(abs_val))

print("""\nIf you don't want to see this message again, make an entry like
Expand Down
5 changes: 3 additions & 2 deletions src/jmclient/taker_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
import numbers
from typing import Callable, Optional, Union

from jmbase import get_log, jmprint, bintohex, hextobin
from jmbase import get_log, jmprint, bintohex, hextobin, \
cli_prompt_user_yesno
from .configure import jm_single, validate_address, is_burn_destination
from .schedule import human_readable_schedule_entry, tweak_tumble_schedule,\
schedule_to_text
Expand Down Expand Up @@ -211,7 +212,7 @@ def direct_send(wallet_service: WalletService, amount: int, mixdepth: int,
log.info(sending_info)
if not answeryes:
if not accept_callback:
if input('Would you like to push to the network? (y/n):')[0] != 'y':
if not cli_prompt_user_yesno('Would you like to push to the network?'):
log.info("You chose not to broadcast the transaction, quitting.")
return False
else:
Expand Down
30 changes: 15 additions & 15 deletions src/jmclient/wallet_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
from jmclient.wallet_service import WalletService
from jmbase.support import (get_password, jmprint, EXIT_FAILURE,
EXIT_ARGERROR, utxo_to_utxostr, hextobin, bintohex,
IndentedHelpFormatterWithNL, dict_factory)
IndentedHelpFormatterWithNL, dict_factory,
cli_prompt_user_yesno)

from .cryptoengine import TYPE_P2PKH, TYPE_P2SH_P2WPKH, TYPE_P2WPKH, \
TYPE_SEGWIT_WALLET_FIDELITY_BONDS
Expand Down Expand Up @@ -696,28 +697,28 @@ def cli_user_mnemonic_entry():
mnemonic_extension = None
return (mnemonic_phrase, mnemonic_extension)

def cli_do_use_mnemonic_extension():
uin = input("Would you like to use a two-factor mnemonic recovery "
"phrase? write 'n' if you don't know what this is (y/n): ")
if len(uin) == 0 or uin[0] != 'y':
def cli_do_use_mnemonic_extension() -> bool:
if cli_prompt_user_yesno("Would you like to use a two-factor mnemonic "
"recovery phrase? "
"Write 'n' if you don't know what this is"):
return True
else:
jmprint("Not using mnemonic extension", "info")
return False #no mnemonic extension
else:
return True

def cli_get_mnemonic_extension():
jmprint("Note: This will be stored in a reversible way. Do not reuse!",
"info")
return input("Enter mnemonic extension: ")

def cli_do_support_fidelity_bonds():
uin = input("Would you like this wallet to support fidelity bonds? "
"write 'n' if you don't know what this is (y/n): ")
if len(uin) == 0 or uin[0] != 'y':
def cli_do_support_fidelity_bonds() -> bool:
if cli_prompt_user_yesno("Would you like this wallet to support "
"fidelity bonds? "
"Write 'n' if you don't know what this is"):
return True
else:
jmprint("Not supporting fidelity bonds", "info")
return False
else:
return True

def wallet_generate_recover_bip39(method, walletspath, default_wallet_name,
display_seed_callback, enter_seed_callback, enter_wallet_password_callback,
Expand Down Expand Up @@ -1205,8 +1206,7 @@ def wallet_signpsbt(wallet_service, psbt):
jmprint("Base64 of the above PSBT:")
jmprint(signedpsbt.to_base64())
if signresult.is_final:
if input("Above PSBT is fully signed. Do you want to broadcast?"
"(y/n):") != "y":
if not cli_prompt_user_yesno("Above PSBT is fully signed. Do you want to broadcast?"):
jmprint("Not broadcasting.")
else:
jmprint("Broadcasting...")
Expand Down
Loading