basicswap/bin/basicswap_prepare.py

824 lines
34 KiB
Python
Raw Normal View History

2019-07-21 18:50:32 +00:00
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
2021-01-10 18:30:07 +00:00
# Copyright (c) 2019-2021 tecnovert
2019-07-21 18:50:32 +00:00
# Distributed under the MIT software license, see the accompanying
2020-10-30 08:55:45 +00:00
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
2019-07-21 18:50:32 +00:00
import os
2020-12-02 21:19:10 +00:00
import sys
2019-07-21 18:50:32 +00:00
import json
import mmap
2020-12-02 21:19:10 +00:00
import stat
import gnupg
import signal
2020-12-02 21:19:10 +00:00
import hashlib
2019-07-21 18:50:32 +00:00
import tarfile
2019-07-28 12:45:26 +00:00
import zipfile
2019-07-21 18:50:32 +00:00
import logging
import platform
2020-12-02 21:19:10 +00:00
import urllib.parse
from urllib.request import urlretrieve
2019-07-21 18:50:32 +00:00
2019-07-21 19:20:36 +00:00
import basicswap.config as cfg
2020-02-01 23:18:29 +00:00
from basicswap.rpc import (
callrpc_cli,
waitForRPC,
)
from basicswap.basicswap import BasicSwap
from basicswap.chainparams import Coins
from basicswap.contrib.rpcauth import generate_salt, password_to_hmac
from bin.basicswap_run import startDaemon, startXmrWalletDaemon
2019-07-21 19:20:36 +00:00
if platform.system() == 'Darwin':
2021-01-10 18:30:07 +00:00
BIN_ARCH = 'osx64'
FILE_EXT = 'tar.gz'
2019-07-28 12:45:26 +00:00
elif platform.system() == 'Windows':
2021-01-10 18:30:07 +00:00
BIN_ARCH = 'win64'
FILE_EXT = 'zip'
else:
2021-01-10 18:30:07 +00:00
BIN_ARCH = 'x86_64-linux-gnu'
FILE_EXT = 'tar.gz'
2019-07-21 18:50:32 +00:00
known_coins = {
2021-09-04 16:54:48 +00:00
'particl': ('0.21.2.3', ''),
'litecoin': ('0.18.1', ''),
'bitcoin': ('0.21.1', ''),
'namecoin': ('0.18.0', ''),
2021-09-04 16:54:48 +00:00
'monero': ('0.17.2.3', ''),
}
2019-07-21 18:50:32 +00:00
logger = logging.getLogger()
logger.level = logging.DEBUG
if not len(logger.handlers):
logger.addHandler(logging.StreamHandler(sys.stdout))
XMR_RPC_HOST = os.getenv('XMR_RPC_HOST', '127.0.0.1')
BASE_XMR_RPC_PORT = int(os.getenv('BASE_XMR_RPC_PORT', 29798))
BASE_XMR_ZMQ_PORT = int(os.getenv('BASE_XMR_ZMQ_PORT', 30898))
BASE_XMR_WALLET_PORT = int(os.getenv('BASE_XMR_WALLET_PORT', 29998))
XMR_WALLET_RPC_HOST = os.getenv('XMR_WALLET_RPC_HOST', '127.0.0.1')
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')
2021-09-04 16:54:48 +00:00
XMR_SITE_COMMIT = 'ba7fcd947349416bedc0eb2e864416c3779af820' # Lock hashes.txt to monero version
DEFAULT_XMR_RESTORE_HEIGHT = 2245107
UI_HTML_PORT = int(os.getenv('UI_HTML_PORT', 12700))
2021-01-16 08:21:59 +00:00
PART_ZMQ_PORT = int(os.getenv('PART_ZMQ_PORT', 20792))
2019-07-21 18:50:32 +00:00
PART_RPC_HOST = os.getenv('PART_RPC_HOST', '127.0.0.1')
LTC_RPC_HOST = os.getenv('LTC_RPC_HOST', '127.0.0.1')
BTC_RPC_HOST = os.getenv('BTC_RPC_HOST', '127.0.0.1')
NMC_RPC_HOST = os.getenv('NMC_RPC_HOST', '127.0.0.1')
2020-12-27 19:39:10 +00:00
2021-01-16 08:21:59 +00:00
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))
PART_RPC_USER = os.getenv('PART_RPC_USER', '')
PART_RPC_PWD = os.getenv('PART_RPC_PWD', '')
BTC_RPC_USER = os.getenv('BTC_RPC_USER', '')
BTC_RPC_PWD = os.getenv('BTC_RPC_PWD', '')
LTC_RPC_USER = os.getenv('LTC_RPC_USER', '')
LTC_RPC_PWD = os.getenv('LTC_RPC_PWD', '')
COINS_BIND_IP = os.getenv('COINS_BIND_IP', '127.0.0.1')
2021-01-16 08:21:59 +00:00
2021-01-10 18:30:07 +00:00
extract_core_overwrite = True
2020-12-27 19:39:10 +00:00
2019-07-21 18:50:32 +00:00
def make_reporthook():
2020-02-01 23:18:29 +00:00
read = 0 # Number of bytes read so far
2019-07-21 18:50:32 +00:00
last_percent_str = ''
def reporthook(blocknum, blocksize, totalsize):
nonlocal read
nonlocal last_percent_str
read += blocksize
if totalsize > 0:
percent_str = '%5.0f%%' % (read * 1e2 / totalsize)
2019-07-21 18:50:32 +00:00
if percent_str != last_percent_str:
logger.info(percent_str)
last_percent_str = percent_str
else:
logger.info('read %d' % (read,))
return reporthook
def downloadFile(url, path):
logger.info('Downloading file %s', url)
2021-01-10 18:30:07 +00:00
logger.info('To %s', path)
2019-07-21 18:50:32 +00:00
opener = urllib.request.build_opener()
opener.addheaders = [('User-agent', 'Mozilla/5.0')]
urllib.request.install_opener(opener)
urlretrieve(url, path, make_reporthook())
def extractCore(coin, version_pair, settings, bin_dir, release_path):
version, version_tag = version_pair
logger.info('extractCore %s v%s%s', coin, version, version_tag)
if coin == 'monero':
2021-01-10 18:30:07 +00:00
bins = ['monerod', 'monero-wallet-rpc']
num_exist = 0
for b in bins:
out_path = os.path.join(bin_dir, b)
if os.path.exists(out_path):
num_exist += 1
if not extract_core_overwrite and num_exist == len(bins):
logger.info('Skipping extract, files exist.')
return
with tarfile.open(release_path) as ft:
for member in ft.getmembers():
if member.isdir():
continue
2021-01-10 18:30:07 +00:00
bin_name = os.path.basename(member.name)
if bin_name not in bins:
continue
out_path = os.path.join(bin_dir, bin_name)
if (not os.path.exists(out_path)) or extract_core_overwrite:
with open(out_path, 'wb') as fout, ft.extractfile(member) as fi:
2021-01-10 18:30:07 +00:00
fout.write(fi.read())
try:
os.chmod(out_path, stat.S_IRWXU | stat.S_IXGRP | stat.S_IXOTH)
except Exception as e:
logging.warning('Unable to set file permissions: %s, for %s', str(e), out_path)
return
bins = [coin + 'd', coin + '-cli', coin + '-tx']
versions = version.split('.')
if int(versions[1]) >= 19:
bins.append(coin + '-wallet')
if 'win32' in BIN_ARCH or 'win64' in BIN_ARCH:
with zipfile.ZipFile(release_path) as fz:
for b in bins:
b += '.exe'
out_path = os.path.join(bin_dir, b)
2021-01-10 18:30:07 +00:00
if (not os.path.exists(out_path)) or extract_core_overwrite:
with open(out_path, 'wb') as fout:
fout.write(fz.read('{}-{}/bin/{}'.format(coin, version, b)))
try:
os.chmod(out_path, stat.S_IRWXU | stat.S_IXGRP | stat.S_IXOTH)
except Exception as e:
logging.warning('Unable to set file permissions: %s, for %s', str(e), out_path)
else:
with tarfile.open(release_path) as ft:
for b in bins:
out_path = os.path.join(bin_dir, b)
2021-01-10 18:30:07 +00:00
if not os.path.exists(out_path) or extract_core_overwrite:
with open(out_path, 'wb') as fout, ft.extractfile('{}-{}/bin/{}'.format(coin, version + version_tag, b)) as fi:
2021-01-10 18:30:07 +00:00
fout.write(fi.read())
try:
os.chmod(out_path, stat.S_IRWXU | stat.S_IXGRP | stat.S_IXOTH)
except Exception as e:
logging.warning('Unable to set file permissions: %s, for %s', str(e), out_path)
def prepareCore(coin, version_pair, settings, data_dir):
version, version_tag = version_pair
logger.info('prepareCore %s v%s%s', coin, version, version_tag)
2019-07-21 18:50:32 +00:00
bin_dir = os.path.expanduser(settings['chainclients'][coin]['bindir'])
2019-07-21 18:50:32 +00:00
if not os.path.exists(bin_dir):
os.makedirs(bin_dir)
if 'osx' in BIN_ARCH:
os_dir_name = 'osx-unsigned'
os_name = 'osx'
elif 'win32' in BIN_ARCH or 'win64' in BIN_ARCH:
os_dir_name = 'win-unsigned'
os_name = 'win'
else:
os_dir_name = 'linux'
os_name = 'linux'
2020-11-07 11:08:07 +00:00
if coin == 'monero':
2021-01-10 18:30:07 +00:00
use_file_ext = 'tar.bz2' if FILE_EXT == 'tar.gz' else FILE_EXT
release_filename = '{}-{}-{}.{}'.format(coin, version, BIN_ARCH, use_file_ext)
if os_name == 'osx':
os_name = 'mac'
2021-01-10 18:30:07 +00:00
release_url = 'https://downloads.getmonero.org/cli/monero-{}-x64-v{}.{}'.format(os_name, version, use_file_ext)
2020-11-07 11:08:07 +00:00
release_path = os.path.join(bin_dir, release_filename)
if not os.path.exists(release_path):
downloadFile(release_url, release_path)
assert_filename = 'monero-{}-hashes.txt'.format(version)
2020-12-27 12:09:06 +00:00
# assert_url = 'https://www.getmonero.org/downloads/hashes.txt'
assert_url = 'https://raw.githubusercontent.com/monero-project/monero-site/{}/downloads/hashes.txt'.format(XMR_SITE_COMMIT)
assert_path = os.path.join(bin_dir, assert_filename)
if not os.path.exists(assert_path):
downloadFile(assert_url, assert_path)
2019-07-21 18:50:32 +00:00
else:
release_filename = '{}-{}-{}.{}'.format(coin, version + version_tag, BIN_ARCH, FILE_EXT)
if coin == 'particl':
signing_key_name = 'tecnovert'
release_url = 'https://github.com/tecnovert/particl-core/releases/download/v{}/{}'.format(version + version_tag, release_filename)
assert_filename = '{}-{}-{}-build.assert'.format(coin, os_name, version)
assert_url = 'https://raw.githubusercontent.com/tecnovert/gitian.sigs/master/%s-%s/%s/%s' % (version + version_tag, os_dir_name, signing_key_name, assert_filename)
elif coin == 'litecoin':
signing_key_name = 'thrasher'
release_url = 'https://download2.litecoin.org/litecoin-{}/{}/{}'.format(version, os_name, release_filename)
assert_filename = '{}-{}-{}-build.assert'.format(coin, os_name, version.rsplit('.', 1)[0])
assert_url = 'https://raw.githubusercontent.com/litecoin-project/gitian.sigs.ltc/master/%s-%s/%s/%s' % (version, os_dir_name, signing_key_name, assert_filename)
elif coin == 'bitcoin':
signing_key_name = 'laanwj'
release_url = 'https://bitcoincore.org/bin/bitcoin-core-{}/{}'.format(version, release_filename)
assert_filename = '{}-core-{}-{}-build.assert'.format(coin, os_name, '.'.join(version.split('.')[:2]))
assert_url = 'https://raw.githubusercontent.com/bitcoin-core/gitian.sigs/master/%s-%s/%s/%s' % (version, os_dir_name, signing_key_name, assert_filename)
elif coin == 'namecoin':
signing_key_name = 'JeremyRand'
release_url = 'https://beta.namecoin.org/files/namecoin-core/namecoin-core-{}/{}'.format(version, release_filename)
assert_filename = '{}-{}-{}-build.assert'.format(coin, os_name, version.rsplit('.', 1)[0])
assert_url = 'https://raw.githubusercontent.com/namecoin/gitian.sigs/master/%s-%s/%s/%s' % (version, os_dir_name, signing_key_name, assert_filename)
else:
raise ValueError('Unknown coin')
2019-07-21 18:50:32 +00:00
assert_sig_filename = assert_filename + '.sig'
assert_sig_url = assert_url + '.sig'
release_path = os.path.join(bin_dir, release_filename)
if not os.path.exists(release_path):
downloadFile(release_url, release_path)
2019-07-21 18:50:32 +00:00
# Rename assert files with full version
assert_filename = '{}-{}-{}-build.assert'.format(coin, os_name, version)
assert_path = os.path.join(bin_dir, assert_filename)
if not os.path.exists(assert_path):
downloadFile(assert_url, assert_path)
2019-07-21 18:50:32 +00:00
assert_sig_filename = '{}-{}-{}-build.assert.sig'.format(coin, os_name, version)
assert_sig_path = os.path.join(bin_dir, assert_sig_filename)
if not os.path.exists(assert_sig_path):
downloadFile(assert_sig_url, assert_sig_path)
2019-07-21 18:50:32 +00:00
hasher = hashlib.sha256()
with open(release_path, 'rb') as fp:
hasher.update(fp.read())
release_hash = hasher.digest()
logger.info('%s hash: %s', release_filename, release_hash.hex())
with open(assert_path, 'rb', 0) as fp, mmap.mmap(fp.fileno(), 0, access=mmap.ACCESS_READ) as s:
if s.find(bytes(release_hash.hex(), 'utf-8')) == -1:
raise ValueError('Error: release hash %s not found in assert file.' % (release_hash.hex()))
else:
logger.info('Found release hash in assert file.')
"""
gnupghome = os.path.join(data_dir, 'gpg')
if not os.path.exists(gnupghome):
os.makedirs(gnupghome)
"""
gpg = gnupg.GPG()
if coin == 'monero':
with open(assert_path, 'rb') as fp:
verified = gpg.verify_file(fp)
if verified.username is None:
logger.warning('Signature not verified.')
pubkeyurl = 'https://raw.githubusercontent.com/monero-project/monero/master/utils/gpg_keys/binaryfate.asc'
logger.info('Importing public key from url: ' + pubkeyurl)
rv = gpg.import_keys(urllib.request.urlopen(pubkeyurl).read())
print('import_keys', rv)
2020-12-01 20:45:03 +00:00
assert('F0AF4D462A0BDF92' in rv.fingerprints[0])
2020-12-31 08:01:01 +00:00
gpg.trust_keys(rv.fingerprints[0], 'TRUST_FULLY')
with open(assert_path, 'rb') as fp:
verified = gpg.verify_file(fp)
else:
with open(assert_sig_path, 'rb') as fp:
verified = gpg.verify_file(fp, assert_path)
2019-07-21 18:50:32 +00:00
if verified.username is None:
logger.warning('Signature not verified.')
pubkeyurl = 'https://raw.githubusercontent.com/tecnovert/basicswap/master/gitianpubkeys/{}_{}.pgp'.format(coin, signing_key_name)
logger.info('Importing public key from url: ' + pubkeyurl)
rv = gpg.import_keys(urllib.request.urlopen(pubkeyurl).read())
for key in rv.fingerprints:
gpg.trust_keys(key, 'TRUST_FULLY')
with open(assert_sig_path, 'rb') as fp:
verified = gpg.verify_file(fp, assert_path)
if verified.valid is False \
and not (verified.status == 'signature valid' and verified.key_status == 'signing key has expired'):
raise ValueError('Signature verification failed.')
2019-07-21 18:50:32 +00:00
extractCore(coin, version_pair, settings, bin_dir, release_path)
2019-07-21 18:50:32 +00:00
2020-12-27 19:39:10 +00:00
def prepareDataDir(coin, settings, chain, particl_mnemonic):
core_settings = settings['chainclients'][coin]
bin_dir = core_settings['bindir']
data_dir = core_settings['datadir']
if not os.path.exists(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')
2021-01-10 18:30:07 +00:00
else:
fp.write('bootstrap-daemon-address=auto\n')
fp.write('restricted-rpc=1\n')
if 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={}\n'.format(COINS_BIND_IP))
fp.write('zmq-rpc-bind-port={}\n'.format(core_settings['zmqport']))
fp.write('zmq-rpc-bind-ip={}\n'.format(COINS_BIND_IP))
2021-01-10 18:30:07 +00:00
fp.write('prune-blockchain=1\n')
wallets_dir = core_settings.get('walletsdir', data_dir)
if not os.path.exists(wallets_dir):
os.makedirs(wallets_dir)
wallet_conf_path = os.path.join(wallets_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('rpc-bind-ip={}\n'.format(COINS_BIND_IP))
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')
if os.path.exists(core_conf_path):
exitWithError('{} exists'.format(core_conf_path))
with open(core_conf_path, 'w') as fp:
if chain != 'mainnet':
fp.write(chain + '=1\n')
if chain == 'testnet':
fp.write('[test]\n\n')
2019-07-27 17:26:06 +00:00
if chain == 'regtest':
fp.write('[regtest]\n\n')
else:
logger.warning('Unknown chain %s', chain)
if COINS_BIND_IP != '127.0.0.1':
fp.write('rpcallowip=127.0.0.1\n')
fp.write('rpcallowip=172.0.0.0/8\n') # Allow 172.x.x.x, range used by docker
fp.write('rpcbind={}\n'.format(COINS_BIND_IP))
fp.write('rpcport={}\n'.format(core_settings['rpcport']))
fp.write('printtoconsole=0\n')
fp.write('daemon=0\n')
fp.write('wallet=wallet.dat\n')
salt = generate_salt(16)
if coin == 'particl':
fp.write('debugexclude=libevent\n')
fp.write('zmqpubsmsg=tcp://{}:{}\n'.format(COINS_BIND_IP, settings['zmqport']))
2019-08-15 22:31:39 +00:00
fp.write('spentindex=1\n')
fp.write('txindex=1\n')
fp.write('staking=0\n')
if PART_RPC_USER != '':
fp.write('rpcauth={}:{}${}\n'.format(PART_RPC_USER, salt, password_to_hmac(salt, PART_RPC_PWD)))
if particl_mnemonic == 'none':
fp.write('createdefaultmasterkey=1')
elif coin == 'litecoin':
fp.write('prune=2000\n')
if LTC_RPC_USER != '':
fp.write('rpcauth={}:{}${}\n'.format(LTC_RPC_USER, salt, password_to_hmac(salt, LTC_RPC_PWD)))
elif coin == 'bitcoin':
fp.write('prune=2000\n')
fp.write('fallbackfee=0.0002\n')
if BTC_RPC_USER != '':
fp.write('rpcauth={}:{}${}\n'.format(BTC_RPC_USER, salt, password_to_hmac(salt, BTC_RPC_PWD)))
elif coin == 'namecoin':
fp.write('prune=2000\n')
else:
logger.warning('Unknown coin %s', coin)
wallet_util = coin + '-wallet'
if os.path.exists(os.path.join(bin_dir, wallet_util)):
logger.info('Creating wallet.dat for {}.'.format(wallet_util.capitalize()))
callrpc_cli(bin_dir, data_dir, chain, '-wallet=wallet.dat create', wallet_util)
2019-07-21 18:50:32 +00:00
def printVersion():
from basicswap import __version__
2021-01-10 18:30:07 +00:00
logger.info('Basicswap version: %s', __version__)
logger.info('Core versions:')
for coin, version in known_coins.items():
logger.info('\t%s: %s%s', coin, version[0], version[1])
2019-07-21 18:50:32 +00:00
def printHelp():
logger.info('Usage: basicswap-prepare ')
logger.info('\n--help, -h Print help.')
2019-07-22 18:29:59 +00:00
logger.info('--version, -v Print version.')
2020-02-01 18:57:20 +00:00
logger.info('--datadir=PATH Path to basicswap data directory, default:{}.'.format(cfg.DEFAULT_DATADIR))
logger.info('--bindir=PATH Path to cores directory, default:datadir/bin.')
2019-07-22 18:29:59 +00:00
logger.info('--mainnet Run in mainnet mode.')
logger.info('--testnet Run in testnet mode.')
logger.info('--regtest Run in regtest mode.')
2019-10-02 20:34:03 +00:00
logger.info('--particl_mnemonic= Recovery phrase to use for the Particl wallet, default is randomly generated,\n'
+ ' "none" to set autogenerate account mode.')
2019-07-22 18:29:59 +00:00
logger.info('--withcoin= Prepare system to run daemon for coin.')
logger.info('--withoutcoin= Do not prepare system to run daemon for coin.')
logger.info('--addcoin= Add coin to existing setup.')
2019-07-25 18:30:01 +00:00
logger.info('--disablecoin= Make coin inactive.')
logger.info('--preparebinonly Don\'t prepare settings or datadirs.')
2020-12-27 19:39:10 +00:00
logger.info('--nocores Don\'t download and extract any coin clients.')
logger.info('--portoffset=n Raise all ports by n.')
logger.info('--htmlhost= Interface to host on, default:127.0.0.1.')
logger.info('--xmrrestoreheight=n Block height to restore Monero wallet from, default:{}.'.format(DEFAULT_XMR_RESTORE_HEIGHT))
2021-01-10 18:30:07 +00:00
logger.info('--noextractover Prevent extracting cores if files exist. Speeds up tests')
logger.info('\n' + 'Known coins: %s', ', '.join(known_coins.keys()))
2019-07-21 18:50:32 +00:00
2019-07-21 19:20:36 +00:00
def make_rpc_func(bin_dir, data_dir, chain):
bin_dir = bin_dir
data_dir = data_dir
chain = chain
2019-07-21 19:20:36 +00:00
def rpc_func(cmd):
nonlocal bin_dir
nonlocal data_dir
nonlocal chain
2019-07-21 19:20:36 +00:00
return callrpc_cli(bin_dir, data_dir, chain, cmd, cfg.PARTICL_CLI)
return rpc_func
def exitWithError(error_msg):
sys.stderr.write('Error: {}, exiting.\n'.format(error_msg))
sys.exit(1)
2019-07-21 18:50:32 +00:00
def main():
2021-01-10 18:30:07 +00:00
global extract_core_overwrite
2019-07-21 18:50:32 +00:00
data_dir = None
bin_dir = None
port_offset = None
2019-07-21 18:50:32 +00:00
chain = 'mainnet'
particl_wallet_mnemonic = None
prepare_bin_only = False
2020-12-27 19:39:10 +00:00
no_cores = False
2021-01-05 18:10:33 +00:00
with_coins = {'particl'}
add_coin = ''
2019-07-25 18:30:01 +00:00
disable_coin = ''
htmlhost = '127.0.0.1'
xmr_restore_height = DEFAULT_XMR_RESTORE_HEIGHT
2019-07-21 18:50:32 +00:00
for v in sys.argv[1:]:
if len(v) < 2 or v[0] != '-':
exitWithError('Unknown argument {}'.format(v))
2019-07-21 18:50:32 +00:00
s = v.split('=')
name = s[0].strip()
for i in range(2):
if name[0] == '-':
name = name[1:]
if name == 'v' or name == 'version':
printVersion()
return 0
if name == 'h' or name == 'help':
printHelp()
return 0
if name == 'mainnet':
continue
if name == 'testnet':
chain = 'testnet'
continue
if name == 'regtest':
chain = 'regtest'
continue
if name == 'preparebinonly':
prepare_bin_only = True
continue
2020-12-27 19:39:10 +00:00
if name == 'nocores':
no_cores = True
continue
2021-01-10 18:30:07 +00:00
if name == 'noextractover':
extract_core_overwrite = False
continue
2019-07-21 18:50:32 +00:00
if len(s) == 2:
if name == 'datadir':
data_dir = os.path.expanduser(s[1].strip('"'))
continue
if name == 'bindir':
bin_dir = os.path.expanduser(s[1].strip('"'))
continue
if name == 'portoffset':
port_offset = int(s[1])
2019-07-21 18:50:32 +00:00
continue
if name == 'particl_mnemonic':
particl_wallet_mnemonic = s[1].strip('"')
2019-07-21 18:50:32 +00:00
continue
2020-12-01 20:45:03 +00:00
if name == 'withcoin' or name == 'withcoins':
coins = s[1].split(',')
for coin in coins:
if coin not in known_coins:
exitWithError('Unknown coin {}'.format(coin))
with_coins.add(coin)
continue
2020-12-01 20:45:03 +00:00
if name == 'withoutcoin' or name == 'withoutcoins':
coins = s[1].split(',')
for coin in coins:
if coin not in known_coins:
exitWithError('Unknown coin {}'.format(coin))
with_coins.discard(coin)
continue
if name == 'addcoin':
if s[1] not in known_coins:
exitWithError('Unknown coin {}'.format(s[1]))
add_coin = s[1]
with_coins = [add_coin, ]
continue
2019-07-25 18:30:01 +00:00
if name == 'disablecoin':
if s[1] not in known_coins:
exitWithError('Unknown coin {}'.format(s[1]))
disable_coin = s[1]
continue
if name == 'htmlhost':
htmlhost = s[1].strip('"')
continue
if name == 'xmrrestoreheight':
xmr_restore_height = int(s[1])
continue
2019-07-21 18:50:32 +00:00
exitWithError('Unknown argument {}'.format(v))
2019-07-21 18:50:32 +00:00
if data_dir is None:
2020-02-01 18:57:20 +00:00
data_dir = os.path.join(os.path.expanduser(cfg.DEFAULT_DATADIR))
if bin_dir is None:
bin_dir = os.path.join(data_dir, 'bin')
2021-01-10 18:30:07 +00:00
logger.info('datadir: %s', data_dir)
logger.info('bindir: %s', bin_dir)
2019-07-21 18:50:32 +00:00
logger.info('Chain: %s', chain)
if port_offset is None:
port_offset = 300 if chain == 'testnet' else 0
2019-07-21 18:50:32 +00:00
if not os.path.exists(data_dir):
os.makedirs(data_dir)
2020-02-01 18:57:20 +00:00
config_path = os.path.join(data_dir, cfg.CONFIG_FILENAME)
2019-07-27 17:26:06 +00:00
withchainclients = {}
chainclients = {
'particl': {
'connection_type': 'rpc',
'manage_daemon': True if ('particl' in with_coins and PART_RPC_HOST == '127.0.0.1') else False,
2020-12-27 19:39:10 +00:00
'rpchost': PART_RPC_HOST,
2021-01-16 08:21:59 +00:00
'rpcport': PART_RPC_PORT + port_offset,
2020-12-27 19:39:10 +00:00
'datadir': os.getenv('PART_DATA_DIR', os.path.join(data_dir, 'particl')),
'bindir': os.path.join(bin_dir, 'particl'),
'blocks_confirmed': 2,
'override_feerate': 0.002,
2019-07-26 21:03:56 +00:00
'conf_target': 2,
'core_version_group': 18,
2019-08-05 22:04:40 +00:00
'chain_lookups': 'local',
},
'litecoin': {
'connection_type': 'rpc' if 'litecoin' in with_coins else 'none',
'manage_daemon': True if ('litecoin' in with_coins and LTC_RPC_HOST == '127.0.0.1') else False,
2020-12-27 19:39:10 +00:00
'rpchost': LTC_RPC_HOST,
2021-01-16 08:21:59 +00:00
'rpcport': LTC_RPC_PORT + port_offset,
2020-12-27 19:39:10 +00:00
'datadir': os.getenv('LTC_DATA_DIR', os.path.join(data_dir, 'litecoin')),
'bindir': os.path.join(bin_dir, 'litecoin'),
'use_segwit': True,
2019-07-26 21:03:56 +00:00
'blocks_confirmed': 2,
'conf_target': 2,
'core_version_group': 18,
2019-08-05 22:04:40 +00:00
'chain_lookups': 'local',
},
'bitcoin': {
'connection_type': 'rpc' if 'bitcoin' in with_coins else 'none',
'manage_daemon': True if ('bitcoin' in with_coins and BTC_RPC_HOST == '127.0.0.1') else False,
2020-12-27 19:39:10 +00:00
'rpchost': BTC_RPC_HOST,
2021-01-16 08:21:59 +00:00
'rpcport': BTC_RPC_PORT + port_offset,
2020-12-27 19:39:10 +00:00
'datadir': os.getenv('BTC_DATA_DIR', os.path.join(data_dir, 'bitcoin')),
'bindir': os.path.join(bin_dir, 'bitcoin'),
'use_segwit': True,
2019-07-26 21:03:56 +00:00
'blocks_confirmed': 1,
'conf_target': 2,
'core_version_group': 18,
2019-08-05 22:04:40 +00:00
'chain_lookups': 'local',
},
'namecoin': {
'connection_type': 'rpc' if 'namecoin' in with_coins else 'none',
'manage_daemon': True if ('namecoin' in with_coins and NMC_RPC_HOST == '127.0.0.1') else False,
2020-12-27 19:39:10 +00:00
'rpchost': NMC_RPC_HOST,
2021-01-16 08:21:59 +00:00
'rpcport': NMC_RPC_PORT + port_offset,
2020-12-27 19:39:10 +00:00
'datadir': os.getenv('NMC_DATA_DIR', os.path.join(data_dir, 'namecoin')),
'bindir': os.path.join(bin_dir, 'namecoin'),
'use_segwit': False,
'use_csv': False,
2019-07-26 21:03:56 +00:00
'blocks_confirmed': 1,
'conf_target': 2,
'core_version_group': 18,
2019-08-05 22:04:40 +00:00
'chain_lookups': 'local',
},
'monero': {
'connection_type': 'rpc' if 'monero' in with_coins else 'none',
'manage_daemon': True if ('monero' in with_coins and XMR_RPC_HOST == '127.0.0.1') else False,
'manage_wallet_daemon': True if ('monero' in with_coins and XMR_WALLET_RPC_HOST == '127.0.0.1') else False,
'rpcport': BASE_XMR_RPC_PORT + port_offset,
'zmqport': BASE_XMR_ZMQ_PORT + port_offset,
'walletrpcport': BASE_XMR_WALLET_PORT + port_offset,
'rpchost': XMR_RPC_HOST,
2020-12-27 19:39:10 +00:00
'walletrpchost': XMR_WALLET_RPC_HOST,
'walletrpcuser': XMR_WALLET_RPC_USER,
'walletrpcpassword': XMR_WALLET_RPC_PWD,
'walletfile': 'swap_wallet',
2020-12-27 19:39:10 +00:00
'datadir': os.getenv('XMR_DATA_DIR', os.path.join(data_dir, 'monero')),
'bindir': os.path.join(bin_dir, 'monero'),
'restore_height': xmr_restore_height,
'blocks_confirmed': 7, # TODO: 10?
}
}
if PART_RPC_USER != '':
chainclients['particl']['rpcuser'] = PART_RPC_USER
chainclients['particl']['rpcpassword'] = PART_RPC_PWD
if LTC_RPC_USER != '':
chainclients['litecoin']['rpcuser'] = LTC_RPC_USER
chainclients['litecoin']['rpcpassword'] = LTC_RPC_PWD
if BTC_RPC_USER != '':
chainclients['bitcoin']['rpcuser'] = BTC_RPC_USER
chainclients['bitcoin']['rpcpassword'] = BTC_RPC_PWD
chainclients['monero']['walletsdir'] = os.getenv('XMR_WALLETS_DIR', chainclients['monero']['datadir'])
2019-07-25 18:30:01 +00:00
if disable_coin != '':
logger.info('Disabling coin: %s', disable_coin)
if not os.path.exists(config_path):
exitWithError('{} does not exist'.format(config_path))
with open(config_path) as fs:
settings = json.load(fs)
if disable_coin not in settings['chainclients']:
exitWithError('{} has not been prepared'.format(disable_coin))
settings['chainclients'][disable_coin]['connection_type'] = 'none'
settings['chainclients'][disable_coin]['manage_daemon'] = False
with open(config_path, 'w') as fp:
json.dump(settings, fp, indent=4)
logger.info('Done.')
return 0
if add_coin != '':
logger.info('Adding coin: %s', add_coin)
if not os.path.exists(config_path):
exitWithError('{} does not exist'.format(config_path))
with open(config_path) as fs:
settings = json.load(fs)
if add_coin in settings['chainclients']:
2019-07-25 18:30:01 +00:00
coin_settings = settings['chainclients'][add_coin]
if coin_settings['connection_type'] == 'none' and coin_settings['manage_daemon'] is False:
logger.info('Enabling coin: %s', add_coin)
coin_settings['connection_type'] = 'rpc'
coin_settings['manage_daemon'] = True
with open(config_path, 'w') as fp:
json.dump(settings, fp, indent=4)
logger.info('Done.')
return 0
exitWithError('{} is already in the settings file'.format(add_coin))
settings['chainclients'][add_coin] = chainclients[add_coin]
2020-12-27 19:39:10 +00:00
if not no_cores:
prepareCore(add_coin, known_coins[add_coin], settings, data_dir)
if not prepare_bin_only:
2020-12-27 19:39:10 +00:00
prepareDataDir(add_coin, settings, chain, particl_wallet_mnemonic)
with open(config_path, 'w') as fp:
json.dump(settings, fp, indent=4)
logger.info('Done.')
return 0
logger.info('With coins: %s', ', '.join(with_coins))
if os.path.exists(config_path):
if not prepare_bin_only:
exitWithError('{} exists'.format(config_path))
else:
with open(config_path) as fs:
settings = json.load(fs)
else:
for c in with_coins:
withchainclients[c] = chainclients[c]
settings = {
'debug': True,
'zmqhost': 'tcp://127.0.0.1',
2021-01-16 08:21:59 +00:00
'zmqport': PART_ZMQ_PORT + port_offset,
'htmlhost': htmlhost,
2021-01-16 08:21:59 +00:00
'htmlport': UI_HTML_PORT + port_offset,
'network_key': '7sW2UEcHXvuqEjkpE5mD584zRaQYs6WXYohue4jLFZPTvMSxwvgs',
'network_pubkey': '035758c4a22d7dd59165db02a56156e790224361eb3191f02197addcb3bde903d2',
'chainclients': withchainclients,
2020-12-02 21:19:10 +00:00
'min_delay_event': 5, # Min delay in seconds before reacting to an event
'max_delay_event': 50, # Max delay in seconds before reacting to an event
'check_progress_seconds': 60,
'check_watched_seconds': 60,
'check_expired_seconds': 60
}
2019-07-21 18:50:32 +00:00
2020-12-27 19:39:10 +00:00
if not no_cores:
for c in with_coins:
prepareCore(c, known_coins[c], settings, data_dir)
if prepare_bin_only:
logger.info('Done.')
return 0
for c in with_coins:
2020-12-27 19:39:10 +00:00
prepareDataDir(c, settings, chain, particl_wallet_mnemonic)
2019-07-21 18:50:32 +00:00
with open(config_path, 'w') as fp:
json.dump(settings, fp, indent=4)
if particl_wallet_mnemonic == 'none':
logger.info('Done.')
return 0
2019-07-21 19:20:36 +00:00
logger.info('Loading Particl mnemonic')
particl_settings = settings['chainclients']['particl']
partRpc = make_rpc_func(particl_settings['bindir'], particl_settings['datadir'], chain)
daemons = []
daemons.append(startDaemon(particl_settings['datadir'], particl_settings['bindir'], cfg.PARTICLD, ['-noconnect', '-nofindpeers', '-nostaking', '-nodnsseed', '-nolisten']))
2019-07-21 19:20:36 +00:00
try:
waitForRPC(partRpc)
if particl_wallet_mnemonic is None:
particl_wallet_mnemonic = partRpc('mnemonic new')['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)
swap_client.finalise()
del swap_client
2019-07-21 19:20:36 +00:00
finally:
for d in daemons:
logging.info('Interrupting {}'.format(d.pid))
d.send_signal(signal.SIGINT)
d.wait(timeout=120)
for fp in (d.stdout, d.stderr, d.stdin):
if fp:
fp.close()
2019-07-21 19:20:36 +00:00
logger.info('IMPORTANT - Save your particl wallet recovery phrase:\n{}\n'.format(particl_wallet_mnemonic))
2019-07-21 18:50:32 +00:00
logger.info('Done.')
if __name__ == '__main__':
main()