limited Monero mnemonic seed phrase ('xmrseed') support
- only 256-bit (25-word) new-style mnemonics are supported Testing: $ test/unit_tests.py baseconv $ test/tooltest2.py hex2mn mn2hex $ test/scrambletest.py pw $ test/test.py ref_xmrseed_25_passwdgen_3 $ test/test.py ref_passwdfile_chk_xmrseed_25 The following operations are supported: Generate a random Monero mnemonic: $ mmgen-tool mn_rand256 fmt=xmrseed Generate a Monero mnemonic from hexadecimal data: $ mmgen-tool hex2mn deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef fmt=xmrseed Convert the resulting mnemonic back to hexadecimal data: $ mmgen-tool mn2hex 'viewpoint donuts ardent template unveil agile meant unafraid urgent athlete rustled mime azure jaded hawk baby jagged haystack baby jagged haystack ramped oncoming point template' fmt=xmrseed Note that the result of the reversal does not match the original input. This is because input data is reduced to a spendkey before conversion so that a canonical seed phrase is produced. This is required because Monero seeds, unlike ordinary wallet seeds, are tied to a concrete key/address pair. The spendkey can be generated directly using the `hex2wif` command: $ mmgen-tool --coin=xmr hex2wif deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef Generate a list of passwords in Monero mnemonic format with ID 'mymonero': $ mmgen-passgen -f xmrseed 'mymonero' 1-10
This commit is contained in:
parent
c7220cc5e3
commit
cfa16418b3
11 changed files with 1776 additions and 13 deletions
|
|
@ -783,6 +783,9 @@ def is_bip39_str(s):
|
|||
from mmgen.bip39 import bip39
|
||||
return bool(bip39.tohex(s.split(),wl_id='bip39'))
|
||||
|
||||
def is_xmrseed(s):
|
||||
return bool(baseconv.tobytes(s.split(),wl_id='xmrseed'))
|
||||
|
||||
from collections import namedtuple
|
||||
class PasswordList(AddrList):
|
||||
msgs = {
|
||||
|
|
@ -824,6 +827,7 @@ Record this checksum: it will be used to verify the password file in the future
|
|||
'b32': pwinfo(10, 42 ,24, None, 'base32 password', is_b32_str), # 32**24 < 2**128
|
||||
'b58': pwinfo(8, 36 ,20, None, 'base58 password', is_b58_str), # 58**20 < 2**128
|
||||
'bip39': pwinfo(12, 24 ,24, [12,18,24], 'BIP39 mnemonic', is_bip39_str),
|
||||
'xmrseed': pwinfo(25, 25, 25, [25], 'Monero new-style mnemonic', is_xmrseed),
|
||||
'hex': pwinfo(32, 64 ,64, [32,48,64], 'hexadecimal password', is_hex_str),
|
||||
}
|
||||
chksum_rec_f = lambda foo,e: (str(e.idx), e.passwd)
|
||||
|
|
@ -855,7 +859,7 @@ Record this checksum: it will be used to verify the password file in the future
|
|||
self.al_id = AddrListID(seed.sid,MMGenPasswordType('P'))
|
||||
self.data = self.generate(seed,pw_idxs)
|
||||
|
||||
if self.pw_fmt == 'bip39':
|
||||
if self.pw_fmt in ('bip39','xmrseed'):
|
||||
self.msgs['file_header'] = self.msgs['file_header_mn'].format(self.pw_fmt.upper())
|
||||
|
||||
self.num_addrs = len(self.data)
|
||||
|
|
@ -916,7 +920,13 @@ Record this checksum: it will be used to verify the password file in the future
|
|||
elif pf == 'bip39':
|
||||
from mmgen.bip39 import bip39
|
||||
pw_bytes = bip39.nwords2seedlen(self.pw_len,in_bytes=True)
|
||||
good_pw_len = bip39.seedlen2nwords(len(seed.data),in_bytes=True)
|
||||
good_pw_len = bip39.seedlen2nwords(seed.byte_len,in_bytes=True)
|
||||
elif pf == 'xmrseed':
|
||||
pw_bytes = baseconv.seedlen_map_rev['xmrseed'][self.pw_len]
|
||||
try:
|
||||
good_pw_len = baseconv.seedlen_map['xmrseed'][seed.byte_len]
|
||||
except:
|
||||
die(1,'{}: unsupported seed length for Monero new-style mnemonic'.format(seed.byte_len*8))
|
||||
elif pf in ('b32','b58'):
|
||||
pw_int = (32 if pf == 'b32' else 58) ** self.pw_len
|
||||
pw_bytes = pw_int.bit_length() // 8
|
||||
|
|
@ -946,6 +956,13 @@ Record this checksum: it will be used to verify the password file in the future
|
|||
pw_len_hex = bip39.nwords2seedlen(self.pw_len,in_hex=True)
|
||||
# take most significant part
|
||||
return ' '.join(bip39.fromhex(hex_sec[:pw_len_hex],wl_id='bip39'))
|
||||
elif self.pw_fmt == 'xmrseed':
|
||||
pw_len_hex = baseconv.seedlen_map_rev['xmrseed'][self.pw_len] * 2
|
||||
# take most significant part
|
||||
bytes_trunc = bytes.fromhex(hex_sec[:pw_len_hex])
|
||||
from mmgen.protocol import MoneroProtocol
|
||||
bytes_preproc = MoneroProtocol.preprocess_key(bytes_trunc,None)
|
||||
return ' '.join(baseconv.frombytes(bytes_preproc,wl_id='xmrseed'))
|
||||
else:
|
||||
# take least significant part
|
||||
return baseconv.fromhex(hex_sec,self.pw_fmt,pad=self.pw_len,tostr=True)[-self.pw_len:]
|
||||
|
|
@ -953,7 +970,7 @@ Record this checksum: it will be used to verify the password file in the future
|
|||
def check_format(self,pw):
|
||||
if not self.pw_info[self.pw_fmt].chk_func(pw):
|
||||
raise ValueError('Password is not valid {} data'.format(self.pw_info[self.pw_fmt].desc))
|
||||
pwlen = len(pw.split()) if self.pw_fmt == 'bip39' else len(pw)
|
||||
pwlen = len(pw.split()) if self.pw_fmt in ('bip39','xmrseed') else len(pw)
|
||||
if pwlen != self.pw_len:
|
||||
raise ValueError('Password has incorrect length ({} != {})'.format(pwlen,self.pw_len))
|
||||
return True
|
||||
|
|
@ -975,7 +992,7 @@ Record this checksum: it will be used to verify the password file in the future
|
|||
|
||||
def get_line(self,lines):
|
||||
self.line_ctr += 1
|
||||
if self.pw_fmt == 'bip39':
|
||||
if self.pw_fmt in ('bip39','xmrseed'):
|
||||
ret = lines.pop(0).split(None,self.pw_len+1)
|
||||
if len(ret) > self.pw_len+1:
|
||||
m1 = 'extraneous text {!r} found after password'.format(ret[self.pw_len+1])
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ baseconv.py: base conversion class for the MMGen suite
|
|||
|
||||
from hashlib import sha256
|
||||
from mmgen.exception import *
|
||||
from mmgen.util import die
|
||||
|
||||
def is_b58_str(s): return set(list(s)) <= set(baseconv.digits['b58'])
|
||||
def is_b32_str(s): return set(list(s)) <= set(baseconv.digits['b32'])
|
||||
|
|
@ -38,6 +39,7 @@ class baseconv(object):
|
|||
'tirosh':('Tirosh mnemonic', 'base1626 mnemonic using truncated Tirosh wordlist'), # not used by wallet
|
||||
'mmgen': ('MMGen native mnemonic',
|
||||
'MMGen native mnemonic seed phrase created using old Electrum wordlist and simple base conversion'),
|
||||
'xmrseed': ('Monero mnemonic', 'Monero new-style mnemonic seed phrase'),
|
||||
}
|
||||
# https://en.wikipedia.org/wiki/Base32#RFC_4648_Base32_alphabet
|
||||
# https://tools.ietf.org/html/rfc4648
|
||||
|
|
@ -52,6 +54,7 @@ class baseconv(object):
|
|||
mn_base = 1626 # tirosh list is 1633 words long!
|
||||
wl_chksums = {
|
||||
'mmgen': '5ca31424',
|
||||
'xmrseed':'3c381ebb',
|
||||
'tirosh': '48f05e1f', # tirosh truncated to mn_base (1626)
|
||||
# 'tirosh1633': '1a5faeff'
|
||||
}
|
||||
|
|
@ -59,11 +62,13 @@ class baseconv(object):
|
|||
'b58': { 16:22, 24:33, 32:44 },
|
||||
'b6d': { 16:50, 24:75, 32:100 },
|
||||
'mmgen': { 16:12, 24:18, 32:24 },
|
||||
'xmrseed': { 32:25 },
|
||||
}
|
||||
seedlen_map_rev = {
|
||||
'b58': { 22:16, 33:24, 44:32 },
|
||||
'b6d': { 50:16, 75:24, 100:32 },
|
||||
'mmgen': { 12:16, 18:24, 24:32 },
|
||||
'xmrseed': { 25:32 },
|
||||
}
|
||||
|
||||
@classmethod
|
||||
|
|
@ -73,6 +78,9 @@ class baseconv(object):
|
|||
if mn_id == 'mmgen':
|
||||
from mmgen.mn_electrum import words
|
||||
cls.digits[mn_id] = words
|
||||
elif mn_id == 'xmrseed':
|
||||
from mmgen.mn_monero 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]
|
||||
|
|
@ -127,6 +135,12 @@ class baseconv(object):
|
|||
m = "{!r}: illegal value for 'pad' (must be None,'seed' or int)"
|
||||
raise BaseConversionPadError(m.format(pad))
|
||||
|
||||
@staticmethod
|
||||
def monero_mn_checksum(words):
|
||||
from binascii import crc32
|
||||
wstr = ''.join(word[:3] for word in words)
|
||||
return words[crc32(wstr.encode()) % len(words)]
|
||||
|
||||
@classmethod
|
||||
def tohex(cls,words_arg,wl_id,pad=None):
|
||||
"convert string or list data of base 'wl_id' to hex string"
|
||||
|
|
@ -161,6 +175,21 @@ class baseconv(object):
|
|||
m = ('{w!r}:','seed data')[pad=='seed'] + ' not in {d} format'
|
||||
raise BaseConversionError(m.format(w=words_arg,d=desc))
|
||||
|
||||
if wl_id == 'xmrseed':
|
||||
if len(words) not in cls.seedlen_map_rev['xmrseed']:
|
||||
die(2,'{}: invalid length for Monero mnemonic'.format(len(words)))
|
||||
|
||||
z = cls.monero_mn_checksum(words[:-1])
|
||||
assert z == words[-1],'{!r}: invalid Monero checksum (should be {!r})'.format(words[-1],z)
|
||||
words = tuple(words[:-1])
|
||||
|
||||
ret = b''
|
||||
for i in range(len(words)//3):
|
||||
w1,w2,w3 = [wl.index(w) for w in words[3*i:3*i+3]]
|
||||
x = w1 + base*((w2-w1)%base) + base*base*((w3-w2)%base)
|
||||
ret += x.to_bytes(4,'big')[::-1]
|
||||
return ret
|
||||
|
||||
ret = sum([wl.index(words[::-1][i])*(base**i) for i in range(len(words))])
|
||||
bl = ret.bit_length()
|
||||
return ret.to_bytes(max(pad_val,bl//8+bool(bl%8)),'big')
|
||||
|
|
@ -198,10 +227,26 @@ class baseconv(object):
|
|||
wl = cls.digits[wl_id]
|
||||
base = len(wl)
|
||||
|
||||
num = int.from_bytes(bytestr,'big')
|
||||
ret = []
|
||||
while num:
|
||||
ret.append(num % base)
|
||||
num //= base
|
||||
o = [wl[n] for n in [0] * (pad-len(ret)) + ret[::-1]]
|
||||
return ''.join(o) if tostr else o
|
||||
if wl_id == 'xmrseed':
|
||||
if len(bytestr) not in cls.seedlen_map['xmrseed']:
|
||||
die(2,'{}: invalid seed byte length for Monero mnemonic'.format(len(bytestr)))
|
||||
|
||||
def num2base_monero(num):
|
||||
w1 = num % base
|
||||
w2 = (num//base + w1) % base
|
||||
w3 = (num//base//base + w2) % base
|
||||
return [wl[w1], wl[w2], wl[w3]]
|
||||
|
||||
o = []
|
||||
for i in range(len(bytestr)//4):
|
||||
o += num2base_monero(int.from_bytes(bytestr[i*4:i*4+4][::-1],'big'))
|
||||
o.append(cls.monero_mn_checksum(o))
|
||||
else:
|
||||
num = int.from_bytes(bytestr,'big')
|
||||
ret = []
|
||||
while num:
|
||||
ret.append(num % base)
|
||||
num //= base
|
||||
o = [wl[n] for n in [0] * (pad-len(ret)) + ret[::-1]]
|
||||
|
||||
return (' ' if wl_id in ('mmgen','xmrseed') else '').join(o) if tostr else o
|
||||
|
|
|
|||
1632
mmgen/mn_monero.py
Executable file
1632
mmgen/mn_monero.py
Executable file
File diff suppressed because it is too large
Load diff
|
|
@ -234,6 +234,7 @@ dfl_mnemonic_fmt = 'mmgen'
|
|||
mnemonic_fmts = {
|
||||
'mmgen': { 'fmt': 'words', 'conv_cls': lambda: baseconv },
|
||||
'bip39': { 'fmt': 'bip39', 'conv_cls': conv_cls_bip39 },
|
||||
'xmrseed': { 'fmt': 'xmrseed','conv_cls': lambda: baseconv },
|
||||
}
|
||||
mn_opts_disp = "(valid options: '{}')".format("', '".join(mnemonic_fmts))
|
||||
|
||||
|
|
@ -473,7 +474,7 @@ class MMGenToolCmdCoin(MMGenToolCmdBase):
|
|||
|
||||
class MMGenToolCmdMnemonic(MMGenToolCmdBase):
|
||||
"""
|
||||
seed phrase utilities (valid formats: 'mmgen' (default), 'bip39')
|
||||
seed phrase utilities (valid formats: 'mmgen' (default), 'bip39', 'xmrseed')
|
||||
|
||||
IMPORTANT NOTE: MMGen's default seed phrase format uses the Electrum
|
||||
wordlist, however seed phrases are computed using a different algorithm
|
||||
|
|
@ -484,10 +485,24 @@ class MMGenToolCmdMnemonic(MMGenToolCmdBase):
|
|||
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.
|
||||
|
||||
For Monero ('xmrseed') seed phrases, input data is reduced to a spendkey
|
||||
before conversion so that a canonical seed phrase is produced. This is
|
||||
required because Monero seeds, unlike ordinary wallet seeds, are tied
|
||||
to a concrete key/address pair. To manually generate a Monero spendkey,
|
||||
use the 'hex2wif' command.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def _xmr_reduce(bytestr):
|
||||
from mmgen.protocol import MoneroProtocol
|
||||
return MoneroProtocol.preprocess_key(bytestr,None)
|
||||
|
||||
def _do_random_mn(self,nbytes:int,fmt:str):
|
||||
assert nbytes in (16,24,32), 'nbytes must be 16, 24 or 32'
|
||||
randbytes = get_random(nbytes)
|
||||
if fmt == 'xmrseed':
|
||||
randbytes = self._xmr_reduce(randbytes)
|
||||
if opt.verbose:
|
||||
msg('Seed: {}'.format(randbytes.hex()))
|
||||
return self.hex2mn(randbytes.hex(),fmt=fmt)
|
||||
|
|
@ -518,6 +533,8 @@ class MMGenToolCmdMnemonic(MMGenToolCmdBase):
|
|||
return ' '.join(bip39.fromhex(hexstr,fmt))
|
||||
else:
|
||||
bytestr = bytes.fromhex(hexstr)
|
||||
if fmt == 'xmrseed':
|
||||
bytestr = self._xmr_reduce(bytestr)
|
||||
return baseconv.frombytes(bytestr,fmt,'seed',tostr=True)
|
||||
|
||||
def mn2hex( self, seed_mnemonic:'sstr', fmt:mn_opts_disp = dfl_mnemonic_fmt ):
|
||||
|
|
|
|||
1
setup.py
1
setup.py
|
|
@ -113,6 +113,7 @@ setup(
|
|||
'mmgen.keccak',
|
||||
'mmgen.license',
|
||||
'mmgen.mn_electrum',
|
||||
'mmgen.mn_monero',
|
||||
'mmgen.mn_tirosh',
|
||||
'mmgen.obj',
|
||||
'mmgen.opts',
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
# B488 21D3 4539 968D
|
||||
98831F3A фубар@crypto.org xmrseed:25 {
|
||||
1 amended jailed extra apart hinder upbeat dating onboard yesterday aided irony guide cogs apricot aggravate twofold tepid nuance ripped vessel soothe woven vials when aided
|
||||
4 godfather arrow hobby mailed educated abnormal boss cavernous skirting alumni voted hunter vessel unsafe mohawk pylons amused rift jury code assorted oscar himself inquest oscar
|
||||
1100 molten icon lectures amaze foes lurk camp divers goldfish zombie neutral drunk topic abort boldly recipe natural verification circle polar woozy biplane yoyo adapt abort
|
||||
}
|
||||
|
|
@ -88,6 +88,7 @@ passwd_data = {
|
|||
'bip39_dfl_αω':td('95b383d5092a55df', 'bip39:24:αω','-αω-bip39-24','αω bip39:24','treat athlete brand top beauty poverty senior unhappy vacant domain yellow scale fossil aim lonely fatal sun nuclear such ancient stage require stool similar'),
|
||||
'bip39_18_αω': td('29e5a605ffa36142', 'bip39:18:αω','-αω-bip39-18','αω bip39:18','better legal various ketchup then range festival either tomato cradle say absorb solar earth alter pattern canyon liar'),
|
||||
'bip39_12_αω': td('efa13cb309d7fc1d', 'bip39:12:αω','-αω-bip39-12','αω bip39:12','lady oppose theme fit position merry reopen acquire tuna dentist young chunk'),
|
||||
'xmrseed_dfl_αω':td('62f5b72a5ca89cab', 'xmrseed:25:αω','-αω-xmrseed-25','αω xmrseed:25','tequila eden skulls giving jester hospital dreams bakery adjust nanny cactus inwardly films amply nanny soggy vials muppet yellow woken ashtray organs exhale foes eden'),
|
||||
}
|
||||
|
||||
cvr_opts = ' -m trace --count --coverdir={} --file={}'.format(*init_coverage()) if opt.coverage else ''
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ class TestSuiteRef(TestSuiteBase,TestSuiteShared):
|
|||
'ref_passwdfile_bip39_12': '98831F3A-фубар@crypto.org-bip39-12[1,4,1100].pws',
|
||||
'ref_passwdfile_bip39_18': '98831F3A-фубар@crypto.org-bip39-18[1,4,1100].pws',
|
||||
'ref_passwdfile_bip39_24': '98831F3A-фубар@crypto.org-bip39-24[1,4,1100].pws',
|
||||
'ref_passwdfile_xmrseed_25': '98831F3A-фубар@crypto.org-xmrseed-25[1,4,1100].pws',
|
||||
'ref_passwdfile_hex2bip39_12': '98831F3A-фубар@crypto.org-hex2bip39-12[1,4,1100].pws',
|
||||
'ref_tx_file': { # data shared with ref_altcoin, autosign
|
||||
'btc': ('0B8D5A[15.31789,14,tl=1320969600].rawtx',
|
||||
|
|
@ -99,6 +100,7 @@ class TestSuiteRef(TestSuiteBase,TestSuiteShared):
|
|||
'ref_passwdfile_bip39_12_chksum': 'BF57 02A3 5229 CF18',
|
||||
'ref_passwdfile_bip39_18_chksum': '31D3 1656 B7DC 27CF',
|
||||
'ref_passwdfile_bip39_24_chksum': 'E565 3A59 7D91 4671',
|
||||
'ref_passwdfile_xmrseed_25_chksum': 'B488 21D3 4539 968D',
|
||||
'ref_passwdfile_hex2bip39_12_chksum': '93AD 4AE2 03D1 8A0A',
|
||||
}
|
||||
cmd_group = ( # TODO: move to tooltest2
|
||||
|
|
@ -123,6 +125,7 @@ class TestSuiteRef(TestSuiteBase,TestSuiteShared):
|
|||
('ref_passwdfile_chk_bip39_12','saved reference password file (BIP39, 12 words)'),
|
||||
('ref_passwdfile_chk_bip39_18','saved reference password file (BIP39, 18 words)'),
|
||||
('ref_passwdfile_chk_bip39_24','saved reference password file (BIP39, 24 words)'),
|
||||
('ref_passwdfile_chk_xmrseed_25','saved reference password file (Monero new-style mnemonic, 25 words)'),
|
||||
('ref_passwdfile_chk_hex2bip39_12','saved reference password file (hex-to-BIP39, 12 words)'),
|
||||
|
||||
# Create the fake inputs:
|
||||
|
|
@ -252,6 +255,7 @@ class TestSuiteRef(TestSuiteBase,TestSuiteShared):
|
|||
def ref_passwdfile_chk_bip39_12(self): return self.ref_passwdfile_chk(key='bip39_12',pat=r'BIP39.*len.* 12\b')
|
||||
def ref_passwdfile_chk_bip39_18(self): return self.ref_passwdfile_chk(key='bip39_18',pat=r'BIP39.*len.* 18\b')
|
||||
def ref_passwdfile_chk_bip39_24(self): return self.ref_passwdfile_chk(key='bip39_24',pat=r'BIP39.*len.* 24\b')
|
||||
def ref_passwdfile_chk_xmrseed_25(self): return self.ref_passwdfile_chk(key='xmrseed_25',pat=r'Mon.*len.* 25\b')
|
||||
def ref_passwdfile_chk_hex2bip39_12(self): return self.ref_passwdfile_chk(key='hex2bip39_12',pat=r'BIP39.*len.* 12\b')
|
||||
|
||||
def ref_tx_chk(self):
|
||||
|
|
|
|||
|
|
@ -42,6 +42,10 @@ class TestSuiteRef3Seed(TestSuiteBase,TestSuiteShared):
|
|||
'sids': ('FE3C6545', '1378FC64', '98831F3A'),
|
||||
}
|
||||
shared_deps = ['mmdat',pwfile]
|
||||
skip_cmds = (
|
||||
'ref_xmrseed_25_passwdgen_1',
|
||||
'ref_xmrseed_25_passwdgen_2',
|
||||
)
|
||||
cmd_group = (
|
||||
# reading saved reference wallets
|
||||
('ref_wallet_chk', ([],'saved reference wallet')),
|
||||
|
|
@ -319,6 +323,7 @@ class TestSuiteRef3Addr(TestSuiteRef3Seed):
|
|||
'ref_bip39_18_passwdgen_3': 'EF87 9904 88E2 5884',
|
||||
'ref_bip39_24_passwdgen_3': 'EBE8 2A8F 8F8C 7DBD',
|
||||
'ref_hex2bip39_24_passwdgen_3': '93FA 5EFD 33F3 760E',
|
||||
'ref_xmrseed_25_passwdgen_3': '91AE E76A 2827 C8CC',
|
||||
}
|
||||
|
||||
cmd_group = (
|
||||
|
|
@ -339,6 +344,7 @@ class TestSuiteRef3Addr(TestSuiteRef3Seed):
|
|||
('ref_bip39_12_passwdgen', ([],'new refwallet passwd file chksum (BIP39, 12 words)')),
|
||||
('ref_bip39_18_passwdgen', ([],'new refwallet passwd file chksum (BIP39, up to 18 words)')),
|
||||
('ref_bip39_24_passwdgen', ([],'new refwallet passwd file chksum (BIP39, up to 24 words)')),
|
||||
('ref_xmrseed_25_passwdgen', ([],'new refwallet passwd file chksum (Monero new-style mnemonic, 25 words)')),
|
||||
('ref_hex2bip39_24_passwdgen',([],'new refwallet passwd file chksum (hex-to-BIP39, up to 24 words)')),
|
||||
)
|
||||
|
||||
|
|
@ -383,6 +389,8 @@ class TestSuiteRef3Addr(TestSuiteRef3Seed):
|
|||
|
||||
def mn_pwgen(self,req_pw_len,pwfmt,ftype='passbip39',stdout=False):
|
||||
pwlen = min(req_pw_len,{'1':12,'2':18,'3':24}[self.test_name[-1]])
|
||||
if pwfmt == 'xmrseed':
|
||||
pwlen += 1
|
||||
ea = ['--accept-defaults']
|
||||
return self.pwgen(ftype,'фубар@crypto.org',pwfmt,pwlen,ea,stdout=stdout)
|
||||
|
||||
|
|
@ -390,3 +398,4 @@ class TestSuiteRef3Addr(TestSuiteRef3Seed):
|
|||
def ref_bip39_18_passwdgen(self): return self.mn_pwgen(18,'bip39',stdout=True)
|
||||
def ref_bip39_24_passwdgen(self): return self.mn_pwgen(24,'bip39')
|
||||
def ref_hex2bip39_24_passwdgen(self): return self.mn_pwgen(24,'hex2bip39')
|
||||
def ref_xmrseed_25_passwdgen(self): return self.mn_pwgen(24,'xmrseed',ftype='passxmrseed')
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ from mmgen.common import *
|
|||
from test.common import *
|
||||
from mmgen.obj import is_wif,is_coin_addr
|
||||
from mmgen.seed import is_bip39_mnemonic,is_mmgen_mnemonic
|
||||
from mmgen.addr import is_xmrseed
|
||||
from mmgen.baseconv import *
|
||||
|
||||
NL = ('\n','\r\n')[g.platform=='win']
|
||||
|
|
@ -112,6 +113,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') ),
|
||||
( ['e8164dda6d42bd1e261a3406b2038dcbddadbeefdeadbeefdeadbeefdeadbe0f','fmt=xmrseed'],
|
||||
('viewpoint donuts ardent template unveil agile meant unafraid urgent athlete rustled mime azure ' +
|
||||
'jaded hawk baby jagged haystack baby jagged haystack ramped oncoming point template') ),
|
||||
] + [([a,'fmt=bip39'],b) for a,b in bip39.vectors],
|
||||
'mn2hex': [
|
||||
( ['table cast forgive master funny gaze sadness ripple million paint moral match','fmt=mmgen'],
|
||||
|
|
@ -133,6 +137,10 @@ 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'),
|
||||
( ['viewpoint donuts ardent template unveil agile meant unafraid urgent athlete ' +
|
||||
'rustled mime azure jaded hawk baby jagged haystack baby jagged haystack ' +
|
||||
'ramped oncoming point template','fmt=xmrseed'],
|
||||
'e8164dda6d42bd1e261a3406b2038dcbddadbeefdeadbeefdeadbeefdeadbe0f'),
|
||||
] + [([b,'fmt=bip39'],a) for a,b in bip39.vectors],
|
||||
'mn_rand128': [
|
||||
( [], is_mmgen_mnemonic, ['-r0']),
|
||||
|
|
@ -146,16 +154,19 @@ tests = {
|
|||
'mn_rand256': [
|
||||
( ['fmt=mmgen'], is_mmgen_mnemonic, ['-r0']),
|
||||
( ['fmt=bip39'], is_bip39_mnemonic, ['-r0']),
|
||||
( ['fmt=xmrseed'], is_xmrseed, ['-r0']),
|
||||
],
|
||||
'mn_stats': [
|
||||
( [], is_str ),
|
||||
( ['fmt=mmgen'], is_str ),
|
||||
( ['fmt=bip39'], is_str ),
|
||||
( ['fmt=xmrseed'], is_str ),
|
||||
],
|
||||
'mn_printlist': [
|
||||
( [], is_str ),
|
||||
( ['fmt=mmgen'], is_str ),
|
||||
( ['fmt=bip39'], is_str ),
|
||||
( ['fmt=xmrseed','enum=true'], is_str ),
|
||||
],
|
||||
},
|
||||
'Util': {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,23 @@ from mmgen.exception import *
|
|||
class unit_test(object):
|
||||
|
||||
vectors = {
|
||||
'xmrseed': (
|
||||
# 42nsXK8WbVGTNayQ6Kjw5UdgqbQY5KCCufdxdCgF7NgTfjC69Mna7DJSYyie77hZTQ8H92G2HwgFhgEUYnDzrnLnQdF28r3
|
||||
(('0000000000000000000000000000000000000000000000000000000000000001','seed'), # 0x1
|
||||
'abbey abbey abbey abbey abbey abbey abbey abbey abbey abbey abbey abbey abbey abbey abbey abbey abbey abbey abbey abbey abbey bamboo jaws jerseys abbey'),
|
||||
|
||||
# 49voQEbjouUQSDikRWKUt1PGbS47TBde4hiGyftN46CvTDd8LXCaimjHRGtofCJwY5Ed5QhYwc12P15AH5w7SxUAMCz1nr1
|
||||
(('1c95988d7431ecd670cf7d73f45befc6feffffffffffffffffffffffffffff0f','seed'), # 0xffffffff * 8
|
||||
'powder directed sayings enmity bacon vapidly entrance bumper noodles iguana sleepless nasty flying soil software foamy solved soggy foamy solved soggy jury yawning ankle solved'),
|
||||
|
||||
# 41i7saPWA53EoHenmJVRt34dubPxsXwoWMnw8AdMyx4mTD1svf7qYzcVjxxRfteLNdYrAxWUMmiPegFW9EfoNgXx7vDMExv
|
||||
(('e8164dda6d42bd1e261a3406b2038dcbddadbeefdeadbeefdeadbeefdeadbe0f','seed'), # 0xdeadbeef * 8
|
||||
'viewpoint donuts ardent template unveil agile meant unafraid urgent athlete rustled mime azure jaded hawk baby jagged haystack baby jagged haystack ramped oncoming point template'),
|
||||
|
||||
# 42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm
|
||||
(('148d78d2aba7dbca5cd8f6abcfb0b3c009ffbdbea1ff373d50ed94d78286640e','seed'), # Monero repo
|
||||
'velvet lymph giddy number token physics poetry unquoted nibs useful sabotage limits benches lifestyle eden nitrogen anvil fewest avoid batch vials washing fences goat unquoted'),
|
||||
),
|
||||
'b58': (
|
||||
(('00',None),'1'),
|
||||
(('00',1),'1'),
|
||||
|
|
@ -161,7 +178,10 @@ class unit_test(object):
|
|||
for (hexstr,pad),ret_chk in data:
|
||||
if type(pad) == int:
|
||||
pad = len(hexstr)
|
||||
ret = baseconv.tohex(ret_chk,wl_id=base,pad=pad)
|
||||
ret = baseconv.tohex(
|
||||
ret_chk.split() if base == 'xmrseed' else ret_chk,
|
||||
wl_id=base,
|
||||
pad=pad)
|
||||
if pad == None:
|
||||
assert int(ret,16) == int(hexstr,16), rerr.format(int(ret,16),int(hexstr,16))
|
||||
else:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue