Add Decred rpc

This commit is contained in:
tecnovert 2024-04-18 22:15:35 +02:00
parent 6afbd55aec
commit 89ca350ff2
14 changed files with 141 additions and 72 deletions

View file

@ -654,6 +654,9 @@ class BasicSwap(BaseApp):
interface = LTCInterface(self.coin_clients[coin], self.chain, self)
self.coin_clients[coin]['interface_mweb'] = LTCInterfaceMWEB(self.coin_clients[coin], self.chain, self)
return interface
elif coin == Coins.DCR:
from .interface.dcr import DCRInterface
return DCRInterface(self.coin_clients[coin], self.chain, self)
elif coin == Coins.NMC:
from .interface.nmc import NMCInterface
return NMCInterface(self.coin_clients[coin], self.chain, self)

View file

@ -0,0 +1,4 @@
from .dcr import DCRInterface
__all__ = ['DCRInterface',]

View file

@ -15,6 +15,7 @@ from basicswap.util.crypto import (
blake256,
ripemd160,
)
from basicswap.interface.dcr.rpc import make_rpc_func
class DCRInterface(Secp256k1Interface):
@ -41,6 +42,10 @@ class DCRInterface(Secp256k1Interface):
def __init__(self, coin_settings, network, swap_client=None):
super().__init__(network)
self._rpc_host = coin_settings.get('rpchost', '127.0.0.1')
self._rpcport = coin_settings['rpcport']
self._rpcauth = coin_settings['rpcauth']
self.rpc = make_rpc_func(self._rpcport, self._rpcauth, host=self._rpc_host)
def pkh(self, pubkey: bytes) -> bytes:
return ripemd160(blake256(pubkey))
@ -61,3 +66,9 @@ class DCRInterface(Secp256k1Interface):
if blake256(blake256(prefixed_data))[:4] != checksum:
raise ValueError('Checksum mismatch')
return prefixed_data
def testDaemonRPC(self, with_wallet=True) -> None:
if with_wallet:
self.rpc_wallet('getwalletinfo')
else:
self.rpc('getblockchaininfo')

View file

@ -0,0 +1,39 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2024 tecnovert
# Distributed under the MIT software license, see the accompanying
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
import json
import traceback
from basicswap.rpc import Jsonrpc
def callrpc(rpc_port, auth, method, params=[], host='127.0.0.1'):
try:
url = 'http://{}@{}:{}/'.format(auth, host, rpc_port)
x = Jsonrpc(url)
x.__handler = None
v = x.json_request(method, params)
x.close()
print('[rm] v', v)
r = json.loads(v.decode('utf-8'))
except Exception as ex:
traceback.print_exc()
raise ValueError('RPC server error ' + str(ex) + ', method: ' + method)
if 'error' in r and r['error'] is not None:
raise ValueError('RPC error ' + str(r['error']))
return r['result']
def make_rpc_func(port, auth, host='127.0.0.1'):
port = port
auth = auth
host = host
def rpc_func(method, params=None):
nonlocal port, auth, host
return callrpc(port, auth, method, params, host)
return rpc_func

View file

@ -1,15 +1,13 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2020-2023 tecnovert
# Copyright (c) 2020-2024 tecnovert
# Distributed under the MIT software license, see the accompanying
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
import os
import time
import json
import shlex
import urllib
import logging
import traceback
import subprocess
from xmlrpc.client import (
@ -20,18 +18,6 @@ from xmlrpc.client import (
from .util import jsonDecimal
def waitForRPC(rpc_func, expect_wallet=True, max_tries=7):
for i in range(max_tries + 1):
try:
rpc_func('getwalletinfo' if expect_wallet else 'getblockchaininfo')
return
except Exception as ex:
if i < max_tries:
logging.warning('Can\'t connect to RPC: %s. Retrying in %d second/s.', str(ex), (i + 1))
time.sleep(i + 1)
raise ValueError('waitForRPC failed')
class Jsonrpc():
# __getattr__ complicates extending ServerProxy
def __init__(self, uri, transport=None, encoding=None, verbose=False,

View file

@ -312,6 +312,20 @@ def make_rpc_func(node_id, base_rpc_port=BASE_RPC_PORT):
return rpc_func
def waitForRPC(rpc_func, delay_event, rpc_command='getwalletinfo', max_tries=7):
for i in range(max_tries + 1):
if delay_event.is_set():
raise ValueError('Test stopped.')
try:
rpc_func(rpc_command)
return
except Exception as ex:
if i < max_tries:
logging.warning('Can\'t connect to RPC: %s. Retrying in %d second/s.', str(ex), (i + 1))
delay_event.wait(i + 1)
raise ValueError('waitForRPC failed')
def extract_states_from_xu_file(file_path, prefix):
states = {}

View file

@ -41,7 +41,6 @@ from basicswap.util.address import (
)
from basicswap.rpc import (
callrpc_cli,
waitForRPC,
)
from basicswap.contrib.key import (
ECKey,
@ -67,6 +66,7 @@ from tests.basicswap.common import (
BASE_RPC_PORT,
BASE_ZMQ_PORT,
PREFIX_SECRET_KEY_REGTEST,
waitForRPC,
)
from bin.basicswap_run import startDaemon
@ -323,7 +323,7 @@ class Test(unittest.TestCase):
for i in range(NUM_NODES):
rpc = make_part_cli_rpc_func(i)
waitForRPC(rpc)
waitForRPC(rpc, delay_event)
if i == 0:
rpc('extkeyimportmaster', ['abandon baby cabbage dad eager fabric gadget habit ice kangaroo lab absorb'])
elif i == 1:
@ -340,23 +340,23 @@ class Test(unittest.TestCase):
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)))
swap_client = cls.swap_clients[-1]
swap_client.setDaemonPID(Coins.BTC, cls.daemons[0].handle.pid)
swap_client.setDaemonPID(Coins.DASH, cls.daemons[1].handle.pid)
swap_client.setDaemonPID(Coins.PART, cls.daemons[2 + i].handle.pid)
sc = BasicSwap(fp, basicswap_dir, settings, 'regtest', log_name='BasicSwap{}'.format(i))
cls.swap_clients.append(sc)
sc.setDaemonPID(Coins.BTC, cls.daemons[0].handle.pid)
sc.setDaemonPID(Coins.DASH, cls.daemons[1].handle.pid)
sc.setDaemonPID(Coins.PART, cls.daemons[2 + i].handle.pid)
waitForRPC(dashRpc, expect_wallet=False)
waitForRPC(dashRpc, delay_event, rpc_command='getblockchaininfo')
if len(dashRpc('listwallets')) < 1:
dashRpc('createwallet wallet.dat')
swap_client.start()
sc.start()
t = HttpThread(cls.swap_clients[i].fp, TEST_HTTP_HOST, TEST_HTTP_PORT + i, False, swap_client)
t = HttpThread(sc.fp, TEST_HTTP_HOST, TEST_HTTP_PORT + i, False, sc)
cls.http_threads.append(t)
t.start()
waitForRPC(dashRpc)
waitForRPC(dashRpc, delay_event)
num_blocks = 500
logging.info('Mining %d dash blocks', num_blocks)
cls.dash_addr = dashRpc('getnewaddress mining_addr')
@ -372,7 +372,7 @@ class Test(unittest.TestCase):
except Exception:
logging.info('dash: segwit is not active')
waitForRPC(btcRpc)
waitForRPC(btcRpc, delay_event)
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))

View file

