You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

326 lines
11 KiB

"""
Test doing full coinjoins, bypassing IRC
"""
import os
import sys
import pytest
from twisted.internet import reactor
from jmbase import get_log
from jmclient import load_test_config, jm_single,\
YieldGeneratorBasic, Taker, LegacyWallet, SegwitLegacyWallet,\
NO_ROUNDING
from jmclient.podle import set_commitment_file
from commontest import make_wallets, binarize_tx, default_max_cj_fee
from test_taker import dummy_filter_orderbook
import jmbitcoin as btc
testdir = os.path.dirname(os.path.realpath(__file__))
log = get_log()
def make_wallets_to_list(make_wallets_data):
wallets = [None for x in range(len(make_wallets_data))]
for i in make_wallets_data:
wallets[i] = make_wallets_data[i]['wallet']
assert all(wallets)
return wallets
def sync_wallets(wallet_services, fast=True):
for wallet_service in wallet_services:
wallet_service.synced = False
wallet_service.gap_limit = 0
for x in range(20):
if wallet_service.synced:
break
wallet_service.sync_wallet(fast=fast)
else:
assert False, "Failed to sync wallet"
# because we don't run the monitoring loops for the
# wallet services, we need to update them on the latest
# block manually:
for wallet_service in wallet_services:
wallet_service.update_blockheight()
def create_orderbook(makers):
orderbook = []
for i in range(len(makers)):
m = makers[i]
assert len(m.offerlist) == 1
m.offerlist[0]['counterparty'] = str(i)
orderbook.extend(m.offerlist)
return orderbook
def create_taker(wallet, schedule, monkeypatch):
def on_finished_callback(*args, **kwargs):
log.debug("on finished called with: {}, {}".format(args, kwargs))
on_finished_callback.status = args[0]
on_finished_callback.called = True
on_finished_callback.called = False
on_finished_callback.status = None
taker = Taker(wallet, schedule, default_max_cj_fee,
callbacks=(dummy_filter_orderbook, None, on_finished_callback))
# we have skipped irc key setup and key exchange, handled by jmdaemon
monkeypatch.setattr(taker, 'auth_counterparty', lambda *args: True)
return taker
def init_coinjoin(taker, makers, orderbook, cj_amount):
init_data = taker.initialize(orderbook)
assert init_data[0], "taker.initialize error"
active_orders = init_data[4]
maker_data = {}
for mid in init_data[4]:
m = makers[int(mid)]
# note: '00' is kphex, usually set up by jmdaemon
response = m.on_auth_received(
'TAKER', init_data[4][mid], init_data[2][1:],
init_data[3], init_data[1], '00')
assert response[0], "maker.on_auth_received error"
ioauth_data = list(response[1:])
ioauth_data[0] = list(ioauth_data[0].keys())
# maker_pk which is set up by jmdaemon
ioauth_data.append(None)
maker_data[mid] = ioauth_data
# this is handled by jmdaemon
active_orders[mid]['utxos'] = response[1]
active_orders[mid]['cjaddr'] = ioauth_data[2]
active_orders[mid]['changeaddr'] = ioauth_data[3]
active_orders[mid]['offer'] = m.offerlist[0]
active_orders[mid]['amount'] = cj_amount
return active_orders, maker_data
def do_tx_signing(taker, makers, active_orders, txdata):
taker_final_result = 'not called'
maker_signatures = {} # left here for easier debugging
for mid in txdata[1]:
m = makers[int(mid)]
result = m.on_tx_received('TAKER', txdata[2], active_orders[mid])
assert result[0], "maker.on_tx_received error"
maker_signatures[mid] = result[1]
for sig in result[1]:
taker_final_result = taker.on_sig(mid, sig)
assert taker_final_result != 'not called'
return taker_final_result
@pytest.mark.parametrize('wallet_cls', (LegacyWallet, SegwitLegacyWallet))
def test_simple_coinjoin(monkeypatch, tmpdir, setup_cj, wallet_cls):
def raise_exit(i):
raise Exception("sys.exit called")
monkeypatch.setattr(sys, 'exit', raise_exit)
set_commitment_file(str(tmpdir.join('commitments.json')))
MAKER_NUM = 3
wallet_services = make_wallets_to_list(make_wallets(
MAKER_NUM + 1, wallet_structures=[[4, 0, 0, 0, 0]] * (MAKER_NUM + 1),
mean_amt=1, wallet_cls=wallet_cls))
jm_single().bc_interface.tickchain()
jm_single().bc_interface.tickchain()
sync_wallets(wallet_services)
makers = [YieldGeneratorBasic(
wallet_services[i],
[0, 2000, 0, 'swabsoffer', 10**7]) for i in range(MAKER_NUM)]
orderbook = create_orderbook(makers)
assert len(orderbook) == MAKER_NUM
cj_amount = int(1.1 * 10**8)
# mixdepth, amount, counterparties, dest_addr, waittime, rounding
schedule = [(0, cj_amount, MAKER_NUM, 'INTERNAL', 0, NO_ROUNDING)]
taker = create_taker(wallet_services[-1], schedule, monkeypatch)
active_orders, maker_data = init_coinjoin(taker, makers,
orderbook, cj_amount)
txdata = taker.receive_utxos(maker_data)
assert txdata[0], "taker.receive_utxos error"
taker_final_result = do_tx_signing(taker, makers, active_orders, txdata)
assert taker_final_result is not False
assert taker.on_finished_callback.status is not False
def test_coinjoin_mixdepth_wrap_taker(monkeypatch, tmpdir, setup_cj):
def raise_exit(i):
raise Exception("sys.exit called")
monkeypatch.setattr(sys, 'exit', raise_exit)
set_commitment_file(str(tmpdir.join('commitments.json')))
MAKER_NUM = 3
wallet_services = make_wallets_to_list(make_wallets(
MAKER_NUM + 1,
wallet_structures=[[4, 0, 0, 0, 0]] * MAKER_NUM + [[0, 0, 0, 0, 3]],
mean_amt=1))
for wallet_service in wallet_services:
assert wallet_service.max_mixdepth == 4
jm_single().bc_interface.tickchain()
jm_single().bc_interface.tickchain()
sync_wallets(wallet_services)
cj_fee = 2000
makers = [YieldGeneratorBasic(
wallet_services[i],
[0, cj_fee, 0, 'swabsoffer', 10**7]) for i in range(MAKER_NUM)]
orderbook = create_orderbook(makers)
assert len(orderbook) == MAKER_NUM
cj_amount = int(1.1 * 10**8)
# mixdepth, amount, counterparties, dest_addr, waittime, rounding
schedule = [(4, cj_amount, MAKER_NUM, 'INTERNAL', 0, NO_ROUNDING)]
taker = create_taker(wallet_services[-1], schedule, monkeypatch)
active_orders, maker_data = init_coinjoin(taker, makers,
orderbook, cj_amount)
txdata = taker.receive_utxos(maker_data)
assert txdata[0], "taker.receive_utxos error"
taker_final_result = do_tx_signing(taker, makers, active_orders, txdata)
assert taker_final_result is not False
tx = btc.deserialize(txdata[2])
binarize_tx(tx)
wallet_service = wallet_services[-1]
# TODO change for new tx monitoring:
wallet_service.remove_old_utxos_(tx)
wallet_service.add_new_utxos_(tx, b'\x00' * 32) # fake txid
balances = wallet_service.get_balance_by_mixdepth()
assert balances[0] == cj_amount
# <= because of tx fee
assert balances[4] <= 3 * 10**8 - cj_amount - (cj_fee * MAKER_NUM)
def test_coinjoin_mixdepth_wrap_maker(monkeypatch, tmpdir, setup_cj):
def raise_exit(i):
raise Exception("sys.exit called")
monkeypatch.setattr(sys, 'exit', raise_exit)
set_commitment_file(str(tmpdir.join('commitments.json')))
MAKER_NUM = 2
wallet_services = make_wallets_to_list(make_wallets(
MAKER_NUM + 1,
wallet_structures=[[0, 0, 0, 0, 4]] * MAKER_NUM + [[3, 0, 0, 0, 0]],
mean_amt=1))
for wallet_service in wallet_services:
assert wallet_service.max_mixdepth == 4
jm_single().bc_interface.tickchain()
jm_single().bc_interface.tickchain()
sync_wallets(wallet_services)
cj_fee = 2000
makers = [YieldGeneratorBasic(
wallet_services[i],
[0, cj_fee, 0, 'swabsoffer', 10**7]) for i in range(MAKER_NUM)]
orderbook = create_orderbook(makers)
assert len(orderbook) == MAKER_NUM
cj_amount = int(1.1 * 10**8)
# mixdepth, amount, counterparties, dest_addr, waittime, rounding
schedule = [(0, cj_amount, MAKER_NUM, 'INTERNAL', 0, NO_ROUNDING)]
taker = create_taker(wallet_services[-1], schedule, monkeypatch)
active_orders, maker_data = init_coinjoin(taker, makers,
orderbook, cj_amount)
txdata = taker.receive_utxos(maker_data)
assert txdata[0], "taker.receive_utxos error"
taker_final_result = do_tx_signing(taker, makers, active_orders, txdata)
assert taker_final_result is not False
tx = btc.deserialize(txdata[2])
binarize_tx(tx)
for i in range(MAKER_NUM):
wallet_service = wallet_services[i]
# TODO as above re: monitoring
wallet_service.remove_old_utxos_(tx)
wallet_service.add_new_utxos_(tx, b'\x00' * 32) # fake txid
balances = wallet_service.get_balance_by_mixdepth()
assert balances[0] == cj_amount
assert balances[4] == 4 * 10**8 - cj_amount + cj_fee
@pytest.mark.parametrize('wallet_cls,wallet_cls_sec', (
(SegwitLegacyWallet, LegacyWallet),
#(LegacyWallet, SegwitLegacyWallet)
))
def test_coinjoin_mixed_maker_addresses(monkeypatch, tmpdir, setup_cj,
wallet_cls, wallet_cls_sec):
set_commitment_file(str(tmpdir.join('commitments.json')))
MAKER_NUM = 2
wallet_services = make_wallets_to_list(make_wallets(
MAKER_NUM + 1,
wallet_structures=[[1, 0, 0, 0, 0]] * MAKER_NUM + [[3, 0, 0, 0, 0]],
mean_amt=1, wallet_cls=wallet_cls))
wallet_services_sec = make_wallets_to_list(make_wallets(
MAKER_NUM,
wallet_structures=[[1, 0, 0, 0, 0]] * MAKER_NUM,
mean_amt=1, wallet_cls=wallet_cls_sec))
for i in range(MAKER_NUM):
wif = wallet_services_sec[i].get_wif(0, False, 0)
wallet_services[i].wallet.import_private_key(0, wif,
key_type=wallet_services_sec[i].wallet.TYPE)
jm_single().bc_interface.tickchain()
jm_single().bc_interface.tickchain()
sync_wallets(wallet_services, fast=False)
makers = [YieldGeneratorBasic(
wallet_services[i],
[0, 2000, 0, 'swabsoffer', 10**7]) for i in range(MAKER_NUM)]
orderbook = create_orderbook(makers)
cj_amount = int(1.1 * 10**8)
# mixdepth, amount, counterparties, dest_addr, waittime, rounding
schedule = [(0, cj_amount, MAKER_NUM, 'INTERNAL', 0, NO_ROUNDING)]
taker = create_taker(wallet_services[-1], schedule, monkeypatch)
active_orders, maker_data = init_coinjoin(taker, makers,
orderbook, cj_amount)
txdata = taker.receive_utxos(maker_data)
assert txdata[0], "taker.receive_utxos error"
taker_final_result = do_tx_signing(taker, makers, active_orders, txdata)
assert taker_final_result is not False
assert taker.on_finished_callback.status is not False
@pytest.fixture(scope='module')
def setup_cj():
load_test_config()
jm_single().config.set('POLICY', 'tx_broadcast', 'self')
jm_single().bc_interface.tick_forward_chain_interval = 5
jm_single().bc_interface.simulate_blocks()
yield None
# teardown
for dc in reactor.getDelayedCalls():
dc.cancel()