Browse Source
Also added some blockr tests (not complete). Minor packaging changes, in particular pick_orders removed from package as requires user intervention, moved to sendpayment.py for now.master
5 changed files with 304 additions and 31 deletions
@ -0,0 +1,133 @@
|
||||
#! /usr/bin/env python |
||||
from __future__ import absolute_import |
||||
'''Blockchain access via blockr tests.''' |
||||
|
||||
import sys |
||||
import os |
||||
import time |
||||
import binascii |
||||
from mock import patch |
||||
import json |
||||
|
||||
import jmbitcoin as btc |
||||
import pytest |
||||
from jmclient import (load_program_config, jm_single, sync_wallet, BlockrInterface, |
||||
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(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) |
||||
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: |
||||
privkey = btc.wif_compressed_privkey( |
||||
wallet.get_key(m, forchange, k), 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 == 96085297 |
||||
|
||||
@patch('jmbitcoin.bci.make_request') |
||||
def test_blockr_error_429(make_request): |
||||
error = {u'code': 429, |
||||
u'data': None, |
||||
u'message': u'Too many requests. Wait a bit...', |
||||
u'status': u'error'} |
||||
success = {u'code': 200, |
||||
u'data': {u'address': u'mqG1k82TDWfxSYFyDRkomjYonDUYjPRbsb', |
||||
u'limit_txs': 200, |
||||
u'nb_txs': 1, |
||||
u'nb_txs_displayed': 1, |
||||
u'txs': [{u'amount': 1, |
||||
u'amount_multisig': 0, |
||||
u'confirmations': 400, |
||||
u'time_utc': u'2016-09-15T19:46:14Z', |
||||
u'tx': u'6a1bfbdd011cbb2ab2a000d477bd6372150238b4c24e43a850220dba4dbf2c0d'}]}, |
||||
u'message': u'', |
||||
u'status': u'success'} |
||||
make_request.side_effect = map(json.dumps, [error]*3 + [success]) |
||||
|
||||
d = btc.make_request_blockr(blockr_root_url + "address/txs/", "mqG1k82TDWfxSYFyDRkomjYonDUYjPRbsb") |
||||
assert d['code'] == 200 |
||||
assert d['data'] is not None |
||||
|
||||
|
||||
@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) |
||||
@ -0,0 +1,137 @@
|
||||
#! /usr/bin/env python |
||||
from __future__ import absolute_import |
||||
'''support functions for jmclient tests.''' |
||||
|
||||
import pytest |
||||
from jmclient import (select, select_gradual, select_greedy, select_greediest, |
||||
choose_orders, choose_sweep_orders, weighted_order_choose) |
||||
from jmclient.support import (calc_cj_fee, rand_exp_array, rand_pow_array, |
||||
rand_norm_array, rand_weighted_choice, |
||||
cheapest_order_choose) |
||||
from taker_test_data import t_orderbook |
||||
import copy |
||||
|
||||
def test_utxo_selection(): |
||||
"""Check that all the utxo selection algorithms work with a random |
||||
variety of wallet contents. |
||||
""" |
||||
unspent = [{'utxo':'a', 'value': 10000000}, |
||||
{'utxo':'b', 'value': 20000000}, |
||||
{'utxo':'c', 'value': 50000000}, |
||||
{'utxo':'d', 'value': 50000000}] |
||||
for selector in [select, select_gradual, select_greedy, select_greediest]: |
||||
for amt in [9999999, 10000000, 110000000, 19999999, 20000000, |
||||
49999999, 50000000, 99999999, 100000000]: |
||||
selector(unspent, amt) |
||||
for amt in [1300000010, 2000000000]: |
||||
with pytest.raises(Exception) as e_info: |
||||
x = selector(unspent, amt) |
||||
print(x) |
||||
assert e_info.match("Not enough funds") |
||||
|
||||
def test_random_funcs(): |
||||
x1 = rand_norm_array(5, 2, 10) |
||||
assert len(x1) == 10 |
||||
for x in x1: |
||||
assert x > -7 #6 sigma! |
||||
x2 = rand_exp_array(100, 10) |
||||
assert len(x2) == 10 |
||||
for x in x2: |
||||
assert x > 0 |
||||
x3 = rand_pow_array(100, 10) |
||||
assert len(x3) == 10 |
||||
for x in x3: |
||||
assert x > 0 |
||||
assert x < 1 |
||||
x4 = rand_weighted_choice(5, [0.2, 0.1, 0.3, 0.15, 0.25]) |
||||
assert x4 in range(5) |
||||
#test weighted choice fails with invalid inputs |
||||
with pytest.raises(ValueError) as e_info: |
||||
x = rand_weighted_choice(5, [0.2, 0.1, 0.3, 0.15, 0.26]) |
||||
assert e_info.match("Sum of probabilities") |
||||
with pytest.raises(ValueError) as e_info: |
||||
x = rand_weighted_choice(5, [0.25, 0.25, 0.25, 0.25]) |
||||
assert e_info.match("Need: 5 probabilities.") |
||||
|
||||
def test_calc_cjfee(): |
||||
assert calc_cj_fee("absoffer", 3000, 200000000) == 3000 |
||||
assert calc_cj_fee("reloffer", "0.01", 100000000) == 1000000 |
||||
with pytest.raises(RuntimeError) as e_info: |
||||
calc_cj_fee("dummyoffer", 2, 3) |
||||
|
||||
def test_choose_orders(): |
||||
orderbook = copy.deepcopy(t_orderbook) |
||||
#test not enough liquidity |
||||
orders_fees = choose_orders(orderbook, 10000000, 7, weighted_order_choose) |
||||
assert orders_fees == (None, 0) |
||||
orders_fees = choose_orders(orderbook, 10000000, 3, weighted_order_choose) |
||||
#need variable fee sizes |
||||
for i, o in enumerate(orderbook): |
||||
o['cjfee'] = str(float(o['cjfee']) + 0.0001*i) |
||||
#test phi not zero |
||||
orders_fees = choose_orders(orderbook, 10000000, 3, weighted_order_choose) |
||||
assert len(orders_fees[0]) == 3 |
||||
#test M < orderbook size for weighted |
||||
orders_fees = choose_orders(orderbook, 10000000, 1, weighted_order_choose) |
||||
assert len(orders_fees[0]) == 1 |
||||
#test the hated 'cheapest' |
||||
orders_fees = choose_orders(orderbook, 100000000, 3, cheapest_order_choose) |
||||
assert len(orders_fees[0]) == 3 |
||||
#test sweep |
||||
result, cjamount, total_fee = choose_sweep_orders(orderbook, 50000000, |
||||
30000, |
||||
3, |
||||
weighted_order_choose, |
||||
None) |
||||
assert cjamount >= 49800000 |
||||
assert cjamount <= 50000000 |
||||
assert total_fee >= 30000 |
||||
assert total_fee <= 100000 |
||||
assert len(result) == 3 |
||||
#test not enough liquidity |
||||
result, cjamount, total_fee = choose_sweep_orders(orderbook, 50000000, |
||||
30000, 7, |
||||
weighted_order_choose, |
||||
None) |
||||
assert result == None |
||||
assert cjamount == 0 |
||||
assert total_fee == 0 |
||||
|
||||
#here we doctor the orderbook; (a) include an absfee |
||||
#(b) add an unrecognized ordertype |
||||
#(c) put an order with wrong minsize |
||||
orderbook.append({u'counterparty': u'fake', |
||||
u'ordertype': u'absoffer', u'oid': 0, |
||||
u'minsize': 7500000, u'txfee': 1000, |
||||
u'maxsize': 599972700, u'cjfee': 9000}) |
||||
result, cjamount, total_fee = choose_sweep_orders(orderbook, 50000000, |
||||
30000, 7, |
||||
cheapest_order_choose, |
||||
None) |
||||
assert total_fee > 0 |
||||
#(b) |
||||
orderbook.append({u'counterparty': u'fake2', |
||||
u'ordertype': u'dummyoffer', u'oid': 0, |
||||
u'minsize': 7500000, u'txfee': 1000, |
||||
u'maxsize': 599972700, u'cjfee': 9000}) |
||||
with pytest.raises(RuntimeError) as e_info: |
||||
result, cjamount, total_fee = choose_sweep_orders(orderbook, |
||||
50000000, |
||||
30000, |
||||
8, |
||||
weighted_order_choose, |
||||
None) |
||||
#(c) |
||||
#remove bad offer |
||||
orderbook = orderbook[:-1] |
||||
for i in range(7): |
||||
orderbook[i]['minsize'] = 49999999 |
||||
result, cjamount, total_fee = choose_sweep_orders(orderbook, |
||||
50000000, |
||||
30000, |
||||
4, |
||||
weighted_order_choose, |
||||
None) |
||||
assert result == None |
||||
assert cjamount == 0 |
||||
assert total_fee == 0 |
||||
Loading…
Reference in new issue