MMGen & BIP39 mnemonic: refactor interactive input code, clean up test

This commit is contained in:
The MMGen Project 2019-10-30 12:49:27 +00:00
commit 3931cc7be1
Signed by: mmgen
GPG key ID: 3F8B1861E32B7DA2
6 changed files with 61 additions and 39 deletions

View file

@ -58,10 +58,12 @@ class baseconv(object):
seedlen_map = {
'b58': { 16:22, 24:33, 32:44 },
'b6d': { 16:50, 24:75, 32:100 },
'mmgen': { 16:12, 24:18, 32:24 },
}
seedlen_map_rev = {
'b58': { 22:16, 33:24, 44:32 },
'b6d': { 50:16, 75:24, 100:32 },
'mmgen': { 12:16, 18:24, 24:32 },
}
@classmethod

View file

@ -2082,6 +2082,8 @@ zoo
mn_base = 2048
wl_chksums = { 'bip39': 'f18b9a84' }
seedlen_map = { 'bip39': { 16:12, 24:18, 32:24 } }
seedlen_map_rev = { 'bip39': { 12:16, 18:24, 24:32 } }
# ENT CS MS
constants = {
'128': (4, 12),

View file

@ -204,6 +204,9 @@ class g(object):
max_tx_file_size = 100000
max_input_size = 1024 * 1024
# pexpect chokes on these utf8 chars under MSYS2
lq,rq = (('',''),('"','"'))[bool(os.getenv('MMGEN_TEST_SUITE')) and platform=='win']
passwd_max_tries = 5
max_urandchars = 80

View file

@ -692,6 +692,29 @@ class SeedSourceUnenc(SeedSource):
self.ext,
x='' if g.debug_utf8 else '')
def _choose_seedlen(self,desc,ok_lens,subtype):
from mmgen.term import get_char
def choose_len():
prompt = self.choose_seedlen_prompt
while True:
r = get_char('\r'+prompt).decode()
if is_int(r) and 1 <= int(r) <= len(ok_lens):
break
msg_r(('\r','\n')[g.test_suite] + ' '*len(prompt) + '\r')
return ok_lens[int(r)-1]
m1 = blue('{} type:'.format(capfirst(desc)))
m2 = yellow(subtype)
msg('{} {}'.format(m1,m2))
while True:
usr_len = choose_len()
prompt = self.choose_seedlen_confirm.format(usr_len)
if keypress_confirm(prompt,default_yes=True,no_nl=not g.test_suite):
return usr_len
class SeedSourceEnc(SeedSource):
_msg = {
@ -819,40 +842,28 @@ class MMGenMnemonic(SeedSourceUnenc):
stdin_ok = True
fmt_codes = 'mmwords','words','mnemonic','mnem','mn','m'
desc = 'MMGen native mnemonic data'
mn_name = 'MMGen native'
wclass = 'mnemonic'
mn_type = 'MMGen native'
ext = 'mmwords'
mn_lens = [i // 32 * 3 for i in g.seed_lens]
wl_id = 'mmgen'
conv_cls = baseconv
choose_seedlen_prompt = 'Choose a mnemonic length: 1) 12 words, 2) 18 words, 3) 24 words: '
choose_seedlen_confirm = 'Mnemonic length of {} words chosen. OK?'
@property
def mn_lens(self):
return sorted(self.conv_cls.seedlen_map_rev[self.wl_id])
def _get_data_from_user(self,desc):
if not g.stdin_tty:
return get_data_from_user(desc)
from mmgen.term import get_char_raw,get_char
def choose_mn_len():
prompt = 'Choose a mnemonic length: 1) 12 words, 2) 18 words, 3) 24 words: '
urange = [str(i+1) for i in range(len(self.mn_lens))]
while True:
r = get_char('\r'+prompt).decode()
if r in urange: break
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
mn_len = self._choose_seedlen(self.wclass,self.mn_lens,self.mn_type)
self.conv_cls.init_mn(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
m = 'Enter your {ml}-word seed phrase, hitting ENTER or SPACE after each word.\n'
m += "Optionally, you may use pad characters. Anything you type that's not a\n"
@ -861,10 +872,10 @@ class MMGenMnemonic(SeedSourceUnenc):
m += "of words. For each word, once you've typed {lw} characters total (including\n"
m += 'pad characters) any pad character will enter the word.'
# pexpect chokes on these utf8 chars under MSYS2
lq,rq = (('',''),('"','"'))[g.test_suite and g.platform=='win']
msg(m.format(ml=mn_len,lw=longest_word,lq=lq,rq=rq))
msg(m.format(ml=mn_len,lw=longest_word,lq=g.lq,rq=g.rq))
from string import ascii_lowercase
from mmgen.term import get_char_raw
def get_word():
s,pad = '',0
while True:
@ -954,7 +965,7 @@ class BIP39Mnemonic(MMGenMnemonic):
fmt_codes = ('bip39',)
desc = 'BIP39 mnemonic data'
mn_name = 'BIP39'
mn_type = 'BIP39'
ext = 'bip39'
wl_id = 'bip39'

View file

@ -46,8 +46,6 @@ 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

View file

@ -25,6 +25,7 @@ from test.common import *
from test.test_py_d.common import *
from test.test_py_d.ts_base import *
from test.test_py_d.ts_main import TestSuiteMain
from mmgen.seed import SeedSource
class TestSuiteHelp(TestSuiteBase):
'help, info and usage screens'
@ -143,23 +144,28 @@ 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,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')
def _user_seed_entry(self,fmt,usr_rand=False,out_fmt=None):
wcls = SeedSource.fmt_code_to_type(fmt)
wf = os.path.join(ref_dir,'FE3C6545.{}'.format(wcls.ext))
if wcls.wclass == 'mnemonic':
mn = read_from_file(wf).strip().split()
mn = ['foo'] + mn[:5] + ['grac','graceful'] + mn[5:]
t = self.spawn('mmgen-walletconv',['-r10','-S','-i',fmt,'-o',out_fmt or fmt])
t.expect('{} type: {}'.format(capfirst(wcls.wclass),wcls.mn_type))
t.expect(wcls.choose_seedlen_prompt,'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 '.format(mn_name))[:8]
assert sid == sid_chk,'Seed ID mismatch! {} != {}'.format(sid,sid_chk)
if wcls.wclass == 'mnemonic':
stealth_mnemonic_entry(t,mn,fmt=fmt)
if not usr_rand:
sid_chk = 'FE3C6545'
sid = t.expect_getend('Valid {} for Seed ID '.format(wcls.desc))[: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_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)
def mnemonic_entry_mmgen(self): return self._user_seed_entry('words')
def mnemonic_entry_bip39(self): return self._user_seed_entry('bip39')
class TestSuiteTool(TestSuiteMain,TestSuiteBase):
"tests for interactive 'mmgen-tool' commands"