@ -14,18 +14,18 @@ import basicswap.config as cfg
from basicswap.basicswap import (
Coins,
)
from basicswap.rpc import (
waitForRPC,
from basicswap.interface.dcr.rpc import (
callrpc,
)
from tests.basicswap.common import (
stopDaemons,
make_rpc_func,
waitForRPC,
)
from tests.basicswap.util import (
REQUIRED_SETTINGS,
)
from tests.basicswap.test_xmr import BaseTest
from tests.basicswap.test_xmr import BaseTest, test_delay_event
from basicswap.interface.dcr import DCRInterface
from bin.basicswap_run import startDaemon
@ -40,6 +40,16 @@ DCR_BASE_PORT = 44932
DCR_BASE_RPC_PORT = 45932
def make_rpc_func(node_id, base_rpc_port):
node_id = node_id
auth = 'test{0}:test_pass{0}'.format(node_id)
def rpc_func(method, params=None):
nonlocal node_id, auth
return callrpc(base_rpc_port + node_id, auth, method, params)
return rpc_func
def prepareDCDDataDir(datadir, node_id, conf_file, dir_prefix, base_p2p_port, base_rpc_port, num_nodes=3):
node_dir = os.path.join(datadir, dir_prefix + str(node_id))
if not os.path.exists(node_dir):
@ -51,10 +61,11 @@ def prepareDCDDataDir(datadir, node_id, conf_file, dir_prefix, base_p2p_port, ba
config = [
'regnet=1\n', # or simnet?
'debuglevel=debug\n',
f'listen=127.0.0.1:{base_p2p_port}\n',
f'rpclisten=127.0.0.1:{base_rpc_port}\n',
f'listen=127.0.0.1:{base_p2p_port + node_id}\n',
f'rpclisten=127.0.0.1:{base_rpc_port + node_id}\n',
f'rpcuser=test{node_id}\n',
f'rpcpass=test_pass{node_id}\n',]
f'rpcpass=test_pass{node_id}\n',
'notls=1\n',]
for i in range(0, num_nodes):
if node_id == i:
@ -100,7 +111,7 @@ class Test(BaseTest):
cls.dcr_daemons.append(startDaemon(appdata, DCR_BINDIR, DCRD, opts=extra_opts, extra_config={'add_datadir': False, 'stdout_to_file': True, 'stdout_filename': 'dcrd_stdout.log'}))
logging.info('Started %s %d', DCRD, cls.dcr_daemons[-1].handle.pid)
waitForRPC(make_rpc_func(i, base_rpc_port=DCR_BASE_RPC_PORT), max_tries=12)
waitForRPC(make_rpc_func(i, base_rpc_port=DCR_BASE_RPC_PORT), test_delay_event, rpc_command='getnetworkinfo', max_tries=12)
@classmethod
def addCoinSettings(cls, settings, datadir, node_id):

View file

@ -26,7 +26,6 @@ from basicswap.util import (
)
from basicswap.rpc import (
callrpc_cli,
waitForRPC,
)
from tests.basicswap.util import (
read_json_api,
@ -37,6 +36,7 @@ from tests.basicswap.common import (
make_rpc_func,
TEST_HTTP_PORT,
wait_for_offer,
waitForRPC,
)
from basicswap.interface.contrib.firo_test_framework.mininode import (
FromHex,
@ -139,7 +139,7 @@ class Test(BaseTest):
cls.firo_daemons.append(startDaemon(os.path.join(cfg.TEST_DATADIRS, 'firo_' + str(i)), FIRO_BINDIR, FIROD, opts=extra_opts))
logging.info('Started %s %d', FIROD, cls.firo_daemons[-1].handle.pid)
waitForRPC(make_rpc_func(i, base_rpc_port=FIRO_BASE_RPC_PORT))
waitForRPC(make_rpc_func(i, base_rpc_port=FIRO_BASE_RPC_PORT), test_delay_event)
@classmethod
def addPIDInfo(cls, sc, i):

View file

@ -29,9 +29,6 @@ from basicswap.util import (
from basicswap.util.address import (
decodeWif,
)
from basicswap.rpc import (
waitForRPC,
)
from tests.basicswap.util import (
read_json_api,
)
@ -45,6 +42,7 @@ from tests.basicswap.common import (
wait_for_unspent,
wait_for_in_progress,
wait_for_bid_tx_state,
waitForRPC,
)
from basicswap.interface.contrib.nav_test_framework.mininode import (
ToHex,
@ -161,7 +159,7 @@ class Test(TestFunctions):
cls.nav_daemons.append(startDaemon(os.path.join(cfg.TEST_DATADIRS, 'nav_' + str(i)), NAV_BINDIR, NAVD, opts=extra_opts))
logging.info('Started %s %d', NAVD, cls.nav_daemons[-1].handle.pid)
waitForRPC(make_rpc_func(i, base_rpc_port=NAV_BASE_RPC_PORT), max_tries=12)
waitForRPC(make_rpc_func(i, base_rpc_port=NAV_BASE_RPC_PORT), test_delay_event, max_tries=12)
@classmethod
def addPIDInfo(cls, sc, i):

View file

@ -31,7 +31,6 @@ from basicswap.util.address import (
from basicswap.rpc import (
callrpc,
callrpc_cli,
waitForRPC,
)
from basicswap.contrib.key import (
ECKey,
@ -56,6 +55,7 @@ from tests.basicswap.common import (
BTC_BASE_PORT,
BTC_BASE_RPC_PORT,
PREFIX_SECRET_KEY_REGTEST,
waitForRPC,
)
from bin.basicswap_run import startDaemon
@ -211,7 +211,7 @@ class Test(unittest.TestCase):
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)
waitForRPC(rpc, delay_event)
if i == 0:
rpc('extkeyimportmaster', ['abandon baby cabbage dad eager fabric gadget habit ice kangaroo lab absorb'])
elif i == 1:
@ -232,7 +232,7 @@ class Test(unittest.TestCase):
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.handle.part_daemons[-1].handle.pid)
waitForRPC(make_rpc_func(i, base_rpc_port=BTC_BASE_RPC_PORT))
waitForRPC(make_rpc_func(i, base_rpc_port=BTC_BASE_RPC_PORT), delay_event)
logging.info('Preparing swap clients.')
eckey = ECKey()
@ -248,12 +248,12 @@ class Test(unittest.TestCase):
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))
cls.swap_clients.append(sc)
sc.setDaemonPID(Coins.BTC, cls.btc_daemons[i].handle.pid)
sc.setDaemonPID(Coins.PART, cls.part_daemons[i].handle.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])
t = HttpThread(sc.fp, TEST_HTTP_HOST, TEST_HTTP_PORT + i, False, sc)
cls.http_threads.append(t)
t.start()

View file

@ -39,7 +39,6 @@ from basicswap.util.address import (
)
from basicswap.rpc import (
callrpc_cli,
waitForRPC,
)
from basicswap.contrib.key import (
ECKey,
@ -63,6 +62,7 @@ from tests.basicswap.common import (
BASE_RPC_PORT,
BASE_ZMQ_PORT,
PREFIX_SECRET_KEY_REGTEST,
waitForRPC,
)
from bin.basicswap_run import startDaemon
@ -289,7 +289,7 @@ class Test(unittest.TestCase):
for i in range(NUM_NODES):
rpc = make_part_cli_rpc_func(i)
waitForRPC(rpc)
waitForRPC(rpc, delay_event)
if i == 0:
rpc('extkeyimportmaster', ['abandon baby cabbage dad eager fabric gadget habit ice kangaroo lab absorb'])
elif i == 1:
@ -306,17 +306,19 @@ class Test(unittest.TestCase):
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].handle.pid)
cls.swap_clients[-1].setDaemonPID(Coins.NMC, cls.daemons[1].handle.pid)
cls.swap_clients[-1].setDaemonPID(Coins.PART, cls.daemons[2 + i].handle.pid)
cls.swap_clients[-1].start()
sc = BasicSwap(fp, basicswap_dir, settings, 'regtest', log_name='BasicSwap{}'.format(i))
cls.swap_clients.append(sc)
t = HttpThread(cls.swap_clients[i].fp, TEST_HTTP_HOST, TEST_HTTP_PORT + i, False, cls.swap_clients[i])
sc.setDaemonPID(Coins.BTC, cls.daemons[0].handle.pid)
sc.setDaemonPID(Coins.NMC, cls.daemons[1].handle.pid)
sc.setDaemonPID(Coins.PART, cls.daemons[2 + i].handle.pid)
sc.start()
t = HttpThread(sc.fp, TEST_HTTP_HOST, TEST_HTTP_PORT + i, False, sc)
cls.http_threads.append(t)
t.start()
waitForRPC(nmcRpc)
waitForRPC(nmcRpc, delay_event)
num_blocks = 500
logging.info('Mining %d namecoin blocks', num_blocks)
cls.nmc_addr = nmcRpc('getnewaddress mining_addr legacy')
@ -332,7 +334,7 @@ class Test(unittest.TestCase):
except Exception:
logging.info('nmc: segwit is not active')
waitForRPC(btcRpc)
waitForRPC(btcRpc, delay_event)
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))

