Exemple #1
0
    def setUpClass(cls):
        super(Test, cls).setUpClass()

        eckey = ECKey()
        eckey.generate()
        cls.network_key = toWIF(PREFIX_SECRET_KEY_REGTEST, eckey.get_bytes())
        cls.network_pubkey = eckey.get_pubkey().get_bytes().hex()

        if os.path.isdir(cfg.TEST_DATADIRS):
            logging.info('Removing ' + cfg.TEST_DATADIRS)
            shutil.rmtree(cfg.TEST_DATADIRS)

        for i in range(NUM_NODES):
            prepareDir(cfg.TEST_DATADIRS, i, cls.network_key, cls.network_pubkey)

        prepareOtherDir(cfg.TEST_DATADIRS, NMC_NODE)
        prepareOtherDir(cfg.TEST_DATADIRS, BTC_NODE, 'bitcoin.conf')

        cls.daemons = []
        cls.swap_clients = []
        cls.http_threads = []

        cls.daemons.append(startDaemon(os.path.join(cfg.TEST_DATADIRS, str(BTC_NODE)), cfg.BITCOIN_BINDIR, cfg.BITCOIND))
        logging.info('Started %s %d', cfg.BITCOIND, cls.daemons[-1].pid)
        cls.daemons.append(startDaemon(os.path.join(cfg.TEST_DATADIRS, str(NMC_NODE)), cfg.NAMECOIN_BINDIR, cfg.NAMECOIND))
        logging.info('Started %s %d', cfg.NAMECOIND, cls.daemons[-1].pid)

        for i in range(NUM_NODES):
            cls.daemons.append(startDaemon(os.path.join(cfg.TEST_DATADIRS, str(i)), cfg.PARTICL_BINDIR, cfg.PARTICLD))
            logging.info('Started %s %d', cfg.PARTICLD, cls.daemons[-1].pid)

        for i in range(NUM_NODES):
            rpc = make_part_cli_rpc_func(i)
            waitForRPC(rpc)
            if i == 0:
                rpc('extkeyimportmaster', ['abandon baby cabbage dad eager fabric gadget habit ice kangaroo lab absorb'])
            elif i == 1:
                rpc('extkeyimportmaster', ['pact mammal barrel matrix local final lecture chunk wasp survey bid various book strong spread fall ozone daring like topple door fatigue limb olympic', '', 'true'])
                rpc('getnewextaddress', ['lblExtTest'])
                rpc('rescanblockchain')
            else:
                rpc('extkeyimportmaster', [rpc('mnemonic', ['new'])['master']])

            basicswap_dir = os.path.join(os.path.join(cfg.TEST_DATADIRS, str(i)), 'basicswap')
            settings_path = os.path.join(basicswap_dir, cfg.CONFIG_FILENAME)
            with open(settings_path) as fs:
                settings = json.load(fs)
            fp = open(os.path.join(basicswap_dir, 'basicswap.log'), 'w')
            cls.swap_clients.append(BasicSwap(fp, basicswap_dir, settings, 'regtest', log_name='BasicSwap{}'.format(i)))
            cls.swap_clients[-1].setDaemonPID(Coins.BTC, cls.daemons[0].pid)
            cls.swap_clients[-1].setDaemonPID(Coins.NMC, cls.daemons[1].pid)
            cls.swap_clients[-1].setDaemonPID(Coins.PART, cls.daemons[2 + i].pid)
            cls.swap_clients[-1].start()

            t = HttpThread(cls.swap_clients[i].fp, TEST_HTTP_HOST, TEST_HTTP_PORT + i, False, cls.swap_clients[i])
            cls.http_threads.append(t)
            t.start()

        waitForRPC(nmcRpc)
        num_blocks = 500
        logging.info('Mining %d namecoin blocks', num_blocks)
        cls.nmc_addr = nmcRpc('getnewaddress mining_addr legacy')
        nmcRpc('generatetoaddress {} {}'.format(num_blocks, cls.nmc_addr))

        ro = nmcRpc('getblockchaininfo')
        try:
            assert(ro['bip9_softforks']['csv']['status'] == 'active')
        except Exception:
            logging.info('nmc: csv is not active')
        try:
            assert(ro['bip9_softforks']['segwit']['status'] == 'active')
        except Exception:
            logging.info('nmc: segwit is not active')

        waitForRPC(btcRpc)
        cls.btc_addr = btcRpc('getnewaddress mining_addr bech32')
        logging.info('Mining %d Bitcoin blocks to %s', num_blocks, cls.btc_addr)
        btcRpc('generatetoaddress {} {}'.format(num_blocks, cls.btc_addr))

        ro = btcRpc('getblockchaininfo')
        checkForks(ro)

        ro = nmcRpc('getwalletinfo')
        print('nmcRpc', ro)

        signal.signal(signal.SIGINT, signal_handler)
        cls.update_thread = threading.Thread(target=run_loop, args=(cls,))
        cls.update_thread.start()

        cls.coins_update_thread = threading.Thread(target=run_coins_loop, args=(cls,))
        cls.coins_update_thread.start()

        # Wait for height, or sequencelock is thrown off by genesis blocktime
        num_blocks = 3
        logging.info('Waiting for Particl chain height %d', num_blocks)
        for i in range(60):
            particl_blocks = cls.swap_clients[0].callrpc('getblockchaininfo')['blocks']
            print('particl_blocks', particl_blocks)
            if particl_blocks >= num_blocks:
                break
            delay_event.wait(1)
        assert(particl_blocks >= num_blocks)
