baseconv: minor cleanups & fixes (import, init_mn)

This commit is contained in:
The MMGen Project 2019-10-29 13:40:07 +00:00
commit f1e7cbb7a0
Signed by: mmgen
GPG key ID: 3F8B1861E32B7DA2
6 changed files with 38 additions and 28 deletions

View file

@ -29,50 +29,53 @@ def is_b32_str(s): return set(list(s)) <= set(baseconv.digits['b32'])
class baseconv(object):
desc = {
'b58': ('base58', 'base58-encoded data'),
'b32': ('MMGen base32', 'MMGen base32-encoded data created using simple base conversion'),
'b16': ('hexadecimal string', 'base16 (hexadecimal) string data'),
'b10': ('base10 string', 'base10 (decimal) string data'),
'b8': ('base8 string', 'base8 (octal) string data'),
'b6d': ('base6d (die roll)', 'base6 data using the digits from one to six'),
'b58': ('base58', 'base58-encoded data'),
'b32': ('MMGen base32', 'MMGen base32-encoded data created using simple base conversion'),
'b16': ('hexadecimal string','base16 (hexadecimal) string data'),
'b10': ('base10 string', 'base10 (decimal) string data'),
'b8': ('base8 string', 'base8 (octal) string data'),
'b6d': ('base6d (die roll)', 'base6 data using the digits from one to six'),
'tirosh':('Tirosh mnemonic', 'base1626 mnemonic using truncated Tirosh wordlist'), # not used by wallet
'mmgen': ('MMGen native mnemonic',
'MMGen native mnemonic seed phrase data created using old Electrum wordlist and simple base conversion'),
}
# https://en.wikipedia.org/wiki/Base32#RFC_4648_Base32_alphabet
# https://tools.ietf.org/html/rfc4648
digits = {
'b58': tuple('123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'),
'b32': tuple('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'),
'b32': tuple('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'), # RFC 4648 alphabet
'b16': tuple('0123456789abcdef'),
'b10': tuple('0123456789'),
'b8': tuple('01234567'),
'b6d': tuple('123456'),
}
mn_base = 1626 # tirosh list is 1633 words long!
mn_ids = ('mmgen','tirosh')
wl_chksums = {
'mmgen': '5ca31424',
'tirosh': '48f05e1f', # tirosh truncated to mn_base (1626)
# 'tirosh1633': '1a5faeff'
}
seed_pad_lens = {
seedlen_map = {
'b58': { 16:22, 24:33, 32:44 },
'b6d': { 16:50, 24:75, 32:100 },
}
seed_pad_lens_rev = {
seedlen_map_rev = {
'b58': { 22:16, 33:24, 44:32 },
'b6d': { 50:16, 75:24, 100:32 },
}
@classmethod
def init_mn(cls,mn_id):
assert mn_id in cls.mn_ids
if mn_id in cls.digits:
return
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
else:
raise ValueError('{}: unrecognized mnemonic ID'.format(mn_id))
@classmethod
def get_wordlist(cls,wl_id):
@ -109,7 +112,7 @@ class baseconv(object):
"""
'pad' argument to baseconv conversion methods must be either None, 'seed' or an integer.
If None, output of minimum (but never zero) length will be produced.
If 'seed', output length will be mapped from input length using data in seed_pad_lens.
If 'seed', output length will be mapped from input length using data in seedlen_map.
If an integer, the string, hex string or byte output will be padded to this length.
"""
if pad == None:
@ -131,6 +134,9 @@ class baseconv(object):
def tobytes(cls,words_arg,wl_id,pad=None):
"convert string or list data of base 'wl_id' to byte string"
if wl_id not in cls.digits:
cls.init_mn(wl_id)
words = words_arg if isinstance(words_arg,(list,tuple)) else tuple(words_arg.strip())
desc = cls.desc[wl_id][0]
@ -138,8 +144,8 @@ class baseconv(object):
raise BaseConversionError('empty {} data'.format(desc))
def get_seed_pad():
assert wl_id in cls.seed_pad_lens_rev,'seed padding not supported for base {!r}'.format(wl_id)
d = cls.seed_pad_lens_rev[wl_id]
assert wl_id in cls.seedlen_map_rev,'seed padding not supported for base {!r}'.format(wl_id)
d = cls.seedlen_map_rev[wl_id]
if not len(words) in d:
m = '{}: invalid length for seed-padded {} data in base conversion'
raise BaseConversionError(m.format(len(words),desc))
@ -172,12 +178,15 @@ class baseconv(object):
def frombytes(cls,bytestr,wl_id,pad=None,tostr=False):
"convert byte string to list or string data of base 'wl_id'"
if wl_id not in cls.digits:
cls.init_mn(wl_id)
if not bytestr:
raise BaseConversionError('empty data not allowed in base conversion')
def get_seed_pad():
assert wl_id in cls.seed_pad_lens,'seed padding not supported for base {!r}'.format(wl_id)
d = cls.seed_pad_lens[wl_id]
assert wl_id in cls.seedlen_map,'seed padding not supported for base {!r}'.format(wl_id)
d = cls.seedlen_map[wl_id]
if not len(bytestr) in d:
m = '{}: invalid byte length for seed data in seed-padded base conversion'
raise SeedLengthError(m.format(len(bytestr)))

View file

@ -2081,7 +2081,6 @@ zoo
""".split())
mn_base = 2048
mn_ids = ('bip39',)
wl_chksums = { 'bip39': 'f18b9a84' }
# ENT CS MS
constants = {
@ -2170,3 +2169,7 @@ zoo
res = seed_bin + chk_bin
return tuple(wl[int(res[i*11:(i+1)*11],2)] for i in range(mn_len))
@classmethod
def init_mn(cls,mn_id):
assert mn_id == 'bip39', "'mn_id' must be 'bip39'"

View file

@ -25,7 +25,6 @@ from mmgen.util import msg,ymsg,Msg,ydie
from mmgen.obj import MMGenObject,BTCAmt,LTCAmt,BCHAmt,B2XAmt,ETHAmt
from mmgen.globalvars import g
import mmgen.bech32 as bech32
from mmgen.baseconv import baseconv,is_b58_str
def hash160(hexnum): # take hex, return hex - OP_HASH160
return hashlib.new('ripemd160',hashlib.sha256(bytes.fromhex(hexnum)).digest()).hexdigest()
@ -407,6 +406,8 @@ class MoneroProtocol(DummyWIF,BitcoinProtocolAddrgen):
@classmethod
def verify_addr(cls,addr,hex_width,return_dict=False):
from mmgen.baseconv import baseconv,is_b58_str
def b58dec(addr_str):
l = len(addr_str)
a = ''.join([baseconv.tohex(addr_str[i*11:i*11+11],'b58',pad=16) for i in range(l//11)])

View file

@ -825,10 +825,6 @@ class MMGenMnemonic(SeedSourceUnenc):
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):
if not g.stdin_tty:
@ -853,6 +849,7 @@ class MMGenMnemonic(SeedSourceUnenc):
if keypress_confirm(prompt,default_yes=True,no_nl=not g.test_suite):
break
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
@ -923,6 +920,7 @@ class MMGenMnemonic(SeedSourceUnenc):
def _deformat(self):
self.conv_cls.init_mn(self.wl_id)
mn = self.fmt_data.split()
if len(mn) not in self.mn_lens:
@ -1227,7 +1225,7 @@ class Wallet (SeedSourceEnc):
msg("Hash parameters '{}' don't match hash preset '{}'".format(' '.join(hash_params),d.hash_preset))
return False
lmin,foo,lmax = sorted(baseconv.seed_pad_lens_rev['b58']) # 22,33,44
lmin,foo,lmax = sorted(baseconv.seedlen_map_rev['b58']) # 22,33,44
for i,key in (4,'salt'),(5,'enc_seed'):
l = lines[i].split(' ')
chk = l.pop(0)

View file

@ -24,7 +24,6 @@ import sys,os,json
from stat import *
from mmgen.common import *
from mmgen.obj import *
from mmgen.baseconv import *
wmsg = lambda k: {
'addr_in_addrfile_only': """
@ -699,6 +698,7 @@ Selected non-{pnm} inputs: {{}}""".strip().format(pnm=g.proj_name,pnl=g.proj_nam
repr([amt_to_str(e.__dict__) for e in self.outputs])
]
if self.label:
from mmgen.baseconv import baseconv
lines.append(baseconv.frombytes(self.label.encode(),'b58',tostr=True))
if self.coin_txid:
if not self.label: lines.append('-') # keep old tx files backwards compatible
@ -1215,6 +1215,7 @@ Selected non-{pnm} inputs: {{}}""".strip().format(pnm=g.proj_name,pnl=g.proj_nam
c = tx_data.pop(-1)
if c != '-':
desc = 'encoded comment (not base58)'
from mmgen.baseconv import baseconv
comment = baseconv.tobytes(c,'b58').decode()
assert comment != False,'invalid comment'
desc = 'comment'

View file

@ -264,8 +264,6 @@ def is_int(s):
except:
return False
# https://en.wikipedia.org/wiki/Base32#RFC_4648_Base32_alphabet
# https://tools.ietf.org/html/rfc4648
def is_hex_str(s): return set(list(s.lower())) <= set(list(hexdigits.lower()))
def is_hex_str_lc(s): return set(list(s)) <= set(list(hexdigits.lower()))
def is_hex_str_uc(s): return set(list(s)) <= set(list(hexdigits.upper()))