Skip to content

Commit

Permalink
Merge #1674: Unify cli user input code where limited range of answers…
Browse files Browse the repository at this point in the history
… are allowed

904b780 Unify cli user input code where limited range of answers are allowed (Kristaps Kaupe)

Pull request description:

  Fixes #1494.

Top commit has no ACKs.

Tree-SHA512: ec19d8e3fa9651f0eba8930dc0fc57495770cda8f1d6e926d7a361eb0e5dabe876f9e9fceaf48ae50808e30549be17109452a43411ba5e76b7f44fad0888d559
  • Loading branch information
kristapsk committed Mar 12, 2024
2 parents f5da629 + 904b780 commit cf5a841
Show file tree
Hide file tree
Showing 10 changed files with 85 additions and 60 deletions.
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 @@ -697,28 +698,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 @@ -1207,8 +1208,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

0 comments on commit cf5a841

Please sign in to comment.