diff --git a/.gitignore b/.gitignore index 0aae2bb..ce381d7 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,8 @@ blockchain.cache env htmlcov/ +joinmarket.cfg +joinmarket.cfg.bak scripts/joinmarket.cfg scripts/cmttools/commitments.json scripts/blacklist @@ -13,6 +15,7 @@ alicekey alicepubkey bobkey commitments_debug.txt +dummyext deps/ jmvenv/ logs/ diff --git a/.travis.yml b/.travis.yml index a4f5026..a122150 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,19 +16,31 @@ matrix: - python-qt4 python-sip - os: linux services: docker - env: DOCKER_IMG_JM=xenial + env: DOCKER_IMG_JM=xenial-py2 - os: linux services: docker - env: DOCKER_IMG_JM=bionic + env: DOCKER_IMG_JM=xenial-py3 - os: linux services: docker - env: DOCKER_IMG_JM=stretch + env: DOCKER_IMG_JM=bionic-py2 - os: linux services: docker - env: DOCKER_IMG_JM=centos7 + env: DOCKER_IMG_JM=bionic-py3 - os: linux services: docker - env: DOCKER_IMG_JM=fedora27 + env: DOCKER_IMG_JM=stretch-py2 + - os: linux + services: docker + env: DOCKER_IMG_JM=stretch-py3 + - os: linux + services: docker + env: DOCKER_IMG_JM=centos7-py2 + - os: linux + services: docker + env: DOCKER_IMG_JM=fedora27-py2 + - os: linux + services: docker + env: DOCKER_IMG_JM=fedora27-py3 before_install: - do_on(){ if [ "$TRAVIS_OS_NAME" = "$1" ]; then shift; $@ ; fi; } - on_host(){ if [ -z "$DOCKER_IMG_JM" ]; then $@ ; fi; } diff --git a/install.sh b/install.sh index 22b3e41..a24f145 100755 --- a/install.sh +++ b/install.sh @@ -14,10 +14,18 @@ sha256_verify () deps_install () { if [[ ${install_os} == 'debian' ]]; then - if deb_deps_install "python-virtualenv curl python-dev python-pip build-essential automake pkg-config libtool libgmp-dev"; then - return 0 + if is_python3; then + if deb_deps_install "python-virtualenv curl python3-dev python3-pip build-essential automake pkg-config libtool libgmp-dev"; then + return 0 + else + return 1 + fi else - return 1 + if deb_deps_install "python-virtualenv curl python-dev python-pip build-essential automake pkg-config libtool libgmp-dev"; then + return 0 + else + return 1 + fi fi else echo "OS can not be determined. Trying to build." @@ -360,6 +368,18 @@ os_is_deb () ( which apt-get && which dpkg-query ) 2>/dev/null 1>&2 } +is_python3 () +{ + if [[ ${python} == 'python3' ]]; then + return 0 + fi + if eval "${python} -c 'import sys; sys.exit(0) if sys.version_info >= (3,0) else sys.exit(1)'"; then + return 0 + else + return 1 + fi +} + install_get_os () { if os_is_deb; then @@ -372,8 +392,14 @@ install_get_os () qt_deps_install () { if [[ ${install_os} == 'debian' ]]; then - if deb_deps_install "python-qt4 python-sip"; then - return 0; + if is_python3; then + if deb_deps_install "python3-pyqt4 python3-sip"; then + return 0; + fi + else + if deb_deps_install "python-qt4 python-sip"; then + return 0; + fi fi else return 1 diff --git a/jmbase/jmbase/__init__.py b/jmbase/jmbase/__init__.py index 91dbab6..e5a6eb4 100644 --- a/jmbase/jmbase/__init__.py +++ b/jmbase/jmbase/__init__.py @@ -3,7 +3,7 @@ from __future__ import (absolute_import, division, from builtins import * from .support import (get_log, chunks, debug_silence, debug_dump_object, - joinmarket_alert, core_alert, get_password, _byteify, + joinmarket_alert, core_alert, get_password, set_logging_level) -from commands import * +from .commands import * diff --git a/jmbase/jmbase/bigstring.py b/jmbase/jmbase/bigstring.py index dcc90a1..ecfe9cc 100644 --- a/jmbase/jmbase/bigstring.py +++ b/jmbase/jmbase/bigstring.py @@ -1,19 +1,25 @@ from __future__ import (absolute_import, division, print_function, unicode_literals) from builtins import * # noqa: F401 -try: - from cStringIO import StringIO -except ImportError: #pragma: no cover - from StringIO import StringIO #pragma: no cover -from itertools import count +import itertools from twisted.protocols import amp -CHUNK_MAX = 0xffff -class BigString(amp.Argument): +def split_string(x, size): + return list(x[i*size:(i+1)*size] for i in range((len(x)+size-1)//size)) + +class StringList(amp.Argument): + def fromBox(self, name, strings, objects, proto): + nk = amp._wireNameToPythonIdentifier(name) + objects[nk] = list(itertools.takewhile(bool, (strings.pop(b'%s.%d' % (name, i), None) for i in itertools.count()))) + + def toBox(self, name, strings, objects, proto): + for i, elem in enumerate(objects.pop(name)): + strings[b'%s.%d' % (name, i)] = elem + +class BigString(StringList): """ A byte-string amp.Argument with no 65,535 length limit. - Each value for a key/value pair in an AMP box may not exceed 65,535 bytes in length. So if we *really* want to send potentially larger values, this class will implicitly @@ -22,29 +28,18 @@ class BigString(amp.Argument): names by prefixing this Argument's key name to a counter. """ def fromBox(self, name, strings, objects, proto): - value = StringIO() - value.write(strings.get(name)) - for counter in count(2): - chunk = strings.get("%s.%d" % (name, counter)) - if chunk is None: - break - value.write(chunk) - objects[name] = self.buildvalue(value.getvalue()) - - def buildvalue(self, value): - return value + nk = amp._wireNameToPythonIdentifier(name) + StringList.fromBox(self, name, strings, objects, proto) + objects[nk] = b''.join((elem) for elem in objects[nk]).decode('utf-8') def toBox(self, name, strings, objects, proto): - value = StringIO(self.fromvalue(objects[name])) - firstChunk = value.read(CHUNK_MAX) - strings[name] = firstChunk - counter = 2 - while True: - nextChunk = value.read(CHUNK_MAX) - if not nextChunk: - break - strings["{}.{}".format(name, counter).encode('ascii')] = nextChunk - counter += 1 - - def fromvalue(self, value): - return value + obj = self.retrieve(objects, amp._wireNameToPythonIdentifier(name), proto).encode('utf-8') + objects[name] = split_string(obj, amp.MAX_VALUE_LENGTH) + StringList.toBox(self, name, strings, objects, proto) + +class BigUnicode(BigString): + def toString(self, inObject): + return BigString.toString(self, inObject.encode('utf-8')) + + def fromString(self, inString): + return BigString.fromString(self, inString).decode('utf-8') diff --git a/jmbase/jmbase/commands.py b/jmbase/jmbase/commands.py index 590a1bd..21eb24c 100644 --- a/jmbase/jmbase/commands.py +++ b/jmbase/jmbase/commands.py @@ -7,7 +7,7 @@ messaging protocol (*not* Joinmarket p2p protocol). Used for AMP asynchronous messages. """ from twisted.protocols.amp import Boolean, Command, Integer, Unicode -from .bigstring import BigString +from .bigstring import BigUnicode class DaemonNotReady(Exception): pass @@ -185,7 +185,7 @@ class JMOffers(JMCommand): """Return the entire contents of the orderbook to TAKER, as a json-ified dict. """ - arguments = [(b'orderbook', BigString())] + arguments = [(b'orderbook', BigUnicode())] class JMFillResponse(JMCommand): """Returns ioauth data from MAKER if successful. diff --git a/jmbase/jmbase/support.py b/jmbase/jmbase/support.py index 0109ce2..d4219b1 100644 --- a/jmbase/jmbase/support.py +++ b/jmbase/jmbase/support.py @@ -1,6 +1,7 @@ from __future__ import (absolute_import, division, print_function, unicode_literals) from builtins import * # noqa: F401 +from future.utils import iteritems import sys @@ -53,13 +54,16 @@ def chunks(d, n): return [d[x:x + n] for x in range(0, len(d), n)] def get_password(msg): #pragma: no cover - return getpass(msg) + password = getpass(msg) + 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 obj.__dict__.iteritems(): + for k, v in iteritems(obj.__dict__): if k in skip_fields: continue if k == 'password' or k == 'given_password': @@ -72,20 +76,3 @@ def debug_dump_object(obj, skip_fields=None): log.debug(pprint.pformat(v)) else: log.debug(str(v)) - -def _byteify(data, ignore_dicts = False): - # if this is a unicode string, return its string representation - if isinstance(data, unicode): - return data.encode('utf-8') - # if this is a list of values, return list of byteified values - if isinstance(data, list): - return [ _byteify(item, ignore_dicts=True) for item in data ] - # if this is a dictionary, return dictionary of byteified keys and values - # but only if we haven't already byteified it - if isinstance(data, dict) and not ignore_dicts: - return { - _byteify(key, ignore_dicts=True): _byteify(value, ignore_dicts=True) - for key, value in data.iteritems() - } - # if it's anything else, return it in its original form - return data diff --git a/jmbitcoin/jmbitcoin/secp256k1_main.py b/jmbitcoin/jmbitcoin/secp256k1_main.py index c2a698a..3d1dca2 100644 --- a/jmbitcoin/jmbitcoin/secp256k1_main.py +++ b/jmbitcoin/jmbitcoin/secp256k1_main.py @@ -2,7 +2,7 @@ from __future__ import (absolute_import, division, print_function, unicode_literals) from builtins import * # noqa: F401 -from future.utils import bytes_to_native_str +from future.utils import native_bytes, bytes_to_native_str import binascii import hashlib import sys @@ -53,11 +53,8 @@ def bin_to_b58check(inp, magicbyte=b'\x00'): checksum = bin_dbl_sha256(inp_fmtd)[:4] return b58encode(inp_fmtd + checksum) -def bytes_to_hex_string(b): - return b.encode('hex') - def safe_from_hex(s): - return s.decode('hex') + return binascii.unhexlify(s) def from_int_to_byte(a): return struct.pack(b'B', a) @@ -69,7 +66,7 @@ def from_string_to_bytes(a): return a if isinstance(a, bytes) else bytes(a, 'utf-8') def safe_hexlify(a): - return str(binascii.hexlify(a), 'utf-8') + return binascii.hexlify(a).decode('ascii') class SerializationError(Exception): """Base class for serialization errors""" @@ -109,7 +106,7 @@ def b58encode(b): """Encode bytes to a base58-encoded string""" # Convert big-endian bytes to integer - n = int('0x0' + binascii.hexlify(b).decode('utf8'), 16) + n = int('0x0' + binascii.hexlify(b).decode('ascii'), 16) # Divide that integer into bas58 res = [] @@ -120,7 +117,7 @@ def b58encode(b): # Encode leading zeros as base58 zeros czero = b'\x00' - if sys.version > '3': + if sys.version_info >= (3,0): # In Python3 indexing a bytes returns numbers, not characters. czero = 0 pad = 0 @@ -252,6 +249,8 @@ def bin_hash160(string): return hashlib.new('ripemd160', intermed).digest() def hash160(string): + if not isinstance(string, bytes): + string = string.encode('utf-8') return safe_hexlify(bin_hash160(string)) def bin_sha256(string): @@ -260,9 +259,11 @@ def bin_sha256(string): return hashlib.sha256(binary_data).digest() def sha256(string): - return bytes_to_hex_string(bin_sha256(string)) + return safe_hexlify(bin_sha256(string)) def bin_dbl_sha256(bytes_to_hash): + if not isinstance(bytes_to_hash, bytes): + bytes_to_hash = bytes_to_hash.encode('utf-8') return hashlib.sha256(hashlib.sha256(bytes_to_hash).digest()).digest() def dbl_sha256(string): @@ -306,7 +307,7 @@ def hex_to_b58check(inp, magicbyte=b'\x00'): return bin_to_b58check(binascii.unhexlify(inp), magicbyte) def b58check_to_hex(inp): - return binascii.hexlify(b58check_to_bin(inp)) + return binascii.hexlify(b58check_to_bin(inp)).decode('ascii') def pubkey_to_address(pubkey, magicbyte=0): if len(pubkey) in [66, 130]: @@ -351,20 +352,20 @@ def ecdsa_sign(msg, priv, formsg=False, usehex=True): hashed_msg = message_sig_hash(msg) if usehex: #arguments to raw sign must be consistently hex or bin - hashed_msg = binascii.hexlify(hashed_msg) + hashed_msg = binascii.hexlify(hashed_msg).decode('ascii') sig = ecdsa_raw_sign(hashed_msg, priv, usehex, rawmsg=True, formsg=formsg) #note those functions only handles binary, not hex if usehex: sig = binascii.unhexlify(sig) - return base64.b64encode(sig) + return base64.b64encode(sig).decode('ascii') def ecdsa_verify(msg, sig, pub, usehex=True): hashed_msg = message_sig_hash(msg) sig = base64.b64decode(sig) if usehex: #arguments to raw_verify must be consistently hex or bin - hashed_msg = binascii.hexlify(hashed_msg) - sig = binascii.hexlify(sig) + hashed_msg = binascii.hexlify(hashed_msg).decode('ascii') + sig = binascii.hexlify(sig).decode('ascii') return ecdsa_raw_verify(hashed_msg, pub, sig, usehex, rawmsg=True) #Use secp256k1 to handle all EC and ECDSA operations. @@ -389,7 +390,7 @@ def hexbin(func): if isinstance(returnval, bool): return returnval else: - return binascii.hexlify(returnval) + return binascii.hexlify(returnval).decode('ascii') else: return func(*args, **kwargs) @@ -415,7 +416,10 @@ def privkey_to_pubkey_inner(priv, usehex): and return compressed/uncompressed public key as appropriate.''' compressed, priv = read_privkey(priv) #secp256k1 checks for validity of key value. - newpriv = secp256k1.PrivateKey(secret=bytes_to_native_str(priv)) + if sys.version_info >= (3,0): + newpriv = secp256k1.PrivateKey(secret=native_bytes(priv)) + else: + newpriv = secp256k1.PrivateKey(secret=bytes_to_native_str(priv)) return newpriv.public_key.format(compressed) def privkey_to_pubkey(priv, usehex=True): @@ -440,7 +444,10 @@ def multiply(s, pub, usehex, rawpub=True, return_serialized=True): ''' newpub = secp256k1.PublicKey(pub) #see note to "tweak_mul" function in podle.py - res = newpub.multiply(bytes_to_native_str(s)) + if sys.version_info >= (3,0): + res = newpub.multiply(native_bytes(s)) + else: + res = newpub.multiply(bytes_to_native_str(s)) if not return_serialized: return res return res.format() diff --git a/jmbitcoin/jmbitcoin/secp256k1_transaction.py b/jmbitcoin/jmbitcoin/secp256k1_transaction.py index e9d0aff..bca5e70 100644 --- a/jmbitcoin/jmbitcoin/secp256k1_transaction.py +++ b/jmbitcoin/jmbitcoin/secp256k1_transaction.py @@ -14,7 +14,7 @@ from jmbitcoin.bech32 import * # Transaction serialization and deserialization def deserialize(txinp): - if isinstance(txinp, basestring) and re.match('^[0-9a-fA-F]*$', txinp): + if isinstance(txinp, basestring) and not isinstance(txinp, bytes) and re.match('^[0-9a-fA-F]*$', txinp): tx = BytesIO(binascii.unhexlify(txinp)) hexout = True else: @@ -23,7 +23,7 @@ def deserialize(txinp): def hex_string(scriptbytes, hexout): if hexout: - return binascii.hexlify(scriptbytes) + return binascii.hexlify(scriptbytes).decode('ascii') else: return scriptbytes @@ -156,7 +156,7 @@ def serialize(tx): o.write(struct.pack(b' # Copyright (C) 2014 by phelix / blockchained.com # @@ -23,8 +25,9 @@ from __future__ import absolute_import, print_function import errno import socket import base64 -import httplib +import http.client import json +from decimal import Decimal from jmclient import get_log jlog = get_log() @@ -56,8 +59,8 @@ class JsonRpc(object): def __init__(self, host, port, user, password, wallet_file=""): self.host = host - self.port = port - self.conn = httplib.HTTPConnection(self.host, self.port) + self.port = int(port) + self.conn = http.client.HTTPConnection(self.host, self.port) self.authstr = "%s:%s" % (user, password) if len(wallet_file) > 0: self.url = "/wallet/" + wallet_file @@ -76,7 +79,7 @@ class JsonRpc(object): headers = {"User-Agent": "joinmarket", "Content-Type": "application/json", "Accept": "application/json"} - headers["Authorization"] = "Basic %s" % base64.b64encode(self.authstr) + headers["Authorization"] = b"Basic %s" % base64.b64encode(self.authstr.encode('utf-8')) body = json.dumps(obj) @@ -97,20 +100,26 @@ class JsonRpc(object): data = response.read() - return json.loads(data) + return json.loads(data.decode('utf-8'), parse_float=Decimal) except JsonRpcConnectionError as exc: raise exc - except httplib.BadStatusLine: + except http.client.BadStatusLine: return "CONNFAILURE" except socket.error as e: - if e.errno != errno.ECONNRESET: + if e.errno == errno.ECONNRESET: + jlog.warn('Connection was reset, attempting reconnect.') + self.conn.close() + self.conn.connect() + continue + elif e.errno == errno.EPIPE: + jlog.warn('Connection had broken pipe, attempting reconnect.') + self.conn.close() + self.conn.connect() + continue + else: jlog.error('Unhandled connection error ' + str(e)) raise e - jlog.warn('Connection was reset, attempting reconnect.') - self.conn.close() - self.conn.connect() - continue except Exception as exc: raise JsonRpcConnectionError("JSON-RPC connection failed. Err:" + repr(exc)) @@ -136,7 +145,7 @@ class JsonRpc(object): response_received = True break #Failure means keepalive timed out, just make a new one - self.conn = httplib.HTTPConnection(self.host, self.port) + self.conn = http.client.HTTPConnection(self.host, self.port) if not response_received: raise JsonRpcConnectionError("Unable to connect over RPC") if response["id"] != currentId: diff --git a/jmclient/jmclient/maker.py b/jmclient/jmclient/maker.py index bf1b858..e7c56cf 100644 --- a/jmclient/jmclient/maker.py +++ b/jmclient/jmclient/maker.py @@ -1,13 +1,15 @@ #! /usr/bin/env python -from __future__ import print_function +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * # noqa: F401 import base64 import pprint import sys from binascii import unhexlify -import btc -from btc import SerializationError, SerializationTruncationError +from . import btc +from .btc import SerializationError, SerializationTruncationError from jmclient.configure import jm_single from jmbase.support import get_log from jmclient.support import (calc_cj_fee) @@ -97,7 +99,7 @@ class Maker(object): # Need to choose an input utxo pubkey to sign with # (no longer using the coinjoin pubkey from 0.2.0) # Just choose the first utxo in self.utxos and retrieve key from wallet. - auth_address = utxos[utxos.keys()[0]]['address'] + auth_address = utxos[list(utxos.keys())[0]]['address'] auth_key = self.wallet.get_key_from_addr(auth_address) auth_pub = btc.privtopub(auth_key) btc_sig = btc.ecdsa_sign(kphex, auth_key) @@ -140,7 +142,7 @@ class Maker(object): #also, the items in witness are not serialize_script-ed. sigmsg = b''.join(btc.serialize_script_unit(x) for x in txs['ins'][index]['txinwitness']) + sigmsg - sigs.append(base64.b64encode(sigmsg)) + sigs.append(base64.b64encode(sigmsg).decode('ascii')) return (True, sigs) def verify_unsigned_tx(self, txd, offerinfo): diff --git a/jmclient/jmclient/old_mnemonic.py b/jmclient/jmclient/old_mnemonic.py index 32d6761..df8937c 100644 --- a/jmclient/jmclient/old_mnemonic.py +++ b/jmclient/jmclient/old_mnemonic.py @@ -1,4 +1,7 @@ #!/usr/bin/env python +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * # noqa: F401 # # Electrum - lightweight Bitcoin client # Copyright (C) 2011 thomasv@gitorious @@ -244,19 +247,19 @@ n = 1626 def mn_encode(message): assert len(message) % 8 == 0 out = [] - for i in range(len(message) / 8): + for i in range(len(message) // 8): word = message[8 * i:8 * i + 8] x = int(word, 16) w1 = (x % n) - w2 = ((x / n) + w1) % n - w3 = ((x / n / n) + w2) % n + w2 = ((x // n) + w1) % n + w3 = ((x // n // n) + w2) % n out += [words[w1], words[w2], words[w3]] return out def mn_decode(wlist): out = '' - for i in range(len(wlist) / 3): + for i in range(len(wlist) // 3): word1, word2, word3 = wlist[3 * i:3 * i + 3] w1 = words.index(word1) w2 = (words.index(word2)) % n diff --git a/jmclient/jmclient/output.py b/jmclient/jmclient/output.py index 7393064..7457123 100644 --- a/jmclient/jmclient/output.py +++ b/jmclient/jmclient/output.py @@ -1,3 +1,6 @@ +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * # noqa: F401 from binascii import hexlify @@ -11,7 +14,7 @@ def fmt_utxos(utxos, wallet, prefix=''): def fmt_utxo(utxo): - return '{}:{}'.format(hexlify(utxo[0]), utxo[1]) + return '{}:{}'.format(hexlify(utxo[0]).decode('ascii'), utxo[1]) def fmt_tx_data(tx_data, wallet): diff --git a/jmclient/jmclient/podle.py b/jmclient/jmclient/podle.py index bf2abfe..142e3d3 100644 --- a/jmclient/jmclient/podle.py +++ b/jmclient/jmclient/podle.py @@ -1,5 +1,7 @@ #!/usr/bin/env python -from __future__ import print_function +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * # noqa: F401 #Proof Of Discrete Logarithm Equivalence #For algorithm steps, see https://gist.github.com/AdamISZ/9cbba5e9408d23813ca8 import os @@ -7,7 +9,8 @@ import sys import hashlib import json import binascii -from btc import multiply, add_pubkeys, getG, podle_PublicKey,\ +import struct +from .btc import multiply, add_pubkeys, getG, podle_PublicKey,\ podle_PrivateKey, encode, decode, N, podle_PublicKey_class @@ -84,7 +87,7 @@ class PoDLE(object): if not isinstance(self.P2, podle_PublicKey_class): raise PoDLEError("Cannot construct commitment, P2 is not a pubkey") self.commitment = hashlib.sha256(self.P2.format()).digest() - return binascii.hexlify(self.commitment) + return binascii.hexlify(self.commitment).decode('ascii') def generate_podle(self, index=0, k=None): """Given a raw private key, in hex format, @@ -123,7 +126,7 @@ class PoDLE(object): KJ = multiply(k, J.format(), False, return_serialized=False) self.P2 = getP2(self.priv, J) self.get_commitment() - self.e = hashlib.sha256(''.join([x.format( + self.e = hashlib.sha256(b''.join([x.format( ) for x in [KG, KJ, self.P, self.P2]])).digest() k_int = decode(k, 256) priv_int = decode(self.priv.secret, 256) @@ -141,7 +144,7 @@ class PoDLE(object): if not self.commitment: self.get_commitment() Phex, P2hex, shex, ehex, commit = [ - binascii.hexlify(x) + binascii.hexlify(x).decode('ascii') for x in [self.P.format(), self.P2.format(), self.s, self.e, self.commitment] ] @@ -217,14 +220,14 @@ def getNUMS(index=0): assert index in range(256) nums_point = None for G in [getG(True), getG(False)]: - seed = G + chr(index) + seed = G + struct.pack(b'B', index) for counter in range(256): - seed_c = seed + chr(counter) + seed_c = seed + struct.pack(b'B', counter) hashed_seed = hashlib.sha256(seed_c).digest() #Every x-coord on the curve has two y-values, encoded #in compressed form with 02/03 parity byte. We just #choose the former. - claimed_point = "\x02" + hashed_seed + claimed_point = b"\x02" + hashed_seed try: nums_point = podle_PublicKey(claimed_point) return nums_point @@ -242,11 +245,11 @@ def verify_all_NUMS(write=False): """ nums_points = {} for i in range(256): - nums_points[i] = binascii.hexlify(getNUMS(i).format()) + nums_points[i] = binascii.hexlify(getNUMS(i).format()).decode('ascii') if write: with open("nums_basepoints.txt", "wb") as f: from pprint import pformat - f.write(pformat(nums_points)) + f.write(pformat(nums_points).encode('utf-8')) assert nums_points == precomp_NUMS, "Precomputed NUMS points are not valid!" @@ -279,7 +282,7 @@ def get_podle_commitments(): if not os.path.isfile(PODLE_COMMIT_FILE): return ([], {}) with open(PODLE_COMMIT_FILE, "rb") as f: - c = json.loads(f.read()) + c = json.loads(f.read().decode('utf-8')) if 'used' not in c.keys() or 'external' not in c.keys(): raise PoDLEError("Incorrectly formatted file: " + PODLE_COMMIT_FILE) return (c['used'], c['external']) @@ -306,7 +309,7 @@ def update_commitments(commitment=None, if os.path.isfile(PODLE_COMMIT_FILE): with open(PODLE_COMMIT_FILE, "rb") as f: try: - c = json.loads(f.read()) + 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.") @@ -335,7 +338,7 @@ def update_commitments(commitment=None, to_write['used'] = commitments to_write['external'] = external with open(PODLE_COMMIT_FILE, "wb") as f: - f.write(json.dumps(to_write, indent=4)) + f.write(json.dumps(to_write, indent=4).encode('utf-8')) def get_podle_tries(utxo, priv=None, max_tries=1, external=False): used_commitments, external_commitments = get_podle_commitments() diff --git a/jmclient/jmclient/schedule.py b/jmclient/jmclient/schedule.py index 90287d3..c89edc5 100644 --- a/jmclient/jmclient/schedule.py +++ b/jmclient/jmclient/schedule.py @@ -1,5 +1,7 @@ #!/usr/bin/env python -from __future__ import print_function +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * # noqa: F401 import copy from jmclient import (validate_address, rand_exp_array, rand_norm_array, rand_pow_array, jm_single) @@ -20,6 +22,7 @@ def get_schedule(filename): schedule = [] schedule_lines = f.readlines() for sl in schedule_lines: + sl = sl.decode('utf-8') if sl.startswith("#"): continue try: @@ -213,4 +216,4 @@ def human_readable_schedule_entry(se, amt=None, destn=None): return ", ".join(hrs) def schedule_to_text(schedule): - return "\n".join([",".join([str(y) for y in x]) for x in schedule]) + return "\n".join([",".join([str(y) for y in x]) for x in schedule]).encode('utf-8') diff --git a/jmclient/jmclient/slowaes.py b/jmclient/jmclient/slowaes.py index 258694f..1f0d16e 100644 --- a/jmclient/jmclient/slowaes.py +++ b/jmclient/jmclient/slowaes.py @@ -11,6 +11,9 @@ # Licensed under the Apache License, Version 2.0 # http://www.apache.org/licenses/ # +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * import math import os diff --git a/jmclient/jmclient/storage.py b/jmclient/jmclient/storage.py index 573c572..8c427f9 100644 --- a/jmclient/jmclient/storage.py +++ b/jmclient/jmclient/storage.py @@ -1,4 +1,7 @@ -from __future__ import print_function, absolute_import, division, unicode_literals +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * # noqa: F401 +from future.utils import native import os import shutil @@ -244,7 +247,7 @@ class Storage(object): def _encrypt(self, data, iv): encrypter = pyaes.Encrypter( - pyaes.AESModeOfOperationCBC(self._hash.hash, iv=iv)) + pyaes.AESModeOfOperationCBC(self._hash.hash, iv=native(iv))) enc_data = encrypter.feed(self.MAGIC_DETECT_ENC + data) enc_data += encrypter.feed() @@ -252,7 +255,7 @@ class Storage(object): def _decrypt(self, data, iv): decrypter = pyaes.Decrypter( - pyaes.AESModeOfOperationCBC(self._hash.hash, iv=iv)) + pyaes.AESModeOfOperationCBC(self._hash.hash, iv=native(iv))) try: dec_data = decrypter.feed(data) dec_data += decrypter.feed() diff --git a/jmclient/jmclient/support.py b/jmclient/jmclient/support.py index 34fe1cc..8507d1a 100644 --- a/jmclient/jmclient/support.py +++ b/jmclient/jmclient/support.py @@ -1,4 +1,7 @@ -from __future__ import absolute_import, print_function +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * # noqa: F401 +from functools import reduce import random from jmbase.support import get_log @@ -25,7 +28,7 @@ def get_random_bytes(num_bytes, cryptographically_secure=False): generator = random.SystemRandom() else: generator = random - return bytes(bytearray((generator.randrange(256) for b in xrange(num_bytes)))) + return bytes(bytearray((generator.randrange(256) for b in range(num_bytes)))) def rand_norm_array(mu, sigma, n): @@ -43,7 +46,7 @@ def rand_pow_array(power, n): # for basis of formula, see: http://mathworld.wolfram.com/RandomNumber.html return [y**(1.0 / power) for y in [x * 0.0001 for x in random.sample( - xrange(10000), n)]] + range(10000), n)]] def rand_weighted_choice(n, p_arr): @@ -58,7 +61,7 @@ def rand_weighted_choice(n, p_arr): raise ValueError("Sum of probabilities must be 1") if len(p_arr) != n: raise ValueError("Need: " + str(n) + " probabilities.") - cum_pr = [sum(p_arr[:i + 1]) for i in xrange(len(p_arr))] + cum_pr = [sum(p_arr[:i + 1]) for i in range(len(p_arr))] r = random.random() return sorted(cum_pr + [r]).index(r) diff --git a/jmclient/jmclient/taker.py b/jmclient/jmclient/taker.py index b8ee1ca..0f13ed1 100644 --- a/jmclient/jmclient/taker.py +++ b/jmclient/jmclient/taker.py @@ -1,12 +1,15 @@ #! /usr/bin/env python -from __future__ import print_function +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * # noqa: F401 +from future.utils import iteritems import base64 import pprint import random from binascii import hexlify, unhexlify -import btc +from . import btc from jmclient.configure import get_p2sh_vbyte, jm_single, validate_address from jmbase.support import get_log from jmclient.support import (calc_cj_fee, weighted_order_choose, choose_orders, @@ -216,7 +219,7 @@ class Taker(object): #Initialization has been successful. We must set the nonrespondants #now to keep track of what changed when we receive the utxo data - self.nonrespondants = self.orderbook.keys() + self.nonrespondants = list(self.orderbook.keys()) return (True, self.cjamount, commitment, revelation, self.orderbook) @@ -324,7 +327,7 @@ class Taker(object): self.cjamount): return False - self.utxos = {None: self.input_utxos.keys()} + self.utxos = {None: list(self.input_utxos.keys())} return True def receive_utxos(self, ioauth_data): @@ -338,7 +341,7 @@ class Taker(object): #Temporary list used to aggregate all ioauth data that must be removed rejected_counterparties = [] #Need to authorize against the btc pubkey first. - for nick, nickdata in ioauth_data.iteritems(): + for nick, nickdata in iteritems(ioauth_data): utxo_list, auth_pub, cj_addr, change_addr, btc_sig, maker_pk = nickdata if not self.auth_counterparty(btc_sig, auth_pub, maker_pk): jlog.debug( @@ -358,7 +361,7 @@ class Taker(object): self.maker_utxo_data = {} - for nick, nickdata in ioauth_data.iteritems(): + for nick, nickdata in iteritems(ioauth_data): utxo_list, auth_pub, cj_addr, change_addr, btc_sig, maker_pk = nickdata self.utxos[nick] = utxo_list utxo_data = jm_single().bc_interface.query_utxo_set(self.utxos[ @@ -422,7 +425,7 @@ class Taker(object): #Apply business logic of how many counterparties are enough; note that #this must occur after the above ioauth data processing, since we only now #know for sure that the data meets all business-logic requirements. - if len(self.maker_utxo_data.keys()) < jm_single().config.getint( + if len(self.maker_utxo_data) < jm_single().config.getint( "POLICY", "minimum_makers"): self.taker_info_callback("INFO", "Not enough counterparties, aborting.") return (False, @@ -434,7 +437,7 @@ class Taker(object): #used to track return of signatures for phase 2 self.nonrespondants = list(self.maker_utxo_data.keys()) - my_total_in = sum([va['value'] for u, va in self.input_utxos.iteritems() + my_total_in = sum([va['value'] for u, va in iteritems(self.input_utxos) ]) if self.my_change_addr: #Estimate fee per choice of next/3/6 blocks targetting. @@ -494,7 +497,7 @@ class Taker(object): # placeholders required ins['script'] = 'deadbeef' self.taker_info_callback("INFO", "Built tx, sending to counterparties.") - return (True, self.maker_utxo_data.keys(), tx) + return (True, list(self.maker_utxo_data.keys()), tx) def auth_counterparty(self, btc_sig, auth_pub, maker_pk): """Validate the counterpartys claim to own the btc @@ -522,7 +525,7 @@ class Taker(object): jlog.debug(('add_signature => nick={} ' 'not in nonrespondants {}').format(nick, self.nonrespondants)) return - sig = base64.b64decode(sigb64).encode('hex') + sig = hexlify(base64.b64decode(sigb64)).decode('ascii') inserted_sig = False txhex = btc.serialize(self.latest_tx) @@ -541,7 +544,7 @@ class Taker(object): 1] for x in utxo.values()]) # insert signatures - for i, u in utxo.iteritems(): + for i, u in iteritems(utxo): if utxo_data[i] is None: continue #Check if the sender serialize_scripted the witness @@ -643,10 +646,10 @@ class Taker(object): #also returns lists "too_old" and "too_small" for any #utxos that did not satisfy the criteria for debugging. priv_utxo_pairs = [] - new_utxos, too_old, too_small = filter_by_coin_age_amt(utxos.keys(), + new_utxos, too_old, too_small = filter_by_coin_age_amt(list(utxos.keys()), age, amt) new_utxos_dict = {k: v for k, v in utxos.items() if k in new_utxos} - for k, v in new_utxos_dict.iteritems(): + for k, v in iteritems(new_utxos_dict): addr = v['address'] priv = self.wallet.get_key_from_addr(addr) if priv: #can be null from create-unsigned @@ -684,9 +687,9 @@ class Taker(object): #Pre-filter the set of external commitments that work for this #transaction according to its size and age. dummy, extdict = get_podle_commitments() - if len(extdict.keys()) > 0: + if len(extdict) > 0: ext_valid, ext_to, ext_ts = filter_by_coin_age_amt( - extdict.keys(), age, amt) + list(extdict.keys()), age, amt) else: ext_valid = None podle_data = generate_podle(priv_utxo_pairs, tries, ext_valid) @@ -706,10 +709,10 @@ class Taker(object): jm_single().config.get("POLICY", "taker_utxo_amtpercent")) with open("commitments_debug.txt", "wb") as f: - errmsgfileheader = ("THIS IS A TEMPORARY FILE FOR DEBUGGING; " - "IT CAN BE SAFELY DELETED ANY TIME.\n") - errmsgfileheader += ("***\n") - f.write(errmsgfileheader + errmsg) + errmsgfileheader = (b"THIS IS A TEMPORARY FILE FOR DEBUGGING; " + b"IT CAN BE SAFELY DELETED ANY TIME.\n") + errmsgfileheader += (b"***\n") + f.write(errmsgfileheader + errmsg.encode('utf-8')) return (None, (priv_utxo_pairs, to, ts), errmsgheader + errmsg) @@ -735,7 +738,7 @@ class Taker(object): tx_bin = btc.deserialize(unhexlify(btc.serialize(self.latest_tx))) self.wallet.sign_tx(tx_bin, our_inputs) - self.latest_tx = btc.deserialize(hexlify(btc.serialize(tx_bin))) + self.latest_tx = btc.deserialize(hexlify(btc.serialize(tx_bin)).decode('ascii')) def push(self): diff --git a/jmclient/jmclient/taker_utils.py b/jmclient/jmclient/taker_utils.py index b83300a..8d566bf 100644 --- a/jmclient/jmclient/taker_utils.py +++ b/jmclient/jmclient/taker_utils.py @@ -1,4 +1,7 @@ -from __future__ import absolute_import, print_function +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * # noqa: F401 +from future.utils import iteritems import logging import pprint import os @@ -53,7 +56,7 @@ def direct_send(wallet, amount, mixdepth, destaddr, answeryes=False, log.error( "There are no utxos in mixdepth: " + str(mixdepth) + ", quitting.") return - total_inputs_val = sum([va['value'] for u, va in utxos.iteritems()]) + total_inputs_val = sum([va['value'] for u, va in iteritems(utxos)]) fee_est = estimate_tx_fee(len(utxos), 1, txtype=txtype) outs = [{"address": destaddr, "value": total_inputs_val - fee_est}] else: @@ -64,7 +67,7 @@ def direct_send(wallet, amount, mixdepth, destaddr, answeryes=False, fee_est = estimate_tx_fee(len(utxos), 2, txtype=txtype) else: fee_est = initial_fee_est - total_inputs_val = sum([va['value'] for u, va in utxos.iteritems()]) + total_inputs_val = sum([va['value'] for u, va in iteritems(utxos)]) changeval = total_inputs_val - fee_est - amount outs = [{"value": amount, "address": destaddr}] change_addr = wallet.get_internal_addr(mixdepth) @@ -75,7 +78,7 @@ def direct_send(wallet, amount, mixdepth, destaddr, answeryes=False, log.info("Using a fee of : " + str(fee_est) + " satoshis.") if amount != 0: log.info("Using a change value of: " + str(changeval) + " satoshis.") - tx = sign_tx(wallet, mktx(utxos.keys(), outs), utxos) + tx = sign_tx(wallet, mktx(list(utxos.keys()), outs), utxos) txsigned = deserialize(tx) log.info("Got signed transaction:\n") log.info(tx + "\n") @@ -84,7 +87,7 @@ def direct_send(wallet, amount, mixdepth, destaddr, answeryes=False, log.info("Sends: " + str(actual_amount) + " satoshis to address: " + destaddr) if not answeryes: if not accept_callback: - if raw_input('Would you like to push to the network? (y/n):')[0] != 'y': + if input('Would you like to push to the network? (y/n):')[0] != 'y': log.info("You chose not to broadcast the transaction, quitting.") return False else: @@ -112,7 +115,7 @@ def sign_tx(wallet, tx, utxos): # FIXME: ugly hack tx_bin = deserialize(unhexlify(serialize(stx))) wallet.sign_tx(tx_bin, our_inputs) - return hexlify(serialize(tx_bin)) + return hexlify(serialize(tx_bin)).decode('ascii') def import_new_addresses(wallet, addr_list): @@ -239,7 +242,7 @@ def tumbler_taker_finished_update(taker, schedulefile, tumble_log, options, print(' for example click the button that gives a new deposit address') print('\n'.join(['=' * 60] * 1)) while True: - destaddr = raw_input('insert new address: ') + destaddr = input('insert new address: ') addr_valid, errormsg = validate_address(destaddr) if addr_valid: break diff --git a/jmclient/jmclient/wallet.py b/jmclient/jmclient/wallet.py index 7fee692..776eaf5 100644 --- a/jmclient/jmclient/wallet.py +++ b/jmclient/jmclient/wallet.py @@ -1,6 +1,8 @@ -from __future__ import print_function, absolute_import, division, unicode_literals +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * # noqa: F401 -from ConfigParser import NoOptionError +from configparser import NoOptionError import warnings import functools import collections @@ -8,7 +10,7 @@ import numbers from binascii import hexlify, unhexlify from datetime import datetime from copy import deepcopy -from mnemonic import Mnemonic +from mnemonic import Mnemonic as MnemonicParent from hashlib import sha256 from itertools import chain from decimal import Decimal @@ -58,6 +60,12 @@ class WalletError(Exception): pass +class Mnemonic(MnemonicParent): + @classmethod + def detect_language(cls, code): + return "english" + + def estimate_tx_fee(ins, outs, txtype='p2pkh'): '''Returns an estimate of the number of satoshis required for a transaction with the given number of inputs and outputs, @@ -337,7 +345,7 @@ class BaseWallet(object): script = self._ENGINE.address_to_script(addr) path = self.script_to_path(script) privkey = self._get_priv_from_path(path)[0] - return hexlify(privkey) + return hexlify(privkey).decode('ascii') def get_external_addr(self, mixdepth): """ @@ -439,7 +447,7 @@ class BaseWallet(object): removed_utxos = {} for (txid, index), val in ret.items(): val['address'] = self.get_addr_path(val['path']) - removed_utxos[hexlify(txid) + ':' + str(index)] = val + removed_utxos[hexlify(txid).decode('ascii') + ':' + str(index)] = val return removed_utxos def remove_old_utxos_(self, tx): @@ -526,7 +534,7 @@ class BaseWallet(object): ret_conv = {} for utxo, data in ret.items(): addr = self.get_addr_path(data['path']) - utxo_txt = hexlify(utxo[0]) + ':' + str(utxo[1]) + utxo_txt = hexlify(utxo[0]).decode('ascii') + ':' + str(utxo[1]) ret_conv[utxo_txt] = {'address': addr, 'value': data['value']} return ret_conv @@ -580,7 +588,7 @@ class BaseWallet(object): utxos_conv = collections.defaultdict(dict) for md, utxos in ret.items(): for utxo, data in utxos.items(): - utxo_str = hexlify(utxo[0]) + ':' + str(utxo[1]) + utxo_str = hexlify(utxo[0]).decode('ascii') + ':' + str(utxo[1]) addr = self.get_addr_path(data['path']) data['address'] = addr utxos_conv[md][utxo_str] = data @@ -1060,6 +1068,7 @@ class BIP32Wallet(BaseWallet): _master_entropy = self._create_master_key() assert _master_entropy + assert isinstance(_master_entropy, bytes) self._master_key = self._derive_bip32_master_key(_master_entropy) # used to verify paths for sanity checking and for wallet id creation @@ -1258,7 +1267,7 @@ class BIP32Wallet(BaseWallet): int_type = self._get_internal_type(internal) path = self.get_path(mixdepth, int_type, index) priv = self._ENGINE.derive_bip32_privkey(self._master_key, path) - return hexlify(priv) + return hexlify(priv).decode('ascii') def get_bip32_priv_export(self, mixdepth=None, internal=None): path = self._get_bip32_export_path(mixdepth, internal) @@ -1304,7 +1313,7 @@ class BIP32Wallet(BaseWallet): return self._index_cache[mixdepth][int_type] def get_mnemonic_words(self): - return ' '.join(mn_encode(hexlify(self._entropy))), None + return ' '.join(mn_encode(hexlify(self._entropy).decode('ascii'))), None @classmethod def entropy_from_mnemonic(cls, seed): @@ -1315,7 +1324,7 @@ class BIP32Wallet(BaseWallet): return unhexlify(mn_decode(words)) def get_wallet_id(self): - return hexlify(self._key_ident) + return hexlify(self._key_ident).decode('ascii') def set_next_index(self, mixdepth, internal, index, force=False): int_type = self._get_internal_type(internal) @@ -1334,7 +1343,7 @@ class LegacyWallet(ImportWalletMixin, BIP32Wallet): _ENGINE = BTC_P2PKH def _create_master_key(self): - return hexlify(self._entropy).encode('ascii') + return hexlify(self._entropy) def _get_bip32_base_path(self): return self._key_ident, 0 diff --git a/jmclient/jmclient/wallet_utils.py b/jmclient/jmclient/wallet_utils.py index b7032f0..c6e0d0d 100644 --- a/jmclient/jmclient/wallet_utils.py +++ b/jmclient/jmclient/wallet_utils.py @@ -1,4 +1,7 @@ -from __future__ import print_function +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * # noqa: F401 +from future.utils import iteritems import json import os import sys @@ -12,7 +15,7 @@ from jmclient import (get_network, WALLET_IMPLEMENTATIONS, Storage, podle, VolatileStorage, StoragePasswordError, is_segwit_mode, SegwitLegacyWallet, LegacyWallet) from jmbase.support import get_password -from cryptoengine import TYPE_P2PKH, TYPE_P2SH_P2WPKH +from .cryptoengine import TYPE_P2PKH, TYPE_P2SH_P2WPKH import jmclient.btc as btc @@ -326,7 +329,7 @@ def wallet_showutxos(wallet, showprivkey): unsp[u]['privkey'] = wallet.get_wif_path(av['path']) used_commitments, external_commitments = podle.get_podle_commitments() - for u, ec in external_commitments.iteritems(): + for u, ec in iteritems(external_commitments): tries = podle.get_podle_tries(utxo=u, max_tries=max_tries, external=True) tries_remaining = max(0, max_tries - tries) @@ -342,7 +345,7 @@ def wallet_display(wallet, gaplimit, showprivkey, displayall=False, else return the WalletView object. """ acctlist = [] - for m in xrange(wallet.mixdepth + 1): + for m in range(wallet.mixdepth + 1): branchlist = [] for forchange in [0, 1]: entrylist = [] @@ -353,7 +356,7 @@ def wallet_display(wallet, gaplimit, showprivkey, displayall=False, xpub_key = "" unused_index = wallet.get_next_unused_index(m, forchange) - for k in xrange(unused_index + gaplimit): + for k in range(unused_index + gaplimit): path = wallet.get_path(m, forchange, k) addr = wallet.get_addr_path(path) balance = 0 @@ -398,7 +401,7 @@ def cli_get_wallet_passphrase_check(): return password def cli_get_wallet_file_name(): - return raw_input('Input wallet file name (default: wallet.jmdat): ') + return input('Input wallet file name (default: wallet.jmdat): ') def cli_display_user_words(words, mnemonic_extension): text = 'Write down this wallet recovery mnemonic\n\n' + words +'\n' @@ -407,20 +410,20 @@ def cli_display_user_words(words, mnemonic_extension): print(text) def cli_user_mnemonic_entry(): - mnemonic_phrase = raw_input("Input mnemonic recovery phrase: ") - mnemonic_extension = raw_input("Input mnemonic extension, leave blank if there isnt one: ") + mnemonic_phrase = input("Input mnemonic recovery phrase: ") + mnemonic_extension = input("Input mnemonic extension, leave blank if there isnt one: ") if len(mnemonic_extension.strip()) == 0: mnemonic_extension = None return (mnemonic_phrase, mnemonic_extension) def cli_get_mnemonic_extension(): - uin = raw_input("Would you like to use a two-factor mnemonic recovery " + 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") return None #no mnemonic extension print("Note: This will be stored in a reversible way. Do not reuse!") - return raw_input("Enter mnemonic extension: ") + return input("Enter mnemonic extension: ") def wallet_generate_recover_bip39(method, walletspath, default_wallet_name, @@ -483,7 +486,7 @@ def wallet_generate_recover(method, walletspath, entropy = None if method == 'recover': - seed = raw_input("Input 12 word recovery seed: ") + seed = input("Input 12 word recovery seed: ") try: entropy = LegacyWallet.entropy_from_mnemonic(seed) except WalletError as e: @@ -510,10 +513,17 @@ def wallet_generate_recover(method, walletspath, return True +def dict_factory(cursor, row): + d = {} + for idx, col in enumerate(cursor.description): + d[col[0]] = row[idx] + return d + + def wallet_fetch_history(wallet, options): # sort txes in a db because python can be really bad with large lists con = sqlite3.connect(":memory:") - con.row_factory = sqlite3.Row + con.row_factory = dict_factory tx_db = con.cursor() tx_db.execute("CREATE TABLE transactions(txid TEXT, " "blockhash TEXT, blocktime INTEGER);") @@ -665,7 +675,7 @@ def wallet_fetch_history(wallet, options): amount = cj_amount delta_balance = out_value - our_input_value mixdepth_src = wallet.get_script_mixdepth(list(our_input_scripts)[0]) - cj_script = list(set([a for a, v in output_script_values.iteritems() + cj_script = list(set([a for a, v in iteritems(output_script_values) if v == cj_amount]).intersection(our_output_scripts))[0] mixdepth_dst = wallet.get_script_mixdepth(cj_script) else: @@ -781,7 +791,7 @@ def wallet_importprivkey(wallet, mixdepth, key_type): print("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.") - privkeys = raw_input("Enter private key(s) to import: ") + privkeys = input("Enter private key(s) to import: ") privkeys = privkeys.split(',') if ',' in privkeys else privkeys.split() imported_addr = [] import_failed = 0 diff --git a/jmclient/jmclient/yieldgenerator.py b/jmclient/jmclient/yieldgenerator.py index d325223..3d9039d 100644 --- a/jmclient/jmclient/yieldgenerator.py +++ b/jmclient/jmclient/yieldgenerator.py @@ -1,5 +1,8 @@ #! /usr/bin/env python -from __future__ import absolute_import, print_function +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * # noqa: F401 +from future.utils import iteritems import datetime import os @@ -79,7 +82,7 @@ class YieldGeneratorBasic(YieldGenerator): def create_my_orders(self): mix_balance = self.wallet.get_balance_by_mixdepth(verbose=False) - if len([b for m, b in mix_balance.iteritems() if b > 0]) == 0: + if len([b for m, b in iteritems(mix_balance) if b > 0]) == 0: jlog.error('do not have any coins left') return [] @@ -118,7 +121,7 @@ class YieldGeneratorBasic(YieldGenerator): max_mix = max(mix_balance, key=mix_balance.get) filtered_mix_balance = [m - for m in mix_balance.iteritems() + for m in iteritems(mix_balance) if m[1] >= total_amount] if not filtered_mix_balance: return None, None, None diff --git a/jmclient/setup.py b/jmclient/setup.py index 3a858a9..83e7e71 100644 --- a/jmclient/setup.py +++ b/jmclient/setup.py @@ -9,5 +9,5 @@ setup(name='joinmarketclient', author_email='', license='GPL', packages=['jmclient'], - install_requires=['future', 'joinmarketbase==0.4.2', 'mnemonic', 'qt4reactor', 'argon2_cffi', 'bencoder.pyx', 'pyaes'], + install_requires=['future', 'configparser;python_version<"3.2"', 'joinmarketbase==0.4.2', 'mnemonic', 'qt4reactor', 'argon2_cffi', 'bencoder.pyx', 'pyaes'], zip_safe=False) diff --git a/jmclient/test/commontest.py b/jmclient/test/commontest.py index e5795e9..68d5888 100644 --- a/jmclient/test/commontest.py +++ b/jmclient/test/commontest.py @@ -1,5 +1,7 @@ #! /usr/bin/env python -from __future__ import absolute_import, print_function +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * # noqa: F401 '''Some helper functions for testing''' import os @@ -131,7 +133,7 @@ def make_sign_and_push(ins_full, from wallets """ total = sum(x['value'] for x in ins_full.values()) - ins = ins_full.keys() + ins = list(ins_full.keys()) #random output address and change addr output_addr = wallet.get_new_addr(1, 1) if not output_addr else output_addr change_addr = wallet.get_new_addr(1, 0) if not change_addr else change_addr @@ -149,7 +151,7 @@ def make_sign_and_push(ins_full, binarize_tx(de_tx) de_tx = wallet.sign_tx(de_tx, scripts, hashcode=hashcode) #pushtx returns False on any error - tx = binascii.hexlify(btc.serialize(de_tx)) + tx = binascii.hexlify(btc.serialize(de_tx)).decode('ascii') push_succeed = jm_single().bc_interface.pushtx(tx) if push_succeed: return btc.txhash(tx) @@ -178,7 +180,7 @@ def make_wallets(n, if len(wallet_structures) != n: raise Exception("Number of wallets doesn't match wallet structures") if not fixed_seeds: - seeds = chunks(binascii.hexlify(os.urandom(BIP32Wallet.ENTROPY_BYTES * n)), + seeds = chunks(binascii.hexlify(os.urandom(BIP32Wallet.ENTROPY_BYTES * n)).decode('ascii'), BIP32Wallet.ENTROPY_BYTES * 2) else: seeds = fixed_seeds diff --git a/jmclient/test/taker_test_data.py b/jmclient/test/taker_test_data.py index 0ee3c09..7a6195d 100644 --- a/jmclient/test/taker_test_data.py +++ b/jmclient/test/taker_test_data.py @@ -1,3 +1,6 @@ +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * # noqa: F401 #orderbook t_orderbook = [{u'counterparty': u'J6FA1Gj7Ln4vSGne', u'ordertype': u'swreloffer', u'oid': 0, u'minsize': 7500000, u'txfee': 1000, u'maxsize': 599972700, u'cjfee': u'0.0002'}, diff --git a/jmclient/test/test_aes.py b/jmclient/test/test_aes.py index e093b9e..7f79586 100644 --- a/jmclient/test/test_aes.py +++ b/jmclient/test/test_aes.py @@ -1,14 +1,18 @@ +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * # noqa: F401 import jmclient.slowaes as sa """test general AES operation; probably not needed. Not included in coverage, but should be included in suite.""" import os +import sys import pytest def test_pkcs7_bad_padding(): #used in seed decryption; check that it throws #if wrongly padded (this caused a REAL bug before!) - bad_padded = ['\x07'*14, '\x07'*31, '\x07'*31+'\x11', '\x07'*31+'\x00', - '\x07'*14+'\x01\x02'] + bad_padded = [b'\x07'*14, b'\x07'*31, b'\x07'*31+b'\x11', b'\x07'*31+b'\x00', + b'\x07'*14+b'\x01\x02'] for b in bad_padded: with pytest.raises(Exception) as e_info: fake_unpadded = sa.strip_PKCS7_padding(b) @@ -19,7 +23,10 @@ def test_aes(): 92] for ks in [16,24,32]: for mode in ["CFB", "CBC", "OFB"]: - cypherkey = map(ord, os.urandom(ks)) + if sys.version_info >= (3,0): + cypherkey = list(map(int, os.urandom(ks))) + else: + cypherkey = list(map(ord, os.urandom(ks))) moo = sa.AESModeOfOperation() mode, orig_len, ciph = moo.encrypt(cleartext, moo.modeOfOperation[mode], cypherkey, ks, diff --git a/jmclient/test/test_argon2.py b/jmclient/test/test_argon2.py index 158c084..8ad11c2 100644 --- a/jmclient/test/test_argon2.py +++ b/jmclient/test/test_argon2.py @@ -1,4 +1,6 @@ -from __future__ import print_function, absolute_import, division, unicode_literals +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * # noqa: F401 from jmclient import Argon2Hash, get_random_bytes diff --git a/jmclient/test/test_blockchaininterface.py b/jmclient/test/test_blockchaininterface.py index f6e6bcf..f092593 100644 --- a/jmclient/test/test_blockchaininterface.py +++ b/jmclient/test/test_blockchaininterface.py @@ -1,4 +1,6 @@ -from __future__ import absolute_import, print_function +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * # noqa: F401 """Blockchaininterface functionality tests.""" diff --git a/jmclient/test/test_client_protocol.py b/jmclient/test/test_client_protocol.py index 5c58691..c767c2f 100644 --- a/jmclient/test/test_client_protocol.py +++ b/jmclient/test/test_client_protocol.py @@ -1,5 +1,7 @@ #! /usr/bin/env python -from __future__ import absolute_import +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * '''test client-protocol interfacae.''' from jmclient import load_program_config, Taker, get_log,\ diff --git a/jmclient/test/test_coinjoin.py b/jmclient/test/test_coinjoin.py index 5498fac..b7c59a7 100644 --- a/jmclient/test/test_coinjoin.py +++ b/jmclient/test/test_coinjoin.py @@ -1,4 +1,6 @@ -from __future__ import print_function, absolute_import, division, unicode_literals +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * # noqa: F401 """ Test doing full coinjoins, bypassing IRC diff --git a/jmclient/test/test_commitment_utils.py b/jmclient/test/test_commitment_utils.py index 32ba4fe..9d0f54c 100644 --- a/jmclient/test/test_commitment_utils.py +++ b/jmclient/test/test_commitment_utils.py @@ -1,5 +1,7 @@ #!/usr/bin/env python -from __future__ import print_function +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * # noqa: F401 from commontest import DummyBlockchainInterface import pytest diff --git a/jmclient/test/test_configure.py b/jmclient/test/test_configure.py index adcfccc..e6401d9 100644 --- a/jmclient/test/test_configure.py +++ b/jmclient/test/test_configure.py @@ -1,4 +1,6 @@ -from __future__ import absolute_import +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * # noqa: F401 '''test configure module.''' import pytest diff --git a/jmclient/test/test_maker.py b/jmclient/test/test_maker.py index 58d7667..31daad2 100644 --- a/jmclient/test/test_maker.py +++ b/jmclient/test/test_maker.py @@ -1,6 +1,8 @@ #!/usr/bin/env python -from __future__ import print_function +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * # noqa: F401 from jmclient import Maker, btc, get_p2sh_vbyte, get_p2pk_vbyte, \ load_program_config, jm_single @@ -46,7 +48,7 @@ def construct_tx_offerlist(cjaddr, changeaddr, maker_utxos, maker_utxos_value, def create_tx_inputs(count=1): inp = [] - for i in xrange(count): + for i in range(count): inp.append({'outpoint': {'hash': '0'*64, 'index': i}, 'script': '', 'sequence': 4294967295}) @@ -71,9 +73,9 @@ def address_p2sh_generator(): def get_address_generator(script_pre, script_post, vbyte): counter = 0 while True: - script = script_pre + struct.pack('=LQQ', 0, 0, counter) + script_post + script = script_pre + struct.pack(b'=LQQ', 0, 0, counter) + script_post addr = btc.script_to_address(script, vbyte) - yield addr, binascii.hexlify(script) + yield addr, binascii.hexlify(script).decode('ascii') counter += 1 @@ -120,21 +122,21 @@ def test_verify_unsigned_tx_sw_valid(setup_env_nodeps): # test standard cj tx, offerlist = create_tx_and_offerlist(cj_addr, changeaddr, - [next(p2sh_gen)[1] for s in xrange(4)], cj_script, cj_change_script) + [next(p2sh_gen)[1] for s in range(4)], cj_script, cj_change_script) assert maker.verify_unsigned_tx(tx, offerlist) == (True, None), "standard sw cj" # test cj with mixed outputs tx, offerlist = create_tx_and_offerlist(cj_addr, changeaddr, - list(chain((next(p2sh_gen)[1] for s in xrange(3)), - (next(p2pkh_gen)[1] for s in xrange(1)))), + list(chain((next(p2sh_gen)[1] for s in range(3)), + (next(p2pkh_gen)[1] for s in range(1)))), cj_script, cj_change_script) assert maker.verify_unsigned_tx(tx, offerlist) == (True, None), "sw cj with p2pkh output" # test cj with only p2pkh outputs tx, offerlist = create_tx_and_offerlist(cj_addr, changeaddr, - [next(p2pkh_gen)[1] for s in xrange(4)], cj_script, cj_change_script) + [next(p2pkh_gen)[1] for s in range(4)], cj_script, cj_change_script) assert maker.verify_unsigned_tx(tx, offerlist) == (True, None), "sw cj with only p2pkh outputs" @@ -153,21 +155,21 @@ def test_verify_unsigned_tx_nonsw_valid(setup_env_nodeps): # test standard cj tx, offerlist = create_tx_and_offerlist(cj_addr, changeaddr, - [next(p2pkh_gen)[1] for s in xrange(4)], cj_script, cj_change_script, 'reloffer') + [next(p2pkh_gen)[1] for s in range(4)], cj_script, cj_change_script, 'reloffer') assert maker.verify_unsigned_tx(tx, offerlist) == (True, None), "standard nonsw cj" # test cj with mixed outputs tx, offerlist = create_tx_and_offerlist(cj_addr, changeaddr, - list(chain((next(p2sh_gen)[1] for s in xrange(1)), - (next(p2pkh_gen)[1] for s in xrange(3)))), + list(chain((next(p2sh_gen)[1] for s in range(1)), + (next(p2pkh_gen)[1] for s in range(3)))), cj_script, cj_change_script, 'reloffer') assert maker.verify_unsigned_tx(tx, offerlist) == (True, None), "nonsw cj with p2sh output" # test cj with only p2sh outputs tx, offerlist = create_tx_and_offerlist(cj_addr, changeaddr, - [next(p2sh_gen)[1] for s in xrange(4)], cj_script, cj_change_script, 'reloffer') + [next(p2sh_gen)[1] for s in range(4)], cj_script, cj_change_script, 'reloffer') assert maker.verify_unsigned_tx(tx, offerlist) == (True, None), "nonsw cj with only p2sh outputs" diff --git a/jmclient/test/test_mnemonic.py b/jmclient/test/test_mnemonic.py index 2c703f5..40772f1 100644 --- a/jmclient/test/test_mnemonic.py +++ b/jmclient/test/test_mnemonic.py @@ -1,3 +1,6 @@ +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * # noqa: F401 from jmclient import old_mnemonic import pytest @@ -43,12 +46,12 @@ def test_old_mnemonic(seedphrase, key, valid): #Already known error condition: an incorrectly short #word list will NOT throw an error; this is handled by calling code if len(seedphrase) < 12: - print "For known failure case of seedphrase less than 12: " - print old_mnemonic.mn_decode(seedphrase) + print("For known failure case of seedphrase less than 12: ") + print(old_mnemonic.mn_decode(seedphrase)) else: with pytest.raises(Exception) as e_info: dummy = old_mnemonic.mn_decode(seedphrase) - print "Got this return value from mn_decode: " + str(dummy) + print("Got this return value from mn_decode: " + str(dummy)) diff --git a/jmclient/test/test_podle.py b/jmclient/test/test_podle.py index c25fc1b..21f6d8d 100644 --- a/jmclient/test/test_podle.py +++ b/jmclient/test/test_podle.py @@ -1,9 +1,12 @@ #! /usr/bin/env python -from __future__ import print_function +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * # noqa: F401 '''Tests of Proof of discrete log equivalence commitments.''' import os import jmbitcoin as bitcoin import binascii +import struct import json import pytest import copy @@ -46,7 +49,7 @@ def generate_single_podle_sig(priv, i): library 'generate_podle' which intelligently searches and updates commitments. """ dummy_utxo = bitcoin.sha256(priv) + ":3" - podle = PoDLE(dummy_utxo, binascii.hexlify(priv)) + podle = PoDLE(dummy_utxo, binascii.hexlify(priv).decode('ascii')) r = podle.generate_podle(i) return (r['P'], r['P2'], r['sig'], r['e'], r['commit']) @@ -112,9 +115,9 @@ def test_external_commitments(setup_podle): known_utxos = [] tries = 3 for i in range(1, 6): - u = binascii.hexlify(chr(i)*32) + u = binascii.hexlify(struct.pack(b'B', i)*32).decode('ascii') known_utxos.append(u) - priv = chr(i)*32+"\x01" + priv = struct.pack(b'B', i)*32+b"\x01" ecs[u] = {} ecs[u]['reveal']={} for j in range(tries): @@ -130,20 +133,20 @@ def test_external_commitments(setup_podle): #this should find the remaining one utxo and return from it assert generate_podle([], max_tries=tries, allow_external=known_utxos) #test commitment removal - to_remove = ecs[binascii.hexlify(chr(3)*32)] - update_commitments(external_to_remove={binascii.hexlify(chr(3)*32):to_remove}) + to_remove = ecs[binascii.hexlify(struct.pack(b'B', 3)*32).decode('ascii')] + update_commitments(external_to_remove={binascii.hexlify(struct.pack(b'B', 3)*32).decode('ascii'):to_remove}) #test that an incorrectly formatted file raises with open(get_commitment_file(), "rb") as f: - validjson = json.loads(f.read()) + validjson = json.loads(f.read().decode('utf-8')) corruptjson = copy.deepcopy(validjson) del corruptjson['used'] with open(get_commitment_file(), "wb") as f: - f.write(json.dumps(corruptjson, indent=4)) + f.write(json.dumps(corruptjson, indent=4).encode('utf-8')) with pytest.raises(PoDLEError) as e_info: get_podle_commitments() #clean up with open(get_commitment_file(), "wb") as f: - f.write(json.dumps(validjson, indent=4)) + f.write(json.dumps(validjson, indent=4).encode('utf-8')) diff --git a/jmclient/test/test_schedule.py b/jmclient/test/test_schedule.py index 578491d..05a4e61 100644 --- a/jmclient/test/test_schedule.py +++ b/jmclient/test/test_schedule.py @@ -1,5 +1,7 @@ #! /usr/bin/env python -from __future__ import absolute_import +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * # noqa: F401 '''test schedule module.''' import pytest @@ -42,7 +44,7 @@ def test_get_schedule(): if os.path.exists(tsf): os.remove(tsf) with open(tsf, "wb") as f: - f.write(s) + f.write(s.encode('utf-8')) result = get_schedule(tsf) if s== valids: assert result[0] diff --git a/jmclient/test/test_storage.py b/jmclient/test/test_storage.py index 1c54304..1989a69 100644 --- a/jmclient/test/test_storage.py +++ b/jmclient/test/test_storage.py @@ -1,4 +1,6 @@ -from __future__ import print_function, absolute_import, division, unicode_literals +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * # noqa: F401 from jmclient import storage import pytest diff --git a/jmclient/test/test_support.py b/jmclient/test/test_support.py index a9b40df..2df4d9f 100644 --- a/jmclient/test/test_support.py +++ b/jmclient/test/test_support.py @@ -1,5 +1,7 @@ #! /usr/bin/env python -from __future__ import absolute_import +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * # noqa: F401 '''support functions for jmclient tests.''' import pytest diff --git a/jmclient/test/test_taker.py b/jmclient/test/test_taker.py index 7656d0c..8fac8f2 100644 --- a/jmclient/test/test_taker.py +++ b/jmclient/test/test_taker.py @@ -1,5 +1,8 @@ #!/usr/bin/env python -from __future__ import print_function +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * # noqa: F401 +from future.utils import iteritems from commontest import DummyBlockchainInterface import jmbitcoin as bitcoin import binascii @@ -8,6 +11,7 @@ import copy import shutil import pytest import json +import struct from base64 import b64encode from jmclient import load_program_config, jm_single, set_commitment_file,\ get_commitment_file, SegwitLegacyWallet, Taker, VolatileStorage,\ @@ -84,7 +88,7 @@ class DummyWallet(SegwitLegacyWallet): def get_key_from_addr(self, addr): """usable addresses: privkey all 1s, 2s, 3s, ... :""" - privs = [x*32 + "\x01" for x in [chr(y) for y in range(1,6)]] + privs = [x*32 + b"\x01" for x in [struct.pack(b'B', y) for y in range(1,6)]] addrs = {} """ mrcNu71ztWjAQA6ww9kHiW3zBWSQidHXTQ @@ -95,9 +99,9 @@ class DummyWallet(SegwitLegacyWallet): """ for p in privs: addrs[p] = bitcoin.privkey_to_address(p, False, magicbyte=0x6f) - for p, a in addrs.iteritems(): + for p, a in iteritems(addrs): if a == addr: - return binascii.hexlify(p) + return binascii.hexlify(p).decode('ascii') raise ValueError("No such keypair") def _is_my_bip32_path(self, path): @@ -154,7 +158,7 @@ def test_make_commitment(createcmtdata, failquery, external): os.remove('dummyext') old_commitment_file = get_commitment_file() with open('dummyext', 'wb') as f: - f.write(json.dumps(t_dummy_ext, indent=4)) + f.write(json.dumps(t_dummy_ext, indent=4).encode('utf-8')) if external: set_commitment_file('dummyext') old_taker_utxo_age = jm_single().config.get("POLICY", "taker_utxo_age") @@ -301,7 +305,7 @@ def test_taker_init(createcmtdata, schedule, highfee, toomuchcoins, minmakers, return clean_up() if schedule[0][1] == 199599800: #need to force negative fees to make this feasible - for k, v in taker.orderbook.iteritems(): + for k, v in iteritems(taker.orderbook): v['cjfee'] = '-0.002' # change_amount = (total_input - self.cjamount - # self.orderbook[nick]['txfee'] + real_cjfee) @@ -320,7 +324,7 @@ def test_taker_init(createcmtdata, schedule, highfee, toomuchcoins, minmakers, #TODO note this test is not adequate, because the code is not; #the code does not *DO* anything if a condition is unexpected. taker.input_utxos = copy.deepcopy(t_utxos_by_mixdepth)[0] - for k,v in taker.input_utxos.iteritems(): + for k,v in iteritems(taker.input_utxos): v["value"] = int(0.999805228 * v["value"]) res = taker.receive_utxos(maker_response) assert res[0] @@ -389,7 +393,7 @@ def test_on_sig(createcmtdata, dummyaddr, schedule): #return the right values in query_utxo_set #create 2 privkey + utxos that are to be ours - privs = [x*32 + "\x01" for x in [chr(y) for y in range(1,6)]] + privs = [x*32 + b"\x01" for x in [struct.pack(b'B', y) for y in range(1,6)]] utxos = [str(x)*64+":1" for x in range(5)] fake_query_results = [{'value': 200000000, 'utxo': utxos[x], @@ -426,12 +430,12 @@ def test_on_sig(createcmtdata, dummyaddr, schedule): taker.my_cj_addr = dummyaddr #make signatures for the last 3 fake utxos, considered as "not ours": tx3 = bitcoin.sign(tx, 2, privs[2]) - sig3 = b64encode(bitcoin.deserialize(tx3)['ins'][2]['script'].decode('hex')) + sig3 = b64encode(binascii.unhexlify(bitcoin.deserialize(tx3)['ins'][2]['script'])) taker.on_sig("cp1", sig3) #try sending the same sig again; should be ignored taker.on_sig("cp1", sig3) tx4 = bitcoin.sign(tx, 3, privs[3]) - sig4 = b64encode(bitcoin.deserialize(tx4)['ins'][3]['script'].decode('hex')) + sig4 = b64encode(binascii.unhexlify(bitcoin.deserialize(tx4)['ins'][3]['script'])) #try sending junk instead of cp2's correct sig taker.on_sig("cp2", str("junk")) taker.on_sig("cp2", sig4) @@ -440,7 +444,7 @@ def test_on_sig(createcmtdata, dummyaddr, schedule): #signing, try with an injected failure of query utxo set, which should #prevent this signature being accepted. dbci.setQUSFail(True) - sig5 = b64encode(bitcoin.deserialize(tx5)['ins'][4]['script'].decode('hex')) + sig5 = b64encode(binascii.unhexlify(bitcoin.deserialize(tx5)['ins'][4]['script'])) taker.on_sig("cp3", sig5) #allow it to succeed, and try again dbci.setQUSFail(False) diff --git a/jmclient/test/test_tx_creation.py b/jmclient/test/test_tx_creation.py index 434cff6..5d327a6 100644 --- a/jmclient/test/test_tx_creation.py +++ b/jmclient/test/test_tx_creation.py @@ -1,10 +1,13 @@ #! /usr/bin/env python -from __future__ import absolute_import +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * # noqa: F401 '''Test of unusual transaction types creation and push to network to check validity.''' import time import binascii +import struct from commontest import make_wallets, make_sign_and_push import jmbitcoin as bitcoin @@ -59,7 +62,7 @@ def test_mktx(setup_tx_creation): """Testing exceptional conditions; not guaranteed to create valid tx objects""" #outpoint structure must be {"outpoint":{"hash":hash, "index": num}} - ins = [{'outpoint': {"hash":x*32, "index":0}, + ins = [{'outpoint': {"hash":x*64, "index":0}, "script": "", "sequence": 4294967295} for x in ["a", "b", "c"]] pub = vpubs[0] addr = bitcoin.pubkey_to_address(pub, magicbyte=get_p2pk_vbyte()) @@ -75,7 +78,7 @@ def test_mktx(setup_tx_creation): def test_bintxhash(setup_tx_creation): tx = "abcdef1234" x = bitcoin.bin_txhash(tx) - assert binascii.hexlify(x) == "121480fc2cccd5103434a9c88b037e08ef6c4f9f95dfb85b56f7043a344613fe" + assert binascii.hexlify(x).decode('ascii') == "121480fc2cccd5103434a9c88b037e08ef6c4f9f95dfb85b56f7043a344613fe" def test_all_same_priv(setup_tx_creation): #recipient @@ -88,7 +91,7 @@ def test_all_same_priv(setup_tx_creation): sync_wallet(wallet, fast=True) insfull = wallet.select_utxos(0, 110000000) outs = [{"address": addr, "value": 1000000}] - ins = insfull.keys() + ins = list(insfull.keys()) tx = bitcoin.mktx(ins, outs) tx = bitcoin.signall(tx, wallet.get_key_from_addr(addrinwallet)) @@ -107,11 +110,11 @@ def test_verify_tx_input(setup_tx_creation, signall, mktxlist): print(insfull) if not mktxlist: outs = [{"address": addr, "value": 1000000}] - ins = insfull.keys() + ins = list(insfull.keys()) tx = bitcoin.mktx(ins, outs) else: out1 = addr+":1000000" - ins0, ins1 = insfull.keys() + ins0, ins1 = list(insfull.keys()) print("INS0 is: " + str(ins0)) print("INS1 is: " + str(ins1)) tx = bitcoin.mktx(ins0, ins1, out1) @@ -134,7 +137,7 @@ def test_verify_tx_input(setup_tx_creation, signall, mktxlist): tx = binascii.unhexlify(tx) tx = bitcoin.sign(tx, index, priv) if index % 2: - tx = binascii.hexlify(tx) + tx = binascii.hexlify(tx).decode('ascii') desertx2 = bitcoin.deserialize(tx) print(desertx2) sig, pub = bitcoin.deserialize_script(desertx2['ins'][0]['script']) @@ -169,7 +172,7 @@ def test_create_sighash_txs(setup_tx_creation): sync_wallet(wallet, fast=True) amount = 350000000 ins_full = wallet.select_utxos(0, amount) - print "using hashcode: " + str(sighash) + print("using hashcode: " + str(sighash)) txid = make_sign_and_push(ins_full, wallet, amount, hashcode=sighash) assert txid @@ -180,8 +183,8 @@ def test_create_sighash_txs(setup_tx_creation): def test_spend_p2sh_utxos(setup_tx_creation): #make a multisig address from 3 privs - privs = [chr(x) * 32 + '\x01' for x in range(1, 4)] - pubs = [bitcoin.privkey_to_pubkey(binascii.hexlify(priv)) for priv in privs] + privs = [struct.pack(b'B', x) * 32 + b'\x01' for x in range(1, 4)] + pubs = [bitcoin.privkey_to_pubkey(binascii.hexlify(priv).decode('ascii')) for priv in privs] script = bitcoin.mk_multisig_script(pubs, 2) msig_addr = bitcoin.scriptaddr(script, magicbyte=196) #pay into it @@ -203,7 +206,7 @@ def test_spend_p2sh_utxos(setup_tx_creation): tx = bitcoin.mktx(ins, outs) sigs = [] for priv in privs[:2]: - sigs.append(bitcoin.multisign(tx, 0, script, binascii.hexlify(priv))) + sigs.append(bitcoin.multisign(tx, 0, script, binascii.hexlify(priv).decode('ascii'))) tx = bitcoin.apply_multisignatures(tx, 0, script, sigs) txid = jm_single().bc_interface.pushtx(tx) assert txid diff --git a/jmclient/test/test_utxomanager.py b/jmclient/test/test_utxomanager.py index 2c64e8e..915bb61 100644 --- a/jmclient/test/test_utxomanager.py +++ b/jmclient/test/test_utxomanager.py @@ -1,4 +1,6 @@ -from __future__ import print_function, absolute_import, division, unicode_literals +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * # noqa: F401 from jmclient.wallet import UTXOManager from test_storage import MockStorage @@ -32,9 +34,9 @@ def test_utxomanager_persist(setup_env_nodeps): um = UTXOManager(storage, select) - assert um.have_utxo(txid, index) is mixdepth - assert um.have_utxo(txid, index+1) is mixdepth + 1 - assert um.have_utxo(txid, index+2) is False + assert um.have_utxo(txid, index) == mixdepth + assert um.have_utxo(txid, index+1) == mixdepth + 1 + assert um.have_utxo(txid, index+2) == False utxos = um.get_utxos_by_mixdepth() assert len(utxos[mixdepth]) == 1 @@ -46,15 +48,15 @@ def test_utxomanager_persist(setup_env_nodeps): assert balances[mixdepth+1] == value um.remove_utxo(txid, index, mixdepth) - assert um.have_utxo(txid, index) is False + assert um.have_utxo(txid, index) == False um.save() del um um = UTXOManager(storage, select) - assert um.have_utxo(txid, index) is False - assert um.have_utxo(txid, index+1) is mixdepth + 1 + assert um.have_utxo(txid, index) == False + assert um.have_utxo(txid, index+1) == mixdepth + 1 utxos = um.get_utxos_by_mixdepth() assert len(utxos[mixdepth]) == 0 diff --git a/jmclient/test/test_valid_addresses.py b/jmclient/test/test_valid_addresses.py index 70139c6..e76bd60 100644 --- a/jmclient/test/test_valid_addresses.py +++ b/jmclient/test/test_valid_addresses.py @@ -1,4 +1,6 @@ -from __future__ import print_function +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * # noqa: F401 from jmclient.configure import validate_address, load_program_config from jmclient import jm_single import json diff --git a/jmclient/test/test_wallet.py b/jmclient/test/test_wallet.py index 91e5dd6..5f6c40e 100644 --- a/jmclient/test/test_wallet.py +++ b/jmclient/test/test_wallet.py @@ -1,4 +1,6 @@ -from __future__ import print_function, absolute_import, division, unicode_literals +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * # noqa: F401 '''Wallet functionality tests.''' import os @@ -64,14 +66,14 @@ def get_bip39_vectors(): def test_bip39_seeds(monkeypatch, setup_wallet, entropy, mnemonic, key, xpriv): jm_single().config.set('BLOCKCHAIN', 'network', 'mainnet') created_entropy = SegwitLegacyWallet.entropy_from_mnemonic(mnemonic) - assert entropy == hexlify(created_entropy) + assert entropy == hexlify(created_entropy).decode('ascii') storage = VolatileStorage() SegwitLegacyWallet.initialize( storage, get_network(), entropy=created_entropy, entropy_extension=b'TREZOR', max_mixdepth=4) wallet = SegwitLegacyWallet(storage) assert (mnemonic, b'TREZOR') == wallet.get_mnemonic_words() - assert key == hexlify(wallet._create_master_key()) + assert key == hexlify(wallet._create_master_key()).decode('ascii') # need to monkeypatch this, else we'll default to the BIP-49 path monkeypatch.setattr(SegwitLegacyWallet, '_get_bip32_base_path', @@ -93,7 +95,7 @@ def test_bip49_seed(monkeypatch, setup_wallet): wallet = SegwitLegacyWallet(storage) assert (mnemonic, None) == wallet.get_mnemonic_words() assert account0_xpriv == wallet.get_bip32_priv_export(0) - assert addr0_script_hash == hexlify(wallet.get_external_script(0)[2:-1]) + assert addr0_script_hash == hexlify(wallet.get_external_script(0)[2:-1]).decode('ascii') # FIXME: is this desired behaviour? BIP49 wallet will not return xpriv for # the root key but only for key after base path @@ -298,13 +300,13 @@ def test_signing_imported(setup_wallet, wif, keytype, type_check): MIXDEPTH = 0 path = wallet.import_private_key(MIXDEPTH, wif, keytype) utxo = fund_wallet_addr(wallet, wallet.get_addr_path(path)) - tx = btc.deserialize(btc.mktx(['{}:{}'.format(hexlify(utxo[0]), utxo[1])], + tx = btc.deserialize(btc.mktx(['{}:{}'.format(hexlify(utxo[0]).decode('ascii'), utxo[1])], ['00'*17 + ':' + str(10**8 - 9000)])) binarize_tx(tx) script = wallet.get_script_path(path) wallet.sign_tx(tx, {0: (script, 10**8)}) type_check(tx) - txout = jm_single().bc_interface.pushtx(hexlify(btc.serialize(tx))) + txout = jm_single().bc_interface.pushtx(hexlify(btc.serialize(tx)).decode('ascii')) assert txout @@ -318,13 +320,13 @@ def test_signing_simple(setup_wallet, wallet_cls, type_check): wallet_cls.initialize(storage, get_network()) wallet = wallet_cls(storage) utxo = fund_wallet_addr(wallet, wallet.get_internal_addr(0)) - tx = btc.deserialize(btc.mktx(['{}:{}'.format(hexlify(utxo[0]), utxo[1])], + tx = btc.deserialize(btc.mktx(['{}:{}'.format(hexlify(utxo[0]).decode('ascii'), utxo[1])], ['00'*17 + ':' + str(10**8 - 9000)])) binarize_tx(tx) script = wallet.get_script(0, 1, 0) wallet.sign_tx(tx, {0: (script, 10**8)}) type_check(tx) - txout = jm_single().bc_interface.pushtx(hexlify(btc.serialize(tx))) + txout = jm_single().bc_interface.pushtx(hexlify(btc.serialize(tx)).decode('ascii')) assert txout @@ -381,7 +383,7 @@ def test_add_new_utxos(setup_wallet): tx_scripts.append(b'\x22'*17) tx = btc.deserialize(btc.mktx( - ['0'*64 + ':2'], [{'script': hexlify(s), 'value': 10**8} + ['0'*64 + ':2'], [{'script': hexlify(s).decode('ascii'), 'value': 10**8} for s in tx_scripts])) binarize_tx(tx) txid = b'\x01' * 32 @@ -417,7 +419,7 @@ def test_remove_old_utxos(setup_wallet): tx_inputs.append((b'\x12'*32, 6)) tx = btc.deserialize(btc.mktx( - ['{}:{}'.format(hexlify(txid), i) for txid, i in tx_inputs], + ['{}:{}'.format(hexlify(txid).decode('ascii'), i) for txid, i in tx_inputs], ['0' * 36 + ':' + str(3 * 10**8 - 1000)])) binarize_tx(tx) diff --git a/jmclient/test/test_wallets.py b/jmclient/test/test_wallets.py index c9bce63..049a96f 100644 --- a/jmclient/test/test_wallets.py +++ b/jmclient/test/test_wallets.py @@ -1,19 +1,19 @@ #! /usr/bin/env python -from __future__ import absolute_import +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * # noqa: F401 '''Wallet functionality tests.''' -import sys import os import time import binascii -from mnemonic import Mnemonic from commontest import create_wallet_for_sync, make_sign_and_push import json import pytest from jmclient import ( load_program_config, jm_single, sync_wallet, get_log, - estimate_tx_fee, BitcoinCoreInterface) + estimate_tx_fee, BitcoinCoreInterface, Mnemonic) from taker_test_data import t_raw_signed_tx testdir = os.path.dirname(os.path.realpath(__file__)) log = get_log() @@ -94,9 +94,7 @@ def check_bip39_case(vectors, language="english"): mnemo = Mnemonic(language) for v in vectors: code = mnemo.to_mnemonic(binascii.unhexlify(v[0])) - seed = binascii.hexlify(Mnemonic.to_seed(code, passphrase=v[4])) - if sys.version >= '3': - seed = seed.decode('utf8') + seed = binascii.hexlify(Mnemonic.to_seed(code, passphrase=v[4])).decode('ascii') print('checking this phrase: ' + v[1]) assert mnemo.check(v[1]) assert v[1] == code diff --git a/jmdaemon/jmdaemon/daemon_protocol.py b/jmdaemon/jmdaemon/daemon_protocol.py index 8a17261..510a4a8 100644 --- a/jmdaemon/jmdaemon/daemon_protocol.py +++ b/jmdaemon/jmdaemon/daemon_protocol.py @@ -2,6 +2,7 @@ from __future__ import (absolute_import, division, print_function, unicode_literals) from builtins import * +from future.utils import iteritems from .message_channel import MessageChannelCollection from .orderbookwatch import OrderbookWatch @@ -13,7 +14,6 @@ from .protocol import (COMMAND_PREFIX, ORDER_KEYS, NICK_HASH_LENGTH, from .irc import IRCMessageChannel from jmbase.commands import * -from jmbase import _byteify from twisted.protocols import amp from twisted.internet import reactor, ssl from twisted.internet.protocol import ServerFactory @@ -72,7 +72,7 @@ def check_utxo_blacklist(commitment, persist=False): fname = "commitmentlist" if os.path.isfile(fname): with open(fname, "rb") as f: - blacklisted_commitments = [x.strip() for x in f.readlines()] + blacklisted_commitments = [x.decode('ascii').strip() for x in f.readlines()] else: blacklisted_commitments = [] if commitment in blacklisted_commitments: @@ -80,7 +80,7 @@ def check_utxo_blacklist(commitment, persist=False): elif persist: blacklisted_commitments += [commitment] with open(fname, "wb") as f: - f.write('\n'.join(blacklisted_commitments)) + f.write('\n'.join(blacklisted_commitments).encode('ascii')) f.flush() #If the commitment is new and we are *not* persisting, nothing to do #(we only add it to the list on sending io_auth, which represents actual @@ -242,9 +242,9 @@ class JMDaemonServerProtocol(amp.AMP, OrderbookWatch): #Reset utxo data to null for this new transaction self.ioauth_data = {} self.active_orders = json.loads(filled_offers) - for nick, offer_dict in self.active_orders.iteritems(): - offer_fill_msg = " ".join([str(offer_dict["oid"]), str(amount), str( - self.kp.hex_pk()), str(commitment)]) + for nick, offer_dict in iteritems(self.active_orders): + offer_fill_msg = " ".join([str(offer_dict["oid"]), str(amount), + self.kp.hex_pk().decode('ascii'), str(commitment)]) self.mcc.prepare_privmsg(nick, "fill", offer_fill_msg) reactor.callLater(self.maker_timeout_sec, self.completeStage1) self.jm_state = 2 @@ -293,8 +293,6 @@ class JMDaemonServerProtocol(amp.AMP, OrderbookWatch): channel based on data from Maker. Relevant data (utxos, addresses) are stored in the active_orders dict keyed by the nick of the Taker. """ - nick, utxolist, pubkey, cjaddr, changeaddr, pubkeysig = [_byteify( - x) for x in (nick, utxolist, pubkey, cjaddr, changeaddr, pubkeysig)] if not self.role == "MAKER": return if not nick in self.active_orders: @@ -324,7 +322,7 @@ class JMDaemonServerProtocol(amp.AMP, OrderbookWatch): broadcast one by one. TODO: could shorten this, have more than one sig per message. """ - sigs = _byteify(json.loads(sigs)) + sigs = json.loads(sigs) for sig in sigs: self.mcc.prepare_privmsg(nick, "sig", sig) return {"accepted": True} @@ -395,7 +393,7 @@ class JMDaemonServerProtocol(amp.AMP, OrderbookWatch): "offer": offer, "amount": amount, "commit": scommit} - self.mcc.prepare_privmsg(nick, "pubkey", kp.hex_pk()) + self.mcc.prepare_privmsg(nick, "pubkey", kp.hex_pk().decode('ascii')) @maker_only def on_seen_auth(self, nick, commitment_revelation): @@ -414,7 +412,7 @@ class JMDaemonServerProtocol(amp.AMP, OrderbookWatch): commitment=ao["commit"], revelation=json.dumps(commitment_revelation), amount=ao["amount"], - kphex=ao["kp"].hex_pk()) + kphex=ao["kp"].hex_pk().decode('ascii')) self.defaultCallbacks(d) @maker_only @@ -576,7 +574,7 @@ class JMDaemonServerProtocol(amp.AMP, OrderbookWatch): ).fetchone() if crow is None: return - counterparty = crow[b'counterparty'] + counterparty = crow['counterparty'] #TODO de-hardcode hp2 log.msg("Sending commitment to: " + str(counterparty)) self.mcc.prepare_privmsg(counterparty, 'hp2', commit) diff --git a/jmdaemon/jmdaemon/enc_wrapper.py b/jmdaemon/jmdaemon/enc_wrapper.py index 4866605..d4244b5 100644 --- a/jmdaemon/jmdaemon/enc_wrapper.py +++ b/jmdaemon/jmdaemon/enc_wrapper.py @@ -38,7 +38,7 @@ def get_pubkey(kp, as_hex=False): optionally in hex.""" if not isinstance(kp, public.SecretKey): raise NaclError("Object is not a nacl keypair") - return kp.hex_pk() if as_hex else kp.pk + return kp.hex_pk().decode('ascii') if as_hex else kp.pk def init_pubkey(hexpk, fname=None): @@ -48,7 +48,7 @@ def init_pubkey(hexpk, fname=None): """ try: bin_pk = binascii.unhexlify(hexpk) - except TypeError: + except (TypeError, binascii.Error): raise NaclError("Invalid hex") if not len(bin_pk) == 32: raise NaclError("Public key must be 32 bytes") @@ -91,7 +91,7 @@ Notes: # encoding for passing over the wire def encrypt_encode(msg, box): encrypted = box.encrypt(msg) - return base64.b64encode(encrypted) + return base64.b64encode(encrypted).decode('ascii') def decode_decrypt(msg, box): diff --git a/jmdaemon/jmdaemon/irc.py b/jmdaemon/jmdaemon/irc.py index 968ecc3..0e6553c 100644 --- a/jmdaemon/jmdaemon/irc.py +++ b/jmdaemon/jmdaemon/irc.py @@ -78,7 +78,7 @@ class IRCMessageChannel(MessageChannel): daemon=None): MessageChannel.__init__(self, daemon=daemon) self.give_up = True - self.serverport = (configdata['host'], configdata['port']) + self.serverport = (configdata['host'], int(configdata['port'])) #default hostid for use with miniircd which doesnt send NETWORK self.hostid = configdata['host'] + str(configdata['port']) self.socks5 = configdata["socks5"] @@ -152,7 +152,7 @@ class IRCMessageChannel(MessageChannel): else: try: factory = TxIRCFactory(self) - wlog('build_irc: ', self.serverport[0], self.serverport[1], + wlog('build_irc: ', self.serverport[0], str(self.serverport[1]), self.channel) self.tcp_connector = reactor.connectTCP( self.serverport[0], self.serverport[1], factory) @@ -198,7 +198,7 @@ class txIRC_Client(irc.IRCClient, object): def send(self, send_to, msg): # todo: use proper twisted IRC support (encoding + sendCommand) omsg = 'PRIVMSG %s :' % (send_to,) + msg - self.sendLine(omsg.encode('ascii')) + self.sendLine(omsg) def _pubmsg(self, message): self.send(self.channel, message) diff --git a/jmdaemon/jmdaemon/message_channel.py b/jmdaemon/jmdaemon/message_channel.py index 165bb51..1e1c7d6 100644 --- a/jmdaemon/jmdaemon/message_channel.py +++ b/jmdaemon/jmdaemon/message_channel.py @@ -2,8 +2,10 @@ from __future__ import (absolute_import, division, print_function, unicode_literals) from builtins import * # noqa: F401 +from future.utils import iteritems import abc import base64 +import binascii import threading from jmdaemon import encrypt_encode, decode_decrypt, COMMAND_PREFIX,\ NICK_HASH_LENGTH, NICK_MAX_ENCODED, plaintext_commands,\ @@ -320,7 +322,7 @@ class MessageChannelCollection(object): """ for mc in self.available_channels(): filtered_nick_order_dict = {k: v - for k, v in nick_order_dict.iteritems() + for k, v in iteritems(nick_order_dict) if mc == self.active_channels[k]} mc.fill_orders(filtered_nick_order_dict, cj_amount, taker_pubkey, commitment) @@ -336,7 +338,7 @@ class MessageChannelCollection(object): #TODO supporting sending to arbitrary nicks #adds quite a bit of complexity, not supported #initially; will fail if nick is not part of TX - txb64 = base64.b64encode(txhex.decode('hex')) + txb64 = base64.b64encode(binascii.unhexlify(txhex)).decode('ascii') self.prepare_privmsg(nick, "push", txb64) def send_tx(self, nick_list, txhex): @@ -356,11 +358,11 @@ class MessageChannelCollection(object): tx_nick_sets[self.active_channels[nick]] = [nick] else: tx_nick_sets[self.active_channels[nick]].append(nick) - for mc, nl in tx_nick_sets.iteritems(): + for mc, nl in iteritems(tx_nick_sets): self.prepare_send_tx(mc, nl, txhex) def prepare_send_tx(self, mc, nick_list, txhex): - txb64 = base64.b64encode(txhex.decode('hex')) + txb64 = base64.b64encode(binascii.unhexlify(txhex)).decode('ascii') for nick in nick_list: self.prepare_privmsg(nick, "tx", txb64, mc=mc) @@ -790,7 +792,7 @@ class MessageChannel(object): # Taker callbacks def fill_orders(self, nick_order_dict, cj_amount, taker_pubkey, commitment): - for c, order in nick_order_dict.iteritems(): + for c, order in iteritems(nick_order_dict): msg = str(order['oid']) + ' ' + str(cj_amount) + ' ' + taker_pubkey msg += ' ' + commitment self.privmsg(c, 'fill', msg) @@ -798,7 +800,7 @@ class MessageChannel(object): def push_tx(self, nick, txhex): #Note: not currently used; will require prepare_privmsg call so #not in this class (see send_error) - txb64 = base64.b64encode(txhex.decode('hex')) + txb64 = base64.b64encode(binascii.unhexlify(txhex)).decode('ascii') self.privmsg(nick, 'push', txb64) def send_error(self, nick, errormsg): @@ -968,7 +970,7 @@ class MessageChannel(object): elif _chunks[0] == 'tx': b64tx = _chunks[1] try: - txhex = base64.b64decode(b64tx).encode('hex') + txhex = binascii.hexlify(base64.b64decode(b64tx)).decode('ascii') except TypeError as e: self.send_error(nick, 'bad base64 tx. ' + repr(e)) return @@ -977,7 +979,7 @@ class MessageChannel(object): elif _chunks[0] == 'push': b64tx = _chunks[1] try: - txhex = base64.b64decode(b64tx).encode('hex') + txhex = binascii.hexlify(base64.b64decode(b64tx)).decode('ascii') except TypeError as e: self.send_error(nick, 'bad base64 tx. ' + repr(e)) return diff --git a/jmdaemon/jmdaemon/orderbookwatch.py b/jmdaemon/jmdaemon/orderbookwatch.py index 233e464..53e02c6 100644 --- a/jmdaemon/jmdaemon/orderbookwatch.py +++ b/jmdaemon/jmdaemon/orderbookwatch.py @@ -14,6 +14,13 @@ from jmbase.support import get_log, joinmarket_alert, DUST_THRESHOLD log = get_log() +def dict_factory(cursor, row): + d = {} + for idx, col in enumerate(cursor.description): + d[col[0]] = row[idx] + return d + + class JMTakerError(Exception): pass @@ -30,11 +37,15 @@ class OrderbookWatch(object): self.dblock = threading.Lock() con = sqlite3.connect(":memory:", check_same_thread=False) - con.row_factory = sqlite3.Row + con.row_factory = dict_factory self.db = con.cursor() - self.db.execute("CREATE TABLE orderbook(counterparty TEXT, " - "oid INTEGER, ordertype TEXT, minsize INTEGER, " - "maxsize INTEGER, txfee INTEGER, cjfee TEXT);") + try: + self.dblock.acquire(True) + self.db.execute("CREATE TABLE orderbook(counterparty TEXT, " + "oid INTEGER, ordertype TEXT, minsize INTEGER, " + "maxsize INTEGER, txfee INTEGER, cjfee TEXT);") + finally: + self.dblock.release() @staticmethod def on_set_topic(newtopic): @@ -59,7 +70,11 @@ class OrderbookWatch(object): txfee, cjfee): try: self.dblock.acquire(True) - if int(oid) < 0 or int(oid) > sys.maxint: + if sys.version_info >= (3,0): + maxint = sys.maxsize + else: + maxint = sys.maxint + if int(oid) < 0 or int(oid) > maxint: log.debug("Got invalid order ID: " + oid + " from " + counterparty) return @@ -114,16 +129,25 @@ class OrderbookWatch(object): self.dblock.release() def on_order_cancel(self, counterparty, oid): - with self.dblock: + try: + self.dblock.acquire(True) self.db.execute( ("DELETE FROM orderbook WHERE " "counterparty=? AND oid=?;"), (counterparty, oid)) + finally: + self.dblock.release() def on_nick_leave(self, nick): - with self.dblock: + try: + self.dblock.acquire(True) self.db.execute('DELETE FROM orderbook WHERE counterparty=?;', (nick,)) + finally: + self.dblock.release() def on_disconnect(self): - with self.dblock: + try: + self.dblock.acquire(True) self.db.execute('DELETE FROM orderbook;') + finally: + self.dblock.release() diff --git a/jmdaemon/jmdaemon/protocol.py b/jmdaemon/jmdaemon/protocol.py index 0532cc5..25b5581 100644 --- a/jmdaemon/jmdaemon/protocol.py +++ b/jmdaemon/jmdaemon/protocol.py @@ -17,10 +17,10 @@ offertypes = {"reloffer": [(int, "oid"), (int, "minsize"), (int, "maxsize"), "swabsoffer": [(int, "oid"), (int, "minsize"), (int, "maxsize"), (int, "txfee"), (int, "cjfee")]} -offername_list = offertypes.keys() +offername_list = list(offertypes.keys()) -ORDER_KEYS = [b'counterparty', b'oid', b'ordertype', b'minsize', b'maxsize', b'txfee', - b'cjfee'] +ORDER_KEYS = ['counterparty', 'oid', 'ordertype', 'minsize', 'maxsize', 'txfee', + 'cjfee'] COMMAND_PREFIX = '!' JOINMARKET_NICK_HEADER = 'J' @@ -36,5 +36,5 @@ commitment_broadcast_list = ["hp2"] plaintext_commands += offername_list plaintext_commands += commitment_broadcast_list public_commands = commitment_broadcast_list + ["orderbook", "cancel" - ] + list(offername_list) + ] + offername_list private_commands = encrypted_commands + plaintext_commands diff --git a/jmdaemon/test/test_daemon_protocol.py b/jmdaemon/test/test_daemon_protocol.py index 2ea57ca..3190611 100644 --- a/jmdaemon/test/test_daemon_protocol.py +++ b/jmdaemon/test/test_daemon_protocol.py @@ -2,6 +2,7 @@ from __future__ import (absolute_import, division, print_function, unicode_literals) from builtins import * +from future.utils import iteritems '''test daemon-protocol interfacae.''' from jmdaemon import MessageChannelCollection @@ -11,6 +12,7 @@ from jmdaemon.protocol import NICK_HASH_LENGTH, NICK_MAX_ENCODED, JM_VERSION,\ JOINMARKET_NICK_HEADER from jmclient import (load_program_config, get_log, jm_single, get_irc_mchannels) from twisted.python.log import msg as tmsg +from twisted.python.log import startLogging from twisted.internet import protocol, reactor, task from twisted.internet.protocol import ServerFactory from twisted.internet.error import (ConnectionLost, ConnectionAborted, @@ -22,6 +24,7 @@ from jmbase.commands import * from msgdata import * import json import base64 +import sys from dummy_mc import DummyMessageChannel test_completed = False end_early = False @@ -84,7 +87,7 @@ class JMTestClientProtocol(JMBaseProtocol): show_receipt("JMUP") d = self.callRemote(JMSetup, role="TAKER", - n_counterparties=4) #TODO this number should be set + initdata="none") self.defaultCallbacks(d) return {'accepted': True} @@ -103,7 +106,7 @@ class JMTestClientProtocol(JMBaseProtocol): def maketx(self, ioauth_data): ioauth_data = json.loads(ioauth_data) - nl = ioauth_data.keys() + nl = list(ioauth_data.keys()) d = self.callRemote(JMMakeTx, nick_list= json.dumps(nl), txhex="deadbeef") @@ -115,8 +118,8 @@ class JMTestClientProtocol(JMBaseProtocol): return {'accepted': True} jlog.debug("JMOFFERS" + str(orderbook)) #Trigger receipt of verified privmsgs, including unverified - nick = str(t_chosen_orders.keys()[0]) - b64tx = base64.b64encode("deadbeef") + nick = str(list(t_chosen_orders.keys())[0]) + b64tx = base64.b64encode(b"deadbeef").decode('ascii') d1 = self.callRemote(JMMsgSignatureVerify, verif_result=True, nick=nick, @@ -244,16 +247,16 @@ class JMDaemonTestServerProtocol(JMDaemonServerProtocol): dummypub = "073732a7ca60470f709f23c602b2b8a6b1ba62ee8f3f83a61e5484ab5cbf9c3d" #trigger invalid on_pubkey conditions reactor.callLater(1, self.on_pubkey, "notrealcp", dummypub) - reactor.callLater(2, self.on_pubkey, tmpfo.keys()[0], dummypub + "deadbeef") + reactor.callLater(2, self.on_pubkey, list(tmpfo.keys())[0], dummypub + "deadbeef") #trigger invalid on_ioauth condition reactor.callLater(2, self.on_ioauth, "notrealcp", 1, 2, 3, 4, 5) #trigger msg sig verify request operation for a dummy message #currently a pass-through reactor.callLater(1, self.request_signature_verify, "1", "!push abcd abc def", "3", "4", - str(tmpfo.keys()[0]), 6, 7, self.mcc.mchannels[0].hostid) + str(list(tmpfo.keys())[0]), 6, 7, self.mcc.mchannels[0].hostid) #send "valid" onpubkey, onioauth messages - for k, v in tmpfo.iteritems(): + for k, v in iteritems(tmpfo): reactor.callLater(1, self.on_pubkey, k, dummypub) reactor.callLater(2, self.on_ioauth, k, ['a', 'b'], "auth_pub", "cj_addr", "change_addr", "btc_sig") @@ -262,7 +265,7 @@ class JMDaemonTestServerProtocol(JMDaemonServerProtocol): @JMMakeTx.responder def on_JM_MAKE_TX(self, nick_list, txhex): - for n in nick_list: + for n in json.loads(nick_list): reactor.callLater(1, self.on_sig, n, "dummytxsig") return super(JMDaemonTestServerProtocol, self).on_JM_MAKE_TX(nick_list, txhex) @@ -289,6 +292,7 @@ class JMDaemonTest2ServerProtocolFactory(ServerFactory): class TrialTestJMDaemonProto(unittest.TestCase): def setUp(self): + startLogging(sys.stdout) load_program_config() jm_single().maker_timeout_sec = 1 self.port = reactor.listenTCP(28184, JMDaemonTestServerProtocolFactory()) diff --git a/jmdaemon/test/test_enc_wrapper.py b/jmdaemon/test/test_enc_wrapper.py index ef39b93..46d6084 100644 --- a/jmdaemon/test/test_enc_wrapper.py +++ b/jmdaemon/test/test_enc_wrapper.py @@ -16,10 +16,10 @@ from jmdaemon import (init_keypair, get_pubkey, init_pubkey, as_init_encryption, # short ascii (b"Attack at dawn", b"Not tonight Josephine!", 5), # long base64 encoded - (base64.b64encode(''.join(random.choice( - string.ascii_letters) for _ in range(5000))), - base64.b64encode(''.join(random.choice( - string.ascii_letters) for _ in range(5000))), + (base64.b64encode(b''.join(random.choice( + string.ascii_letters).encode('ascii') for _ in range(5000))), + base64.b64encode(b''.join(random.choice( + string.ascii_letters).encode('ascii') for _ in range(5000))), 5,), # large number of messages on the same connection (b'rand', b'rand', 40000), diff --git a/jmdaemon/test/test_message_channel.py b/jmdaemon/test/test_message_channel.py index 9f54beb..12b558b 100644 --- a/jmdaemon/test/test_message_channel.py +++ b/jmdaemon/test/test_message_channel.py @@ -15,6 +15,7 @@ from msgdata import * import time import hashlib import base64 +import struct import traceback import threading import jmbitcoin as bitcoin @@ -24,9 +25,9 @@ from dummy_mc import DummyMessageChannel jlog = get_log() def make_valid_nick(i=0): - nick_priv = hashlib.sha256(chr(i)*16).hexdigest() + '01' + nick_priv = hashlib.sha256(struct.pack(b'B', i)*16).hexdigest() + '01' nick_pubkey = bitcoin.privtopub(nick_priv) - nick_pkh_raw = hashlib.sha256(nick_pubkey).digest()[:NICK_HASH_LENGTH] + nick_pkh_raw = hashlib.sha256(nick_pubkey.encode('ascii')).digest()[:NICK_HASH_LENGTH] nick_pkh = bitcoin.b58encode(nick_pkh_raw) #right pad to maximum possible; b58 is not fixed length. #Use 'O' as one of the 4 not included chars in base58. @@ -271,8 +272,8 @@ def test_setup_mc(): dmcs[0].on_privmsg(cps[2], "!reloffer sig1 sig2") #Simulating receipt of encrypted messages: #ioauth - dummy_on_ioauth_msg = "deadbeef:0,deadbeef:1 XauthpubX XcjaddrX XchangeaddrX XbtcsigX" - b64dummyioauth = base64.b64encode(dummy_on_ioauth_msg) + dummy_on_ioauth_msg = b"deadbeef:0,deadbeef:1 XauthpubX XcjaddrX XchangeaddrX XbtcsigX" + b64dummyioauth = base64.b64encode(dummy_on_ioauth_msg).decode('ascii') dmcs[0].on_privmsg(cps[3], "!ioauth " + b64dummyioauth + " sig1 sig2") #Try with a garbage b64 (but decodable); should throw index error at least dmcs[0].on_privmsg(cps[3], "!ioauth _*_ sig1 sig2") @@ -282,23 +283,23 @@ def test_setup_mc(): b64dummyioauth = "999" dmcs[0].on_privmsg(cps[3], "!ioauth " + b64dummyioauth + " sig1 sig2") #sig - dummy_on_sig_msg = "dummysig" - b64dummysig = base64.b64encode(dummy_on_sig_msg) + dummy_on_sig_msg = b"dummysig" + b64dummysig = base64.b64encode(dummy_on_sig_msg).decode('ascii') dmcs[0].on_privmsg(cps[3], "!sig " + b64dummysig + " sig1 sig2") #auth - dummy_auth_msg = "dummyauth" - b64dummyauth = base64.b64encode(dummy_auth_msg) + dummy_auth_msg = b"dummyauth" + b64dummyauth = base64.b64encode(dummy_auth_msg).decode('ascii') dmcs[0].on_privmsg(cps[2], "!auth " + b64dummyauth + " sig1 sig2") #invalid auth (only no message is invalid) - dmcs[0].on_privmsg(cps[3], "!auth " +base64.b64encode("") + " sig1 sig2") + dmcs[0].on_privmsg(cps[3], "!auth " +base64.b64encode(b"").decode('ascii') + " sig1 sig2") #tx #valid - dummy_tx = "deadbeefdeadbeef" + dummy_tx = b"deadbeefdeadbeef" b64dummytx = base64.b64encode(dummy_tx) - b642dummytx = base64.b64encode(b64dummytx) + b642dummytx = base64.b64encode(b64dummytx).decode('ascii') dmcs[0].on_privmsg(cps[2], "!tx " + b642dummytx + " sig1 sig2") - badbase64tx = "999" - badbase64tx2 = base64.b64encode(badbase64tx) + badbase64tx = b"999" + badbase64tx2 = base64.b64encode(badbase64tx).decode('ascii') #invalid txhex; here the first round will work (msg decryption), second shouldn't dmcs[0].on_privmsg(cps[2], "!tx " + badbase64tx2 + " sig1 sig2") #push diff --git a/scripts/add-utxo.py b/scripts/add-utxo.py index 5e0959c..263a788 100644 --- a/scripts/add-utxo.py +++ b/scripts/add-utxo.py @@ -1,5 +1,8 @@ #! /usr/bin/env python -from __future__ import absolute_import +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * # noqa: F401 +from future.utils import iteritems """A very simple command line tool to import utxos to be used as commitments into joinmarket's commitments.json file, allowing users to retry transactions more often without getting banned by @@ -156,18 +159,18 @@ def main(): if options.delete_ext: other = options.in_file or options.in_json or options.loadwallet if len(args) > 0 or other: - if raw_input("You have chosen to delete commitments, other arguments " + if input("You have chosen to delete commitments, other arguments " "will be ignored; continue? (y/n)") != 'y': - print "Quitting" + print("Quitting") sys.exit(0) c, e = get_podle_commitments() - print pformat(e) - if raw_input( + print(pformat(e)) + if input( "You will remove the above commitments; are you sure? (y/n): ") != 'y': - print "Quitting" + print("Quitting") sys.exit(0) update_commitments(external_to_remove=e) - print "Commitments deleted." + print("Commitments deleted.") sys.exit(0) #Three options (-w, -r, -R) for loading utxo and privkey pairs from a wallet, @@ -180,7 +183,7 @@ def main(): for md, utxos in wallet.get_utxos_by_mixdepth_().items(): for (txid, index), utxo in utxos.items(): - txhex = binascii.hexlify(txid) + ':' + str(index) + txhex = binascii.hexlify(txid).decode('ascii') + ':' + str(index) wif = wallet.get_wif_path(utxo['path']) utxo_data.append((txhex, wif)) @@ -196,19 +199,19 @@ 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." + print("File: " + options.in_json + " not found.") 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 + print("Failed to read json from " + options.in_json) sys.exit(0) - for u, pva in utxo_json.iteritems(): + for u, pva in iteritems(utxo_json): utxo_data.append((u, pva['privkey'])) elif len(args) == 1: u = args[0] - priv = raw_input( + priv = input( 'input private key for ' + u + ', in WIF compressed format : ') u, priv = get_utxo_info(','.join([u, priv])) if not u: diff --git a/scripts/cli_options.py b/scripts/cli_options.py index 0ae91dd..29f121f 100644 --- a/scripts/cli_options.py +++ b/scripts/cli_options.py @@ -1,8 +1,10 @@ #! /usr/bin/env python -from __future__ import absolute_import, print_function +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * # noqa: F401 import random from optparse import OptionParser, OptionValueError -from ConfigParser import NoOptionError +from configparser import NoOptionError import jmclient.support @@ -58,7 +60,7 @@ def add_common_options(parser): parser.add_option( '--order-choose-algorithm', action='callback', - type=str, + type='string', default=jmclient.support.random_under_max_order_choose, callback=get_order_choose_algorithm, help="Set the algorithm to use for selecting orders from the order book.\n" @@ -113,7 +115,7 @@ def get_max_cj_fee_values(config, parser_options): except NoOptionError: pass - if len(filter(lambda x: x is None, fee_values)): + if any(x is None for x in fee_values): fee_values = prompt_user_for_cj_fee(*fee_values) return tuple(map(lambda j: fee_types[j](fee_values[j]), @@ -138,7 +140,7 @@ counterparties are selected.""" def prompt_user_value(m, val, check): while True: - data = raw_input(m) + data = input(m) if data == 'y': return val try: @@ -223,7 +225,7 @@ def get_tumbler_parser(): 'logs directory, with name TUMBLE.schedule or what is set in the ' 'schedulefile option.')) parser.add_option('--schedulefile', - type='str', + type='string', dest='schedulefile', default='TUMBLE.schedule', help=('Name of schedule file for tumbler, useful for restart, default ' @@ -372,7 +374,7 @@ def get_sendpayment_parser(): default=random.randint(4, 6)) parser.add_option('-S', '--schedule-file', - type='str', + type='string', dest='schedule', help='schedule file name; see file "sample-schedule-for-testnet" for explanation and example', default='') diff --git a/scripts/convert_old_wallet.py b/scripts/convert_old_wallet.py index 23bc5da..76bca6a 100644 --- a/scripts/convert_old_wallet.py +++ b/scripts/convert_old_wallet.py @@ -1,5 +1,7 @@ -#!/usr/bin/env python2 - +#!/usr/bin/env python +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * # noqa: F401 import argparse import json import os.path @@ -35,7 +37,7 @@ def decrypt_entropy_extension(enc_data, key): if data[-9] != b'\xff': raise ConvertException("Wrong password.") chunks = data.split(b'\xff') - if len(chunks) < 3 or data[-8:] != hexlify(double_sha256(chunks[1])[:4]): + if len(chunks) < 3 or data[-8:] != hexlify(double_sha256(chunks[1]).decode('ascii')[:4]): raise ConvertException("Wrong password.") return chunks[1] @@ -92,7 +94,7 @@ def new_wallet_from_data(data, file_name): for md in data['imported']: for privkey in data['imported'][md]: privkey += b'\x01' - wif = wif_compressed_privkey(hexlify(privkey)) + wif = wif_compressed_privkey(hexlify(privkey).decode('ascii')) wallet.import_private_key(md, wif) wallet.save() diff --git a/scripts/joinmarket-qt.py b/scripts/joinmarket-qt.py index dcc5663..ac8cc8d 100644 --- a/scripts/joinmarket-qt.py +++ b/scripts/joinmarket-qt.py @@ -1,5 +1,8 @@ #!/usr/bin/env python -from __future__ import print_function +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * +from future.utils import iteritems ''' Joinmarket GUI using PyQt for doing coinjoins. @@ -707,7 +710,7 @@ class SpendTab(QWidget): mbinfo.append(" ") mbinfo.append("Counterparties chosen:") mbinfo.append('Name, Order id, Coinjoin fee (sat.)') - for k, o in offers.iteritems(): + for k, o in iteritems(offers): if o['ordertype'] in ['swreloffer', 'reloffer']: display_fee = int(self.taker.cjamount * float(o['cjfee'])) - int(o['txfee']) @@ -1256,7 +1259,7 @@ class JMMainWindow(QMainWindow): mbtype='crit') return transaction.writerow(["%34s" % addr, pk]) - except (IOError, os.error), reason: + except (IOError, os.error) as reason: export_error_label = "JoinmarketQt was unable to produce a private key-export." JMQtMessageBox(None, export_error_label + "\n" + str(reason), diff --git a/scripts/joinmarketd.py b/scripts/joinmarketd.py index 7aa20b8..b392198 100644 --- a/scripts/joinmarketd.py +++ b/scripts/joinmarketd.py @@ -1,3 +1,6 @@ +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * # noqa: F401 import sys from twisted.internet import reactor from twisted.python.log import startLogging diff --git a/scripts/obwatch/ob-watcher.py b/scripts/obwatch/ob-watcher.py index 46ffef4..df37f4c 100644 --- a/scripts/obwatch/ob-watcher.py +++ b/scripts/obwatch/ob-watcher.py @@ -1,7 +1,11 @@ -from __future__ import absolute_import, print_function - -import BaseHTTPServer -import SimpleHTTPServer +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * +from future.utils import iteritems +from past.builtins import cmp +from functools import cmp_to_key + +import http.server import base64 import io import json @@ -10,7 +14,7 @@ import time import hashlib import os import sys -import urllib2 +from future.moves.urllib.parse import parse_qs from decimal import Decimal from optparse import OptionParser from twisted.internet import reactor @@ -35,8 +39,8 @@ from jmdaemon.protocol import * log = get_log() #Initial state: allow only SW offer types -swoffers = filter(lambda x: x[0:2] == 'sw', offername_list) -pkoffers = filter(lambda x: x[0:2] != 'sw', offername_list) +swoffers = list(filter(lambda x: x[0:2] == 'sw', offername_list)) +pkoffers = list(filter(lambda x: x[0:2] != 'sw', offername_list)) filtered_offername_list = swoffers shutdownform = '
' @@ -53,73 +57,10 @@ def calc_depth_data(db, value): pass -def create_depth_chart(db, cj_amount, args=None): - if args is None: - args = {} - rows = db.execute('SELECT * FROM orderbook;').fetchall() - sqlorders = [o for o in rows if o["ordertype"] in filtered_offername_list] - orderfees = sorted([calc_cj_fee(o['ordertype'], o['cjfee'], cj_amount) / 1e8 - for o in sqlorders - if o['minsize'] <= cj_amount <= o[ - 'maxsize']]) - - if len(orderfees) == 0: - return 'No orders at amount ' + str(cj_amount / 1e8) - fig = plt.figure() - scale = args.get("scale") - if (scale is not None) and (scale[0] == "log"): - orderfees = [float(fee) for fee in orderfees] - if orderfees[0] > 0: - ratio = orderfees[-1] / orderfees[0] - step = ratio ** 0.0333 # 1/30 - bins = [orderfees[0] * (step ** i) for i in range(30)] - else: - ratio = orderfees[-1] / 1e-8 # single satoshi placeholder - step = ratio ** 0.0333 # 1/30 - bins = [1e-8 * (step ** i) for i in range(30)] - bins[0] = orderfees[0] # replace placeholder - plt.xscale('log') - else: - bins = 30 - if len(orderfees) == 1: # these days we have liquidity, but just in case... - plt.hist(orderfees, bins, rwidth=0.8, range=(0, orderfees[0] * 2)) - else: - plt.hist(orderfees, bins, rwidth=0.8) - plt.grid() - plt.title('CoinJoin Orderbook Depth Chart for amount=' + str(cj_amount / - 1e8) + 'btc') - plt.xlabel('CoinJoin Fee / btc') - plt.ylabel('Frequency') - return get_graph_html(fig) - - -def create_size_histogram(db, args): - rows = db.execute('SELECT maxsize, ordertype FROM orderbook;').fetchall() - rows = [o for o in rows if o["ordertype"] in filtered_offername_list] - ordersizes = sorted([r['maxsize'] / 1e8 for r in rows]) - - fig = plt.figure() - scale = args.get("scale") - if (scale is not None) and (scale[0] == "log"): - ratio = ordersizes[-1] / ordersizes[0] - step = ratio ** 0.0333 # 1/30 - bins = [ordersizes[0] * (step ** i) for i in range(30)] - else: - bins = 30 - plt.hist(ordersizes, bins, histtype='bar', rwidth=0.8) - if bins is not 30: - fig.axes[0].set_xscale('log') - plt.grid() - plt.xlabel('Order sizes / btc') - plt.ylabel('Frequency') - return get_graph_html(fig) + ("
log scale" if - bins == 30 else "
linear") - - def get_graph_html(fig): imbuf = io.BytesIO() fig.savefig(imbuf, format='png') - b64 = base64.b64encode(imbuf.getvalue()) + b64 = base64.b64encode(imbuf.getvalue()).decode('utf-8') return '' @@ -151,36 +92,6 @@ def order_str(s, order, btc_unit, rel_unit): return str(s) -def create_orderbook_table(db, btc_unit, rel_unit): - result = '' - rows = db.execute('SELECT * FROM orderbook;').fetchall() - if not rows: - return 0, result - #print("len rows before filter: " + str(len(rows))) - rows = [o for o in rows if o["ordertype"] in filtered_offername_list] - order_keys_display = (('ordertype', ordertype_display), - ('counterparty', do_nothing), ('oid', order_str), - ('cjfee', cjfee_display), ('txfee', satoshi_to_unit), - ('minsize', satoshi_to_unit), - ('maxsize', satoshi_to_unit)) - - # somewhat complex sorting to sort by cjfee but with swabsoffers on top - - def orderby_cmp(x, y): - if x['ordertype'] == y['ordertype']: - return cmp(Decimal(x['cjfee']), Decimal(y['cjfee'])) - return cmp(offername_list.index(x['ordertype']), - offername_list.index(y['ordertype'])) - - for o in sorted(rows, cmp=orderby_cmp): - result += ' \n' - for key, displayer in order_keys_display: - result += ' ' + displayer(o[key], o, btc_unit, - rel_unit) + '\n' - result += ' \n' - return len(rows), result - - def create_table_heading(btc_unit, rel_unit): col = ' {1}\n' # .format(field,label) tableheading = '\n ' + ''.join( @@ -214,15 +125,19 @@ def create_choose_units_form(selected_btc, selected_rel): return choose_units_form -class OrderbookPageRequestHeader(SimpleHTTPServer.SimpleHTTPRequestHandler): +class OrderbookPageRequestHeader(http.server.SimpleHTTPRequestHandler): def __init__(self, request, client_address, base_server): self.taker = base_server.taker self.base_server = base_server - SimpleHTTPServer.SimpleHTTPRequestHandler.__init__( + http.server.SimpleHTTPRequestHandler.__init__( self, request, client_address, base_server) def create_orderbook_obj(self): - rows = self.taker.db.execute('SELECT * FROM orderbook;').fetchall() + try: + self.taker.dblock.acquire(True) + rows = self.taker.db.execute('SELECT * FROM orderbook;').fetchall() + finally: + self.taker.dblock.release() if not rows: return [] @@ -235,18 +150,124 @@ class OrderbookPageRequestHeader(SimpleHTTPServer.SimpleHTTPRequestHandler): result.append(o) return result + def create_depth_chart(self, cj_amount, args=None): + if args is None: + args = {} + try: + self.taker.dblock.acquire(True) + rows = self.taker.db.execute('SELECT * FROM orderbook;').fetchall() + finally: + self.taker.dblock.release() + sqlorders = [o for o in rows if o["ordertype"] in filtered_offername_list] + orderfees = sorted([calc_cj_fee(o['ordertype'], o['cjfee'], cj_amount) / 1e8 + for o in sqlorders + if o['minsize'] <= cj_amount <= o[ + 'maxsize']]) + + if len(orderfees) == 0: + return 'No orders at amount ' + str(cj_amount / 1e8) + fig = plt.figure() + scale = args.get("scale") + if (scale is not None) and (scale[0] == "log"): + orderfees = [float(fee) for fee in orderfees] + if orderfees[0] > 0: + ratio = orderfees[-1] / orderfees[0] + step = ratio ** 0.0333 # 1/30 + bins = [orderfees[0] * (step ** i) for i in range(30)] + else: + ratio = orderfees[-1] / 1e-8 # single satoshi placeholder + step = ratio ** 0.0333 # 1/30 + bins = [1e-8 * (step ** i) for i in range(30)] + bins[0] = orderfees[0] # replace placeholder + plt.xscale('log') + else: + bins = 30 + if len(orderfees) == 1: # these days we have liquidity, but just in case... + plt.hist(orderfees, bins, rwidth=0.8, range=(0, orderfees[0] * 2)) + else: + plt.hist(orderfees, bins, rwidth=0.8) + plt.grid() + plt.title('CoinJoin Orderbook Depth Chart for amount=' + str(cj_amount / + 1e8) + 'btc') + plt.xlabel('CoinJoin Fee / btc') + plt.ylabel('Frequency') + return get_graph_html(fig) + + def create_size_histogram(self, args): + try: + self.taker.dblock.acquire(True) + rows = self.taker.db.execute('SELECT maxsize, ordertype FROM orderbook;').fetchall() + finally: + self.taker.dblock.release() + rows = [o for o in rows if o["ordertype"] in filtered_offername_list] + ordersizes = sorted([r['maxsize'] / 1e8 for r in rows]) + + fig = plt.figure() + scale = args.get("scale") + if (scale is not None) and (scale[0] == "log"): + ratio = ordersizes[-1] / ordersizes[0] + step = ratio ** 0.0333 # 1/30 + bins = [ordersizes[0] * (step ** i) for i in range(30)] + else: + bins = 30 + plt.hist(ordersizes, bins, histtype='bar', rwidth=0.8) + if bins is not 30: + fig.axes[0].set_xscale('log') + plt.grid() + plt.xlabel('Order sizes / btc') + plt.ylabel('Frequency') + return get_graph_html(fig) + ("
log scale" if + bins == 30 else "
linear") + + def create_orderbook_table(self, btc_unit, rel_unit): + result = '' + try: + self.taker.dblock.acquire(True) + rows = self.taker.db.execute('SELECT * FROM orderbook;').fetchall() + finally: + self.taker.dblock.release() + if not rows: + return 0, result + #print("len rows before filter: " + str(len(rows))) + rows = [o for o in rows if o["ordertype"] in filtered_offername_list] + order_keys_display = (('ordertype', ordertype_display), + ('counterparty', do_nothing), ('oid', order_str), + ('cjfee', cjfee_display), ('txfee', satoshi_to_unit), + ('minsize', satoshi_to_unit), + ('maxsize', satoshi_to_unit)) + + # somewhat complex sorting to sort by cjfee but with swabsoffers on top + + def orderby_cmp(x, y): + if x['ordertype'] == y['ordertype']: + return cmp(Decimal(x['cjfee']), Decimal(y['cjfee'])) + return cmp(offername_list.index(x['ordertype']), + offername_list.index(y['ordertype'])) + + for o in sorted(rows, key=cmp_to_key(orderby_cmp)): + result += ' \n' + for key, displayer in order_keys_display: + result += ' \n' + result += ' \n' + return len(rows), result + def get_counterparty_count(self): - counterparties = self.taker.db.execute( - 'SELECT DISTINCT counterparty FROM orderbook WHERE ordertype=? OR ordertype=?;', - filtered_offername_list).fetchall() + try: + self.taker.dblock.acquire(True) + counterparties = self.taker.db.execute( + 'SELECT DISTINCT counterparty FROM orderbook WHERE ordertype=? OR ordertype=?;', + filtered_offername_list).fetchall() + finally: + self.taker.dblock.release() return str(len(counterparties)) def do_GET(self): - # SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self) + # http.server.SimpleHTTPRequestHandler.do_GET(self) # print 'httpd received ' + self.path + ' request' self.path, query = self.path.split('?', 1) if '?' in self.path else ( self.path, '') - args = urllib2.urlparse.parse_qs(query) + args = parse_qs(query) pages = ['/', '/ordersize', '/depth', '/orderbook.json'] if self.path not in pages: return @@ -266,8 +287,8 @@ class OrderbookPageRequestHeader(SimpleHTTPServer.SimpleHTTPRequestHandler): btc_unit = sorted_units[0] if rel_unit not in sorted_rel_units: rel_unit = sorted_rel_units[0] - ordercount, ordertable = create_orderbook_table( - self.taker.db, btc_unit, rel_unit) + ordercount, ordertable = self.create_orderbook_table( + btc_unit, rel_unit) choose_units_form = create_choose_units_form(btc_unit, rel_unit) table_heading = create_table_heading(btc_unit, rel_unit) replacements = { @@ -285,13 +306,13 @@ class OrderbookPageRequestHeader(SimpleHTTPServer.SimpleHTTPRequestHandler): 'PAGETITLE': 'JoinMarket Browser Interface', 'MAINHEADING': 'Order Sizes', 'SECONDHEADING': 'Order Size Histogram' + alert_msg, - 'MAINBODY': create_size_histogram(self.taker.db, args) + 'MAINBODY': self.create_size_histogram(args) } elif self.path.startswith('/depth'): # if self.path[6] == '?': # quantity = cj_amounts = [10 ** cja for cja in range(4, 12, 1)] - mainbody = [create_depth_chart(self.taker.db, cja, args) \ + mainbody = [self.create_depth_chart(cja, args) \ for cja in cj_amounts] + \ ["
linear" if args.get("scale") \ else "
log scale"] @@ -305,7 +326,7 @@ class OrderbookPageRequestHeader(SimpleHTTPServer.SimpleHTTPRequestHandler): replacements = {} orderbook_fmt = json.dumps(self.create_orderbook_obj()) orderbook_page = orderbook_fmt - for key, rep in replacements.iteritems(): + for key, rep in iteritems(replacements): orderbook_page = orderbook_page.replace(key, rep) self.send_response(200) if self.path.endswith('.json'): @@ -314,7 +335,7 @@ class OrderbookPageRequestHeader(SimpleHTTPServer.SimpleHTTPRequestHandler): self.send_header('Content-Type', 'text/html') self.send_header('Content-Length', len(orderbook_page)) self.end_headers() - self.wfile.write(orderbook_page) + self.wfile.write(orderbook_page.encode('utf-8')) def do_POST(self): global filtered_offername_list @@ -351,7 +372,7 @@ class HTTPDThread(threading.Thread): def run(self): # hostport = ('localhost', 62601) - httpd = BaseHTTPServer.HTTPServer(self.hostport, + httpd = http.server.HTTPServer(self.hostport, OrderbookPageRequestHeader) httpd.taker = self.taker print('\nstarted http server, visit http://{0}:{1}/\n'.format( diff --git a/scripts/qtsupport.py b/scripts/qtsupport.py index 36b8060..45caa2a 100644 --- a/scripts/qtsupport.py +++ b/scripts/qtsupport.py @@ -1,5 +1,7 @@ #!/usr/bin/env python -from __future__ import print_function +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * ''' Qt files for the wizard for initiating a tumbler run. diff --git a/scripts/sendpayment.py b/scripts/sendpayment.py index 03e0c1e..962fbd1 100644 --- a/scripts/sendpayment.py +++ b/scripts/sendpayment.py @@ -1,5 +1,7 @@ #! /usr/bin/env python -from __future__ import absolute_import, print_function +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * # noqa: F401 """ A sample implementation of a single coinjoin script, @@ -34,7 +36,7 @@ def pick_order(orders, n): #pragma: no cover return orders[0] while pickedOrderIndex == -1: try: - pickedOrderIndex = int(raw_input('Pick an order between 0 and ' + + pickedOrderIndex = int(input('Pick an order between 0 and ' + str(i) + ': ')) except ValueError: pickedOrderIndex = -1 @@ -153,7 +155,7 @@ def main(): log.info('WARNING ' * 6) log.info('\n'.join(['=' * 60] * 3)) if not options.answeryes: - if raw_input('send with these orders? (y/n):')[0] != 'y': + if input('send with these orders? (y/n):')[0] != 'y': return False return True diff --git a/scripts/sendtomany.py b/scripts/sendtomany.py index d00120f..8bc7c39 100644 --- a/scripts/sendtomany.py +++ b/scripts/sendtomany.py @@ -1,5 +1,7 @@ #! /usr/bin/env python -from __future__ import absolute_import, print_function +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * # noqa: F401 """A simple command line tool to create a bunch of utxos from one (thus giving more potential commitments for a Joinmarket user, although of course it may be useful @@ -93,7 +95,7 @@ def main(): if len(args) < 2: quit(parser, 'Invalid syntax') u = args[0] - priv = raw_input( + priv = input( 'input private key for ' + u + ', in WIF compressed format : ') u, priv = get_utxo_info(','.join([u, priv])) if not u: @@ -109,7 +111,7 @@ def main(): log.debug("Got signed transaction:\n" + txsigned) log.debug("Deserialized:") log.debug(pformat(btc.deserialize(txsigned))) - if raw_input('Would you like to push to the network? (y/n):')[0] != 'y': + if input('Would you like to push to the network? (y/n):')[0] != 'y': log.info("You chose not to broadcast the transaction, quitting.") return jm_single().bc_interface.pushtx(txsigned) diff --git a/scripts/tumbler.py b/scripts/tumbler.py index 79c5813..de661ca 100644 --- a/scripts/tumbler.py +++ b/scripts/tumbler.py @@ -1,5 +1,7 @@ #! /usr/bin/env python -from __future__ import absolute_import, print_function +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * # noqa: F401 import sys from twisted.internet import reactor diff --git a/scripts/wallet-tool.py b/scripts/wallet-tool.py index 2e50513..eb28254 100644 --- a/scripts/wallet-tool.py +++ b/scripts/wallet-tool.py @@ -1,4 +1,6 @@ -from __future__ import absolute_import, print_function +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * # noqa: F401 from jmclient import load_program_config, wallet_tool_main diff --git a/scripts/yg-privacyenhanced.py b/scripts/yg-privacyenhanced.py index a25f844..57a499e 100644 --- a/scripts/yg-privacyenhanced.py +++ b/scripts/yg-privacyenhanced.py @@ -1,5 +1,8 @@ #! /usr/bin/env python -from __future__ import absolute_import, print_function +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * # noqa: F401 +from future.utils import iteritems import random @@ -44,7 +47,7 @@ class YieldGeneratorPrivacyEnhanced(YieldGeneratorBasic): f = self.cjfee_r elif ordertype == 'swabsoffer': f = str(self.txfee + self.cjfee_a) - mix_balance = dict([(m, b) for m, b in mix_balance.iteritems() + mix_balance = dict([(m, b) for m, b in iteritems(mix_balance) if b > self.minsize]) if len(mix_balance) == 0: jlog.error('You do not have the minimum required amount of coins' diff --git a/scripts/yield-generator-basic.py b/scripts/yield-generator-basic.py index 36cf52f..309f367 100644 --- a/scripts/yield-generator-basic.py +++ b/scripts/yield-generator-basic.py @@ -1,5 +1,7 @@ #! /usr/bin/env python -from __future__ import absolute_import, print_function +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * # noqa: F401 from jmclient import YieldGeneratorBasic, ygmain, get_log diff --git a/test/Dockerfiles/bionic.Dockerfile b/test/Dockerfiles/bionic-py2.Dockerfile similarity index 100% rename from test/Dockerfiles/bionic.Dockerfile rename to test/Dockerfiles/bionic-py2.Dockerfile diff --git a/test/Dockerfiles/bionic-py3.Dockerfile b/test/Dockerfiles/bionic-py3.Dockerfile new file mode 100755 index 0000000..38e0147 --- /dev/null +++ b/test/Dockerfiles/bionic-py3.Dockerfile @@ -0,0 +1,37 @@ +FROM ubuntu:bionic +SHELL ["/bin/bash", "-c"] + +# dependencies +RUN apt-get update +RUN apt-get install -y build-essential +RUN apt-get install -y \ + automake pkg-config libtool libgmp-dev +RUN apt-get install -y \ + python3-dev python3-pip python-virtualenv python3-pyqt4 python3-sip + +# curl is a better tool +RUN apt-get install -y curl + +RUN useradd --home-dir /home/chaum --create-home --shell /bin/bash --skel /etc/skel/ chaum +ARG core_version +ARG core_dist +ARG repo_name +RUN mkdir -p /home/chaum/${repo_name} +COPY ${repo_name} /home/chaum/${repo_name} +RUN ls -la /home/chaum +RUN chown -R chaum:chaum /home/chaum/${repo_name} +USER chaum + +# copy node software from the host and install +WORKDIR /home/chaum +RUN ls -la . +RUN ls -la ${repo_name} +RUN ls -la ${repo_name}/deps/cache +RUN tar xaf ./${repo_name}/deps/cache/${core_dist} -C /home/chaum +ENV PATH "/home/chaum/bitcoin-${core_version}/bin:${PATH}" +RUN bitcoind --version | head -1 + +# install script +WORKDIR ${repo_name} +RUN ./install.sh --python=python3 +RUN source jmvenv/bin/activate && ./test/run_tests.sh diff --git a/test/Dockerfiles/centos7.Dockerfile b/test/Dockerfiles/centos7-py2.Dockerfile similarity index 100% rename from test/Dockerfiles/centos7.Dockerfile rename to test/Dockerfiles/centos7-py2.Dockerfile diff --git a/test/Dockerfiles/fedora27.Dockerfile b/test/Dockerfiles/fedora27-py2.Dockerfile similarity index 100% rename from test/Dockerfiles/fedora27.Dockerfile rename to test/Dockerfiles/fedora27-py2.Dockerfile diff --git a/test/Dockerfiles/fedora27-py3.Dockerfile b/test/Dockerfiles/fedora27-py3.Dockerfile new file mode 100755 index 0000000..233e480 --- /dev/null +++ b/test/Dockerfiles/fedora27-py3.Dockerfile @@ -0,0 +1,36 @@ +FROM fedora:27 +SHELL ["/bin/bash", "-c"] + +# dependencies +RUN dnf -y groups install 'Development tools' +RUN dnf -y install \ + autoconf libtool pkgconfig \ + python3-devel python3-pip python-virtualenv gmp-devel + +# needed for build time +# https://stackoverflow.com/questions/34624428/g-error-usr-lib-rpm-redhat-redhat-hardened-cc1-no-such-file-or-directory +RUN dnf -y install redhat-rpm-config + +RUN useradd --home-dir /home/chaum --create-home --shell /bin/bash --skel /etc/skel/ chaum +ARG core_version +ARG core_dist +ARG repo_name +RUN mkdir -p /home/chaum/${repo_name} +COPY ${repo_name} /home/chaum/${repo_name} +RUN ls -la /home/chaum +RUN chown -R chaum:chaum /home/chaum/${repo_name} +USER chaum + +# copy node software from the host and install +WORKDIR /home/chaum +RUN ls -la . +RUN ls -la ${repo_name} +RUN ls -la ${repo_name}/deps/cache +RUN tar xaf ./${repo_name}/deps/cache/${core_dist} -C /home/chaum +ENV PATH "/home/chaum/bitcoin-${core_version}/bin:${PATH}" +RUN bitcoind --version | head -1 + +# install script +WORKDIR ${repo_name} +RUN ./install.sh --python=python3 +RUN source jmvenv/bin/activate && ./test/run_tests.sh diff --git a/test/Dockerfiles/stretch.Dockerfile b/test/Dockerfiles/stretch-py2.Dockerfile similarity index 100% rename from test/Dockerfiles/stretch.Dockerfile rename to test/Dockerfiles/stretch-py2.Dockerfile diff --git a/test/Dockerfiles/stretch-py3.Dockerfile b/test/Dockerfiles/stretch-py3.Dockerfile new file mode 100755 index 0000000..d979a99 --- /dev/null +++ b/test/Dockerfiles/stretch-py3.Dockerfile @@ -0,0 +1,37 @@ +FROM debian:stretch +SHELL ["/bin/bash", "-c"] + +# dependencies +RUN apt-get update +RUN apt-get install -y build-essential +RUN apt-get install -y \ + automake pkg-config libtool libgmp-dev +RUN apt-get install -y \ + python3-dev python3-pip python-virtualenv python3-pyqt4 python3-sip + +# curl is a better tool +RUN apt-get install -y curl + +RUN useradd --home-dir /home/chaum --create-home --shell /bin/bash --skel /etc/skel/ chaum +ARG core_version +ARG core_dist +ARG repo_name +RUN mkdir -p /home/chaum/${repo_name} +COPY ${repo_name} /home/chaum/${repo_name} +RUN ls -la /home/chaum +RUN chown -R chaum:chaum /home/chaum/${repo_name} +USER chaum + +# copy node software from the host and install +WORKDIR /home/chaum +RUN ls -la . +RUN ls -la ${repo_name} +RUN ls -la ${repo_name}/deps/cache +RUN tar xaf ./${repo_name}/deps/cache/${core_dist} -C /home/chaum +ENV PATH "/home/chaum/bitcoin-${core_version}/bin:${PATH}" +RUN bitcoind --version | head -1 + +# install script +WORKDIR ${repo_name} +RUN ./install.sh --python=python3 +RUN source jmvenv/bin/activate && ./test/run_tests.sh diff --git a/test/Dockerfiles/xenial.Dockerfile b/test/Dockerfiles/xenial-py2.Dockerfile similarity index 100% rename from test/Dockerfiles/xenial.Dockerfile rename to test/Dockerfiles/xenial-py2.Dockerfile diff --git a/test/Dockerfiles/xenial-py3.Dockerfile b/test/Dockerfiles/xenial-py3.Dockerfile new file mode 100755 index 0000000..d53b379 --- /dev/null +++ b/test/Dockerfiles/xenial-py3.Dockerfile @@ -0,0 +1,37 @@ +FROM ubuntu:xenial +SHELL ["/bin/bash", "-c"] + +# dependencies +RUN apt-get update +RUN apt-get install -y build-essential +RUN apt-get install -y \ + automake pkg-config libtool libgmp-dev +RUN apt-get install -y \ + python3-dev python3-pip python-virtualenv python3-pyqt4 python3-sip + +# curl is a better tool +RUN apt-get install -y curl + +RUN useradd --home-dir /home/chaum --create-home --shell /bin/bash --skel /etc/skel/ chaum +ARG core_version +ARG core_dist +ARG repo_name +RUN mkdir -p /home/chaum/${repo_name} +COPY ${repo_name} /home/chaum/${repo_name} +RUN ls -la /home/chaum +RUN chown -R chaum:chaum /home/chaum/${repo_name} +USER chaum + +# copy node software from the host and install +WORKDIR /home/chaum +RUN ls -la . +RUN ls -la ${repo_name} +RUN ls -la ${repo_name}/deps/cache +RUN tar xaf ./${repo_name}/deps/cache/${core_dist} -C /home/chaum +ENV PATH "/home/chaum/bitcoin-${core_version}/bin:${PATH}" +RUN bitcoind --version | head -1 + +# install script +WORKDIR ${repo_name} +RUN ./install.sh --python=python3 +RUN source jmvenv/bin/activate && ./test/run_tests.sh diff --git a/test/common.py b/test/common.py index 95a3978..2cae456 100644 --- a/test/common.py +++ b/test/common.py @@ -1,5 +1,7 @@ #! /usr/bin/env python -from __future__ import absolute_import +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * # noqa: F401 '''Some helper functions for testing''' import sys @@ -48,7 +50,7 @@ def make_sign_and_push(ins_full, priv = binascii.unhexlify(priv) tx = btc.sign(tx, index, priv, hashcode=hashcode) #pushtx returns False on any error - print btc.deserialize(tx) + print(btc.deserialize(tx)) push_succeed = jm_single().bc_interface.pushtx(tx) if push_succeed: return btc.txhash(tx) @@ -96,7 +98,7 @@ def make_wallets(n, w = open_test_wallet_maybe(seeds[i], seeds[i], mixdepths - 1, test_wallet_cls=walletclass) - wallets[i + start_index] = {'seed': seeds[i], + wallets[i + start_index] = {'seed': seeds[i].decode('ascii'), 'wallet': w} for j in range(mixdepths): for k in range(wallet_structures[i][j]): diff --git a/test/test_full_coinjoin.py b/test/test_full_coinjoin.py index e389443..e332461 100644 --- a/test/test_full_coinjoin.py +++ b/test/test_full_coinjoin.py @@ -1,5 +1,7 @@ #! /usr/bin/env python -from __future__ import absolute_import, print_function +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * # noqa: F401 '''Runs a full joinmarket pit (using `nirc` miniircd servers, with `nirc` options specified as an option to pytest),in bitcoin regtest mode with 3 maker bots and 1 taker bot, diff --git a/test/test_segwit.py b/test/test_segwit.py index b590500..b452e7f 100644 --- a/test/test_segwit.py +++ b/test/test_segwit.py @@ -1,5 +1,7 @@ #! /usr/bin/env python -from __future__ import absolute_import +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * # noqa: F401 '''Test creation of segwit transactions.''' import binascii @@ -20,7 +22,7 @@ def test_segwit_valid_txs(setup_segwit): if len(j) < 2: continue deserialized_tx = btc.deserialize(str(j[1])) - print pformat(deserialized_tx) + print(pformat(deserialized_tx)) assert btc.serialize(deserialized_tx) == str(j[1]) #TODO use bcinterface to decoderawtransaction #and compare the json values @@ -93,7 +95,7 @@ def test_spend_p2sh_p2wpkh_multi(setup_segwit, wallet_structure, in_amt, amount, # FIXME: encoding mess, mktx should accept binary input formats tx_ins = [] for i, (txid, data) in sorted(all_ins.items(), key=lambda x: x[0]): - tx_ins.append('{}:{}'.format(binascii.hexlify(txid[0]), txid[1])) + tx_ins.append('{}:{}'.format(binascii.hexlify(txid[0]).decode('ascii'), txid[1])) # create outputs FEE = 50000 @@ -104,9 +106,9 @@ def test_spend_p2sh_p2wpkh_multi(setup_segwit, wallet_structure, in_amt, amount, change_amt = total_amt_in_sat - amount - FEE tx_outs = [ - {'script': binascii.hexlify(cj_script), + {'script': binascii.hexlify(cj_script).decode('ascii'), 'value': amount}, - {'script': binascii.hexlify(change_script), + {'script': binascii.hexlify(change_script).decode('ascii'), 'value': change_amt}] tx = btc.deserialize(btc.mktx(tx_ins, tx_outs)) binarize_tx(tx) @@ -133,7 +135,7 @@ def test_spend_p2sh_p2wpkh_multi(setup_segwit, wallet_structure, in_amt, amount, print(tx) # push and verify - txid = jm_single().bc_interface.pushtx(binascii.hexlify(btc.serialize(tx))) + txid = jm_single().bc_interface.pushtx(binascii.hexlify(btc.serialize(tx)).decode('ascii')) assert txid balances = jm_single().bc_interface.get_received_by_addr( diff --git a/test/ygrunner.py b/test/ygrunner.py index 8a948ae..6e71021 100644 --- a/test/ygrunner.py +++ b/test/ygrunner.py @@ -1,5 +1,7 @@ #! /usr/bin/env python -from __future__ import absolute_import, print_function +from __future__ import (absolute_import, division, + print_function, unicode_literals) +from builtins import * # noqa: F401 '''Creates wallets and yield generators in regtest. Provides seed for joinmarket-qt test. This should be run via pytest, even though
' + displayer(o[key], o, btc_unit, + rel_unit) + '