whitespace: test/unit_tests_d

This commit is contained in:
The MMGen Project 2024-10-18 10:32:14 +00:00
commit 6d90d180c1
Signed by: mmgen
GPG key ID: 3F8B1861E32B7DA2
28 changed files with 856 additions and 816 deletions

View file

@ -4,7 +4,7 @@
test.unit_tests_d.__init__: shared data for unit tests for the MMGen suite
"""
import sys,os
import sys, os
from mmgen.cfg import gv
from ..include.common import cfg
@ -17,7 +17,7 @@ class unit_tests_base:
if not cfg.verbose:
self.stdout_save = sys.stdout
self.stderr_save = sys.stderr
sys.stdout = sys.stderr = gv.stdout = gv.stderr = open(os.devnull,'w')
sys.stdout = sys.stderr = gv.stdout = gv.stderr = open(os.devnull, 'w')
def _end_silence(self):
if not cfg.verbose:

View file

@ -9,10 +9,10 @@ from mmgen.util import msg
from mmgen.seed import Seed
from mmgen.addr import MMGenAddrType
from mmgen.addrlist import AddrIdxList,AddrList,KeyList,KeyAddrList,ViewKeyAddrList
from mmgen.addrlist import AddrIdxList, AddrList, KeyList, KeyAddrList, ViewKeyAddrList
from mmgen.passwdlist import PasswordList
from mmgen.protocol import init_proto
from ..include.common import cfg,qmsg,vmsg
from ..include.common import cfg, qmsg, vmsg
def do_test(
list_type,
@ -24,8 +24,8 @@ def do_test(
addrtype = None):
qmsg(blue(f'Testing {list_type.__name__}'))
proto = init_proto( cfg, coin or 'btc' )
seed = Seed(cfg,seed_bin=bytes.fromhex('feedbead'*8))
proto = init_proto(cfg, coin or 'btc')
seed = Seed(cfg, seed_bin=bytes.fromhex('feedbead'*8))
mmtype = MMGenAddrType(proto, addrtype or 'C')
idxs = AddrIdxList(idx_spec or '1-3')
@ -47,7 +47,7 @@ def do_test(
if add_kwargs:
kwargs.update(add_kwargs)
al = list_type( cfg, proto, **kwargs )
al = list_type(cfg, proto, **kwargs)
al.file.format()
@ -65,10 +65,10 @@ def do_test(
class unit_tests:
altcoin_deps = ('keyaddr_xmr','viewkeyaddr')
altcoin_deps = ('keyaddr_xmr', 'viewkeyaddr')
def idxlist(self,name,ut):
for i,o in (
def idxlist(self, name, ut):
for i, o in (
('99,88-102,1-3,4,9,818,444-445,816', '1-4,9,88-102,444-445,816,818'),
('99,88-99,100,102,4-7,9,818,444-445,816,1', '1,4-7,9,88-100,102,444-445,816,818'),
('8', '8'),
@ -91,26 +91,26 @@ class unit_tests:
return True
def addr(self,name,ut):
def addr(self, name, ut):
return (
do_test(AddrList,'BCE8 082C 0973 A525','1-3') and
do_test(AddrList,'88FA B04B A380 C1CB','199999,99-101,77-78,7,3,2-9')
do_test(AddrList, 'BCE8 082C 0973 A525', '1-3') and
do_test(AddrList, '88FA B04B A380 C1CB', '199999,99-101,77-78,7,3,2-9')
)
def key(self,name,ut):
return do_test(KeyList,None)
def key(self, name, ut):
return do_test(KeyList, None)
def keyaddr(self,name,ut):
return do_test(KeyAddrList,'4A36 AA65 8C2B 7C35')
def keyaddr(self, name, ut):
return do_test(KeyAddrList, '4A36 AA65 8C2B 7C35')
def keyaddr_xmr(self,name,ut):
return do_test(KeyAddrList,'AAA2 BA69 17FC 9A88',coin='XMR',addrtype='M')
def keyaddr_xmr(self, name, ut):
return do_test(KeyAddrList, 'AAA2 BA69 17FC 9A88', coin='XMR', addrtype='M')
def viewkeyaddr(self,name,ut):
return do_test(ViewKeyAddrList,'C122 2E58 DC28 D6AE',coin='XMR',addrtype='M')
def viewkeyaddr(self, name, ut):
return do_test(ViewKeyAddrList, 'C122 2E58 DC28 D6AE', coin='XMR', addrtype='M')
def passwd(self,name,ut):
return do_test(PasswordList,'FF4A B716 4513 8F8F',pw_id_str='foo')
def passwd(self, name, ut):
return do_test(PasswordList, 'FF4A B716 4513 8F8F', pw_id_str='foo')
def passwd_bip39(self,name,ut):
return do_test(PasswordList,'C3A8 B2B2 1AA1 FB40',pw_id_str='foo',add_kwargs={'pw_fmt':'bip39'})
def passwd_bip39(self, name, ut):
return do_test(PasswordList, 'C3A8 B2B2 1AA1 FB40', pw_id_str='foo', add_kwargs={'pw_fmt': 'bip39'})

View file

@ -4,10 +4,10 @@
test/unit_tests_d/ut_addrparse: address parsing tests for the MMGen suite
"""
from mmgen.color import yellow,cyan
from mmgen.util import msg,msg_r,pp_fmt
from mmgen.color import yellow, cyan
from mmgen.util import msg, msg_r, pp_fmt
from ..include.common import cfg,vmsg
from ..include.common import cfg, vmsg
vectors = {
'btc_mainnet': [
@ -22,14 +22,14 @@ vectors = {
],
'xmr_mainnet': [
{ # ut_xmrseed.vectors[0]:
'std': '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm',
# https://github.com/monero-project/monero/tests/functional_tests/integrated_address.py
'int': '4CMe2PUhs4J4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfSbLRB61BQVATzerHGj',
'id': '0123456789abcdef'
},{
'std': '46r4nYSevkfBUMhuykdK3gQ98XDqDTYW1hNLaXNvjpsJaSbNtdXh1sKMsdVgqkaihChAzEy29zEDPMR3NHQvGoZCLGwTerK',
'int': '4GYjoMG9Y2BBUMhuykdK3gQ98XDqDTYW1hNLaXNvjpsJaSbNtdXh1sKMsdVgqkaihChAzEy29zEDPMR3NHQvGoZCVSs1ZojwrDCGS5rUuo',
'id': '1122334455667788'
'std': '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm',
# https://github.com/monero-project/monero/tests/functional_tests/integrated_address.py
'int': '4CMe2PUhs4J4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfSbLRB61BQVATzerHGj',
'id': '0123456789abcdef'
}, {
'std': '46r4nYSevkfBUMhuykdK3gQ98XDqDTYW1hNLaXNvjpsJaSbNtdXh1sKMsdVgqkaihChAzEy29zEDPMR3NHQvGoZCLGwTerK',
'int': '4GYjoMG9Y2BBUMhuykdK3gQ98XDqDTYW1hNLaXNvjpsJaSbNtdXh1sKMsdVgqkaihChAzEy29zEDPMR3NHQvGoZCVSs1ZojwrDCGS5rUuo',
'id': '1122334455667788'
}
],
'zec_mainnet': [
@ -41,22 +41,22 @@ vectors = {
],
}
def test_network(proto,addrs):
def test_network(proto, addrs):
def check_equal(a,b):
def check_equal(a, b):
assert a == b, f'{a.hex()} != {b.hex()}'
def check_bytes(addr):
if addr.parsed.ver_bytes is not None:
check_equal(
addr.parsed.ver_bytes,
proto.addr_fmt_to_ver_bytes.get(addr.addr_fmt) )
proto.addr_fmt_to_ver_bytes.get(addr.addr_fmt))
check_equal(
addr.parsed.data + ((addr.parsed.payment_id or b'') if proto.coin == 'XMR' else b''),
addr.bytes )
addr.bytes)
def fmt_addr_data(addr):
return pp_fmt({k:(v.hex() if isinstance(v,bytes) else v) for k,v in addr.parsed._asdict().items()})
return pp_fmt({k:(v.hex() if isinstance(v, bytes) else v) for k, v in addr.parsed._asdict().items()})
def print_info(addr):
vmsg('\n{}\n{}\n{}'.format(yellow(addr.addr_fmt), cyan(addr), fmt_addr_data(addr)))
@ -67,17 +67,17 @@ def test_network(proto,addrs):
from mmgen.addr import CoinAddr
for addr in addrs:
a1 = CoinAddr(proto,addr['std'])
a1 = CoinAddr(proto, addr['std'])
print_info(a1)
check_bytes(a1)
assert not hasattr(a1.parsed,'payment_id') or a1.parsed.payment_id is None
assert not hasattr(a1.parsed, 'payment_id') or a1.parsed.payment_id is None
if 'int' in addr:
a2 = CoinAddr(proto,addr['int'])
a2 = CoinAddr(proto, addr['int'])
print_info(a2)
check_bytes(a2)
check_equal( a1.parsed.data, a2.parsed.data )
check_equal( a2.parsed.payment_id, bytes.fromhex(addr['id']) )
check_equal(a1.parsed.data, a2.parsed.data)
check_equal(a2.parsed.payment_id, bytes.fromhex(addr['id']))
msg('OK')
vmsg('')
@ -85,16 +85,16 @@ def test_network(proto,addrs):
class unit_test:
def run_test(self,name,ut):
def run_test(self, name, ut):
from mmgen.protocol import init_proto
for net_id,addrs in vectors.items():
coin,network = net_id.split('_')
for net_id, addrs in vectors.items():
coin, network = net_id.split('_')
if cfg.no_altcoin_deps and coin != 'btc':
continue
test_network(
init_proto( cfg, coin, network=network ),
addrs )
init_proto(cfg, coin, network=network),
addrs)
return True

View file

