diff --git a/jmclient/jmclient/__init__.py b/jmclient/jmclient/__init__.py index d6dc3da..dd57eec 100644 --- a/jmclient/jmclient/__init__.py +++ b/jmclient/jmclient/__init__.py @@ -33,9 +33,11 @@ from .electruminterface import ElectrumInterface from .client_protocol import (JMTakerClientProtocol, JMClientProtocolFactory, start_reactor) from .podle import (set_commitment_file, get_commitment_file, - generate_podle_error_string, add_external_commitments, + add_external_commitments, PoDLE, generate_podle, get_podle_commitments, update_commitments) +from .output import generate_podle_error_string, fmt_utxos, fmt_utxo,\ + fmt_tx_data from .schedule import (get_schedule, get_tumble_schedule, schedule_to_text, tweak_tumble_schedule, human_readable_schedule_entry, schedule_to_text) diff --git a/jmclient/jmclient/client_protocol.py b/jmclient/jmclient/client_protocol.py index 32abe7c..06418af 100644 --- a/jmclient/jmclient/client_protocol.py +++ b/jmclient/jmclient/client_protocol.py @@ -15,9 +15,9 @@ import json import hashlib import os import sys -import pprint from jmclient import (jm_single, get_irc_mchannels, get_log, get_p2sh_vbyte, RegtestBitcoinCoreInterface) +from .output import fmt_tx_data from jmbase import _byteify import btc @@ -240,8 +240,9 @@ class JMMakerClientProtocol(JMClientProtocol): jlog.info("Failed to find notified unconfirmed transaction: " + txid) return removed_utxos = self.client.wallet.remove_old_utxos(txd) - jlog.info('saw tx on network, removed_utxos=\n{}'.format( - pprint.pformat(removed_utxos))) + jlog.info('saw tx on network, removed_utxos=\n{}'.format('\n'.join( + '{} - {}'.format(u, fmt_tx_data(tx_data, self.client.wallet)) + for u, tx_data in removed_utxos.items()))) to_cancel, to_announce = self.client.on_tx_unconfirmed(offerinfo, txid, removed_utxos) self.client.modify_orders(to_cancel, to_announce) diff --git a/jmclient/jmclient/maker.py b/jmclient/jmclient/maker.py index 9690d12..a697b3f 100644 --- a/jmclient/jmclient/maker.py +++ b/jmclient/jmclient/maker.py @@ -7,11 +7,10 @@ import sys from binascii import unhexlify import btc -from jmclient.configure import jm_single, get_p2pk_vbyte, get_p2sh_vbyte +from jmclient.configure import jm_single from jmbase.support import get_log from jmclient.support import (calc_cj_fee) -from jmclient.podle import (generate_podle, get_podle_commitments, verify_podle, - PoDLE, PoDLEError, generate_podle_error_string) +from jmclient.podle import verify_podle, PoDLE from twisted.internet import task jlog = get_log() diff --git a/jmclient/jmclient/output.py b/jmclient/jmclient/output.py new file mode 100644 index 0000000..7393064 --- /dev/null +++ b/jmclient/jmclient/output.py @@ -0,0 +1,69 @@ +from binascii import hexlify + + +def fmt_utxos(utxos, wallet, prefix=''): + output = [] + for u in utxos: + utxo_str = '{}{} - {}'.format( + prefix, fmt_utxo(u), fmt_tx_data(utxos[u], wallet)) + output.append(utxo_str) + return '\n'.join(output) + + +def fmt_utxo(utxo): + return '{}:{}'.format(hexlify(utxo[0]), utxo[1]) + + +def fmt_tx_data(tx_data, wallet): + return 'path: {}, address: {}, value: {}'.format( + wallet.get_path_repr(wallet.script_to_path(tx_data['script'])), + wallet.script_to_addr(tx_data['script']), tx_data['value']) + + +def generate_podle_error_string(priv_utxo_pairs, to, ts, wallet, cjamount, + taker_utxo_age, taker_utxo_amtpercent): + """Gives detailed error information on why commitment sourcing failed. + """ + errmsg = "" + errmsgheader = ("Failed to source a commitment; this debugging information" + " may help:\n\n") + errmsg += ("1: Utxos that passed age and size limits, but have " + "been used too many times (see taker_utxo_retries " + "in the config):\n") + if len(priv_utxo_pairs) == 0: + errmsg += ("None\n") + else: + for p, u in priv_utxo_pairs: + errmsg += (str(u) + "\n") + errmsg += "2: Utxos that have less than " + taker_utxo_age + " confirmations:\n" + if len(to) == 0: + errmsg += ("None\n") + else: + for t in to: + errmsg += (str(t) + "\n") + errmsg += ("3: Utxos that were not at least " + taker_utxo_amtpercent + \ + "% of the size of the coinjoin amount " + str(cjamount) + "\n") + if len(ts) == 0: + errmsg += ("None\n") + else: + for t in ts: + errmsg += (str(t) + "\n") + errmsg += ('***\n') + errmsg += ("Utxos that appeared in item 1 cannot be used again.\n") + errmsg += ("Utxos only in item 2 can be used by waiting for more " + "confirmations, (set by the value of taker_utxo_age).\n") + errmsg += ("Utxos only in item 3 are not big enough for this " + "coinjoin transaction, set by the value " + "of taker_utxo_amtpercent.\n") + errmsg += ("If you cannot source a utxo from your wallet according " + "to these rules, use the tool add-utxo.py to source a " + "utxo external to your joinmarket wallet. Read the help " + "with 'python add-utxo.py --help'\n\n") + errmsg += ("***\nFor reference, here are the utxos in your wallet:\n") + + for md, utxos in wallet.get_utxos_by_mixdepth_().items(): + if not utxos: + continue + errmsg += ("\nmixdepth {}:\n{}".format( + md, fmt_utxos(utxos, wallet, prefix=' '))) + return (errmsgheader, errmsg) diff --git a/jmclient/jmclient/podle.py b/jmclient/jmclient/podle.py index 4170bfd..0aa658e 100644 --- a/jmclient/jmclient/podle.py +++ b/jmclient/jmclient/podle.py @@ -21,49 +21,6 @@ def get_commitment_file(): return PODLE_COMMIT_FILE -def generate_podle_error_string(priv_utxo_pairs, to, ts, unspent, cjamount, - taker_utxo_age, taker_utxo_amtpercent): - """Gives detailed error information on why commitment sourcing failed. - """ - errmsg = "" - errmsgheader = ("Failed to source a commitment; this debugging information" - " may help:\n\n") - errmsg += ("1: Utxos that passed age and size limits, but have " - "been used too many times (see taker_utxo_retries " - "in the config):\n") - if len(priv_utxo_pairs) == 0: - errmsg += ("None\n") - else: - for p, u in priv_utxo_pairs: - errmsg += (str(u) + "\n") - errmsg += "2: Utxos that have less than " + taker_utxo_age + " confirmations:\n" - if len(to) == 0: - errmsg += ("None\n") - else: - for t in to: - errmsg += (str(t) + "\n") - errmsg += ("3: Utxos that were not at least " + taker_utxo_amtpercent + \ - "% of the size of the coinjoin amount " + str(cjamount) + "\n") - if len(ts) == 0: - errmsg += ("None\n") - else: - for t in ts: - errmsg += (str(t) + "\n") - errmsg += ('***\n') - errmsg += ("Utxos that appeared in item 1 cannot be used again.\n") - errmsg += ("Utxos only in item 2 can be used by waiting for more " - "confirmations, (set by the value of taker_utxo_age).\n") - errmsg += ("Utxos only in item 3 are not big enough for this " - "coinjoin transaction, set by the value " - "of taker_utxo_amtpercent.\n") - errmsg += ("If you cannot source a utxo from your wallet according " - "to these rules, use the tool add-utxo.py to source a " - "utxo external to your joinmarket wallet. Read the help " - "with 'python add-utxo.py --help'\n\n") - errmsg += ("***\nFor reference, here are the utxos in your wallet:\n") - errmsg += ("\n" + str(unspent)) - return (errmsgheader, errmsg) - class PoDLEError(Exception): pass diff --git a/jmclient/jmclient/taker.py b/jmclient/jmclient/taker.py index dd75722..bc06f54 100644 --- a/jmclient/jmclient/taker.py +++ b/jmclient/jmclient/taker.py @@ -12,8 +12,9 @@ from jmbase.support import get_log from jmclient.support import (calc_cj_fee, weighted_order_choose, choose_orders, choose_sweep_orders) from jmclient.wallet import estimate_tx_fee -from jmclient.podle import (generate_podle, get_podle_commitments, - PoDLE, PoDLEError, generate_podle_error_string) +from jmclient.podle import generate_podle, get_podle_commitments, PoDLE +from .output import generate_podle_error_string + jlog = get_log() @@ -690,7 +691,7 @@ class Taker(object): "Commitment sourced OK") else: errmsgheader, errmsg = generate_podle_error_string(priv_utxo_pairs, - to, ts, self.wallet.get_utxos_by_mixdepth(), self.cjamount, + to, ts, self.wallet, self.cjamount, jm_single().config.get("POLICY", "taker_utxo_age"), jm_single().config.get("POLICY", "taker_utxo_amtpercent")) diff --git a/jmclient/test/test_podle.py b/jmclient/test/test_podle.py index 7cf7a2c..6eaee75 100644 --- a/jmclient/test/test_podle.py +++ b/jmclient/test/test_podle.py @@ -12,6 +12,7 @@ from jmclient import (load_program_config, get_log, jm_single, generate_podle, get_commitment_file, PoDLE, get_podle_commitments, add_external_commitments, update_commitments) from jmclient.podle import verify_all_NUMS, verify_podle, PoDLEError +from commontest import make_wallets log = get_log() def test_commitments_empty(setup_podle): @@ -192,14 +193,14 @@ def test_podle_error_string(setup_podle): ('fakepriv2', 'fakeutxo2')] to = ['tooold1', 'tooold2'] ts = ['toosmall1', 'toosmall2'] - unspent = "dummyunspent" + wallet = make_wallets(1, [[1, 0, 0, 0, 0]])[0]['wallet'] cjamt = 100 tua = "3" tuamtper = "20" errmgsheader, errmsg = generate_podle_error_string(priv_utxo_pairs, to, ts, - unspent, + wallet, cjamt, tua, tuamtper) @@ -208,7 +209,7 @@ def test_podle_error_string(setup_podle): y = [x[1] for x in priv_utxo_pairs] assert all([errmsg.find(x) != -1 for x in to + ts + y]) #ensure OK with nothing - errmgsheader, errmsg = generate_podle_error_string([], [], [], unspent, + errmgsheader, errmsg = generate_podle_error_string([], [], [], wallet, cjamt, tua, tuamtper) @pytest.fixture(scope="module") diff --git a/jmclient/test/test_taker.py b/jmclient/test/test_taker.py index 585358b..e49e269 100644 --- a/jmclient/test/test_taker.py +++ b/jmclient/test/test_taker.py @@ -29,8 +29,11 @@ class DummyWallet(SegwitLegacyWallet): for md, utxo in t_utxos_by_mixdepth.items(): for i, (txid, data) in enumerate(utxo.items()): txid, index = txid.split(':') + path = (b'dummy', md, i) self._utxos.add_utxo(binascii.unhexlify(txid), int(index), - (b'dummy', md, i), data['value'], md) + path, data['value'], md) + script = self._ENGINE.address_to_script(data['address']) + self._script_map[script] = path def get_utxos_by_mixdepth(self, verbose=True): return t_utxos_by_mixdepth @@ -97,6 +100,10 @@ class DummyWallet(SegwitLegacyWallet): return binascii.hexlify(p) raise ValueError("No such keypair") + def _is_my_bip32_path(self, path): + return True + + def dummy_order_chooser(): return t_chosen_orders