basicswap-prepare tries to initialise coin wallets from Particl mnemonic

Bitcoin 0.20: 'Cannot set a new HD seed while still in Initial Block Download.' Removed in 0.21
This commit is contained in:
tecnovert 2020-12-04 01:46:01 +02:00
parent 3bbb483a0a
commit 5a163e0f86
No known key found for this signature in database
GPG key ID: 8ED6D8750C4E3F93
18 changed files with 754 additions and 93 deletions

View file

@ -11,7 +11,7 @@ lint_task:
test_task: test_task:
environment: environment:
- PART_VERSION: 0.19.1.1 - PART_VERSION: 0.19.1.2
- BTC_VERSION: 0.20.1 - BTC_VERSION: 0.20.1
- LTC_VERSION: 0.18.1 - LTC_VERSION: 0.18.1
- TEST_RELOAD_PATH: $HOME/test_basicswap1/ - TEST_RELOAD_PATH: $HOME/test_basicswap1/

View file

@ -7,7 +7,7 @@ stages:
- test - test
env: env:
global: global:
- PART_VERSION=0.19.1.1 - PART_VERSION=0.19.1.2
- BTC_VERSION=0.20.1 - BTC_VERSION=0.20.1
- LTC_VERSION=0.18.1 - LTC_VERSION=0.18.1
- TEST_DIR=~/test_basicswap2/ - TEST_DIR=~/test_basicswap2/

View file

@ -49,6 +49,9 @@ class BaseApp:
self.log = logging.getLogger(self.log_name) self.log = logging.getLogger(self.log_name)
self.log.propagate = False self.log.propagate = False
# Remove any existing handlers
self.log.handlers = []
formatter = logging.Formatter('%(asctime)s %(levelname)s : %(message)s') formatter = logging.Formatter('%(asctime)s %(levelname)s : %(message)s')
stream_stdout = logging.StreamHandler() stream_stdout = logging.StreamHandler()
if self.log_name != 'BasicSwap': if self.log_name != 'BasicSwap':
@ -81,6 +84,12 @@ class BaseApp:
testnet_name = '' if self.chain == 'mainnet' else chainparams[coin][self.chain].get('name', self.chain) testnet_name = '' if self.chain == 'mainnet' else chainparams[coin][self.chain].get('name', self.chain)
return os.path.join(datadir, testnet_name) return os.path.join(datadir, testnet_name)
def getCoinIdFromName(self, coin_name):
for c, params in chainparams.items():
if coin_name.lower() == params['name'].lower():
return c
raise ValueError('Unknown coin: {}'.format(coin_name))
def getTicker(self, coin_type): def getTicker(self, coin_type):
ticker = chainparams[coin_type]['ticker'] ticker = chainparams[coin_type]['ticker']
if self.chain == 'testnet': if self.chain == 'testnet':

View file

