Browse Source

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

master
Kristaps Kaupe 2 years ago
parent
commit
904b780b80
No known key found for this signature in database
GPG Key ID: 33E472FE870C7E5D
  1. 11
      scripts/add-utxo.py
  2. 5
      scripts/bumpfee.py
  3. 18
      scripts/sendpayment.py
  4. 4
      scripts/sendtomany.py
  5. 4
      scripts/tumbler.py
  6. 3
      src/jmbase/__init__.py
  7. 30
      src/jmbase/support.py
  8. 35
      src/jmclient/cli_options.py
  9. 5
      src/jmclient/taker_utils.py
  10. 30
      src/jmclient/wallet_utils.py

11
scripts/add-utxo.py

@ -16,7 +16,7 @@ from jmclient import load_program_config, jm_single,\
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):
@ -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)

5
scripts/bumpfee.py

@ -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
@ -272,7 +272,8 @@ if __name__ == '__main__':
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)

18
scripts/sendpayment.py

@ -21,7 +21,7 @@ from jmclient import Taker, load_program_config, get_schedule,\
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
@ -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))
@ -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%}"
@ -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:
@ -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:
@ -304,7 +304,7 @@ def main():
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

4
scripts/sendtomany.py

@ -8,7 +8,7 @@ for other reasons).
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
@ -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())

4
scripts/tumbler.py

@ -16,7 +16,7 @@ from jmclient.wallet_utils import DEFAULT_MIXDEPTH
from jmbase.support import get_log, jmprint, EXIT_SUCCESS, \
EXIT_FAILURE, EXIT_ARGERROR
EXIT_FAILURE, EXIT_ARGERROR, cli_prompt_user_yesno
log = get_log()
@ -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),

3
src/jmbase/__init__.py

@ -8,7 +8,8 @@ from .support import (get_log, chunks, debug_silence, jmprint,
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,

30
src/jmbase/support.py

@ -8,7 +8,7 @@ from os import path, environ
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
@ -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
src/jmclient/cli_options.py

@ -6,7 +6,7 @@ from configparser import NoOptionError
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
@ -157,20 +157,6 @@ coinjoin amount, depending on which is larger. The actual fee is likely to be
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
@ -186,27 +172,34 @@ counterparties are selected."""
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

5
src/jmclient/taker_utils.py

@ -6,7 +6,8 @@ import time
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
@ -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:

30
src/jmclient/wallet_utils.py

@ -20,7 +20,8 @@ from jmclient.blockchaininterface import (BitcoinCoreInterface,
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
@ -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,
@ -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...")

Loading…
Cancel
Save