From 4cf77ed77417e11a56742fbb819917d31d7dafbb Mon Sep 17 00:00:00 2001 From: Adam Gibson Date: Wed, 6 May 2020 21:04:28 +0100 Subject: [PATCH] Various bugfixes: Fix bug in Coins tab, get_utxos_by_mixdepth calls Fix bug in jmbitcoin.mktx, now respects nVersion choice Fix bug in tumbler restart wait Fix bug in makercount for payjoin for fee check, set to 0: The value of `options.makercount` is set to zero so that the fee sanity check in the sendpayment script operates approximately correctly (the receiver will bump the fee to keep the fee rate the same if necessary). Also the `bip79` variable is better named `payjoinurl`. Fix bug in "freeze" context menu function in Qt --- jmbitcoin/jmbitcoin/secp256k1_transaction.py | 2 +- jmclient/jmclient/taker_utils.py | 4 ++-- jmclient/jmclient/wallet_utils.py | 10 ++++---- scripts/joinmarket-qt.py | 13 +++++++---- scripts/sendpayment.py | 24 +++++++++++++------- 5 files changed, 33 insertions(+), 20 deletions(-) diff --git a/jmbitcoin/jmbitcoin/secp256k1_transaction.py b/jmbitcoin/jmbitcoin/secp256k1_transaction.py index cf988fb..705302c 100644 --- a/jmbitcoin/jmbitcoin/secp256k1_transaction.py +++ b/jmbitcoin/jmbitcoin/secp256k1_transaction.py @@ -294,7 +294,7 @@ def mktx(ins, outs, version=1, locktime=0): out = CMutableTxOut(o["value"], CCoinAddress(o["address"]).to_scriptPubKey()) vout.append(out) - return CMutableTransaction(vin, vout, nLockTime=locktime) + return CMutableTransaction(vin, vout, nLockTime=locktime, nVersion=version) def make_shuffled_tx(ins, outs, version=1, locktime=0): """ Simple wrapper to ensure transaction diff --git a/jmclient/jmclient/taker_utils.py b/jmclient/jmclient/taker_utils.py index d8e8a2d..875b343 100644 --- a/jmclient/jmclient/taker_utils.py +++ b/jmclient/jmclient/taker_utils.py @@ -4,7 +4,7 @@ import os import sys import time import numbers -from jmbase import get_log, jmprint, bintohex +from jmbase import get_log, jmprint, bintohex, hextobin from .configure import jm_single, validate_address, is_burn_destination from .schedule import human_readable_schedule_entry, tweak_tumble_schedule,\ schedule_to_text @@ -209,7 +209,7 @@ def restart_wait(txid): and confirmed (it must be an in-wallet transaction since it always spends coins from the wallet). """ - res = jm_single().bc_interface.get_transaction(txid) + res = jm_single().bc_interface.get_transaction(hextobin(txid)) if not res: return False if res["confirmations"] == 0: diff --git a/jmclient/jmclient/wallet_utils.py b/jmclient/jmclient/wallet_utils.py index 4fb30e0..ee7b336 100644 --- a/jmclient/jmclient/wallet_utils.py +++ b/jmclient/jmclient/wallet_utils.py @@ -345,8 +345,8 @@ def get_imported_privkey_branch(wallet_service, m, showprivkey): addr = wallet_service.get_address_from_path(path) script = wallet_service.get_script_from_path(path) balance = 0.0 - for data in wallet_service.get_utxos_by_mixdepth(include_disabled=True, - hexfmt=False)[m].values(): + for data in wallet_service.get_utxos_by_mixdepth( + include_disabled=True)[m].values(): if script == data['script']: balance += data['value'] used = ('used' if balance > 0.0 else 'empty') @@ -962,7 +962,7 @@ def wallet_fetch_history(wallet, options): 'history (%s)') % (btc.sat_to_str(total_wallet_balance), btc.sat_to_str(balance))) wallet_utxo_count = sum(map(len, wallet.get_utxos_by_mixdepth( - include_disabled=True, hexfmt=False).values())) + include_disabled=True).values())) if utxo_count + unconfirmed_utxo_count != wallet_utxo_count: jmprint(('BUG ERROR: wallet utxo count (%d) does not match utxo count from ' + 'history (%s)') % (wallet_utxo_count, utxo_count)) @@ -1087,8 +1087,8 @@ def display_utxos_for_disable_choice_default(wallet_service, utxos_enabled, def get_utxos_enabled_disabled(wallet_service, md): """ Returns dicts for enabled and disabled separately """ - utxos_enabled = wallet_service.get_utxos_by_mixdepth(hexfmt=False)[md] - utxos_all = wallet_service.get_utxos_by_mixdepth(include_disabled=True, hexfmt=False)[md] + utxos_enabled = wallet_service.get_utxos_by_mixdepth()[md] + utxos_all = wallet_service.get_utxos_by_mixdepth(include_disabled=True)[md] utxos_disabled_keyset = set(utxos_all).difference(set(utxos_enabled)) utxos_disabled = {} for u in utxos_disabled_keyset: diff --git a/scripts/joinmarket-qt.py b/scripts/joinmarket-qt.py index 46f033e..cbfa6d5 100755 --- a/scripts/joinmarket-qt.py +++ b/scripts/joinmarket-qt.py @@ -64,7 +64,8 @@ donation_address_url = "https://bitcoinprivacy.me/joinmarket-donations" JM_GUI_VERSION = '15dev' from jmbase import get_log -from jmbase.support import DUST_THRESHOLD, EXIT_FAILURE, JM_CORE_VERSION +from jmbase.support import DUST_THRESHOLD, EXIT_FAILURE, utxo_to_utxostr,\ + bintohex, hextobin, JM_CORE_VERSION from jmclient import load_program_config, get_network, update_persist_config,\ open_test_wallet_maybe, get_wallet_path,\ jm_single, validate_address, weighted_order_choose, Taker,\ @@ -627,6 +628,7 @@ class SpendTab(QWidget): self.waitingtxid=txid self.restartTimer.timeout.connect(self.restartWaitWrap) self.restartTimer.start(5000) + self.updateSchedView() return self.updateSchedView() self.startJoin() @@ -1174,7 +1176,9 @@ class CoinsTab(QWidget): else: for k, v in um.items(): # txid:index, btc, address - t = btc.safe_hexlify(k[0])+":"+str(k[1]) + success, t = utxo_to_utxostr(k) + # keys must be utxo format else a coding error: + assert success s = "{0:.08f}".format(v['value']/1e8) a = mainWindow.wallet_service.script_to_addr(v["script"]) item = QTreeWidgetItem([t, s, a]) @@ -1187,7 +1191,7 @@ class CoinsTab(QWidget): def toggle_utxo_disable(self, txids, idxs): for i in range(0, len(txids)): txid = txids[i] - txid_bytes = btc.safe_from_hex(txid) + txid_bytes = hextobin(txid) mainWindow.wallet_service.toggle_disable_utxo(txid_bytes, idxs[i]) self.updateUtxos() @@ -1206,7 +1210,8 @@ class CoinsTab(QWidget): assert idx >= 0 txids.append(txid) idxs.append(idx) - except: + except Exception as e: + log.error("Error retrieving txids in Coins tab: " + repr(e)) return # current item item = self.cTW.currentItem() diff --git a/scripts/sendpayment.py b/scripts/sendpayment.py index af401a5..79dd521 100755 --- a/scripts/sendpayment.py +++ b/scripts/sendpayment.py @@ -66,7 +66,7 @@ def main(): #without schedule file option, use the arguments to create a schedule #of a single transaction sweeping = False - bip79 = False + payjoinurl = None if options.schedule == '': if btc.is_bip21_uri(args[1]): parsed = btc.decode_bip21_uri(args[1]) @@ -78,14 +78,22 @@ def main(): destaddr = parsed['address'] if 'jmnick' in parsed: if "pj" in parsed: - parser.error("Cannot specify both BIP79 and Joinmarket " + parser.error("Cannot specify both BIP79++ and Joinmarket " "peer-to-peer payjoin at the same time!") sys.exit(EXIT_ARGERROR) options.p2ep = parsed['jmnick'] elif "pj" in parsed: # note that this is a URL; its validity # checking is deferred to twisted.web.client.Agent - bip79 = parsed["pj"] + payjoinurl = parsed["pj"] + # setting makercount only for fee sanity check. + # note we ignore any user setting and enforce N=0, + # as this is a flag in the code for a non-JM coinjoin; + # for the fee sanity check, note that BIP79++ currently + # will only allow very small fee changes, so N=0 won't + # be very inaccurate. + jmprint("Attempting to pay via payjoin.", "info") + options.makercount = 0 else: amount = btc.amount_to_sat(args[1]) if amount == 0: @@ -159,13 +167,13 @@ def main(): fee_per_cp_guess)) maxcjfee = (1, float('inf')) - if not (options.p2ep or bip79) and not options.pickorders and \ + if not options.p2ep and not options.pickorders and \ options.makercount != 0: maxcjfee = get_max_cj_fee_values(jm_single().config, options) log.info("Using maximum coinjoin fee limits per maker of {:.4%}, {} " "".format(maxcjfee[0], btc.amount_to_str(maxcjfee[1]))) - log.debug('starting sendpayment') + log.info('starting sendpayment') max_mix_depth = max([mixdepth, options.amtmixdepths - 1]) @@ -203,7 +211,7 @@ def main(): log.info("Estimated miner/tx fees for this coinjoin amount: {:.1%}" .format(exp_tx_fees_ratio)) - if options.makercount == 0 and not options.p2ep and not bip79: + if options.makercount == 0 and not options.p2ep and not payjoinurl: tx = direct_send(wallet_service, amount, mixdepth, destaddr, options.answeryes, with_final_psbt=options.with_psbt) if options.with_psbt: @@ -314,10 +322,10 @@ def main(): taker = P2EPTaker(options.p2ep, wallet_service, schedule, callbacks=(None, None, p2ep_on_finished_callback)) - elif bip79: + elif payjoinurl: # TODO sanity check wallet type is segwit manager = parse_payjoin_setup(args[1], wallet_service, options.mixdepth) - reactor.callWhenRunning(send_payjoin, manager) + reactor.callWhenRunning(send_payjoin, manager, tls_whitelist=["127.0.0.1"]) reactor.run() return