Browse Source

Convert jmclient to py3 style

master
James Hilliard 7 years ago
parent
commit
6dc3504f8a
  1. 3
      .gitignore
  2. 22
      .travis.yml
  3. 36
      install.sh
  4. 4
      jmbase/jmbase/__init__.py
  5. 59
      jmbase/jmbase/bigstring.py
  6. 4
      jmbase/jmbase/commands.py
  7. 25
      jmbase/jmbase/support.py
  8. 41
      jmbitcoin/jmbitcoin/secp256k1_main.py
  9. 68
      jmbitcoin/jmbitcoin/secp256k1_transaction.py
  10. 4
      jmbitcoin/test/test_ecc_signing.py
  11. 2
      jmbitcoin/test/test_keys.py
  12. 2
      jmbitcoin/test/test_main.py
  13. 8
      jmclient/jmclient/__init__.py
  14. 12
      jmclient/jmclient/blockchaininterface.py
  15. 9
      jmclient/jmclient/btc.py
  16. 18
      jmclient/jmclient/client_protocol.py
  17. 4
      jmclient/jmclient/commitment_utils.py
  18. 16
      jmclient/jmclient/configure.py
  19. 14
      jmclient/jmclient/cryptoengine.py
  20. 3
      jmclient/jmclient/electrum_data.py
  21. 10
      jmclient/jmclient/electruminterface.py
  22. 35
      jmclient/jmclient/jsonrpc.py
  23. 12
      jmclient/jmclient/maker.py
  24. 11
      jmclient/jmclient/old_mnemonic.py
  25. 5
      jmclient/jmclient/output.py
  26. 29
      jmclient/jmclient/podle.py
  27. 7
      jmclient/jmclient/schedule.py
  28. 3
      jmclient/jmclient/slowaes.py
  29. 9
      jmclient/jmclient/storage.py
  30. 11
      jmclient/jmclient/support.py
  31. 43
      jmclient/jmclient/taker.py
  32. 17
      jmclient/jmclient/taker_utils.py
  33. 31
      jmclient/jmclient/wallet.py
  34. 38
      jmclient/jmclient/wallet_utils.py
  35. 9
      jmclient/jmclient/yieldgenerator.py
  36. 2
      jmclient/setup.py
  37. 10
      jmclient/test/commontest.py
  38. 3
      jmclient/test/taker_test_data.py
  39. 13
      jmclient/test/test_aes.py
  40. 4
      jmclient/test/test_argon2.py
  41. 4
      jmclient/test/test_blockchaininterface.py
  42. 4
      jmclient/test/test_client_protocol.py
  43. 4
      jmclient/test/test_coinjoin.py
  44. 4
      jmclient/test/test_commitment_utils.py
  45. 4
      jmclient/test/test_configure.py
  46. 26
      jmclient/test/test_maker.py
  47. 9
      jmclient/test/test_mnemonic.py
  48. 21
      jmclient/test/test_podle.py
  49. 6
      jmclient/test/test_schedule.py
  50. 4
      jmclient/test/test_storage.py
  51. 4
      jmclient/test/test_support.py
  52. 26
      jmclient/test/test_taker.py
  53. 25
      jmclient/test/test_tx_creation.py
  54. 16
      jmclient/test/test_utxomanager.py
  55. 4
      jmclient/test/test_valid_addresses.py
  56. 22
      jmclient/test/test_wallet.py
  57. 12
      jmclient/test/test_wallets.py
  58. 22
      jmdaemon/jmdaemon/daemon_protocol.py
  59. 6
      jmdaemon/jmdaemon/enc_wrapper.py
  60. 6
      jmdaemon/jmdaemon/irc.py
  61. 18
      jmdaemon/jmdaemon/message_channel.py
  62. 40
      jmdaemon/jmdaemon/orderbookwatch.py
  63. 8
      jmdaemon/jmdaemon/protocol.py
  64. 20
      jmdaemon/test/test_daemon_protocol.py
  65. 8
      jmdaemon/test/test_enc_wrapper.py
  66. 27
      jmdaemon/test/test_message_channel.py
  67. 27
      scripts/add-utxo.py
  68. 16
      scripts/cli_options.py
  69. 10
      scripts/convert_old_wallet.py
  70. 9
      scripts/joinmarket-qt.py
  71. 3
      scripts/joinmarketd.py
  72. 253
      scripts/obwatch/ob-watcher.py
  73. 4
      scripts/qtsupport.py
  74. 8
      scripts/sendpayment.py
  75. 8
      scripts/sendtomany.py
  76. 4
      scripts/tumbler.py
  77. 4
      scripts/wallet-tool.py
  78. 7
      scripts/yg-privacyenhanced.py
  79. 4
      scripts/yield-generator-basic.py
  80. 0
      test/Dockerfiles/bionic-py2.Dockerfile
  81. 37
      test/Dockerfiles/bionic-py3.Dockerfile
  82. 0
      test/Dockerfiles/centos7-py2.Dockerfile
  83. 0
      test/Dockerfiles/fedora27-py2.Dockerfile
  84. 36
      test/Dockerfiles/fedora27-py3.Dockerfile
  85. 0
      test/Dockerfiles/stretch-py2.Dockerfile
  86. 37
      test/Dockerfiles/stretch-py3.Dockerfile
  87. 0
      test/Dockerfiles/xenial-py2.Dockerfile
  88. 37
      test/Dockerfiles/xenial-py3.Dockerfile
  89. 8
      test/common.py
  90. 4
      test/test_full_coinjoin.py
  91. 14
      test/test_segwit.py
  92. 4
      test/ygrunner.py

3
.gitignore vendored

@ -6,6 +6,8 @@
blockchain.cache blockchain.cache
env env
htmlcov/ htmlcov/
joinmarket.cfg
joinmarket.cfg.bak
scripts/joinmarket.cfg scripts/joinmarket.cfg
scripts/cmttools/commitments.json scripts/cmttools/commitments.json
scripts/blacklist scripts/blacklist
@ -13,6 +15,7 @@ alicekey
alicepubkey alicepubkey
bobkey bobkey
commitments_debug.txt commitments_debug.txt
dummyext
deps/ deps/
jmvenv/ jmvenv/
logs/ logs/

22
.travis.yml

@ -16,19 +16,31 @@ matrix:
- python-qt4 python-sip - python-qt4 python-sip
- os: linux - os: linux
services: docker services: docker
env: DOCKER_IMG_JM=xenial env: DOCKER_IMG_JM=xenial-py2
- os: linux - os: linux
services: docker services: docker
env: DOCKER_IMG_JM=bionic env: DOCKER_IMG_JM=xenial-py3
- os: linux - os: linux
services: docker services: docker
env: DOCKER_IMG_JM=stretch env: DOCKER_IMG_JM=bionic-py2
- os: linux - os: linux
services: docker services: docker
env: DOCKER_IMG_JM=centos7 env: DOCKER_IMG_JM=bionic-py3
- os: linux - os: linux
services: docker 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: before_install:
- do_on(){ if [ "$TRAVIS_OS_NAME" = "$1" ]; then shift; $@ ; fi; } - do_on(){ if [ "$TRAVIS_OS_NAME" = "$1" ]; then shift; $@ ; fi; }
- on_host(){ if [ -z "$DOCKER_IMG_JM" ]; then $@ ; fi; } - on_host(){ if [ -z "$DOCKER_IMG_JM" ]; then $@ ; fi; }

36
install.sh

@ -14,10 +14,18 @@ sha256_verify ()
deps_install () deps_install ()
{ {
if [[ ${install_os} == 'debian' ]]; then 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 if is_python3; then
return 0 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 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 fi
else else
echo "OS can not be determined. Trying to build." 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 ( 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 () install_get_os ()
{ {
if os_is_deb; then if os_is_deb; then
@ -372,8 +392,14 @@ install_get_os ()
qt_deps_install () qt_deps_install ()
{ {
if [[ ${install_os} == 'debian' ]]; then if [[ ${install_os} == 'debian' ]]; then
if deb_deps_install "python-qt4 python-sip"; then if is_python3; then
return 0; 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 fi
else else
return 1 return 1

4
jmbase/jmbase/__init__.py

@ -3,7 +3,7 @@ from __future__ import (absolute_import, division,
from builtins import * from builtins import *
from .support import (get_log, chunks, debug_silence, debug_dump_object, 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) set_logging_level)
from commands import * from .commands import *

59
jmbase/jmbase/bigstring.py

@ -1,19 +1,25 @@
from __future__ import (absolute_import, division, from __future__ import (absolute_import, division,
print_function, unicode_literals) print_function, unicode_literals)
from builtins import * # noqa: F401 from builtins import * # noqa: F401
try: import itertools
from cStringIO import StringIO
except ImportError: #pragma: no cover
from StringIO import StringIO #pragma: no cover
from itertools import count
from twisted.protocols import amp 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. A byte-string amp.Argument with no 65,535 length limit.
Each value for a key/value pair in an AMP box may not 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 exceed 65,535 bytes in length. So if we *really* want to
send potentially larger values, this class will implicitly 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. names by prefixing this Argument's key name to a counter.
""" """
def fromBox(self, name, strings, objects, proto): def fromBox(self, name, strings, objects, proto):
value = StringIO() nk = amp._wireNameToPythonIdentifier(name)
value.write(strings.get(name)) StringList.fromBox(self, name, strings, objects, proto)
for counter in count(2): objects[nk] = b''.join((elem) for elem in objects[nk]).decode('utf-8')
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
def toBox(self, name, strings, objects, proto): def toBox(self, name, strings, objects, proto):
value = StringIO(self.fromvalue(objects[name])) obj = self.retrieve(objects, amp._wireNameToPythonIdentifier(name), proto).encode('utf-8')
firstChunk = value.read(CHUNK_MAX) objects[name] = split_string(obj, amp.MAX_VALUE_LENGTH)
strings[name] = firstChunk StringList.toBox(self, name, strings, objects, proto)
counter = 2
while True: class BigUnicode(BigString):
nextChunk = value.read(CHUNK_MAX) def toString(self, inObject):
if not nextChunk: return BigString.toString(self, inObject.encode('utf-8'))
break
strings["{}.{}".format(name, counter).encode('ascii')] = nextChunk def fromString(self, inString):
counter += 1 return BigString.fromString(self, inString).decode('utf-8')
def fromvalue(self, value):
return value

4
jmbase/jmbase/commands.py

@ -7,7 +7,7 @@ messaging protocol (*not* Joinmarket p2p protocol).
Used for AMP asynchronous messages. Used for AMP asynchronous messages.
""" """
from twisted.protocols.amp import Boolean, Command, Integer, Unicode from twisted.protocols.amp import Boolean, Command, Integer, Unicode
from .bigstring import BigString from .bigstring import BigUnicode
class DaemonNotReady(Exception): class DaemonNotReady(Exception):
pass pass
@ -185,7 +185,7 @@ class JMOffers(JMCommand):
"""Return the entire contents of the """Return the entire contents of the
orderbook to TAKER, as a json-ified dict. orderbook to TAKER, as a json-ified dict.
""" """
arguments = [(b'orderbook', BigString())] arguments = [(b'orderbook', BigUnicode())]
class JMFillResponse(JMCommand): class JMFillResponse(JMCommand):
"""Returns ioauth data from MAKER if successful. """Returns ioauth data from MAKER if successful.

25
jmbase/jmbase/support.py

@ -1,6 +1,7 @@
from __future__ import (absolute_import, division, from __future__ import (absolute_import, division,
print_function, unicode_literals) print_function, unicode_literals)
from builtins import * # noqa: F401 from builtins import * # noqa: F401
from future.utils import iteritems
import sys import sys
@ -53,13 +54,16 @@ def chunks(d, n):
return [d[x:x + n] for x in range(0, len(d), n)] return [d[x:x + n] for x in range(0, len(d), n)]
def get_password(msg): #pragma: no cover 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): def debug_dump_object(obj, skip_fields=None):
if skip_fields is None: if skip_fields is None:
skip_fields = [] skip_fields = []
log.debug('Class debug dump, name:' + obj.__class__.__name__) 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: if k in skip_fields:
continue continue
if k == 'password' or k == 'given_password': if k == 'password' or k == 'given_password':
@ -72,20 +76,3 @@ def debug_dump_object(obj, skip_fields=None):
log.debug(pprint.pformat(v)) log.debug(pprint.pformat(v))
else: else:
log.debug(str(v)) 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

41
jmbitcoin/jmbitcoin/secp256k1_main.py

@ -2,7 +2,7 @@
from __future__ import (absolute_import, division, from __future__ import (absolute_import, division,
print_function, unicode_literals) print_function, unicode_literals)
from builtins import * # noqa: F401 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 binascii
import hashlib import hashlib
import sys import sys
@ -53,11 +53,8 @@ def bin_to_b58check(inp, magicbyte=b'\x00'):
checksum = bin_dbl_sha256(inp_fmtd)[:4] checksum = bin_dbl_sha256(inp_fmtd)[:4]
return b58encode(inp_fmtd + checksum) return b58encode(inp_fmtd + checksum)
def bytes_to_hex_string(b):
return b.encode('hex')
def safe_from_hex(s): def safe_from_hex(s):
return s.decode('hex') return binascii.unhexlify(s)
def from_int_to_byte(a): def from_int_to_byte(a):
return struct.pack(b'B', 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') return a if isinstance(a, bytes) else bytes(a, 'utf-8')
def safe_hexlify(a): def safe_hexlify(a):
return str(binascii.hexlify(a), 'utf-8') return binascii.hexlify(a).decode('ascii')
class SerializationError(Exception): class SerializationError(Exception):
"""Base class for serialization errors""" """Base class for serialization errors"""
@ -109,7 +106,7 @@ def b58encode(b):
"""Encode bytes to a base58-encoded string""" """Encode bytes to a base58-encoded string"""
# Convert big-endian bytes to integer # 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 # Divide that integer into bas58
res = [] res = []
@ -120,7 +117,7 @@ def b58encode(b):
# Encode leading zeros as base58 zeros # Encode leading zeros as base58 zeros
czero = b'\x00' czero = b'\x00'
if sys.version > '3': if sys.version_info >= (3,0):
# In Python3 indexing a bytes returns numbers, not characters. # In Python3 indexing a bytes returns numbers, not characters.
czero = 0 czero = 0
pad = 0 pad = 0
@ -252,6 +249,8 @@ def bin_hash160(string):
return hashlib.new('ripemd160', intermed).digest() return hashlib.new('ripemd160', intermed).digest()
def hash160(string): def hash160(string):
if not isinstance(string, bytes):
string = string.encode('utf-8')
return safe_hexlify(bin_hash160(string)) return safe_hexlify(bin_hash160(string))
def bin_sha256(string): def bin_sha256(string):
@ -260,9 +259,11 @@ def bin_sha256(string):
return hashlib.sha256(binary_data).digest() return hashlib.sha256(binary_data).digest()
def sha256(string): def sha256(string):
return bytes_to_hex_string(bin_sha256(string)) return safe_hexlify(bin_sha256(string))
def bin_dbl_sha256(bytes_to_hash): 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() return hashlib.sha256(hashlib.sha256(bytes_to_hash).digest()).digest()
def dbl_sha256(string): def dbl_sha256(string):
@ -306,7 +307,7 @@ def hex_to_b58check(inp, magicbyte=b'\x00'):
return bin_to_b58check(binascii.unhexlify(inp), magicbyte) return bin_to_b58check(binascii.unhexlify(inp), magicbyte)
def b58check_to_hex(inp): 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): def pubkey_to_address(pubkey, magicbyte=0):
if len(pubkey) in [66, 130]: 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) hashed_msg = message_sig_hash(msg)
if usehex: if usehex:
#arguments to raw sign must be consistently hex or bin #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) sig = ecdsa_raw_sign(hashed_msg, priv, usehex, rawmsg=True, formsg=formsg)
#note those functions only handles binary, not hex #note those functions only handles binary, not hex
if usehex: if usehex:
sig = binascii.unhexlify(sig) sig = binascii.unhexlify(sig)
return base64.b64encode(sig) return base64.b64encode(sig).decode('ascii')
def ecdsa_verify(msg, sig, pub, usehex=True): def ecdsa_verify(msg, sig, pub, usehex=True):
hashed_msg = message_sig_hash(msg) hashed_msg = message_sig_hash(msg)
sig = base64.b64decode(sig) sig = base64.b64decode(sig)
if usehex: if usehex:
#arguments to raw_verify must be consistently hex or bin #arguments to raw_verify must be consistently hex or bin
hashed_msg = binascii.hexlify(hashed_msg) hashed_msg = binascii.hexlify(hashed_msg).decode('ascii')
sig = binascii.hexlify(sig) sig = binascii.hexlify(sig).decode('ascii')
return ecdsa_raw_verify(hashed_msg, pub, sig, usehex, rawmsg=True) return ecdsa_raw_verify(hashed_msg, pub, sig, usehex, rawmsg=True)
#Use secp256k1 to handle all EC and ECDSA operations. #Use secp256k1 to handle all EC and ECDSA operations.
@ -389,7 +390,7 @@ def hexbin(func):
if isinstance(returnval, bool): if isinstance(returnval, bool):
return returnval return returnval
else: else:
return binascii.hexlify(returnval) return binascii.hexlify(returnval).decode('ascii')
else: else:
return func(*args, **kwargs) return func(*args, **kwargs)
@ -415,7 +416,10 @@ def privkey_to_pubkey_inner(priv, usehex):
and return compressed/uncompressed public key as appropriate.''' and return compressed/uncompressed public key as appropriate.'''
compressed, priv = read_privkey(priv) compressed, priv = read_privkey(priv)
#secp256k1 checks for validity of key value. #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) return newpriv.public_key.format(compressed)
def privkey_to_pubkey(priv, usehex=True): 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) newpub = secp256k1.PublicKey(pub)
#see note to "tweak_mul" function in podle.py #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: if not return_serialized:
return res return res
return res.format() return res.format()

68
jmbitcoin/jmbitcoin/secp256k1_transaction.py

@ -14,7 +14,7 @@ from jmbitcoin.bech32 import *
# Transaction serialization and deserialization # Transaction serialization and deserialization
def deserialize(txinp): 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)) tx = BytesIO(binascii.unhexlify(txinp))
hexout = True hexout = True
else: else:
@ -23,7 +23,7 @@ def deserialize(txinp):
def hex_string(scriptbytes, hexout): def hex_string(scriptbytes, hexout):
if hexout: if hexout:
return binascii.hexlify(scriptbytes) return binascii.hexlify(scriptbytes).decode('ascii')
else: else:
return scriptbytes return scriptbytes
@ -156,7 +156,7 @@ def serialize(tx):
o.write(struct.pack(b'<I', inp["outpoint"]["index"])) o.write(struct.pack(b'<I', inp["outpoint"]["index"]))
if len(inp["script"]) == 0: if len(inp["script"]) == 0:
o.write(b'\x00') o.write(b'\x00')
elif isinstance(inp["script"], basestring) and re.match('^[0-9a-fA-F]*$', inp["script"]): elif isinstance(inp["script"], basestring) and not isinstance(inp["script"], bytes) and re.match('^[0-9a-fA-F]*$', inp["script"]):
o.write(num_to_var_int(len(binascii.unhexlify(inp["script"])))) o.write(num_to_var_int(len(binascii.unhexlify(inp["script"]))))
o.write(binascii.unhexlify(inp["script"])) o.write(binascii.unhexlify(inp["script"]))
else: else:
@ -168,7 +168,7 @@ def serialize(tx):
o.write(struct.pack(b'<Q', out["value"])) o.write(struct.pack(b'<Q', out["value"]))
if len(out["script"]) == 0: if len(out["script"]) == 0:
o.write(b'\x00') o.write(b'\x00')
elif isinstance(out["script"], basestring) and re.match('^[0-9a-fA-F]*$', out["script"]): elif isinstance(out["script"], basestring) and not isinstance(out["script"], bytes) and re.match('^[0-9a-fA-F]*$', out["script"]):
o.write(num_to_var_int(len(binascii.unhexlify(out["script"])))) o.write(num_to_var_int(len(binascii.unhexlify(out["script"]))))
o.write(binascii.unhexlify(out["script"])) o.write(binascii.unhexlify(out["script"]))
else: else:
@ -184,13 +184,13 @@ def serialize(tx):
items = inp["txinwitness"] items = inp["txinwitness"]
o.write(num_to_var_int(len(items))) o.write(num_to_var_int(len(items)))
for item in items: for item in items:
if isinstance(item, basestring) and re.match('^[0-9a-fA-F]*$', item): if isinstance(item, basestring) and not isinstance(item, bytes) and re.match('^[0-9a-fA-F]*$', item):
item = binascii.unhexlify(item) item = binascii.unhexlify(item)
o.write(num_to_var_int(len(item)) + item) o.write(num_to_var_int(len(item)) + item)
o.write(struct.pack(b'<I', txobj["locktime"])) o.write(struct.pack(b'<I', txobj["locktime"]))
if hexout: if hexout:
return binascii.hexlify(o.getvalue()) return binascii.hexlify(o.getvalue()).decode('ascii')
else: else:
return o.getvalue() return o.getvalue()
@ -261,12 +261,12 @@ def segwit_signature_form(txobj, i, script, amount, hashcode=SIGHASH_ALL,
def signature_form(tx, i, script, hashcode=SIGHASH_ALL): def signature_form(tx, i, script, hashcode=SIGHASH_ALL):
if not isinstance(hashcode, int): if not isinstance(hashcode, int):
hashcode = struct.unpack(b'B', hashcode)[0] hashcode = struct.unpack(b'B', hashcode)[0]
if isinstance(tx, basestring) and re.match('^[0-9a-fA-F]*$', tx): if isinstance(tx, basestring) and not isinstance(tx, bytes) and re.match('^[0-9a-fA-F]*$', tx):
tx = deserialize(tx) tx = deserialize(tx)
elif isinstance(tx, basestring): elif isinstance(tx, basestring):
tx = deserialize(binascii.hexlify(tx)) tx = deserialize(binascii.hexlify(tx).decode('ascii'))
if isinstance(script, basestring) and not re.match('^[0-9a-fA-F]*$', script): if isinstance(script, basestring) and isinstance(script, bytes) and not re.match(b'^[0-9a-fA-F]*$', script):
script = binascii.hexlify(script) script = binascii.hexlify(script).decode('ascii')
newtx = copy.deepcopy(tx) newtx = copy.deepcopy(tx)
for inp in newtx["ins"]: for inp in newtx["ins"]:
#If tx is passed in in segwit form, it must be switched to non-segwit. #If tx is passed in in segwit form, it must be switched to non-segwit.
@ -317,10 +317,10 @@ def txhash(tx, hashcode=None, check_sw=True):
""" """
if not isinstance(tx, basestring): if not isinstance(tx, basestring):
tx = serialize(tx) tx = serialize(tx)
if isinstance(tx, basestring) and re.match('^[0-9a-fA-F]*$', tx): if isinstance(tx, basestring) and not isinstance(tx, bytes) and re.match('^[0-9a-fA-F]*$', tx):
tx = binascii.unhexlify(tx) tx = binascii.unhexlify(tx)
if check_sw and from_byte_to_int(tx[4]) == 0: if check_sw and from_byte_to_int(tx[4:5]) == 0:
if not from_byte_to_int(tx[5]) == 1: if not from_byte_to_int(tx[5:6]) == 1:
#This invalid, but a raise is a DOS vector in some contexts. #This invalid, but a raise is a DOS vector in some contexts.
return None return None
return segwit_txid(tx, hashcode) return segwit_txid(tx, hashcode)
@ -374,7 +374,7 @@ def mk_native_segwit_script(addr):
hrp = addr[:2] hrp = addr[:2]
ver, prog = bech32addr_decode(hrp, addr) ver, prog = bech32addr_decode(hrp, addr)
scriptpubkey = segwit_scriptpubkey(ver, prog) scriptpubkey = segwit_scriptpubkey(ver, prog)
return binascii.hexlify(scriptpubkey) return binascii.hexlify(scriptpubkey).decode('ascii')
# Address representation to output script # Address representation to output script
def address_to_script(addr): def address_to_script(addr):
@ -400,7 +400,7 @@ def is_segwit_native_script(script):
return False return False
def script_to_address(script, vbyte=0, witver=0): def script_to_address(script, vbyte=0, witver=0):
if re.match('^[0-9a-fA-F]*$', script): if not isinstance(script, bytes) and re.match('^[0-9a-fA-F]*$', script):
script = binascii.unhexlify(script) script = binascii.unhexlify(script)
if is_segwit_native_script(script): if is_segwit_native_script(script):
#hrp interpreted from the vbyte entry, TODO this should be cleaner. #hrp interpreted from the vbyte entry, TODO this should be cleaner.
@ -418,18 +418,18 @@ def script_to_address(script, vbyte=0, witver=0):
return bin_to_b58check(script[2:-1], vbyte) return bin_to_b58check(script[2:-1], vbyte)
def pubkey_to_p2sh_p2wpkh_script(pub): def pubkey_to_p2sh_p2wpkh_script(pub):
if re.match('^[0-9a-fA-F]*$', pub): if not isinstance(pub, bytes) and re.match('^[0-9a-fA-F]*$', pub):
pub = binascii.unhexlify(pub) pub = binascii.unhexlify(pub)
return "0014" + hash160(pub) return "0014" + hash160(pub)
def pubkey_to_p2sh_p2wpkh_address(pub, magicbyte=5): def pubkey_to_p2sh_p2wpkh_address(pub, magicbyte=5):
if re.match('^[0-9a-fA-F]*$', pub): if not isinstance(pub, bytes) and re.match('^[0-9a-fA-F]*$', pub):
pub = binascii.unhexlify(pub) pub = binascii.unhexlify(pub)
script = pubkey_to_p2sh_p2wpkh_script(pub) script = pubkey_to_p2sh_p2wpkh_script(pub)
return p2sh_scriptaddr(script, magicbyte=magicbyte) return p2sh_scriptaddr(script, magicbyte=magicbyte)
def p2sh_scriptaddr(script, magicbyte=5): def p2sh_scriptaddr(script, magicbyte=5):
if re.match('^[0-9a-fA-F]*$', script): if not isinstance(script, bytes) and re.match('^[0-9a-fA-F]*$', script):
script = binascii.unhexlify(script) script = binascii.unhexlify(script)
return hex_to_b58check(hash160(script), magicbyte) return hex_to_b58check(hash160(script), magicbyte)
@ -450,7 +450,7 @@ def deserialize_script(scriptinp):
""" """
def hex_string(scriptbytes, hexout): def hex_string(scriptbytes, hexout):
if hexout: if hexout:
return binascii.hexlify(scriptbytes) return binascii.hexlify(scriptbytes).decode('ascii')
else: else:
return scriptbytes return scriptbytes
@ -496,7 +496,7 @@ def serialize_script_unit(unit):
elif unit is None: elif unit is None:
return b'\x00' return b'\x00'
else: else:
if isinstance(unit, basestring) and re.match('^[0-9a-fA-F]*$', unit): if isinstance(unit, basestring) and not isinstance(unit, bytes) and re.match('^[0-9a-fA-F]*$', unit):
unit = binascii.unhexlify(unit) unit = binascii.unhexlify(unit)
if len(unit) <= 75: if len(unit) <= 75:
return from_int_to_byte(len(unit)) + unit return from_int_to_byte(len(unit)) + unit
@ -512,11 +512,11 @@ def serialize_script(script):
result = b'' result = b''
hexout = True hexout = True
for b in script: for b in script:
if isinstance(b, basestring) and not re.match('^[0-9a-fA-F]*$', b): if isinstance(b, basestring) and isinstance(b, bytes) and not re.match(b'^[0-9a-fA-F]*$', b):
hexout = False hexout = False
result += serialize_script_unit(b) result += serialize_script_unit(b)
if hexout: if hexout:
return binascii.hexlify(result) return binascii.hexlify(result).decode('ascii')
else: else:
return result return result
@ -533,16 +533,16 @@ def mk_multisig_script(*args): # [pubs],k or pub1,pub2...pub[n],k
def verify_tx_input(tx, i, script, sig, pub, witness=None, amount=None): def verify_tx_input(tx, i, script, sig, pub, witness=None, amount=None):
if re.match('^[0-9a-fA-F]*$', tx): if not isinstance(tx, bytes) and re.match('^[0-9a-fA-F]*$', tx):
tx = binascii.unhexlify(tx) tx = binascii.unhexlify(tx)
if re.match('^[0-9a-fA-F]*$', script): if not isinstance(script, bytes) and re.match('^[0-9a-fA-F]*$', script):
script = binascii.unhexlify(script) script = binascii.unhexlify(script)
if not re.match('^[0-9a-fA-F]*$', sig): if isinstance(sig, bytes) and not re.match(b'^[0-9a-fA-F]*$', sig):
sig = binascii.hexlify(sig) sig = binascii.hexlify(sig).decode('ascii')
if not re.match('^[0-9a-fA-F]*$', pub): if isinstance(pub, bytes) and not re.match(b'^[0-9a-fA-F]*$', pub):
pub = binascii.hexlify(pub) pub = binascii.hexlify(pub).decode('ascii')
if witness: if witness:
if not re.match('^[0-9a-fA-F]*$', witness): if isinstance(witness, bytes) and not re.match(b'^[0-9a-fA-F]*$', witness):
witness = safe_hexlify(witness) witness = safe_hexlify(witness)
hashcode = binascii.unhexlify(sig[-2:]) hashcode = binascii.unhexlify(sig[-2:])
if witness and amount: if witness and amount:
@ -557,7 +557,7 @@ def verify_tx_input(tx, i, script, sig, pub, witness=None, amount=None):
def sign(tx, i, priv, hashcode=SIGHASH_ALL, usenonce=None, amount=None): def sign(tx, i, priv, hashcode=SIGHASH_ALL, usenonce=None, amount=None):
i = int(i) i = int(i)
if isinstance(tx, basestring) and re.match('^[0-9a-fA-F]*$', tx): if isinstance(tx, basestring) and not isinstance(tx, bytes) and re.match('^[0-9a-fA-F]*$', tx):
tx = binascii.unhexlify(tx) tx = binascii.unhexlify(tx)
hexout = True hexout = True
else: else:
@ -574,9 +574,9 @@ def sign(tx, i, priv, hashcode=SIGHASH_ALL, usenonce=None, amount=None):
txobj = deserialize(tx) txobj = deserialize(tx)
txobj["ins"][i]["script"] = serialize_script([sig, pub]) txobj["ins"][i]["script"] = serialize_script([sig, pub])
serobj = serialize(txobj) serobj = serialize(txobj)
if hexout and isinstance(serobj, basestring) and not re.match('^[0-9a-fA-F]*$', serobj): if hexout and isinstance(serobj, basestring) and isinstance(serobj, bytes) and not re.match(b'^[0-9a-fA-F]*$', serobj):
return binascii.hexlify(serobj) return binascii.hexlify(serobj).decode('ascii')
elif not hexout and re.match('^[0-9a-fA-F]*$', serobj): elif not hexout and not isinstance(serobj, bytes) and re.match('^[0-9a-fA-F]*$', serobj):
return binascii.unhexlify(serobj) return binascii.unhexlify(serobj)
else: else:
return serobj return serobj
@ -664,7 +664,7 @@ def mktx(*args):
"sequence": 4294967295 "sequence": 4294967295
}) })
for o in outs: for o in outs:
if isinstance(o, basestring): if isinstance(o, basestring) and not isinstance(o, bytes):
addr = o[:o.find(':')] addr = o[:o.find(':')]
val = int(o[o.find(':') + 1:]) val = int(o[o.find(':') + 1:])
o = {} o = {}

