def hex2mn(self,hexstr:'sstr',wordlist_id=dfl_wl_id): "convert a 16, 24 or 32-byte hexadecimal number to a mnemonic" opt.out_fmt = 'words' from mmgen.seed import SeedSource s = SeedSource(seed=bytes.fromhex(hexstr)) s._format() return ' '.join(s.ssdata.mnemonic)
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" opt.out_fmt = self._get_mnemonic_fmt(fmt) from mmgen.seed import SeedSource s = SeedSource(seed_bin=bytes.fromhex(hexstr)) s._format() return ' '.join(s.ssdata.mnemonic)
def list_subseeds(self, subseed_idx_range: str, wallet=''): "list a range of subseed Seed IDs for default or specified wallet" opt.quiet = True sf = get_seed_file([wallet] if wallet else [], 1) from mmgen.seed import SeedSource return SeedSource(sf).seed.subseeds.format( *SubSeedIdxRange(subseed_idx_range))
def get_subseed_by_seed_id(self,seed_id:str,wallet='',last_idx=g.subseeds): "get the Subseed Index of a single subseed by Seed ID for default or specified wallet" opt.quiet = True sf = get_seed_file([wallet] if wallet else [],1) from mmgen.seed import SeedSource ret = SeedSource(sf).seed.subseed_by_seed_id(seed_id,last_idx) return ret.ss_idx if ret else None
def mn2hex(self, seed_mnemonic: 'sstr', fmt: mn_opts_disp = dfl_mnemonic_fmt): "convert a 12, 18 or 24-word mnemonic seed phrase to a hexadecimal number" in_fmt = self._get_mnemonic_fmt(fmt) opt.quiet = True from mmgen.seed import SeedSource return SeedSource(in_data=seed_mnemonic, in_fmt=in_fmt).seed.hexdata
def setup(): remove_wallet_dir() gen_key(no_unmount=True) from mmgen.seed import SeedSource opt.hidden_incog_input_params = None opt.quiet = True opt.in_fmt = 'words' ss_in = SeedSource() opt.out_fmt = 'mmdat' opt.usr_randchars = 0 opt.hash_preset = '1' opt.set_by_user = ['hash_preset'] opt.passwd_file = os.path.join(tx_dir,key_fn) from mmgen.obj import MMGenWalletLabel opt.label = MMGenWalletLabel('Autosign Wallet') ss_out = SeedSource(ss=ss_in) ss_out.write_to_file(desc='autosign wallet',outdir=wallet_dir)
def setup(): remove_wallet_dir() gen_key(no_unmount=True) from mmgen.seed import SeedSource opt.hidden_incog_input_params = None opt.quiet = True opt.in_fmt = 'words' ss_in = SeedSource() opt.out_fmt = 'mmdat' opt.usr_randchars = 0 opt.hash_preset = '1' opt.set_by_user = ['hash_preset'] opt.passwd_file = os.path.join(tx_dir, key_fn) from mmgen.obj import MMGenWalletLabel opt.label = MMGenWalletLabel('Autosign Wallet') ss_out = SeedSource(ss=ss_in) ss_out.write_to_file(desc='autosign wallet', outdir=wallet_dir)
def Gen_addr(addr,wallet='',target='addr'): addr = MMGenID(addr) sf = get_seed_file([wallet] if wallet else [],1) opt.quiet = True from mmgen.seed import SeedSource ss = SeedSource(sf) if ss.seed.sid != addr.sid: m = 'Seed ID of requested address ({}) does not match wallet ({})' die(1,m.format(addr.sid,ss.seed.sid)) al = AddrList(seed=ss.seed,addr_idxs=AddrIdxList(str(addr.idx)),mmtype=addr.mmtype,do_chksum=False) d = al.data[0] Msg(d.sec.wif if target=='wif' else d.addr)
def __init__(self, fn, ftype=None, write=False): self.name = fn self.dirname = os.path.dirname(fn) self.basename = os.path.basename(fn) self.ext = get_extension(fn) self.ftype = None # the file's associated class self.mtime = None self.ctime = None self.atime = None from mmgen.seed import SeedSource from mmgen.tx import MMGenTX if ftype: if type(ftype) == type: if issubclass(ftype, SeedSource) or issubclass(ftype, MMGenTX): self.ftype = ftype # elif: # other MMGen file types else: die( 3, "'{}': not a recognized file type for SeedSource". format(ftype)) else: die(3, "'{}': not a class".format(ftype)) else: # TODO: other file types self.ftype = SeedSource.ext_to_type(self.ext) if not self.ftype: die( 3, "'{}': not a recognized extension for SeedSource".format( self.ext)) import stat if stat.S_ISBLK(os.stat(fn).st_mode): mode = (os.O_RDONLY, os.O_RDWR)[bool(write)] if g.platform == 'win': mode |= os.O_BINARY try: fd = os.open(fn, mode) except OSError as e: if e.errno == 13: die(2, "'{}': permission denied".format(fn)) # if e.errno != 17: raise else: self.size = os.lseek(fd, 0, os.SEEK_END) os.close(fd) else: self.size = os.stat(fn).st_size self.mtime = os.stat(fn).st_mtime self.ctime = os.stat(fn).st_ctime self.atime = os.stat(fn).st_atime
def gen_addr(self,mmgen_addr:str,wallet='',target='addr'): "generate a single MMGen address from default or specified wallet" addr = MMGenID(mmgen_addr) opt.quiet = True sf = get_seed_file([wallet] if wallet else [],1) from mmgen.seed import SeedSource ss = SeedSource(sf) if ss.seed.sid != addr.sid: m = 'Seed ID of requested address ({}) does not match wallet ({})' die(1,m.format(addr.sid,ss.seed.sid)) al = AddrList(seed=ss.seed,addr_idxs=AddrIdxList(str(addr.idx)),mmtype=addr.mmtype) d = al.data[0] ret = d.sec.wif if target=='wif' else d.addr return ret
def __init__(self,fn,ftype=None,write=False): self.name = fn self.dirname = os.path.dirname(fn) self.basename = os.path.basename(fn) self.ext = get_extension(fn) self.ftype = None # the file's associated class self.mtime = None self.ctime = None self.atime = None from mmgen.seed import SeedSource from mmgen.tx import MMGenTX if ftype: if type(ftype) == type: if issubclass(ftype,SeedSource) or issubclass(ftype,MMGenTX): self.ftype = ftype # elif: # other MMGen file types else: die(3,"'{}': not a recognized file type for SeedSource".format(ftype)) else: die(3,"'{}': not a class".format(ftype)) else: # TODO: other file types self.ftype = SeedSource.ext_to_type(self.ext) if not self.ftype: die(3,"'{}': not a recognized extension for SeedSource".format(self.ext)) import stat if stat.S_ISBLK(os.stat(fn).st_mode): mode = (os.O_RDONLY,os.O_RDWR)[bool(write)] if g.platform == 'win': mode |= os.O_BINARY try: fd = os.open(fn, mode) except OSError as e: if e.errno == 13: die(2,"'{}': permission denied".format(fn)) # if e.errno != 17: raise else: self.size = os.lseek(fd, 0, os.SEEK_END) os.close(fd) else: self.size = os.stat(fn).st_size self.mtime = os.stat(fn).st_mtime self.ctime = os.stat(fn).st_ctime self.atime = os.stat(fn).st_atime
def decrypt_wallets(): opt.hash_preset = '1' opt.set_by_user = ['hash_preset'] opt.passwd_file = os.path.join(tx_dir, key_fn) # opt.passwd_file = '/tmp/key' from mmgen.seed import SeedSource msg("Unlocking wallet{} with key from '{}'".format(suf(wfs), opt.passwd_file)) fails = 0 for wf in wfs: try: SeedSource(wf) except SystemExit as e: if e.code != 0: fails += 1 return False if fails else True
{n_at} NOTES FOR ALL GENERATOR COMMANDS {n_pw} {n_bw} FMT CODES: {n_fmt} """.format(n_secp=note_secp256k1, n_addrkey=note_addrkey, n_pw=help_notes('passwd'), n_bw=help_notes('brainwallet'), n_fmt='\n '.join(SeedSource.format_fmt_codes().splitlines()), n_at='\n '.join([ "'{}','{:<12} - {}".format(k, v['name'] + "'", v['desc']) for k, v in MAT.mmtypes.items() ]), o=opts) } cmd_args = opts.init(opts_data, add_opts=['b16'], opt_filter=opt_filter) errmsg = "'{}': invalid parameter for --type option".format(opt.type) addr_type = MAT(opt.type or g.proto.dfl_mmtype, errmsg=errmsg) if len(cmd_args) < 1: opts.usage() if opt.use_internal_ed25519_mod:
required {pnm} keys using 'bitcoind importprivkey'. For transaction outputs that are {pnm} addresses, {pnm}-to-Bitcoin address mappings are verified. Therefore, seed material or a key-address file for these addresses must be supplied on the command line. Seed data supplied in files must have the following extensions: wallet: '.{g.wallet_ext}' seed: '.{g.seed_ext}' mnemonic: '.{g.mn_ext}' brainwallet: '.{g.brain_ext}' FMT CODES: {f} """.format( f="\n ".join(SeedSource.format_fmt_codes().splitlines()), g=g,pnm=pnm,pnl=pnl ) } wmsg = { 'mm2btc_mapping_error': """ {pnm} -> BTC address mappings differ! From %-18s %s -> %s From %-18s %s -> %s """.strip().format(pnm=pnm), 'removed_dups': """ Removed %s duplicate wif key%s from keylist (also in {pnm} key-address file """.strip().format(pnm=pnm), }
def check_opts(usr_opts): # Returns false if any check fails def opt_splits(val,sep,n,desc): sepword = 'comma' if sep == ',' else 'colon' if sep == ':' else "'{}'".format(sep) try: l = val.split(sep) except: msg("'{}': invalid {} (not {}-separated list)".format(val,desc,sepword)) return False if len(l) == n: return True else: msg("'{}': invalid {} ({} {}-separated items required)".format(val,desc,n,sepword)) return False def opt_compares(val,op_str,target,desc,what=''): import operator as o op_f = { '<':o.lt, '<=':o.le, '>':o.gt, '>=':o.ge, '=':o.eq }[op_str] if what: what += ' ' if not op_f(val,target): msg('{}: invalid {} ({}not {} {})'.format(val,desc,what,op_str,target)) return False return True def opt_is_int(val,desc): try: int(val) except: msg("'{}': invalid {} (not an integer)".format(val,desc)) return False return True def opt_is_float(val,desc): try: float(val) except: msg("'{}': invalid {} (not a floating-point number)".format(val,desc)) return False return True def opt_is_in_list(val,lst,desc): if val not in lst: q,sep = (('',','),("'","','"))[type(lst[0]) == str] fs = '{q}{v}{q}: invalid {w}\nValid choices: {q}{o}{q}' msg(fs.format(v=val,w=desc,q=q,o=sep.join(map(str,sorted(lst))))) return False return True def opt_unrecognized(key,val,desc): msg("'{}': unrecognized {} for option '{}'".format(val,desc,fmt_opt(key))) return False def opt_display(key,val='',beg='For selected',end=':\n'): s = '{}={}'.format(fmt_opt(key),val) if val else fmt_opt(key) msg_r("{} option '{}'{}".format(beg,s,end)) global opt for key,val in [(k,getattr(opt,k)) for k in usr_opts]: desc = "parameter for '{}' option".format(fmt_opt(key)) from mmgen.util import check_infile,check_outfile,check_outdir # Check for file existence and readability if key in ('keys_from_file','mmgen_keys_from_file', 'passwd_file','keysforaddrs','comment_file'): check_infile(val) # exits on error continue if key == 'outdir': check_outdir(val) # exits on error # # NEW elif key in ('in_fmt','out_fmt'): from mmgen.seed import SeedSource,IncogWallet,Brainwallet,IncogWalletHidden sstype = SeedSource.fmt_code_to_type(val) if not sstype: return opt_unrecognized(key,val,'format code') if key == 'out_fmt': p = 'hidden_incog_output_params' if sstype == IncogWalletHidden and not getattr(opt,p): m1 = 'Hidden incog format output requested. ' m2 = "You must supply a file and offset with the '{}' option" die(1,m1+m2.format(fmt_opt(p))) if issubclass(sstype,IncogWallet) and opt.old_incog_fmt: opt_display(key,val,beg='Selected',end=' ') opt_display('old_incog_fmt',beg='conflicts with',end=':\n') die(1,'Export to old incog wallet format unsupported') elif issubclass(sstype,Brainwallet): die(1,'Output to brainwallet format unsupported') elif key in ('hidden_incog_input_params','hidden_incog_output_params'): a = val.split(',') if len(a) < 2: opt_display(key,val) msg('Option requires two comma-separated arguments') return False fn,ofs = ','.join(a[:-1]),a[-1] # permit comma in filename if not opt_is_int(ofs,desc): return False if key == 'hidden_incog_input_params': check_infile(fn,blkdev_ok=True) key2 = 'in_fmt' else: try: os.stat(fn) except: b = os.path.dirname(fn) if b: check_outdir(b) else: check_outfile(fn,blkdev_ok=True) key2 = 'out_fmt' if hasattr(opt,key2): val2 = getattr(opt,key2) from mmgen.seed import IncogWalletHidden if val2 and val2 not in IncogWalletHidden.fmt_codes: fs = 'Option conflict:\n {}, with\n {}={}' die(1,fs.format(fmt_opt(key),fmt_opt(key2),val2)) elif key == 'seed_len': if not opt_is_int(val,desc): return False if not opt_is_in_list(int(val),g.seed_lens,desc): return False elif key == 'hash_preset': if not opt_is_in_list(val,list(g.hash_presets.keys()),desc): return False elif key == 'brain_params': a = val.split(',') if len(a) != 2: opt_display(key,val) msg('Option requires two comma-separated arguments') return False d = 'seed length ' + desc if not opt_is_int(a[0],d): return False if not opt_is_in_list(int(a[0]),g.seed_lens,d): return False d = 'hash preset ' + desc if not opt_is_in_list(a[1],list(g.hash_presets.keys()),d): return False elif key == 'usr_randchars': if val == 0: continue if not opt_is_int(val,desc): return False if not opt_compares(val,'>=',g.min_urandchars,desc): return False if not opt_compares(val,'<=',g.max_urandchars,desc): return False elif key == 'tx_fee': if not opt_is_tx_fee(val,desc): return False elif key == 'tx_confs': if not opt_is_int(val,desc): return False if not opt_compares(val,'>=',1,desc): return False elif key == 'vsize_adj': if not opt_is_float(val,desc): return False ymsg('Adjusting transaction vsize by a factor of {:1.2f}'.format(float(val))) elif key == 'key_generator': if not opt_compares(val,'<=',len(g.key_generators),desc): return False if not opt_compares(val,'>',0,desc): return False elif key == 'coin': from mmgen.protocol import CoinProtocol if not opt_is_in_list(val.lower(),list(CoinProtocol.coins.keys()),'coin'): return False elif key == 'rbf': if not g.proto.cap('rbf'): msg('--rbf requested, but {} does not support replace-by-fee transactions'.format(g.coin)) return False elif key in ('bob','alice'): from mmgen.regtest import daemon_dir m = "Regtest (Bob and Alice) mode not set up yet. Run '{}-regtest setup' to initialize." try: os.stat(daemon_dir) except: die(1,m.format(g.proj_name.lower())) elif key == 'locktime': if not opt_is_int(val,desc): return False if not opt_compares(int(val),'>',0,desc): return False elif key == 'token': if not 'token' in g.proto.caps: msg("Coin '{}' does not support the --token option".format(g.coin)) return False elif len(val) == 40 and is_hex_str(val): pass elif len(val) > 20 or not all(s.isalnum() for s in val): msg("u'{}: invalid parameter for --token option".format(val)) return False elif key == 'contract_data': check_infile(val) else: if g.debug: Msg("check_opts(): No test for opt '{}'".format(key)) return True
def check_opts(usr_opts): # Returns false if any check fails def opt_splits(val,sep,n,desc): sepword = 'comma' if sep == ',' else 'colon' if sep == ':' else "'%s'" % sep try: l = val.split(sep) except: msg("'%s': invalid %s (not %s-separated list)" % (val,desc,sepword)) return False if len(l) == n: return True else: msg("'%s': invalid %s (%s %s-separated items required)" % (val,desc,n,sepword)) return False def opt_compares(val,op,target,desc,what=''): if what: what += ' ' if not eval('%s %s %s' % (val, op, target)): msg('%s: invalid %s (%snot %s %s)' % (val,desc,what,op,target)) return False return True def opt_is_int(val,desc): try: int(val) except: msg("'%s': invalid %s (not an integer)" % (val,desc)) return False return True def opt_is_in_list(val,lst,desc): if val not in lst: q,sep = (('',','),("'","','"))[type(lst[0]) == str] msg('{q}{v}{q}: invalid {w}\nValid choices: {q}{o}{q}'.format( v=val,w=desc,q=q, o=sep.join([str(i) for i in sorted(lst)]) )) return False return True def opt_unrecognized(key,val,desc): msg("'%s': unrecognized %s for option '%s'" % (val,desc,fmt_opt(key))) return False def opt_display(key,val='',beg='For selected',end=':\n'): s = '%s=%s' % (fmt_opt(key),val) if val else fmt_opt(key) msg_r("%s option '%s'%s" % (beg,s,end)) global opt for key,val in [(k,getattr(opt,k)) for k in usr_opts]: desc = "parameter for '%s' option" % fmt_opt(key) from mmgen.util import check_infile,check_outfile,check_outdir # Check for file existence and readability if key in ('keys_from_file','mmgen_keys_from_file', 'passwd_file','keysforaddrs','comment_file'): check_infile(val) # exits on error continue if key == 'outdir': check_outdir(val) # exits on error # # NEW elif key in ('in_fmt','out_fmt'): from mmgen.seed import SeedSource,IncogWallet,Brainwallet,IncogWalletHidden sstype = SeedSource.fmt_code_to_type(val) if not sstype: return opt_unrecognized(key,val,'format code') if key == 'out_fmt': p = 'hidden_incog_output_params' if sstype == IncogWalletHidden and not getattr(opt,p): die(1,'Hidden incog format output requested. You must supply' + " a file and offset with the '%s' option" % fmt_opt(p)) if issubclass(sstype,IncogWallet) and opt.old_incog_fmt: opt_display(key,val,beg='Selected',end=' ') opt_display('old_incog_fmt',beg='conflicts with',end=':\n') die(1,'Export to old incog wallet format unsupported') elif issubclass(sstype,Brainwallet): die(1,'Output to brainwallet format unsupported') elif key in ('hidden_incog_input_params','hidden_incog_output_params'): a = val.split(',') if len(a) < 2: opt_display(key,val) msg('Option requires two comma-separated arguments') return False fn,ofs = ','.join(a[:-1]),a[-1] # permit comma in filename if not opt_is_int(ofs,desc): return False if key == 'hidden_incog_input_params': check_infile(fn,blkdev_ok=True) key2 = 'in_fmt' else: try: os.stat(fn) except: b = os.path.dirname(fn) if b: check_outdir(b) else: check_outfile(fn,blkdev_ok=True) key2 = 'out_fmt' if hasattr(opt,key2): val2 = getattr(opt,key2) from mmgen.seed import IncogWalletHidden if val2 and val2 not in IncogWalletHidden.fmt_codes: die(1, 'Option conflict:\n %s, with\n %s=%s' % ( fmt_opt(key),fmt_opt(key2),val2 )) elif key == 'seed_len': if not opt_is_int(val,desc): return False if not opt_is_in_list(int(val),g.seed_lens,desc): return False elif key == 'hash_preset': if not opt_is_in_list(val,g.hash_presets.keys(),desc): return False elif key == 'brain_params': a = val.split(',') if len(a) != 2: opt_display(key,val) msg('Option requires two comma-separated arguments') return False d = 'seed length ' + desc if not opt_is_int(a[0],d): return False if not opt_is_in_list(int(a[0]),g.seed_lens,d): return False d = 'hash preset ' + desc if not opt_is_in_list(a[1],g.hash_presets.keys(),d): return False elif key == 'usr_randchars': if val == 0: continue if not opt_is_int(val,desc): return False if not opt_compares(val,'>=',g.min_urandchars,desc): return False if not opt_compares(val,'<=',g.max_urandchars,desc): return False elif key == 'tx_fee': if not opt_is_tx_fee(val,desc): return False elif key == 'tx_confs': if not opt_is_int(val,desc): return False if not opt_compares(val,'>=',1,desc): return False elif key == 'key_generator': if not opt_compares(val,'<=',len(g.key_generators),desc): return False if not opt_compares(val,'>',0,desc): return False elif key == 'coin': from mmgen.protocol import CoinProtocol if not opt_is_in_list(val.lower(),CoinProtocol.coins.keys(),'coin'): return False elif key == 'rbf': if not g.proto.cap('rbf'): die(1,'--rbf requested, but {} does not support replace-by-fee transactions'.format(g.coin)) elif key in ('bob','alice'): from mmgen.regtest import daemon_dir m = "Regtest (Bob and Alice) mode not set up yet. Run '{}-regtest setup' to initialize." try: os.stat(daemon_dir) except: die(1,m.format(g.proj_name.lower())) elif key == 'locktime': if not opt_is_int(val,desc): return False if not opt_compares(val,'>',0,desc): return False else: if g.debug: Msg("check_opts(): No test for opt '%s'" % key) return True
(min={g.min_urandchars}, max={g.max_urandchars}, default={g.usr_randchars}) -S, --stdout Write wallet data to stdout instead of file -v, --verbose Produce more verbose output """.format( g=g, iaction=capfirst(iaction), oaction=capfirst(oaction), ), 'notes': u""" {n_pw}{n_bw} FMT CODES: {f} """.format(f=u'\n '.join(SeedSource.format_fmt_codes().splitlines()), n_pw=help_notes('passwd'), n_bw=(u'', '\n\n' + help_notes('brainwallet'))[bw_note]) } cmd_args = opts.init(opts_data, opt_filter=opt_filter) if opt.label: opt.label = MMGenWalletLabel(opt.label, msg="Error in option '--label'") sf = get_seed_file(cmd_args, nargs, invoked_as=invoked_as) if not invoked_as == 'chk': do_license_msg() if invoked_as in ('conv', 'passchg'): m1 = green('Processing input wallet')
FMT CODES: {f} """ }, 'code': { 'options': lambda s: s.format( iaction=capfirst(iaction), oaction=capfirst(oaction), g=g, ), 'notes': lambda s: s.format( f='\n '.join(SeedSource.format_fmt_codes().splitlines()), n_sw=('', help_notes('subwallet') + '\n\n')[do_sw_note], n_pw=help_notes('passwd'), n_bw=('', '\n\n' + help_notes('brainwallet'))[do_bw_note]) } } cmd_args = opts.init(opts_data, opt_filter=opt_filter) if opt.label: opt.label = MMGenWalletLabel(opt.label, msg="Error in option '--label'") if invoked_as == 'subgen': from mmgen.obj import SubSeedIdx ss_idx = SubSeedIdx(cmd_args.pop())
def check_opts(usr_opts): # Returns false if any check fails def opt_splits(val, sep, n, desc): sepword = 'comma' if sep == ',' else 'colon' if sep == ':' else "'%s'" % sep try: l = val.split(sep) except: msg("'%s': invalid %s (not %s-separated list)" % (val, desc, sepword)) return False if len(l) == n: return True else: msg("'%s': invalid %s (%s %s-separated items required)" % (val, desc, n, sepword)) return False def opt_compares(val, op, target, desc, what=''): if what: what += ' ' if not eval('%s %s %s' % (val, op, target)): msg('%s: invalid %s (%snot %s %s)' % (val, desc, what, op, target)) return False return True def opt_is_int(val, desc): try: int(val) except: msg("'%s': invalid %s (not an integer)" % (val, desc)) return False return True def opt_is_tx_fee(val, desc): from mmgen.tx import MMGenTX ret = MMGenTX().convert_fee_spec(val, 224, on_fail='return') if ret == False: msg("'{}': invalid {} (not a {} amount or satoshis-per-byte specification)" .format(val, desc, g.coin.upper())) elif ret != None and ret > g.max_tx_fee: msg("'{}': invalid {} (> max_tx_fee ({} {}))".format( val, desc, g.max_tx_fee, g.coin.upper())) else: return True return False def opt_is_in_list(val, lst, desc): if val not in lst: q, sep = (('', ','), ("'", "','"))[type(lst[0]) == str] msg('{q}{v}{q}: invalid {w}\nValid choices: {q}{o}{q}'.format( v=val, w=desc, q=q, o=sep.join([str(i) for i in sorted(lst)]))) return False return True def opt_unrecognized(key, val, desc): msg("'%s': unrecognized %s for option '%s'" % (val, desc, fmt_opt(key))) return False def opt_display(key, val='', beg='For selected', end=':\n'): s = '%s=%s' % (fmt_opt(key), val) if val else fmt_opt(key) msg_r("%s option '%s'%s" % (beg, s, end)) global opt for key, val in [(k, getattr(opt, k)) for k in usr_opts]: desc = "parameter for '%s' option" % fmt_opt(key) from mmgen.util import check_infile, check_outfile, check_outdir # Check for file existence and readability if key in ('keys_from_file', 'mmgen_keys_from_file', 'passwd_file', 'keysforaddrs', 'comment_file'): check_infile(val) # exits on error continue if key == 'outdir': check_outdir(val) # exits on error # # NEW elif key in ('in_fmt', 'out_fmt'): from mmgen.seed import SeedSource, IncogWallet, Brainwallet, IncogWalletHidden sstype = SeedSource.fmt_code_to_type(val) if not sstype: return opt_unrecognized(key, val, 'format code') if key == 'out_fmt': p = 'hidden_incog_output_params' if sstype == IncogWalletHidden and not getattr(opt, p): die( 1, 'Hidden incog format output requested. You must supply' + " a file and offset with the '%s' option" % fmt_opt(p)) if issubclass(sstype, IncogWallet) and opt.old_incog_fmt: opt_display(key, val, beg='Selected', end=' ') opt_display('old_incog_fmt', beg='conflicts with', end=':\n') die(1, 'Export to old incog wallet format unsupported') elif issubclass(sstype, Brainwallet): die(1, 'Output to brainwallet format unsupported') elif key in ('hidden_incog_input_params', 'hidden_incog_output_params'): a = val.split(',') if len(a) < 2: opt_display(key, val) msg('Option requires two comma-separated arguments') return False fn, ofs = ','.join(a[:-1]), a[-1] # permit comma in filename if not opt_is_int(ofs, desc): return False if key == 'hidden_incog_input_params': check_infile(fn, blkdev_ok=True) key2 = 'in_fmt' else: try: os.stat(fn) except: b = os.path.dirname(fn) if b: check_outdir(b) else: check_outfile(fn, blkdev_ok=True) key2 = 'out_fmt' if hasattr(opt, key2): val2 = getattr(opt, key2) from mmgen.seed import IncogWalletHidden if val2 and val2 not in IncogWalletHidden.fmt_codes: die( 1, 'Option conflict:\n %s, with\n %s=%s' % (fmt_opt(key), fmt_opt(key2), val2)) elif key == 'seed_len': if not opt_is_int(val, desc): return False if not opt_is_in_list(int(val), g.seed_lens, desc): return False elif key == 'hash_preset': if not opt_is_in_list(val, g.hash_presets.keys(), desc): return False elif key == 'brain_params': a = val.split(',') if len(a) != 2: opt_display(key, val) msg('Option requires two comma-separated arguments') return False d = 'seed length ' + desc if not opt_is_int(a[0], d): return False if not opt_is_in_list(int(a[0]), g.seed_lens, d): return False d = 'hash preset ' + desc if not opt_is_in_list(a[1], g.hash_presets.keys(), d): return False elif key == 'usr_randchars': if val == 0: continue if not opt_is_int(val, desc): return False if not opt_compares(val, '>=', g.min_urandchars, desc): return False if not opt_compares(val, '<=', g.max_urandchars, desc): return False elif key == 'tx_fee': if not opt_is_tx_fee(val, desc): return False elif key == 'tx_confs': if not opt_is_int(val, desc): return False if not opt_compares(val, '>=', 1, desc): return False elif key == 'key_generator': if not opt_compares(val, '<=', len(g.key_generators), desc): return False if not opt_compares(val, '>', 0, desc): return False elif key == 'coin': if not opt_is_in_list(val.upper(), g.coins, 'coin'): return False else: if g.debug: Msg("check_opts(): No test for opt '%s'" % key) return True
def check_opts(usr_opts): # Returns false if any check fails def opt_splits(val,sep,n,desc): sepword = "comma" if sep == "," else ( "colon" if sep == ":" else ("'"+sep+"'")) try: l = val.split(sep) except: msg("'%s': invalid %s (not %s-separated list)" % (val,desc,sepword)) return False if len(l) == n: return True else: msg("'%s': invalid %s (%s %s-separated items required)" % (val,desc,n,sepword)) return False def opt_compares(val,op,target,desc,what=""): if what: what += " " if not eval("%s %s %s" % (val, op, target)): msg("%s: invalid %s (%snot %s %s)" % (val,desc,what,op,target)) return False return True def opt_is_int(val,desc): try: int(val) except: msg("'%s': invalid %s (not an integer)" % (val,desc)) return False return True def opt_is_in_list(val,lst,desc): if val not in lst: q,sep = ("'","','") if type(lst[0]) == str else ("",",") msg("{q}{v}{q}: invalid {w}\nValid choices: {q}{o}{q}".format( v=val,w=desc,q=q, o=sep.join([str(i) for i in sorted(lst)]) )) return False return True def opt_unrecognized(key,val,desc): msg("'%s': unrecognized %s for option '%s'" % (val,desc,fmt_opt(key))) return False def opt_display(key,val='',beg="For selected",end=":\n"): s = "%s=%s" % (fmt_opt(key),val) if val else fmt_opt(key) msg_r("%s option '%s'%s" % (beg,s,end)) global opt for key,val in [(k,getattr(opt,k)) for k in usr_opts]: desc = "parameter for '%s' option" % fmt_opt(key) from mmgen.util import check_infile,check_outfile,check_outdir # Check for file existence and readability if key in ('keys_from_file','mmgen_keys_from_file', 'passwd_file','keysforaddrs','comment_file'): check_infile(val) # exits on error continue if key == 'outdir': check_outdir(val) # exits on error elif key == 'label': if not is_mmgen_wallet_label(val): msg("Illegal value for option '%s': '%s'" % (fmt_opt(key),val)) return False # NEW elif key in ('in_fmt','out_fmt'): from mmgen.seed import SeedSource,IncogWallet,Brainwallet,IncogWalletHidden sstype = SeedSource.fmt_code_to_sstype(val) if not sstype: return opt_unrecognized(key,val,"format code") if key == 'out_fmt': p = 'hidden_incog_output_params' if sstype == IncogWalletHidden and not getattr(opt,p): die(1,"Hidden incog format output requested. You must supply" + " a file and offset with the '%s' option" % fmt_opt(p)) if issubclass(sstype,IncogWallet) and opt.old_incog_fmt: opt_display(key,val,beg="Selected",end=" ") opt_display('old_incog_fmt',beg="conflicts with",end=":\n") die(1,"Export to old incog wallet format unsupported") elif issubclass(sstype,Brainwallet): die(1,"Output to brainwallet format unsupported") elif key in ('hidden_incog_input_params','hidden_incog_output_params'): a = val.split(",") if len(a) != 2: opt_display(key,val) msg("Option requires two comma-separated arguments") return False if not opt_is_int(a[1],desc): return False if key == 'hidden_incog_input_params': check_infile(a[0],blkdev_ok=True) key2 = 'in_fmt' else: import os try: os.stat(a[0]) except: b = os.path.dirname(a[0]) if b: check_outdir(b) else: check_outfile(a[0],blkdev_ok=True) key2 = 'out_fmt' if hasattr(opt,key2): val2 = getattr(opt,key2) from mmgen.seed import IncogWalletHidden if val2 and val2 not in IncogWalletHidden.fmt_codes: die(1, "Option conflict:\n %s, with\n %s=%s" % ( fmt_opt(key),fmt_opt(key2),val2 )) elif key == 'seed_len': if not opt_is_int(val,desc): return False if not opt_is_in_list(int(val),g.seed_lens,desc): return False elif key == 'hash_preset': if not opt_is_in_list(val,g.hash_presets.keys(),desc): return False elif key == 'brain_params': a = val.split(",") if len(a) != 2: opt_display(key,val) msg("Option requires two comma-separated arguments") return False d = "seed length " + desc if not opt_is_int(a[0],d): return False if not opt_is_in_list(int(a[0]),g.seed_lens,d): return False d = "hash preset " + desc if not opt_is_in_list(a[1],g.hash_presets.keys(),d): return False elif key == 'usr_randchars': if val == 0: continue if not opt_is_int(val,desc): return False if not opt_compares(val,">=",g.min_urandchars,desc): return False if not opt_compares(val,"<=",g.max_urandchars,desc): return False else: if g.debug: Msg("check_opts(): No test for opt '%s'" % key) return True
def help_notes(k): from mmgen.seed import SeedSource from mmgen.tx import MMGenTX def fee_spec_letters(use_quotes=False): cu = g.proto.coin_amt.units sep, conj = ((',', ' or '), ("','", "' or '"))[use_quotes] return sep.join(u[0] for u in cu[:-1]) + ('', conj)[len(cu) > 1] + cu[-1][0] def fee_spec_names(): cu = g.proto.coin_amt.units return ', '.join(cu[:-1]) + ('', ' and ')[len(cu) > 1] + cu[-1] + ( '', ',\nrespectively')[len(cu) > 1] return { 'rel_fee_desc': MMGenTX().rel_fee_desc, 'fee_spec_letters': fee_spec_letters(), 'passwd': """ For passphrases all combinations of whitespace are equal and leading and trailing space is ignored. This permits reading passphrase or brainwallet data from a multi-line file with free spacing and indentation. """.strip(), 'brainwallet': """ BRAINWALLET NOTE: To thwart dictionary attacks, it's recommended to use a strong hash preset with brainwallets. For a brainwallet passphrase to generate the correct seed, the same seed length and hash preset parameters must always be used. """.strip(), 'txcreate': """ The transaction's outputs are specified on the command line, while its inputs are chosen from a list of the user's unpent outputs via an interactive menu. If the transaction fee is not specified on the command line (see FEE SPECIFICATION below), it will be calculated dynamically using network fee estimation for the default (or user-specified) number of confirmations. If network fee estimation fails, the user will be prompted for a fee. Network-estimated fees will be multiplied by the value of '--tx-fee-adj', if specified. Ages of transactions are approximate based on an average block discovery interval of one per {g.proto.secs_per_block} seconds. All addresses on the command line can be either {pnu} addresses or {pnm} addresses of the form <seed ID>:<index>. To send the value of all inputs (minus TX fee) to a single output, specify one address with no amount on the command line. """.format(g=g, pnm=g.proj_name, pnu=g.proto.name.capitalize()), 'fee': """ FEE SPECIFICATION: Transaction fees, both on the command line and at the interactive prompt, may be specified as either absolute {c} amounts, using a plain decimal number, or as {r}, using an integer followed by '{l}', for {u}. """.format(c=g.coin, r=MMGenTX().rel_fee_desc, l=fee_spec_letters(use_quotes=True), u=fee_spec_names()), 'txsign': u""" Transactions may contain both {pnm} or non-{pnm} input addresses. To sign non-{pnm} inputs, a {dn} wallet dump or flat key list is used as the key source ('--keys-from-file' option). To sign {pnm} inputs, key data is generated from a seed as with the {pnl}-addrgen and {pnl}-keygen commands. Alternatively, a key-address file may be used (--mmgen-keys-from-file option). Multiple wallets or other seed files can be listed on the command line in any order. If the seeds required to sign the transaction's inputs are not found in these files (or in the default wallet), the user will be prompted for seed data interactively. To prevent an attacker from crafting transactions with bogus {pnm}-to-{pnu} address mappings, all outputs to {pnm} addresses are verified with a seed source. Therefore, seed files or a key-address file for all {pnm} outputs must also be supplied on the command line if the data can't be found in the default wallet. Seed source files must have the canonical extensions listed in the 'FileExt' column below: {n_fmt} """.format(dn=g.proto.daemon_name, n_fmt='\n '.join(SeedSource.format_fmt_codes().splitlines()), pnm=g.proj_name, pnu=g.proto.name.capitalize(), pnl=g.proj_name.lower()) }[k] + (u'-α' if g.debug_utf8 else '')
def help_notes(k): from mmgen.seed import SeedSource return { 'passwd': """ For passphrases all combinations of whitespace are equal and leading and trailing space is ignored. This permits reading passphrase or brainwallet data from a multi-line file with free spacing and indentation. """.strip(), 'brainwallet': """ BRAINWALLET NOTE: To thwart dictionary attacks, it's recommended to use a strong hash preset with brainwallets. For a brainwallet passphrase to generate the correct seed, the same seed length and hash preset parameters must always be used. """.strip(), 'txcreate': """ The transaction's outputs are specified on the command line, while its inputs are chosen from a list of the user's unpent outputs via an interactive menu. If the transaction fee is not specified on the command line (see FEE SPECIFICATION below), it will be calculated dynamically using {dn}'s "estimatefee" function for the default (or user-specified) number of confirmations. If "estimatefee" fails, the user will be prompted for a fee. Dynamic ("estimatefee") fees will be multiplied by the value of '--tx-fee-adj', if specified. Ages of transactions are approximate based on an average block discovery interval of one per {g.proto.secs_per_block} seconds. All addresses on the command line can be either {pnu} addresses or {pnm} addresses of the form <seed ID>:<index>. To send the value of all inputs (minus TX fee) to a single output, specify one address with no amount on the command line. """.format(g=g, pnm=g.proj_name, dn=g.proto.daemon_name, pnu=g.proto.name.capitalize()), 'fee': """ FEE SPECIFICATION: Transaction fees, both on the command line and at the interactive prompt, may be specified as either absolute {} amounts, using a plain decimal number, or as satoshis per byte, using an integer followed by the letter 's'. """.format(g.coin), 'txsign': """ Transactions may contain both {pnm} or non-{pnm} input addresses. To sign non-{pnm} inputs, a {dn} wallet dump or flat key list is used as the key source ('--keys-from-file' option). To sign {pnm} inputs, key data is generated from a seed as with the {pnl}-addrgen and {pnl}-keygen commands. Alternatively, a key-address file may be used (--mmgen-keys-from-file option). Multiple wallets or other seed files can be listed on the command line in any order. If the seeds required to sign the transaction's inputs are not found in these files (or in the default wallet), the user will be prompted for seed data interactively. To prevent an attacker from crafting transactions with bogus {pnm}-to-{pnu} address mappings, all outputs to {pnm} addresses are verified with a seed source. Therefore, seed files or a key-address file for all {pnm} outputs must also be supplied on the command line if the data can't be found in the default wallet. Seed source files must have the canonical extensions listed in the 'FileExt' column below: {n_fmt} """.format(dn=g.proto.daemon_name, n_fmt='\n '.join(SeedSource.format_fmt_codes().splitlines()), pnm=g.proj_name, pnu=g.proto.name.capitalize(), pnl=g.proj_name.lower()) }[k]
{n_sw}{n_pw}{n_bw} FMT CODES: {f} """ }, 'code': { 'options': lambda s: s.format( iaction=capfirst(iaction), oaction=capfirst(oaction), g=g, ), 'notes': lambda s: s.format( f='\n '.join(SeedSource.format_fmt_codes().splitlines()), n_sw=('',help_notes('subwallet')+'\n\n')[do_sw_note], n_pw=help_notes('passwd'), n_bw=('','\n\n'+help_notes('brainwallet'))[do_bw_note] ) } } cmd_args = opts.init(opts_data,opt_filter=opt_filter) if opt.label: opt.label = MMGenWalletLabel(opt.label,msg="Error in option '--label'") if invoked_as == 'subgen': from mmgen.obj import SubSeedIdx ss_idx = SubSeedIdx(cmd_args.pop())
def check_opts(usr_opts): # Returns false if any check fails def opt_splits(val, sep, n, desc): sepword = 'comma' if sep == ',' else 'colon' if sep == ':' else "'{}'".format( sep) try: l = val.split(sep) except: msg("'{}': invalid {} (not {}-separated list)".format( val, desc, sepword)) return False if len(l) == n: return True else: msg("'{}': invalid {} ({} {}-separated items required)".format( val, desc, n, sepword)) return False def opt_compares(val, op_str, target, desc, what=''): import operator as o op_f = { '<': o.lt, '<=': o.le, '>': o.gt, '>=': o.ge, '=': o.eq }[op_str] if what: what += ' ' if not op_f(val, target): msg('{}: invalid {} ({}not {} {})'.format(val, desc, what, op_str, target)) return False return True def opt_is_int(val, desc): try: int(val) except: msg("'{}': invalid {} (not an integer)".format(val, desc)) return False return True def opt_is_float(val, desc): try: float(val) except: msg("'{}': invalid {} (not a floating-point number)".format( val, desc)) return False return True def opt_is_in_list(val, lst, desc): if val not in lst: q, sep = (('', ','), ("'", "','"))[type(lst[0]) == str] fs = '{q}{v}{q}: invalid {w}\nValid choices: {q}{o}{q}' msg( fs.format(v=val, w=desc, q=q, o=sep.join(map(str, sorted(lst))))) return False return True def opt_unrecognized(key, val, desc): msg("'{}': unrecognized {} for option '{}'".format( val, desc, fmt_opt(key))) return False def opt_display(key, val='', beg='For selected', end=':\n'): s = '{}={}'.format(fmt_opt(key), val) if val else fmt_opt(key) msg_r("{} option '{}'{}".format(beg, s, end)) global opt for key, val in [(k, getattr(opt, k)) for k in usr_opts]: desc = "parameter for '{}' option".format(fmt_opt(key)) from mmgen.util import check_infile, check_outfile, check_outdir # Check for file existence and readability if key in ('keys_from_file', 'mmgen_keys_from_file', 'passwd_file', 'keysforaddrs', 'comment_file'): check_infile(val) # exits on error continue if key == 'outdir': check_outdir(val) # exits on error # # NEW elif key in ('in_fmt', 'out_fmt'): from mmgen.seed import SeedSource, IncogWallet, Brainwallet, IncogWalletHidden sstype = SeedSource.fmt_code_to_type(val) if not sstype: return opt_unrecognized(key, val, 'format code') if key == 'out_fmt': p = 'hidden_incog_output_params' if sstype == IncogWalletHidden and not getattr(opt, p): m1 = 'Hidden incog format output requested. ' m2 = "You must supply a file and offset with the '{}' option" die(1, m1 + m2.format(fmt_opt(p))) if issubclass(sstype, IncogWallet) and opt.old_incog_fmt: opt_display(key, val, beg='Selected', end=' ') opt_display('old_incog_fmt', beg='conflicts with', end=':\n') die(1, 'Export to old incog wallet format unsupported') elif issubclass(sstype, Brainwallet): die(1, 'Output to brainwallet format unsupported') elif key in ('hidden_incog_input_params', 'hidden_incog_output_params'): a = val.split(',') if len(a) < 2: opt_display(key, val) msg('Option requires two comma-separated arguments') return False fn, ofs = ','.join(a[:-1]), a[-1] # permit comma in filename if not opt_is_int(ofs, desc): return False if key == 'hidden_incog_input_params': check_infile(fn, blkdev_ok=True) key2 = 'in_fmt' else: try: os.stat(fn) except: b = os.path.dirname(fn) if b: check_outdir(b) else: check_outfile(fn, blkdev_ok=True) key2 = 'out_fmt' if hasattr(opt, key2): val2 = getattr(opt, key2) from mmgen.seed import IncogWalletHidden if val2 and val2 not in IncogWalletHidden.fmt_codes: fs = 'Option conflict:\n {}, with\n {}={}' die(1, fs.format(fmt_opt(key), fmt_opt(key2), val2)) elif key == 'seed_len': if not opt_is_int(val, desc): return False if not opt_is_in_list(int(val), g.seed_lens, desc): return False elif key == 'hash_preset': if not opt_is_in_list(val, g.hash_presets.keys(), desc): return False elif key == 'brain_params': a = val.split(',') if len(a) != 2: opt_display(key, val) msg('Option requires two comma-separated arguments') return False d = 'seed length ' + desc if not opt_is_int(a[0], d): return False if not opt_is_in_list(int(a[0]), g.seed_lens, d): return False d = 'hash preset ' + desc if not opt_is_in_list(a[1], g.hash_presets.keys(), d): return False elif key == 'usr_randchars': if val == 0: continue if not opt_is_int(val, desc): return False if not opt_compares(val, '>=', g.min_urandchars, desc): return False if not opt_compares(val, '<=', g.max_urandchars, desc): return False elif key == 'tx_fee': if not opt_is_tx_fee(val, desc): return False elif key == 'tx_confs': if not opt_is_int(val, desc): return False if not opt_compares(val, '>=', 1, desc): return False elif key == 'vsize_adj': if not opt_is_float(val, desc): return False ymsg('Adjusting transaction vsize by a factor of {:1.2f}'.format( float(val))) elif key == 'key_generator': if not opt_compares(val, '<=', len(g.key_generators), desc): return False if not opt_compares(val, '>', 0, desc): return False elif key == 'coin': from mmgen.protocol import CoinProtocol if not opt_is_in_list(val.lower(), CoinProtocol.coins.keys(), 'coin'): return False elif key == 'rbf': if not g.proto.cap('rbf'): die( 1, '--rbf requested, but {} does not support replace-by-fee transactions' .format(g.coin)) elif key in ('bob', 'alice'): from mmgen.regtest import daemon_dir m = "Regtest (Bob and Alice) mode not set up yet. Run '{}-regtest setup' to initialize." try: os.stat(daemon_dir) except: die(1, m.format(g.proj_name.lower())) elif key == 'locktime': if not opt_is_int(val, desc): return False if not opt_compares(val, '>', 0, desc): return False else: if g.debug: Msg("check_opts(): No test for opt '{}'".format(key)) return True
def get_subseed(self,subseed_idx:str,wallet=''): "get the Seed ID of a single subseed by Subseed Index for default or specified wallet" opt.quiet = True sf = get_seed_file([wallet] if wallet else [],1) from mmgen.seed import SeedSource return SeedSource(sf).seed.subseed(subseed_idx).sid
def mn2hex(self,seed_mnemonic:'sstr',wordlist=dfl_wl_id): "convert a 12, 18 or 24-word mnemonic to a hexadecimal number" opt.quiet = True from mmgen.seed import SeedSource return SeedSource(in_data=seed_mnemonic,in_fmt='words').seed.hexdata
""" }, 'code': { 'options': lambda s: s.format( g=g,pnm=g.proj_name,pnl=g.proj_name.lower(), kgs=' '.join(['{}:{}'.format(n,k) for n,k in enumerate(g.key_generators,1)]), fu=help_notes('rel_fee_desc'), fl=help_notes('fee_spec_letters'), ss=g.subseeds,ss_max=SubSeedIdxRange.max_idx, kg=g.key_generator, cu=g.coin), 'notes': lambda s: s.format( help_notes('txcreate'), help_notes('fee'), help_notes('txsign'), f='\n '.join(SeedSource.format_fmt_codes().splitlines())) } } cmd_args = opts.init(opts_data) rpc_init() from mmgen.tx import * from mmgen.txsign import * seed_files = get_seed_files(opt,cmd_args) kal = get_keyaddrlist(opt) kl = get_keylist(opt) if kl and kal: kl.remove_dup_keys(kal)
(min={g.min_urandchars}, max={g.max_urandchars}, default={g.usr_randchars}). -S, --stdout Write wallet data to stdout instead of file. -v, --verbose Produce more verbose output. """.format( g=g, iaction=capfirst(iaction), oaction=capfirst(oaction), ), 'notes': """ {pw_note}{bw_note} FMT CODES: {f} """.format( f="\n ".join(SeedSource.format_fmt_codes().splitlines()), pw_note=pw_note, bw_note=("","\n\n" + bw_note)[int(bool(bw_note))] ) } cmd_args = opt.opts.init(opts_data,opt_filter=opt_filter) if len(cmd_args) < nargs \ and not opt.hidden_incog_input_params and not opt.in_fmt: die(1,"An input file or input format must be specified") elif len(cmd_args) > nargs \ or (len(cmd_args) == nargs and opt.hidden_incog_input_params): msg("No input files may be specified" if invoked_as == "gen" else "Too many input files specified") opt.opts.usage()