mmgen-wallet/test/modtest_d/baseconv.py

255 lines
9.2 KiB
Python
Executable file

#!/usr/bin/env python3
"""
test.modtest_d.baseconv: Base conversion unit test for the MMGen suite
"""
from mmgen.util import msg, msg_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'),
),
# 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'),
),
'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'),
),
'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'),
),
'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'),
),
'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'),
(('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'
),
),
}
def run_test(self, name, ut):
msg_r('Testing base conversion routines...')
from mmgen.baseconv import baseconv
perr = "length of {!r} less than pad length ({})"
rerr = "return value ({!r}) does not match reference value ({!r})"
qmsg_r('\nChecking hex-to-base conversion:')
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)
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)))
qmsg_r('\nChecking base-to-hex conversion:')
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:
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))
else:
assert ret == hexstr, rerr.format(ret, hexstr)
vmsg(fs.format(h=ret_chk, r=ret, p=str(pad)))
qmsg_r('\nChecking wordlist checksums:')
vmsg('')
for wl_id in baseconv.constants['wl_chksum']:
vmsg_r(f' {wl_id+":":9}')
baseconv(wl_id).check_wordlist(cfg)
qmsg('')
qmsg('Checking error handling:')
bad_b58 = 'I'*22
bad_b58len = 'a'*23
fr58 = baseconv('b58').fromhex
to58 = baseconv('b58').tohex
to32 = baseconv('b32').tohex
to8 = baseconv('b8').tohex
hse = 'HexadecimalStringError'
bce = 'BaseConversionError'
bpe = 'BaseConversionPadError'
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')),
)
ut.process_bad_data(bad_data)
msg('OK')
return True