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:
parent
26ba8baa97
commit
729a547c7d
11 changed files with 890 additions and 661 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
|
|
@ -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'])
|
||||
|
|
|
|||
855
mmgen/tool.py
855
mmgen/tool.py
File diff suppressed because it is too large
Load diff
28
mmgen/tw.py
28
mmgen/tw.py
|
|
@ -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)])
|
||||
|
|
|
|||
12
mmgen/tx.py
12
mmgen/tx.py
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
86
test/test.py
86
test/test.py
|
|
@ -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'])
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue