diff --git a/mmgen/altcoin.py b/mmgen/altcoin.py index 80bb7267..0c20fd64 100755 --- a/mmgen/altcoin.py +++ b/mmgen/altcoin.py @@ -510,16 +510,79 @@ class CoinInfo(object): return tt trust_override = {'BTC':3,'BCH':3,'LTC':3,'DASH':1,'EMC':2} + + @classmethod + def get_test_support(cls,coin,addr_type,network,tool=None,verbose=False): + """ + If requested tool supports coin/addr_type/network triplet, return tool name. + If 'tool' is None, return tool that supports coin/addr_type/network triplet. + Return None on failure. + """ + tool_arg = tool + all_tools = [tool] if tool else list(cls.external_tests[network].keys()) + coin = coin.upper() + + for tool in all_tools: + if coin in cls.external_tests[network][tool]: + break + else: + if verbose: + m1 = 'Requested tool {t!r} does not support coin {c} on network {n}' + m2 = 'No test tool found for coin {c} on network {n}' + m = m1 if tool_arg else m2 + msg(m.format(t=tool,c=coin,n=network)) + return None + + if addr_type == 'zcash_z': + if tool_arg in (None,'zcash-mini'): + return 'zcash-mini' + else: + if verbose: + m = "Address type {a!r} supported only by tool 'zcash-mini'" + msg(m.format(a=addr_type)) + return None + + try: + assert ( + cls.external_tests_blacklist[addr_type][tool] == True + or coin in cls.external_tests_blacklist[addr_type][tool] ) + except: + pass + else: + if verbose: + m = 'Tool {t!r} blacklisted for coin {c}, addr_type {a!r}' + msg(m.format(t=tool,c=coin,a=addr_type)) + return None + + if tool_arg: # skip whitelists + return tool + + if addr_type in ('segwit','bech32'): + st = cls.external_tests_segwit_whitelist + if addr_type in st and coin in st[addr_type]: + return tool + else: + if verbose: + m1 = 'Requested tool {t!r} does not support coin {c}, addr_type {a!r}, on network {n}' + m2 = 'No test tool found supporting coin {c}, addr_type {a!r}, on network {n}' + m = m1 if tool_arg else m2 + msg(m.format(t=tool,c=coin,n=network,a=addr_type)) + return None + + return tool + external_tests = { 'mainnet': { - 'moneropy': ('XMR',), + # List in order of preference. + # If 'tool' is not specified, the first tool supporting the coin will be selected. 'pycoin': ( - # broken: DASH - only compressed, LTC segwit old fmt + 'DASH', # only compressed 'BTC','LTC','VIA','FTC','DOGE','MEC', 'JBS','MZC','RIC','DFC','FAI','ARG','ZEC','DCR'), 'ethkey': ('ETH','ETC'), - 'zcash_mini': ('ZEC',), - 'keyconv': ( # all supported by vanitygen-plus 'keyconv' util + 'zcash-mini': ('ZEC',), + 'moneropy': ('XMR',), + 'keyconv': ( # broken: PIVX '42','AC','AIB','ANC','ARS','ATMOS','AUR','BLK','BQC','BTC','TEST','BTCD','CCC','CCN','CDN', 'CLAM','CNC','CNOTE','CON','CRW','DEEPONION','DGB','DGC','DMD','DOGED','DOGE','DOPE', @@ -531,16 +594,26 @@ class CoinInfo(object): }, 'testnet': { 'pycoin': { - # broken: DASH - only compressed { 'DASH':'tDASH' } + 'DASH':'tDASH', # only compressed 'BTC':'XTN','LTC':'XLT','VIA':'TVI','FTC':'FTX','DOGE':'XDT','DCR':'DCRT' }, 'ethkey': {}, 'keyconv': {} } } - external_tests_segwit_compressed = { - 'segwit': ('BTC'), + external_tests_segwit_whitelist = { + # Whitelists apply to the *first* tool in cls.external_tests supporting the given coin/addr_type. + # They're ignored if specific tool is requested. + 'segwit': ('BTC',), # LTC Segwit broken on pycoin: uses old fmt + 'bech32': ('BTC','LTC'), 'compressed': ( - 'BTC','LTC','VIA','FTC','DOGE','DASH','MEC','MYR','UNO', - 'JBS','MZC','RIC','DFC','FAI','ARG','ZEC','DCR','ZEC'), + 'BTC','LTC','VIA','FTC','DOGE','DASH','MEC','MYR','UNO', + 'JBS','MZC','RIC','DFC','FAI','ARG','ZEC','DCR','ZEC' + ), + } + external_tests_blacklist = { + # Unconditionally block testing of the given coin/addr_type with given tool, or all coins if True + 'legacy': { 'pycoin': ('DASH',) }, + 'segwit': { 'pycoin': ('LTC',), 'keyconv': True }, + 'bech32': { 'keyconv': True }, } diff --git a/test/gentest.py b/test/gentest.py index 6e2fad44..57c77191 100755 --- a/test/gentest.py +++ b/test/gentest.py @@ -28,53 +28,82 @@ os.environ['MMGEN_TEST_SUITE'] = '1' # Import these _after_ local path's been added to sys.path from mmgen.common import * -from mmgen.obj import MMGenAddrType rounds = 100 opts_data = { 'text': { - 'desc': 'Test address generation in various ways', + 'desc': 'Test address generation of the MMGen suite in various ways', 'usage':'[options] [spec] [rounds | dump file]', 'options': """ -h, --help Print this help message --a, --all Test all supported coins for external generator 'ext' +-a, --all Test all coins supported by specified external tool -k, --use-internal-keccak-module Force use of the internal keccak module --, --longhelp Print help message for long options (common options) -q, --quiet Produce quieter output --t, --type=t Specify address type (valid options: 'compressed','segwit','zcash_z') +-t, --type=t Specify address type (e.g. 'compressed','segwit','zcash_z','bech32') -v, --verbose Produce more verbose output """, 'notes': """ - Tests: - A/B: {prog} a:b [rounds] (compare output of two key generators) - Speed: {prog} a [rounds] (test speed of one key generator) - Compare: {prog} a (compare output of a key generator against wallet dump) - where a and b are one of: - '1' - native Python ecdsa library (very slow) - '2' - bitcoincore.org's secp256k1 library (default from v0.8.6) +TEST TYPES: + + A/B: {prog} A:B [rounds] (compare key generators A and B) + Speed: {prog} A [rounds] (test speed of key generator A) + Compare: {prog} A (compare generator A to wallet dump) + + where A and B are one of: + '1' - native Python ECDSA library (slow), or + '2' - bitcoincore.org's libsecp256k1 library (default); + or: + B is name of an external tool (see below) or 'ext'. + If B is 'ext', the external tool will be chosen automatically. EXAMPLES: - {prog} 1:2 100 - (compare output of native Python ECDSA with secp256k1 library, 100 rounds) - {prog} 2:ext 100 - (compare output of secp256k1 library with external library (see below), 100 rounds) - {prog} 2 1000 - (test speed of secp256k1 library address generation, 1000 rounds) - {prog} 2 my.dump - (compare addrs generated with secp256k1 library to {dn} wallet dump) - External libraries required for the 'ext' generator: - + ethkey (for ETH,ETC) https://github.com/paritytech/parity-ethereum - + zcash-mini (for zcash_z addresses) https://github.com/FiloSottile/zcash-mini - + moneropy (for Monero addresses) https://github.com/bigreddmachine/MoneroPy - + pycoin (for supported coins) https://github.com/richardkiss/pycoin - + keyconv (for all other coins) https://github.com/exploitagency/vanitygen-plus - ('keyconv' generates uncompressed addresses only) + Compare addresses generated by native Python ECDSA library and libsecp256k1, + 100 rounds: + $ {prog} 1:2 100 + + Compare mmgen-secp256k1 Segwit address generation to pycoin library for all + supported coins, 100 rounds: + $ {prog} --all --type=segwit 2:pycoin 100 + + Compare mmgen-secp256k1 address generation to keyconv tool for all + supported coins, 100 rounds: + $ {prog} --all --type=compressed 2:keyconv 100 + + Compare mmgen-secp256k1 XMR address generation to configured external tool, + 10 rounds: + $ {prog} --coin=xmr 2:ext 10 + + Test speed of mmgen-secp256k1 address generation, 10,000 rounds: + $ {prog} 2 10000 + + Compare mmgen-secp256k1-generated bech32 addrs to {dn} wallet dump: + $ {prog} --type=bech32 2 bech32wallet.dump + +Supported external tools: + + + ethkey (for ETH,ETC) + https://github.com/paritytech/parity-ethereum + (build with 'cargo build -p ethkey-cli --release') + + + zcash-mini (for Zcash Z-addresses) + https://github.com/FiloSottile/zcash-mini + + + moneropy (for Monero addresses) + https://github.com/bigreddmachine/MoneroPy + + + pycoin (for supported coins) + https://github.com/richardkiss/pycoin + + + keyconv (for supported coins) + https://github.com/exploitagency/vanitygen-plus + ('keyconv' does not generate Segwit addresses) """ }, 'code': { 'notes': lambda s: s.format( - prog='gentest.py', + prog='test/gentest.py', pnm=g.proj_name, snum=rounds, dn=g.proto.daemon_name) @@ -85,98 +114,120 @@ sys.argv = [sys.argv[0]] + ['--skip-cfg-file'] + sys.argv[1:] cmd_args = opts.init(opts_data,add_opts=['exact_output','use_old_ed25519']) -if not 1 <= len(cmd_args) <= 2: opts.usage() - -addr_type = MMGenAddrType(opt.type or g.proto.dfl_mmtype) - -from collections import namedtuple -ep = namedtuple('external_prog_output',['wif','addr','vk']) +if not 1 <= len(cmd_args) <= 2: + opts.usage() from subprocess import run,PIPE,DEVNULL def get_cmd_output(cmd,input=None): return run(cmd,input=input,stdout=PIPE,stderr=DEVNULL).stdout.decode().splitlines() -def ethkey_sec2addr(sec): - o = get_cmd_output(['ethkey','info',sec]) - return ep(o[0].split()[1],o[-1].split()[1],None) +from collections import namedtuple +gtr = namedtuple('gen_tool_result',['wif','addr','vk']) -def keyconv_sec2addr(sec): - o = get_cmd_output(['keyconv','-C',g.coin,sec.wif]) - return ep(o[1].split()[1],o[0].split()[1],None) +class GenTool(object): pass -def zcash_mini_sec2addr(sec): - o = get_cmd_output(['zcash-mini','-key','-simple'],input=(sec.wif+'\n').encode()) - return ep(o[1],o[0],o[-1]) +class GenToolEthkey(GenTool): + desc = 'ethkey' + def __init__(self): + init_coin('eth') + global addr_type + addr_type = MMGenAddrType('E') -def pycoin_sec2addr(sec): - coin = ci.external_tests['testnet']['pycoin'][g.coin] if g.testnet else g.coin - network = network_for_netcode(coin) - key = network.keys.private(secret_exponent=int(sec,16),is_compressed=addr_type.name != 'legacy') - if key is None: - die(1,"can't parse {}".format(sec)) - if addr_type.name in ('segwit','bech32'): - hash160_c = key.hash160(is_compressed=True) - if addr_type.name == 'segwit': - p2sh_script = network.contract.for_p2pkh_wit(hash160_c) - addr = network.address.for_p2s(p2sh_script) - else: - addr = network.address.for_p2pkh_wit(hash160_c) - else: - addr = key.address() - return ep(key.wif(),addr,None) + def run(self,sec): + o = get_cmd_output(['ethkey','info',sec]) + return gtr(o[0].split()[1],o[-1].split()[1],None) -def moneropy_sec2addr(sec): - sk_t,vk_t,addr_t = mp_acc.account_from_spend_key(sec) # VERY slow! - return ep(sk_t,addr_t,vk_t) +class GenToolKeyconv(GenTool): + desc = 'keyconv' + def run(self,sec): + o = get_cmd_output(['keyconv','-C',g.coin,sec.wif]) + return gtr(o[1].split()[1],o[0].split()[1],None) -# pycoin/networks/all.py pycoin/networks/legacy_networks.py -def init_external_prog(): - global b,b_desc,ext_prog,ext_sec2addr,eth,addr_type - - def test_support(k): - if b == k: return True - if b != 'ext' and b != k: return False - if g.coin in ci.external_tests['mainnet'][k] and not g.testnet: return True - if g.coin in ci.external_tests['testnet'][k]: return True - return False - - if b == 'zcash_mini' or addr_type.name == 'zcash_z': - ext_sec2addr = zcash_mini_sec2addr - ext_prog = 'zcash_mini' +class GenToolZcash_mini(GenTool): + desc = 'zcash-mini' + def __init__(self): init_coin('zec') + global addr_type addr_type = MMGenAddrType('Z') - elif test_support('ethkey'): # build with 'cargo build -p ethkey-cli --release' - ext_sec2addr = ethkey_sec2addr - ext_prog = 'ethkey' - elif test_support('pycoin'): - global network_for_netcode + + def run(self,sec): + o = get_cmd_output(['zcash-mini','-key','-simple'],input=(sec.wif+'\n').encode()) + return gtr(o[1],o[0],o[-1]) + +class GenToolPycoin(GenTool): + """ + pycoin/networks/all.py pycoin/networks/legacy_networks.py + """ + desc = 'pycoin' + def __init__(self): + m = "Unable to import pycoin.networks.registry. Is pycoin installed on your system?" try: from pycoin.networks.registry import network_for_netcode except: - raise ImportError("Unable to import pycoin.networks.registry. Is pycoin installed on your system?") - ext_sec2addr = pycoin_sec2addr - ext_prog = 'pycoin' - elif test_support('moneropy'): - global mp_acc - try: - import moneropy.account as mp_acc - except: - raise ImportError("Unable to import moneropy. Is moneropy installed on your system?") - ext_sec2addr = moneropy_sec2addr - init_coin('xmr') - ext_prog = 'moneropy' - addr_type = MMGenAddrType('M') - elif test_support('keyconv'): - ext_sec2addr = keyconv_sec2addr - ext_prog = 'keyconv' - else: - m = '{}: coin supported by MMGen but unsupported by gentest.py for {}' - raise ValueError(m.format(g.coin,('mainnet','testnet')[g.testnet])) - b_desc = ext_prog - b = 'ext' + raise ImportError(m) + self.nfnc = network_for_netcode -def test_equal(a_addr,b_addr,in_bytes,sec,wif,a,b): - if a_addr != b_addr: + def run(self,sec): + coin = ci.external_tests['testnet']['pycoin'][g.coin] if g.testnet else g.coin + network = self.nfnc(coin) + key = network.keys.private(secret_exponent=int(sec,16),is_compressed=addr_type.name != 'legacy') + if key is None: + die(1,"can't parse {}".format(sec)) + if addr_type.name in ('segwit','bech32'): + hash160_c = key.hash160(is_compressed=True) + if addr_type.name == 'segwit': + p2sh_script = network.contract.for_p2pkh_wit(hash160_c) + addr = network.address.for_p2s(p2sh_script) + else: + addr = network.address.for_p2pkh_wit(hash160_c) + else: + addr = key.address() + return gtr(key.wif(),addr,None) + +class GenToolMoneropy(GenTool): + desc = 'moneropy' + def __init__(self): + + m = "Unable to import moneropy. Is moneropy installed on your system?" + try: + import moneropy.account + except: + raise ImportError(m) + + self.mpa = moneropy.account + init_coin('xmr') + + global addr_type + addr_type = MMGenAddrType('M') + + def run(self,sec): + sk_t,vk_t,addr_t = self.mpa.account_from_spend_key(sec) # VERY slow! + return gtr(sk_t,addr_t,vk_t) + +def get_tool(arg): + + if arg not in ext_progs + ['ext']: + die(1,'{!r}: unsupported tool for network {}'.format(arg,g.network)) + + if opt.all: + if arg == 'ext': + die(1,"'--all' must be combined with a specific external testing tool") + return arg + else: + tool = ci.get_test_support( + g.coin, + addr_type.name, + g.network, + verbose = not opt.quiet, + tool = arg if arg in ext_progs else None ) + if not tool: + sys.exit(2) + if arg in ext_progs and arg != tool: + sys.exit(3) + return tool + +def test_equal(desc,a_val,b_val,in_bytes,sec,wif,a_desc,b_desc): + if a_val != b_val: fs = """ {i:{w}}: {} {s:{w}}: {} @@ -185,32 +236,27 @@ def test_equal(a_addr,b_addr,in_bytes,sec,wif,a,b): {b:{w}}: {} """ die(3, - red('\nERROR: Values do not match!') + red('\nERROR: {} do not match!').format(desc) + fs.format( - in_bytes.hex(), sec, wif, a_addr, b_addr, - i='input', s='sec key', W='WIF key', a=kg_a.desc, b=b_desc, - w=max(len(e) for e in (kg_a.desc,b_desc)) + 1 + in_bytes.hex(), sec, wif, a_val, b_val, + i='input', s='sec key', W='WIF key', a=a_desc, b=b_desc, + w=max(len(e) for e in (a_desc,b_desc)) + 1 ).rstrip()) -def compare_test(): - for k in ('segwit','compressed'): - if b == 'ext' and addr_type.name == k and g.coin not in ci.external_tests_segwit_compressed[k]: - m = 'skipping - external program does not support {} for coin {}' - msg(m.format(addr_type.name.capitalize(),g.coin)) - return - if 'ext_prog' in globals(): - if g.coin not in ci.external_tests[('mainnet','testnet')[g.testnet]][ext_prog]: - msg("Coin '{}' incompatible with external generator '{}'".format(g.coin,ext_prog)) - return +def gentool_test(kg_a,kg_b,ag,rounds): + + m = "Comparing address generators '{A}' and '{B}' for {N} {c} ({n}), addrtype {a!r}" + e = ci.get_entry(g.coin,g.network) + qmsg(green(m.format( + A = kg_a.desc, + B = kg_b.desc, + N = g.network, + c = g.coin, + n = e.name if e else '---', + a = addr_type.name ))) + global last_t last_t = time.time() - A = kg_a.desc - B = b_desc - if A == B: - msg('skipping - generation methods A and B are the same ({})'.format(A)) - return - m = "Comparing address generators '{}' and '{}' for coin {}, addrtype {!r}" - qmsg(green(m.format(A,B,g.coin,addr_type.name))) def do_compare_test(n,trounds,in_bytes): global last_t @@ -221,24 +267,23 @@ def compare_test(): a_ph = kg_a.to_pubhex(sec) a_addr = ag.to_addr(a_ph) a_vk = None - if b == 'ext': - ret = ext_sec2addr(sec) - tinfo = (in_bytes,sec,sec.wif,a,ext_prog) - test_equal(sec.wif,ret.wif,*tinfo) - test_equal(a_addr,ret.addr,*tinfo) - if ret.vk: + tinfo = (in_bytes,sec,sec.wif,kg_a.desc,kg_b.desc) + if isinstance(kg_b,GenTool): + b = kg_b.run(sec) + test_equal('WIF keys',sec.wif,b.wif,*tinfo) + test_equal('addresses',a_addr,b.addr,*tinfo) + if b.vk: a_vk = ag.to_viewkey(a_ph) - test_equal(a_vk,ret.vk,*tinfo) + test_equal('view keys',a_vk,b.vk,*tinfo) else: b_addr = ag.to_addr(kg_b.to_pubhex(sec)) - tinfo = (in_bytes,sec,sec.wif,a,b) - test_equal(a_addr,b_addr,*tinfo) - vmsg(ct_fs.format(b=in_bytes.hex(),k=sec.wif,v=a_vk,a=a_addr)) + test_equal('addresses',a_addr,b_addr,*tinfo) + vmsg(fs.format(b=in_bytes.hex(),k=sec.wif,v=a_vk,a=a_addr)) qmsg_r('\rRound {}/{} '.format(n+1,trounds)) - ct_fs = ( '\ninput: {b}\n%-9s {k}\naddr: {a}\n', - '\ninput: {b}\n%-9s {k}\nviewkey: {v}\naddr: {a}\n')[ - 'viewkey' in addr_type.extra_attrs] % (addr_type.wif_label + ':') + fs = ( '\ninput: {b}\n%-9s {k}\naddr: {a}\n', + '\ninput: {b}\n%-9s {k}\nviewkey: {v}\naddr: {a}\n')[ + 'viewkey' in addr_type.extra_attrs] % (addr_type.wif_label + ':') # test some important private key edge cases: edgecase_sks = ( @@ -259,9 +304,9 @@ def compare_test(): do_compare_test(i,rounds,os.urandom(32)) qmsg(green('\rOK ' if opt.verbose else 'OK')) -def speed_test(): +def speed_test(kg,ag,rounds): m = "Testing speed of address generator '{}' for coin {}" - qmsg(green(m.format(kg_a.desc,g.coin))) + qmsg(green(m.format(kg.desc,g.coin))) from struct import pack,unpack seed = os.urandom(28) qmsg('Incrementing key with each round') @@ -274,101 +319,119 @@ def speed_test(): qmsg_r('\rRound {}/{} '.format(i+1,rounds)) last_t = time.time() sec = PrivKey(seed+pack('I',i),compressed=addr_type.compressed,pubkey_type=addr_type.pubkey_type) - a_addr = ag.to_addr(kg_a.to_pubhex(sec)) - vmsg('\nkey: {}\naddr: {}\n'.format(sec.wif,a_addr)) + addr = ag.to_addr(kg.to_pubhex(sec)) + vmsg('\nkey: {}\naddr: {}\n'.format(sec.wif,addr)) qmsg_r('\rRound {}/{} '.format(i+1,rounds)) qmsg('\n{} addresses generated in {:.2f} seconds'.format(rounds,time.time()-start)) -def dump_test(): - m = "Comparing output of address generator '{}' against wallet dump '{}'" - qmsg(green(m.format(kg_a.desc,cmd_args[1]))) - for n,[wif,a_addr] in enumerate(dump,1): - qmsg_r('\rKey {}/{} '.format(n,len(dump))) +def dump_test(kg,ag,fh): + + dump = [[*(e.split()[0] for e in line.split('addr='))] for line in fh.readlines() if 'addr=' in line] + if not dump: + die(1,'File {!r} appears not to be a wallet dump'.format(fh.name)) + + m = 'Comparing output of address generator {!r} against wallet dump {!r}' + qmsg(green(m.format(kg.desc,fh.name))) + + for count,(b_wif,b_addr) in enumerate(dump,1): + qmsg_r('\rKey {}/{} '.format(count,len(dump))) try: - sec = PrivKey(wif=wif) + b_sec = PrivKey(wif=b_wif) except: - die(2,'\nInvalid {}net WIF address in dump file: {}'.format(('main','test')[g.testnet],wif)) - b_addr = ag.to_addr(kg_a.to_pubhex(sec)) - vmsg('\nwif: {}\naddr: {}\n'.format(wif,b_addr)) - tinfo = (bytes.fromhex(sec),sec,wif,3,a) - test_equal(a_addr,b_addr,*tinfo) + die(2,'\nInvalid {} WIF address in dump file: {}'.format(g.network,b_wif)) + a_addr = ag.to_addr(kg.to_pubhex(b_sec)) + vmsg('\nwif: {}\naddr: {}\n'.format(b_wif,b_addr)) + tinfo = (bytes.fromhex(b_sec),b_sec,b_wif,kg.desc,fh.name) + test_equal('addresses',a_addr,b_addr,*tinfo) qmsg(green(('\n','')[bool(opt.verbose)] + 'OK')) +def init_tool(tname): + return globals()['GenTool'+capfirst(tname.replace('-','_'))]() + +def parse_arg1(arg,arg_id): + + m1 = 'First argument must be a numeric generator ID or two colon-separated generator IDs' + m2 = 'Second part of first argument must be a numeric generator ID or one of {}' + + def check_gen_num(n): + if not (1 <= int(n) <= len(g.key_generators)): + die(1,'{}: invalid generator ID'.format(n)) + return int(n) + + if arg_id == 'a': + if is_int(arg): + a_num = check_gen_num(arg) + return (KeyGenerator(addr_type,a_num),a_num) + else: + die(1,m1) + elif arg_id == 'b': + if is_int(arg): + return KeyGenerator(addr_type,check_gen_num(arg)) + elif arg in ext_progs + ['ext']: + return init_tool(get_tool(arg)) + else: + die(1,m2.format(ext_progs)) + +def parse_arg2(): + m = 'Second argument must be dump filename or integer rounds specification' + if len(cmd_args) == 1: + return None + arg = cmd_args[1] + if is_int(arg) and int(arg) > 0: + return int(arg) + try: + return open(arg) + except: + die(1,m) + # begin execution from mmgen.protocol import init_coin from mmgen.altcoin import CoinInfo as ci - -urounds,fh = None,None -dump = [] - -if len(cmd_args) == 2: - try: - urounds = int(cmd_args[1]) - assert urounds > 0 - except: - try: - fh = open(cmd_args[1]) - except: - die(1,'Second argument must be filename or positive integer') - else: - for line in fh.readlines(): - if 'addr=' in line: - x,addr = line.split('addr=') - dump.append([x.split()[0],addr.split()[0]]) - -if urounds: rounds = urounds - -a,b = None,None -b_desc = 'unknown' -try: - a,b = cmd_args[0].split(':') -except: - try: - a = cmd_args[0] - a = int(a) - assert 1 <= a <= len(g.key_generators) - except: - die(1,'First argument must be one or two generator IDs, colon separated') -else: - try: - a = int(a) - assert 1 <= a <= len(g.key_generators),'{}: invalid key generator'.format(a) - if b in ('ext','ethkey','pycoin','keyconv','zcash_mini','moneropy'): - init_external_prog() - else: - b = int(b) - assert 1 <= b <= len(g.key_generators),'{}: invalid key generator'.format(b) - assert a != b,'Key generators are the same!' - except Exception as e: - die(1,'{}\n{}: invalid generator argument'.format(e.args[0],cmd_args[0])) - +from mmgen.obj import MMGenAddrType,PrivKey from mmgen.addr import KeyGenerator,AddrGenerator -from mmgen.obj import PrivKey -kg_a = KeyGenerator(addr_type,a) +addr_type = MMGenAddrType(opt.type or g.proto.dfl_mmtype) +ext_progs = list(ci.external_tests[g.network]) + +arg1 = cmd_args[0].split(':') +if len(arg1) == 1: + a,a_num = parse_arg1(arg1[0],'a') + b = None +elif len(arg1) == 2: + a,a_num = parse_arg1(arg1[0],'a') + b = parse_arg1(arg1[1],'b') +else: + opts.usage() + +if type(a) == type(b): + die(1,'Address generators are the same!') + +arg2 = parse_arg2() + ag = AddrGenerator(addr_type) -if a and b: +if not b and type(arg2) == int: + speed_test(a,ag,arg2) +elif not b and hasattr(arg2,'read'): + dump_test(a,ag,arg2) +elif a and b and type(arg2) == int: if opt.all: from mmgen.protocol import init_genonly_altcoins,CoinProtocol init_genonly_altcoins() - for coin in ci.external_tests[('mainnet','testnet')[g.testnet]][ext_prog]: - if coin not in CoinProtocol.coins: continue + for coin in ci.external_tests[g.network][b.desc]: + if coin.lower() not in CoinProtocol.coins: +# ymsg('Coin {} not configured'.format(coin)) + continue init_coin(coin) if addr_type not in g.proto.mmtypes: - addr_type = MMGenAddrType(g.proto.dfl_mmtype) - kg_a = KeyGenerator(addr_type,a) + continue + # g.proto has changed, so reinit kg and ag just to be on the safe side: + a = KeyGenerator(addr_type,a_num) ag = AddrGenerator(addr_type) - compare_test() + b_chk = ci.get_test_support(g.coin,addr_type.name,g.network,tool=b.desc,verbose=not opt.quiet) + if b_chk == b.desc: + gentool_test(a,b,ag,arg2) else: - if b != 'ext': - kg_b = KeyGenerator(addr_type,b) - b_desc = kg_b.desc - compare_test() -elif a and not fh: - speed_test() -elif a and dump: - b_desc = 'dump' - dump_test() + gentool_test(a,b,ag,arg2) else: - die(2,'Illegal invocation') + opts.usage() diff --git a/test/test-release.sh b/test/test-release.sh index 9e7b0a09..5ca081f1 100755 --- a/test/test-release.sh +++ b/test/test-release.sh @@ -97,7 +97,7 @@ do mmgen_tool="$python $mmgen_tool" mmgen_keygen="$python $mmgen_keygen" ;& f) FAST=1 rounds=10 rounds_min=3 rounds_mid=25 rounds_max=50 monero_addrs='3,23' unit_tests_py+=" --fast" ;; - F) FAST=1 rounds=2 rounds_min=1 rounds_mid=3 rounds_max=5 monero_addrs='3,23' unit_tests_py+=" --fast" ;; + F) FAST=1 rounds=3 rounds_min=1 rounds_mid=3 rounds_max=5 monero_addrs='3,23' unit_tests_py+=" --fast" ;; i) INSTALL=1 ;; I) INSTALL_ONLY=1 ;; l) echo -e "Default tests:\n $dfl_tests" @@ -241,8 +241,6 @@ f_ref='Miscellaneous reference data tests completed' i_alts='Gen-only altcoin' s_alts='The following tests will test generation operations for all supported altcoins' t_alts=" - $gentest_py --all 2:keyconv $rounds_mid - # speed tests, no verification: $gentest_py --coin=btc 2 $rounds $gentest_py --coin=btc --type=compressed 2 $rounds @@ -260,25 +258,27 @@ t_alts=" $gentest_py --coin=xmr --use-internal-keccak-module 2 $rounds_min $gentest_py --coin=zec 2 $rounds $gentest_py --coin=zec --type=zcash_z 2 $rounds_mid + + # verification against external libraries and tools: + $gentest_py --all --type=legacy 2:keyconv $rounds + $gentest_py --all --type=compressed 2:keyconv $rounds + $gentest_py --all --coin=xmr 2:moneropy $rounds_min # very slow, be patient! " -# disabled, pycoin generates old-style LTC Segwit addrs: -# $gentest_py --coin=ltc --type=segwit 2:ext $rounds - -[ "$MSYS2" ] || { # no pycoin, zcash-mini +[ "$MSYS2" ] || { # no pycoin (libsecp256k1), zcash-mini (golang), ethkey (?) t_alts="$t_alts - # verification using external libraries and tools: $gentest_py --all --type=legacy 2:pycoin $rounds $gentest_py --all --type=compressed 2:pycoin $rounds - $gentest_py --coin=btc --type=segwit 2:ext $rounds - $gentest_py --coin=btc --type=bech32 2:ext $rounds - $gentest_py --coin=etc 2:ext $rounds - $gentest_py --coin=eth 2:ext $rounds + $gentest_py --all --type=segwit 2:pycoin $rounds + $gentest_py --all --type=bech32 2:pycoin $rounds + + $gentest_py --all --type=legacy --testnet=1 2:pycoin $rounds + $gentest_py --all --type=compressed --testnet=1 2:pycoin $rounds + $gentest_py --all --type=segwit --testnet=1 2:pycoin $rounds + $gentest_py --all --type=bech32 --testnet=1 2:pycoin $rounds + + $gentest_py --all 2:zcash-mini $rounds_mid $gentest_py --all 2:ethkey $rounds - $gentest_py --coin=zec 2:ext $rounds - $gentest_py --coin=zec --type=zcash_z 2:ext $rounds_mid - $gentest_py --all 2:zcash_mini $rounds_mid - $gentest_py --all 2:moneropy $rounds_mid # very slow, be patient! " }