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