mmgen-tool usage: various fixes and cleanups
This commit is contained in:
parent
784ae44596
commit
642b45b2e3
14 changed files with 176 additions and 136 deletions
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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'):
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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: MMGen’s 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)]
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 )
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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': """
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue