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
env
htmlcov/
joinmarket.cfg
joinmarket.cfg.bak
scripts/joinmarket.cfg
scripts/cmttools/commitments.json
scripts/blacklist
@ -13,6 +15,7 @@ alicekey
alicepubkey
bobkey
commitments_debug.txt
dummyext
deps/
jmvenv/
logs/

22
.travis.yml

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

36
install.sh

@ -14,10 +14,18 @@ sha256_verify ()
deps_install ()
{
if [[ ${install_os} == 'debian' ]]; then
if deb_deps_install "python-virtualenv curl python-dev python-pip build-essential automake pkg-config libtool libgmp-dev"; then
return 0
if is_python3; then
if deb_deps_install "python-virtualenv curl python3-dev python3-pip build-essential automake pkg-config libtool libgmp-dev"; then
return 0
else
return 1
fi
else
return 1
if deb_deps_install "python-virtualenv curl python-dev python-pip build-essential automake pkg-config libtool libgmp-dev"; then
return 0
else
return 1
fi
fi
else
echo "OS can not be determined. Trying to build."
@ -360,6 +368,18 @@ os_is_deb ()
( which apt-get && which dpkg-query ) 2>/dev/null 1>&2
}
is_python3 ()
{
if [[ ${python} == 'python3' ]]; then
return 0
fi
if eval "${python} -c 'import sys; sys.exit(0) if sys.version_info >= (3,0) else sys.exit(1)'"; then
return 0
else
return 1
fi
}
install_get_os ()
{
if os_is_deb; then
@ -372,8 +392,14 @@ install_get_os ()
qt_deps_install ()
{
if [[ ${install_os} == 'debian' ]]; then
if deb_deps_install "python-qt4 python-sip"; then
return 0;
if is_python3; then
if deb_deps_install "python3-pyqt4 python3-sip"; then
return 0;
fi
else
if deb_deps_install "python-qt4 python-sip"; then
return 0;
fi
fi
else
return 1

4
jmbase/jmbase/__init__.py

@ -3,7 +3,7 @@ from __future__ import (absolute_import, division,
from builtins import *
from .support import (get_log, chunks, debug_silence, debug_dump_object,
joinmarket_alert, core_alert, get_password, _byteify,
joinmarket_alert, core_alert, get_password,
set_logging_level)
from commands import *
from .commands import *

59
jmbase/jmbase/bigstring.py

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

4
jmbase/jmbase/commands.py

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

25
jmbase/jmbase/support.py

@ -1,6 +1,7 @@
from __future__ import (absolute_import, division,
print_function, unicode_literals)
from builtins import * # noqa: F401
from future.utils import iteritems
import sys
@ -53,13 +54,16 @@ def chunks(d, n):
return [d[x:x + n] for x in range(0, len(d), n)]
def get_password(msg): #pragma: no cover
return getpass(msg)
password = getpass(msg)
if not isinstance(password, bytes):
password = password.encode('utf-8')
return password
def debug_dump_object(obj, skip_fields=None):
if skip_fields is None:
skip_fields = []
log.debug('Class debug dump, name:' + obj.__class__.__name__)
for k, v in obj.__dict__.iteritems():
for k, v in iteritems(obj.__dict__):
if k in skip_fields:
continue
if k == 'password' or k == 'given_password':
@ -72,20 +76,3 @@ def debug_dump_object(obj, skip_fields=None):
log.debug(pprint.pformat(v))
else:
log.debug(str(v))
def _byteify(data, ignore_dicts = False):
# if this is a unicode string, return its string representation
if isinstance(data, unicode):
return data.encode('utf-8')
# if this is a list of values, return list of byteified values
if isinstance(data, list):
return [ _byteify(item, ignore_dicts=True) for item in data ]
# if this is a dictionary, return dictionary of byteified keys and values
# but only if we haven't already byteified it
if isinstance(data, dict) and not ignore_dicts:
return {
_byteify(key, ignore_dicts=True): _byteify(value, ignore_dicts=True)
for key, value in data.iteritems()
}
# if it's anything else, return it in its original form
return data

41
jmbitcoin/jmbitcoin/secp256k1_main.py

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

68
jmbitcoin/jmbitcoin/secp256k1_transaction.py

@ -14,7 +14,7 @@ from jmbitcoin.bech32 import *
# Transaction serialization and deserialization
def deserialize(txinp):
if isinstance(txinp, basestring) and re.match('^[0-9a-fA-F]*$', txinp):
if isinstance(txinp, basestring) and not isinstance(txinp, bytes) and re.match('^[0-9a-fA-F]*$', txinp):
tx = BytesIO(binascii.unhexlify(txinp))
hexout = True
else:
@ -23,7 +23,7 @@ def deserialize(txinp):
def hex_string(scriptbytes, hexout):
if hexout:
return binascii.hexlify(scriptbytes)
return binascii.hexlify(scriptbytes).decode('ascii')
else:
return scriptbytes
@ -156,7 +156,7 @@ def serialize(tx):
o.write(struct.pack(b'<I', inp["outpoint"]["index"]))
if len(inp["script"]) == 0:
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(binascii.unhexlify(inp["script"]))
else:
@ -168,7 +168,7 @@ def serialize(tx):
o.write(struct.pack(b'<Q', out["value"]))
if len(out["script"]) == 0:
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(binascii.unhexlify(out["script"]))
else:
@ -184,13 +184,13 @@ def serialize(tx):
items = inp["txinwitness"]
o.write(num_to_var_int(len(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)
o.write(num_to_var_int(len(item)) + item)
o.write(struct.pack(b'<I', txobj["locktime"]))
if hexout:
return binascii.hexlify(o.getvalue())
return binascii.hexlify(o.getvalue()).decode('ascii')
else:
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):
if not isinstance(hashcode, int):
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)
elif isinstance(tx, basestring):
tx = deserialize(binascii.hexlify(tx))
if isinstance(script, basestring) and not re.match('^[0-9a-fA-F]*$', script):
script = binascii.hexlify(script)
tx = deserialize(binascii.hexlify(tx).decode('ascii'))
if isinstance(script, basestring) and isinstance(script, bytes) and not re.match(b'^[0-9a-fA-F]*$', script):
script = binascii.hexlify(script).decode('ascii')
newtx = copy.deepcopy(tx)
for inp in newtx["ins"]:
#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):
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)
if check_sw and from_byte_to_int(tx[4]) == 0:
if not from_byte_to_int(tx[5]) == 1:
if check_sw and from_byte_to_int(tx[4:5]) == 0:
if not from_byte_to_int(tx[5:6]) == 1:
#This invalid, but a raise is a DOS vector in some contexts.
return None
return segwit_txid(tx, hashcode)
@ -374,7 +374,7 @@ def mk_native_segwit_script(addr):
hrp = addr[:2]
ver, prog = bech32addr_decode(hrp, addr)
scriptpubkey = segwit_scriptpubkey(ver, prog)
return binascii.hexlify(scriptpubkey)
return binascii.hexlify(scriptpubkey).decode('ascii')
# Address representation to output script
def address_to_script(addr):
@ -400,7 +400,7 @@ def is_segwit_native_script(script):
return False
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)
if is_segwit_native_script(script):
#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)
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)
return "0014" + hash160(pub)
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)
script = pubkey_to_p2sh_p2wpkh_script(pub)
return p2sh_scriptaddr(script, magicbyte=magicbyte)
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)
return hex_to_b58check(hash160(script), magicbyte)
@ -450,7 +450,7 @@ def deserialize_script(scriptinp):
"""
def hex_string(scriptbytes, hexout):
if hexout:
return binascii.hexlify(scriptbytes)
return binascii.hexlify(scriptbytes).decode('ascii')
else:
return scriptbytes
@ -496,7 +496,7 @@ def serialize_script_unit(unit):
elif unit is None:
return b'\x00'
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)
if len(unit) <= 75:
return from_int_to_byte(len(unit)) + unit
@ -512,11 +512,11 @@ def serialize_script(script):
result = b''
hexout = True
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
result += serialize_script_unit(b)
if hexout:
return binascii.hexlify(result)
return binascii.hexlify(result).decode('ascii')
else:
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):
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)
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)
if not re.match('^[0-9a-fA-F]*$', sig):
sig = binascii.hexlify(sig)
if not re.match('^[0-9a-fA-F]*$', pub):
pub = binascii.hexlify(pub)
if isinstance(sig, bytes) and not re.match(b'^[0-9a-fA-F]*$', sig):
sig = binascii.hexlify(sig).decode('ascii')
if isinstance(pub, bytes) and not re.match(b'^[0-9a-fA-F]*$', pub):
pub = binascii.hexlify(pub).decode('ascii')
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)
hashcode = binascii.unhexlify(sig[-2:])
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):
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)
hexout = True
else:
@ -574,9 +574,9 @@ def sign(tx, i, priv, hashcode=SIGHASH_ALL, usenonce=None, amount=None):
txobj = deserialize(tx)
txobj["ins"][i]["script"] = serialize_script([sig, pub])
serobj = serialize(txobj)
if hexout and isinstance(serobj, basestring) and not re.match('^[0-9a-fA-F]*$', serobj):
return binascii.hexlify(serobj)
elif not hexout and 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).decode('ascii')
elif not hexout and not isinstance(serobj, bytes) and re.match('^[0-9a-fA-F]*$', serobj):
return binascii.unhexlify(serobj)
else:
return serobj
@ -664,7 +664,7 @@ def mktx(*args):
"sequence": 4294967295
})
for o in outs:
if isinstance(o, basestring):
if isinstance(o, basestring) and not isinstance(o, bytes):
addr = o[:o.find(':')]
val = int(o[o.find(':') + 1:])
o = {}

4
jmbitcoin/test/test_ecc_signing.py

@ -18,7 +18,7 @@ def test_valid_sigs(setup_ecc):
msg = v['msg']
sig = v['sig']
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)
pubkey = btc.privtopub(priv)
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
binsig = binascii.unhexlify(sig)
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
#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:
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
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
privraw = "aa"*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"

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

9
jmclient/jmclient/btc.py

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

18
jmclient/jmclient/client_protocol.py

@ -1,5 +1,8 @@
#! /usr/bin/env python
from __future__ import print_function
from __future__ import (absolute_import, division,
print_function, unicode_literals)
from builtins import * # noqa: F401
from future.utils import iteritems
from twisted.internet import protocol, reactor, task
from twisted.internet.error import (ConnectionLost, ConnectionAborted,
ConnectionClosed, ConnectionDone)
@ -17,8 +20,7 @@ import sys
from jmclient import (jm_single, get_irc_mchannels, get_log, get_p2sh_vbyte,
RegtestBitcoinCoreInterface)
from .output import fmt_tx_data
from jmbase import _byteify
import btc
from . import btc
jlog = get_log()
@ -60,7 +62,7 @@ class JMClientProtocol(amp.AMP):
def set_nick(self):
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_pkh = btc.b58encode(self.nick_pkh_raw)
#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.")
verif_result = False
#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]
#strip right padding
nick_unpadded = ''.join([x for x in nick_stripped if x != 'O'])
@ -206,7 +208,7 @@ class JMMakerClientProtocol(JMClientProtocol):
@commands.JMTXReceived.responder
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)
if not retval[0]:
jlog.info("Maker refuses to continue on receipt of tx")
@ -230,7 +232,7 @@ class JMMakerClientProtocol(JMClientProtocol):
def unconfirm_callback(self, txd, txid):
#find the offer for this tx
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
if v["txd"]["outs"] == txd["outs"]:
offerinfo = v
@ -254,7 +256,7 @@ class JMMakerClientProtocol(JMClientProtocol):
def confirm_callback(self, txd, txid, confirmations):
#find the offer for this tx
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
if v["txd"]["outs"] == txd["outs"]:
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 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 logging
@ -6,9 +8,9 @@ import threading
import os
import binascii
from ConfigParser import SafeConfigParser, NoOptionError
from configparser import ConfigParser, NoOptionError
import btc
from . import btc
from jmclient.jsonrpc import JsonRpc
from jmbase.support import (get_log, joinmarket_alert, core_alert, debug_silence,
set_logging_level)
@ -33,7 +35,7 @@ class AttributeDict(object):
def add_entries(self, **entries):
for key, value in entries.items():
if type(value) is dict:
if isinstance(value, dict):
self.__dict__[key] = AttributeDict(**value)
else:
self.__dict__[key] = value
@ -73,7 +75,7 @@ global_singleton.blacklist_file_lock = threading.Lock()
global_singleton.core_alert = core_alert
global_singleton.joinmarket_alert = joinmarket_alert
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
global_singleton.config_location = 'joinmarket.cfg'
#as above
@ -362,7 +364,7 @@ def donation_address(reusable_donation_pubkey=None): #pragma: no cover
if not reusable_donation_pubkey:
reusable_donation_pubkey = ('02be838257fbfddabaea03afbb9f16e852'
'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))
sender_pubkey = btc.add_pubkeys(
[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):
global_singleton.config.readfp(io.BytesIO(defaultconfig))
global_singleton.config.readfp(io.StringIO(defaultconfig))
remove_unwanted_default_settings(global_singleton.config)
if not config_path:
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
@ -30,12 +32,12 @@ P2WPKH_PRE = b'\x00\x14'
def _pubkey_to_script(pubkey, script_pre, script_post=b''):
# sanity check for public key
# 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
(len(pubkey) == 65 and pubkey[0] in (b'\x04', b'\x06', b'\x07'))):
if not ((len(pubkey) == 33 and pubkey[:1] in (b'\x02', b'\x03')) or
(len(pubkey) == 65 and pubkey[:1] in (b'\x04', b'\x06', b'\x07'))):
raise Exception("Invalid public key!")
h = btc.bin_hash160(pubkey)
assert len(h) == 0x14
assert script_pre[-1] == b'\x14'
assert script_pre[-1:] == b'\x14'
return script_pre + h + script_post
@ -229,7 +231,7 @@ class BTC_P2PKH(BTCEngine):
signing_tx = btc.serialize(btc.signature_form(tx, index, script,
hashcode=hashcode))
# 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))
tx['ins'][index]['script'] = btc.serialize_script([sig, pubkey])
@ -262,7 +264,7 @@ class BTC_P2SH_P2WPKH(BTCEngine):
hashcode=hashcode,
decoder_func=lambda x: x)
# 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))
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
# 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 Queue
import queue as Queue
import os
import pprint
import random
@ -330,7 +334,7 @@ class ElectrumInterface(BlockchainInterface):
for m in range(wallet.max_mixdepth):
for fc in [0, 1]:
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":
continue
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) 2014 by phelix / blockchained.com
#
@ -23,8 +25,9 @@ from __future__ import absolute_import, print_function
import errno
import socket
import base64
import httplib
import http.client
import json
from decimal import Decimal
from jmclient import get_log
jlog = get_log()
@ -56,8 +59,8 @@ class JsonRpc(object):
def __init__(self, host, port, user, password, wallet_file=""):
self.host = host
self.port = port
self.conn = httplib.HTTPConnection(self.host, self.port)
self.port = int(port)
self.conn = http.client.HTTPConnection(self.host, self.port)
self.authstr = "%s:%s" % (user, password)
if len(wallet_file) > 0:
self.url = "/wallet/" + wallet_file
@ -76,7 +79,7 @@ class JsonRpc(object):
headers = {"User-Agent": "joinmarket",
"Content-Type": "application/json",
"Accept": "application/json"}
headers["Authorization"] = "Basic %s" % base64.b64encode(self.authstr)
headers["Authorization"] = b"Basic %s" % base64.b64encode(self.authstr.encode('utf-8'))
body = json.dumps(obj)
@ -97,20 +100,26 @@ class JsonRpc(object):
data = response.read()
return json.loads(data)
return json.loads(data.decode('utf-8'), parse_float=Decimal)
except JsonRpcConnectionError as exc:
raise exc
except httplib.BadStatusLine:
except http.client.BadStatusLine:
return "CONNFAILURE"
except socket.error as e:
if e.errno != errno.ECONNRESET:
if e.errno == errno.ECONNRESET:
jlog.warn('Connection was reset, attempting reconnect.')
self.conn.close()
self.conn.connect()
continue
elif e.errno == errno.EPIPE:
jlog.warn('Connection had broken pipe, attempting reconnect.')
self.conn.close()
self.conn.connect()
continue
else:
jlog.error('Unhandled connection error ' + str(e))
raise e
jlog.warn('Connection was reset, attempting reconnect.')
self.conn.close()
self.conn.connect()
continue
except Exception as exc:
raise JsonRpcConnectionError("JSON-RPC connection failed. Err:" +
repr(exc))
@ -136,7 +145,7 @@ class JsonRpc(object):
response_received = True
break
#Failure means keepalive timed out, just make a new one
self.conn = httplib.HTTPConnection(self.host, self.port)
self.conn = http.client.HTTPConnection(self.host, self.port)
if not response_received:
raise JsonRpcConnectionError("Unable to connect over RPC")
if response["id"] != currentId:

12
jmclient/jmclient/maker.py

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

11
jmclient/jmclient/old_mnemonic.py

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

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
@ -11,7 +14,7 @@ def fmt_utxos(utxos, wallet, prefix=''):
def fmt_utxo(utxo):
return '{}:{}'.format(hexlify(utxo[0]), utxo[1])
return '{}:{}'.format(hexlify(utxo[0]).decode('ascii'), utxo[1])
def fmt_tx_data(tx_data, wallet):

29
jmclient/jmclient/podle.py

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

7
jmclient/jmclient/schedule.py

@ -1,5 +1,7 @@
#!/usr/bin/env python
from __future__ import print_function
from __future__ import (absolute_import, division,
print_function, unicode_literals)
from builtins import * # noqa: F401
import copy
from jmclient import (validate_address, rand_exp_array,
rand_norm_array, rand_pow_array, jm_single)
@ -20,6 +22,7 @@ def get_schedule(filename):
schedule = []
schedule_lines = f.readlines()
for sl in schedule_lines:
sl = sl.decode('utf-8')
if sl.startswith("#"):
continue
try:
@ -213,4 +216,4 @@ def human_readable_schedule_entry(se, amt=None, destn=None):
return ", ".join(hrs)
def schedule_to_text(schedule):
return "\n".join([",".join([str(y) for y in x]) for x in schedule])
return "\n".join([",".join([str(y) for y in x]) for x in schedule]).encode('utf-8')

3
jmclient/jmclient/slowaes.py

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

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

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

43
jmclient/jmclient/taker.py

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

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 pprint
import os
@ -53,7 +56,7 @@ def direct_send(wallet, amount, mixdepth, destaddr, answeryes=False,
log.error(
"There are no utxos in mixdepth: " + str(mixdepth) + ", quitting.")
return
total_inputs_val = sum([va['value'] for u, va in utxos.iteritems()])
total_inputs_val = sum([va['value'] for u, va in iteritems(utxos)])
fee_est = estimate_tx_fee(len(utxos), 1, txtype=txtype)
outs = [{"address": destaddr, "value": total_inputs_val - fee_est}]
else:
@ -64,7 +67,7 @@ def direct_send(wallet, amount, mixdepth, destaddr, answeryes=False,
fee_est = estimate_tx_fee(len(utxos), 2, txtype=txtype)
else:
fee_est = initial_fee_est
total_inputs_val = sum([va['value'] for u, va in utxos.iteritems()])
total_inputs_val = sum([va['value'] for u, va in iteritems(utxos)])
changeval = total_inputs_val - fee_est - amount
outs = [{"value": amount, "address": destaddr}]
change_addr = wallet.get_internal_addr(mixdepth)
@ -75,7 +78,7 @@ def direct_send(wallet, amount, mixdepth, destaddr, answeryes=False,
log.info("Using a fee of : " + str(fee_est) + " satoshis.")
if amount != 0:
log.info("Using a change value of: " + str(changeval) + " satoshis.")
tx = sign_tx(wallet, mktx(utxos.keys(), outs), utxos)
tx = sign_tx(wallet, mktx(list(utxos.keys()), outs), utxos)
txsigned = deserialize(tx)
log.info("Got signed transaction:\n")
log.info(tx + "\n")
@ -84,7 +87,7 @@ def direct_send(wallet, amount, mixdepth, destaddr, answeryes=False,
log.info("Sends: " + str(actual_amount) + " satoshis to address: " + destaddr)
if not answeryes:
if not accept_callback:
if raw_input('Would you like to push to the network? (y/n):')[0] != 'y':
if input('Would you like to push to the network? (y/n):')[0] != 'y':
log.info("You chose not to broadcast the transaction, quitting.")
return False
else:
@ -112,7 +115,7 @@ def sign_tx(wallet, tx, utxos):
# FIXME: ugly hack
tx_bin = deserialize(unhexlify(serialize(stx)))
wallet.sign_tx(tx_bin, our_inputs)
return hexlify(serialize(tx_bin))
return hexlify(serialize(tx_bin)).decode('ascii')
def import_new_addresses(wallet, addr_list):
@ -239,7 +242,7 @@ def tumbler_taker_finished_update(taker, schedulefile, tumble_log, options,
print(' for example click the button that gives a new deposit address')
print('\n'.join(['=' * 60] * 1))
while True:
destaddr = raw_input('insert new address: ')
destaddr = input('insert new address: ')
addr_valid, errormsg = validate_address(destaddr)
if addr_valid:
break

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

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

9
jmclient/jmclient/yieldgenerator.py

@ -1,5 +1,8 @@
#! /usr/bin/env python
from __future__ import absolute_import, print_function
from __future__ import (absolute_import, division,
print_function, unicode_literals)
from builtins import * # noqa: F401
from future.utils import iteritems
import datetime
import os
@ -79,7 +82,7 @@ class YieldGeneratorBasic(YieldGenerator):
def create_my_orders(self):
mix_balance = self.wallet.get_balance_by_mixdepth(verbose=False)
if len([b for m, b in mix_balance.iteritems() if b > 0]) == 0:
if len([b for m, b in iteritems(mix_balance) if b > 0]) == 0:
jlog.error('do not have any coins left')
return []
@ -118,7 +121,7 @@ class YieldGeneratorBasic(YieldGenerator):
max_mix = max(mix_balance, key=mix_balance.get)
filtered_mix_balance = [m
for m in mix_balance.iteritems()
for m in iteritems(mix_balance)
if m[1] >= total_amount]
if not filtered_mix_balance:
return None, None, None

2
jmclient/setup.py

@ -9,5 +9,5 @@ setup(name='joinmarketclient',
author_email='',
license='GPL',
packages=['jmclient'],
install_requires=['future', 'joinmarketbase==0.4.2', 'mnemonic', 'qt4reactor', 'argon2_cffi', 'bencoder.pyx', 'pyaes'],
install_requires=['future', 'configparser;python_version<"3.2"', 'joinmarketbase==0.4.2', 'mnemonic', 'qt4reactor', 'argon2_cffi', 'bencoder.pyx', 'pyaes'],
zip_safe=False)

10
jmclient/test/commontest.py

@ -1,5 +1,7 @@
#! /usr/bin/env python
from __future__ import absolute_import, print_function
from __future__ import (absolute_import, division,
print_function, unicode_literals)
from builtins import * # noqa: F401
'''Some helper functions for testing'''
import os
@ -131,7 +133,7 @@ def make_sign_and_push(ins_full,
from wallets
"""
total = sum(x['value'] for x in ins_full.values())
ins = ins_full.keys()
ins = list(ins_full.keys())
#random output address and change addr
output_addr = wallet.get_new_addr(1, 1) if not output_addr else output_addr
change_addr = wallet.get_new_addr(1, 0) if not change_addr else change_addr
@ -149,7 +151,7 @@ def make_sign_and_push(ins_full,
binarize_tx(de_tx)
de_tx = wallet.sign_tx(de_tx, scripts, hashcode=hashcode)
#pushtx returns False on any error
tx = binascii.hexlify(btc.serialize(de_tx))
tx = binascii.hexlify(btc.serialize(de_tx)).decode('ascii')
push_succeed = jm_single().bc_interface.pushtx(tx)
if push_succeed:
return btc.txhash(tx)
@ -178,7 +180,7 @@ def make_wallets(n,
if len(wallet_structures) != n:
raise Exception("Number of wallets doesn't match wallet structures")
if not fixed_seeds:
seeds = chunks(binascii.hexlify(os.urandom(BIP32Wallet.ENTROPY_BYTES * n)),
seeds = chunks(binascii.hexlify(os.urandom(BIP32Wallet.ENTROPY_BYTES * n)).decode('ascii'),
BIP32Wallet.ENTROPY_BYTES * 2)
else:
seeds = fixed_seeds

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
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'},

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

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

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."""

4
jmclient/test/test_client_protocol.py

@ -1,5 +1,7 @@
#! /usr/bin/env python
from __future__ import absolute_import
from __future__ import (absolute_import, division,
print_function, unicode_literals)
from builtins import *
'''test client-protocol interfacae.'''
from jmclient import load_program_config, Taker, get_log,\

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

4
jmclient/test/test_commitment_utils.py

@ -1,5 +1,7 @@
#!/usr/bin/env python
from __future__ import print_function
from __future__ import (absolute_import, division,
print_function, unicode_literals)
from builtins import * # noqa: F401
from commontest import DummyBlockchainInterface
import pytest

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.'''
import pytest

26
jmclient/test/test_maker.py

@ -1,6 +1,8 @@
#!/usr/bin/env python
from __future__ import print_function
from __future__ import (absolute_import, division,
print_function, unicode_literals)
from builtins import * # noqa: F401
from jmclient import Maker, btc, get_p2sh_vbyte, get_p2pk_vbyte, \
load_program_config, jm_single
@ -46,7 +48,7 @@ def construct_tx_offerlist(cjaddr, changeaddr, maker_utxos, maker_utxos_value,
def create_tx_inputs(count=1):
inp = []
for i in xrange(count):
for i in range(count):
inp.append({'outpoint': {'hash': '0'*64, 'index': i},
'script': '',
'sequence': 4294967295})
@ -71,9 +73,9 @@ def address_p2sh_generator():
def get_address_generator(script_pre, script_post, vbyte):
counter = 0
while True:
script = script_pre + struct.pack('=LQQ', 0, 0, counter) + script_post
script = script_pre + struct.pack(b'=LQQ', 0, 0, counter) + script_post
addr = btc.script_to_address(script, vbyte)
yield addr, binascii.hexlify(script)
yield addr, binascii.hexlify(script).decode('ascii')
counter += 1
@ -120,21 +122,21 @@ def test_verify_unsigned_tx_sw_valid(setup_env_nodeps):
# test standard cj
tx, offerlist = create_tx_and_offerlist(cj_addr, changeaddr,
[next(p2sh_gen)[1] for s in xrange(4)], cj_script, cj_change_script)
[next(p2sh_gen)[1] for s in range(4)], cj_script, cj_change_script)
assert maker.verify_unsigned_tx(tx, offerlist) == (True, None), "standard sw cj"
# test cj with mixed outputs
tx, offerlist = create_tx_and_offerlist(cj_addr, changeaddr,
list(chain((next(p2sh_gen)[1] for s in xrange(3)),
(next(p2pkh_gen)[1] for s in xrange(1)))),
list(chain((next(p2sh_gen)[1] for s in range(3)),
(next(p2pkh_gen)[1] for s in range(1)))),
cj_script, cj_change_script)
assert maker.verify_unsigned_tx(tx, offerlist) == (True, None), "sw cj with p2pkh output"
# test cj with only p2pkh outputs
tx, offerlist = create_tx_and_offerlist(cj_addr, changeaddr,
[next(p2pkh_gen)[1] for s in xrange(4)], cj_script, cj_change_script)
[next(p2pkh_gen)[1] for s in range(4)], cj_script, cj_change_script)
assert maker.verify_unsigned_tx(tx, offerlist) == (True, None), "sw cj with only p2pkh outputs"
@ -153,21 +155,21 @@ def test_verify_unsigned_tx_nonsw_valid(setup_env_nodeps):
# test standard cj
tx, offerlist = create_tx_and_offerlist(cj_addr, changeaddr,
[next(p2pkh_gen)[1] for s in xrange(4)], cj_script, cj_change_script, 'reloffer')
[next(p2pkh_gen)[1] for s in range(4)], cj_script, cj_change_script, 'reloffer')
assert maker.verify_unsigned_tx(tx, offerlist) == (True, None), "standard nonsw cj"
# test cj with mixed outputs
tx, offerlist = create_tx_and_offerlist(cj_addr, changeaddr,
list(chain((next(p2sh_gen)[1] for s in xrange(1)),
(next(p2pkh_gen)[1] for s in xrange(3)))),
list(chain((next(p2sh_gen)[1] for s in range(1)),
(next(p2pkh_gen)[1] for s in range(3)))),
cj_script, cj_change_script, 'reloffer')
assert maker.verify_unsigned_tx(tx, offerlist) == (True, None), "nonsw cj with p2sh output"
# test cj with only p2sh outputs
tx, offerlist = create_tx_and_offerlist(cj_addr, changeaddr,
[next(p2sh_gen)[1] for s in xrange(4)], cj_script, cj_change_script, 'reloffer')
[next(p2sh_gen)[1] for s in range(4)], cj_script, cj_change_script, 'reloffer')
assert maker.verify_unsigned_tx(tx, offerlist) == (True, None), "nonsw cj with only p2sh outputs"

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

21
jmclient/test/test_podle.py

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

6
jmclient/test/test_schedule.py

@ -1,5 +1,7 @@
#! /usr/bin/env python
from __future__ import absolute_import
from __future__ import (absolute_import, division,
print_function, unicode_literals)
from builtins import * # noqa: F401
'''test schedule module.'''
import pytest
@ -42,7 +44,7 @@ def test_get_schedule():
if os.path.exists(tsf):
os.remove(tsf)
with open(tsf, "wb") as f:
f.write(s)
f.write(s.encode('utf-8'))
result = get_schedule(tsf)
if s== valids:
assert result[0]

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
import pytest

4
jmclient/test/test_support.py

@ -1,5 +1,7 @@
#! /usr/bin/env python
from __future__ import absolute_import
from __future__ import (absolute_import, division,
print_function, unicode_literals)
from builtins import * # noqa: F401
'''support functions for jmclient tests.'''
import pytest

26
jmclient/test/test_taker.py

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

25
jmclient/test/test_tx_creation.py

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

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

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

12
jmclient/test/test_wallets.py

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

22
jmdaemon/jmdaemon/daemon_protocol.py

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

6
jmdaemon/jmdaemon/enc_wrapper.py

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

6
jmdaemon/jmdaemon/irc.py

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

18
jmdaemon/jmdaemon/message_channel.py

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

40
jmdaemon/jmdaemon/orderbookwatch.py

@ -14,6 +14,13 @@ from jmbase.support import get_log, joinmarket_alert, DUST_THRESHOLD
log = get_log()
def dict_factory(cursor, row):
d = {}
for idx, col in enumerate(cursor.description):
d[col[0]] = row[idx]
return d
class JMTakerError(Exception):
pass
@ -30,11 +37,15 @@ class OrderbookWatch(object):
self.dblock = threading.Lock()
con = sqlite3.connect(":memory:", check_same_thread=False)
con.row_factory = sqlite3.Row
con.row_factory = dict_factory
self.db = con.cursor()
self.db.execute("CREATE TABLE orderbook(counterparty TEXT, "
"oid INTEGER, ordertype TEXT, minsize INTEGER, "
"maxsize INTEGER, txfee INTEGER, cjfee TEXT);")
try:
self.dblock.acquire(True)
self.db.execute("CREATE TABLE orderbook(counterparty TEXT, "
"oid INTEGER, ordertype TEXT, minsize INTEGER, "
"maxsize INTEGER, txfee INTEGER, cjfee TEXT);")
finally:
self.dblock.release()
@staticmethod
def on_set_topic(newtopic):
@ -59,7 +70,11 @@ class OrderbookWatch(object):
txfee, cjfee):
try:
self.dblock.acquire(True)
if int(oid) < 0 or int(oid) > sys.maxint:
if sys.version_info >= (3,0):
maxint = sys.maxsize
else:
maxint = sys.maxint
if int(oid) < 0 or int(oid) > maxint:
log.debug("Got invalid order ID: " + oid + " from " +
counterparty)
return
@ -114,16 +129,25 @@ class OrderbookWatch(object):
self.dblock.release()
def on_order_cancel(self, counterparty, oid):
with self.dblock:
try:
self.dblock.acquire(True)
self.db.execute(
("DELETE FROM orderbook WHERE "
"counterparty=? AND oid=?;"), (counterparty, oid))
finally:
self.dblock.release()
def on_nick_leave(self, nick):
with self.dblock:
try:
self.dblock.acquire(True)
self.db.execute('DELETE FROM orderbook WHERE counterparty=?;',
(nick,))
finally:
self.dblock.release()
def on_disconnect(self):
with self.dblock:
try:
self.dblock.acquire(True)
self.db.execute('DELETE FROM orderbook;')
finally:
self.dblock.release()

8
jmdaemon/jmdaemon/protocol.py

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

20
jmdaemon/test/test_daemon_protocol.py

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

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
(b"Attack at dawn", b"Not tonight Josephine!", 5),
# long base64 encoded
(base64.b64encode(''.join(random.choice(
string.ascii_letters) for _ in range(5000))),
base64.b64encode(''.join(random.choice(
string.ascii_letters) for _ in range(5000))),
(base64.b64encode(b''.join(random.choice(
string.ascii_letters).encode('ascii') for _ in range(5000))),
base64.b64encode(b''.join(random.choice(
string.ascii_letters).encode('ascii') for _ in range(5000))),
5,),
# large number of messages on the same connection
(b'rand', b'rand', 40000),

27
jmdaemon/test/test_message_channel.py

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

27
scripts/add-utxo.py

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

16
scripts/cli_options.py

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

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

9
scripts/joinmarket-qt.py

@ -1,5 +1,8 @@
#!/usr/bin/env python
from __future__ import print_function
from __future__ import (absolute_import, division,
print_function, unicode_literals)
from builtins import *
from future.utils import iteritems
'''
Joinmarket GUI using PyQt for doing coinjoins.
@ -707,7 +710,7 @@ class SpendTab(QWidget):
mbinfo.append(" ")
mbinfo.append("Counterparties chosen:")
mbinfo.append('Name, Order id, Coinjoin fee (sat.)')
for k, o in offers.iteritems():
for k, o in iteritems(offers):
if o['ordertype'] in ['swreloffer', 'reloffer']:
display_fee = int(self.taker.cjamount *
float(o['cjfee'])) - int(o['txfee'])
@ -1256,7 +1259,7 @@ class JMMainWindow(QMainWindow):
mbtype='crit')
return
transaction.writerow(["%34s" % addr, pk])
except (IOError, os.error), reason:
except (IOError, os.error) as reason:
export_error_label = "JoinmarketQt was unable to produce a private key-export."
JMQtMessageBox(None,
export_error_label + "\n" + str(reason),

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
from twisted.internet import reactor
from twisted.python.log import startLogging

253
scripts/obwatch/ob-watcher.py

@ -1,7 +1,11 @@
from __future__ import absolute_import, print_function
import BaseHTTPServer
import SimpleHTTPServer
from __future__ import (absolute_import, division,
print_function, unicode_literals)
from builtins import *
from future.utils import iteritems
from past.builtins import cmp
from functools import cmp_to_key
import http.server
import base64
import io
import json
@ -10,7 +14,7 @@ import time
import hashlib
import os
import sys
import urllib2
from future.moves.urllib.parse import parse_qs
from decimal import Decimal
from optparse import OptionParser
from twisted.internet import reactor
@ -35,8 +39,8 @@ from jmdaemon.protocol import *
log = get_log()
#Initial state: allow only SW offer types
swoffers = filter(lambda x: x[0:2] == 'sw', offername_list)
pkoffers = filter(lambda x: x[0:2] != 'sw', offername_list)
swoffers = list(filter(lambda x: x[0:2] == 'sw', offername_list))
pkoffers = list(filter(lambda x: x[0:2] != 'sw', offername_list))
filtered_offername_list = swoffers
shutdownform = '<form action="shutdown" method="post"><input type="submit" value="Shutdown" /></form>'
@ -53,73 +57,10 @@ def calc_depth_data(db, value):
pass
def create_depth_chart(db, cj_amount, args=None):
if args is None:
args = {}
rows = db.execute('SELECT * FROM orderbook;').fetchall()
sqlorders = [o for o in rows if o["ordertype"] in filtered_offername_list]
orderfees = sorted([calc_cj_fee(o['ordertype'], o['cjfee'], cj_amount) / 1e8
for o in sqlorders
if o['minsize'] <= cj_amount <= o[
'maxsize']])
if len(orderfees) == 0:
return 'No orders at amount ' + str(cj_amount / 1e8)
fig = plt.figure()
scale = args.get("scale")
if (scale is not None) and (scale[0] == "log"):
orderfees = [float(fee) for fee in orderfees]
if orderfees[0] > 0:
ratio = orderfees[-1] / orderfees[0]
step = ratio ** 0.0333 # 1/30
bins = [orderfees[0] * (step ** i) for i in range(30)]
else:
ratio = orderfees[-1] / 1e-8 # single satoshi placeholder
step = ratio ** 0.0333 # 1/30
bins = [1e-8 * (step ** i) for i in range(30)]
bins[0] = orderfees[0] # replace placeholder
plt.xscale('log')
else:
bins = 30
if len(orderfees) == 1: # these days we have liquidity, but just in case...
plt.hist(orderfees, bins, rwidth=0.8, range=(0, orderfees[0] * 2))
else:
plt.hist(orderfees, bins, rwidth=0.8)
plt.grid()
plt.title('CoinJoin Orderbook Depth Chart for amount=' + str(cj_amount /
1e8) + 'btc')
plt.xlabel('CoinJoin Fee / btc')
plt.ylabel('Frequency')
return get_graph_html(fig)
def create_size_histogram(db, args):
rows = db.execute('SELECT maxsize, ordertype FROM orderbook;').fetchall()
rows = [o for o in rows if o["ordertype"] in filtered_offername_list]
ordersizes = sorted([r['maxsize'] / 1e8 for r in rows])
fig = plt.figure()
scale = args.get("scale")
if (scale is not None) and (scale[0] == "log"):
ratio = ordersizes[-1] / ordersizes[0]
step = ratio ** 0.0333 # 1/30
bins = [ordersizes[0] * (step ** i) for i in range(30)]
else:
bins = 30
plt.hist(ordersizes, bins, histtype='bar', rwidth=0.8)
if bins is not 30:
fig.axes[0].set_xscale('log')
plt.grid()
plt.xlabel('Order sizes / btc')
plt.ylabel('Frequency')
return get_graph_html(fig) + ("<br/><a href='?scale=log'>log scale</a>" if
bins == 30 else "<br/><a href='?'>linear</a>")
def get_graph_html(fig):
imbuf = io.BytesIO()
fig.savefig(imbuf, format='png')
b64 = base64.b64encode(imbuf.getvalue())
b64 = base64.b64encode(imbuf.getvalue()).decode('utf-8')
return '<img src="data:image/png;base64,' + b64 + '" />'
@ -151,36 +92,6 @@ def order_str(s, order, btc_unit, rel_unit):
return str(s)
def create_orderbook_table(db, btc_unit, rel_unit):
result = ''
rows = db.execute('SELECT * FROM orderbook;').fetchall()
if not rows:
return 0, result
#print("len rows before filter: " + str(len(rows)))
rows = [o for o in rows if o["ordertype"] in filtered_offername_list]
order_keys_display = (('ordertype', ordertype_display),
('counterparty', do_nothing), ('oid', order_str),
('cjfee', cjfee_display), ('txfee', satoshi_to_unit),
('minsize', satoshi_to_unit),
('maxsize', satoshi_to_unit))
# somewhat complex sorting to sort by cjfee but with swabsoffers on top
def orderby_cmp(x, y):
if x['ordertype'] == y['ordertype']:
return cmp(Decimal(x['cjfee']), Decimal(y['cjfee']))
return cmp(offername_list.index(x['ordertype']),
offername_list.index(y['ordertype']))
for o in sorted(rows, cmp=orderby_cmp):
result += ' <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):
col = ' <th>{1}</th>\n' # .format(field,label)
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
class OrderbookPageRequestHeader(SimpleHTTPServer.SimpleHTTPRequestHandler):
class OrderbookPageRequestHeader(http.server.SimpleHTTPRequestHandler):
def __init__(self, request, client_address, base_server):
self.taker = base_server.taker
self.base_server = base_server
SimpleHTTPServer.SimpleHTTPRequestHandler.__init__(
http.server.SimpleHTTPRequestHandler.__init__(
self, request, client_address, base_server)
def create_orderbook_obj(self):
rows = self.taker.db.execute('SELECT * FROM orderbook;').fetchall()
try:
self.taker.dblock.acquire(True)
rows = self.taker.db.execute('SELECT * FROM orderbook;').fetchall()
finally:
self.taker.dblock.release()
if not rows:
return []
@ -235,18 +150,124 @@ class OrderbookPageRequestHeader(SimpleHTTPServer.SimpleHTTPRequestHandler):
result.append(o)
return result
def create_depth_chart(self, cj_amount, args=None):
if args is None:
args = {}
try:
self.taker.dblock.acquire(True)
rows = self.taker.db.execute('SELECT * FROM orderbook;').fetchall()
finally:
self.taker.dblock.release()
sqlorders = [o for o in rows if o["ordertype"] in filtered_offername_list]
orderfees = sorted([calc_cj_fee(o['ordertype'], o['cjfee'], cj_amount) / 1e8
for o in sqlorders
if o['minsize'] <= cj_amount <= o[
'maxsize']])
if len(orderfees) == 0:
return 'No orders at amount ' + str(cj_amount / 1e8)
fig = plt.figure()
scale = args.get("scale")
if (scale is not None) and (scale[0] == "log"):
orderfees = [float(fee) for fee in orderfees]
if orderfees[0] > 0:
ratio = orderfees[-1] / orderfees[0]
step = ratio ** 0.0333 # 1/30
bins = [orderfees[0] * (step ** i) for i in range(30)]
else:
ratio = orderfees[-1] / 1e-8 # single satoshi placeholder
step = ratio ** 0.0333 # 1/30
bins = [1e-8 * (step ** i) for i in range(30)]
bins[0] = orderfees[0] # replace placeholder
plt.xscale('log')
else:
bins = 30
if len(orderfees) == 1: # these days we have liquidity, but just in case...
plt.hist(orderfees, bins, rwidth=0.8, range=(0, orderfees[0] * 2))
else:
plt.hist(orderfees, bins, rwidth=0.8)
plt.grid()
plt.title('CoinJoin Orderbook Depth Chart for amount=' + str(cj_amount /
1e8) + 'btc')
plt.xlabel('CoinJoin Fee / btc')
plt.ylabel('Frequency')
return get_graph_html(fig)
def create_size_histogram(self, args):
try:
self.taker.dblock.acquire(True)
rows = self.taker.db.execute('SELECT maxsize, ordertype FROM orderbook;').fetchall()
finally:
self.taker.dblock.release()
rows = [o for o in rows if o["ordertype"] in filtered_offername_list]
ordersizes = sorted([r['maxsize'] / 1e8 for r in rows])
fig = plt.figure()
scale = args.get("scale")
if (scale is not None) and (scale[0] == "log"):
ratio = ordersizes[-1] / ordersizes[0]
step = ratio ** 0.0333 # 1/30
bins = [ordersizes[0] * (step ** i) for i in range(30)]
else:
bins = 30
plt.hist(ordersizes, bins, histtype='bar', rwidth=0.8)
if bins is not 30:
fig.axes[0].set_xscale('log')
plt.grid()
plt.xlabel('Order sizes / btc')
plt.ylabel('Frequency')
return get_graph_html(fig) + ("<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):
counterparties = self.taker.db.execute(
'SELECT DISTINCT counterparty FROM orderbook WHERE ordertype=? OR ordertype=?;',
filtered_offername_list).fetchall()
try:
self.taker.dblock.acquire(True)
counterparties = self.taker.db.execute(
'SELECT DISTINCT counterparty FROM orderbook WHERE ordertype=? OR ordertype=?;',
filtered_offername_list).fetchall()
finally:
self.taker.dblock.release()
return str(len(counterparties))
def do_GET(self):
# SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)
# http.server.SimpleHTTPRequestHandler.do_GET(self)
# print 'httpd received ' + self.path + ' request'
self.path, query = self.path.split('?', 1) if '?' in self.path else (
self.path, '')
args = urllib2.urlparse.parse_qs(query)
args = parse_qs(query)
pages = ['/', '/ordersize', '/depth', '/orderbook.json']
if self.path not in pages:
return
@ -266,8 +287,8 @@ class OrderbookPageRequestHeader(SimpleHTTPServer.SimpleHTTPRequestHandler):
btc_unit = sorted_units[0]
if rel_unit not in sorted_rel_units:
rel_unit = sorted_rel_units[0]
ordercount, ordertable = create_orderbook_table(
self.taker.db, btc_unit, rel_unit)
ordercount, ordertable = self.create_orderbook_table(
btc_unit, rel_unit)
choose_units_form = create_choose_units_form(btc_unit, rel_unit)
table_heading = create_table_heading(btc_unit, rel_unit)
replacements = {
@ -285,13 +306,13 @@ class OrderbookPageRequestHeader(SimpleHTTPServer.SimpleHTTPRequestHandler):
'PAGETITLE': 'JoinMarket Browser Interface',
'MAINHEADING': 'Order Sizes',
'SECONDHEADING': 'Order Size Histogram' + alert_msg,
'MAINBODY': create_size_histogram(self.taker.db, args)
'MAINBODY': self.create_size_histogram(args)
}
elif self.path.startswith('/depth'):
# if self.path[6] == '?':
# quantity =
cj_amounts = [10 ** cja for cja in range(4, 12, 1)]
mainbody = [create_depth_chart(self.taker.db, cja, args) \
mainbody = [self.create_depth_chart(cja, args) \
for cja in cj_amounts] + \
["<br/><a href='?'>linear</a>" if args.get("scale") \
else "<br/><a href='?scale=log'>log scale</a>"]
@ -305,7 +326,7 @@ class OrderbookPageRequestHeader(SimpleHTTPServer.SimpleHTTPRequestHandler):
replacements = {}
orderbook_fmt = json.dumps(self.create_orderbook_obj())
orderbook_page = orderbook_fmt
for key, rep in replacements.iteritems():
for key, rep in iteritems(replacements):
orderbook_page = orderbook_page.replace(key, rep)
self.send_response(200)
if self.path.endswith('.json'):
@ -314,7 +335,7 @@ class OrderbookPageRequestHeader(SimpleHTTPServer.SimpleHTTPRequestHandler):
self.send_header('Content-Type', 'text/html')
self.send_header('Content-Length', len(orderbook_page))
self.end_headers()
self.wfile.write(orderbook_page)
self.wfile.write(orderbook_page.encode('utf-8'))
def do_POST(self):
global filtered_offername_list
@ -351,7 +372,7 @@ class HTTPDThread(threading.Thread):
def run(self):
# hostport = ('localhost', 62601)
httpd = BaseHTTPServer.HTTPServer(self.hostport,
httpd = http.server.HTTPServer(self.hostport,
OrderbookPageRequestHeader)
httpd.taker = self.taker
print('\nstarted http server, visit http://{0}:{1}/\n'.format(

4
scripts/qtsupport.py

@ -1,5 +1,7 @@
#!/usr/bin/env python
from __future__ import print_function
from __future__ import (absolute_import, division,
print_function, unicode_literals)
from builtins import *
'''
Qt files for the wizard for initiating a tumbler run.

8
scripts/sendpayment.py

@ -1,5 +1,7 @@
#! /usr/bin/env python
from __future__ import absolute_import, print_function
from __future__ import (absolute_import, division,
print_function, unicode_literals)
from builtins import * # noqa: F401
"""
A sample implementation of a single coinjoin script,
@ -34,7 +36,7 @@ def pick_order(orders, n): #pragma: no cover
return orders[0]
while pickedOrderIndex == -1:
try:
pickedOrderIndex = int(raw_input('Pick an order between 0 and ' +
pickedOrderIndex = int(input('Pick an order between 0 and ' +
str(i) + ': '))
except ValueError:
pickedOrderIndex = -1
@ -153,7 +155,7 @@ def main():
log.info('WARNING ' * 6)
log.info('\n'.join(['=' * 60] * 3))
if not options.answeryes:
if raw_input('send with these orders? (y/n):')[0] != 'y':
if input('send with these orders? (y/n):')[0] != 'y':
return False
return True

8
scripts/sendtomany.py

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

4
scripts/tumbler.py

@ -1,5 +1,7 @@
#! /usr/bin/env python
from __future__ import absolute_import, print_function
from __future__ import (absolute_import, division,
print_function, unicode_literals)
from builtins import * # noqa: F401
import sys
from twisted.internet import reactor

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

7
scripts/yg-privacyenhanced.py

@ -1,5 +1,8 @@
#! /usr/bin/env python
from __future__ import absolute_import, print_function
from __future__ import (absolute_import, division,
print_function, unicode_literals)
from builtins import * # noqa: F401
from future.utils import iteritems
import random
@ -44,7 +47,7 @@ class YieldGeneratorPrivacyEnhanced(YieldGeneratorBasic):
f = self.cjfee_r
elif ordertype == 'swabsoffer':
f = str(self.txfee + self.cjfee_a)
mix_balance = dict([(m, b) for m, b in mix_balance.iteritems()
mix_balance = dict([(m, b) for m, b in iteritems(mix_balance)
if b > self.minsize])
if len(mix_balance) == 0:
jlog.error('You do not have the minimum required amount of coins'

4
scripts/yield-generator-basic.py

@ -1,5 +1,7 @@
#! /usr/bin/env python
from __future__ import absolute_import, print_function
from __future__ import (absolute_import, division,
print_function, unicode_literals)
from builtins import * # noqa: F401
from jmclient import YieldGeneratorBasic, ygmain, get_log

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

4
test/test_full_coinjoin.py

@ -1,5 +1,7 @@
#! /usr/bin/env python
from __future__ import absolute_import, print_function
from __future__ import (absolute_import, division,
print_function, unicode_literals)
from builtins import * # noqa: F401
'''Runs a full joinmarket pit (using `nirc` miniircd servers,
with `nirc` options specified as an option to pytest),in
bitcoin regtest mode with 3 maker bots and 1 taker bot,

14
test/test_segwit.py

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

4
test/ygrunner.py

@ -1,5 +1,7 @@
#! /usr/bin/env python
from __future__ import absolute_import, print_function
from __future__ import (absolute_import, division,
print_function, unicode_literals)
from builtins import * # noqa: F401
'''Creates wallets and yield generators in regtest.
Provides seed for joinmarket-qt test.
This should be run via pytest, even though

Loading…
Cancel
Save