Exemple #2
0
    def setUpClass(cls):
        super(Test, cls).setUpClass()

        eckey = ECKey()
        eckey.generate()
        cls.network_key = toWIF(PREFIX_SECRET_KEY_REGTEST, eckey.get_bytes())
        cls.network_pubkey = eckey.get_pubkey().get_bytes().hex()

        if os.path.isdir(cfg.TEST_DATADIRS):
            logging.info('Removing ' + cfg.TEST_DATADIRS)
            shutil.rmtree(cfg.TEST_DATADIRS)

        for i in range(NUM_NODES):
            data_dir = prepareDir(cfg.TEST_DATADIRS, i, cls.network_key, cls.network_pubkey)
            callrpc_cli(cfg.PARTICL_BINDIR, data_dir, 'regtest', '-wallet=wallet.dat create', 'particl-wallet')  # Necessary for 0.21

        prepareOtherDir(cfg.TEST_DATADIRS, LTC_NODE)
        data_dir = prepareOtherDir(cfg.TEST_DATADIRS, BTC_NODE, 'bitcoin.conf')
        callrpc_cli(cfg.BITCOIN_BINDIR, data_dir, 'regtest', '-wallet=wallet.dat create', 'bitcoin-wallet')  # Necessary for 0.21

        cls.daemons = []
        cls.swap_clients = []
        cls.http_threads = []

        cls.daemons.append(startDaemon(os.path.join(cfg.TEST_DATADIRS, str(BTC_NODE)), cfg.BITCOIN_BINDIR, cfg.BITCOIND))
        logging.info('Started %s %d', cfg.BITCOIND, cls.daemons[-1].pid)
        cls.daemons.append(startDaemon(os.path.join(cfg.TEST_DATADIRS, str(LTC_NODE)), cfg.LITECOIN_BINDIR, cfg.LITECOIND))
        logging.info('Started %s %d', cfg.LITECOIND, cls.daemons[-1].pid)

        for i in range(NUM_NODES):
            cls.daemons.append(startDaemon(os.path.join(cfg.TEST_DATADIRS, str(i)), cfg.PARTICL_BINDIR, cfg.PARTICLD))
            logging.info('Started %s %d', cfg.PARTICLD, cls.daemons[-1].pid)

        for i in range(NUM_NODES):
            # Load mnemonics after all nodes have started to avoid staking getting stuck in TryToSync
            rpc = make_part_cli_rpc_func(i)
            waitForRPC(rpc)
            if i == 0:
                rpc('extkeyimportmaster', ['abandon baby cabbage dad eager fabric gadget habit ice kangaroo lab absorb'])
            elif i == 1:
                rpc('extkeyimportmaster', ['pact mammal barrel matrix local final lecture chunk wasp survey bid various book strong spread fall ozone daring like topple door fatigue limb olympic', '', 'true'])
                rpc('getnewextaddress', ['lblExtTest'])
                rpc('rescanblockchain')
            else:
                rpc('extkeyimportmaster', [rpc('mnemonic', ['new'])['master']])
            # Lower output split threshold for more stakeable outputs
            rpc('walletsettings', ['stakingoptions', {'stakecombinethreshold': 100, 'stakesplitthreshold': 200}])

            basicswap_dir = os.path.join(os.path.join(cfg.TEST_DATADIRS, str(i)), 'basicswap')
            settings_path = os.path.join(basicswap_dir, cfg.CONFIG_FILENAME)
            with open(settings_path) as fs:
                settings = json.load(fs)
            fp = open(os.path.join(basicswap_dir, 'basicswap.log'), 'w')
            sc = BasicSwap(fp, basicswap_dir, settings, 'regtest', log_name='BasicSwap{}'.format(i))
            sc.setDaemonPID(Coins.BTC, cls.daemons[0].pid)
            sc.setDaemonPID(Coins.LTC, cls.daemons[1].pid)
            sc.setDaemonPID(Coins.PART, cls.daemons[2 + i].pid)
            sc.start()
            cls.swap_clients.append(sc)

            t = HttpThread(cls.swap_clients[i].fp, TEST_HTTP_HOST, TEST_HTTP_PORT + i, False, cls.swap_clients[i])
            cls.http_threads.append(t)
            t.start()

        waitForRPC(ltcRpc)
        num_blocks = 500
        logging.info('Mining %d litecoin blocks', num_blocks)
        cls.ltc_addr = ltcRpc('getnewaddress mining_addr legacy')
        ltcRpc('generatetoaddress {} {}'.format(num_blocks, cls.ltc_addr))

        ro = ltcRpc('getblockchaininfo')
        checkForks(ro)

        waitForRPC(btcRpc)
        cls.btc_addr = btcRpc('getnewaddress mining_addr bech32')
        logging.info('Mining %d Bitcoin blocks to %s', num_blocks, cls.btc_addr)
        btcRpc('generatetoaddress {} {}'.format(num_blocks, cls.btc_addr))

        ro = btcRpc('getblockchaininfo')
        checkForks(ro)

        ro = ltcRpc('getwalletinfo')
        print('ltcRpc', ro)

        signal.signal(signal.SIGINT, signal_handler)
        cls.update_thread = threading.Thread(target=run_loop, args=(cls,))
        cls.update_thread.start()

        cls.coins_update_thread = threading.Thread(target=run_coins_loop, args=(cls,))
        cls.coins_update_thread.start()

        # Wait for height, or sequencelock is thrown off by genesis blocktime
        num_blocks = 3
        logging.info('Waiting for Particl chain height %d', num_blocks)
        for i in range(60):
            particl_blocks = cls.swap_clients[0].callrpc('getblockchaininfo')['blocks']
            print('particl_blocks', particl_blocks)
            if particl_blocks >= num_blocks:
                break
            test_delay_event.wait(1)
        assert(particl_blocks >= num_blocks)
    def setUpClass(cls):
        super(Test, cls).setUpClass()

        cls.update_thread = None
        cls.coins_update_thread = None
        cls.http_threads = []
        cls.swap_clients = []
        cls.part_daemons = []
        cls.btc_daemons = []

        cls.part_stakelimit = 0
        cls.btc_addr = None

        logger.propagate = False
        logger.handlers = []
        logger.setLevel(
            logging.INFO)  # DEBUG shows many messages from requests.post
        formatter = logging.Formatter(
            '%(asctime)s %(levelname)s : %(message)s')
        stream_stdout = logging.StreamHandler()
        stream_stdout.setFormatter(formatter)
        logger.addHandler(stream_stdout)

        if os.path.isdir(TEST_DIR):
            logging.info('Removing ' + TEST_DIR)
            shutil.rmtree(TEST_DIR)
        if not os.path.exists(TEST_DIR):
            os.makedirs(TEST_DIR)

        cls.stream_fp = logging.FileHandler(os.path.join(TEST_DIR, 'test.log'))
        cls.stream_fp.setFormatter(formatter)
        logger.addHandler(cls.stream_fp)

        try:
            logging.info('Preparing coin nodes.')
            for i in range(NUM_NODES):
                prepareDataDir(TEST_DIR, i, 'particl.conf', 'part_')

                cls.part_daemons.append(
                    startDaemon(os.path.join(TEST_DIR, 'part_' + str(i)),
                                cfg.PARTICL_BINDIR, cfg.PARTICLD))
                logging.info('Started %s %d', cfg.PARTICLD,
                             cls.part_daemons[-1].pid)

            for i in range(NUM_NODES):
                # Load mnemonics after all nodes have started to avoid staking getting stuck in TryToSync
                rpc = make_rpc_func(i)
                waitForRPC(rpc)
                if i == 0:
                    rpc('extkeyimportmaster', [
                        'abandon baby cabbage dad eager fabric gadget habit ice kangaroo lab absorb'
                    ])
                elif i == 1:
                    rpc('extkeyimportmaster', [
                        'pact mammal barrel matrix local final lecture chunk wasp survey bid various book strong spread fall ozone daring like topple door fatigue limb olympic',
                        '', 'true'
                    ])
                    rpc('getnewextaddress', ['lblExtTest'])
                    rpc('rescanblockchain')
                else:
                    rpc('extkeyimportmaster',
                        [rpc('mnemonic', ['new'])['master']])
                # Lower output split threshold for more stakeable outputs
                rpc('walletsettings', [
                    'stakingoptions', {
                        'stakecombinethreshold': 100,
                        'stakesplitthreshold': 200
                    }
                ])

            for i in range(NUM_BTC_NODES):
                prepareDataDir(TEST_DIR,
                               i,
                               'bitcoin.conf',
                               'btc_',
                               base_p2p_port=BTC_BASE_PORT,
                               base_rpc_port=BTC_BASE_RPC_PORT)

                cls.btc_daemons.append(
                    startDaemon(os.path.join(TEST_DIR, 'btc_' + str(i)),
                                cfg.BITCOIN_BINDIR, cfg.BITCOIND))
                logging.info('Started %s %d', cfg.BITCOIND,
                             cls.part_daemons[-1].pid)

                waitForRPC(make_rpc_func(i, base_rpc_port=BTC_BASE_RPC_PORT))

            logging.info('Preparing swap clients.')
            eckey = ECKey()
            eckey.generate()
            cls.network_key = toWIF(PREFIX_SECRET_KEY_REGTEST,
                                    eckey.get_bytes())
            cls.network_pubkey = eckey.get_pubkey().get_bytes().hex()

            for i in range(NUM_NODES):
                prepare_swapclient_dir(TEST_DIR, i, cls.network_key,
                                       cls.network_pubkey)
                basicswap_dir = os.path.join(
                    os.path.join(TEST_DIR, 'basicswap_' + str(i)))
                settings_path = os.path.join(basicswap_dir,
                                             cfg.CONFIG_FILENAME)
                with open(settings_path) as fs:
                    settings = json.load(fs)
                fp = open(os.path.join(basicswap_dir, 'basicswap.log'), 'w')
                sc = BasicSwap(fp,
                               basicswap_dir,
                               settings,
                               'regtest',
                               log_name='BasicSwap{}'.format(i))
                sc.setDaemonPID(Coins.BTC, cls.btc_daemons[i].pid)
                sc.setDaemonPID(Coins.PART, cls.part_daemons[i].pid)
                sc.start()
                cls.swap_clients.append(sc)

                t = HttpThread(cls.swap_clients[i].fp, TEST_HTTP_HOST,
                               TEST_HTTP_PORT + i, False, cls.swap_clients[i])
                cls.http_threads.append(t)
                t.start()

            cls.btc_addr = callnoderpc(0,
                                       'getnewaddress',
                                       ['mining_addr', 'bech32'],
                                       base_rpc_port=BTC_BASE_RPC_PORT)

            num_blocks = 500
            logging.info('Mining %d Bitcoin blocks to %s', num_blocks,
                         cls.btc_addr)
            callnoderpc(0,
                        'generatetoaddress', [num_blocks, cls.btc_addr],
                        base_rpc_port=BTC_BASE_RPC_PORT)

            checkForks(
                callnoderpc(0,
                            'getblockchaininfo',
                            base_rpc_port=BTC_BASE_RPC_PORT))

            logging.info('Starting update thread.')
            signal.signal(signal.SIGINT, signal_handler)
            cls.update_thread = threading.Thread(target=run_loop, args=(cls, ))
            cls.update_thread.start()

            cls.coins_update_thread = threading.Thread(target=run_coins_loop,
                                                       args=(cls, ))
            cls.coins_update_thread.start()
        except Exception:
            traceback.print_exc()
            Test.tearDownClass()
            raise ValueError('setUpClass() failed.')
