Complete BIP39 mnemonic support
- provided as an alternative to MMGen's native mnemonic format
# Run the BIP39 unit test:
$ test/unit_tests.py -v bip39
# Generate a random 128-bit BIP39 seed phrase:
$ mmgen-tool mn_rand128 fmt=bip39
# Export your default wallet to BIP39 format:
$ mmgen-walletconv -o bip39
...
BIP39 mnemonic data written to file '98831F3A[256].bip39'
# Generate ten addresses from the exported wallet:
$ mmgen-addrgen '98831F3A[256].bip39' 1-10
...
Addresses written to file '98831F3A[1-10].addrs'
# Generate ten addresses directly from your BIP39 seed phrase:
$ mmgen-addrgen -q -i bip39 1-10
...
Addresses written to file '98831F3A[1-10].addrs'
# Export subwallet 10L of your default wallet to BIP39 format:
$ mmgen-subwalletgen -o bip39 10L
...
BIP39 mnemonic data written to file 'A17F8E90[256].bip39'
This commit is contained in:
parent
c82c9c5bd7
commit
8519b68b89
19 changed files with 2507 additions and 79 deletions
2163
mmgen/bip39.py
Executable file
2163
mmgen/bip39.py
Executable file
File diff suppressed because it is too large
Load diff
|
|
@ -26,6 +26,7 @@ class BadAgeFormat(Exception): mmcode = 1
|
|||
class BadFilename(Exception): mmcode = 1
|
||||
class SocketError(Exception): mmcode = 1
|
||||
class UserAddressNotInWallet(Exception): mmcode = 1
|
||||
class MnemonicError(Exception): mmcode = 1
|
||||
|
||||
# 2: yellow hl, message only
|
||||
class InvalidTokenAddress(Exception): mmcode = 2
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
# https://github.com/spesmilo/electrum/blob/1.9.5/lib/mnemonic.py
|
||||
# Electrum - lightweight Bitcoin client. Copyright (C) 2011 thomasv@gitorious
|
||||
|
||||
words = """
|
||||
words = tuple("""
|
||||
able
|
||||
about
|
||||
above
|
||||
|
|
@ -1648,4 +1648,4 @@ young
|
|||
yours
|
||||
yourself
|
||||
youth
|
||||
"""
|
||||
""".split())
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@
|
|||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
words = """
|
||||
words = tuple("""
|
||||
abraham
|
||||
absent
|
||||
absorb
|
||||
|
|
@ -1682,4 +1682,4 @@ zigzag
|
|||
zipper
|
||||
zodiac
|
||||
zoom
|
||||
"""
|
||||
""".split())
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import os
|
|||
from mmgen.common import *
|
||||
from mmgen.obj import *
|
||||
from mmgen.crypto import *
|
||||
from mmgen.bip39 import bip39
|
||||
|
||||
pnm = g.proj_name
|
||||
|
||||
|
|
@ -33,11 +34,11 @@ def check_usr_seed_len(seed_len):
|
|||
m = "ERROR: requested seed length ({}) doesn't match seed length of source ({})"
|
||||
die(1,m.format((opt.seed_len,seed_len)))
|
||||
|
||||
def is_mnemonic(s):
|
||||
def _is_mnemonic(s,fmt):
|
||||
oq_save = opt.quiet
|
||||
opt.quiet = True
|
||||
try:
|
||||
SeedSource(in_data=s,in_fmt='words')
|
||||
SeedSource(in_data=s,in_fmt=fmt)
|
||||
ret = True
|
||||
except:
|
||||
ret = False
|
||||
|
|
@ -45,6 +46,9 @@ def is_mnemonic(s):
|
|||
opt.quiet = oq_save
|
||||
return ret
|
||||
|
||||
def is_bip39_mnemonic(s): return _is_mnemonic(s,fmt='bip39')
|
||||
def is_mmgen_mnemonic(s): return _is_mnemonic(s,fmt='words')
|
||||
|
||||
class SeedBase(MMGenObject):
|
||||
|
||||
data = MMGenImmutableAttr('data',bytes,typeconv=False)
|
||||
|
|
@ -743,14 +747,20 @@ an empty passphrase, just hit ENTER twice.
|
|||
d.key_id = make_chksum_8(key)
|
||||
d.enc_seed = encrypt_seed(self.seed.data,key)
|
||||
|
||||
class Mnemonic (SeedSourceUnenc):
|
||||
class MMGenMnemonic(SeedSourceUnenc):
|
||||
|
||||
stdin_ok = True
|
||||
fmt_codes = 'mmwords','words','mnemonic','mnem','mn','m'
|
||||
desc = 'mnemonic data'
|
||||
desc = 'MMGen native mnemonic data'
|
||||
mn_name = 'MMGen native'
|
||||
ext = 'mmwords'
|
||||
mn_lens = [i // 32 * 3 for i in g.seed_lens]
|
||||
wl_id = 'electrum' # or 'tirosh'
|
||||
wl_id = 'mmgen'
|
||||
conv_cls = baseconv
|
||||
|
||||
def __init__(self,*args,**kwargs):
|
||||
self.conv_cls.init_mn(self.wl_id)
|
||||
super().__init__(*args,**kwargs)
|
||||
|
||||
def _get_data_from_user(self,desc):
|
||||
|
||||
|
|
@ -768,12 +778,15 @@ class Mnemonic (SeedSourceUnenc):
|
|||
msg_r(('\r','\n')[g.test_suite] + ' '*len(prompt) + '\r')
|
||||
return self.mn_lens[int(r)-1]
|
||||
|
||||
msg('{} {}'.format(blue('Mnemonic type:'),yellow(self.mn_name)))
|
||||
|
||||
while True:
|
||||
mn_len = choose_mn_len()
|
||||
prompt = 'Mnemonic length of {} words chosen. OK?'.format(mn_len)
|
||||
if keypress_confirm(prompt,default_yes=True,no_nl=not g.test_suite):
|
||||
break
|
||||
wl = baseconv.digits[self.wl_id]
|
||||
|
||||
wl = self.conv_cls.digits[self.wl_id]
|
||||
longest_word = max(len(w) for w in wl)
|
||||
from string import ascii_lowercase
|
||||
|
||||
|
|
@ -832,8 +845,8 @@ class Mnemonic (SeedSourceUnenc):
|
|||
|
||||
hexseed = self.seed.hexdata
|
||||
|
||||
mn = baseconv.fromhex(hexseed,self.wl_id,self._hex2mn_pad(hexseed))
|
||||
ret = baseconv.tohex(mn,self.wl_id,self._mn2hex_pad(mn))
|
||||
mn = self.conv_cls.fromhex(hexseed,self.wl_id,self._hex2mn_pad(hexseed))
|
||||
ret = self.conv_cls.tohex(mn,self.wl_id,self._mn2hex_pad(mn))
|
||||
|
||||
# Internal error, so just die on fail
|
||||
compare_or_die(ret,'recomputed seed',hexseed,'original',e='Internal error')
|
||||
|
|
@ -851,12 +864,12 @@ class Mnemonic (SeedSourceUnenc):
|
|||
return False
|
||||
|
||||
for n,w in enumerate(mn,1):
|
||||
if w not in baseconv.digits[self.wl_id]:
|
||||
msg('Invalid mnemonic: word #{} is not in the wordlist'.format(n))
|
||||
if w not in self.conv_cls.digits[self.wl_id]:
|
||||
msg('Invalid mnemonic: word #{} is not in the {} wordlist'.format(n,self.wl_id.upper()))
|
||||
return False
|
||||
|
||||
hexseed = baseconv.tohex(mn,self.wl_id,self._mn2hex_pad(mn))
|
||||
ret = baseconv.fromhex(hexseed,self.wl_id,self._hex2mn_pad(hexseed))
|
||||
hexseed = self.conv_cls.tohex(mn,self.wl_id,self._mn2hex_pad(mn))
|
||||
ret = self.conv_cls.fromhex(hexseed,self.wl_id,self._hex2mn_pad(hexseed))
|
||||
|
||||
if len(hexseed) * 4 not in g.seed_lens:
|
||||
msg('Invalid mnemonic (produces too large a number)')
|
||||
|
|
@ -872,6 +885,15 @@ class Mnemonic (SeedSourceUnenc):
|
|||
|
||||
return True
|
||||
|
||||
class BIP39Mnemonic(MMGenMnemonic):
|
||||
|
||||
fmt_codes = ('bip39',)
|
||||
desc = 'BIP39 mnemonic data'
|
||||
mn_name = 'BIP39'
|
||||
ext = 'bip39'
|
||||
wl_id = 'bip39'
|
||||
conv_cls = bip39
|
||||
|
||||
class SeedFile (SeedSourceUnenc):
|
||||
|
||||
stdin_ok = True
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ from mmgen.protocol import hash160
|
|||
from mmgen.common import *
|
||||
from mmgen.crypto import *
|
||||
from mmgen.addr import *
|
||||
from mmgen.bip39 import bip39
|
||||
|
||||
NL = ('\n','\r\n')[g.platform=='win']
|
||||
|
||||
|
|
@ -97,7 +98,7 @@ def _usage(cmd=None,exit_val=1):
|
|||
Msg(m2)
|
||||
elif cmd in MMGenToolCmd._user_commands():
|
||||
docstr = getattr(MMGenToolCmd,cmd).__doc__.strip()
|
||||
msg('{}\n'.format(capfirst(docstr)))
|
||||
msg('{}'.format(capfirst(docstr)))
|
||||
msg('USAGE: {} {} {}'.format(g.prog_name,cmd,_create_call_sig(cmd)))
|
||||
else:
|
||||
die(1,"'{}': no such tool command".format(cmd))
|
||||
|
|
@ -224,8 +225,12 @@ def init_generators(arg=None):
|
|||
kg = KeyGenerator(at)
|
||||
ag = AddrGenerator(at)
|
||||
|
||||
wordlists = 'electrum','tirosh'
|
||||
dfl_wl_id = 'electrum'
|
||||
dfl_mnemonic_fmt = 'mmgen'
|
||||
mnemonic_fmts = {
|
||||
'mmgen': { 'fmt': 'words', 'conv_cls': baseconv },
|
||||
'bip39': { 'fmt': 'bip39', 'conv_cls': bip39 },
|
||||
}
|
||||
mn_opts_disp = "(valid options: '{}')".format("', '".join(mnemonic_fmts))
|
||||
|
||||
class MMGenToolCmdBase(object):
|
||||
|
||||
|
|
@ -454,53 +459,68 @@ class MMGenToolCmdCoin(MMGenToolCmdBase):
|
|||
|
||||
class MMGenToolCmdMnemonic(MMGenToolCmdBase):
|
||||
"""
|
||||
seed mnemonic utilities (wordlist: choose 'electrum' (default) or 'tirosh')
|
||||
seed phrase utilities (valid formats: 'mmgen' (default), 'bip39')
|
||||
|
||||
IMPORTANT NOTE: Though MMGen mnemonics use the Electrum wordlist, they're
|
||||
computed using a different algorithm and are NOT Electrum-compatible!
|
||||
IMPORTANT NOTE: MMGen's default seed phrase format uses the Electrum
|
||||
wordlist, however seed phrases are computed using a different algorithm
|
||||
and are NOT Electrum-compatible!
|
||||
|
||||
BIP39 support is fully compatible with the standard, allowing users to
|
||||
import and export seed entropy from BIP39-compatible wallets. However,
|
||||
users should be aware that BIP39 support does not imply BIP32 support!
|
||||
MMGen uses its own key derivation scheme differing from the one described
|
||||
by the BIP32 protocol.
|
||||
"""
|
||||
def _do_random_mn(self,nbytes:int,wordlist:str):
|
||||
def _do_random_mn(self,nbytes:int,fmt:str):
|
||||
assert nbytes in (16,24,32), 'nbytes must be 16, 24 or 32'
|
||||
hexrand = get_random(nbytes).hex()
|
||||
Vmsg('Seed: {}'.format(hexrand))
|
||||
return self.hex2mn(hexrand,wordlist=wordlist)
|
||||
return self.hex2mn(hexrand,fmt=fmt)
|
||||
|
||||
def mn_rand128(self,wordlist=dfl_wl_id):
|
||||
def mn_rand128(self, fmt:mn_opts_disp = dfl_mnemonic_fmt ):
|
||||
"generate random 128-bit mnemonic seed phrase"
|
||||
return self._do_random_mn(16,wordlist)
|
||||
return self._do_random_mn(16,fmt)
|
||||
|
||||
def mn_rand192(self,wordlist=dfl_wl_id):
|
||||
def mn_rand192(self, fmt:mn_opts_disp = dfl_mnemonic_fmt ):
|
||||
"generate random 192-bit mnemonic seed phrase"
|
||||
return self._do_random_mn(24,wordlist)
|
||||
return self._do_random_mn(24,fmt)
|
||||
|
||||
def mn_rand256(self,wordlist=dfl_wl_id):
|
||||
def mn_rand256(self, fmt:mn_opts_disp = dfl_mnemonic_fmt ):
|
||||
"generate random 256-bit mnemonic seed phrase"
|
||||
return self._do_random_mn(32,wordlist)
|
||||
return self._do_random_mn(32,fmt)
|
||||
|
||||
def hex2mn(self,hexstr:'sstr',wordlist=dfl_wl_id):
|
||||
"convert a 16, 24 or 32-byte hexadecimal number to a mnemonic"
|
||||
opt.out_fmt = 'words'
|
||||
def _get_mnemonic_fmt(self,fmt):
|
||||
if fmt not in mnemonic_fmts:
|
||||
m = '{!r}: invalid format (valid options: {})'
|
||||
die(1,m.format(fmt,', '.join(mnemonic_fmts)))
|
||||
return mnemonic_fmts[fmt]['fmt']
|
||||
|
||||
def hex2mn( self, hexstr:'sstr', fmt:mn_opts_disp = dfl_mnemonic_fmt ):
|
||||
"convert a 16, 24 or 32-byte hexadecimal number to a mnemonic seed phrase"
|
||||
opt.out_fmt = self._get_mnemonic_fmt(fmt)
|
||||
from mmgen.seed import SeedSource
|
||||
s = SeedSource(seed_bin=bytes.fromhex(hexstr))
|
||||
s._format()
|
||||
return ' '.join(s.ssdata.mnemonic)
|
||||
|
||||
def mn2hex(self,seed_mnemonic:'sstr',wordlist=dfl_wl_id):
|
||||
"convert a 12, 18 or 24-word mnemonic to a hexadecimal number"
|
||||
def mn2hex( self, seed_mnemonic:'sstr', fmt:mn_opts_disp = dfl_mnemonic_fmt ):
|
||||
"convert a 12, 18 or 24-word mnemonic seed phrase to a hexadecimal number"
|
||||
in_fmt = self._get_mnemonic_fmt(fmt)
|
||||
opt.quiet = True
|
||||
from mmgen.seed import SeedSource
|
||||
return SeedSource(in_data=seed_mnemonic,in_fmt='words').seed.hexdata
|
||||
return SeedSource(in_data=seed_mnemonic,in_fmt=in_fmt).seed.hexdata
|
||||
|
||||
def mn_stats(self,wordlist=dfl_wl_id):
|
||||
def mn_stats(self, fmt:mn_opts_disp = dfl_mnemonic_fmt ):
|
||||
"show stats for mnemonic wordlist"
|
||||
wordlist in baseconv.digits or die(1,"'{}': not a valid wordlist".format(wordlist))
|
||||
baseconv.check_wordlist(wordlist)
|
||||
conv_cls = mnemonic_fmts[fmt]['conv_cls']
|
||||
fmt in conv_cls.digits or die(1,"'{}': not a valid format".format(fmt))
|
||||
conv_cls.check_wordlist(fmt)
|
||||
return True
|
||||
|
||||
def mn_printlist(self,wordlist=dfl_wl_id,enum=False,pager=False):
|
||||
def mn_printlist( self, fmt:mn_opts_disp = dfl_mnemonic_fmt, enum=False, pager=False ):
|
||||
"print mnemonic wordlist"
|
||||
wordlist in baseconv.digits or die(1,"'{}': not a valid wordlist".format(wordlist))
|
||||
ret = baseconv.digits[wordlist]
|
||||
self._get_mnemonic_fmt(fmt) # perform check
|
||||
ret = mnemonic_fmts[fmt]['conv_cls'].digits[fmt]
|
||||
if enum:
|
||||
ret = ['{:>4} {}'.format(n,e) for n,e in enumerate(ret)]
|
||||
return '\n'.join(ret)
|
||||
|
|
|
|||
|
|
@ -283,9 +283,8 @@ def is_utf8(s): return is_ascii(s,enc='utf8')
|
|||
class baseconv(object):
|
||||
|
||||
mn_base = 1626 # tirosh list is 1633 words long!
|
||||
mn_ids = ('mmgen','tirosh')
|
||||
digits = {
|
||||
'electrum': tuple(__import__('mmgen.mn_electrum',fromlist=['words']).words.split()),
|
||||
'tirosh': tuple(__import__('mmgen.mn_tirosh',fromlist=['words']).words.split()[:mn_base]),
|
||||
'b58': tuple('123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'),
|
||||
'b32': tuple('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'),
|
||||
'b16': tuple('0123456789abcdef'),
|
||||
|
|
@ -293,13 +292,25 @@ class baseconv(object):
|
|||
'b8': tuple('01234567'),
|
||||
}
|
||||
wl_chksums = {
|
||||
'electrum': '5ca31424',
|
||||
'tirosh': '48f05e1f', # tirosh truncated to mn_base (1626)
|
||||
'mmgen': '5ca31424',
|
||||
'tirosh': '48f05e1f', # tirosh truncated to mn_base (1626)
|
||||
# 'tirosh1633': '1a5faeff'
|
||||
}
|
||||
b58pad_lens = [(16,22), (24,33), (32,44)]
|
||||
b58pad_lens_rev = [(v,k) for k,v in b58pad_lens]
|
||||
|
||||
@classmethod
|
||||
def init_mn(cls,mn_id):
|
||||
assert mn_id in cls.mn_ids
|
||||
if mn_id == 'mmgen':
|
||||
from mmgen.mn_electrum import words
|
||||
cls.digits[mn_id] = words
|
||||
elif mn_id == 'tirosh':
|
||||
from mmgen.mn_tirosh import words
|
||||
cls.digits[mn_id] = words[:cls.mn_base]
|
||||
else: # bip39
|
||||
cls.digits[mn_id] = cls.words
|
||||
|
||||
@classmethod
|
||||
def b58encode(cls,s,pad=None):
|
||||
pad = cls._get_pad(s,pad,'b58encode',cls.b58pad_lens,(bytes,))
|
||||
|
|
@ -327,6 +338,7 @@ class baseconv(object):
|
|||
|
||||
@classmethod
|
||||
def get_wordlist_chksum(cls,wl_id):
|
||||
cls.init_mn(wl_id)
|
||||
return sha256(' '.join(cls.digits[wl_id]).encode()).hexdigest()[:8]
|
||||
|
||||
@classmethod
|
||||
|
|
@ -364,7 +376,7 @@ class baseconv(object):
|
|||
|
||||
@classmethod
|
||||
def fromhex(cls,hexnum,wl_id,pad=None,tostr=False):
|
||||
if wl_id in ('electrum','tirosh'):
|
||||
if wl_id in ('mmgen','tirosh'):
|
||||
assert tostr == False,"'tostr' must be False for '{}'".format(wl_id)
|
||||
|
||||
if not is_hex_str(hexnum):
|
||||
|
|
@ -379,8 +391,6 @@ class baseconv(object):
|
|||
o = [wl[n] for n in [0] * ((pad or 0)-len(ret)) + ret[::-1]]
|
||||
return ''.join(o) if tostr else o
|
||||
|
||||
baseconv.check_wordlists()
|
||||
|
||||
def match_ext(addr,ext):
|
||||
return addr.split('.')[-1] == ext
|
||||
|
||||
|
|
|
|||
1
setup.py
1
setup.py
|
|
@ -84,6 +84,7 @@ setup(
|
|||
'mmgen.addr',
|
||||
'mmgen.altcoin',
|
||||
'mmgen.bech32',
|
||||
'mmgen.bip39',
|
||||
'mmgen.color',
|
||||
'mmgen.common',
|
||||
'mmgen.crypto',
|
||||
|
|
|
|||
1
test/ref/1378FC64.bip39
Normal file
1
test/ref/1378FC64.bip39
Normal file
|
|
@ -0,0 +1 @@
|
|||
earth hip style decade say bulb cattle strike install air once labor asset bronze piece pact digital hollow
|
||||
1
test/ref/98831F3A.bip39
Normal file
1
test/ref/98831F3A.bip39
Normal file
|
|
@ -0,0 +1 @@
|
|||
earth hip style decade say bulb cattle strike install air once labor asset bronze piece pact digital gym dry candy finger across define doll
|
||||
1
test/ref/FE3C6545.bip39
Normal file
1
test/ref/FE3C6545.bip39
Normal file
|
|
@ -0,0 +1 @@
|
|||
earth hip style decade say bulb cattle strike install air once large
|
||||
|
|
@ -47,6 +47,7 @@ non_mmgen_fn = 'coinkey'
|
|||
ref_dir = os.path.join('test','ref')
|
||||
dfl_words_file = os.path.join(ref_dir,'98831F3A.mmwords')
|
||||
mn_words_mmgen = os.path.join(ref_dir,'FE3C6545.mmwords')
|
||||
mn_words_bip39 = os.path.join(ref_dir,'FE3C6545.bip39')
|
||||
|
||||
from mmgen.obj import MMGenTXLabel,TwComment
|
||||
|
||||
|
|
@ -149,7 +150,7 @@ def get_label(do_shuffle=False):
|
|||
|
||||
def stealth_mnemonic_entry(t,mn,fmt):
|
||||
wnum = 1
|
||||
max_wordlen = { 'words': 12 }[fmt]
|
||||
max_wordlen = { 'words': 12, 'bip39': 8 }[fmt]
|
||||
|
||||
def get_pad_chars(n):
|
||||
ret = ''
|
||||
|
|
|
|||
|
|
@ -94,6 +94,7 @@ class TestSuiteMain(TestSuiteBase,TestSuiteShared):
|
|||
('export_seed', (1,'seed export to mmseed format', [[['mmdat'],1]])),
|
||||
('export_hex', (1,'seed export to hexadecimal format', [[['mmdat'],1]])),
|
||||
('export_mnemonic', (1,'seed export to mmwords format', [[['mmdat'],1]])),
|
||||
('export_bip39', (1,'seed export to bip39 format', [[['mmdat'],1]])),
|
||||
('export_incog', (1,'seed export to mmincog format', [[['mmdat'],1]])),
|
||||
('export_incog_hex',(1,'seed export to mmincog hex format', [[['mmdat'],1]])),
|
||||
('export_incog_hidden',(1,'seed export to hidden mmincog format', [[['mmdat'],1]])),
|
||||
|
|
@ -227,7 +228,7 @@ class TestSuiteMain(TestSuiteBase,TestSuiteShared):
|
|||
t.license()
|
||||
t.passphrase('MMGen wallet',self.cfgs['1']['wpasswd'])
|
||||
t.expect('Generating subseed 3L')
|
||||
fn = t.written_to_file('Mnemonic data')
|
||||
fn = t.written_to_file('MMGen native mnemonic data')
|
||||
assert fn[-8:] == '.mmwords','incorrect file extension: {}'.format(fn[-8:])
|
||||
return t
|
||||
|
||||
|
|
@ -523,7 +524,10 @@ class TestSuiteMain(TestSuiteBase,TestSuiteShared):
|
|||
return self.export_seed(wf,desc=desc,out_fmt=out_fmt,pf=pf)
|
||||
|
||||
def export_mnemonic(self,wf):
|
||||
return self.export_seed(wf,desc='mnemonic data',out_fmt='words')
|
||||
return self.export_seed(wf,desc='MMGen native mnemonic data',out_fmt='words')
|
||||
|
||||
def export_bip39(self,wf):
|
||||
return self.export_seed(wf,desc='BIP39 mnemonic data',out_fmt='bip39')
|
||||
|
||||
def export_incog(self,wf,desc='incognito data',out_fmt='i',add_args=[]):
|
||||
uargs = ['-p1',self.usr_rand_arg] + add_args
|
||||
|
|
@ -560,7 +564,7 @@ class TestSuiteMain(TestSuiteBase,TestSuiteShared):
|
|||
return self.addrgen_seed(wf,foo,desc=desc,in_fmt=in_fmt)
|
||||
|
||||
def addrgen_mnemonic(self,wf,foo):
|
||||
return self.addrgen_seed(wf,foo,desc='mnemonic data',in_fmt='words')
|
||||
return self.addrgen_seed(wf,foo,desc='MMGen native mnemonic data',in_fmt='words')
|
||||
|
||||
def addrgen_incog(self,wf=[],foo='',in_fmt='i',desc='incognito data',args=[]):
|
||||
t = self.spawn('mmgen-addrgen', args + self.segwit_arg + ['-i'+in_fmt,'-d',self.tmpdir]+
|
||||
|
|
|
|||
|
|
@ -116,7 +116,8 @@ class TestSuiteInput(TestSuiteBase):
|
|||
cmd_group = (
|
||||
('password_entry_noecho', (1,"utf8 password entry", [])),
|
||||
('password_entry_echo', (1,"utf8 password entry (echoed)", [])),
|
||||
('mnemonic_entry', (1,"stealth mnemonic entry", [])),
|
||||
('mnemonic_entry_mmgen', (1,"stealth mnemonic entry (MMGen native)", [])),
|
||||
('mnemonic_entry_bip39', (1,"stealth mnemonic entry (BIP39)", [])),
|
||||
)
|
||||
|
||||
def password_entry(self,prompt,cmd_args):
|
||||
|
|
@ -141,21 +142,23 @@ class TestSuiteInput(TestSuiteBase):
|
|||
return 'skip' # pexpect double-escapes utf8, so skip
|
||||
return self.password_entry('Enter passphrase (echoed): ',['--echo-passphrase'])
|
||||
|
||||
def _mnemonic_entry(self,fmt,wf):
|
||||
def _mnemonic_entry(self,fmt,mn_name,wf):
|
||||
mn = read_from_file(wf).strip().split()
|
||||
mn = ['foo'] + mn[:5] + ['grac','graceful'] + mn[5:]
|
||||
t = self.spawn('mmgen-walletconv',['-S','-i',fmt,'-o',fmt])
|
||||
t.expect('Mnemonic type: {}'.format(mn_name))
|
||||
t.expect('words: ','1')
|
||||
t.expect('(Y/n): ','y')
|
||||
stealth_mnemonic_entry(t,mn,fmt=fmt)
|
||||
sid_chk = 'FE3C6545'
|
||||
sid = t.expect_getend('Valid mnemonic data for Seed ID ')[:8]
|
||||
sid = t.expect_getend('Valid {} mnemonic data for Seed ID '.format(mn_name))[:8]
|
||||
assert sid == sid_chk,'Seed ID mismatch! {} != {}'.format(sid,sid_chk)
|
||||
t.expect('to confirm: ','YES\n')
|
||||
t.read()
|
||||
return t
|
||||
|
||||
def mnemonic_entry(self): return self._mnemonic_entry('words',mn_words_mmgen)
|
||||
def mnemonic_entry_mmgen(self): return self._mnemonic_entry('words','MMGen native',mn_words_mmgen)
|
||||
def mnemonic_entry_bip39(self): return self._mnemonic_entry('bip39','BIP39',mn_words_bip39)
|
||||
|
||||
class TestSuiteTool(TestSuiteMain,TestSuiteBase):
|
||||
"tests for interactive 'mmgen-tool' commands"
|
||||
|
|
|
|||
|
|
@ -125,12 +125,12 @@ class TestSuiteRef(TestSuiteBase,TestSuiteShared):
|
|||
t = self.spawn('mmgen-subwalletgen',args,extra_desc='(generate subwallet)')
|
||||
t.expect('Generating subseed {}'.format(ss_idx))
|
||||
chk_sid = self.chk_data['ref_subwallet_sid']['98831F3A:{}'.format(ss_idx)]
|
||||
fn = t.written_to_file('Mnemonic data')
|
||||
fn = t.written_to_file('MMGen native mnemonic data')
|
||||
assert chk_sid in fn,'incorrect filename: {} (does not contain {})'.format(fn,chk_sid)
|
||||
ok()
|
||||
|
||||
t = self.spawn('mmgen-walletchk',[fn],extra_desc='(check subwallet)')
|
||||
t.expect(r'Valid mnemonic data for Seed ID ([0-9A-F]*)\b',regex=True)
|
||||
t.expect(r'Valid MMGen native mnemonic data for Seed ID ([0-9A-F]*)\b',regex=True)
|
||||
sid = t.p.match.group(1)
|
||||
assert sid == chk_sid,'subseed ID {} does not match expected value {}'.format(sid,chk_sid)
|
||||
t.read()
|
||||
|
|
|
|||
|
|
@ -151,12 +151,14 @@ class TestSuiteRef3Seed(TestSuiteBase,TestSuiteShared):
|
|||
('ref_wallet_chk', ([],'saved reference wallet')),
|
||||
('ref_seed_chk', ([],'saved seed file')),
|
||||
('ref_hex_chk', ([],'saved mmhex file')),
|
||||
('ref_mn_chk', ([],'saved mnemonic file')),
|
||||
('ref_mn_chk', ([],'saved native MMGen mnemonic file')),
|
||||
('ref_bip39_chk', ([],'saved BIP39 mnemonic file')),
|
||||
('ref_hincog_chk', ([],'saved hidden incog reference wallet')),
|
||||
('ref_brain_chk', ([],'saved brainwallet')), # in ts_shared
|
||||
# generating new reference ('abc' brainwallet) files:
|
||||
('ref_walletgen_brain', ([],'generating new reference wallet + filename check (brain)')),
|
||||
('ref_walletconv_words', (['mmdat',pwfile],'wallet filename (words)')),
|
||||
('ref_walletconv_words', (['mmdat',pwfile],'wallet filename (native mnemonic)')),
|
||||
('ref_walletconv_bip39', (['mmdat',pwfile],'wallet filename (bip39)')),
|
||||
('ref_walletconv_seed', (['mmdat',pwfile],'wallet filename (seed)')),
|
||||
('ref_walletconv_hexseed',(['mmdat',pwfile],'wallet filename (hex seed)')),
|
||||
('ref_walletconv_incog', (['mmdat',pwfile],'wallet filename (incog)')),
|
||||
|
|
@ -201,8 +203,12 @@ class TestSuiteRef3Seed(TestSuiteBase,TestSuiteShared):
|
|||
return self.ref_ss_chk(ss=HexSeedFile)
|
||||
|
||||
def ref_mn_chk(self):
|
||||
from mmgen.seed import Mnemonic
|
||||
return self.ref_ss_chk(ss=Mnemonic)
|
||||
from mmgen.seed import MMGenMnemonic
|
||||
return self.ref_ss_chk(ss=MMGenMnemonic)
|
||||
|
||||
def ref_bip39_chk(self):
|
||||
from mmgen.seed import BIP39Mnemonic
|
||||
return self.ref_ss_chk(ss=BIP39Mnemonic)
|
||||
|
||||
def ref_hincog_chk(self,desc='hidden incognito data'):
|
||||
source = TestSuiteWalletConv.sources[str(self.seed_len)]
|
||||
|
|
@ -263,7 +269,10 @@ class TestSuiteRef3Seed(TestSuiteBase,TestSuiteShared):
|
|||
return t
|
||||
|
||||
def ref_walletconv_words(self,fn,pf):
|
||||
return self.ref_walletconv(fn,pf,ofmt='mn',desc='Mnemonic data',ext='mmwords')
|
||||
return self.ref_walletconv(fn,pf,ofmt='mn',desc='MMGen native mnemonic data',ext='mmwords')
|
||||
|
||||
def ref_walletconv_bip39(self,fn,pf):
|
||||
return self.ref_walletconv(fn,pf,ofmt='bip39',desc='BIP39 mnemonic data',ext='bip39')
|
||||
|
||||
def ref_walletconv_seed(self,fn,pf):
|
||||
return self.ref_walletconv(fn,pf,ofmt='mmseed',desc='Seed data',ext='mmseed')
|
||||
|
|
|
|||
|
|
@ -59,7 +59,8 @@ class TestSuiteWalletConv(TestSuiteBase,TestSuiteShared):
|
|||
cmd_group = (
|
||||
# reading
|
||||
('ref_wallet_conv', 'conversion of saved reference wallet'),
|
||||
('ref_mn_conv', 'conversion of saved mnemonic'),
|
||||
('ref_mn_conv', 'conversion of saved MMGen native mnemonic'),
|
||||
('ref_bip39_conv', 'conversion of saved BIP39 mnemonic'),
|
||||
('ref_seed_conv', 'conversion of saved seed file'),
|
||||
('ref_hex_conv', 'conversion of saved hexadecimal seed file'),
|
||||
('ref_brain_conv', 'conversion of ref brainwallet'),
|
||||
|
|
@ -69,7 +70,8 @@ class TestSuiteWalletConv(TestSuiteBase,TestSuiteShared):
|
|||
('ref_hincog_conv_old','conversion of saved hidden incog wallet (old format)'),
|
||||
# writing
|
||||
('ref_wallet_conv_out', 'ref seed conversion to wallet'),
|
||||
('ref_mn_conv_out', 'ref seed conversion to mnemonic'),
|
||||
('ref_mn_conv_out', 'ref seed conversion to MMGen native mnemonic'),
|
||||
('ref_bip39_conv_out', 'ref seed conversion to BIP39 mnemonic'),
|
||||
('ref_hex_conv_out', 'ref seed conversion to hex seed'),
|
||||
('ref_seed_conv_out', 'ref seed conversion to seed'),
|
||||
('ref_incog_conv_out', 'ref seed conversion to incog data'),
|
||||
|
|
@ -88,10 +90,13 @@ class TestSuiteWalletConv(TestSuiteBase,TestSuiteShared):
|
|||
wf = joinpath(ref_dir,self.sources[str(self.seed_len)]['ref_wallet'])
|
||||
return self.walletconv_in(wf,'MMGen wallet',pw=True,oo=True)
|
||||
|
||||
def ref_mn_conv(self,ext='mmwords',desc='Mnemonic data'):
|
||||
def ref_mn_conv(self,ext='mmwords',desc='MMGen native mnemonic data'):
|
||||
wf = joinpath(ref_dir,self.seed_id+'.'+ext)
|
||||
return self.walletconv_in(wf,desc,oo=True)
|
||||
|
||||
def ref_bip39_conv(self):
|
||||
return self.ref_mn_conv(ext='bip39',desc='BIP39 mnemonic data')
|
||||
|
||||
def ref_seed_conv(self):
|
||||
return self.ref_mn_conv(ext='mmseed',desc='Seed data')
|
||||
|
||||
|
|
@ -123,7 +128,10 @@ class TestSuiteWalletConv(TestSuiteBase,TestSuiteShared):
|
|||
return self.walletconv_out('MMGen wallet','w',pw=True)
|
||||
|
||||
def ref_mn_conv_out(self):
|
||||
return self.walletconv_out('mnemonic data','mn')
|
||||
return self.walletconv_out('MMGen native mnemonic data','mn')
|
||||
|
||||
def ref_bip39_conv_out(self):
|
||||
return self.walletconv_out('BIP39 mnemonic data','bip39')
|
||||
|
||||
def ref_seed_conv_out(self):
|
||||
return self.walletconv_out('seed data','seed')
|
||||
|
|
@ -181,14 +189,14 @@ class TestSuiteWalletConv(TestSuiteBase,TestSuiteShared):
|
|||
else:
|
||||
t.expect(['Passphrase is OK',' are correct'])
|
||||
# Output
|
||||
wf = t.written_to_file('Mnemonic data',oo=oo)
|
||||
wf = t.written_to_file('MMGen native mnemonic data',oo=oo)
|
||||
t.p.wait()
|
||||
# back check of result
|
||||
msg('' if opt.profile else ' OK')
|
||||
return self.walletchk( wf,
|
||||
pf = None,
|
||||
extra_desc = '(check)',
|
||||
desc = 'mnemonic data',
|
||||
desc = 'MMGen native mnemonic data',
|
||||
sid = self.seed_id )
|
||||
|
||||
def walletconv_out(self,desc,out_fmt='w',uopts=[],uopts_chk=[],pw=False):
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ os.environ['MMGEN_TEST_SUITE'] = '1'
|
|||
from mmgen.common import *
|
||||
from test.common import *
|
||||
from mmgen.obj import is_wif,is_coin_addr
|
||||
from mmgen.seed import is_mnemonic
|
||||
from mmgen.seed import is_bip39_mnemonic,is_mmgen_mnemonic
|
||||
|
||||
NL = ('\n','\r\n')[g.platform=='win']
|
||||
|
||||
|
|
@ -94,10 +94,11 @@ kafile_code = (
|
|||
"\nopt.use_old_ed25519 = None" +
|
||||
"\nopt.passwd_file = 'test/ref/keyaddrfile_password'" )
|
||||
|
||||
from test.unit_tests_d.ut_bip39 import unit_test as bip39
|
||||
tests = {
|
||||
'Mnemonic': {
|
||||
'hex2mn': [
|
||||
( ['deadbeefdeadbeefdeadbeefdeadbeef'],
|
||||
( ['deadbeefdeadbeefdeadbeefdeadbeef','fmt=mmgen'],
|
||||
'table cast forgive master funny gaze sadness ripple million paint moral match' ),
|
||||
( ['deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef'],
|
||||
('swirl maybe anymore mix scale stray fog use approach page crime rhyme ' +
|
||||
|
|
@ -116,9 +117,9 @@ tests = {
|
|||
( ['0000000000000000000000000000000000000000000000000000000000000001'],
|
||||
('able able able able able able able able able able able able ' +
|
||||
'able able able able able able able able able able able about') ),
|
||||
],
|
||||
] + [([a,'fmt=bip39'],b) for a,b in bip39.vectors],
|
||||
'mn2hex': [
|
||||
( ['table cast forgive master funny gaze sadness ripple million paint moral match'],
|
||||
( ['table cast forgive master funny gaze sadness ripple million paint moral match','fmt=mmgen'],
|
||||
'deadbeefdeadbeefdeadbeefdeadbeef' ),
|
||||
( ['swirl maybe anymore mix scale stray fog use approach page crime rhyme ' +
|
||||
'class former strange window snap soon'],
|
||||
|
|
@ -137,12 +138,30 @@ tests = {
|
|||
( ['able able able able able able able able able able able able ' +
|
||||
'able able able able able able able able able able able about'],
|
||||
'0000000000000000000000000000000000000000000000000000000000000001'),
|
||||
] + [([b,'fmt=bip39'],a) for a,b in bip39.vectors],
|
||||
'mn_rand128': [
|
||||
( [], is_mmgen_mnemonic, ['-r0']),
|
||||
( ['fmt=mmgen'], is_mmgen_mnemonic, ['-r0']),
|
||||
( ['fmt=bip39'], is_bip39_mnemonic, ['-r0']),
|
||||
],
|
||||
'mn_rand192': [
|
||||
( ['fmt=mmgen'], is_mmgen_mnemonic, ['-r0']),
|
||||
( ['fmt=bip39'], is_bip39_mnemonic, ['-r0']),
|
||||
],
|
||||
'mn_rand256': [
|
||||
( ['fmt=mmgen'], is_mmgen_mnemonic, ['-r0']),
|
||||
( ['fmt=bip39'], is_bip39_mnemonic, ['-r0']),
|
||||
],
|
||||
'mn_stats': [
|
||||
( [], is_str ),
|
||||
( ['fmt=mmgen'], is_str ),
|
||||
( ['fmt=bip39'], is_str ),
|
||||
],
|
||||
'mn_printlist': [
|
||||
( [], is_str ),
|
||||
( ['fmt=mmgen'], is_str ),
|
||||
( ['fmt=bip39'], is_str ),
|
||||
],
|
||||
'mn_rand128': [ ( [], is_mnemonic, ['-r0']), ( ['wordlist=tirosh'], is_mnemonic, ['-r0']), ],
|
||||
'mn_rand192': [ ( [], is_mnemonic, ['-r0']), ( ['wordlist=tirosh'], is_mnemonic, ['-r0']), ],
|
||||
'mn_rand256': [ ( [], is_mnemonic, ['-r0']), ( ['wordlist=tirosh'], is_mnemonic, ['-r0']), ],
|
||||
'mn_stats': [ ( [], is_str ), ( ['wordlist=tirosh'], is_str ), ],
|
||||
'mn_printlist': [ ( [], is_str ), ( ['wordlist=tirosh'], is_str ), ],
|
||||
},
|
||||
'Util': {
|
||||
'hextob32': [
|
||||
|
|
|
|||
164
test/unit_tests_d/ut_bip39.py
Executable file
164
test/unit_tests_d/ut_bip39.py
Executable file
|
|
@ -0,0 +1,164 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
test/unit_tests_d/ut_bip39: BIP39 unit test for the MMGen suite
|
||||
"""
|
||||
|
||||
from mmgen.common import *
|
||||
from mmgen.exception import *
|
||||
from mmgen.bip39 import *
|
||||
|
||||
class unit_test(object):
|
||||
|
||||
vectors = (
|
||||
( "00000000000000000000000000000000",
|
||||
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
|
||||
),
|
||||
( "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",
|
||||
"legal winner thank year wave sausage worth useful legal winner thank yellow"
|
||||
),
|
||||
( "80808080808080808080808080808080",
|
||||
"letter advice cage absurd amount doctor acoustic avoid letter advice cage above"
|
||||
),
|
||||
( "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",
|
||||
"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",
|
||||
"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",
|
||||
"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",
|
||||
"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",
|
||||
"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",
|
||||
"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"
|
||||
)
|
||||
)
|
||||
|
||||
def run_test(self,name):
|
||||
|
||||
msg_r('Testing BIP39 conversion routines...')
|
||||
qmsg('')
|
||||
|
||||
from mmgen.bip39 import bip39
|
||||
|
||||
bip39.check_wordlists()
|
||||
bip39.check_wordlist('bip39')
|
||||
|
||||
vmsg('')
|
||||
qmsg('Checking seed to mnemonic conversion:')
|
||||
for v in self.vectors:
|
||||
chk = tuple(v[1].split())
|
||||
vmsg(' '+v[1])
|
||||
res = bip39.fromhex(v[0],'bip39')
|
||||
assert res == chk, 'mismatch:\nres: {}\nchk: {}'.format(res,chk)
|
||||
|
||||
vmsg('')
|
||||
qmsg('Checking mnemonic to seed conversion:')
|
||||
for v in self.vectors:
|
||||
chk = v[0]
|
||||
vmsg(' '+chk)
|
||||
res = bip39.tohex(v[1].split(),'bip39')
|
||||
assert res == chk, 'mismatch:\nres: {}\nchk: {}'.format(res,chk)
|
||||
|
||||
vmsg('')
|
||||
qmsg('Checking error handling:')
|
||||
|
||||
bad_data = (
|
||||
('bad hex', 'AssertionError', 'not a hexadecimal'),
|
||||
('bad id (tohex)', 'AssertionError', "must be 'bip39'"),
|
||||
('bad seed len', 'AssertionError', 'invalid seed bit length'),
|
||||
('bad mnemonic type', 'AssertionError', 'must be list'),
|
||||
('bad id (fromhex)', 'AssertionError', "must be 'bip39'"),
|
||||
('tostr = True', 'AssertionError', "'tostr' must be"),
|
||||
('bad pad length (fromhex)', 'AssertionError', "invalid pad len"),
|
||||
('bad pad length (tohex)', 'AssertionError', "invalid pad len"),
|
||||
('bad word', 'MnemonicError', "not in the BIP39 word list"),
|
||||
('bad checksum', 'MnemonicError', "checksum"),
|
||||
('bad seed phrase length', 'MnemonicError', "phrase len"),
|
||||
)
|
||||
|
||||
good_mn = "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong".split()
|
||||
bad_len_mn = "zoo zoo zoo".split()
|
||||
bad_chksum_mn = "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo".split()
|
||||
bad_word_mn = "admire zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo".split()
|
||||
bad_seed = 'deadbeef'
|
||||
good_seed = 'deadbeef' * 4
|
||||
|
||||
def bad0(): bip39.fromhex('xx','bip39')
|
||||
def bad1(): bip39.fromhex(good_seed,'foo')
|
||||
def bad2(): bip39.fromhex(bad_seed,'bip39')
|
||||
def bad3(): bip39.tohex('string','bip39')
|
||||
def bad4(): bip39.tohex(good_mn,'foo')
|
||||
def bad5(): bip39.fromhex(good_seed,'bip39',tostr=True)
|
||||
def bad6(): bip39.fromhex(good_seed,'bip39',pad=23)
|
||||
def bad7(): bip39.tohex(good_mn,'bip39',pad=23)
|
||||
def bad8(): bip39.tohex(bad_word_mn,'bip39')
|
||||
def bad9(): bip39.tohex(bad_chksum_mn,'bip39')
|
||||
def bad10(): bip39.tohex(bad_len_mn,'bip39')
|
||||
|
||||
for i in range(len(bad_data)):
|
||||
try:
|
||||
vmsg_r(' {:26}'.format(bad_data[i][0]+':'))
|
||||
locals()['bad'+str(i)]()
|
||||
except Exception as e:
|
||||
n = type(e).__name__
|
||||
vmsg(' {:15} [{}]'.format(n,e.args[0]))
|
||||
assert n == bad_data[i][1]
|
||||
assert bad_data[i][2] in e.args[0]
|
||||
else:
|
||||
rdie(3,"\nillegal action '{}' failed to raise exception".format(bad_data[n][0]))
|
||||
|
||||
vmsg('')
|
||||
msg('OK')
|
||||
|
||||
return True
|
||||
Loading…
Add table
Add a link
Reference in a new issue