From 84bb263b9652aa85cb7c6b351853d20d0e04e78b Mon Sep 17 00:00:00 2001 From: Adam Gibson Date: Sat, 26 Mar 2016 14:58:49 +0200 Subject: [PATCH] Several new tests and corrections/removals from bitcoin remove unused code from test_regtest add several new tests, remove unused code in bitcoin module add tests of ecc, donations, irc, and several improvements covering more error cases add test of spend to p2sh fix up sighash codes in transaction code, more tests test spend-from-p2sh Add tumbler test to repo (but not build test) Fix make_wallets to only reset index for syncing after coins allocated, since otherwise all coins in one mixdepth go to the same address. Minor cleanup. add dependencies for secp256k1 to build fix/remove ripemd dependencies minor fix for nonsecp256k1 tests --- .coveragerc | 33 + .travis.yml | 9 +- bitcoin/__init__.py | 2 +- bitcoin/bci.py | 318 --------- bitcoin/main.py | 90 --- bitcoin/py2specials.py | 8 - bitcoin/py3specials.py | 5 - bitcoin/ripemd.py | 430 ------------- bitcoin/secp256k1_deterministic.py | 13 - bitcoin/secp256k1_main.py | 37 +- bitcoin/secp256k1_transaction.py | 60 +- bitcoin/transaction.py | 9 - joinmarket/blockchaininterface.py | 4 - joinmarket/message_channel.py | 26 +- joinmarket/slowaes.py | 13 - test/commontest.py | 4 +- test/ecc_sigs_rfc6979_valid.json | 999 +++++++++++++++++++++++++++++ test/test_bip32.py | 30 + test/test_blockr.py | 113 ++++ test/test_btc_formatting.py | 47 ++ test/test_donations.py | 109 ++++ test/test_ecc_signing.py | 80 +++ test/test_irc_messaging.py | 83 +++ test/test_keys.py | 32 + test/test_regtest.py | 13 +- test/test_tumbler.py | 186 ++++++ test/test_tx_creation.py | 148 +++++ test/test_tx_serialize.py | 15 +- test/test_wallets.py | 28 + 29 files changed, 1954 insertions(+), 990 deletions(-) create mode 100644 .coveragerc delete mode 100644 bitcoin/ripemd.py create mode 100644 test/ecc_sigs_rfc6979_valid.json create mode 100644 test/test_blockr.py create mode 100644 test/test_btc_formatting.py create mode 100644 test/test_donations.py create mode 100644 test/test_ecc_signing.py create mode 100644 test/test_irc_messaging.py create mode 100644 test/test_tumbler.py create mode 100644 test/test_tx_creation.py diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 00000000..f27b66a8 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,33 @@ +# .coveragerc to control coverage.py +[run] +omit = + bitcoin/main.py + bitcoin/deterministic.py + bitcoin/transaction.py + bitcoin/py3specials.py + test/fee-estimation.py + test/randomfunc-test.py + test/tumbler-test.py + test/test_tumbler.py + test/test_donations.py + +[report] +# Regexes for lines to exclude from consideration +exclude_lines = + # Have to re-enable the standard pragma + pragma: no cover + + # Don't complain about missing debug-only code: + def __repr__ + if self\.debug + + # Don't complain if tests don't hit defensive assertion code: + raise AssertionError + raise NotImplementedError + except ImportError + # Don't complain if non-runnable code isn't run: + if 0: + if __name__ == .__main__.: + +ignore_errors = True + diff --git a/.travis.yml b/.travis.yml index 6fa3e964..69b24d3b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,14 +1,20 @@ language: python python: - "2.7_with_system_site_packages" + before_install: - sudo apt-add-repository ppa:bitcoin/bitcoin -y - sudo apt-get update -q - sudo apt-get install --no-install-recommends --no-upgrade -qq bitcoind - sudo apt-get install -y build-essential - sudo apt-get install -y automake + - sudo apt-get install -y pkg-config + - sudo apt-get install -y libtool + - sudo apt-get install -y libffi-dev + - sudo apt-get install -y libgmp-dev install: - pip install -r requirements-dev.txt + - sudo pip install secp256k1 script: #install and test libsodium - git clone git://github.com/jedisct1/libsodium.git @@ -25,7 +31,7 @@ script: - mkdir /home/travis/.bitcoin - cp test/bitcoin.conf /home/travis/.bitcoin/. - chmod 600 /home/travis/.bitcoin/bitcoin.conf - - PYTHONPATH=.$PYTHONPATH py.test --cov-report html --btcconf=/home/travis/.bitcoin/bitcoin.conf --btcpwd=123456abcdef + - PYTHONPATH=.:$PYTHONPATH py.test --cov-report html --btcconf=/home/travis/.bitcoin/bitcoin.conf --btcpwd=123456abcdef --ignore test/test_tumbler.py - cd logs - for x in `ls`; do tail -50 $x; done - cd .. @@ -34,3 +40,4 @@ after_success: branches: only: - develop + - more_tests diff --git a/bitcoin/__init__.py b/bitcoin/__init__.py index 649946ce..3ca1223a 100644 --- a/bitcoin/__init__.py +++ b/bitcoin/__init__.py @@ -7,7 +7,7 @@ from bitcoin.secp256k1_main import * from bitcoin.secp256k1_transaction import * from bitcoin.secp256k1_deterministic import * -except ImportError: +except ImportError as e: from bitcoin.main import * from bitcoin.deterministic import * from bitcoin.transaction import * diff --git a/bitcoin/bci.py b/bitcoin/bci.py index 4e1956ab..9f4da43c 100644 --- a/bitcoin/bci.py +++ b/bitcoin/bci.py @@ -7,7 +7,6 @@ except: from urllib2 import build_opener - # Makes a request to a given URL (first arg) and optional params (second arg) def make_request(*args): opener = build_opener() @@ -22,189 +21,12 @@ def make_request(*args): p = e raise Exception(p) - -def parse_addr_args(*args): - # Valid input formats: blockr_unspent([addr1, addr2,addr3]) - # blockr_unspent(addr1, addr2, addr3) - # blockr_unspent([addr1, addr2, addr3], network) - # blockr_unspent(addr1, addr2, addr3, network) - # Where network is 'btc' or 'testnet' - network = 'btc' - addr_args = args - if len(args) >= 1 and args[-1] in ('testnet', 'btc'): - network = args[-1] - addr_args = args[:-1] - if len(addr_args) == 1 and isinstance(addr_args, list): - addr_args = addr_args[0] - - return network, addr_args - - -# Gets the unspent outputs of one or more addresses -def bci_unspent(*args): - network, addrs = parse_addr_args(*args) - u = [] - for a in addrs: - try: - data = make_request('https://blockchain.info/unspent?address=' + a) - except Exception as e: - if str(e) == 'No free outputs to spend': - continue - else: - raise Exception(e) - try: - jsonobj = json.loads(data) - for o in jsonobj["unspent_outputs"]: - h = o['tx_hash'].decode('hex')[::-1].encode('hex') - u.append({ - "output": h + ':' + str(o['tx_output_n']), - "value": o['value'] - }) - except: - raise Exception("Failed to decode data: " + data) - return u - - -def blockr_unspent(*args): - # Valid input formats: blockr_unspent([addr1, addr2,addr3]) - # blockr_unspent(addr1, addr2, addr3) - # blockr_unspent([addr1, addr2, addr3], network) - # blockr_unspent(addr1, addr2, addr3, network) - # Where network is 'btc' or 'testnet' - network, addr_args = parse_addr_args(*args) - - if network == 'testnet': - blockr_url = 'https://tbtc.blockr.io/api/v1/address/unspent/' - elif network == 'btc': - blockr_url = 'https://btc.blockr.io/api/v1/address/unspent/' - else: - raise Exception('Unsupported network {0} for blockr_unspent'.format( - network)) - - if len(addr_args) == 0: - return [] - elif isinstance(addr_args[0], list): - addrs = addr_args[0] - else: - addrs = addr_args - res = make_request(blockr_url + ','.join(addrs)) - data = json.loads(res)['data'] - o = [] - if 'unspent' in data: - data = [data] - for dat in data: - for u in dat['unspent']: - o.append({ - "output": u['tx'] + ':' + str(u['n']), - "value": int(u['amount'].replace('.', '')) - }) - return o - - -def helloblock_unspent(*args): - network, addrs = parse_addr_args(*args) - if network == 'testnet': - url = 'http://testnet.helloblock.io/v1/addresses/%s/unspents?limit=500&offset=%s' - elif network == 'btc': - url = 'http://mainnet.helloblock.io/v1/addresses/%s/unspents?limit=500&offset=%s' - o = [] - for addr in addrs: - for offset in xrange(0, 10**9, 500): - res = make_request(url % (addr, offset)) - data = json.loads(res)["data"] - if not len(data["unspents"]): - break - elif offset: - sys.stderr.write("Getting more unspents: %d\n" % offset) - for dat in data["unspents"]: - o.append({ - "output": dat["txHash"] + ':' + str(dat["index"]), - "value": dat["value"], - }) - return o - - -unspent_getters = { - 'bci': bci_unspent, - 'blockr': blockr_unspent, - 'helloblock': helloblock_unspent -} - - -def unspent(*args, **kwargs): - f = unspent_getters.get(kwargs.get('source', ''), bci_unspent) - return f(*args) - - -# Gets the transaction output history of a given set of addresses, -# including whether or not they have been spent -def history(*args): - # Valid input formats: history([addr1, addr2,addr3]) - # history(addr1, addr2, addr3) - if len(args) == 0: - return [] - elif isinstance(args[0], list): - addrs = args[0] - else: - addrs = args - - txs = [] - for addr in addrs: - offset = 0 - while 1: - data = make_request( - 'https://blockchain.info/address/%s?format=json&offset=%s' % - (addr, offset)) - try: - jsonobj = json.loads(data) - except: - raise Exception("Failed to decode data: " + data) - txs.extend(jsonobj["txs"]) - if len(jsonobj["txs"]) < 50: - break - offset += 50 - sys.stderr.write("Fetching more transactions... " + str(offset) + - '\n') - outs = {} - for tx in txs: - for o in tx["out"]: - if o['addr'] in addrs: - key = str(tx["tx_index"]) + ':' + str(o["n"]) - outs[key] = { - "address": o["addr"], - "value": o["value"], - "output": tx["hash"] + ':' + str(o["n"]), - "block_height": tx.get("block_height", None) - } - for tx in txs: - for i, inp in enumerate(tx["inputs"]): - if inp["prev_out"]["addr"] in addrs: - key = str(inp["prev_out"]["tx_index"]) + \ - ':'+str(inp["prev_out"]["n"]) - if outs.get(key): - outs[key]["spend"] = tx["hash"] + ':' + str(i) - return [outs[k] for k in outs] - - # Pushes a transaction to the network using https://blockchain.info/pushtx def bci_pushtx(tx): if not re.match('^[0-9a-fA-F]*$', tx): tx = tx.encode('hex') return make_request('https://blockchain.info/pushtx', 'tx=' + tx) - -def eligius_pushtx(tx): - if not re.match('^[0-9a-fA-F]*$', tx): - tx = tx.encode('hex') - s = make_request('http://eligius.st/~wizkid057/newstats/pushtxn.php', - 'transaction=' + tx + '&send=Push') - strings = re.findall('string[^"]*"[^"]*"', s) - for string in strings: - quote = re.findall('"[^"]*"', string)[0] - if len(quote) >= 5: - return quote[1:-1] - - def blockr_pushtx(tx, network='btc'): if network == 'testnet': blockr_url = 'https://tbtc.blockr.io/api/v1/tx/push' @@ -219,148 +41,8 @@ def blockr_pushtx(tx, network='btc'): return make_request(blockr_url, '{"hex":"%s"}' % tx) -def helloblock_pushtx(tx): - if not re.match('^[0-9a-fA-F]*$', tx): - tx = tx.encode('hex') - return make_request('http://mainnet.helloblock.io/v1/transactions', - 'rawTxHex=' + tx) - - -pushtx_getters = { - 'bci': bci_pushtx, - 'blockr': blockr_pushtx, - 'helloblock': helloblock_pushtx -} - - -def pushtx(*args, **kwargs): - f = pushtx_getters.get(kwargs.get('source', ''), bci_pushtx) - return f(*args) - - -def last_block_height(): - data = make_request('https://blockchain.info/latestblock') - jsonobj = json.loads(data) - return jsonobj["height"] - - -# Gets a specific transaction -def bci_fetchtx(txhash): - if not re.match('^[0-9a-fA-F]*$', txhash): - txhash = txhash.encode('hex') - data = make_request('https://blockchain.info/rawtx/' + txhash + - '?format=hex') - return data - - -def blockr_fetchtx(txhash, network='btc'): - if network == 'testnet': - blockr_url = 'https://tbtc.blockr.io/api/v1/tx/raw/' - elif network == 'btc': - blockr_url = 'https://btc.blockr.io/api/v1/tx/raw/' - else: - raise Exception('Unsupported network {0} for blockr_fetchtx'.format( - network)) - if not re.match('^[0-9a-fA-F]*$', txhash): - txhash = txhash.encode('hex') - jsondata = json.loads(make_request(blockr_url + txhash)) - return jsondata['data']['tx']['hex'] - - -def helloblock_fetchtx(txhash, network='btc'): - if not re.match('^[0-9a-fA-F]*$', txhash): - txhash = txhash.encode('hex') - if network == 'testnet': - url = 'http://testnet.helloblock.io/v1/transactions/' - elif network == 'btc': - url = 'http://mainnet.helloblock.io/v1/transactions/' - else: - raise Exception('Unsupported network {0} for helloblock_fetchtx'.format( - network)) - data = json.loads(make_request(url + txhash))["data"]["transaction"] - o = { - "locktime": data["locktime"], - "version": data["version"], - "ins": [], - "outs": [] - } - for inp in data["inputs"]: - o["ins"].append({ - "script": inp["scriptSig"], - "outpoint": { - "index": inp["prevTxoutIndex"], - "hash": inp["prevTxHash"], - }, - "sequence": 4294967295 - }) - for outp in data["outputs"]: - o["outs"].append({ - "value": outp["value"], - "script": outp["scriptPubKey"] - }) - from bitcoin.transaction import serialize - from bitcoin.transaction import txhash as TXHASH - tx = serialize(o) - assert TXHASH(tx) == txhash - return tx - - -fetchtx_getters = { - 'bci': bci_fetchtx, - 'blockr': blockr_fetchtx, - 'helloblock': helloblock_fetchtx -} - - -def fetchtx(*args, **kwargs): - f = fetchtx_getters.get(kwargs.get('source', ''), bci_fetchtx) - return f(*args) - - -def firstbits(address): - if len(address) >= 25: - return make_request('https://blockchain.info/q/getfirstbits/' + address) - else: - return make_request('https://blockchain.info/q/resolvefirstbits/' + - address) - - -def get_block_at_height(height): - j = json.loads(make_request("https://blockchain.info/block-height/" + str( - height) + "?format=json")) - for b in j['blocks']: - if b['main_chain'] is True: - return b - raise Exception("Block at this height not found") - - -def _get_block(inp): - if len(str(inp)) < 64: - return get_block_at_height(inp) - else: - return json.loads(make_request('https://blockchain.info/rawblock/' + - inp)) - -def get_block_header_data(inp): - j = _get_block(inp) - return { - 'version': j['ver'], - 'hash': j['hash'], - 'prevhash': j['prev_block'], - 'timestamp': j['time'], - 'merkle_root': j['mrkl_root'], - 'bits': j['bits'], - 'nonce': j['nonce'], - } -def get_txs_in_block(inp): - j = _get_block(inp) - hashes = [t['hash'] for t in j['tx']] - return hashes -def get_block_height(txhash): - j = json.loads(make_request('https://blockchain.info/rawtx/' + txhash)) - return j['block_height'] diff --git a/bitcoin/main.py b/bitcoin/main.py index 2d9e4bf0..bf6d67db 100644 --- a/bitcoin/main.py +++ b/bitcoin/main.py @@ -22,19 +22,7 @@ Gy = 32670510020758816978083085130507043184471273380659243275938904335757337482424 G = (Gx, Gy) - -def change_curve(p, n, a, b, gx, gy): - global P, N, A, B, Gx, Gy, G - P, N, A, B, Gx, Gy = p, n, a, b, gx, gy - G = (Gx, Gy) - - -def getG(): - return G - # Extended Euclidean Algorithm - - def inv(a, n): lm, hm = 1, 0 low, high = a % n, n @@ -44,43 +32,9 @@ def inv(a, n): lm, low, hm, high = nm, new, lm, low return lm % n -# JSON access (for pybtctool convenience) - - -def access(obj, prop): - if isinstance(obj, dict): - if prop in obj: - return obj[prop] - elif '.' in prop: - return obj[float(prop)] - else: - return obj[int(prop)] - else: - return obj[int(prop)] - - -def multiaccess(obj, prop): - return [access(o, prop) for o in obj] - - -def slice(obj, start=0, end=2**200): - return obj[int(start):int(end)] - - -def count(obj): - return len(obj) - - -_sum = sum - - -def sum(obj): - return _sum(obj) - # Elliptic curve Jordan form functions # P = (m, n, p, q) where m/n = x, p/q = y - def isinf(p): return p[0] == 0 and p[1] == 0 @@ -158,8 +112,6 @@ def to_jordan(p): def from_jordan(p): return (p[0][0] * inv(p[0][1], P) % P, p[1][0] * inv(p[1][1], P) % P) - return (p[0][0] * inv(p[0][1], P) % P, p[1][0] * inv(p[1][1], P) % P) - def fast_multiply(a, n): return from_jordan(jordan_multiply(to_jordan(a), n)) @@ -449,24 +401,8 @@ def electrum_sig_hash(message): message)) + from_string_to_bytes(message) return bin_dbl_sha256(padded) - -def random_key(): - # Gotta be secure after that java.SecureRandom fiasco... - entropy = random_string(32) \ - + str(random.randrange(2**256)) \ - + str(int(time.time() * 1000000)) - return sha256(entropy) - - -def random_electrum_seed(): - entropy = os.urandom(32) \ - + str(random.randrange(2**256)) \ - + str(int(time.time() * 1000000)) - return sha256(entropy)[:32] - # Encodings - def b58check_to_bin(inp): leadingzbytes = len(re.match('^1*', inp).group(0)) data = b'\x00' * leadingzbytes + changebase(inp, 58, 256) @@ -545,7 +481,6 @@ def ecdsa_raw_sign(msghash, priv): def ecdsa_sign(msg, priv): return encode_sig(*ecdsa_raw_sign(electrum_sig_hash(msg), priv)) - def ecdsa_raw_verify(msghash, vrs, pub): v, r, s = vrs @@ -557,34 +492,9 @@ def ecdsa_raw_verify(msghash, vrs, pub): return r == x - def ecdsa_verify(msg, sig, pub): return ecdsa_raw_verify(electrum_sig_hash(msg), decode_sig(sig), pub) - -def ecdsa_raw_recover(msghash, vrs): - v, r, s = vrs - - x = r - beta = pow(x * x * x + A * x + B, (P + 1) // 4, P) - y = beta if v % 2 ^ beta % 2 else (P - beta) - z = hash_to_int(msghash) - Gz = jordan_multiply(((Gx, 1), (Gy, 1)), (N - z) % N) - XY = jordan_multiply(((x, 1), (y, 1)), s) - Qr = jordan_add(Gz, XY) - Q = jordan_multiply(Qr, inv(r, N)) - Q = from_jordan(Q) - - if ecdsa_raw_verify(msghash, vrs, Q): - return Q - return False - - -def ecdsa_recover(msg, sig): - return encode_pubkey( - ecdsa_raw_recover( - electrum_sig_hash(msg), decode_sig(sig)), 'hex') - def estimate_tx_size(ins, outs, txtype='p2pkh'): '''Estimate transaction size. Assuming p2pkh: diff --git a/bitcoin/py2specials.py b/bitcoin/py2specials.py index e123dc16..96a91e4c 100644 --- a/bitcoin/py2specials.py +++ b/bitcoin/py2specials.py @@ -50,18 +50,12 @@ def bytes_to_hex_string(b): def safe_from_hex(s): return s.decode('hex') - def from_int_representation_to_bytes(a): - return str(a) - def from_int_to_byte(a): return chr(a) def from_byte_to_int(a): return ord(a) - def from_bytes_to_string(s): - return s - def from_string_to_bytes(a): return a @@ -89,5 +83,3 @@ def decode(string, base): string = string[1:] return result - def random_string(x): - return os.urandom(x) diff --git a/bitcoin/py3specials.py b/bitcoin/py3specials.py index 5072ac58..eadb9a1d 100644 --- a/bitcoin/py3specials.py +++ b/bitcoin/py3specials.py @@ -57,9 +57,6 @@ def bytes_to_hex_string(b): def safe_from_hex(s): return bytes.fromhex(s) - def from_int_representation_to_bytes(a): - return bytes(str(a), 'utf-8') - def from_int_to_byte(a): return bytes([a]) @@ -116,5 +113,3 @@ def extract(d, cs): string = string[1:] return result - def random_string(x): - return str(os.urandom(x)) diff --git a/bitcoin/ripemd.py b/bitcoin/ripemd.py deleted file mode 100644 index 0b462b6f..00000000 --- a/bitcoin/ripemd.py +++ /dev/null @@ -1,430 +0,0 @@ -## ripemd.py - pure Python implementation of the RIPEMD-160 algorithm. -## Bjorn Edstrom 16 december 2007. -## -## Copyrights -## ========== -## -## This code is a derived from an implementation by Markus Friedl which is -## subject to the following license. This Python implementation is not -## subject to any other license. -## -##/* -## * Copyright (c) 2001 Markus Friedl. All rights reserved. -## * -## * Redistribution and use in source and binary forms, with or without -## * modification, are permitted provided that the following conditions -## * are met: -## * 1. Redistributions of source code must retain the above copyright -## * notice, this list of conditions and the following disclaimer. -## * 2. Redistributions in binary form must reproduce the above copyright -## * notice, this list of conditions and the following disclaimer in the -## * documentation and/or other materials provided with the distribution. -## * -## * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -## * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -## * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -## * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -## * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -## * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -## * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -## * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -## * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -## * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -## */ -##/* -## * Preneel, Bosselaers, Dobbertin, "The Cryptographic Hash Function RIPEMD-160", -## * RSA Laboratories, CryptoBytes, Volume 3, Number 2, Autumn 1997, -## * ftp://ftp.rsasecurity.com/pub/cryptobytes/crypto3n2.pdf -## */ - -try: - import psyco - psyco.full() -except ImportError: - pass - -import sys - -is_python2 = sys.version_info.major == 2 -#block_size = 1 -digest_size = 20 -digestsize = 20 - -try: - range = xrange -except: - pass - - -class RIPEMD160: - """Return a new RIPEMD160 object. An optional string argument - may be provided; if present, this string will be automatically - hashed.""" - - def __init__(self, arg=None): - self.ctx = RMDContext() - if arg: - self.update(arg) - self.dig = None - - def update(self, arg): - """update(arg)""" - RMD160Update(self.ctx, arg, len(arg)) - self.dig = None - - def digest(self): - """digest()""" - if self.dig: - return self.dig - ctx = self.ctx.copy() - self.dig = RMD160Final(self.ctx) - self.ctx = ctx - return self.dig - - def hexdigest(self): - """hexdigest()""" - dig = self.digest() - hex_digest = '' - for d in dig: - if (is_python2): - hex_digest += '%02x' % ord(d) - else: - hex_digest += '%02x' % d - return hex_digest - - def copy(self): - """copy()""" - import copy - return copy.deepcopy(self) - - -def new(arg=None): - """Return a new RIPEMD160 object. An optional string argument - may be provided; if present, this string will be automatically - hashed.""" - return RIPEMD160(arg) - -# -# Private. -# - - -class RMDContext: - - def __init__(self): - self.state = [0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, - 0xC3D2E1F0] # uint32 - self.count = 0 # uint64 - self.buffer = [0] * 64 # uchar - - def copy(self): - ctx = RMDContext() - ctx.state = self.state[:] - ctx.count = self.count - ctx.buffer = self.buffer[:] - return ctx - - -K0 = 0x00000000 -K1 = 0x5A827999 -K2 = 0x6ED9EBA1 -K3 = 0x8F1BBCDC -K4 = 0xA953FD4E - -KK0 = 0x50A28BE6 -KK1 = 0x5C4DD124 -KK2 = 0x6D703EF3 -KK3 = 0x7A6D76E9 -KK4 = 0x00000000 - - -def ROL(n, x): - return ((x << n) & 0xffffffff) | (x >> (32 - n)) - - -def F0(x, y, z): - return x ^ y ^ z - - -def F1(x, y, z): - return (x & y) | (((~x) % 0x100000000) & z) - - -def F2(x, y, z): - return (x | ((~y) % 0x100000000)) ^ z - - -def F3(x, y, z): - return (x & z) | (((~z) % 0x100000000) & y) - - -def F4(x, y, z): - return x ^ (y | ((~z) % 0x100000000)) - - -def R(a, b, c, d, e, Fj, Kj, sj, rj, X): - a = ROL(sj, (a + Fj(b, c, d) + X[rj] + Kj) % 0x100000000) + e - c = ROL(10, c) - return a % 0x100000000, c - - -PADDING = [0x80] + [0] * 63 - -import sys -import struct - - -def RMD160Transform(state, block): #uint32 state[5], uchar block[64] - x = [0] * 16 - if sys.byteorder == 'little': - if is_python2: - x = struct.unpack('<16L', ''.join([chr(x) for x in block[0:64]])) - else: - x = struct.unpack('<16L', bytes(block[0:64])) - else: - raise "Error!!" - a = state[0] - b = state[1] - c = state[2] - d = state[3] - e = state[4] - - #/* Round 1 */ - a, c = R(a, b, c, d, e, F0, K0, 11, 0, x) - e, b = R(e, a, b, c, d, F0, K0, 14, 1, x) - d, a = R(d, e, a, b, c, F0, K0, 15, 2, x) - c, e = R(c, d, e, a, b, F0, K0, 12, 3, x) - b, d = R(b, c, d, e, a, F0, K0, 5, 4, x) - a, c = R(a, b, c, d, e, F0, K0, 8, 5, x) - e, b = R(e, a, b, c, d, F0, K0, 7, 6, x) - d, a = R(d, e, a, b, c, F0, K0, 9, 7, x) - c, e = R(c, d, e, a, b, F0, K0, 11, 8, x) - b, d = R(b, c, d, e, a, F0, K0, 13, 9, x) - a, c = R(a, b, c, d, e, F0, K0, 14, 10, x) - e, b = R(e, a, b, c, d, F0, K0, 15, 11, x) - d, a = R(d, e, a, b, c, F0, K0, 6, 12, x) - c, e = R(c, d, e, a, b, F0, K0, 7, 13, x) - b, d = R(b, c, d, e, a, F0, K0, 9, 14, x) - a, c = R(a, b, c, d, e, F0, K0, 8, 15, x) - #/* #15 */ - #/* Round 2 */ - e, b = R(e, a, b, c, d, F1, K1, 7, 7, x) - d, a = R(d, e, a, b, c, F1, K1, 6, 4, x) - c, e = R(c, d, e, a, b, F1, K1, 8, 13, x) - b, d = R(b, c, d, e, a, F1, K1, 13, 1, x) - a, c = R(a, b, c, d, e, F1, K1, 11, 10, x) - e, b = R(e, a, b, c, d, F1, K1, 9, 6, x) - d, a = R(d, e, a, b, c, F1, K1, 7, 15, x) - c, e = R(c, d, e, a, b, F1, K1, 15, 3, x) - b, d = R(b, c, d, e, a, F1, K1, 7, 12, x) - a, c = R(a, b, c, d, e, F1, K1, 12, 0, x) - e, b = R(e, a, b, c, d, F1, K1, 15, 9, x) - d, a = R(d, e, a, b, c, F1, K1, 9, 5, x) - c, e = R(c, d, e, a, b, F1, K1, 11, 2, x) - b, d = R(b, c, d, e, a, F1, K1, 7, 14, x) - a, c = R(a, b, c, d, e, F1, K1, 13, 11, x) - e, b = R(e, a, b, c, d, F1, K1, 12, 8, x) - #/* #31 */ - #/* Round 3 */ - d, a = R(d, e, a, b, c, F2, K2, 11, 3, x) - c, e = R(c, d, e, a, b, F2, K2, 13, 10, x) - b, d = R(b, c, d, e, a, F2, K2, 6, 14, x) - a, c = R(a, b, c, d, e, F2, K2, 7, 4, x) - e, b = R(e, a, b, c, d, F2, K2, 14, 9, x) - d, a = R(d, e, a, b, c, F2, K2, 9, 15, x) - c, e = R(c, d, e, a, b, F2, K2, 13, 8, x) - b, d = R(b, c, d, e, a, F2, K2, 15, 1, x) - a, c = R(a, b, c, d, e, F2, K2, 14, 2, x) - e, b = R(e, a, b, c, d, F2, K2, 8, 7, x) - d, a = R(d, e, a, b, c, F2, K2, 13, 0, x) - c, e = R(c, d, e, a, b, F2, K2, 6, 6, x) - b, d = R(b, c, d, e, a, F2, K2, 5, 13, x) - a, c = R(a, b, c, d, e, F2, K2, 12, 11, x) - e, b = R(e, a, b, c, d, F2, K2, 7, 5, x) - d, a = R(d, e, a, b, c, F2, K2, 5, 12, x) - #/* #47 */ - #/* Round 4 */ - c, e = R(c, d, e, a, b, F3, K3, 11, 1, x) - b, d = R(b, c, d, e, a, F3, K3, 12, 9, x) - a, c = R(a, b, c, d, e, F3, K3, 14, 11, x) - e, b = R(e, a, b, c, d, F3, K3, 15, 10, x) - d, a = R(d, e, a, b, c, F3, K3, 14, 0, x) - c, e = R(c, d, e, a, b, F3, K3, 15, 8, x) - b, d = R(b, c, d, e, a, F3, K3, 9, 12, x) - a, c = R(a, b, c, d, e, F3, K3, 8, 4, x) - e, b = R(e, a, b, c, d, F3, K3, 9, 13, x) - d, a = R(d, e, a, b, c, F3, K3, 14, 3, x) - c, e = R(c, d, e, a, b, F3, K3, 5, 7, x) - b, d = R(b, c, d, e, a, F3, K3, 6, 15, x) - a, c = R(a, b, c, d, e, F3, K3, 8, 14, x) - e, b = R(e, a, b, c, d, F3, K3, 6, 5, x) - d, a = R(d, e, a, b, c, F3, K3, 5, 6, x) - c, e = R(c, d, e, a, b, F3, K3, 12, 2, x) - #/* #63 */ - #/* Round 5 */ - b, d = R(b, c, d, e, a, F4, K4, 9, 4, x) - a, c = R(a, b, c, d, e, F4, K4, 15, 0, x) - e, b = R(e, a, b, c, d, F4, K4, 5, 5, x) - d, a = R(d, e, a, b, c, F4, K4, 11, 9, x) - c, e = R(c, d, e, a, b, F4, K4, 6, 7, x) - b, d = R(b, c, d, e, a, F4, K4, 8, 12, x) - a, c = R(a, b, c, d, e, F4, K4, 13, 2, x) - e, b = R(e, a, b, c, d, F4, K4, 12, 10, x) - d, a = R(d, e, a, b, c, F4, K4, 5, 14, x) - c, e = R(c, d, e, a, b, F4, K4, 12, 1, x) - b, d = R(b, c, d, e, a, F4, K4, 13, 3, x) - a, c = R(a, b, c, d, e, F4, K4, 14, 8, x) - e, b = R(e, a, b, c, d, F4, K4, 11, 11, x) - d, a = R(d, e, a, b, c, F4, K4, 8, 6, x) - c, e = R(c, d, e, a, b, F4, K4, 5, 15, x) - b, d = R(b, c, d, e, a, F4, K4, 6, 13, x) - #/* #79 */ - - aa = a - bb = b - cc = c - dd = d - ee = e - - a = state[0] - b = state[1] - c = state[2] - d = state[3] - e = state[4] - - #/* Parallel round 1 */ - a, c = R(a, b, c, d, e, F4, KK0, 8, 5, x) - e, b = R(e, a, b, c, d, F4, KK0, 9, 14, x) - d, a = R(d, e, a, b, c, F4, KK0, 9, 7, x) - c, e = R(c, d, e, a, b, F4, KK0, 11, 0, x) - b, d = R(b, c, d, e, a, F4, KK0, 13, 9, x) - a, c = R(a, b, c, d, e, F4, KK0, 15, 2, x) - e, b = R(e, a, b, c, d, F4, KK0, 15, 11, x) - d, a = R(d, e, a, b, c, F4, KK0, 5, 4, x) - c, e = R(c, d, e, a, b, F4, KK0, 7, 13, x) - b, d = R(b, c, d, e, a, F4, KK0, 7, 6, x) - a, c = R(a, b, c, d, e, F4, KK0, 8, 15, x) - e, b = R(e, a, b, c, d, F4, KK0, 11, 8, x) - d, a = R(d, e, a, b, c, F4, KK0, 14, 1, x) - c, e = R(c, d, e, a, b, F4, KK0, 14, 10, x) - b, d = R(b, c, d, e, a, F4, KK0, 12, 3, x) - a, c = R(a, b, c, d, e, F4, KK0, 6, 12, x) #/* #15 */ - #/* Parallel round 2 */ - e, b = R(e, a, b, c, d, F3, KK1, 9, 6, x) - d, a = R(d, e, a, b, c, F3, KK1, 13, 11, x) - c, e = R(c, d, e, a, b, F3, KK1, 15, 3, x) - b, d = R(b, c, d, e, a, F3, KK1, 7, 7, x) - a, c = R(a, b, c, d, e, F3, KK1, 12, 0, x) - e, b = R(e, a, b, c, d, F3, KK1, 8, 13, x) - d, a = R(d, e, a, b, c, F3, KK1, 9, 5, x) - c, e = R(c, d, e, a, b, F3, KK1, 11, 10, x) - b, d = R(b, c, d, e, a, F3, KK1, 7, 14, x) - a, c = R(a, b, c, d, e, F3, KK1, 7, 15, x) - e, b = R(e, a, b, c, d, F3, KK1, 12, 8, x) - d, a = R(d, e, a, b, c, F3, KK1, 7, 12, x) - c, e = R(c, d, e, a, b, F3, KK1, 6, 4, x) - b, d = R(b, c, d, e, a, F3, KK1, 15, 9, x) - a, c = R(a, b, c, d, e, F3, KK1, 13, 1, x) - e, b = R(e, a, b, c, d, F3, KK1, 11, 2, x) #/* #31 */ - #/* Parallel round 3 */ - d, a = R(d, e, a, b, c, F2, KK2, 9, 15, x) - c, e = R(c, d, e, a, b, F2, KK2, 7, 5, x) - b, d = R(b, c, d, e, a, F2, KK2, 15, 1, x) - a, c = R(a, b, c, d, e, F2, KK2, 11, 3, x) - e, b = R(e, a, b, c, d, F2, KK2, 8, 7, x) - d, a = R(d, e, a, b, c, F2, KK2, 6, 14, x) - c, e = R(c, d, e, a, b, F2, KK2, 6, 6, x) - b, d = R(b, c, d, e, a, F2, KK2, 14, 9, x) - a, c = R(a, b, c, d, e, F2, KK2, 12, 11, x) - e, b = R(e, a, b, c, d, F2, KK2, 13, 8, x) - d, a = R(d, e, a, b, c, F2, KK2, 5, 12, x) - c, e = R(c, d, e, a, b, F2, KK2, 14, 2, x) - b, d = R(b, c, d, e, a, F2, KK2, 13, 10, x) - a, c = R(a, b, c, d, e, F2, KK2, 13, 0, x) - e, b = R(e, a, b, c, d, F2, KK2, 7, 4, x) - d, a = R(d, e, a, b, c, F2, KK2, 5, 13, x) #/* #47 */ - #/* Parallel round 4 */ - c, e = R(c, d, e, a, b, F1, KK3, 15, 8, x) - b, d = R(b, c, d, e, a, F1, KK3, 5, 6, x) - a, c = R(a, b, c, d, e, F1, KK3, 8, 4, x) - e, b = R(e, a, b, c, d, F1, KK3, 11, 1, x) - d, a = R(d, e, a, b, c, F1, KK3, 14, 3, x) - c, e = R(c, d, e, a, b, F1, KK3, 14, 11, x) - b, d = R(b, c, d, e, a, F1, KK3, 6, 15, x) - a, c = R(a, b, c, d, e, F1, KK3, 14, 0, x) - e, b = R(e, a, b, c, d, F1, KK3, 6, 5, x) - d, a = R(d, e, a, b, c, F1, KK3, 9, 12, x) - c, e = R(c, d, e, a, b, F1, KK3, 12, 2, x) - b, d = R(b, c, d, e, a, F1, KK3, 9, 13, x) - a, c = R(a, b, c, d, e, F1, KK3, 12, 9, x) - e, b = R(e, a, b, c, d, F1, KK3, 5, 7, x) - d, a = R(d, e, a, b, c, F1, KK3, 15, 10, x) - c, e = R(c, d, e, a, b, F1, KK3, 8, 14, x) #/* #63 */ - #/* Parallel round 5 */ - b, d = R(b, c, d, e, a, F0, KK4, 8, 12, x) - a, c = R(a, b, c, d, e, F0, KK4, 5, 15, x) - e, b = R(e, a, b, c, d, F0, KK4, 12, 10, x) - d, a = R(d, e, a, b, c, F0, KK4, 9, 4, x) - c, e = R(c, d, e, a, b, F0, KK4, 12, 1, x) - b, d = R(b, c, d, e, a, F0, KK4, 5, 5, x) - a, c = R(a, b, c, d, e, F0, KK4, 14, 8, x) - e, b = R(e, a, b, c, d, F0, KK4, 6, 7, x) - d, a = R(d, e, a, b, c, F0, KK4, 8, 6, x) - c, e = R(c, d, e, a, b, F0, KK4, 13, 2, x) - b, d = R(b, c, d, e, a, F0, KK4, 6, 13, x) - a, c = R(a, b, c, d, e, F0, KK4, 5, 14, x) - e, b = R(e, a, b, c, d, F0, KK4, 15, 0, x) - d, a = R(d, e, a, b, c, F0, KK4, 13, 3, x) - c, e = R(c, d, e, a, b, F0, KK4, 11, 9, x) - b, d = R(b, c, d, e, a, F0, KK4, 11, 11, x) #/* #79 */ - - t = (state[1] + cc + d) % 0x100000000 - state[1] = (state[2] + dd + e) % 0x100000000 - state[2] = (state[3] + ee + a) % 0x100000000 - state[3] = (state[4] + aa + b) % 0x100000000 - state[4] = (state[0] + bb + c) % 0x100000000 - state[0] = t % 0x100000000 - - pass - - -def RMD160Update(ctx, inp, inplen): - if type(inp) == str: - inp = [ord(i) & 0xff for i in inp] - - have = int((ctx.count // 8) % 64) - inplen = int(inplen) - need = 64 - have - ctx.count += 8 * inplen - off = 0 - if inplen >= need: - if have: - for i in range(need): - ctx.buffer[have + i] = inp[i] - RMD160Transform(ctx.state, ctx.buffer) - off = need - have = 0 - while off + 64 <= inplen: - RMD160Transform(ctx.state, inp[off:]) #<--- - off += 64 - if off < inplen: - # memcpy(ctx->buffer + have, input+off, len-off); - for i in range(inplen - off): - ctx.buffer[have + i] = inp[off + i] - - -def RMD160Final(ctx): - size = struct.pack(" len(newtx["outs"]): + raise Exception( + "Transactions with sighash single should have len in <= len out") + newtx["outs"] = newtx["outs"][:i+1] + for out in newtx["outs"][:i]: + out['value'] = 2**64 - 1 + out['script'] = "" + for j, inp in enumerate(newtx["ins"]): + if j != i: + inp["sequence"] = 0 + if hashcode & SIGHASH_ANYONECANPAY: newtx["ins"] = [newtx["ins"][i]] else: pass @@ -442,33 +450,3 @@ def select(unspent, value): if tv < value: raise Exception("Not enough funds") return low[:i] - -# Only takes inputs of the form { "output": blah, "value": foo } - - -def mksend(*args): - argz, change, fee = args[:-2], args[-2], int(args[-1]) - ins, outs = [], [] - for arg in argz: - if isinstance(arg, list): - for a in arg: - (ins if is_inp(a) else outs).append(a) - else: - (ins if is_inp(arg) else outs).append(arg) - - isum = sum([i["value"] for i in ins]) - osum, outputs2 = 0, [] - for o in outs: - if isinstance(o, string_types): - o2 = {"address": o[:o.find(':')], "value": int(o[o.find(':') + 1:])} - else: - o2 = o - outputs2.append(o2) - osum += o2["value"] - - if isum < osum + fee: - raise Exception("Not enough money") - elif isum > osum + fee + 5430: - outputs2 += [{"address": change, "value": isum - osum - fee}] - - return mktx(ins, outputs2) diff --git a/bitcoin/transaction.py b/bitcoin/transaction.py index 2581776b..4b9be322 100644 --- a/bitcoin/transaction.py +++ b/bitcoin/transaction.py @@ -200,17 +200,8 @@ def ecdsa_tx_sign(tx, priv, hashcode=SIGHASH_ALL): def ecdsa_tx_verify(tx, sig, pub, hashcode=SIGHASH_ALL): return ecdsa_raw_verify(bin_txhash(tx, hashcode), der_decode_sig(sig), pub) - -def ecdsa_tx_recover(tx, sig, hashcode=SIGHASH_ALL): - z = bin_txhash(tx, hashcode) - _, r, s = der_decode_sig(sig) - left = ecdsa_raw_recover(z, (0, r, s)) - right = ecdsa_raw_recover(z, (1, r, s)) - return (encode_pubkey(left, 'hex'), encode_pubkey(right, 'hex')) - # Scripts - def mk_pubkey_script(addr): # Keep the auxiliary functions around for altcoins' sake return '76a914' + b58check_to_hex(addr) + '88ac' diff --git a/joinmarket/blockchaininterface.py b/joinmarket/blockchaininterface.py index abc062ab..64a7510f 100644 --- a/joinmarket/blockchaininterface.py +++ b/joinmarket/blockchaininterface.py @@ -81,23 +81,19 @@ def sync_wallet(self, wallet): def sync_addresses(self, wallet): """Finds which addresses have been used and sets wallet.index appropriately""" - pass @abc.abstractmethod def sync_unspent(self, wallet): """Finds the unspent transaction outputs belonging to this wallet, sets wallet.unspent """ - pass @abc.abstractmethod def add_tx_notify(self, txd, unconfirmfun, confirmfun, notifyaddr): """Invokes unconfirmfun and confirmfun when tx is seen on the network""" - pass @abc.abstractmethod def pushtx(self, txhex): """pushes tx to the network, returns txhash, or None if failed""" - pass @abc.abstractmethod def query_utxo_set(self, txouts): diff --git a/joinmarket/message_channel.py b/joinmarket/message_channel.py index e838fbd8..c3da8ea4 100644 --- a/joinmarket/message_channel.py +++ b/joinmarket/message_channel.py @@ -31,13 +31,13 @@ def __init__(self): self.on_push_tx = None def run(self): - pass #pragma no cover + pass #pragma: no cover def shutdown(self): - pass #pragma no cover + pass #pragma: no cover def send_error(self, nick, errormsg): - pass #pragma no cover + pass #pragma: no cover # callbacks for everyone # some of these many not have meaning in a future channel, like bitmessage @@ -63,7 +63,7 @@ def register_orderbookwatch_callbacks(self, self.on_order_cancel = on_order_cancel def request_orderbook(self): - pass #pragma no cover + pass #pragma: no cover # taker commands def register_taker_callbacks(self, @@ -77,16 +77,16 @@ def register_taker_callbacks(self, self.on_sig = on_sig def fill_orders(self, nickoid_dict, cj_amount, taker_pubkey): - pass #pragma no cover + pass #pragma: no cover def send_auth(self, nick, pubkey, sig): - pass #pragma no cover + pass #pragma: no cover def send_tx(self, nick_list, txhex): - pass #pragma no cover + pass #pragma: no cover def push_tx(self, nick, txhex): - pass #pragma no cover + pass #pragma: no cover # maker commands def register_maker_callbacks(self, @@ -103,16 +103,16 @@ def register_maker_callbacks(self, def announce_orders(self, orderlist, nick=None): # nick=None means announce publicly - pass #pragma no cover + pass #pragma: no cover def cancel_orders(self, oid_list): - pass #pragma no cover + pass #pragma: no cover def send_pubkey(self, nick, pubkey): - pass #pragma no cover + pass #pragma: no cover def send_ioauth(self, nick, utxo_list, cj_pubkey, change_addr, sig): - pass #pragma no cover + pass #pragma: no cover def send_sigs(self, nick, sig_list): - pass #pragma no cover + pass #pragma: no cover diff --git a/joinmarket/slowaes.py b/joinmarket/slowaes.py index 8b8d60ce..b5160941 100644 --- a/joinmarket/slowaes.py +++ b/joinmarket/slowaes.py @@ -658,19 +658,6 @@ def decryptData(key, data, mode=AESModeOfOperation.modeOfOperation["CBC"]): decr = strip_PKCS7_padding(decr) return decr - -def generateRandomKey(keysize): - """Generates a key from random data of length `keysize`. - - The returned key is a string of bytes. - - """ - if keysize not in (16, 24, 32): - emsg = 'Invalid keysize, %s. Should be one of (16, 24, 32).' - raise ValueError, emsg % keysize - return os.urandom(keysize) - - if __name__ == "__main__": moo = AESModeOfOperation() cleartext = "This is a test!" diff --git a/test/commontest.py b/test/commontest.py index 4d14f007..b0239fcb 100644 --- a/test/commontest.py +++ b/test/commontest.py @@ -85,8 +85,8 @@ def make_wallets(n, jm_single().bc_interface.grab_coins( wallets[i + start_index]['wallet'].get_external_addr(j), amt) - #reset the index so the coins can be seen if running in same script - wallets[i + start_index]['wallet'].index[j][0] -= 1 + #reset the index so the coins can be seen if running in same script + wallets[i + start_index]['wallet'].index[j][0] -= wallet_structures[i][j] return wallets diff --git a/test/ecc_sigs_rfc6979_valid.json b/test/ecc_sigs_rfc6979_valid.json new file mode 100644 index 00000000..af161797 --- /dev/null +++ b/test/ecc_sigs_rfc6979_valid.json @@ -0,0 +1,999 @@ +{ + "vectors": [ + { + "msg": "9e5755ec2f328cc8635a55415d0e9a09c2b6f2c9b0343c945fbbfe08247a4cbe", + "sig": "30440220132382ca59240c2e14ee7ff61d90fc63276325f4cbe8169fc53ade4a407c2fc802204d86fbe3bde6975dd5a91fdc95ad6544dcdf0dab206f02224ce7e2b151bd82ab01", + "privkey": "31a84594060e103f5a63eb742bd46cf5f5900d8406e2726dedfc61c7cf43ebad" + }, + { + "msg": "2d46a712699bae19a634563d74d04cc2da497b841456da270dccb75ac2f7c4e7", + "sig": "3045022100d80cf7abc9ab601373780cee3733d2cb5ff69ba1452ec2d2a058adf9645c13be0220011d1213b7d152f72fd8759b45276ba32d9c909602e5ec89550baf3aaa8ed95001", + "privkey": "7177f0d04c79fa0b8c91fe90c1cf1d44772d1fba6e5eb9b281a22cd3aafb51fe" + }, + { + "msg": "c94f4ec84be928017cbbb447d2ab5b5d4d69e5e5fd03da7eae4378a1b1c9c402", + "sig": "3045022100d0f5b740cbe3ee5b098d3c5afdefa61bb0797cb4e7b596afbd38174e1c653bb602200329e9f1a09632de477664814791ac31544e04715db68f4b02657ba35863e71101", + "privkey": "989e500d6b1397f2c5dcdf43c58ac2f14df753eb6089654e07ff946b3f84f3d5" + }, + { + "msg": "dfeb2092955572ce0695aa038f58df5499949e18f58785553c3e83343cd5eb93", + "sig": "30440220692c01edf8aeab271df3ed4e8d57a170f014f8f9d65031aac28b5e1840acfb5602205075f9d1fdbf5079ee052e5f3572d518b3594ef49582899ec44d065f71a5519201", + "privkey": "39dfc615f2b718397f6903b0c46c47c5687e97d3d2a5e1f2b200f459f7b1219b" + }, + { + "msg": "49e558d232ca204ecc27b321b197bfe0e165acd5fe46f986d5b007c68c2dcbd7", + "sig": "30440220330f62769e8e0fd43721029e1343b7f2c2575edc568007aa1b4458b2928dfc530220549c8967e8e8f414be10c6e2d36f451f1c50007c28511f9410fd7a921220190401", + "privkey": "1e17c938ef3754781130edcbe8fa2c8a27ecb43f93d333e64d89af3d9c26c23a" + }, + { + "msg": "12a683325e3063e4d9d32d38f878300d84d4ebb63247607dfb2bd72849e5fb14", + "sig": "3045022100f1391011657e358464165b53f0993a1a637f6e296c1a8f6a984d522b441b470602206c78004f1489c398aeb05b6db63d0839875f4751b117a6bd239b173428f1c2e201", + "privkey": "9120318c2d0792a29516b18a3467051445e5a6c358da577dcf06ea5ddfb8839f" + }, + { + "msg": "06e42f8adc7c2ac118b6e6d1082940c66f7d2544fa2eaae0d2b1d016fea26092", + "sig": "304402202c947ee0cf77bce3c4aab68737f07f9c43733a961552da82798a2628d6650a7c02205bb4a8634e8204bc3da98ddb1c5a7d7f5a2f4d60ee447e6e7ab20cff9dfdc5bc01", + "privkey": "a7433f154076cd9317e048562fe60529140a1155a1497ebd165b081c053e8944" + }, + { + "msg": "43307439480b2d4c3a95e725d1a13a97ec032cdbdda467533dce304513752d46", + "sig": "3045022100ac6123107cc08a63fb80e2af53937cf5aa7dee9f4e233a668d6c15d0b474903b02205bdcd30cae23c89692ee53badd610385b84567f98dc65babf000055f33877ac801", + "privkey": "58a127499af1fa4f6cc95d6b2213171762a3be0d929246f27044d64de24c4db2" + }, + { + "msg": "3a75a427f8311ae278b76bcce9a2ba99b9adc1ee91d653f9f00052b9e154b511", + "sig": "304402202f54a17ffa1b631f67232785b469d794969d3fdb140bb683ae8097217ec12c7d0220159f78ceece7e2fa1dea9e31f53374aebe6b8d893588c27e951bbc40b7ca57cf01", + "privkey": "17449f296f72790a85284a3eead10e775b452a32e7cdf1f0e68f109e5732d8a4" + }, + { + "msg": "d2ae97ae7b0b488f3a9cca6b7ed92832b4a6a7a3546a3ed9964eb51fbb2fcd98", + "sig": "3045022100c842a06f6818768fc3d93d98332affc7160fc2f58ba6fc0239c2c9cf951e6bc302202756c90464f85193a52a29001eaf824d3b0602028d4c8f5abadf6995262bc7e001", + "privkey": "229d075d7a48c5725f21e585e9effb08c80ea768883fc372ed3ad48af60a653d" + }, + { + "msg": "3492a279ef2ddfebc47cc7ee28be41b5ffc03c54b738f657862590237e56841b", + "sig": "30440220708aae22df437d7280fd8e06b9ab043abc26f6286973e94df9ae28982c534fa5022063724a156b0766c59fe75c571a5f47b80f313676f60830699b0f0c171841c32a01", + "privkey": "f2f83ccb7ecd11e6ef61a8a090890b6db56e87810be8f283626f6028843e70c2" + }, + { + "msg": "bab37255a348dcfd8864aa2bd1b964c951da28370743905f892bbf5533b2c881", + "sig": "3044022054cc561ced7052b9b0d709578e691e8e4edb04e2a38d1b79fbdbf16302674c6f022013c9ec89010fe1d9643019c7bb863abd618d4dd492c116d8fb099710e33f5b5101", + "privkey": "c476e1df4d6bc771b23836b58616ed5bb6417baaab3c533dd255562d41cce2df" + }, + { + "msg": "3543080c755844091a26bc812fefa40d00663f57622bdb63334ffbdd70ac8cfd", + "sig": "3044022066b520cb7f51656e610aefb238bfa5a62426953b22cc09689408429b68ff53f902205971ec48692210b050683f9ab2cd4443ade8a9d0ae3f95d32e865458515b8e4601", + "privkey": "e9fd9db20943a760c4cf89a9eb8aa150405686332c8a2ab8789ef19593312ac9" + }, + { + "msg": "aa05ef24738774890500e5d4f25f2801802a4c20f1be4f06721220d065abe042", + "sig": "3045022100d081a3dcc756f91b4c6aaaa5eb57c18ebe733086391c4a0761a9b11a43dd477002205cf7ce2565d977572dd10fc17875d07b2f8e3d824740db45a30f8977f57c7a1d01", + "privkey": "251e11bc1fdf9ce349c904af6f3df523e906e0367e18c96e111104715fbbfcbc" + }, + { + "msg": "57176b5966096a1818148936e1c56bea9717a7b9f4e3decf582b4ae3585e794d", + "sig": "304502210093a5c31daffcaa47b22a1f34dd969510acb76440879e91c51bb1b465fedfa6d302206edf49b1ad26bae5a1c179ae6913d3f3c3fcc0c5a3c7abec07d8adb315a0782a01", + "privkey": "099796458bffc713b89b1332089b093f8ed08039fa1d8323d7a8b198225d89d5" + }, + { + "msg": "8b339212a72cbea99b8e882e4ce7c32a1a1f0e5185d3c8e4b99c9a8ebfe58060", + "sig": "30440220640997e496194c03b7e56fe5b97a44bbad81df5a9c2501138453b86e3d1ad16a02201a1dd1ffdbe9300184b7b1ff7d0a1339dd677a081acc2208050e0dde313f515b01", + "privkey": "00417c436e3f135eb2292eb5d88cab40157b0989dc0d917ab6e87e8faa0f1ee5" + }, + { + "msg": "f1415142ca90a4f6d1f5c63dd0d776642e34bc3fe3a9ea1427afdf9d71d79231", + "sig": "304402202364525dea549ced7856a0551be97d73b1e5f20237e857cd0193f3aacaa6e1100220245974967a1f0a849a3aa7492f044c53fb10a0198c280caf075aad9beb80126401", + "privkey": "1d77dcd3d3631285c0a99a6c669d804fb603cc8a788c4568a46971b46718453a" + }, + { + "msg": "e5a093e9d225f09961f35f44b3171962b2ad3f9c859c3c21d0f03fc7dfadbd51", + "sig": "304402200d2a510384e4ec4d294150598e893ef97827c11c61a081cf694cf53e1c0dfcc102200eed9c0dbe6294f692ead6d729eddfe9267411d67a08031012c4b46230561bb701", + "privkey": "df693dfdca61122d2e633d2a2d8f4270a9199624d06f9230325d82708da5e9d2" + }, + { + "msg": "42f5f89f123967d7ecb0f0ffbac97d1e414d22f8aeea5f61ab18c67d9b05b062", + "sig": "3045022100a251fd56b0630c642143c05cc20fef8979163e85d8dd4413acf97c7f0b21832002206959532ff89b8515c5934f7dedef47c8ea1c3b0f3e1630f85445cc6989b8e61c01", + "privkey": "3e21112d32f27677d715c4f12d4005d551a5a523d7c63717f2166ebea89addc5" + }, + { + "msg": "f1914da938a0c2e8112c769591ee070ac4123b9c487c0ed7fab6b04473bf7714", + "sig": "3044022010c823ef2f6bb11e4a974723499be7a4fc9610da16229ae49e025525eca9088102203e3ae2173dcc83b754e39fbc88eb1b31effda488c1c9632572513146ed5c7dfe01", + "privkey": "ec3eaa66a6ef4b6c31acd75528218e702419bdf1c086431137926fe71daaaea0" + }, + { + "msg": "3cf9fb4cf59c6d37da6c36e0d680b1d364800e94b54f2b9074d755a456a0d7f4", + "sig": "3045022100cd40d40dbef9ac2e27f3c8d40a7f8dd538d417a7bfbcd0c00a6e92c93bcb145202205172d09504b59aeb6d2afc298441615665e4ec8fb7bf064a9364ac5f6ad2171201", + "privkey": "a51997dff0ae72ff3005ae6cad243a5d5c6864691d238fd58123bea364008009" + }, + { + "msg": "ada128dfd83338a8b0f5ead162a0fc7acaa513d5c13b9b1d5d921d2648e674f5", + "sig": "3044022075c1178f1bb941fdfa4fa4e21f056c5717a5a94247030c321ad51ac6d8a2a860022036759348c6e87c3b27388ba92895ccc6c7af659f57b4205a236bebccfe6f3e3f01", + "privkey": "9a8114c3bfe3656290a0d092bb80a9f5c67b3e938ba719424c5c8c83e13ce266" + }, + { + "msg": "d23cdc4accc2566b82fe6270e144f366f08007dffcf9e47c1c66d40708747da0", + "sig": "3045022100fbe82220345d27c870e152048698340e70d701ab868a5482a7cc22977962f8d4022009181b343e76c8645dc640762d49911df336941d79dedccba313ba03dd8c28b701", + "privkey": "f1d7fb29ab98d00f46135b74de1b7ea80f3e38360c9ee0cd7056404c5d1db57b" + }, + { + "msg": "e89e786443b5634afbe711f66530326320a0e5ec27d1c42697e5fbc11e5ce169", + "sig": "3045022100be23de4212c4bca5d1be2882ab339aa8a586dd656daae12aa2287cdfb2809af002200d460339847f0c08311a59343997d5e307136a4fc0494f84aacbbf918deb11ff01", + "privkey": "ab8f7d2eb73af6349990d2b4296d98432da8bbd858863bfa136e234a56351090" + }, + { + "msg": "ba7de66731341e48fca15ef5b62c88f19b2aee7786987d73754be8e339f706aa", + "sig": "304402202c2c1b9bee9b018df561d1f45ec07527ac916e8e56a915862cf80a4fe73779c8022037aff510509b3704390da0db4b647540647f897df5a211648f4180a292b12bca01", + "privkey": "d142f008e4ee702e6ddbf6f5429e4b39e0cd59034018a332635e6fab028f2fbe" + }, + { + "msg": "1ddaa971fd3b025d0566d9bf893a0043329379e2c52561ad3ebd3815bae0e818", + "sig": "30440220110aa7dfe50948e3a8ae149a01d9b3824e7274a6f380e64d8b189b01e71ebc4902207712465f4f09e039f46d06fa949ab5f875a0f612c86c76702c07a4007532122f01", + "privkey": "c03a06e7e3958f790f3d1bbd8c9938e3c1636301a04b15b2774d27381a383d35" + }, + { + "msg": "c5b4b2981c007f5e5b6aceb950985e64f8c5afec77955e973bc65f1a913ccd2d", + "sig": "3045022100d9f8081fc24e267dd5e2a70604277496c9c8a20b7c37f3d788e92e7356754b6e02207ffdafb0ba4efee3211a1d8e4623f13b63141054c942cd47e388faf3489bb8ff01", + "privkey": "795b50f2d308bf999565a084c8f709e6393f69a7842ce889c5addbd17c24ff5c" + }, + { + "msg": "1544d33544fa93334739a60a35bab4390b523bdbaa43ba3956ca450ad03f6201", + "sig": "304402204d74290c6daedc88e3fdae9edac89984065c2774ed69d90668544aacc6e7e16402201ec57cf928279f30d56d8e5a1a46cd976cc7117bf2d03c9123833f08db6132ee01", + "privkey": "fd2190d00de40b004f9a5d6d06829fe87eb1b11e483e94023fa01b41c27da90b" + }, + { + "msg": "cd40650b1d22d7a7777ac3a6d8701c12b485dd3859dacbaca56ad4ee4a3f6024", + "sig": "30450221008c17c93e1aa880c36c87ec08fe52ed68a96ee014a4cb8045fdd08a0537cdb602022045728db5a2bc1860cdf0767122e567f903a0de25f770d3a96b51833b2530e03e01", + "privkey": "3a6857669abb2d6f2f529b548f6386435856ebc98d8760e75642ff30a149454a" + }, + { + "msg": "5817d0ffe477cab901884793ce1e5d40302941ea7460a2b468b7b02f08203527", + "sig": "304402205ab83ee406a958901837e7f155fa8c043558963fcac8156515166bf5d008b21c022026e18000c7050b8d7415337b10eccef395db63c6fdd8b008d2b613ac2583e8d201", + "privkey": "9054a4c8279b1812748c690f7de4a8305e7ba2afa27bb30a70ec2be6cfdcd104" + }, + { + "msg": "082bca2d141957a5bcf2e6f0653557ddb12d0ef393d89c5c41849464cca6b8f3", + "sig": "3045022100d62ef631bc28d285f0ea2bde820c4edd321539e209c3575b139b4fa7fb571e35022015f47f15f24d40900ad13ddcf9809a4f8f45e8c1af2a241cd2641df02772809a01", + "privkey": "eb75f55fc51a5664c46889eb93af6834b8fc4760ffd12f36f2a7a54180765644" + }, + { + "msg": "58a7fca8920df4322b3118241aca431418796937f2dbb32bf36b2c230ac3ce78", + "sig": "3045022100efed6c4c4287746b5870de319fd0fd1e7f39cfe19771764eea8bcc2641bb87120220284f66abc53254d9fce663ecc1648d417251b246c1f72e2668e786d65b01db0a01", + "privkey": "9d6cc489b02c25d0a33f3e2c71dc90c71bb91942bea45d543f41907f026fdf94" + }, + { + "msg": "33cfeae00abc4f593d9499a7e11e314f9ead2bc6d033977d91fa13df224b56c4", + "sig": "304402205d51ef7d53b77f1e4933e38b72e60e2cb505f5c04c4589691c4afb66d45299b302202a9b34129c747c1a98dbbee519ccb8f45354cbe36b4e4bc6e9cb0079d8a8b0df01", + "privkey": "cdeee2375309bcb5cdf33d6e43f8949b3936a0bb776bb20b40087150c4eaaa9d" + }, + { + "msg": "789d7a8a926e65c632dd4ae279c23c7cbbe1938082000a4f7e663dae4d2e6565", + "sig": "3045022100c316f7d0f51e6ea313d2b03805d695ac4426f959d14d9138ef171deddf0e593702203a94ff889daed2b366a17d5b0bbbe46f96faa439f3e0bf3da3755f2313cfd9e501", + "privkey": "f22cce5bfc11406b3c2013be86cda5e5597a10c99f045af275aa5c4136ae8247" + }, + { + "msg": "cad973f4d052f59654c8b714e4c777c297217de855f7e72679ddc4d88353179a", + "sig": "30440220140c11ee563970fd34a31076fb92a81320bad49373682aab2d11ad0e9e71da5902202bc4a45f694e1a25b89e87b21b659afe87e83f57a53a2fe7a4160a93fab4331701", + "privkey": "9056b9aeb9b88e4b2d2cc34a3be77e6120551c9ec9991aeac46d75b3b07307ab" + }, + { + "msg": "08f3306ded0c07980fc8e008c6805fa19582a1c635f892d0027256b8f6e658ef", + "sig": "3045022100ea0f425a8f27a9fc82286ca7545207cfe0ca1e41d54ad3168836086bc097740b022027ede91105e79097d091efa8d3045ee9a2e250ee9029245560caa5b8bb5450fd01", + "privkey": "54f8d7d845453d60ee39167ea8c0b3203487eb9fd7f9aa83f8b92ee41799d3ac" + }, + { + "msg": "ab440f7e37a9ac189e08850866f9fec3fc2c323dc802f5cc13c9b2dd6ea9c366", + "sig": "30450221008e6a5185622e53f986c0f865b376b69cf7e2297b681c7b4baf92396808e90de90220633313c8afcb4bde5d1e099871e75df82245c9f2fe24bcdae86e53a48116f74001", + "privkey": "ee1eda8fc9d0cb958fa518d0b95189c7eb82202e6d07099b8a6a11106797af70" + }, + { + "msg": "c0d12e0b35fa92dd04ca66b9e9537a71c5683a4fe5da9354e9f74cec0746251e", + "sig": "304402201e78c59ad8c6ea7ea75735696271eca86851f7d65b2c6771eb5ebbae2c3a5c0a02203f0e9b34333f544e692f7ae92ef6ad68fc455099760944d3e074bc994f2da7bb01", + "privkey": "56983efa120cfd3a7fb92df780a60463decd5f47c6422c6dd0538a68d3c7bfec" + }, + { + "msg": "98f7eeb9417e950aa953c110b245adba213084db4bbd58978f2aac125ec2e584", + "sig": "3045022100ba311522b51cfceb11f8fadb34a79f5a75e7eb6b5878c907fb63c983feaf695402205b9e0910415722ce1bf94ac5b6c3bcb09919d3497999ecdde7de6fa76f3d00b301", + "privkey": "04dbf45e1b5620e3ca700a82e4e83a971551e25e15abfcf49c8a201a4174bb58" + }, + { + "msg": "2e18fcb984ba778c0017cdf882a862dfcadee1ba4c7ad44937006a7ee0ee90cb", + "sig": "3044022013ccb3f7845476ceea2b990d15ec53253f333ebc1cf392e96590d88aac70dff902202c4874427363f84ab7bf331d2a811d57399237d58fe0ceef34056f26bcd9de2501", + "privkey": "8bfb17789767931b9602e3a1310ae79ffbd5fe7fe1f2583099568a4308cb80c8" + }, + { + "msg": "d6fe1d5d5670b02f399ab0125c57fdaa181ae93aeea97c5ad238e0521dc10873", + "sig": "304402202d633a9bbdf3e486fbcd8dae7afb7b9604cf9a806d72121d08a95895d16db21202202f83e6d1c5d17d676885bc05660271770618acaccd35a30b1d3723176c1b395201", + "privkey": "e74ff299a3e399fc7833799b1591c128102d441361dc2ec77810d99abf280fae" + }, + { + "msg": "798a41c8bc899e458e9e07d3d83410752ea580020accae54a3ff65ebdf607f54", + "sig": "3045022100ac0d8bee62c3aa92ee6f29c98978a7e47d179f6954fb2b04b977489a2d24e7af022057808f4c12d8deb7737827a1543e88bbd44abbafb4d4db43e2b5a6566d0e821801", + "privkey": "cd4d9c04ec290aa6fe5cb0e34e3f585beaddcba4f39d883bcd0a7e989801a778" + }, + { + "msg": "1bafdf652c98542066d53aedf3b0fdd1d3517bc6fcb0155be621ed0032675a1a", + "sig": "304402204ffba8cc994840f5824b2217188bd0e671cc4b787ac33509c774a4075c62a989022041129edaee766692b1c8d9301974f6daeb7139d463bad561f650801f097b4b2601", + "privkey": "51385eadf7238fa581b13905b83ff1c59cb7923d2afdfe2b29396ec9da1f708e" + }, + { + "msg": "6fc96a01cf2e3ab6ab277cce670d446f0da3a25bc4344c02651cda1510bda8cc", + "sig": "30450221008bf5889acf97948be7bf6afd1da1504bab75d841c09e3e6cf1cc445da0ed5cd2022030cf8407f6aabdd056e1d4cc14b4a0e4ac09c6cb45f572788f5353fda223923701", + "privkey": "20dc42c28ec79d4b62d84e2be93945888178a0c29aa9b67cb1afa5e4ff4f1073" + }, + { + "msg": "524fc94cd49f7fb80b5d2a111d7fa9f9022b79f5368608dc5b50d1706e7e7fce", + "sig": "3045022100d391974a51503f4b8675328abdbeecc998b54bf6de23492f57631c15b8f7ef8a022075e1d215853ce491a0efb1b2ca60776142a26b1e8975158988569c2db2af87b601", + "privkey": "8cdfd627ab00b392e38c27ab8eaae73b202f306deea075815bec61a0d2cec7b2" + }, + { + "msg": "d2a53bebeaeb041272699990b027da459ca5badb811e4ad57b3445c56e5112d7", + "sig": "3045022100e2e221910d4ce1fbd7d489a4807a5d11e6fa45f35a997ea5583f8508a1faaf1102200d5fc4fa852eb1b26abfaf6799559a7165b4590707ac446019acf2d468712c1801", + "privkey": "021144fa085375c86c681cd021ad5ed3e7070503309745e97143194248dca839" + }, + { + "msg": "63446421e65a1fde302d32b962dcc75a2286b7c2f0491c25b3fb6f051b208dc4", + "sig": "304402200c792c52f28ffdcccda08d7875b3a62e665dbd65ffdd80b0767fefe7af88bcb102207780c849bbd193e805916fded694b89376136cc7b2b8567f52179fd10c64a48601", + "privkey": "dae1c4edd2a889f677615ba48e22e9144580360cb3c7094495579b3a4c17ee24" + }, + { + "msg": "53751b08cdfd84b6e7a72d9d32f8684efcec290ec4ff53e5d2b66d85fe1075e4", + "sig": "304402206fa2f2ec4f36d3b92f901d6d1935f6167752c13bce38fde71e1cccace629abe802201575d02c1f9599e19e72c1552a1e67c2b84d780bb663cfe26f0200f7857152ac01", + "privkey": "81560d6c8f7f56947e85c05a5bf58247972a91974dc886fda04557e42cf7aeb6" + }, + { + "msg": "a8f00b68747f932865b1e652495e2ee2b962d58ae65a803a83d5dd723b961195", + "sig": "30440220113a2d165fa751a5fbbd96032fe0ba55a9f7395c546c56460c74800206c7689d022037cf2dbd41bfca49004f603aa6476f9a2ead3c0e55f4fe437b4f968379b8951f01", + "privkey": "3648124d1c85eab5ab6a9e48377e1727d1da31b9dafe46844bdbda5381364b16" + }, + { + "msg": "c0c93d49025eba4dcc15427306e598ce42b2b301dfda34e8fc944a10cbdf827d", + "sig": "3045022100fc15d1c1a7ab3647c56aec5fa47f1d3d52d082fb797701e32278ecc0ae93dc3f022074bd65fb1164ce8f856dd8c0d5432bef396357302409ca101f02904b7b7fd44801", + "privkey": "5516f2b53a5e43cc284f074d8960a918e67afc6c0c6fb74246fefdf2ae55b0eb" + }, + { + "msg": "8cf263c83989fa23970f41b30fdaaf226792c0348d21da89d659bca19fd6d26c", + "sig": "3044022072a768ab0f2f878492d56153ee51b170deb652b6b2a8fe940ded965dd32e49180220300b9252ba85a611adb8827c315d47c2cc33627db9be8e283fdf09b527bd21f701", + "privkey": "467904e0c8850c5b84066e2c135096df2374e5a2066c0e94114b1d06eb98c454" + }, + { + "msg": "ab2e3da1e5a5f2acb51e00f7f8e6cda712882080f24bcd9ea43ca555577f3b30", + "sig": "3045022100b1e275667ba42709e6a6da1306df173728b032c42fe955e60721819d7ca77c2302201219d9db72e278fab11dbc80fd14191d2d37a13fb31338280cf2837d64e27b7c01", + "privkey": "6a7536d6733334e204ac5bfa2172c86f7446fd32cd7180027391087f98f18116" + }, + { + "msg": "6eba52cd7a8f859632b2b5e8a6ac69581ce266d82a6b010c7b666d3afd0e19df", + "sig": "3045022100fcaa0e84521f46c9a9dcff5da933c981fd2d0275c2cef76881243c6390077ba702207f7aefa9e2e9d2b27526a34c26bfa57b7cb2e22ddbd9ecd830e7cfaf81a7b10b01", + "privkey": "e814cd0bf690b780f8c615cc6f1202247b5218725e76eca315faf9b438c5e554" + }, + { + "msg": "85b07d8a8d4100a4302ca49cfde58266911ab1ad45368cf7451dbd9b6f45eb78", + "sig": "3045022100fe510f6954ac3f2fba233f109ec7a08dd93aa955336e47fe7b095f1d79cfe198022037925a771d139f2649a5665a3281b4a363ca96fef8843444f23f101400f247f001", + "privkey": "4f4587386955963eaaac84b32eefff63b32dfbb540bfb578ff7b0cf9f01298b1" + }, + { + "msg": "13063f26e50c5b6e62ea39fda446e3beaccf50ad07441241df62e3115f939552", + "sig": "3045022100d44570f08a9e64119d9885d73f688e51176807a4143856d06b684fff4a8ac2da02205f757d21c8c56258c63034d9b169c402889532493cab2c06c697154f44705a9a01", + "privkey": "b6b2e1edbcf892e41285f4ac942a331c4a88d785267c45c8095059c5914d3a98" + }, + { + "msg": "738516a95cdb7d1b374324ab22e642b3b23db43464d898e80789eccb25240e6d", + "sig": "30440220655630c6bef940bc7360febfc8e7a09614e871b7ad8c500ad658a6ba7f1669d502202ad85b78a32a10c28b2d581d7e364f6c2b7caacb24a61e31130f2fae5a951de701", + "privkey": "23bf641b12ea841b7a8386a2ff4f378c8d33218fd147906db34c64363f59c1ae" + }, + { + "msg": "2a7f44ec15c00e5c457c545a7bd27dcf38ad636cd98d48f0964496d3a01f781f", + "sig": "3044022059bfd164d817b91b94dba6c3843cc85d08442662a9f3736b12f294630f75578b022017f6dffcc2039291bcf0719281b2e599044a63709e94ab0050ebe37a2394933c01", + "privkey": "86b53e69d27fa695bf8217b0a78d72651584e24f1d85e928647310ecc04d33da" + }, + { + "msg": "865c6d42b0ca9767593c8736c8b5151753f9c0068c9c52eaaf4305a19e2ea559", + "sig": "304402202b995842e430ed36900761f11440116c4a0ca5c4702aecf293625e608011b3df0220051b45eacacb9448cadd96cf4daef05db8f41dddfcb1943cb8ed67ded4a8e9e901", + "privkey": "8ca07dc245d61105afc9b40e69bbd1532b65e72094f929c117c5a72b4b6ddd25" + }, + { + "msg": "5170ee5b8311f24570aa8a639e33c22c8f883e8d947548d47e1e330b965cc57d", + "sig": "3045022100d523659075fe10d9f6cc3e75cc74bf94ee65f4a099eabf7fe6dbe24bbc17f65c02204423cb40c94d732a8a51487b9e398fdca78005bdf1571b7126d67d9f50b712c901", + "privkey": "44e488036d994a53d6d62bd6e3201b7d61aac740157bc25ea41c7da37b5d6149" + }, + { + "msg": "29b9327f728c7107537475f99ac50f28e33ec0f68f4db619ff0f8e5b314935f5", + "sig": "3044022035bc811ac5054c04febe41208c422922750ac045acc1cacf2529666a7fb7e40d022071837d85c64cf07a1d598de345125306053c3c9014b408898dc5edd04dd919e401", + "privkey": "1dc87fae4276cdae8ffd67563e0f8539231582818b2e2226dbbe159c021e61a0" + }, + { + "msg": "99c751a334d1a3687432e87a34301beae87ec78aed2d48ce066f118b476a85b0", + "sig": "3045022100efcfddecdfbd8157fbe996563256efdc6f29955fab32edb3efed9cab08ef1dcb022067b01f720312ae64475eccb6381e1d3fa342cbcf2852b1ca6a53f5b1a6e16ff901", + "privkey": "6e9f1467ebbfa71faa565e02a8663fd138487dc9526e5d096cfbb2ad38883d22" + }, + { + "msg": "76bdddc10926a641df369a6179ad622c6188d3081a3cc90d8dec398c68ab7af6", + "sig": "304402204d275af1bcd7ad578aff98d8a1551a83584b8ee9b57ef6d425fd191426f6f63a02206cb25c37fff11029f8fa5c2b99d0aa5bbc5a5fc484c863a49ab4ab4479892b3201", + "privkey": "f64b62c51f2de8f4643857ba12f81a433764a3e0cd9fa2316b8e202cad9778bd" + }, + { + "msg": "f87a7e12df4d4a0ed99ec33e3ce805151526bdbced2bbcb0a012019a5cec7f99", + "sig": "3044022029fa907c4e4ba985b362c2e291291271c6acbbecaf4c49b938261ea6f255ddde02206cfc13e0dbc8d5ec4c98684fb36087738f9b449cb0bd9d930cda0025083543d201", + "privkey": "c21f2a248126a5b5d964d404911682ae1cbe7b7741681226867337dab8974fcb" + }, + { + "msg": "493f0c699d505325bfd1dd86e28d7bf94d698120dc10d475aa2547bfdcf5d66e", + "sig": "3045022100c716b0c65339df2357089c202f4644603c21d7139f2cd77336f871834c27150d02206ef0c8cad1605b3957a15695db5fada122b9a8b9b4c49743088e88f93112320101", + "privkey": "5a4b74a8e52b8f2dfd45c2ff015641be2b1f66fc285661de8fc65badbd6707f5" + }, + { + "msg": "82c5878eb438661cf7c50655890ed64c374611f3cef2e309a76565f745fc2c96", + "sig": "3044022030db6955679658577e9a7cac8f7081105a173647362178efa7c74bc813396b8102206836332abe41ab0e23e6f62b0aa08666dbe8228c5138638ed0c2351c22e6c57301", + "privkey": "aa6db6512e8b33be3543a46a908f26720402e3c15bd70ba8ebdfe7ab80787afc" + }, + { + "msg": "652c4068be47d9437d49d36173e095118367e1d9d0e86295604c0eb94484d5ea", + "sig": "3044022065c05eecb47346d6b45b588a90eb0f5489a23f93170557eb969f258adffabde902202682a1ab6397c49892e349ba581fd623033a35910c97f4079e4c2e505dfdb21f01", + "privkey": "ef0d812b85eec684b3314de835f73fc06c3c90078d404862133643c3347ebe15" + }, + { + "msg": "a04a3ee113f5d3054fd1eaacf8f3b1db506122c413ab346250e08ef6b5be8236", + "sig": "3045022100ff6927f3d0c14cfff2ac2c8b47b20ac99baa48ac72726aca35e87af7965725730220654333918dc7615a2e1f5b59af74a1565c281db62201c54befc6158222f8284f01", + "privkey": "b85e9ba6297a46fa4f017742485616c61ce5dbf080782ba1dd961933b7390e18" + }, + { + "msg": "428f1dde2440673a19d0b97fe46ece447721e61a700376cc56263e63cac5a3da", + "sig": "3044022070ebbfb49ab2d2529546115e55f8aabb7784bd38fa16eb6a5cc6ac7ccce2fce202206d9afb41c0254c2aa1ba4fe3a05be5d5c1297803adb701197c918609184c2cda01", + "privkey": "c17b6ff03bc75792c3a3b4ed5612140e2f9d9e5d6b21650701523665b3819b05" + }, + { + "msg": "ca0d05a273ef3b2782059307d838599eb027b0962798d71efc00648a5b07b744", + "sig": "30450221009c88cd669ab7743fc2f3ccde723fa8ef653f951f67abad5afe8031c6fa3652c4022012cf4d5d66bc0e9d40322489ceedce43bcad5fb41cbf92a6ea0f387c4b3b214f01", + "privkey": "c658044aab784be15a5776d2c4346e1099368a45f06853277352d70823ea7572" + }, + { + "msg": "f6f4aa6943deafc1879c00589208656e927422b5f25cc9526a9fa6139c66613c", + "sig": "304402205325ccf115446eef902499ead3734d02065dfd337d924ca615f5d00b1580ef8b02205285496cb0cb63e8ccebf4ff999c922bc3bb5d4a4eb3267fa2c5608e6d0b5b1301", + "privkey": "480989d814f56895a0c5781497adb1ab82744c009815f00362321dcdb709f036" + }, + { + "msg": "f5747a05e3488bd03da4210f9233131e1983f9e969d23330b3858a6c71f5c484", + "sig": "30450221009e81de985b1bad7843c72cfb6ee2a30295f13ffb7e1c3b10ddd505cd5d6feb6d02203389442e24b7027466eeebf44a9390ff8f9832716544a4f508574fa919b4848701", + "privkey": "db1e27ec5686db0d8a2b7c609847652067534751944f610bc7ba9f3792e92fda" + }, + { + "msg": "ee0a83c20ff143b6629bd9599b5beb2e63384be72a53221c3263bb83efde8647", + "sig": "3044022063f2d7f32985641841f7c218e7a90add74b82e76004eb349d9e122162a574d7c022070e4793900af91b659030179ef46db54f478d74a31b31dd527af0952c49d461f01", + "privkey": "88624e28090c22fa80888c3f15000bd52da334c7b295587773a172aa95bffe2c" + }, + { + "msg": "80aaab2aba272c8ccb67022ae9a54bbfaf8443bb37d2f922021a681bc4252a90", + "sig": "304402204457849658bacecab40c4893021d2e567fc86f1c2ed2b20d9f1982138f7f00450220225b04c91531470606088263b28b3df2cccc5a9f1df36130d8960343ed89e13c01", + "privkey": "ac461d001fce26e7f75fd82fb910e839d6e92bca1db3969fd78e74674d3a6bff" + }, + { + "msg": "285b0f5cc3c35deb52d5e1f5fe6e7c5a82a0c3ae10249c39c4b81307bbc9a1df", + "sig": "304402205eed90c7c43c9dced57edcce95ae69e5babadaff58f033055d998e4df596121702201e47072efa39405f2920b240b56d3ee41c0b1c2adbef1f161c8da1e7eda6fbde01", + "privkey": "786f5c06867cc7e8a6b16c003ad6809da46851a8e3024527e93af60b1ff70a07" + }, + { + "msg": "d615006da4b6aeb980bc12f769e263821890003123b1d2439c69da2cecbe51bc", + "sig": "3045022100e13137464625b65c0403b71a3ff57b42da0f5d35be2384da2a4f52e93d8039b302207ae93812fb1aeffaf60c39b0096b39f2fd73bf2e695be8bddea458a2bd55b89101", + "privkey": "226140d24cc86b64a1c2aadc77bc8b05552386da2d707668b3c460f69d70108b" + }, + { + "msg": "0c26d76afb90e2fe6a8c4b2a9d2496e2a6e27f25380885f7985d8c1ab7d2dda6", + "sig": "304402207dfd5a172204356d4dbffe018b04cd6c123a48daf9b9fc201cf7e32088989b11022030217c1dce7e1d039ac011138569c4c897fd201694b10052a1f3ecb2aade3a7901", + "privkey": "1e807394e21a0412d2d48711c0be3685a49fa3810ff84517b5adb1189ebc452e" + }, + { + "msg": "ae93855f5897620269b0fbb6f2444fabf304c570c2bc3d1342aa3b0615ad5b70", + "sig": "3045022100bd83f85e36346c428909a60eddd7b1c52abfaf662cab6fc810e0fc398371f83d02207caafa58dceabf7176c2e3b8a8890e47b9c1cddd1cf000ba428a91c396932c7301", + "privkey": "c8d382437c63d3debc2844e00c7eea0e0f400b111e805ba32695de93fc150a65" + }, + { + "msg": "b9df3eba9ad579c1840fab29448186122ae27c9add5b5fd6c8a2c7556e0fda8c", + "sig": "30450221008bded4f87ee25852e45908397876b3601c6a0e177dc986bc43f52c5e25fe5d9a02205473929f0502abfc9ae0da25086b13d92e0ee4f1e1c9fcaf334fb4623048dbe101", + "privkey": "54a92b44ea83313d0779475a1e664ad132abc53dfd8e4742ef81d01b3ef657e5" + }, + { + "msg": "f304cff5f74fc72bddb353ecb5d1a250848ca764ace690cc308d00b247577226", + "sig": "3045022100c1f9fc9a63f0d26677bfd68687dd0e56a6342bc536a71c9b1f3a2bbd2a48360e022040a3f3179228bb89b82ebb648073e713cb759c1cb7d4e4f5486c29945d5c55e501", + "privkey": "b59821f0dba07cf87f0bcfb4310c6ab4e676ca319fc6c0d9d11ae80be8a0468b" + }, + { + "msg": "77ba3ec6b8fa5c2b8f6e4bfab18b2b51ecc94586a41a1dd80290665913412266", + "sig": "304402205d9fb1e3c6ea1b18849cbb2666ba5d5c67bffa78d6952e7b0f009226db6eac85022043cecaab697ed0684d53b497ac61ae614477ccc153c87f938e5a3302bb70d59f01", + "privkey": "a62d04ca999d3b5311f317edec9418fbb065805872b25dcf69c088ad7d3b9d11" + }, + { + "msg": "63c7a73d7d35dd73b94d23b446bde100dc15e3544222b9c7e6c9d0be84b1f5d6", + "sig": "30450221008043f2ee24182f28ecfc6c53cc3a51ae6da4ab902ca4dbcbcc629f3480b9a97c022052e44edd783f81cae9bcbf4e7185da17a82c09894bc44b0405392aa00000f85901", + "privkey": "57b66d323c278d6aa3799db509d1909da6d7f69385b2627f8785712777f8236d" + }, + { + "msg": "2ce5f3aff72b0867bc0b7c286dd49f011f65eb563a93cafef5d554ef3d9c1488", + "sig": "304402206b1cacf8fcc9e813f421f88ec8541c04de7f00430e1332829ded5ff73f218835022055b9da3bc1de4ed413f35765529c1df9d5b9b8e8ba1fbc7507c6e8fd747b191601", + "privkey": "81db6a86b8e6d840a97da1969d5ccb9b9eb4f9d36f6e59df65bcd42b9f3ef12b" + }, + { + "msg": "475ab4796a2038629a3b9f43a14d5a2c86467bc1e32ba654e87ba67ff99d2890", + "sig": "304402207d3375beafa30381e9ec3b32b0ab819d67c7f63c61df796993085ed9f61a1a100220050e89b744e94e4d7301701d602886a39e0529740c24f6a0f8bed0d91cc01ed501", + "privkey": "8b51025a732cd22a179591598414b66bc370295c1d35284f706d468de4684dda" + }, + { + "msg": "af1a72105095a243242132ad24085c444fc78bce26de4421467266d248536b87", + "sig": "3044022025accec34080896b7ef072474e0096fc58ae84bd73f045027007925dbb7fb5e002205602c0a5cce0b4d9e10a3009f9dca9d2599b694b6ef0e7c4360b08d35fbd607001", + "privkey": "5f0cc5d1b7996f0187a0e1c709981dc6dc944e92f908367eebd82f1babd2ea63" + }, + { + "msg": "029b0efbdf0374c6a23593cd7c6917a9404b9fa43ed32428a82be05bc7f2fc3a", + "sig": "304402205fee5f55838ab4f7f7551b64ed346565f64dd5b42e2694d7c372233db2fae37d0220026e8f5d54af25529d7ca1a4991a1385e7ea3ea9fb37db826d81aa32b0aeadbd01", + "privkey": "b7906b63b11eeeb3f69586b7a8323aab9f4dd51c157f813d24f8e34407eb5c18" + }, + { + "msg": "9db7ee956f52eba9fbfabcf840d9c107117a00f5e2874eeb54a91025951d2ea9", + "sig": "304402201fed017ec208e65cca442552269487cd15961c2603f6aa19fd035491cd4fa09602200d0c5c9ac89dd501c34f3b7403a82832f58153bb77d070c44fd8f9396dd0c2b401", + "privkey": "3e89238cb4a7de25f833f4b3cd3374cd5687d9cbb858de906a570818e14031db" + }, + { + "msg": "b502771c17443b8835fd6582fb2b44512b66f9bbe92dbed5e710b76fe35bcc56", + "sig": "3045022100a424be7d1cad14f52a4bc99736635e3130dceff4f9321dc3eec31915a99d365202202c573a3a8d2d24a79ae70ea15e32e0a79819406edcf54bcbae4d49ff87512c8001", + "privkey": "6a4aa42e630ede3342539fae35f43c6fa02c94c634753adcc2130de76e62dafe" + }, + { + "msg": "5c66cbe4059db5bc8046fc393266d2db410a81ec2a02ecae0368aa0eb35d68c6", + "sig": "304302204203bed95a0e92b6a4b6776551e01473b891487b051824cf707ca553b40972c7021f64f496fab45d743ffa95a520a7a6f5e98c49379e69f0b8a7dbeb98b576215001", + "privkey": "d7e3eb80f852de183837573ad3aef624e16e8302f7d0d4b4d5ff4abf9063e438" + }, + { + "msg": "12e16733960b2c7d5de1f06b824094bd116ee9ed83b680233298be7cead86741", + "sig": "304502210091d303cb2e80690531645c79743cdb29906fe6119023844b8c66c81d39c9e299022021e6614ab44c6da7bba1d7de4b37871cfaea7a76df43bb5b90ffc6f61fa7e86f01", + "privkey": "0686905b967248099704688e15d8e3eeeeb58ff24a06e8c7a0e17b534a4714f9" + }, + { + "msg": "2309bde84fa5fd4ed530fa7ba01af3dbb71e7967df1792a53b13bfa8225dca77", + "sig": "3045022100e8f17e0c541518d5eaa7ddfe24d7caec497783da37b7605522039024bcb835a2022075d5ef13cef091ebb861006299cac29fa762c2d635b3be798649633e4d8e0cf301", + "privkey": "4ded96d9315bf52be4ef30a2295d76a3c6a997a331555bebb5012491b6d315a5" + }, + { + "msg": "1453bb7e9be2757cfac795c751dcac6ea24a1a597d8ec4b5c0131142a985e17e", + "sig": "304402201a686a1eca60be4ec2d95f22765af2afe91a6493ae70dee9ec0ab8915792c02a022071aa6f6f48f2c04e1a56e116eca35cb36866595561cd10e75bfdaf7f08f2af2e01", + "privkey": "6d9ed1aae9dc352667d4feb5e3f41f51da9ef9eb56a58b2497d502779680ac4a" + }, + { + "msg": "863f7f5d7f0619816d65ef3eac2121c9d3b3a6980b044074652f99c70abf539d", + "sig": "304402200fcb08df78fd9aa098d627f6683faaf6535cb4452ac075a228ee72a3e5cdb16b02206936c264990abb530ab705249e564bb4bfa7227edbb71c5686387f7d82ec251501", + "privkey": "34f87676d627e5ce30cec37ca8b7add7b30e82f3e510b8ebac31fcedcabdb22a" + }, + { + "msg": "9d3469873f54a1c26498eaffa9fc7c49644664cc4b25297fefc1b1b8429f6674", + "sig": "304402206735289ce27cf7e55251526382a07eb9e4a132971834082002d4464faf012c1602204a6c9b451f9ad560bd5c10561a551f33b712d553dd209b5e9f12fec46c2ef9f201", + "privkey": "8b4bf88a79deedef116259d5dcc331e4e566c04f1581c2ce0d02e6e2f15b1ce0" + }, + { + "msg": "f479dd8ca6bef32889a4a0159462eb2b56e387b6ad15f8ae0b09e1f489d49cb3", + "sig": "3044022046df345f1bacaee73ab2157bcbe6e1b333d2abbe5cce53fb5a50f81c952caead022071ef3cab8a146da86366c1f79ccc7edc76bc630ab0e30956305ae041b908be7701", + "privkey": "d3259f9685535af057328f4b4aba66311fa654da96b01a4161749f12d27fdf60" + }, + { + "msg": "9b4ecdd6c6fd1a43ba3cdbf534a8eb0159d0216ccbff47c3e41665776f00aeba", + "sig": "3045022100e7f0b77b9f767b6cfcf33ecf00a5dde22fc7fb46f87a3289f09501dfd889a0360220645639cfb7bcf062d3bc62a81c27c79d6909758f2ee37b08032b2351988a7b5601", + "privkey": "3a6f3397de4bedee3cce0568298f039d97ef8f4ea1d3d136e6c857cc3cf477a4" + }, + { + "msg": "bc64a2216423e642a30a0f1d790f3cec68b71f06618494723541d3c778237c5e", + "sig": "3045022100f85ea06b50d834109e5a0cb1e4f506e86cddb875867a9c3b28b48b1ae086589c02206e875f5abce1a66c30cb082243dbd9ceca233ccfc758cf91bd16cbf08648f98501", + "privkey": "3783268c39a70a6ba824e2adbaa07cb11f94b39b63cc3e8d53cf3603042c5fd3" + }, + { + "msg": "3962c385ea39d4df584dc1d9b93c1424bce2bef2aebff1a26dc7ed181e57ca50", + "sig": "3044022043d17ebe7a376f79dd2ecd1b745548d9633e5c7871dd521f922af941c7ad47cb02203190a04501956eb66840ff7f7af13fb492e51b59ac981a6ae9e43fb06552365401", + "privkey": "c26e9a48223b3a3966495620a14db81803ffb6f3b3a69932b72b65f52b46ca80" + }, + { + "msg": "f87d0fe24e507120efd2d10aeed2905131d296d50e688684d1bb847e68c02bef", + "sig": "3045022100fe995bc6c1bc4e6f60fd8054baa3685cb09344fb83b18496f0a74da81a4ca46d02203ae1bed24bee86d68bad7501091593c5db3442b6f21fe02445ff14a0574d98af01", + "privkey": "53dc1c26dcbcd6220e7329e5d12fcf17d838b9f8936333b03d0abed3cfd3ac2d" + }, + { + "msg": "62d97d2a26ab35d9e2d18e4335f7e45665a712eb65a1b2da9c2824339c4f9780", + "sig": "3045022100e8542c42cb929f670c2c7e5faf2b46ff9a02852704e91e1a79097610476ed4f302206d9aa52a7b886fb4062a87ffb587e54c0c2a5a7ed1c12dcf112123e1b827b51401", + "privkey": "6742a01cbff6a2e83a4f68a338c6f0c0995f6cac6e842c6f06de8337fc679f19" + }, + { + "msg": "364f0f3c7f17aab5567cb6ab0d9a364a59ad8ff294a006eb89193396618ff58e", + "sig": "3045022100ec728047c10274e6c1f3ee2404a9bfb5d58beebed4ed7d0c80901926a2c5caa4022027f1905073e6730963b440c22e150504b47ae4b02b10dde1aa2177a169a80c9a01", + "privkey": "f2415dd427697296d40dfc72084df1249b04d33cedbc880df1d9a6c88917dd9d" + }, + { + "msg": "d0080a4d6d945980321e346feefa6d8056cde94b974210dc2064837d8f47ebaf", + "sig": "3045022100a81edb49b4d7544d27a5cd7e112b4caf58898cb83d76740934c151e79566f97802200156257235e64803ad6b15ee58d0eb7a103fbe1f6ea024fc25420db1d4b22b5b01", + "privkey": "a7ba1b4480cf766d833940311d8a8c59a22d761858443412a94aa3407e330c16" + }, + { + "msg": "f09feb58a0f48e1678434d3995d12185a9d340baf3d19902a4680a532456cd20", + "sig": "3045022100df54f5a3f4791b77107c11ac15b0ed7ac59cdc8a46234031f16317d1dfba946402203d768943db33aa775d86000428699ac45e683d9d0b4b5ecd47b99ec7c548709601", + "privkey": "ecc32707de9576a9e91fa013190253e12ef780a5d30a0b0806da2ac05678eb6e" + }, + { + "msg": "2e8e25a6cf4c689b3560007bbe26a3df37dbf0eb5562c68b8efcb42fd5760f1b", + "sig": "304402206d2f9b5b37afa234eabc8e9905f7094c56cb936b038abce5166a0e35b82ff9ad02206e0060d1b784e880f517a5d8607c77a82e93d6dab3c3b0ceffbd9e632ed3c78d01", + "privkey": "bd42ee836125cdb5cc41ef767933be53fd374924441c4ef829b18c88ae1340f8" + }, + { + "msg": "5d659bd8337e119afa182ead19ef91f5d2f4dc6f5849e8fc56b4e6045204679a", + "sig": "304402203ebb171ab9732a29344b2c5474f46068a7466c6db0694ce2935bb85f5a6cb57602201b80113965c0670f3b0065b0c3a527b89a4c328865362767d8fdd6d5c97258a201", + "privkey": "17324fd463b6990e89218846d24f13813e415ac8c29faa2c6e7b2a6169e32e8c" + }, + { + "msg": "1cb65e2253db80412c92e092db5864c2074fae2e0800ee213b4ee82d69d639d4", + "sig": "304402205c216ab6a7cb4bb9d33360999d2e00ac6b7c6e956150db10865ba8fcb87cda90022064fceefaee8c40a8f5ed8cee478d45d0e5da1aa15d29e103d1cfda7748f8160101", + "privkey": "5edd47eb6580ae11bf3a148e01ed28a94d6a7912621707408da31ce4c541f46c" + }, + { + "msg": "5ad8d22c2e648851a09d74f1f47d2f116b51e4cb662a7f6b217f609f6d578901", + "sig": "304402203c151a74e81770d564b18efbb895f0df2f4d225d8e215e671b5fa44b43b098f70220540f560d75456eef77ba2e52848326624ec76ae7edff7b2199b822d78e62a0b601", + "privkey": "0a2804f109b57c7768f1d034112ecf46c705c6bf01b68c24220ff80e332e1023" + }, + { + "msg": "53c82bb675afbb5f6ceea4f6b3bdb777cca87f9f27fca9c688432b69b80a8d9a", + "sig": "3045022100f49e0096e15d8d2183240fc3013fb71696aed95977d9788e1f6b99394006f0b502203d01efe4296b409db81a4e6cd601a7440859249339ca7c3a62188537802b488201", + "privkey": "9d0b7120991d0910765b1dd563a787ab9ed51afdab8fb6c214fa3c6d6e32a08f" + }, + { + "msg": "51ba637c78e753468782866a8eac39fed07f10e2e38946ae0ce7a9a087aeabc4", + "sig": "304402202067df8cec2b427a98fb7e0ec60715a4512102b74b651c9600a5b4883a0e5a2e02200daf8fb6d240f12f6a7afb2e9cb138160e16009498e49c8fc8f7f198dd1eda5001", + "privkey": "a73b76b643651ce1211f0f98ab19050a3aed4774f9ca2521249581c3b505817c" + }, + { + "msg": "5aa29ded6e2f76817a66fd97d28838a35b61bcb4f04bd7c9418f99786d9b67ff", + "sig": "304402205c5b3b02b9e0d50e2c5abe23cb046dcbb541b37f90c06bdad2d8a25bb91eaf050220346cf30343afa33695e160d2e8a66fa98fb2f8eaec1816178caadc223f93720701", + "privkey": "c9b796aa7aaebafcf3e7a2ab52b2dcc59acb926ded580c85ac95fb7333922c71" + }, + { + "msg": "e79b7d427349690bd001f4bf0a3fcb61a9a62f710dd7b84fa04a9ac8621dae22", + "sig": "304502210081a3126be36cef173363ac2b3d157e6b12ae905ebe0cc76bedece1f4451f2fed022046896d45d55e8698f6b628e930b09e230a778075e1c2ca5e1c61e75347eedc1e01", + "privkey": "a79e2a1d32fdec5d2425af329680cc7e466f37b559fe54a9414f383e0c02ce3e" + }, + { + "msg": "116eb6113b0e918e4590d9c0b6e335da67ca3c4e9ff65abf11655a8eea47e4f0", + "sig": "304502210094626b44fd9475c5f1300bdb3f66227371b42bf169ba0d75a9c5babd37ffc946022052b21f0bd6053814b76532320360c4ad93c0b856f4c80f43a4ff7757b3870a1f01", + "privkey": "326117f45d2e6e19c287813c7ad10e40be40d543951c9e81ce2547cb887e1308" + }, + { + "msg": "1c354d127988b843c7e73cc55767272a6ef7290b13ae90651202ce51653a9aa3", + "sig": "304402202fc5e5f3ad67a0b9515b0afe1f8e76b829ec604f112045cb34ccda7876f9f1110220090627218a60f449af87bc4703d8bdf226e502775d17fcc05ca86bbf125650c901", + "privkey": "7bb2bb79141834fc87df40d66db087c943613dba9ae64f44a7b6a78f4116df72" + }, + { + "msg": "f30ebdd3c9d82677fa3a1666c091d9edfb9a73bc44ad4e9addd8de7fe9216459", + "sig": "30450221009f649d2f5269d5bcc3636c5f7115952b93babbbcd679a746b7fc62d5b7645e9f02200e6fb9d63f00275f9fcfc91441e0110482a6931ae683d5b92cce9663a50b0d0801", + "privkey": "39884d1f793eb949ce5bb07dd1b045e5024904eca7845f2e2a682b3bd716f1a2" + }, + { + "msg": "a1c2b956c8d16378f8b37ba5a6507a39b2019f4920cea9e0fd8954535017d929", + "sig": "3045022100bd7cfc17f329f5248a3e90884ff27f6e43989b6955f0235027d633c80d414cba02205f465341ce8cdac4b3dbd3aed639816a883b3e10ec0c42dd00dc8151a8afe6b401", + "privkey": "7a214c6fe5a723dfa73db996d223a67e58f83f3b835bd054798f993146d962c1" + }, + { + "msg": "2a0425e8362bde896e2ea09a1228359e62911e8f9710e80918f26a74f0952cf0", + "sig": "3045022100c1f17cbe35264ec7643ecb9e9d9eb96251ff7fc627d288c5b9987a0907de33f1022005484896418729326860e8abeacfc7ac511f163e766510ae8f17df56f188558f01", + "privkey": "529e409a353f09bfec047cad40cc74c8990680498f30269b2869b1804b47410c" + }, + { + "msg": "dfe1ae0090ae3a721a001dbd9b3e33ceb4028c887b74b2b9557609229184a8c4", + "sig": "3044022002bebdab55b43bbeefc4c92fbcb0c8ec6462a63f80cf6b5b5876e2e8ece24a2f022006e667ab416bfa3c9052565a5199f16f4c68e9aaac3e5b20d52ae1286e8eaf9c01", + "privkey": "d2fc0076b87e6aee2852492a7092a49f80dd7a4fba43b1119ca23a0d5833f870" + }, + { + "msg": "a0fd53dafe53e8362b26116ec475e58da990cf7cf68c81c5bb643dd98e3952fb", + "sig": "304402205419be40e45705c8ede1de4aa194bea70c5aec160aa3ff0b7b897135097437da022076729f0571e5ab1c2a76d0f768103be8fe8170863f7501ead08d50fc5037b26b01", + "privkey": "68260c731196804f1f6a5325c0ea3fa6d8fb1b9964db1ea41f3add37dc65d31f" + }, + { + "msg": "4d23d02f8c91ec285631c4084bb6dfff6d8a96bfaf78ec3fe5f48ef25b4e911e", + "sig": "304402201ad146e7601eae64f06689dc5bebe1e5fa8f4dc0aaa1dda72659153b1db8ff4102204b5b3f639231a567c5c839f6c804febf9558403f553b6d1acdbb602aa790a0e701", + "privkey": "f48fca4688a9d6f522f345cfda72b847191b77ec891d39f54c422c0b0a4ad528" + }, + { + "msg": "88155ae33b918ecf6bafc7f24fe75b9de573e0f7217fda91fd2ca6b33c51ba0b", + "sig": "304402206af1fadaddf318e5cc6ea0d630c966a9aa6fdcfa1c51188dd18a30a5ce9292d702207b53272a1a29f92d9cc335005a9777a6352de56541694b42c3610f8287f3688e01", + "privkey": "31b614dbfa8820d15fa932946142ccf1e1f12c9dd57a6a7547e4fd80c5e418c5" + }, + { + "msg": "4dd10d1a99d91fa5677cff8285b8f1bc8c3e3addf2c45c4d7d5b3c7ed75f6970", + "sig": "30450221008f930b4e25f295f3c5a5e87fc0ec52bb915b96dacf2130be56540a6683d7a17802207b9f7eb912a8eca3990a6e192f01c072287f63202fbfcae20688117d30cc1a8f01", + "privkey": "db8b58e63fffcac3fc0b3d475059738d4c4f549ff3f0944bb9695353efb799a1" + }, + { + "msg": "925a060c837b7732e411d6dd6323c09f8cb8b2245b7855a51ec095f17828b953", + "sig": "3045022100e687b57e41c22c2f78aa75f000410bfddc852469e32352ce5e759a81c14ebe1702204ec924703af322348b6f06fd62567e689258988fe9928ab8987bbf25ca8a164301", + "privkey": "08c1df65ddd6405219f2f20c905693477db910566cbefc1fa3a53593c14d1157" + }, + { + "msg": "16ba4a02006bd0764558a71601542408b5dce7839ed91fdd3c229d37afd5cb66", + "sig": "3045022100eeec3696d777d1d4cc658ef6236356e32a92cee32670dc566e5ee28df32fe6f80220558c75df7de5286d89f7cdc7de3e1cb8d45aa73f34a05dad48e1079685813f2001", + "privkey": "c82829e681cc6cb6f124b4e1a0ea68d9aa063cf64be68fc95f6ccd095d97cf90" + }, + { + "msg": "977394a3cdb1151a226393902e9d4003b63678bbb4f67d784e45cbff6831a611", + "sig": "3045022100d3023f62a83b3d7a252fc7ddeb3072f1dd9c25b0c604f1c68959f22b9266186f02207e4d9f9ab40bd5fde9e55b42b05324d54bfd307a1b5c32a35ae2ce26ca8f1ce401", + "privkey": "1aabaf1d2953ff2b5f3f5ce80d5ab318bc31cad97eeb0fbf0f126b21b720c4ec" + }, + { + "msg": "0c9ebc4b38648729b33d86a001308c099b020e2cd2c989b82e9316b3687bc7fd", + "sig": "304402203aad8e8efb5721c59456d5121512db983672342dd2945f5666389f4fcef37d61022005b9a0d5ffd84e309ac186e91ceab6c5ffdb10afbde94f2c7fa904a1c241204901", + "privkey": "60f77c61b9fc3e3fdfbafb33a97d5da172c7223db73a22e8c19d8aee8e6bf8dd" + }, + { + "msg": "e672cf35e5b186843607321255beb9a626e9b16582ffe6cba68251cfa2d26edd", + "sig": "304402207a84304fa4d6521840d6e1b9582602a12574c1dba1f7088221748d08839e6101022076773ff0fb876865a3318f939e0f07721034e6886f6a9afe9546543df238e59401", + "privkey": "64e7e1a9a9e7b80cb5dae781b9d4060ac5129a2bb254076bc1bf1b3b5d0b6068" + }, + { + "msg": "7972a81012267be81555aac3644b4d6aac91061717ed33552b3cbbb5825151e4", + "sig": "3045022100f67a1f761743f7abf3ba3e0f0097e93579fc45cdba95476e5f5b9d8155ebc11402202984c12e518f5a6b3c59e248bf2660f9b4313f6030cf0b10a5bde3125aca1aad01", + "privkey": "af681d1431a137ca2ccfd9456123af2a50901826985d1b4d46ac33fe7439a0d6" + }, + { + "msg": "2884813390313e6832156ad2b86050d04dbfc3a810c1adb38a4ccfc3031db3e9", + "sig": "30450221009fc5d7058df747afb88e47dd18a9d8d73949b1af1ac4a7f4324b7e6d5ff499b602206e353305f58dfb727062bafd7dfe797c9f3071230f3780f5875d331bd99fbe5301", + "privkey": "c6904733a3f4a8786d04eb725ac9d7814ec66c667ee7f56d7e9c1d39c7a06d0a" + }, + { + "msg": "17dd2ab0e67376ec6dcfccaed832a5a154710034818a78498b2987a9453d61a5", + "sig": "3045022100d2c15d6611c1bde642076edf2b89fa2e1062c99c866935d4b7eca979a0ea63a10220624da519888c2316c265b554281b327d471eb98cf8c3fb72a7c7dbee22553edc01", + "privkey": "493a3a53de5389e4c75092732be209d2716b93795add2210f6a1460c92fe238f" + }, + { + "msg": "bf0bfaa0d35c77239d0c98b900c5dc4efdca9a83eaa0c1fc4c7a8d50c5136384", + "sig": "3044022032f05dc70f0b08a5b586732f377928ae5edaddf7d58d3cabf0e262d768db47cf02206faf588d1682554be1773f11fc52b0101aaad3acdedddce71ceb2cd4d0ed46f801", + "privkey": "649306bf872e1626d8531d655a38e4ed7c455af514dc9a90dd1bc434223a2e70" + }, + { + "msg": "eb73158bcf51a0d754baaf7e68c20c385185644acb701cf9d54fd41487f83709", + "sig": "3045022100beec086b0341c966e02014c7576b7d0d0ae007489bdece567fa64de4b810a09902203cbc3e4f414f766c4653c53f52272ab2b87c88d49d8be05bd2810bf67f4a9f0f01", + "privkey": "0548d0c8d32215ac696d32ae2f28c785d86d68433345fd83e6101b7bdbcd7199" + }, + { + "msg": "8558f4a0e8714bb6fe788d0a0c1e4bf5622eee75a163118dd312dd0bf12bc971", + "sig": "304502210093fb277d192b50368665826384f3f537917b4ab9122e3826c9039e45871fab380220745c193fa30a2a582be64072a71f8476e1ec8ee374424689f0656b24fb1c002d01", + "privkey": "303b41d5ba88eab108428fcdacb335ea96929989372069ae4cd27e692ffb3955" + }, + { + "msg": "727402055ed837f5eaf1329ac81082929aee22abe2246c6dcb483ee8c31b580a", + "sig": "30440220181a4a02b57b3223808dbde211d2cbb502f9d66c31975ae9f94cea26dddd5e0602204e6eaa65344ea524c207429647a27fefb4eb967c403c080e46877f61d22eb4fc01", + "privkey": "982e3cae8f991b965b44541243e2e9de3122c8faf05718ea7fa5ef00b4098220" + }, + { + "msg": "29a4c44b4d8a4d920fec9368f0dfbde2d245eeab89016af90e7019ef6e020f91", + "sig": "3044022038abb43bc0a1f4bda4d0dcd9e13d983a24e8c4b299dca06d61459fc56edb39fb02207fd3e3e37fa318907ee41222ec7cd4ce085b35c64dec1d8c6b61424306db50e601", + "privkey": "fc85d8dd8c854dbf7ec922c6124c654aebb61263b0572ff922beb523a743654f" + }, + { + "msg": "6f15ebe59374436bd4d6b1ddc7a2a016679ad682f077a8a096db0ac0f159229e", + "sig": "3045022100aa23abb03621b4834084e2372c8aa571b125a4e897d7a8b3690890e7cae22f08022021a5534de67162c0e02f946f49f72ea1cabc753395b80cbbfec117c2b253cc0f01", + "privkey": "c5fbec66e7c8b8d37e3dc65aec3ce66321983e87a7f48022e6d67f23f133fd2b" + }, + { + "msg": "08854040e59a06d8fa647274c751e69980a53045c7e10dac29e6c1f2293f8096", + "sig": "3045022100fafb9d2b34ae5a6efba997e865c34ac02402bdd1eacd4e51859e35ed16f73832022044bf0182a26d15905c672bae0dfb1ddd21264795947b591507f786541faba37301", + "privkey": "86410a06d6053dd75d05ab51e3a08608018bb7d70c14199ecbf0167cf459a3cc" + }, + { + "msg": "66d7220679b951b05ceee33cafa9490de8669514790200a5f86409722ffd5a23", + "sig": "3045022100cfad593cc4e424ccc6b7dcc8d576eb34d837ff3862ced984e61b3c0031ea11b4022034dc395f1c4cf1d84f074f45e4583ad15b94ebd74366f138d6b3228ac1cc1a2901", + "privkey": "148d9ae3cd5de9347b0ef76dd49022c416e05cab1d3e9f91de0bffabd9728aa3" + }, + { + "msg": "16d5f60b487f97c54ca4931e1804ff5e7f31b1a7546b90a515780b6aae66c67b", + "sig": "304402202eaa80e56f1fb665cb995bf12e4ad161e275c59a13930a78a1c8b990b2b40711022070603f86e4dbc0b89bfe02aa941bc901a2de2d880253f641c83c0fd6e59988ea01", + "privkey": "00b1ecd81941a3fbafe4420b9e96c36f40cb5067ccd0c128dcb40c4f9d508e68" + }, + { + "msg": "efe532630bef5f6404ab56e4329a08800c5f2ee06743b58c5c69bf47e78b27a5", + "sig": "30450221008807c44f387e9db50eea548928c1129308cf501f3bbe3fc1a4cabc400e57ab64022062d127baac5aa2d1fe08404b7ce8984a8fe1bdef50efd41eac5ee28b3d0e5ff301", + "privkey": "fb2ea5631dbc89764f2f592de36f1e087a313461f44c70fb231b0ef7928143c1" + }, + { + "msg": "02ceba7e7cfa451c9af56995c36f77252b3ec4a653bbc41ac94ea2751e8e7a07", + "sig": "304402200b028943013a0fdd47eba1117b89bc838a269ab923ffb3adfb125fdf971c7737022050f416497dc081b223c50349fea72a4cfec33c588474ea6dc9d7823076de856901", + "privkey": "35ce2144bc6e3fd449cc57516f2e0f63a9878a592ed1b6cce21c3de85d98d3af" + }, + { + "msg": "038decd7e8ecf898206c4af77fc8a6342d5b543a60f121685715319a6de3d947", + "sig": "304402201046959b39f101a68880dcd8958cd2908d228bc115988691d1cbc3f7bf5090a702202c2a82c6504ac301223d33608f7e9314825355b4e0907e6d7a965d8a90b14f5c01", + "privkey": "d71f018138fa752798132a8393f81f32c474bd60cecda2aa1baeb5a4b00e86e4" + }, + { + "msg": "517398a39596dea9899429688afe62c49ff2265f177b32ea452fab47ac57928e", + "sig": "3044022041c9adb3aa577da5f6a4ec2237bc7ade73b0f1db122a4ed3152fd53723067dd10220774bf2d35837da21ad6073330449ef969b3f28d1cf3ed8e35664e75d0257855b01", + "privkey": "533b93438ceccd4d12401e86b6810200211dcdeefec130410fd978814a8e76ac" + }, + { + "msg": "eb18410a53353de79a56a86f95ed94f7cc8efb20c7662f6acd1d0cb46dec6729", + "sig": "304402203beff0b6d06157a96bd48d84b70b2c8d5e4be1e60c358db05bab94c7b03685fb02205a3c07368ad539c0edff9ffc35f4d44cab663fbb3cb03f8e466929b5c9efd5c401", + "privkey": "5d006eeabbadd13ffcac0944381081f6f0dd7b3564fa5e9e0c0b2c8418d1ca52" + }, + { + "msg": "c886d6c0d4e62bd8c65c1d3319b32a82dcc4fb5780896c287f3bfac415a768ed", + "sig": "3044022068541ffbaab7dfdc9dd0d2a8e0d8d3a7e1acae685bceac3f1a0a19b61bdb9bf20220544d24dad0c195d0d57d2dbac2d22b328eb118da055f0e741b4346931a66de1c01", + "privkey": "c0d837694699e53ed82bf4fedfe7e8cf70dc02dbcad884c3b822b3ef6ddc5a19" + }, + { + "msg": "826f8c6ab61ed5928a1d6d9cd6d69082ec9f9e45c4e73f2121bf061ca1d88a87", + "sig": "3045022100e1d3b83cf96d324fb9bf2ac9096c70758799bf330104edcbf6c16ba672e8968d02206a9a826cf32840db5f2ddf887e819eb8f41d14e8be2e94aa0e7f064d96a4d21701", + "privkey": "3f523b03447d06bfa64fc78db115c7e5828739f38ba39663095309ae6051c092" + }, + { + "msg": "da71ad31c818c04ce018937d33a2428be6d4199af6e24f01b83f26212e3cb186", + "sig": "3044022000a0856075820b29361a83fb332c190048ad5f4a81f9b993765e5c76b6de462302200433ebf787caebb7d549fc93636e7e79b347a97fd56826f75adb69af01654efe01", + "privkey": "c235de75530efcf7c8bf08e3b3b69c13d5a306364172617f61c20d2d02d4b768" + }, + { + "msg": "de8f24d7924433bb1868e48a92aa49020c464140c1c69d36f8268b38798b1171", + "sig": "3045022100fbcc16baf92400595a5df863680c76388ff152e4e6ca3efb26d041f6126b51db022069c8c6ba124306ac55a3f5b5b95988fc45166aced7265d9fffdec77113cf572201", + "privkey": "231913cd2deed9aa0483d2a23719d9dc7a9354518ce5eea88f4d2de219891888" + }, + { + "msg": "3f87a3f5199d729f5683d90b1977e1403afab944a8a107abe8aeab1c2bbc4710", + "sig": "304402200e816369dc132b4331713cc481258cbe9a4526a767aeed50dd9c00d3cd5f321602200a592715b5538fe25713a179334dc0e275b710f74f16e11c599b79d397f1d01e01", + "privkey": "5b274ede3c80bcee4d5c1cf02f5b9f76adb95667a0369418370aa81530c28064" + }, + { + "msg": "12bb4666adc2cede1826b582085caed6fb64438a5462f74010eea04b4f2fb5fb", + "sig": "30450221008258561ae898aa8e689b6a8a878704020d1025d7ae835985eaac27a555996627022006bdaec9eaea8dc2eefcd197e1a10828e26f9114c394d1f2d161cfcc7e3d1cd401", + "privkey": "4fc83d5a7155af0810791a6db59e307295f0897682a1a6b958fd699f3ef9cb53" + }, + { + "msg": "1ac99db7e7ef6ee4af2016dd0c71b2a6d2995c1a2029c13959ddea0a4af1e839", + "sig": "30440220110722d507a7da0dc7a520ce7efe0e1fb42790c00ee8d565e0c7c8c4292cbcea0220267ae97ea9a2b1ef08239553cef818f625ed1e3e87ef766e8b6063f21a9daf9901", + "privkey": "f1fcd239bad3e3ef1483e10f3753283b88df5b8603f9bab20d66e00e81cef441" + }, + { + "msg": "b5d1b36d95311a86e450f507a0dea0805eaefb68c89f222d8a3d4761e5619b9d", + "sig": "3044022053a91f2c657aed63d265aa6249b3c0db90844210d828980b6ddf885b4e98b0840220325ba1809427c15527da92751f4fbe6e56230533ad7afccd91a244ff7c25ecb901", + "privkey": "ae74253a454718f1c08b4bc3b9673e2b084b1cd0b4c3bbc324da8329d5bced7c" + }, + { + "msg": "fb710ffc70334a93c4052be5e229875f55aa3f3067e6c493d53f115e39ba387a", + "sig": "304502210099b8afbdecefb8e2b5a3e2acdb7fb28da7d7f24c6ed6eb88e43c423b67954ebf022050050a3e69fbdfa91fa16d9ad669720c65b2fabe7e011ecab9336e92d18be19401", + "privkey": "40e53957385d806936ffba31637c7bcbc353616d896b6cda0e20d56e16baabaa" + }, + { + "msg": "b27b1bbaf48f7d3c38ce061542054b2fb19cce7a40a779253ece58169caa6efb", + "sig": "30450221009fccc94220c12ef508bfbe9a0d1a592875272e4213bdb78baa290d5a01054b1a02204101988977098b835b4b1ceff4e6af9925a92b7f879718a60340083679bbb63401", + "privkey": "67ff6b87850ab1fc1f8f7c0ddc606610fedd027e926fbed60f280abd2972a440" + }, + { + "msg": "433955be0f609ee6685e49229652c1311b6141f4b85a4f018e2f9b2ac49a4b54", + "sig": "304402202ccf1a22e503d11bad88fb90da3a5b3547397de3f125b86334ead3497fcd1ca302207065bdd03323de65b784384926fb26f637094774837b8368f59a191cf50b6f9101", + "privkey": "7befa50da27667bc46825f960dec45e461940da6a2267d8da6ce4b04bf4888c4" + }, + { + "msg": "577650e585efa3b2c2aa04f5c561306524178f0643c9208ec7034923a6949492", + "sig": "30450221008b75fcfac6277b160a555bbbb2dcb477c2509967946edfb0106f8d142fa1543c0220442de5a96c7e8f670398164a4380b97f8066304c62f5f7fef8b86e05941e94d301", + "privkey": "8cfe9d574c49070e41fc473352c4029547bba2299a2a61770fedb87d32de2edd" + }, + { + "msg": "a2fc75d302dadb11fc4bce7bafeb90406b9a367e368d0cb7a885e0b96d695fa6", + "sig": "30450221009ab4c7be6d7bebfbb9bdafa5abd628c8874b56872d77d04171209e692128b7ba022048f66d9206126e8a2a48a8cacb44dc7a6a660d04998b3f6d07d88481e4f230b301", + "privkey": "a1ab7d29541f45b96ccb881522f7dd45901765ac1160ebbabc50e2947b8853ef" + }, + { + "msg": "9acd7c5fedc120d69095eb7ce77684aae974cf7b48cfc806baece4dd812d560a", + "sig": "3045022100e8fb2e2817d7f481e059ccee51e8449f550f1488634ac012811aece52e5e23d502203d8d06125a037af6e32c0a0cd7032dbc12c7ca84f5d3952afb58857685c68fad01", + "privkey": "e2bccf63d91102cd90d519b18463ecdf41378c9f9b0e1d5a1ed58a837be11afa" + }, + { + "msg": "23ecbc3ed373e19e84d3d8e6420613d6e759b3e5d606e6ba9700234beaa7ae8d", + "sig": "3044022013b03a0a39a56f9241b21ee74acf83d93b5b6a81bcf254f7558648e686fc300e022065a5999b37b2fd7294f853b5c312cbf62c12a1db58f0d95041e7b7dd157cce7f01", + "privkey": "61dc64fb0590b9615bd5f381b0a301316857dd32f0b214c2e814f35e5fe956f2" + }, + { + "msg": "aea4c1124be7afa6c25af9f542b79997370ff6cd870e185a20c7be78c9c24b84", + "sig": "3045022100adac0cbb49e7d492b9f877a5f842860e0cbbce99fb4592558dee58f8e3cc44a4022074caa58d7edf18f2ed5c436930a5b8dd3a48a6024aa2c038b2f56c1187ab28b201", + "privkey": "0b31aea8e0af9a18f02e9cd4e06530e9ec9c4cab1e6a3bd52fdca5183617fc66" + }, + { + "msg": "f793e37b0dc14ffae24d6e5a3477aaf23cc3bba1ca939ccd0c4a4bc3f1943e68", + "sig": "304402200096eeb7256f916c256ae3884df5416bfac00143bcfb46a501cc0eee45ee432602200ba266f42639556de6a0f7c9f0bc2ee14d80cc035965c7fad9bd57c04826684601", + "privkey": "3fe28fcb15f00c93c5d628b4ae942beb2fb73e5f2c28f05dfd98998824c115c9" + }, + { + "msg": "85e774c6574ca6ae8f30af0ba108bbc487835a6ab9b9d7e00d17f1e710b3bde1", + "sig": "3045022100bb5a0cfb531d6bf5b817c1f6a17db02e7b592111a2a572a6ee4bf31e9de1ccd402202afab1acafd825fb8a51d801cb20564c67fa42a33afa5d7526f0d9f57535b5da01", + "privkey": "3902f9706403006e3fbc35d12b06fc78670509aa50b552544d708ab5dc5f5b79" + }, + { + "msg": "671db247e553158156e7c670136e9fd299a9d1fc33c394ee593752b0a1cc1f03", + "sig": "304402206d9f228584074ab3eafccffe240a1fbb448e8125836461da7423def37313aa1802205d076e6066caab751e95323be6761215e2fc5b69f2c9392b571e66a8f12ce2cb01", + "privkey": "2bc7499a1da26f836ed93be9834f9cfef1120e9512f3bc90c6d57c3ce366c58d" + }, + { + "msg": "d069674f48c2038bbb0dadcb18a9b18795eb71cc16cb34e91b4cbca86aec61d2", + "sig": "3045022100d1260fdebd27eed66d577b77a1f299cc42ac137227aa7730e2035077b71161da02204a8522a2f2eb8cc78e7ea11ad56f463f7d8f08c6e4e9a7db0cec3ad6779ff38d01", + "privkey": "0fc62bc36495f5f9b0d23145d0ee1983c29f822c7c60e22239959c13acec8da9" + }, + { + "msg": "e9a4c69779c44c72858e580297452f3ed488849a0cf204d75de42e591844f008", + "sig": "3045022100849c6e7b4d66ac63540a2784620c43ac1b558f7a1432237965949be3624af1d10220674f15f7779e4f0677f4c4cf66f85a5f7ca6a8b966f17c05ac1dd38e4664f0d801", + "privkey": "1229ade4f8919c0e61a5221c6cc37e7a2da7dcfc22eaf6d58b8e995d38a830c0" + }, + { + "msg": "183b7c41bb27526528ba72277a92ff7d39241539f6616f024e4389e76696db65", + "sig": "3045022100ba80e06c2a798b0dc206eb44b99703a31536ccc930eb0500a043f241aec21d1702200ce955a1627c9a073f8f49072afd5aa289e544bc90e5983b093c06c5c67e51a201", + "privkey": "08ece7a61cc766fee43dce42570d418d7509d63cc293a15e2292bef473e5c49a" + }, + { + "msg": "a119183cedb4d2385c204482f6f6aad863947f4dc336810a2cd494d73a7d63e6", + "sig": "3045022100bd8fe449160c9ec8029bb6f554f225fa985db131e2a7f56cda04d48d7133f8c10220060a2e95874ffddee22ce3e1a47bf15360922c73fb7c2a9ffe267447bbbd24d301", + "privkey": "70ea418b73f35a4d5fa241c97661be9e88d4e773478fc9ad59602e0d18d7499b" + }, + { + "msg": "e53804e86688bfb3e2d7a72d9a07d0b76c8d043472a70375f04bc26e5175d380", + "sig": "3044022076927896b56154a8eec11decbe729aa0a2543979cb388ceee3570b35e511e1ff0220730367ce5443b1522e61d990b5f0fe7c3e14d62fecf141d189da51285a65938e01", + "privkey": "4d2da431e0b77707cf388cc9d44f067d3fb82a3e544a436c2c94abfd0bd1a8eb" + }, + { + "msg": "c0a7969a9bd9689925f5b801e3937772fce4c20a238acf58e6baa4ddd68f958e", + "sig": "30440220700c5e64229194d774b222c34e436673c69aa8f1c9a18865895c05fccd02bf3a0220384b60122c477d03bc5ecfae58908dfc195dd94f09ae7e2eb18e14d54314982901", + "privkey": "7253a7b52381bce33b2502dd8026c6c242f66d75dbef89c6e47e8983a373941e" + }, + { + "msg": "d767a2f44d17543cb9833a4f5aa792f12064bcb1b7882feedd014cb9aeaba94c", + "sig": "3045022100d6cbcf05a476a5e05b92acf93c2ed41c97af666fc03f9a45e797af67f6f6eaf302202485861dd6538828e6ef0fe17f4e41c25053f4a2f28b017e181c0f364b44867f01", + "privkey": "90fb55d3cd1b87138b2983581f5f65efd7dc444c06438f492621e99311840805" + }, + { + "msg": "47d6db6d8e2a9aae31ade5ae2b1ae6ed17c25b7b74be25fbb6ea590046cf2d97", + "sig": "3044022054d1dd346604659a1ae9905a375f52124c066938bdd19200aa6598fe3cda676202202671b0da6d94702269909d2481347c328d789d052ff43b6821686fa65117627101", + "privkey": "cdcf9bb9730b0b88669876bc0692393a5dabaa75f78ca68372098a81374dee4e" + }, + { + "msg": "3e54cafef15a961d757071aee94b690280aad8201af7582aa81fafb64b748527", + "sig": "304402200e9cd509a33cc07318ac8e8c61c6e35a4825361bf255b47996ac6a6c45252a98022071092aee8df6e329303b5483c2e8fc6292b51df5e48ebef6548f043c11ad298201", + "privkey": "3b829c6c16002db5ba3323ac1ca6745cd1f6d1f67eb9c8f5f11b48df08d3fa75" + }, + { + "msg": "ff7482425542c945e2b83931583e2f516682dd24b2bac632ce00b0bf95f3764a", + "sig": "304402204ac0df04b0b83d87938c7d9591095acdcb6e0c6edaeb99a0ea40a1951f77470102201a40cd8a308c3a752cfb8c3741d6fec79b410a6bc4a3c64c4c377ea5b9bfddbe01", + "privkey": "1c1d11ed9739251c3e065db25ac10fd6413a54d8f9e553be3c67f4eb86bbdb08" + }, + { + "msg": "b5c1a15c2c219f845d5253f64947d49f94519a57aa484b6005517e0a8f933a58", + "sig": "3045022100c17a386ef1263ce5c1816e17504ccf8cbcdaf5f09ba00f141099c990ce2cc30002201d64bf0ad8da6d1b3fa02191145c19a94fb1011865933c2a8aaec4728fa5e06501", + "privkey": "df972b1d45f99fd4dbcb5e7354d6292b56b4a0f25f6432f9e1d7a318eecb2120" + }, + { + "msg": "bd15aac26ece339e81248e364dcc66dcbaadd6bac3f94047d161f0e71f44758c", + "sig": "30440220035a4c4bb28381cb4ed4f6f5714cb34bab019339299212fcc86ae90f83725ea502204f63ae13c03dc80e320b4274f9bd403f4966fde7c89c21328264a6305ee8c42701", + "privkey": "66eaec33187b285b3c604973aaacab3ae8d383a075dfd996c4cc3ac4cd16d0fd" + }, + { + "msg": "85fc198a649ee134d2116a2e0b189faf1a4d15c3afbb182033032f9ac322e1bd", + "sig": "3045022100aa128e5391a4bec3181d4a2127bd45fb38bc142993c967af8d7de33e90e3c03e022034eef4d80133c0002671027d55a1e10eaa12cd7e24f62d037077d1dd0e90775801", + "privkey": "5a6331ff79f2930f95df8a4747cdf56b0475eeae453af8da9257982f68fe66f4" + }, + { + "msg": "8b66547716aee0c7bcce9bc93051bd2babbcefaaed51a94605506f6ab6492744", + "sig": "3045022100bdadcc9d82b6bce0d4c453001e06481ba48b92ea2a8ed81b9186ceb247a6bb1e022005b30ff42cb05defa923dc09a9727eea67cbbaf1b5e5b561ea4d854b67e8208001", + "privkey": "994f92698ef865cc782429f13e47040ece8b95a2d6dc30fd8c6a9188db655668" + }, + { + "msg": "54fe0067ad6b4c7f4314802d40ab01896237015a466439d35ae986d81a6e5dbf", + "sig": "304402207b4f7d5bd15f520aabc9dabda7b8e73c660f10cca48c7e8ab5533a69137badc702204cd239932e7932d768bd65b55da2147ac527e61567f45bb14a953b0338211d3701", + "privkey": "21664545b76332766ec8959baedc77b7c842379d4400ce7f5f30417e5a1760e1" + }, + { + "msg": "438bafa3b8dbead8b2eeac1b9fd6527b9510d56d5bc09a2ae2a51b67e64ea8a9", + "sig": "30450221009d3fa71c0f2f7b015fff020316c81476930ed4d4f6fe3fdc348cae358d70206302202e2e69d6ca559ee5eea309b72c13bf22c643660e27cc7799d99bc807a28fbe3401", + "privkey": "dba5a15df6ef7b060b7e9a4ee819a431bd2c3146430e5d90b15c8c7f34fc5d9f" + }, + { + "msg": "2560b38e6a265f413fd762a88c4c492d46270b007e18cc844feb1dc06429a110", + "sig": "30440220361980512e7d7f6da1b37fb225c336801092d9ed05cf72b7723d7205de1b2baa02205cdf459c367d1f274b8067d008c533385c6c4330fd6376c4d330005312d4834b01", + "privkey": "d384f6a007cadf556e10c1bb59eb3e1e7dd940aae5ec6d305b64a6dbbe73ff64" + }, + { + "msg": "bf1d36ee509e65f04d78be03e0fa0d5ceae29397a33d9efad93332cb47ba0155", + "sig": "3045022100be83c754e4426893b698437baef7fe19329df4cd46f5cf05b33ed1090be2b4fa02202986f9ceb8ea9d2a30876b2a3e4c2e495d9716cc21c66018c91726955ea4b82201", + "privkey": "7f2776e0a044ff802d622f6ac37637ae83b4e2d83dbbf83c681f3606c46a7a8e" + }, + { + "msg": "fb75554ed0cc5485d2559c29f6510b6ef60dacb5f17d81564d25a87e79ac826e", + "sig": "304402207ab3815aae1a99a367489a31bf7a94661ee086e887641ebac2e455871994db3702204f6f8b7677bb30efb5c4602169f2541274b8f51e1938a2f84a828b9da0137ee201", + "privkey": "4c6f22a57c553e05a0a7a35adf46e1cb4c4d004a984e5af88bb2d2188ba7ab88" + }, + { + "msg": "6d18019673f7a984b0a222b7299b35e61622681cad5161748040c5375c951505", + "sig": "3044022034375c54a58c8562889c8e41f9c299f7f85704459f8d11f317dd52038ce7e29d02203e8a421f7f4d9d3dbad692dc81ce89ae060b734a01e9c048526ffb0eee908edb01", + "privkey": "0c0b3289b2c6fc93986902fab4457f42d22b0b461c5e5a53a5ec727051586fa7" + }, + { + "msg": "7e1e2a99702c16bed9055d399af557c2a63da0a5083292adeb3897af6b802564", + "sig": "3045022100c85073b669d65fa197cb73e4fc7a90c65b11cf195b8d718e998b22be071bd3c302207e7def3e6fe2e72aec48855ce06f9425c712fb874baa94484be96f9b9f472d3101", + "privkey": "4d1c0ae59c723633336f8dcc6bde9b358f9b75603e45168e406807212252b112" + }, + { + "msg": "a33b04ec26e5cbc6558c43abeed149ae35391bcccef36b684d345ff13496ca9b", + "sig": "3045022100f09854120ef83925f0a865aed3b256c5f0efef8f35f2bcf91384545d4ab6315e02205e9ff2aba9fe7270a73281f176d4675163a26b52e71819ac18f001cd5c93225401", + "privkey": "b2eef7149d49c01b51ee926e926e2e26f73c25bbf74371c990617eef8b50ecfe" + }, + { + "msg": "fa01a5e23fa06dd60c852d89edeb778f94ce904182a8b9d8d1ca9fa6634f9662", + "sig": "304402204cd469bb017d011fabb29cceb655148b0462c666dd940a2442176f4a02f5b02702207c64b92d62db3390facc8db753acbd05a9a1b63896ff4b27dbe47f256502bfad01", + "privkey": "7a572657b960cbeef0c13fd97855fd61147e896093d160fdb1191fe2849bf8e1" + }, + { + "msg": "14a93f4e03431f95877f6dc9f41f59bc967686bc8401ae10527483e1cdcfebf0", + "sig": "3045022100e502a90aa2908a903d45d5077d718812fd7341b551519f96fa7af94e66f39ef902201bef0e0b54a6fbc28e011d8ecefd0f33f6c7283cc2227ae7ae847b12b873e01001", + "privkey": "d661888426cf1c65cba5f96ec26314b55f421cd447ced732809e32220f28c246" + }, + { + "msg": "b6ec194ce825fc8ebee69325abfa2acb952d45495ab351455122d4901d4f64bb", + "sig": "304402205edce4c4ab58df2c58efeef2f12975a3a3ad38d6da9d4f18d76b086d5fc4d69d022074844f4cb83eae0aa01f409fbb27bccc32b44b0bca12b614831cfaea711edd9f01", + "privkey": "27cdce4f1e5ffa46fbbfe615bc1d1599db6869404714f0cc46300901b767383a" + }, + { + "msg": "9b86e708eb9574a8c3ded5d3932a6de43ae4982dcbdca3269af14e967c639b7f", + "sig": "30440220557f81e8f67ed361d2289aec02013731f75248350ecec55ac8dfb14418da68c202202598c35a9c0e062bc48d1ccb9494630a5daae324595be83c473fbaf2b3e7bdd201", + "privkey": "8d6fa8a66bc4684b35d9eb3226d352c7300452dd9b3d548d7a2d947459bc5a0f" + }, + { + "msg": "170a2926a37a37dbe2e406e2d5a3923973bae98e82ab55e26864a1dcf1785da1", + "sig": "3045022100fc7726b77976e58fd1f8a48c9232747edc40359eb1f6b393540608de72e2b4b5022005c22000c2c1c719c6f4f43ba81ba6ed4643a8645a34ec50619a4945849bd50a01", + "privkey": "339699b552be47df9e751d7bbd53fa052d953738d851741e656bba90de4a53b5" + }, + { + "msg": "90de053a840eb83db882433d067a2451bfc81f58db606d4e411a6b8fd82c953a", + "sig": "3045022100ae765968d1add07797a390b99ddb7faad3451e922a2e8f38adee56c654a3274902201f992b0f3605c54a6ae60d8604c7260b9a4db93be2512ad64937be4f0b3ad55501", + "privkey": "8b0377d0b6e665a0c6072009b50044c6891cec9316bfac8fc9b6a5a34e4eabb1" + }, + { + "msg": "38d14fe6dd85bdb8748f5cb23e1fb2e58df2e8bf841f1e1c68bd4d20dc94cfc5", + "sig": "30450221008e35cd8d034a2fc8234d9638f5baa6fe2bd96570df09b7b15ccd007f5c05a93e022041ae8101af3721d99668f87a743bad7bebfa3e0fbfb1a84d6e0f2e73e1d053a601", + "privkey": "6e99a1772417ee7deb91d0e3762d2642d7278f2f7431a4f659da50d43af879b8" + }, + { + "msg": "00ed851dc354b6df5a3e3bab543ee399b02f3600c595aa3e5ca6a1483465b155", + "sig": "3045022100deeb14e5b93035c0658f20aa05b87cb132609978bfa5ec008a3ca9502961244102205d0aef23a1f146edcda709137a5c5a376bb0f75abd031ba12128c4578ab93d8401", + "privkey": "56580e7ac5163e4e6579ab0e772f2a3848a80af534588e70f230a18158953803" + }, + { + "msg": "4d46ab40f75ab52c1fd8a7974e06ff684c01bf3e746977e811a1a442bf2912fd", + "sig": "3044022062aa9538861667b57609b750f7260df90373c01523b1341b7f841f3f53dafa0202200a3296141e23dec3292ae553a28c001efcdef18eca50ef09a0b4795b6d4b61b201", + "privkey": "3d38b60f2a844fb43dea0262aac32301ead58568074ca9915c6498155cb47f13" + }, + { + "msg": "9dd253813dd4ea8902617a09cfdcf060dd062849b76f3c1e556500d13d77170f", + "sig": "3045022100dccc41da7fdac14735bb98a2c59aae91d26284244f4cbc0beedb7851172a444a02206c5ac076f60a7f5243ef4e1630c51f411a03cdf57049d4b92a055ff90160ce8101", + "privkey": "80ac1ff9b4219c1aea37fa937299dfa946e48d23c858d463eafa1bd8d986d9e8" + }, + { + "msg": "c783fcc63873a8b39563fd0d39ded5453d9c7737e32a0946bc62ce4f6a4773bc", + "sig": "3045022100d782b60cca256c5b7918a62c3f6759588ed8bc3bad7b704dd8de3fe6e720a36802206d163563810897555f0a90c969d476f6c0452cc7a3027ab4ceaf8aafce1cb5a501", + "privkey": "527f7b367a63d977568bd25593860ae36bc3ba4b65f43b121f1af776c995d224" + }, + { + "msg": "4519ef8ca61e50159ee43d921a679ca78ab70784167ebe656112c2017f1e2a8e", + "sig": "3045022100dd23d3fee7e8fc4bac8c82378aee98fd7d3efa9140f18c1949b83f13654a7f8202201aabe8f08204e36e414d2a1cc42d76a7ba46bc3e4907dddae30944f14107b17a01", + "privkey": "e13d124062e954974d5e5918bf1409a7a5d1d20ad4f4217006e2d5612a255da3" + }, + { + "msg": "a3a2d23da4f1a6c4031879ba02e11952bd05209863f90907606615edc6dce871", + "sig": "3045022100983403797071adeae9cf36b3541580903d4fe20bbc29d7c85da3d5e86ebfbac202202b1cbdefdfa24e3c53b066b5bba6491f4117ab2b378c04552a4c1297d397670a01", + "privkey": "1f25a034e5df2a5ae72f91d63be72497c3a1df79300ce195ee73e4cfeeda37a6" + }, + { + "msg": "0b34440573c1ea6ea03e4b5500307948698f229b5a1315f81a5c622fdf660ddf", + "sig": "3045022100a5a3d9ffc866c3a747ad2a015f306d8a6c98540c7622f2bd0409e7a909924a7402201d0606df93f8f423fc5559623a829ae81632c6205af9a69e86965b1aafe9f7f101", + "privkey": "2dfc3b63b1c8f04643f550281371a846a1d31fa651fb861f7e6ae9db88122041" + }, + { + "msg": "88dde95676e529d3bd4422e8a50d106a4bbd465e8623b37d09228ff66c9dd275", + "sig": "30440220411a5d0613c89a246f08d1cf3db4b5d94b3c7de70a2464a0c6b930bd179db38702206253cc6df480e8fcc3716aa2e2c3cd10990fa73c6f2347e8aa7a499528ef2dec01", + "privkey": "d2f35b4f8e5f9db7e884ab4bcb64f84dc4d87a8d4b9055995df62b759e7ba4aa" + }, + { + "msg": "3617b55b4aed94d2fddf0f96f9c8c9f4359f948a2b902b76f0d9474a9e549710", + "sig": "3045022100ae0f294fbc13c28259019d000a9a9817173febfb37fb0a273511add46af4226102202d2a8b8b0e058179a0baf190dc76f649075a8d3ab3832d3be859159cbe1a9ae701", + "privkey": "66dc7feb6d7c733005c5d8716b22f38819738bd1538a40f46205f3f3be191efc" + } + ] +} \ No newline at end of file diff --git a/test/test_bip32.py b/test/test_bip32.py index 2cbecf6c..fff9a922 100644 --- a/test/test_bip32.py +++ b/test/test_bip32.py @@ -69,3 +69,33 @@ def test_bip32_vector(vector): print vector['keys'][i][1] assert pub == vector['keys'][i][ 1], 'failed: child pub key, should be: ' + vector['keys'][i][1] + +def test_invalid_bip32_key(): + #keys with invalid checksum + bad_keys = ( + 'xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8BrrngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7', + 'xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQQKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw' + ) + for x in bad_keys: + with pytest.raises(Exception) as e_info: + fake_tuple = btc.bip32_deserialize(x) + +def test_ckd_pubkeys(): + pub = 'xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw' + new_pub = btc.bip32_ckd(pub, 4) + #how to check it's right? + print new_pub + #try to do on hardened, should fail, that's the idea: + with pytest.raises(Exception) as e_info: + new_pub = btc.bip32_ckd(pub, 2**31+1) + +def test_bip32_descend(): + master = btc.bip32_master_key('\x07'*32) + end_key = btc.bip32_descend(master, [2, 3, 10000]) + assert end_key=="6856ef965940a1a7b1311dc041050ac0013e326c7ff4e2c677a7694b4f0405c901" + end_key = btc.bip32_descend(master, 2, 5, 4, 5) + assert end_key=="d2d816b6485103c0d7ff95482788f0e8e73fa11817079e006d47979d8196c4b101" + + + + \ No newline at end of file diff --git a/test/test_blockr.py b/test/test_blockr.py new file mode 100644 index 00000000..92a7939c --- /dev/null +++ b/test/test_blockr.py @@ -0,0 +1,113 @@ +#! /usr/bin/env python +from __future__ import absolute_import +'''Blockchain access via blockr tests.''' + +import sys +import os +import time +import binascii + +import bitcoin as btc +import pytest +from joinmarket import load_program_config, jm_single +from joinmarket.blockchaininterface import BlockrInterface +from joinmarket import get_p2pk_vbyte, get_log, Wallet + +log = get_log() + +#TODO: some kind of mainnet testing, harder. +blockr_root_url = "https://tbtc.blockr.io/api/v1/" + +def test_blockr_bad_request(): + with pytest.raises(Exception) as e_info: + btc.make_request(blockr_root_url+"address/txs/", "0000") + +def test_blockr_bad_pushtx(): + inps = [("00000000", "btc"), ("00000000", "testnet"), + ('\x00'*8, "testnet"), ('\x00'*8, "x")] + for i in inps: + with pytest.raises(Exception) as e_info: + btc.blockr_pushtx(i[0],i[1]) + +def test_bci_bad_pushtx(): + inps = [("00000000"), ('\x00'*8)] + for i in inps: + with pytest.raises(Exception) as e_info: + btc.bci_pushtx(i[0]) + +def test_blockr_estimate_fee(setup_blockr): + res = [] + for N in [1,3,6]: + res.append(jm_single().bc_interface.estimate_fee_per_kb(N)) + assert res[0] >= res[2] + #Note this can fail, it isn't very accurate. + #assert res[1] >= res[2] + #sanity checks: + assert res[0] < 200000 + assert res[2] < 150000 + +@pytest.mark.parametrize( + "net, seed, gaplimit, showprivkey, method", + [ + ("testnet", + #Dont take these testnet coins, itll botch up our tests!! + "I think i did pretty good with Christmas", + 6, + True, + #option "displayall" here will show all addresses from beginning + "display"), + ]) +def test_blockr_sync(setup_blockr, net, seed, gaplimit, showprivkey, method): + jm_single().config.set("BLOCKCHAIN", "network", net) + wallet = Wallet(seed, max_mix_depth = 5) + jm_single().bc_interface.sync_wallet(wallet) + + #copy pasted from wallet-tool; some boiled down form of + #this should really be in wallet.py in the joinmarket module. + def cus_print(s): + print s + + total_balance = 0 + for m in range(wallet.max_mix_depth): + cus_print('mixing depth %d m/0/%d/' % (m, m)) + balance_depth = 0 + for forchange in [0, 1]: + cus_print(' ' + ('external' if forchange == 0 else 'internal') + + ' addresses m/0/%d/%d/' % (m, forchange)) + + for k in range(wallet.index[m][forchange] + gaplimit): + addr = wallet.get_addr(m, forchange, k) + balance = 0.0 + for addrvalue in wallet.unspent.values(): + if addr == addrvalue['address']: + balance += addrvalue['value'] + balance_depth += balance + used = ('used' if k < wallet.index[m][forchange] else ' new') + if showprivkey: + if btc.secp_present: + privkey = btc.wif_compressed_privkey( + wallet.get_key(m, forchange, k), get_p2pk_vbyte()) + else: + privkey = btc.encode_privkey(wallet.get_key(m, + forchange, k), 'wif_compressed', get_p2pk_vbyte()) + else: + privkey = '' + if (method == 'displayall' or balance > 0 or + (used == ' new' and forchange == 0)): + cus_print(' m/0/%d/%d/%03d %-35s%s %.8f btc %s' % + (m, forchange, k, addr, used, balance / 1e8, + privkey)) + total_balance += balance_depth + print('for mixdepth=%d balance=%.8fbtc' % (m, balance_depth / 1e8)) + assert total_balance == 96143257 + + +@pytest.fixture(scope="module") +def setup_blockr(request): + def blockr_teardown(): + jm_single().config.set("BLOCKCHAIN", "blockchain_source", "regtest") + jm_single().config.set("BLOCKCHAIN", "network", "testnet") + request.addfinalizer(blockr_teardown) + load_program_config() + jm_single().config.set("BLOCKCHAIN", "blockchain_source", "blockr") + jm_single().bc_interface = BlockrInterface(True) diff --git a/test/test_btc_formatting.py b/test/test_btc_formatting.py new file mode 100644 index 00000000..67e2084f --- /dev/null +++ b/test/test_btc_formatting.py @@ -0,0 +1,47 @@ +#! /usr/bin/env python +from __future__ import absolute_import +'''Test bitcoin module data handling''' + +import bitcoin as btc +import pytest +import binascii +import hashlib + +#used in p2sh addresses +def test_hash160(): + assert '0e3397b4abc7a382b3ea2365883c3c7ca5f07600' == \ + btc.hash160('The quick brown fox jumps over the lazy dog') + +def test_bad_code_string(): + for i in [1,9,257,-3,"256"]: + with pytest.raises(ValueError) as e_info: + btc.get_code_string(i) + +@pytest.mark.parametrize( + "st, frm, to, minlen, res", + [ + ("0101aa", 16, 16, 12, "0000000101aa"), + ]) +def test_changebase(st, frm, to, minlen, res): + assert btc.changebase(st, frm, to, minlen) == res + + +#Tests of compactsize encoding, see: +#https://bitcoin.org/en/developer-reference#compactsize-unsigned-integers +#note that little endian is used. +@pytest.mark.parametrize( + "num, compactsize", + [ + (252, "fc"), + (253, "fdfd00"), + (254, "fdfe00"), + (515, "fd0302"), + (65535, "fdffff"), + (65536, "fe00000100"), + (65537, "fe01000100"), + (4294967295, "feffffffff"), + (4294967296, "ff0000000001000000"), + + ]) +def test_compact_size(num, compactsize): + assert btc.num_to_var_int(num) == binascii.unhexlify(compactsize) diff --git a/test/test_donations.py b/test/test_donations.py new file mode 100644 index 00000000..e0986c6d --- /dev/null +++ b/test/test_donations.py @@ -0,0 +1,109 @@ +import sys +import bitcoin as btc +import pytest +import json +from commontest import make_wallets +from joinmarket import load_program_config, get_p2pk_vbyte, get_log + +log = get_log() + +# This is a primitive version +# to test the basic concept is working (specifically the imports). +# It isn't required that the transaction is valid, only that a valid +# utxo from the wallet can extract a private key. +# A real test of donations fits into the tumbler test cases, although +# with effort an "intermediately realistic" version could be set up. + + +@pytest.mark.parametrize( + "tx_type, tx_id, tx_hex, address", + [("simple-tx", + "f31916a1d398a4ec18d56a311c942bb6db934cee6aa8ac30af0b30aad9efb841", + "0100000001c74265f31fc5e24895fdc83f7157cc40045235f3a71ae326a219de9de873" + + "0d8b010000006a473044022076055917470b7ec4f4bb008096266cf816ebb089ad983e" + + "6a0f63340ba0e6a6cb022059ec938b996a75db10504e46830e13d399f28191b9832bd5" + + "f61df097b9e0d47801210291941334a00959af4aa5757abf81d2a7d1aca8adb3431c67" + + "e89419271ba71cb4feffffff023cdeeb03000000001976a914a2426748f14eba44b3f6" + + "abba3e8bce216ea233f388acf4ebf303000000001976a914bfa366464a464005ba0df8" + + "6024a6c3ed859f03ac88ac33280600", + "msWrR3Gm2mBmdLZH8vGHbHifM53N2vuYBq"), + + ]) +def test_donation_address(setup_donations, tx_type, tx_id, tx_hex, address): + wallets = make_wallets(1, wallet_structures=[[1,1,1,0,0]], + mean_amt=0.5) + wallet = wallets[0]['wallet'] + priv, addr = donation_address(tx_hex, wallet) + print addr + #just a check that it doesn't throw + sign_donation_tx(tx_hex, 0, priv) + +if not btc.secp_present: + #See note above, this is NOT the real code, see taker.py + def donation_address(tx, wallet): + from bitcoin.main import multiply, G, deterministic_generate_k, add_pubkeys + reusable_donation_pubkey = ('02be838257fbfddabaea03afbb9f16e852' + '9dfe2de921260a5c46036d97b5eacf2a') + + privkey = wallet.get_key_from_addr(wallet.get_new_addr(0,0)) + msghash = btc.bin_txhash(tx, btc.SIGHASH_ALL) + # generate unpredictable k + global sign_k + sign_k = deterministic_generate_k(msghash, privkey) + c = btc.sha256(multiply(reusable_donation_pubkey, sign_k)) + sender_pubkey = add_pubkeys( + reusable_donation_pubkey, multiply( + G, c)) + sender_address = btc.pubtoaddr(sender_pubkey, get_p2pk_vbyte()) + log.debug('sending coins to ' + sender_address) + return privkey, sender_address + + #See note above, this is NOT the real code, see taker.py + def sign_donation_tx(tx, i, priv): + from bitcoin.main import fast_multiply, decode_privkey, G, inv, N + from bitcoin.transaction import der_encode_sig + k = sign_k + hashcode = btc.SIGHASH_ALL + i = int(i) + if len(priv) <= 33: + priv = btc.safe_hexlify(priv) + pub = btc.privkey_to_pubkey(priv) + address = btc.pubkey_to_address(pub) + signing_tx = btc.signature_form( + tx, i, btc.mk_pubkey_script(address), hashcode) + + msghash = btc.bin_txhash(signing_tx, hashcode) + z = btc.hash_to_int(msghash) + # k = deterministic_generate_k(msghash, priv) + r, y = fast_multiply(G, k) + s = inv(k, N) * (z + r * decode_privkey(priv)) % N + rawsig = 27 + (y % 2), r, s + + sig = der_encode_sig(*rawsig) + btc.encode(hashcode, 16, 2) + # sig = ecdsa_tx_sign(signing_tx, priv, hashcode) + txobj = btc.deserialize(tx) + txobj["ins"][i]["script"] = btc.serialize_script([sig, pub]) + return btc.serialize(txobj) +else: + def donation_address(cjtx, wallet): + privkey = wallet.get_key_from_addr(wallet.get_new_addr(0,0)) + reusable_donation_pubkey = '02be838257fbfddabaea03afbb9f16e8529dfe2de921260a5c46036d97b5eacf2a' + global sign_k + import os + import binascii + sign_k = os.urandom(32) + log.debug("Using the following nonce value: "+binascii.hexlify(sign_k)) + c = btc.sha256(btc.multiply(binascii.hexlify(sign_k), + reusable_donation_pubkey, True)) + sender_pubkey = btc.add_pubkeys([reusable_donation_pubkey, + btc.privtopub(c+'01', True)], True) + sender_address = btc.pubtoaddr(sender_pubkey, get_p2pk_vbyte()) + log.debug('sending coins to ' + sender_address) + return privkey, sender_address + + def sign_donation_tx(tx, i, priv): + return btc.sign(tx, i, priv, usenonce=sign_k) + +@pytest.fixture(scope="module") +def setup_donations(): + load_program_config() diff --git a/test/test_ecc_signing.py b/test/test_ecc_signing.py new file mode 100644 index 00000000..c063e083 --- /dev/null +++ b/test/test_ecc_signing.py @@ -0,0 +1,80 @@ +#! /usr/bin/env python +from __future__ import absolute_import +'''Test ECDSA signing and other key operations, including legacy message +signature conversion.''' + +import bitcoin as btc +import binascii +from joinmarket.configure import load_program_config, get_p2pk_vbyte +from joinmarket import jm_single +import json +import pytest + +vectors = None + +def test_valid_sigs(setup_ecc): + for v in vectors['vectors']: + msg = v['msg'] + sig = v['sig'] + priv = v['privkey'] + 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) + #check that it fails to verify against corrupted signatures + for i in [0,1,2,4,7,25,55]: + #corrupt one byte + binsig = binascii.unhexlify(sig) + checksig = binascii.hexlify(binsig[:i] + chr( + (ord(binsig[i])+1) %256) + binsig[i+1:-1]) + + #this kind of corruption will sometimes lead to an assert + #failure (if the DER format is corrupted) and sometimes lead + #to a signature verification failure. + try: + res = btc.ecdsa_raw_verify(msg, pubkey, checksig, True, rawmsg=True) + except: + continue + assert res==False + +def test_legacy_conversions(setup_ecc): + #run some checks of legacy conversion + for v in vectors['vectors']: + msg = v['msg'] + sig = binascii.unhexlify(v['sig'])[:-1] + priv = v['privkey'] + #check back-and-forth translation + assert btc.legacy_ecdsa_verify_convert( + btc.legacy_ecdsa_sign_convert(sig)) == sig + + #Correct cases passed, now try invalid signatures + + #screw up r-length + bad_sig = sig[:3] + '\x90' + sig[4:] + with pytest.raises(Exception) as e_info: + fake_sig = btc.legacy_ecdsa_sign_convert(bad_sig) + #screw up s-length + rlen = ord(sig[3]) + bad_sig = sig[:4+rlen+1] + '\x90' + sig[4+rlen+2:] + with pytest.raises(Exception) as e_info: + fake_sig = btc.legacy_ecdsa_sign_convert(bad_sig) + #valid length, but doesn't match s + bad_sig = sig[:4+rlen+1] + '\x06' + sig[4+rlen+2:] + with pytest.raises(Exception) as e_info: + fake_sig = btc.legacy_ecdsa_sign_convert(bad_sig) + #invalid inputs to legacy convert + #too short + bad_sig = '\x07'*32 + assert not btc.legacy_ecdsa_verify_convert(bad_sig) + #r OK, s too short + bad_sig = '\x07'*64 + assert not btc.legacy_ecdsa_verify_convert(bad_sig) + #note - no parity byte check, we don't bother (this is legacy) + + +@pytest.fixture(scope='module') +def setup_ecc(): + global vectors + with open("test/ecc_sigs_rfc6979_valid.json", "r") as f: + json_data = f.read() + vectors = json.loads(json_data) \ No newline at end of file diff --git a/test/test_irc_messaging.py b/test/test_irc_messaging.py new file mode 100644 index 00000000..ab8d604d --- /dev/null +++ b/test/test_irc_messaging.py @@ -0,0 +1,83 @@ +#! /usr/bin/env python +from __future__ import absolute_import +'''Tests of joinmarket bots end-to-end (including IRC and bitcoin) ''' + +import subprocess +import signal +import os +import pytest +import time +import threading +from commontest import local_command, make_wallets +from joinmarket.message_channel import CJPeerError +import joinmarket.irc +from joinmarket import load_program_config, IRCMessageChannel + +python_cmd = "python2" +yg_cmd = "yield-generator-basic.py" +yg_name = None + +class DummyMC(IRCMessageChannel): + def __init__(self, nick): + super(DummyMC, self).__init__(nick) + +def on_order_seen(counterparty, oid, ordertype, minsize, + maxsize, txfee, cjfee): + global yg_name + yg_name = counterparty + +def on_pubkey(pubkey): + print "received pubkey: " + pubkey + +class RawIRCThread(threading.Thread): + + def __init__(self, ircmsgchan): + threading.Thread.__init__(self, name='RawIRCThread') + self.daemon = True + self.ircmsgchan = ircmsgchan + + def run(self): + self.ircmsgchan.run() + +def test_junk_messages(setup_messaging): + #start a yg bot just to receive messages + wallets = make_wallets(1, + wallet_structures=[[1,0,0,0,0]], + mean_amt=1) + wallet = wallets[0]['wallet'] + ygp = local_command([python_cmd, yg_cmd,\ + str(wallets[0]['seed'])], bg=True) + + #start a raw IRCMessageChannel instance in a thread; + #then call send_* on it with various errant messages + mc = DummyMC("irc_ping_test") + mc.register_orderbookwatch_callbacks(on_order_seen=on_order_seen) + mc.register_taker_callbacks(on_pubkey=on_pubkey) + RawIRCThread(mc).start() + time.sleep(1) + mc._IRCMessageChannel__pubmsg("!orderbook") + time.sleep(1) + mc._IRCMessageChannel__pubmsg("!orderbook!orderbook") + time.sleep(1) + mc._IRCMessageChannel__pubmsg("junk and crap"*20) + time.sleep(5) + #try: + with pytest.raises(CJPeerError) as e_info: + mc.send_error(yg_name, "fly you fools!") + #except CJPeerError: + # print "CJPeerError raised" + # pass + time.sleep(5) + mc.shutdown() + ygp.send_signal(signal.SIGINT) + ygp.wait() + +@pytest.fixture(scope="module") +def setup_messaging(): + #Trigger PING LAG sending artificially + joinmarket.irc.PING_INTERVAL = 3 + load_program_config() + + + + diff --git a/test/test_keys.py b/test/test_keys.py index cec810bf..2dcb3f34 100644 --- a/test/test_keys.py +++ b/test/test_keys.py @@ -10,7 +10,39 @@ import pytest +def test_read_raw_privkeys(): + if not btc.secp_present: + #these are tests of format which the pybtc library didnt do + return + badkeys = ['', '\x07'*31,'\x07'*34, '\x07'*33] + for b in badkeys: + with pytest.raises(Exception) as e_info: + c, k = btc.read_privkey(b) + goodkeys = [('\x07'*32, False), ('\x07'*32 + '\x01', True)] + for g in goodkeys: + c, k = btc.read_privkey(g[0]) + assert c == g[1] + def test_wif_privkeys_invalid(setup_keys): + #first try to create wif privkey from key of wrong length + bad_privs = ['\x01\x02'*17] #some silly private key but > 33 bytes + + #next try to create wif with correct length but wrong compression byte + bad_privs.append('\x07'*32 + '\x02') + + for priv in bad_privs: + with pytest.raises(Exception) as e_info: + fake_wif = btc.wif_compressed_privkey(binascii.hexlify(priv)) + + #Create a wif with wrong length + bad_wif1 = btc.bin_to_b58check('\x01\x02'*34, 128) + #Create a wif with wrong compression byte + bad_wif2 = btc.bin_to_b58check('\x07'*33, 128) + for bw in [bad_wif1, bad_wif2]: + with pytest.raises(Exception) as e_info: + fake_priv = btc.from_wif_privkey(bw) + + #Some invalid b58 from bitcoin repo; #none of these are valid as any kind of key or address with open("test/base58_keys_invalid.json", "r") as f: json_data = f.read() diff --git a/test/test_regtest.py b/test/test_regtest.py index 7be5f0ed..ffb4b556 100644 --- a/test/test_regtest.py +++ b/test/test_regtest.py @@ -13,6 +13,7 @@ from joinmarket import random_nick, get_p2pk_vbyte from joinmarket import get_log, choose_sweep_orders, choose_orders, \ pick_order, cheapest_order_choose, weighted_order_choose, debug_dump_object +import joinmarket.irc import json import sendpayment import bitcoin as btc @@ -81,6 +82,10 @@ def test_sendpayment(setup_regtest, num_ygs, wallet_structures, mean_amt, log.debug('starting sendpayment') jm_single().bc_interface.sync_wallet(wallet) + + #Trigger PING LAG sending artificially + joinmarket.irc.PING_INTERVAL = 3 + irc = IRCMessageChannel(jm_single().nickname) taker = sendpayment.SendPayment(irc, wallet, destaddr, amount, makercount, txfee, waittime, mixdepth, answeryes, @@ -88,12 +93,6 @@ def test_sendpayment(setup_regtest, num_ygs, wallet_structures, mean_amt, try: log.debug('starting irc') irc.run() - except: - log.debug('CRASHING, DUMPING EVERYTHING') - debug_dump_object(wallet, ['addr_cache', 'keys', 'wallet_name', 'seed']) - debug_dump_object(taker) - import traceback - log.debug(traceback.format_exc()) finally: if any(yigen_procs): for ygp in yigen_procs: @@ -109,6 +108,8 @@ def test_sendpayment(setup_regtest, num_ygs, wallet_structures, mean_amt, assert received == amount, "sendpayment failed - coins not arrived, " +\ "received: " + str(received) #TODO: how to check success for sweep case? + else: + assert received != 0 @pytest.fixture(scope="module") diff --git a/test/test_tumbler.py b/test/test_tumbler.py new file mode 100644 index 00000000..43ddc68a --- /dev/null +++ b/test/test_tumbler.py @@ -0,0 +1,186 @@ +#! /usr/bin/env python +from __future__ import absolute_import +'''Tests of joinmarket bots end-to-end (including IRC and bitcoin) ''' + +import subprocess +import signal +from commontest import local_command, make_wallets +import os +import pytest +import time +from joinmarket import Taker, load_program_config, IRCMessageChannel +from joinmarket import validate_address, jm_single +from joinmarket import random_nick, get_p2pk_vbyte +from joinmarket import get_log, choose_sweep_orders, choose_orders, \ + pick_order, cheapest_order_choose, weighted_order_choose, debug_dump_object +import json +import tumbler +import bitcoin as btc +import copy +from pprint import pprint + +#for running bots as subprocesses +python_cmd = 'python2' +yg_cmd = 'yield-generator-basic.py' +#yg_cmd = 'yield-generator-mixdepth.py' +#yg_cmd = 'yield-generator-deluxe.py' + +class Options(object): + pass + +def get_options(): + options = Options() + options.mixdepthsrc = 0 + options.mixdepthcount = 4 + options.minmakercount = 2 + options.makercountrange = (6, 0) + options.maxcjfee = (0.01, 10000) + options.txfee = 5000 + options.addrcount = 3 + options.donateamount = 0.5 + options.txcountparams = (4, 1) + options.mintxcount = 1 + options.amountpower = 100 + options.timelambda = 0.2 + options.waittime = 10 + options.mincjamount = 1000000 + options.liquiditywait = 5 + return options + +def test_generate_tumbler_tx(): + options = get_options() + destaddrs = ["A","B","C"] + tx_list = tumbler.generate_tumbler_tx(destaddrs, options) + from pprint import pformat + print pformat(tx_list) + +@pytest.mark.parametrize( + "num_ygs, wallet_structures, mean_amt, sdev_amt, yg_excess", + [ + # basic 1sp 2yg + (6, [[4, 4, 4, 4, 4]] * 7, 0.5, 0.3, 10), + ]) +def test_tumbler(setup_tumbler, num_ygs, wallet_structures, mean_amt, sdev_amt, + yg_excess): + """Test of tumbler code, with yield generators in background. + """ + log = get_log() + options = Options() + options.mixdepthsrc = 0 + options.mixdepthcount = 4 + options.minmakercount = 2 + options.makercountrange = (num_ygs, 0) + options.maxcjfee = (0.01, 10000) + options.txfee = 5000 + options.addrcount = 3 + options.donateamount = 0.5 + options.txcountparams = (4, 1) + options.mintxcount = 1 + options.amountpower = 100 + options.timelambda = 0.2 + options.waittime = 10 + options.mincjamount = 1000000 + options.liquiditywait = 5 + + wallets = make_wallets(num_ygs + 1, + wallet_structures=wallet_structures, + mean_amt=mean_amt, sdev_amt=sdev_amt) + #need to make sure that at least some ygs have substantially + #more coins for last stages of sweep/spend in tumble: + for i in range(num_ygs): + jm_single().bc_interface.grab_coins( + wallets[i]['wallet'].get_external_addr(0), yg_excess) + #the tumbler bot uses the last wallet in the list + wallet = wallets[num_ygs]['wallet'] + + yigen_procs = [] + for i in range(num_ygs): + ygp = local_command([python_cmd, yg_cmd,\ + str(wallets[i]['seed'])], bg=True) + time.sleep(2) #give it a chance + yigen_procs.append(ygp) + + #A significant delay is needed to wait for the yield generators to sync + time.sleep(20) + destaddrs = [] + for i in range(3): + if btc.secp_present: + destaddr = btc.privkey_to_address( + os.urandom(32), + from_hex=False, + magicbyte=get_p2pk_vbyte()) + else: + destaddr = btc.privkey_to_address( + os.urandom(32), + magicbyte=get_p2pk_vbyte()) + addr_valid, errormsg = validate_address(destaddr) + assert addr_valid, "Invalid destination address: " + destaddr + \ + ", error message: " + errormsg + destaddrs.append(destaddr) + tx_list = tumbler.generate_tumbler_tx(destaddrs, options) + pprint(tx_list) + if options.addrcount + 1 > options.mixdepthcount: + print('not enough mixing depths to pay to all destination addresses, ' + 'increasing mixdepthcount') + options.mixdepthcount = options.addrcount + 1 + + tx_list2 = copy.deepcopy(tx_list) + tx_dict = {} + for tx in tx_list2: + srcmixdepth = tx['srcmixdepth'] + tx.pop('srcmixdepth') + if srcmixdepth not in tx_dict: + tx_dict[srcmixdepth] = [] + tx_dict[srcmixdepth].append(tx) + dbg_tx_list = [] + for srcmixdepth, txlist in tx_dict.iteritems(): + dbg_tx_list.append({'srcmixdepth': srcmixdepth, 'tx': txlist}) + log.debug('tumbler transaction list') + pprint(dbg_tx_list) + + total_wait = sum([tx['wait'] for tx in tx_list]) + print('creates ' + str(len(tx_list)) + ' transactions in total') + print('waits in total for ' + str(len(tx_list)) + ' blocks and ' + str( + total_wait) + ' minutes') + total_block_and_wait = len(tx_list) * 10 + total_wait + print('estimated time taken ' + str(total_block_and_wait) + ' minutes or ' + + str(round(total_block_and_wait / 60.0, 2)) + ' hours') + + jm_single().nickname = random_nick() + + log.debug('starting tumbler') + + jm_single().bc_interface.sync_wallet(wallet) + irc = IRCMessageChannel(jm_single().nickname) + tumbler_bot = tumbler.Tumbler(irc, wallet, tx_list, options) + try: + log.debug('starting irc') + irc.run() + except: + log.debug('CRASHING, DUMPING EVERYTHING') + debug_dump_object(wallet, ['addr_cache', 'keys', 'wallet_name', 'seed']) + debug_dump_object(tumbler_bot) + import traceback + log.debug(traceback.format_exc()) + finally: + if any(yigen_procs): + for ygp in yigen_procs: + #NB *GENTLE* shutdown is essential for + #test coverage reporting! + ygp.send_signal(signal.SIGINT) + ygp.wait() + #wait for block generation + time.sleep(5) + received = jm_single().bc_interface.get_received_by_addr( + [destaddr], None)['data'][0]['balance'] + assert received != 0 + """TODO: figure out a sensible assertion check for the destination + if amount != 0: + assert received == amount, "sendpayment failed - coins not arrived, " +\ + "received: " + str(received) + """ + + +@pytest.fixture(scope="module") +def setup_tumbler(): + load_program_config() diff --git a/test/test_tx_creation.py b/test/test_tx_creation.py new file mode 100644 index 00000000..4ac30a99 --- /dev/null +++ b/test/test_tx_creation.py @@ -0,0 +1,148 @@ +#! /usr/bin/env python +from __future__ import absolute_import +'''Test of unusual transaction types creation and push to +network to check validity.''' + +import sys +import os +import time +import binascii +import pexpect +import random +import subprocess +import unittest +from commontest import local_command, interact, make_wallets + +import bitcoin as btc +import pytest +from joinmarket import load_program_config, jm_single +from joinmarket import get_p2pk_vbyte, get_log, Wallet +from joinmarket.support import chunks, select_gradual, \ + select_greedy, select_greediest + +log = get_log() +#just a random selection of pubkeys for receiving multisigs; +#if we ever need the privkeys, they are in a json file somewhere +vpubs = ["03e9a06e539d6bf5cf1ca5c41b59121fa3df07a338322405a312c67b6349a707e9", + "0280125e42c1984923106e281615dfada44d38c4125c005963b322427110d709d6", + "02726fa5b19e9406aaa46ee22fd9e81a09dd5eb7c87505b93a11efcf4b945e778c", + "03600a739be32a14938680b3b3d61b51f217a60df118160d0decab22c9e1329862", + "028a2f126e3999ff66d01dcb101ab526d3aa1bf5cbdc4bde14950a4cead95f6fcb", + "02bea84d70e74f7603746b62d79bf035e16d982b56e6a1ee07dfd3b9130e8a2ad9"] + + +@pytest.mark.parametrize( + "nw, wallet_structures, mean_amt, sdev_amt, amount, pubs, k", [ + (1, [[2, 1, 4, 0, 0]], 4, 1.4, 600000000, vpubs[1:4], 2), + (1, [[3, 3, 0, 0, 3]], 4, 1.4, 100000000, vpubs[:4], 3), + ]) +def test_create_p2sh_output_tx(setup_tx_creation, nw, wallet_structures, + mean_amt, sdev_amt, amount, pubs, k): + wallets = make_wallets(nw, wallet_structures, mean_amt, sdev_amt) + for w in wallets.values(): + jm_single().bc_interface.sync_wallet(w['wallet']) + for k, w in enumerate(wallets.values()): + wallet = w['wallet'] + ins_full = wallet.select_utxos(0, amount) + script = btc.mk_multisig_script(pubs, k) + #try the alternative argument passing + pubs.append(k) + script2 = btc.mk_multisig_script(*pubs) + assert script2 == script + output_addr = btc.scriptaddr(script, magicbyte=196) + txid = make_sign_and_push(ins_full, + wallet, + amount, + output_addr=output_addr) + assert txid + + +def make_sign_and_push(ins_full, + wallet, + amount, + output_addr=None, + change_addr=None, + hashcode=btc.SIGHASH_ALL): + total = sum(x['value'] for x in ins_full.values()) + ins = 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 + outs = [{'value': amount, + 'address': output_addr}, {'value': total - amount - 100000, + 'address': change_addr}] + + tx = btc.mktx(ins, outs) + de_tx = btc.deserialize(tx) + for index, ins in enumerate(de_tx['ins']): + utxo = ins['outpoint']['hash'] + ':' + str(ins['outpoint']['index']) + addr = ins_full[utxo]['address'] + priv = wallet.get_key_from_addr(addr) + if index % 2: + priv = binascii.unhexlify(priv) + tx = btc.sign(tx, index, priv, hashcode=hashcode) + #pushtx returns False on any error + print btc.deserialize(tx) + return jm_single().bc_interface.pushtx(tx) + + +def test_create_sighash_txs(setup_tx_creation): + #non-standard hash codes: + for sighash in [btc.SIGHASH_ANYONECANPAY + btc.SIGHASH_SINGLE, + btc.SIGHASH_NONE, btc.SIGHASH_SINGLE]: + wallet = make_wallets(1, [[2, 0, 0, 0, 1]], 3)[0]['wallet'] + jm_single().bc_interface.sync_wallet(wallet) + amount = 350000000 + ins_full = wallet.select_utxos(0, amount) + print "using hashcode: " + str(sighash) + txid = make_sign_and_push(ins_full, wallet, amount, hashcode=sighash) + assert txid + + #Create an invalid sighash single (too many inputs) + extra = wallet.select_utxos(4, 100000000) #just a few more inputs + ins_full.update(extra) + with pytest.raises(Exception) as e_info: + txid = make_sign_and_push(ins_full, + wallet, + amount, + hashcode=btc.SIGHASH_SINGLE) + + #trigger insufficient funds + with pytest.raises(Exception) as e_info: + fake_utxos = wallet.select_utxos(4, 1000000000) + + +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 = [btc.privkey_to_pubkey(binascii.hexlify(priv)) for priv in privs] + script = btc.mk_multisig_script(pubs, 2) + msig_addr = btc.scriptaddr(script, magicbyte=196) + #pay into it + wallet = make_wallets(1, [[2, 0, 0, 0, 1]], 3)[0]['wallet'] + jm_single().bc_interface.sync_wallet(wallet) + amount = 350000000 + ins_full = wallet.select_utxos(0, amount) + txid = make_sign_and_push(ins_full, wallet, amount, output_addr=msig_addr) + assert txid + #wait for mining + time.sleep(4) + #spend out; the input can be constructed from the txid of previous + msig_in = txid + ":0" + ins = [msig_in] + #random output address and change addr + output_addr = wallet.get_new_addr(1, 1) + amount2 = amount - 50000 + outs = [{'value': amount2, 'address': output_addr}] + tx = btc.mktx(ins, outs) + sigs = [] + for priv in privs[:2]: + sigs.append(btc.multisign(tx, 0, script, binascii.hexlify(priv))) + tx = btc.apply_multisignatures(tx, 0, script, sigs) + txid = jm_single().bc_interface.pushtx(tx) + assert txid + + +@pytest.fixture(scope="module") +def setup_tx_creation(): + load_program_config() diff --git a/test/test_tx_serialize.py b/test/test_tx_serialize.py index b841db14..a605c1e3 100644 --- a/test/test_tx_serialize.py +++ b/test/test_tx_serialize.py @@ -199,7 +199,21 @@ def test_serialization_roundtrip(tx_type, tx_id, tx_hex): assert tx_hex == btc.serialize(btc.deserialize(tx_hex)) +@pytest.mark.parametrize( + "ins, outs, txtype, valid", + [ + (4, 3, "p2pkh", True), + (4, 3, "p2sh", False), + ]) +def test_estimate_tx_size(ins, outs, txtype, valid): + #TODO: this function should throw on invalid number of ins or outs + if valid: + assert btc.estimate_tx_size(ins, outs, txtype)== 10 + 147*ins + 34*outs + else: + with pytest.raises(NotImplementedError) as e_info: + btc.estimate_tx_size(ins, outs, txtype) + def test_serialization_roundtrip2(): #Data extracted from: #https://github.com/bitcoin/bitcoin/blob/master/src/test/data/tx_valid.json @@ -209,7 +223,6 @@ def test_serialization_roundtrip2(): #only currently of very limited significance with open("test/tx_valid.json", "r") as f: json_data = f.read() - print 'read the tx data' valid_txs = json.loads(json_data) for j in valid_txs: #ignore comment entries diff --git a/test/test_wallets.py b/test/test_wallets.py index 9b313ba2..74f5313e 100644 --- a/test/test_wallets.py +++ b/test/test_wallets.py @@ -197,6 +197,34 @@ def run_recover(self, seed): return False return True +def test_pkcs7_bad_padding(): + #used in seed decryption; check that it throws + #if wrongly padded (this caused a REAL bug before!) + import joinmarket.slowaes + bad_padded = ['\x07'*14, '\x07'*31, '\x07'*31+'\x11', '\x07'*31+'\x00', + '\x07'*14+'\x01\x02'] + for b in bad_padded: + with pytest.raises(Exception) as e_info: + fake_unpadded = joinmarket.slowaes.strip_PKCS7_padding(b) + +def test_aes(): + #test general AES operation; probably not needed + import joinmarket.slowaes as sa + cleartext = "This is a test!" + iv = [103, 35, 148, 239, 76, 213, 47, 118, 255, 222, 123, 176, 106, 134, 98, + 92] + for ks in [16,24,32]: + for mode in ["CFB", "CBC", "OFB"]: + cypherkey = map(ord, os.urandom(ks)) + moo = sa.AESModeOfOperation() + mode, orig_len, ciph = moo.encrypt(cleartext, moo.modeOfOperation[mode], + cypherkey, ks, + iv) + decr = moo.decrypt(ciph, orig_len, mode, cypherkey, + ks, iv) + assert decr==cleartext + + @pytest.fixture(scope="module") def setup_wallets(): load_program_config()