4
jmbitcoin/test/test_ecc_signing.py

@ -18,7 +18,7 @@ def test_valid_sigs(setup_ecc):
msg = v['msg'] msg = v['msg']
sig = v['sig'] sig = v['sig']
priv = v['privkey'] priv = v['privkey']
assert btc.from_string_to_bytes(sig) == btc.ecdsa_raw_sign(msg, priv, True, rawmsg=True)+b'01' assert sig == btc.ecdsa_raw_sign(msg, priv, True, rawmsg=True)+'01'
#check that the signature verifies against the key(pair) #check that the signature verifies against the key(pair)
pubkey = btc.privtopub(priv) pubkey = btc.privtopub(priv)
assert btc.ecdsa_raw_verify(msg, pubkey, sig[:-2], True, rawmsg=True) assert btc.ecdsa_raw_verify(msg, pubkey, sig[:-2], True, rawmsg=True)
@ -27,7 +27,7 @@ def test_valid_sigs(setup_ecc):
#corrupt one byte #corrupt one byte
binsig = binascii.unhexlify(sig) binsig = binascii.unhexlify(sig)
checksig = binascii.hexlify(binsig[:i] + btc.from_string_to_bytes(chr( checksig = binascii.hexlify(binsig[:i] + btc.from_string_to_bytes(chr(
(ord(binsig[i:i+1])+1) %256)) + binsig[i+1:-1]) (ord(binsig[i:i+1])+1) %256)) + binsig[i+1:-1]).decode('ascii')
#this kind of corruption will sometimes lead to an assert #this kind of corruption will sometimes lead to an assert
#failure (if the DER format is corrupted) and sometimes lead #failure (if the DER format is corrupted) and sometimes lead

2
jmbitcoin/test/test_keys.py

@ -30,7 +30,7 @@ def test_wif_privkeys_invalid():
for priv in bad_privs: for priv in bad_privs:
with pytest.raises(Exception) as e_info: with pytest.raises(Exception) as e_info:
fake_wif = btc.wif_compressed_privkey(binascii.hexlify(priv)) fake_wif = btc.wif_compressed_privkey(binascii.hexlify(priv).decode('ascii'))
#Create a wif with wrong length #Create a wif with wrong length
bad_wif1 = btc.bin_to_b58check(b'\x01\x02'*34, b'\x80') bad_wif1 = btc.bin_to_b58check(b'\x01\x02'*34, b'\x80')

2
jmbitcoin/test/test_main.py

@ -57,7 +57,7 @@ def test_ecdsa_raw_sign():
#build non-raw priv object as input #build non-raw priv object as input
privraw = "aa"*32 privraw = "aa"*32
msghash = b"\xbb"*32 msghash = b"\xbb"*32
sig = binascii.hexlify(btc.ecdsa_raw_sign(msghash, privraw, False, rawpriv=False, rawmsg=True)) sig = binascii.hexlify(btc.ecdsa_raw_sign(msghash, privraw, False, rawpriv=False, rawmsg=True)).decode('ascii')
assert sig == "3045022100b81960b4969b423199dea555f562a66b7f49dea5836a0168361f1a5f8a3c8298022003eea7d7ee4462e3e9d6d59220f950564caeb77f7b1cdb42af3c83b013ff3b2f" assert sig == "3045022100b81960b4969b423199dea555f562a66b7f49dea5836a0168361f1a5f8a3c8298022003eea7d7ee4462e3e9d6d59220f950564caeb77f7b1cdb42af3c83b013ff3b2f"

8
jmclient/jmclient/__init__.py

@ -1,4 +1,6 @@
from __future__ import print_function from __future__ import (absolute_import, division,
print_function, unicode_literals)
from builtins import *
import logging import logging
@ -6,7 +8,7 @@ import logging
#other implementations (like wallet plugins) #other implementations (like wallet plugins)
#can optionally include their own, which must #can optionally include their own, which must
#be implemented as an interface in btc.py #be implemented as an interface in btc.py
from btc import * from .btc import *
from .support import (calc_cj_fee, choose_sweep_orders, choose_orders, from .support import (calc_cj_fee, choose_sweep_orders, choose_orders,
cheapest_order_choose, weighted_order_choose, cheapest_order_choose, weighted_order_choose,
@ -17,7 +19,7 @@ from .jsonrpc import JsonRpcError, JsonRpcConnectionError, JsonRpc
from .old_mnemonic import mn_decode, mn_encode from .old_mnemonic import mn_decode, mn_encode
from .slowaes import decryptData, encryptData from .slowaes import decryptData, encryptData
from .taker import Taker from .taker import Taker
from .wallet import (estimate_tx_fee, WalletError, BaseWallet, ImportWalletMixin, from .wallet import (Mnemonic, estimate_tx_fee, WalletError, BaseWallet, ImportWalletMixin,
BIP39WalletMixin, BIP32Wallet, BIP49Wallet, LegacyWallet, BIP39WalletMixin, BIP32Wallet, BIP49Wallet, LegacyWallet,
SegwitLegacyWallet, UTXOManager, WALLET_IMPLEMENTATIONS) SegwitLegacyWallet, UTXOManager, WALLET_IMPLEMENTATIONS)
from .storage import (Argon2Hash, Storage, StorageError, from .storage import (Argon2Hash, Storage, StorageError,

12
jmclient/jmclient/blockchaininterface.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
import abc import abc
import ast import ast
@ -10,7 +12,7 @@ from copy import deepcopy
from decimal import Decimal from decimal import Decimal
from twisted.internet import reactor, task from twisted.internet import reactor, task
import btc from . import btc
from jmclient.jsonrpc import JsonRpcConnectionError, JsonRpcError from jmclient.jsonrpc import JsonRpcConnectionError, JsonRpcError
from jmclient.configure import get_p2pk_vbyte, jm_single from jmclient.configure import get_p2pk_vbyte, jm_single
@ -345,8 +347,6 @@ class BitcoinCoreInterface(BlockchainInterface):
'gettransaction', 'getrawtransaction', 'gettxout']: 'gettransaction', 'getrawtransaction', 'gettxout']:
log.debug('rpc: ' + method + " " + str(args)) log.debug('rpc: ' + method + " " + str(args))
res = self.jsonRpc.call(method, args) res = self.jsonRpc.call(method, args)
if isinstance(res, unicode):
res = str(res)
return res return res
def import_addresses(self, addr_list, wallet_name): def import_addresses(self, addr_list, wallet_name):
@ -990,6 +990,6 @@ class RegtestBitcoinCoreInterface(BitcoinCoreInterface): #pragma: no cover
for address in addresses: for address in addresses:
#self.rpc('importaddress', [address, 'watchonly']) #self.rpc('importaddress', [address, 'watchonly'])
res.append({'address': address, res.append({'address': address,
'balance': int(round(Decimal(1e8) * Decimal(self.rpc( 'balance': int(Decimal(1e8) * self.rpc(
'getreceivedbyaddress', [address, 0]))))}) 'getreceivedbyaddress', [address, 0]))})
return {'data': res} return {'data': res}

9
jmclient/jmclient/btc.py

@ -1,6 +1,9 @@
"""Module to support bitcoin operations using a """Module to support bitcoin operations using a
different codebase than joinmarket's own. different codebase than joinmarket's own.
""" """
from __future__ import (absolute_import, division,
print_function, unicode_literals)
from builtins import *
#Protocol constants #Protocol constants
BTC_P2PK_VBYTE = {"mainnet": b'\x00', "testnet": b'\x6f'} BTC_P2PK_VBYTE = {"mainnet": b'\x00', "testnet": b'\x6f'}
BTC_P2SH_VBYTE = {"mainnet": b'\x05', "testnet": b'\xc4'} BTC_P2SH_VBYTE = {"mainnet": b'\x05', "testnet": b'\xc4'}
@ -158,7 +161,7 @@ except ImportError:
return ebt.sha256(ebt.sha256(x)) return ebt.sha256(ebt.sha256(x))
def dbl_sha256(x): def dbl_sha256(x):
return binascii.hexlify(bin_dbl_sha256(x)) return binascii.hexlify(bin_dbl_sha256(x)).decode('ascii')
def verify_tx_input(tx, i, script, sig, pub): def verify_tx_input(tx, i, script, sig, pub):
pub, sig, script = (binascii.unhexlify(x) for x in [pub, sig, script]) pub, sig, script = (binascii.unhexlify(x) for x in [pub, sig, script])
@ -242,7 +245,7 @@ except ImportError:
for inp in txobj["ins"]: for inp in txobj["ins"]:
binhash = binascii.unhexlify(inp["outpoint"]["hash"]) binhash = binascii.unhexlify(inp["outpoint"]["hash"])
binhash = binhash[::-1] binhash = binhash[::-1]
o.append(binascii.hexlify(binhash)) o.append(binascii.hexlify(binhash).decode('ascii'))
o.append(ebt.int_to_hex(inp["outpoint"]["index"], 4)) o.append(ebt.int_to_hex(inp["outpoint"]["index"], 4))
o.append(ebt.var_int(len(inp["script"])/2) + inp["script"]) o.append(ebt.var_int(len(inp["script"])/2) + inp["script"])
o.append(ebt.int_to_hex(inp["sequence"], 4)) o.append(ebt.int_to_hex(inp["sequence"], 4))
@ -321,7 +324,7 @@ except ImportError:
def from_wif_privkey(privkey, vbyte=0): def from_wif_privkey(privkey, vbyte=0):
#converts a WIF compressed privkey to a hex private key #converts a WIF compressed privkey to a hex private key
return binascii.hexlify(ebt.ASecretToSecret(privkey)) return binascii.hexlify(ebt.ASecretToSecret(privkey)).decode('ascii')
def txhash(txhex): def txhash(txhex):
t = etr.Transaction(txhex) t = etr.Transaction(txhex)

18
jmclient/jmclient/client_protocol.py

@ -1,5 +1,8 @@
#! /usr/bin/env python #! /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 twisted.internet import protocol, reactor, task from twisted.internet import protocol, reactor, task
from twisted.internet.error import (ConnectionLost, ConnectionAborted, from twisted.internet.error import (ConnectionLost, ConnectionAborted,
ConnectionClosed, ConnectionDone) ConnectionClosed, ConnectionDone)
@ -17,8 +20,7 @@ import sys
from jmclient import (jm_single, get_irc_mchannels, get_log, get_p2sh_vbyte, from jmclient import (jm_single, get_irc_mchannels, get_log, get_p2sh_vbyte,
RegtestBitcoinCoreInterface) RegtestBitcoinCoreInterface)
from .output import fmt_tx_data from .output import fmt_tx_data
from jmbase import _byteify from . import btc
import btc
jlog = get_log() jlog = get_log()
@ -60,7 +62,7 @@ class JMClientProtocol(amp.AMP):
def set_nick(self): def set_nick(self):
self.nick_pubkey = btc.privtopub(self.nick_priv) self.nick_pubkey = btc.privtopub(self.nick_priv)
self.nick_pkh_raw = hashlib.sha256(self.nick_pubkey).digest()[ self.nick_pkh_raw = btc.bin_sha256(self.nick_pubkey)[
:self.nick_hashlen] :self.nick_hashlen]
self.nick_pkh = btc.b58encode(self.nick_pkh_raw) self.nick_pkh = btc.b58encode(self.nick_pkh_raw)
#right pad to maximum possible; b58 is not fixed length. #right pad to maximum possible; b58 is not fixed length.
@ -107,7 +109,7 @@ class JMClientProtocol(amp.AMP):
jlog.debug("nick signature verification failed, ignoring.") jlog.debug("nick signature verification failed, ignoring.")
verif_result = False verif_result = False
#check that nick matches hash of pubkey #check that nick matches hash of pubkey
nick_pkh_raw = hashlib.sha256(pubkey).digest()[:hashlen] nick_pkh_raw = btc.bin_sha256(pubkey)[:hashlen]
nick_stripped = nick[2:2 + max_encoded] nick_stripped = nick[2:2 + max_encoded]
#strip right padding #strip right padding
nick_unpadded = ''.join([x for x in nick_stripped if x != 'O']) nick_unpadded = ''.join([x for x in nick_stripped if x != 'O'])
@ -206,7 +208,7 @@ class JMMakerClientProtocol(JMClientProtocol):
@commands.JMTXReceived.responder @commands.JMTXReceived.responder
def on_JM_TX_RECEIVED(self, nick, txhex, offer): def on_JM_TX_RECEIVED(self, nick, txhex, offer):
offer = _byteify(json.loads(offer)) offer = json.loads(offer)
retval = self.client.on_tx_received(nick, txhex, offer) retval = self.client.on_tx_received(nick, txhex, offer)
if not retval[0]: if not retval[0]:
jlog.info("Maker refuses to continue on receipt of tx") jlog.info("Maker refuses to continue on receipt of tx")
@ -230,7 +232,7 @@ class JMMakerClientProtocol(JMClientProtocol):
def unconfirm_callback(self, txd, txid): def unconfirm_callback(self, txd, txid):
#find the offer for this tx #find the offer for this tx
offerinfo = None offerinfo = None
for k,v in self.finalized_offers.iteritems(): for k,v in iteritems(self.finalized_offers):
#Tx considered defined by its output set #Tx considered defined by its output set
if v["txd"]["outs"] == txd["outs"]: if v["txd"]["outs"] == txd["outs"]:
offerinfo = v offerinfo = v
@ -254,7 +256,7 @@ class JMMakerClientProtocol(JMClientProtocol):
def confirm_callback(self, txd, txid, confirmations): def confirm_callback(self, txd, txid, confirmations):
#find the offer for this tx #find the offer for this tx
offerinfo = None offerinfo = None
for k,v in self.finalized_offers.iteritems(): for k,v in iteritems(self.finalized_offers):
#Tx considered defined by its output set #Tx considered defined by its output set
if v["txd"]["outs"] == txd["outs"]: if v["txd"]["outs"] == txd["outs"]:
offerinfo = v offerinfo = v

4
jmclient/jmclient/commitment_utils.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
import sys import sys
import jmclient.btc as btc import jmclient.btc as btc

16
jmclient/jmclient/configure.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
import io import io
import logging import logging
@ -6,9 +8,9 @@ import threading
import os import os
import binascii import binascii
from ConfigParser import SafeConfigParser, NoOptionError from configparser import ConfigParser, NoOptionError
import btc from . import btc
from jmclient.jsonrpc import JsonRpc from jmclient.jsonrpc import JsonRpc
from jmbase.support import (get_log, joinmarket_alert, core_alert, debug_silence, from jmbase.support import (get_log, joinmarket_alert, core_alert, debug_silence,
set_logging_level) set_logging_level)
@ -33,7 +35,7 @@ class AttributeDict(object):
def add_entries(self, **entries): def add_entries(self, **entries):
for key, value in entries.items(): for key, value in entries.items():
if type(value) is dict: if isinstance(value, dict):
self.__dict__[key] = AttributeDict(**value) self.__dict__[key] = AttributeDict(**value)
else: else:
self.__dict__[key] = value self.__dict__[key] = value
@ -73,7 +75,7 @@ global_singleton.blacklist_file_lock = threading.Lock()
global_singleton.core_alert = core_alert global_singleton.core_alert = core_alert
global_singleton.joinmarket_alert = joinmarket_alert global_singleton.joinmarket_alert = joinmarket_alert
global_singleton.debug_silence = debug_silence global_singleton.debug_silence = debug_silence
global_singleton.config = SafeConfigParser() global_singleton.config = ConfigParser(strict=False)
#This is reset to a full path after load_program_config call #This is reset to a full path after load_program_config call
global_singleton.config_location = 'joinmarket.cfg' global_singleton.config_location = 'joinmarket.cfg'
#as above #as above
@ -362,7 +364,7 @@ def donation_address(reusable_donation_pubkey=None): #pragma: no cover
if not reusable_donation_pubkey: if not reusable_donation_pubkey:
reusable_donation_pubkey = ('02be838257fbfddabaea03afbb9f16e852' reusable_donation_pubkey = ('02be838257fbfddabaea03afbb9f16e852'
'9dfe2de921260a5c46036d97b5eacf2a') '9dfe2de921260a5c46036d97b5eacf2a')
sign_k = binascii.hexlify(os.urandom(32)) sign_k = binascii.hexlify(os.urandom(32)).decode('ascii')
c = btc.sha256(btc.multiply(sign_k, reusable_donation_pubkey, True)) c = btc.sha256(btc.multiply(sign_k, reusable_donation_pubkey, True))
sender_pubkey = btc.add_pubkeys( sender_pubkey = btc.add_pubkeys(
[reusable_donation_pubkey, btc.privtopub(c + '01', True)], True) [reusable_donation_pubkey, btc.privtopub(c + '01', True)], True)
@ -378,7 +380,7 @@ def remove_unwanted_default_settings(config):
def load_program_config(config_path=None, bs=None): def load_program_config(config_path=None, bs=None):
global_singleton.config.readfp(io.BytesIO(defaultconfig)) global_singleton.config.readfp(io.StringIO(defaultconfig))
remove_unwanted_default_settings(global_singleton.config) remove_unwanted_default_settings(global_singleton.config)
if not config_path: if not config_path:
config_path = os.getcwd() config_path = os.getcwd()

14
jmclient/jmclient/cryptoengine.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 binascii import hexlify, unhexlify from binascii import hexlify, unhexlify
@ -30,12 +32,12 @@ P2WPKH_PRE = b'\x00\x14'
def _pubkey_to_script(pubkey, script_pre, script_post=b''): def _pubkey_to_script(pubkey, script_pre, script_post=b''):
# sanity check for public key # sanity check for public key
# see https://github.com/bitcoin/bitcoin/blob/master/src/pubkey.h # see https://github.com/bitcoin/bitcoin/blob/master/src/pubkey.h
if not ((len(pubkey) == 33 and pubkey[0] in (b'\x02', b'\x03')) or if not ((len(pubkey) == 33 and pubkey[:1] in (b'\x02', b'\x03')) or
(len(pubkey) == 65 and pubkey[0] in (b'\x04', b'\x06', b'\x07'))): (len(pubkey) == 65 and pubkey[:1] in (b'\x04', b'\x06', b'\x07'))):
raise Exception("Invalid public key!") raise Exception("Invalid public key!")
h = btc.bin_hash160(pubkey) h = btc.bin_hash160(pubkey)
assert len(h) == 0x14 assert len(h) == 0x14
assert script_pre[-1] == b'\x14' assert script_pre[-1:] == b'\x14'
return script_pre + h + script_post return script_pre + h + script_post
@ -229,7 +231,7 @@ class BTC_P2PKH(BTCEngine):
signing_tx = btc.serialize(btc.signature_form(tx, index, script, signing_tx = btc.serialize(btc.signature_form(tx, index, script,
hashcode=hashcode)) hashcode=hashcode))
# FIXME: encoding mess # FIXME: encoding mess
sig = unhexlify(btc.ecdsa_tx_sign(signing_tx, hexlify(privkey), sig = unhexlify(btc.ecdsa_tx_sign(signing_tx, hexlify(privkey).decode('ascii'),
**kwargs)) **kwargs))
tx['ins'][index]['script'] = btc.serialize_script([sig, pubkey]) tx['ins'][index]['script'] = btc.serialize_script([sig, pubkey])
@ -262,7 +264,7 @@ class BTC_P2SH_P2WPKH(BTCEngine):
hashcode=hashcode, hashcode=hashcode,
decoder_func=lambda x: x) decoder_func=lambda x: x)
# FIXME: encoding mess # FIXME: encoding mess
sig = unhexlify(btc.ecdsa_tx_sign(signing_tx, hexlify(privkey), sig = unhexlify(btc.ecdsa_tx_sign(signing_tx, hexlify(privkey).decode('ascii'),
hashcode=hashcode, **kwargs)) hashcode=hashcode, **kwargs))
assert len(wpkscript) == 0x16 assert len(wpkscript) == 0x16

3
jmclient/jmclient/electrum_data.py

@ -1,3 +1,6 @@
from __future__ import (absolute_import, division,
print_function, unicode_literals)
from builtins import * # noqa: F401
# Default server list from electrum client # Default server list from electrum client
# https://github.com/spesmilo/electrum, file https://github.com/spesmilo/electrum/blob/7dbd612d5dad13cd6f1c0df32534a578bad331ad/lib/servers.json # https://github.com/spesmilo/electrum, file https://github.com/spesmilo/electrum/blob/7dbd612d5dad13cd6f1c0df32534a578bad331ad/lib/servers.json

10
jmclient/jmclient/electruminterface.py

@ -1,6 +1,10 @@
import btc from __future__ import (absolute_import, division,
print_function, unicode_literals)
from builtins import * # noqa: F401
from future.utils import iteritems
from . import btc
import json import json
import Queue import queue as Queue
import os import os
import pprint import pprint
import random import random
@ -330,7 +334,7 @@ class ElectrumInterface(BlockchainInterface):
for m in range(wallet.max_mixdepth): for m in range(wallet.max_mixdepth):
for fc in [0, 1]: for fc in [0, 1]:
branch_list = [] branch_list = []
for k, v in self.temp_addr_history[m][fc].iteritems(): for k, v in iteritems(self.temp_addr_history[m][fc]):
if k == "finished": if k == "finished":
continue continue
if v["used"]: if v["used"]:

35
jmclient/jmclient/jsonrpc.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
# Copyright (C) 2013,2015 by Daniel Kraft <d@domob.eu> # Copyright (C) 2013,2015 by Daniel Kraft <d@domob.eu>
# Copyright (C) 2014 by phelix / blockchained.com # Copyright (C) 2014 by phelix / blockchained.com
# #
@ -23,8 +25,9 @@ from __future__ import absolute_import, print_function
import errno import errno
import socket import socket
import base64 import base64
import httplib import http.client
import json import json
from decimal import Decimal
from jmclient import get_log from jmclient import get_log
jlog = get_log() jlog = get_log()
@ -56,8 +59,8 @@ class JsonRpc(object):
def __init__(self, host, port, user, password, wallet_file=""): def __init__(self, host, port, user, password, wallet_file=""):
self.host = host self.host = host
self.port = port self.port = int(port)
self.conn = httplib.HTTPConnection(self.host, self.port) self.conn = http.client.HTTPConnection(self.host, self.port)
self.authstr = "%s:%s" % (user, password) self.authstr = "%s:%s" % (user, password)
if len(wallet_file) > 0: if len(wallet_file) > 0:
self.url = "/wallet/" + wallet_file self.url = "/wallet/" + wallet_file
@ -76,7 +79,7 @@ class JsonRpc(object):
headers = {"User-Agent": "joinmarket", headers = {"User-Agent": "joinmarket",
"Content-Type": "application/json", "Content-Type": "application/json",
"Accept": "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) body = json.dumps(obj)
@ -97,20 +100,26 @@ class JsonRpc(object):
data = response.read() data = response.read()
return json.loads(data) return json.loads(data.decode('utf-8'), parse_float=Decimal)
except JsonRpcConnectionError as exc: except JsonRpcConnectionError as exc:
raise exc raise exc
except httplib.BadStatusLine: except http.client.BadStatusLine:
return "CONNFAILURE" return "CONNFAILURE"
except socket.error as e: 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)) jlog.error('Unhandled connection error ' + str(e))
raise e raise e
jlog.warn('Connection was reset, attempting reconnect.')
self.conn.close()
self.conn.connect()
continue
except Exception as exc: except Exception as exc:
raise JsonRpcConnectionError("JSON-RPC connection failed. Err:" + raise JsonRpcConnectionError("JSON-RPC connection failed. Err:" +
repr(exc)) repr(exc))
@ -136,7 +145,7 @@ class JsonRpc(object):
response_received = True response_received = True
break break
#Failure means keepalive timed out, just make a new one #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: if not response_received:
raise JsonRpcConnectionError("Unable to connect over RPC") raise JsonRpcConnectionError("Unable to connect over RPC")
if response["id"] != currentId: if response["id"] != currentId:

12
jmclient/jmclient/maker.py

@ -1,13 +1,15 @@
#! /usr/bin/env python #! /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 base64
import pprint import pprint
import sys import sys
from binascii import unhexlify from binascii import unhexlify
import btc from . import btc
from btc import SerializationError, SerializationTruncationError from .btc import SerializationError, SerializationTruncationError
from jmclient.configure import jm_single from jmclient.configure import jm_single
from jmbase.support import get_log from jmbase.support import get_log
from jmclient.support import (calc_cj_fee) from jmclient.support import (calc_cj_fee)
@ -97,7 +99,7 @@ class Maker(object):
# Need to choose an input utxo pubkey to sign with # Need to choose an input utxo pubkey to sign with
# (no longer using the coinjoin pubkey from 0.2.0) # (no longer using the coinjoin pubkey from 0.2.0)
# Just choose the first utxo in self.utxos and retrieve key from wallet. # 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_key = self.wallet.get_key_from_addr(auth_address)
auth_pub = btc.privtopub(auth_key) auth_pub = btc.privtopub(auth_key)
btc_sig = btc.ecdsa_sign(kphex, 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. #also, the items in witness are not serialize_script-ed.
sigmsg = b''.join(btc.serialize_script_unit(x) sigmsg = b''.join(btc.serialize_script_unit(x)
for x in txs['ins'][index]['txinwitness']) + sigmsg for x in txs['ins'][index]['txinwitness']) + sigmsg
sigs.append(base64.b64encode(sigmsg)) sigs.append(base64.b64encode(sigmsg).decode('ascii'))
return (True, sigs) return (True, sigs)
def verify_unsigned_tx(self, txd, offerinfo): def verify_unsigned_tx(self, txd, offerinfo):

11
jmclient/jmclient/old_mnemonic.py

@ -1,4 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
from __future__ import (absolute_import, division,
print_function, unicode_literals)
from builtins import * # noqa: F401
# #
# Electrum - lightweight Bitcoin client # Electrum - lightweight Bitcoin client
# Copyright (C) 2011 thomasv@gitorious # Copyright (C) 2011 thomasv@gitorious
@ -244,19 +247,19 @@ n = 1626
def mn_encode(message): def mn_encode(message):
assert len(message) % 8 == 0 assert len(message) % 8 == 0
out = [] out = []
for i in range(len(message) / 8): for i in range(len(message) // 8):
word = message[8 * i:8 * i + 8] word = message[8 * i:8 * i + 8]
x = int(word, 16) x = int(word, 16)
w1 = (x % n) w1 = (x % n)
w2 = ((x / n) + w1) % n w2 = ((x // n) + w1) % n
w3 = ((x / n / n) + w2) % n w3 = ((x // n // n) + w2) % n
out += [words[w1], words[w2], words[w3]] out += [words[w1], words[w2], words[w3]]
return out return out
def mn_decode(wlist): def mn_decode(wlist):
out = '' out = ''
for i in range(len(wlist) / 3): for i in range(len(wlist) // 3):
word1, word2, word3 = wlist[3 * i:3 * i + 3] word1, word2, word3 = wlist[3 * i:3 * i + 3]
w1 = words.index(word1) w1 = words.index(word1)
w2 = (words.index(word2)) % n w2 = (words.index(word2)) % n

5
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 from binascii import hexlify
@ -11,7 +14,7 @@ def fmt_utxos(utxos, wallet, prefix=''):
def fmt_utxo(utxo): 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): def fmt_tx_data(tx_data, wallet):

29
jmclient/jmclient/podle.py

@ -1,5 +1,7 @@
#!/usr/bin/env python #!/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 #Proof Of Discrete Logarithm Equivalence
#For algorithm steps, see https://gist.github.com/AdamISZ/9cbba5e9408d23813ca8 #For algorithm steps, see https://gist.github.com/AdamISZ/9cbba5e9408d23813ca8
import os import os
@ -7,7 +9,8 @@ import sys
import hashlib import hashlib
import json import json
import binascii 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 podle_PrivateKey, encode, decode, N, podle_PublicKey_class
@ -84,7 +87,7 @@ class PoDLE(object):
if not isinstance(self.P2, podle_PublicKey_class): if not isinstance(self.P2, podle_PublicKey_class):
raise PoDLEError("Cannot construct commitment, P2 is not a pubkey") raise PoDLEError("Cannot construct commitment, P2 is not a pubkey")
self.commitment = hashlib.sha256(self.P2.format()).digest() 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): def generate_podle(self, index=0, k=None):
"""Given a raw private key, in hex format, """Given a raw private key, in hex format,
@ -123,7 +126,7 @@ class PoDLE(object):
KJ = multiply(k, J.format(), False, return_serialized=False) KJ = multiply(k, J.format(), False, return_serialized=False)
self.P2 = getP2(self.priv, J) self.P2 = getP2(self.priv, J)
self.get_commitment() 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() ) for x in [KG, KJ, self.P, self.P2]])).digest()
k_int = decode(k, 256) k_int = decode(k, 256)
priv_int = decode(self.priv.secret, 256) priv_int = decode(self.priv.secret, 256)
@ -141,7 +144,7 @@ class PoDLE(object):
if not self.commitment: if not self.commitment:
self.get_commitment() self.get_commitment()
Phex, P2hex, shex, ehex, commit = [ 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, for x in [self.P.format(), self.P2.format(), self.s, self.e,
self.commitment] self.commitment]
] ]
@ -217,14 +220,14 @@ def getNUMS(index=0):
assert index in range(256) assert index in range(256)
nums_point = None nums_point = None
for G in [getG(True), getG(False)]: for G in [getG(True), getG(False)]:
seed = G + chr(index) seed = G + struct.pack(b'B', index)
for counter in range(256): 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() hashed_seed = hashlib.sha256(seed_c).digest()
#Every x-coord on the curve has two y-values, encoded #Every x-coord on the curve has two y-values, encoded
#in compressed form with 02/03 parity byte. We just #in compressed form with 02/03 parity byte. We just
#choose the former. #choose the former.
claimed_point = "\x02" + hashed_seed claimed_point = b"\x02" + hashed_seed
try: try:
nums_point = podle_PublicKey(claimed_point) nums_point = podle_PublicKey(claimed_point)
return nums_point return nums_point
@ -242,11 +245,11 @@ def verify_all_NUMS(write=False):
""" """
nums_points = {} nums_points = {}
for i in range(256): 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: if write:
with open("nums_basepoints.txt", "wb") as f: with open("nums_basepoints.txt", "wb") as f:
from pprint import pformat 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!" 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): if not os.path.isfile(PODLE_COMMIT_FILE):
return ([], {}) return ([], {})
with open(PODLE_COMMIT_FILE, "rb") as f: 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(): if 'used' not in c.keys() or 'external' not in c.keys():
raise PoDLEError("Incorrectly formatted file: " + PODLE_COMMIT_FILE) raise PoDLEError("Incorrectly formatted file: " + PODLE_COMMIT_FILE)
return (c['used'], c['external']) return (c['used'], c['external'])
@ -306,7 +309,7 @@ def update_commitments(commitment=None,
if os.path.isfile(PODLE_COMMIT_FILE): if os.path.isfile(PODLE_COMMIT_FILE):
with open(PODLE_COMMIT_FILE, "rb") as f: with open(PODLE_COMMIT_FILE, "rb") as f:
try: try:
c = json.loads(f.read()) c = json.loads(f.read().decode('utf-8'))
except ValueError: #pragma: no cover except ValueError: #pragma: no cover
#Exit conditions cannot be included in tests. #Exit conditions cannot be included in tests.
print("the file: " + PODLE_COMMIT_FILE + " is not valid json.") 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['used'] = commitments
to_write['external'] = external to_write['external'] = external
with open(PODLE_COMMIT_FILE, "wb") as f: 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): def get_podle_tries(utxo, priv=None, max_tries=1, external=False):
used_commitments, external_commitments = get_podle_commitments() used_commitments, external_commitments = get_podle_commitments()

7
jmclient/jmclient/schedule.py

@ -1,5 +1,7 @@
#!/usr/bin/env python #!/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 import copy
from jmclient import (validate_address, rand_exp_array, from jmclient import (validate_address, rand_exp_array,
rand_norm_array, rand_pow_array, jm_single) rand_norm_array, rand_pow_array, jm_single)
@ -20,6 +22,7 @@ def get_schedule(filename):
schedule = [] schedule = []
schedule_lines = f.readlines() schedule_lines = f.readlines()
for sl in schedule_lines: for sl in schedule_lines:
sl = sl.decode('utf-8')
if sl.startswith("#"): if sl.startswith("#"):
continue continue
try: try:
@ -213,4 +216,4 @@ def human_readable_schedule_entry(se, amt=None, destn=None):
return ", ".join(hrs) return ", ".join(hrs)
def schedule_to_text(schedule): 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')

3
jmclient/jmclient/slowaes.py

@ -11,6 +11,9 @@
# Licensed under the Apache License, Version 2.0 # Licensed under the Apache License, Version 2.0
# http://www.apache.org/licenses/ # http://www.apache.org/licenses/
# #
from __future__ import (absolute_import, division,
print_function, unicode_literals)
from builtins import *
import math import math
import os import os

9
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 os
import shutil import shutil
@ -244,7 +247,7 @@ class Storage(object):
def _encrypt(self, data, iv): def _encrypt(self, data, iv):
encrypter = pyaes.Encrypter( 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(self.MAGIC_DETECT_ENC + data)
enc_data += encrypter.feed() enc_data += encrypter.feed()
@ -252,7 +255,7 @@ class Storage(object):
def _decrypt(self, data, iv): def _decrypt(self, data, iv):
decrypter = pyaes.Decrypter( decrypter = pyaes.Decrypter(
pyaes.AESModeOfOperationCBC(self._hash.hash, iv=iv)) pyaes.AESModeOfOperationCBC(self._hash.hash, iv=native(iv)))
try: try:
dec_data = decrypter.feed(data) dec_data = decrypter.feed(data)
dec_data += decrypter.feed() dec_data += decrypter.feed()

11
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 import random
from jmbase.support import get_log from jmbase.support import get_log
@ -25,7 +28,7 @@ def get_random_bytes(num_bytes, cryptographically_secure=False):
generator = random.SystemRandom() generator = random.SystemRandom()
else: else:
generator = random 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): 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 # for basis of formula, see: http://mathworld.wolfram.com/RandomNumber.html
return [y**(1.0 / power) return [y**(1.0 / power)
for y in [x * 0.0001 for x in random.sample( for y in [x * 0.0001 for x in random.sample(
xrange(10000), n)]] range(10000), n)]]
def rand_weighted_choice(n, p_arr): 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") raise ValueError("Sum of probabilities must be 1")
if len(p_arr) != n: if len(p_arr) != n:
raise ValueError("Need: " + str(n) + " probabilities.") 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() r = random.random()
return sorted(cum_pr + [r]).index(r) return sorted(cum_pr + [r]).index(r)

43
jmclient/jmclient/taker.py

@ -1,12 +1,15 @@
#! /usr/bin/env python #! /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 base64
import pprint import pprint
import random import random
from binascii import hexlify, unhexlify from binascii import hexlify, unhexlify
import btc from . import btc
from jmclient.configure import get_p2sh_vbyte, jm_single, validate_address from jmclient.configure import get_p2sh_vbyte, jm_single, validate_address
from jmbase.support import get_log from jmbase.support import get_log
from jmclient.support import (calc_cj_fee, weighted_order_choose, choose_orders, 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 #Initialization has been successful. We must set the nonrespondants
#now to keep track of what changed when we receive the utxo data #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) return (True, self.cjamount, commitment, revelation, self.orderbook)
@ -324,7 +327,7 @@ class Taker(object):
self.cjamount): self.cjamount):
return False return False
self.utxos = {None: self.input_utxos.keys()} self.utxos = {None: list(self.input_utxos.keys())}
return True return True
def receive_utxos(self, ioauth_data): 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 #Temporary list used to aggregate all ioauth data that must be removed
rejected_counterparties = [] rejected_counterparties = []
#Need to authorize against the btc pubkey first. #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 utxo_list, auth_pub, cj_addr, change_addr, btc_sig, maker_pk = nickdata
if not self.auth_counterparty(btc_sig, auth_pub, maker_pk): if not self.auth_counterparty(btc_sig, auth_pub, maker_pk):
jlog.debug( jlog.debug(
@ -358,7 +361,7 @@ class Taker(object):
self.maker_utxo_data = {} 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 utxo_list, auth_pub, cj_addr, change_addr, btc_sig, maker_pk = nickdata
self.utxos[nick] = utxo_list self.utxos[nick] = utxo_list
utxo_data = jm_single().bc_interface.query_utxo_set(self.utxos[ 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 #Apply business logic of how many counterparties are enough; note that
#this must occur after the above ioauth data processing, since we only now #this must occur after the above ioauth data processing, since we only now
#know for sure that the data meets all business-logic requirements. #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"): "POLICY", "minimum_makers"):
self.taker_info_callback("INFO", "Not enough counterparties, aborting.") self.taker_info_callback("INFO", "Not enough counterparties, aborting.")
return (False, return (False,
@ -434,7 +437,7 @@ class Taker(object):
#used to track return of signatures for phase 2 #used to track return of signatures for phase 2
self.nonrespondants = list(self.maker_utxo_data.keys()) 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: if self.my_change_addr:
#Estimate fee per choice of next/3/6 blocks targetting. #Estimate fee per choice of next/3/6 blocks targetting.
@ -494,7 +497,7 @@ class Taker(object):
# placeholders required # placeholders required
ins['script'] = 'deadbeef' ins['script'] = 'deadbeef'
self.taker_info_callback("INFO", "Built tx, sending to counterparties.") 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): def auth_counterparty(self, btc_sig, auth_pub, maker_pk):
"""Validate the counterpartys claim to own the btc """Validate the counterpartys claim to own the btc
@ -522,7 +525,7 @@ class Taker(object):
jlog.debug(('add_signature => nick={} ' jlog.debug(('add_signature => nick={} '
'not in nonrespondants {}').format(nick, self.nonrespondants)) 'not in nonrespondants {}').format(nick, self.nonrespondants))
return return
sig = base64.b64decode(sigb64).encode('hex') sig = hexlify(base64.b64decode(sigb64)).decode('ascii')
inserted_sig = False inserted_sig = False
txhex = btc.serialize(self.latest_tx) txhex = btc.serialize(self.latest_tx)
@ -541,7 +544,7 @@ class Taker(object):
1] for x in utxo.values()]) 1] for x in utxo.values()])
# insert signatures # insert signatures
for i, u in utxo.iteritems(): for i, u in iteritems(utxo):
if utxo_data[i] is None: if utxo_data[i] is None:
continue continue
#Check if the sender serialize_scripted the witness #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 #also returns lists "too_old" and "too_small" for any
#utxos that did not satisfy the criteria for debugging. #utxos that did not satisfy the criteria for debugging.
priv_utxo_pairs = [] 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) age, amt)
new_utxos_dict = {k: v for k, v in utxos.items() if k in new_utxos} 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'] addr = v['address']
priv = self.wallet.get_key_from_addr(addr) priv = self.wallet.get_key_from_addr(addr)
if priv: #can be null from create-unsigned 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 #Pre-filter the set of external commitments that work for this
#transaction according to its size and age. #transaction according to its size and age.
dummy, extdict = get_podle_commitments() 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( ext_valid, ext_to, ext_ts = filter_by_coin_age_amt(
extdict.keys(), age, amt) list(extdict.keys()), age, amt)
else: else:
ext_valid = None ext_valid = None
podle_data = generate_podle(priv_utxo_pairs, tries, ext_valid) 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")) jm_single().config.get("POLICY", "taker_utxo_amtpercent"))
with open("commitments_debug.txt", "wb") as f: with open("commitments_debug.txt", "wb") as f:
errmsgfileheader = ("THIS IS A TEMPORARY FILE FOR DEBUGGING; " errmsgfileheader = (b"THIS IS A TEMPORARY FILE FOR DEBUGGING; "
"IT CAN BE SAFELY DELETED ANY TIME.\n") b"IT CAN BE SAFELY DELETED ANY TIME.\n")
errmsgfileheader += ("***\n") errmsgfileheader += (b"***\n")
f.write(errmsgfileheader + errmsg) f.write(errmsgfileheader + errmsg.encode('utf-8'))
return (None, (priv_utxo_pairs, to, ts), errmsgheader + errmsg) 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))) tx_bin = btc.deserialize(unhexlify(btc.serialize(self.latest_tx)))
self.wallet.sign_tx(tx_bin, our_inputs) 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): def push(self):

17
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 logging
import pprint import pprint
import os import os
@ -53,7 +56,7 @@ def direct_send(wallet, amount, mixdepth, destaddr, answeryes=False,
log.error( log.error(
"There are no utxos in mixdepth: " + str(mixdepth) + ", quitting.") "There are no utxos in mixdepth: " + str(mixdepth) + ", quitting.")
return 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) fee_est = estimate_tx_fee(len(utxos), 1, txtype=txtype)
outs = [{"address": destaddr, "value": total_inputs_val - fee_est}] outs = [{"address": destaddr, "value": total_inputs_val - fee_est}]
else: else:
@ -64,7 +67,7 @@ def direct_send(wallet, amount, mixdepth, destaddr, answeryes=False,
fee_est = estimate_tx_fee(len(utxos), 2, txtype=txtype) fee_est = estimate_tx_fee(len(utxos), 2, txtype=txtype)
else: else:
fee_est = initial_fee_est 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 changeval = total_inputs_val - fee_est - amount
outs = [{"value": amount, "address": destaddr}] outs = [{"value": amount, "address": destaddr}]
change_addr = wallet.get_internal_addr(mixdepth) 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.") log.info("Using a fee of : " + str(fee_est) + " satoshis.")
if amount != 0: if amount != 0:
log.info("Using a change value of: " + str(changeval) + " satoshis.") 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) txsigned = deserialize(tx)
log.info("Got signed transaction:\n") log.info("Got signed transaction:\n")
log.info(tx + "\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) log.info("Sends: " + str(actual_amount) + " satoshis to address: " + destaddr)
if not answeryes: if not answeryes:
if not accept_callback: 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.") log.info("You chose not to broadcast the transaction, quitting.")
return False return False
else: else:
@ -112,7 +115,7 @@ def sign_tx(wallet, tx, utxos):
# FIXME: ugly hack # FIXME: ugly hack
tx_bin = deserialize(unhexlify(serialize(stx))) tx_bin = deserialize(unhexlify(serialize(stx)))
wallet.sign_tx(tx_bin, our_inputs) 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): 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(' for example click the button that gives a new deposit address')
print('\n'.join(['=' * 60] * 1)) print('\n'.join(['=' * 60] * 1))
while True: while True:
destaddr = raw_input('insert new address: ') destaddr = input('insert new address: ')
addr_valid, errormsg = validate_address(destaddr) addr_valid, errormsg = validate_address(destaddr)
if addr_valid: if addr_valid:
break break

31
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 warnings
import functools import functools
import collections import collections
@ -8,7 +10,7 @@ import numbers
from binascii import hexlify, unhexlify from binascii import hexlify, unhexlify
from datetime import datetime from datetime import datetime
from copy import deepcopy from copy import deepcopy
from mnemonic import Mnemonic from mnemonic import Mnemonic as MnemonicParent
from hashlib import sha256 from hashlib import sha256
from itertools import chain from itertools import chain
from decimal import Decimal from decimal import Decimal
@ -58,6 +60,12 @@ class WalletError(Exception):
pass pass
class Mnemonic(MnemonicParent):
@classmethod
def detect_language(cls, code):
return "english"
def estimate_tx_fee(ins, outs, txtype='p2pkh'): def estimate_tx_fee(ins, outs, txtype='p2pkh'):
'''Returns an estimate of the number of satoshis required '''Returns an estimate of the number of satoshis required
for a transaction with the given number of inputs and outputs, 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) script = self._ENGINE.address_to_script(addr)
path = self.script_to_path(script) path = self.script_to_path(script)
privkey = self._get_priv_from_path(path)[0] privkey = self._get_priv_from_path(path)[0]
return hexlify(privkey) return hexlify(privkey).decode('ascii')
def get_external_addr(self, mixdepth): def get_external_addr(self, mixdepth):
""" """
@ -439,7 +447,7 @@ class BaseWallet(object):
removed_utxos = {} removed_utxos = {}
for (txid, index), val in ret.items(): for (txid, index), val in ret.items():
val['address'] = self.get_addr_path(val['path']) 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 return removed_utxos
def remove_old_utxos_(self, tx): def remove_old_utxos_(self, tx):
@ -526,7 +534,7 @@ class BaseWallet(object):
ret_conv = {} ret_conv = {}
for utxo, data in ret.items(): for utxo, data in ret.items():
addr = self.get_addr_path(data['path']) 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']} ret_conv[utxo_txt] = {'address': addr, 'value': data['value']}
return ret_conv return ret_conv
@ -580,7 +588,7 @@ class BaseWallet(object):
utxos_conv = collections.defaultdict(dict) utxos_conv = collections.defaultdict(dict)
for md, utxos in ret.items(): for md, utxos in ret.items():
for utxo, data in utxos.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']) addr = self.get_addr_path(data['path'])
data['address'] = addr data['address'] = addr
utxos_conv[md][utxo_str] = data utxos_conv[md][utxo_str] = data
@ -1060,6 +1068,7 @@ class BIP32Wallet(BaseWallet):
_master_entropy = self._create_master_key() _master_entropy = self._create_master_key()
assert _master_entropy assert _master_entropy
assert isinstance(_master_entropy, bytes)
self._master_key = self._derive_bip32_master_key(_master_entropy) self._master_key = self._derive_bip32_master_key(_master_entropy)
# used to verify paths for sanity checking and for wallet id creation # 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) int_type = self._get_internal_type(internal)
path = self.get_path(mixdepth, int_type, index) path = self.get_path(mixdepth, int_type, index)
priv = self._ENGINE.derive_bip32_privkey(self._master_key, path) 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): def get_bip32_priv_export(self, mixdepth=None, internal=None):
path = self._get_bip32_export_path(mixdepth, internal) path = self._get_bip32_export_path(mixdepth, internal)
@ -1304,7 +1313,7 @@ class BIP32Wallet(BaseWallet):
return self._index_cache[mixdepth][int_type] return self._index_cache[mixdepth][int_type]
def get_mnemonic_words(self): def get_mnemonic_words(self):
return ' '.join(mn_encode(hexlify(self._entropy))), None return ' '.join(mn_encode(hexlify(self._entropy).decode('ascii'))), None
@classmethod @classmethod
def entropy_from_mnemonic(cls, seed): def entropy_from_mnemonic(cls, seed):
@ -1315,7 +1324,7 @@ class BIP32Wallet(BaseWallet):
return unhexlify(mn_decode(words)) return unhexlify(mn_decode(words))
def get_wallet_id(self): 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): def set_next_index(self, mixdepth, internal, index, force=False):
int_type = self._get_internal_type(internal) int_type = self._get_internal_type(internal)
@ -1334,7 +1343,7 @@ class LegacyWallet(ImportWalletMixin, BIP32Wallet):
_ENGINE = BTC_P2PKH _ENGINE = BTC_P2PKH
def _create_master_key(self): def _create_master_key(self):
return hexlify(self._entropy).encode('ascii') return hexlify(self._entropy)
def _get_bip32_base_path(self): def _get_bip32_base_path(self):
return self._key_ident, 0 return self._key_ident, 0

38
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 json
import os import os
import sys import sys
@ -12,7 +15,7 @@ from jmclient import (get_network, WALLET_IMPLEMENTATIONS, Storage, podle,
VolatileStorage, StoragePasswordError, VolatileStorage, StoragePasswordError,
is_segwit_mode, SegwitLegacyWallet, LegacyWallet) is_segwit_mode, SegwitLegacyWallet, LegacyWallet)
from jmbase.support import get_password 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 import jmclient.btc as btc
@ -326,7 +329,7 @@ def wallet_showutxos(wallet, showprivkey):
unsp[u]['privkey'] = wallet.get_wif_path(av['path']) unsp[u]['privkey'] = wallet.get_wif_path(av['path'])
used_commitments, external_commitments = podle.get_podle_commitments() 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, tries = podle.get_podle_tries(utxo=u, max_tries=max_tries,
external=True) external=True)
tries_remaining = max(0, max_tries - tries) tries_remaining = max(0, max_tries - tries)
@ -342,7 +345,7 @@ def wallet_display(wallet, gaplimit, showprivkey, displayall=False,
else return the WalletView object. else return the WalletView object.
""" """
acctlist = [] acctlist = []
for m in xrange(wallet.mixdepth + 1): for m in range(wallet.mixdepth + 1):
branchlist = [] branchlist = []
for forchange in [0, 1]: for forchange in [0, 1]:
entrylist = [] entrylist = []
@ -353,7 +356,7 @@ def wallet_display(wallet, gaplimit, showprivkey, displayall=False,
xpub_key = "" xpub_key = ""
unused_index = wallet.get_next_unused_index(m, forchange) 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) path = wallet.get_path(m, forchange, k)
addr = wallet.get_addr_path(path) addr = wallet.get_addr_path(path)
balance = 0 balance = 0
@ -398,7 +401,7 @@ def cli_get_wallet_passphrase_check():
return password return password
def cli_get_wallet_file_name(): 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): def cli_display_user_words(words, mnemonic_extension):
text = 'Write down this wallet recovery mnemonic\n\n' + words +'\n' 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) print(text)
def cli_user_mnemonic_entry(): def cli_user_mnemonic_entry():
mnemonic_phrase = raw_input("Input mnemonic recovery phrase: ") mnemonic_phrase = input("Input mnemonic recovery phrase: ")
mnemonic_extension = raw_input("Input mnemonic extension, leave blank if there isnt one: ") mnemonic_extension = input("Input mnemonic extension, leave blank if there isnt one: ")
if len(mnemonic_extension.strip()) == 0: if len(mnemonic_extension.strip()) == 0:
mnemonic_extension = None mnemonic_extension = None
return (mnemonic_phrase, mnemonic_extension) return (mnemonic_phrase, mnemonic_extension)
def cli_get_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): ") "phrase? write 'n' if you don't know what this is (y/n): ")
if len(uin) == 0 or uin[0] != 'y': if len(uin) == 0 or uin[0] != 'y':
print("Not using mnemonic extension") print("Not using mnemonic extension")
return None #no mnemonic extension return None #no mnemonic extension
print("Note: This will be stored in a reversible way. Do not reuse!") 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, def wallet_generate_recover_bip39(method, walletspath, default_wallet_name,
@ -483,7 +486,7 @@ def wallet_generate_recover(method, walletspath,
entropy = None entropy = None
if method == 'recover': if method == 'recover':
seed = raw_input("Input 12 word recovery seed: ") seed = input("Input 12 word recovery seed: ")
try: try:
entropy = LegacyWallet.entropy_from_mnemonic(seed) entropy = LegacyWallet.entropy_from_mnemonic(seed)
except WalletError as e: except WalletError as e:
@ -510,10 +513,17 @@ def wallet_generate_recover(method, walletspath,
return True 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): def wallet_fetch_history(wallet, options):
# sort txes in a db because python can be really bad with large lists # sort txes in a db because python can be really bad with large lists
con = sqlite3.connect(":memory:") con = sqlite3.connect(":memory:")
con.row_factory = sqlite3.Row con.row_factory = dict_factory
tx_db = con.cursor() tx_db = con.cursor()
tx_db.execute("CREATE TABLE transactions(txid TEXT, " tx_db.execute("CREATE TABLE transactions(txid TEXT, "
"blockhash TEXT, blocktime INTEGER);") "blockhash TEXT, blocktime INTEGER);")
@ -665,7 +675,7 @@ def wallet_fetch_history(wallet, options):
amount = cj_amount amount = cj_amount
delta_balance = out_value - our_input_value delta_balance = out_value - our_input_value
mixdepth_src = wallet.get_script_mixdepth(list(our_input_scripts)[0]) 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] if v == cj_amount]).intersection(our_output_scripts))[0]
mixdepth_dst = wallet.get_script_mixdepth(cj_script) mixdepth_dst = wallet.get_script_mixdepth(cj_script)
else: else:
@ -781,7 +791,7 @@ def wallet_importprivkey(wallet, mixdepth, key_type):
print("WARNING: Handling of raw ECDSA bitcoin private keys can lead to " print("WARNING: Handling of raw ECDSA bitcoin private keys can lead to "
"non-intuitive behaviour and loss of funds.\n Recommended instead " "non-intuitive behaviour and loss of funds.\n Recommended instead "
"is to use the \'sweep\' feature of sendpayment.py.") "is to use the \'sweep\' feature of sendpayment.py.")
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() privkeys = privkeys.split(',') if ',' in privkeys else privkeys.split()
imported_addr = [] imported_addr = []
import_failed = 0 import_failed = 0

9
jmclient/jmclient/yieldgenerator.py

@ -1,5 +1,8 @@
#! /usr/bin/env python #! /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 datetime
import os import os
@ -79,7 +82,7 @@ class YieldGeneratorBasic(YieldGenerator):
def create_my_orders(self): def create_my_orders(self):
mix_balance = self.wallet.get_balance_by_mixdepth(verbose=False) 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') jlog.error('do not have any coins left')
return [] return []
@ -118,7 +121,7 @@ class YieldGeneratorBasic(YieldGenerator):
max_mix = max(mix_balance, key=mix_balance.get) max_mix = max(mix_balance, key=mix_balance.get)
filtered_mix_balance = [m filtered_mix_balance = [m
for m in mix_balance.iteritems() for m in iteritems(mix_balance)
if m[1] >= total_amount] if m[1] >= total_amount]
if not filtered_mix_balance: if not filtered_mix_balance:
return None, None, None return None, None, None

2
jmclient/setup.py

@ -9,5 +9,5 @@ setup(name='joinmarketclient',
author_email='', author_email='',
license='GPL', license='GPL',
packages=['jmclient'], 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) zip_safe=False)

10
jmclient/test/commontest.py

@ -1,5 +1,7 @@
#! /usr/bin/env python #! /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''' '''Some helper functions for testing'''
import os import os
@ -131,7 +133,7 @@ def make_sign_and_push(ins_full,
from wallets from wallets
""" """
total = sum(x['value'] for x in ins_full.values()) 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 #random output address and change addr
output_addr = wallet.get_new_addr(1, 1) if not output_addr else output_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 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) binarize_tx(de_tx)
de_tx = wallet.sign_tx(de_tx, scripts, hashcode=hashcode) de_tx = wallet.sign_tx(de_tx, scripts, hashcode=hashcode)
#pushtx returns False on any error #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) push_succeed = jm_single().bc_interface.pushtx(tx)
if push_succeed: if push_succeed:
return btc.txhash(tx) return btc.txhash(tx)
@ -178,7 +180,7 @@ def make_wallets(n,
if len(wallet_structures) != n: if len(wallet_structures) != n:
raise Exception("Number of wallets doesn't match wallet structures") raise Exception("Number of wallets doesn't match wallet structures")
if not fixed_seeds: 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) BIP32Wallet.ENTROPY_BYTES * 2)
else: else:
seeds = fixed_seeds seeds = fixed_seeds

3
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 #orderbook
t_orderbook = [{u'counterparty': u'J6FA1Gj7Ln4vSGne', u'ordertype': u'swreloffer', u'oid': 0, 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'}, u'minsize': 7500000, u'txfee': 1000, u'maxsize': 599972700, u'cjfee': u'0.0002'},

13
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 import jmclient.slowaes as sa
"""test general AES operation; probably not needed. """test general AES operation; probably not needed.
Not included in coverage, but should be included in suite.""" Not included in coverage, but should be included in suite."""
import os import os
import sys
import pytest import pytest
def test_pkcs7_bad_padding(): def test_pkcs7_bad_padding():
#used in seed decryption; check that it throws #used in seed decryption; check that it throws
#if wrongly padded (this caused a REAL bug before!) #if wrongly padded (this caused a REAL bug before!)
bad_padded = ['\x07'*14, '\x07'*31, '\x07'*31+'\x11', '\x07'*31+'\x00', bad_padded = [b'\x07'*14, b'\x07'*31, b'\x07'*31+b'\x11', b'\x07'*31+b'\x00',
'\x07'*14+'\x01\x02'] b'\x07'*14+b'\x01\x02']
for b in bad_padded: for b in bad_padded:
with pytest.raises(Exception) as e_info: with pytest.raises(Exception) as e_info:
fake_unpadded = sa.strip_PKCS7_padding(b) fake_unpadded = sa.strip_PKCS7_padding(b)
@ -19,7 +23,10 @@ def test_aes():
92] 92]
for ks in [16,24,32]: for ks in [16,24,32]:
for mode in ["CFB", "CBC", "OFB"]: 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() moo = sa.AESModeOfOperation()
mode, orig_len, ciph = moo.encrypt(cleartext, moo.modeOfOperation[mode], mode, orig_len, ciph = moo.encrypt(cleartext, moo.modeOfOperation[mode],
cypherkey, ks, cypherkey, ks,

4
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 from jmclient import Argon2Hash, get_random_bytes

4
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.""" """Blockchaininterface functionality tests."""

4
jmclient/test/test_client_protocol.py

@ -1,5 +1,7 @@
#! /usr/bin/env python #! /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.''' '''test client-protocol interfacae.'''
from jmclient import load_program_config, Taker, get_log,\ from jmclient import load_program_config, Taker, get_log,\

4
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 Test doing full coinjoins, bypassing IRC

4
jmclient/test/test_commitment_utils.py

@ -1,5 +1,7 @@
#!/usr/bin/env python #!/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 from commontest import DummyBlockchainInterface
import pytest import pytest

4
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.''' '''test configure module.'''
import pytest import pytest

26
jmclient/test/test_maker.py

@ -1,6 +1,8 @@
#!/usr/bin/env python #!/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, \ from jmclient import Maker, btc, get_p2sh_vbyte, get_p2pk_vbyte, \
load_program_config, jm_single 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): def create_tx_inputs(count=1):
inp = [] inp = []
for i in xrange(count): for i in range(count):
inp.append({'outpoint': {'hash': '0'*64, 'index': i}, inp.append({'outpoint': {'hash': '0'*64, 'index': i},
'script': '', 'script': '',
'sequence': 4294967295}) 'sequence': 4294967295})
@ -71,9 +73,9 @@ def address_p2sh_generator():
def get_address_generator(script_pre, script_post, vbyte): def get_address_generator(script_pre, script_post, vbyte):
counter = 0 counter = 0
while True: 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) addr = btc.script_to_address(script, vbyte)
yield addr, binascii.hexlify(script) yield addr, binascii.hexlify(script).decode('ascii')
counter += 1 counter += 1
@ -120,21 +122,21 @@ def test_verify_unsigned_tx_sw_valid(setup_env_nodeps):
# test standard cj # test standard cj
tx, offerlist = create_tx_and_offerlist(cj_addr, changeaddr, 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" assert maker.verify_unsigned_tx(tx, offerlist) == (True, None), "standard sw cj"
# test cj with mixed outputs # test cj with mixed outputs
tx, offerlist = create_tx_and_offerlist(cj_addr, changeaddr, tx, offerlist = create_tx_and_offerlist(cj_addr, changeaddr,
list(chain((next(p2sh_gen)[1] for s in xrange(3)), list(chain((next(p2sh_gen)[1] for s in range(3)),
(next(p2pkh_gen)[1] for s in xrange(1)))), (next(p2pkh_gen)[1] for s in range(1)))),
cj_script, cj_change_script) cj_script, cj_change_script)
assert maker.verify_unsigned_tx(tx, offerlist) == (True, None), "sw cj with p2pkh output" assert maker.verify_unsigned_tx(tx, offerlist) == (True, None), "sw cj with p2pkh output"
# test cj with only p2pkh outputs # test cj with only p2pkh outputs
tx, offerlist = create_tx_and_offerlist(cj_addr, changeaddr, 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" 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 # test standard cj
tx, offerlist = create_tx_and_offerlist(cj_addr, changeaddr, 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" assert maker.verify_unsigned_tx(tx, offerlist) == (True, None), "standard nonsw cj"
# test cj with mixed outputs # test cj with mixed outputs
tx, offerlist = create_tx_and_offerlist(cj_addr, changeaddr, tx, offerlist = create_tx_and_offerlist(cj_addr, changeaddr,
list(chain((next(p2sh_gen)[1] for s in xrange(1)), list(chain((next(p2sh_gen)[1] for s in range(1)),
(next(p2pkh_gen)[1] for s in xrange(3)))), (next(p2pkh_gen)[1] for s in range(3)))),
cj_script, cj_change_script, 'reloffer') cj_script, cj_change_script, 'reloffer')
assert maker.verify_unsigned_tx(tx, offerlist) == (True, None), "nonsw cj with p2sh output" assert maker.verify_unsigned_tx(tx, offerlist) == (True, None), "nonsw cj with p2sh output"
# test cj with only p2sh outputs # test cj with only p2sh outputs
tx, offerlist = create_tx_and_offerlist(cj_addr, changeaddr, 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" assert maker.verify_unsigned_tx(tx, offerlist) == (True, None), "nonsw cj with only p2sh outputs"

9
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 from jmclient import old_mnemonic
import pytest import pytest
@ -43,12 +46,12 @@ def test_old_mnemonic(seedphrase, key, valid):
#Already known error condition: an incorrectly short #Already known error condition: an incorrectly short
#word list will NOT throw an error; this is handled by calling code #word list will NOT throw an error; this is handled by calling code
if len(seedphrase) < 12: if len(seedphrase) < 12:
print "For known failure case of seedphrase less than 12: " print("For known failure case of seedphrase less than 12: ")
print old_mnemonic.mn_decode(seedphrase) print(old_mnemonic.mn_decode(seedphrase))
else: else:
with pytest.raises(Exception) as e_info: with pytest.raises(Exception) as e_info:
dummy = old_mnemonic.mn_decode(seedphrase) 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))

21
jmclient/test/test_podle.py

@ -1,9 +1,12 @@
#! /usr/bin/env python #! /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.''' '''Tests of Proof of discrete log equivalence commitments.'''
import os import os
import jmbitcoin as bitcoin import jmbitcoin as bitcoin
import binascii import binascii
import struct
import json import json
import pytest import pytest
import copy import copy
@ -46,7 +49,7 @@ def generate_single_podle_sig(priv, i):
library 'generate_podle' which intelligently searches and updates commitments. library 'generate_podle' which intelligently searches and updates commitments.
""" """
dummy_utxo = bitcoin.sha256(priv) + ":3" 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) r = podle.generate_podle(i)
return (r['P'], r['P2'], r['sig'], return (r['P'], r['P2'], r['sig'],
r['e'], r['commit']) r['e'], r['commit'])
@ -112,9 +115,9 @@ def test_external_commitments(setup_podle):
known_utxos = [] known_utxos = []
tries = 3 tries = 3
for i in range(1, 6): 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) known_utxos.append(u)
priv = chr(i)*32+"\x01" priv = struct.pack(b'B', i)*32+b"\x01"
ecs[u] = {} ecs[u] = {}
ecs[u]['reveal']={} ecs[u]['reveal']={}
for j in range(tries): 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 #this should find the remaining one utxo and return from it
assert generate_podle([], max_tries=tries, allow_external=known_utxos) assert generate_podle([], max_tries=tries, allow_external=known_utxos)
#test commitment removal #test commitment removal
to_remove = ecs[binascii.hexlify(chr(3)*32)] to_remove = ecs[binascii.hexlify(struct.pack(b'B', 3)*32).decode('ascii')]
update_commitments(external_to_remove={binascii.hexlify(chr(3)*32):to_remove}) update_commitments(external_to_remove={binascii.hexlify(struct.pack(b'B', 3)*32).decode('ascii'):to_remove})
#test that an incorrectly formatted file raises #test that an incorrectly formatted file raises
with open(get_commitment_file(), "rb") as f: 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) corruptjson = copy.deepcopy(validjson)
del corruptjson['used'] del corruptjson['used']
with open(get_commitment_file(), "wb") as f: 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: with pytest.raises(PoDLEError) as e_info:
get_podle_commitments() get_podle_commitments()
#clean up #clean up
with open(get_commitment_file(), "wb") as f: 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'))

6
jmclient/test/test_schedule.py

@ -1,5 +1,7 @@
#! /usr/bin/env python #! /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.''' '''test schedule module.'''
import pytest import pytest
@ -42,7 +44,7 @@ def test_get_schedule():
if os.path.exists(tsf): if os.path.exists(tsf):
os.remove(tsf) os.remove(tsf)
with open(tsf, "wb") as f: with open(tsf, "wb") as f:
f.write(s) f.write(s.encode('utf-8'))
result = get_schedule(tsf) result = get_schedule(tsf)
if s== valids: if s== valids:
assert result[0] assert result[0]

4
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 from jmclient import storage
import pytest import pytest

4
jmclient/test/test_support.py

@ -1,5 +1,7 @@
#! /usr/bin/env python #! /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.''' '''support functions for jmclient tests.'''
import pytest import pytest

26
jmclient/test/test_taker.py

@ -1,5 +1,8 @@
#!/usr/bin/env python #!/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 from commontest import DummyBlockchainInterface
import jmbitcoin as bitcoin import jmbitcoin as bitcoin
import binascii import binascii
@ -8,6 +11,7 @@ import copy
import shutil import shutil
import pytest import pytest
import json import json
import struct
from base64 import b64encode from base64 import b64encode
from jmclient import load_program_config, jm_single, set_commitment_file,\ from jmclient import load_program_config, jm_single, set_commitment_file,\
get_commitment_file, SegwitLegacyWallet, Taker, VolatileStorage,\ get_commitment_file, SegwitLegacyWallet, Taker, VolatileStorage,\
@ -84,7 +88,7 @@ class DummyWallet(SegwitLegacyWallet):
def get_key_from_addr(self, addr): def get_key_from_addr(self, addr):
"""usable addresses: privkey all 1s, 2s, 3s, ... :""" """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 = {} addrs = {}
""" """
mrcNu71ztWjAQA6ww9kHiW3zBWSQidHXTQ mrcNu71ztWjAQA6ww9kHiW3zBWSQidHXTQ
@ -95,9 +99,9 @@ class DummyWallet(SegwitLegacyWallet):
""" """
for p in privs: for p in privs:
addrs[p] = bitcoin.privkey_to_address(p, False, magicbyte=0x6f) 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: if a == addr:
return binascii.hexlify(p) return binascii.hexlify(p).decode('ascii')
raise ValueError("No such keypair") raise ValueError("No such keypair")
def _is_my_bip32_path(self, path): def _is_my_bip32_path(self, path):
@ -154,7 +158,7 @@ def test_make_commitment(createcmtdata, failquery, external):
os.remove('dummyext') os.remove('dummyext')
old_commitment_file = get_commitment_file() old_commitment_file = get_commitment_file()
with open('dummyext', 'wb') as f: 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: if external:
set_commitment_file('dummyext') set_commitment_file('dummyext')
old_taker_utxo_age = jm_single().config.get("POLICY", "taker_utxo_age") 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() return clean_up()
if schedule[0][1] == 199599800: if schedule[0][1] == 199599800:
#need to force negative fees to make this feasible #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' v['cjfee'] = '-0.002'
# change_amount = (total_input - self.cjamount - # change_amount = (total_input - self.cjamount -
# self.orderbook[nick]['txfee'] + real_cjfee) # 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; #TODO note this test is not adequate, because the code is not;
#the code does not *DO* anything if a condition is unexpected. #the code does not *DO* anything if a condition is unexpected.
taker.input_utxos = copy.deepcopy(t_utxos_by_mixdepth)[0] 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"]) v["value"] = int(0.999805228 * v["value"])
res = taker.receive_utxos(maker_response) res = taker.receive_utxos(maker_response)
assert res[0] assert res[0]
@ -389,7 +393,7 @@ def test_on_sig(createcmtdata, dummyaddr, schedule):
#return the right values in query_utxo_set #return the right values in query_utxo_set
#create 2 privkey + utxos that are to be ours #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)] utxos = [str(x)*64+":1" for x in range(5)]
fake_query_results = [{'value': 200000000, fake_query_results = [{'value': 200000000,
'utxo': utxos[x], 'utxo': utxos[x],
@ -426,12 +430,12 @@ def test_on_sig(createcmtdata, dummyaddr, schedule):
taker.my_cj_addr = dummyaddr taker.my_cj_addr = dummyaddr
#make signatures for the last 3 fake utxos, considered as "not ours": #make signatures for the last 3 fake utxos, considered as "not ours":
tx3 = bitcoin.sign(tx, 2, privs[2]) 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) taker.on_sig("cp1", sig3)
#try sending the same sig again; should be ignored #try sending the same sig again; should be ignored
taker.on_sig("cp1", sig3) taker.on_sig("cp1", sig3)
tx4 = bitcoin.sign(tx, 3, privs[3]) 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 #try sending junk instead of cp2's correct sig
taker.on_sig("cp2", str("junk")) taker.on_sig("cp2", str("junk"))
taker.on_sig("cp2", sig4) 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 #signing, try with an injected failure of query utxo set, which should
#prevent this signature being accepted. #prevent this signature being accepted.
dbci.setQUSFail(True) 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) taker.on_sig("cp3", sig5)
#allow it to succeed, and try again #allow it to succeed, and try again
dbci.setQUSFail(False) dbci.setQUSFail(False)

25
jmclient/test/test_tx_creation.py

@ -1,10 +1,13 @@
#! /usr/bin/env python #! /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 '''Test of unusual transaction types creation and push to
network to check validity.''' network to check validity.'''
import time import time
import binascii import binascii
import struct
from commontest import make_wallets, make_sign_and_push from commontest import make_wallets, make_sign_and_push
import jmbitcoin as bitcoin import jmbitcoin as bitcoin
@ -59,7 +62,7 @@ def test_mktx(setup_tx_creation):
"""Testing exceptional conditions; not guaranteed """Testing exceptional conditions; not guaranteed
to create valid tx objects""" to create valid tx objects"""
#outpoint structure must be {"outpoint":{"hash":hash, "index": num}} #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"]] "script": "", "sequence": 4294967295} for x in ["a", "b", "c"]]
pub = vpubs[0] pub = vpubs[0]
addr = bitcoin.pubkey_to_address(pub, magicbyte=get_p2pk_vbyte()) 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): def test_bintxhash(setup_tx_creation):
tx = "abcdef1234" tx = "abcdef1234"
x = bitcoin.bin_txhash(tx) 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): def test_all_same_priv(setup_tx_creation):
#recipient #recipient
@ -88,7 +91,7 @@ def test_all_same_priv(setup_tx_creation):
sync_wallet(wallet, fast=True) sync_wallet(wallet, fast=True)
insfull = wallet.select_utxos(0, 110000000) insfull = wallet.select_utxos(0, 110000000)
outs = [{"address": addr, "value": 1000000}] outs = [{"address": addr, "value": 1000000}]
ins = insfull.keys() ins = list(insfull.keys())
tx = bitcoin.mktx(ins, outs) tx = bitcoin.mktx(ins, outs)
tx = bitcoin.signall(tx, wallet.get_key_from_addr(addrinwallet)) 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) print(insfull)
if not mktxlist: if not mktxlist:
outs = [{"address": addr, "value": 1000000}] outs = [{"address": addr, "value": 1000000}]
ins = insfull.keys() ins = list(insfull.keys())
tx = bitcoin.mktx(ins, outs) tx = bitcoin.mktx(ins, outs)
else: else:
out1 = addr+":1000000" out1 = addr+":1000000"
ins0, ins1 = insfull.keys() ins0, ins1 = list(insfull.keys())
print("INS0 is: " + str(ins0)) print("INS0 is: " + str(ins0))
print("INS1 is: " + str(ins1)) print("INS1 is: " + str(ins1))
tx = bitcoin.mktx(ins0, ins1, out1) 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 = binascii.unhexlify(tx)
tx = bitcoin.sign(tx, index, priv) tx = bitcoin.sign(tx, index, priv)
if index % 2: if index % 2:
tx = binascii.hexlify(tx) tx = binascii.hexlify(tx).decode('ascii')
desertx2 = bitcoin.deserialize(tx) desertx2 = bitcoin.deserialize(tx)
print(desertx2) print(desertx2)
sig, pub = bitcoin.deserialize_script(desertx2['ins'][0]['script']) 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) sync_wallet(wallet, fast=True)
amount = 350000000 amount = 350000000
ins_full = wallet.select_utxos(0, amount) 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) txid = make_sign_and_push(ins_full, wallet, amount, hashcode=sighash)
assert txid assert txid
@ -180,8 +183,8 @@ def test_create_sighash_txs(setup_tx_creation):
def test_spend_p2sh_utxos(setup_tx_creation): def test_spend_p2sh_utxos(setup_tx_creation):
#make a multisig address from 3 privs #make a multisig address from 3 privs
privs = [chr(x) * 32 + '\x01' for x in range(1, 4)] privs = [struct.pack(b'B', x) * 32 + b'\x01' for x in range(1, 4)]
pubs = [bitcoin.privkey_to_pubkey(binascii.hexlify(priv)) for priv in privs] pubs = [bitcoin.privkey_to_pubkey(binascii.hexlify(priv).decode('ascii')) for priv in privs]
script = bitcoin.mk_multisig_script(pubs, 2) script = bitcoin.mk_multisig_script(pubs, 2)
msig_addr = bitcoin.scriptaddr(script, magicbyte=196) msig_addr = bitcoin.scriptaddr(script, magicbyte=196)
#pay into it #pay into it
@ -203,7 +206,7 @@ def test_spend_p2sh_utxos(setup_tx_creation):
tx = bitcoin.mktx(ins, outs) tx = bitcoin.mktx(ins, outs)
sigs = [] sigs = []
for priv in privs[:2]: 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) tx = bitcoin.apply_multisignatures(tx, 0, script, sigs)
txid = jm_single().bc_interface.pushtx(tx) txid = jm_single().bc_interface.pushtx(tx)
assert txid assert txid

