diff --git a/jmbase/setup.py b/jmbase/setup.py index 821c271d8..707ecb6f4 100644 --- a/jmbase/setup.py +++ b/jmbase/setup.py @@ -2,7 +2,7 @@ setup(name='joinmarketbase', - version='0.2.0', + version='0.2.1', description='Joinmarket client library for Bitcoin coinjoins', url='http://github.com/AdamISZ/joinmarket-clientserver/jmbase', author='Adam Gibson', diff --git a/jmbitcoin/jmbitcoin/secp256k1_main.py b/jmbitcoin/jmbitcoin/secp256k1_main.py index fe2b79f62..38868303a 100644 --- a/jmbitcoin/jmbitcoin/secp256k1_main.py +++ b/jmbitcoin/jmbitcoin/secp256k1_main.py @@ -230,12 +230,12 @@ def from_wif_privkey(wif_priv, compressed=True, vbyte=0): raise Exception("Private key has incorrect compression byte") return safe_hexlify(bin_key) -def ecdsa_sign(msg, priv, usehex=True): +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) - sig = ecdsa_raw_sign(hashed_msg, priv, usehex, rawmsg=True) + 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) @@ -365,7 +365,8 @@ def ecdsa_raw_sign(msg, usehex, rawpriv=True, rawmsg=False, - usenonce=None): + usenonce=None, + formsg=False): '''Take the binary message msg and sign it with the private key priv. By default priv is just a 32 byte string, if rawpriv is false @@ -386,8 +387,12 @@ def ecdsa_raw_sign(msg, newpriv = secp256k1.PrivateKey(p, raw=True, ctx=ctx) else: newpriv = secp256k1.PrivateKey(priv, raw=False, ctx=ctx) + if formsg: + sig = newpriv.ecdsa_sign_recoverable(msg, raw=rawmsg) + s, rid = newpriv.ecdsa_recoverable_serialize(sig) + return chr(31+rid) + s #Donations, thus custom nonce, currently disabled, hence not covered. - if usenonce: #pragma: no cover + elif usenonce: #pragma: no cover raise NotImplementedError #if len(usenonce) != 32: # raise ValueError("Invalid nonce passed to ecdsa_sign: " + str( @@ -395,8 +400,6 @@ def ecdsa_raw_sign(msg, #nf = ffi.addressof(_noncefunc.lib, "nonce_function_rand") #ndata = ffi.new("char [32]", usenonce) #usenonce = (nf, ndata) - if usenonce: #pragma: no cover - raise NotImplementedError #sig = newpriv.ecdsa_sign(msg, raw=rawmsg, custom_nonce=usenonce) else: #partial fix for secp256k1-transient not including customnonce; diff --git a/jmbitcoin/setup.py b/jmbitcoin/setup.py index 65a5cd86c..19cd5915c 100644 --- a/jmbitcoin/setup.py +++ b/jmbitcoin/setup.py @@ -2,7 +2,7 @@ setup(name='joinmarketbitcoin', - version='0.2.0', + version='0.2.1', description='Joinmarket client library for Bitcoin coinjoins', url='http://github.com/AdamISZ/joinmarket-clientserver/jmbitcoin', author='Adam Gibson', diff --git a/jmclient/setup.py b/jmclient/setup.py index 47abd584d..23c39ac8d 100644 --- a/jmclient/setup.py +++ b/jmclient/setup.py @@ -2,12 +2,12 @@ setup(name='joinmarketclient', - version='0.2.0', + version='0.2.1', description='Joinmarket client library for Bitcoin coinjoins', url='http://github.com/AdamISZ/joinmarket-clientserver/jmclient', author='Adam Gibson', author_email='ekaggata@gmail.com', license='GPL', packages=['jmclient'], - install_requires=['joinmarketbase==0.2.0'], + install_requires=['joinmarketbase==0.2.1'], zip_safe=False) diff --git a/jmdaemon/setup.py b/jmdaemon/setup.py index 56fb6c4b1..4f646592c 100644 --- a/jmdaemon/setup.py +++ b/jmdaemon/setup.py @@ -2,12 +2,12 @@ setup(name='joinmarketdaemon', - version='0.2.0', + version='0.2.1', description='Joinmarket client library for Bitcoin coinjoins', url='http://github.com/AdamISZ/joinmarket-clientserver/jmdaemon', author='Adam Gibson', author_email='ekaggata@gmail.com', license='GPL', packages=['jmdaemon'], - install_requires=['txsocksx', 'pyopenssl', 'libnacl', 'joinmarketbase==0.2.0'], + install_requires=['txsocksx', 'pyopenssl', 'libnacl', 'joinmarketbase==0.2.1'], zip_safe=False) diff --git a/scripts/wallet-tool.py b/scripts/wallet-tool.py index 283e9646e..e2238eec3 100644 --- a/scripts/wallet-tool.py +++ b/scripts/wallet-tool.py @@ -32,7 +32,10 @@ 'privkeys are spaces or commas separated. (dumpprivkey) Export ' 'a single private key, specify an hd wallet path (listwallets) ' 'Lists all wallets with creator and timestamp. (history) Show ' - 'all historical transaction details. Requires Bitcoin Core.') + 'all historical transaction details. Requires Bitcoin Core.\n' + 'signmessage\t\tSign a message with the private key from an address\n' + '\t\t\tin the wallet. Use with -H and specify an HD wallet\n' + '\t\t\tpath for the address.') parser = OptionParser(usage='usage: %prog [options] [wallet file] [method]', description=description) @@ -92,7 +95,7 @@ methods = ['display', 'displayall', 'summary', 'showseed', 'importprivkey', 'history', 'showutxos'] methods.extend(noseed_methods) -noscan_methods = ['showseed', 'importprivkey', 'dumpprivkey'] +noscan_methods = ['showseed', 'importprivkey', 'dumpprivkey', 'signmessage'] if len(args) < 1: parser.error('Needs a wallet file or method') @@ -316,6 +319,18 @@ def cus_print(s): print(' ') i += 1 print(str(i - 1) + ' Wallets have been found.') +elif method == 'signmessage': + message = args[2] + if options.hd_path.startswith('m/0/'): + m, forchange, k = [int(y) for y in options.hd_path[4:].split('/')] + key = wallet.get_key(m, forchange, k) + addr = btc.privkey_to_address(key, magicbyte=get_p2pk_vbyte()) + print('Using address: ' + addr) + else: + print('%s is not a valid hd wallet path' % options.hd_path) + sig = btc.ecdsa_sign(message, key, formsg=True) + print("Signature: " + str(sig)) + print("To verify this in Bitcoin Core use the RPC command 'verifymessage'") elif method == 'history': #sort txes in a db because python can be really bad with large lists con = sqlite3.connect(":memory:")