diff --git a/mmgen/main_tool.py b/mmgen/main_tool.py index f5969992..52285140 100755 --- a/mmgen/main_tool.py +++ b/mmgen/main_tool.py @@ -53,6 +53,9 @@ Wallet/TX operations (coin daemon must be running): txview - show raw/signed {pnm} transaction in human-readable form twview - view tracking wallet + keyaddrlist2monerowallets - create Monero wallets from key-address list + syncmonerowallets - sync Monero wallets from key-address list + General utilities: hexdump - encode data into formatted hexadecimal form (file or stdin) unhexdump - decode formatted hexadecimal data (file or stdin) diff --git a/mmgen/tool.py b/mmgen/tool.py index 1826d057..0fca3e8c 100755 --- a/mmgen/tool.py +++ b/mmgen/tool.py @@ -94,7 +94,8 @@ cmd_data = OrderedDict([ ('Decrypt', [' [str]',"outfile [str='']","hash_preset [str='']"]), ('Bytespec', [' [str]']), - ('Keyaddrlist2monerowallet',['<{} XMR key-address file> [str]'.format(pnm),'blockheight [int=(current height)]']), + ('Keyaddrlist2monerowallets',['<{} XMR key-address file> [str]'.format(pnm),'blockheight [int=(current height)]']), + ('Syncmonerowallets', ['<{} XMR key-address file> [str]'.format(pnm)]), ]) def usage(command): @@ -475,10 +476,12 @@ def Rand2file(outfile,nbytes,threads=4,silent=False): def Bytespec(s): Msg(str(parse_nbytes(s))) -def Keyaddrlist2monerowallet(infile,blockheight=None): - import pexpect +def Syncmonerowallets(infile): monero_wallet_ops(infile=infile,op='sync') - if blockheight != None and int(blockheight) < 0: blockheight = 0 +def Keyaddrlist2monerowallets(infile,blockheight=None): + monero_wallet_ops(infile=infile,op='create',blockheight=blockheight) + +def monero_wallet_ops(infile,op,blockheight=None): def run_cmd(cmd): import subprocess as sp @@ -495,15 +498,6 @@ def Keyaddrlist2monerowallet(infile,blockheight=None): die(1,'Unable to connect to monerod!') return int(ret[8:].split('/')[0]) - cur_height = test_rpc() - - from mmgen.protocol import init_coin - init_coin('xmr') - from mmgen.addr import AddrList - al = KeyAddrList(infile) - sid = al.al_id.sid - os.environ['LANG'] = 'C' - def my_expect(p,m,s,regex=False): if m: msg_r(' {}...'.format(m)) ret = (p.expect_exact,p.expect)[regex](s) @@ -520,53 +514,95 @@ def Keyaddrlist2monerowallet(infile,blockheight=None): if m: msg('OK') vmsg("sendline: '{}' => {}".format(s,ret)) - def create(): - gmsg('\nCreating {} wallet{}'.format(dl,suf(dl))) - for n,d in enumerate(al.data): + def create(n,d,fn): + try: os.stat(fn) + except: pass + else: die(1,"Wallet '{}' already exists!".format(fn)) + p = pexpect.spawn('monero-wallet-cli --generate-from-spend-key {}'.format(fn)) + my_expect(p,'Awaiting initial prompt','Secret spend key: ') + my_sendline(p,'',d.sec,65) + my_expect(p,'','Enter new wallet password: ') + my_sendline(p,'Sending password',d.wallet_passwd,33) + my_expect(p,'','Confirm password: ') + my_sendline(p,'Sending password again',d.wallet_passwd,33) + my_expect(p,'','of your choice: ') + my_sendline(p,'','1',2) + my_expect(p,'monerod generating wallet','Generated new wallet: ') + my_expect(p,'','\n') + if d.addr not in p.before: + die(3,'Addresses do not match!\n MMGen: {}\n Monero: {}'.format(d.addr,p.before)) + my_expect(p,'','View key: ') + my_expect(p,'','\n') + if d.viewkey not in p.before: + die(3,'View keys do not match!\n MMGen: {}\n Monero: {}'.format(d.viewkey,p.before)) + my_expect(p,'','(YYYY-MM-DD): ') + h = str(blockheight or cur_height-1) + my_sendline(p,'',h,len(h)+1) + ret = my_expect(p,'',['Starting refresh','Still apply restore height? (Y/Yes/N/No): ']) + if ret == 1: + my_sendline(p,'','Y',2) + m = ' Warning: {}: blockheight argument is higher than current blockheight' + ymsg(m.format(blockheight)) + elif blockheight != None: + p.logfile = sys.stderr + my_expect(p,'Syncing wallet','\[wallet.*$',regex=True) + p.logfile = None + my_sendline(p,'Exiting','exit',5) + p.read() + + def sync(n,d,fn): + try: os.stat(fn) + except: die(1,"Wallet '{}' does not exist!".format(fn)) + p = pexpect.spawn('monero-wallet-cli --wallet-file={}'.format(fn)) + my_expect(p,'Awaiting password prompt','Wallet password: ') + my_sendline(p,'Sending password',d.wallet_passwd,33) + + msg(' Starting refresh...') + height = None + while True: + ret = p.expect([r' / .*',r'\[wallet.*:.*']) + if ret == 0: # TODO: coverage + height = p.after + msg_r('\r Block {}{}'.format(p.before.split()[-1],height)) + elif ret == 1: + if height: + height = height.split()[-1] + msg('\r Block {h} / {h}'.format(h=height)) + else: + msg(' Wallet in sync') + msg(' '+[l for l in p.before.splitlines() if l[:8] == 'Balance:'][0]) + my_sendline(p,'Exiting','exit',5) + p.read() + break + else: + die(2,"\nExpect failed: (return value: {})".format(ret)) + + def process_wallets(): + m = { 'create': ('Creat','Generat',create,False), + 'sync': ('Sync', 'Sync', sync, True) } + opt.accept_defaults = opt.accept_defaults or m[op][3] + from mmgen.protocol import init_coin + init_coin('xmr') + from mmgen.addr import AddrList + al = KeyAddrList(infile) + dl = len(al.data) + gmsg('\n{}ing {} wallet{}'.format(m[op][0],dl,suf(dl))) + for n,d in enumerate(al.data): # [d.sec,d.wallet_passwd,d.viewkey,d.addr] fn = '{}{}-{}-MoneroWallet'.format( (opt.outdir+'/' if opt.outdir else ''), - sid,d.idx) - gmsg("\nGenerating wallet {}/{} ({})".format(n+1,dl,fn)) - try: os.stat(fn) - except: pass - else: die(1,"Wallet '{}' already exists!".format(fn)) -# pmsg([d.sec,d.wallet_passwd,d.viewkey,d.addr,fn]) - p = pexpect.spawn('monero-wallet-cli --generate-from-spend-key {}'.format(fn)) - my_expect(p,'Awaiting initial prompt','Secret spend key: ') - my_sendline(p,'',d.sec,65) - my_expect(p,'','Enter new wallet password: ') - my_sendline(p,'Sending password',d.wallet_passwd,33) - my_expect(p,'','Confirm password: ') - my_sendline(p,'Sending password again',d.wallet_passwd,33) - my_expect(p,'','of your choice: ') - my_sendline(p,'','1',2) - my_expect(p,'monerod generating wallet','Generated new wallet: ') - my_expect(p,'','\n') - if d.addr not in p.before: - die(3,'Addresses do not match!\n MMGen: {}\n Monero: {}'.format(d.addr,p.before)) - my_expect(p,'','View key: ') - my_expect(p,'','\n') - if d.viewkey not in p.before: - die(3,'View keys do not match!\n MMGen: {}\n Monero: {}'.format(d.viewkey,p.before)) - my_expect(p,'','(YYYY-MM-DD): ') - h = str(blockheight or cur_height-1) - my_sendline(p,'',h,len(h)+1) - ret = my_expect(p,'',['Starting refresh','Still apply restore height? (Y/Yes/N/No): ']) - if ret == 1: - my_sendline(p,'','Y',2) - m = ' Warning: {}: blockheight argument is higher than current blockheight' - ymsg(m.format(blockheight)) - elif blockheight != None: - p.logfile = sys.stderr - my_expect(p,'Syncing wallet','\[wallet.*$',regex=True) - p.logfile = None - my_sendline(p,'Exiting','exit',5) - p.read() + al.al_id.sid, + d.idx) + gmsg('\n{}ing wallet {}/{} ({})'.format(m[op][1],n+1,dl,fn)) + m[op][2](n,d,fn) + gmsg('\n{} wallet{} {}ed'.format(dl,suf(dl),m[op][0].lower())) + + os.environ['LANG'] = 'C' + import pexpect + if blockheight != None and int(blockheight) < 0: blockheight = 0 # TODO: non-zero coverage + cur_height = test_rpc() - dl = len(al.data) try: - create() - gmsg('\n{} wallet{} created'.format(dl,suf(dl))) + process_wallets() except KeyboardInterrupt: rdie(1,'\nUser interrupt\n') except EOFError: diff --git a/scripts/test-release.sh b/scripts/test-release.sh index 73a97eac..0f58c3ce 100755 --- a/scripts/test-release.sh +++ b/scripts/test-release.sh @@ -12,7 +12,7 @@ while getopts hinPt OPT do case "$OPT" in h) printf " %-16s Test MMGen release\n" "${PROGNAME}:" - echo " USAGE: $PROGNAME [options] branch [tests]" + echo " USAGE: $PROGNAME [options] [branch] [tests]" echo " OPTIONS: '-h' Print this help message" echo " '-i' Install only; don't run tests" echo " '-n' Don't install; test in place" @@ -50,10 +50,12 @@ shift $((OPTIND-1)) RED="\e[31;1m" GREEN="\e[32;1m" YELLOW="\e[33;1m" RESET="\e[0m" -BRANCH=$1; shift -BRANCHES=$(git branch) -FOUND_BRANCH=$(for b in ${BRANCHES/\*}; do [ "$b" == "$BRANCH" ] && echo ok; done) -[ "$FOUND_BRANCH" ] || { echo "Branch '$BRANCH' not found!"; exit; } +[ "$NO_INSTALL" ] || { + BRANCH=$1; shift + BRANCHES=$(git branch) + FOUND_BRANCH=$(for b in ${BRANCHES/\*}; do [ "$b" == "$BRANCH" ] && echo ok; done) + [ "$FOUND_BRANCH" ] || { echo "Branch '$BRANCH' not found!"; exit; } +} set -e @@ -158,8 +160,10 @@ s_monero='Testing generation and wallet creation operations for Monero' s_monero='The monerod (mainnet) daemon must be running for the following tests' ROUNDS=1000 t_monero=( -'python cmds/mmgen-keygen --accept-defaults --outdir $TMPDIR --coin=xmr test/ref/98831F3A.mmwords 3,99,2,22-29,101-109' -'python cmds/mmgen-tool -q --accept-defaults --outdir $TMPDIR keyaddrlist2monerowallet $TMPDIR/988*XMR*akeys') +'python cmds/mmgen-keygen --accept-defaults --outdir $TMPDIR --coin=xmr test/ref/98831F3A.mmwords 3,99,2,22-24,101-104' +'python cmds/mmgen-tool -q --accept-defaults --outdir $TMPDIR keyaddrlist2monerowallets $TMPDIR/988*XMR*akeys' +'python cmds/mmgen-tool -q --outdir $TMPDIR syncmonerowallets $TMPDIR/988*XMR*akeys' +) [ "$MINGW" ] && t_monero=("$t_monero") f_monero='Monero tests completed' diff --git a/setup.py b/setup.py index 3e0c1e27..f0d49bd6 100755 --- a/setup.py +++ b/setup.py @@ -145,7 +145,6 @@ setup( 'mmgen.main_txsign', 'mmgen.main_txsend', 'mmgen.main_txdo', - 'mmgen.txcreate', 'mmgen.txsign', 'mmgen.main_tool',