MMGen & BIP39 mnemonic: refactor interactive input code, clean up test
This commit is contained in:
parent
809fd0503d
commit
3931cc7be1
6 changed files with 61 additions and 39 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue