test.py: new xmrwallet test

Testing:

    $ test/test.py --coin=xmr -e xmrwallet
This commit is contained in:
The MMGen Project 2021-05-14 11:26:33 +00:00
commit d6def8f819
Signed by: mmgen
GPG key ID: 3F8B1861E32B7DA2
4 changed files with 543 additions and 46 deletions

View file

@ -241,6 +241,7 @@ class MoneroWalletOps:
wallet_dir = opt.outdir or '.',
test_suite = g.test_suite,
daemon_addr = uarg.daemon or None,
testnet = g.testnet,
)
if uarg.start_wallet_daemon:
@ -275,10 +276,11 @@ class MoneroWalletOps:
def get_wallet_fn(self,d):
return os.path.join(
opt.outdir or '.','{}-{}-MoneroWallet{}'.format(
opt.outdir or '.','{}-{}-MoneroWallet{}{}'.format(
self.kal.al_id.sid,
d.idx,
'' if g.debug_utf8 else ''))
'.testnet' if g.testnet else '',
'' if g.debug_utf8 else '' ))
async def process_wallets(self):
gmsg('\n{}ing {} wallet{}'.format(self.desc,len(self.addr_data),suf(self.addr_data)))
@ -441,6 +443,7 @@ class MoneroWalletOps:
daemon_addr = m[1],
proxy = m[2],
port_shift = 16,
testnet = g.testnet,
)
if g.test_suite:

View file

