tool.py, main_tool.py: major rewrite, cleanup

- group tool commands into classes
- add docstrings to command classes and methods
- annotate command method args
- generate call signatures + help and usage screens on the fly

- API changes:
  - listaddresses,twview: new 'age_fmt' option replaces 'show_days' and 'show_age'
  - addrfile_chksum and friends: 'mmtype' option removed

- tooltest.py, tooltest2.py and test.py have been updated accordingly
This commit is contained in:
The MMGen Project 2019-02-22 20:23:06 +00:00
commit 729a547c7d
Signed by: mmgen
GPG key ID: 3F8B1861E32B7DA2
11 changed files with 890 additions and 661 deletions

View file

@ -22,6 +22,8 @@ mmgen.exception: Exception classes for the MMGen suite
# 1: no hl, message only
class UserNonConfirmation(Exception): mmcode = 1
class BadAgeFormat(Exception): mmcode = 1
class BadFilename(Exception): mmcode = 1
# 2: yellow hl, message only
class UnrecognizedTokenSymbol(Exception): mmcode = 2

View file

@ -50,7 +50,7 @@ class g(object):
keywords = 'Bitcoin, BTC, cryptocurrency, wallet, cold storage, offline, online, spending, open-source, command-line, Python, Linux, Bitcoin Core, bitcoind, hd, deterministic, hierarchical, secure, anonymous, Electrum, seed, mnemonic, brainwallet, Scrypt, utility, script, scriptable, blockchain, raw, transaction, permissionless, console, terminal, curses, ansi, color, tmux, remote, client, daemon, RPC, json, entropy, xterm, rxvt, PowerShell, MSYS, MinGW, mswin, Armbian, Raspbian, Raspberry Pi, Orange Pi, BCash, BCH, Litecoin, LTC, altcoin, ZEC, Zcash, DASH, Dashpay, ETH, Ethereum, Classic, SHA256Compress, XMR, Monero, monerod, EMC, Emercoin, ERC20, token, deploy, contract, gas, fee, smart contract, solidity, Parity, testnet, devmode, Kovan'
max_int = 0xffffffff
stdin_tty = bool(sys.stdin.isatty() or os.getenv('MMGEN_TEST_SUITE'))
stdin_tty = bool(sys.stdin.isatty() or os.getenv('MMGEN_TEST_SUITE_POPEN_SPAWN'))
stdout_fileno = sys.stdout.fileno()
stderr_fileno = sys.stderr.fileno()

View file

@ -23,96 +23,35 @@ mmgen-tool: Perform various MMGen- and cryptocoin-related operations.
from mmgen.common import *
stdin_msg = """
To force a command to read from STDIN in place of its first argument (for
supported commands), use '-' as the first argument.
""".strip()
def make_cmd_help():
import mmgen.tool
out = []
for bc in mmgen.tool.MMGenToolCmd.__bases__:
cls_doc = bc.__doc__.strip().split('\n')
for l in cls_doc:
if l is cls_doc[0]: l += ':'
l = l.replace('\t','',1)
if l:
l = l.replace('\t',' ')
out.append(l[0].upper() + l[1:])
else:
out.append('')
out.append('')
cmd_help = """
Cryptocoin address/key operations (compressed public keys supported):
addr2hexaddr - convert coin address from base58 to hex format
hex2wif - convert a private key from hex to WIF format (use '--type=zcash_z' for zcash-z key)
pubhash2addr - convert public key hash to address
privhex2addr - generate coin address from private key in hex format
privhex2pubhex - generate a hex public key from a hex private key
pubhex2addr - convert a hex pubkey to an address
pubhex2redeem_script - convert a hex pubkey to a Segwit P2SH-P2WPKH redeem script
wif2redeem_script - convert a WIF private key to a Segwit P2SH-P2WPKH redeem script
wif2segwit_pair - generate both a Segwit P2SH-P2WPKH redeem script and address from WIF
pubkey2addr - convert coin public key to address
randpair - generate a random private key/address pair
randwif - generate a random private key in WIF format
wif2addr - generate a coin address from a key in WIF format
wif2hex - convert a private key from WIF to hex format
cls_funcs = bc.user_commands()
max_w = max(map(len,cls_funcs))
fs = ' {{:{}}} - {{}}'.format(max_w)
for func in cls_funcs:
m = getattr(bc,func)
if m.__doc__:
out.append(fs.format(func,
pretty_format( m.__doc__.strip().replace('\n\t\t',' '),
width=79-(max_w+7),
pfx=' '*(max_w+5)).lstrip()
))
out.append('')
Wallet/TX operations (coin daemon must be running):
gen_addr - generate a single MMGen address from default or specified wallet
gen_key - generate a single MMGen WIF key from default or specified wallet
getbalance - like '{pn}-cli getbalance' but shows confirmed/unconfirmed,
spendable/unspendable balances for individual {pnm} wallets
listaddress - list the specified {pnm} address and its balance
listaddresses - list {pnm} addresses and their balances
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)
bytespec - convert a byte specifier such as '1GB' into an integer
hexlify - display string in hexadecimal format
hexreverse - reverse bytes of a hexadecimal string
rand2file - write 'n' bytes of random data to specified file
randhex - print 'n' bytes (default 32) of random data in hex format
hash256 - compute sha256(sha256(data)) (double sha256)
hash160 - compute ripemd160(sha256(data)) (converts hexpubkey to hexaddr)
b58randenc - generate a random 32-byte number and convert it to base 58
b58tostr - convert a base 58 number to a string
strtob58 - convert a string to base 58
b58tohex - convert a base 58 number to hexadecimal
b58chktohex - convert a base58-check encoded number to hexadecimal
hextob58 - convert a hexadecimal number to base 58
hextob58chk - convert a hexadecimal number to base58-check encoding
b32tohex - convert a base 32 number to hexadecimal
hextob32 - convert a hexadecimal number to base 32
File encryption:
encrypt - encrypt a file
decrypt - decrypt a file
{pnm} encryption suite:
* Key: Scrypt (user-configurable hash parameters, 32-byte salt)
* Enc: AES256_CTR, 16-byte rand IV, sha256 hash + 32-byte nonce + data
* The encrypted file is indistinguishable from random data
{pnm}-specific operations:
remove_address - remove an address from tracking wallet
add_label - add descriptive label for {pnm} address in tracking wallet
remove_label - remove descriptive label for {pnm} address in tracking wallet
addrfile_chksum - compute checksum for {pnm} address file
keyaddrfile_chksum - compute checksum for {pnm} key-address file
passwdfile_chksum - compute checksum for {pnm} password file
find_incog_data - Use an Incog ID to find hidden incognito wallet data
id6 - generate 6-character {pnm} ID for a file (or stdin)
id8 - generate 8-character {pnm} ID for a file (or stdin)
str2id6 - generate 6-character {pnm} ID for a string, ignoring spaces
Mnemonic operations (choose 'electrum' (default), 'tirosh' or 'all'
wordlists):
mn_rand128 - generate random 128-bit mnemonic
mn_rand192 - generate random 192-bit mnemonic
mn_rand256 - generate random 256-bit mnemonic
mn_stats - show stats for mnemonic wordlist
mn_printlist - print mnemonic wordlist
hex2mn - convert a 16, 24 or 32-byte number in hex format to a mnemonic
mn2hex - convert a 12, 18 or 24-word mnemonic to a number in hex format
IMPORTANT NOTE: Though {pnm} mnemonics use the Electrum wordlist, they're
computed using a different algorithm and are NOT Electrum-compatible!
{sm}
"""
return '\n'.join(out)
opts_data = lambda: {
'desc': 'Perform various {pnm}- and cryptocoin-related operations'.format(pnm=g.proj_name),
@ -125,20 +64,17 @@ opts_data = lambda: {
-q, --quiet Produce quieter output
-r, --usr-randchars=n Get 'n' characters of additional randomness from
user (min={g.min_urandchars}, max={g.max_urandchars})
-t, --type=t Specify address type (valid options: 'compressed','segwit','bech32','zcash_z')
-t, --type=t Specify address type (valid options: 'legacy',
'compressed', 'segwit', 'bech32', 'zcash_z')
-v, --verbose Produce more verbose output
""".format(g=g),
'notes': """
COMMANDS
{ch}
Type '{pn} help <command> for help on a particular command
""".format( pn=g.prog_name,
ch=cmd_help.format(
pn=g.proto.name,
pnm=g.proj_name,
sm='\n '.join(stdin_msg.split('\n')))
)
Type '{pn} help <command>' for help on a particular command
""".format(pn=g.prog_name,ch=make_cmd_help())
}
cmd_args = opts.init(opts_data,add_opts=['hidden_incog_input_params','in_fmt','use_old_ed25519'])
@ -149,8 +85,9 @@ cmd = cmd_args.pop(0)
import mmgen.tool as tool
tc = tool.MMGenToolCmd()
if cmd == 'help' and not cmd_args:
tool._usage(exit_val=0)
if cmd in ('help','usage') and cmd_args:
cmd_args[0] = 'command_name=' + cmd_args[0]
if cmd not in dir(tc):
die(1,"'{}': no such command".format(cmd))
@ -158,4 +95,4 @@ args,kwargs = tool._process_args(cmd,cmd_args)
ret = getattr(tc,cmd)(*args,**kwargs)
tool._print_result(ret,'pager' in kwargs and kwargs['pager'])
tool._process_result(ret,to_screen=True,pager='pager' in kwargs and kwargs['pager'])

File diff suppressed because it is too large Load diff

View file

@ -81,15 +81,26 @@ watch-only wallet using '{}-addrimport' and then re-run this program.
self.cols = None
self.reverse = False
self.group = False
self.show_days = True
self.show_mmid = True
self.minconf = minconf
self.get_unspent_data()
self.age_fmt = 'days'
self.sort_key = 'age'
self.do_sort()
self.total = self.get_total_coin()
self.disp_prec = self.get_display_precision()
@property
def age_fmt(self):
return self._age_fmt
@age_fmt.setter
def age_fmt(self,val):
age_fmts = ('days','confs')
if val not in age_fmts:
raise BadAgeFormat("'{}': invalid age format (must be one of {!r})".format(val,age_fmts))
self._age_fmt = val
def get_display_precision(self):
return g.proto.coin_amt.max_prec
@ -199,7 +210,7 @@ watch-only wallet using '{}-addrimport' and then re-run this program.
a='Address'.ljust(addr_w),
A='Amt({})'.format(g.dcoin).ljust(self.disp_prec+3),
A2=' Amt({})'.format(g.coin).ljust(self.disp_prec+4),
c=('Confs','Age(d)')[self.show_days]
c=('Confs','Age(d)')[self.age_fmt=='days']
).rstrip()]
for n,i in enumerate(unsp):
@ -225,7 +236,7 @@ watch-only wallet using '{}-addrimport' and then re-run this program.
a=addr_out,
A=i.amt.fmt(color=True,prec=self.disp_prec),
A2=(i.amt2.fmt(color=True,prec=self.disp_prec) if i.amt2 is not None else ''),
c=i.days if self.show_days else i.confs
c=i.days if self.age_fmt == 'days' else i.confs
).rstrip())
self.fmt_display = '\n'.join(out) + '\n'
@ -327,7 +338,7 @@ watch-only wallet using '{}-addrimport' and then re-run this program.
if action[:2] == 's_':
self.do_sort(action[2:])
if action == 's_twmmid': self.show_mmid = True
elif action == 'd_days': self.show_days = not self.show_days
elif action == 'd_days': self.age_fmt = ('days','confs')[self.age_fmt=='days']
elif action == 'd_mmid': self.show_mmid = not self.show_mmid
elif action == 'd_group':
if self.can_group:
@ -453,7 +464,10 @@ class TwAddrList(MMGenDict):
def coinaddr_list(self): return [self[k]['addr'] for k in self]
def format(self,showbtcaddrs,sort,show_age,show_days):
def format(self,showbtcaddrs,sort,show_age,age_fmt):
age_fmts = ('days','confs')
if age_fmt not in age_fmts:
raise BadAgeFormat("'{}': invalid age format (must be one of {!r})".format(age_fmt,age_fmts))
out = ['Chain: '+green(g.chain.upper())] if g.chain != 'mainnet' else []
fs = '{{mid}}{} {{cmt}} {{amt}}{}'.format(('',' {addr}')[showbtcaddrs],('',' {age}')[show_age])
mmaddrs = [k for k in self.keys() if k.type == 'mmgen']
@ -468,7 +482,7 @@ class TwAddrList(MMGenDict):
addr=(CoinAddr.fmtc('ADDRESS',width=addr_width) if showbtcaddrs else None),
cmt=TwComment.fmtc('COMMENT',width=max_cmt_len+1),
amt='BALANCE'.ljust(max_fp_len+4),
age=('CONFS','DAYS')[show_days],
age=('CONFS','DAYS')[age_fmt=='days'],
)]
def sort_algo(j):
@ -500,7 +514,7 @@ class TwAddrList(MMGenDict):
addr=(e['addr'].fmt(color=True,width=addr_width) if showbtcaddrs else None),
cmt=e['lbl'].comment.fmt(width=max_cmt_len,color=True,nullrepl='-'),
amt=e['amt'].fmt('4.{}'.format(max(max_fp_len,3)),color=True),
age=mmid.confs // (1,confs_per_day)[show_days] if hasattr(mmid,'confs') and mmid.confs != None else '-'
age=mmid.confs // (1,confs_per_day)[age_fmt=='days'] if hasattr(mmid,'confs') and mmid.confs != None else '-'
))
return '\n'.join(out + ['\nTOTAL: {} {}'.format(self.total.hl(color=True),g.dcoin)])

View file

@ -1069,11 +1069,13 @@ Selected non-{pnm} inputs: {{}}""".strip().format(pnm=g.proj_name,pnl=g.proj_nam
return out + '\n'
def format_view(self,terse=False):
try:
rpc_init()
blockcount = self.get_blockcount()
except:
blockcount = None
blockcount = None
if g.proto.base_coin != 'ETH':
try:
rpc_init()
blockcount = self.get_blockcount()
except:
pass
def get_max_mmwid(io):
if io == self.inputs:

View file

@ -389,6 +389,17 @@ def get_from_brain_opt_params():
l,p = opt.from_brain.split(',')
return(int(l),p)
def pretty_format(s,width=80,pfx=''):
out = []
while(s):
if len(s) <= width:
out.append(s)
break
i = s[:width].rfind(' ')
out.append(s[:i])
s = s[i+1:]
return pfx + ('\n'+pfx).join(out)
def pretty_hexdump(data,gw=2,cols=8,line_nums=False):
r = (0,1)[bool(len(data) % gw)]
return ''.join(
@ -566,7 +577,7 @@ def write_data_to_file( outfile,data,desc='data',
def do_stdout():
qmsg('Output to STDOUT requested')
if sys.stdout.isatty():
if g.stdin_tty:
if no_tty:
die(2,'Printing {} to screen is not allowed'.format(desc))
if (ask_tty and not opt.quiet) or binary:

View file

@ -21,11 +21,11 @@ python='python3'
rounds=100 rounds_low=20 rounds_spec=500 gen_rounds=10
monero_addrs='3,99,2,22-24,101-104'
dfl_tests='obj sha256 alts monero eth autosign btc btc_tn btc_rt bch bch_rt ltc ltc_tn ltc_rt tool gen'
dfl_tests='obj sha256 alts monero eth autosign btc btc_tn btc_rt bch bch_rt ltc ltc_tn ltc_rt tool tool2 gen'
add_tests='autosign_minimal autosign_live'
PROGNAME=$(basename $0)
while getopts hbCfilnPRtvV OPT
while getopts hbCfilnOPRtvV OPT
do
case "$OPT" in
h) printf " %-16s Test MMGen release\n" "${PROGNAME}:"
@ -37,6 +37,7 @@ do
echo " '-i' Install only; don't run tests"
echo " '-l' List the test name symbols"
echo " '-n' Don't install; test in place"
echo " '-O' Use popen_spawn rather than popen for applicable tests"
echo " '-P' Don't pause between tests"
echo " '-R' Don't remove temporary files after program has exited"
echo " '-t' Print the tests without running them"
@ -58,6 +59,7 @@ do
echo " ltc_tn - litecoin testnet"
echo " ltc_rt - litecoin regtest"
echo " tool - tooltest (all supported coins)"
echo " tool2 - tooltest2 (all supported coins)"
echo " gen - gentest (all supported coins)"
echo " By default, all tests are run"
exit ;;
@ -80,6 +82,7 @@ do
echo -e "Additional tests:\n $add_tests"
exit ;;
n) NO_INSTALL=1 ;;
O) test_py+=" --popen-spawn" ;;
P) NO_PAUSE=1 ;;
R) NO_TMPFILE_REMOVAL=1 ;;
t) TESTING=1 ;;
@ -265,7 +268,6 @@ t_btc=(
"$test_py --segwit dfl_wallet main ref ref_files"
"$test_py --segwit-random dfl_wallet main"
"$test_py --bech32 dfl_wallet main ref ref_files"
"$tooltest_py rpc"
"$python scripts/compute-file-chksum.py $REFDIR/*testnet.rawtx >/dev/null 2>&1")
f_btc='You may stop the bitcoin (mainnet) daemon if you wish'
@ -275,8 +277,7 @@ t_btc_tn=(
"$test_py --testnet=1"
"$test_py --testnet=1 --segwit dfl_wallet main ref ref_files"
"$test_py --testnet=1 --segwit-random dfl_wallet main"
"$test_py --testnet=1 --bech32 dfl_wallet main ref ref_files"
"$tooltest_py --testnet=1 rpc")
"$test_py --testnet=1 --bech32 dfl_wallet main ref ref_files")
f_btc_tn='You may stop the bitcoin testnet daemon if you wish'
i_btc_rt='Bitcoin regtest'
@ -313,9 +314,7 @@ t_ltc=(
"$test_py --coin=ltc dfl_wallet main ref ref_files"
"$test_py --coin=ltc --segwit dfl_wallet main ref ref_files"
"$test_py --coin=ltc --segwit-random dfl_wallet main"
"$test_py --coin=ltc --bech32 dfl_wallet main ref ref_files"
"$tooltest_py --coin=ltc rpc"
)
"$test_py --coin=ltc --bech32 dfl_wallet main ref ref_files")
f_ltc='You may stop the litecoin daemon if you wish'
i_ltc_tn='Litecoin testnet'
@ -324,8 +323,7 @@ t_ltc_tn=(
"$test_py --coin=ltc --testnet=1"
"$test_py --coin=ltc --testnet=1 --segwit dfl_wallet main ref ref_files"
"$test_py --coin=ltc --testnet=1 --segwit-random dfl_wallet main"
"$test_py --coin=ltc --testnet=1 --bech32 dfl_wallet main ref ref_files"
"$tooltest_py --coin=ltc --testnet=1 rpc")
"$test_py --coin=ltc --testnet=1 --bech32 dfl_wallet main ref ref_files")
f_ltc_tn='You may stop the litecoin testnet daemon if you wish'
i_ltc_rt='Litecoin regtest'
@ -333,10 +331,30 @@ s_ltc_rt="The following tests will test MMGen's regtest (Bob and Alice) mode"
t_ltc_rt=("$test_py --coin=ltc regtest")
f_ltc_rt='Regtest (Bob and Alice) mode tests for LTC completed'
i_tool2='Tooltest2'
s_tool2="The following tests will run '$tooltest2_py' for all supported coins"
t_tool2=(
"$tooltest2_py --quiet --non-coin-dependent"
"$tooltest2_py --quiet --coin=btc --coin-dependent"
"$tooltest2_py --quiet --coin=btc --testnet=1 --coin-dependent"
"$tooltest2_py --quiet --coin=ltc --coin-dependent"
"$tooltest2_py --quiet --coin=ltc --testnet=1 --coin-dependent"
"$tooltest2_py --quiet --coin=bch --coin-dependent"
"$tooltest2_py --quiet --coin=bch --testnet=1 --coin-dependent"
"$tooltest2_py --quiet --coin=zec --coin-dependent"
"$tooltest2_py --quiet --coin=zec --type=zcash_z --coin-dependent"
"$tooltest2_py --quiet --coin=xmr --coin-dependent"
"$tooltest2_py --quiet --coin=dash --coin-dependent"
"$tooltest2_py --quiet --coin=eth --coin-dependent"
"$tooltest2_py --quiet --coin=eth --testnet=1 --coin-dependent"
"$tooltest2_py --quiet --coin=eth --token=mm1 --coin-dependent"
"$tooltest2_py --quiet --coin=eth --token=mm1 --testnet=1 --coin-dependent"
"$tooltest2_py --quiet --coin=etc --coin-dependent")
f_tool2='tooltest2 tests completed'
i_tool='Tooltest'
s_tool="The following tests will run '$tooltest_py' for all supported coins"
t_tool=(
"$tooltest2_py"
"$tooltest_py --coin=btc util"
"$tooltest_py --coin=btc cryptocoin"
"$tooltest_py --coin=btc mnemonic"

View file

@ -148,7 +148,8 @@ opts_data = lambda: {
-L, --list-cmd-groups Output a list of command groups, with no descriptions
-n, --names Display command names instead of descriptions
-o, --log Log commands to file {lf}
-O, --popen-spawn Use pexpect's popen_spawn instead of popen
-O, --popen-spawn Use pexpect's popen_spawn instead of popen (much faster,
but doesn't emulate terminal)
-p, --pause Pause between tests, resuming on keypress
-P, --profile Record the execution time of each script
-q, --quiet Produce minimal output. Suppress dependency info
@ -173,9 +174,8 @@ If no command is given, the whole suite of tests is run.
sys.argv = [sys.argv[0]] + ['--data-dir',data_dir] + sys.argv[1:]
cmd_args = opts.init(opts_data)
# Under python3, with PopenSpawn we can no longer imitate cbreak mode with sys.stdin.read(1)
# opt.popen_spawn = True # popen has issues, so use popen_spawn always
if opt.popen_spawn: os.environ['MMGEN_TEST_SUITE_POPEN_SPAWN'] = '1'
if not opt.system: os.environ['PYTHONPATH'] = repo_root
lbl_id = ('account','label')[g.coin=='BTC'] # update as other coins adopt Core's label API
@ -198,11 +198,14 @@ rtFee = {
}[coin_sel]
rtBals = {
'btc': ('499.9999488','399.9998282','399.9998147','399.9996877',
'52.99990000','946.99933647','999.99923647','52.9999','946.99933647'),
'52.99990000','946.99933647','999.99923647','52.9999',
'946.99933647'),
'bch': ('499.9999484','399.9999194','399.9998972','399.9997692',
'46.78900000','953.20966920','999.99866920','46.789','953.2096692'),
'46.78900000','953.20966920','999.99866920','46.789',
'953.2096692'),
'ltc': ('5499.99744','5399.994425','5399.993885','5399.987535',
'52.99000000','10946.93753500','10999.92753500','52.99','10946.937535'),
'52.99000000','10946.93753500','10999.92753500','52.99',
'10946.937535'),
}[coin_sel]
rtBals_gb = {
'btc': ('116.77629233','283.22339537'),
@ -210,6 +213,11 @@ rtBals_gb = {
'ltc': ('5116.77036263','283.21717237')
}[coin_sel]
rtBobOp3 = {'btc':'S:2','bch':'L:3','ltc':'S:2'}[coin_sel]
rtAmts = {
'btc': ('500',),
'bch': ('500',),
'ltc': ('5500',)
}[coin_sel]
if opt.segwit and 'S' not in g.proto.mmtypes:
die(1,'--segwit option incompatible with {}'.format(g.proto.__name__))
@ -841,14 +849,14 @@ cmd_group['ref'] = (
# reference files
cmd_group['ref_files'] = (
('ref_addrfile_chk', 'saved reference address file'),
('ref_segwitaddrfile_chk','saved reference address file (segwit)'),
('ref_bech32addrfile_chk','saved reference address file (bech32)'),
('ref_addrfile_chk', 'saved reference address file'), # TODO: move to tooltest2
('ref_segwitaddrfile_chk','saved reference address file (segwit)'), # TODO: move to tooltest2
('ref_bech32addrfile_chk','saved reference address file (bech32)'), # TODO: move to tooltest2
('ref_keyaddrfile_chk','saved reference key-address file'),
('ref_passwdfile_chk', 'saved reference password file'),
# Create the fake inputs:
# ('txcreate8', 'transaction creation (8)'),
('ref_tx_chk', 'saved reference tx file'),
('ref_tx_chk', 'saved reference tx file'), # TODO: move to tooltest2
('ref_brain_chk_spc3', 'saved brainwallet (non-standard spacing)'),
('ref_tool_decrypt', 'decryption of saved MMGen-encrypted file'),
)
@ -889,15 +897,16 @@ cmd_group['regtest'] = (
('regtest_fund_alice', "funding Alice's wallet"),
('regtest_bob_bal1', "Bob's balance"),
('regtest_bob_add_label', "adding a 40-character UTF-8 encoded label"),
('regtest_bob_twview', "viewing Bob's tracking wallet"),
('regtest_bob_split1', "splitting Bob's funds"),
('regtest_generate', 'mining a block'),
('regtest_bob_bal2', "Bob's balance"),
('regtest_bob_bal2a', "Bob's balance (show_days=1)"),
('regtest_bob_bal2b', "Bob's balance (show_age=1)"),
('regtest_bob_bal2c', "Bob's balance (showempty=1 show_age=1 minconf=2)"),
('regtest_bob_bal2d', "Bob's balance (show_age=1 minconf=2)"),
('regtest_bob_bal2e', "Bob's balance (showempty=1 show_age=1 sort=age)"),
('regtest_bob_bal2f', "Bob's balance (showempty=1 show_age=1 sort=age,reverse)"),
('regtest_bob_bal2a', "Bob's balance (age_fmt=confs)"),
('regtest_bob_bal2b', "Bob's balance (showempty=1)"),
('regtest_bob_bal2c', "Bob's balance (showempty=1 minconf=2 age_fmt=days)"),
('regtest_bob_bal2d', "Bob's balance (minconf=2)"),
('regtest_bob_bal2e', "Bob's balance (showempty=1 sort=age)"),
('regtest_bob_bal2f', "Bob's balance (showempty=1 sort=age,reverse)"),
('regtest_bob_rbf_send', 'sending funds to Alice (RBF)'),
('regtest_get_mempool1', 'mempool (before RBF bump)'),
('regtest_bob_rbf_bump', 'bumping RBF transaction'),
@ -1079,7 +1088,7 @@ cmd_group['ethdev'] = (
('ethdev_twview3','twview wide=1 sort=age (ignored)'),
('ethdev_twview4','twview wide=1 minconf=999999999 (ignored)'),
('ethdev_twview5','twview wide=1 minconf=0 (ignored)'),
('ethdev_twview6','twview show_days=0 (ignored)'),
('ethdev_twview6','twview age_fmt=days (ignored)'),
('ethdev_token_twview1','twview --token=mm1'),
('ethdev_token_twview2','twview --token=mm1 wide=1'),
@ -2861,8 +2870,7 @@ class MMGenTestSuite(object):
def ref_addrfile_chk_zec_z(self,name):
if skip_for_win(): return
self.ref_addrfile_chk(name,ftype='addr',coin='ZEC',subdir='zcash',pfx='-ZEC-Z',
mmtype='z',add_args=['mmtype=zcash_z'])
self.ref_addrfile_chk(name,ftype='addr',coin='ZEC',subdir='zcash',pfx='-ZEC-Z',mmtype='z')
def ref_addrfile_chk_xmr(self,name):
self.ref_addrfile_chk(name,ftype='addr',coin='XMR',subdir='monero',pfx='-XMR-M')
@ -2882,8 +2890,7 @@ class MMGenTestSuite(object):
def ref_keyaddrfile_chk_zec_z(self,name):
if skip_for_win(): return
self.ref_addrfile_chk(name,ftype='keyaddr',coin='ZEC',subdir='zcash',pfx='-ZEC-Z',
mmtype='z',add_args=['mmtype=zcash_z'])
self.ref_addrfile_chk(name,ftype='keyaddr',coin='ZEC',subdir='zcash',pfx='-ZEC-Z',mmtype='z')
def ref_keyaddrfile_chk_xmr(self,name):
self.ref_addrfile_chk(name,ftype='keyaddr',coin='XMR',subdir='monero',pfx='-XMR-M')
@ -2922,16 +2929,14 @@ class MMGenTestSuite(object):
def ref_tool_decrypt(self,name):
f = os.path.join(ref_dir,ref_enc_fn)
disable_debug()
t = MMGenExpect(name,'mmgen-tool', ['-q','decrypt',f,'outfile=-','hash_preset=1'])
dec_fn = get_tmpfile_fn(cfg,'famous.txt')
t = MMGenExpect(name,'mmgen-tool', ['-q','decrypt',f,'outfile='+dec_fn,'hash_preset=1'])
restore_debug()
t.passphrase('user data',tool_enc_passwd)
t.expect(NL,nonl=True)
t.expect('to confirm: ','YES\n')
import re
o = t.read()
o = re.sub('YES\r\n','',o).split('\n')[0]
o = re.sub('\r','\n',o)
cmp_or_die(sample_text,o)
t.written_to_file('Decrypted data')
dec_txt = read_from_file(dec_fn)
imsg_r(dec_txt)
cmp_or_die(sample_text,dec_txt)
# wallet conversion tests
def walletconv_in(self,name,infile,desc,uopts=[],pw=False,oo=False):
@ -3075,6 +3080,15 @@ class MMGenTestSuite(object):
def regtest_fund_bob(self,name): self.regtest_fund_wallet(name,'bob','C',rtFundAmt)
def regtest_fund_alice(self,name): self.regtest_fund_wallet(name,'alice',('L','S')[g.proto.cap('segwit')],rtFundAmt)
def regtest_user_twview(self,name,user):
t = MMGenExpect(name,'mmgen-tool',['--'+user,'twview'])
t.expect(r'1\).*\b{}\b'.format(rtAmts[0]),regex=True)
t.read()
t.ok()
def regtest_bob_twview(self,name):
return self.regtest_user_twview(name,'bob')
def regtest_user_bal(self,name,user,bal,args=['showempty=1'],skip_check=False,exit_val=0):
t = MMGenExpect(name,'mmgen-tool',['--'+user,'listaddresses'] + args)
if skip_check:
@ -3097,22 +3111,22 @@ class MMGenTestSuite(object):
return self.regtest_user_bal(name,'bob',rtBals[0])
def regtest_bob_bal2a(self,name):
return self.regtest_user_bal(name,'bob',rtBals[0],args=['showempty=1','show_days=1'])
return self.regtest_user_bal(name,'bob',rtBals[0],args=['showempty=1','age_fmt="confs"'])
def regtest_bob_bal2b(self,name):
return self.regtest_user_bal(name,'bob',rtBals[0],args=['showempty=1','show_age=1'])
return self.regtest_user_bal(name,'bob',rtBals[0],args=['showempty=1'])
def regtest_bob_bal2c(self,name):
return self.regtest_user_bal(name,'bob',rtBals[0],args=['showempty=1','show_age=1','minconf=2'],skip_check=True)
return self.regtest_user_bal(name,'bob',rtBals[0],args=['showempty=1','minconf=2','age_fmt="days"'],skip_check=True)
def regtest_bob_bal2d(self,name):
return self.regtest_user_bal(name,'bob',rtBals[0],args=['show_age=1','minconf=2'],skip_check=True)
return self.regtest_user_bal(name,'bob',rtBals[0],args=['minconf=2'],skip_check=True)
def regtest_bob_bal2e(self,name):
return self.regtest_user_bal(name,'bob',rtBals[0],args=['showempty=1','show_age=1','sort=age'])
return self.regtest_user_bal(name,'bob',rtBals[0],args=['showempty=1','sort=age'])
def regtest_bob_bal2f(self,name):
return self.regtest_user_bal(name,'bob',rtBals[0],args=['showempty=1','show_age=1','sort=age,reverse'])
return self.regtest_user_bal(name,'bob',rtBals[0],args=['showempty=1','sort=age,reverse'])
def regtest_bob_bal3(self,name):
return self.regtest_user_bal(name,'bob',rtBals[1])
@ -3952,7 +3966,7 @@ class MMGenTestSuite(object):
def ethdev_twview5(self,name):
return self.ethdev_twview(name,tool_args=['wide=1','minconf=0'])
def ethdev_twview6(self,name):
return self.ethdev_twview(name,tool_args=['show_days=0'])
return self.ethdev_twview(name,tool_args=['age_fmt=days'])
def ethdev_token_twview1(self,name):
return self.ethdev_twview(name,args=['--token=mm1'])

View file

@ -37,6 +37,7 @@ opts_data = lambda: {
'options': """
-h, --help Print this help message
-C, --coverage Produce code coverage info using trace module
-d, --debug Produce debugging output (stderr from spawned script)
--, --longhelp Print help message for long options (common options)
-l, --list-cmds List and describe the tests and commands in this test suite
-L, --list-names List the names of all tested 'mmgen-tool' commands
@ -119,18 +120,6 @@ cmd_data = OrderedDict([
])
}
),
('rpc', {
'desc': 'Coin daemon RPC commands',
'cmd_data': OrderedDict([
# ('keyaddrfile_chksum', ()), # interactive
('addrfile_chksum', ()),
('getbalance', ()),
('listaddresses', ()),
('twview', ()),
('txview', ()),
])
}
),
])
cfg = {
@ -186,30 +175,35 @@ if opt.list_cmds:
Msg(fs.format('clean','Clean the tmp directory',w=w))
sys.exit(0)
if opt.list_names:
tested_here = []
tested_in = {
'tooltest.py': [],
'test.py': (
'encrypt','decrypt','find_incog_data',
'addrfile_chksum','keyaddrfile_chksum','passwdfile_chksum',
'add_label','remove_label','remove_address','twview',
'getbalance','listaddresses','listaddress'),
'test-release.sh': ('keyaddrlist2monerowallets','syncmonerowallets'),
'tooltest2.py': subprocess.check_output(['test/tooltest2.py','--list-tested-cmds']).decode().split()
}
for v in cmd_data.values():
tested_here += list(v['cmd_data'].keys())
msg('{}\n{}'.format(green("Tested 'mmgen-tool' commands:"),'\n'.join(sorted(c.lower() for c in tested_here))))
import mmgen.tool
tested_in_test_py = (
'encrypt','decrypt','find_incog_data',
'addrfile_chksum','keyaddrfile_chksum','passwdfile_chksum',
'add_label','remove_label','remove_address',
'getbalance','listaddresses','listaddress','twview')
tested_in_test_release = ('keyaddrlist2monerowallets','syncmonerowallets')
tested_in_tooltest2 = subprocess.check_output(['test/tooltest2.py','--list-tested-cmds']).decode().split()
tested_in['tooltest.py'] += list(v['cmd_data'].keys())
ignore = ('help','usage')
uc = sorted(c.lower() for c in (
set(mmgen.tool.cmd_data.keys()) -
msg(green("TESTED 'MMGEN-TOOL' COMMANDS"))
for l in ('tooltest.py','tooltest2.py','test.py','test-release.sh'):
msg(blue(l+':'))
msg(' '+'\n '.join(sorted(tested_in[l])))
ignore = ()
from mmgen.tool import MMGenToolCmd
uc = sorted(
set(MMGenToolCmd.user_commands()) -
set(ignore) -
set(tested_here) -
set(tested_in_tooltest2) -
set(tested_in_test_py) -
set(tested_in_test_release)
))
msg('\n{}\n{}'.format(yellow('Untested commands:'),'\n'.join(uc)))
die()
set(tested_in['tooltest.py']) -
set(tested_in['tooltest2.py']) -
set(tested_in['test.py']) -
set(tested_in['test-release.sh'])
)
die(0,'\n{}\n {}'.format(yellow('Untested commands:'),'\n '.join(uc)))
from mmgen.tx import is_wif,is_coin_addr
@ -248,6 +242,9 @@ class MMGenToolTestUtils(object):
p = subprocess.Popen(sys_cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
a,b = p.communicate()
if opt.debug:
try: dmsg(b.decode())
except: dmsg(repr(b))
if not binary: a = a.decode()
retcode = p.wait()
if retcode != 0:
@ -460,20 +457,6 @@ class MMGenToolTestCmds(object):
tu.run_cmd(name,[])
ok()
# RPC
def addrfile_chksum(self,name):
fn = os.path.join(cfg['refdir'],ref_subdir,cfg['addrfile'].format(altcoin_pfx,tn_ext))
tu.run_cmd_out(name,fn,literal=True,chkdata=cfg['addrfile_chk'][g.coin.lower()][g.testnet])
def getbalance(self,name):
tu.run_cmd_out(name,literal=True)
def listaddresses(self,name):
tu.run_cmd_out(name,literal=True)
def twview(self,name):
tu.run_cmd_out(name,literal=True)
def txview(self,name):
fn = os.path.join(cfg['refdir'],ref_subdir,cfg['txfile'][g.coin.lower()][bool(tn_ext)])
tu.run_cmd_out(name,fn,literal=True)
# main()
import time
start_time = int(time.time())

View file

@ -20,6 +20,9 @@
test/tooltest2.py: Simple tests for the 'mmgen-tool' utility
"""
# TODO: move all non-interactive 'mmgen-tool' tests in 'test.py' here
# TODO: move all(?) tests in 'tooltest.py' here (or duplicate them?)
import sys,os,time
from subprocess import Popen,PIPE
from decimal import Decimal
@ -32,22 +35,27 @@ os.environ['MMGEN_TEST_SUITE'] = '1'
# Import these _after_ prepending repo_root to sys.path
from mmgen.common import *
from mmgen.test import *
from mmgen.obj import is_wif,is_coin_addr
opts_data = lambda: {
'desc': "Simple test suite for the 'mmgen-tool' utility",
'usage':'[options] [command]',
'options': """
-h, --help Print this help message
-C, --coverage Produce code coverage info using trace module
--, --longhelp Print help message for long options (common options)
-l, --list-tests List the tests in this test suite
-h, --help Print this help message
-C, --coverage Produce code coverage info using trace module
-d, --coin-dependent Run only coin-dependent tests
-D, --non-coin-dependent Run only non-coin-dependent tests
--, --longhelp Print help message for long options (common options)
-l, --list-tests List the tests in this test suite
-L, --list-tested-cmds Output the 'mmgen-tool' commands that are tested by this test suite
-n, --names Print command names instead of descriptions
-s, --system Test scripts and modules installed on system rather than
those in the repo root
-f, --fork Run commands via tool executable instead of importing tool module
-t, --traceback Run tool inside traceback script
-v, --verbose Produce more verbose output
-n, --names Print command names instead of descriptions
-q, --quiet Produce quieter output
-s, --system Test scripts and modules installed on system rather than
those in the repo root
-t, --type= Specify coin type
-f, --fork Run commands via tool executable instead of importing tool module
-t, --traceback Run tool inside traceback script
-v, --verbose Produce more verbose output
""",
'notes': """
@ -58,101 +66,223 @@ If no command is given, the whole suite of tests is run.
tests = (
('util', 'base conversion, hashing and file utilities',
(
('b58chktohex','conversion from base58chk to hex',
[ ( ['eFGDJPketnz'], 'deadbeef' ),
( ['5CizhNNRPYpBjrbYX'], 'deadbeefdeadbeef' ),
( ['5qCHTcgbQwprzjWrb'], 'ffffffffffffffff' ),
( ['111111114FCKVB'], '0000000000000000' ),
( ['3QJmnh'], '' ),
( ['1111111111111111111114oLvT2'], '000000000000000000000000000000000000000000' ),
]),
('hextob58chk','conversion from hex to base58chk',
[ ( ['deadbeef'], 'eFGDJPketnz' ),
( ['deadbeefdeadbeef'], '5CizhNNRPYpBjrbYX' ),
( ['ffffffffffffffff'], '5qCHTcgbQwprzjWrb' ),
( ['0000000000000000'], '111111114FCKVB' ),
( [''], '3QJmnh' ),
( ['000000000000000000000000000000000000000000'], '1111111111111111111114oLvT2' ),
]),
('bytespec',"conversion of 'dd'-style byte specifier to bytes",
[ ( ['1G'], str(1024*1024*1024) ),
( ['1234G'], str(1234*1024*1024*1024) ),
( ['1GB'], str(1000*1000*1000) ),
( ['1234GB'], str(1234*1000*1000*1000) ),
( ['1.234MB'], str(1234*1000) ),
( ['1.234567M'], str(int(Decimal('1.234567')*1024*1024)) ),
( ['1234'], str(1234) ),
]),
('b58chktohex','conversion from base58chk to hex', [
( ['eFGDJPketnz'], 'deadbeef' ),
( ['5CizhNNRPYpBjrbYX'], 'deadbeefdeadbeef' ),
( ['5qCHTcgbQwprzjWrb'], 'ffffffffffffffff' ),
( ['111111114FCKVB'], '0000000000000000' ),
( ['3QJmnh'], '' ),
( ['1111111111111111111114oLvT2'], '000000000000000000000000000000000000000000' ),
]),
('hextob58chk','conversion from hex to base58chk', [
( ['deadbeef'], 'eFGDJPketnz' ),
( ['deadbeefdeadbeef'], '5CizhNNRPYpBjrbYX' ),
( ['ffffffffffffffff'], '5qCHTcgbQwprzjWrb' ),
( ['0000000000000000'], '111111114FCKVB' ),
( [''], '3QJmnh' ),
( ['000000000000000000000000000000000000000000'], '1111111111111111111114oLvT2' ),
]),
('bytespec',"conversion of 'dd'-style byte specifier to bytes", [
( ['1G'], str(1024*1024*1024) ),
( ['1234G'], str(1234*1024*1024*1024) ),
( ['1GB'], str(1000*1000*1000) ),
( ['1234GB'], str(1234*1000*1000*1000) ),
( ['1.234MB'], str(1234*1000) ),
( ['1.234567M'], str(int(Decimal('1.234567')*1024*1024)) ),
( ['1234'], str(1234) ),
]),
),
),
('wallet', 'MMGen wallet operations',
(
('gen_key','generation of single key from wallet',
[ ( ['98831F3A:11','wallet=test/ref/98831F3A.mmwords'],
'5JKLcdYbhP6QQ4BXc9HtjfqJ79FFRXP2SZTKUyEuyXJo9QSFUkv'
),
( ['98831F3A:C:11','wallet=test/ref/98831F3A.mmwords'],
'L2LwXv94XTU2HjCbJPXCFuaHjrjucGipWPWUi1hkM5EykgektyqR'
),
( ['98831F3A:B:11','wallet=test/ref/98831F3A.mmwords'],
'L2K4Y9MWb5oUfKKZtwdgCm6FLZdUiWJDHjh9BYxpEvtfcXt4iM5g'
),
( ['98831F3A:S:11','wallet=test/ref/98831F3A.mmwords'],
'KwmkkfC9GghnJhnKoRXRn5KwGCgXrCmDw6Uv83NzE4kJS5axCR9A'
),]),
('gen_addr','generation of single address from wallet',
[ ( ['98831F3A:11','wallet=test/ref/98831F3A.mmwords'],
'12bYUGXS8SRArZneQDN9YEEYAtEa59Rykm'
),
( ['98831F3A:L:11','wallet=test/ref/98831F3A.mmwords'],
'12bYUGXS8SRArZneQDN9YEEYAtEa59Rykm'
),
( ['98831F3A:C:11','wallet=test/ref/98831F3A.mmwords'],
'1MPsZ7BY9qikqfPxqmrovE8gLDX2rYArZk'
),
( ['98831F3A:B:11','wallet=test/ref/98831F3A.mmwords'],
'bc1qxptlvmwaymaxa7pxkr2u5pn7c0508stcncv7ms'
),
( ['98831F3A:S:11','wallet=test/ref/98831F3A.mmwords'],
'3Eevao3DRVXnYym3tdrJDqS3Wc39PQzahn'
),]),
('gen_key','generation of single key from wallet', [
( ['98831F3A:11','wallet=test/ref/98831F3A.mmwords'],
'5JKLcdYbhP6QQ4BXc9HtjfqJ79FFRXP2SZTKUyEuyXJo9QSFUkv'
),
( ['98831F3A:C:11','wallet=test/ref/98831F3A.mmwords'],
'L2LwXv94XTU2HjCbJPXCFuaHjrjucGipWPWUi1hkM5EykgektyqR'
),
( ['98831F3A:B:11','wallet=test/ref/98831F3A.mmwords'],
'L2K4Y9MWb5oUfKKZtwdgCm6FLZdUiWJDHjh9BYxpEvtfcXt4iM5g'
),
( ['98831F3A:S:11','wallet=test/ref/98831F3A.mmwords'],
'KwmkkfC9GghnJhnKoRXRn5KwGCgXrCmDw6Uv83NzE4kJS5axCR9A'
),
]),
('gen_addr','generation of single address from wallet', [
( ['98831F3A:11','wallet=test/ref/98831F3A.mmwords'],
'12bYUGXS8SRArZneQDN9YEEYAtEa59Rykm'
),
( ['98831F3A:L:11','wallet=test/ref/98831F3A.mmwords'],
'12bYUGXS8SRArZneQDN9YEEYAtEa59Rykm'
),
( ['98831F3A:C:11','wallet=test/ref/98831F3A.mmwords'],
'1MPsZ7BY9qikqfPxqmrovE8gLDX2rYArZk'
),
( ['98831F3A:B:11','wallet=test/ref/98831F3A.mmwords'],
'bc1qxptlvmwaymaxa7pxkr2u5pn7c0508stcncv7ms'
),
( ['98831F3A:S:11','wallet=test/ref/98831F3A.mmwords'],
'3Eevao3DRVXnYym3tdrJDqS3Wc39PQzahn'
),
]),
),
),
('cryptocoin', 'coin-dependent utilities',
(
('randwif','random WIF key', {
'btc_mainnet': [ ( [], is_wif, ['-r0'] ) ],
'btc_testnet': [ ( [], is_wif, ['-r0'] ) ],
}),
('randpair','random key/address pair', {
'btc_mainnet': [ ( [], [is_wif,is_coin_addr], ['-r0'] ) ],
'btc_testnet': [ ( [], [is_wif,is_coin_addr], ['-r0'] ) ],
}),
('wif2addr','WIF-to-address conversion', {
'btc_mainnet': [
( ['5JKLcdYbhP6QQ4BXc9HtjfqJ79FFRXP2SZTKUyEuyXJo9QSFUkv'],
'12bYUGXS8SRArZneQDN9YEEYAtEa59Rykm', ['--type=legacy'], 'opt.type="legacy"' ),
( ['L2LwXv94XTU2HjCbJPXCFuaHjrjucGipWPWUi1hkM5EykgektyqR'],
'1MPsZ7BY9qikqfPxqmrovE8gLDX2rYArZk', ['--type=compressed'], 'opt.type="compressed"' ),
( ['KwmkkfC9GghnJhnKoRXRn5KwGCgXrCmDw6Uv83NzE4kJS5axCR9A'],
'3Eevao3DRVXnYym3tdrJDqS3Wc39PQzahn', ['--type=segwit'], 'opt.type="segwit"' ),
( ['L2K4Y9MWb5oUfKKZtwdgCm6FLZdUiWJDHjh9BYxpEvtfcXt4iM5g'],
'bc1qxptlvmwaymaxa7pxkr2u5pn7c0508stcncv7ms', ['--type=bech32'], 'opt.type="bech32"' ),
],
}),
),
),
# TODO: compressed address files are missing
# 'addrfile_compressed_chk': {
# 'btc': ('A33C 4FDE F515 F5BC','6C48 AA57 2056 C8C8'),
# 'ltc': ('3FC0 8F03 C2D6 BD19','4C0A 49B6 2DD1 1BE0'),
('file', 'Operations with MMGen files',
(
('addrfile_chksum','address file checksums', {
'btc_mainnet': [
( ['test/ref/98831F3A[1,31-33,500-501,1010-1011].addrs'],
'6FEF 6FB9 7B13 5D91'),
( ['test/ref/98831F3A-S[1,31-33,500-501,1010-1011].addrs'],
'06C1 9C87 F25C 4EE6'),
( ['test/ref/98831F3A-B[1,31-33,500-501,1010-1011].addrs'],
'9D2A D4B6 5117 F02E'),
],
'btc_testnet': [
( ['test/ref/98831F3A[1,31-33,500-501,1010-1011].testnet.addrs'],
'424E 4326 CFFE 5F51'),
( ['test/ref/98831F3A-S[1,31-33,500-501,1010-1011].testnet.addrs'],
'072C 8B07 2730 CB7A'),
( ['test/ref/98831F3A-B[1,31-33,500-501,1010-1011].testnet.addrs'],
'0527 9C39 6C1B E39A'),
],
'ltc_mainnet': [
( ['test/ref/litecoin/98831F3A-LTC[1,31-33,500-501,1010-1011].addrs'],
'AD52 C3FE 8924 AAF0'),
( ['test/ref/litecoin/98831F3A-LTC-S[1,31-33,500-501,1010-1011].addrs'],
'63DF E42A 0827 21C3'),
( ['test/ref/litecoin/98831F3A-LTC-B[1,31-33,500-501,1010-1011].addrs'],
'FF1C 7939 5967 AB82'),
],
'ltc_testnet': [
( ['test/ref/litecoin/98831F3A-LTC[1,31-33,500-501,1010-1011].testnet.addrs'],
'4EBE 2E85 E969 1B30'),
( ['test/ref/litecoin/98831F3A-LTC-S[1,31-33,500-501,1010-1011].testnet.addrs'],
'5DD1 D186 DBE1 59F2'),
( ['test/ref/litecoin/98831F3A-LTC-B[1,31-33,500-501,1010-1011].testnet.addrs'],
'ED3D 8AA4 BED4 0B40'),
],
'zec_mainnet': [
( ['test/ref/zcash/98831F3A-ZEC-C[1,31-33,500-501,1010-1011].addrs'],'903E 7225 DD86 6E01'), ],
'zec_z_mainnet': [
( ['test/ref/zcash/98831F3A-ZEC-Z[1,31-33,500-501,1010-1011].addrs'],'9C7A 72DC 3D4A B3AF'), ],
'xmr_mainnet': [
( ['test/ref/monero/98831F3A-XMR-M[1,31-33,500-501,1010-1011].addrs'],'4369 0253 AC2C 0E38'), ],
'dash_mainnet': [
( ['test/ref/dash/98831F3A-DASH-C[1,31-33,500-501,1010-1011].addrs'],'FBC1 6B6A 0988 4403'), ],
'eth_mainnet': [
( ['test/ref/ethereum/98831F3A-ETH[1,31-33,500-501,1010-1011].addrs'],'E554 076E 7AF6 66A3'), ],
'etc_mainnet': [
( ['test/ref/ethereum_classic/98831F3A-ETC[1,31-33,500-501,1010-1011].addrs'],
'E97A D796 B495 E8BC'), ],
}),
('txview','transaction file view', {
'btc_mainnet': [ ( ['test/ref/0B8D5A[15.31789,14,tl=1320969600].rawtx'], None ), ],
'btc_testnet': [ ( ['test/ref/0C7115[15.86255,14,tl=1320969600].testnet.rawtx'], None ), ],
'bch_mainnet': [ ( ['test/ref/460D4D-BCH[10.19764,tl=1320969600].rawtx'], None ), ],
'bch_testnet': [ ( ['test/ref/359FD5-BCH[6.68868,tl=1320969600].testnet.rawtx'], None ), ],
'ltc_mainnet': [ ( ['test/ref/litecoin/AF3CDF-LTC[620.76194,1453,tl=1320969600].rawtx'], None ), ],
'ltc_testnet': [ ( ['test/ref/litecoin/A5A1E0-LTC[1454.64322,1453,tl=1320969600].testnet.rawtx'],
None ), ],
'eth_mainnet': [ ( ['test/ref/ethereum/88FEFD-ETH[23.45495,40000].rawtx'], None ), ],
'eth_testnet': [ ( ['test/ref/ethereum/B472BD-ETH[23.45495,40000].testnet.rawtx'], None ), ],
'mm1_mainnet': [ ( ['test/ref/ethereum/5881D2-MM1[1.23456,50000].rawtx'], None ), ],
'mm1_testnet': [ ( ['test/ref/ethereum/6BDB25-MM1[1.23456,50000].testnet.rawtx'], None ), ],
'etc_mainnet': [ ( ['test/ref/ethereum_classic/ED3848-ETC[1.2345,40000].rawtx'], None ), ],
}),
),
),
)
def do_cmd(cdata):
cmd_name,desc,data = cdata
m = 'Testing {}'.format(cmd_name if opt.names else desc)
if type(data) == dict:
if opt.non_coin_dependent: return
k = '{}_{}net'.format((g.token.lower() if g.token else g.coin.lower()),('main','test')[g.testnet])
if k in data:
data = data[k]
m2 = ' ({})'.format(k)
else:
msg("-- no data for {} ({}) - skipping".format(cmd_name,k))
return
else:
if opt.coin_dependent: return
m2 = ''
m = '{} {}{}'.format(cyan('Testing'),cmd_name if opt.names else desc,m2)
msg_r(green(m)+'\n' if opt.verbose else m)
for args,out in data:
for d in data:
args,out,opts,exec_code = d + tuple([None] * (4-len(d)))
if opt.fork:
cmd = list(tool_cmd) + [cmd_name] + args
cmd = list(tool_cmd) + (opts or []) + [cmd_name] + args
vmsg('{} {}'.format(green('Executing'),cyan(' '.join(cmd))))
p = Popen(cmd,stdout=PIPE,stderr=PIPE)
cmd_out = p.stdout.read()
if type(out) != bytes:
cmd_out = cmd_out.strip().decode()
cmd_err = p.stderr.read()
if cmd_err: vmsg(cmd_err.strip().decode())
if p.wait() != 0:
die(1,'Spawned program exited with error')
else:
vmsg('{}: {}'.format(purple('Running'),' '.join([cmd_name]+args)))
if exec_code: exec(exec_code)
aargs,kwargs = tool._process_args(cmd_name,args)
cmd_out = tool._get_result(getattr(tc,cmd_name)(*aargs,**kwargs))
if type(out) == str:
oq_save = opt.quiet
if not opt.verbose: opt.quiet = True
cmd_out = tool._process_result(getattr(tc,cmd_name)(*aargs,**kwargs))
opt.quiet = oq_save
if type(out) != bytes:
cmd_out = cmd_out.strip()
if opt.fork:
cmd_out = cmd_out.decode()
vmsg('Output: {}\n'.format(cmd_out))
else:
vmsg('Output: {}\n'.format(repr(cmd_out)))
assert cmd_out == out,"Output ({}) doesn't match expected output ({})".format(cmd_out,out)
if type(out).__name__ == 'function':
assert out(cmd_out),"{}({}) failed!".format(out.__name__,cmd_out)
elif type(out) == list and type(out[0]).__name__ == 'function':
for i in range(len(out)):
s = cmd_out.split('\n')[i]
assert out[i](s),"{}({}) failed!".format(out[i].__name__,s)
elif out is not None:
assert cmd_out == out,"Output ({}) doesn't match expected output ({})".format(cmd_out,out)
if not opt.verbose: msg_r('.')
if not opt.verbose:
msg('OK')
def do_group(garg):
gid,gdesc,gdata = garg
msg(blue("Testing {}".format("command group '{}'".format(gid) if opt.names else gdesc)))
qmsg(blue("Testing {}".format("command group '{}'".format(gid) if opt.names else gdesc)))
for cdata in gdata:
do_cmd(cdata)
@ -193,6 +323,11 @@ else:
if opt.fork:
tool_cmd = (tool_exec,'--skip-cfg-file')
passthru_args = ['coin','type','testnet','token']
tool_cmd += tuple(['--{}{}'.format(k.replace('_','-'),
'='+getattr(opt,k) if getattr(opt,k) != True else ''
) for k in passthru_args if getattr(opt,k)])
if opt.traceback:
tool_cmd = (os.path.join('scripts','traceback_run.py'),) + tool_cmd
@ -202,7 +337,7 @@ if opt.fork:
elif g.platform == 'win':
tool_cmd = ('python3') + tool_cmd
else:
opt.quiet = True
opt.usr_randchars = 0
import mmgen.tool as tool
tc = tool.MMGenToolCmd()