@ -4,158 +4,168 @@
test.unit_tests_d.ut_baseconv: Base conversion unit test for the MMGen suite
"""
from mmgen.util import msg,msg_r
from mmgen.util import msg, msg_r
from ..include.common import cfg,qmsg,qmsg_r,vmsg,vmsg_r
from ..include.common import cfg, qmsg, qmsg_r, vmsg, vmsg_r
class unit_test:
vectors = {
'b58': (
(('00',None),'1'),
(('00',1),'1'),
(('00',2),'11'),
(('01',None),'2'),
(('01',1),'2'),
(('01',2),'12'),
(('0f',None),'G'),
(('0f',1),'G'),
(('0f',2),'1G'),
(('deadbeef',None),'6h8cQN'),
(('deadbeef',20),'111111111111116h8cQN'),
(('00000000',None),'1'),
(('00000000',20),'11111111111111111111'),
(('ffffffff',None),'7YXq9G'),
(('ffffffff',20),'111111111111117YXq9G'),
(('ff'*16,'seed'),'YcVfxkQb6JRzqk5kF2tNLv'),
(('ff'*24,'seed'),'QLbz7JHiBTspS962RLKV8GndWFwiEaqKL'),
(('ff'*32,'seed'),'JEKNVnkbo3jma5nREBBJCDoXFVeKkD56V3xKrvRmWxFG'),
(('00'*16,'seed'),'1111111111111111111111'),
(('00'*24,'seed'),'111111111111111111111111111111111'),
(('00'*32,'seed'),'11111111111111111111111111111111111111111111'),
(('00', None), '1'),
(('00', 1), '1'),
(('00', 2), '11'),
(('01', None), '2'),
(('01', 1), '2'),
(('01', 2), '12'),
(('0f', None), 'G'),
(('0f', 1), 'G'),
(('0f', 2), '1G'),
(('deadbeef', None), '6h8cQN'),
(('deadbeef', 20), '111111111111116h8cQN'),
(('00000000', None), '1'),
(('00000000', 20), '11111111111111111111'),
(('ffffffff', None), '7YXq9G'),
(('ffffffff', 20), '111111111111117YXq9G'),
(('ff'*16, 'seed'), 'YcVfxkQb6JRzqk5kF2tNLv'),
(('ff'*24, 'seed'), 'QLbz7JHiBTspS962RLKV8GndWFwiEaqKL'),
(('ff'*32, 'seed'), 'JEKNVnkbo3jma5nREBBJCDoXFVeKkD56V3xKrvRmWxFG'),
(('00'*16, 'seed'), '1111111111111111111111'),
(('00'*24, 'seed'), '111111111111111111111111111111111'),
(('00'*32, 'seed'), '11111111111111111111111111111111111111111111'),
),
# MMGen-flavored base32 using simple base conversion
'b32': (
(('00',None),'A'),
(('00',1),'A'),
(('00',2),'AA'),
(('01',None),'B'),
(('01',1),'B'),
(('01',2),'AB'),
(('0f',None),'P'),
(('0f',1),'P'),
(('0f',2),'AP'),
(('deadbeef',None),'DPK3PXP'),
(('deadbeef',20),'AAAAAAAAAAAAADPK3PXP'),
(('00000000',None),'A'),
(('00000000',20),'AAAAAAAAAAAAAAAAAAAA'),
(('ffffffff',None),'D777777'),
(('ffffffff',20),'AAAAAAAAAAAAAD777777'),
(('00', None), 'A'),
(('00', 1), 'A'),
(('00', 2), 'AA'),
(('01', None), 'B'),
(('01', 1), 'B'),
(('01', 2), 'AB'),
(('0f', None), 'P'),
(('0f', 1), 'P'),
(('0f', 2), 'AP'),
(('deadbeef', None), 'DPK3PXP'),
(('deadbeef', 20), 'AAAAAAAAAAAAADPK3PXP'),
(('00000000', None), 'A'),
(('00000000', 20), 'AAAAAAAAAAAAAAAAAAAA'),
(('ffffffff', None), 'D777777'),
(('ffffffff', 20), 'AAAAAAAAAAAAAD777777'),
),
'b16': (
(('00',None),'0'),
(('00',1),'0'),
(('00',2),'00'),
(('01',None),'1'),
(('01',1),'1'),
(('01',2),'01'),
(('0f',None),'f'),
(('0f',1),'f'),
(('0f',2),'0f'),
(('deadbeef',None),'deadbeef'),
(('deadbeef',20),'000000000000deadbeef'),
(('00000000',None),'0'),
(('00000000',20),'00000000000000000000'),
(('ffffffff',None),'ffffffff'),
(('ffffffff',20),'000000000000ffffffff'),
(('00', None), '0'),
(('00', 1), '0'),
(('00', 2), '00'),
(('01', None), '1'),
(('01', 1), '1'),
(('01', 2), '01'),
(('0f', None), 'f'),
(('0f', 1), 'f'),
(('0f', 2), '0f'),
(('deadbeef', None), 'deadbeef'),
(('deadbeef', 20), '000000000000deadbeef'),
(('00000000', None), '0'),
(('00000000', 20), '00000000000000000000'),
(('ffffffff', None), 'ffffffff'),
(('ffffffff', 20), '000000000000ffffffff'),
),
'b10': (
(('00',None),'0'),
(('00',1),'0'),
(('00',2),'00'),
(('01',None),'1'),
(('01',1),'1'),
(('01',2),'01'),
(('0f',None),'15'),
(('0f',1),'15'),
(('0f',2),'15'),
(('deadbeef',None),'3735928559'),
(('deadbeef',20),'00000000003735928559'),
(('00000000',None),'0'),
(('00000000',20),'00000000000000000000'),
(('ffffffff',None),'4294967295'),
(('ffffffff',20),'00000000004294967295'),
(('00', None), '0'),
(('00', 1), '0'),
(('00', 2), '00'),
(('01', None), '1'),
(('01', 1), '1'),
(('01', 2), '01'),
(('0f', None), '15'),
(('0f', 1), '15'),
(('0f', 2), '15'),
(('deadbeef', None), '3735928559'),
(('deadbeef', 20), '00000000003735928559'),
(('00000000', None), '0'),
(('00000000', 20), '00000000000000000000'),
(('ffffffff', None), '4294967295'),
(('ffffffff', 20), '00000000004294967295'),
),
'b8': (
(('00',None),'0'),
(('00',1),'0'),
(('00',2),'00'),
(('01',None),'1'),
(('01',1),'1'),
(('01',2),'01'),
(('0f',None),'17'),
(('0f',1),'17'),
(('0f',2),'17'),
(('deadbeef',None),'33653337357'),
(('deadbeef',20),'00000000033653337357'),
(('00000000',None),'0'),
(('00000000',20),'00000000000000000000'),
(('ffffffff',None),'37777777777'),
(('ffffffff',20),'00000000037777777777'),
(('00', None), '0'),
(('00', 1), '0'),
(('00', 2), '00'),
(('01', None), '1'),
(('01', 1), '1'),
(('01', 2), '01'),
(('0f', None), '17'),
(('0f', 1), '17'),
(('0f', 2), '17'),
(('deadbeef', None), '33653337357'),
(('deadbeef', 20), '00000000033653337357'),
(('00000000', None), '0'),
(('00000000', 20), '00000000000000000000'),
(('ffffffff', None), '37777777777'),
(('ffffffff', 20), '00000000037777777777'),
),
'b6d': (
(('00',None),'1'),
(('00',1),'1'),
(('00',2),'11'),
(('01',None),'2'),
(('01',1),'2'),
(('01',2),'12'),
(('0f',None),'34'),
(('0f',1),'34'),
(('0f',2),'34'),
(('010f',None),'2242'),
(('010f',20),'11111111111111112242'),
(('deadbeef',None),'2525524636426'),
(('deadbeef',20),'11111112525524636426'),
(('00000000',None),'1'),
(('00000000',20),'11111111111111111111'),
(('ffffffff',None),'2661215126614'),
(('ffffffff',20),'11111112661215126614'),
(('00', None), '1'),
(('00', 1), '1'),
(('00', 2), '11'),
(('01', None), '2'),
(('01', 1), '2'),
(('01', 2), '12'),
(('0f', None), '34'),
(('0f', 1), '34'),
(('0f', 2), '34'),
(('010f', None), '2242'),
(('010f', 20), '11111111111111112242'),
(('deadbeef', None), '2525524636426'),
(('deadbeef', 20), '11111112525524636426'),
(('00000000', None), '1'),
(('00000000', 20), '11111111111111111111'),
(('ffffffff', None), '2661215126614'),
(('ffffffff', 20), '11111112661215126614'),
(('ff'*16,'seed'),'34164464641266661652465154654653354436664555521414'),
(('ff'*24,'seed'),'246111411433323364222465566552324652566121541623426135163525451613554313654'),
(('ff'*32,'seed'),'2132521653312613134145131423465414636252114131225324246311141642456513416322412146151432142242565134'),
(('00'*16,'seed'),'1'*50),
(('00'*24,'seed'),'1'*75),
(('00'*32,'seed'),'1'*100),
(('ff'*16, 'seed'), '34164464641266661652465154654653354436664555521414'),
(('ff'*24, 'seed'), '246111411433323364222465566552324652566121541623426135163525451613554313654'),
(('ff'*32, 'seed'), '2132521653312613134145131423465414636252114131225324246311141642456513416322412146151432142242565134'),
(('00'*16, 'seed'), '1'*50),
(('00'*24, 'seed'), '1'*75),
(('00'*32, 'seed'), '1'*100),
),
'mmgen': (
(('deadbeefdeadbeefdeadbeefdeadbeef','seed'),
'table cast forgive master funny gaze sadness ripple million paint moral match' ),
(('deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef','seed'),
'swirl maybe anymore mix scale stray fog use approach page crime rhyme ' +
'class former strange window snap soon'),
(('deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef','seed'),
'swell type milk figure cheese phone fill black test bloom heard comfort ' +
'image terrible radio lesson own reply battle goal goodbye need laugh stream'),
(('ffffffffffffffffffffffffffffffff','seed'),
'yellow yeah show bowl season spider cling defeat poison law shelter reflect'),
(('ffffffffffffffffffffffffffffffffffffffffffffffff','seed'),
'yeah youth quit fail perhaps drum out person young click skin ' +
'weird inside silently perfectly together anyone memory'),
(('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff','seed'),
'wrote affection object cell opinion here laughter stare honest north cost begin ' +
'murder something yourself effort acid dot doubt game broke tell guilt innocent'),
(('00000000000000000000000000000001','seed'),
'able ' * 11 + 'about'),
(('000000000000000000000000000000000000000000000001','seed'),
'able ' * 17 + 'about'),
(('0000000000000000000000000000000000000000000000000000000000000001','seed'),
'able ' * 23 + 'about'),
(
('deadbeefdeadbeefdeadbeefdeadbeef', 'seed'),
'table cast forgive master funny gaze sadness ripple million paint moral match'
), (
('deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef', 'seed'),
'swirl maybe anymore mix scale stray fog use approach page crime rhyme ' +
'class former strange window snap soon'
), (
('deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef', 'seed'),
'swell type milk figure cheese phone fill black test bloom heard comfort ' +
'image terrible radio lesson own reply battle goal goodbye need laugh stream'
), (
('ffffffffffffffffffffffffffffffff', 'seed'),
'yellow yeah show bowl season spider cling defeat poison law shelter reflect'
), (
('ffffffffffffffffffffffffffffffffffffffffffffffff', 'seed'),
'yeah youth quit fail perhaps drum out person young click skin ' +
'weird inside silently perfectly together anyone memory'
), (
('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 'seed'),
'wrote affection object cell opinion here laughter stare honest north cost begin ' +
'murder something yourself effort acid dot doubt game broke tell guilt innocent'
), (
('00000000000000000000000000000001', 'seed'),
'able ' * 11 + 'about'
), (
('000000000000000000000000000000000000000000000001', 'seed'),
'able ' * 17 + 'about'
), (
('0000000000000000000000000000000000000000000000000000000000000001', 'seed'),
'able ' * 23 + 'about'
),
),
}
def run_test(self,name,ut):
def run_test(self, name, ut):
msg_r('Testing base conversion routines...')
@ -164,38 +174,35 @@ class unit_test:
rerr = "return value ({!r}) does not match reference value ({!r})"
qmsg_r('\nChecking hex-to-base conversion:')
for base,data in self.vectors.items():
for base, data in self.vectors.items():
fs = " {h:%s} {p:<6} {r}" % max(len(d[0][0]) for d in data)
if not cfg.verbose:
qmsg_r(f' {base}')
vmsg(f'\nBase: {base}')
vmsg(fs.format(h='Input',p='Pad',r='Output'))
for (hexstr,pad),ret_chk in data:
ret = baseconv(base).fromhex(hexstr,pad=pad,tostr=True)
vmsg(fs.format(h='Input', p='Pad', r='Output'))
for (hexstr, pad), ret_chk in data:
ret = baseconv(base).fromhex(hexstr, pad=pad, tostr=True)
if pad != 'seed':
assert len(ret) >= (pad or 0), perr.format(ret,pad or 0)
assert ret == ret_chk, rerr.format(ret,ret_chk)
vmsg(fs.format(h=hexstr,r=ret,p=str(pad)))
# msg("(('{h}',{p}),'{r}'),".format(h=hexstr,r=ret,c=ret_chk,p=pad))
# msg('')
# return True
assert len(ret) >= (pad or 0), perr.format(ret, pad or 0)
assert ret == ret_chk, rerr.format(ret, ret_chk)
vmsg(fs.format(h=hexstr, r=ret, p=str(pad)))
qmsg_r('\nChecking base-to-hex conversion:')
for base,data in self.vectors.items():
for base, data in self.vectors.items():
fs = " {h:%s} {p:<6} {r}" % max(len(d[1]) for d in data)
if not cfg.verbose:
qmsg_r(f' {base}')
vmsg(f'\nBase: {base}')
vmsg(fs.format(h='Input',p='Pad',r='Output'))
for (hexstr,pad),ret_chk in data:
vmsg(fs.format(h='Input', p='Pad', r='Output'))
for (hexstr, pad), ret_chk in data:
if type(pad) is int:
pad = len(hexstr)
ret = baseconv(base).tohex( ret_chk.split() if base == 'mmgen' else ret_chk, pad=pad )
if pad is None:
assert int(ret,16) == int(hexstr,16), rerr.format(int(ret,16),int(hexstr,16))
assert int(ret, 16) == int(hexstr, 16), rerr.format(int(ret, 16), int(hexstr, 16))
else:
assert ret == hexstr, rerr.format(ret,hexstr)
vmsg(fs.format(h=ret_chk,r=ret,p=str(pad)))
# msg("(('{h}',{p}),'{r}'),".format(h=hexstr,r=ret_chk,c=ret_chk,p=pad))
assert ret == hexstr, rerr.format(ret, hexstr)
vmsg(fs.format(h=ret_chk, r=ret, p=str(pad)))
qmsg_r('\nChecking wordlist checksums:')
vmsg('')
@ -222,23 +229,23 @@ class unit_test:
sle = 'SeedLengthError'
bad_data = (
('hexstr', hse, ': not a hexadecimal str', lambda:fr58('x')),
('hexstr (seed)', hse, 'seed data not a hexadec', lambda:fr58('x',pad='seed')),
('hexstr (empty)', bce, 'empty data not allowed', lambda:fr58('')),
('b58 data', bce, ': not in base58', lambda:to58('IfFzZ')),
('b58 data (seed)', bce, 'seed data not in base58', lambda:to58(bad_b58,pad='seed')),
('b58 len (seed)', bce, 'invalid length for', lambda:to58(bad_b58len,pad='seed')),
('b58 data (empty)', bce, 'empty base58 data', lambda:to58('')),
('b8 data (empty)' , bce, 'empty base8 string data', lambda:to8('')),
('b32 data', bce, 'not in MMGen base32', lambda:to32('1az')),
('pad arg (in)', bpe, "illegal value for 'pad'", lambda:fr58('ff',pad='foo')),
('pad arg (in)', bpe, "illegal value for 'pad'", lambda:fr58('ff',pad=False)),
('pad arg (in)', bpe, "illegal value for 'pad'", lambda:fr58('ff',pad=True)),
('seedlen (in)', sle, 'invalid byte length', lambda:fr58('ff',pad='seed')),
('pad arg (out)', bpe, "illegal value for 'pad'", lambda:to58('Z',pad='foo')),
('pad arg (out)', bpe, "illegal value for 'pad'", lambda:to58('Z',pad=False)),
('pad arg (out)', bpe, "illegal value for 'pad'", lambda:to58('Z',pad=True)),
('seedlen (out)', bce, 'invalid length for seed', lambda:to58('Z',pad='seed')),
('hexstr', hse, ': not a hexadecimal str', lambda: fr58('x')),
('hexstr (seed)', hse, 'seed data not a hexadec', lambda: fr58('x', pad='seed')),
('hexstr (empty)', bce, 'empty data not allowed', lambda: fr58('')),
('b58 data', bce, ': not in base58', lambda: to58('IfFzZ')),
('b58 data (seed)', bce, 'seed data not in base58', lambda: to58(bad_b58, pad='seed')),
('b58 len (seed)', bce, 'invalid length for', lambda: to58(bad_b58len, pad='seed')),
('b58 data (empty)', bce, 'empty base58 data', lambda: to58('')),
('b8 data (empty)' , bce, 'empty base8 string data', lambda: to8('')),
('b32 data', bce, 'not in MMGen base32', lambda: to32('1az')),
('pad arg (in)', bpe, "illegal value for 'pad'", lambda: fr58('ff', pad='foo')),
('pad arg (in)', bpe, "illegal value for 'pad'", lambda: fr58('ff', pad=False)),
('pad arg (in)', bpe, "illegal value for 'pad'", lambda: fr58('ff', pad=True)),
('seedlen (in)', sle, 'invalid byte length', lambda: fr58('ff', pad='seed')),
('pad arg (out)', bpe, "illegal value for 'pad'", lambda: to58('Z', pad='foo')),
('pad arg (out)', bpe, "illegal value for 'pad'", lambda: to58('Z', pad=False)),
('pad arg (out)', bpe, "illegal value for 'pad'", lambda: to58('Z', pad=True)),
('seedlen (out)', bce, 'invalid length for seed', lambda: to58('Z', pad='seed')),
)
ut.process_bad_data(bad_data)

View file

@ -12,81 +12,96 @@ from ..include.common import cfg, qmsg, vmsg
class unit_tests:
vectors = (
( "00000000000000000000000000000000",
(
"00000000000000000000000000000000",
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
),
( "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",
), (
"7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",
"legal winner thank year wave sausage worth useful legal winner thank yellow"
),
( "80808080808080808080808080808080",
), (
"80808080808080808080808080808080",
"letter advice cage absurd amount doctor acoustic avoid letter advice cage above"
),
( "ffffffffffffffffffffffffffffffff",
), (
"ffffffffffffffffffffffffffffffff",
"zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong"
),
( "000000000000000000000000000000000000000000000000",
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon agent"
),
( "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",
"legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal will"
),
( "808080808080808080808080808080808080808080808080",
"letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter always"
),
( "ffffffffffffffffffffffffffffffffffffffffffffffff",
), (
"000000000000000000000000000000000000000000000000",
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon "
"abandon abandon abandon abandon abandon agent"
), (
"7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",
"legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth "
"useful legal will"
), (
"808080808080808080808080808080808080808080808080",
"letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor "
"acoustic avoid letter always"
), (
"ffffffffffffffffffffffffffffffffffffffffffffffff",
"zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo when"
),
( "0000000000000000000000000000000000000000000000000000000000000000",
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art"
),
( "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",
"legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth title"
),
( "8080808080808080808080808080808080808080808080808080808080808080",
"letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic bless"
),
( "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
), (
"0000000000000000000000000000000000000000000000000000000000000000",
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon "
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art"
), (
"7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",
"legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth "
"useful legal winner thank year wave sausage worth title"
), (
"8080808080808080808080808080808080808080808080808080808080808080",
"letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor "
"acoustic avoid letter advice cage absurd amount doctor acoustic bless"
), (
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo vote"
),
( "9e885d952ad362caeb4efe34a8e91bd2",
), (
"9e885d952ad362caeb4efe34a8e91bd2",
"ozone drill grab fiber curtain grace pudding thank cruise elder eight picnic"
),
( "6610b25967cdcca9d59875f5cb50b0ea75433311869e930b",
"gravity machine north sort system female filter attitude volume fold club stay feature office ecology stable narrow fog"
),
( "68a79eaca2324873eacc50cb9c6eca8cc68ea5d936f98787c60c7ebc74e6ce7c",
"hamster diagram private dutch cause delay private meat slide toddler razor book happy fancy gospel tennis maple dilemma loan word shrug inflict delay length"
),
( "c0ba5a8e914111210f2bd131f3d5e08d",
), (
"6610b25967cdcca9d59875f5cb50b0ea75433311869e930b",
"gravity machine north sort system female filter attitude volume fold club stay feature office "
"ecology stable narrow fog"
), (
"68a79eaca2324873eacc50cb9c6eca8cc68ea5d936f98787c60c7ebc74e6ce7c",
"hamster diagram private dutch cause delay private meat slide toddler razor book happy fancy "
"gospel tennis maple dilemma loan word shrug inflict delay length"
), (
"c0ba5a8e914111210f2bd131f3d5e08d",
"scheme spot photo card baby mountain device kick cradle pact join borrow"
),
( "6d9be1ee6ebd27a258115aad99b7317b9c8d28b6d76431c3",
"horn tenant knee talent sponsor spell gate clip pulse soap slush warm silver nephew swap uncle crack brave"
),
( "9f6a2878b2520799a44ef18bc7df394e7061a224d2c33cd015b157d746869863",
"panda eyebrow bullet gorilla call smoke muffin taste mesh discover soft ostrich alcohol speed nation flash devote level hobby quick inner drive ghost inside"
),
( "23db8160a31d3e0dca3688ed941adbf3",
), (
"6d9be1ee6ebd27a258115aad99b7317b9c8d28b6d76431c3",
"horn tenant knee talent sponsor spell gate clip pulse soap slush warm silver nephew swap uncle "
"crack brave"
), (
"9f6a2878b2520799a44ef18bc7df394e7061a224d2c33cd015b157d746869863",
"panda eyebrow bullet gorilla call smoke muffin taste mesh discover soft ostrich alcohol speed "
"nation flash devote level hobby quick inner drive ghost inside"
), (
"23db8160a31d3e0dca3688ed941adbf3",
"cat swing flag economy stadium alone churn speed unique patch report train"
),
( "8197a4a47f0425faeaa69deebc05ca29c0a5b5cc76ceacc0",
"light rule cinnamon wrap drastic word pride squirrel upgrade then income fatal apart sustain crack supply proud access"
),
( "066dca1a2bb7e8a1db2832148ce9933eea0f3ac9548d793112d9a95c9407efad",
"all hour make first leader extend hole alien behind guard gospel lava path output census museum junior mass reopen famous sing advance salt reform"
),
( "f30f8c1da665478f49b001d94c5fc452",
), (
"8197a4a47f0425faeaa69deebc05ca29c0a5b5cc76ceacc0",
"light rule cinnamon wrap drastic word pride squirrel upgrade then income fatal apart sustain "
"crack supply proud access"
), (
"066dca1a2bb7e8a1db2832148ce9933eea0f3ac9548d793112d9a95c9407efad",
"all hour make first leader extend hole alien behind guard gospel lava path output census museum "
"junior mass reopen famous sing advance salt reform"
), (
"f30f8c1da665478f49b001d94c5fc452",
"vessel ladder alter error federal sibling chat ability sun glass valve picture"
),
( "c10ec20dc3cd9f652c7fac2f1230f7a3c828389a14392f05",
"scissors invite lock maple supreme raw rapid void congress muscle digital elegant little brisk hair mango congress clump"
),
( "f585c11aec520db57dd353c69554b21a89b20fb0650966fa0a9d6f74fd989d8f",
"void come effort suffer camp survey warrior heavy shoot primary clutch crush open amazing screen patrol group space point ten exist slush involve unfold"
), (
"c10ec20dc3cd9f652c7fac2f1230f7a3c828389a14392f05",
"scissors invite lock maple supreme raw rapid void congress muscle digital elegant little brisk "
"hair mango congress clump"
), (
"f585c11aec520db57dd353c69554b21a89b20fb0650966fa0a9d6f74fd989d8f",
"void come effort suffer camp survey warrior heavy shoot primary clutch crush open amazing screen "
"patrol group space point ten exist slush involve unfold"
)
)
def conversion(self,name,ut):
def conversion(self, name, ut):
vmsg('')
qmsg(blue('Testing BIP39 conversion routines'))
@ -99,7 +114,7 @@ class unit_tests:
for v in self.vectors:
chk = tuple(v[1].split())
vmsg(' '+v[1])
res = b.fromhex( v[0] )
res = b.fromhex(v[0])
assert res == chk, f'mismatch:\nres: {res}\nchk: {chk}'
vmsg('')
@ -107,7 +122,7 @@ class unit_tests:
for v in self.vectors:
chk = v[0]
vmsg(' '+chk)
res = b.tohex( v[1].split() )
res = b.tohex(v[1].split())
assert res == chk, f'mismatch:\nres: {res}\nchk: {chk}'
qmsg('OK')
@ -115,7 +130,7 @@ class unit_tests:
return True
def errors(self,name,ut):
def errors(self, name, ut):
vmsg('')
qmsg(blue('Testing error handling'))
@ -131,15 +146,15 @@ class unit_tests:
th = b.tohex
fh = b.fromhex
bad_data = (
('hex', 'AssertionError', 'not a hexadecimal',lambda:fh('xx')),
('seed len', 'AssertionError', 'invalid seed bit', lambda:fh(bad_seed)),
('mnemonic type', 'AssertionError', 'must be list', lambda:th('string')),
('arg (tostr=True)', 'AssertionError', "'tostr' must be", lambda:fh(good_seed,tostr=True)),
('pad len (fromhex)','AssertionError', "invalid 'pad' arg",lambda:fh(good_seed,pad=23)),
('pad len (tohex)', 'AssertionError', "invalid 'pad' arg",lambda:th(good_mn,pad=23)),
('word', 'MnemonicError', "not in the BIP39", lambda:th(bad_word_mn)),
('checksum', 'MnemonicError', "checksum", lambda:th(bad_chksum_mn)),
('seed phrase len', 'MnemonicError', "phrase len", lambda:th(bad_len_mn)),
('hex', 'AssertionError', 'not a hexadecimal', lambda: fh('xx')),
('seed len', 'AssertionError', 'invalid seed bit', lambda: fh(bad_seed)),
('mnemonic type', 'AssertionError', 'must be list', lambda: th('string')),
('arg (tostr=True)', 'AssertionError', "'tostr' must be", lambda: fh(good_seed, tostr=True)),
('pad len (fromhex)', 'AssertionError', "invalid 'pad' arg", lambda: fh(good_seed, pad=23)),
('pad len (tohex)', 'AssertionError', "invalid 'pad' arg", lambda: th(good_mn, pad=23)),
('word', 'MnemonicError', "not in the BIP39", lambda: th(bad_word_mn)),
('checksum', 'MnemonicError', "checksum", lambda: th(bad_chksum_mn)),
('seed phrase len', 'MnemonicError', "phrase len", lambda: th(bad_len_mn)),
)
ut.process_bad_data(bad_data)
@ -149,7 +164,7 @@ class unit_tests:
return True
def genseed(self,name,ut):
def genseed(self, name, ut):
vmsg('')
qmsg(blue('Testing seed generation with password'))
@ -165,7 +180,7 @@ class unit_tests:
assert seed_hex == '3c30b98d3d9a713cf5a7a42f5dd27b3bf7f4d792d2b9225f6f519a0da978e13c6f36989ef2123b12a96d6ad5a443a95d61022ffaa9fbce8f946da7b67f75d339'
passwd = 'passw0rd'
seed_hex = bip39().generate_seed(mnemonic.split(),passwd).hex()
seed_hex = bip39().generate_seed(mnemonic.split(), passwd).hex()
vmsg(f' Password: {orange(passwd)}\n {seed_hex}')
assert seed_hex == '7eb773bf60f1a5071f96736b6ddbe5c544a7b7740182a80493e29577e58b7cde011d4e38d26f65dab6c9fdebe5594e523447a1427ffd60746e6d04b4daa42eb1'

View file

@ -12,11 +12,11 @@
test.unit_tests_d.ut_bip_hd: bip_hd unit test for the MMGen suite
"""
from mmgen.color import gray,pink,blue
from mmgen.color import gray, pink, blue
from mmgen.util import fmt
from mmgen.bip_hd import Bip32ExtendedKey,BipHDConfig,BipHDNode,MasterNode,get_chain_params
from mmgen.bip_hd import Bip32ExtendedKey, BipHDConfig, BipHDNode, MasterNode, get_chain_params
from ..include.common import cfg,vmsg
from ..include.common import cfg, vmsg
# Source: BIP-32
vectors_bip32 = [
@ -46,7 +46,7 @@ vectors_bip32 = [
'xpub': 'xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy',
'xprv': 'xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76',
},
},{
}, {
'seed': 'fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542',
'm': {
'xpub': 'xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB',
@ -72,7 +72,7 @@ vectors_bip32 = [
'xpub': 'xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt',
'xprv': 'xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j',
},
},{
}, {
'comment': 'These vectors test for the retention of leading zeros. See bitpay/bitcore-lib#47 and iancoleman/bip39#58 for more information.',
'seed': '4b381541583be4423346c643850da4b320e46a87ae3d2a4e6da11eba819cd4acba45d239319ac14f863b8d5ab5a0d0c64d2e8a1e7d1457df2e5a3c51c73235be',
'm': {
@ -83,7 +83,7 @@ vectors_bip32 = [
'xpub': 'xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y',
'xprv': 'xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L',
},
},{
}, {
'comment': 'These vectors test for the retention of leading zeros. See btcsuite/btcutil#172 for more information.',
'seed': '3ddd5602285899a946114506157c7997e5444528f3003f6134712147db19b678',
"m": {
@ -156,7 +156,7 @@ vectors_multicoin = {
'bnb_beacon': 'bnb179c3ymltqm4utlp089zxqeta5dvn48a305rhe5',
}
def wif2addr(cfg,wif):
def wif2addr(cfg, wif):
from mmgen.tool.coin import tool_cmd
return tool_cmd(
cfg = cfg.base_cfg,
@ -170,15 +170,15 @@ class unit_tests:
@property
def _seed(self):
if not hasattr(self,'__seed'):
if not hasattr(self, '__seed'):
with open('test/ref/98831F3A.bip39') as fh:
mnemonic = fh.read().strip()
from mmgen.bip39 import bip39
self.__seed = bip39().generate_seed(mnemonic.split())
return self.__seed
def chainparams(self,name,ut):
for bipnum,idx,chain,addr_cls in (
def chainparams(self, name, ut):
for bipnum, idx, chain, addr_cls in (
(44, 0, 'btc', 'P2PKH'),
(49, 0, 'btc', 'P2SH'),
(84, 0, 'btc', 'P2WPKH'),
@ -187,7 +187,7 @@ class unit_tests:
(44, 2, 'ltc', 'P2PKH'),
(44, 3, 'doge', 'P2PKH'),
):
res = get_chain_params(bipnum,chain)
res = get_chain_params(bipnum, chain)
assert res.idx == idx, res.idx
assert res.chain == chain.upper()
assert res.addr_cls == addr_cls
@ -195,35 +195,35 @@ class unit_tests:
vmsg('')
return True
def derive(self,name,ut):
def derive(self, name, ut):
vmsg('seed: 98831F3A (default derivation)')
m = MasterNode(cfg,self._seed)
m = MasterNode(cfg, self._seed)
purpose = m.init_cfg(coin='btc',addr_type='bech32').derive_private()
purpose = m.init_cfg(coin='btc', addr_type='bech32').derive_private()
vmsg(f' {purpose.address=}')
coin_type1 = purpose.derive_private()
coin_type2 = m.to_coin_type('btc',addr_type='bech32')
coin_type2 = m.to_coin_type('btc', addr_type='bech32')
assert coin_type1.address == coin_type2.address
vmsg(f' {coin_type1.address=}')
acct = coin_type2.derive_private(idx=0)
chain1 = acct.derive_private(idx=0,hardened=False)
chain1 = acct.derive_private(idx=0, hardened=False)
chain2 = m.to_chain(idx=0,coin='btc',addr_type='bech32',public=False)
chain2 = m.to_chain(idx=0, coin='btc', addr_type='bech32', public=False)
assert chain2.address == chain1.address
chain3 = m.to_coin_type(coin='btc',addr_type='bech32').to_chain(0,public=True)
chain3 = m.to_coin_type(coin='btc', addr_type='bech32').to_chain(0, public=True)
assert chain3.address == chain1.address
vmsg(f' {chain1.address=}')
a = BipHDNode.from_extended_key(cfg,'btc',chain2.xpub)
b = BipHDNode.from_extended_key(cfg,'btc',chain2.xprv)
a = BipHDNode.from_extended_key(cfg, 'btc', chain2.xpub)
b = BipHDNode.from_extended_key(cfg, 'btc', chain2.xprv)
vmsg(
'\n xpub:\n' +
fmt(str(Bip32ExtendedKey(b.xpub)),indent=' ')
fmt(str(Bip32ExtendedKey(b.xpub)), indent=' ')
)
assert a.xpub == b.xpub
@ -238,20 +238,20 @@ class unit_tests:
vmsg('')
return True
def derive_addrfmt(self,name,ut):
def derive_addrfmt(self, name, ut):
vmsg('seed: 98831F3A (default derivation)')
m = MasterNode(cfg,self._seed)
m = MasterNode(cfg, self._seed)
for addr_type in ('compressed','segwit','bech32'):
for addr_type in ('compressed', 'segwit', 'bech32'):
chk_xpub = vectors_addrfmt['pub'][addr_type]
chk_xprv = vectors_addrfmt['prv'][addr_type]
res1 = m.to_chain(idx=0,coin='btc',addr_type=addr_type).derive_public(0)
res1 = m.to_chain(idx=0, coin='btc', addr_type=addr_type).derive_public(0)
vmsg(f' {addr_type}: {res1.xpub}')
assert res1.xpub == chk_xpub
res2 = m.to_chain(idx=0,coin='btc',addr_type=addr_type).derive_private(0,False)
res2 = m.to_chain(idx=0, coin='btc', addr_type=addr_type).derive_private(0, False)
vmsg(f' {addr_type}: {res2.xprv}')
assert res2.xprv == chk_xprv
assert res2.xpub == chk_xpub
@ -261,55 +261,55 @@ class unit_tests:
vmsg('')
return True
def path(self,name,ut):
def path(self, name, ut):
for vec in vectors_bip32:
seed = bytes.fromhex(vec['seed'])
vmsg(f'Seed: {vec["seed"]}')
for n,path_str in enumerate(vec):
if path_str in ('seed','comment'):
for n, path_str in enumerate(vec):
if path_str in ('seed', 'comment'):
continue
path_arg = path_str.replace("'",'H') if n % 2 else path_str
node = BipHDNode.from_path(cfg,seed,path_arg,no_path_checks=True)
vmsg(' Path {} {}'.format(pink(path_str),blue('('+node.desc+')')))
path_arg = path_str.replace("'", 'H') if n % 2 else path_str
node = BipHDNode.from_path(cfg, seed, path_arg, no_path_checks=True)
vmsg(' Path {} {}'.format(pink(path_str), blue('('+node.desc+')')))
for xkey_type in ('xpub','xprv'):
vmsg(f' {getattr(node,xkey_type)}')
assert getattr(node,xkey_type) == vec[path_str][xkey_type]
for xkey_type in ('xpub', 'xprv'):
vmsg(f' {getattr(node, xkey_type)}')
assert getattr(node, xkey_type) == vec[path_str][xkey_type]
vmsg('')
return True
def parse_extended(self,name,ut):
def parse_extended(self, name, ut):
vmsg('Parsing and validating extended keys:\n')
for vec in vectors_bip32:
vmsg(f' Seed: {vec["seed"]}')
for path_str in vec:
if path_str in ('seed','comment'):
if path_str in ('seed', 'comment'):
continue
vmsg(' Path {}'.format(pink(path_str)))
for xkey_type in ('xpub','xprv'):
for xkey_type in ('xpub', 'xprv'):
xkey = vec[path_str][xkey_type]
vmsg(f' {xkey}')
node = BipHDNode.from_extended_key(cfg,'btc',xkey)
assert getattr(node,xkey_type) == xkey
node = BipHDNode.from_extended_key(cfg, 'btc', xkey)
assert getattr(node, xkey_type) == xkey
vmsg('')
return True
def multicoin(self,name,ut):
m = MasterNode(cfg,self._seed)
def multicoin(self, name, ut):
m = MasterNode(cfg, self._seed)
fs = ' {:6} {:10} {}'
vmsg(fs.format('COIN','ADDR_TYPE','ADDR'))
for id_str,addr_chk in vectors_multicoin.items():
vmsg(fs.format('COIN', 'ADDR_TYPE', 'ADDR'))
for id_str, addr_chk in vectors_multicoin.items():
ss = id_str.split('_')
coin = ss[0]
addr_type = ss[1] if len(ss) == 2 else None
@ -317,7 +317,7 @@ class unit_tests:
vmsg(gray(fs.format(coin.upper(), (addr_type or ''), '[not supported yet]')))
continue
vmsg(fs.format(coin.upper(), (addr_type or 'auto'), addr_chk))
node = m.to_chain(idx=0,coin=coin,addr_type=addr_type).derive_private(0)
node = m.to_chain(idx=0, coin=coin, addr_type=addr_type).derive_private(0)
xpub_parsed = node.key_extended(public=True)
xprv_parsed = node.key_extended(public=False)
addr = node.address
@ -339,19 +339,19 @@ class unit_tests:
vmsg('')
return True
def errors(self,name,ut):
def errors(self, name, ut):
vmsg('Checking error handling:')
m = MasterNode(cfg,self._seed)
m_btc = m.init_cfg(coin='btc',addr_type='bech32')
m = MasterNode(cfg, self._seed)
m_btc = m.init_cfg(coin='btc', addr_type='bech32')
purpose = m_btc.derive_private()
coin_type = purpose.derive_private()
acct = coin_type.derive_private(idx=0)
chain = acct.derive_private(idx=0,hardened=False)
chain = acct.derive_private(idx=0, hardened=False)
def bad01():
m.to_chain(idx=0,coin='erq',addr_type='C')
m.to_chain(idx=0, coin='erq', addr_type='C')
def bad02():
m_btc.derive_private(idx=0)
def bad03():
@ -365,13 +365,13 @@ class unit_tests:
def bad08():
m_btc.derive_public() # must be private
def bad09():
coin_type.derive_private(idx=8,hardened=False)
coin_type.derive_private(idx=8, hardened=False)
def bad10():
acct.derive_private()
def bad11():
chain.derive_private()
def bad12():
chain.derive_private(hardened=True,idx=3)
chain.derive_private(hardened=True, idx=3)
bad_data = (
('unsupported coin', 'ValueError', 'not supported', bad01),
@ -387,14 +387,14 @@ class unit_tests:
('depth 5 (leaf node): hardened True', 'ValueError', 'must be None', bad12),
)
ut.process_bad_data(bad_data,pfx='')
ut.process_bad_data(bad_data, pfx='')
vmsg('')
return True
def parse_extended_errors(self,name,ut):
def parse_extended_errors(self, name, ut):
vmsg('Parsing and validating invalid extended keys:')
vec = vectors_bip32_invalid
func = [lambda m=n: BipHDNode.from_extended_key(cfg,'btc',vec[m][0]) for n in range(len(vec))]
func = [lambda m=n: BipHDNode.from_extended_key(cfg, 'btc', vec[m][0]) for n in range(len(vec))]
exc = (
'first byte for public',
'first byte for private',
@ -413,6 +413,6 @@ class unit_tests:
'Public key could not be parsed', # extmod
'incorrect checksum',
)
ut.process_bad_data([(vec[n][1], 'ValueError', exc[n], func[n]) for n in range(len(vec))],pfx='')
ut.process_bad_data([(vec[n][1], 'ValueError', exc[n], func[n]) for n in range(len(vec))], pfx='')
vmsg('')
return True

View file

@ -135,7 +135,7 @@ class unit_tests:
def errors(self, name, ut, desc='error handling'):
# could do these in objtest.py:
def bad1(): a = CoinAddr(proto, self.aliases[0].cashaddr.replace('g','G'))
def bad1(): a = CoinAddr(proto, self.aliases[0].cashaddr.replace('g', 'G'))
def bad2(): a = CoinAddr(proto, 'x' + self.aliases[0].cashaddr)
def bad3(): a = CoinAddr(proto, self.aliases[0].cashaddr[:-1])
def bad4(): a = CoinAddr(proto, self.aliases[0].cashaddr[:-1]+'i')

View file

@ -4,7 +4,7 @@
test.unit_tests_d.ut_daemon: unit test for the MMGen suite's Daemon class
"""
from subprocess import run,PIPE
from subprocess import run, PIPE
from collections import namedtuple
from mmgen.color import orange, red
@ -14,19 +14,19 @@ from mmgen.daemon import CoinDaemon
from ..include.common import cfg, qmsg, qmsg_r, vmsg, msg
def test_flags():
d = CoinDaemon(cfg,'eth')
vmsg(f'Available opts: {fmt_list(d.avail_opts,fmt="bare")}')
vmsg(f'Available flags: {fmt_list(d.avail_flags,fmt="bare")}')
vals = namedtuple('vals',['online','no_daemonize','keep_cfg_file'])
d = CoinDaemon(cfg, 'eth')
vmsg(f'Available opts: {fmt_list(d.avail_opts, fmt="bare")}')
vmsg(f'Available flags: {fmt_list(d.avail_flags, fmt="bare")}')
vals = namedtuple('vals', ['online', 'no_daemonize', 'keep_cfg_file'])
def gen():
for opts,flags,val in (
(None,None, vals(False,False,False)),
(None,['keep_cfg_file'], vals(False,False,True)),
(['online'],['keep_cfg_file'], vals(True,False,True)),
(['online','no_daemonize'],['keep_cfg_file'], vals(True,True,True)),
for opts, flags, val in (
(None, None, vals(False, False, False)),
(None, ['keep_cfg_file'], vals(False, False, True)),
(['online'], ['keep_cfg_file'], vals(True, False, True)),
(['online', 'no_daemonize'], ['keep_cfg_file'], vals(True, True, True)),
):
d = CoinDaemon(cfg,'eth',opts=opts,flags=flags)
d = CoinDaemon(cfg, 'eth', opts=opts, flags=flags)
assert d.flag.keep_cfg_file == val.keep_cfg_file
assert d.opt.online == val.online
assert d.opt.no_daemonize == val.no_daemonize
@ -36,7 +36,7 @@ def test_flags():
return tuple(gen())
def test_flags_err(ut,d):
def test_flags_err(ut, d):
def bad1(): d[0].flag.foo = False
def bad2(): d[0].opt.foo = False
@ -47,23 +47,23 @@ def test_flags_err(ut,d):
def bad7(): d[1].flag.keep_cfg_file = True
ut.process_bad_data((
('flag (1)', 'ClassFlagsError', 'unrecognized flag', bad1 ),
('opt (1)', 'ClassFlagsError', 'unrecognized opt', bad2 ),
('opt (2)', 'AttributeError', 'is read-only', bad3 ),
('flag (2)', 'AssertionError', 'not boolean', bad4 ),
('opt (3)', 'AttributeError', 'is read-only', bad5 ),
('flag (3)', 'ClassFlagsError', 'not set', bad6 ),
('flag (4)', 'ClassFlagsError', 'already set', bad7 ),
('flag (1)', 'ClassFlagsError', 'unrecognized flag', bad1),
('opt (1)', 'ClassFlagsError', 'unrecognized opt', bad2),
('opt (2)', 'AttributeError', 'is read-only', bad3),
('flag (2)', 'AssertionError', 'not boolean', bad4),
('opt (3)', 'AttributeError', 'is read-only', bad5),
('flag (3)', 'ClassFlagsError', 'not set', bad6),
('flag (4)', 'ClassFlagsError', 'already set', bad7),
))
class unit_tests:
win_skip = ('start','status','stop')
win_skip = ('start', 'status', 'stop')
def _pre(self):
self.daemon_ctrl_args = ['btc','btc_tn','btc_rt'] if cfg.no_altcoin_deps else ['all']
self.daemon_ctrl_args = ['btc', 'btc_tn', 'btc_rt'] if cfg.no_altcoin_deps else ['all']
def _test_cmd(self,args_in,message):
def _test_cmd(self, args_in, message):
qmsg_r(message)
args = ['python3', f'test/{args_in[0]}-coin-daemons.py'] + list(args_in[1:]) + self.daemon_ctrl_args
vmsg('\n' + orange(f"Running '{' '.join(args)}':"))
@ -77,7 +77,7 @@ class unit_tests:
qmsg('OK')
return True
def flags(self,name,ut):
def flags(self, name, ut):
qmsg_r('Testing flags and opts...')
vmsg('')
@ -86,22 +86,22 @@ class unit_tests:
qmsg_r('Testing error handling for flags and opts...')
vmsg('')
test_flags_err(ut,daemons)
test_flags_err(ut, daemons)
qmsg('OK')
return True
def exec(self,name,ut):
return self._test_cmd(['start','-Vm'], 'Testing availability of coin daemons...')
def exec(self, name, ut):
return self._test_cmd(['start', '-Vm'], 'Testing availability of coin daemons...')
def cmds(self,name,ut):
return self._test_cmd(['start','-t'], 'Testing start commands for coin daemons...')
def cmds(self, name, ut):
return self._test_cmd(['start', '-t'], 'Testing start commands for coin daemons...')
def start(self,name,ut):
def start(self, name, ut):
return self._test_cmd(['start'], 'Starting coin daemons...')
def status(self,name,ut):
def status(self, name, ut):
return self._test_cmd(['start'], 'Checking status of coin daemons...')
def stop(self,name,ut):
def stop(self, name, ut):
return self._test_cmd(['stop'], 'Stopping coin daemons...')

View file

@ -7,19 +7,19 @@ test.unit_tests_d.ut_dep: dependency unit tests for the MMGen suite
No data verification is performed.
"""
from subprocess import run,PIPE
from subprocess import run, PIPE
from mmgen.util import msg,rmsg,ymsg,gmsg
from mmgen.util import msg, rmsg, ymsg, gmsg
from mmgen.exception import NoLEDSupport
from ..include.common import cfg,vmsg,check_solc_ver
from ..include.common import cfg, vmsg, check_solc_ver
class unit_tests:
altcoin_deps = ('py_ecc','solc','keccak','pysocks')
altcoin_deps = ('py_ecc', 'solc', 'keccak', 'pysocks')
win_skip = ('led',)
def led(self,name,ut):
def led(self, name, ut):
from mmgen.led import LEDControl
try:
LEDControl(enabled=True)
@ -29,7 +29,7 @@ class unit_tests:
gmsg('LED support found!')
return True
def keccak(self,name,ut): # used by ETH, XMR
def keccak(self, name, ut): # used by ETH, XMR
from mmgen.util2 import get_keccak
try:
get_keccak()
@ -39,12 +39,12 @@ class unit_tests:
else:
return True
def py_ecc(self,name,ut): # ETH
def py_ecc(self, name, ut): # ETH
from py_ecc.secp256k1 import privtopub
return True
def pysocks(self,name,ut):
import requests,urllib3
def pysocks(self, name, ut):
import requests, urllib3
urllib3.disable_warnings()
session = requests.Session()
session.trust_env = False
@ -59,62 +59,62 @@ class unit_tests:
msg('Is the ‘pysocks’ package installed?')
return False
def secp256k1(self,name,ut):
def secp256k1(self, name, ut):
from mmgen.proto.secp256k1.secp256k1 import pubkey_gen
pubkey_gen(bytes.fromhex('deadbeef'*8),1)
pubkey_gen(bytes.fromhex('deadbeef'*8), 1)
return True
def cryptography(self,name,ut):
from cryptography.hazmat.primitives.ciphers import Cipher,algorithms,modes
def cryptography(self, name, ut):
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
c = Cipher(algorithms.AES(b'deadbeef'*4),modes.CTR(b'deadbeef'*2),backend=default_backend())
c = Cipher(algorithms.AES(b'deadbeef'*4), modes.CTR(b'deadbeef'*2), backend=default_backend())
encryptor = c.encryptor()
encryptor.update(b'foo') + encryptor.finalize()
return True
def ecdsa(self,name,ut):
def ecdsa(self, name, ut):
import ecdsa
pko = ecdsa.SigningKey.from_secret_exponent(12345678901234,curve=ecdsa.SECP256k1)
pko = ecdsa.SigningKey.from_secret_exponent(12345678901234, curve=ecdsa.SECP256k1)
pko.get_verifying_key().to_string().hex()
return True
def ripemd160(self,name,ut):
def ripemd160(self, name, ut):
import hashlib
if hashlib.new.__name__ == 'hashlib_new_wrapper':
ymsg('Warning: RIPEMD160 missing in hashlib, falling back on pure-Python implementation')
hashlib.new('ripemd160')
return True
def gmpy(self,name,ut):
from gmpy2 import context,set_context,sqrt,cbrt
def gmpy(self, name, ut):
from gmpy2 import context, set_context, sqrt, cbrt
# context() parameters are platform-dependent!
set_context(context(precision=75,round=1)) # OK for gmp 6.1.2 / gmpy 2.1.0
set_context(context(precision=75, round=1)) # OK for gmp 6.1.2 / gmpy 2.1.0
return True
def aiohttp(self,name,ut):
import asyncio,aiohttp
def aiohttp(self, name, ut):
import asyncio, aiohttp
async def do():
async with aiohttp.ClientSession(
headers = { 'Content-Type': 'application/json' },
headers = {'Content-Type': 'application/json'},
connector = aiohttp.TCPConnector(),
):
pass
asyncio.run(do())
return True
def pexpect(self,name,ut):
def pexpect(self, name, ut):
import pexpect
from pexpect.popen_spawn import PopenSpawn
return True
def scrypt(self,name,ut):
passwd,salt = b'foo',b'bar'
N,r,p = 4,8,16
def scrypt(self, name, ut):
passwd, salt = b'foo', b'bar'
N, r, p = 4, 8, 16
buflen = 64
vmsg('Testing builtin scrypt module (hashlib)')
from hashlib import scrypt # max N == 14!!
scrypt(password=passwd,salt=salt,n=2**N,r=r,p=p,maxmem=0,dklen=buflen)
scrypt(password=passwd, salt=salt, n=2**N, r=r, p=p, maxmem=0, dklen=buflen)
vmsg('Testing standalone scrypt module')
import scrypt
@ -122,7 +122,7 @@ class unit_tests:
return True
def solc(self,name,ut):
def solc(self, name, ut):
from mmgen.protocol import init_proto
solc_ok = check_solc_ver()
if solc_ok:
@ -135,9 +135,9 @@ class unit_tests:
'--supply=100000000000000000000000000',
'--decimals=18',
'--stdout',
init_proto( cfg, 'eth' ).checksummed_addr('deadbeef'*5),
init_proto(cfg, 'eth').checksummed_addr('deadbeef'*5),
]
cp = run(cmd,stdout=PIPE,stderr=PIPE)
cp = run(cmd, stdout=PIPE, stderr=PIPE)
vmsg(cp.stderr.decode())
if cp.returncode:
msg(cp.stderr.decode())

View file

@ -4,9 +4,9 @@
test.unit_tests_d.ut_devtools: devtools unit tests for the MMGen suite
"""
import os,json
import os, json
from mmgen.util import msg
from mmgen.devtools import print_diff,get_ndiff,print_stack_trace,pmsg_r,pmsg,Pmsg
from mmgen.devtools import print_diff, get_ndiff, print_stack_trace, pmsg_r, pmsg, Pmsg
from . import unit_tests_base
textA = """
@ -47,34 +47,34 @@ def print_hdr(hdr):
print('{a} {b} {c}'.format(
a = '-' * ((78 - len(hdr))//2),
b = hdr,
c = '-' * ((78 - len(hdr))//2 + (len(hdr) % 2)) ))
c = '-' * ((78 - len(hdr))//2 + (len(hdr) % 2))))
# TODO: add data checks
class unit_tests(unit_tests_base):
silence_output = True
def _post_subtest(self,name,subname,ut):
def _post_subtest(self, name, subname, ut):
print('-' * 80 + '\n')
def diff(self,name,ut):
def diff(self, name, ut):
for data in text_data + json_data:
print_hdr(data[-1])
print_diff(*data[:-1])
return True
def ndiff(self,name,ut):
def ndiff(self, name, ut):
for data in text_data:
print_hdr(data[-1])
print('\n'.join(get_ndiff(*data[:2])))
return True
def stack_trace(self,name,ut):
def stack_trace(self, name, ut):
print_hdr('stack trace')
print_stack_trace('Test',fh_list=[open(os.devnull,'w')],trim=0)
print_stack_trace('Test', fh_list=[open(os.devnull, 'w')], trim=0)
return True
def obj_pmsg(self,name,ut):
def obj_pmsg(self, name, ut):
from mmgen.protocol import init_proto
from mmgen.seed import Seed
from mmgen.addrlist import AddrList
@ -82,26 +82,26 @@ class unit_tests(unit_tests_base):
print_hdr('MMGenObject.pmsg()')
AddrList(
cfg = cfg,
proto = init_proto( cfg, 'btc' ),
seed = Seed(cfg,seed_bin=bytes.fromhex('bead'*16)),
proto = init_proto(cfg, 'btc'),
seed = Seed(cfg, seed_bin=bytes.fromhex('bead'*16)),
addr_idxs = '1',
mmtype = 'B',
skip_chksum = True ).pmsg(color='green')
skip_chksum = True).pmsg(color='green')
return True
def pmsg(self,name,ut):
colors = (None,'red','green','yellow','blue','purple')
def pmsg(self, name, ut):
colors = (None, 'red', 'green', 'yellow', 'blue', 'purple')
msg('\npmsg_r():')
for color in colors:
pmsg_r({'color':color},color=color)
pmsg_r({'color':color}, color=color)
msg('\n\npmsg():')
for color in colors:
pmsg({'color':color},color=color)
pmsg({'color':color}, color=color)
msg('\nPmsg():')
for color in colors:
Pmsg({'color':color},color=color)
Pmsg({'color':color}, color=color)
return True

View file

@ -4,7 +4,7 @@
test.unit_tests_d.ut_ecc: elliptic curve unit test for the MMGen suite
"""
from mmgen.proto.secp256k1.secp256k1 import pubkey_gen,pubkey_tweak_add,pubkey_check
from mmgen.proto.secp256k1.secp256k1 import pubkey_gen, pubkey_tweak_add, pubkey_check
from ..include.common import vmsg
from ..include.ecc import pubkey_tweak_add_pyecdsa
@ -14,9 +14,9 @@ secp256k1_group_order = CoinProtocol.Secp256k1.secp256k1_group_order
class unit_tests:
def pubkey_ops(self,name,ut):
def pubkey_ops(self, name, ut):
vmsg(' Generating pubkey, adding scalar 123456789 to pubkey:')
pk_addend_bytes = int.to_bytes(123456789,length=32,byteorder='big')
pk_addend_bytes = int.to_bytes(123456789, length=32, byteorder='big')
for privkey in (
'beadcafe' * 8,
@ -24,9 +24,9 @@ class unit_tests:
f'{secp256k1_group_order-1:x}',
):
vmsg(f' privkey = 0x{privkey}')
for compressed,length in ((False,65),(True,33)):
for compressed, length in ((False, 65), (True, 33)):
vmsg(f' {compressed=}')
pubkey_bytes = pubkey_gen(bytes.fromhex(privkey),int(compressed))
pubkey_bytes = pubkey_gen(bytes.fromhex(privkey), int(compressed))
pubkey_check(pubkey_bytes)
vmsg(f' pubkey: {pubkey_bytes.hex()}')
@ -34,7 +34,7 @@ class unit_tests:
pubkey_check(res1)
vmsg(f' tweaked: {res1.hex()}')
res2 = pubkey_tweak_add_pyecdsa(pubkey_bytes,pk_addend_bytes)
res2 = pubkey_tweak_add_pyecdsa(pubkey_bytes, pk_addend_bytes)
pubkey_check(res2)
assert len(res1) == length
@ -42,18 +42,18 @@ class unit_tests:
return True
def pubkey_errors(self,name,ut):
def pubkey_errors(self, name, ut):
def gen1(): pubkey_gen(bytes(32),1)
def gen2(): pubkey_gen(secp256k1_group_order.to_bytes(length=32,byteorder='big'),1)
def gen3(): pubkey_gen((secp256k1_group_order+1).to_bytes(length=32,byteorder='big'),1)
def gen4(): pubkey_gen(bytes.fromhex('ff'*32),1)
def gen5(): pubkey_gen(bytes.fromhex('ab'*31),1)
def gen6(): pubkey_gen(bytes.fromhex('ab'*33),1)
def gen1(): pubkey_gen(bytes(32), 1)
def gen2(): pubkey_gen(secp256k1_group_order.to_bytes(length=32, byteorder='big'), 1)
def gen3(): pubkey_gen((secp256k1_group_order+1).to_bytes(length=32, byteorder='big'), 1)
def gen4(): pubkey_gen(bytes.fromhex('ff'*32), 1)
def gen5(): pubkey_gen(bytes.fromhex('ab'*31), 1)
def gen6(): pubkey_gen(bytes.fromhex('ab'*33), 1)
pubkey_bytes = pubkey_gen(bytes.fromhex('beadcafe'*8), 1)
def tweak1(): pubkey_tweak_add(pubkey_bytes,bytes(32))
def tweak2(): pubkey_tweak_add(bytes.fromhex('03'*64),int.to_bytes(1,length=32,byteorder='big'))
def tweak1(): pubkey_tweak_add(pubkey_bytes, bytes(32))
def tweak2(): pubkey_tweak_add(bytes.fromhex('03'*64), int.to_bytes(1, length=32, byteorder='big'))
def check1(): pubkey_check(bytes.fromhex('04'*33))
def check2(): pubkey_check(bytes.fromhex('03'*65))
@ -79,5 +79,5 @@ class unit_tests:
('pubkey length == 0', 'ValueError', 'Serialized public key length not', check5),
)
ut.process_bad_data(bad_data,pfx='')
ut.process_bad_data(bad_data, pfx='')
return True

View file

@ -4,13 +4,13 @@
test.unit_tests_d.ut_flags: unit test for the MMGen suite's ClassFlags class
"""
from mmgen.flags import ClassOpts,ClassFlags
from mmgen.flags import ClassOpts, ClassFlags
from ..include.common import qmsg,qmsg_r,vmsg
from ..include.common import qmsg, qmsg_r, vmsg
class unit_test:
def run_test(self,name,ut):
def run_test(self, name, ut):
class MyClassOpts(ClassOpts):
reserved_attrs = ('foo',)
@ -18,12 +18,12 @@ class unit_test:
class cls1:
avail_opts = ()
avail_flags = ()
def __init__(self,opts=None,flags=None):
self.opt = ClassOpts(self,opts)
self.flag = ClassFlags(self,flags)
def __init__(self, opts=None, flags=None):
self.opt = ClassOpts(self, opts)
self.flag = ClassFlags(self, flags)
class cls2(cls1):
avail_opts = ('foo','bar')
avail_opts = ('foo', 'bar')
avail_flags = ('baz',)
class cls3(cls1):
@ -31,20 +31,20 @@ class unit_test:
class cls4(cls1):
avail_opts = ('foo',)
def __init__(self,opts=None,flags=None):
self.opt = MyClassOpts(self,opts)
def __init__(self, opts=None, flags=None):
self.opt = MyClassOpts(self, opts)
def test_flags():
def gen():
for n,cls in enumerate((
for n, cls in enumerate((
cls1(),
cls2(),
cls2(opts=['bar']),
cls2(flags=['baz']),
)):
vmsg(f'Cfg {n+1}:')
for k in ('opt','flag'):
vmsg(f' {k}s: {getattr(cls,k)}')
for k in ('opt', 'flag'):
vmsg(f' {k}s: {getattr(cls, k)}')
yield cls
return list(gen())
@ -61,15 +61,15 @@ class unit_test:
def bad9(): d[1].flag.baz = 'x'
ut.process_bad_data((
('flag (1)', 'ClassFlagsError', 'unrecognized flag', bad1 ),
('opt (1)', 'ClassFlagsError', 'unrecognized opt', bad2 ),
('avail_opts (1)', 'ClassFlagsError', 'underscore', bad3 ),
('avail_opts (2)', 'ClassFlagsError', 'reserved name', bad4 ),
('class invocation (1)', 'AssertionError', 'list or tuple', bad5 ),
('class invocation (2)', 'ClassFlagsError', 'unrecognized opt', bad6 ),
('flag (2)', 'ClassFlagsError', 'not set', bad7 ),
('flag (3)', 'ClassFlagsError', 'already set', bad8 ),
('flag (4)', 'AssertionError', 'not boolean', bad9 ),
('flag (1)', 'ClassFlagsError', 'unrecognized flag', bad1),
('opt (1)', 'ClassFlagsError', 'unrecognized opt', bad2),
('avail_opts (1)', 'ClassFlagsError', 'underscore', bad3),
('avail_opts (2)', 'ClassFlagsError', 'reserved name', bad4),
('class invocation (1)', 'AssertionError', 'list or tuple', bad5),
('class invocation (2)', 'ClassFlagsError', 'unrecognized opt', bad6),
('flag (2)', 'ClassFlagsError', 'not set', bad7),
('flag (3)', 'ClassFlagsError', 'already set', bad8),
('flag (4)', 'AssertionError', 'not boolean', bad9),
))
qmsg_r('Testing flags and opts...')

View file

@ -8,14 +8,14 @@ from mmgen.color import blue
from mmgen.protocol import init_proto
from mmgen.key import PrivKey
from mmgen.addr import MMGenAddrType
from mmgen.addrgen import KeyGenerator,AddrGenerator
from mmgen.addrgen import KeyGenerator, AddrGenerator
from mmgen.keygen import get_backends
from ..include.common import cfg,qmsg
from ..include.common import cfg, qmsg
# TODO: add viewkey checks
vectors = { # from tooltest2
'btc': ( (
'btc': ((
'5HwzecKMWD82ppJK3qMKpC7ohXXAwcyAN5VgdJ9PLFaAzpBG4sX',
'1C5VPtgq9xQ6AcTgMAR3J6GDrs72HC4pS1',
'legacy'
@ -30,26 +30,26 @@ vectors = { # from tooltest2
), (
'KwojSzt1VvW343mQfWQi3J537siAt5ktL2qbuCg1ZyKR8BLQ6UJm',
'bc1q6pqnfwwakuuejpm9w52ds342f9d5u36v0qnz7c',
'bech32' ),
'bech32'),
),
'eth': ( (
'eth': ((
'0000000000000000000000000000000000000000000000000000000000000001',
'7e5f4552091a69125d5dfcb7b8c2659029395bdf',
'ethereum',
), ),
'xmr': ( (
),),
'xmr': ((
'0000000000000000000000000000000000000000000000000000000000000001',
'42nsXK8WbVGTNayQ6Kjw5UdgqbQY5KCCufdxdCgF7NgTfjC69Mna7DJSYyie77hZTQ8H92G2HwgFhgEUYnDzrnLnQdF28r3',
'monero',
), ),
'zec': ( (
),),
'zec': ((
'SKxny894fJe2rmZjeuoE6GVfNkWoXfPp8337VrLLNWG56FjqVUYR',
'zceQDpyNwek7dKqF5ZuFGj7YrNVxh7X1aPkrVxDLVxWSiZAFDEuy5C7XNV8VhyZ3ghTPQ61xjCGiyLT3wqpiN1Yi6mdmaCq',
'zcash_z',
), ),
),),
}
def do_test(proto,wif,addr_chk,addr_type,internal_keccak):
def do_test(proto, wif, addr_chk, addr_type, internal_keccak):
if internal_keccak:
cfg.use_internal_keccak_module = True
@ -57,21 +57,21 @@ def do_test(proto,wif,addr_chk,addr_type,internal_keccak):
else:
add_msg = ''
at = MMGenAddrType(proto,addr_type)
privkey = PrivKey(proto,wif=wif)
at = MMGenAddrType(proto, addr_type)
privkey = PrivKey(proto, wif=wif)
for n,backend in enumerate(get_backends(at.pubkey_type)):
for n, backend in enumerate(get_backends(at.pubkey_type)):
kg = KeyGenerator( cfg, proto, at.pubkey_type, n+1 )
kg = KeyGenerator( cfg, proto, at.pubkey_type, n+1)
qmsg(blue(f' Testing backend {backend!r} for addr type {addr_type!r}{add_msg}'))
data = kg.gen_data(privkey)
for k,v in data._asdict().items():
if v and k in ('pubkey','viewkey_bytes'):
for k, v in data._asdict().items():
if v and k in ('pubkey', 'viewkey_bytes'):
qmsg(f' {k+":":19} {v.hex()}')
ag = AddrGenerator( cfg, proto, addr_type )
ag = AddrGenerator( cfg, proto, addr_type)
addr = ag.to_addr(data)
qmsg(f' addr: {addr}\n')
@ -79,27 +79,27 @@ def do_test(proto,wif,addr_chk,addr_type,internal_keccak):
cfg.use_internal_keccak_module = False
def do_tests(coin,internal_keccak=False):
proto = init_proto( cfg, coin )
for wif,addr,addr_type in vectors[coin]:
do_test(proto,wif,addr,addr_type,internal_keccak)
def do_tests(coin, internal_keccak=False):
proto = init_proto( cfg, coin)
for wif, addr, addr_type in vectors[coin]:
do_test(proto, wif, addr, addr_type, internal_keccak)
return True
class unit_tests:
altcoin_deps = ('eth','xmr','zec')
altcoin_deps = ('eth', 'xmr', 'zec')
def btc(self,name,ut):
def btc(self, name, ut):
return do_tests('btc')
def eth(self,name,ut):
def eth(self, name, ut):
do_tests('eth')
return do_tests('eth',internal_keccak=True)
return do_tests('eth', internal_keccak=True)
def xmr(self,name,ut):
def xmr(self, name, ut):
if not cfg.fast:
do_tests('xmr')
return do_tests('xmr',internal_keccak=True)
return do_tests('xmr', internal_keccak=True)
def zec(self,name,ut):
def zec(self, name, ut):
return do_tests('zec')

View file

@ -4,13 +4,13 @@
test/unit_tests_d/ut_indexed_dict: IndexedDict class unit test for the MMGen suite
"""
from mmgen.util import msg,msg_r,die
from mmgen.util import msg, msg_r, die
from ..include.common import vmsg
class unit_test:
def run_test(self,name,ut):
def run_test(self, name, ut):
bad_msg = (
'initializing values via constructor',
'reassignment to existing key',
@ -25,7 +25,7 @@ class unit_test:
def bad4(): d.clear()
def bad5(): d.update(d)
def odie(n): die(4,f'\nillegal action {bad_msg[n]!r} failed to raise exception')
def odie(n): die(4, f'\nillegal action {bad_msg[n]!r} failed to raise exception')
def omsg(e): vmsg(' - ' + e.args[0])
msg_r('Testing class IndexedDict...')
@ -38,7 +38,7 @@ class unit_test:
vmsg('\nChecking error handling:')
arg = [('a',1),('b',2)]
arg = [('a', 1), ('b', 2)]
dict(arg)
for n, func in enumerate([bad0, bad1, bad2, bad3, bad4, bad5]):
@ -58,10 +58,10 @@ class unit_test:
d['c'] = 3
d_chk = {'a':1,'b':2,'c':3}
d_chk = {'a':1, 'b':2, 'c':3}
assert d == d_chk, d
d_keys_chk = ['a','b','c']
d_keys_chk = ['a', 'b', 'c']
assert d.keys == d_keys_chk, d.keys
A = d.key(0)

View file

@ -66,8 +66,8 @@ class unit_tests:
class MyLockable(Lockable): # class without attrs
_autolock = False
_set_ok = ('foo','baz','alpha','beta','gamma','delta','epsilon')
_reset_ok = ('bar','baz')
_set_ok = ('foo', 'baz', 'alpha', 'beta', 'gamma', 'delta', 'epsilon')
_reset_ok = ('bar', 'baz')
lc = MyLockable()
lc.foo = None
@ -121,8 +121,8 @@ class unit_tests:
class MyLockableClsCheck(Lockable): # class with attrs
_autolock = False
_use_class_attr = True
_set_ok = ('foo','baz')
_reset_ok = ('bar','baz')
_set_ok = ('foo', 'baz')
_reset_ok = ('bar', 'baz')
foo = None
bar = 1
baz = 3.5
@ -171,7 +171,7 @@ class unit_tests:
assert lcdn.bar is None
class MyLockableBad(Lockable):
_set_ok = ('foo','bar')
_set_ok = ('foo', 'bar')
foo = 1
def bad1(): lca.foo = None

View file

@ -68,8 +68,8 @@ class unit_tests:
vs = namedtuple('vector_data', ['text', 'groups'])
fs = '{:16} {}'
vmsg(blue(' ' + fs.format('ID','ANNOT')))
for k,v in uarg_info.items():
vmsg(blue(' ' + fs.format('ID', 'ANNOT')))
for k, v in uarg_info.items():
vmsg(' ' + fs.format(k, v[0]))
vectors = {
@ -81,7 +81,7 @@ class unit_tests:
}
vmsg('')
for k,v in uarg_info.items():
for k, v in uarg_info.items():
vmsg(f' {k}')
if k in vectors:
vmsg(f' pat: {v.pat}')
@ -96,17 +96,17 @@ class unit_tests:
return True
def pyversion(self, name, ut, desc='class pyversion.PythonVersion'):
from mmgen.pyversion import PythonVersion,python_version
from mmgen.pyversion import PythonVersion, python_version
ver = {}
fs = '{:<7} {:<9} {:<5} {}'
vmsg('\n' + fs.format('Version','PyVersion','Major','Minor'))
vmsg('\n' + fs.format('Version', 'PyVersion', 'Major', 'Minor'))
for k in ('current','3.3','3.12','4.3','7.0'):
for k in ('current', '3.3', '3.12', '4.3', '7.0'):
obj = python_version if k == 'current' else PythonVersion(k)
major,minor = [int(s) for s in obj.split('.')]
major, minor = [int(s) for s in obj.split('.')]
assert obj.major == major and obj.minor == minor
vmsg(fs.format(k.upper(),obj,major,minor))
vmsg(fs.format(k.upper(), obj, major, minor))
ver[k] = obj
vmsg('\nPerforming comparison tests:')

View file

@ -13,37 +13,40 @@ class unit_tests:
'mmgen': {
'usl': 10, 'sw': 3, 'lw': 12,
'idx_minimal': ( # None: non-unique match. False: no match
('a', None),
('aa', False),
('as', None),
('ask', 70),
('afte', None),
('after', None),
('aftern', 20),
('afternoon', 20),
('afternoons',False),
('g', None),
('gg', False),
('z', False),
('abi', False),
('abo', None),
('abl', 0),
('able', 0),
('abler', False),
('you', None),
('yout', 1625),
('youth', 1625),
('youths', False),
('a', None),
('aa', False),
('as', None),
('ask', 70),
('afte', None),
('after', None),
('aftern', 20),
('afternoon', 20),
('afternoons', False),
('g', None),
('gg', False),
('z', False),
('abi', False),
('abo', None),
('abl', 0),
('able', 0),
('abler', False),
('you', None),
('yout', 1625),
('youth', 1625),
('youths', False),
),
},
'xmrseed': { 'usl': 3, 'sw': 4, 'lw': 12 },
'bip39': { 'usl': 4, 'sw': 3, 'lw': 8 },
'xmrseed': {'usl': 3, 'sw': 4, 'lw': 12},
'bip39': {'usl': 4, 'sw': 3, 'lw': 8},
}
def wl(self, name, ut, desc='MnemonicEntry - computed wordlist constants'):
for wl_id in self.vectors:
for j,k in (('uniq_ss_len','usl'),('shortest_word','sw'),('longest_word','lw')):
a = getattr(mn_entry( cfg, wl_id ),j)
for j, k in (
('uniq_ss_len', 'usl'),
('shortest_word', 'sw'),
('longest_word', 'lw')):
a = getattr(mn_entry(cfg, wl_id), j)
b = self.vectors[wl_id][k]
assert a == b, f'{wl_id}:{j} {a} != {b}'
return True
@ -51,24 +54,24 @@ class unit_tests:
def idx(self, name, ut, desc='MnemonicEntry - idx()'):
junk = 'a g z aa gg zz aaa ggg zzz aaaa gggg zzzz aaaaaaaaaaaaaa gggggggggggggg zzzzzzzzzzzzzz'
for wl_id in self.vectors:
m = mn_entry( cfg, wl_id )
m = mn_entry(cfg, wl_id)
vmsg('Wordlist: '+wl_id)
for entry_mode in ('full','short'):
for a,word in enumerate(m.wl):
b = m.idx(word,entry_mode)
for entry_mode in ('full', 'short'):
for a, word in enumerate(m.wl):
b = m.idx(word, entry_mode)
assert a == b, f'{a} != {b} ({word!r} - entry mode: {entry_mode!r})'
a = None
for word in junk.split():
b = m.idx(word,entry_mode)
b = m.idx(word, entry_mode)
assert a == b, f'{a} != {b} ({word!r} - entry mode: {entry_mode!r})'
if 'idx_minimal' in self.vectors[wl_id]:
for vec in self.vectors[wl_id]['idx_minimal']:
chk = vec[1]
b = m.idx(vec[0],'minimal')
b = m.idx(vec[0], 'minimal')
if chk is False:
assert b is None, (b,None)
assert b is None, (b, None)
elif chk is None:
assert type(b) is tuple, (type(b),tuple)
assert type(b) is tuple, (type(b), tuple)
elif type(chk) is int:
assert b == chk, (b,chk)
assert b == chk, (b, chk)
return True

View file

@ -8,12 +8,12 @@ import os
from mmgen.util import msg, pumsg, suf
from mmgen.protocol import CoinProtocol
from mmgen.msg import NewMsg,UnsignedMsg,SignedMsg,SignedOnlineMsg,ExportedMsgSigs
from mmgen.msg import NewMsg, UnsignedMsg, SignedMsg, SignedOnlineMsg, ExportedMsgSigs
from mmgen.addr import MMGenID
from ..include.common import cfg,silence,end_silence,restart_test_daemons,stop_test_daemons
from ..include.common import cfg, silence, end_silence, restart_test_daemons, stop_test_daemons
def get_obj(coin,network,msghash_type):
def get_obj(coin, network, msghash_type):
if coin == 'bch':
addrlists = 'DEADBEEF:C:1-20 98831F3A:C:8,2 A091ABAA:L:111 A091ABAA:C:1'
@ -29,85 +29,85 @@ def get_obj(coin,network,msghash_type):
network = network,
message = '08/Jun/2021 Bitcoin Law Enacted by El Salvador Legislative Assembly',
addrlists = addrlists,
msghash_type = msghash_type )
msghash_type = msghash_type)
def print_total(n):
msg(f'{n} signature{suf(n)} verified')
async def run_test(network_id,chksum,msghash_type='raw'):
async def run_test(network_id, chksum, msghash_type='raw'):
coin,network = CoinProtocol.Base.parse_network_id(network_id)
coin, network = CoinProtocol.Base.parse_network_id(network_id)
if not cfg.verbose:
silence()
m = get_obj(coin,network,msghash_type)
m = get_obj(coin, network, msghash_type)
if m.proto.sign_mode == 'daemon':
restart_test_daemons(network_id)
pumsg('\nTesting data creation:\n')
tmpdir = os.path.join('test','trash2')
tmpdir = os.path.join('test', 'trash2')
os.makedirs(tmpdir,exist_ok=True)
os.makedirs(tmpdir, exist_ok=True)
assert m.chksum.upper() == chksum, f'{m.chksum.upper()} != {chksum}'
m.write_to_file(
outdir = tmpdir,
ask_overwrite = False )
ask_overwrite = False)
pumsg('\nTesting signing:\n')
m = UnsignedMsg( cfg, infile = os.path.join(tmpdir,get_obj(coin,network,msghash_type).filename) )
m = UnsignedMsg(cfg, infile = os.path.join(tmpdir, get_obj(coin, network, msghash_type).filename))
await m.sign(wallet_files=['test/ref/98831F3A.mmwords'])
m = SignedMsg( cfg, data=m.__dict__ )
m = SignedMsg(cfg, data=m.__dict__)
m.write_to_file(
outdir = tmpdir,
ask_overwrite = False )
ask_overwrite = False)
pumsg('\nTesting display:\n')
m = SignedOnlineMsg( cfg, infile = os.path.join(tmpdir,get_obj(coin,network,msghash_type).signed_filename) )
m = SignedOnlineMsg(cfg, infile = os.path.join(tmpdir, get_obj(coin, network, msghash_type).signed_filename))
msg(m.format())
single_addr = 'A091ABAA:E:111' if m.proto.base_proto == 'Ethereum' else 'A091ABAA:111'
single_addr_coin = m.sigs[MMGenID(m.proto,single_addr)]['addr']
single_addr_coin = m.sigs[MMGenID(m.proto, single_addr)]['addr']
pumsg('\nTesting single address display:\n')
msg(m.format(single_addr))
pumsg('\nTesting verification:\n')
print_total( await m.verify() )
print_total(await m.verify())
pumsg('\nTesting single address verification:\n')
print_total( await m.verify(single_addr) )
print_total(await m.verify(single_addr))
pumsg('\nTesting JSON dump for export:\n')
msg( m.get_json_for_export() )
msg(m.get_json_for_export())
pumsg('\nTesting single address JSON dump for export:\n')
msg( m.get_json_for_export(single_addr) )
msg(m.get_json_for_export(single_addr))
from mmgen.fileutil import write_data_to_file
exported_sigs = os.path.join(tmpdir,'signatures.json')
exported_sigs = os.path.join(tmpdir, 'signatures.json')
write_data_to_file(
cfg = cfg,
outfile = exported_sigs,
data = m.get_json_for_export(),
desc = 'signature data',
ask_overwrite = False )
ask_overwrite = False)
m = ExportedMsgSigs( cfg, infile=exported_sigs )
m = ExportedMsgSigs(cfg, infile=exported_sigs)
pumsg('\nTesting verification (exported data):\n')
print_total( await m.verify() )
print_total(await m.verify())
pumsg('\nTesting single address verification (exported data):\n')
print_total( await m.verify(single_addr_coin) )
print_total(await m.verify(single_addr_coin))
pumsg('\nTesting display (exported data):\n')
msg(m.format())
@ -127,25 +127,25 @@ async def run_test(network_id,chksum,msghash_type='raw'):
class unit_tests:
altcoin_deps = ('ltc','bch','eth','eth_raw')
altcoin_deps = ('ltc', 'bch', 'eth', 'eth_raw')
def btc(self, name, ut, desc='Bitcoin mainnet'):
return run_test('btc','AA0DB5')
return run_test('btc', 'AA0DB5')
def btc_tn(self, name, ut, desc='Bitcoin testnet'):
return run_test('btc_tn','A88E1D')
return run_test('btc_tn', 'A88E1D')
def btc_rt(self, name, ut, desc='Bitcoin regtest'):
return run_test('btc_rt','578018')
return run_test('btc_rt', '578018')
def ltc(self, name, ut, desc='Litecoin mainnet'):
return run_test('ltc','BA7549')
return run_test('ltc', 'BA7549')
def bch(self, name, ut, desc='Bitcoin Cash mainnet'):
return run_test('bch','1B8065')
return run_test('bch', '1B8065')
def eth(self, name, ut, desc='Ethereum mainnet'):
return run_test('eth','35BAD9',msghash_type='eth_sign')
return run_test('eth', '35BAD9', msghash_type='eth_sign')
def eth_raw(self, name, ut, desc='Ethereum mainnet (raw message)'):
return run_test('eth','9D900C')
return run_test('eth', '9D900C')

View file

@ -34,7 +34,7 @@ def coinamt_test(cls, aa, bb, ut):
do('a', a, A)
do('b', b, B)
do('b + a', b + a, B + A)
do('sum([b,a])', sum([b,a]), B + A)
do('sum([b,a])', sum([b, a]), B + A)
do('b - a', b - a, B - A)
do('b * a', b * a, B * A)
do('b * A', b * A, B * A)

View file

@ -7,15 +7,15 @@ test.unit_tests_d.ut_rpc: RPC unit test for the MMGen suite
import sys, os
from mmgen.cfg import Config
from mmgen.color import yellow,cyan
from mmgen.util import msg,gmsg,make_timestr,pp_fmt,die
from mmgen.color import yellow, cyan
from mmgen.util import msg, gmsg, make_timestr, pp_fmt, die
from mmgen.protocol import init_proto
from mmgen.rpc import rpc_init
from mmgen.daemon import CoinDaemon
from mmgen.proto.xmr.rpc import MoneroRPCClient,MoneroWalletRPCClient
from mmgen.proto.xmr.rpc import MoneroRPCClient, MoneroWalletRPCClient
from mmgen.proto.xmr.daemon import MoneroWalletDaemon
from ..include.common import cfg,qmsg,vmsg
from ..include.common import cfg, qmsg, vmsg
async def cfg_file_auth_test(cfg, d, bad_auth=False):
m = 'missing credentials' if bad_auth else f'credentials from {d.cfg_file}'
@ -25,22 +25,22 @@ async def cfg_file_auth_test(cfg, d, bad_auth=False):
os.makedirs(d.network_datadir)
if not bad_auth:
cf = os.path.join(d.datadir,d.cfg_file)
with open(cf,'a') as fp:
cf = os.path.join(d.datadir, d.cfg_file)
with open(cf, 'a') as fp:
fp.write('\nrpcuser = ut_rpc\nrpcpassword = ut_rpc_passw0rd\n')
d.flag.keep_cfg_file = True
d.start()
if bad_auth:
os.rename(d.auth_cookie_fn,d.auth_cookie_fn+'.bak')
os.rename(d.auth_cookie_fn, d.auth_cookie_fn+'.bak')
try:
await rpc_init(cfg, d.proto)
except Exception as e:
vmsg(yellow(str(e)))
else:
die(3,'No error on missing credentials!')
os.rename(d.auth_cookie_fn+'.bak',d.auth_cookie_fn)
die(3, 'No error on missing credentials!')
os.rename(d.auth_cookie_fn+'.bak', d.auth_cookie_fn)
else:
rpc = await rpc_init(cfg, d.proto)
assert rpc.auth.user == 'ut_rpc', f'{rpc.auth.user}: user is not ut_rpc!'
@ -68,7 +68,7 @@ async def print_daemon_info(rpc):
if rpc.proto.base_proto == 'Bitcoin':
def fmt_dict(d):
return '\n ' + '\n '.join( pp_fmt(d).split('\n') ) + '\n'
return '\n ' + '\n '.join(pp_fmt(d).split('\n')) + '\n'
msg(f"""
NETWORKINFO: {fmt_dict(rpc.cached["networkinfo"])}
BLOCKCHAININFO: {fmt_dict(rpc.cached["blockchaininfo"])}
@ -78,30 +78,30 @@ async def print_daemon_info(rpc):
msg('')
def do_msg(rpc,backend):
def do_msg(rpc, backend):
bname = type(rpc.backend).__name__
qmsg(' Testing backend {!r}{}'.format( bname, '' if backend == bname else f' [{backend}]' ))
qmsg(' Testing backend {!r}{}'.format(bname, '' if backend == bname else f' [{backend}]'))
class init_test:
@staticmethod
async def btc(cfg, daemon, backend):
rpc = await rpc_init(cfg, daemon.proto, backend, daemon)
do_msg(rpc,backend)
do_msg(rpc, backend)
if cfg.tw_name:
wi = await rpc.walletinfo
assert wi['walletname'] == rpc.cfg.tw_name, f'{wi["walletname"]!r} != {rpc.cfg.tw_name!r}'
bh = (await rpc.call('getblockchaininfo',timeout=300))['bestblockhash']
await rpc.gathered_call('getblock',((bh,),(bh,1)),timeout=300)
await rpc.gathered_call(None,(('getblock',(bh,)),('getblock',(bh,1))),timeout=300)
bh = (await rpc.call('getblockchaininfo', timeout=300))['bestblockhash']
await rpc.gathered_call('getblock', ((bh,), (bh, 1)), timeout=300)
await rpc.gathered_call(None, (('getblock', (bh,)), ('getblock', (bh, 1))), timeout=300)
return rpc
@staticmethod
async def bch(cfg, daemon, backend):
rpc = await rpc_init(cfg, daemon.proto, backend, daemon)
do_msg(rpc,backend)
do_msg(rpc, backend)
return rpc
ltc = bch
@ -109,8 +109,8 @@ class init_test:
@staticmethod
async def eth(cfg, daemon, backend):
rpc = await rpc_init(cfg, daemon.proto, backend, daemon)
do_msg(rpc,backend)
await rpc.call('eth_blockNumber',timeout=300)
do_msg(rpc, backend)
await rpc.call('eth_blockNumber', timeout=300)
return rpc
etc = eth
@ -152,33 +152,33 @@ async def run_test(network_ids, test_cf_auth=False, daemon_ids=None, cfg_in=None
all_ids = CoinDaemon.get_daemon_ids(cfg_arg, proto.coin)
ids = set(daemon_ids) & set(all_ids) if daemon_ids else all_ids
for daemon_id in ids:
await do_test(CoinDaemon(cfg_arg, proto=proto,test_suite=True,daemon_id=daemon_id), cfg_arg)
await do_test(CoinDaemon(cfg_arg, proto=proto, test_suite=True, daemon_id=daemon_id), cfg_arg)
return True
class unit_tests:
altcoin_deps = ('ltc','bch','geth','erigon','parity','xmrwallet')
altcoin_deps = ('ltc', 'bch', 'geth', 'erigon', 'parity', 'xmrwallet')
arm_skip = ('parity',) # no prebuilt binaries for ARM
async def btc(self, name, ut):
return await run_test(
['btc','btc_tn'],
['btc', 'btc_tn'],
test_cf_auth = True,
cfg_in = Config({'_clone': cfg, 'tw_name': 'alternate-tracking-wallet'}))
async def ltc(self, name, ut):
return await run_test(['ltc','ltc_tn'], test_cf_auth=True)
return await run_test(['ltc', 'ltc_tn'], test_cf_auth=True)
async def bch(self, name, ut):
return await run_test(['bch','bch_tn'], test_cf_auth=True)
return await run_test(['bch', 'bch_tn'], test_cf_auth=True)
async def geth(self, name, ut):
# mainnet returns EIP-155 error on empty blockchain:
return await run_test(['eth_tn','eth_rt'], daemon_ids=['geth'])
return await run_test(['eth_tn', 'eth_rt'], daemon_ids=['geth'])
async def erigon(self, name, ut):
return await run_test(['eth','eth_tn','eth_rt'], daemon_ids=['erigon'])
return await run_test(['eth', 'eth_tn', 'eth_rt'], daemon_ids=['erigon'])
async def parity(self, name, ut):
return await run_test(['etc'])
@ -203,40 +203,40 @@ class unit_tests:
from mmgen.xmrseed import xmrseed
async def run():
networks = init_proto( cfg, 'xmr' ).networks
networks = init_proto(cfg, 'xmr').networks
daemons = [(
CoinDaemon( cfg, proto=proto, test_suite=True ),
CoinDaemon(cfg, proto=proto, test_suite=True),
MoneroWalletDaemon(
cfg = cfg,
proto = proto,
test_suite = True,
wallet_dir = os.path.join('test','trash2'),
datadir = os.path.join('test','trash2','wallet_rpc'),
passwd = 'ut_rpc_passw0rd' )
) for proto in (init_proto( cfg, 'xmr', network=network ) for network in networks) ]
wallet_dir = os.path.join('test', 'trash2'),
datadir = os.path.join('test', 'trash2', 'wallet_rpc'),
passwd = 'ut_rpc_passw0rd')
) for proto in (init_proto(cfg, 'xmr', network=network) for network in networks)]
for md,wd in daemons:
for md, wd in daemons:
if not cfg.no_daemon_autostart:
md.start()
wd.start()
await test_monerod_rpc(md)
c = MoneroWalletRPCClient( cfg=cfg, daemon=wd )
c = MoneroWalletRPCClient(cfg=cfg, daemon=wd)
fn = f'monero-{wd.network}-junk-wallet'
qmsg(f'Creating {wd.network} wallet')
c.call(
'restore_deterministic_wallet',
filename = fn,
password = 'foo',
seed = xmrseed().fromhex('beadface'*8,tostr=True) )
seed = xmrseed().fromhex('beadface'*8, tostr=True))
if sys.platform == 'win32':
wd.stop()
wd.start()
qmsg(f'Opening {wd.network} wallet')
c.call( 'open_wallet', filename=fn, password='foo' )
c.call('open_wallet', filename=fn, password='foo')
await c.stop_daemon()
@ -249,7 +249,7 @@ class unit_tests:
gmsg('OK')
import shutil
shutil.rmtree('test/trash2',ignore_errors=True)
shutil.rmtree('test/trash2', ignore_errors=True)
os.makedirs('test/trash2/wallet_rpc')
await run()
return True

View file

@ -4,13 +4,13 @@
test.unit_tests_d.ut_scrypt: password hashing unit test for the MMGen suite
"""
from mmgen.util import msg,msg_r
from mmgen.util import msg, msg_r
from ..include.common import cfg,qmsg,vmsg,omsg_r,silence,end_silence
from ..include.common import cfg, qmsg, vmsg, omsg_r, silence, end_silence
class unit_test:
def run_test(self,name,ut):
def run_test(self, name, ut):
import time
msg_r('Testing password hashing...')
@ -21,27 +21,31 @@ class unit_test:
salt = bytes.fromhex('f00f' * 16)
presets = { '1': '64898d01ffcea1252c17411e7bd6c12d00191ee8ddcbf95420e5de8f37a7a1cb',
'2': '625d0c59fcbcf85eca51b4337d3a4952e554c7c95fe32f4199ad0e9e9370e279',
'3': 'b30a5cf0e606515be4a983cc4a1a4c21e04806f19aaa0ad354a353c83aecd3ee',
'4': 'a3a1b99393734510b68adf2c2cfdc627a2bc3281913d8ea6fbb677d39781a9fa',
'5': '0c7e1a672738cee49cf0ff6f3208190ca418e741835fd6995ce9558cc19f3f04',
'6': '91f9d1c9baf3948433dab58dcc912d96035392c1db21ede96d2f369e025ab06d',
'7': 'fcb2cd05268de43b0d2d45f78a56e5d446b0bd2d3b57bdbc77cc17a42942f1bd' }
presets = {
'1': '64898d01ffcea1252c17411e7bd6c12d00191ee8ddcbf95420e5de8f37a7a1cb',
'2': '625d0c59fcbcf85eca51b4337d3a4952e554c7c95fe32f4199ad0e9e9370e279',
'3': 'b30a5cf0e606515be4a983cc4a1a4c21e04806f19aaa0ad354a353c83aecd3ee',
'4': 'a3a1b99393734510b68adf2c2cfdc627a2bc3281913d8ea6fbb677d39781a9fa',
'5': '0c7e1a672738cee49cf0ff6f3208190ca418e741835fd6995ce9558cc19f3f04',
'6': '91f9d1c9baf3948433dab58dcc912d96035392c1db21ede96d2f369e025ab06d',
'7': 'fcb2cd05268de43b0d2d45f78a56e5d446b0bd2d3b57bdbc77cc17a42942f1bd'
}
pws = ( ('', 'cc8d99ce7365d8a9d2422d71ce330e130b2cade46a8cc0459a3f83e1a6ac3d30'),
('foo', 'f0e2cce1d9980edf2373a2070ad3560c2506faf9bc50704a1bc5cdb3c7f63f3b'),
('φυβαρ', '64898d01ffcea1252c17411e7bd6c12d00191ee8ddcbf95420e5de8f37a7a1cb') )
pws = (
('', 'cc8d99ce7365d8a9d2422d71ce330e130b2cade46a8cc0459a3f83e1a6ac3d30'),
('foo', 'f0e2cce1d9980edf2373a2070ad3560c2506faf9bc50704a1bc5cdb3c7f63f3b'),
('φυβαρ', '64898d01ffcea1252c17411e7bd6c12d00191ee8ddcbf95420e5de8f37a7a1cb')
)
def test_passwords():
for pw_base,res in pws:
for pw in (pw_base,pw_base.encode()):
pw_disp = "'"+pw+"'" if isinstance(pw,str) else "b'"+pw.decode()+"'"
for pw_base, res in pws:
for pw in (pw_base, pw_base.encode()):
pw_disp = "'"+pw+"'" if isinstance(pw, str) else "b'"+pw.decode()+"'"
if cfg.quiet:
omsg_r('.')
else:
msg_r(f'\n password {pw_disp:9} ')
ret = crypto.scrypt_hash_passphrase(pw,salt,'1').hex()
ret = crypto.scrypt_hash_passphrase(pw, salt, '1').hex()
assert ret == res, ret
def test_presets(do_presets):
@ -54,7 +58,7 @@ class unit_test:
else:
msg_r(f'\n {hp!r:3}: {crypto.hash_presets[hp]!r:12} ')
st = time.time()
ret = crypto.scrypt_hash_passphrase(pw,salt,hp).hex()
ret = crypto.scrypt_hash_passphrase(pw, salt, hp).hex()
t = time.time() - st
vmsg('' if cfg.test_suite_deterministic else f' {t:0.4f} secs')
assert ret == res, ret
@ -66,13 +70,13 @@ class unit_test:
vmsg('Passwords (auto module selection):')
test_passwords()
vmsg('Hash presets (auto module selection):')
test_presets((1,2,3,4) if cfg.fast else (1,2,3,4,5,6,7))
test_presets((1, 2, 3, 4) if cfg.fast else (1, 2, 3, 4, 5, 6, 7))
cfg.force_standalone_scrypt_module = True
vmsg('Passwords (force standalone scrypt module):')
test_passwords()
vmsg('Hash presets (force standalone scrypt module):')
test_presets((1,2,3))
test_presets((1, 2, 3))
if cfg.quiet:
end_silence()

View file

@ -4,66 +4,76 @@
test/unit_tests_d/ut_seedsplit: seed splitting unit test for the MMGen suite
"""
from mmgen.util import msg,msg_r
from mmgen.util import msg, msg_r
from ..include.common import cfg,vmsg,vmsg_r
from ..include.common import cfg, vmsg, vmsg_r
class unit_test:
def run_test(self,name,ut):
def run_test(self, name, ut):
from mmgen.seed import Seed
from mmgen.seedsplit import SeedShareList,SeedShareIdx
from mmgen.seedsplit import SeedShareList, SeedShareIdx
cfg.debug_subseed = bool(cfg.verbose)
def basic_ops(master_idx):
test_data = {
'default': (
(8,'4710FBF0','B3D9411B','2670E83D','D1FC57ED','AE49CABE','63FFBA62',0,0),
(6,'9D07ABBD','AF5DC2F6','1A3BBDAC','2548AEE9','B94F7450','1F4E5A12',0,0),
(4,'43670520','1F72C066','E5AA8DA1','A33966A0','D2BCE0A5','A568C315',0,0),
(8, '4710FBF0', 'B3D9411B', '2670E83D', 'D1FC57ED', 'AE49CABE', '63FFBA62', 0, 0),
(6, '9D07ABBD', 'AF5DC2F6', '1A3BBDAC', '2548AEE9', 'B94F7450', '1F4E5A12', 0, 0),
(4, '43670520', '1F72C066', 'E5AA8DA1', 'A33966A0', 'D2BCE0A5', 'A568C315', 0, 0),
),
'φυβαρ': (
(8,'4710FBF0','269D658C','9D25889E','6D730ECB','C61A963F','9FE99C05',0,0),
(6,'9D07ABBD','4998B33E','F00CE041','C612BEE5','35CD3675','41B3BE61',0,0),
(4,'43670520','77140076','EA82CB30','80F7AEDE','D168D768','77BE57AA',0,0),
(8, '4710FBF0', '269D658C', '9D25889E', '6D730ECB', 'C61A963F', '9FE99C05', 0, 0),
(6, '9D07ABBD', '4998B33E', 'F00CE041', 'C612BEE5', '35CD3675', '41B3BE61', 0, 0),
(4, '43670520', '77140076', 'EA82CB30', '80F7AEDE', 'D168D768', '77BE57AA', 0, 0),
)
}
test_data_master = {
'1': {
'default': (
(8,'4710FBF0','6AE6177F','AC12090C','6AE6177F','3E87A907','7D1FEA56','BFEBFFFF','629A9808'),
(4,'43670520','6739535C','ABF4DD38','6739535C','778E9C60','89CBCFD2','689FABF5','70BED76B'),
(8, '4710FBF0', '6AE6177F', 'AC12090C', '6AE6177F',
'3E87A907', '7D1FEA56', 'BFEBFFFF', '629A9808'),
(4, '43670520', '6739535C', 'ABF4DD38', '6739535C',
'778E9C60', '89CBCFD2', '689FABF5', '70BED76B'),
),
'φυβαρ': (
(8,'4710FBF0','6AE6177F','AC5FA32E','6AE6177F','9777A750','C7CF2AFC','035AAACB','C777FBE4'),
(4,'43670520','6739535C','37EBA2F5','6739535C','927549D2','29BADEE7','9CA73A03','313F5528'))
(8, '4710FBF0', '6AE6177F', 'AC5FA32E', '6AE6177F',
'9777A750', 'C7CF2AFC', '035AAACB', 'C777FBE4'),
(4, '43670520', '6739535C', '37EBA2F5', '6739535C',
'927549D2', '29BADEE7', '9CA73A03', '313F5528'))
},
'5': {
'default': (
(8,'4710FBF0','5EFAC3D6','B489167D','5EFAC3D6','BB004DC5','1A0381C0','4EA182E3','547FB2DC'),
(4,'43670520','EE93DB0E','44962A7D','EE93DB0E','07339882','376A05B1','CE51D022','00149CA3'),
(8, '4710FBF0', '5EFAC3D6', 'B489167D', '5EFAC3D6',
'BB004DC5', '1A0381C0', '4EA182E3', '547FB2DC'),
(4, '43670520', 'EE93DB0E', '44962A7D', 'EE93DB0E',
'07339882', '376A05B1', 'CE51D022', '00149CA3'),
),
'φυβαρ': (
(8,'4710FBF0','5EFAC3D6','A6E27EE3','5EFAC3D6','32C24668','B4C54297','1EC9B71B','8C5C6B1C'),
(4,'43670520','EE93DB0E','B584E963','EE93DB0E','4BEA2AB2','4BEA65C7','140FC43F','BBD19461'))
(8, '4710FBF0', '5EFAC3D6', 'A6E27EE3', '5EFAC3D6',
'32C24668', 'B4C54297', '1EC9B71B', '8C5C6B1C'),
(4, '43670520', 'EE93DB0E', 'B584E963', 'EE93DB0E',
'4BEA2AB2', '4BEA65C7', '140FC43F', 'BBD19461'))
}
}
if master_idx:
test_data = test_data_master[str(master_idx)]
for id_str in (None,'default','φυβαρ'):
for id_str in (None, 'default', 'φυβαρ'):
msg_r(f'Testing basic ops (id_str={id_str!r}, master_idx={master_idx})...')
vmsg('')
for a,b,c,d,e,f,h,i,p in test_data[id_str if id_str is not None else 'default']:
for a, b, c, d, e, f, h, i, p in test_data[id_str if id_str is not None else 'default']:
seed_bin = bytes.fromhex('deadbeef' * a)
seed = Seed( cfg, seed_bin )
seed = Seed(cfg, seed_bin)
assert seed.sid == b, seed.sid
for share_count,j,k,l,m in ((2,c,c,d,i),(5,e,f,h,p)):
for share_count, j, k, l, m in (
(2, c, c, d, i),
(5, e, f, h, p)):
shares = seed.split(share_count,id_str,master_idx)
shares = seed.split(share_count, id_str, master_idx)
A = len(shares)
assert A == share_count, A
@ -72,16 +82,16 @@ class unit_test:
assert len(s.strip().split('\n')) == share_count+6, s
if master_idx:
A = shares.get_share_by_idx(1,base_seed=False).sid
B = shares.get_share_by_seed_id(j,base_seed=False).sid
A = shares.get_share_by_idx(1, base_seed=False).sid
B = shares.get_share_by_seed_id(j, base_seed=False).sid
assert A == B == m, A
A = shares.get_share_by_idx(1,base_seed=True).sid
B = shares.get_share_by_seed_id(j,base_seed=True).sid
A = shares.get_share_by_idx(1, base_seed=True).sid
B = shares.get_share_by_seed_id(j, base_seed=True).sid
assert A == B == j, A
A = shares.get_share_by_idx(share_count-1,base_seed=True).sid
B = shares.get_share_by_seed_id(k,base_seed=True).sid
A = shares.get_share_by_idx(share_count-1, base_seed=True).sid
B = shares.get_share_by_seed_id(k, base_seed=True).sid
assert A == B == k, A
A = shares.get_share_by_idx(share_count).sid
@ -92,8 +102,8 @@ class unit_test:
assert A == b, A
if master_idx:
slist = [shares.get_share_by_idx(i+1,base_seed=True) for i in range(len(shares))]
A = Seed.join_shares( cfg, slist, master_idx, id_str ).sid
slist = [shares.get_share_by_idx(i+1, base_seed=True) for i in range(len(shares))]
A = Seed.join_shares(cfg, slist, master_idx, id_str).sid
assert A == b, A
msg('OK')
@ -102,7 +112,7 @@ class unit_test:
msg_r('Testing defaults and limits...')
seed_bin = bytes.fromhex('deadbeef' * 8)
seed = Seed( cfg, seed_bin )
seed = Seed(cfg, seed_bin)
shares = seed.split(SeedShareIdx.max_val)
s = shares.format()
@ -120,16 +130,16 @@ class unit_test:
msg('OK')
def collisions(seed_hex,ss_count,last_sid,collisions_chk,master_idx):
def collisions(seed_hex, ss_count, last_sid, collisions_chk, master_idx):
msg_r(f'Testing Seed ID collisions ({ss_count} seed shares, master_idx={master_idx})...')
vmsg('')
seed_bin = bytes.fromhex(seed_hex)
seed = Seed( cfg, seed_bin )
seed = Seed(cfg, seed_bin)
SeedShareIdx.max_val = ss_count
shares = seed.split(ss_count,master_idx=master_idx)
shares = seed.split(ss_count, master_idx=master_idx)
A = shares.get_share_by_idx(ss_count).sid
B = shares.get_share_by_seed_id(last_sid).sid
assert A == last_sid, A
@ -149,10 +159,10 @@ class unit_test:
msg_r('Testing last share collisions with shortened Seed IDs')
vmsg('')
seed_bin = bytes.fromhex('2eadbeef'*8)
seed = Seed( cfg, seed_bin )
seed = Seed(cfg, seed_bin)
ssm_save = SeedShareIdx.max_val
ssm = SeedShareIdx.max_val = 2048
shares = SeedShareList(seed,count=ssm,id_str='foo',master_idx=1,debug_last_share=True)
shares = SeedShareList(seed, count=ssm, id_str='foo', master_idx=1, debug_last_share=True)
lsid = shares.last_share.sid
collisions = shares.data['long'][lsid][1]
assert collisions == 2, collisions
@ -166,7 +176,7 @@ class unit_test:
basic_ops(master_idx=5)
defaults_and_limits()
last_share_collisions()
collisions('1dabcdef'*4,65535,'B5CBCE0A',3,master_idx=None)
collisions('18abcdef'*4,65535,'FF03CE82',3,master_idx=1)
collisions('1dabcdef'*4, 65535, 'B5CBCE0A', 3, master_idx=None)
collisions('18abcdef'*4, 65535, 'FF03CE82', 3, master_idx=1)
return True

View file

@ -4,28 +4,28 @@
test/unit_tests_d/ut_subseed: subseed unit test for the MMGen suite
"""
from mmgen.util import msg,msg_r
from mmgen.util import msg, msg_r
from ..include.common import cfg,vmsg_r
from ..include.common import cfg, vmsg_r
class unit_test:
def run_test(self,name,ut):
def run_test(self, name, ut):
from mmgen.seed import Seed
from mmgen.subseed import SubSeedList,SubSeedIdxRange
from mmgen.subseed import SubSeedList, SubSeedIdxRange
nSubseeds = SubSeedList.dfl_len
def basic_ops():
msg_r('Testing basic ops...')
for a,b,c,d,e,f,h in (
(8,'4710FBF0','0C1B0615','803B165C','2669AC64',256,'10L'),
(6,'9D07ABBD','EBA9C33F','20787E6A','192E2AA2',192,'10L'),
(4,'43670520','04A4CCB3','B5F21D7B','C1934CFF',128,'10L'),
for a, b, c, d, e, f, h in (
(8, '4710FBF0', '0C1B0615', '803B165C', '2669AC64', 256, '10L'),
(6, '9D07ABBD', 'EBA9C33F', '20787E6A', '192E2AA2', 192, '10L'),
(4, '43670520', '04A4CCB3', 'B5F21D7B', 'C1934CFF', 128, '10L'),
):
seed_bin = bytes.fromhex('deadbeef' * a)
seed = Seed( cfg, seed_bin )
seed = Seed(cfg, seed_bin)
assert seed.sid == b, seed.sid
subseed = seed.subseed('2s')
@ -40,7 +40,7 @@ class unit_test:
assert subseed.idx == 10, subseed.idx
assert subseed.ss_idx == h, subseed.ss_idx
seed2 = Seed( cfg, seed_bin )
seed2 = Seed(cfg, seed_bin)
ss2_list = seed2.subseeds
seed2.subseeds._generate(1)
@ -61,7 +61,7 @@ class unit_test:
assert seed.pfmt() == seed2.pfmt()
assert seed.subseeds.pfmt() == seed2.subseeds.pfmt()
s = seed.subseeds.format(1,nSubseeds)
s = seed.subseeds.format(1, nSubseeds)
s_lines = s.strip().split('\n')
assert len(s_lines) == nSubseeds + 4, s
@ -76,7 +76,7 @@ class unit_test:
b = [e for e in s_lines if ' 5S:' in e][0].strip().split()[3]
assert a == b, b
s = seed.subseeds.format(nSubseeds+1,nSubseeds+2)
s = seed.subseeds.format(nSubseeds+1, nSubseeds+2)
s_lines = s.strip().split('\n')
assert len(s_lines) == 6, s
@ -85,7 +85,7 @@ class unit_test:
b = [e for e in s_lines if f' {ss_idx}:' in e][0].strip().split()[3]
assert a == b, b
s = seed.subseeds.format(1,10)
s = seed.subseeds.format(1, 10)
s_lines = s.strip().split('\n')
assert len(s_lines) == 14, s
@ -98,48 +98,48 @@ class unit_test:
seed_bin = bytes.fromhex('deadbeef' * 8)
seed = Seed( cfg, seed_bin, nSubseeds=11 )
seed = Seed(cfg, seed_bin, nSubseeds=11)
seed.subseeds._generate()
ss = seed.subseeds
assert len(ss.data['long']) == len(ss.data['short']), len(ss.data['short'])
assert len(ss) == 11, len(ss)
seed = Seed( cfg, seed_bin )
seed = Seed(cfg, seed_bin)
seed.subseeds._generate()
ss = seed.subseeds
assert len(ss.data['long']) == len(ss.data['short']), len(ss.data['short'])
assert len(ss) == nSubseeds, len(ss)
seed = Seed( cfg, seed_bin )
seed = Seed(cfg, seed_bin)
seed.subseed_by_seed_id('EEEEEEEE')
ss = seed.subseeds
assert len(ss.data['long']) == len(ss.data['short']), len(ss.data['short'])
assert len(ss) == nSubseeds, len(ss)
seed = Seed( cfg, seed_bin )
seed = Seed(cfg, seed_bin)
subseed = seed.subseed_by_seed_id('803B165C')
assert len(ss.data['long']) == len(ss.data['short']), len(ss.data['short'])
assert subseed.sid == '803B165C', subseed.sid
assert subseed.idx == 3, subseed.idx
seed = Seed( cfg, seed_bin )
subseed = seed.subseed_by_seed_id('803B165C',last_idx=1)
seed = Seed(cfg, seed_bin)
subseed = seed.subseed_by_seed_id('803B165C', last_idx=1)
assert len(ss.data['long']) == len(ss.data['short']), len(ss.data['short'])
assert subseed is None, subseed
r = SubSeedIdxRange('1-5')
r2 = SubSeedIdxRange(1,5)
r2 = SubSeedIdxRange(1, 5)
assert r2 == r, r2
assert r == (r.first,r.last), r
assert r == (r.first, r.last), r
assert r.first == 1, r.first
assert r.last == 5, r.last
assert r.items == [1,2,3,4,5], r.items
assert r.items == [1, 2, 3, 4, 5], r.items
assert list(r.iterate()) == r.items, list(r.iterate())
r = SubSeedIdxRange('22')
r2 = SubSeedIdxRange(22,22)
r2 = SubSeedIdxRange(22, 22)
assert r2 == r, r2
assert r == (r.first,r.last), r
assert r == (r.first, r.last), r
assert r.first == 22, r.first
assert r.last == 22, r.last
assert r.items == [22], r
@ -149,9 +149,9 @@ class unit_test:
assert r.items == [3], r.items
r = SubSeedIdxRange(f'{nSubseeds-1}-{nSubseeds}')
assert r.items == [nSubseeds-1,nSubseeds], r.items
assert r.items == [nSubseeds-1, nSubseeds], r.items
for n,e in enumerate(SubSeedIdxRange('1-5').iterate(),1):
for n, e in enumerate(SubSeedIdxRange('1-5').iterate(), 1):
assert n == e, e
assert n == 5, n
@ -159,9 +159,9 @@ class unit_test:
msg('OK')
def collisions():
ss_count,ltr,last_sid,collisions_chk = (
(SubSeedIdxRange.max_idx,'S','2788F26B',470),
(49509,'L','8D1FE500',2)
ss_count, ltr, last_sid, collisions_chk = (
(SubSeedIdxRange.max_idx, 'S', '2788F26B', 470),
(49509, 'L', '8D1FE500', 2)
)[bool(cfg.fast)]
last_idx = str(ss_count) + ltr
@ -169,7 +169,7 @@ class unit_test:
msg_r(f'Testing Seed ID collisions ({ss_count} subseed pairs)...')
seed_bin = bytes.fromhex('12abcdef' * 8) # 95B3D78D
seed = Seed( cfg, seed_bin )
seed = Seed(cfg, seed_bin)
seed.subseeds._generate(ss_count)
ss = seed.subseeds
@ -181,7 +181,7 @@ class unit_test:
assert sid not in ss.data['short']
collisions = 0
for k in ('short','long'):
for k in ('short', 'long'):
for sid in ss.data[k]:
collisions += ss.data[k][sid][1]

View file

@ -4,64 +4,64 @@
test.unit_tests_d.ut_testdep: test dependency unit tests for the MMGen suite
"""
import sys,os
from subprocess import run,DEVNULL
import sys, os
from subprocess import run, DEVNULL
from mmgen.util import ymsg,bmsg
from mmgen.util import ymsg, bmsg
from ..include.common import cfg
sec = 'deadbeef' * 8
class unit_tests:
altcoin_deps = ('pycoin','monero_python','keyconv','zcash_mini','ethkey','ssh_socks_proxy')
altcoin_deps = ('pycoin', 'monero_python', 'keyconv', 'zcash_mini', 'ethkey', 'ssh_socks_proxy')
win_skip = ('losetup', 'zcash_mini', 'sudo')
mac_skip = ('losetup',)
def pylint(self,name,ut):
def pylint(self, name, ut):
try:
return run(['pylint','--version'],stdout=None if cfg.verbose else DEVNULL).returncode == 0
return run(['pylint', '--version'], stdout=None if cfg.verbose else DEVNULL).returncode == 0
except OSError as e:
ymsg(' ' + str(e))
bmsg(" Install pylint with 'python3 -m pip install pylint'")
return False
def core_repo(self,name,ut):
def core_repo(self, name, ut):
crr = os.getenv('CORE_REPO_ROOT')
if not crr or not os.path.exists(os.path.join(crr,'src/test/data/tx_valid.json')):
if not crr or not os.path.exists(os.path.join(crr, 'src/test/data/tx_valid.json')):
ymsg('CORE_REPO_ROOT not set, or does not point to Bitcoin Core repository')
return False
return True
def losetup(self,name,ut):
def losetup(self, name, ut):
os.stat('/dev/loop0')
run(['/sbin/losetup','-f'],check=True,stdout=DEVNULL)
run(['/sbin/losetup', '-f'], check=True, stdout=DEVNULL)
return True
def pycoin(self,name,ut):
def pycoin(self, name, ut):
from pycoin.networks.registry import network_for_netcode as nfnc
network = nfnc('btc')
key = network.keys.private(secret_exponent=int(sec,16),is_compressed=True)
key = network.keys.private(secret_exponent=int(sec, 16), is_compressed=True)
hash160_c = key.hash160(is_compressed=True)
network.address.for_p2pkh_wit(hash160_c)
return True
def monero_python(self,name,ut):
def monero_python(self, name, ut):
from mmgen.util2 import load_cryptodomex
load_cryptodomex()
from monero.seed import Seed
Seed('deadbeef' * 8).public_address()
return True
def keyconv(self,name,ut):
run(['keyconv','-G','ltc'],stdout=DEVNULL,stderr=DEVNULL,check=True)
def keyconv(self, name, ut):
run(['keyconv', '-G', 'ltc'], stdout=DEVNULL, stderr=DEVNULL, check=True)
return True
def zcash_mini(self,name,ut):
run(['zcash-mini'],stdout=DEVNULL,check=True)
def zcash_mini(self, name, ut):
run(['zcash-mini'], stdout=DEVNULL, check=True)
return True
def ethkey(self,name,ut):
def ethkey(self, name, ut):
if sys.platform == 'linux' and os.uname().machine != 'x86_64':
distro = [l for l in open('/etc/os-release').read().split('\n') if l.startswith('ID=')][0][3:]
if distro != 'archarm':
@ -71,11 +71,11 @@ class unit_tests:
get_ethkey()
return True
def ssh_socks_proxy(self,name,ut):
def ssh_socks_proxy(self, name, ut):
from test.cmdtest_py_d.ct_xmrwallet import CmdTestXMRWallet
return CmdTestXMRWallet.init_proxy(external_call=True)
def sudo(self,name,ut):
def sudo(self, name, ut):
from mmgen.util import have_sudo
if have_sudo():
return True

View file

@ -4,10 +4,10 @@
test.unit_tests_d.ut_tx: TX unit tests for the MMGen suite
"""
import os,re
import os, re
from mmgen.devtools import get_diff,get_ndiff
from mmgen.tx import NewTX,CompletedTX,UnsignedTX
from mmgen.devtools import get_diff, get_ndiff
from mmgen.tx import NewTX, CompletedTX, UnsignedTX
from mmgen.tx.file import MMGenTxFile
from mmgen.daemon import CoinDaemon
from mmgen.protocol import init_proto
@ -19,7 +19,7 @@ async def do_txfile_test(desc, fns, cfg=cfg, check=False):
qmsg(f' Testing CompletedTX initializer ({desc})')
for fn in fns:
qmsg(f' parsing: {os.path.basename(fn)}')
fpath = os.path.join('test','ref',fn)
fpath = os.path.join('test', 'ref', fn)
tx = await CompletedTX(cfg=cfg, filename=fpath, quiet_open=True)
vmsg('\n' + tx.info.format())
@ -28,7 +28,7 @@ async def do_txfile_test(desc, fns, cfg=cfg, check=False):
fn_gen = f.make_filename()
if cfg.debug_utf8:
fn_gen = fn_gen.replace('','')
fn_gen = fn_gen.replace('', '')
assert fn_gen == os.path.basename(fn), f'{fn_gen} != {fn}'
@ -45,13 +45,13 @@ class unit_tests:
altcoin_deps = ('txfile_alt', 'txfile_alt_legacy')
async def tx(self,name,ut):
async def tx(self, name, ut):
qmsg(' Testing NewTX initializer')
d = CoinDaemon( cfg, 'btc', test_suite=True )
d = CoinDaemon(cfg, 'btc', test_suite=True)
d.start()
proto = init_proto( cfg, 'btc', need_amt=True )
await NewTX( cfg=cfg, proto=proto )
proto = init_proto(cfg, 'btc', need_amt=True)
await NewTX(cfg=cfg, proto=proto)
d.stop()
d.remove_datadir()
@ -108,7 +108,7 @@ class unit_tests:
)
)
def errors(self,name,ut):
def errors(self, name, ut):
async def bad1():
await CompletedTX(cfg, filename='foo')
def bad2():

View file

@ -4,19 +4,19 @@
test/unit_tests_d/ut_tx_deserialize: TX deserialization unit tests for the MMGen suite
"""
import os,json
import os, json
from mmgen.color import purple,cyan
from mmgen.util import msg,Msg,Msg_r
from mmgen.color import purple, cyan
from mmgen.util import msg, Msg, Msg_r
from mmgen.devtools import Pmsg
from mmgen.protocol import init_proto
from mmgen.tx import CompletedTX
from mmgen.proto.btc.tx.base import DeserializeTX
from mmgen.rpc import rpc_init
from ..include.common import cfg,start_test_daemons,stop_test_daemons
from ..include.common import cfg, start_test_daemons, stop_test_daemons
def print_info(name,extra_desc):
def print_info(name, extra_desc):
if cfg.names:
Msg_r('{} {} {}'.format(
purple('Testing'),
@ -27,22 +27,22 @@ def print_info(name,extra_desc):
if not cfg.quiet:
Msg('')
async def test_tx(tx_proto,tx_hex,desc,n):
async def test_tx(tx_proto, tx_hex, desc, n):
def has_nonstandard_outputs(outputs):
for o in outputs:
t = o['scriptPubKey']['type']
if t in ('nonstandard','pubkey','nulldata'):
if t in ('nonstandard', 'pubkey', 'nulldata'):
return True
return False
rpc = await rpc_init( cfg, proto=tx_proto, ignore_wallet=True )
d = await rpc.call('decoderawtransaction',tx_hex)
rpc = await rpc_init(cfg, proto=tx_proto, ignore_wallet=True)
d = await rpc.call('decoderawtransaction', tx_hex)
if has_nonstandard_outputs(d['vout']):
return False
dt = DeserializeTX(tx_proto,tx_hex)
dt = DeserializeTX(tx_proto, tx_hex)
if cfg.verbose:
Msg('\n\n================================ Core vector: ==================================')
@ -53,51 +53,51 @@ async def test_tx(tx_proto,tx_hex,desc,n):
Pmsg(dt._asdict())
# metadata
assert dt.txid == d['txid'],'TXID does not match'
assert dt.locktime == d['locktime'],'Locktime does not match'
assert dt.version == d['version'],'Version does not match'
assert dt.txid == d['txid'], 'TXID does not match'
assert dt.locktime == d['locktime'], 'Locktime does not match'
assert dt.version == d['version'], 'Version does not match'
# inputs
a,b = d['vin'],dt.txins
a, b = d['vin'], dt.txins
for i in range(len(a)):
assert a[i]['txid'] == b[i]['txid'],f'TxID of input {i} does not match'
assert a[i]['vout'] == b[i]['vout'],f'vout of input {i} does not match'
assert a[i]['sequence'] == int(b[i]['nSeq'],16),(
assert a[i]['txid'] == b[i]['txid'], f'TxID of input {i} does not match'
assert a[i]['vout'] == b[i]['vout'], f'vout of input {i} does not match'
assert a[i]['sequence'] == int(b[i]['nSeq'], 16), (
f'nSeq of input {i} does not match')
if 'txinwitness' in a[i]:
assert a[i]['txinwitness'] == b[i]['witness'],(
assert a[i]['txinwitness'] == b[i]['witness'], (
f'witness of input {i} does not match')
# outputs
a,b = d['vout'],dt.txouts
a, b = d['vout'], dt.txouts
for i in range(len(a)):
if 'addresses' in a[i]['scriptPubKey']:
A = a[i]['scriptPubKey']['addresses'][0]
B = b[i]['address']
fs = 'address of output {} does not match\nA: {}\nB: {}'
assert A == B, fs.format(i,A,B)
assert A == B, fs.format(i, A, B)
A = tx_proto.coin_amt(a[i]['value'])
B = b[i]['amt']
fs = 'value of output {} does not match\nA: {}\nB: {}'
assert A == B, fs.format(i,A,B)
assert A == B, fs.format(i, A, B)
A = a[i]['scriptPubKey']['hex']
B = b[i]['scriptPubKey']
fs = 'scriptPubKey of output {} does not match\nA: {}\nB: {}'
assert A == B, fs.format(i,A,B)
assert A == B, fs.format(i, A, B)
async def do_mmgen_ref(daemons,fns,name,desc):
async def do_mmgen_ref(daemons, fns, name, desc):
# NB: remove_datadir is required here for some reason (seems to be Bitcoin Core version-dependent)
start_test_daemons(*daemons,remove_datadir=True)
print_info(name,desc)
for n,fn in enumerate(fns):
tx = await CompletedTX( cfg=cfg, filename=fn, quiet_open=True )
start_test_daemons(*daemons, remove_datadir=True)
print_info(name, desc)
for n, fn in enumerate(fns):
tx = await CompletedTX(cfg=cfg, filename=fn, quiet_open=True)
await test_tx(
tx_proto = tx.proto,
tx_hex = tx.serialized,
desc = fn,
n = n+1 )
n = n + 1)
stop_test_daemons(*daemons, remove_datadir=True)
Msg('OK')
return True
@ -106,7 +106,7 @@ class unit_tests:
altcoin_deps = ('mmgen_ref_alt',)
async def core_vectors(self,name,ut):
async def core_vectors(self, name, ut):
core_repo_root = os.getenv('CORE_REPO_ROOT')
if not core_repo_root:
@ -116,18 +116,18 @@ class unit_tests:
start_test_daemons('btc')
fn_b = 'src/test/data/tx_valid.json'
fn = os.path.join(core_repo_root,fn_b)
fn = os.path.join(core_repo_root, fn_b)
with open(fn) as fp:
core_data = json.loads(fp.read())
print_info(name,'Bitcoin Core test vectors')
print_info(name, 'Bitcoin Core test vectors')
n = 1
for e in core_data:
if isinstance(e[0],list):
if isinstance(e[0], list):
await test_tx(
tx_proto = init_proto( cfg, 'btc', need_amt=True ),
tx_proto = init_proto(cfg, 'btc', need_amt=True),
tx_hex = e[1],
desc = desc,
n = n )
n = n)
n += 1
else:
desc = e[0]
@ -136,24 +136,24 @@ class unit_tests:
stop_test_daemons('btc', remove_datadir=True)
return True
async def mmgen_ref(self,name,ut):
async def mmgen_ref(self, name, ut):
return await do_mmgen_ref(
('btc','btc_tn'),
('btc', 'btc_tn'),
(
'test/ref/0B8D5A[15.31789,14,tl=1320969600].rawtx',
'test/ref/0C7115[15.86255,14,tl=1320969600].testnet.rawtx',
'test/ref/542169[5.68152,34].sigtx',
),
name,
'MMGen reference transactions [Bitcoin]' )
'MMGen reference transactions [Bitcoin]')
async def mmgen_ref_alt(self,name,ut):
async def mmgen_ref_alt(self, name, ut):
return await do_mmgen_ref(
('ltc','ltc_tn','bch'),
('ltc', 'ltc_tn', 'bch'),
(
'test/ref/litecoin/AF3CDF-LTC[620.76194,1453,tl=1320969600].rawtx',
'test/ref/litecoin/A5A1E0-LTC[1454.64322,1453,tl=1320969600].testnet.rawtx',
'test/ref/460D4D-BCH[10.19764,tl=1320969600].rawtx'
),
name,
'MMGen reference transactions [Altcoin]' )
'MMGen reference transactions [Altcoin]')

View file

@ -5,17 +5,17 @@ test.unit_tests_d.ut_misc: utility unit tests for the MMGen suite
"""
from mmgen.color import cyan
from mmgen.util import fmt_list,fmt_dict,list_gen
from mmgen.util import fmt_list, fmt_dict, list_gen
from ..include.common import vmsg
class unit_tests:
def fmt_list(self,name,ut):
def fmt_list(self, name, ut):
samples = {
'pids': [18234,18444,19324],
'vardata': [None,True,1234,'sample string'],
'pids': [18234, 18444, 19324],
'vardata': [None, True, 1234, 'sample string'],
}
chks = {
'vardata': {
@ -38,9 +38,9 @@ class unit_tests:
for _name, sample in samples.items():
vmsg(cyan(f'Input: {sample}'))
for fmt in list(chks.values())[0]:
spc = '\n' if fmt in ('col','list') else ' '
spc = '\n' if fmt in ('col', 'list') else ' '
indent = ' + ' if fmt == 'col' else ''
res = fmt_list(sample,fmt=fmt,indent=indent) if fmt else fmt_list(sample,indent=indent)
res = fmt_list(sample, fmt=fmt, indent=indent) if fmt else fmt_list(sample, indent=indent)
vmsg(f' {str(fmt)+":":{col1_w}}{spc}{res}')
if _name in chks:
assert res == chks[_name][fmt], f'{res} != {chks[_name][fmt]}'
@ -48,14 +48,14 @@ class unit_tests:
vmsg('')
return True
def fmt_dict(self,name,ut):
def fmt_dict(self, name, ut):
samples = {
'url': {
'name':'Example',
'desc':'Sample URL',
'name': 'Example',
'desc': 'Sample URL',
'rank': 1,
'error': None,
'url':'https://example.com/foobar.html',
'url': 'https://example.com/foobar.html',
},
'choice': {
'c': 'curl',
@ -63,7 +63,7 @@ class unit_tests:
'r': 'requests',
},
'cmdline': {
'cmd': ["ls","-l"],
'cmd': ['ls', '-l'],
'text': 'foo bar',
'stdin': None,
'offset': 123,
@ -87,7 +87,7 @@ class unit_tests:
for _name, sample in samples.items():
vmsg(cyan(f'Input: {sample}'))
for fmt in list(chks.values())[0]:
res = fmt_dict(sample,fmt=fmt) if fmt else fmt_dict(sample)
res = fmt_dict(sample, fmt=fmt) if fmt else fmt_dict(sample)
vmsg(f' {str(fmt)+":":{col1_w}} {res}')
if _name in chks:
assert res == chks[_name][fmt], f'{res} != {chks[_name][fmt]}'
@ -95,13 +95,13 @@ class unit_tests:
vmsg('')
return True
def list_gen(self,name,ut):
def list_gen(self, name, ut):
res = list_gen(
['a'],
['b', False],
['c', 'x'],
['d', int],
['e', None, 1, 'f', isinstance(7,int)],
['e', None, 1, 'f', isinstance(7, int)],
['g', 'h', 0],
[None],
[0],

View file

@ -13,43 +13,44 @@ from ..include.common import cfg, vmsg
class unit_tests:
vectors = ( # private keys are reduced
( '148d78d2aba7dbca5cd8f6abcfb0b3c009ffbdbea1ff373d50ed94d78286640e', # Monero repo
(
'148d78d2aba7dbca5cd8f6abcfb0b3c009ffbdbea1ff373d50ed94d78286640e', # Monero repo
'velvet lymph giddy number token physics poetry unquoted nibs useful sabotage limits benches ' +
'lifestyle eden nitrogen anvil fewest avoid batch vials washing fences goat unquoted',
),
( 'e8164dda6d42bd1e261a3406b2038dcbddadbeefdeadbeefdeadbeefdeadbe0f',
), (
'e8164dda6d42bd1e261a3406b2038dcbddadbeefdeadbeefdeadbeefdeadbe0f',
'viewpoint donuts ardent template unveil agile meant unafraid urgent athlete rustled mime azure ' +
'jaded hawk baby jagged haystack baby jagged haystack ramped oncoming point template'
),
( '6900dea9753f5c7ced87b53bdcfb109a8417bca6a2797a708194157b227fb60b',
), (
'6900dea9753f5c7ced87b53bdcfb109a8417bca6a2797a708194157b227fb60b',
'criminal bamboo scamper gnaw limits womanly wrong tuition birth mundane donuts square cohesive ' +
'dolphin titans narrate fuel saved wrap aloof magically mirror together update wrap'
),
( '0000000000000000000000000000000000000000000000000000000000000001',
), (
'0000000000000000000000000000000000000000000000000000000000000001',
'abbey abbey abbey abbey abbey abbey abbey abbey abbey abbey abbey abbey abbey abbey abbey abbey ' +
'abbey abbey abbey abbey abbey bamboo jaws jerseys abbey'
),
( '1c95988d7431ecd670cf7d73f45befc6feffffffffffffffffffffffffffff0f',
), (
'1c95988d7431ecd670cf7d73f45befc6feffffffffffffffffffffffffffff0f',
'powder directed sayings enmity bacon vapidly entrance bumper noodles iguana sleepless nasty flying ' +
'soil software foamy solved soggy foamy solved soggy jury yawning ankle solved'
),
( '2c94988d7431ecd670cf7d73f45befc6feffffffffffffffffffffffffffff0f',
), (
'2c94988d7431ecd670cf7d73f45befc6feffffffffffffffffffffffffffff0f',
'memoir apart olive enmity bacon vapidly entrance bumper noodles iguana sleepless nasty flying soil ' +
'software foamy solved soggy foamy solved soggy jury yawning ankle foamy'
),
( '4bb0288c9673b69fa68c2174851884abbaaedce6af48a03bbfd25e8cd0364102',
), (
'4bb0288c9673b69fa68c2174851884abbaaedce6af48a03bbfd25e8cd0364102',
'rated bicycle pheasants dejected pouch fizzle shipped rash citadel queen avatar sample muzzle mews ' +
'jagged origin yeti dunes obtains godfather unbending pastry vortex washing citadel'
),
( '4bb0288c9673b69fa68c2174851884abbaaedce6af48a03bbfd25e8cd0364100',
), (
'4bb0288c9673b69fa68c2174851884abbaaedce6af48a03bbfd25e8cd0364100',
'rated bicycle pheasants dejected pouch fizzle shipped rash citadel queen avatar sample muzzle mews ' +
'jagged origin yeti dunes obtains godfather unbending kangaroo auctions audio citadel'
),
( '1d95988d7431ecd670cf7d73f45befc6feffffffffffffffffffffffffffff0e',
), (
'1d95988d7431ecd670cf7d73f45befc6feffffffffffffffffffffffffffff0e',
'pram distance scamper enmity bacon vapidly entrance bumper noodles iguana sleepless nasty flying ' +
'soil software foamy solved soggy foamy solved soggy hashing mullet onboard solved'
),
( 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0f',
), (
'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0f',
'foamy solved soggy foamy solved soggy foamy solved soggy foamy solved soggy foamy solved soggy ' +
'foamy solved soggy foamy solved soggy jury yawning ankle soggy'
),
@ -57,7 +58,7 @@ class unit_tests:
@property
def _use_monero_python(self):
if not hasattr(self,'_use_monero_python_'):
if not hasattr(self, '_use_monero_python_'):
try:
from monero.wordlists.english import English
self.wl = English()
@ -80,7 +81,7 @@ class unit_tests:
chk = tuple(chk.split())
res = b.fromhex(privhex)
if self._use_monero_python:
mp_chk = tuple( self.wl.encode(privhex).split() )
mp_chk = tuple(self.wl.encode(privhex).split())
assert res == mp_chk, f'check failed:\nres: {res}\nchk: {chk}'
assert res == chk, f'check failed:\nres: {res}\nchk: {chk}'
return True
@ -90,9 +91,9 @@ class unit_tests:
vmsg('Checking mnemonic to seed conversion:')
for chk, words in self.vectors:
vmsg(f' {chk}')
res = b.tohex( words.split() )
res = b.tohex(words.split())
if self._use_monero_python:
mp_chk = self.wl.decode( words )
mp_chk = self.wl.decode(words)
assert res == mp_chk, f'check failed:\nres: {res}\nchk: {mp_chk}'
assert res == chk, f'check failed:\nres: {res}\nchk: {chk}'
return True
@ -119,8 +120,8 @@ class unit_tests:
('hex', hse, 'not a hexadecimal', lambda: fh('xx')),
('seed len', sle, 'invalid seed byte len', lambda: fh(bad_seed)),
('mnemonic type', ase, 'must be list', lambda: th('string')),
('pad arg (fromhex)', ase, "invalid 'pad' arg", lambda: fh(good_hex,pad=23)),
('pad arg (tohex)', ase, "invalid 'pad' arg", lambda: th(good_mn,pad=23)),
('pad arg (fromhex)', ase, "invalid 'pad' arg", lambda: fh(good_hex, pad=23)),
('pad arg (tohex)', ase, "invalid 'pad' arg", lambda: th(good_mn, pad=23)),
('word', mne, "not in Monero", lambda: th(bad_word_mn)),
('checksum', mne, "checksum", lambda: th(bad_chksum_mn)),
('seed phrase len', mne, "phrase len", lambda: th(bad_len_mn)),