tests: Start wallet restore test.

Fix LTC pidfile in config.
Update LTC onion port for core version 21.
This commit is contained in:
tecnovert 2022-07-15 16:38:05 +02:00
parent a2830afc06
commit ede01d3fc8
No known key found for this signature in database
GPG key ID: 8ED6D8750C4E3F93
9 changed files with 232 additions and 78 deletions

View file

@ -507,8 +507,9 @@ class BasicSwap(BaseApp):
for i in range(20):
try:
# Workaround for mismatched pid file name in litecoin 0.21.2
# Also set with pid= in .conf
# TODO: Remove
if cc['name'] == 'litecoin' and not os.path.exists(pidfilepath) and \
if cc['name'] == 'litecoin' and (not os.path.exists(pidfilepath)) and \
os.path.exists(os.path.join(self.getChainDatadirPath(coin), 'bitcoind.pid')):
pidfilepath = os.path.join(self.getChainDatadirPath(coin), 'bitcoind.pid')
@ -517,7 +518,7 @@ class BasicSwap(BaseApp):
assert(datadir_pid == cc['pid']), 'Mismatched pid'
assert(os.path.exists(authcookiepath))
except Exception:
time.sleep(0.5)
self.delay_event.wait(0.5)
try:
if os.name != 'nt' or cc['core_version_group'] > 17: # Litecoin on windows doesn't write a pid file
assert(datadir_pid == cc['pid']), 'Mismatched pid'

View file

