Browse Source

Adds libsecp256k1 installation and addresses reviews

Update no-history-sync code:
This updates the new functionality in jmclient.wallet_utils
in the no-history-sync PR #444 to be compatible
with the python-bitcointx refactoring.

Remove all future/py2 compatibility code remaining:
This is in line with #525 and corrects erroneous
addition of more compatibility code.

Addresses all flake8 complaints (ununsed imports etc)

Addresses review of @dgpv

Addresses review of @kristapsk
master
Adam Gibson 6 years ago
parent
commit
03a13598e8
No known key found for this signature in database
GPG Key ID: 141001A1AF77F20B
  1. 38
      install.sh
  2. 16
      jmbase/jmbase/support.py
  3. 4
      jmbase/setup.py
  4. 2
      jmbitcoin/jmbitcoin/__init__.py
  5. 9
      jmbitcoin/jmbitcoin/secp256k1_ecies.py
  6. 35
      jmbitcoin/jmbitcoin/secp256k1_main.py
  7. 28
      jmbitcoin/jmbitcoin/secp256k1_transaction.py
  8. 6
      jmbitcoin/jmbitcoin/snicker.py
  9. 4
      jmbitcoin/setup.py
  10. 3
      jmbitcoin/test/test_ecdh.py
  11. 3
      jmbitcoin/test/test_ecies.py
  12. 5
      jmbitcoin/test/test_tx_signing.py
  13. 5
      jmclient/jmclient/client_protocol.py
  14. 5
      jmclient/jmclient/commitment_utils.py
  15. 4
      jmclient/jmclient/configure.py
  16. 10
      jmclient/jmclient/cryptoengine.py
  17. 3
      jmclient/jmclient/electruminterface.py
  18. 8
      jmclient/jmclient/maker.py
  19. 1
      jmclient/jmclient/podle.py
  20. 6
      jmclient/jmclient/snicker_receiver.py
  21. 6
      jmclient/jmclient/storage.py
  22. 23
      jmclient/jmclient/taker.py
  23. 16
      jmclient/jmclient/taker_utils.py
  24. 28
      jmclient/jmclient/wallet.py
  25. 5
      jmclient/jmclient/wallet_service.py
  26. 34
      jmclient/jmclient/wallet_utils.py
  27. 7
      jmclient/jmclient/yieldgenerator.py
  28. 5
      jmclient/setup.py
  29. 5
      jmclient/test/commontest.py
  30. 2
      jmclient/test/test_client_protocol.py
  31. 2
      jmclient/test/test_coinjoin.py
  32. 3
      jmclient/test/test_maker.py
  33. 2
      jmclient/test/test_payjoin.py
  34. 1
      jmclient/test/test_podle.py
  35. 8
      jmclient/test/test_privkeys.py
  36. 5
      jmclient/test/test_psbt_wallet.py
  37. 14
      jmclient/test/test_snicker.py
  38. 3
      jmclient/test/test_taker.py
  39. 5
      jmclient/test/test_tx_creation.py
  40. 3
      jmclient/test/test_wallet.py
  41. 1
      jmclient/test/test_wallets.py
  42. 3
      jmdaemon/jmdaemon/daemon_protocol.py
  43. 1
      jmdaemon/jmdaemon/irc.py
  44. 7
      jmdaemon/jmdaemon/message_channel.py
  45. 4
      jmdaemon/setup.py
  46. 3
      jmdaemon/test/test_daemon_protocol.py
  47. 5
      scripts/add-utxo.py
  48. 2
      scripts/joinmarket-qt.py
  49. 5
      scripts/sendtomany.py
  50. 11
      test/test_segwit.py

38
install.sh

