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.
 
 
 
 

329 lines
11 KiB

from __future__ import (absolute_import, division,
print_function, unicode_literals)
from builtins import * # noqa: F401
"""
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_program_config, jm_single,\
YieldGeneratorBasic, Taker, LegacyWallet, SegwitLegacyWallet,\
NO_ROUNDING
from jmclient.podle import set_commitment_file
from commontest import make_wallets, binarize_tx
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, 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_program_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()