Host-customized fork of https://github.com/tecnovert/basicswap/
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.
 
 
 
 
 
 

913 lines
37 KiB

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright (c) 2020-2021 tecnovert
# Distributed under the MIT software license, see the accompanying
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
import os
import json
import time
import random
import shutil
import signal
import logging
import unittest
import traceback
import threading
import subprocess
from urllib.request import urlopen
import basicswap.config as cfg
from basicswap.basicswap import (
BasicSwap,
Coins,
SwapTypes,
BidStates,
DebugTypes,
)
from basicswap.basicswap_util import (
SEQUENCE_LOCK_BLOCKS,
)
from basicswap.util import (
COIN,
toWIF,
make_int,
format_amount,
)
from basicswap.rpc import (
callrpc,
callrpc_cli,
waitForRPC,
)
from basicswap.rpc_xmr import (
callrpc_xmr_na,
callrpc_xmr,
)
from basicswap.interface_xmr import (
XMR_COIN,
)
from basicswap.contrib.key import (
ECKey,
)
from basicswap.http_server import (
HttpThread,
)
from tests.basicswap.common import (
prepareDataDir,
make_rpc_func,
checkForks,
stopDaemons,
wait_for_bid,
wait_for_offer,
wait_for_no_offer,
wait_for_none_active,
wait_for_balance,
post_json_req,
TEST_HTTP_HOST,
TEST_HTTP_PORT,
BASE_RPC_PORT,
BASE_ZMQ_PORT,
BTC_BASE_PORT,
BTC_BASE_RPC_PORT,
PREFIX_SECRET_KEY_REGTEST,
)
from bin.basicswap_run import startDaemon, startXmrDaemon
logger = logging.getLogger()
NUM_NODES = 3
NUM_XMR_NODES = 3
NUM_BTC_NODES = 3
TEST_DIR = cfg.TEST_DATADIRS
XMR_BASE_P2P_PORT = 17792
XMR_BASE_RPC_PORT = 21792
XMR_BASE_ZMQ_PORT = 22792
XMR_BASE_WALLET_RPC_PORT = 23792
test_delay_event = threading.Event()
def prepareXmrDataDir(datadir, node_id, conf_file):
node_dir = os.path.join(datadir, 'xmr_' + str(node_id))
if not os.path.exists(node_dir):
os.makedirs(node_dir)
cfg_file_path = os.path.join(node_dir, conf_file)
if os.path.exists(cfg_file_path):
return
with open(cfg_file_path, 'w+') as fp:
fp.write('regtest=1\n')
fp.write('keep-fakechain=1\n')
fp.write('data-dir={}\n'.format(node_dir))
fp.write('fixed-difficulty=1\n')
# fp.write('offline=1\n')
fp.write('p2p-bind-port={}\n'.format(XMR_BASE_P2P_PORT + node_id))
fp.write('rpc-bind-port={}\n'.format(XMR_BASE_RPC_PORT + node_id))
fp.write('p2p-bind-ip=127.0.0.1\n')
fp.write('rpc-bind-ip=127.0.0.1\n')
fp.write('prune-blockchain=1\n')
fp.write('zmq-rpc-bind-port={}\n'.format(XMR_BASE_ZMQ_PORT + node_id))
fp.write('zmq-rpc-bind-ip=127.0.0.1\n')
for i in range(0, NUM_XMR_NODES):
if node_id == i:
continue
fp.write('add-exclusive-node=127.0.0.1:{}\n'.format(XMR_BASE_P2P_PORT + i))
def startXmrWalletRPC(node_dir, bin_dir, wallet_bin, node_id, opts=[]):
daemon_bin = os.path.expanduser(os.path.join(bin_dir, wallet_bin))
data_dir = os.path.expanduser(node_dir)
args = [daemon_bin]
args += ['--non-interactive']
args += ['--daemon-address=127.0.0.1:{}'.format(XMR_BASE_RPC_PORT + node_id)]
args += ['--no-dns']
args += ['--rpc-bind-port={}'.format(XMR_BASE_WALLET_RPC_PORT + node_id)]
args += ['--wallet-dir={}'.format(os.path.join(data_dir, 'wallets'))]
args += ['--log-file={}'.format(os.path.join(data_dir, 'wallet.log'))]
args += ['--rpc-login=test{0}:test_pass{0}'.format(node_id)]
args += ['--shared-ringdb-dir={}'.format(os.path.join(data_dir, 'shared-ringdb'))]
args += opts
logging.info('Starting daemon {} --wallet-dir={}'.format(daemon_bin, node_dir))
wallet_stdout = open(os.path.join(data_dir, 'wallet_stdout.log'), 'w')
wallet_stderr = open(os.path.join(data_dir, 'wallet_stderr.log'), 'w')
return subprocess.Popen(args, stdin=subprocess.PIPE, stdout=wallet_stdout, stderr=wallet_stderr, cwd=data_dir)
def prepare_swapclient_dir(datadir, node_id, network_key, network_pubkey):
basicswap_dir = os.path.join(datadir, 'basicswap_' + str(node_id))
if not os.path.exists(basicswap_dir):
os.makedirs(basicswap_dir)
settings_path = os.path.join(basicswap_dir, cfg.CONFIG_FILENAME)
settings = {
'debug': True,
'zmqhost': 'tcp://127.0.0.1',
'zmqport': BASE_ZMQ_PORT + node_id,
'htmlhost': '127.0.0.1',
'htmlport': TEST_HTTP_PORT + node_id,
'network_key': network_key,
'network_pubkey': network_pubkey,
'chainclients': {
'particl': {
'connection_type': 'rpc',
'manage_daemon': False,
'rpcport': BASE_RPC_PORT + node_id,
'rpcuser': 'test' + str(node_id),
'rpcpassword': 'test_pass' + str(node_id),
'datadir': os.path.join(datadir, 'part_' + str(node_id)),
'bindir': cfg.PARTICL_BINDIR,
'blocks_confirmed': 2, # Faster testing
},
'monero': {
'connection_type': 'rpc',
'manage_daemon': False,
'rpcport': XMR_BASE_RPC_PORT + node_id,
'walletrpcport': XMR_BASE_WALLET_RPC_PORT + node_id,
'walletrpcuser': 'test' + str(node_id),
'walletrpcpassword': 'test_pass' + str(node_id),
'walletfile': 'testwallet',
'datadir': os.path.join(datadir, 'xmr_' + str(node_id)),
'bindir': cfg.XMR_BINDIR,
},
'bitcoin': {
'connection_type': 'rpc',
'manage_daemon': False,
'rpcport': BTC_BASE_RPC_PORT + node_id,
'rpcuser': 'test' + str(node_id),
'rpcpassword': 'test_pass' + str(node_id),
'datadir': os.path.join(datadir, 'btc_' + str(node_id)),
'bindir': cfg.BITCOIN_BINDIR,
'use_segwit': True,
}
},
'check_progress_seconds': 2,
'check_watched_seconds': 4,
'check_expired_seconds': 60,
'check_events_seconds': 1,
'check_xmr_swaps_seconds': 1,
'min_delay_event': 1,
'max_delay_event': 5,
'min_delay_retry': 2,
'max_delay_retry': 10
}
with open(settings_path, 'w') as fp:
json.dump(settings, fp, indent=4)
def btcRpc(cmd, node_id=0):
return callrpc_cli(cfg.BITCOIN_BINDIR, os.path.join(TEST_DIR, 'btc_' + str(node_id)), 'regtest', cmd, cfg.BITCOIN_CLI)
def signal_handler(sig, frame):
logging.info('signal {} detected.'.format(sig))
test_delay_event.set()
def waitForXMRNode(rpc_offset, max_tries=7):
for i in range(max_tries + 1):
try:
callrpc_xmr_na(XMR_BASE_RPC_PORT + rpc_offset, 'get_block_count')
return
except Exception as ex:
if i < max_tries:
logging.warning('Can\'t connect to XMR RPC: %s. Retrying in %d second/s.', str(ex), (i + 1))
time.sleep(i + 1)
raise ValueError('waitForXMRNode failed')
def waitForXMRWallet(rpc_offset, auth, max_tries=7):
for i in range(max_tries + 1):
try:
callrpc_xmr(XMR_BASE_WALLET_RPC_PORT + rpc_offset, auth, 'get_languages')
return
except Exception as ex:
if i < max_tries:
logging.warning('Can\'t connect to XMR wallet RPC: %s. Retrying in %d second/s.', str(ex), (i + 1))
time.sleep(i + 1)
raise ValueError('waitForXMRWallet failed')
def callnoderpc(node_id, method, params=[], wallet=None, base_rpc_port=BASE_RPC_PORT):
auth = 'test{0}:test_pass{0}'.format(node_id)
return callrpc(base_rpc_port + node_id, auth, method, params, wallet)
pause_event = threading.Event()
def run_coins_loop(cls):
while not test_delay_event.is_set():
pause_event.wait()
try:
if cls.btc_addr is not None:
btcRpc('generatetoaddress 1 {}'.format(cls.btc_addr))
if cls.xmr_addr is not None:
callrpc_xmr_na(XMR_BASE_RPC_PORT + 1, 'generateblocks', {'wallet_address': cls.xmr_addr, 'amount_of_blocks': 1})
except Exception as e:
logging.warning('run_coins_loop ' + str(e))
test_delay_event.wait(1.0)
def run_loop(cls):
while not test_delay_event.is_set():
for c in cls.swap_clients:
c.update()
test_delay_event.wait(1.0)
class Test(unittest.TestCase):
@classmethod
def setUpClass(cls):
super(Test, cls).setUpClass()
random.seed(time.time())
cls.update_thread = None
cls.coins_update_thread = None
cls.http_threads = []
cls.swap_clients = []
cls.part_daemons = []
cls.btc_daemons = []
cls.xmr_daemons = []
cls.xmr_wallet_auth = []
cls.xmr_addr = None
cls.btc_addr = None
logger.propagate = False
logger.handlers = []
logger.setLevel(logging.INFO) # DEBUG shows many messages from requests.post
formatter = logging.Formatter('%(asctime)s %(levelname)s : %(message)s')
stream_stdout = logging.StreamHandler()
stream_stdout.setFormatter(formatter)
logger.addHandler(stream_stdout)
if os.path.isdir(TEST_DIR):
logging.info('Removing ' + TEST_DIR)
shutil.rmtree(TEST_DIR)
if not os.path.exists(TEST_DIR):
os.makedirs(TEST_DIR)
cls.stream_fp = logging.FileHandler(os.path.join(TEST_DIR, 'test.log'))
cls.stream_fp.setFormatter(formatter)
logger.addHandler(cls.stream_fp)
try:
logging.info('Preparing coin nodes.')
for i in range(NUM_NODES):
data_dir = prepareDataDir(TEST_DIR, i, 'particl.conf', 'part_')
if os.path.exists(os.path.join(cfg.PARTICL_BINDIR, 'particl-wallet')):
callrpc_cli(cfg.PARTICL_BINDIR, data_dir, 'regtest', '-wallet=wallet.dat create', 'particl-wallet')
cls.part_daemons.append(startDaemon(os.path.join(TEST_DIR, 'part_' + str(i)), cfg.PARTICL_BINDIR, cfg.PARTICLD))
logging.info('Started %s %d', cfg.PARTICLD, cls.part_daemons[-1].pid)
for i in range(NUM_NODES):
# Load mnemonics after all nodes have started to avoid staking getting stuck in TryToSync
rpc = make_rpc_func(i)
waitForRPC(rpc)
if i == 0:
rpc('extkeyimportmaster', ['abandon baby cabbage dad eager fabric gadget habit ice kangaroo lab absorb'])
elif i == 1:
rpc('extkeyimportmaster', ['pact mammal barrel matrix local final lecture chunk wasp survey bid various book strong spread fall ozone daring like topple door fatigue limb olympic', '', 'true'])
rpc('getnewextaddress', ['lblExtTest'])
rpc('rescanblockchain')
else:
rpc('extkeyimportmaster', [rpc('mnemonic', ['new'])['master']])
# Lower output split threshold for more stakeable outputs
rpc('walletsettings', ['stakingoptions', {'stakecombinethreshold': 100, 'stakesplitthreshold': 200}])
for i in range(NUM_BTC_NODES):
data_dir = prepareDataDir(TEST_DIR, i, 'bitcoin.conf', 'btc_', base_p2p_port=BTC_BASE_PORT, base_rpc_port=BTC_BASE_RPC_PORT)
if os.path.exists(os.path.join(cfg.BITCOIN_BINDIR, 'bitcoin-wallet')):
callrpc_cli(cfg.BITCOIN_BINDIR, data_dir, 'regtest', '-wallet=wallet.dat create', 'bitcoin-wallet')
cls.btc_daemons.append(startDaemon(os.path.join(TEST_DIR, 'btc_' + str(i)), cfg.BITCOIN_BINDIR, cfg.BITCOIND))
logging.info('Started %s %d', cfg.BITCOIND, cls.part_daemons[-1].pid)
waitForRPC(make_rpc_func(i, base_rpc_port=BTC_BASE_RPC_PORT))
for i in range(NUM_XMR_NODES):
prepareXmrDataDir(TEST_DIR, i, 'monerod.conf')
cls.xmr_daemons.append(startXmrDaemon(os.path.join(TEST_DIR, 'xmr_' + str(i)), cfg.XMR_BINDIR, cfg.XMRD))
logging.info('Started %s %d', cfg.XMRD, cls.xmr_daemons[-1].pid)
waitForXMRNode(i)
cls.xmr_daemons.append(startXmrWalletRPC(os.path.join(TEST_DIR, 'xmr_' + str(i)), cfg.XMR_BINDIR, cfg.XMR_WALLET_RPC, i))
for i in range(NUM_XMR_NODES):
cls.xmr_wallet_auth.append(('test{0}'.format(i), 'test_pass{0}'.format(i)))
logging.info('Creating XMR wallet %i', i)
waitForXMRWallet(i, cls.xmr_wallet_auth[i])
cls.callxmrnodewallet(cls, i, 'create_wallet', {'filename': 'testwallet', 'language': 'English'})
cls.callxmrnodewallet(cls, i, 'open_wallet', {'filename': 'testwallet'})
logging.info('Preparing swap clients.')
eckey = ECKey()
eckey.generate()
cls.network_key = toWIF(PREFIX_SECRET_KEY_REGTEST, eckey.get_bytes())
cls.network_pubkey = eckey.get_pubkey().get_bytes().hex()
for i in range(NUM_NODES):
prepare_swapclient_dir(TEST_DIR, i, cls.network_key, cls.network_pubkey)
basicswap_dir = os.path.join(os.path.join(TEST_DIR, 'basicswap_' + str(i)))
settings_path = os.path.join(basicswap_dir, cfg.CONFIG_FILENAME)
with open(settings_path) as fs:
settings = json.load(fs)
fp = open(os.path.join(basicswap_dir, 'basicswap.log'), 'w')
sc = BasicSwap(fp, basicswap_dir, settings, 'regtest', log_name='BasicSwap{}'.format(i))
sc.setDaemonPID(Coins.BTC, cls.btc_daemons[i].pid)
sc.setDaemonPID(Coins.PART, cls.part_daemons[i].pid)
sc.start()
# Set XMR main wallet address
xmr_ci = sc.ci(Coins.XMR)
sc.setStringKV('main_wallet_addr_' + xmr_ci.coin_name().lower(), xmr_ci.getMainWalletAddress())
cls.swap_clients.append(sc)
t = HttpThread(cls.swap_clients[i].fp, TEST_HTTP_HOST, TEST_HTTP_PORT + i, False, cls.swap_clients[i])
cls.http_threads.append(t)
t.start()
cls.btc_addr = callnoderpc(0, 'getnewaddress', ['mining_addr', 'bech32'], base_rpc_port=BTC_BASE_RPC_PORT)
cls.xmr_addr = cls.callxmrnodewallet(cls, 1, 'get_address')['address']
num_blocks = 500 # Mine enough to activate segwit
logging.info('Mining %d Bitcoin blocks to %s', num_blocks, cls.btc_addr)
callnoderpc(0, 'generatetoaddress', [num_blocks, cls.btc_addr], base_rpc_port=BTC_BASE_RPC_PORT)
checkForks(callnoderpc(0, 'getblockchaininfo', base_rpc_port=BTC_BASE_RPC_PORT))
num_blocks = 100
if callrpc_xmr_na(XMR_BASE_RPC_PORT + 1, 'get_block_count')['count'] < num_blocks:
logging.info('Mining %d Monero blocks to %s.', num_blocks, cls.xmr_addr)
callrpc_xmr_na(XMR_BASE_RPC_PORT + 1, 'generateblocks', {'wallet_address': cls.xmr_addr, 'amount_of_blocks': num_blocks})
logging.info('XMR blocks: %d', callrpc_xmr_na(XMR_BASE_RPC_PORT + 1, 'get_block_count')['count'])
logging.info('Adding anon outputs')
outputs = []
for i in range(8):
sx_addr = callnoderpc(1, 'getnewstealthaddress')
outputs.append({'address': sx_addr, 'amount': 0.5})
for i in range(6):
callnoderpc(0, 'sendtypeto', ['part', 'anon', outputs])
logging.info('Starting update thread.')
signal.signal(signal.SIGINT, signal_handler)
cls.update_thread = threading.Thread(target=run_loop, args=(cls,))
cls.update_thread.start()
pause_event.set()
cls.coins_update_thread = threading.Thread(target=run_coins_loop, args=(cls,))
cls.coins_update_thread.start()
except Exception:
traceback.print_exc()
Test.tearDownClass()
raise ValueError('setUpClass() failed.')
@classmethod
def tearDownClass(cls):
logging.info('Finalising')
test_delay_event.set()
if cls.update_thread is not None:
try:
cls.update_thread.join()
except Exception:
logging.info('Failed to join update_thread')
if cls.coins_update_thread is not None:
try:
cls.coins_update_thread.join()
except Exception:
logging.info('Failed to join coins_update_thread')
for t in cls.http_threads:
t.stop()
t.join()
for c in cls.swap_clients:
c.finalise()
c.fp.close()
stopDaemons(cls.xmr_daemons)
stopDaemons(cls.part_daemons)
stopDaemons(cls.btc_daemons)
super(Test, cls).tearDownClass()
def callxmrnodewallet(self, node_id, method, params=None):
return callrpc_xmr(XMR_BASE_WALLET_RPC_PORT + node_id, self.xmr_wallet_auth[node_id], method, params)
def test_01_part_xmr(self):
logging.info('---------- Test PART to XMR')
swap_clients = self.swap_clients
js_1 = json.loads(urlopen('http://127.0.0.1:1801/json/wallets').read())
assert(make_int(js_1[str(int(Coins.XMR))]['balance'], scale=12) > 0)
assert(make_int(js_1[str(int(Coins.XMR))]['unconfirmed'], scale=12) > 0)
offer_id = swap_clients[0].postOffer(Coins.PART, Coins.XMR, 100 * COIN, 0.11 * XMR_COIN, 100 * COIN, SwapTypes.XMR_SWAP)
wait_for_offer(test_delay_event, swap_clients[1], offer_id)
offers = swap_clients[1].listOffers(filters={'offer_id': offer_id})
assert(len(offers) == 1)
offer = offers[0]
bid_id = swap_clients[1].postXmrBid(offer_id, offer.amount_from)
wait_for_bid(test_delay_event, swap_clients[0], bid_id, BidStates.BID_RECEIVED)
bid, xmr_swap = swap_clients[0].getXmrBid(bid_id)
assert(xmr_swap)
swap_clients[0].acceptXmrBid(bid_id)
wait_for_bid(test_delay_event, swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, wait_for=180)
wait_for_bid(test_delay_event, swap_clients[1], bid_id, BidStates.SWAP_COMPLETED, sent=True)
js_0_end = json.loads(urlopen('http://127.0.0.1:1800/json/wallets').read())
end_xmr = float(js_0_end['6']['balance']) + float(js_0_end['6']['unconfirmed'])
assert(end_xmr > 10.9 and end_xmr < 11.0)
def test_011_smsgaddresses(self):
logging.info('---------- Test address management and private offers')
swap_clients = self.swap_clients
js_1 = json.loads(urlopen('http://127.0.0.1:1801/json/smsgaddresses').read())
post_json = {
'addressnote': 'testing',
}
json_rv = json.loads(post_json_req('http://127.0.0.1:1801/json/smsgaddresses/new', post_json))
new_address = json_rv['new_address']
new_address_pk = json_rv['pubkey']
js_2 = json.loads(urlopen('http://127.0.0.1:1801/json/smsgaddresses').read())
assert(len(js_2) == len(js_1) + 1)
found = False
for addr in js_2:
if addr['addr'] == new_address:
assert(addr['note'] == 'testing')
found = True
assert(found is True)
found = False
lks = callnoderpc(1, 'smsglocalkeys')
for key in lks['wallet_keys']:
if key['address'] == new_address:
assert(key['receive'] == '1')
found = True
assert(found is True)
# Disable
post_json = {
'address': new_address,
'addressnote': 'testing2',
'active_ind': '0',
}
json_rv = json.loads(post_json_req('http://127.0.0.1:1801/json/smsgaddresses/edit', post_json))
assert(json_rv['edited_address'] == new_address)
js_3 = json.loads(urlopen('http://127.0.0.1:1801/json/smsgaddresses').read())
found = False
for addr in js_3:
if addr['addr'] == new_address:
assert(addr['note'] == 'testing2')
assert(addr['active_ind'] == 0)
found = True
assert(found is True)
found = False
lks = callnoderpc(1, 'smsglocalkeys')
for key in lks['wallet_keys']:
if key['address'] == new_address:
found = True
assert(found is False)
# Re-enable
post_json = {
'address': new_address,
'active_ind': '1',
}
json_rv = json.loads(post_json_req('http://127.0.0.1:1801/json/smsgaddresses/edit', post_json))
assert(json_rv['edited_address'] == new_address)
found = False
lks = callnoderpc(1, 'smsglocalkeys')
for key in lks['wallet_keys']:
if key['address'] == new_address:
assert(key['receive'] == '1')
found = True
assert(found is True)
post_json = {
'addresspubkey': new_address_pk,
'addressnote': 'testing_add_addr',
}
json_rv = json.loads(post_json_req('http://127.0.0.1:1800/json/smsgaddresses/add', post_json))
assert(json_rv['added_address'] == new_address)
post_json = {
'addr_to': new_address,
'addr_from': -1,
'coin_from': 1,
'coin_to': 6,
'amt_from': 1,
'amt_to': 1,
'lockhrs': 24,
'autoaccept': True}
rv = json.loads(post_json_req('http://127.0.0.1:1800/json/offers/new', post_json))
offer_id_hex = rv['offer_id']
wait_for_offer(test_delay_event, swap_clients[1], bytes.fromhex(offer_id_hex))
rv = json.loads(urlopen(f'http://127.0.0.1:1801/json/offers/{offer_id_hex}').read())
assert(rv[0]['addr_to'] == new_address)
rv = json.loads(urlopen(f'http://127.0.0.1:1800/json/offers/{offer_id_hex}').read())
assert(rv[0]['addr_to'] == new_address)
def test_02_leader_recover_a_lock_tx(self):
logging.info('---------- Test PART to XMR leader recovers coin a lock tx')
swap_clients = self.swap_clients
js_w0_before = json.loads(urlopen('http://127.0.0.1:1800/json/wallets').read())
offer_id = swap_clients[0].postOffer(
Coins.PART, Coins.XMR, 101 * COIN, 0.12 * XMR_COIN, 101 * COIN, SwapTypes.XMR_SWAP,
lock_type=SEQUENCE_LOCK_BLOCKS, lock_value=12)
wait_for_offer(test_delay_event, swap_clients[1], offer_id)
offer = swap_clients[1].getOffer(offer_id)
bid_id = swap_clients[1].postXmrBid(offer_id, offer.amount_from)
wait_for_bid(test_delay_event, swap_clients[0], bid_id, BidStates.BID_RECEIVED)
bid, xmr_swap = swap_clients[0].getXmrBid(bid_id)
assert(xmr_swap)
swap_clients[1].setBidDebugInd(bid_id, DebugTypes.BID_STOP_AFTER_COIN_A_LOCK)
swap_clients[0].acceptXmrBid(bid_id)
wait_for_bid(test_delay_event, swap_clients[0], bid_id, BidStates.XMR_SWAP_FAILED_REFUNDED, wait_for=180)
wait_for_bid(test_delay_event, swap_clients[1], bid_id, BidStates.XMR_SWAP_FAILED_REFUNDED, sent=True)
js_w0_after = json.loads(urlopen('http://127.0.0.1:1800/json/wallets').read())
print('[rm] js_w0_before', json.dumps(js_w0_before))
print('[rm] js_w0_after', json.dumps(js_w0_after))
def test_03_follower_recover_a_lock_tx(self):
logging.info('---------- Test PART to XMR follower recovers coin a lock tx')
swap_clients = self.swap_clients
js_w0_before = json.loads(urlopen('http://127.0.0.1:1800/json/wallets').read())
offer_id = swap_clients[0].postOffer(
Coins.PART, Coins.XMR, 101 * COIN, 0.13 * XMR_COIN, 101 * COIN, SwapTypes.XMR_SWAP,
lock_type=SEQUENCE_LOCK_BLOCKS, lock_value=12)
wait_for_offer(test_delay_event, swap_clients[1], offer_id)
offer = swap_clients[1].getOffer(offer_id)
bid_id = swap_clients[1].postXmrBid(offer_id, offer.amount_from)
wait_for_bid(test_delay_event, swap_clients[0], bid_id, BidStates.BID_RECEIVED)
bid, xmr_swap = swap_clients[0].getXmrBid(bid_id)
assert(xmr_swap)
swap_clients[1].setBidDebugInd(bid_id, DebugTypes.BID_STOP_AFTER_COIN_A_LOCK)
swap_clients[0].setBidDebugInd(bid_id, DebugTypes.BID_DONT_SPEND_COIN_A_LOCK_REFUND)
swap_clients[0].acceptXmrBid(bid_id)
wait_for_bid(test_delay_event, swap_clients[0], bid_id, BidStates.BID_STALLED_FOR_TEST, wait_for=180)
wait_for_bid(test_delay_event, swap_clients[1], bid_id, BidStates.XMR_SWAP_FAILED_SWIPED, wait_for=80, sent=True)
js_w0_after = json.loads(urlopen('http://127.0.0.1:1800/json/wallets').read())
wait_for_none_active(test_delay_event, 1800)
wait_for_none_active(test_delay_event, 1801)
def test_04_follower_recover_b_lock_tx(self):
logging.info('---------- Test PART to XMR follower recovers coin b lock tx')
swap_clients = self.swap_clients
offer_id = swap_clients[0].postOffer(
Coins.PART, Coins.XMR, 101 * COIN, 0.14 * XMR_COIN, 101 * COIN, SwapTypes.XMR_SWAP,
lock_type=SEQUENCE_LOCK_BLOCKS, lock_value=18)
wait_for_offer(test_delay_event, swap_clients[1], offer_id)
offer = swap_clients[1].getOffer(offer_id)
bid_id = swap_clients[1].postXmrBid(offer_id, offer.amount_from)
wait_for_bid(test_delay_event, swap_clients[0], bid_id, BidStates.BID_RECEIVED)
bid, xmr_swap = swap_clients[0].getXmrBid(bid_id)
assert(xmr_swap)
swap_clients[1].setBidDebugInd(bid_id, DebugTypes.CREATE_INVALID_COIN_B_LOCK)
swap_clients[0].acceptXmrBid(bid_id)
wait_for_bid(test_delay_event, swap_clients[0], bid_id, BidStates.XMR_SWAP_FAILED_REFUNDED, wait_for=180)
wait_for_bid(test_delay_event, swap_clients[1], bid_id, BidStates.XMR_SWAP_FAILED_REFUNDED, sent=True)
def test_05_btc_xmr(self):
logging.info('---------- Test BTC to XMR')
swap_clients = self.swap_clients
offer_id = swap_clients[0].postOffer(Coins.BTC, Coins.XMR, 10 * COIN, 100 * XMR_COIN, 10 * COIN, SwapTypes.XMR_SWAP)
wait_for_offer(test_delay_event, swap_clients[1], offer_id)
offers = swap_clients[1].listOffers(filters={'offer_id': offer_id})
offer = offers[0]
swap_clients[1].ci(Coins.XMR).setFeePriority(3)
bid_id = swap_clients[1].postXmrBid(offer_id, offer.amount_from)
wait_for_bid(test_delay_event, swap_clients[0], bid_id, BidStates.BID_RECEIVED)
bid, xmr_swap = swap_clients[0].getXmrBid(bid_id)
assert(xmr_swap)
swap_clients[0].acceptXmrBid(bid_id)
wait_for_bid(test_delay_event, swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, wait_for=180)
wait_for_bid(test_delay_event, swap_clients[1], bid_id, BidStates.SWAP_COMPLETED, sent=True)
swap_clients[1].ci(Coins.XMR).setFeePriority(0)
def test_06_multiple_swaps(self):
logging.info('---------- Test Multiple concurrent swaps')
swap_clients = self.swap_clients
js_w0_before = json.loads(urlopen('http://127.0.0.1:1800/json/wallets').read())
js_w1_before = json.loads(urlopen('http://127.0.0.1:1801/json/wallets').read())
amt_1 = make_int(random.uniform(0.001, 49.0), scale=8, r=1)
amt_2 = make_int(random.uniform(0.001, 49.0), scale=8, r=1)
rate_1 = make_int(random.uniform(80.0, 110.0), scale=12, r=1)
rate_2 = make_int(random.uniform(0.01, 0.5), scale=12, r=1)
logging.info('amt_1 {}, rate_1 {}'.format(amt_1, rate_1))
logging.info('amt_2 {}, rate_2 {}'.format(amt_2, rate_2))
offer1_id = swap_clients[0].postOffer(Coins.BTC, Coins.XMR, amt_1, rate_1, amt_1, SwapTypes.XMR_SWAP)
offer2_id = swap_clients[0].postOffer(Coins.PART, Coins.XMR, amt_2, rate_2, amt_2, SwapTypes.XMR_SWAP)
wait_for_offer(test_delay_event, swap_clients[1], offer1_id)
offer1 = swap_clients[1].getOffer(offer1_id)
wait_for_offer(test_delay_event, swap_clients[1], offer2_id)
offer2 = swap_clients[1].getOffer(offer2_id)
bid1_id = swap_clients[1].postXmrBid(offer1_id, offer1.amount_from)
bid2_id = swap_clients[1].postXmrBid(offer2_id, offer2.amount_from)
offer3_id = swap_clients[0].postOffer(Coins.PART, Coins.XMR, 11 * COIN, 0.15 * XMR_COIN, 11 * COIN, SwapTypes.XMR_SWAP)
wait_for_bid(test_delay_event, swap_clients[0], bid1_id, BidStates.BID_RECEIVED)
swap_clients[0].acceptXmrBid(bid1_id)
wait_for_offer(test_delay_event, swap_clients[1], offer3_id)
offer3 = swap_clients[1].getOffer(offer3_id)
bid3_id = swap_clients[1].postXmrBid(offer3_id, offer3.amount_from)
wait_for_bid(test_delay_event, swap_clients[0], bid2_id, BidStates.BID_RECEIVED)
swap_clients[0].acceptXmrBid(bid2_id)
wait_for_bid(test_delay_event, swap_clients[0], bid3_id, BidStates.BID_RECEIVED)
swap_clients[0].acceptXmrBid(bid3_id)
wait_for_bid(test_delay_event, swap_clients[0], bid1_id, BidStates.SWAP_COMPLETED, wait_for=180)
wait_for_bid(test_delay_event, swap_clients[1], bid1_id, BidStates.SWAP_COMPLETED, sent=True)
wait_for_bid(test_delay_event, swap_clients[0], bid2_id, BidStates.SWAP_COMPLETED, wait_for=120)
wait_for_bid(test_delay_event, swap_clients[1], bid2_id, BidStates.SWAP_COMPLETED, sent=True)
wait_for_bid(test_delay_event, swap_clients[0], bid3_id, BidStates.SWAP_COMPLETED, wait_for=120)
wait_for_bid(test_delay_event, swap_clients[1], bid3_id, BidStates.SWAP_COMPLETED, sent=True)
wait_for_none_active(test_delay_event, 1800)
wait_for_none_active(test_delay_event, 1801)
js_w0_after = json.loads(urlopen('http://127.0.0.1:1800/json/wallets').read())
js_w1_after = json.loads(urlopen('http://127.0.0.1:1801/json/wallets').read())
logging.info('[rm] js_w0_after {}'.format(json.dumps(js_w0_after, indent=4)))
logging.info('[rm] js_w1_after {}'.format(json.dumps(js_w1_after, indent=4)))
assert(make_int(js_w1_after['2']['balance'], scale=8, r=1) - (make_int(js_w1_before['2']['balance'], scale=8, r=1) + amt_1) < 1000)
def test_07_revoke_offer(self):
logging.info('---------- Test offer revocaction')
swap_clients = self.swap_clients
offer_id = swap_clients[0].postOffer(Coins.BTC, Coins.XMR, 10 * COIN, 100 * XMR_COIN, 10 * COIN, SwapTypes.XMR_SWAP)
wait_for_offer(test_delay_event, swap_clients[1], offer_id)
swap_clients[0].revokeOffer(offer_id)
wait_for_no_offer(test_delay_event, swap_clients[1], offer_id)
def test_08_withdraw(self):
logging.info('---------- Test XMR withdrawals')
swap_clients = self.swap_clients
js_0 = json.loads(urlopen('http://127.0.0.1:1800/json/wallets').read())
address_to = js_0[str(int(Coins.XMR))]['deposit_address']
js_1 = json.loads(urlopen('http://127.0.0.1:1801/json/wallets').read())
assert(float(js_1[str(int(Coins.XMR))]['balance']) > 0.0)
swap_clients[1].withdrawCoin(Coins.XMR, 1.1, address_to, False)
def test_09_auto_accept(self):
logging.info('---------- Test BTC to XMR auto accept')
swap_clients = self.swap_clients
offer_id = swap_clients[0].postOffer(Coins.BTC, Coins.XMR, 11 * COIN, 101 * XMR_COIN, 10 * COIN, SwapTypes.XMR_SWAP, auto_accept_bids=True)
wait_for_offer(test_delay_event, swap_clients[1], offer_id)
offer = swap_clients[1].listOffers(filters={'offer_id': offer_id})[0]
bid_id = swap_clients[1].postXmrBid(offer_id, offer.amount_from)
wait_for_bid(test_delay_event, swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, wait_for=180)
wait_for_bid(test_delay_event, swap_clients[1], bid_id, BidStates.SWAP_COMPLETED, sent=True)
def test_10_locked_refundtx(self):
logging.info('---------- Test Refund tx is locked')
swap_clients = self.swap_clients
offer_id = swap_clients[0].postOffer(Coins.BTC, Coins.XMR, 10 * COIN, 100 * XMR_COIN, 10 * COIN, SwapTypes.XMR_SWAP)
wait_for_offer(test_delay_event, swap_clients[1], offer_id)
offers = swap_clients[1].listOffers(filters={'offer_id': offer_id})
offer = offers[0]
bid_id = swap_clients[1].postXmrBid(offer_id, offer.amount_from)
wait_for_bid(test_delay_event, swap_clients[0], bid_id, BidStates.BID_RECEIVED)
bid, xmr_swap = swap_clients[0].getXmrBid(bid_id)
assert(xmr_swap)
swap_clients[1].setBidDebugInd(bid_id, DebugTypes.BID_STOP_AFTER_COIN_A_LOCK)
swap_clients[0].acceptXmrBid(bid_id)
wait_for_bid(test_delay_event, swap_clients[0], bid_id, BidStates.XMR_SWAP_SCRIPT_COIN_LOCKED, wait_for=180)
bid, xmr_swap = swap_clients[0].getXmrBid(bid_id)
assert(xmr_swap)
try:
swap_clients[0].ci(Coins.BTC).publishTx(xmr_swap.a_lock_refund_tx)
assert(False), 'Lock refund tx should be locked'
except Exception as e:
assert('non-BIP68-final' in str(e))
def test_11_particl_anon(self):
logging.info('---------- Test Particl anon transactions')
swap_clients = self.swap_clients
js_0 = json.loads(urlopen('http://127.0.0.1:1800/json/wallets/part').read())
assert(float(js_0['anon_balance']) == 0.0)
node0_anon_before = js_0['anon_balance'] + js_0['anon_pending']
wait_for_balance(test_delay_event, 'http://127.0.0.1:1801/json/wallets/part', 'balance', 200.0)
js_1 = json.loads(urlopen('http://127.0.0.1:1801/json/wallets/part').read())
assert(float(js_1['balance']) > 200.0)
node1_anon_before = js_1['anon_balance'] + js_1['anon_pending']
callnoderpc(1, 'reservebalance', [True, 1000000]) # Stop staking to avoid conflicts (input used by tx->anon staked before tx gets in the chain)
post_json = {
'value': 100,
'address': js_1['stealth_address'],
'subfee': False,
'type_to': 'anon',
}
json_rv = json.loads(post_json_req('http://127.0.0.1:1801/json/wallets/part/withdraw', post_json))
assert(len(json_rv['txid']) == 64)
logging.info('Waiting for anon balance')
wait_for_balance(test_delay_event, 'http://127.0.0.1:1801/json/wallets/part', 'anon_balance', 100.0 + node1_anon_before)
js_1 = json.loads(urlopen('http://127.0.0.1:1801/json/wallets/part').read())
node1_anon_before = js_1['anon_balance'] + js_1['anon_pending']
callnoderpc(1, 'reservebalance', [False])
post_json = {
'value': 10,
'address': js_0['stealth_address'],
'subfee': True,
'type_from': 'anon',
'type_to': 'blind',
}
json_rv = json.loads(post_json_req('http://127.0.0.1:1801/json/wallets/part/withdraw', post_json))
assert(len(json_rv['txid']) == 64)
logging.info('Waiting for blind balance')
wait_for_balance(test_delay_event, 'http://127.0.0.1:1800/json/wallets/part', 'blind_balance', 9.8)
if float(js_0['blind_balance']) >= 10.0:
raise ValueError('Expect blind balance < 10')
amt_swap = make_int(random.uniform(0.1, 2.0), scale=8, r=1)
rate_swap = make_int(random.uniform(2.0, 20.0), scale=8, r=1)
offer_id = swap_clients[0].postOffer(Coins.BTC, Coins.PART_ANON, amt_swap, rate_swap, amt_swap, SwapTypes.XMR_SWAP)
wait_for_offer(test_delay_event, swap_clients[1], offer_id)
offers = swap_clients[0].listOffers(filters={'offer_id': offer_id})
offer = offers[0]
bid_id = swap_clients[1].postXmrBid(offer_id, offer.amount_from)
wait_for_bid(test_delay_event, swap_clients[0], bid_id, BidStates.BID_RECEIVED)
bid, xmr_swap = swap_clients[0].getXmrBid(bid_id)
assert(xmr_swap)
amount_to = float(format_amount(bid.amount_to, 8))
swap_clients[0].acceptXmrBid(bid_id)
wait_for_bid(test_delay_event, swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, wait_for=180)
wait_for_bid(test_delay_event, swap_clients[1], bid_id, BidStates.SWAP_COMPLETED, sent=True)
js_1 = json.loads(urlopen('http://127.0.0.1:1801/json/wallets/part').read())
assert(js_1['anon_balance'] < node1_anon_before - amount_to)
js_0 = json.loads(urlopen('http://127.0.0.1:1800/json/wallets/part').read())
assert(js_0['anon_balance'] + js_0['anon_pending'] > node0_anon_before + (amount_to - 0.1))
def test_12_particl_blind(self):
return # TODO
def test_98_withdraw_all(self):
logging.info('---------- Test XMR withdrawal all')
try:
logging.info('Disabling XMR mining')
pause_event.clear()
js_0 = json.loads(urlopen('http://127.0.0.1:1800/json/wallets').read())
address_to = js_0[str(int(Coins.XMR))]['deposit_address']
wallets1 = json.loads(urlopen('http://127.0.0.1:{}/json/wallets'.format(TEST_HTTP_PORT + 1)).read())
xmr_total = float(wallets1[str(int(Coins.XMR))]['balance'])
assert(xmr_total > 10)
post_json = {
'value': 10,
'address': address_to,
'subfee': True,
}
json_rv = json.loads(post_json_req('http://127.0.0.1:{}/json/wallets/xmr/withdraw'.format(TEST_HTTP_PORT + 1), post_json))
assert(json_rv['error'] == 'Withdraw value must be close to total to use subfee/sweep_all.')
post_json['value'] = xmr_total
json_rv = json.loads(post_json_req('http://127.0.0.1:{}/json/wallets/xmr/withdraw'.format(TEST_HTTP_PORT + 1), post_json))
assert(len(json_rv['txid']) == 64)
finally:
logging.info('Restoring XMR mining')
pause_event.set()
if __name__ == '__main__':
unittest.main()