@ -234,6 +234,40 @@ libffi_install ()
popd popd
} }
libsecp256k1_build()
{
make clean
./autogen.sh
./configure \
--enable-module-recovery \
--disable-jni \
--prefix "${jm_root}" \
--enable-experimental \
--enable-module-ecdh \
--enable-benchmark=no
make
if ! make check; then
return 1
fi
}
libsecp256k1_install()
{
secp256k1_lib_tar='0d9540b13ffcd7cd44cc361b8744b93d88aa76ba'
secp256k1_lib_sha="0803d2dddbf6dd702c379118f066f638bcef6b07eea959f12d31ad2f4721fbe1"
secp256k1_lib_url='https://github.com/bitcoin-core/secp256k1/archive'
if ! dep_get "${secp256k1_lib_tar}.tar.gz" "${secp256k1_lib_sha}" "${secp256k1_lib_url}"; then
return 1
fi
pushd "secp256k1-${secp256k1_lib_tar}"
if libsecp256k1_build; then
make install
else
return 1
fi
popd
}
libsodium_build () libsodium_build ()
{ {
make uninstall make uninstall
@ -419,6 +453,10 @@ main ()
# echo "Openssl was not built. Exiting." # echo "Openssl was not built. Exiting."
# return 1 # return 1
# fi # fi
if ! libsecp256k1_install; then
echo "libsecp256k1 was not built. Exiting."
return 1
fi
if ! libffi_install; then if ! libffi_install; then
echo "Libffi was not built. Exiting." echo "Libffi was not built. Exiting."
return 1 return 1

16
jmbase/jmbase/support.py

@ -220,9 +220,9 @@ def print_jm_version(option, opt_str, value, parser):
# helper functions for conversions of format between over-the-wire JM # helper functions for conversions of format between over-the-wire JM
# and internal. See details in hexbin() docstring. # and internal. See details in hexbin() docstring.
def cv(x): def _convert(x):
success, utxo = utxostr_to_utxo(x) good, utxo = utxostr_to_utxo(x)
if success: if good:
return utxo return utxo
else: else:
try: try:
@ -239,18 +239,18 @@ def listchanger(l):
elif isinstance(x, dict): elif isinstance(x, dict):
rlist.append(dictchanger(x)) rlist.append(dictchanger(x))
else: else:
rlist.append(cv(x)) rlist.append(_convert(x))
return rlist return rlist
def dictchanger(d): def dictchanger(d):
rdict = {} rdict = {}
for k, v in d.items(): for k, v in d.items():
if isinstance(v, dict): if isinstance(v, dict):
rdict[cv(k)] = dictchanger(v) rdict[_convert(k)] = dictchanger(v)
elif isinstance(v, list): elif isinstance(v, list):
rdict[cv(k)] = listchanger(v) rdict[_convert(k)] = listchanger(v)
else: else:
rdict[cv(k)] = cv(v) rdict[_convert(k)] = _convert(v)
return rdict return rdict
def hexbin(func): def hexbin(func):
@ -276,7 +276,7 @@ def hexbin(func):
elif isinstance(arg, dict): elif isinstance(arg, dict):
newargs.append(dictchanger(arg)) newargs.append(dictchanger(arg))
else: else:
newargs.append(cv(arg)) newargs.append(_convert(arg))
return func(inst, *newargs, **kwargs) return func(inst, *newargs, **kwargs)
return func_wrapper return func_wrapper

4
jmbase/setup.py

@ -9,7 +9,7 @@ setup(name='joinmarketbase',
author_email='', author_email='',
license='GPL', license='GPL',
packages=['jmbase'], packages=['jmbase'],
install_requires=['future', 'twisted==19.7.0', 'service-identity', install_requires=['twisted==19.7.0', 'service-identity',
'chromalog==1.0.5'], 'chromalog==1.0.5'],
python_requires='>=3.3', python_requires='>=3.6',
zip_safe=False) zip_safe=False)

2
jmbitcoin/jmbitcoin/__init__.py

@ -13,7 +13,5 @@ from bitcointx.core import (x, b2x, b2lx, lx, COutPoint, CTxOut, CTxIn,
from bitcointx.core.key import KeyStore from bitcointx.core.key import KeyStore
from bitcointx.core.script import (CScript, OP_0, SignatureHash, SIGHASH_ALL, from bitcointx.core.script import (CScript, OP_0, SignatureHash, SIGHASH_ALL,
SIGVERSION_WITNESS_V0, CScriptWitness) SIGVERSION_WITNESS_V0, CScriptWitness)
from bitcointx.wallet import (CBitcoinSecret, P2WPKHBitcoinAddress, CCoinAddress,
P2SHCoinAddress)
from bitcointx.core.psbt import PartiallySignedTransaction from bitcointx.core.psbt import PartiallySignedTransaction

9
jmbitcoin/jmbitcoin/secp256k1_ecies.py

@ -1,8 +1,5 @@
#!/usr/bin/python #!/usr/bin/python
from __future__ import (absolute_import, division,
print_function, unicode_literals)
from builtins import * # noqa: F401
from future.utils import native
import coincurve as secp256k1 import coincurve as secp256k1
import base64 import base64
import hmac import hmac
@ -19,7 +16,7 @@ class ECIESDecryptionError(Exception):
# AES primitives. See BIP-SNICKER for specification. # AES primitives. See BIP-SNICKER for specification.
def aes_encrypt(key, data, iv): def aes_encrypt(key, data, iv):
encrypter = pyaes.Encrypter( encrypter = pyaes.Encrypter(
pyaes.AESModeOfOperationCBC(key, iv=native(iv))) pyaes.AESModeOfOperationCBC(key, iv=iv))
enc_data = encrypter.feed(data) enc_data = encrypter.feed(data)
enc_data += encrypter.feed() enc_data += encrypter.feed()
@ -27,7 +24,7 @@ def aes_encrypt(key, data, iv):
def aes_decrypt(key, data, iv): def aes_decrypt(key, data, iv):
decrypter = pyaes.Decrypter( decrypter = pyaes.Decrypter(
pyaes.AESModeOfOperationCBC(key, iv=native(iv))) pyaes.AESModeOfOperationCBC(key, iv=iv))
try: try:
dec_data = decrypter.feed(data) dec_data = decrypter.feed(data)
dec_data += decrypter.feed() dec_data += decrypter.feed()

35
jmbitcoin/jmbitcoin/secp256k1_main.py

@ -1,15 +1,11 @@
#!/usr/bin/python #!/usr/bin/python
from future.utils import native_bytes, bytes_to_native_str
import binascii
import hashlib
import sys
import base64 import base64
import struct import struct
import coincurve as secp256k1 import coincurve as secp256k1
from bitcointx import base58 from bitcointx import base58
from bitcointx.core import Hash, CBitcoinTransaction from bitcointx.core import Hash
from bitcointx.core.key import CKeyBase, CPubKey from bitcointx.core.key import CKeyBase
from bitcointx.signmessage import BitcoinMessage from bitcointx.signmessage import BitcoinMessage
#Required only for PoDLE calculation: #Required only for PoDLE calculation:
@ -60,7 +56,7 @@ def privkey_to_pubkey(priv):
and return compressed/uncompressed public key as appropriate.''' and return compressed/uncompressed public key as appropriate.'''
compressed, priv = read_privkey(priv) compressed, priv = read_privkey(priv)
#secp256k1 checks for validity of key value. #secp256k1 checks for validity of key value.
newpriv = secp256k1.PrivateKey(secret=native_bytes(priv)) newpriv = secp256k1.PrivateKey(secret=priv)
return newpriv.public_key.format(compressed) return newpriv.public_key.format(compressed)
# b58check wrapper functions around bitcointx.base58 functions: # b58check wrapper functions around bitcointx.base58 functions:
@ -137,7 +133,7 @@ def multiply(s, pub, return_serialized=True):
''' '''
newpub = secp256k1.PublicKey(pub) newpub = secp256k1.PublicKey(pub)
#see note to "tweak_mul" function in podle.py #see note to "tweak_mul" function in podle.py
res = newpub.multiply(native_bytes(s)) res = newpub.multiply(s)
if not return_serialized: if not return_serialized:
return res return res
return res.format() return res.format()
@ -245,32 +241,9 @@ class JMCKey(bytes, CKeyBase):
def __init__(self, b): def __init__(self, b):
CKeyBase.__init__(self, b, compressed=True) CKeyBase.__init__(self, b, compressed=True)
def is_compressed(self):
return True
@property
def secret_bytes(self):
assert isinstance(self, bytes)
return self[:32]
def sign(self, hash): def sign(self, hash):
assert isinstance(hash, (bytes, bytearray)) assert isinstance(hash, (bytes, bytearray))
if len(hash) != 32: if len(hash) != 32:
raise ValueError('Hash must be exactly 32 bytes long') raise ValueError('Hash must be exactly 32 bytes long')
# TODO: non default sighash flag. # TODO: non default sighash flag.
return ecdsa_raw_sign(hash, self.secret_bytes + b"\x01", rawmsg=True) return ecdsa_raw_sign(hash, self.secret_bytes + b"\x01", rawmsg=True)
def verify(self, hash, sig):
return self.pub.verify(hash, sig)
def verify_nonstrict(self, hash, sig):
return self.pub.verify_nonstrict(hash, sig)
@classmethod
def from_secret_bytes(cls, secret, compressed=True):
return cls(secret, compressed=compressed)
@classmethod
def from_bytes(cls, data):
raise NotImplementedError('subclasses must override from_bytes()')

28
jmbitcoin/jmbitcoin/secp256k1_transaction.py

@ -1,20 +1,14 @@
#!/usr/bin/python #!/usr/bin/python
from past.builtins import basestring
from io import BytesIO
import binascii
import copy
import re
import os
import struct
# note, only used for non-cryptographic randomness: # note, only used for non-cryptographic randomness:
import random import random
from jmbitcoin.secp256k1_main import * from jmbitcoin.secp256k1_main import *
from bitcointx.core import (CMutableTransaction, Hash160, CTxInWitness, from bitcointx.core import (CMutableTransaction, Hash160, CTxInWitness,
CTxWitness, CMutableOutPoint, CMutableTxIn, CMutableOutPoint, CMutableTxIn,
CMutableTxOut, ValidationError, lx, x) CMutableTxOut, ValidationError)
from bitcointx.core.script import * from bitcointx.core.script import *
from bitcointx.wallet import P2WPKHBitcoinAddress, CCoinAddress from bitcointx.wallet import P2WPKHCoinAddress, CCoinAddress, P2PKHCoinAddress
from bitcointx.core.scripteval import (VerifyScript, SCRIPT_VERIFY_WITNESS, from bitcointx.core.scripteval import (VerifyScript, SCRIPT_VERIFY_WITNESS,
SCRIPT_VERIFY_P2SH, SIGVERSION_WITNESS_V0) SCRIPT_VERIFY_P2SH, SIGVERSION_WITNESS_V0)
@ -74,10 +68,7 @@ def pubkey_to_p2pkh_script(pub, require_compressed=False):
representing the corresponding pay-to-pubkey-hash representing the corresponding pay-to-pubkey-hash
scriptPubKey. scriptPubKey.
""" """
if not is_valid_pubkey(pub, require_compressed=require_compressed): return P2PKHCoinAddress.from_pubkey(pub).to_scriptPubKey()
raise Exception("Invalid pubkey")
return CScript([OP_DUP, OP_HASH160, Hash160(pub),
OP_EQUALVERIFY, OP_CHECKSIG])
def pubkey_to_p2wpkh_script(pub): def pubkey_to_p2wpkh_script(pub):
""" """
@ -85,9 +76,7 @@ def pubkey_to_p2wpkh_script(pub):
representing the corresponding pay-to-witness-pubkey-hash representing the corresponding pay-to-witness-pubkey-hash
scriptPubKey. scriptPubKey.
""" """
if not is_valid_pubkey(pub, True): return P2WPKHCoinAddress.from_pubkey(pub).to_scriptPubKey()
raise Exception("Invalid pubkey")
return CScript([OP_0, Hash160(pub)])
def pubkey_to_p2sh_p2wpkh_script(pub): def pubkey_to_p2sh_p2wpkh_script(pub):
""" """
@ -146,8 +135,7 @@ def sign(tx, i, priv, hashcode=SIGHASH_ALL, amount=None, native=False):
return None, "Error in signing: " + repr(e) return None, "Error in signing: " + repr(e)
assert isinstance(tx, CMutableTransaction) assert isinstance(tx, CMutableTransaction)
# using direct local access to libsecp256k1 binding, because
# python-bitcoinlib uses OpenSSL key management:
pub = privkey_to_pubkey(priv) pub = privkey_to_pubkey(priv)
if not amount: if not amount:
@ -175,7 +163,7 @@ def sign(tx, i, priv, hashcode=SIGHASH_ALL, amount=None, native=False):
input_scriptPubKey = pubkey_to_p2wpkh_script(pub) input_scriptPubKey = pubkey_to_p2wpkh_script(pub)
# only created for convenience access to scriptCode: # only created for convenience access to scriptCode:
input_address = P2WPKHBitcoinAddress.from_scriptPubKey(input_scriptPubKey) input_address = P2WPKHCoinAddress.from_scriptPubKey(input_scriptPubKey)
# function name is misleading here; redeemScript only applies to p2sh. # function name is misleading here; redeemScript only applies to p2sh.
scriptCode = input_address.to_redeemScript() scriptCode = input_address.to_redeemScript()

6
jmbitcoin/jmbitcoin/snicker.py

@ -1,7 +1,3 @@
from __future__ import (absolute_import, division,
print_function, unicode_literals)
from builtins import * # noqa: F401
# Implementation of proposal as per # Implementation of proposal as per
# https://gist.github.com/AdamISZ/2c13fb5819bd469ca318156e2cf25d79 # https://gist.github.com/AdamISZ/2c13fb5819bd469ca318156e2cf25d79
# (BIP SNICKER) # (BIP SNICKER)
@ -45,7 +41,7 @@ def verify_snicker_output(tx, pub, tweak, spk_type='p2sh-p2wpkh'):
or -1 and None if it is not found exactly once. or -1 and None if it is not found exactly once.
TODO Add support for other scriptPubKey types. TODO Add support for other scriptPubKey types.
""" """
assert isinstance(tx, btc.CBitcoinTransaction) assert isinstance(tx, btc.CTransaction)
expected_destination_pub = snicker_pubkey_tweak(pub, tweak) expected_destination_pub = snicker_pubkey_tweak(pub, tweak)
expected_destination_spk = pubkey_to_p2sh_p2wpkh_script(expected_destination_pub) expected_destination_spk = pubkey_to_p2sh_p2wpkh_script(expected_destination_pub)
found = 0 found = 0

4
jmbitcoin/setup.py

@ -9,6 +9,6 @@ setup(name='joinmarketbitcoin',
author_email='', author_email='',
license='GPL', license='GPL',
packages=['jmbitcoin'], packages=['jmbitcoin'],
install_requires=['future', 'coincurve', 'urldecode', python_requires='>=3.6',
'python-bitcointx>=1.0.5', 'pyaes'], install_requires=['coincurve', 'python-bitcointx>=1.0.5', 'pyaes', 'urldecode'],
zip_safe=False) zip_safe=False)

3
jmbitcoin/test/test_ecdh.py

@ -1,7 +1,4 @@
#! /usr/bin/env python #! /usr/bin/env python
from __future__ import (absolute_import, division,
print_function, unicode_literals)
from builtins import * # noqa: F401
'''Tests coincurve binding to libsecp256k1 ecdh module code''' '''Tests coincurve binding to libsecp256k1 ecdh module code'''
import hashlib import hashlib

3
jmbitcoin/test/test_ecies.py

@ -1,7 +1,4 @@
#! /usr/bin/env python #! /usr/bin/env python
from __future__ import (absolute_import, division,
print_function, unicode_literals)
from builtins import * # noqa: F401
'''Tests ECIES implementation as defined in BIP-SNICKER '''Tests ECIES implementation as defined in BIP-SNICKER
(and will be updated if that is).''' (and will be updated if that is).'''

5
jmbitcoin/test/test_tx_signing.py

@ -1,6 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import sys
import pytest import pytest
import binascii import binascii
@ -26,7 +25,7 @@ def test_sign_standard_txs(addrtype):
# (note that the input utxo is fake so we are really only creating # (note that the input utxo is fake so we are really only creating
# a destination here). # a destination here).
scriptPubKey = btc.CScript([btc.OP_0, btc.Hash160(pub)]) scriptPubKey = btc.CScript([btc.OP_0, btc.Hash160(pub)])
address = btc.P2WPKHBitcoinAddress.from_scriptPubKey(scriptPubKey) address = btc.P2WPKHCoinAddress.from_scriptPubKey(scriptPubKey)
# Create a dummy outpoint; use same 32 bytes for convenience # Create a dummy outpoint; use same 32 bytes for convenience
txid = priv[:32] txid = priv[:32]
@ -66,7 +65,7 @@ def test_mk_shuffled_tx():
# prepare two addresses for the outputs # prepare two addresses for the outputs
pub = btc.privkey_to_pubkey(btc.Hash(b"priv") + b"\x01") pub = btc.privkey_to_pubkey(btc.Hash(b"priv") + b"\x01")
scriptPubKey = btc.CScript([btc.OP_0, btc.Hash160(pub)]) scriptPubKey = btc.CScript([btc.OP_0, btc.Hash160(pub)])
addr1 = btc.P2WPKHBitcoinAddress.from_scriptPubKey(scriptPubKey) addr1 = btc.P2WPKHCoinAddress.from_scriptPubKey(scriptPubKey)
scriptPubKey_p2sh = scriptPubKey.to_p2sh_scriptPubKey() scriptPubKey_p2sh = scriptPubKey.to_p2sh_scriptPubKey()
addr2 = btc.CCoinAddress.from_scriptPubKey(scriptPubKey_p2sh) addr2 = btc.CCoinAddress.from_scriptPubKey(scriptPubKey_p2sh)

5
jmclient/jmclient/client_protocol.py

@ -1,5 +1,4 @@
#! /usr/bin/env python #! /usr/bin/env python
from future.utils import iteritems
from twisted.internet import protocol, reactor, task from twisted.internet import protocol, reactor, task
from twisted.internet.error import (ConnectionLost, ConnectionAborted, from twisted.internet.error import (ConnectionLost, ConnectionAborted,
ConnectionClosed, ConnectionDone) ConnectionClosed, ConnectionDone)
@ -15,7 +14,7 @@ import hashlib
import os import os
import sys import sys
from jmbase import (get_log, EXIT_FAILURE, hextobin, bintohex, from jmbase import (get_log, EXIT_FAILURE, hextobin, bintohex,
utxo_to_utxostr, dictchanger) utxo_to_utxostr)
from jmclient import (jm_single, get_irc_mchannels, from jmclient import (jm_single, get_irc_mchannels,
RegtestBitcoinCoreInterface) RegtestBitcoinCoreInterface)
import jmbitcoin as btc import jmbitcoin as btc
@ -303,7 +302,7 @@ class JMMakerClientProtocol(JMClientProtocol):
return {"accepted": True} return {"accepted": True}
def tx_match(self, txd): def tx_match(self, txd):
for k,v in iteritems(self.finalized_offers): for k,v in self.finalized_offers.items():
# Tx considered defined by its output set # Tx considered defined by its output set
if v["txd"].vout == txd.vout: if v["txd"].vout == txd.vout:
offerinfo = v offerinfo = v

5
jmclient/jmclient/commitment_utils.py

@ -1,10 +1,7 @@
import sys import sys
import jmbitcoin as btc
from jmbase import jmprint from jmbase import jmprint
from jmclient import (jm_single, get_p2pk_vbyte, get_p2sh_vbyte, from jmclient import jm_single, BTCEngine, BTC_P2PKH, BTC_P2SH_P2WPKH
BTCEngine, TYPE_P2PKH, TYPE_P2SH_P2WPKH,
BTC_P2PKH, BTC_P2SH_P2WPKH)
from jmbase.support import EXIT_FAILURE, utxostr_to_utxo from jmbase.support import EXIT_FAILURE, utxostr_to_utxo

4
jmclient/jmclient/configure.py

@ -379,13 +379,13 @@ def validate_address(addr):
try: try:
# automatically respects the network # automatically respects the network
# as set in btc.select_chain_params(...) # as set in btc.select_chain_params(...)
x = btc.CCoinAddress(addr) dummyaddr = btc.CCoinAddress(addr)
except Exception as e: except Exception as e:
return False, repr(e) return False, repr(e)
# additional check necessary because python-bitcointx # additional check necessary because python-bitcointx
# does not check hash length on p2sh construction. # does not check hash length on p2sh construction.
try: try:
x.to_scriptPubKey() dummyaddr.to_scriptPubKey()
except Exception as e: except Exception as e:
return False, repr(e) return False, repr(e)
return True, "address validated" return True, "address validated"

10
jmclient/jmclient/cryptoengine.py

@ -1,11 +1,11 @@
from binascii import hexlify, unhexlify
from collections import OrderedDict from collections import OrderedDict
import struct import struct
import jmbitcoin as btc import jmbitcoin as btc
from .configure import get_network, jm_single from jmbase import bintohex
from .configure import get_network
#NOTE: before fidelity bonds and watchonly wallet, each of these types corresponded #NOTE: before fidelity bonds and watchonly wallet, each of these types corresponded
# to one wallet type and one engine, not anymore # to one wallet type and one engine, not anymore
@ -31,7 +31,7 @@ def detect_script_type(script_str):
script = btc.CScript(script_str) script = btc.CScript(script_str)
if not script.is_valid(): if not script.is_valid():
raise EngineError("Unknown script type for script '{}'" raise EngineError("Unknown script type for script '{}'"
.format(hexlify(script_str))) .format(bintohex(script_str)))
if script.is_p2pkh(): if script.is_p2pkh():
return TYPE_P2PKH return TYPE_P2PKH
elif script.is_p2sh(): elif script.is_p2sh():
@ -42,7 +42,7 @@ def detect_script_type(script_str):
elif script.is_witness_v0_keyhash(): elif script.is_witness_v0_keyhash():
return TYPE_P2WPKH return TYPE_P2WPKH
raise EngineError("Unknown script type for script '{}'" raise EngineError("Unknown script type for script '{}'"
.format(hexlify(script_str))) .format(bintohex(script_str)))
class classproperty(object): class classproperty(object):
""" """

3
jmclient/jmclient/electruminterface.py

@ -1,4 +1,3 @@
from future.utils import iteritems
import jmbitcoin as btc import jmbitcoin as btc
import json import json
import queue as Queue import queue as Queue
@ -332,7 +331,7 @@ class ElectrumInterface(BlockchainInterface):
for m in range(wallet.max_mixdepth): for m in range(wallet.max_mixdepth):
for fc in [0, 1]: for fc in [0, 1]:
branch_list = [] branch_list = []
for k, v in iteritems(self.temp_addr_history[m][fc]): for k, v in self.temp_addr_history[m][fc].items():
if k == "finished": if k == "finished":
continue continue
if v["used"]: if v["used"]:

8
jmclient/jmclient/maker.py

@ -1,5 +1,4 @@
#! /usr/bin/env python #! /usr/bin/env python
from future.utils import iteritems
import base64 import base64
import pprint import pprint
import random import random
@ -7,8 +6,7 @@ import sys
import abc import abc
import jmbitcoin as btc import jmbitcoin as btc
from jmbase import (bintohex, hextobin, hexbin, from jmbase import bintohex, hexbin, get_log, EXIT_SUCCESS, EXIT_FAILURE
get_log, EXIT_SUCCESS, EXIT_FAILURE)
from jmclient.wallet import estimate_tx_fee, compute_tx_locktime from jmclient.wallet import estimate_tx_fee, compute_tx_locktime
from jmclient.wallet_service import WalletService from jmclient.wallet_service import WalletService
from jmclient.configure import jm_single from jmclient.configure import jm_single
@ -470,7 +468,7 @@ class P2EPMaker(Maker):
[x[1] for x in utxo.values()]) [x[1] for x in utxo.values()])
total_sender_input = 0 total_sender_input = 0
for i, u in iteritems(utxo): for i, u in utxo.items():
if utxo_data[i] is None: if utxo_data[i] is None:
return (False, "Proposed transaction contains invalid utxos") return (False, "Proposed transaction contains invalid utxos")
total_sender_input += utxo_data[i]["value"] total_sender_input += utxo_data[i]["value"]
@ -505,7 +503,7 @@ class P2EPMaker(Maker):
# Manual verification of the transaction signatures. # Manual verification of the transaction signatures.
# TODO handle native segwit properly # TODO handle native segwit properly
for i, u in iteritems(utxo): for i, u in utxo.items():
if not btc.verify_tx_input(tx, i, if not btc.verify_tx_input(tx, i,
tx.vin[i].scriptSig, tx.vin[i].scriptSig,
btc.CScript(utxo_data[i]["script"]), btc.CScript(utxo_data[i]["script"]),

1
jmclient/jmclient/podle.py

@ -5,7 +5,6 @@ import os
import sys import sys
import hashlib import hashlib
import json import json
import binascii
import struct import struct
from pprint import pformat from pprint import pformat
from jmbase import jmprint from jmbase import jmprint

6
jmclient/jmclient/snicker_receiver.py

@ -1,13 +1,9 @@
#! /usr/bin/env python #! /usr/bin/env python
from __future__ import (absolute_import, division,
print_function, unicode_literals)
from builtins import * # noqa: F401
import sys import sys
import binascii
import jmbitcoin as btc import jmbitcoin as btc
from jmclient.configure import get_p2pk_vbyte, jm_single from jmclient.configure import jm_single
from jmbase import (get_log, EXIT_FAILURE, utxo_to_utxostr, from jmbase import (get_log, EXIT_FAILURE, utxo_to_utxostr,
bintohex, hextobin) bintohex, hextobin)

6
jmclient/jmclient/storage.py

@ -1,5 +1,3 @@
from future.utils import native
import os import os
import shutil import shutil
import atexit import atexit
@ -253,7 +251,7 @@ class Storage(object):
def _encrypt(self, data, iv): def _encrypt(self, data, iv):
encrypter = pyaes.Encrypter( encrypter = pyaes.Encrypter(
pyaes.AESModeOfOperationCBC(self._hash.hash, iv=native(iv))) pyaes.AESModeOfOperationCBC(self._hash.hash, iv=iv))
enc_data = encrypter.feed(self.MAGIC_DETECT_ENC + data) enc_data = encrypter.feed(self.MAGIC_DETECT_ENC + data)
enc_data += encrypter.feed() enc_data += encrypter.feed()
@ -261,7 +259,7 @@ class Storage(object):
def _decrypt(self, data, iv): def _decrypt(self, data, iv):
decrypter = pyaes.Decrypter( decrypter = pyaes.Decrypter(
pyaes.AESModeOfOperationCBC(self._hash.hash, iv=native(iv))) pyaes.AESModeOfOperationCBC(self._hash.hash, iv=iv))
try: try:
dec_data = decrypter.feed(data) dec_data = decrypter.feed(data)
dec_data += decrypter.feed() dec_data += decrypter.feed()

23
jmclient/jmclient/taker.py

@ -1,5 +1,4 @@
#! /usr/bin/env python #! /usr/bin/env python
from future.utils import iteritems
import base64 import base64
import pprint import pprint
@ -8,11 +7,11 @@ from twisted.internet import reactor, task
import jmbitcoin as btc import jmbitcoin as btc
from jmclient.configure import jm_single, validate_address from jmclient.configure import jm_single, validate_address
from jmbase import get_log, hextobin, bintohex, hexbin from jmbase import get_log, bintohex, hexbin
from jmclient.support import (calc_cj_fee, weighted_order_choose, choose_orders, from jmclient.support import (calc_cj_fee, weighted_order_choose, choose_orders,
choose_sweep_orders) choose_sweep_orders)
from jmclient.wallet import estimate_tx_fee, compute_tx_locktime from jmclient.wallet import estimate_tx_fee, compute_tx_locktime
from jmclient.podle import generate_podle, get_podle_commitments, PoDLE from jmclient.podle import generate_podle, get_podle_commitments
from jmclient.wallet_service import WalletService from jmclient.wallet_service import WalletService
from .output import generate_podle_error_string from .output import generate_podle_error_string
from .cryptoengine import EngineError from .cryptoengine import EngineError
@ -354,7 +353,7 @@ class Taker(object):
rejected_counterparties = [] rejected_counterparties = []
#Need to authorize against the btc pubkey first. #Need to authorize against the btc pubkey first.
for nick, nickdata in iteritems(ioauth_data): for nick, nickdata in ioauth_data.items():
utxo_list, auth_pub, cj_addr, change_addr, btc_sig, maker_pk = nickdata utxo_list, auth_pub, cj_addr, change_addr, btc_sig, maker_pk = nickdata
if not self.auth_counterparty(btc_sig, auth_pub, maker_pk): if not self.auth_counterparty(btc_sig, auth_pub, maker_pk):
jlog.debug( jlog.debug(
@ -374,7 +373,7 @@ class Taker(object):
self.maker_utxo_data = {} self.maker_utxo_data = {}
for nick, nickdata in iteritems(ioauth_data): for nick, nickdata in ioauth_data.items():
utxo_list, auth_pub, cj_addr, change_addr, _, _ = nickdata utxo_list, auth_pub, cj_addr, change_addr, _, _ = nickdata
utxo_data = jm_single().bc_interface.query_utxo_set(utxo_list) utxo_data = jm_single().bc_interface.query_utxo_set(utxo_list)
self.utxos[nick] = utxo_list self.utxos[nick] = utxo_list
@ -452,8 +451,7 @@ class Taker(object):
#used to track return of signatures for phase 2 #used to track return of signatures for phase 2
self.nonrespondants = list(self.maker_utxo_data.keys()) self.nonrespondants = list(self.maker_utxo_data.keys())
my_total_in = sum([va['value'] for u, va in iteritems(self.input_utxos) my_total_in = sum([va['value'] for u, va in self.input_utxos.items()])
])
if self.my_change_addr: if self.my_change_addr:
#Estimate fee per choice of next/3/6 blocks targetting. #Estimate fee per choice of next/3/6 blocks targetting.
estimated_fee = estimate_tx_fee( estimated_fee = estimate_tx_fee(
@ -559,7 +557,7 @@ class Taker(object):
utxo_data = jm_single().bc_interface.query_utxo_set([x[ utxo_data = jm_single().bc_interface.query_utxo_set([x[
1] for x in utxo.values()]) 1] for x in utxo.values()])
# insert signatures # insert signatures
for i, u in iteritems(utxo): for i, u in utxo.items():
if utxo_data[i] is None: if utxo_data[i] is None:
continue continue
# Check if the sender included the scriptCode in the sig message; # Check if the sender included the scriptCode in the sig message;
@ -698,7 +696,7 @@ class Taker(object):
new_utxos, too_old, too_small = filter_by_coin_age_amt(list(utxos.keys()), new_utxos, too_old, too_small = filter_by_coin_age_amt(list(utxos.keys()),
age, amt) age, amt)
new_utxos_dict = {k: v for k, v in utxos.items() if k in new_utxos} new_utxos_dict = {k: v for k, v in utxos.items() if k in new_utxos}
for k, v in iteritems(new_utxos_dict): for k, v in new_utxos_dict.items():
addr = self.wallet_service.script_to_addr(v["script"]) addr = self.wallet_service.script_to_addr(v["script"])
priv = self.wallet_service.get_key_from_addr(addr) priv = self.wallet_service.get_key_from_addr(addr)
if priv: #can be null from create-unsigned if priv: #can be null from create-unsigned
@ -831,7 +829,8 @@ class Taker(object):
self.on_finished_callback(False, fromtx=True) self.on_finished_callback(False, fromtx=True)
else: else:
if nick_to_use: if nick_to_use:
return (nick_to_use, tx) # TODO option not currently functional
return (nick_to_use, self.latest_tx.serialize())
#if push was not successful, return None #if push was not successful, return None
def self_sign_and_push(self): def self_sign_and_push(self):
@ -985,7 +984,7 @@ class P2EPTaker(Taker):
# use output destination self.my_cj_addr and use amount self.amount # use output destination self.my_cj_addr and use amount self.amount
self.outputs.append({'address': self.my_cj_addr, self.outputs.append({'address': self.my_cj_addr,
'value': self.cjamount}) 'value': self.cjamount})
my_total_in = sum([va['value'] for u, va in iteritems(self.input_utxos)]) my_total_in = sum([va['value'] for u, va in self.input_utxos.items()])
# estimate the fee for the version of the transaction which is # estimate the fee for the version of the transaction which is
# not coinjoined: # not coinjoined:
est_fee = estimate_tx_fee(len(self.input_utxos), 2, est_fee = estimate_tx_fee(len(self.input_utxos), 2,
@ -1137,7 +1136,7 @@ class P2EPTaker(Taker):
# Next we'll verify each of the counterparty's inputs, # Next we'll verify each of the counterparty's inputs,
# while at the same time gathering the total they spent. # while at the same time gathering the total they spent.
total_receiver_input = 0 total_receiver_input = 0
for i, u in iteritems(retrieve_utxos): for i, u in retrieve_utxos.items():
if utxo_data[i] is None: if utxo_data[i] is None:
return (False, "Proposed transaction contains invalid utxos") return (False, "Proposed transaction contains invalid utxos")
total_receiver_input += utxo_data[i]["value"] total_receiver_input += utxo_data[i]["value"]

16
jmclient/jmclient/taker_utils.py

@ -1,4 +1,3 @@
from future.utils import iteritems
import logging import logging
import pprint import pprint
import os import os
@ -81,8 +80,7 @@ def direct_send(wallet_service, amount, mixdepth, destination, answeryes=False,
log.error( log.error(
"There are no available utxos in mixdepth: " + str(mixdepth) + ", quitting.") "There are no available utxos in mixdepth: " + str(mixdepth) + ", quitting.")
return return
total_inputs_val = sum([va['value'] for u, va in utxos.items()])
total_inputs_val = sum([va['value'] for u, va in iteritems(utxos)])
if is_burn_destination(destination): if is_burn_destination(destination):
if len(utxos) > 1: if len(utxos) > 1:
@ -116,7 +114,7 @@ def direct_send(wallet_service, amount, mixdepth, destination, answeryes=False,
fee_est = estimate_tx_fee(len(utxos), 2, txtype=txtype) fee_est = estimate_tx_fee(len(utxos), 2, txtype=txtype)
else: else:
fee_est = initial_fee_est fee_est = initial_fee_est
total_inputs_val = sum([va['value'] for u, va in iteritems(utxos)]) total_inputs_val = sum([va['value'] for u, va in utxos.items()])
changeval = total_inputs_val - fee_est - amount changeval = total_inputs_val - fee_est - amount
outs = [{"value": amount, "address": destination}] outs = [{"value": amount, "address": destination}]
change_addr = wallet_service.get_internal_addr(mixdepth) change_addr = wallet_service.get_internal_addr(mixdepth)
@ -193,16 +191,6 @@ def direct_send(wallet_service, amount, mixdepth, destination, answeryes=False,
txinfo = txid if not return_transaction else tx txinfo = txid if not return_transaction else tx
return txinfo return txinfo
def sign_tx(wallet_service, tx, utxos):
stx = deserialize(tx)
our_inputs = {}
for index, ins in enumerate(stx['ins']):
utxo = ins['outpoint']['hash'] + ':' + str(ins['outpoint']['index'])
script = wallet_service.addr_to_script(utxos[utxo]['address'])
amount = utxos[utxo]['value']
our_inputs[index] = (script, amount)
return wallet_service.sign_tx(stx, our_inputs)
def get_tumble_log(logsdir): def get_tumble_log(logsdir):
tumble_log = logging.getLogger('tumbler') tumble_log = logging.getLogger('tumbler')
tumble_log.setLevel(logging.DEBUG) tumble_log.setLevel(logging.DEBUG)

28
jmclient/jmclient/wallet.py

@ -28,7 +28,7 @@ from .cryptoengine import TYPE_P2PKH, TYPE_P2SH_P2WPKH,\
from .support import get_random_bytes from .support import get_random_bytes
from . import mn_encode, mn_decode from . import mn_encode, mn_decode
import jmbitcoin as btc import jmbitcoin as btc
from jmbase import JM_WALLET_NAME_PREFIX, bintohex from jmbase import JM_WALLET_NAME_PREFIX
""" """
@ -998,23 +998,6 @@ class BaseWallet(object):
def __del__(self): def __del__(self):
self.close() self.close()
class DummyKeyStore(btc.KeyStore):
@classmethod
def from_iterable(cls, iterable, **kwargs):
kstore = cls(**kwargs)
for k in iterable:
kstore.add_key(k)
return kstore
def add_key(self, k):
if isinstance(k, btc.CKeyBase):
if k.pub.key_id in self._privkeys:
assert self._privkeys[k.pub.key_id] == k
else:
self._privkeys[k.pub.key_id] = k
else:
raise ValueError('object supplied to add_key is of unrecognized type')
class PSBTWalletMixin(object): class PSBTWalletMixin(object):
""" """
Mixin for BaseWallet to provide BIP174 Mixin for BaseWallet to provide BIP174
@ -1062,7 +1045,7 @@ class PSBTWalletMixin(object):
assert False, "invalid spent output type passed into PSBT creator" assert False, "invalid spent output type passed into PSBT creator"
# we now insert redeemscripts where that is possible and necessary: # we now insert redeemscripts where that is possible and necessary:
for i, txinput in enumerate(new_psbt.inputs): for i, txinput in enumerate(new_psbt.inputs):
if isinstance(txinput.utxo, btc.CMutableTxOut): if isinstance(txinput.utxo, btc.CTxOut):
# witness # witness
if txinput.utxo.scriptPubKey.is_witness_scriptpubkey(): if txinput.utxo.scriptPubKey.is_witness_scriptpubkey():
# nothing needs inserting; the scriptSig is empty. # nothing needs inserting; the scriptSig is empty.
@ -1094,14 +1077,15 @@ class PSBTWalletMixin(object):
""" """
try: try:
new_psbt = btc.PartiallySignedTransaction.from_binary(in_psbt) new_psbt = btc.PartiallySignedTransaction.from_binary(in_psbt)
except: except Exception as e:
return None, "Unable to deserialize the PSBT object, invalid format." return None, "Unable to deserialize binary PSBT, error: " + repr(e)
privkeys = [] privkeys = []
for k, v in self._utxos._utxo.items(): for k, v in self._utxos._utxo.items():
for k2, v2 in v.items(): for k2, v2 in v.items():
privkeys.append(self._get_priv_from_path(v2[0])) privkeys.append(self._get_priv_from_path(v2[0]))
jmckeys = list(btc.JMCKey(x[0][:-1]) for x in privkeys) jmckeys = list(btc.JMCKey(x[0][:-1]) for x in privkeys)
new_keystore = DummyKeyStore.from_iterable(jmckeys) new_keystore = btc.KeyStore.from_iterable(jmckeys,
require_path_templates=False)
# for p2sh inputs that we want to sign, the redeem_script # for p2sh inputs that we want to sign, the redeem_script
# field must be populated by us, as the counterparty did not # field must be populated by us, as the counterparty did not

5
jmclient/jmclient/wallet_service.py

@ -3,7 +3,6 @@
import collections import collections
import time import time
import ast import ast
import binascii
import sys import sys
from decimal import Decimal from decimal import Decimal
from copy import deepcopy from copy import deepcopy
@ -16,8 +15,8 @@ from jmclient.output import fmt_tx_data
from jmclient.blockchaininterface import (INF_HEIGHT, BitcoinCoreInterface, from jmclient.blockchaininterface import (INF_HEIGHT, BitcoinCoreInterface,
BitcoinCoreNoHistoryInterface) BitcoinCoreNoHistoryInterface)
from jmclient.wallet import FidelityBondMixin from jmclient.wallet import FidelityBondMixin
from jmbase.support import jmprint, EXIT_SUCCESS, utxo_to_utxostr, bintohex, hextobin from jmbase.support import jmprint, EXIT_SUCCESS, utxo_to_utxostr, hextobin
from jmbitcoin import lx
"""Wallet service """Wallet service

34
jmclient/jmclient/wallet_utils.py

@ -1,4 +1,3 @@
from future.utils import iteritems
import json import json
import os import os
import sys import sys
@ -17,7 +16,7 @@ from jmclient import (get_network, WALLET_IMPLEMENTATIONS, Storage, podle,
is_native_segwit_mode, load_program_config, add_base_options, check_regtest) is_native_segwit_mode, load_program_config, add_base_options, check_regtest)
from jmclient.wallet_service import WalletService from jmclient.wallet_service import WalletService
from jmbase.support import (get_password, jmprint, EXIT_FAILURE, from jmbase.support import (get_password, jmprint, EXIT_FAILURE,
EXIT_ARGERROR, utxo_to_utxostr) EXIT_ARGERROR, utxo_to_utxostr, hextobin)
from .cryptoengine import TYPE_P2PKH, TYPE_P2SH_P2WPKH, TYPE_P2WPKH, \ from .cryptoengine import TYPE_P2PKH, TYPE_P2SH_P2WPKH, TYPE_P2WPKH, \
TYPE_SEGWIT_LEGACY_WALLET_FIDELITY_BONDS TYPE_SEGWIT_LEGACY_WALLET_FIDELITY_BONDS
@ -324,9 +323,8 @@ def get_tx_info(txid):
""" """
rpctx = jm_single().bc_interface.get_transaction(txid) rpctx = jm_single().bc_interface.get_transaction(txid)
txhex = str(rpctx['hex']) txhex = str(rpctx['hex'])
txd = btc.deserialize(txhex) tx = btc.CMutableTransaction.deserialize(hextobin(txhex))
output_script_values = {binascii.unhexlify(sv['script']): sv['value'] output_script_values = {x.scriptPubKey: x.nValue for x in tx.vout}
for sv in txd['outs']}
value_freq_list = sorted( value_freq_list = sorted(
Counter(output_script_values.values()).most_common(), Counter(output_script_values.values()).most_common(),
key=lambda x: -x[1]) key=lambda x: -x[1])
@ -338,7 +336,7 @@ def get_tx_info(txid):
cj_amount = value_freq_list[0][0] cj_amount = value_freq_list[0][0]
cj_n = value_freq_list[0][1] cj_n = value_freq_list[0][1]
return is_coinjoin, cj_amount, cj_n, output_script_values,\ return is_coinjoin, cj_amount, cj_n, output_script_values,\
rpctx.get('blocktime', 0), txd rpctx.get('blocktime', 0), tx
def get_imported_privkey_branch(wallet_service, m, showprivkey): def get_imported_privkey_branch(wallet_service, m, showprivkey):
@ -383,7 +381,7 @@ def wallet_showutxos(wallet, showprivkey):
unsp[us]['privkey'] = wallet.get_wif_path(av['path']) unsp[us]['privkey'] = wallet.get_wif_path(av['path'])
used_commitments, external_commitments = podle.get_podle_commitments() used_commitments, external_commitments = podle.get_podle_commitments()
for u, ec in iteritems(external_commitments): for u, ec in external_commitments.items():
success, us = utxo_to_utxostr(u) success, us = utxo_to_utxostr(u)
assert success assert success
tries = podle.get_podle_tries(utxo=u, max_tries=max_tries, tries = podle.get_podle_tries(utxo=u, max_tries=max_tries,
@ -404,7 +402,7 @@ def wallet_display(wallet_service, showprivkey, displayall=False,
def get_addr_status(addr_path, utxos, is_new, is_internal): def get_addr_status(addr_path, utxos, is_new, is_internal):
addr_balance = 0 addr_balance = 0
status = [] status = []
for utxo, utxodata in iteritems(utxos): for utxo, utxodata in utxos.items():
if addr_path != utxodata['path']: if addr_path != utxodata['path']:
continue continue
addr_balance += utxodata['value'] addr_balance += utxodata['value']
@ -415,7 +413,7 @@ def wallet_display(wallet_service, showprivkey, displayall=False,
# to bci # to bci
if jm_single().bc_interface.__class__ == BitcoinCoreInterface: if jm_single().bc_interface.__class__ == BitcoinCoreInterface:
is_coinjoin, cj_amount, cj_n = \ is_coinjoin, cj_amount, cj_n = \
get_tx_info(binascii.hexlify(utxo[0]).decode('ascii'))[:3] get_tx_info(utxo[0])[:3]
if is_coinjoin and utxodata['value'] == cj_amount: if is_coinjoin and utxodata['value'] == cj_amount:
status.append('cj-out') status.append('cj-out')
elif is_coinjoin: elif is_coinjoin:
@ -774,7 +772,7 @@ def wallet_fetch_history(wallet, options):
tx_number = 0 tx_number = 0
for tx in txes: for tx in txes:
is_coinjoin, cj_amount, cj_n, output_script_values, blocktime, txd =\ is_coinjoin, cj_amount, cj_n, output_script_values, blocktime, txd =\
get_tx_info(tx['txid']) get_tx_info(hextobin(tx['txid']))
# unconfirmed transactions don't have blocktime, get_tx_info() returns # unconfirmed transactions don't have blocktime, get_tx_info() returns
# 0 in that case # 0 in that case
@ -784,21 +782,21 @@ def wallet_fetch_history(wallet, options):
output_script_values.keys()) output_script_values.keys())
rpc_inputs = [] rpc_inputs = []
for ins in txd['ins']: for ins in txd.vin:
wallet_tx = jm_single().bc_interface.get_transaction( wallet_tx = jm_single().bc_interface.get_transaction(
ins['outpoint']['hash']) ins.prevout.hash[::-1])
if wallet_tx is None: if wallet_tx is None:
continue continue
input_dict = btc.deserialize(str(wallet_tx['hex']))['outs'][ins[ inp = btc.CMutableTransaction.deserialize(hextobin(
'outpoint']['index']] wallet_tx['hex'])).vout[ins.prevout.n]
input_dict = {"script": inp.scriptPubKey, "value": inp.nValue}
rpc_inputs.append(input_dict) rpc_inputs.append(input_dict)
rpc_input_scripts = set(binascii.unhexlify(ind['script']) rpc_input_scripts = set(ind['script'] for ind in rpc_inputs)
for ind in rpc_inputs)
our_input_scripts = wallet_script_set.intersection(rpc_input_scripts) our_input_scripts = wallet_script_set.intersection(rpc_input_scripts)
our_input_values = [ our_input_values = [
ind['value'] for ind in rpc_inputs ind['value'] for ind in rpc_inputs
if binascii.unhexlify(ind['script']) in our_input_scripts] if ind['script'] in our_input_scripts]
our_input_value = sum(our_input_values) our_input_value = sum(our_input_values)
utxos_consumed = len(our_input_values) utxos_consumed = len(our_input_values)
@ -866,7 +864,7 @@ def wallet_fetch_history(wallet, options):
amount = cj_amount amount = cj_amount
delta_balance = out_value - our_input_value delta_balance = out_value - our_input_value
mixdepth_src = wallet.get_script_mixdepth(list(our_input_scripts)[0]) mixdepth_src = wallet.get_script_mixdepth(list(our_input_scripts)[0])
cj_script = list(set([a for a, v in iteritems(output_script_values) cj_script = list(set([a for a, v in output_script_values.items()
if v == cj_amount]).intersection(our_output_scripts))[0] if v == cj_amount]).intersection(our_output_scripts))[0]
mixdepth_dst = wallet.get_script_mixdepth(cj_script) mixdepth_dst = wallet.get_script_mixdepth(cj_script)
else: else:

7
jmclient/jmclient/yieldgenerator.py

@ -1,5 +1,4 @@
#! /usr/bin/env python #! /usr/bin/env python
from future.utils import iteritems
import datetime import datetime
import os import os
@ -78,7 +77,7 @@ class YieldGeneratorBasic(YieldGenerator):
def create_my_orders(self): def create_my_orders(self):
mix_balance = self.get_available_mixdepths() mix_balance = self.get_available_mixdepths()
if len([b for m, b in iteritems(mix_balance) if b > 0]) == 0: if len([b for m, b in mix_balance.items() if b > 0]) == 0:
jlog.error('do not have any coins left') jlog.error('do not have any coins left')
return [] return []
@ -115,7 +114,7 @@ class YieldGeneratorBasic(YieldGenerator):
mix_balance = self.get_available_mixdepths() mix_balance = self.get_available_mixdepths()
filtered_mix_balance = {m: b filtered_mix_balance = {m: b
for m, b in iteritems(mix_balance) for m, b in mix_balance.items()
if b >= total_amount} if b >= total_amount}
if not filtered_mix_balance: if not filtered_mix_balance:
return None, None, None return None, None, None
@ -177,7 +176,7 @@ class YieldGeneratorBasic(YieldGenerator):
inputs. available is a mixdepth/balance dict of all the mixdepths inputs. available is a mixdepth/balance dict of all the mixdepths
that can be chosen from, i.e. have enough balance. If there is no that can be chosen from, i.e. have enough balance. If there is no
suitable input, the function can return None to abort the order.""" suitable input, the function can return None to abort the order."""
available = sorted(iteritems(available), key=lambda entry: entry[0]) available = sorted(available.items(), key=lambda entry: entry[0])
return available[0][0] return available[0][0]
def select_output_address(self, input_mixdepth, offer, amount): def select_output_address(self, input_mixdepth, offer, amount):

5
jmclient/setup.py

@ -9,8 +9,7 @@ setup(name='joinmarketclient',
author_email='', author_email='',
license='GPL', license='GPL',
packages=['jmclient'], packages=['jmclient'],
install_requires=['future', 'configparser;python_version<"3.2"', install_requires=['joinmarketbase==0.7.0dev', 'mnemonic', 'argon2_cffi',
'joinmarketbase==0.7.0dev', 'mnemonic', 'argon2_cffi',
'bencoder.pyx>=2.0.0', 'pyaes'], 'bencoder.pyx>=2.0.0', 'pyaes'],
python_requires='>=3.3', python_requires='>=3.6',
zip_safe=False) zip_safe=False)

5
jmclient/test/commontest.py

@ -6,12 +6,11 @@ import binascii
import random import random
from decimal import Decimal from decimal import Decimal
from jmbase import (get_log, hextobin, utxostr_to_utxo, from jmbase import (get_log, hextobin, dictchanger)
utxo_to_utxostr, listchanger, dictchanger)
from jmclient import ( from jmclient import (
jm_single, open_test_wallet_maybe, estimate_tx_fee, jm_single, open_test_wallet_maybe, estimate_tx_fee,
BlockchainInterface, get_p2sh_vbyte, BIP32Wallet, BlockchainInterface, BIP32Wallet,
SegwitLegacyWallet, WalletService, BTC_P2SH_P2WPKH) SegwitLegacyWallet, WalletService, BTC_P2SH_P2WPKH)
from jmbase.support import chunks from jmbase.support import chunks
import jmbitcoin as btc import jmbitcoin as btc

2
jmclient/test/test_client_protocol.py

@ -1,7 +1,7 @@
#! /usr/bin/env python #! /usr/bin/env python
'''test client-protocol interfacae.''' '''test client-protocol interfacae.'''
from jmbase import get_log, bintohex, hextobin from jmbase import get_log, bintohex
from jmbase.commands import * from jmbase.commands import *
from jmclient import load_test_config, Taker,\ from jmclient import load_test_config, Taker,\
JMClientProtocolFactory, jm_single, Maker, WalletService JMClientProtocolFactory, jm_single, Maker, WalletService

2
jmclient/test/test_coinjoin.py

@ -9,7 +9,7 @@ import pytest
import copy import copy
from twisted.internet import reactor from twisted.internet import reactor
from jmbase import get_log, hextobin, bintohex from jmbase import get_log, hextobin
from jmclient import load_test_config, jm_single,\ from jmclient import load_test_config, jm_single,\
YieldGeneratorBasic, Taker, LegacyWallet, SegwitLegacyWallet,\ YieldGeneratorBasic, Taker, LegacyWallet, SegwitLegacyWallet,\
NO_ROUNDING NO_ROUNDING

3
jmclient/test/test_maker.py

@ -1,8 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
import jmbitcoin as btc import jmbitcoin as btc
from jmclient import Maker, get_p2sh_vbyte, get_p2pk_vbyte, \ from jmclient import Maker, load_test_config, jm_single, WalletService
load_test_config, jm_single, WalletService
import jmclient import jmclient
from commontest import DummyBlockchainInterface from commontest import DummyBlockchainInterface
from test_taker import DummyWallet from test_taker import DummyWallet

2
jmclient/test/test_payjoin.py

@ -11,7 +11,7 @@ from jmbase import get_log
from jmclient import cryptoengine from jmclient import cryptoengine
from jmclient import (load_test_config, jm_single, from jmclient import (load_test_config, jm_single,
P2EPMaker, P2EPTaker, P2EPMaker, P2EPTaker,
LegacyWallet, SegwitLegacyWallet, SegwitWallet) SegwitLegacyWallet, SegwitWallet)
from commontest import make_wallets from commontest import make_wallets
from test_coinjoin import make_wallets_to_list, create_orderbook, sync_wallets from test_coinjoin import make_wallets_to_list, create_orderbook, sync_wallets

1
jmclient/test/test_podle.py

@ -2,7 +2,6 @@
'''Tests of Proof of discrete log equivalence commitments.''' '''Tests of Proof of discrete log equivalence commitments.'''
import os import os
import jmbitcoin as bitcoin import jmbitcoin as bitcoin
import binascii
import struct import struct
import json import json
import pytest import pytest

8
jmclient/test/test_privkeys.py

@ -2,10 +2,8 @@
'''Public and private key validity and formatting tests.''' '''Public and private key validity and formatting tests.'''
import jmbitcoin as btc import jmbitcoin as btc
from jmclient import (BTCEngine, BTC_P2PKH, BTC_P2SH_P2WPKH, from jmbase import hextobin
jm_single, load_test_config) from jmclient import BTCEngine, jm_single, load_test_config
import binascii
import struct
import json import json
import pytest import pytest
import os import os
@ -76,7 +74,7 @@ def test_wif_privkeys_valid(setup_keys):
# we only handle compressed keys # we only handle compressed keys
continue continue
from_wif_key, keytype = BTCEngine.wif_to_privkey(key) from_wif_key, keytype = BTCEngine.wif_to_privkey(key)
expected_key = binascii.unhexlify(hex_key) + b"\x01" expected_key = hextobin(hex_key) + b"\x01"
assert from_wif_key == expected_key, "Incorrect key decoding: " + \ assert from_wif_key == expected_key, "Incorrect key decoding: " + \
str(from_wif_key) + ", should be: " + str(expected_key) str(from_wif_key) + ", should be: " + str(expected_key)
jm_single().config.set("BLOCKCHAIN", "network", "testnet") jm_single().config.set("BLOCKCHAIN", "network", "testnet")

5
jmclient/test/test_psbt_wallet.py

@ -6,15 +6,12 @@
BIP174 are tested there, not here. BIP174 are tested there, not here.
''' '''
import time
import binascii
import struct
import copy import copy
from commontest import make_wallets, dummy_accept_callback, dummy_info_callback from commontest import make_wallets, dummy_accept_callback, dummy_info_callback
import jmbitcoin as bitcoin import jmbitcoin as bitcoin
import pytest import pytest
from jmbase import get_log, bintohex, hextobin from jmbase import get_log, bintohex
from jmclient import (load_test_config, jm_single, direct_send, from jmclient import (load_test_config, jm_single, direct_send,
SegwitLegacyWallet, SegwitWallet, LegacyWallet) SegwitLegacyWallet, SegwitWallet, LegacyWallet)

14
jmclient/test/test_snicker.py

@ -1,18 +1,14 @@
#! /usr/bin/env python #! /usr/bin/env python
from __future__ import (absolute_import, division, '''Test of SNICKER functionality using Joinmarket
print_function, unicode_literals) wallets as defined in jmclient.wallet.'''
from builtins import * # noqa: F401
'''Test of unusual transaction types creation and push to
network to check validity.'''
import binascii
from commontest import make_wallets, dummy_accept_callback, dummy_info_callback from commontest import make_wallets, dummy_accept_callback, dummy_info_callback
import jmbitcoin as btc import jmbitcoin as btc
import pytest import pytest
from jmbase import get_log, bintohex, hextobin from jmbase import get_log, bintohex
from jmclient import (load_test_config, jm_single, from jmclient import (load_test_config, estimate_tx_fee, SNICKERReceiver,
estimate_tx_fee, SNICKERReceiver, direct_send) direct_send)
log = get_log() log = get_log()

3
jmclient/test/test_taker.py

@ -10,8 +10,7 @@ import pytest
import json import json
import struct import struct
from base64 import b64encode from base64 import b64encode
from jmbase import (utxostr_to_utxo, utxo_to_utxostr, hextobin, from jmbase import utxostr_to_utxo, hextobin
dictchanger, listchanger)
from jmclient import load_test_config, jm_single, set_commitment_file,\ from jmclient import load_test_config, jm_single, set_commitment_file,\
get_commitment_file, SegwitLegacyWallet, Taker, VolatileStorage,\ get_commitment_file, SegwitLegacyWallet, Taker, VolatileStorage,\
get_network, WalletService, NO_ROUNDING, BTC_P2PKH get_network, WalletService, NO_ROUNDING, BTC_P2PKH

5
jmclient/test/test_tx_creation.py

@ -5,8 +5,6 @@
p2(w)sh tests, these have been removed since Joinmarket p2(w)sh tests, these have been removed since Joinmarket
does not use this feature.''' does not use this feature.'''
import time
import binascii
import struct import struct
from binascii import unhexlify from binascii import unhexlify
from commontest import make_wallets, make_sign_and_push, ensure_bip65_activated from commontest import make_wallets, make_sign_and_push, ensure_bip65_activated
@ -14,8 +12,7 @@ from commontest import make_wallets, make_sign_and_push, ensure_bip65_activated
import jmbitcoin as bitcoin import jmbitcoin as bitcoin
import pytest import pytest
from jmbase import get_log from jmbase import get_log
from jmclient import load_test_config, jm_single,\ from jmclient import load_test_config, jm_single
get_p2pk_vbyte
log = get_log() log = get_log()
#just a random selection of pubkeys for receiving multisigs; #just a random selection of pubkeys for receiving multisigs;

3
jmclient/test/test_wallet.py

@ -7,8 +7,7 @@ from binascii import hexlify, unhexlify
import pytest import pytest
import jmbitcoin as btc import jmbitcoin as btc
from commontest import ensure_bip65_activated from commontest import ensure_bip65_activated
from jmbase import (get_log, utxostr_to_utxo, utxo_to_utxostr, from jmbase import get_log, hextobin
hextobin, bintohex)
from jmclient import load_test_config, jm_single, \ from jmclient import load_test_config, jm_single, \
SegwitLegacyWallet,BIP32Wallet, BIP49Wallet, LegacyWallet,\ SegwitLegacyWallet,BIP32Wallet, BIP49Wallet, LegacyWallet,\
VolatileStorage, get_network, cryptoengine, WalletError,\ VolatileStorage, get_network, cryptoengine, WalletError,\

1
jmclient/test/test_wallets.py

@ -9,7 +9,6 @@ import json
import pytest import pytest
from jmbase import get_log, hextobin from jmbase import get_log, hextobin
import jmbitcoin as btc
from jmclient import ( from jmclient import (
load_test_config, jm_single, load_test_config, jm_single,
estimate_tx_fee, BitcoinCoreInterface, Mnemonic) estimate_tx_fee, BitcoinCoreInterface, Mnemonic)

3
jmdaemon/jmdaemon/daemon_protocol.py

@ -1,5 +1,4 @@
#! /usr/bin/env python #! /usr/bin/env python
from future.utils import iteritems
from .message_channel import MessageChannelCollection from .message_channel import MessageChannelCollection
from .orderbookwatch import OrderbookWatch from .orderbookwatch import OrderbookWatch
@ -239,7 +238,7 @@ class JMDaemonServerProtocol(amp.AMP, OrderbookWatch):
#Reset utxo data to null for this new transaction #Reset utxo data to null for this new transaction
self.ioauth_data = {} self.ioauth_data = {}
self.active_orders = json.loads(filled_offers) self.active_orders = json.loads(filled_offers)
for nick, offer_dict in iteritems(self.active_orders): for nick, offer_dict in self.active_orders.items():
offer_fill_msg = " ".join([str(offer_dict["oid"]), str(amount), offer_fill_msg = " ".join([str(offer_dict["oid"]), str(amount),
self.kp.hex_pk().decode('ascii'), str(commitment)]) self.kp.hex_pk().decode('ascii'), str(commitment)])
self.mcc.prepare_privmsg(nick, "fill", offer_fill_msg) self.mcc.prepare_privmsg(nick, "fill", offer_fill_msg)

1
jmdaemon/jmdaemon/irc.py

@ -17,7 +17,6 @@ def wlog(*x):
"""Simplifier to add lists to the debug log """Simplifier to add lists to the debug log
""" """
def conv(s): def conv(s):
# note: this only works because of the future package
if isinstance(s, str): if isinstance(s, str):
return s return s
elif isinstance(s, bytes): elif isinstance(s, bytes):

7
jmdaemon/jmdaemon/message_channel.py

@ -1,5 +1,4 @@
#! /usr/bin/env python #! /usr/bin/env python
from future.utils import iteritems
import abc import abc
import base64 import base64
import binascii import binascii
@ -334,7 +333,7 @@ class MessageChannelCollection(object):
""" """
for mc in self.available_channels(): for mc in self.available_channels():
filtered_nick_order_dict = {k: v filtered_nick_order_dict = {k: v
for k, v in iteritems(nick_order_dict) for k, v in nick_order_dict.items()
if mc == self.active_channels[k]} if mc == self.active_channels[k]}
mc.fill_orders(filtered_nick_order_dict, cj_amount, taker_pubkey, mc.fill_orders(filtered_nick_order_dict, cj_amount, taker_pubkey,
commitment) commitment)
@ -370,7 +369,7 @@ class MessageChannelCollection(object):
tx_nick_sets[self.active_channels[nick]] = [nick] tx_nick_sets[self.active_channels[nick]] = [nick]
else: else:
tx_nick_sets[self.active_channels[nick]].append(nick) tx_nick_sets[self.active_channels[nick]].append(nick)
for mc, nl in iteritems(tx_nick_sets): for mc, nl in tx_nick_sets.items():
self.prepare_send_tx(mc, nl, txhex) self.prepare_send_tx(mc, nl, txhex)
def prepare_send_tx(self, mc, nick_list, txhex): def prepare_send_tx(self, mc, nick_list, txhex):
@ -824,7 +823,7 @@ class MessageChannel(object):
# Taker callbacks # Taker callbacks
def fill_orders(self, nick_order_dict, cj_amount, taker_pubkey, commitment): def fill_orders(self, nick_order_dict, cj_amount, taker_pubkey, commitment):
for c, order in iteritems(nick_order_dict): for c, order in nick_order_dict.items():
msg = str(order['oid']) + ' ' + str(cj_amount) + ' ' + taker_pubkey msg = str(order['oid']) + ' ' + str(cj_amount) + ' ' + taker_pubkey
msg += ' ' + commitment msg += ' ' + commitment
self.privmsg(c, 'fill', msg) self.privmsg(c, 'fill', msg)

4
jmdaemon/setup.py

@ -9,6 +9,6 @@ setup(name='joinmarketdaemon',
author_email='', author_email='',
license='GPL', license='GPL',
packages=['jmdaemon'], packages=['jmdaemon'],
install_requires=['future', 'txtorcon', 'pyopenssl', 'libnacl', 'joinmarketbase==0.7.0dev'], install_requires=['txtorcon', 'pyopenssl', 'libnacl', 'joinmarketbase==0.7.0dev'],
python_requires='>=3.3', python_requires='>=3.6',
zip_safe=False) zip_safe=False)

3
jmdaemon/test/test_daemon_protocol.py

@ -1,5 +1,4 @@
#! /usr/bin/env python #! /usr/bin/env python
from future.utils import iteritems
'''test daemon-protocol interfacae.''' '''test daemon-protocol interfacae.'''
from jmdaemon import MessageChannelCollection from jmdaemon import MessageChannelCollection
@ -254,7 +253,7 @@ class JMDaemonTestServerProtocol(JMDaemonServerProtocol):
"!push abcd abc def", "3", "4", "!push abcd abc def", "3", "4",
str(list(tmpfo.keys())[0]), 6, 7, self.mcc.mchannels[0].hostid) str(list(tmpfo.keys())[0]), 6, 7, self.mcc.mchannels[0].hostid)
#send "valid" onpubkey, onioauth messages #send "valid" onpubkey, onioauth messages
for k, v in iteritems(tmpfo): for k, v in tmpfo.items():
reactor.callLater(1, self.on_pubkey, k, dummypub) reactor.callLater(1, self.on_pubkey, k, dummypub)
reactor.callLater(2, self.on_ioauth, k, ['a', 'b'], "auth_pub", reactor.callLater(2, self.on_ioauth, k, ['a', 'b'], "auth_pub",
"cj_addr", "change_addr", "btc_sig") "cj_addr", "change_addr", "btc_sig")

5
scripts/add-utxo.py

@ -11,10 +11,9 @@ import os
import json import json
import binascii import binascii
from pprint import pformat from pprint import pformat
from optparse import OptionParser from optparse import OptionParser
import jmbitcoin as btc
from jmclient import load_program_config, jm_single, get_p2pk_vbyte,\ from jmclient import load_program_config, jm_single,\
open_wallet, WalletService, add_external_commitments, update_commitments,\ open_wallet, WalletService, add_external_commitments, update_commitments,\
PoDLE, get_podle_commitments, get_utxo_info, validate_utxo_data, quit,\ PoDLE, get_podle_commitments, get_utxo_info, validate_utxo_data, quit,\
get_wallet_path, add_base_options, BTCEngine, BTC_P2SH_P2WPKH get_wallet_path, add_base_options, BTCEngine, BTC_P2SH_P2WPKH

2
scripts/joinmarket-qt.py

@ -66,7 +66,7 @@ JM_GUI_VERSION = '15dev'
from jmbase import get_log from jmbase import get_log
from jmbase.support import DUST_THRESHOLD, EXIT_FAILURE, JM_CORE_VERSION from jmbase.support import DUST_THRESHOLD, EXIT_FAILURE, JM_CORE_VERSION
from jmclient import load_program_config, get_network, update_persist_config,\ from jmclient import load_program_config, get_network, update_persist_config,\
open_test_wallet_maybe, get_wallet_path, get_p2sh_vbyte, get_p2pk_vbyte,\ open_test_wallet_maybe, get_wallet_path,\
jm_single, validate_address, weighted_order_choose, Taker,\ jm_single, validate_address, weighted_order_choose, Taker,\
JMClientProtocolFactory, start_reactor, get_schedule, schedule_to_text,\ JMClientProtocolFactory, start_reactor, get_schedule, schedule_to_text,\
get_blockchain_interface_instance, direct_send, WalletService,\ get_blockchain_interface_instance, direct_send, WalletService,\

5
scripts/sendtomany.py

@ -10,9 +10,8 @@ from optparse import OptionParser
import jmbitcoin as btc import jmbitcoin as btc
from jmbase import get_log, jmprint, bintohex, utxostr_to_utxo from jmbase import get_log, jmprint, bintohex, utxostr_to_utxo
from jmclient import load_program_config, estimate_tx_fee, jm_single,\ from jmclient import load_program_config, estimate_tx_fee, jm_single,\
get_p2pk_vbyte, validate_address, get_utxo_info, add_base_options,\ validate_address, get_utxo_info, add_base_options,\
validate_utxo_data, quit, BTCEngine, BTC_P2SH_P2WPKH, BTC_P2PKH validate_utxo_data, quit, BTCEngine
log = get_log() log = get_log()

11
test/test_segwit.py

@ -1,13 +1,12 @@
#! /usr/bin/env python #! /usr/bin/env python
'''Test creation of segwit transactions.''' '''Test creation of segwit transactions.'''
import binascii
import json import json
from common import make_wallets from common import make_wallets
from pprint import pformat from pprint import pformat
import jmbitcoin as btc import jmbitcoin as btc
import pytest import pytest
from jmbase import get_log, hextobin, bintohex from jmbase import get_log, hextobin
from jmclient import load_test_config, jm_single, LegacyWallet from jmclient import load_test_config, jm_single, LegacyWallet
log = get_log() log = get_log()
@ -25,14 +24,6 @@ def test_segwit_valid_txs(setup_segwit):
#TODO use bcinterface to decoderawtransaction #TODO use bcinterface to decoderawtransaction
#and compare the json values #and compare the json values
def binarize_tx(tx):
for o in tx['outs']:
o['script'] = binascii.unhexlify(o['script'])
for i in tx['ins']:
i['outpoint']['hash'] = binascii.unhexlify(i['outpoint']['hash'])
@pytest.mark.parametrize( @pytest.mark.parametrize(
"wallet_structure, in_amt, amount, segwit_amt, segwit_ins, o_ins", [ "wallet_structure, in_amt, amount, segwit_amt, segwit_ins, o_ins", [
([[1, 0, 0, 0, 0]], 1, 1000000, 1, [0, 1, 2], []), ([[1, 0, 0, 0, 0]], 1, 1000000, 1, [0, 1, 2], []),

Loading…
Cancel
Save