@ -551,7 +551,6 @@ class BasicSwap(BaseApp):
self.coin_clients[coin]['walletrpcauth'] = (chain_client_settings['walletrpcuser'], chain_client_settings['walletrpcpassword']) self.coin_clients[coin]['walletrpcauth'] = (chain_client_settings['walletrpcuser'], chain_client_settings['walletrpcpassword'])
else: else:
raise ValueError('Missing XMR wallet rpc credentials.') raise ValueError('Missing XMR wallet rpc credentials.')
self.coin_clients[coin]['interface'] = self.createInterface(coin)
def ci(self, coin): # coin interface def ci(self, coin): # coin interface
return self.coin_clients[coin]['interface'] return self.coin_clients[coin]['interface']
@ -593,17 +592,17 @@ class BasicSwap(BaseApp):
try: try:
with open(pidfilepath, 'rb') as fp: with open(pidfilepath, 'rb') as fp:
datadir_pid = int(fp.read().decode('utf-8')) datadir_pid = int(fp.read().decode('utf-8'))
assert(datadir_pid == cc['pid']) assert(datadir_pid == cc['pid']), 'Mismatched pid'
assert(os.path.exists(authcookiepath)) assert(os.path.exists(authcookiepath))
except Exception: except Exception:
time.sleep(0.5) time.sleep(0.5)
try: try:
if os.name != 'nt' or cc['core_version_group'] > 17: # litecoin on windows doesn't write a pid file if os.name != 'nt' or cc['core_version_group'] > 17: # litecoin on windows doesn't write a pid file
assert(datadir_pid == cc['pid']) assert(datadir_pid == cc['pid']), 'Mismatched pid'
with open(authcookiepath, 'rb') as fp: with open(authcookiepath, 'rb') as fp:
cc['rpcauth'] = fp.read().decode('utf-8') cc['rpcauth'] = fp.read().decode('utf-8')
except Exception: except Exception as e:
self.log.error('Unable to read authcookie for %s, %s, datadir pid %d, daemon pid %s', str(coin), authcookiepath, datadir_pid, cc['pid']) self.log.error('Unable to read authcookie for %s, %s, datadir pid %d, daemon pid %s. Error: %s', str(coin), authcookiepath, datadir_pid, cc['pid'], str(e))
raise ValueError('Error, terminating') raise ValueError('Error, terminating')
def createCoinInterface(self, coin): def createCoinInterface(self, coin):
@ -626,6 +625,9 @@ class BasicSwap(BaseApp):
self.log.info('%s Core version %d', chainparams[c]['name'].capitalize(), core_version) self.log.info('%s Core version %d', chainparams[c]['name'].capitalize(), core_version)
self.coin_clients[c]['core_version'] = core_version self.coin_clients[c]['core_version'] = core_version
if c == Coins.XMR:
self.coin_clients[c]['interface'].ensureWalletExists()
if c == Coins.PART: if c == Coins.PART:
self.coin_clients[c]['have_spent_index'] = self.coin_clients[c]['interface'].haveSpentIndex() self.coin_clients[c]['have_spent_index'] = self.coin_clients[c]['interface'].haveSpentIndex()
@ -636,6 +638,8 @@ class BasicSwap(BaseApp):
self.initialise() self.initialise()
def stopDaemon(self, coin): def stopDaemon(self, coin):
if coin == Coins.XMR:
return
num_tries = 10 num_tries = 10
authcookiepath = os.path.join(self.getChainDatadirPath(coin), '.cookie') authcookiepath = os.path.join(self.getChainDatadirPath(coin), '.cookie')
stopping = False stopping = False
@ -697,6 +701,19 @@ class BasicSwap(BaseApp):
if synced < 1.0: if synced < 1.0:
raise ValueError('{} chain is still syncing, currently at {}.'.format(self.coin_clients[c]['name'], synced)) raise ValueError('{} chain is still syncing, currently at {}.'.format(self.coin_clients[c]['name'], synced))
def initialiseWallet(self, coin_type):
ci = self.ci(coin_type)
self.log.info('Initialising {} wallet.'.format(ci.coin_name()))
if coin_type == Coins.XMR:
key_view = self.getWalletKey(coin_type, 1, for_ed25519=True)
key_spend = self.getWalletKey(coin_type, 2, for_ed25519=True)
ci.initialiseWallet(key_view, key_spend)
return
root_key = self.getWalletKey(coin_type, 1)
ci.initialiseWallet(root_key)
def setIntKV(self, str_key, int_val): def setIntKV(self, str_key, int_val):
session = scoped_session(self.session_factory) session = scoped_session(self.session_factory)
kv = session.query(DBKVInt).filter_by(key=str_key).first() kv = session.query(DBKVInt).filter_by(key=str_key).first()
@ -963,19 +980,8 @@ class BasicSwap(BaseApp):
session.remove() session.remove()
self.mxDB.release() self.mxDB.release()
def getPathKey(self, coin_from, coin_to, offer_created_at, contract_count, key_no, for_xmr=False): def grindForEd25519Key(self, coin_type, evkey, key_path_base):
account = self.callcoinrpc(Coins.PART, 'extkey', ['account']) ci = self.ci(coin_type)
evkey = self.callcoinrpc(Coins.PART, 'extkey', ['account', 'default', 'true'])['evkey']
ci = self.ci(coin_to)
days = offer_created_at // 86400
secs = offer_created_at - days * 86400
key_path_base = '44445555h/999999/{}/{}/{}/{}/{}/{}'.format(int(coin_from), int(coin_to), days, secs, contract_count, key_no)
if not for_xmr:
extkey = self.callcoinrpc(Coins.PART, 'extkey', ['info', evkey, key_path_base])['key_info']['result']
return decodeWif(self.callcoinrpc(Coins.PART, 'extkey', ['info', extkey])['key_info']['privkey'])
nonce = 1 nonce = 1
while True: while True:
key_path = key_path_base + '/{}'.format(nonce) key_path = key_path_base + '/{}'.format(nonce)
@ -986,7 +992,34 @@ class BasicSwap(BaseApp):
return privkey return privkey
nonce += 1 nonce += 1
if nonce > 1000: if nonce > 1000:
raise ValueError('getKeyForXMR failed') raise ValueError('grindForEd25519Key failed')
def getWalletKey(self, coin_type, key_num, for_ed25519=False):
account = self.callcoinrpc(Coins.PART, 'extkey', ['account'])
evkey = self.callcoinrpc(Coins.PART, 'extkey', ['account', 'default', 'true'])['evkey']
key_path_base = '44445555h/1h/{}/{}'.format(int(coin_type), key_num)
if not for_ed25519:
extkey = self.callcoinrpc(Coins.PART, 'extkey', ['info', evkey, key_path_base])['key_info']['result']
return decodeWif(self.callcoinrpc(Coins.PART, 'extkey', ['info', extkey])['key_info']['privkey'])
return self.grindForEd25519Key(coin_type, evkey, key_path_base)
def getPathKey(self, coin_from, coin_to, offer_created_at, contract_count, key_no, for_ed25519=False):
account = self.callcoinrpc(Coins.PART, 'extkey', ['account'])
evkey = self.callcoinrpc(Coins.PART, 'extkey', ['account', 'default', 'true'])['evkey']
ci = self.ci(coin_to)
days = offer_created_at // 86400
secs = offer_created_at - days * 86400
key_path_base = '44445555h/999999/{}/{}/{}/{}/{}/{}'.format(int(coin_from), int(coin_to), days, secs, contract_count, key_no)
if not for_ed25519:
extkey = self.callcoinrpc(Coins.PART, 'extkey', ['info', evkey, key_path_base])['key_info']['result']
return decodeWif(self.callcoinrpc(Coins.PART, 'extkey', ['info', extkey])['key_info']['privkey'])
return self.grindForEd25519Key(coin_to, evkey, key_path_base)
def getContractPubkey(self, date, contract_count): def getContractPubkey(self, date, contract_count):
account = self.callcoinrpc(Coins.PART, 'extkey', ['account']) account = self.callcoinrpc(Coins.PART, 'extkey', ['account'])
@ -1550,8 +1583,8 @@ class BasicSwap(BaseApp):
xmr_swap.contract_count = self.getNewContractId() xmr_swap.contract_count = self.getNewContractId()
xmr_swap.dest_af = msg_buf.dest_af xmr_swap.dest_af = msg_buf.dest_af
xmr_swap.b_restore_height = self.ci(coin_to).getChainHeight() xmr_swap.b_restore_height = self.ci(coin_to).getChainHeight()
kbvf = self.getPathKey(coin_from, coin_to, bid_created_at, xmr_swap.contract_count, 1, for_xmr=True) kbvf = self.getPathKey(coin_from, coin_to, bid_created_at, xmr_swap.contract_count, 1, for_ed25519=True)
kbsf = self.getPathKey(coin_from, coin_to, bid_created_at, xmr_swap.contract_count, 2, for_xmr=True) kbsf = self.getPathKey(coin_from, coin_to, bid_created_at, xmr_swap.contract_count, 2, for_ed25519=True)
kaf = self.getPathKey(coin_from, coin_to, bid_created_at, xmr_swap.contract_count, 3) kaf = self.getPathKey(coin_from, coin_to, bid_created_at, xmr_swap.contract_count, 3)
karf = self.getPathKey(coin_from, coin_to, bid_created_at, xmr_swap.contract_count, 4) karf = self.getPathKey(coin_from, coin_to, bid_created_at, xmr_swap.contract_count, 4)
@ -1660,8 +1693,8 @@ class BasicSwap(BaseApp):
contract_secret = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 1) contract_secret = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 1)
kbvl = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 2, for_xmr=True) kbvl = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 2, for_ed25519=True)
kbsl = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 3, for_xmr=True) kbsl = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 3, for_ed25519=True)
kal = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 4) kal = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 4)
karl = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 5) karl = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 5)
@ -3746,7 +3779,7 @@ class BasicSwap(BaseApp):
ci_from = self.ci(coin_from) ci_from = self.ci(coin_from)
ci_to = self.ci(coin_to) ci_to = self.ci(coin_to)
kbsf = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 2, for_xmr=True) kbsf = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 2, for_ed25519=True)
kaf = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 3) kaf = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 3)
al_lock_spend_sig = ci_from.decryptOtVES(kbsf, xmr_swap.al_lock_spend_tx_esig) al_lock_spend_sig = ci_from.decryptOtVES(kbsf, xmr_swap.al_lock_spend_tx_esig)
@ -3801,7 +3834,7 @@ class BasicSwap(BaseApp):
kbsf = ci_from.recoverEncKey(xmr_swap.al_lock_spend_tx_esig, xmr_swap.al_lock_spend_tx_sig, xmr_swap.pkasf) kbsf = ci_from.recoverEncKey(xmr_swap.al_lock_spend_tx_esig, xmr_swap.al_lock_spend_tx_sig, xmr_swap.pkasf)
assert(kbsf is not None) assert(kbsf is not None)
kbsl = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 3, for_xmr=True) kbsl = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 3, for_ed25519=True)
vkbs = ci_to.sumKeys(kbsl, kbsf) vkbs = ci_to.sumKeys(kbsl, kbsf)
address_to = ci_to.getMainWalletAddress() address_to = ci_to.getMainWalletAddress()
@ -3835,7 +3868,7 @@ class BasicSwap(BaseApp):
kbsl = ci_from.recoverEncKey(xmr_swap.af_lock_refund_spend_tx_esig, af_lock_refund_spend_tx_sig, xmr_swap.pkasl) kbsl = ci_from.recoverEncKey(xmr_swap.af_lock_refund_spend_tx_esig, af_lock_refund_spend_tx_sig, xmr_swap.pkasl)
assert(kbsl is not None) assert(kbsl is not None)
kbsf = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 2, for_xmr=True) kbsf = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 2, for_ed25519=True)
vkbs = ci_to.sumKeys(kbsl, kbsf) vkbs = ci_to.sumKeys(kbsl, kbsf)
address_to = ci_to.getMainWalletAddress() address_to = ci_to.getMainWalletAddress()
@ -3874,7 +3907,7 @@ class BasicSwap(BaseApp):
xmr_swap.af_lock_refund_spend_tx_esig = msg_data.af_lock_refund_spend_tx_esig xmr_swap.af_lock_refund_spend_tx_esig = msg_data.af_lock_refund_spend_tx_esig
xmr_swap.af_lock_refund_tx_sig = msg_data.af_lock_refund_tx_sig xmr_swap.af_lock_refund_tx_sig = msg_data.af_lock_refund_tx_sig
kbsl = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 3, for_xmr=True) kbsl = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 3, for_ed25519=True)
karl = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 5) karl = self.getPathKey(coin_from, coin_to, bid.created_at, xmr_swap.contract_count, 5)
xmr_swap.af_lock_refund_spend_tx_sig = ci_from.decryptOtVES(kbsl, xmr_swap.af_lock_refund_spend_tx_esig) xmr_swap.af_lock_refund_spend_tx_sig = ci_from.decryptOtVES(kbsl, xmr_swap.af_lock_refund_spend_tx_esig)