@ -34,7 +34,6 @@ mmgen_tool='cmds/mmgen-tool'
mmgen_keygen='cmds/mmgen-keygen'
python='python3'
rounds=100 rounds_min=20 rounds_mid=250 rounds_max=500
xmr_addrs='3,99,2,22-24,101-104'
dfl_tests='misc obj color unit hash ref altref alts xmr eth autosign btc btc_tn btc_rt bch bch_rt ltc ltc_rt tool tool2 gen'
extra_tests='autosign_btc autosign_live etc ltc_tn bch_tn'
@ -43,7 +42,7 @@ quick_tests='misc obj color unit hash ref altref alts xmr eth autosign btc btc_r
qskip_tests='btc_tn bch bch_rt ltc ltc_rt'
PROGNAME=$(basename $0)
while getopts hAbCfFi:I:lOpRtvV OPT
while getopts hAbCfFi:I:lOptvV OPT
do
case "$OPT" in
h) printf " %-16s Test MMGen release\n" "${PROGNAME}:"
@ -60,7 +59,6 @@ do
echo " '-l' List the test name symbols"
echo " '-O' Use pexpect.spawn rather than popen_spawn for applicable tests"
echo " '-p' Pause between tests"
echo " '-R' Re-run fast XMR test with existing tmp data, leave daemon running"
echo " '-t' Print the tests without running them"
echo " '-v' Run test/test.py with '--exact-output' and other commands with"
echo " '--verbose' switch"
@ -74,7 +72,7 @@ do
echo " ref - reference file checks"
echo " altref - altcoin reference file checks"
echo " alts - operations for all supported gen-only altcoins"
echo " xmr - operations for Monero"
echo " xmr - Monero xmrwallet operations"
echo " eth - operations for Ethereum"
echo " etc - operations for Ethereum Classic"
echo " autosign - autosign"
@ -116,8 +114,8 @@ do
gentest_py="$python $gentest_py"
mmgen_tool="$python $mmgen_tool"
mmgen_keygen="$python $mmgen_keygen" ;&
f) FAST=1 rounds=10 rounds_min=3 rounds_mid=25 rounds_max=50 xmr_addrs='3,23' unit_tests_py+=" --fast" ;;
F) FAST=1 rounds=3 rounds_min=1 rounds_mid=3 rounds_max=5 xmr_addrs='3,23' unit_tests_py+=" --fast" ;;
f) FAST=1 rounds=10 rounds_min=3 rounds_mid=25 rounds_max=50 unit_tests_py+=" --fast" ;;
F) FAST=1 rounds=3 rounds_min=1 rounds_mid=3 rounds_max=5 unit_tests_py+=" --fast" ;;
i) INSTALL=$OPTARG ;;
I) INSTALL=$OPTARG INSTALL_ONLY=1 ;;
l) echo -e "Default tests:\n $dfl_tests"
@ -128,7 +126,6 @@ do
exit ;;
O) test_py+=" --pexpect-spawn" ;;
p) PAUSE=1 ;;
R) RERUN_XMR=1 ;;
t) LIST_CMDS=1 ;;
v) EXACT_OUTPUT=1 test_py+=" --exact-output" ;&
V) VERBOSE=1
@ -332,41 +329,15 @@ create_tmpdir() {
mkdir -p $TMPDIR
}
if [ "$RERUN_XMR" ]; then
TMPDIR=/tmp/mmgen-test-release*
if [ -e $TMPDIR ]; then HAVE_XMR_DATA=1; else create_tmpdir; fi
else
rm -rf /tmp/mmgen-test-release*
create_tmpdir
fi
rm -rf /tmp/mmgen-test-release*
create_tmpdir
# xmr_addrs: normal: 3,99,2,22-24,101-104 fast: 3,23
mmgen_tool_xmr="$mmgen_tool -q --yes --outdir $TMPDIR --monero-wallet-rpc-password=passw0rd"
i_xmr='Monero'
s_xmr='Testing key-address file generation and wallet creation and sync operations for Monero'
s_xmr='The monerod (mainnet) daemon must be running for the following tests'
i_xmr='Monero xmrwallet'
s_xmr='Testing xmrwallet operations'
t_xmr="
a cmds/mmgen-walletgen -q -r0 -p1 -Llabel --outdir $TMPDIR -o words
a $mmgen_keygen -q --accept-defaults --use-internal-keccak-module --outdir $TMPDIR --coin=xmr $TMPDIR/*.mmwords $xmr_addrs
a cs1=\$(cmds/mmgen-tool -q --accept-defaults --coin=xmr keyaddrfile_chksum $TMPDIR/*-XMR*.akeys)
x $mmgen_keygen -q --use-old-ed25519 --accept-defaults --outdir $TMPDIR --coin=xmr $TMPDIR/*.mmwords $xmr_addrs
x cs2=\$(cmds/mmgen-tool -q --accept-defaults --coin=xmr keyaddrfile_chksum $TMPDIR/*-XMR*.akeys)
x [ \"\$cs1\" == \"\$cs2\" ]
a test/start-coin-daemons.py xmr
x $mmgen_tool_xmr xmrwallet create $TMPDIR/*-XMR*.akeys wallets=23
x $mmgen_tool_xmr xmrwallet create $TMPDIR/*-XMR*.akeys wallets=101-104
x rm $TMPDIR/*-MoneroWallet*
a $mmgen_tool_xmr xmrwallet create $TMPDIR/*-XMR*.akeys
- $mmgen_tool_xmr xmrwallet sync $TMPDIR/*-XMR*.akeys wallets=3,23
x $mmgen_tool_xmr xmrwallet sync $TMPDIR/*-XMR*.akeys wallets=101-104
x $mmgen_tool_xmr xmrwallet sync $TMPDIR/*-XMR*.akeys
s test/stop-coin-daemons.py -W xmr
- $test_py --coin=xmr xmrwallet
"
f_xmr='Monero tests completed'
[ "$FAST" ] && t_xmr_skip='x'
[ "$RERUN_XMR" ] && t_xmr_skip='x s'
[ "$RERUN_XMR" -a "$HAVE_XMR_DATA" ] && t_xmr_skip='a x s'
f_xmr='Monero xmrwallet tests completed'
i_eth='Ethereum'
s_eth='Testing transaction and tracking wallet operations for Ethereum'
@ -593,6 +564,4 @@ run_tests "$tests"
TIME=$(($(date +%s)-START))
MS=$(printf %02d:%02d $((TIME/60)) $((TIME%60)))
[ "$RERUN_XMR" ] || rm -rf /tmp/mmgen-test-release-*
[ "$LIST_CMDS" ] || echo -e "${GREEN}All OK. Total elapsed time: $MS$RESET"

View file

@ -356,6 +356,7 @@ cfgs = { # addr_idx_lists (except 31,32,33,34) must contain exactly 8 addresses
'22': {},
'23': {},
# 26,27,28 are taken
'29': {}, # xmrwallet
'31': {},
'32': {},
'33': {},
@ -509,6 +510,7 @@ class CmdGroupMgr(object):
'regtest': ('TestSuiteRegtest',{}),
# 'chainsplit': ('TestSuiteChainsplit',{}),
'ethdev': ('TestSuiteEthdev',{}),
'xmrwallet': ('TestSuiteXMRWallet',{}),
}
cmd_groups_extra = {
@ -1000,17 +1002,17 @@ if opt.pause:
set_restore_term_at_exit()
set_environ_for_spawned_scripts()
if network_id not in ('eth','etc'):
if network_id not in ('eth','etc','xmr'):
start_test_daemons(network_id,remove_datadir=True)
try:
tr = TestSuiteRunner(data_dir,trash_dir)
tr.run_tests(usr_args)
tr.warn_skipped()
if network_id not in ('eth','etc'):
if network_id not in ('eth','etc','xmr'):
stop_test_daemons(network_id)
except KeyboardInterrupt:
if network_id not in ('eth','etc'):
if network_id not in ('eth','etc','xmr'):
stop_test_daemons(network_id)
tr.warn_skipped()
die(1,'\ntest.py exiting at user request')

523
test/test_py_d/ts_xmrwallet.py Executable file
View file

@ -0,0 +1,523 @@
#!/usr/bin/env python3
#
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
# Copyright (C)2013-2021 The MMGen Project <mmgen@tuta.io>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
ts_xmrwallet.py: xmrwallet tests for the test.py test suite
"""
import sys,os,atexit,asyncio
from subprocess import run,PIPE
from mmgen.globalvars import g
from mmgen.opts import opt
from mmgen.obj import MMGenRange,XMRAmt
from mmgen.addr import KeyAddrList,AddrIdxList
from ..include.common import *
from .common import *
from .ts_base import *
class TestSuiteXMRWallet(TestSuiteBase):
"""
Monero wallet operations
"""
networks = ('xmr',)
passthru_opts = ('coin',)
tmpdir_nums = [29]
dfl_random_txs = 3
cmd_group = (
('gen_kafiles', 'generating key-address files'),
('create_wallets', 'creating Monero wallets'),
('set_dest_miner', 'opening miner wallet'),
('mine_blocks', 'mining blocks'),
('fund_alice', 'sending funds'),
('mine_blocks_tx', 'mining blocks'),
('sync_wallets', 'syncing all wallets'),
('sync_wallets_selected', 'syncing selected wallets'),
('sweep_to_address_proxy', 'sweeping to new address (via TX relay + proxy)'),
('mine_blocks', 'mining blocks'),
('sweep_to_account', 'sweeping to new account'),
('mine_blocks', 'mining blocks'),
('sweep_to_address_noproxy', 'sweeping to new address (via TX relay)'),
('mine_blocks', 'mining blocks'),
)
def __init__(self,trunner,cfgs,spawn):
TestSuiteBase.__init__(self,trunner,cfgs,spawn)
if trunner == None:
return
from mmgen.protocol import init_proto
self.proto = init_proto('XMR',network='testnet')
self.datadir_base = os.path.join('test','daemons','xmrtest')
self.tool_args = ['--testnet=1', '--monero-wallet-rpc-password=passw0rd']
self.init_users()
self.init_daemon_args()
for v in self.users.values():
run(['mkdir','-p',v.udir])
if not opt.no_daemon_autostart:
self.start_daemons()
self.start_wallet_daemons()
if not opt.no_daemon_stop:
atexit.register(self.stop_daemons)
atexit.register(self.stop_wallet_daemons)
self.init_proxy()
self.balance = None
# init methods
def init_proxy(self):
def kill_proxy():
omsg(f'Killing SSH SOCKS server at localhost:{self.socks_port}')
cmd = [ 'pkill', '-f', ' '.join(a + b2) ]
run(cmd)
self.use_proxy = False
self.socks_port = 9060
a = ['ssh','-x','-o','ExitOnForwardFailure=True','-D',f'localhost:{self.socks_port}']
b1 = ['localhost','true']
b2 = ['-fN','-E','txrelay-proxy.debug','localhost']
cp = run(a+b1,stdout=PIPE,stderr=PIPE)
if cp.returncode == 0:
if not opt.no_daemon_autostart:
run(a+b2)
omsg(f'SSH SOCKS server started, listening at localhost:{self.socks_port}')
self.use_proxy = True
elif b'already in use' in cp.stderr:
omsg(f'Port {self.socks_port} already in use. Assuming SSH SOCKS server is running')
self.use_proxy = True
else:
m1 = 'Unable to start command {!r}\n'.format(' '.join(a + b))
m2 = 'Will not test proxied TX relay daemon'
omsg(cp.stderr.decode())
omsg(yellow(m1+m2))
if not opt.no_daemon_stop:
atexit.register(kill_proxy)
def init_users(self):
from mmgen.daemon import CoinDaemon,MoneroWalletDaemon
from mmgen.rpc import MoneroRPCClient,MoneroRPCClientRaw,MoneroWalletRPCClient
self.users = {}
n = self.tmpdir_nums[0]
ud = namedtuple('user_data',[
'sid',
'mmwords',
'udir',
'datadir',
'kal_range',
'kafile',
'walletfile_fs',
'addrfile_fs',
'md',
'md_rpc',
'md_json_rpc',
'wd',
'wd_rpc',
])
for user,sid,shift,kal_range in ( # kal_range must be None, a single digit, or a single hyphenated range
('miner', '98831F3A', 130, '1'),
('bob', '1378FC64', 140, None),
('alice', 'FE3C6545', 150, '1-4'),
):
udir = os.path.join('test',f'tmp{n}',user)
datadir = os.path.join(self.datadir_base,user)
md = CoinDaemon(
proto = self.proto,
test_suite = True,
port_shift = shift,
opts = ['online'],
datadir = datadir
)
md_rpc = MoneroRPCClientRaw(
host = md.host,
port = md.rpc_port,
user = None,
passwd = None,
test_connection = False,
)
md_json_rpc = MoneroRPCClient(
host = md.host,
port = md.rpc_port,
user = None,
passwd = None,
test_connection = False,
)
wd = MoneroWalletDaemon(
user = 'foo',
passwd = 'bar',
wallet_dir = udir,
test_suite = True,
port_shift = shift,
datadir = os.path.join('test','daemons'),
daemon_addr = f'127.0.0.1:{md.rpc_port}',
testnet = True
)
wd_rpc = MoneroWalletRPCClient(
host = wd.host,
port = wd.rpc_port,
user = wd.user,
passwd = wd.passwd,
test_connection = False,
)
self.users[user] = ud(
sid = sid,
mmwords = f'test/ref/{sid}.mmwords',
udir = udir,
datadir = datadir,
kal_range = kal_range,
kafile = f'{udir}/{sid}-XMR-M[{kal_range}].testnet.akeys',
walletfile_fs = f'{udir}/{sid}-{{}}-MoneroWallet.testnet',
addrfile_fs = f'{udir}/{sid}-{{}}-MoneroWallet.testnet.address.txt',
md = md,
md_rpc = md_rpc,
md_json_rpc = md_json_rpc,
wd = wd,
wd_rpc = wd_rpc,
)
def init_daemon_args(self):
common_args = ['--p2p-bind-ip=127.0.0.1','--fixed-difficulty=1'] # ,'--rpc-ssl-allow-any-cert']
for u in self.users:
other_ports = [self.users[u2].md.p2p_port for u2 in self.users if u2 != u]
node_args = [f'--add-exclusive-node=127.0.0.1:{p}' for p in other_ports]
self.users[u].md.usr_coind_args = common_args + node_args
# cmd_group methods
def gen_kafiles(self):
for user,data in self.users.items():
if not data.kal_range:
continue
run(['mkdir','-p',data.udir])
run(f'rm -f {data.kafile}',shell=True)
t = self.spawn(
'mmgen-keygen', [
'--testnet=1','-q', '--accept-defaults', '--coin=xmr',
f'--outdir={data.udir}', data.mmwords, data.kal_range
],
extra_desc = f'({capfirst(user)})' )
t.read()
t.ok()
t.skip_ok = True
return t
def create_wallets(self):
for user,data in self.users.items():
if not data.kal_range:
continue
run('rm -f {}*'.format( data.walletfile_fs.format('*') ),shell=True)
dir_arg = [f'--outdir='+data.udir]
cmd_opts = ['wallets={}'.format(data.kal_range)]
t = self.spawn(
'mmgen-tool',
self.tool_args + dir_arg + [ 'xmrwallet', 'create', data.kafile ] + cmd_opts,
extra_desc = f'({capfirst(user)})' )
t.expect('Check key-to-address validity? (y/N): ','n')
for i in MMGenRange(data.kal_range).items:
t.expect('Address: ')
t.read()
t.ok()
t.skip_ok = True
return t
async def set_dest_miner(self):
self.do_msg()
self.set_dest('miner',1,0,lambda x: x > 20,'unlocked balance > 20')
await self.open_wallet_user('miner',1)
return 'ok'
async def fund_alice(self):
self.do_msg()
await self.transfer(
'miner',
1234567891234,
read_from_file(self.users['alice'].addrfile_fs.format(1)),
)
self.set_dest('alice',1,0,lambda x: x > 1,'unlocked balance > 1')
return 'ok'
def sync_wallets_selected(self):
return self.sync_wallets(wallets='1,3-4')
def sync_wallets(self,wallets=None):
data = self.users['alice']
dir_arg = [f'--outdir={data.udir}']
cmd_opts = list_gen(
[f'daemon=localhost:{data.md.rpc_port}'],
[f'wallets={wallets}', wallets],
)
t = self.spawn(
'mmgen-tool',
self.tool_args + dir_arg + [ 'xmrwallet', 'sync', data.kafile ] + cmd_opts )
t.expect('Check key-to-address validity? (y/N): ','n')
wlist = AddrIdxList(wallets) if wallets else MMGenRange(data.kal_range).items
for n,wnum in enumerate(wlist):
t.expect('Syncing wallet {}/{} ({})'.format(
n+1,
len(wlist),
os.path.basename(data.walletfile_fs.format(wnum)),
))
t.expect('Chain height: ')
t.expect('Wallet height: ')
t.expect('Balance: ')
t.read()
return t
def _sweep_user(self,user,spec,tx_relay_daemon=None):
data = self.users[user]
dir_arg = [f'--outdir='+data.udir]
cmd_opts = list_gen(
[f'daemon=localhost:{data.md.rpc_port}'],
[f'wallets={spec}'],
[f'tx_relay_daemon={tx_relay_daemon}', tx_relay_daemon]
)
t = self.spawn(
'mmgen-tool',
self.tool_args + dir_arg + [ 'xmrwallet', 'sweep', data.kafile ] + cmd_opts,
extra_desc = f'({capfirst(user)})' )
t.expect('Check key-to-address validity? (y/N): ','n')
t.expect(
'Create new {} .* \(y/N\): '.format('account' if ',' in spec else 'address'),
'y', regex=True )
t.expect('Relay sweep transaction? (y/N): ','y')
t.read()
return t
def sweep_to_address_proxy(self):
ret = self._sweep_user(
'alice',
'1:0',
tx_relay_daemon = 'localhost:{}:127.0.0.1:{}'.format( # proxy must be IP, not 'localhost'
self.users['bob'].md.rpc_port,
self.socks_port
) if self.use_proxy else None
)
self.set_dest('alice',1,0,lambda x: x > 1,'unlocked balance > 1')
return ret
def sweep_to_account(self):
ret = self._sweep_user('alice','1:0,2')
self.set_dest('alice',2,1,lambda x: x > 1,'unlocked balance > 1')
return ret
def sweep_to_address_noproxy(self):
ret = self._sweep_user(
'alice',
'2:1',
tx_relay_daemon = 'localhost:{}'.format(self.users['bob'].md.rpc_port)
)
self.set_dest('alice',2,1,lambda x: x > 1,'unlocked balance > 1')
return ret
# wallet methods
async def open_wallet_user(self,user,wnum):
data = self.users[user]
silence()
kal = KeyAddrList(self.proto,data.kafile,skip_key_address_validity_check=True)
end_silence()
return await data.wd_rpc.call(
'open_wallet',
filename = os.path.basename(data.walletfile_fs.format(wnum)),
password = kal.entry(wnum).wallet_passwd )
async def close_wallet_user(self,user):
ret = await self.users[user].wd_rpc.call('close_wallet')
return 'ok'
# mining methods
async def start_mining(self):
data = self.users['miner']
addr = read_from_file(data.addrfile_fs.format(1)) # mine to wallet #1, account 0
for i in range(20):
ret = await data.md_rpc.call(
'start_mining',
do_background_mining = False, # run mining in background or foreground
ignore_battery = True, # ignore battery state (on laptop)
miner_address = addr, # account address to mine to
threads_count = 3 ) # number of mining threads to run
status = self.get_status(ret)
if status == 'OK':
return True
elif status == 'BUSY':
await asyncio.sleep(5)
omsg('Daemon busy. Attempting to start mining...')
else:
die(2,f'Monerod returned status {status}')
else:
die(2,'Max retries exceeded')
async def stop_mining(self):
ret = await self.users['miner'].md_rpc.call('stop_mining')
return self.get_status(ret)
async def mine_blocks(self,random_txs=None):
"""
- open destination wallet
- optionally create and broadcast random TXs
- start mining
- mine until funds appear in wallet
- stop mining
- close wallet
"""
async def get_height():
u = self.users['miner']
for i in range(20):
try:
return (await u.md_json_rpc.call('get_last_block_header'))['block_header']['height']
except Exception as e:
if 'onnection refused' in str(e):
omsg(f'{e}\nMonerod appears to have crashed. Attempting to restart...')
await asyncio.sleep(5)
u.md.restart()
await asyncio.sleep(5)
await self.start_mining()
else:
raise
else:
die(2,'Restart attempt limit exceeded')
async def send_random_txs():
from mmgen.tool import tool_api
t = tool_api()
t.init_coin('XMR','testnet')
t.usr_randchars = 0
imsg_r(f'Sending random transactions: ')
for i in range(random_txs):
await self.transfer(
'miner',
123456789,
t.randpair()[1],
)
imsg_r(f'{i+1} ')
oqmsg_r('+')
await asyncio.sleep(0.5)
imsg('')
def print_balance(dest,ub):
imsg('Total balance in {}’s wallet #{}, account {}: {}'.format(
capfirst(dest.user),
dest.wnum,
dest.account,
ub.hl()
))
async def get_balance(dest):
data = self.users[dest.user]
await data.wd_rpc.call('refresh')
ret = await data.wd_rpc.call('get_accounts')
return XMRAmt(ret['subaddress_accounts'][dest.account]['unlocked_balance'],from_unit='atomic')
self.do_msg(extra_desc=f'+{random_txs} random TXs' if random_txs else None)
if self.dest.user != 'miner':
await self.open_wallet_user(self.dest.user,self.dest.wnum)
if random_txs:
await send_random_txs()
await self.start_mining()
h = await get_height()
imsg_r(f'Chain height: {h} ')
while True:
ub = await get_balance(self.dest)
if self.dest.test(ub):
imsg('')
oqmsg_r('+')
print_balance(self.dest,ub)
break
# else:
# imsg(f'Test {self.dest.test_desc!r} failed')
await asyncio.sleep(2)
h = await get_height()
imsg_r(f'{h} ')
oqmsg_r('+')
await self.stop_mining()
if self.dest.user != 'miner':
await self.close_wallet_user(self.dest.user)
return 'ok'
async def mine_blocks_tx(self):
return await self.mine_blocks(random_txs=self.dfl_random_txs)
# util methods
def get_status(self,ret):
if ret['status'] != 'OK':
imsg( 'RPC status: {}'.format(ret['status']) )
return ret['status']
def do_msg(self,extra_desc=None):
self.spawn(
'',
msg_only = True,
extra_desc = f'({extra_desc})' if extra_desc else None
)
def set_dest(self,user,wnum,account,test,test_desc):
self.dest = namedtuple(
'dest_info',['user','wnum','account','test','test_desc'])(user,wnum,account,test,test_desc)
async def transfer(self,user,amt,addr):
return await self.users[user].wd_rpc.call('transfer',destinations=[{'amount':amt,'address':addr}])
# daemon start/stop methods
def start_daemons(self):
self.stop_daemons()
for v in self.users.values():
run(['mkdir','-p',v.datadir])
v.md.start()
def stop_daemons(self):
for v in self.users.values():
if v.md.state != 'stopped':
v.md.stop()
run(['rm','-rf',self.datadir_base])
def start_wallet_daemons(self):
for v in self.users.values():
if v.kal_range:
v.wd.start()
def stop_wallet_daemons(self):
for v in self.users.values():
if v.kal_range and v.wd.state != 'stopped':
v.wd.stop()