16
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 jmclient.wallet import UTXOManager
from test_storage import MockStorage from test_storage import MockStorage
@ -32,9 +34,9 @@ def test_utxomanager_persist(setup_env_nodeps):
um = UTXOManager(storage, select) um = UTXOManager(storage, select)
assert um.have_utxo(txid, index) is mixdepth assert um.have_utxo(txid, index) == mixdepth
assert um.have_utxo(txid, index+1) is mixdepth + 1 assert um.have_utxo(txid, index+1) == mixdepth + 1
assert um.have_utxo(txid, index+2) is False assert um.have_utxo(txid, index+2) == False
utxos = um.get_utxos_by_mixdepth() utxos = um.get_utxos_by_mixdepth()
assert len(utxos[mixdepth]) == 1 assert len(utxos[mixdepth]) == 1
@ -46,15 +48,15 @@ def test_utxomanager_persist(setup_env_nodeps):
assert balances[mixdepth+1] == value assert balances[mixdepth+1] == value
um.remove_utxo(txid, index, mixdepth) um.remove_utxo(txid, index, mixdepth)
assert um.have_utxo(txid, index) is False assert um.have_utxo(txid, index) == False
um.save() um.save()
del um del um
um = UTXOManager(storage, select) um = UTXOManager(storage, select)
assert um.have_utxo(txid, index) is False assert um.have_utxo(txid, index) == False
assert um.have_utxo(txid, index+1) is mixdepth + 1 assert um.have_utxo(txid, index+1) == mixdepth + 1
utxos = um.get_utxos_by_mixdepth() utxos = um.get_utxos_by_mixdepth()
assert len(utxos[mixdepth]) == 0 assert len(utxos[mixdepth]) == 0

4
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.configure import validate_address, load_program_config
from jmclient import jm_single from jmclient import jm_single
import json import json

22
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.''' '''Wallet functionality tests.'''
import os import os
@ -64,14 +66,14 @@ def get_bip39_vectors():
def test_bip39_seeds(monkeypatch, setup_wallet, entropy, mnemonic, key, xpriv): def test_bip39_seeds(monkeypatch, setup_wallet, entropy, mnemonic, key, xpriv):
jm_single().config.set('BLOCKCHAIN', 'network', 'mainnet') jm_single().config.set('BLOCKCHAIN', 'network', 'mainnet')
created_entropy = SegwitLegacyWallet.entropy_from_mnemonic(mnemonic) created_entropy = SegwitLegacyWallet.entropy_from_mnemonic(mnemonic)
assert entropy == hexlify(created_entropy) assert entropy == hexlify(created_entropy).decode('ascii')
storage = VolatileStorage() storage = VolatileStorage()
SegwitLegacyWallet.initialize( SegwitLegacyWallet.initialize(
storage, get_network(), entropy=created_entropy, storage, get_network(), entropy=created_entropy,
entropy_extension=b'TREZOR', max_mixdepth=4) entropy_extension=b'TREZOR', max_mixdepth=4)
wallet = SegwitLegacyWallet(storage) wallet = SegwitLegacyWallet(storage)
assert (mnemonic, b'TREZOR') == wallet.get_mnemonic_words() 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 # need to monkeypatch this, else we'll default to the BIP-49 path
monkeypatch.setattr(SegwitLegacyWallet, '_get_bip32_base_path', monkeypatch.setattr(SegwitLegacyWallet, '_get_bip32_base_path',
@ -93,7 +95,7 @@ def test_bip49_seed(monkeypatch, setup_wallet):
wallet = SegwitLegacyWallet(storage) wallet = SegwitLegacyWallet(storage)
assert (mnemonic, None) == wallet.get_mnemonic_words() assert (mnemonic, None) == wallet.get_mnemonic_words()
assert account0_xpriv == wallet.get_bip32_priv_export(0) 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 # FIXME: is this desired behaviour? BIP49 wallet will not return xpriv for
# the root key but only for key after base path # 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 MIXDEPTH = 0
path = wallet.import_private_key(MIXDEPTH, wif, keytype) path = wallet.import_private_key(MIXDEPTH, wif, keytype)
utxo = fund_wallet_addr(wallet, wallet.get_addr_path(path)) 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)])) ['00'*17 + ':' + str(10**8 - 9000)]))
binarize_tx(tx) binarize_tx(tx)
script = wallet.get_script_path(path) script = wallet.get_script_path(path)
wallet.sign_tx(tx, {0: (script, 10**8)}) wallet.sign_tx(tx, {0: (script, 10**8)})
type_check(tx) 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 assert txout
@ -318,13 +320,13 @@ def test_signing_simple(setup_wallet, wallet_cls, type_check):
wallet_cls.initialize(storage, get_network()) wallet_cls.initialize(storage, get_network())
wallet = wallet_cls(storage) wallet = wallet_cls(storage)
utxo = fund_wallet_addr(wallet, wallet.get_internal_addr(0)) 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)])) ['00'*17 + ':' + str(10**8 - 9000)]))
binarize_tx(tx) binarize_tx(tx)
script = wallet.get_script(0, 1, 0) script = wallet.get_script(0, 1, 0)
wallet.sign_tx(tx, {0: (script, 10**8)}) wallet.sign_tx(tx, {0: (script, 10**8)})
type_check(tx) 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 assert txout
@ -381,7 +383,7 @@ def test_add_new_utxos(setup_wallet):
tx_scripts.append(b'\x22'*17) tx_scripts.append(b'\x22'*17)
tx = btc.deserialize(btc.mktx( 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])) for s in tx_scripts]))
binarize_tx(tx) binarize_tx(tx)
txid = b'\x01' * 32 txid = b'\x01' * 32
@ -417,7 +419,7 @@ def test_remove_old_utxos(setup_wallet):
tx_inputs.append((b'\x12'*32, 6)) tx_inputs.append((b'\x12'*32, 6))
tx = btc.deserialize(btc.mktx( 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)])) ['0' * 36 + ':' + str(3 * 10**8 - 1000)]))
binarize_tx(tx) binarize_tx(tx)

12
jmclient/test/test_wallets.py

@ -1,19 +1,19 @@
#! /usr/bin/env python #! /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.''' '''Wallet functionality tests.'''
import sys
import os import os
import time import time
import binascii import binascii
from mnemonic import Mnemonic
from commontest import create_wallet_for_sync, make_sign_and_push from commontest import create_wallet_for_sync, make_sign_and_push
import json import json
import pytest import pytest
from jmclient import ( from jmclient import (
load_program_config, jm_single, sync_wallet, get_log, 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 from taker_test_data import t_raw_signed_tx
testdir = os.path.dirname(os.path.realpath(__file__)) testdir = os.path.dirname(os.path.realpath(__file__))
log = get_log() log = get_log()
@ -94,9 +94,7 @@ def check_bip39_case(vectors, language="english"):
mnemo = Mnemonic(language) mnemo = Mnemonic(language)
for v in vectors: for v in vectors:
code = mnemo.to_mnemonic(binascii.unhexlify(v[0])) code = mnemo.to_mnemonic(binascii.unhexlify(v[0]))
seed = binascii.hexlify(Mnemonic.to_seed(code, passphrase=v[4])) seed = binascii.hexlify(Mnemonic.to_seed(code, passphrase=v[4])).decode('ascii')
if sys.version >= '3':
seed = seed.decode('utf8')
print('checking this phrase: ' + v[1]) print('checking this phrase: ' + v[1])
assert mnemo.check(v[1]) assert mnemo.check(v[1])
assert v[1] == code assert v[1] == code

22
jmdaemon/jmdaemon/daemon_protocol.py

@ -2,6 +2,7 @@
from __future__ import (absolute_import, division, from __future__ import (absolute_import, division,
print_function, unicode_literals) print_function, unicode_literals)
from builtins import * from builtins import *
from future.utils import iteritems
from .message_channel import MessageChannelCollection from .message_channel import MessageChannelCollection
from .orderbookwatch import OrderbookWatch from .orderbookwatch import OrderbookWatch
@ -13,7 +14,6 @@ from .protocol import (COMMAND_PREFIX, ORDER_KEYS, NICK_HASH_LENGTH,
from .irc import IRCMessageChannel from .irc import IRCMessageChannel
from jmbase.commands import * from jmbase.commands import *
from jmbase import _byteify
from twisted.protocols import amp from twisted.protocols import amp
from twisted.internet import reactor, ssl from twisted.internet import reactor, ssl
from twisted.internet.protocol import ServerFactory from twisted.internet.protocol import ServerFactory
@ -72,7 +72,7 @@ def check_utxo_blacklist(commitment, persist=False):
fname = "commitmentlist" fname = "commitmentlist"
if os.path.isfile(fname): if os.path.isfile(fname):
with open(fname, "rb") as f: 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: else:
blacklisted_commitments = [] blacklisted_commitments = []
if commitment in blacklisted_commitments: if commitment in blacklisted_commitments:
@ -80,7 +80,7 @@ def check_utxo_blacklist(commitment, persist=False):
elif persist: elif persist:
blacklisted_commitments += [commitment] blacklisted_commitments += [commitment]
with open(fname, "wb") as f: with open(fname, "wb") as f:
f.write('\n'.join(blacklisted_commitments)) f.write('\n'.join(blacklisted_commitments).encode('ascii'))
f.flush() f.flush()
#If the commitment is new and we are *not* persisting, nothing to do #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 #(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 #Reset utxo data to null for this new transaction
self.ioauth_data = {} self.ioauth_data = {}
self.active_orders = json.loads(filled_offers) self.active_orders = json.loads(filled_offers)
for nick, offer_dict in self.active_orders.iteritems(): for nick, offer_dict in iteritems(self.active_orders):
offer_fill_msg = " ".join([str(offer_dict["oid"]), str(amount), str( offer_fill_msg = " ".join([str(offer_dict["oid"]), str(amount),
self.kp.hex_pk()), str(commitment)]) self.kp.hex_pk().decode('ascii'), str(commitment)])
self.mcc.prepare_privmsg(nick, "fill", offer_fill_msg) self.mcc.prepare_privmsg(nick, "fill", offer_fill_msg)
reactor.callLater(self.maker_timeout_sec, self.completeStage1) reactor.callLater(self.maker_timeout_sec, self.completeStage1)
self.jm_state = 2 self.jm_state = 2
@ -293,8 +293,6 @@ class JMDaemonServerProtocol(amp.AMP, OrderbookWatch):
channel based on data from Maker. Relevant data (utxos, addresses) channel based on data from Maker. Relevant data (utxos, addresses)
are stored in the active_orders dict keyed by the nick of the Taker. 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": if not self.role == "MAKER":
return return
if not nick in self.active_orders: if not nick in self.active_orders:
@ -324,7 +322,7 @@ class JMDaemonServerProtocol(amp.AMP, OrderbookWatch):
broadcast one by one. TODO: could shorten this, broadcast one by one. TODO: could shorten this,
have more than one sig per message. have more than one sig per message.
""" """
sigs = _byteify(json.loads(sigs)) sigs = json.loads(sigs)
for sig in sigs: for sig in sigs:
self.mcc.prepare_privmsg(nick, "sig", sig) self.mcc.prepare_privmsg(nick, "sig", sig)
return {"accepted": True} return {"accepted": True}
@ -395,7 +393,7 @@ class JMDaemonServerProtocol(amp.AMP, OrderbookWatch):
"offer": offer, "offer": offer,
"amount": amount, "amount": amount,
"commit": scommit} "commit": scommit}
self.mcc.prepare_privmsg(nick, "pubkey", kp.hex_pk()) self.mcc.prepare_privmsg(nick, "pubkey", kp.hex_pk().decode('ascii'))
@maker_only @maker_only
def on_seen_auth(self, nick, commitment_revelation): def on_seen_auth(self, nick, commitment_revelation):
@ -414,7 +412,7 @@ class JMDaemonServerProtocol(amp.AMP, OrderbookWatch):
commitment=ao["commit"], commitment=ao["commit"],
revelation=json.dumps(commitment_revelation), revelation=json.dumps(commitment_revelation),
amount=ao["amount"], amount=ao["amount"],
kphex=ao["kp"].hex_pk()) kphex=ao["kp"].hex_pk().decode('ascii'))
self.defaultCallbacks(d) self.defaultCallbacks(d)
@maker_only @maker_only
@ -576,7 +574,7 @@ class JMDaemonServerProtocol(amp.AMP, OrderbookWatch):
).fetchone() ).fetchone()
if crow is None: if crow is None:
return return
counterparty = crow[b'counterparty'] counterparty = crow['counterparty']
#TODO de-hardcode hp2 #TODO de-hardcode hp2
log.msg("Sending commitment to: " + str(counterparty)) log.msg("Sending commitment to: " + str(counterparty))
self.mcc.prepare_privmsg(counterparty, 'hp2', commit) self.mcc.prepare_privmsg(counterparty, 'hp2', commit)

6
jmdaemon/jmdaemon/enc_wrapper.py

@ -38,7 +38,7 @@ def get_pubkey(kp, as_hex=False):
optionally in hex.""" optionally in hex."""
if not isinstance(kp, public.SecretKey): if not isinstance(kp, public.SecretKey):
raise NaclError("Object is not a nacl keypair") 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): def init_pubkey(hexpk, fname=None):
@ -48,7 +48,7 @@ def init_pubkey(hexpk, fname=None):
""" """
try: try:
bin_pk = binascii.unhexlify(hexpk) bin_pk = binascii.unhexlify(hexpk)
except TypeError: except (TypeError, binascii.Error):
raise NaclError("Invalid hex") raise NaclError("Invalid hex")
if not len(bin_pk) == 32: if not len(bin_pk) == 32:
raise NaclError("Public key must be 32 bytes") raise NaclError("Public key must be 32 bytes")
@ -91,7 +91,7 @@ Notes:
# encoding for passing over the wire # encoding for passing over the wire
def encrypt_encode(msg, box): def encrypt_encode(msg, box):
encrypted = box.encrypt(msg) encrypted = box.encrypt(msg)
return base64.b64encode(encrypted) return base64.b64encode(encrypted).decode('ascii')
def decode_decrypt(msg, box): def decode_decrypt(msg, box):

6
jmdaemon/jmdaemon/irc.py

@ -78,7 +78,7 @@ class IRCMessageChannel(MessageChannel):
daemon=None): daemon=None):
MessageChannel.__init__(self, daemon=daemon) MessageChannel.__init__(self, daemon=daemon)
self.give_up = True 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 #default hostid for use with miniircd which doesnt send NETWORK
self.hostid = configdata['host'] + str(configdata['port']) self.hostid = configdata['host'] + str(configdata['port'])
self.socks5 = configdata["socks5"] self.socks5 = configdata["socks5"]
@ -152,7 +152,7 @@ class IRCMessageChannel(MessageChannel):
else: else:
try: try:
factory = TxIRCFactory(self) 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.channel)
self.tcp_connector = reactor.connectTCP( self.tcp_connector = reactor.connectTCP(
self.serverport[0], self.serverport[1], factory) self.serverport[0], self.serverport[1], factory)
@ -198,7 +198,7 @@ class txIRC_Client(irc.IRCClient, object):
def send(self, send_to, msg): def send(self, send_to, msg):
# todo: use proper twisted IRC support (encoding + sendCommand) # todo: use proper twisted IRC support (encoding + sendCommand)
omsg = 'PRIVMSG %s :' % (send_to,) + msg omsg = 'PRIVMSG %s :' % (send_to,) + msg
self.sendLine(omsg.encode('ascii')) self.sendLine(omsg)
def _pubmsg(self, message): def _pubmsg(self, message):
self.send(self.channel, message) self.send(self.channel, message)

18
jmdaemon/jmdaemon/message_channel.py