View file

@ -41,7 +41,6 @@ from basicswap.util.address import (
)
from basicswap.rpc import (
callrpc_cli,
waitForRPC,
)
from basicswap.contrib.key import (
ECKey,
@ -67,6 +66,7 @@ from tests.basicswap.common import (
BASE_RPC_PORT,
BASE_ZMQ_PORT,
PREFIX_SECRET_KEY_REGTEST,
waitForRPC,
)
from bin.basicswap_run import startDaemon
from bin.basicswap_prepare import downloadPIVXParams
@ -326,7 +326,7 @@ class Test(unittest.TestCase):
for i in range(NUM_NODES):
rpc = make_part_cli_rpc_func(i)
waitForRPC(rpc)
waitForRPC(rpc, delay_event)
if i == 0:
rpc('extkeyimportmaster', ['abandon baby cabbage dad eager fabric gadget habit ice kangaroo lab absorb'])
elif i == 1:
@ -343,17 +343,18 @@ class Test(unittest.TestCase):
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].handle.pid)
cls.swap_clients[-1].setDaemonPID(Coins.PIVX, cls.daemons[1].handle.pid)
cls.swap_clients[-1].setDaemonPID(Coins.PART, cls.daemons[2 + i].handle.pid)
cls.swap_clients[-1].start()
sc = BasicSwap(fp, basicswap_dir, settings, 'regtest', log_name='BasicSwap{}'.format(i))
cls.swap_clients.append(sc)
sc.setDaemonPID(Coins.BTC, cls.daemons[0].handle.pid)
sc.setDaemonPID(Coins.PIVX, cls.daemons[1].handle.pid)
sc.setDaemonPID(Coins.PART, cls.daemons[2 + i].handle.pid)
sc.start()
t = HttpThread(cls.swap_clients[i].fp, TEST_HTTP_HOST, TEST_HTTP_PORT + i, False, cls.swap_clients[i])
t = HttpThread(sc.fp, TEST_HTTP_HOST, TEST_HTTP_PORT + i, False, sc)
cls.http_threads.append(t)
t.start()
waitForRPC(pivxRpc)
waitForRPC(pivxRpc, delay_event)
num_blocks = 1352 # CHECKLOCKTIMEVERIFY soft-fork activates at (regtest) block height 1351.
logging.info('Mining %d pivx blocks', num_blocks)
cls.pivx_addr = pivxRpc('getnewaddress mining_addr')
@ -369,7 +370,7 @@ class Test(unittest.TestCase):
except Exception:
logging.info('pivx: segwit is not active')
waitForRPC(btcRpc)
waitForRPC(btcRpc, delay_event)
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))