Exemple #4
0
def main():
    global extract_core_overwrite
    data_dir = None
    bin_dir = None
    port_offset = None
    chain = 'mainnet'
    particl_wallet_mnemonic = None
    prepare_bin_only = False
    no_cores = False
    with_coins = {'particl'}
    add_coin = ''
    disable_coin = ''
    htmlhost = '127.0.0.1'
    xmr_restore_height = DEFAULT_XMR_RESTORE_HEIGHT

    for v in sys.argv[1:]:
        if len(v) < 2 or v[0] != '-':
            exitWithError('Unknown argument {}'.format(v))

        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
        if name == 'nocores':
            no_cores = True
            continue
        if name == 'noextractover':
            extract_core_overwrite = False
            continue
        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])
                continue
            if name == 'particl_mnemonic':
                particl_wallet_mnemonic = s[1].strip('"')
                continue
            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
            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
            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

        exitWithError('Unknown argument {}'.format(v))

    if data_dir is None:
        data_dir = os.path.join(os.path.expanduser(cfg.DEFAULT_DATADIR))
    if bin_dir is None:
        bin_dir = os.path.join(data_dir, 'bin')

    logger.info('datadir: %s', data_dir)
    logger.info('bindir:  %s', bin_dir)
    logger.info('Chain: %s', chain)

    if port_offset is None:
        port_offset = 300 if chain == 'testnet' else 0

    if not os.path.exists(data_dir):
        os.makedirs(data_dir)
    config_path = os.path.join(data_dir, cfg.CONFIG_FILENAME)

    withchainclients = {}
    chainclients = {
        'particl': {
            'connection_type':
            'rpc',
            'manage_daemon':
            True if ('particl' in with_coins and PART_RPC_HOST == '127.0.0.1')
            else False,
            'rpchost':
            PART_RPC_HOST,
            'rpcport':
            PART_RPC_PORT + port_offset,
            '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,
            'conf_target':
            2,
            'core_version_group':
            18,
            '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,
            'rpchost':
            LTC_RPC_HOST,
            'rpcport':
            LTC_RPC_PORT + port_offset,
            'datadir':
            os.getenv('LTC_DATA_DIR', os.path.join(data_dir, 'litecoin')),
            'bindir':
            os.path.join(bin_dir, 'litecoin'),
            'use_segwit':
            True,
            'blocks_confirmed':
            2,
            'conf_target':
            2,
            'core_version_group':
            18,
            '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,
            'rpchost':
            BTC_RPC_HOST,
            'rpcport':
            BTC_RPC_PORT + port_offset,
            'datadir':
            os.getenv('BTC_DATA_DIR', os.path.join(data_dir, 'bitcoin')),
            'bindir':
            os.path.join(bin_dir, 'bitcoin'),
            'use_segwit':
            True,
            'blocks_confirmed':
            1,
            'conf_target':
            2,
            'core_version_group':
            18,
            '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,
            'rpchost':
            NMC_RPC_HOST,
            'rpcport':
            NMC_RPC_PORT + port_offset,
            '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,
            'blocks_confirmed':
            1,
            'conf_target':
            2,
            'core_version_group':
            18,
            '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,
            'walletrpchost':
            XMR_WALLET_RPC_HOST,
            'walletrpcuser':
            XMR_WALLET_RPC_USER,
            'walletrpcpassword':
            XMR_WALLET_RPC_PWD,
            'walletfile':
            'swap_wallet',
            '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 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']:
            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]

        if not no_cores:
            prepareCore(add_coin, known_coins[add_coin], settings, data_dir)

        if not prepare_bin_only:
            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',
            'zmqport': PART_ZMQ_PORT + port_offset,
            'htmlhost': htmlhost,
            'htmlport': UI_HTML_PORT + port_offset,
            'network_key':
            '7sW2UEcHXvuqEjkpE5mD584zRaQYs6WXYohue4jLFZPTvMSxwvgs',
            'network_pubkey':
            '035758c4a22d7dd59165db02a56156e790224361eb3191f02197addcb3bde903d2',
            'chainclients': withchainclients,
            '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
        }

    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:
        prepareDataDir(c, settings, chain, particl_wallet_mnemonic)

    with open(config_path, 'w') as fp:
        json.dump(settings, fp, indent=4)

    if particl_wallet_mnemonic == 'none':
        logger.info('Done.')
        return 0

    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'
                    ]))
    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
    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()

    logger.info(
        'IMPORTANT - Save your particl wallet recovery phrase:\n{}\n'.format(
            particl_wallet_mnemonic))
    logger.info('Done.')