mmgen-tool usage: various fixes and cleanups

This commit is contained in:
The MMGen Project 2022-08-15 12:38:46 +00:00
commit 642b45b2e3
Signed by: mmgen
GPG key ID: 3F8B1861E32B7DA2
14 changed files with 176 additions and 136 deletions

View file

@ -132,7 +132,7 @@ class baseconv(object):
die('BaseConversionPadError',f"{pad!r}: illegal value for 'pad' (must be None,'seed' or int)")
def tohex(self,words_arg,pad=None):
"convert string or list data of instance base to hex string"
"convert string or list data of instance base to a hexadecimal string"
return self.tobytes(words_arg,pad//2 if type(pad)==int else pad).hex()
def tobytes(self,words_arg,pad=None):
@ -166,7 +166,7 @@ class baseconv(object):
return ret.to_bytes(max(pad_val,bl//8+bool(bl%8)),'big')
def fromhex(self,hexstr,pad=None,tostr=False):
"convert hex string to list or string data of instance base"
"convert a hexadecimal string to a list or string data of instance base"
from .util import is_hex_str
if not is_hex_str(hexstr):

View file

@ -42,7 +42,7 @@ opts_data = {
-q, --quiet Produce quieter output
-r, --usr-randchars=n Get 'n' characters of additional randomness from
user (min={g.min_urandchars}, max={g.max_urandchars})
-t, --type=t Specify address type (valid options: 'legacy',
-t, --type=t Specify address type (valid choices: 'legacy',
'compressed', 'segwit', 'bech32', 'zcash_z')
-v, --verbose Produce more verbose output
-X, --cached-balances Use cached balances (Ethereum only)
@ -53,7 +53,7 @@ opts_data = {
COMMANDS
{ch}
Type '{pn} help <command>' for help on a particular command
Type {pn} help <command> for help on a particular command
"""
},
'code': {
@ -171,7 +171,7 @@ mods = {
),
}
def create_call_sig(cmd,cls,parsed=False):
def create_call_sig(cmd,cls,as_string=False):
m = getattr(cls,cmd)
@ -190,29 +190,26 @@ def create_call_sig(cmd,cls,parsed=False):
ann[a] if a in ann and isinstance(ann[a],type) else type(dfls[i])
for i,a in enumerate(args[nargs:]) )
def get_type_from_ann(arg):
return (
('str' + ('' if parsed else ' or STDIN')) if ann[arg] == 'sstr' else
ann[arg].__name__ )
if parsed:
if as_string:
get_type_from_ann = lambda x: 'str or STDIN' if ann[x] == 'sstr' else ann[x].__name__
return ' '.join(
[f'{a} [{get_type_from_ann(a)}]' for a in args[:nargs]] +
['{a} [{b}={c!r}{d}]'.format(
a = a,
b = dfl_types[n].__name__,
c = dfls[n],
d = (' ' + ann[a] if a in ann and isinstance(ann[a],str) else ''))
for n,a in enumerate(args[nargs:])] )
else:
get_type_from_ann = lambda x: 'str' if ann[x] == 'sstr' else ann[x].__name__
return (
[(a,get_type_from_ann(a)) for a in args[:nargs]], # c_args
dict([(a,dfls[n]) for n,a in enumerate(args[nargs:])]), # c_kwargs
dict([(a,dfl_types[n]) for n,a in enumerate(args[nargs:])]), # c_kwargs_types
('STDIN_OK' if nargs and ann[args[0]] == 'sstr' else flag) ) # flag
else:
c_args = [f'{a} [{get_type_from_ann(a)}]' for a in args[:nargs]]
c_kwargs = ['{a} [{b}={c!r}{d}]'.format(
a = a,
b = dfl_types[n].__name__,
c = dfls[n],
d = (' ' + ann[a] if a in ann and isinstance(ann[a],str) else '') )
for n,a in enumerate(args[nargs:])]
return ' '.join(c_args + c_kwargs)
def process_args(cmd,cmd_args,cls):
c_args,c_kwargs,c_kwargs_types,flag = create_call_sig(cmd,cls,parsed=True)
c_args,c_kwargs,c_kwargs_types,flag = create_call_sig(cmd,cls)
have_stdin_input = False
def usage_die(s):

View file

@ -392,7 +392,7 @@ class MnemonicEntry(object):
}
wl = wl.lower()
if wl not in d:
raise ValueError(f'wordlist {wl!r} not recognized (valid options: {fmt_list(list(d))})')
raise ValueError(f'wordlist {wl!r} not recognized (valid choices: {fmt_list(list(d))})')
return d[wl]
@classmethod
@ -402,7 +402,7 @@ class MnemonicEntry(object):
if v not in tcls.entry_modes:
raise ValueError(
f'entry mode {v!r} not recognized for wordlist {k!r}:' +
f'\n (valid options: {fmt_list(tcls.entry_modes)})' )
f'\n (valid choices: {fmt_list(tcls.entry_modes)})' )
tcls.usr_dfl_entry_mode = v
class MnemonicEntryMMGen(MnemonicEntry):

View file

@ -32,11 +32,11 @@ class tool_cmd(tool_cmd_base):
"""
cryptocoin key/address utilities
May require use of the '--coin', '--type' and/or '--testnet' options
May require use of the '--coin', '--type' and/or '--testnet' options
Examples:
mmgen-tool --coin=ltc --type=bech32 wif2addr <wif key>
mmgen-tool --coin=zec --type=zcash_z randpair
Examples:
mmgen-tool --coin=ltc --type=bech32 wif2addr <wif key>
mmgen-tool --coin=zec --type=zcash_z randpair
"""
need_proto = True
@ -71,13 +71,13 @@ class tool_cmd(tool_cmd_base):
gd.ag.to_addr( gd.kg.gen_data(privkey) ))
def wif2hex(self,wifkey:'sstr'):
"convert a private key from WIF to hex format"
"convert a private key from WIF to hexadecimal format"
return PrivKey(
self.proto,
wif = wifkey ).hex()
def hex2wif(self,privhex:'sstr'):
"convert a private key from hex to WIF format"
"convert a private key from hexadecimal to WIF format"
return PrivKey(
self.proto,
bytes.fromhex(privhex),
@ -102,7 +102,7 @@ class tool_cmd(tool_cmd_base):
return gd.ag.to_segwit_redeem_script( gd.kg.gen_data(privkey) )
def wif2segwit_pair(self,wifkey:'sstr'):
"generate both a Segwit P2SH-P2WPKH redeem script and address from WIF"
"generate a Segwit P2SH-P2WPKH redeem script and address from a WIF private key"
assert self.mmtype.name == 'segwit','This command is meaningful only for --type=segwit'
gd = self._init_generators()
data = gd.kg.gen_data(PrivKey(
@ -112,8 +112,7 @@ class tool_cmd(tool_cmd_base):
gd.ag.to_segwit_redeem_script(data),
gd.ag.to_addr(data) )
def privhex2addr(self,privhex:'sstr',output_pubhex=False):
"generate coin address from raw private key data in hexadecimal format"
def _privhex2out(self,privhex:'sstr',output_pubhex=False):
gd = self._init_generators()
pk = PrivKey(
self.proto,
@ -123,12 +122,16 @@ class tool_cmd(tool_cmd_base):
data = gd.kg.gen_data(pk)
return data.pubkey.hex() if output_pubhex else gd.ag.to_addr(data)
def privhex2addr(self,privhex:'sstr'):
"generate a coin address from raw hexadecimal private key data"
return self._privhex2out(privhex)
def privhex2pubhex(self,privhex:'sstr'): # new
"generate a hex public key from a hex private key"
return self.privhex2addr(privhex,output_pubhex=True)
"generate a hexadecimal public key from raw hexadecimal private key data"
return self._privhex2out(privhex,output_pubhex=True)
def pubhex2addr(self,pubkeyhex:'sstr'):
"convert a hex pubkey to an address"
"convert a hexadecimal pubkey to an address"
if self.proto.base_proto == 'Ethereum' and len(pubkeyhex) == 128: # support raw ETH pubkeys
pubkeyhex = '04' + pubkeyhex
from ..keygen import keygen_public_data
@ -141,19 +144,19 @@ class tool_cmd(tool_cmd_base):
))
def pubhex2redeem_script(self,pubkeyhex:'sstr'): # new
"convert a hex pubkey to a Segwit P2SH-P2WPKH redeem script"
"convert a hexadecimal pubkey to a Segwit P2SH-P2WPKH redeem script"
assert self.mmtype.name == 'segwit','This command is meaningful only for --type=segwit'
from ..proto.common import hash160
return self.proto.pubhash2redeem_script( hash160(bytes.fromhex(pubkeyhex)) ).hex()
def redeem_script2addr(self,redeem_scripthex:'sstr'): # new
def redeem_script2addr(self,redeem_script_hex:'sstr'): # new
"convert a Segwit P2SH-P2WPKH redeem script to an address"
assert self.mmtype.name == 'segwit', 'This command is meaningful only for --type=segwit'
assert redeem_scripthex[:4] == '0014', f'{redeem_scripthex!r}: invalid redeem script'
assert len(redeem_scripthex) == 44, f'{len(redeem_scripthex)//2} bytes: invalid redeem script length'
assert redeem_script_hex[:4] == '0014', f'{redeem_script_hex!r}: invalid redeem script'
assert len(redeem_script_hex) == 44, f'{len(redeem_script_hex)//2} bytes: invalid redeem script length'
from ..proto.common import hash160
return self.proto.pubhash2addr(
hash160( bytes.fromhex(redeem_scripthex) ),
hash160( bytes.fromhex(redeem_script_hex) ),
p2sh = True )
def pubhash2addr(self,pubhashhex:'sstr'):

View file

@ -23,7 +23,7 @@ tool/common.py: Base class and shared routines for the 'mmgen-tool' utility
from ..objmethods import MMGenObject
def options_annot_str(l):
return "(valid options: '{}')".format( "','".join(l) )
return "(valid choices: '{}')".format( "','".join(l) )
class tool_cmd_base(MMGenObject):

View file

@ -78,13 +78,13 @@ class tool_cmd(tool_cmd_base):
'dfls': ( False, False, 'addr', 'mtime' ),
'annots': {
'mmgen_tx_file(s)': str,
'sort': options_annot_str(['addr','raw']),
'sort': options_annot_str(['addr','raw']),
'filesort': options_annot_str(['mtime','ctime','atime']),
}
},
*infiles,
**kwargs ):
"show raw/signed MMGen transaction in human-readable form"
"display specified raw or signed MMGen transaction files in human-readable form"
terse = bool(kwargs.get('terse'))
tx_sort = kwargs.get('sort') or 'addr'

View file

@ -30,10 +30,10 @@ class tool_cmd(tool_cmd_base):
"""
file encryption and decryption
MMGen encryption suite:
* Key: Scrypt (user-configurable hash parameters, 32-byte salt)
* Enc: AES256_CTR, 16-byte rand IV, sha256 hash + 32-byte nonce + data
* The encrypted file is indistinguishable from random data
MMGen encryption suite:
* Key: Scrypt (user-configurable hash parameters, 32-byte salt)
* Enc: AES256_CTR, 16-byte rand IV, sha256 hash + 32-byte nonce + data
* The encrypted file is indistinguishable from random data
"""
def encrypt(self,infile:str,outfile='',hash_preset=''):
"encrypt a file"

View file

@ -65,7 +65,9 @@ class tool_cmd(tool_cmd_base):
return True
def rand2file(self,outfile:str,nbytes:str,threads=4,silent=False):
"write 'n' bytes of random data to specified file"
"""
write nbytes bytes of random data to specified file (dd-style byte specifiers supported)
"""
from threading import Thread
from queue import Queue
from cryptography.hazmat.primitives.ciphers import Cipher,algorithms,modes

View file

@ -59,7 +59,9 @@ def main_help():
return '\n'.join(do())
def usage(cmdname=None,exit_val=1):
def gen_tool_usage():
from ..util import capfirst
m1 = """
USAGE INFORMATION FOR MMGEN-TOOL COMMANDS:
@ -68,12 +70,16 @@ def usage(cmdname=None,exit_val=1):
Arguments with both type and default value specified in square brackets are
optional and must be specified in the form name=value
For more detailed usage information for a particular tool command, type
mmgen-tool help <command name>
"""
m2 = """
To force a command to read from STDIN instead of file (for commands taking
a filename as their first argument), substitute "-" for the filename.
EXAMPLES:
Generate a random LTC Bech32 public/private keypair:
@ -97,39 +103,66 @@ def usage(cmdname=None,exit_val=1):
Reverse a hex string:
$ mmgen-tool hexreverse "deadbeefcafe"
Same as above, but supply input via stdin:
Same as above, but supply input via STDIN:
$ echo "deadbeefcafe" | mmgen-tool hexreverse -
"""
from ..util import Msg,Msg_r,fmt,die,capfirst
for line in m1.lstrip().split('\n'):
yield line.lstrip('\t')
for clsname,cmdlist in main_tool.mods.items():
cls = main_tool.get_mod_cls(clsname)
cls_docstr = cls.__doc__.strip()
yield ''
yield ' {}:'.format( capfirst(cls_docstr.split('\n')[0].strip()) )
yield ''
if '\n' in cls_docstr:
for line in cls_docstr.split('\n')[2:]:
yield ' ' + line.lstrip('\t')
yield ''
max_w = max(map(len,cmdlist))
for cmdname in cmdlist:
yield ' {a:{w}} {b}'.format(
a = cmdname,
b = main_tool.create_call_sig(cmdname,cls,as_string=True),
w = max_w )
yield ''
for line in m2.rstrip().split('\n'):
yield line.lstrip('\t')
def gen_tool_cmd_usage(mod,cmdname):
from ..globalvars import g
from ..util import capfirst
cls = main_tool.get_mod_cls(mod)
docstr = getattr(cls,cmdname).__doc__.strip()
args,kwargs,kwargs_types,flag = main_tool.create_call_sig(cmdname,cls)
yield '{a}\n\nUSAGE: {b} {c} {d}{e}'.format(
a = capfirst( docstr.split('\n')[0].strip() ),
b = g.prog_name,
c = cmdname,
d = main_tool.create_call_sig(cmdname,cls,as_string=True),
e = '\n\n' + fmt('\n'.join(docstr.split('\n')[1:]),strip_char='\t').rstrip()
if '\n' in docstr else '' )
def usage(cmdname=None,exit_val=1):
from ..util import Msg,die,do_pager
if cmdname:
from ..globalvars import g
for mod,cmdlist in main_tool.mods.items():
if cmdname in cmdlist:
cls = main_tool.get_mod_cls(mod)
docstr = getattr(cls,cmdname).__doc__.strip()
Msg('{a}\n\nUSAGE: {b} {c} {d}{e}'.format(
a = capfirst( docstr.split('\n')[0].strip() ),
b = g.prog_name,
c = cmdname,
d = main_tool.create_call_sig(cmdname,cls),
e = '\n\n' + fmt('\n'.join(docstr.split('\n')[1:]),strip_char='\t').rstrip()
if '\n' in docstr else '' ))
Msg('\n'.join(gen_tool_cmd_usage(mod,cmdname)))
break
else:
die(1,f'{cmdname!r}: no such tool command')
else:
Msg(fmt(m1,strip_char='\t'))
for clsname,cmdlist in main_tool.mods.items():
cls = main_tool.get_mod_cls(clsname)
cls_info = cls.__doc__.strip().split('\n')[0]
Msg(' {}{}:\n'.format( cls_info[0].upper(), cls_info[1:] ))
max_w = max(map(len,cmdlist))
for cmdname in cmdlist:
Msg(f' {cmdname:{max_w}} {main_tool.create_call_sig(cmdname,cls)}')
Msg('')
Msg_r(' ' + fmt(m2,strip_char='\t'))
do_pager('\n'.join(gen_tool_usage()))
import sys
sys.exit(exit_val)
@ -142,5 +175,5 @@ class tool_cmd(tool_cmd_base):
usage(command_name,exit_val=0)
def usage(self,command_name=''):
"display usage information for a single command"
"display usage information for a single command or all commands"
usage(command_name,exit_val=0)

View file

@ -39,23 +39,25 @@ mn_opts_disp = options_annot_str(mnemonic_fmts)
class tool_cmd(tool_cmd_base):
"""
seed phrase utilities (valid formats: 'mmgen' (default), 'bip39', 'xmrseed')
seed phrase utilities
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!
Supported seed phrase formats: 'mmgen' (default), 'bip39', 'xmrseed'
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.
IMPORTANT NOTE: MMGens default seed phrase format uses the Electrum
wordlist, however seed phrases are computed using a different algorithm
and are NOT Electrum-compatible!
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.
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.
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
@ -81,31 +83,31 @@ class tool_cmd(tool_cmd_base):
return self.hex2mn(randbytes.hex(),fmt=fmt)
def mn_rand128(self, fmt:mn_opts_disp = dfl_mnemonic_fmt ):
"generate random 128-bit mnemonic seed phrase"
"generate a random 128-bit mnemonic seed phrase"
return self._do_random_mn(16,fmt)
def mn_rand192(self, fmt:mn_opts_disp = dfl_mnemonic_fmt ):
"generate random 192-bit mnemonic seed phrase"
"generate a random 192-bit mnemonic seed phrase"
return self._do_random_mn(24,fmt)
def mn_rand256(self, fmt:mn_opts_disp = dfl_mnemonic_fmt ):
"generate random 256-bit mnemonic seed phrase"
"generate a random 256-bit mnemonic seed phrase"
return self._do_random_mn(32,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"
"convert a 16, 24 or 32-byte hexadecimal string to a mnemonic seed phrase"
if fmt == 'xmrseed':
hexstr = self._xmr_reduce(bytes.fromhex(hexstr)).hex()
f = mnemonic_fmts[fmt]
return ' '.join( f.conv_cls(fmt).fromhex(hexstr,f.pad) )
def mn2hex( self, seed_mnemonic:'sstr', fmt:mn_opts_disp = dfl_mnemonic_fmt ):
"convert a mnemonic seed phrase to a hexadecimal number"
"convert a mnemonic seed phrase to a hexadecimal string"
f = mnemonic_fmts[fmt]
return f.conv_cls(fmt).tohex( seed_mnemonic.split(), f.pad )
def mn2hex_interactive( self, fmt:mn_opts_disp = dfl_mnemonic_fmt, mn_len=24, print_mn=False ):
"convert an interactively supplied mnemonic seed phrase to a hexadecimal number"
"convert an interactively supplied mnemonic seed phrase to a hexadecimal string"
from ..mn_entry import mn_entry
mn = mn_entry(fmt).get_mnemonic_from_user(25 if fmt == 'xmrseed' else mn_len,validate=False)
if print_mn:
@ -114,11 +116,11 @@ class tool_cmd(tool_cmd_base):
return self.mn2hex(seed_mnemonic=mn,fmt=fmt)
def mn_stats(self, fmt:mn_opts_disp = dfl_mnemonic_fmt ):
"show stats for mnemonic wordlist"
"show stats for a mnemonic wordlist"
return mnemonic_fmts[fmt].conv_cls(fmt).check_wordlist()
def mn_printlist( self, fmt:mn_opts_disp = dfl_mnemonic_fmt, enum=False, pager=False ):
"print mnemonic wordlist"
"print a mnemonic wordlist"
ret = mnemonic_fmts[fmt].conv_cls(fmt).get_wordlist()
if enum:
ret = [f'{n:>4} {e}' for n,e in enumerate(ret)]

View file

@ -24,7 +24,7 @@ from .common import tool_cmd_base,options_annot_str
from ..tw.common import TwCommon
class tool_cmd(tool_cmd_base):
"tracking wallet commands using the JSON-RPC interface"
"tracking-wallet commands using the JSON-RPC interface"
need_proto = True
need_amt = True
@ -43,17 +43,13 @@ class tool_cmd(tool_cmd_base):
async def listaddress(self,
mmgen_addr:str,
minconf = 1,
pager = False,
showempty = True,
showbtcaddr = True,
age_fmt: options_annot_str(TwCommon.age_fmts) = 'confs' ):
"list the specified MMGen address and its balance"
"list the specified MMGen address in the tracking wallet and its balance"
return await self.listaddresses(
mmgen_addrs = mmgen_addr,
minconf = minconf,
pager = pager,
showempty = showempty,
showbtcaddrs = showbtcaddr,
age_fmt = age_fmt )
@ -140,7 +136,7 @@ class tool_cmd(tool_cmd_base):
sort = 'age',
age_fmt: options_annot_str(TwCommon.age_fmts) = 'confs',
interactive = False ):
"view transaction history"
"view transaction history of tracking wallet"
from ..tw.txhistory import TwTxHistory
obj = await TwTxHistory(self.proto,sinceblock=sinceblock)

View file

@ -39,10 +39,10 @@ class tool_cmd(tool_cmd_base):
from ..util import int2bytespec
return int2bytespec( n, dd_style_byte_specifier, fmt, print_sym )
def randhex(self,nbytes='32'):
def randhex(self,nbytes=32):
"print 'n' bytes (default 32) of random data in hex format"
from ..crypto import get_random
return get_random( int(nbytes) ).hex()
return get_random( nbytes ).hex()
def hexreverse(self,hexstr:'sstr'):
"reverse bytes of a hexadecimal string"
@ -55,7 +55,7 @@ class tool_cmd(tool_cmd_base):
return data.hex()
def unhexlify(self,hexstr:'sstr'):
"convert hexadecimal value to bytes (warning: outputs binary data)"
"convert a hexadecimal string to bytes (warning: outputs binary data)"
return bytes.fromhex(hexstr)
def hexdump(self,infile:str,cols=8,line_nums='hex'):
@ -81,17 +81,17 @@ class tool_cmd(tool_cmd_base):
from ..proto.common import hash160
return hash160( bytes.fromhex(hexstr) ).hex()
def hash256(self,string_or_bytes:str,file_input=False,hex_input=False): # TODO: handle stdin
def hash256(self,data:str,file_input=False,hex_input=False): # TODO: handle stdin
"compute sha256(sha256(data)) (double sha256)"
from hashlib import sha256
if file_input:
from ..fileutil import get_data_from_file
b = get_data_from_file( string_or_bytes, binary=True )
b = get_data_from_file( data, binary=True )
elif hex_input:
from ..util import decode_pretty_hexdump
b = decode_pretty_hexdump(string_or_bytes)
b = decode_pretty_hexdump(data)
else:
b = string_or_bytes
b = data
return sha256(sha256(b.encode()).digest()).hexdigest()
def id6(self,infile:str):
@ -102,7 +102,7 @@ class tool_cmd(tool_cmd_base):
get_data_from_file( infile, dash=True, quiet=True, binary=True ))
def str2id6(self,string:'sstr'): # retain ignoring of space for backwards compat
"generate 6-character MMGen ID for a string, ignoring spaces"
"generate 6-character MMGen ID for a string, ignoring spaces in string"
from ..util import make_chksum_6
return make_chksum_6( ''.join(string.split()) )
@ -126,50 +126,50 @@ class tool_cmd(tool_cmd_base):
data = get_data_from_file( infile, dash=True, quiet=True, binary=True )
return baseconv('b58').frombytes( data, pad=pad, tostr=True )
def b58tobytes(self,b58num:'sstr',pad=0):
"convert a base 58 number to bytes (warning: outputs binary data)"
def b58tobytes(self,b58_str:'sstr',pad=0):
"convert a base 58 string to bytes (warning: outputs binary data)"
from ..baseconv import baseconv
return baseconv('b58').tobytes( b58num, pad=pad )
return baseconv('b58').tobytes( b58_str, pad=pad )
def hextob58(self,hexstr:'sstr',pad=0):
"convert a hexadecimal number to base 58"
"convert a hexadecimal string to base 58"
from ..baseconv import baseconv
return baseconv('b58').fromhex( hexstr, pad=pad, tostr=True )
def b58tohex(self,b58num:'sstr',pad=0):
"convert a base 58 number to hexadecimal"
def b58tohex(self,b58_str:'sstr',pad=0):
"convert a base 58 string to hexadecimal"
from ..baseconv import baseconv
return baseconv('b58').tohex( b58num, pad=pad )
return baseconv('b58').tohex( b58_str, pad=pad )
def hextob58chk(self,hexstr:'sstr'):
"convert a hexadecimal number to base58-check encoding"
"convert a hexadecimal string to base58-check encoding"
from ..proto.common import b58chk_encode
return b58chk_encode( bytes.fromhex(hexstr) )
def b58chktohex(self,b58chk_num:'sstr'):
"convert a base58-check encoded number to hexadecimal"
def b58chktohex(self,b58chk_str:'sstr'):
"convert a base58-check encoded string to hexadecimal"
from ..proto.common import b58chk_decode
return b58chk_decode(b58chk_num).hex()
return b58chk_decode(b58chk_str).hex()
def hextob32(self,hexstr:'sstr',pad=0):
"convert a hexadecimal number to MMGen's flavor of base 32"
"convert a hexadecimal string to an MMGen-flavor base 32 string"
from ..baseconv import baseconv
return baseconv('b32').fromhex( hexstr, pad, tostr=True )
def b32tohex(self,b32num:'sstr',pad=0):
"convert an MMGen-flavor base 32 number to hexadecimal"
def b32tohex(self,b32_str:'sstr',pad=0):
"convert an MMGen-flavor base 32 string to hexadecimal"
from ..baseconv import baseconv
return baseconv('b32').tohex( b32num.upper(), pad )
return baseconv('b32').tohex( b32_str.upper(), pad )
def hextob6d(self,hexstr:'sstr',pad=0,add_spaces=True):
"convert a hexadecimal number to die roll base6 (base6d)"
"convert a hexadecimal string to die roll base6 (base6d)"
from ..baseconv import baseconv
from ..util import block_format
ret = baseconv('b6d').fromhex(hexstr,pad,tostr=True)
return block_format( ret, gw=5, cols=None ).strip() if add_spaces else ret
def b6dtohex(self,b6d_num:'sstr',pad=0):
"convert a die roll base6 (base6d) number to hexadecimal"
def b6dtohex(self,b6d_str:'sstr',pad=0):
"convert a die roll base6 (base6d) string to hexadecimal"
from ..baseconv import baseconv
from ..util import remove_whitespace
return baseconv('b6d').tohex( remove_whitespace(b6d_num), pad )
return baseconv('b6d').tohex( remove_whitespace(b6d_str), pad )

View file

@ -67,23 +67,30 @@ class tool_cmd(tool_cmd_base):
return Wallet(sf).seed.split( share_count, id_str, master_share ).format()
def gen_key(self,mmgen_addr:str,wallet=''):
"generate a single MMGen WIF key from default or specified wallet"
return self.gen_addr( mmgen_addr, wallet, target='wif' )
"generate a single WIF key for specified MMGen address from default or specified wallet"
return self._gen_keyaddr( mmgen_addr, 'wif', wallet )
def gen_addr(self,mmgen_addr:str,wallet='',target='addr'):
def gen_addr(self,mmgen_addr:str,wallet=''):
"generate a single MMGen address from default or specified wallet"
return self._gen_keyaddr( mmgen_addr, 'addr', wallet )
def _gen_keyaddr(self,mmgen_addr,target,wallet=''):
from ..addr import MMGenID
from ..addrlist import AddrList,AddrIdxList
addr = MMGenID( self.proto, mmgen_addr )
opt.quiet = True
sf = get_seed_file([wallet] if wallet else [],1)
ss = Wallet(sf)
if ss.seed.sid != addr.sid:
from ..util import die
die(1,f'Seed ID of requested address ({addr.sid}) does not match wallet ({ss.seed.sid})')
d = AddrList(
proto = self.proto,
seed = ss.seed,
addr_idxs = AddrIdxList(str(addr.idx)),
mmtype = addr.mmtype ).data[0]
return d.sec.wif if target == 'wif' else d.addr
return { 'wif': d.sec.wif, 'addr': d.addr }[target]

View file

@ -43,7 +43,7 @@ opts_data = {
-L, --list-names List the names of all tested 'mmgen-tool' commands
-s, --system Test scripts and modules installed on system rather than
those in the repo root
-t, --type=t Specify address type (valid options: 'zcash_z')
-t, --type=t Specify address type (valid choices: 'zcash_z')
-v, --verbose Produce more verbose output
""",
'notes': """