@ -2,8 +2,10 @@
from __future__ import (absolute_import, division, from __future__ import (absolute_import, division,
print_function, unicode_literals) print_function, unicode_literals)
from builtins import * # noqa: F401 from builtins import * # noqa: F401
from future.utils import iteritems
import abc import abc
import base64 import base64
import binascii
import threading import threading
from jmdaemon import encrypt_encode, decode_decrypt, COMMAND_PREFIX,\ from jmdaemon import encrypt_encode, decode_decrypt, COMMAND_PREFIX,\
NICK_HASH_LENGTH, NICK_MAX_ENCODED, plaintext_commands,\ NICK_HASH_LENGTH, NICK_MAX_ENCODED, plaintext_commands,\
@ -320,7 +322,7 @@ class MessageChannelCollection(object):
""" """
for mc in self.available_channels(): for mc in self.available_channels():
filtered_nick_order_dict = {k: v 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]} if mc == self.active_channels[k]}
mc.fill_orders(filtered_nick_order_dict, cj_amount, taker_pubkey, mc.fill_orders(filtered_nick_order_dict, cj_amount, taker_pubkey,
commitment) commitment)
@ -336,7 +338,7 @@ class MessageChannelCollection(object):
#TODO supporting sending to arbitrary nicks #TODO supporting sending to arbitrary nicks
#adds quite a bit of complexity, not supported #adds quite a bit of complexity, not supported
#initially; will fail if nick is not part of TX #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) self.prepare_privmsg(nick, "push", txb64)
def send_tx(self, nick_list, txhex): def send_tx(self, nick_list, txhex):
@ -356,11 +358,11 @@ class MessageChannelCollection(object):
tx_nick_sets[self.active_channels[nick]] = [nick] tx_nick_sets[self.active_channels[nick]] = [nick]
else: else:
tx_nick_sets[self.active_channels[nick]].append(nick) 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) self.prepare_send_tx(mc, nl, txhex)
def prepare_send_tx(self, mc, nick_list, 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: for nick in nick_list:
self.prepare_privmsg(nick, "tx", txb64, mc=mc) self.prepare_privmsg(nick, "tx", txb64, mc=mc)
@ -790,7 +792,7 @@ class MessageChannel(object):
# Taker callbacks # Taker callbacks
def fill_orders(self, nick_order_dict, cj_amount, taker_pubkey, commitment): 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 = str(order['oid']) + ' ' + str(cj_amount) + ' ' + taker_pubkey
msg += ' ' + commitment msg += ' ' + commitment
self.privmsg(c, 'fill', msg) self.privmsg(c, 'fill', msg)
@ -798,7 +800,7 @@ class MessageChannel(object):
def push_tx(self, nick, txhex): def push_tx(self, nick, txhex):
#Note: not currently used; will require prepare_privmsg call so #Note: not currently used; will require prepare_privmsg call so
#not in this class (see send_error) #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) self.privmsg(nick, 'push', txb64)
def send_error(self, nick, errormsg): def send_error(self, nick, errormsg):
@ -968,7 +970,7 @@ class MessageChannel(object):
elif _chunks[0] == 'tx': elif _chunks[0] == 'tx':
b64tx = _chunks[1] b64tx = _chunks[1]
try: try:
txhex = base64.b64decode(b64tx).encode('hex') txhex = binascii.hexlify(base64.b64decode(b64tx)).decode('ascii')
except TypeError as e: except TypeError as e:
self.send_error(nick, 'bad base64 tx. ' + repr(e)) self.send_error(nick, 'bad base64 tx. ' + repr(e))
return return
@ -977,7 +979,7 @@ class MessageChannel(object):
elif _chunks[0] == 'push': elif _chunks[0] == 'push':
b64tx = _chunks[1] b64tx = _chunks[1]
try: try:
txhex = base64.b64decode(b64tx).encode('hex') txhex = binascii.hexlify(base64.b64decode(b64tx)).decode('ascii')
except TypeError as e: except TypeError as e:
self.send_error(nick, 'bad base64 tx. ' + repr(e)) self.send_error(nick, 'bad base64 tx. ' + repr(e))
return return

40
jmdaemon/jmdaemon/orderbookwatch.py

@ -14,6 +14,13 @@ from jmbase.support import get_log, joinmarket_alert, DUST_THRESHOLD
log = get_log() 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): class JMTakerError(Exception):
pass pass
@ -30,11 +37,15 @@ class OrderbookWatch(object):
self.dblock = threading.Lock() self.dblock = threading.Lock()
con = sqlite3.connect(":memory:", check_same_thread=False) con = sqlite3.connect(":memory:", check_same_thread=False)
con.row_factory = sqlite3.Row con.row_factory = dict_factory
self.db = con.cursor() self.db = con.cursor()
self.db.execute("CREATE TABLE orderbook(counterparty TEXT, " try:
"oid INTEGER, ordertype TEXT, minsize INTEGER, " self.dblock.acquire(True)
"maxsize INTEGER, txfee INTEGER, cjfee TEXT);") 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 @staticmethod
def on_set_topic(newtopic): def on_set_topic(newtopic):
@ -59,7 +70,11 @@ class OrderbookWatch(object):
txfee, cjfee): txfee, cjfee):
try: try:
self.dblock.acquire(True) 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 " + log.debug("Got invalid order ID: " + oid + " from " +
counterparty) counterparty)
return return
@ -114,16 +129,25 @@ class OrderbookWatch(object):
self.dblock.release() self.dblock.release()
def on_order_cancel(self, counterparty, oid): def on_order_cancel(self, counterparty, oid):
with self.dblock: try:
self.dblock.acquire(True)
self.db.execute( self.db.execute(
("DELETE FROM orderbook WHERE " ("DELETE FROM orderbook WHERE "
"counterparty=? AND oid=?;"), (counterparty, oid)) "counterparty=? AND oid=?;"), (counterparty, oid))
finally:
self.dblock.release()
def on_nick_leave(self, nick): def on_nick_leave(self, nick):
with self.dblock: try:
self.dblock.acquire(True)
self.db.execute('DELETE FROM orderbook WHERE counterparty=?;', self.db.execute('DELETE FROM orderbook WHERE counterparty=?;',
(nick,)) (nick,))
finally:
self.dblock.release()
def on_disconnect(self): def on_disconnect(self):
with self.dblock: try:
self.dblock.acquire(True)
self.db.execute('DELETE FROM orderbook;') self.db.execute('DELETE FROM orderbook;')
finally:
self.dblock.release()

8
jmdaemon/jmdaemon/protocol.py

@ -17,10 +17,10 @@ offertypes = {"reloffer": [(int, "oid"), (int, "minsize"), (int, "maxsize"),
"swabsoffer": [(int, "oid"), (int, "minsize"), (int, "maxsize"), "swabsoffer": [(int, "oid"), (int, "minsize"), (int, "maxsize"),
(int, "txfee"), (int, "cjfee")]} (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', ORDER_KEYS = ['counterparty', 'oid', 'ordertype', 'minsize', 'maxsize', 'txfee',
b'cjfee'] 'cjfee']
COMMAND_PREFIX = '!' COMMAND_PREFIX = '!'
JOINMARKET_NICK_HEADER = 'J' JOINMARKET_NICK_HEADER = 'J'
@ -36,5 +36,5 @@ commitment_broadcast_list = ["hp2"]
plaintext_commands += offername_list plaintext_commands += offername_list
plaintext_commands += commitment_broadcast_list plaintext_commands += commitment_broadcast_list
public_commands = commitment_broadcast_list + ["orderbook", "cancel" public_commands = commitment_broadcast_list + ["orderbook", "cancel"
] + list(offername_list) ] + offername_list
private_commands = encrypted_commands + plaintext_commands private_commands = encrypted_commands + plaintext_commands

20
jmdaemon/test/test_daemon_protocol.py

@ -2,6 +2,7 @@
from __future__ import (absolute_import, division, from __future__ import (absolute_import, division,
print_function, unicode_literals) print_function, unicode_literals)
from builtins import * from builtins import *
from future.utils import iteritems
'''test daemon-protocol interfacae.''' '''test daemon-protocol interfacae.'''
from jmdaemon import MessageChannelCollection from jmdaemon import MessageChannelCollection
@ -11,6 +12,7 @@ from jmdaemon.protocol import NICK_HASH_LENGTH, NICK_MAX_ENCODED, JM_VERSION,\
JOINMARKET_NICK_HEADER JOINMARKET_NICK_HEADER
from jmclient import (load_program_config, get_log, jm_single, get_irc_mchannels) 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 msg as tmsg
from twisted.python.log import startLogging
from twisted.internet import protocol, reactor, task from twisted.internet import protocol, reactor, task
from twisted.internet.protocol import ServerFactory from twisted.internet.protocol import ServerFactory
from twisted.internet.error import (ConnectionLost, ConnectionAborted, from twisted.internet.error import (ConnectionLost, ConnectionAborted,
@ -22,6 +24,7 @@ from jmbase.commands import *
from msgdata import * from msgdata import *
import json import json
import base64 import base64
import sys
from dummy_mc import DummyMessageChannel from dummy_mc import DummyMessageChannel
test_completed = False test_completed = False
end_early = False end_early = False
@ -84,7 +87,7 @@ class JMTestClientProtocol(JMBaseProtocol):
show_receipt("JMUP") show_receipt("JMUP")
d = self.callRemote(JMSetup, d = self.callRemote(JMSetup,
role="TAKER", role="TAKER",
n_counterparties=4) #TODO this number should be set initdata="none")
self.defaultCallbacks(d) self.defaultCallbacks(d)
return {'accepted': True} return {'accepted': True}
@ -103,7 +106,7 @@ class JMTestClientProtocol(JMBaseProtocol):
def maketx(self, ioauth_data): def maketx(self, ioauth_data):
ioauth_data = json.loads(ioauth_data) ioauth_data = json.loads(ioauth_data)
nl = ioauth_data.keys() nl = list(ioauth_data.keys())
d = self.callRemote(JMMakeTx, d = self.callRemote(JMMakeTx,
nick_list= json.dumps(nl), nick_list= json.dumps(nl),
txhex="deadbeef") txhex="deadbeef")
@ -115,8 +118,8 @@ class JMTestClientProtocol(JMBaseProtocol):
return {'accepted': True} return {'accepted': True}
jlog.debug("JMOFFERS" + str(orderbook)) jlog.debug("JMOFFERS" + str(orderbook))
#Trigger receipt of verified privmsgs, including unverified #Trigger receipt of verified privmsgs, including unverified
nick = str(t_chosen_orders.keys()[0]) nick = str(list(t_chosen_orders.keys())[0])
b64tx = base64.b64encode("deadbeef") b64tx = base64.b64encode(b"deadbeef").decode('ascii')
d1 = self.callRemote(JMMsgSignatureVerify, d1 = self.callRemote(JMMsgSignatureVerify,
verif_result=True, verif_result=True,
nick=nick, nick=nick,
@ -244,16 +247,16 @@ class JMDaemonTestServerProtocol(JMDaemonServerProtocol):
dummypub = "073732a7ca60470f709f23c602b2b8a6b1ba62ee8f3f83a61e5484ab5cbf9c3d" dummypub = "073732a7ca60470f709f23c602b2b8a6b1ba62ee8f3f83a61e5484ab5cbf9c3d"
#trigger invalid on_pubkey conditions #trigger invalid on_pubkey conditions
reactor.callLater(1, self.on_pubkey, "notrealcp", dummypub) 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 #trigger invalid on_ioauth condition
reactor.callLater(2, self.on_ioauth, "notrealcp", 1, 2, 3, 4, 5) reactor.callLater(2, self.on_ioauth, "notrealcp", 1, 2, 3, 4, 5)
#trigger msg sig verify request operation for a dummy message #trigger msg sig verify request operation for a dummy message
#currently a pass-through #currently a pass-through
reactor.callLater(1, self.request_signature_verify, "1", reactor.callLater(1, self.request_signature_verify, "1",
"!push abcd abc def", "3", "4", "!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 #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(1, self.on_pubkey, k, dummypub)
reactor.callLater(2, self.on_ioauth, k, ['a', 'b'], "auth_pub", reactor.callLater(2, self.on_ioauth, k, ['a', 'b'], "auth_pub",
"cj_addr", "change_addr", "btc_sig") "cj_addr", "change_addr", "btc_sig")
@ -262,7 +265,7 @@ class JMDaemonTestServerProtocol(JMDaemonServerProtocol):
@JMMakeTx.responder @JMMakeTx.responder
def on_JM_MAKE_TX(self, nick_list, txhex): 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") reactor.callLater(1, self.on_sig, n, "dummytxsig")
return super(JMDaemonTestServerProtocol, self).on_JM_MAKE_TX(nick_list, return super(JMDaemonTestServerProtocol, self).on_JM_MAKE_TX(nick_list,
txhex) txhex)
@ -289,6 +292,7 @@ class JMDaemonTest2ServerProtocolFactory(ServerFactory):
class TrialTestJMDaemonProto(unittest.TestCase): class TrialTestJMDaemonProto(unittest.TestCase):
def setUp(self): def setUp(self):
startLogging(sys.stdout)
load_program_config() load_program_config()
jm_single().maker_timeout_sec = 1 jm_single().maker_timeout_sec = 1
self.port = reactor.listenTCP(28184, JMDaemonTestServerProtocolFactory()) self.port = reactor.listenTCP(28184, JMDaemonTestServerProtocolFactory())

8
jmdaemon/test/test_enc_wrapper.py

@ -16,10 +16,10 @@ from jmdaemon import (init_keypair, get_pubkey, init_pubkey, as_init_encryption,
# short ascii # short ascii
(b"Attack at dawn", b"Not tonight Josephine!", 5), (b"Attack at dawn", b"Not tonight Josephine!", 5),
# long base64 encoded # long base64 encoded
(base64.b64encode(''.join(random.choice( (base64.b64encode(b''.join(random.choice(
string.ascii_letters) for _ in range(5000))), string.ascii_letters).encode('ascii') for _ in range(5000))),
base64.b64encode(''.join(random.choice( base64.b64encode(b''.join(random.choice(
string.ascii_letters) for _ in range(5000))), string.ascii_letters).encode('ascii') for _ in range(5000))),
5,), 5,),
# large number of messages on the same connection # large number of messages on the same connection
(b'rand', b'rand', 40000), (b'rand', b'rand', 40000),

27
jmdaemon/test/test_message_channel.py

@ -15,6 +15,7 @@ from msgdata import *
import time import time
import hashlib import hashlib
import base64 import base64
import struct
import traceback import traceback
import threading import threading
import jmbitcoin as bitcoin import jmbitcoin as bitcoin
@ -24,9 +25,9 @@ from dummy_mc import DummyMessageChannel
jlog = get_log() jlog = get_log()
def make_valid_nick(i=0): 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_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) nick_pkh = bitcoin.b58encode(nick_pkh_raw)
#right pad to maximum possible; b58 is not fixed length. #right pad to maximum possible; b58 is not fixed length.
#Use 'O' as one of the 4 not included chars in base58. #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") dmcs[0].on_privmsg(cps[2], "!reloffer sig1 sig2")
#Simulating receipt of encrypted messages: #Simulating receipt of encrypted messages:
#ioauth #ioauth
dummy_on_ioauth_msg = "deadbeef:0,deadbeef:1 XauthpubX XcjaddrX XchangeaddrX XbtcsigX" dummy_on_ioauth_msg = b"deadbeef:0,deadbeef:1 XauthpubX XcjaddrX XchangeaddrX XbtcsigX"
b64dummyioauth = base64.b64encode(dummy_on_ioauth_msg) b64dummyioauth = base64.b64encode(dummy_on_ioauth_msg).decode('ascii')
dmcs[0].on_privmsg(cps[3], "!ioauth " + b64dummyioauth + " sig1 sig2") dmcs[0].on_privmsg(cps[3], "!ioauth " + b64dummyioauth + " sig1 sig2")
#Try with a garbage b64 (but decodable); should throw index error at least #Try with a garbage b64 (but decodable); should throw index error at least
dmcs[0].on_privmsg(cps[3], "!ioauth _*_ sig1 sig2") dmcs[0].on_privmsg(cps[3], "!ioauth _*_ sig1 sig2")
@ -282,23 +283,23 @@ def test_setup_mc():
b64dummyioauth = "999" b64dummyioauth = "999"
dmcs[0].on_privmsg(cps[3], "!ioauth " + b64dummyioauth + " sig1 sig2") dmcs[0].on_privmsg(cps[3], "!ioauth " + b64dummyioauth + " sig1 sig2")
#sig #sig
dummy_on_sig_msg = "dummysig" dummy_on_sig_msg = b"dummysig"
b64dummysig = base64.b64encode(dummy_on_sig_msg) b64dummysig = base64.b64encode(dummy_on_sig_msg).decode('ascii')
dmcs[0].on_privmsg(cps[3], "!sig " + b64dummysig + " sig1 sig2") dmcs[0].on_privmsg(cps[3], "!sig " + b64dummysig + " sig1 sig2")
#auth #auth
dummy_auth_msg = "dummyauth" dummy_auth_msg = b"dummyauth"
b64dummyauth = base64.b64encode(dummy_auth_msg) b64dummyauth = base64.b64encode(dummy_auth_msg).decode('ascii')
dmcs[0].on_privmsg(cps[2], "!auth " + b64dummyauth + " sig1 sig2") dmcs[0].on_privmsg(cps[2], "!auth " + b64dummyauth + " sig1 sig2")
#invalid auth (only no message is invalid) #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 #tx
#valid #valid
dummy_tx = "deadbeefdeadbeef" dummy_tx = b"deadbeefdeadbeef"
b64dummytx = base64.b64encode(dummy_tx) 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") dmcs[0].on_privmsg(cps[2], "!tx " + b642dummytx + " sig1 sig2")
badbase64tx = "999" badbase64tx = b"999"
badbase64tx2 = base64.b64encode(badbase64tx) badbase64tx2 = base64.b64encode(badbase64tx).decode('ascii')
#invalid txhex; here the first round will work (msg decryption), second shouldn't #invalid txhex; here the first round will work (msg decryption), second shouldn't
dmcs[0].on_privmsg(cps[2], "!tx " + badbase64tx2 + " sig1 sig2") dmcs[0].on_privmsg(cps[2], "!tx " + badbase64tx2 + " sig1 sig2")
#push #push

27
scripts/add-utxo.py

@ -1,5 +1,8 @@
#! /usr/bin/env python #! /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 """A very simple command line tool to import utxos to be used
as commitments into joinmarket's commitments.json file, allowing as commitments into joinmarket's commitments.json file, allowing
users to retry transactions more often without getting banned by users to retry transactions more often without getting banned by
@ -156,18 +159,18 @@ def main():
if options.delete_ext: if options.delete_ext:
other = options.in_file or options.in_json or options.loadwallet other = options.in_file or options.in_json or options.loadwallet
if len(args) > 0 or other: 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': "will be ignored; continue? (y/n)") != 'y':
print "Quitting" print("Quitting")
sys.exit(0) sys.exit(0)
c, e = get_podle_commitments() c, e = get_podle_commitments()
print pformat(e) print(pformat(e))
if raw_input( if input(
"You will remove the above commitments; are you sure? (y/n): ") != 'y': "You will remove the above commitments; are you sure? (y/n): ") != 'y':
print "Quitting" print("Quitting")
sys.exit(0) sys.exit(0)
update_commitments(external_to_remove=e) update_commitments(external_to_remove=e)
print "Commitments deleted." print("Commitments deleted.")
sys.exit(0) sys.exit(0)
#Three options (-w, -r, -R) for loading utxo and privkey pairs from a wallet, #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 md, utxos in wallet.get_utxos_by_mixdepth_().items():
for (txid, index), utxo in utxos.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']) wif = wallet.get_wif_path(utxo['path'])
utxo_data.append((txhex, wif)) utxo_data.append((txhex, wif))
@ -196,19 +199,19 @@ def main():
utxo_data.append((u, priv)) utxo_data.append((u, priv))
elif options.in_json: elif options.in_json:
if not os.path.isfile(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) sys.exit(0)
with open(options.in_json, "rb") as f: with open(options.in_json, "rb") as f:
try: try:
utxo_json = json.loads(f.read()) utxo_json = json.loads(f.read())
except: except:
print "Failed to read json from " + options.in_json print("Failed to read json from " + options.in_json)
sys.exit(0) sys.exit(0)
for u, pva in utxo_json.iteritems(): for u, pva in iteritems(utxo_json):
utxo_data.append((u, pva['privkey'])) utxo_data.append((u, pva['privkey']))
elif len(args) == 1: elif len(args) == 1:
u = args[0] u = args[0]
priv = raw_input( priv = input(
'input private key for ' + u + ', in WIF compressed format : ') 'input private key for ' + u + ', in WIF compressed format : ')
u, priv = get_utxo_info(','.join([u, priv])) u, priv = get_utxo_info(','.join([u, priv]))
if not u: if not u:

16
scripts/cli_options.py

@ -1,8 +1,10 @@
#! /usr/bin/env python #! /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 import random
from optparse import OptionParser, OptionValueError from optparse import OptionParser, OptionValueError
from ConfigParser import NoOptionError from configparser import NoOptionError
import jmclient.support import jmclient.support
@ -58,7 +60,7 @@ def add_common_options(parser):
parser.add_option( parser.add_option(
'--order-choose-algorithm', '--order-choose-algorithm',
action='callback', action='callback',
type=str, type='string',
default=jmclient.support.random_under_max_order_choose, default=jmclient.support.random_under_max_order_choose,
callback=get_order_choose_algorithm, callback=get_order_choose_algorithm,
help="Set the algorithm to use for selecting orders from the order book.\n" 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: except NoOptionError:
pass 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) fee_values = prompt_user_for_cj_fee(*fee_values)
return tuple(map(lambda j: fee_types[j](fee_values[j]), 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): def prompt_user_value(m, val, check):
while True: while True:
data = raw_input(m) data = input(m)
if data == 'y': if data == 'y':
return val return val
try: try:
@ -223,7 +225,7 @@ def get_tumbler_parser():
'logs directory, with name TUMBLE.schedule or what is set in the ' 'logs directory, with name TUMBLE.schedule or what is set in the '
'schedulefile option.')) 'schedulefile option.'))
parser.add_option('--schedulefile', parser.add_option('--schedulefile',
type='str', type='string',
dest='schedulefile', dest='schedulefile',
default='TUMBLE.schedule', default='TUMBLE.schedule',
help=('Name of schedule file for tumbler, useful for restart, default ' help=('Name of schedule file for tumbler, useful for restart, default '
@ -372,7 +374,7 @@ def get_sendpayment_parser():
default=random.randint(4, 6)) default=random.randint(4, 6))
parser.add_option('-S', parser.add_option('-S',
'--schedule-file', '--schedule-file',
type='str', type='string',
dest='schedule', dest='schedule',
help='schedule file name; see file "sample-schedule-for-testnet" for explanation and example', help='schedule file name; see file "sample-schedule-for-testnet" for explanation and example',
default='') default='')

10
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 argparse
import json import json
import os.path import os.path
@ -35,7 +37,7 @@ def decrypt_entropy_extension(enc_data, key):
if data[-9] != b'\xff': if data[-9] != b'\xff':
raise ConvertException("Wrong password.") raise ConvertException("Wrong password.")
chunks = data.split(b'\xff') 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.") raise ConvertException("Wrong password.")
return chunks[1] return chunks[1]
@ -92,7 +94,7 @@ def new_wallet_from_data(data, file_name):
for md in data['imported']: for md in data['imported']:
for privkey in data['imported'][md]: for privkey in data['imported'][md]:
privkey += b'\x01' privkey += b'\x01'
wif = wif_compressed_privkey(hexlify(privkey)) wif = wif_compressed_privkey(hexlify(privkey).decode('ascii'))
wallet.import_private_key(md, wif) wallet.import_private_key(md, wif)
wallet.save() wallet.save()

9
scripts/joinmarket-qt.py

@ -1,5 +1,8 @@
#!/usr/bin/env python #!/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. Joinmarket GUI using PyQt for doing coinjoins.
@ -707,7 +710,7 @@ class SpendTab(QWidget):
mbinfo.append(" ") mbinfo.append(" ")
mbinfo.append("Counterparties chosen:") mbinfo.append("Counterparties chosen:")
mbinfo.append('Name, Order id, Coinjoin fee (sat.)') 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']: if o['ordertype'] in ['swreloffer', 'reloffer']:
display_fee = int(self.taker.cjamount * display_fee = int(self.taker.cjamount *
float(o['cjfee'])) - int(o['txfee']) float(o['cjfee'])) - int(o['txfee'])
@ -1256,7 +1259,7 @@ class JMMainWindow(QMainWindow):
mbtype='crit') mbtype='crit')
return return
transaction.writerow(["%34s" % addr, pk]) 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." export_error_label = "JoinmarketQt was unable to produce a private key-export."
JMQtMessageBox(None, JMQtMessageBox(None,
export_error_label + "\n" + str(reason), export_error_label + "\n" + str(reason),

3
scripts/joinmarketd.py

@ -1,3 +1,6 @@
from __future__ import (absolute_import, division,
print_function, unicode_literals)
from builtins import * # noqa: F401
import sys import sys
from twisted.internet import reactor from twisted.internet import reactor
from twisted.python.log import startLogging from twisted.python.log import startLogging

253
scripts/obwatch/ob-watcher.py

@ -1,7 +1,11 @@
from __future__ import absolute_import, print_function from __future__ import (absolute_import, division,
print_function, unicode_literals)
import BaseHTTPServer from builtins import *
import SimpleHTTPServer from future.utils import iteritems
from past.builtins import cmp
from functools import cmp_to_key
import http.server
import base64 import base64
import io import io
import json import json
@ -10,7 +14,7 @@ import time
import hashlib import hashlib
import os import os
import sys import sys
import urllib2 from future.moves.urllib.parse import parse_qs
from decimal import Decimal from decimal import Decimal
from optparse import OptionParser from optparse import OptionParser
from twisted.internet import reactor from twisted.internet import reactor
@ -35,8 +39,8 @@ from jmdaemon.protocol import *
log = get_log() log = get_log()
#Initial state: allow only SW offer types #Initial state: allow only SW offer types
swoffers = filter(lambda x: x[0:2] == 'sw', offername_list) swoffers = list(filter(lambda x: x[0:2] == 'sw', offername_list))
pkoffers = filter(lambda x: x[0:2] != 'sw', offername_list) pkoffers = list(filter(lambda x: x[0:2] != 'sw', offername_list))
filtered_offername_list = swoffers filtered_offername_list = swoffers
shutdownform = '<form action="shutdown" method="post"><input type="submit" value="Shutdown" /></form>' shutdownform = '<form action="shutdown" method="post"><input type="submit" value="Shutdown" /></form>'
@ -53,73 +57,10 @@ def calc_depth_data(db, value):
pass 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) + ("<br/><a href='?scale=log'>log scale</a>" if
bins == 30 else "<br/><a href='?'>linear</a>")
def get_graph_html(fig): def get_graph_html(fig):
imbuf = io.BytesIO() imbuf = io.BytesIO()
fig.savefig(imbuf, format='png') fig.savefig(imbuf, format='png')
b64 = base64.b64encode(imbuf.getvalue()) b64 = base64.b64encode(imbuf.getvalue()).decode('utf-8')
return '<img src="data:image/png;base64,' + b64 + '" />' return '<img src="data:image/png;base64,' + b64 + '" />'
@ -151,36 +92,6 @@ def order_str(s, order, btc_unit, rel_unit):
return str(s) 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 += ' <tr>\n'
for key, displayer in order_keys_display:
result += ' <td>' + displayer(o[key], o, btc_unit,
rel_unit) + '</td>\n'
result += ' </tr>\n'
return len(rows), result
def create_table_heading(btc_unit, rel_unit): def create_table_heading(btc_unit, rel_unit):
col = ' <th>{1}</th>\n' # .format(field,label) col = ' <th>{1}</th>\n' # .format(field,label)
tableheading = '<table class="tftable sortable" border="1">\n <tr>' + ''.join( tableheading = '<table class="tftable sortable" border="1">\n <tr>' + ''.join(
@ -214,15 +125,19 @@ def create_choose_units_form(selected_btc, selected_rel):
return choose_units_form return choose_units_form
class OrderbookPageRequestHeader(SimpleHTTPServer.SimpleHTTPRequestHandler): class OrderbookPageRequestHeader(http.server.SimpleHTTPRequestHandler):
def __init__(self, request, client_address, base_server): def __init__(self, request, client_address, base_server):
self.taker = base_server.taker self.taker = base_server.taker
self.base_server = base_server self.base_server = base_server
SimpleHTTPServer.SimpleHTTPRequestHandler.__init__( http.server.SimpleHTTPRequestHandler.__init__(
self, request, client_address, base_server) self, request, client_address, base_server)
def create_orderbook_obj(self): 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: if not rows:
return [] return []
@ -235,18 +150,124 @@ class OrderbookPageRequestHeader(SimpleHTTPServer.SimpleHTTPRequestHandler):
result.append(o) result.append(o)
return result 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) + ("<br/><a href='?scale=log'>log scale</a>" if
bins == 30 else "<br/><a href='?'>linear</a>")
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 += ' <tr>\n'
for key, displayer in order_keys_display:
result += ' <td>' + displayer(o[key], o, btc_unit,
rel_unit) + '</td>\n'
result += ' </tr>\n'
return len(rows), result
def get_counterparty_count(self): def get_counterparty_count(self):
counterparties = self.taker.db.execute( try:
'SELECT DISTINCT counterparty FROM orderbook WHERE ordertype=? OR ordertype=?;', self.taker.dblock.acquire(True)
filtered_offername_list).fetchall() 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)) return str(len(counterparties))
def do_GET(self): def do_GET(self):
# SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self) # http.server.SimpleHTTPRequestHandler.do_GET(self)
# print 'httpd received ' + self.path + ' request' # print 'httpd received ' + self.path + ' request'
self.path, query = self.path.split('?', 1) if '?' in self.path else ( self.path, query = self.path.split('?', 1) if '?' in self.path else (
self.path, '') self.path, '')
args = urllib2.urlparse.parse_qs(query) args = parse_qs(query)
pages = ['/', '/ordersize', '/depth', '/orderbook.json'] pages = ['/', '/ordersize', '/depth', '/orderbook.json']
if self.path not in pages: if self.path not in pages:
return return
@ -266,8 +287,8 @@ class OrderbookPageRequestHeader(SimpleHTTPServer.SimpleHTTPRequestHandler):
btc_unit = sorted_units[0] btc_unit = sorted_units[0]
if rel_unit not in sorted_rel_units: if rel_unit not in sorted_rel_units:
rel_unit = sorted_rel_units[0] rel_unit = sorted_rel_units[0]
ordercount, ordertable = create_orderbook_table( ordercount, ordertable = self.create_orderbook_table(
self.taker.db, btc_unit, rel_unit) btc_unit, rel_unit)
choose_units_form = create_choose_units_form(btc_unit, rel_unit) choose_units_form = create_choose_units_form(btc_unit, rel_unit)
table_heading = create_table_heading(btc_unit, rel_unit) table_heading = create_table_heading(btc_unit, rel_unit)
replacements = { replacements = {
@ -285,13 +306,13 @@ class OrderbookPageRequestHeader(SimpleHTTPServer.SimpleHTTPRequestHandler):
'PAGETITLE': 'JoinMarket Browser Interface', 'PAGETITLE': 'JoinMarket Browser Interface',
'MAINHEADING': 'Order Sizes', 'MAINHEADING': 'Order Sizes',
'SECONDHEADING': 'Order Size Histogram' + alert_msg, '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'): elif self.path.startswith('/depth'):
# if self.path[6] == '?': # if self.path[6] == '?':
# quantity = # quantity =
cj_amounts = [10 ** cja for cja in range(4, 12, 1)] 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] + \ for cja in cj_amounts] + \
["<br/><a href='?'>linear</a>" if args.get("scale") \ ["<br/><a href='?'>linear</a>" if args.get("scale") \
else "<br/><a href='?scale=log'>log scale</a>"] else "<br/><a href='?scale=log'>log scale</a>"]
@ -305,7 +326,7 @@ class OrderbookPageRequestHeader(SimpleHTTPServer.SimpleHTTPRequestHandler):
replacements = {} replacements = {}
orderbook_fmt = json.dumps(self.create_orderbook_obj()) orderbook_fmt = json.dumps(self.create_orderbook_obj())
orderbook_page = orderbook_fmt orderbook_page = orderbook_fmt
for key, rep in replacements.iteritems(): for key, rep in iteritems(replacements):
orderbook_page = orderbook_page.replace(key, rep) orderbook_page = orderbook_page.replace(key, rep)
self.send_response(200) self.send_response(200)
if self.path.endswith('.json'): if self.path.endswith('.json'):
@ -314,7 +335,7 @@ class OrderbookPageRequestHeader(SimpleHTTPServer.SimpleHTTPRequestHandler):
self.send_header('Content-Type', 'text/html') self.send_header('Content-Type', 'text/html')
self.send_header('Content-Length', len(orderbook_page)) self.send_header('Content-Length', len(orderbook_page))
self.end_headers() self.end_headers()
self.wfile.write(orderbook_page) self.wfile.write(orderbook_page.encode('utf-8'))
def do_POST(self): def do_POST(self):
global filtered_offername_list global filtered_offername_list
@ -351,7 +372,7 @@ class HTTPDThread(threading.Thread):
def run(self): def run(self):
# hostport = ('localhost', 62601) # hostport = ('localhost', 62601)
httpd = BaseHTTPServer.HTTPServer(self.hostport, httpd = http.server.HTTPServer(self.hostport,
OrderbookPageRequestHeader) OrderbookPageRequestHeader)
httpd.taker = self.taker httpd.taker = self.taker
print('\nstarted http server, visit http://{0}:{1}/\n'.format( print('\nstarted http server, visit http://{0}:{1}/\n'.format(

4
scripts/qtsupport.py

@ -1,5 +1,7 @@
#!/usr/bin/env python #!/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. Qt files for the wizard for initiating a tumbler run.

8
scripts/sendpayment.py

@ -1,5 +1,7 @@
#! /usr/bin/env python #! /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, A sample implementation of a single coinjoin script,
@ -34,7 +36,7 @@ def pick_order(orders, n): #pragma: no cover
return orders[0] return orders[0]
while pickedOrderIndex == -1: while pickedOrderIndex == -1:
try: try:
pickedOrderIndex = int(raw_input('Pick an order between 0 and ' + pickedOrderIndex = int(input('Pick an order between 0 and ' +
str(i) + ': ')) str(i) + ': '))
except ValueError: except ValueError:
pickedOrderIndex = -1 pickedOrderIndex = -1
@ -153,7 +155,7 @@ def main():
log.info('WARNING ' * 6) log.info('WARNING ' * 6)
log.info('\n'.join(['=' * 60] * 3)) log.info('\n'.join(['=' * 60] * 3))
if not options.answeryes: 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 False
return True return True

8
scripts/sendtomany.py

@ -1,5 +1,7 @@
#! /usr/bin/env python #! /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 """A simple command line tool to create a bunch
of utxos from one (thus giving more potential commitments of utxos from one (thus giving more potential commitments
for a Joinmarket user, although of course it may be useful for a Joinmarket user, although of course it may be useful
@ -93,7 +95,7 @@ def main():
if len(args) < 2: if len(args) < 2:
quit(parser, 'Invalid syntax') quit(parser, 'Invalid syntax')
u = args[0] u = args[0]
priv = raw_input( priv = input(
'input private key for ' + u + ', in WIF compressed format : ') 'input private key for ' + u + ', in WIF compressed format : ')
u, priv = get_utxo_info(','.join([u, priv])) u, priv = get_utxo_info(','.join([u, priv]))
if not u: if not u:
@ -109,7 +111,7 @@ def main():
log.debug("Got signed transaction:\n" + txsigned) log.debug("Got signed transaction:\n" + txsigned)
log.debug("Deserialized:") log.debug("Deserialized:")
log.debug(pformat(btc.deserialize(txsigned))) 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.") log.info("You chose not to broadcast the transaction, quitting.")
return return
jm_single().bc_interface.pushtx(txsigned) jm_single().bc_interface.pushtx(txsigned)

4
scripts/tumbler.py

@ -1,5 +1,7 @@
#! /usr/bin/env python #! /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 import sys
from twisted.internet import reactor from twisted.internet import reactor

4
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 from jmclient import load_program_config, wallet_tool_main

7
scripts/yg-privacyenhanced.py

@ -1,5 +1,8 @@
#! /usr/bin/env python #! /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 import random
@ -44,7 +47,7 @@ class YieldGeneratorPrivacyEnhanced(YieldGeneratorBasic):
f = self.cjfee_r f = self.cjfee_r
elif ordertype == 'swabsoffer': elif ordertype == 'swabsoffer':
f = str(self.txfee + self.cjfee_a) 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 b > self.minsize])
if len(mix_balance) == 0: if len(mix_balance) == 0:
jlog.error('You do not have the minimum required amount of coins' jlog.error('You do not have the minimum required amount of coins'

4
scripts/yield-generator-basic.py

@ -1,5 +1,7 @@
#! /usr/bin/env python #! /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 from jmclient import YieldGeneratorBasic, ygmain, get_log

0
test/Dockerfiles/bionic.Dockerfile → test/Dockerfiles/bionic-py2.Dockerfile

37
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

0
test/Dockerfiles/centos7.Dockerfile → test/Dockerfiles/centos7-py2.Dockerfile

0
test/Dockerfiles/fedora27.Dockerfile → test/Dockerfiles/fedora27-py2.Dockerfile

36
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

0
test/Dockerfiles/stretch.Dockerfile → test/Dockerfiles/stretch-py2.Dockerfile

37
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

0
test/Dockerfiles/xenial.Dockerfile → test/Dockerfiles/xenial-py2.Dockerfile

37
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

8
test/common.py

@ -1,5 +1,7 @@
#! /usr/bin/env python #! /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''' '''Some helper functions for testing'''
import sys import sys
@ -48,7 +50,7 @@ def make_sign_and_push(ins_full,
priv = binascii.unhexlify(priv) priv = binascii.unhexlify(priv)
tx = btc.sign(tx, index, priv, hashcode=hashcode) tx = btc.sign(tx, index, priv, hashcode=hashcode)
#pushtx returns False on any error #pushtx returns False on any error
print btc.deserialize(tx) print(btc.deserialize(tx))
push_succeed = jm_single().bc_interface.pushtx(tx) push_succeed = jm_single().bc_interface.pushtx(tx)
if push_succeed: if push_succeed:
return btc.txhash(tx) return btc.txhash(tx)
@ -96,7 +98,7 @@ def make_wallets(n,
w = open_test_wallet_maybe(seeds[i], seeds[i], mixdepths - 1, w = open_test_wallet_maybe(seeds[i], seeds[i], mixdepths - 1,
test_wallet_cls=walletclass) test_wallet_cls=walletclass)
wallets[i + start_index] = {'seed': seeds[i], wallets[i + start_index] = {'seed': seeds[i].decode('ascii'),
'wallet': w} 'wallet': w}
for j in range(mixdepths): for j in range(mixdepths):
for k in range(wallet_structures[i][j]): for k in range(wallet_structures[i][j]):

4
test/test_full_coinjoin.py

@ -1,5 +1,7 @@
#! /usr/bin/env python #! /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, '''Runs a full joinmarket pit (using `nirc` miniircd servers,
with `nirc` options specified as an option to pytest),in with `nirc` options specified as an option to pytest),in
bitcoin regtest mode with 3 maker bots and 1 taker bot, bitcoin regtest mode with 3 maker bots and 1 taker bot,

14
test/test_segwit.py

@ -1,5 +1,7 @@
#! /usr/bin/env python #! /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.''' '''Test creation of segwit transactions.'''
import binascii import binascii
@ -20,7 +22,7 @@ def test_segwit_valid_txs(setup_segwit):
if len(j) < 2: if len(j) < 2:
continue continue
deserialized_tx = btc.deserialize(str(j[1])) deserialized_tx = btc.deserialize(str(j[1]))
print pformat(deserialized_tx) print(pformat(deserialized_tx))
assert btc.serialize(deserialized_tx) == str(j[1]) assert btc.serialize(deserialized_tx) == str(j[1])
#TODO use bcinterface to decoderawtransaction #TODO use bcinterface to decoderawtransaction
#and compare the json values #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 # FIXME: encoding mess, mktx should accept binary input formats
tx_ins = [] tx_ins = []
for i, (txid, data) in sorted(all_ins.items(), key=lambda x: x[0]): 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 # create outputs
FEE = 50000 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 change_amt = total_amt_in_sat - amount - FEE
tx_outs = [ tx_outs = [
{'script': binascii.hexlify(cj_script), {'script': binascii.hexlify(cj_script).decode('ascii'),
'value': amount}, 'value': amount},
{'script': binascii.hexlify(change_script), {'script': binascii.hexlify(change_script).decode('ascii'),
'value': change_amt}] 'value': change_amt}]
tx = btc.deserialize(btc.mktx(tx_ins, tx_outs)) tx = btc.deserialize(btc.mktx(tx_ins, tx_outs))
binarize_tx(tx) binarize_tx(tx)
@ -133,7 +135,7 @@ def test_spend_p2sh_p2wpkh_multi(setup_segwit, wallet_structure, in_amt, amount,
print(tx) print(tx)
# push and verify # 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 assert txid
balances = jm_single().bc_interface.get_received_by_addr( balances = jm_single().bc_interface.get_received_by_addr(

4
test/ygrunner.py

@ -1,5 +1,7 @@
#! /usr/bin/env python #! /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. '''Creates wallets and yield generators in regtest.
Provides seed for joinmarket-qt test. Provides seed for joinmarket-qt test.
This should be run via pytest, even though This should be run via pytest, even though

Loading…
Cancel
Save