From c139067be48377dba9d29fc45c1424d199676a9a Mon Sep 17 00:00:00 2001 From: AdamISZ Date: Tue, 8 Jan 2019 18:59:07 +0100 Subject: [PATCH] Include chromalog package for colorized logs Also use this lib to print non-log messages with standardized color formats. Some clean up in jmbase, remove unused objects/methods. --- jmbase/jmbase/__init__.py | 2 +- jmbase/jmbase/support.py | 98 ++++++++++++++++-------- jmbase/setup.py | 3 +- jmbase/test/test_base_support.py | 21 +---- jmclient/jmclient/blockchaininterface.py | 6 +- jmclient/jmclient/client_protocol.py | 2 +- jmclient/jmclient/commitment_utils.py | 20 ++--- jmclient/jmclient/configure.py | 9 ++- jmclient/jmclient/electruminterface.py | 18 ++--- jmclient/jmclient/podle.py | 4 +- jmclient/jmclient/taker.py | 3 +- jmclient/jmclient/taker_utils.py | 17 ++-- jmclient/jmclient/wallet_utils.py | 72 +++++++++-------- scripts/add-utxo.py | 15 ++-- scripts/sendpayment.py | 30 ++++---- scripts/sendtomany.py | 4 +- scripts/tumbler.py | 12 +-- scripts/wallet-tool.py | 3 +- scripts/yg-privacyenhanced.py | 4 +- scripts/yield-generator-basic.py | 4 +- test/ygrunner.py | 19 +++-- 21 files changed, 198 insertions(+), 168 deletions(-) diff --git a/jmbase/jmbase/__init__.py b/jmbase/jmbase/__init__.py index e5a6eb4..5c59349 100644 --- a/jmbase/jmbase/__init__.py +++ b/jmbase/jmbase/__init__.py @@ -2,7 +2,7 @@ from __future__ import (absolute_import, division, print_function, unicode_literals) from builtins import * -from .support import (get_log, chunks, debug_silence, debug_dump_object, +from .support import (get_log, chunks, debug_silence, jmprint, joinmarket_alert, core_alert, get_password, set_logging_level) from .commands import * diff --git a/jmbase/jmbase/support.py b/jmbase/jmbase/support.py index d4219b1..e0f14cc 100644 --- a/jmbase/jmbase/support.py +++ b/jmbase/jmbase/support.py @@ -1,16 +1,48 @@ from __future__ import (absolute_import, division, print_function, unicode_literals) from builtins import * # noqa: F401 -from future.utils import iteritems - -import sys import logging -import pprint from getpass import getpass -logFormatter = logging.Formatter( - "%(asctime)s [%(threadName)-12.12s] [%(levelname)-5.5s] %(message)s") +from chromalog.log import ( + ColorizingStreamHandler, + ColorizingFormatter, +) +from chromalog.colorizer import GenericColorizer +from colorama import Fore, Back, Style + +# magic; importing e.g. 'info' actually instantiates +# that as a function that uses the color map +# defined below. ( noqa because flake doesn't understand) +from chromalog.mark.helpers.simple import ( # noqa: F401 + debug, + info, + important, + success, + warning, + error, + critical, +) + +# our chosen colorings for log messages in JM: +jm_color_map = { + 'debug': (Style.DIM + Fore.LIGHTBLUE_EX, Style.RESET_ALL), + 'info': (Style.BRIGHT + Fore.BLUE, Style.RESET_ALL), + 'important': (Style.BRIGHT, Style.RESET_ALL), + 'success': (Fore.GREEN, Style.RESET_ALL), + 'warning': (Fore.YELLOW, Style.RESET_ALL), + 'error': (Fore.RED, Style.RESET_ALL), + 'critical': (Back.RED, Style.RESET_ALL), +} + +class JMColorizer(GenericColorizer): + default_color_map = jm_color_map + +jm_colorizer = JMColorizer() + +logFormatter = ColorizingFormatter( + "%(asctime)s [%(levelname)s] %(message)s") log = logging.getLogger('joinmarket') log.setLevel(logging.DEBUG) @@ -21,11 +53,10 @@ debug_silence = [False] #TODO pass this through from client, bitcoin paramater: DUST_THRESHOLD = 2730 -#consoleHandler = logging.StreamHandler(stream=sys.stdout) -class JoinMarketStreamHandler(logging.StreamHandler): +class JoinMarketStreamHandler(ColorizingStreamHandler): - def __init__(self, stream): - super(JoinMarketStreamHandler, self).__init__(stream) + def __init__(self): + super(JoinMarketStreamHandler, self).__init__(colorizer=jm_colorizer) def emit(self, record): if joinmarket_alert[0]: @@ -35,10 +66,31 @@ class JoinMarketStreamHandler(logging.StreamHandler): if not debug_silence[0]: super(JoinMarketStreamHandler, self).emit(record) +handler = JoinMarketStreamHandler() +handler.setFormatter(logFormatter) +log.addHandler(handler) + +def jmprint(msg, level="info"): + """ Provides the ability to print messages + with consistent formatting, outside the logging system + (in case you don't want the standard log format). + Example applications are: REPL style stuff, and/or + some very important / user workflow affecting communication. + Note that this exclusively for console printout, NOT for + logging to file (chromalog will handle file streams + properly, but this will not). + """ + if not level in jm_color_map.keys(): + raise Exception("Unsupported formatting") -consoleHandler = JoinMarketStreamHandler(stream=sys.stdout) -consoleHandler.setFormatter(logFormatter) -log.addHandler(consoleHandler) + # .colorize_message function does a .format() on the string, + # which does not work with string-ified json; this should + # result in output as intended: + msg = msg.replace('{', '{{') + msg = msg.replace('}', '}}') + + fmtfn = eval(level) + print(jm_colorizer.colorize_message(fmtfn(msg))) def get_log(): """ @@ -48,7 +100,7 @@ def get_log(): return log def set_logging_level(level): - consoleHandler.setLevel(level) + handler.setLevel(level) def chunks(d, n): return [d[x:x + n] for x in range(0, len(d), n)] @@ -58,21 +110,3 @@ def get_password(msg): #pragma: no cover if not isinstance(password, bytes): password = password.encode('utf-8') return password - -def debug_dump_object(obj, skip_fields=None): - if skip_fields is None: - skip_fields = [] - log.debug('Class debug dump, name:' + obj.__class__.__name__) - for k, v in iteritems(obj.__dict__): - if k in skip_fields: - continue - if k == 'password' or k == 'given_password': - continue - log.debug('key=' + k) - if isinstance(v, str): - log.debug('string: len:' + str(len(v))) - log.debug(v) - elif isinstance(v, dict) or isinstance(v, list): - log.debug(pprint.pformat(v)) - else: - log.debug(str(v)) diff --git a/jmbase/setup.py b/jmbase/setup.py index a2575dd..54e176f 100644 --- a/jmbase/setup.py +++ b/jmbase/setup.py @@ -9,5 +9,6 @@ setup(name='joinmarketbase', author_email='', license='GPL', packages=['jmbase'], - install_requires=['future', 'twisted==18.9.0', 'service-identity'], + install_requires=['future', 'twisted==18.9.0', 'service-identity', + 'chromalog==1.0.5'], zip_safe=False) diff --git a/jmbase/test/test_base_support.py b/jmbase/test/test_base_support.py index aaeb9ea..f086524 100644 --- a/jmbase/test/test_base_support.py +++ b/jmbase/test/test_base_support.py @@ -2,25 +2,10 @@ from __future__ import (absolute_import, division, print_function, unicode_literals) from builtins import * # noqa: F401 -from jmbase.support import debug_dump_object, joinmarket_alert -def test_debug_dump_object(): - joinmarket_alert[0] = "dummy jm alert" - class TestObj(object): - def __init__(self): - self.x = "foo" - self.password = "bar" - self.y = "baz" - to = TestObj() - debug_dump_object(to) - to.given_password = "baa" - debug_dump_object(to) - to.extradict = {1:2, 3:4} - debug_dump_object(to) - to.extralist = ["dummy", "list"] - debug_dump_object(to) - to.extradata = 100 - debug_dump_object(to, skip_fields="y") +def test_color_coded_logging(): + # TODO + pass diff --git a/jmclient/jmclient/blockchaininterface.py b/jmclient/jmclient/blockchaininterface.py index 5ac5f98..7f230c0 100644 --- a/jmclient/jmclient/blockchaininterface.py +++ b/jmclient/jmclient/blockchaininterface.py @@ -16,7 +16,7 @@ import jmbitcoin as btc from jmclient.jsonrpc import JsonRpcConnectionError, JsonRpcError from jmclient.configure import get_p2pk_vbyte, jm_single -from jmbase.support import get_log +from jmbase.support import get_log, jmprint log = get_log() @@ -388,7 +388,7 @@ class BitcoinCoreInterface(BlockchainInterface): if restart_cb: restart_cb(restart_msg) else: - print(restart_msg) + jmprint(restart_msg, "important") sys.exit(0) def sync_wallet(self, wallet, fast=False, restart_cb=None): @@ -936,7 +936,7 @@ class RegtestBitcoinCoreInterface(BitcoinCoreInterface): #pragma: no cover ret = super(RegtestBitcoinCoreInterface, self).pushtx(txhex) if not self.simulating and self.tick_forward_chain_interval > 0: - print('will call tfc after ' + str(self.tick_forward_chain_interval) + ' seconds.') + log.debug('will call tfc after ' + str(self.tick_forward_chain_interval) + ' seconds.') reactor.callLater(self.tick_forward_chain_interval, self.tick_forward_chain, 1) return ret diff --git a/jmclient/jmclient/client_protocol.py b/jmclient/jmclient/client_protocol.py index 6a5d76e..047e721 100644 --- a/jmclient/jmclient/client_protocol.py +++ b/jmclient/jmclient/client_protocol.py @@ -57,7 +57,7 @@ class JMClientProtocol(amp.AMP): d.addErrback(self.defaultErrback) def connectionMade(self): - print('connection was made, starting client') + jlog.debug('connection was made, starting client.') self.factory.setClient(self) self.clientStart() diff --git a/jmclient/jmclient/commitment_utils.py b/jmclient/jmclient/commitment_utils.py index 37ed6a3..73637a2 100644 --- a/jmclient/jmclient/commitment_utils.py +++ b/jmclient/jmclient/commitment_utils.py @@ -4,6 +4,7 @@ from builtins import * # noqa: F401 import sys import jmbitcoin as btc +from jmbase import jmprint from jmclient import jm_single, get_p2pk_vbyte, get_p2sh_vbyte def quit(parser, errmsg): #pragma: no cover @@ -25,12 +26,12 @@ def get_utxo_info(upriv): assert n in range(256) except: #not sending data to stdout in case privkey info - print("Failed to parse utxo information for utxo") + jmprint("Failed to parse utxo information for utxo", "error") raise try: hexpriv = btc.from_wif_privkey(priv, vbyte=get_p2pk_vbyte()) except: - print("failed to parse privkey, make sure it's WIF compressed format.") + jmprint("failed to parse privkey, make sure it's WIF compressed format.", "error") raise return u, priv @@ -43,27 +44,26 @@ def validate_utxo_data(utxo_datas, retrieve=False, segwit=False): """ results = [] for u, priv in utxo_datas: - print('validating this utxo: ' + str(u)) + jmprint('validating this utxo: ' + str(u), "info") hexpriv = btc.from_wif_privkey(priv, vbyte=get_p2pk_vbyte()) if segwit: addr = btc.pubkey_to_p2sh_p2wpkh_address( btc.privkey_to_pubkey(hexpriv), get_p2sh_vbyte()) else: addr = btc.privkey_to_address(hexpriv, magicbyte=get_p2pk_vbyte()) - print('claimed address: ' + addr) + jmprint('claimed address: ' + addr, "info") res = jm_single().bc_interface.query_utxo_set([u]) - print('blockchain shows this data: ' + str(res)) if len(res) != 1 or None in res: - print("utxo not found on blockchain: " + str(u)) + jmprint("utxo not found on blockchain: " + str(u), "error") return False if res[0]['address'] != addr: - print("privkey corresponds to the wrong address for utxo: " + str(u)) - print("blockchain returned address: {}".format(res[0]['address'])) - print("your privkey gave this address: " + addr) + jmprint("privkey corresponds to the wrong address for utxo: " + str(u), "error") + jmprint("blockchain returned address: {}".format(res[0]['address']), "error") + jmprint("your privkey gave this address: " + addr, "error") return False if retrieve: results.append((u, res[0]['value'])) - print('all utxos validated OK') + jmprint('all utxos validated OK', "success") if retrieve: return results return True \ No newline at end of file diff --git a/jmclient/jmclient/configure.py b/jmclient/jmclient/configure.py index cf126fe..9cb103d 100644 --- a/jmclient/jmclient/configure.py +++ b/jmclient/jmclient/configure.py @@ -12,7 +12,7 @@ from configparser import ConfigParser, NoOptionError import jmbitcoin as btc from jmclient.jsonrpc import JsonRpc from jmbase.support import (get_log, joinmarket_alert, core_alert, debug_silence, - set_logging_level) + set_logging_level, jmprint) from jmclient.podle import set_commitment_file log = get_log() @@ -407,8 +407,8 @@ def load_program_config(config_path=None, bs=None): if len(loadedFiles) != 1: with open(global_singleton.config_location, "w") as configfile: configfile.write(defaultconfig) - print("Created a new `joinmarket.cfg`. Please review and adopt the " - "settings and restart joinmarket.") + jmprint("Created a new `joinmarket.cfg`. Please review and adopt the " + "settings and restart joinmarket.", "info") exit(1) #These are left as sanity checks but currently impossible @@ -437,7 +437,8 @@ def load_program_config(config_path=None, bs=None): try: set_logging_level(loglevel) except: - print("Failed to set logging level, must be DEBUG, INFO, WARNING, ERROR") + jmprint("Failed to set logging level, must be DEBUG, INFO, WARNING, ERROR", + "error") try: global_singleton.maker_timeout_sec = global_singleton.config.getint( 'TIMEOUT', 'maker_timeout_sec') diff --git a/jmclient/jmclient/electruminterface.py b/jmclient/jmclient/electruminterface.py index 58a12a3..43128e0 100644 --- a/jmclient/jmclient/electruminterface.py +++ b/jmclient/jmclient/electruminterface.py @@ -18,7 +18,7 @@ from twisted.protocols.basic import LineReceiver from twisted.internet import reactor, task, defer from .blockchaininterface import BlockchainInterface from .configure import get_p2sh_vbyte -from jmbase import get_log +from jmbase import get_log, jmprint from .electrum_data import get_default_servers, set_electrum_testnet,\ DEFAULT_PROTO @@ -96,7 +96,7 @@ class TxElectrumClientProtocolFactory(ClientFactory): self.bci.start_electrum_proto(None) def clientConnectionFailed(self, connector, reason): - print('connection failed') + jmprint('connection failed', "warning") self.bci.start_electrum_proto(None) class ElectrumConn(threading.Thread): @@ -423,7 +423,7 @@ class ElectrumInterface(BlockchainInterface): if super(ElectrumInterface, self).fee_per_kb_has_been_manually_set(N): return int(random.uniform(N * float(0.8), N * float(1.2))) fee_info = self.get_from_electrum('blockchain.estimatefee', N, blocking=True) - print('got fee info result: ' + str(fee_info)) + jmprint('got fee info result: ' + str(fee_info), "debug") fee = fee_info.get('result') fee_per_kb_sat = int(float(fee) * 100000000) return fee_per_kb_sat @@ -437,7 +437,7 @@ class ElectrumInterface(BlockchainInterface): End the loop when the confirmation has been seen (no spent monitoring here). """ wl = self.tx_watcher_loops[notifyaddr] - print('txoutset=' + pprint.pformat(tx_output_set)) + jmprint('txoutset=' + pprint.pformat(tx_output_set), "debug") unconftx = self.get_from_electrum('blockchain.address.get_mempool', notifyaddr, blocking=True).get('result') unconftxs = set([str(t['tx_hash']) for t in unconftx]) @@ -453,14 +453,14 @@ class ElectrumInterface(BlockchainInterface): txhex = txdata['hex'] outs = set([(sv['script'], sv['value']) for sv in btc.deserialize( txhex)['outs']]) - print('unconfirm query outs = ' + str(outs)) + jmprint('unconfirm query outs = ' + str(outs), "debug") if outs == tx_output_set: unconfirmed_txid = txdata['id'] unconfirmed_txhex = txhex break #call unconf callback if it was found in the mempool if unconfirmed_txid and not wl[1]: - print("Tx: " + str(unconfirmed_txid) + " seen on network.") + jmprint("Tx: " + str(unconfirmed_txid) + " seen on network.", "info") unconfirmfun(btc.deserialize(unconfirmed_txhex), unconfirmed_txid) wl[1] = True return @@ -479,7 +479,7 @@ class ElectrumInterface(BlockchainInterface): txhex = txdata['hex'] outs = set([(sv['script'], sv['value']) for sv in btc.deserialize( txhex)['outs']]) - print('confirm query outs = ' + str(outs)) + jmprint('confirm query outs = ' + str(outs), "info") if outs == tx_output_set: confirmed_txid = txdata['id'] confirmed_txhex = txhex @@ -518,7 +518,7 @@ class ElectrumInterface(BlockchainInterface): unconftxs = [str(t['tx_hash']) for t in unconftxs_res] if not wl[1] and txid in unconftxs: - print("Tx: " + str(txid) + " seen on network.") + jmprint("Tx: " + str(txid) + " seen on network.", "info") unconfirmfun(txd, txid) wl[1] = True return @@ -526,7 +526,7 @@ class ElectrumInterface(BlockchainInterface): addr, blocking=True).get('result') conftxs = [str(t['tx_hash']) for t in conftx] if not wl[2] and len(conftxs) and txid in conftxs: - print("Tx: " + str(txid) + " is confirmed.") + jmprint("Tx: " + str(txid) + " is confirmed.", "info") confirmfun(txd, txid, 1) wl[2] = True #Note we do not stop the monitoring loop when diff --git a/jmclient/jmclient/podle.py b/jmclient/jmclient/podle.py index cd02046..d9b72a8 100644 --- a/jmclient/jmclient/podle.py +++ b/jmclient/jmclient/podle.py @@ -10,6 +10,7 @@ import hashlib import json import binascii import struct +from jmbase import jmprint from jmbitcoin import multiply, add_pubkeys, getG, podle_PublicKey,\ podle_PrivateKey, encode, decode, N, podle_PublicKey_class @@ -312,7 +313,8 @@ def update_commitments(commitment=None, c = json.loads(f.read().decode('utf-8')) except ValueError: #pragma: no cover #Exit conditions cannot be included in tests. - print("the file: " + PODLE_COMMIT_FILE + " is not valid json.") + jmprint("the file: " + PODLE_COMMIT_FILE + " is not valid json.", + "error") sys.exit(0) if 'used' in c: diff --git a/jmclient/jmclient/taker.py b/jmclient/jmclient/taker.py index d107115..8e34ea6 100644 --- a/jmclient/jmclient/taker.py +++ b/jmclient/jmclient/taker.py @@ -563,7 +563,8 @@ class Taker(object): #verify_tx_input will not even parse the script if it has integers or None, #so abort in case we were given a junk sig: if not all([not isinstance(x, int) and x for x in sig_deserialized]): - print("Junk signature: ", sig_deserialized, ", not attempting to verify") + jlog.warn("Junk signature: " + str(sig_deserialized) + \ + ", not attempting to verify") break if len(sig_deserialized) == 2: ver_sig, ver_pub = sig_deserialized diff --git a/jmclient/jmclient/taker_utils.py b/jmclient/jmclient/taker_utils.py index 23b2189..eb931d4 100644 --- a/jmclient/jmclient/taker_utils.py +++ b/jmclient/jmclient/taker_utils.py @@ -8,7 +8,7 @@ import os import time import numbers from binascii import unhexlify -from jmbase import get_log +from jmbase import get_log, jmprint from .configure import jm_single, validate_address from .schedule import human_readable_schedule_entry, tweak_tumble_schedule,\ schedule_to_text @@ -233,18 +233,19 @@ def tumbler_taker_finished_update(taker, schedulefile, tumble_log, options, #for command line script TODO if taker.schedule[taker.schedule_index+1][3] == 'addrask': jm_single().debug_silence[0] = True - print('\n'.join(['=' * 60] * 3)) - print('Tumbler requires more addresses to stop amount correlation') - print('Obtain a new destination address from your bitcoin recipient') - print(' for example click the button that gives a new deposit address') - print('\n'.join(['=' * 60] * 1)) + jmprint('\n'.join(['=' * 60] * 3)) + jmprint('Tumbler requires more addresses to stop amount correlation') + jmprint('Obtain a new destination address from your bitcoin recipient') + jmprint(' for example click the button that gives a new deposit address') + jmprint('\n'.join(['=' * 60] * 1)) while True: destaddr = input('insert new address: ') addr_valid, errormsg = validate_address(destaddr) if addr_valid: break - print( - 'Address ' + destaddr + ' invalid. ' + errormsg + ' try again') + jmprint( + 'Address ' + destaddr + ' invalid. ' + errormsg + ' try again', + "warning") jm_single().debug_silence[0] = False taker.schedule[taker.schedule_index+1][3] = destaddr taker.tdestaddrs.append(destaddr) diff --git a/jmclient/jmclient/wallet_utils.py b/jmclient/jmclient/wallet_utils.py index 8723409..12f2c34 100644 --- a/jmclient/jmclient/wallet_utils.py +++ b/jmclient/jmclient/wallet_utils.py @@ -16,7 +16,7 @@ from jmclient import (get_network, WALLET_IMPLEMENTATIONS, Storage, podle, jm_single, BitcoinCoreInterface, JsonRpcError, sync_wallet, WalletError, VolatileStorage, StoragePasswordError, is_segwit_mode, SegwitLegacyWallet, LegacyWallet) -from jmbase.support import get_password +from jmbase.support import get_password, jmprint from .cryptoengine import TYPE_P2PKH, TYPE_P2SH_P2WPKH import jmbitcoin as btc @@ -456,7 +456,7 @@ def cli_get_wallet_passphrase_check(): password = get_password('Enter wallet file encryption passphrase: ') password2 = get_password('Reenter wallet file encryption passphrase: ') if password != password2: - print('ERROR. Passwords did not match') + jmprint('ERROR. Passwords did not match', "error") return False return password @@ -467,7 +467,7 @@ def cli_display_user_words(words, mnemonic_extension): text = 'Write down this wallet recovery mnemonic\n\n' + words +'\n' if mnemonic_extension: text += '\nAnd this mnemonic extension: ' + mnemonic_extension + '\n' - print(text) + jmprint(text, "important") def cli_user_mnemonic_entry(): mnemonic_phrase = input("Input mnemonic recovery phrase: ") @@ -480,9 +480,10 @@ def cli_get_mnemonic_extension(): uin = input("Would you like to use a two-factor mnemonic recovery " "phrase? write 'n' if you don't know what this is (y/n): ") if len(uin) == 0 or uin[0] != 'y': - print("Not using mnemonic extension") + jmprint("Not using mnemonic extension", "info") return None #no mnemonic extension - print("Note: This will be stored in a reversible way. Do not reuse!") + jmprint("Note: This will be stored in a reversible way. Do not reuse!", + "info") return input("Enter mnemonic extension: ") @@ -554,7 +555,7 @@ def wallet_generate_recover(method, walletspath, try: entropy = LegacyWallet.entropy_from_mnemonic(seed) except WalletError as e: - print("Unable to restore seed: {}".format(e.message)) + jmprint("Unable to restore seed: {}".format(e.message), "error") return False elif method != 'generate': raise Exception("unknown method for wallet creation: '{}'" @@ -571,8 +572,8 @@ def wallet_generate_recover(method, walletspath, wallet = create_wallet(wallet_path, password, mixdepth, wallet_cls=LegacyWallet, entropy=entropy) - print("Write down and safely store this wallet recovery seed\n\n{}\n" - .format(wallet.get_mnemonic_words()[0])) + jmprint("Write down and safely store this wallet recovery seed\n\n{}\n" + .format(wallet.get_mnemonic_words()[0]), "important") wallet.close() return True @@ -628,7 +629,7 @@ def wallet_fetch_history(wallet, options): sat_to_str(balance), skip_n1(cj_n), sat_to_str(miner_fees), '% 3d' % utxo_count, skip_n1(mixdepth_src), skip_n1(mixdepth_dst)] if options.verbosity % 2 == 0: data += [txid] - print(s().join(map('"{}"'.format, data))) + jmprint(s().join(map('"{}"'.format, data)), "info") field_names = ['tx#', 'timestamp', 'type', 'amount/btc', @@ -636,9 +637,9 @@ def wallet_fetch_history(wallet, options): 'utxo-count', 'mixdepth-from', 'mixdepth-to'] if options.verbosity % 2 == 0: field_names += ['txid'] if options.csv: - print('Bumping verbosity level to 4 due to --csv flag') + jmprint('Bumping verbosity level to 4 due to --csv flag', "debug") options.verbosity = 4 - if options.verbosity > 0: print(s().join(field_names)) + if options.verbosity > 0: jmprint(s().join(field_names), "info") if options.verbosity <= 2: cj_batch = [0]*8 + [[]]*2 balance = 0 utxo_count = 0 @@ -722,7 +723,7 @@ def wallet_fetch_history(wallet, options): #payment to self out_value = sum([output_script_values[a] for a in our_output_scripts]) if not is_coinjoin: - print('this is wrong TODO handle non-coinjoin internal') + jmprint('this is wrong TODO handle non-coinjoin internal', "warning") tx_type = 'cj internal' amount = cj_amount delta_balance = out_value - our_input_value @@ -732,7 +733,7 @@ def wallet_fetch_history(wallet, options): mixdepth_dst = wallet.get_script_mixdepth(cj_script) else: tx_type = 'unknown type' - print('our utxos: ' + str(len(our_input_scripts)) \ + jmprint('our utxos: ' + str(len(our_input_scripts)) \ + ' in, ' + str(len(our_output_scripts)) + ' out') balance += delta_balance utxo_count += (len(our_output_scripts) - utxos_consumed) @@ -790,10 +791,10 @@ def wallet_fetch_history(wallet, options): )['time'] except JsonRpcError: now = jm_single().bc_interface.rpc('getblock', [bestblockhash])['time'] - print(' %s best block is %s' % (datetime.fromtimestamp(now) + jmprint(' %s best block is %s' % (datetime.fromtimestamp(now) .strftime("%Y-%m-%d %H:%M"), bestblockhash)) total_profit = float(balance - sum(deposits)) / float(100000000) - print('total profit = %.8f BTC' % total_profit) + jmprint('total profit = %.8f BTC' % total_profit) if abs(total_profit) > 0: try: @@ -808,21 +809,21 @@ def wallet_fetch_history(wallet, options): return np.sum(np.exp((now - deposit_times) / 60.0 / 60 / 24 / 365)**r * deposits) - final_balance r = brentq(f, a=1, b=-1, args=(deposits, deposit_times, now, balance)) - print('continuously compounded equivalent annual interest rate = ' + + jmprint('continuously compounded equivalent annual interest rate = ' + str(r * 100) + ' %') - print('(as if yield generator was a bank account)') + jmprint('(as if yield generator was a bank account)') except ImportError: - print('scipy not installed, unable to predict accumulation rate') - print('to add it to this virtualenv, use `pip install scipy`') + jmprint('scipy not installed, unable to predict accumulation rate') + jmprint('to add it to this virtualenv, use `pip install scipy`') total_wallet_balance = sum(wallet.get_balance_by_mixdepth().values()) if balance != total_wallet_balance: - print(('BUG ERROR: wallet balance (%s) does not match balance from ' + + jmprint(('BUG ERROR: wallet balance (%s) does not match balance from ' + 'history (%s)') % (sat_to_str(total_wallet_balance), sat_to_str(balance))) wallet_utxo_count = sum(map(len, wallet.get_utxos_by_mixdepth_().values())) if utxo_count != wallet_utxo_count: - print(('BUG ERROR: wallet utxo count (%d) does not match utxo count from ' + + jmprint(('BUG ERROR: wallet utxo count (%d) does not match utxo count from ' + 'history (%s)') % (wallet_utxo_count, utxo_count)) @@ -835,11 +836,11 @@ def wallet_showseed(wallet): def wallet_importprivkey(wallet, mixdepth, key_type): - print("WARNING: This imported key will not be recoverable with your 12 " - "word mnemonic phrase. Make sure you have backups.") - print("WARNING: Handling of raw ECDSA bitcoin private keys can lead to " + jmprint("WARNING: This imported key will not be recoverable with your 12 " + "word mnemonic phrase. Make sure you have backups.", "warning") + jmprint("WARNING: Handling of raw ECDSA bitcoin private keys can lead to " "non-intuitive behaviour and loss of funds.\n Recommended instead " - "is to use the \'sweep\' feature of sendpayment.py.") + "is to use the \'sweep\' feature of sendpayment.py.", "warning") privkeys = input("Enter private key(s) to import: ") privkeys = privkeys.split(',') if ',' in privkeys else privkeys.split() imported_addr = [] @@ -857,20 +858,22 @@ def wallet_importprivkey(wallet, mixdepth, key_type): imported_addr.append(wallet.get_addr_path(path)) if not imported_addr: - print("Warning: No keys imported!") + jmprint("Warning: No keys imported!", "error") return wallet.save() # show addresses to user so they can verify everything went as expected - print("Imported keys for addresses:\n{}".format('\n'.join(imported_addr))) + jmprint("Imported keys for addresses:\n{}".format('\n'.join(imported_addr)), + "success") if import_failed: - print("Warning: failed to import {} keys".format(import_failed)) + jmprint("Warning: failed to import {} keys".format(import_failed), + "error") def wallet_dumpprivkey(wallet, hdpath): if not hdpath: - print("Error: no hd wallet path supplied") + jmprint("Error: no hd wallet path supplied", "error") return False path = wallet.path_repr_to_path(hdpath) return wallet.get_wif_path(path) # will raise exception on invalid path @@ -992,10 +995,11 @@ def open_wallet(path, ask_for_password=True, password=None, read_only=False, pwd = get_password("Enter wallet decryption passphrase: ") or None storage = Storage(path, password=pwd, read_only=read_only) except StoragePasswordError: - print("Wrong password, try again.") + jmprint("Wrong password, try again.", "warning") continue except Exception as e: - print("Failed to load wallet, error message: " + repr(e)) + jmprint("Failed to load wallet, error message: " + repr(e), + "error") raise e break else: @@ -1081,8 +1085,8 @@ def wallet_tool_main(wallet_root_path): return wallet_display(wallet, options.gaplimit, options.showprivkey, summarized=True) elif method == "history": if not isinstance(jm_single().bc_interface, BitcoinCoreInterface): - print('showing history only available when using the Bitcoin Core ' + - 'blockchain interface') + jmprint('showing history only available when using the Bitcoin Core ' + + 'blockchain interface', "error") sys.exit(0) else: return wallet_fetch_history(wallet, options) @@ -1133,5 +1137,5 @@ if __name__ == "__main__": acctlist.append(WalletViewAccount(rootpath, a, branches=branches)) wallet = WalletView(rootpath + "/" + str(walletbranch), accounts=acctlist) - print(wallet.serialize()) + jmprint(wallet.serialize(), "success") diff --git a/scripts/add-utxo.py b/scripts/add-utxo.py index ec2b812..5e0f0d3 100644 --- a/scripts/add-utxo.py +++ b/scripts/add-utxo.py @@ -16,6 +16,7 @@ import binascii from pprint import pformat from optparse import OptionParser +from jmbase import jmprint import jmbitcoin as btc from jmclient import load_program_config, jm_single, get_p2pk_vbyte,\ open_wallet, sync_wallet, add_external_commitments, update_commitments,\ @@ -161,16 +162,16 @@ def main(): if len(args) > 0 or other: if input("You have chosen to delete commitments, other arguments " "will be ignored; continue? (y/n)") != 'y': - print("Quitting") + jmprint("Quitting", "warning") sys.exit(0) c, e = get_podle_commitments() - print(pformat(e)) + jmprint(pformat(e), "info") if input( "You will remove the above commitments; are you sure? (y/n): ") != 'y': - print("Quitting") + jmprint("Quitting", "warning") sys.exit(0) update_commitments(external_to_remove=e) - print("Commitments deleted.") + jmprint("Commitments deleted.", "important") sys.exit(0) #Three options (-w, -r, -R) for loading utxo and privkey pairs from a wallet, @@ -199,13 +200,13 @@ def main(): utxo_data.append((u, priv)) elif options.in_json: if not os.path.isfile(options.in_json): - print("File: " + options.in_json + " not found.") + jmprint("File: " + options.in_json + " not found.", "error") sys.exit(0) with open(options.in_json, "rb") as f: try: utxo_json = json.loads(f.read()) except: - print("Failed to read json from " + options.in_json) + jmprint("Failed to read json from " + options.in_json, "error") sys.exit(0) for u, pva in iteritems(utxo_json): utxo_data.append((u, pva['privkey'])) @@ -232,4 +233,4 @@ def main(): if __name__ == "__main__": main() - print('done') + jmprint('done', "success") diff --git a/scripts/sendpayment.py b/scripts/sendpayment.py index 962fbd1..30e0008 100644 --- a/scripts/sendpayment.py +++ b/scripts/sendpayment.py @@ -19,20 +19,20 @@ from jmclient import Taker, load_program_config, get_schedule,\ 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 +from jmbase.support import get_log, jmprint from cli_options import get_sendpayment_parser, get_max_cj_fee_values log = get_log() #CLI specific, so relocated here (not used by tumbler) def pick_order(orders, n): #pragma: no cover - print("Considered orders:") + jmprint("Considered orders:", "info") for i, o in enumerate(orders): - print(" %2d. %20s, CJ fee: %6s, tx fee: %6d" % - (i, o[0]['counterparty'], str(o[0]['cjfee']), o[0]['txfee'])) + jmprint(" %2d. %20s, CJ fee: %6s, tx fee: %6d" % + (i, o[0]['counterparty'], str(o[0]['cjfee']), o[0]['txfee']), "info") pickedOrderIndex = -1 if i == 0: - print("Only one possible pick, picking it.") + jmprint("Only one possible pick, picking it.", "info") return orders[0] while pickedOrderIndex == -1: try: @@ -67,15 +67,15 @@ def main(): mixdepth = options.mixdepth addr_valid, errormsg = validate_address(destaddr) if not addr_valid: - print('ERROR: Address invalid. ' + errormsg) + jmprint('ERROR: Address invalid. ' + errormsg, "error") return schedule = [[options.mixdepth, amount, options.makercount, destaddr, 0.0, 0]] else: result, schedule = get_schedule(options.schedule) if not result: - log.info("Failed to load schedule file, quitting. Check the syntax.") - log.info("Error was: " + str(schedule)) + log.error("Failed to load schedule file, quitting. Check the syntax.") + log.error("Error was: " + str(schedule)) sys.exit(0) mixdepth = 0 for s in schedule: @@ -95,8 +95,8 @@ def main(): if options.pickorders: chooseOrdersFunc = pick_order if sweeping: - print('WARNING: You may have to pick offers multiple times') - print('WARNING: due to manual offer picking while sweeping') + jmprint('WARNING: You may have to pick offers multiple times', "warning") + jmprint('WARNING: due to manual offer picking while sweeping', "warning") else: chooseOrdersFunc = options.order_choose_fn @@ -134,8 +134,8 @@ def main(): return if wallet.get_txtype() == 'p2pkh': - print("Only direct sends (use -N 0) are supported for " - "legacy (non-segwit) wallets.") + jmprint("Only direct sends (use -N 0) are supported for " + "legacy (non-segwit) wallets.", "error") return def filter_orders_callback(orders_fees, cjamount): @@ -198,9 +198,9 @@ def main(): "giving up this attempt.") reactor.stop() return - print("We failed to complete the transaction. The following " + jmprint("We failed to complete the transaction. The following " "makers responded honestly: ", taker.honest_makers, - ", so we will retry with them.") + ", so we will retry with them.", "warning") #Now we have to set the specific group we want to use, and hopefully #they will respond again as they showed honesty last time. #we must reset the number of counterparties, as well as fix who they @@ -238,4 +238,4 @@ def main(): if __name__ == "__main__": main() - print('done') + jmprint('done', "success") diff --git a/scripts/sendtomany.py b/scripts/sendtomany.py index fb5329d..a596451 100644 --- a/scripts/sendtomany.py +++ b/scripts/sendtomany.py @@ -11,7 +11,7 @@ for other reasons). from pprint import pformat from optparse import OptionParser import jmbitcoin as btc -from jmbase import get_log +from jmbase import get_log, jmprint from jmclient import load_program_config, estimate_tx_fee, jm_single,\ get_p2pk_vbyte, validate_address, get_utxo_info,\ validate_utxo_data, quit @@ -121,4 +121,4 @@ def main(): if __name__ == "__main__": main() - print('done') + jmprint('done', "success") diff --git a/scripts/tumbler.py b/scripts/tumbler.py index de661ca..d043837 100644 --- a/scripts/tumbler.py +++ b/scripts/tumbler.py @@ -14,7 +14,7 @@ from jmclient import Taker, load_program_config, get_schedule,\ RegtestBitcoinCoreInterface, schedule_to_text, restart_waiter,\ get_tumble_log, tumbler_taker_finished_update,\ tumbler_filter_orders_callback -from jmbase.support import get_log +from jmbase.support import get_log, jmprint from cli_options import get_tumbler_parser, get_max_cj_fee_values log = get_log() logsdir = os.path.join(os.path.dirname( @@ -27,7 +27,7 @@ def main(): options_org = options options = vars(options) if len(args) < 1: - print('Error: Needs a wallet file') + jmprint('Error: Needs a wallet file', "error") sys.exit(0) load_program_config() @@ -50,7 +50,7 @@ def main(): #Output information to log files jm_single().mincjamount = options['mincjamount'] destaddrs = args[1:] - print(destaddrs) + jmprint("Destination addresses: " + str(destaddrs), "important") #If the --restart flag is set we read the schedule #from the file, and filter out entries that are #already complete @@ -58,9 +58,9 @@ def main(): res, schedule = get_schedule(os.path.join(logsdir, options['schedulefile'])) if not res: - print("Failed to load schedule, name: " + str( - options['schedulefile'])) - print("Error was: " + str(schedule)) + jmprint("Failed to load schedule, name: " + str( + options['schedulefile']), "error") + jmprint("Error was: " + str(schedule), "error") sys.exit(0) #This removes all entries that are marked as done schedule = [s for s in schedule if s[5] != 1] diff --git a/scripts/wallet-tool.py b/scripts/wallet-tool.py index eb28254..c2080e6 100644 --- a/scripts/wallet-tool.py +++ b/scripts/wallet-tool.py @@ -2,9 +2,10 @@ from __future__ import (absolute_import, division, print_function, unicode_literals) from builtins import * # noqa: F401 +from jmbase import jmprint from jmclient import load_program_config, wallet_tool_main if __name__ == "__main__": load_program_config() #JMCS follows same convention as JM original; wallet is in "wallets" localdir - print(wallet_tool_main("wallets")) \ No newline at end of file + jmprint(wallet_tool_main("wallets"), "success") \ No newline at end of file diff --git a/scripts/yg-privacyenhanced.py b/scripts/yg-privacyenhanced.py index 175a335..aa783a5 100644 --- a/scripts/yg-privacyenhanced.py +++ b/scripts/yg-privacyenhanced.py @@ -6,7 +6,7 @@ from future.utils import iteritems import random -from jmbase import get_log +from jmbase import get_log, jmprint from jmclient import YieldGeneratorBasic, ygmain, jm_single @@ -98,4 +98,4 @@ if __name__ == "__main__": cjfee_r=cjfee_r, ordertype=ordertype, nickserv_password='', minsize=minsize, gaplimit=gaplimit) - print('done') + jmprint('done', "success") diff --git a/scripts/yield-generator-basic.py b/scripts/yield-generator-basic.py index 9986416..9dee2d2 100644 --- a/scripts/yield-generator-basic.py +++ b/scripts/yield-generator-basic.py @@ -3,7 +3,7 @@ from __future__ import (absolute_import, division, print_function, unicode_literals) from builtins import * # noqa: F401 -from jmbase import get_log +from jmbase import get_log, jmprint from jmclient import YieldGeneratorBasic, ygmain """THESE SETTINGS CAN SIMPLY BE EDITED BY HAND IN THIS FILE: @@ -23,4 +23,4 @@ if __name__ == "__main__": cjfee_r=cjfee_r, ordertype=ordertype, nickserv_password=nickserv_password, minsize=max_minsize, gaplimit=gaplimit) - print('done') + jmprint('done', "success") diff --git a/test/ygrunner.py b/test/ygrunner.py index 6e71021..96d71d1 100644 --- a/test/ygrunner.py +++ b/test/ygrunner.py @@ -17,6 +17,7 @@ from builtins import * # noqa: F401 from common import make_wallets import pytest import random +from jmbase import jmprint from jmclient import YieldGeneratorBasic, load_program_config, jm_single,\ sync_wallet, JMClientProtocolFactory, start_reactor @@ -41,14 +42,14 @@ class MaliciousYieldGenerator(YieldGeneratorBasic): def on_auth_received(self, nick, offer, commitment, cr, amount, kphex): if self.authmal: if random.randint(1, 100) < self.mfrac: - print("Counterparty commitment rejected maliciously") + jmprint("Counterparty commitment rejected maliciously", "debug") return (False,) return super(MaliciousYieldGenerator, self).on_auth_received(nick, offer, commitment, cr, amount, kphex) def on_tx_received(self, nick, txhex, offerinfo): if self.txmal: if random.randint(1, 100) < self.mfrac: - print("Counterparty tx rejected maliciously") + jmprint("Counterparty tx rejected maliciously", "debug") return (False, "malicious tx rejection") return super(MaliciousYieldGenerator, self).on_tx_received(nick, txhex, offerinfo) @@ -74,13 +75,13 @@ class DeterministicMaliciousYieldGenerator(YieldGeneratorBasic): def on_auth_received(self, nick, offer, commitment, cr, amount, kphex): if self.authmal: - print("Counterparty commitment rejected maliciously") + jmprint("Counterparty commitment rejected maliciously", "debug") return (False,) return super(DeterministicMaliciousYieldGenerator, self).on_auth_received(nick, offer, commitment, cr, amount, kphex) def on_tx_received(self, nick, txhex, offerinfo): if self.txmal: - print("Counterparty tx rejected maliciously") + jmprint("Counterparty tx rejected maliciously", "debug") return (False, "malicious tx rejection") return super(DeterministicMaliciousYieldGenerator, self).on_tx_received(nick, txhex, offerinfo) @@ -106,17 +107,15 @@ def test_start_ygs(setup_ygrunner, num_ygs, wallet_structures, mean_amt, mean_amt=mean_amt) #the sendpayment bot uses the last wallet in the list wallet = wallets[num_ygs]['wallet'] - print("\n\nTaker wallet seed : " + wallets[num_ygs]['seed']) + jmprint("\n\nTaker wallet seed : " + wallets[num_ygs]['seed']) # for manual audit if necessary, show the maker's wallet seeds # also (note this audit should be automated in future, see # test_full_coinjoin.py in this directory) - print("\n\nMaker wallet seeds: ") + jmprint("\n\nMaker wallet seeds: ") for i in range(num_ygs): - print("Maker seed: " + wallets[i]['seed']) - print("\n") - #useful to see the utxos on screen sometimes + jmprint("Maker seed: " + wallets[i]['seed']) + jmprint("\n") sync_wallet(wallet, fast=True) - print(wallet.get_utxos_by_mixdepth()) txfee = 1000 cjfee_a = 4200 cjfee_r = '0.001'