diff --git a/jmbitcoin/jmbitcoin/secp256k1_transaction.py b/jmbitcoin/jmbitcoin/secp256k1_transaction.py index 31a4451..c234d34 100644 --- a/jmbitcoin/jmbitcoin/secp256k1_transaction.py +++ b/jmbitcoin/jmbitcoin/secp256k1_transaction.py @@ -178,7 +178,8 @@ SIGHASH_NONE = 2 SIGHASH_SINGLE = 3 SIGHASH_ANYONECANPAY = 0x80 -def segwit_signature_form(txobj, i, script, amount, hashcode=SIGHASH_ALL): +def segwit_signature_form(txobj, i, script, amount, hashcode=SIGHASH_ALL, + decoder_func=binascii.unhexlify): """Given a deserialized transaction txobj, an input index i, which spends from a witness, a script for redemption and an amount in satoshis, prepare @@ -187,7 +188,7 @@ def segwit_signature_form(txobj, i, script, amount, hashcode=SIGHASH_ALL): #if isinstance(txobj, string_or_bytes_types): # return serialize(segwit_signature_form(deserialize(txobj), i, script, # amount, hashcode)) - script = binascii.unhexlify(script) + script = decoder_func(script) nVersion = encode(txobj["version"], 256, 4)[::-1] #create hashPrevouts if hashcode & SIGHASH_ANYONECANPAY: @@ -195,7 +196,7 @@ def segwit_signature_form(txobj, i, script, amount, hashcode=SIGHASH_ALL): else: pi = "" for inp in txobj["ins"]: - pi += binascii.unhexlify(inp["outpoint"]["hash"])[::-1] + pi += decoder_func(inp["outpoint"]["hash"])[::-1] pi += encode(inp["outpoint"]["index"], 256, 4)[::-1] hashPrevouts = bin_dbl_sha256(pi) #create hashSequence @@ -208,7 +209,7 @@ def segwit_signature_form(txobj, i, script, amount, hashcode=SIGHASH_ALL): else: hashSequence = "\x00"*32 #add this input's outpoint - thisOut = binascii.unhexlify(txobj["ins"][i]["outpoint"]["hash"])[::-1] + thisOut = decoder_func(txobj["ins"][i]["outpoint"]["hash"])[::-1] thisOut += encode(txobj["ins"][i]["outpoint"]["index"], 256, 4)[::-1] scriptCode = num_to_var_int(len(script)) + script amt = encode(amount, 256, 8)[::-1] @@ -218,13 +219,13 @@ def segwit_signature_form(txobj, i, script, amount, hashcode=SIGHASH_ALL): pi = "" for out in txobj["outs"]: pi += encode(out["value"], 256, 8)[::-1] - pi += (num_to_var_int(len(binascii.unhexlify(out["script"]))) + \ - binascii.unhexlify(out["script"])) + pi += (num_to_var_int(len(decoder_func(out["script"]))) + \ + decoder_func(out["script"])) hashOutputs = bin_dbl_sha256(pi) elif hashcode & 0x1f == SIGHASH_SINGLE and i < len(txobj['outs']): pi = encode(txobj["outs"][i]["value"], 256, 8)[::-1] - pi += (num_to_var_int(len(binascii.unhexlify(txobj["outs"][i]["script"]))) + - binascii.unhexlify(txobj["outs"][i]["script"])) + pi += (num_to_var_int(len(decoder_func(txobj["outs"][i]["script"]))) + + decoder_func(txobj["outs"][i]["script"])) hashOutputs = bin_dbl_sha256(pi) else: hashOutputs = "\x00"*32 diff --git a/jmclient/jmclient/__init__.py b/jmclient/jmclient/__init__.py index e285696..11f3507 100644 --- a/jmclient/jmclient/__init__.py +++ b/jmclient/jmclient/__init__.py @@ -11,14 +11,18 @@ from btc import * from .support import (calc_cj_fee, choose_sweep_orders, choose_orders, cheapest_order_choose, weighted_order_choose, rand_norm_array, rand_pow_array, rand_exp_array, select, - select_gradual, select_greedy, select_greediest) + select_gradual, select_greedy, select_greediest, + get_random_bytes) from .jsonrpc import JsonRpcError, JsonRpcConnectionError, JsonRpc from .old_mnemonic import mn_decode, mn_encode from .slowaes import decryptData, encryptData from .taker import Taker -from .wallet import (AbstractWallet, BitcoinCoreInterface, Wallet, - BitcoinCoreWallet, estimate_tx_fee, WalletError, - create_wallet_file, SegwitWallet, Bip39Wallet, get_wallet_cls) +from .wallet import (estimate_tx_fee, WalletError, BaseWallet, ImportWalletMixin, + BIP39WalletMixin, BIP32Wallet, BIP49Wallet, LegacyWallet, + SegwitLegacyWallet, UTXOManager, WALLET_IMPLEMENTATIONS) +from .storage import (Argon2Hash, Storage, StorageError, + StoragePasswordError, VolatileStorage) +from .cryptoengine import BTCEngine, BTC_P2PKH, BTC_P2SH_P2WPKH, EngineError from .configure import (load_program_config, get_p2pk_vbyte, jm_single, get_network, validate_address, get_irc_mchannels, get_blockchain_interface_instance, get_p2sh_vbyte, set_config) diff --git a/jmclient/jmclient/client_protocol.py b/jmclient/jmclient/client_protocol.py index 5a9b2d8..32abe7c 100644 --- a/jmclient/jmclient/client_protocol.py +++ b/jmclient/jmclient/client_protocol.py @@ -16,9 +16,8 @@ import hashlib import os import sys import pprint -from jmclient import (Taker, Wallet, jm_single, get_irc_mchannels, - load_program_config, get_log, get_p2sh_vbyte, - RegtestBitcoinCoreInterface) +from jmclient import (jm_single, get_irc_mchannels, get_log, get_p2sh_vbyte, + RegtestBitcoinCoreInterface) from jmbase import _byteify import btc diff --git a/jmclient/jmclient/support.py b/jmclient/jmclient/support.py index 7020d4f..b62d514 100644 --- a/jmclient/jmclient/support.py +++ b/jmclient/jmclient/support.py @@ -1,9 +1,5 @@ from __future__ import absolute_import, print_function -import sys - -import logging -import pprint import random from jmbase.support import get_log from decimal import Decimal @@ -23,6 +19,15 @@ Only for sampling purposes """ +def get_random_bytes(num_bytes, cryptographically_secure=False): + if cryptographically_secure: + # uses os.urandom if available + generator = random.SystemRandom() + else: + generator = random + return bytes(bytearray((generator.randrange(256) for b in xrange(num_bytes)))) + + def rand_norm_array(mu, sigma, n): # use normalvariate instead of gauss for thread safety return [random.normalvariate(mu, sigma) for _ in range(n)] diff --git a/jmclient/jmclient/taker_utils.py b/jmclient/jmclient/taker_utils.py index 83b5afe..a91b108 100644 --- a/jmclient/jmclient/taker_utils.py +++ b/jmclient/jmclient/taker_utils.py @@ -7,7 +7,7 @@ import time import numbers from .configure import get_log, jm_single, validate_address from .schedule import human_readable_schedule_entry, tweak_tumble_schedule -from .wallet import Wallet, SegwitWallet, estimate_tx_fee +from .wallet import BaseWallet, estimate_tx_fee from jmclient import mktx, deserialize, sign, txhash log = get_log() @@ -42,10 +42,10 @@ def direct_send(wallet, amount, mixdepth, destaddr, answeryes=False, assert mixdepth >= 0 assert isinstance(amount, numbers.Integral) assert amount >=0 - assert isinstance(wallet, Wallet) or isinstance(wallet, SegwitWallet) + assert isinstance(wallet, BaseWallet) from pprint import pformat - txtype = 'p2sh-p2wpkh' if isinstance(wallet, SegwitWallet) else 'p2pkh' + txtype = wallet.get_txtype() if amount == 0: utxos = wallet.get_utxos_by_mixdepth()[mixdepth] if utxos == {}: @@ -79,9 +79,8 @@ def direct_send(wallet, amount, mixdepth, destaddr, answeryes=False, utxo = ins['outpoint']['hash'] + ':' + str( ins['outpoint']['index']) addr = utxos[utxo]['address'] - signing_amount = utxos[utxo]['value'] - amt = signing_amount if isinstance(wallet, SegwitWallet) else None - tx = sign(tx, index, wallet.get_key_from_addr(addr), amount=amt) + amount = utxos[utxo]['value'] + tx = sign(tx, index, wallet.get_key_from_addr(addr), amount=amount) txsigned = deserialize(tx) log.info("Got signed transaction:\n") log.info(tx + "\n") diff --git a/scripts/sendpayment.py b/scripts/sendpayment.py index 3c4e9a5..ff6598e 100644 --- a/scripts/sendpayment.py +++ b/scripts/sendpayment.py @@ -17,14 +17,12 @@ import time import os import pprint -from jmclient import (Taker, load_program_config, get_schedule, - JMClientProtocolFactory, start_reactor, - validate_address, jm_single, WalletError, - choose_orders, choose_sweep_orders, - cheapest_order_choose, weighted_order_choose, - sync_wallet, RegtestBitcoinCoreInterface, - estimate_tx_fee, direct_send, get_wallet_cls, - BitcoinCoreWallet) +from jmclient import ( + Taker, load_program_config, get_schedule, JMClientProtocolFactory, + start_reactor, validate_address, jm_single, WalletError, choose_orders, + choose_sweep_orders, cheapest_order_choose, weighted_order_choose, + sync_wallet, RegtestBitcoinCoreInterface, estimate_tx_fee, direct_send, + open_test_wallet_maybe, get_wallet_path) from twisted.python.log import startLogging from jmbase.support import get_log, debug_dump_object, get_password from cli_options import get_sendpayment_parser @@ -126,33 +124,26 @@ def main(): #maxmixdepth in the wallet is actually the *number* of mixdepths (so misnamed); #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 = get_wallet_cls()(wallet_name, None, max_mix_depth, options.gaplimit) - else: - while True: - try: - pwd = get_password("Enter wallet decryption passphrase: ") - wallet = get_wallet_cls()(wallet_name, pwd, max_mix_depth, options.gaplimit) - except WalletError: - print("Wrong password, try again.") - continue - except Exception as e: - print("Failed to load wallet, error message: " + repr(e)) - sys.exit(0) - break + + wallet_path = get_wallet_path(wallet_name, None) + wallet = open_test_wallet_maybe( + wallet_path, wallet_name, max_mix_depth, gap_limit=options.gaplimit) else: - wallet = BitcoinCoreWallet(fromaccount=wallet_name) + raise NotImplemented("Using non-joinmarket wallet is not supported.") if jm_single().config.get("BLOCKCHAIN", "blockchain_source") == "electrum-server" and options.makercount != 0: jm_single().bc_interface.synctype = "with-script" #wallet sync will now only occur on reactor start if we're joining. sync_wallet(wallet, fast=options.fastsync) if options.makercount == 0: - if isinstance(wallet, BitcoinCoreWallet): - raise NotImplementedError("Direct send only supported for JM wallets") direct_send(wallet, amount, mixdepth, destaddr, options.answeryes) return + if wallet.get_txtype() == 'p2pkh': + 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))