@ -98,12 +98,12 @@ BTC_RPC_HOST = os.getenv('BTC_RPC_HOST', '127.0.0.1')
NMC_RPC_HOST = os.getenv('NMC_RPC_HOST', '127.0.0.1')
PART_RPC_PORT = int(os.getenv('PART_RPC_PORT', 19792))
LTC_RPC_PORT = int(os.getenv('LTC_RPC_PORT', 19795))
BTC_RPC_PORT = int(os.getenv('BTC_RPC_PORT', 19796))
NMC_RPC_PORT = int(os.getenv('NMC_RPC_PORT', 19798))
LTC_RPC_PORT = int(os.getenv('LTC_RPC_PORT', 19895))
BTC_RPC_PORT = int(os.getenv('BTC_RPC_PORT', 19996))
NMC_RPC_PORT = int(os.getenv('NMC_RPC_PORT', 19698))
PART_ONION_PORT = int(os.getenv('PART_ONION_PORT', 51734))
LTC_ONION_PORT = int(os.getenv('LTC_ONION_PORT', 9333)) # Still on 0.18 codebase, same port
LTC_ONION_PORT = int(os.getenv('LTC_ONION_PORT', 9333))
BTC_ONION_PORT = int(os.getenv('BTC_ONION_PORT', 8334))
PART_RPC_USER = os.getenv('PART_RPC_USER', '')
@ -433,10 +433,10 @@ def writeTorSettings(fp, coin, coin_settings, tor_control_password):
fp.write(f'torpassword={tor_control_password}\n')
fp.write(f'torcontrol={TOR_PROXY_HOST}:{TOR_CONTROL_PORT}\n')
if coin == 'litecoin':
fp.write(f'bind=0.0.0.0:{onionport}\n')
else:
if coin_settings['core_version_group'] >= 21:
fp.write(f'bind=0.0.0.0:{onionport}=onion\n')
else:
fp.write(f'bind=0.0.0.0:{onionport}\n')
def prepareDataDir(coin, settings, chain, particl_mnemonic, extra_opts={}):
@ -545,6 +545,7 @@ def prepareDataDir(coin, settings, chain, particl_mnemonic, extra_opts={}):
fp.write('createdefaultmasterkey=1')
elif coin == 'litecoin':
fp.write('prune=4000\n')
fp.write('pid=litecoind.pid\n')
if LTC_RPC_USER != '':
fp.write('rpcauth={}:{}${}\n'.format(LTC_RPC_USER, salt, password_to_hmac(salt, LTC_RPC_PWD)))
elif coin == 'bitcoin':
@ -986,7 +987,7 @@ def main():
'blocks_confirmed': 2,
'override_feerate': 0.002,
'conf_target': 2,
'core_version_group': 18,
'core_version_group': 21,
'chain_lookups': 'local',
},
'litecoin': {
@ -1000,7 +1001,7 @@ def main():
'use_segwit': True,
'blocks_confirmed': 2,
'conf_target': 2,
'core_version_group': 18,
'core_version_group': 21,
'chain_lookups': 'local',
},
'bitcoin': {
@ -1014,7 +1015,7 @@ def main():
'use_segwit': True,
'blocks_confirmed': 1,
'conf_target': 2,
'core_version_group': 18,
'core_version_group': 22,
'chain_lookups': 'local',
},
'namecoin': {

View file

@ -196,6 +196,14 @@ def wait_for_in_progress(delay_event, swap_client, bid_id, sent=False):
raise ValueError('wait_for_in_progress timed out.')
def post_json_req(url, json_data):
req = urllib.request.Request(url)
req.add_header('Content-Type', 'application/json; charset=utf-8')
post_bytes = json.dumps(json_data).encode('utf-8')
req.add_header('Content-Length', len(post_bytes))
return urlopen(req, post_bytes).read()
def read_json_api(port, path=None):
url = f'http://127.0.0.1:{port}/json'
if path is not None:
@ -203,6 +211,13 @@ def read_json_api(port, path=None):
return json.loads(urlopen(url).read())
def post_json_api(port, path, json_data):
url = f'http://127.0.0.1:{port}/json'
if path is not None:
url += '/' + path
return json.loads(post_json_req(url, json_data))
def wait_for_none_active(delay_event, port, wait_for=30):
for i in range(wait_for):
if delay_event.is_set():
@ -272,14 +287,6 @@ def wait_for_balance(delay_event, url, balance_key, expect_amount, iterations=20
raise ValueError('Expect {} {}'.format(balance_key, expect_amount))
def post_json_req(url, json_data):
req = urllib.request.Request(url)
req.add_header('Content-Type', 'application/json; charset=utf-8')
post_bytes = json.dumps(json_data).encode('utf-8')
req.add_header('Content-Length', len(post_bytes))
return urlopen(req, post_bytes).read()
def delay_for(delay_event, delay_for=60):
logging.info('Delaying for {} seconds.'.format(delay_for))
delay_event.wait(delay_for)

View file

@ -39,6 +39,11 @@ XMR_BASE_P2P_PORT = 17792
XMR_BASE_RPC_PORT = 29798
XMR_BASE_WALLET_RPC_PORT = 29998
LTC_BASE_PORT = 34792
LTC_BASE_RPC_PORT = 35792
LTC_BASE_ZMQ_PORT = 36792
EXTRA_CONFIG_JSON = json.loads(os.getenv('EXTRA_CONFIG_JSON', '{}'))
@ -118,7 +123,7 @@ def run_prepare(node_id, datadir_path, bins_path, with_coins, mnemonic_in=None,
settings['chainclients']['particl']['rpcpassword'] = rpc_pass
for ip in range(num_nodes):
if ip != node_id:
fp.write('connect=127.0.0.1:{}\n'.format(PARTICL_PORT_BASE + ip))
fp.write('connect=127.0.0.1:{}\n'.format(PARTICL_PORT_BASE + ip + port_ofs))
for opt in EXTRA_CONFIG_JSON.get('part{}'.format(node_id), []):
fp.write(opt + '\n')
@ -147,17 +152,44 @@ def run_prepare(node_id, datadir_path, bins_path, with_coins, mnemonic_in=None,
settings['chainclients']['bitcoin']['rpcpassword'] = rpc_pass
for ip in range(num_nodes):
if ip != node_id:
fp.write('connect=127.0.0.1:{}\n'.format(BITCOIN_PORT_BASE + ip))
fp.write('connect=127.0.0.1:{}\n'.format(BITCOIN_PORT_BASE + ip + port_ofs))
for opt in EXTRA_CONFIG_JSON.get('btc{}'.format(node_id), []):
fp.write(opt + '\n')
if 'litecoin' in coins_array:
# Pruned nodes don't provide blocks
with open(os.path.join(datadir_path, 'litecoin', 'litecoin.conf'), 'r') as fp:
lines = fp.readlines()
with open(os.path.join(datadir_path, 'litecoin', 'litecoin.conf'), 'w') as fp:
for line in lines:
if not line.startswith('prune'):
fp.write(line)
fp.write('port={}\n'.format(LTC_BASE_PORT + node_id + port_ofs))
fp.write('bind=127.0.0.1\n')
fp.write('dnsseed=0\n')
fp.write('discover=0\n')
fp.write('listenonion=0\n')
fp.write('upnp=0\n')
if use_rpcauth:
salt = generate_salt(16)
rpc_user = 'test_ltc_' + str(node_id)
rpc_pass = 'test_ltc_pwd_' + str(node_id)
fp.write('rpcauth={}:{}${}\n'.format(rpc_user, salt, password_to_hmac(salt, rpc_pass)))
settings['chainclients']['litecoin']['rpcuser'] = rpc_user
settings['chainclients']['litecoin']['rpcpassword'] = rpc_pass
for ip in range(num_nodes):
if ip != node_id:
fp.write('connect=127.0.0.1:{}\n'.format(LTC_BASE_PORT + ip + port_ofs))
for opt in EXTRA_CONFIG_JSON.get('ltc{}'.format(node_id), []):
fp.write(opt + '\n')
if 'monero' in coins_array:
with open(os.path.join(datadir_path, 'monero', 'monerod.conf'), 'a') as fp:
fp.write('p2p-bind-ip=127.0.0.1\n')
fp.write('p2p-bind-port={}\n'.format(XMR_BASE_P2P_PORT + node_id + port_ofs))
for ip in range(num_nodes):
if ip != node_id:
fp.write('add-exclusive-node=127.0.0.1:{}\n'.format(XMR_BASE_P2P_PORT + ip))
fp.write('add-exclusive-node=127.0.0.1:{}\n'.format(XMR_BASE_P2P_PORT + ip + port_ofs))
with open(config_path) as fs:
settings = json.load(fs)
@ -200,23 +232,33 @@ def prepare_nodes(num_nodes, extra_coins, use_rpcauth=False, extra_settings={},
num_nodes=num_nodes, use_rpcauth=use_rpcauth, extra_settings=extra_settings, port_ofs=port_ofs)
class XmrTestBase(unittest.TestCase):
@classmethod
class TestBase(unittest.TestCase):
def setUpClass(cls):
super(XmrTestBase, cls).setUpClass()
super(TestBase, cls).setUpClass()
cls.delay_event = threading.Event()
cls.update_thread = None
cls.processes = []
prepare_nodes(3, 'monero')
signal.signal(signal.SIGINT, lambda signal, frame: cls.signal_handler(cls, signal, frame))
def signal_handler(self, sig, frame):
logging.info('signal {} detected.'.format(sig))
self.delay_event.set()
def wait_seconds(self, seconds):
self.delay_event.wait(seconds)
if self.delay_event.is_set():
raise ValueError('Test stopped.')
class XmrTestBase(TestBase):
@classmethod
def setUpClass(cls):
super(XmrTestBase, cls).setUpClass(cls)
cls.update_thread = None
cls.processes = []
prepare_nodes(3, 'monero')
def run_thread(self, client_id):
client_path = os.path.join(TEST_PATH, 'client{}'.format(client_id))
testargs = ['basicswap-run', '-datadir=' + client_path, '-regtest']

View file

@ -21,6 +21,7 @@ import time
import shutil
import logging
import unittest
import threading
import traceback
import multiprocessing
from unittest.mock import patch
@ -37,8 +38,6 @@ import bin.basicswap_run as runSystem
TEST_PATH = os.path.expanduser(os.getenv('TEST_PATH', '~/test_basicswap1'))
stop_test = False
logger = logging.getLogger()
logger.level = logging.DEBUG
if not len(logger.handlers):
@ -50,17 +49,19 @@ class Test(unittest.TestCase):
def setUpClass(cls):
super(Test, cls).setUpClass()
# Load both wallets from the same mnemonic
bins_path = os.path.join(TEST_PATH, 'bin')
for i in range(2):
logging.info('Preparing node: %d.', i)
client_path = os.path.join(TEST_PATH, 'client{}'.format(i))
try:
shutil.rmtree(client_path)
except Exception as ex:
logging.warning('setUpClass %s', str(ex))
cls.delay_event = threading.Event()
run_prepare(i, client_path, bins_path, 'monero,bitcoin', mnemonics[0])
# Load both wallets from the same mnemonic
bins_path = os.path.join(TEST_PATH, 'bin')
for i in range(2):
logging.info('Preparing node: %d.', i)
client_path = os.path.join(TEST_PATH, 'client{}'.format(i))
try:
shutil.rmtree(client_path)
except Exception as ex:
logging.warning('setUpClass %s', str(ex))
run_prepare(i, client_path, bins_path, 'monero,bitcoin', mnemonics[0])
def run_thread(self, client_id):
client_path = os.path.join(TEST_PATH, 'client{}'.format(client_id))
@ -69,7 +70,6 @@ class Test(unittest.TestCase):
runSystem.main()
def test_wallet(self):
global stop_test
update_thread = None
processes = []
@ -79,13 +79,13 @@ class Test(unittest.TestCase):
processes[-1].start()
try:
waitForServer(12700)
waitForServer(self.delay_event, 12700)
wallets_0 = read_json_api(12700, 'wallets')
assert(wallets_0['1']['expected_seed'] is True)
assert(wallets_0['6']['expected_seed'] is True)
waitForServer(12701)
waitForServer(self.delay_event, 12701)
wallets_1 = read_json_api(12701, 'wallets')
assert(wallets_0['1']['expected_seed'] is True)
@ -98,7 +98,6 @@ class Test(unittest.TestCase):
except Exception:
traceback.print_exc()
stop_test = True
if update_thread:
update_thread.join()
for p in processes:

View file

@ -0,0 +1,109 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright (c) 2022 tecnovert
# Distributed under the MIT software license, see the accompanying
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
"""
export TEST_PATH=/tmp/test_basicswap_wallet_restore
mkdir -p ${TEST_PATH}/bin
cp -r ~/tmp/basicswap_bin/* ${TEST_PATH}/bin
export PYTHONPATH=$(pwd)
python tests/basicswap/extended/test_wallet_restore.py
"""
import os
import sys
import shutil
import logging
import unittest
import traceback
import multiprocessing
from unittest.mock import patch
from tests.basicswap.common import (
read_json_api,
waitForServer,
)
from tests.basicswap.common_xmr import (
TestBase,
run_prepare,
)
import bin.basicswap_run as runSystem
TEST_PATH = os.path.expanduser(os.getenv('TEST_PATH', '~/test_basicswap1'))
logger = logging.getLogger()
logger.level = logging.DEBUG
if not len(logger.handlers):
logger.addHandler(logging.StreamHandler(sys.stdout))
def prepare_node(node_id, mnemonic):
logging.info('Preparing node: %d.', node_id)
bins_path = os.path.join(TEST_PATH, 'bin')
client_path = os.path.join(TEST_PATH, 'client{}'.format(node_id))
try:
shutil.rmtree(client_path)
except Exception as ex:
logging.warning('setUpClass %s', str(ex))
return run_prepare(node_id, client_path, bins_path, 'monero,bitcoin,litecoin', mnemonic)
class Test(TestBase):
@classmethod
def setUpClass(cls):
super(Test, cls).setUpClass(cls)
cls.used_mnemonics = []
# Load wallets from random mnemonics
for i in range(3):
cls.used_mnemonics.append(prepare_node(i, None))
def run_thread(self, client_id):
client_path = os.path.join(TEST_PATH, 'client{}'.format(client_id))
testargs = ['basicswap-run', '-datadir=' + client_path, '-regtest']
with patch.object(sys, 'argv', testargs):
runSystem.main()
def test_wallet(self):
update_thread = None
processes = []
self.wait_seconds(5)
for i in range(3):
processes.append(multiprocessing.Process(target=self.run_thread, args=(i,)))
processes[-1].start()
try:
waitForServer(self.delay_event, 12700)
waitForServer(self.delay_event, 12701)
# TODO: Add swaps
ltc_before = read_json_api(12700, 'wallets/ltc')
logging.info('Starting a new node on the same mnemonic as the first')
prepare_node(3, self.used_mnemonics[0])
processes.append(multiprocessing.Process(target=self.run_thread, args=(3,)))
processes[-1].start()
waitForServer(self.delay_event, 12703)
ltc_after = read_json_api(12703, 'wallets/ltc')
assert(ltc_before['deposit_address'] == ltc_after['deposit_address'])
except Exception:
traceback.print_exc()
if update_thread:
update_thread.join()
for p in processes:
p.terminate()
for p in processes:
p.join()
if __name__ == '__main__':
unittest.main()

View file

@ -16,14 +16,11 @@ python tests/basicswap/test_reload.py
import os
import sys
import json
import logging
import unittest
import traceback
import threading
import multiprocessing
from urllib import parse
from urllib.request import urlopen
from unittest.mock import patch
from basicswap.rpc import (
@ -31,6 +28,7 @@ from basicswap.rpc import (
)
from tests.basicswap.common import (
read_json_api,
post_json_api,
waitForServer,
waitForNumOffers,
waitForNumBids,
@ -104,15 +102,15 @@ class Test(unittest.TestCase):
delay_event.wait(2)
assert(blocks >= num_blocks)
data = parse.urlencode({
data = {
'addr_from': '-1',
'coin_from': '1',
'coin_to': '2',
'amt_from': '1',
'amt_to': '1',
'lockhrs': '24'}).encode()
'lockhrs': '24'}
offer_id = json.loads(urlopen('http://127.0.0.1:12700/json/offers/new', data=data).read())
offer_id = post_json_api(12700, 'offers/new', data)
summary = read_json_api(12700)
assert(summary['num_sent_offers'] == 1)
except Exception:
@ -124,21 +122,21 @@ class Test(unittest.TestCase):
offers = read_json_api(12701, 'offers')
offer = offers[0]
data = parse.urlencode({
data = {
'offer_id': offer['offer_id'],
'amount_from': offer['amount_from']}).encode()
'amount_from': offer['amount_from']}
bid_id = json.loads(urlopen('http://127.0.0.1:12701/json/bids/new', data=data).read())
bid_id = post_json_api(12701, 'bids/new', data)
waitForNumBids(delay_event, 12700, 1)
bids = read_json_api(12700, 'bids')
bid = bids[0]
data = parse.urlencode({
data = {
'accept': True
}).encode()
rv = json.loads(urlopen('http://127.0.0.1:12700/json/bids/{}'.format(bid['bid_id']), data=data).read())
}
rv = post_json_api(12700, 'bids/{}'.format(bid['bid_id']), data)
assert(rv['bid_state'] == 'Accepted')
waitForNumSwapping(delay_event, 12701, 1)
@ -162,7 +160,6 @@ class Test(unittest.TestCase):
delay_event.wait(5)
rv = read_json_api(12700, 'bids/{}'.format(bid['bid_id']))
print(rv)
if rv['bid_state'] == 'Completed':
break
assert(rv['bid_state'] == 'Completed')

View file

@ -22,10 +22,10 @@ from basicswap.db import (
Concepts,
)
from basicswap.basicswap import (
BasicSwap,
Coins,
SwapTypes,
BasicSwap,
BidStates,
SwapTypes,
DebugTypes,
)
from basicswap.basicswap_util import (

View file

@ -16,15 +16,13 @@ python tests/basicswap/test_xmr_reload.py
"""
import sys
import json
import logging
import unittest
import multiprocessing
from urllib import parse
from urllib.request import urlopen
from tests.basicswap.common import (
read_json_api,
post_json_api,
waitForServer,
waitForNumOffers,
waitForNumBids,
@ -50,15 +48,15 @@ class Test(XmrTestBase):
wallets1 = read_json_api(12701, 'wallets')
assert(float(wallets1['6']['balance']) > 0.0)
data = parse.urlencode({
data = {
'addr_from': '-1',
'coin_from': 'part',
'coin_to': 'xmr',
'amt_from': '1',
'amt_to': '1',
'lockhrs': '24'}).encode()
'lockhrs': '24'}
offer_id = json.loads(urlopen('http://127.0.0.1:12700/json/offers/new', data=data).read())['offer_id']
offer_id = post_json_api(12700, 'offers/new', data)['offer_id']
summary = read_json_api(12700)
assert(summary['num_sent_offers'] == 1)
@ -73,24 +71,24 @@ class Test(XmrTestBase):
'amount_from': offer['amount_from']}
data['valid_for_seconds'] = 24 * 60 * 60 + 1
bid = json.loads(urlopen('http://127.0.0.1:12701/json/bids/new', data=parse.urlencode(data).encode()).read())
bid = post_json_api(12701, 'bids/new', data)
assert(bid['error'] == 'Bid TTL too high')
del data['valid_for_seconds']
data['validmins'] = 24 * 60 + 1
bid = json.loads(urlopen('http://127.0.0.1:12701/json/bids/new', data=parse.urlencode(data).encode()).read())
bid = post_json_api(12701, 'bids/new', data)
assert(bid['error'] == 'Bid TTL too high')
del data['validmins']
data['valid_for_seconds'] = 10
bid = json.loads(urlopen('http://127.0.0.1:12701/json/bids/new', data=parse.urlencode(data).encode()).read())
bid = post_json_api(12701, 'bids/new', data)
assert(bid['error'] == 'Bid TTL too low')
del data['valid_for_seconds']
data['validmins'] = 1
bid = json.loads(urlopen('http://127.0.0.1:12701/json/bids/new', data=parse.urlencode(data).encode()).read())
bid = post_json_api(12701, 'bids/new', data)
assert(bid['error'] == 'Bid TTL too low')
data['validmins'] = 60
bid_id = json.loads(urlopen('http://127.0.0.1:12701/json/bids/new', data=parse.urlencode(data).encode()).read())
bid_id = post_json_api(12701, 'bids/new', data)
waitForNumBids(self.delay_event, 12700, 1)
@ -102,10 +100,10 @@ class Test(XmrTestBase):
self.delay_event.wait(1)
assert(bid['expire_at'] == bid['created_at'] + data['validmins'] * 60)
data = parse.urlencode({
data = {
'accept': True
}).encode()
rv = json.loads(urlopen('http://127.0.0.1:12700/json/bids/{}'.format(bid['bid_id']), data=data).read())
}
rv = post_json_api(12700, 'bids/{}'.format(bid['bid_id']), data)
assert(rv['bid_state'] == 'Accepted')
waitForNumSwapping(self.delay_event, 12701, 1)
@ -121,7 +119,7 @@ class Test(XmrTestBase):
rv = read_json_api(12701)
assert(rv['num_swapping'] == 1)
rv = json.loads(urlopen('http://127.0.0.1:12700/json/revokeoffer/{}'.format(offer_id)).read())
rv = read_json_api(12700, 'revokeoffer/{}'.format(offer_id))
assert(rv['revoked_offer'] == offer_id)
logger.info('Completing swap')
@ -130,7 +128,7 @@ class Test(XmrTestBase):
raise ValueError('Test stopped.')
self.delay_event.wait(4)
rv = json.loads(urlopen('http://127.0.0.1:12700/json/bids/{}'.format(bid['bid_id'])).read())
rv = read_json_api(12700, 'bids/{}'.format(bid['bid_id']))
if rv['bid_state'] == 'Completed':
break
assert(rv['bid_state'] == 'Completed')