View file

@ -43,7 +43,6 @@ from basicswap.util.address import (
from basicswap.rpc import (
callrpc,
callrpc_cli,
waitForRPC,
)
from basicswap.rpc_xmr import (
callrpc_xmr,
@ -76,6 +75,7 @@ from tests.basicswap.common import (
wait_for_none_active,
wait_for_balance,
wait_for_unspent,
waitForRPC,
compare_bid_states,
extract_states_from_xu_file,
TEST_HTTP_HOST,
@ -374,7 +374,7 @@ class BaseTest(unittest.TestCase):
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)
waitForRPC(rpc, test_delay_event)
if i == 0:
rpc('extkeyimportmaster', ['abandon baby cabbage dad eager fabric gadget habit ice kangaroo lab absorb'])
elif i == 1:
@ -400,7 +400,7 @@ class BaseTest(unittest.TestCase):
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].handle.pid)
waitForRPC(make_rpc_func(i, base_rpc_port=BTC_BASE_RPC_PORT))
waitForRPC(make_rpc_func(i, base_rpc_port=BTC_BASE_RPC_PORT), test_delay_event)
if cls.start_ltc_nodes:
for i in range(NUM_LTC_NODES):
@ -412,7 +412,7 @@ class BaseTest(unittest.TestCase):
cls.ltc_daemons.append(startDaemon(os.path.join(TEST_DIR, 'ltc_' + str(i)), cfg.LITECOIN_BINDIR, cfg.LITECOIND))
logging.info('Started %s %d', cfg.LITECOIND, cls.part_daemons[-1].handle.pid)
waitForRPC(make_rpc_func(i, base_rpc_port=LTC_BASE_RPC_PORT))
waitForRPC(make_rpc_func(i, base_rpc_port=LTC_BASE_RPC_PORT), test_delay_event)
if cls.start_xmr_nodes:
for i in range(NUM_XMR_NODES):
@ -474,6 +474,7 @@ class BaseTest(unittest.TestCase):
cls.network_pubkey = settings['network_pubkey']
fp = open(os.path.join(basicswap_dir, 'basicswap.log'), 'w')
sc = BasicSwap(fp, basicswap_dir, settings, 'regtest', log_name='BasicSwap{}'.format(i))
cls.swap_clients.append(sc)
sc.setDaemonPID(Coins.BTC, cls.btc_daemons[i].handle.pid)
sc.setDaemonPID(Coins.PART, cls.part_daemons[i].handle.pid)
@ -486,9 +487,8 @@ class BaseTest(unittest.TestCase):
# Set XMR main wallet address
xmr_ci = sc.ci(Coins.XMR)
sc.setStringKV('main_wallet_addr_' + xmr_ci.coin_name().lower(), xmr_ci.getMainWalletAddress())
cls.swap_clients.append(sc)
t = HttpThread(cls.swap_clients[i].fp, TEST_HTTP_HOST, TEST_HTTP_PORT + i, False, cls.swap_clients[i])
t = HttpThread(sc.fp, TEST_HTTP_HOST, TEST_HTTP_PORT + i, False, sc)
cls.http_threads.append(t)
t.start()
# Set future block rewards to nowhere (a random address), so wallet amounts stay constant