diff --git a/jmbase/jmbase/support.py b/jmbase/jmbase/support.py index f85c043..7e336ba 100644 --- a/jmbase/jmbase/support.py +++ b/jmbase/jmbase/support.py @@ -8,6 +8,11 @@ from getpass import getpass # global Joinmarket constants JM_WALLET_NAME_PREFIX = "joinmarket-wallet-" +# Exit status codes +EXIT_SUCCESS = 0 +EXIT_FAILURE = 1 +EXIT_ARGERROR = 2 + from chromalog.log import ( ColorizingStreamHandler, ColorizingFormatter, diff --git a/jmclient/jmclient/blockchaininterface.py b/jmclient/jmclient/blockchaininterface.py index 270563a..62a55e1 100644 --- a/jmclient/jmclient/blockchaininterface.py +++ b/jmclient/jmclient/blockchaininterface.py @@ -12,7 +12,8 @@ import jmbitcoin as btc from jmclient.jsonrpc import JsonRpcConnectionError, JsonRpcError from jmclient.configure import jm_single -from jmbase.support import get_log, jmprint +from jmbase.support import get_log, jmprint, EXIT_SUCCESS, EXIT_FAILURE + # an inaccessible blockheight; consider rewriting in 1900 years INF_HEIGHT = 10**8 @@ -227,7 +228,7 @@ class BitcoinCoreInterface(BlockchainInterface): restart_cb(fatal_msg) else: jmprint(fatal_msg, "important") - sys.exit(1) + sys.exit(EXIT_FAILURE) def add_watchonly_addresses(self, addr_list, wallet_name, restart_cb=None): """For backwards compatibility, this fn name is preserved @@ -247,7 +248,7 @@ class BitcoinCoreInterface(BlockchainInterface): restart_cb(restart_msg) else: jmprint(restart_msg, "important") - sys.exit(0) + sys.exit(EXIT_SUCCESS) def _yield_transactions(self, wallet_name): batch_size = 1000 diff --git a/jmclient/jmclient/client_protocol.py b/jmclient/jmclient/client_protocol.py index 4b99382..4aabca8 100644 --- a/jmclient/jmclient/client_protocol.py +++ b/jmclient/jmclient/client_protocol.py @@ -21,6 +21,7 @@ from jmbase import get_log from jmclient import (jm_single, get_irc_mchannels, RegtestBitcoinCoreInterface) import jmbitcoin as btc +from jmbase.support import EXIT_FAILURE jlog = get_log() @@ -556,7 +557,7 @@ def start_reactor(host, port, factory, ish=True, daemon=False, rs=True, jlog.warn("Cannot listen on port " + str(port) + ", trying next port") if port >= (orgport + 100): jlog.error("Tried 100 ports but cannot listen on any of them. Quitting.") - sys.exit(1) + sys.exit(EXIT_FAILURE) port += 1 else: # if daemon run separately, and we do p2ep, we are using diff --git a/jmclient/jmclient/commitment_utils.py b/jmclient/jmclient/commitment_utils.py index 73637a2..fb67b72 100644 --- a/jmclient/jmclient/commitment_utils.py +++ b/jmclient/jmclient/commitment_utils.py @@ -6,10 +6,12 @@ import sys import jmbitcoin as btc from jmbase import jmprint from jmclient import jm_single, get_p2pk_vbyte, get_p2sh_vbyte +from jmbase.support import EXIT_FAILURE + def quit(parser, errmsg): #pragma: no cover parser.error(errmsg) - sys.exit(0) + sys.exit(EXIT_FAILURE) def get_utxo_info(upriv): """Verify that the input string parses correctly as (utxo, priv) @@ -66,4 +68,4 @@ def validate_utxo_data(utxo_datas, retrieve=False, segwit=False): jmprint('all utxos validated OK', "success") if retrieve: return results - return True \ No newline at end of file + return True diff --git a/jmclient/jmclient/maker.py b/jmclient/jmclient/maker.py index 501a9a6..43ebc0d 100644 --- a/jmclient/jmclient/maker.py +++ b/jmclient/jmclient/maker.py @@ -16,7 +16,7 @@ import jmbitcoin as btc from jmclient.wallet import estimate_tx_fee from jmclient.wallet_service import WalletService from jmclient.configure import jm_single -from jmbase.support import get_log +from jmbase.support import get_log, EXIT_SUCCESS, EXIT_FAILURE from jmclient.support import calc_cj_fee, select_one_utxo from jmclient.podle import verify_podle, PoDLE, PoDLEError from twisted.internet import task, reactor @@ -48,7 +48,7 @@ class Maker(object): self.sync_wait_loop.stop() if not self.offerlist: jlog.info("Failed to create offers, giving up.") - sys.exit(0) + sys.exit(EXIT_FAILURE) jlog.info('offerlist={}'.format(self.offerlist)) def on_auth_received(self, nick, offer, commitment, cr, amount, kphex): @@ -339,7 +339,7 @@ class P2EPMaker(Maker): f.write("Amount (in sats): " + str(self.receiving_amount) + "\n") f.write("Receiver nick: " + jm_single().nickname + "\n") if not self.user_check("Enter 'y' to wait for the payment:"): - sys.exit(0) + sys.exit(EXIT_SUCCESS) def create_my_orders(self): """ Fake offer for public consumption. diff --git a/jmclient/jmclient/podle.py b/jmclient/jmclient/podle.py index d9b72a8..fddec11 100644 --- a/jmclient/jmclient/podle.py +++ b/jmclient/jmclient/podle.py @@ -13,6 +13,7 @@ import struct from jmbase import jmprint from jmbitcoin import multiply, add_pubkeys, getG, podle_PublicKey,\ podle_PrivateKey, encode, decode, N, podle_PublicKey_class +from jmbase.support import EXIT_FAILURE PODLE_COMMIT_FILE = None @@ -315,7 +316,7 @@ def update_commitments(commitment=None, #Exit conditions cannot be included in tests. jmprint("the file: " + PODLE_COMMIT_FILE + " is not valid json.", "error") - sys.exit(0) + sys.exit(EXIT_FAILURE) if 'used' in c: commitments = c['used'] diff --git a/jmclient/jmclient/taker_utils.py b/jmclient/jmclient/taker_utils.py index dae40c4..4ed6850 100644 --- a/jmclient/jmclient/taker_utils.py +++ b/jmclient/jmclient/taker_utils.py @@ -13,6 +13,7 @@ from .schedule import human_readable_schedule_entry, tweak_tumble_schedule,\ schedule_to_text from .wallet import BaseWallet, estimate_tx_fee from jmbitcoin import deserialize, mktx, serialize, txhash, amount_to_str +from jmbase.support import EXIT_SUCCESS log = get_log() """ @@ -141,7 +142,7 @@ def restart_wait(txid): return False if res["confirmations"] < 0: log.warn("Tx: " + txid + " has a conflict, abandoning.") - sys.exit(0) + sys.exit(EXIT_SUCCESS) else: log.debug("Tx: " + str(txid) + " has " + str( res["confirmations"]) + " confirmations.") diff --git a/jmclient/jmclient/wallet_utils.py b/jmclient/jmclient/wallet_utils.py index 601bf81..9d5536b 100644 --- a/jmclient/jmclient/wallet_utils.py +++ b/jmclient/jmclient/wallet_utils.py @@ -17,7 +17,8 @@ from jmclient import (get_network, WALLET_IMPLEMENTATIONS, Storage, podle, VolatileStorage, StoragePasswordError, is_segwit_mode, SegwitLegacyWallet, LegacyWallet, SegwitWallet, is_native_segwit_mode) from jmclient.wallet_service import WalletService -from jmbase.support import get_password, jmprint +from jmbase.support import get_password, jmprint, EXIT_FAILURE, EXIT_ARGERROR + from .cryptoengine import TYPE_P2PKH, TYPE_P2SH_P2WPKH, TYPE_P2WPKH from .output import fmt_utxo import jmbitcoin as btc @@ -1218,11 +1219,11 @@ def wallet_tool_main(wallet_root_path): if len(args) < 1: parser.error('Needs a wallet file or method') - sys.exit(0) + sys.exit(EXIT_ARGERROR) if options.mixdepth is not None and options.mixdepth < 0: parser.error("Must have at least one mixdepth.") - sys.exit(0) + sys.exit(EXIT_ARGERROR) if args[0] in noseed_methods: method = args[0] @@ -1263,7 +1264,7 @@ def wallet_tool_main(wallet_root_path): if not isinstance(jm_single().bc_interface, BitcoinCoreInterface): jmprint('showing history only available when using the Bitcoin Core ' + 'blockchain interface', "error") - sys.exit(0) + sys.exit(EXIT_ARGERROR) else: return wallet_fetch_history(wallet_service, options) elif method == "generate": @@ -1293,13 +1294,13 @@ def wallet_tool_main(wallet_root_path): return wallet_freezeutxo(wallet_service, options.mixdepth) else: parser.error("Unknown wallet-tool method: " + method) - sys.exit(0) + sys.exit(EXIT_ARGERROR) #Testing (can port to test modules, TODO) if __name__ == "__main__": if not test_bip32_pathparse(): - sys.exit(0) + sys.exit(EXIT_FAILURE) rootpath="m/0" walletbranch = 0 accounts = range(3) diff --git a/jmclient/jmclient/yieldgenerator.py b/jmclient/jmclient/yieldgenerator.py index 019cc92..8d2c92f 100644 --- a/jmclient/jmclient/yieldgenerator.py +++ b/jmclient/jmclient/yieldgenerator.py @@ -15,6 +15,7 @@ from jmclient import Maker, jm_single, load_program_config, \ JMClientProtocolFactory, start_reactor, \ calc_cj_fee, WalletService from .wallet_utils import open_test_wallet_maybe, get_wallet_path +from jmbase.support import EXIT_ARGERROR jlog = get_log() @@ -228,7 +229,7 @@ def ygmain(ygclass, txfee=1000, cjfee_a=200, cjfee_r=0.002, ordertype='swreloffe (options, args) = parser.parse_args() if len(args) < 1: parser.error('Needs a wallet') - sys.exit(0) + sys.exit(EXIT_ARGERROR) wallet_name = args[0] ordertype = options.ordertype txfee = options.txfee @@ -245,7 +246,7 @@ def ygmain(ygclass, txfee=1000, cjfee_a=200, cjfee_r=0.002, ordertype='swreloffe else: parser.error('You specified an incorrect offer type which ' +\ 'can be either swreloffer or swabsoffer') - sys.exit(0) + sys.exit(EXIT_ARGERROR) nickserv_password = options.password load_program_config() diff --git a/scripts/add-utxo.py b/scripts/add-utxo.py index aaafd3a..b4ac22d 100644 --- a/scripts/add-utxo.py +++ b/scripts/add-utxo.py @@ -22,6 +22,7 @@ from jmclient import load_program_config, jm_single, get_p2pk_vbyte,\ open_wallet, WalletService, add_external_commitments, update_commitments,\ PoDLE, get_podle_commitments, get_utxo_info, validate_utxo_data, quit,\ get_wallet_path +from jmbase.support import EXIT_SUCCESS, EXIT_FAILURE, EXIT_ARGERROR def add_ext_commitments(utxo_datas): @@ -163,16 +164,16 @@ def main(): if input("You have chosen to delete commitments, other arguments " "will be ignored; continue? (y/n)") != 'y': jmprint("Quitting", "warning") - sys.exit(0) + 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': jmprint("Quitting", "warning") - sys.exit(0) + sys.exit(EXIT_SUCCESS) update_commitments(external_to_remove=e) jmprint("Commitments deleted.", "important") - sys.exit(0) + sys.exit(EXIT_SUCCESS) #Three options (-w, -r, -R) for loading utxo and privkey pairs from a wallet, #csv file or json file. @@ -206,13 +207,13 @@ def main(): elif options.in_json: if not os.path.isfile(options.in_json): jmprint("File: " + options.in_json + " not found.", "error") - sys.exit(0) + sys.exit(EXIT_FAILURE) with open(options.in_json, "rb") as f: try: utxo_json = json.loads(f.read()) except: jmprint("Failed to read json from " + options.in_json, "error") - sys.exit(0) + sys.exit(EXIT_FAILURE) for u, pva in iteritems(utxo_json): utxo_data.append((u, pva['privkey'])) elif len(args) == 1: @@ -230,7 +231,7 @@ def main(): if not validate_utxo_data(utxo_data, segwit=sw): quit(parser, "Utxos did not validate, quitting") if options.vonly: - sys.exit(0) + sys.exit(EXIT_ARGERROR) #We are adding utxos to the external list assert len(utxo_data) diff --git a/scripts/obwatch/ob-watcher.py b/scripts/obwatch/ob-watcher.py index 9b10bb8..adfe240 100644 --- a/scripts/obwatch/ob-watcher.py +++ b/scripts/obwatch/ob-watcher.py @@ -19,6 +19,7 @@ from decimal import Decimal from optparse import OptionParser from twisted.internet import reactor +from jmbase.support import EXIT_FAILURE # https://stackoverflow.com/questions/2801882/generating-a-png-with-matplotlib-when-display-is-undefined @@ -29,7 +30,7 @@ try: except: print("matplotlib not found; do `pip install matplotlib`" "in the joinmarket virtualenv.") - sys.exit(0) + sys.exit(EXIT_FAILURE) from jmbase import get_log from jmclient import jm_single, load_program_config, calc_cj_fee, get_irc_mchannels diff --git a/scripts/receive-payjoin.py b/scripts/receive-payjoin.py index e5006f1..618f321 100644 --- a/scripts/receive-payjoin.py +++ b/scripts/receive-payjoin.py @@ -12,6 +12,7 @@ from jmclient import P2EPMaker, jm_single, load_program_config, \ sync_wallet, JMClientProtocolFactory, start_reactor, \ open_test_wallet_maybe, get_wallet_path from cli_options import check_regtest +from jmbase.support import EXIT_FAILURE, EXIT_ARGERROR jlog = get_log() @@ -45,16 +46,16 @@ def receive_payjoin_main(makerclass): (options, args) = parser.parse_args() if len(args) < 2: parser.error('Needs a wallet, and a receiving amount in satoshis') - sys.exit(0) + sys.exit(EXIT_ARGERROR) wallet_name = args[0] try: receiving_amount = int(args[1]) except: parser.error("Invalid receiving amount passed: " + receiving_amount) - sys.exit(0) + sys.exit(EXIT_FAILURE) if receiving_amount < 0: parser.error("Receiving amount must be a positive integer in satoshis") - sys.exit(0) + sys.exit(EXIT_FAILURE) load_program_config() check_regtest() diff --git a/scripts/sendpayment.py b/scripts/sendpayment.py index 12e6808..3741043 100644 --- a/scripts/sendpayment.py +++ b/scripts/sendpayment.py @@ -19,7 +19,9 @@ from jmclient import Taker, P2EPTaker, load_program_config, get_schedule,\ estimate_tx_fee, direct_send, WalletService,\ open_test_wallet_maybe, get_wallet_path from twisted.python.log import startLogging -from jmbase.support import get_log, set_logging_level, jmprint +from jmbase.support import get_log, set_logging_level, jmprint, \ + EXIT_FAILURE, EXIT_ARGERROR + from cli_options import get_sendpayment_parser, get_max_cj_fee_values, \ check_regtest import jmbitcoin as btc @@ -55,11 +57,11 @@ def main(): if options.p2ep and len(args) != 3: parser.error("PayJoin requires exactly three arguments: " "wallet, amount and destination address.") - sys.exit(0) + sys.exit(EXIT_ARGERROR) elif options.schedule == '' and len(args) != 3: parser.error("Joinmarket sendpayment (coinjoin) needs arguments:" " wallet, amount and destination address") - sys.exit(0) + sys.exit(EXIT_ARGERROR) #without schedule file option, use the arguments to create a schedule #of a single transaction @@ -73,18 +75,18 @@ def main(): addr_valid, errormsg = validate_address(destaddr) if not addr_valid: jmprint('ERROR: Address invalid. ' + errormsg, "error") - return + sys.exit(EXIT_ARGERROR) schedule = [[options.mixdepth, amount, options.makercount, destaddr, 0.0, 0]] else: if options.p2ep: parser.error("Schedule files are not compatible with PayJoin") - sys.exit(0) + sys.exit(EXIT_FAILURE) result, schedule = get_schedule(options.schedule) if not result: log.error("Failed to load schedule file, quitting. Check the syntax.") log.error("Error was: " + str(schedule)) - sys.exit(0) + sys.exit(EXIT_FAILURE) mixdepth = 0 for s in schedule: if s[1] == 0: @@ -167,7 +169,7 @@ def main(): if wallet.get_txtype() == 'p2pkh': jmprint("Only direct sends (use -N 0) are supported for " "legacy (non-segwit) wallets.", "error") - return + sys.exit(EXIT_ARGERROR) def filter_orders_callback(orders_fees, cjamount): orders, total_cj_fee = orders_fees diff --git a/scripts/tumbler.py b/scripts/tumbler.py index be2b191..7b303fa 100644 --- a/scripts/tumbler.py +++ b/scripts/tumbler.py @@ -14,7 +14,8 @@ from jmclient import Taker, load_program_config, get_schedule,\ schedule_to_text, estimate_tx_fee, restart_waiter, WalletService,\ get_tumble_log, tumbler_taker_finished_update,\ tumbler_filter_orders_callback, validate_address -from jmbase.support import get_log, jmprint +from jmbase.support import get_log, jmprint, EXIT_SUCCESS, \ + EXIT_FAILURE, EXIT_ARGERROR from cli_options import get_tumbler_parser, get_max_cj_fee_values, \ check_regtest @@ -30,7 +31,7 @@ def main(): options = vars(options) if len(args) < 1: jmprint('Error: Needs a wallet file', "error") - sys.exit(0) + sys.exit(EXIT_ARGERROR) load_program_config() check_regtest() @@ -62,7 +63,7 @@ def main(): success, errmsg = validate_address(daddr) if not success: jmprint("Invalid destination address: " + daddr, "error") - sys.exit(1) + sys.exit(EXIT_ARGERROR) jmprint("Destination addresses: " + str(destaddrs), "important") #If the --restart flag is set we read the schedule #from the file, and filter out entries that are @@ -74,7 +75,7 @@ def main(): jmprint("Failed to load schedule, name: " + str( options['schedulefile']), "error") jmprint("Error was: " + str(schedule), "error") - sys.exit(0) + sys.exit(EXIT_FAILURE) #This removes all entries that are marked as done schedule = [s for s in schedule if s[5] != 1] # remaining destination addresses must be stored in Taker.tdestaddrs @@ -85,7 +86,7 @@ def main(): " so passed destinations on the command line were ignored.", "important") if input("OK? (y/n)") != "y": - sys.exit(0) + 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), "important") @@ -100,7 +101,7 @@ def main(): schedule = schedule[1:] elif schedule[0][5] != 0: print("Error: first schedule entry is invalid.") - sys.exit(0) + sys.exit(EXIT_FAILURE) with open(os.path.join(logsdir, options['schedulefile']), "wb") as f: f.write(schedule_to_text(schedule)) tumble_log.info("TUMBLE RESTARTING") diff --git a/setupall.py b/setupall.py index 03170db..230455d 100644 --- a/setupall.py +++ b/setupall.py @@ -26,7 +26,7 @@ def help(): "`--client-bitcoin` - using joinmarket bitcoin code, installs secp256k1\n" "`--develop` - uses the local code for all packages (does not install to site-packages)." ) - sys.exit(0) + sys.exit(2) if len(sys.argv) != 2: help()