rewrite test/gentest.py utility
- support for testing all configured generators in one go has also been added (via the 'all' subparameter)
This commit is contained in:
parent
3799411071
commit
b43d827b50
2 changed files with 270 additions and 220 deletions
484
test/gentest.py
484
test/gentest.py
|
|
@ -30,68 +30,88 @@ sys.path.insert(0,overlay_setup(repo_root))
|
|||
from mmgen.common import *
|
||||
from test.include.common import getrand
|
||||
|
||||
results_file = 'gentest.out.json'
|
||||
|
||||
rounds = 100
|
||||
opts_data = {
|
||||
'text': {
|
||||
'desc': 'Test key/address generation of the MMGen suite in various ways',
|
||||
'usage':'[options] [spec] [rounds | dump file]',
|
||||
'usage':'[options] <spec> <rounds | dump file>',
|
||||
'options': """
|
||||
-h, --help Print this help message
|
||||
-a, --all Test all coins supported by specified external tool
|
||||
-h, --help Print this help message
|
||||
--, --longhelp Print help message for long options (common options)
|
||||
-a, --all-coins 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 (e.g. 'compressed','segwit','zcash_z','bech32')
|
||||
-v, --verbose Produce more verbose output
|
||||
-q, --quiet Produce quieter output
|
||||
-s, --save-results Save output of external tool in Compare test to
|
||||
{rf!r}
|
||||
-t, --type=t Specify address type (e.g. 'compressed','segwit',
|
||||
'zcash_z','bech32')
|
||||
-v, --verbose Produce more verbose output
|
||||
""",
|
||||
'notes': """
|
||||
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 <dump file> (compare generator A to wallet dump)
|
||||
Compare: {prog} A:B <rounds> (compare address generators A and B)
|
||||
Speed: {prog} A <rounds> (test speed of generator A)
|
||||
Dump: {prog} A <dump file> (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.
|
||||
where:
|
||||
|
||||
A and B are keygen backend numbers ('1' being the default); or
|
||||
B is the name of an external tool (see below) or 'ext'.
|
||||
|
||||
If B is 'ext', the external tool will be chosen automatically.
|
||||
|
||||
For the Compare test, A may be 'all' to test all backends for the current
|
||||
coin/address type combination.
|
||||
|
||||
EXAMPLES:
|
||||
|
||||
Compare addresses generated by native Python ECDSA library and libsecp256k1,
|
||||
100 rounds:
|
||||
Compare addresses generated by 'libsecp256k1' and 'python-ecdsa' backends,
|
||||
with 100 random rounds plus private-key edge cases:
|
||||
$ {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 Segwit addresses from default 'libsecp256k1' backend to 'pycoin'
|
||||
library for all supported coins, 100 rounds + edge cases:
|
||||
$ {prog} --all-coins --type=segwit 1:pycoin 100
|
||||
|
||||
Compare mmgen-secp256k1 address generation to keyconv tool for all
|
||||
supported coins, 100 rounds:
|
||||
$ {prog} --all --type=compressed 2:keyconv 100
|
||||
Compare addresses from 'python-ecdsa' backend to output of 'keyconv' tool
|
||||
for all supported coins, 100 rounds + edge cases:
|
||||
$ {prog} --all-coins --type=compressed 2:keyconv 100
|
||||
|
||||
Compare mmgen-secp256k1 XMR address generation to configured external tool,
|
||||
10 rounds:
|
||||
$ {prog} --coin=xmr 2:ext 10
|
||||
Compare bech32 addrs from 'libsecp256k1' backend to Bitcoin Core wallet
|
||||
dump:
|
||||
$ {prog} --type=bech32 1 bech32wallet.dump
|
||||
|
||||
Test speed of mmgen-secp256k1 address generation, 10,000 rounds:
|
||||
$ {prog} 2 10000
|
||||
Compare addresses from Monero 'ed25519ll' backend to output of default
|
||||
external tool, 10 rounds + edge cases:
|
||||
$ {prog} --coin=xmr 3:ext 10
|
||||
|
||||
Compare mmgen-secp256k1-generated bech32 addrs to coin daemon wallet dump:
|
||||
$ {prog} --type=bech32 2 bech32wallet.dump
|
||||
Test the speed of default Monero 'nacl' backend, 10,000 rounds:
|
||||
$ test/gentest.py --coin=xmr 1 10000
|
||||
|
||||
Supported external tools:
|
||||
Same for Zcash:
|
||||
$ test/gentest.py --coin=zec --type=zcash_z 1 10000
|
||||
|
||||
Test all configured Monero backends against 'moneropy' library, 3 rounds
|
||||
+ edge cases:
|
||||
$ test/gentest.py --coin=xmr all:moneropy 3
|
||||
|
||||
Test 'nacl' and 'ed25519ll_djbec' backends against each other, 10,000 rounds
|
||||
+ edge cases:
|
||||
$ test/gentest.py --coin=xmr 1:2 10000
|
||||
|
||||
SUPPORTED EXTERNAL TOOLS:
|
||||
|
||||
+ ethkey (for ETH,ETC)
|
||||
https://github.com/openethereum/openethereum
|
||||
https://github.com/openethereum/openethereum
|
||||
(build with 'cargo build -p ethkey-cli --release')
|
||||
|
||||
+ zcash-mini (for Zcash Z-addresses)
|
||||
+ zcash-mini (for Zcash-Z addresses and view keys)
|
||||
https://github.com/FiloSottile/zcash-mini
|
||||
|
||||
+ moneropy (for Monero addresses)
|
||||
+ moneropy (for Monero addresses and view keys)
|
||||
https://github.com/bigreddmachine/MoneroPy
|
||||
|
||||
+ pycoin (for supported coins)
|
||||
|
|
@ -103,6 +123,9 @@ Supported external tools:
|
|||
"""
|
||||
},
|
||||
'code': {
|
||||
'options': lambda s: s.format(
|
||||
rf=results_file,
|
||||
),
|
||||
'notes': lambda s: s.format(
|
||||
prog='test/gentest.py',
|
||||
pnm=g.proj_name,
|
||||
|
|
@ -110,36 +133,33 @@ Supported external tools:
|
|||
}
|
||||
}
|
||||
|
||||
sys.argv = [sys.argv[0]] + ['--skip-cfg-file'] + sys.argv[1:]
|
||||
gtr = namedtuple('gen_tool_result',['wif','addr','vk'])
|
||||
|
||||
cmd_args = opts.init(opts_data,add_opts=['exact_output'])
|
||||
|
||||
if not 1 <= len(cmd_args) <= 2:
|
||||
opts.usage()
|
||||
|
||||
from mmgen.protocol import init_proto_from_opts
|
||||
proto = init_proto_from_opts()
|
||||
|
||||
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()
|
||||
|
||||
from collections import namedtuple
|
||||
gtr = namedtuple('gen_tool_result',['wif','addr','vk'])
|
||||
saved_results = {}
|
||||
|
||||
class GenTool(object):
|
||||
|
||||
def __init__(self,proto,addr_type):
|
||||
self.proto = proto
|
||||
self.addr_type = addr_type
|
||||
self.data = {}
|
||||
|
||||
def __del__(self):
|
||||
if opt.save_results:
|
||||
key = f'{self.proto.coin}-{self.proto.network}-{self.addr_type.name}-{self.desc}'.lower()
|
||||
saved_results[key] = self.data
|
||||
|
||||
def run_tool(self,sec):
|
||||
vcoin = 'BTC' if proto.coin == 'BCH' else proto.coin
|
||||
return self.run(sec,vcoin)
|
||||
vcoin = 'BTC' if self.proto.coin == 'BCH' else self.proto.coin
|
||||
ret = self.run(sec,vcoin)
|
||||
self.data[sec] = ret._asdict()
|
||||
return ret
|
||||
|
||||
class GenToolEthkey(GenTool):
|
||||
desc = 'ethkey'
|
||||
def __init__(self):
|
||||
proto = init_proto('eth')
|
||||
global addr_type
|
||||
addr_type = MMGenAddrType(proto,'E')
|
||||
|
||||
def run(self,sec,vcoin):
|
||||
o = get_cmd_output(['ethkey','info',sec])
|
||||
return gtr(o[0].split()[1],o[-1].split()[1],None)
|
||||
|
|
@ -152,11 +172,6 @@ class GenToolKeyconv(GenTool):
|
|||
|
||||
class GenToolZcash_mini(GenTool):
|
||||
desc = 'zcash-mini'
|
||||
def __init__(self):
|
||||
proto = init_proto('zec')
|
||||
global addr_type
|
||||
addr_type = MMGenAddrType(proto,'Z')
|
||||
|
||||
def run(self,sec,vcoin):
|
||||
o = get_cmd_output(['zcash-mini','-key','-simple'],input=(sec.wif+'\n').encode())
|
||||
return gtr(o[1],o[0],o[-1])
|
||||
|
|
@ -166,24 +181,26 @@ 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?"
|
||||
def __init__(self,*args,**kwargs):
|
||||
super().__init__(*args,**kwargs)
|
||||
try:
|
||||
from pycoin.networks.registry import network_for_netcode
|
||||
except:
|
||||
raise ImportError(m)
|
||||
raise ImportError('Unable to import pycoin.networks.registry. Is pycoin installed on your system?')
|
||||
self.nfnc = network_for_netcode
|
||||
|
||||
def run(self,sec,vcoin):
|
||||
if proto.testnet:
|
||||
if self.proto.testnet:
|
||||
vcoin = ci.external_tests['testnet']['pycoin'][vcoin]
|
||||
network = self.nfnc(vcoin)
|
||||
key = network.keys.private(secret_exponent=int(sec,16),is_compressed=addr_type.name != 'legacy')
|
||||
key = network.keys.private(
|
||||
secret_exponent = int(sec,16),
|
||||
is_compressed = self.addr_type.name != 'legacy' )
|
||||
if key is None:
|
||||
die(1,f'can’t parse {sec}')
|
||||
if addr_type.name in ('segwit','bech32'):
|
||||
if self.addr_type.name in ('segwit','bech32'):
|
||||
hash160_c = key.hash160(is_compressed=True)
|
||||
if addr_type.name == 'segwit':
|
||||
if self.addr_type.name == 'segwit':
|
||||
p2sh_script = network.contract.for_p2pkh_wit(hash160_c)
|
||||
addr = network.address.for_p2s(p2sh_script)
|
||||
else:
|
||||
|
|
@ -194,44 +211,42 @@ class GenToolPycoin(GenTool):
|
|||
|
||||
class GenToolMoneropy(GenTool):
|
||||
desc = 'moneropy'
|
||||
def __init__(self):
|
||||
|
||||
m = "Unable to import moneropy. Is moneropy installed on your system?"
|
||||
def __init__(self,*args,**kwargs):
|
||||
super().__init__(*args,**kwargs)
|
||||
try:
|
||||
import moneropy.account
|
||||
except:
|
||||
raise ImportError(m)
|
||||
|
||||
raise ImportError('Unable to import moneropy. Is moneropy installed on your system?')
|
||||
self.mpa = moneropy.account
|
||||
proto = init_proto('xmr')
|
||||
|
||||
global addr_type
|
||||
addr_type = MMGenAddrType(proto,'M')
|
||||
|
||||
def run(self,sec,vcoin):
|
||||
sk_t,vk_t,addr_t = self.mpa.account_from_spend_key(sec) # VERY slow!
|
||||
return gtr(sk_t,addr_t,vk_t)
|
||||
if sec in self.data:
|
||||
return gtr(**self.data[sec])
|
||||
else:
|
||||
sk,vk,addr = self.mpa.account_from_spend_key(sec) # VERY slow!
|
||||
return gtr(sk,addr,vk)
|
||||
|
||||
def get_tool(arg):
|
||||
def find_or_check_tool(proto,addr_type,toolname):
|
||||
|
||||
if arg not in ext_progs + ['ext']:
|
||||
die(1,f'{arg!r}: unsupported tool for network {proto.network}')
|
||||
ext_progs = list(ci.external_tests[proto.network])
|
||||
|
||||
if opt.all:
|
||||
if arg == 'ext':
|
||||
die(1,"'--all' must be combined with a specific external testing tool")
|
||||
return arg
|
||||
if toolname not in ext_progs + ['ext']:
|
||||
die(1,f'{toolname!r}: unsupported tool for network {proto.network}')
|
||||
|
||||
if opt.all_coins and toolname == 'ext':
|
||||
die(1,"'--all-coins' must be combined with a specific external testing tool")
|
||||
else:
|
||||
tool = ci.get_test_support(
|
||||
proto.coin,
|
||||
addr_type.name,
|
||||
proto.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:
|
||||
tool = toolname if toolname != 'ext' else None )
|
||||
if tool and toolname in ext_progs and toolname != tool:
|
||||
sys.exit(3)
|
||||
if tool == None:
|
||||
return None
|
||||
return tool
|
||||
|
||||
def test_equal(desc,a_val,b_val,in_bytes,sec,wif,a_desc,b_desc):
|
||||
|
|
@ -251,13 +266,47 @@ def test_equal(desc,a_val,b_val,in_bytes,sec,wif,a_desc,b_desc):
|
|||
w=max(len(e) for e in (a_desc,b_desc)) + 1
|
||||
).rstrip())
|
||||
|
||||
def gentool_test(kg_a,kg_b,ag,rounds):
|
||||
def do_ab_test(proto,addr_type,kg_b,rounds,backend_num):
|
||||
|
||||
def run_ab_inner(n,trounds,in_bytes):
|
||||
global last_t
|
||||
if opt.verbose or time.time() - last_t >= 0.1:
|
||||
qmsg_r(f'\rRound {i+1}/{trounds} ')
|
||||
last_t = time.time()
|
||||
sec = PrivKey(proto,in_bytes,compressed=addr_type.compressed,pubkey_type=addr_type.pubkey_type)
|
||||
data = kg_a.to_pubhex(sec)
|
||||
ag = AddrGenerator(proto,addr_type)
|
||||
a_addr = ag.to_addr(data)
|
||||
tinfo = (in_bytes,sec,sec.wif,type(kg_a).__name__,type(kg_b).__name__)
|
||||
a_vk = None
|
||||
|
||||
def do_msg():
|
||||
vmsg( fs.format( b=in_bytes.hex(), r=sec, k=sec.wif, v=a_vk, a=a_addr ))
|
||||
|
||||
if isinstance(kg_b,GenTool):
|
||||
def run_tool():
|
||||
b = kg_b.run_tool(sec)
|
||||
test_equal('WIF keys',sec.wif,b.wif,*tinfo)
|
||||
test_equal('addresses',a_addr,b.addr,*tinfo)
|
||||
if b.vk:
|
||||
test_equal( 'view keys', ag.to_viewkey(data), b.vk, *tinfo )
|
||||
return b.vk
|
||||
a_vk = run_tool()
|
||||
do_msg()
|
||||
else:
|
||||
test_equal( 'addresses', a_addr, ag.to_addr(kg_b.to_pubhex(sec)), *tinfo )
|
||||
do_msg()
|
||||
|
||||
qmsg_r(f'\rRound {n+1}/{trounds} ')
|
||||
|
||||
kg_a = KeyGenerator(proto,addr_type,backend_num)
|
||||
if type(kg_a) == type(kg_b):
|
||||
rdie(1,'Key generators are the same!')
|
||||
|
||||
m = "Comparing address generators '{A}' and '{B}' for {N} {c} ({n}), addrtype {a!r}"
|
||||
e = ci.get_entry(proto.coin,proto.network)
|
||||
qmsg(green(m.format(
|
||||
A = kg_a.desc,
|
||||
B = kg_b.desc,
|
||||
qmsg(green("Comparing address generators '{A}' and '{B}' for {N} {c} ({n}), addrtype {a!r}".format(
|
||||
A = type(kg_a).__name__,
|
||||
B = type(kg_b).__name__.replace('GenTool','').replace('_','-').lower(),
|
||||
N = proto.network,
|
||||
c = proto.coin,
|
||||
n = e.name if e else '---',
|
||||
|
|
@ -266,61 +315,71 @@ def gentool_test(kg_a,kg_b,ag,rounds):
|
|||
global last_t
|
||||
last_t = time.time()
|
||||
|
||||
def do_compare_test(n,trounds,in_bytes):
|
||||
global last_t
|
||||
if opt.verbose or time.time() - last_t >= 0.1:
|
||||
qmsg_r(f'\rRound {i+1}/{trounds} ')
|
||||
last_t = time.time()
|
||||
sec = PrivKey(proto,in_bytes,compressed=addr_type.compressed,pubkey_type=addr_type.pubkey_type)
|
||||
a_ph = kg_a.to_pubhex(sec)
|
||||
a_addr = ag.to_addr(a_ph)
|
||||
a_vk = None
|
||||
tinfo = (in_bytes,sec,sec.wif,kg_a.desc,kg_b.desc)
|
||||
if isinstance(kg_b,GenTool):
|
||||
b = kg_b.run_tool(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('view keys',a_vk,b.vk,*tinfo)
|
||||
else:
|
||||
b_addr = ag.to_addr(kg_b.to_pubhex(sec))
|
||||
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(f'\rRound {n+1}/{trounds} ')
|
||||
fs = (
|
||||
'\ninput: {b}' +
|
||||
'\nreduced: {r}' +
|
||||
'\n{:9} {{k}}'.format(addr_type.wif_label+':') +
|
||||
('\nviewkey: {v}' if 'viewkey' in addr_type.extra_attrs else '') +
|
||||
'\naddr: {a}\n' )
|
||||
|
||||
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 + ':')
|
||||
ge = CoinProtocol.Secp256k1.secp256k1_ge
|
||||
|
||||
# test some important private key edge cases:
|
||||
edgecase_sks = (
|
||||
bytes([0x00]*31 + [0x01]), # min
|
||||
bytes([0xff]*32), # max
|
||||
bytes([0x0f] + [0xff]*31), # same key as above for zcash-z
|
||||
bytes([0x0f] + [0xff]*31), # produces same key as above for zcash-z
|
||||
int.to_bytes(ge + 1, 32, 'big'), # bitcoin will reduce
|
||||
int.to_bytes(ge - 1, 32, 'big'), # bitcoin will not reduce
|
||||
bytes([0x00]*31 + [0xff]), # monero will reduce
|
||||
bytes([0xff]*31 + [0x0f]), # monero will not reduce
|
||||
bytes.fromhex('deadbeef'*8),
|
||||
)
|
||||
|
||||
qmsg(purple('edge cases:'))
|
||||
for i,in_bytes in enumerate(edgecase_sks):
|
||||
do_compare_test(i,len(edgecase_sks),in_bytes)
|
||||
run_ab_inner(i,len(edgecase_sks),in_bytes)
|
||||
qmsg(green('\rOK ' if opt.verbose else 'OK'))
|
||||
|
||||
qmsg(purple('random input:'))
|
||||
for i in range(rounds):
|
||||
do_compare_test(i,rounds,getrand(32))
|
||||
run_ab_inner(i,rounds,getrand(32))
|
||||
qmsg(green('\rOK ' if opt.verbose else 'OK'))
|
||||
|
||||
def speed_test(kg,ag,rounds):
|
||||
m = "Testing speed of address generator '{}' for coin {}"
|
||||
qmsg(green(m.format(kg.desc,proto.coin)))
|
||||
def init_tool(proto,addr_type,toolname):
|
||||
return globals()['GenTool'+capfirst(toolname.replace('-','_'))](proto,addr_type)
|
||||
|
||||
def get_backends(proto,foo):
|
||||
return (1,) if isinstance(proto,CoinProtocol.Zcash) else (1,2)
|
||||
|
||||
def ab_test(proto,gen_num,rounds,toolname_or_gen2_num):
|
||||
|
||||
addr_type = MMGenAddrType( proto=proto, id_str=opt.type or proto.dfl_mmtype )
|
||||
|
||||
if is_int(toolname_or_gen2_num):
|
||||
assert gen_num != 'all', "'all' must be used only with external tool"
|
||||
tool = KeyGenerator( proto, addr_type, int(toolname_or_gen2_num) )
|
||||
else:
|
||||
toolname = find_or_check_tool( proto, addr_type, toolname_or_gen2_num )
|
||||
if toolname == None:
|
||||
ymsg(f'Warning: skipping tool {toolname_or_gen2_num!r} for {proto.coin} {addr_type.name}')
|
||||
return
|
||||
tool = init_tool( proto, addr_type, toolname )
|
||||
|
||||
if gen_num == 'all': # check all backends against external tool
|
||||
for n in range(len(get_backends(proto,addr_type.pubkey_type))):
|
||||
do_ab_test( proto, addr_type, tool, rounds, n+1 )
|
||||
else: # check specific backend against external tool or another backend
|
||||
do_ab_test( proto, addr_type, tool, rounds, int(gen_num) )
|
||||
|
||||
def speed_test(proto,addr_type,kg,ag,rounds):
|
||||
qmsg(green('Testing speed of address generator {!r} for coin {}'.format(
|
||||
type(kg).__name__,
|
||||
proto.coin )))
|
||||
from struct import pack,unpack
|
||||
seed = getrand(28)
|
||||
qmsg('Incrementing key with each round')
|
||||
qmsg('Starting key: {}'.format(
|
||||
(seed + pack('I',0)).hex()
|
||||
))
|
||||
qmsg('Starting key: {}'.format( (seed + pack('I',0)).hex() ))
|
||||
import time
|
||||
start = last_t = time.time()
|
||||
|
||||
|
|
@ -328,7 +387,7 @@ def speed_test(kg,ag,rounds):
|
|||
if time.time() - last_t >= 0.1:
|
||||
qmsg_r(f'\rRound {i+1}/{rounds} ')
|
||||
last_t = time.time()
|
||||
sec = PrivKey(proto,seed+pack('I',i),compressed=addr_type.compressed,pubkey_type=addr_type.pubkey_type)
|
||||
sec = PrivKey( proto, seed+pack('I', i), compressed=addr_type.compressed, pubkey_type=addr_type.pubkey_type )
|
||||
addr = ag.to_addr(kg.to_pubhex(sec))
|
||||
vmsg(f'\nkey: {sec.wif}\naddr: {addr}\n')
|
||||
qmsg(
|
||||
|
|
@ -337,15 +396,18 @@ def speed_test(kg,ag,rounds):
|
|||
('' if g.test_suite_deterministic else f' in {time.time()-start:.2f} seconds')
|
||||
)
|
||||
|
||||
def dump_test(kg,ag,fh):
|
||||
def dump_test(proto,kg,ag,filename):
|
||||
|
||||
dump = [[*(e.split()[0] for e in line.split('addr='))] for line in fh.readlines() if 'addr=' in line]
|
||||
if not dump:
|
||||
die(1,f'File {fh.name!r} appears not to be a wallet dump')
|
||||
fh.close()
|
||||
with open(filename) as fp:
|
||||
dump = [[*(e.split()[0] for e in line.split('addr='))] for line in fp.readlines() if 'addr=' in line]
|
||||
if not dump:
|
||||
die(1,f'File {filename!r} appears not to be a wallet dump')
|
||||
|
||||
m = 'Comparing output of address generator {!r} against wallet dump {!r}'
|
||||
qmsg(green(m.format(kg.desc,fh.name)))
|
||||
qmsg(green(
|
||||
"A: generator pair '{}:{}'\nB: wallet dump {!r}".format(
|
||||
type(kg).__name__,
|
||||
type(ag).__name__,
|
||||
filename)))
|
||||
|
||||
for count,(b_wif,b_addr) in enumerate(dump,1):
|
||||
qmsg_r(f'\rKey {count}/{len(dump)} ')
|
||||
|
|
@ -355,102 +417,90 @@ def dump_test(kg,ag,fh):
|
|||
die(2,f'\nInvalid {proto.network} WIF address in dump file: {b_wif}')
|
||||
a_addr = ag.to_addr(kg.to_pubhex(b_sec))
|
||||
vmsg(f'\nwif: {b_wif}\naddr: {b_addr}\n')
|
||||
tinfo = (bytes.fromhex(b_sec),b_sec,b_wif,kg.desc,fh.name)
|
||||
tinfo = (b_sec,b_sec,b_wif,type(kg).__name__,filename)
|
||||
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 get_protos(proto,addr_type,toolname):
|
||||
|
||||
def parse_arg1(arg,arg_id):
|
||||
init_genonly_altcoins(testnet=proto.testnet)
|
||||
|
||||
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 {}'
|
||||
for coin in ci.external_tests[proto.network][toolname]:
|
||||
if coin.lower() not in CoinProtocol.coins:
|
||||
continue
|
||||
ret = init_proto(coin,testnet=proto.testnet)
|
||||
if addr_type not in ret.mmtypes:
|
||||
continue
|
||||
yield ret
|
||||
|
||||
def check_gen_num(n):
|
||||
if not (1 <= int(n) <= len(g.key_generators)):
|
||||
die(1,f'{n}: invalid generator ID')
|
||||
return int(n)
|
||||
def parse_args():
|
||||
|
||||
if arg_id == 'a':
|
||||
if is_int(arg):
|
||||
a_num = check_gen_num(arg)
|
||||
return (KeyGenerator(proto,addr_type,a_num),a_num)
|
||||
else:
|
||||
die(1,m1)
|
||||
elif arg_id == 'b':
|
||||
if is_int(arg):
|
||||
return KeyGenerator(proto,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))
|
||||
if len(cmd_args) != 2:
|
||||
opts.usage()
|
||||
|
||||
arg1,arg2 = cmd_args
|
||||
pa = namedtuple('parsed_args',['test','gen_num','rounds','arg'])
|
||||
|
||||
if is_int(arg1) and is_int(arg2):
|
||||
return pa( test='speed', gen_num=arg1, rounds=int(arg2), arg=None )
|
||||
|
||||
if is_int(arg1) and os.access(arg2,os.R_OK):
|
||||
return pa( test='dump', gen_num=arg1, rounds=None, arg=arg2 )
|
||||
|
||||
if not is_int(arg2):
|
||||
die(1,'Second argument must be dump filename or integer rounds specification')
|
||||
|
||||
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)
|
||||
a,b = arg1.split(':')
|
||||
except:
|
||||
die(1,m)
|
||||
die(1,'First argument must be a generator backend number or two colon-separated arguments')
|
||||
|
||||
# begin execution
|
||||
from mmgen.protocol import init_proto
|
||||
if not is_int(a) and a != 'all':
|
||||
die(1,"First part of first argument must be a generator backend number or 'all'")
|
||||
|
||||
if is_int(b):
|
||||
if opt.all_coins:
|
||||
die(1,'--all-coins must be used with external tool only')
|
||||
else:
|
||||
proto = init_proto_from_opts()
|
||||
ext_progs = list(ci.external_tests[proto.network]) + ['ext']
|
||||
if b not in ext_progs:
|
||||
die(1,f'Second part of first argument must be a generator backend number or one of {ext_progs}')
|
||||
|
||||
return pa( test='ab', gen_num=a, rounds=int(arg2), arg=b )
|
||||
|
||||
def main():
|
||||
|
||||
pa = parse_args()
|
||||
proto = init_proto_from_opts()
|
||||
addr_type = MMGenAddrType( proto=proto, id_str=opt.type or proto.dfl_mmtype )
|
||||
|
||||
if pa.test == 'ab':
|
||||
protos = get_protos(proto,addr_type,pa.arg) if opt.all_coins else [proto]
|
||||
for proto in protos:
|
||||
ab_test( proto, pa.gen_num, pa.rounds, toolname_or_gen2_num=pa.arg )
|
||||
else:
|
||||
kg = KeyGenerator( proto, addr_type, pa.gen_num )
|
||||
ag = AddrGenerator( proto, addr_type )
|
||||
if pa.test == 'speed':
|
||||
speed_test( proto, addr_type, kg, ag, pa.rounds )
|
||||
elif pa.test == 'dump':
|
||||
dump_test( proto, kg, ag, filename=pa.arg )
|
||||
|
||||
if saved_results:
|
||||
import json
|
||||
with open(results_file,'w') as fp:
|
||||
fp.write(json.dumps( saved_results, indent=4 ))
|
||||
|
||||
from subprocess import run,PIPE,DEVNULL
|
||||
from collections import namedtuple
|
||||
from mmgen.protocol import init_proto,init_proto_from_opts,CoinProtocol,init_genonly_altcoins
|
||||
from mmgen.altcoin import CoinInfo as ci
|
||||
from mmgen.key import PrivKey
|
||||
from mmgen.addr import KeyGenerator,AddrGenerator,MMGenAddrType
|
||||
|
||||
addr_type = MMGenAddrType(
|
||||
proto = proto,
|
||||
id_str = opt.type or proto.dfl_mmtype )
|
||||
sys.argv = [sys.argv[0]] + ['--skip-cfg-file'] + sys.argv[1:]
|
||||
cmd_args = opts.init(opts_data,add_opts=['exact_output'])
|
||||
|
||||
ext_progs = list(ci.external_tests[proto.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):
|
||||
rdie(1,'Address generators are the same!')
|
||||
|
||||
arg2 = parse_arg2()
|
||||
|
||||
if not opt.all:
|
||||
ag = AddrGenerator(proto,addr_type)
|
||||
|
||||
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 CoinProtocol,init_genonly_altcoins
|
||||
init_genonly_altcoins(testnet=proto.testnet)
|
||||
for coin in ci.external_tests[proto.network][b.desc]:
|
||||
if coin.lower() not in CoinProtocol.coins:
|
||||
# ymsg(f'Coin {coin} not configured')
|
||||
continue
|
||||
proto = init_proto(coin)
|
||||
if addr_type not in proto.mmtypes:
|
||||
continue
|
||||
# proto has changed, so reinit kg and ag
|
||||
a = KeyGenerator(proto,addr_type,a_num)
|
||||
ag = AddrGenerator(proto,addr_type)
|
||||
b_chk = ci.get_test_support(proto.coin,addr_type.name,proto.network,tool=b.desc,verbose=not opt.quiet)
|
||||
if b_chk == b.desc:
|
||||
gentool_test(a,b,ag,arg2)
|
||||
else:
|
||||
gentool_test(a,b,ag,arg2)
|
||||
else:
|
||||
opts.usage()
|
||||
main()
|
||||
|
|
|
|||
|
|
@ -330,7 +330,7 @@ t_alts="
|
|||
m # moneropy
|
||||
m $gentest_py --all --coin=xmr 2:moneropy $rounds_min # very slow, be patient!
|
||||
z # zcash-mini
|
||||
z $gentest_py --all 2:zcash-mini $rounds_mid
|
||||
z $gentest_py --all --coin=zec --type=zcash_z 1:zcash-mini $rounds_mid
|
||||
"
|
||||
|
||||
[ "$MSYS2" ] && t_alts_skip='m z' # no moneropy (pysha3), zcash-mini (golang)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue