diff --git a/jmclient/jmclient/taker.py b/jmclient/jmclient/taker.py index ab7db58..0e3b8a9 100644 --- a/jmclient/jmclient/taker.py +++ b/jmclient/jmclient/taker.py @@ -303,8 +303,7 @@ class Taker(object): #Construct the Bitcoin address for the auth_pub field #Ensure that at least one address from utxos corresponds. input_addresses = [d['address'] for d in utxo_data] - auth_address = btc.pubkey_to_p2sh_p2wpkh_address(auth_pub, - get_p2sh_vbyte()) + auth_address = self.wallet.pubkey_to_address(auth_pub) if not auth_address in input_addresses: jlog.warn("ERROR maker's (" + nick + ")" " authorising pubkey is not included " @@ -479,7 +478,6 @@ class Taker(object): else: jlog.debug("Invalid signature message - more than 3 items") break - print("Got sig_deserialized: ", sig_deserialized) ver_amt = utxo_data[i]['value'] if wit else None sig_good = btc.verify_tx_input(txhex, u[0], utxo_data[i]['script'], ver_sig, ver_pub, witness=wit, diff --git a/jmclient/jmclient/wallet_utils.py b/jmclient/jmclient/wallet_utils.py index 4b325a5..ad64ac2 100644 --- a/jmclient/jmclient/wallet_utils.py +++ b/jmclient/jmclient/wallet_utils.py @@ -800,8 +800,9 @@ def wallet_importprivkey(wallet, mixdepth): def wallet_dumpprivkey(wallet, hdpath): pathlist = bip32pathparse(hdpath) print('got pathlist: ' + str(pathlist)) - if pathlist and len(pathlist) == 5: - cointype, purpose, m, forchange, k = pathlist + if pathlist and len(pathlist) in [5, 4]: + #note here we assume the path conforms to Wallet or SegwitWallet(BIP49) standard + m, forchange, k = pathlist[-3:] key = wallet.get_key(m, forchange, k) wifkey = btc.wif_compressed_privkey(key, vbyte=get_p2pk_vbyte()) return wifkey @@ -810,9 +811,10 @@ def wallet_dumpprivkey(wallet, hdpath): def wallet_signmessage(wallet, hdpath, message): if hdpath.startswith(wallet.get_root_path()): - m, forchange, k = [int(y) for y in hdpath[4:].split('/')] + hp = bip32pathparse(hdpath) + m, forchange, k = hp[-3:] key = wallet.get_key(m, forchange, k) - addr = btc.privkey_to_address(key, magicbyte=get_p2sh_vbyte()) + addr = wallet.pubkey_to_address(btc.privkey_to_pubkey(key)) print('Using address: ' + addr) else: print('%s is not a valid hd wallet path' % hdpath) @@ -908,7 +910,7 @@ def wallet_tool_main(wallet_root_path): wallet_importprivkey(wallet, options.mixdepth) return "Key import completed." elif method == "signmessage": - return wallet_signmessage(wallet, options.hd_path, args[1]) + return wallet_signmessage(wallet, options.hd_path, args[2]) #Testing (can port to test modules, TODO) if __name__ == "__main__": diff --git a/jmclient/test/test_taker.py b/jmclient/test/test_taker.py index 2019a24..7b20ca7 100644 --- a/jmclient/test/test_taker.py +++ b/jmclient/test/test_taker.py @@ -10,8 +10,8 @@ import pytest import json from base64 import b64encode from jmclient import (load_program_config, jm_single, set_commitment_file, - get_commitment_file, AbstractWallet, Taker, - get_p2sh_vbyte) + get_commitment_file, AbstractWallet, Taker, SegwitWallet, + get_p2sh_vbyte, get_p2pk_vbyte) from taker_test_data import (t_utxos_by_mixdepth, t_selected_utxos, t_orderbook, t_maker_response, t_chosen_orders, t_dummy_ext) @@ -52,8 +52,9 @@ class DummyWallet(AbstractWallet): """ return 'p2sh-p2wpkh' - def get_vbyte(self): - return get_p2sh_vbyte() + @classmethod + def pubkey_to_address(cls, pubkey): + return SegwitWallet.pubkey_to_address(pubkey) def get_key_from_addr(self, addr): """usable addresses: privkey all 1s, 2s, 3s, ... :""" diff --git a/jmclient/test/test_wallets.py b/jmclient/test/test_wallets.py index 8fcdd83..98d944a 100644 --- a/jmclient/test/test_wallets.py +++ b/jmclient/test/test_wallets.py @@ -14,7 +14,7 @@ from mnemonic import Mnemonic from ConfigParser import SafeConfigParser, NoSectionError from decimal import Decimal from commontest import (interact, make_wallets, - make_sign_and_push, DummyBlockchainInterface) + make_sign_and_push) import json import jmbitcoin as bitcoin diff --git a/scripts/add-utxo.py b/scripts/add-utxo.py index 948440b..86c8367 100644 --- a/scripts/add-utxo.py +++ b/scripts/add-utxo.py @@ -15,7 +15,7 @@ from pprint import pformat from optparse import OptionParser import jmclient.btc as btc from jmbase import get_password -from jmclient import (load_program_config, jm_single, get_p2pk_vbyte, SegwitWallet, +from jmclient import (load_program_config, jm_single, get_p2pk_vbyte, get_wallet_cls, WalletError, sync_wallet, add_external_commitments, generate_podle, update_commitments, PoDLE, set_commitment_file, get_podle_commitments, @@ -73,7 +73,7 @@ def main(): "BE CAREFUL about handling private keys! " "Don't do this in insecure environments. " - "Also note this ONLY works for standard (p2pkh) utxos." + "Also note this ONLY works for standard (p2pkh or p2sh-p2wpkh) utxos." ) parser.add_option( '-r', @@ -91,7 +91,7 @@ def main(): type='str', dest='in_json', help='name of json formatted file containing utxos with private keys, as ' - 'output from "python wallet-tool.py -u -p walletname showutxos"' + 'output from "python wallet-tool.py -p walletname showutxos"' ) parser.add_option( '-w', @@ -177,7 +177,7 @@ def main(): while True: pwd = get_password("Enter wallet decryption passphrase: ") try: - wallet = SegwitWallet(options.loadwallet, + wallet = get_wallet_cls()(options.loadwallet, pwd, options.maxmixdepth, options.gaplimit) @@ -231,7 +231,8 @@ def main(): else: quit(parser, 'Invalid syntax') if options.validate or options.vonly: - if not validate_utxo_data(utxo_data, segwit=True): + sw = False if jm_single().config.get("POLICY", "segwit") == "false" else True + if not validate_utxo_data(utxo_data, segwit=sw): quit(parser, "Utxos did not validate, quitting") if options.vonly: sys.exit(0) diff --git a/scripts/jmtainter.py b/scripts/jmtainter.py index 39872f9..6849fec 100644 --- a/scripts/jmtainter.py +++ b/scripts/jmtainter.py @@ -17,8 +17,8 @@ from pprint import pformat import jmbitcoin as btc from jmclient import (load_program_config, validate_address, jm_single, WalletError, sync_wallet, RegtestBitcoinCoreInterface, - estimate_tx_fee, Wallet, SegwitWallet, get_p2pk_vbyte, - get_p2sh_vbyte) + estimate_tx_fee, SegwitWallet, get_p2pk_vbyte, + get_p2sh_vbyte, get_wallet_cls) from jmbase.support import get_password def get_parser(): @@ -79,15 +79,13 @@ def is_utxo(utxo): return True def cli_get_wallet(wallet_name, sync=True): - walletclass = SegwitWallet if jm_single().config.get( - "POLICY", "segwit") == "true" else Wallet if not os.path.exists(os.path.join('wallets', wallet_name)): - wallet = walletclass(wallet_name, None, max_mix_depth=options.amtmixdepths) + wallet = get_wallet_cls()(wallet_name, None, max_mix_depth=options.amtmixdepths) else: while True: try: pwd = get_password("Enter wallet decryption passphrase: ") - wallet = walletclass(wallet_name, pwd, max_mix_depth=options.amtmixdepths) + wallet = get_wallet_cls()(wallet_name, pwd, max_mix_depth=options.amtmixdepths) except WalletError: print("Wrong password, try again.") continue @@ -213,6 +211,10 @@ if __name__ == "__main__": parser = get_parser() (options, args) = parser.parse_args() load_program_config() + if get_wallet_cls() != SegwitWallet: + print("Only segwit wallets are supported; remove any setting of `segwit`" + " in `POLICY` in joinmarket.cfg. Quitting.") + exit(0) #default args causes wallet sync here: wallet = cli_get_wallet(args[0]) if args[1] not in ['make', 'take']: diff --git a/scripts/joinmarket-qt.py b/scripts/joinmarket-qt.py index ba90737..c6bde74 100644 --- a/scripts/joinmarket-qt.py +++ b/scripts/joinmarket-qt.py @@ -1577,6 +1577,15 @@ except Exception as e: ]) JMQtMessageBox(None, config_load_error, mbtype='crit', title='failed to load') exit(1) +#refuse to load non-segwit wallet (needs extra work in wallet-utils). +if not jm_single().config.get("POLICY", "segwit") == "true": + wallet_load_error = ''.join(["Joinmarket-Qt only supports segwit based wallets, ", + "please edit the config file and remove any setting ", + "of the field `segwit` in the `POLICY` section."]) + JMQtMessageBox(None, wallet_load_error, mbtype='crit', + title='Incompatible wallet type') + exit(1) + update_config_for_gui() #to allow testing of confirm/unconfirm callback for multiple txs diff --git a/scripts/sendpayment.py b/scripts/sendpayment.py index 26aee83..c274808 100644 --- a/scripts/sendpayment.py +++ b/scripts/sendpayment.py @@ -22,9 +22,9 @@ from jmclient import (Taker, load_program_config, get_schedule, validate_address, jm_single, WalletError, choose_orders, choose_sweep_orders, cheapest_order_choose, weighted_order_choose, - Wallet, BitcoinCoreWallet, sync_wallet, - RegtestBitcoinCoreInterface, estimate_tx_fee, - direct_send, SegwitWallet) + sync_wallet, RegtestBitcoinCoreInterface, + estimate_tx_fee, direct_send, get_wallet_cls, + BitcoinCoreWallet) from twisted.python.log import startLogging from jmbase.support import get_log, debug_dump_object, get_password from cli_options import get_sendpayment_parser @@ -57,8 +57,6 @@ def main(): parser = get_sendpayment_parser() (options, args) = parser.parse_args() load_program_config() - walletclass = SegwitWallet if jm_single().config.get( - "POLICY", "segwit") == "true" else Wallet if options.schedule == '' and len(args) < 3: parser.error('Needs a wallet, amount and destination address') sys.exit(0) @@ -129,12 +127,12 @@ def main(): #to ensure we have enough, must be at least (requested index+1) max_mix_depth = max([mixdepth+1, options.amtmixdepths]) if not os.path.exists(os.path.join('wallets', wallet_name)): - wallet = walletclass(wallet_name, None, max_mix_depth, options.gaplimit) + wallet = get_wallet_cls()(wallet_name, None, max_mix_depth, options.gaplimit) else: while True: try: pwd = get_password("Enter wallet decryption passphrase: ") - wallet = walletclass(wallet_name, pwd, max_mix_depth, options.gaplimit) + wallet = get_wallet_cls()(wallet_name, pwd, max_mix_depth, options.gaplimit) except WalletError: print("Wrong password, try again.") continue @@ -155,11 +153,6 @@ def main(): direct_send(wallet, amount, mixdepth, destaddr, options.answeryes) return - if walletclass == Wallet: - print("Only direct sends (use -N 0) are supported for " - "legacy (non-segwit) wallets.") - return - def filter_orders_callback(orders_fees, cjamount): orders, total_cj_fee = orders_fees log.info("Chose these orders: " +pprint.pformat(orders)) diff --git a/scripts/sendtomany.py b/scripts/sendtomany.py index a7c58be..a5b44e0 100644 --- a/scripts/sendtomany.py +++ b/scripts/sendtomany.py @@ -103,6 +103,9 @@ def main(): if not validate_address(d): quit(parser, "Address was not valid; wrong network?: " + d) txsigned = sign(u, priv, destaddrs, segwit = not options.nonsegwit) + if not txsigned: + log.info("Transaction signing operation failed, see debug messages for details.") + return log.debug("Got signed transaction:\n" + txsigned) log.debug("Deserialized:") log.debug(pformat(btc.deserialize(txsigned))) diff --git a/scripts/tumbler.py b/scripts/tumbler.py index 2f1cf12..c032918 100644 --- a/scripts/tumbler.py +++ b/scripts/tumbler.py @@ -15,7 +15,7 @@ from twisted.python.log import startLogging from jmclient import (Taker, load_program_config, get_schedule, weighted_order_choose, JMClientProtocolFactory, start_reactor, validate_address, jm_single, WalletError, - Wallet, SegwitWallet, sync_wallet, get_tumble_schedule, + get_wallet_cls, sync_wallet, get_tumble_schedule, RegtestBitcoinCoreInterface, estimate_tx_fee, tweak_tumble_schedule, human_readable_schedule_entry, schedule_to_text, restart_waiter, get_tumble_log, @@ -40,7 +40,7 @@ def main(): wallet_name = args[0] max_mix_depth = options['mixdepthsrc'] + options['mixdepthcount'] if not os.path.exists(os.path.join('wallets', wallet_name)): - wallet = SegwitWallet(wallet_name, None, max_mix_depth) + wallet = get_wallet_cls()(wallet_name, None, max_mix_depth) else: while True: try: diff --git a/test/common.py b/test/common.py index 3be1c40..cda088a 100644 --- a/test/common.py +++ b/test/common.py @@ -12,7 +12,7 @@ from decimal import Decimal data_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) sys.path.insert(0, os.path.join(data_dir)) -from jmclient import SegwitWallet, Wallet, get_log, estimate_tx_fee, jm_single +from jmclient import get_wallet_cls, get_log, estimate_tx_fee, jm_single import jmbitcoin as btc from jmbase import chunks @@ -63,7 +63,7 @@ def make_wallets(n, fixed_seeds=None, test_wallet=False, passwords=None, - walletclass=SegwitWallet): + walletclass=None): '''n: number of wallets to be created wallet_structure: array of n arrays , each subarray specifying the number of addresses to be populated with coins @@ -85,7 +85,11 @@ def make_wallets(n, if test_wallet: w = TestWallet(seeds[i], max_mix_depth=5, pwd=passwords[i]) else: - w = walletclass(seeds[i], pwd=None, max_mix_depth=5) + if walletclass: + wc = walletclass + else: + wc = get_wallet_cls() + w = wc(seeds[i], pwd=None, max_mix_depth=5) wallets[i + start_index] = {'seed': seeds[i], 'wallet': w} for j in range(5):