View file

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2019 tecnovert # Copyright (c) 2019-2020 tecnovert
# Distributed under the MIT software license, see the accompanying # Distributed under the MIT software license, see the accompanying
# file LICENSE or http://www.opensource.org/licenses/mit-license.php. # file LICENSE or http://www.opensource.org/licenses/mit-license.php.
@ -70,6 +70,7 @@ chainparams = {
'rpcport': 8332, 'rpcport': 8332,
'pubkey_address': 0, 'pubkey_address': 0,
'script_address': 5, 'script_address': 5,
'key_prefix': 128,
'hrp': 'bc', 'hrp': 'bc',
'bip44': 0, 'bip44': 0,
'min_amount': 1000, 'min_amount': 1000,
@ -79,6 +80,7 @@ chainparams = {
'rpcport': 18332, 'rpcport': 18332,
'pubkey_address': 111, 'pubkey_address': 111,
'script_address': 196, 'script_address': 196,
'key_prefix': 239,
'hrp': 'tb', 'hrp': 'tb',
'bip44': 1, 'bip44': 1,
'min_amount': 1000, 'min_amount': 1000,
@ -89,6 +91,7 @@ chainparams = {
'rpcport': 18443, 'rpcport': 18443,
'pubkey_address': 111, 'pubkey_address': 111,
'script_address': 196, 'script_address': 196,
'key_prefix': 239,
'hrp': 'bcrt', 'hrp': 'bcrt',
'bip44': 1, 'bip44': 1,
'min_amount': 1000, 'min_amount': 1000,
@ -105,6 +108,7 @@ chainparams = {
'rpcport': 9332, 'rpcport': 9332,
'pubkey_address': 48, 'pubkey_address': 48,
'script_address': 50, 'script_address': 50,
'key_prefix': 176,
'hrp': 'ltc', 'hrp': 'ltc',
'bip44': 2, 'bip44': 2,
'min_amount': 1000, 'min_amount': 1000,
@ -114,6 +118,7 @@ chainparams = {
'rpcport': 19332, 'rpcport': 19332,
'pubkey_address': 111, 'pubkey_address': 111,
'script_address': 58, 'script_address': 58,
'key_prefix': 239,
'hrp': 'tltc', 'hrp': 'tltc',
'bip44': 1, 'bip44': 1,
'min_amount': 1000, 'min_amount': 1000,
@ -124,6 +129,7 @@ chainparams = {
'rpcport': 19443, 'rpcport': 19443,
'pubkey_address': 111, 'pubkey_address': 111,
'script_address': 58, 'script_address': 58,
'key_prefix': 239,
'hrp': 'rltc', 'hrp': 'rltc',
'bip44': 1, 'bip44': 1,
'min_amount': 1000, 'min_amount': 1000,

View file

@ -8,6 +8,7 @@ import os
CONFIG_FILENAME = 'basicswap.json' CONFIG_FILENAME = 'basicswap.json'
DEFAULT_DATADIR = '~/.basicswap' DEFAULT_DATADIR = '~/.basicswap'
DEFAULT_ALLOW_CORS = False
TEST_DATADIRS = os.path.expanduser(os.getenv('DATADIRS', '/tmp/basicswap')) TEST_DATADIRS = os.path.expanduser(os.getenv('DATADIRS', '/tmp/basicswap'))
bin_suffix = ('.exe' if os.name == 'nt' else '') bin_suffix = ('.exe' if os.name == 'nt' else '')

View file

@ -17,6 +17,7 @@ from .util import (
dumpj, dumpj,
format_amount, format_amount,
make_int, make_int,
toWIF,
decodeAddress) decodeAddress)
from coincurve.keys import ( from coincurve.keys import (
PublicKey) PublicKey)
@ -131,6 +132,16 @@ class BTCInterface(CoinInterface):
def getBlockchainInfo(self): def getBlockchainInfo(self):
return self.rpc_callback('getblockchaininfo') return self.rpc_callback('getblockchaininfo')
def initialiseWallet(self, key_bytes):
wif_prefix = chainparams[self.coin_type()][self._network]['key_prefix']
key_wif = toWIF(wif_prefix, key_bytes)
try:
self.rpc_callback('sethdseed', [True, key_wif])
except Exception as e:
# < 0.21: Cannot set a new HD seed while still in Initial Block Download.
logging.error('sethdseed failed: {}'.format(str(e)))
def getWalletInfo(self): def getWalletInfo(self):
return self.rpc_callback('getwalletinfo') return self.rpc_callback('getwalletinfo')

View file

@ -40,3 +40,6 @@ class PARTInterface(BTCInterface):
version = self.getDaemonVersion() version = self.getDaemonVersion()
index_info = self.rpc_callback('getinsightinfo' if int(str(version)[:2]) > 19 else 'getindexinfo') index_info = self.rpc_callback('getinsightinfo' if int(str(version)[:2]) > 19 else 'getindexinfo')
return index_info['spentindex'] return index_info['spentindex']
def initialiseWallet(self, key):
raise ValueError('TODO')

View file

@ -65,11 +65,45 @@ class XMRInterface(CoinInterface):
def setWalletFilename(self, wallet_filename): def setWalletFilename(self, wallet_filename):
self._wallet_filename = wallet_filename self._wallet_filename = wallet_filename
def initialiseWallet(self, key_view, key_spend, restore_height=None):
try:
self.rpc_wallet_cb('open_wallet', {'filename': self._wallet_filename})
# TODO: Check address
return # Wallet exists
except Exception as e:
pass
try:
if restore_height is None:
restore_height = self.getChainHeight()
except Exception as e:
logging.warning('Unable to get restore_height, set to zero. Error: {}'.format(str(e)))
restore_height = 0
Kbv = self.getPubkey(key_view)
Kbs = self.getPubkey(key_spend)
address_b58 = xmr_util.encode_address(Kbv, Kbs)
params = {
'filename': self._wallet_filename,
'address': address_b58,
'viewkey': b2h(key_view[::-1]),
'spendkey': b2h(key_spend[::-1]),
'restore_height': restore_height,
}
rv = self.rpc_wallet_cb('generate_from_keys', params)
logging.info('generate_from_keys %s', dumpj(rv))
self.rpc_wallet_cb('open_wallet', {'filename': self._wallet_filename})
def ensureWalletExists(self):
self.rpc_wallet_cb('open_wallet', {'filename': self._wallet_filename})
def testDaemonRPC(self): def testDaemonRPC(self):
self.rpc_wallet_cb('get_languages') self.rpc_wallet_cb('get_languages')
def getDaemonVersion(self): def getDaemonVersion(self):
return self.rpc_cb('get_version')['version'] return self.rpc_wallet_cb('get_version')['version']
#return self.rpc_cb('get_version')['version']
def getBlockchainInfo(self): def getBlockchainInfo(self):
rv = {} rv = {}

View file

@ -15,7 +15,7 @@ class Peer:
class Network: class Network:
def __init__(self, network_port, network_key): def __init__(self, p2p_port, network_key):
self._network_port = network_port self._p2p_port = p2p_port
self._network_key = network_key self._network_key = network_key
self._peers = [] self._peers = []

View file

@ -16,6 +16,7 @@ import json
import mmap import mmap
import stat import stat
import gnupg import gnupg
import signal
import hashlib import hashlib
import tarfile import tarfile
import zipfile import zipfile
@ -24,13 +25,15 @@ import platform
import urllib.parse import urllib.parse
from urllib.request import urlretrieve from urllib.request import urlretrieve
import basicswap.config as cfg import basicswap.config as cfg
from basicswap.rpc import ( from basicswap.rpc import (
callrpc_cli, callrpc_cli,
waitForRPC, waitForRPC,
) )
from bin.basicswap_run import startDaemon from basicswap.basicswap import BasicSwap
from basicswap.chainparams import Coins
from bin.basicswap_run import startDaemon, startXmrWalletDaemon
if platform.system() == 'Darwin': if platform.system() == 'Darwin':
BIN_ARCH = 'osx64.tar.gz' BIN_ARCH = 'osx64.tar.gz'
@ -40,7 +43,7 @@ else:
BIN_ARCH = 'x86_64-linux-gnu.tar.gz' BIN_ARCH = 'x86_64-linux-gnu.tar.gz'
known_coins = { known_coins = {
'particl': '0.19.1.1', 'particl': '0.19.1.2',
'litecoin': '0.18.1', 'litecoin': '0.18.1',
'bitcoin': '0.20.1', 'bitcoin': '0.20.1',
'namecoin': '0.18.0', 'namecoin': '0.18.0',
@ -52,6 +55,13 @@ logger.level = logging.DEBUG
if not len(logger.handlers): if not len(logger.handlers):
logger.addHandler(logging.StreamHandler(sys.stdout)) logger.addHandler(logging.StreamHandler(sys.stdout))
XMR_RPC_HOST = os.getenv('XMR_RPC_HOST', 'localhost')
BASE_XMR_RPC_PORT = os.getenv('BASE_XMR_RPC_PORT', 29798)
BASE_XMR_ZMQ_PORT = os.getenv('BASE_XMR_ZMQ_PORT', 29898)
BASE_XMR_WALLET_PORT = os.getenv('BASE_XMR_WALLET_PORT', 29998)
XMR_WALLET_RPC_USER = os.getenv('XMR_WALLET_RPC_USER', 'xmr_wallet_user')
XMR_WALLET_RPC_PWD = os.getenv('XMR_WALLET_RPC_PWD', 'xmr_wallet_pwd')
def make_reporthook(): def make_reporthook():
read = 0 # Number of bytes read so far read = 0 # Number of bytes read so far
@ -95,8 +105,6 @@ def extractCore(coin, version, settings, bin_dir, release_path):
fout.write(fi.read()) fout.write(fi.read())
fi.close() fi.close()
os.chmod(out_path, stat.S_IRWXU | stat.S_IXGRP | stat.S_IXOTH) os.chmod(out_path, stat.S_IRWXU | stat.S_IXGRP | stat.S_IXOTH)
print('member', member)
return return
bins = [coin + 'd', coin + '-cli', coin + '-tx'] bins = [coin + 'd', coin + '-cli', coin + '-tx']
@ -257,10 +265,40 @@ def prepareDataDir(coin, settings, data_dir, chain, particl_mnemonic):
if not os.path.exists(data_dir): if not os.path.exists(data_dir):
os.makedirs(data_dir) os.makedirs(data_dir)
if coin == 'monero':
core_conf_path = os.path.join(data_dir, coin + 'd.conf')
if os.path.exists(core_conf_path):
exitWithError('{} exists'.format(core_conf_path))
with open(core_conf_path, 'w') as fp:
if chain == 'regtest':
fp.write('regtest=1\n')
fp.write('keep-fakechain=1\n')
fp.write('fixed-difficulty=1\n')
elif chain == 'testnet':
fp.write('testnet=1\n')
fp.write('data-dir={}\n'.format(data_dir))
fp.write('rpc-bind-port={}\n'.format(core_settings['rpcport']))
fp.write('rpc-bind-ip=127.0.0.1\n')
fp.write('zmq-rpc-bind-port={}\n'.format(core_settings['zmqport']))
fp.write('zmq-rpc-bind-ip=127.0.0.1\n')
#fp.write('zmq-rpc-bind-port={}\n'.format(core_settings['zmqport']))
#fp.write('zmq-rpc-bind-ip=127.0.0.1\n')
wallet_conf_path = os.path.join(data_dir, coin + '_wallet.conf')
if os.path.exists(wallet_conf_path):
exitWithError('{} exists'.format(wallet_conf_path))
with open(wallet_conf_path, 'w') as fp:
fp.write('daemon-address={}:{}\n'.format(core_settings['rpchost'], core_settings['rpcport']))
fp.write('no-dns=1\n')
fp.write('rpc-bind-port={}\n'.format(core_settings['walletrpcport']))
fp.write('wallet-dir={}\n'.format(os.path.join(data_dir, 'wallets')))
fp.write('log-file={}\n'.format(os.path.join(data_dir, 'wallet.log')))
fp.write('shared-ringdb-dir={}\n'.format(os.path.join(data_dir, 'shared-ringdb')))
fp.write('rpc-login={}:{}\n'.format(core_settings['walletrpcuser'], core_settings['walletrpcpassword']))
return
core_conf_path = os.path.join(data_dir, coin + '.conf') core_conf_path = os.path.join(data_dir, coin + '.conf')
if os.path.exists(core_conf_path): if os.path.exists(core_conf_path):
exitWithError('{} exists'.format(core_conf_path)) exitWithError('{} exists'.format(core_conf_path))
with open(core_conf_path, 'w') as fp: with open(core_conf_path, 'w') as fp:
if chain != 'mainnet': if chain != 'mainnet':
fp.write(chain + '=1\n') fp.write(chain + '=1\n')
@ -490,11 +528,14 @@ def main():
'monero': { 'monero': {
'connection_type': 'rpc' if 'monero' in with_coins else 'none', 'connection_type': 'rpc' if 'monero' in with_coins else 'none',
'manage_daemon': True if 'monero' in with_coins else False, 'manage_daemon': True if 'monero' in with_coins else False,
'rpcport': 29798 + port_offset, 'manage_wallet_daemon': True if 'monero' in with_coins else False,
'walletrpcport': 29799 + port_offset, 'rpcport': BASE_XMR_RPC_PORT + port_offset,
#'walletrpcuser': 'test' + str(node_id), 'zmqport': BASE_XMR_ZMQ_PORT + port_offset,
#'walletrpcpassword': 'test_pass' + str(node_id), 'walletrpcport': BASE_XMR_WALLET_PORT + port_offset,
'walletfile': 'basicswap', 'rpchost': XMR_RPC_HOST,
'walletrpcuser': XMR_WALLET_RPC_USER,
'walletrpcpassword': XMR_WALLET_RPC_PWD,
'walletfile': 'swap_wallet',
'datadir': os.path.join(data_dir, 'monero'), 'datadir': os.path.join(data_dir, 'monero'),
'bindir': os.path.join(bin_dir, 'monero'), 'bindir': os.path.join(bin_dir, 'monero'),
} }
@ -597,26 +638,57 @@ def main():
particl_settings = settings['chainclients']['particl'] particl_settings = settings['chainclients']['particl']
partRpc = make_rpc_func(particl_settings['bindir'], particl_settings['datadir'], chain) partRpc = make_rpc_func(particl_settings['bindir'], particl_settings['datadir'], chain)
d = startDaemon(particl_settings['datadir'], particl_settings['bindir'], cfg.PARTICLD, ['-noconnect', '-nofindpeers', '-nostaking', '-nodnsseed', '-nolisten'])
daemons = []
daemons.append(startDaemon(particl_settings['datadir'], particl_settings['bindir'], cfg.PARTICLD, ['-noconnect', '-nofindpeers', '-nostaking', '-nodnsseed', '-nolisten']))
try: try:
waitForRPC(partRpc) waitForRPC(partRpc)
if particl_wallet_mnemonic is None: if particl_wallet_mnemonic is None:
particl_wallet_mnemonic = partRpc('mnemonic new')['mnemonic'] particl_wallet_mnemonic = partRpc('mnemonic new')['mnemonic']
partRpc('extkeyimportmaster "{}"'.format(particl_wallet_mnemonic)) partRpc('extkeyimportmaster "{}"'.format(particl_wallet_mnemonic))
# Initialise wallets
with open(os.path.join(data_dir, 'basicswap.log'), 'a') as fp:
swap_client = BasicSwap(fp, data_dir, settings, chain)
swap_client.setCoinConnectParams(Coins.PART)
swap_client.setDaemonPID(Coins.PART, daemons[-1].pid)
swap_client.setCoinRunParams(Coins.PART)
swap_client.createCoinInterface(Coins.PART)
for coin_name in with_coins:
coin_settings = settings['chainclients'][coin_name]
c = swap_client.getCoinIdFromName(coin_name)
if c == Coins.PART:
continue
swap_client.setCoinConnectParams(c)
if c == Coins.XMR:
if not coin_settings['manage_wallet_daemon']:
continue
daemons.append(startXmrWalletDaemon(coin_settings['datadir'], coin_settings['bindir'], 'monero-wallet-rpc'))
else:
if not coin_settings['manage_daemon']:
continue
filename = coin_name + 'd' + ('.exe' if os.name == 'nt' else '')
daemons.append(startDaemon(coin_settings['datadir'], coin_settings['bindir'], filename, ['-noconnect', '-nodnsseed', '-nolisten']))
swap_client.setDaemonPID(c, daemons[-1].pid)
swap_client.setCoinRunParams(c)
swap_client.createCoinInterface(c)
swap_client.waitForDaemonRPC(c)
swap_client.initialiseWallet(c)
finally: finally:
logger.info('Terminating {}'.format(d.pid)) for d in daemons:
d.terminate() logging.info('Interrupting {}'.format(d.pid))
d.wait(timeout=120) d.send_signal(signal.SIGINT)
if d.stdout: d.wait(timeout=120)
d.stdout.close() for fp in (d.stdout, d.stderr, d.stdin):
if d.stderr: if fp:
d.stderr.close() fp.close()
if d.stdin:
d.stdin.close()
logger.info('IMPORTANT - Save your particl wallet recovery phrase:\n{}\n'.format(particl_wallet_mnemonic)) logger.info('IMPORTANT - Save your particl wallet recovery phrase:\n{}\n'.format(particl_wallet_mnemonic))
logger.info('Done.') logger.info('Done.')

View file

@ -19,7 +19,6 @@ import logging
import traceback import traceback
import subprocess import subprocess
import basicswap.config as cfg import basicswap.config as cfg
from basicswap import __version__ from basicswap import __version__
from basicswap.basicswap import BasicSwap from basicswap.basicswap import BasicSwap
@ -31,7 +30,6 @@ logger.level = logging.DEBUG
if not len(logger.handlers): if not len(logger.handlers):
logger.addHandler(logging.StreamHandler(sys.stdout)) logger.addHandler(logging.StreamHandler(sys.stdout))
ALLOW_CORS = False
swap_client = None swap_client = None
@ -50,6 +48,29 @@ def startDaemon(node_dir, bin_dir, daemon_bin, opts=[]):
return subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) return subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
def startXmrDaemon(node_dir, bin_dir, daemon_bin, opts=[]):
daemon_bin = os.path.expanduser(os.path.join(bin_dir, daemon_bin))
args = [daemon_bin, '--config-file=' + os.path.join(os.path.expanduser(node_dir), 'monerod.conf')] + opts
logging.info('Starting node {} --data-dir={}'.format(daemon_bin, node_dir))
return subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
def startXmrWalletDaemon(node_dir, bin_dir, wallet_bin, opts=[]):
daemon_bin = os.path.expanduser(os.path.join(bin_dir, wallet_bin))
data_dir = os.path.expanduser(node_dir)
args = [daemon_bin, '--config-file=' + os.path.join(os.path.expanduser(node_dir), 'monero_wallet.conf')] + opts
args += opts
logging.info('Starting wallet daemon {} --wallet-dir={}'.format(daemon_bin, node_dir))
#return subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=data_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 runClient(fp, data_dir, chain): def runClient(fp, data_dir, chain):
global swap_client global swap_client
settings_path = os.path.join(data_dir, cfg.CONFIG_FILENAME) settings_path = os.path.join(data_dir, cfg.CONFIG_FILENAME)
@ -79,6 +100,20 @@ def runClient(fp, data_dir, chain):
try: try:
# Try start daemons # Try start daemons
for c, v in settings['chainclients'].items(): for c, v in settings['chainclients'].items():
if c == 'monero':
if v['manage_daemon'] is True:
logger.info('Starting {} daemon'.format(c.capitalize()))
daemons.append(startXmrDaemon(v['datadir'], v['bindir'], 'monerod'))
pid = daemons[-1].pid
logger.info('Started {} {}'.format('monerod', pid))
if v['manage_wallet_daemon'] is True:
logger.info('Starting {} wallet daemon'.format(c.capitalize()))
daemons.append(startXmrWalletDaemon(v['datadir'], v['bindir'], 'monero-wallet-rpc'))
pid = daemons[-1].pid
logger.info('Started {} {}'.format('monero-wallet-rpc', pid))
continue
if v['manage_daemon'] is True: if v['manage_daemon'] is True:
logger.info('Starting {} daemon'.format(c.capitalize())) logger.info('Starting {} daemon'.format(c.capitalize()))
@ -99,7 +134,7 @@ def runClient(fp, data_dir, chain):
if 'htmlhost' in settings: if 'htmlhost' in settings:
swap_client.log.info('Starting server at %s:%d.' % (settings['htmlhost'], settings['htmlport'])) swap_client.log.info('Starting server at %s:%d.' % (settings['htmlhost'], settings['htmlport']))
allow_cors = settings['allowcors'] if 'allowcors' in settings else ALLOW_CORS allow_cors = settings['allowcors'] if 'allowcors' in settings else cfg.DEFAULT_ALLOW_CORS
tS1 = HttpThread(fp, settings['htmlhost'], settings['htmlport'], allow_cors, swap_client) tS1 = HttpThread(fp, settings['htmlhost'], settings['htmlport'], allow_cors, swap_client)
threads.append(tS1) threads.append(tS1)
tS1.start() tS1.start()
@ -118,8 +153,7 @@ def runClient(fp, data_dir, chain):
closed_pids = [] closed_pids = []
for d in daemons: for d in daemons:
int_pid = d.pid logging.info('Interrupting {}'.format(d.pid))
logging.info('Interrupting {}'.format(int_pid))
try: try:
d.send_signal(signal.SIGINT) d.send_signal(signal.SIGINT)
except Exception as e: except Exception as e:
@ -127,13 +161,10 @@ def runClient(fp, data_dir, chain):
for d in daemons: for d in daemons:
try: try:
d.wait(timeout=120) d.wait(timeout=120)
if d.stdout: for fp in (d.stdout, d.stderr, d.stdin):
d.stdout.close() if fp:
if d.stderr: fp.close()
d.stderr.close() closed_pids.append(d.pid)
if d.stdin:
d.stdin.close()
closed_pids.append(int_pid)
except Exception as ex: except Exception as ex:
logger.error('Error: {}'.format(ex)) logger.error('Error: {}'.format(ex))

View file

@ -5,8 +5,8 @@
# Distributed under the MIT software license, see the accompanying # Distributed under the MIT software license, see the accompanying
# file LICENSE or http://www.opensource.org/licenses/mit-license.php. # file LICENSE or http://www.opensource.org/licenses/mit-license.php.
import unittest
import secrets import secrets
import unittest
import basicswap.contrib.ed25519_fast as edf import basicswap.contrib.ed25519_fast as edf
import basicswap.ed25519_fast_util as edu import basicswap.ed25519_fast_util as edu

View file

@ -7,12 +7,13 @@
import os import os
import sys import sys
import unittest
from unittest.mock import patch
from io import StringIO
import logging
import shutil
import json import json
import shutil
import logging
import unittest
from io import StringIO
from unittest.mock import patch
import basicswap.config as cfg import basicswap.config as cfg
import bin.basicswap_prepare as prepareSystem import bin.basicswap_prepare as prepareSystem

View file

@ -18,17 +18,17 @@ python tests/basicswap/test_reload.py
import os import os
import sys import sys
import time
import unittest
import logging
import shutil
import json import json
import time
import shutil
import logging
import unittest
import traceback import traceback
import multiprocessing
import threading import threading
from unittest.mock import patch import multiprocessing
from urllib.request import urlopen
from urllib import parse from urllib import parse
from urllib.request import urlopen
from unittest.mock import patch
from basicswap.rpc import ( from basicswap.rpc import (
callrpc_cli, callrpc_cli,

View file

@ -0,0 +1,264 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright (c) 2020 tecnovert
# Distributed under the MIT software license, see the accompanying
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
"""
export TEST_RELOAD_PATH=/tmp/test_basicswap
mkdir -p ${TEST_RELOAD_PATH}/bin/{particl,monero}
cp ~/tmp/particl-0.19.1.2-x86_64-linux-gnu.tar.gz ${TEST_RELOAD_PATH}/bin/particl
cp ~/tmp/monero-0.17.1.5-x86_64-linux-gnu.tar.gz ${TEST_RELOAD_PATH}/bin/monero
export PYTHONPATH=$(pwd)
python tests/basicswap/test_reload_xmr.py
"""
import os
import sys
import json
import time
import shutil
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 (
callrpc_cli,
)
from basicswap.util import (
dumpj
)
from tests.basicswap.mnemonics import mnemonics
import basicswap.config as cfg
import bin.basicswap_prepare as prepareSystem
import bin.basicswap_run as runSystem
test_path = os.path.expanduser(os.getenv('TEST_RELOAD_PATH', '~/test_basicswap1'))
PARTICL_PORT_BASE = int(os.getenv('PARTICL_PORT_BASE', '11938'))
XMR_BASE_P2P_PORT = 17792
XMR_BASE_RPC_PORT = 21792
XMR_BASE_ZMQ_PORT = 22792
XMR_BASE_WALLET_RPC_PORT = 23792
stop_test = False
logger = logging.getLogger()
logger.level = logging.DEBUG
if not len(logger.handlers):
logger.addHandler(logging.StreamHandler(sys.stdout))
def waitForServer(port):
for i in range(20):
try:
time.sleep(1)
summary = json.loads(urlopen('http://localhost:{}/json'.format(port)).read())
break
except Exception:
traceback.print_exc()
def waitForNumOffers(port, offers):
for i in range(20):
summary = json.loads(urlopen('http://localhost:{}/json'.format(port)).read())
if summary['num_network_offers'] >= offers:
return
time.sleep(1)
raise ValueError('waitForNumOffers failed')
def waitForNumBids(port, bids):
for i in range(20):
summary = json.loads(urlopen('http://localhost:{}/json'.format(port)).read())
if summary['num_recv_bids'] >= bids:
return
time.sleep(1)
raise ValueError('waitForNumBids failed')
def waitForNumSwapping(port, bids):
for i in range(20):
summary = json.loads(urlopen('http://localhost:{}/json'.format(port)).read())
if summary['num_swapping'] >= bids:
return
time.sleep(1)
raise ValueError('waitForNumSwapping failed')
def updateThread(xmr_addr):
#btc_addr = btcRpc(0, 'getnewaddress mining_addr bech32')
while not stop_test:
#btcRpc(0, 'generatetoaddress {} {}'.format(1, btc_addr))
time.sleep(5)
class Test(unittest.TestCase):
@classmethod
def setUpClass(cls):
super(Test, cls).setUpClass()
for i in range(3):
client_path = os.path.join(test_path, 'client{}'.format(i))
config_path = os.path.join(client_path, cfg.CONFIG_FILENAME)
try:
shutil.rmtree(client_path)
except Exception as ex:
logger.warning('setUpClass %s', str(ex))
testargs = [
'basicswap-prepare',
'-datadir="{}"'.format(client_path),
'-bindir="{}"'.format(os.path.join(test_path, 'bin')),
'-portoffset={}'.format(i),
'-particl_mnemonic="{}"'.format(mnemonics[i]),
'-regtest', '-withoutcoin=litecoin', '-withcoin=monero']
with patch.object(sys, 'argv', testargs):
prepareSystem.main()
with open(os.path.join(client_path, 'particl', 'particl.conf'), 'r') as fp:
lines = fp.readlines()
with open(os.path.join(client_path, 'particl', 'particl.conf'), 'w') as fp:
for line in lines:
if not line.startswith('staking'):
fp.write(line)
fp.write('port={}\n'.format(PARTICL_PORT_BASE + i))
fp.write('bind=127.0.0.1\n')
fp.write('dnsseed=0\n')
fp.write('minstakeinterval=5\n')
for ip in range(3):
if ip != i:
fp.write('connect=localhost:{}\n'.format(PARTICL_PORT_BASE + ip))
with open(os.path.join(client_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 + i))
for ip in range(3):
if ip != i:
fp.write('add-exclusive-node=127.0.0.1:{}\n'.format(XMR_BASE_P2P_PORT + ip))
assert(os.path.exists(config_path))
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_reload(self):
global stop_test
update_thread = None
processes = []
for i in range(3):
processes.append(multiprocessing.Process(target=self.run_thread, args=(i,)))
processes[-1].start()
try:
waitForServer(12700)
wallets = json.loads(urlopen('http://localhost:12701/json/wallets').read())
print('[rm] wallets', dumpj(wallets))
xmr_addr1 = wallets['6']['deposit_address']
num_blocks = 500
raise ValueError('TODO')
'''
btc_addr = btcRpc(1, 'getnewaddress mining_addr bech32')
logging.info('Mining %d Bitcoin blocks to %s', num_blocks, btc_addr)
btcRpc(1, 'generatetoaddress {} {}'.format(num_blocks, btc_addr))
for i in range(20):
blocks = btcRpc(0, 'getblockchaininfo')['blocks']
if blocks >= 500:
break
assert(blocks >= 500)
'''
data = parse.urlencode({
'addr_from': '-1',
'coin_from': '1',
'coin_to': '6',
'amt_from': '1',
'amt_to': '1',
'lockhrs': '24'}).encode()
offer_id = json.loads(urlopen('http://localhost:12700/json/offers/new', data=data).read())
summary = json.loads(urlopen('http://localhost:12700/json').read())
assert(summary['num_sent_offers'] == 1)
logger.info('Waiting for offer:')
waitForNumOffers(12701, 1)
offers = json.loads(urlopen('http://localhost:12701/json/offers').read())
offer = offers[0]
data = parse.urlencode({
'offer_id': offer['offer_id'],
'amount_from': offer['amount_from']}).encode()
bid_id = json.loads(urlopen('http://localhost:12701/json/bids/new', data=data).read())
waitForNumBids(12700, 1)
bids = json.loads(urlopen('http://localhost:12700/json/bids').read())
bid = bids[0]
data = parse.urlencode({
'accept': True
}).encode()
rv = json.loads(urlopen('http://localhost:12700/json/bids/{}'.format(bid['bid_id']), data=data).read())
assert(rv['bid_state'] == 'Accepted')
waitForNumSwapping(12701, 1)
logger.info('Restarting client:')
c1 = processes[1]
c1.terminate()
c1.join()
processes[1] = multiprocessing.Process(target=self.run_thread, args=(1,))
processes[1].start()
waitForServer(12701)
rv = json.loads(urlopen('http://localhost:12701/json').read())
assert(rv['num_swapping'] == 1)
update_thread = threading.Thread(target=updateThread, args=(xmr_addr,))
update_thread.start()
logger.info('Completing swap:')
for i in range(240):
time.sleep(5)
rv = json.loads(urlopen('http://localhost:12700/json/bids/{}'.format(bid['bid_id'])).read())
print(rv)
if rv['bid_state'] == 'Completed':
break
assert(rv['bid_state'] == 'Completed')
except Exception:
traceback.print_exc()
stop_test = True
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

@ -0,0 +1,202 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright (c) 2020 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_init
mkdir -p ${TEST_PATH}/bin/{particl,monero,bitcoin}
cp ~/tmp/particl-0.19.1.2-x86_64-linux-gnu.tar.gz ${TEST_PATH}/bin/particl
cp ~/tmp/monero-0.17.1.5-x86_64-linux-gnu.tar.gz ${TEST_PATH}/bin/monero
cp ~/tmp/bitcoin-0.20.1-x86_64-linux-gnu.tar.gz ${TEST_PATH}/bin/bitcoin
export PYTHONPATH=$(pwd)
python tests/basicswap/test_wallet_init.py
"""
import os
import sys
import json
import time
import shutil
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 (
callrpc_cli,
)
from basicswap.util import (
dumpj
)
from tests.basicswap.mnemonics import mnemonics
import basicswap.config as cfg
import bin.basicswap_prepare as prepareSystem
import bin.basicswap_run as runSystem
test_path = os.path.expanduser(os.getenv('TEST_PATH', '~/test_basicswap1'))
PARTICL_PORT_BASE = int(os.getenv('PARTICL_PORT_BASE', '11938'))
BITCOIN_PORT_BASE = int(os.getenv('BITCOIN_PORT_BASE', '10938'))
XMR_BASE_P2P_PORT = 17792
XMR_BASE_RPC_PORT = 21792
XMR_BASE_ZMQ_PORT = 22792
XMR_BASE_WALLET_RPC_PORT = 23792
stop_test = False
logger = logging.getLogger()
logger.level = logging.DEBUG
if not len(logger.handlers):
logger.addHandler(logging.StreamHandler(sys.stdout))
def waitForServer(port):
for i in range(20):
try:
time.sleep(1)
summary = json.loads(urlopen('http://localhost:{}/json'.format(port)).read())
break
except Exception:
traceback.print_exc()
def waitForNumOffers(port, offers):
for i in range(20):
summary = json.loads(urlopen('http://localhost:{}/json'.format(port)).read())
if summary['num_network_offers'] >= offers:
return
time.sleep(1)
raise ValueError('waitForNumOffers failed')
def waitForNumBids(port, bids):
for i in range(20):
summary = json.loads(urlopen('http://localhost:{}/json'.format(port)).read())
if summary['num_recv_bids'] >= bids:
return
time.sleep(1)
raise ValueError('waitForNumBids failed')
def waitForNumSwapping(port, bids):
for i in range(20):
summary = json.loads(urlopen('http://localhost:{}/json'.format(port)).read())
if summary['num_swapping'] >= bids:
return
time.sleep(1)
raise ValueError('waitForNumSwapping failed')
class Test(unittest.TestCase):
@classmethod
def setUpClass(cls):
super(Test, cls).setUpClass()
for i in range(2):
client_path = os.path.join(test_path, 'client{}'.format(i))
config_path = os.path.join(client_path, cfg.CONFIG_FILENAME)
try:
shutil.rmtree(client_path)
except Exception as ex:
logger.warning('setUpClass %s', str(ex))
testargs = [
'basicswap-prepare',
'-datadir="{}"'.format(client_path),
'-bindir="{}"'.format(os.path.join(test_path, 'bin')),
'-portoffset={}'.format(i),
'-particl_mnemonic="{}"'.format(mnemonics[0]),
'-regtest', '-withoutcoin=litecoin', '-withcoin=monero,bitcoin']
with patch.object(sys, 'argv', testargs):
prepareSystem.main()
with open(os.path.join(client_path, 'particl', 'particl.conf'), 'r') as fp:
lines = fp.readlines()
with open(os.path.join(client_path, 'particl', 'particl.conf'), 'w') as fp:
for line in lines:
if not line.startswith('staking'):
fp.write(line)
fp.write('port={}\n'.format(PARTICL_PORT_BASE + i))
fp.write('bind=127.0.0.1\n')
fp.write('dnsseed=0\n')
fp.write('minstakeinterval=5\n')
for ip in range(3):
if ip != i:
fp.write('connect=localhost:{}\n'.format(PARTICL_PORT_BASE + ip))
# Pruned nodes don't provide blocks
with open(os.path.join(client_path, 'bitcoin', 'bitcoin.conf'), 'r') as fp:
lines = fp.readlines()
with open(os.path.join(client_path, 'bitcoin', 'bitcoin.conf'), 'w') as fp:
for line in lines:
if not line.startswith('prune'):
fp.write(line)
fp.write('port={}\n'.format(BITCOIN_PORT_BASE + i))
fp.write('discover=0\n')
fp.write('dnsseed=0\n')
fp.write('listenonion=0\n')
fp.write('upnp=0\n')
fp.write('bind=127.0.0.1\n')
for ip in range(3):
if ip != i:
fp.write('connect=localhost:{}\n'.format(BITCOIN_PORT_BASE + ip))
with open(os.path.join(client_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 + i))
for ip in range(3):
if ip != i:
fp.write('add-exclusive-node=127.0.0.1:{}\n'.format(XMR_BASE_P2P_PORT + ip))
assert(os.path.exists(config_path))
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):
global stop_test
update_thread = None
processes = []
time.sleep(5)
for i in range(2):
processes.append(multiprocessing.Process(target=self.run_thread, args=(i,)))
processes[-1].start()
try:
waitForServer(12700)
wallets = json.loads(urlopen('http://localhost:12700/json/wallets').read())
print('[rm] wallets', dumpj(wallets))
waitForServer(12701)
wallets = json.loads(urlopen('http://localhost:12701/json/wallets').read())
print('[rm] wallets', dumpj(wallets))
raise ValueError('TODO')
except Exception:
traceback.print_exc()
stop_test = True
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

@ -55,7 +55,7 @@ from tests.basicswap.common import (
TEST_HTTP_PORT, TEST_HTTP_PORT,
) )
from basicswap.contrib.rpcauth import generate_salt, password_to_hmac from basicswap.contrib.rpcauth import generate_salt, password_to_hmac
from bin.basicswap_run import startDaemon from bin.basicswap_run import startDaemon, startXmrDaemon
logger = logging.getLogger() logger = logging.getLogger()
@ -479,12 +479,9 @@ class Test(unittest.TestCase):
for d in cls.xmr_daemons: for d in cls.xmr_daemons:
try: try:
d.wait(timeout=20) d.wait(timeout=20)
if d.stdout: for fp in (d.stdout, d.stderr, d.stdin):
d.stdout.close() if fp:
if d.stderr: fp.close()
d.stderr.close()
if d.stdin:
d.stdin.close()
except Exception as e: except Exception as e:
logging.info('Closing %d, error %s', d.pid, str(e)) logging.info('Closing %d, error %s', d.pid, str(e))
@ -497,12 +494,9 @@ class Test(unittest.TestCase):
for d in cls.part_daemons + cls.btc_daemons: for d in cls.part_daemons + cls.btc_daemons:
try: try:
d.wait(timeout=20) d.wait(timeout=20)
if d.stdout: for fp in (d.stdout, d.stderr, d.stdin):
d.stdout.close() if fp:
if d.stderr: fp.close()
d.stderr.close()
if d.stdin:
d.stdin.close()
except Exception as e: except Exception as e:
logging.info('Closing %d, error %s', d.pid, str(e)) logging.info('Closing %d, error %s', d.pid, str(e))