Label editing in mmgen-txcreate
This commit is contained in:
parent
8d8e678977
commit
a6547be82c
12 changed files with 224 additions and 248 deletions
|
|
@ -28,8 +28,8 @@ from mmgen.term import get_char
|
|||
|
||||
crmsg = {
|
||||
'usr_rand_notice': """
|
||||
You've chosen to not fully trust your OS's random number generator and provide
|
||||
some additional entropy of your own. Please type %s symbols on your keyboard.
|
||||
Since we don't fully trust our OS's random number generator, we'll provide
|
||||
some additional entropy of our own. Please type %s symbols on your keyboard.
|
||||
Type slowly and choose your symbols carefully for maximum randomness. Try to
|
||||
use both upper and lowercase as well as punctuation and numerals. What you
|
||||
type will not be displayed on the screen. Note that the timings between your
|
||||
|
|
@ -57,7 +57,6 @@ keystrokes will also be used as a source of randomness.
|
|||
def encrypt_seed(seed, key):
|
||||
return encrypt_data(seed, key, iv=1, desc='seed')
|
||||
|
||||
|
||||
def decrypt_seed(enc_seed, key, seed_id, key_id):
|
||||
|
||||
vmsg_r('Checking key...')
|
||||
|
|
@ -91,7 +90,6 @@ def decrypt_seed(enc_seed, key, seed_id, key_id):
|
|||
|
||||
return dec_seed
|
||||
|
||||
|
||||
def encrypt_data(data, key, iv=1, desc='data', verify=True):
|
||||
|
||||
# 192-bit seed is 24 bytes -> not multiple of 16. Must use MODE_CTR
|
||||
|
|
@ -117,7 +115,6 @@ def encrypt_data(data, key, iv=1, desc='data', verify=True):
|
|||
|
||||
return enc_data
|
||||
|
||||
|
||||
def decrypt_data(enc_data, key, iv=1, desc='data'):
|
||||
|
||||
vmsg_r('Decrypting %s with key...' % desc)
|
||||
|
|
@ -130,7 +127,6 @@ def decrypt_data(enc_data, key, iv=1, desc='data'):
|
|||
|
||||
return c.decrypt(enc_data)
|
||||
|
||||
|
||||
def scrypt_hash_passphrase(passwd, salt, hash_preset, buflen=32):
|
||||
|
||||
# Buflen arg is for brainwallets only, which use this function to generate
|
||||
|
|
@ -141,7 +137,6 @@ def scrypt_hash_passphrase(passwd, salt, hash_preset, buflen=32):
|
|||
import scrypt
|
||||
return scrypt.hash(passwd, salt, 2**N, r, p, buflen=buflen)
|
||||
|
||||
|
||||
def make_key(passwd,salt,hash_preset,
|
||||
desc='encryption key',from_what='passphrase',verbose=False):
|
||||
|
||||
|
|
@ -153,7 +148,6 @@ def make_key(passwd,salt,hash_preset,
|
|||
dmsg('Key: %s' % hexlify(key))
|
||||
return key
|
||||
|
||||
|
||||
def _get_random_data_from_user(uchars):
|
||||
|
||||
if opt.quiet: msg('Enter %s random symbols' % uchars)
|
||||
|
|
@ -189,7 +183,6 @@ def _get_random_data_from_user(uchars):
|
|||
|
||||
return key_data+''.join(fmt_time_data)
|
||||
|
||||
|
||||
def get_random(length):
|
||||
from Crypto import Random
|
||||
os_rand = Random.new().read(length)
|
||||
|
|
@ -206,6 +199,18 @@ def get_random(length):
|
|||
else:
|
||||
return os_rand
|
||||
|
||||
def get_hash_preset_from_user(hp=g.hash_preset,desc='data'):
|
||||
p = """Enter hash preset for %s,
|
||||
or hit ENTER to accept the default value ('%s'): """ % (desc,hp)
|
||||
while True:
|
||||
ret = my_raw_input(p)
|
||||
if ret:
|
||||
if ret in g.hash_presets.keys(): return ret
|
||||
else:
|
||||
msg('Invalid input. Valid choices are %s' %
|
||||
', '.join(sorted(g.hash_presets.keys())))
|
||||
continue
|
||||
else: return hp
|
||||
|
||||
# Vars for mmgen_*crypt functions only
|
||||
salt_len,sha256_len,nonce_len = 32,32,32
|
||||
|
|
@ -224,7 +229,6 @@ def mmgen_encrypt(data,desc='data',hash_preset=''):
|
|||
int(hexlify(iv),16), desc=desc)
|
||||
return salt+iv+enc_d
|
||||
|
||||
|
||||
def mmgen_decrypt(data,desc='data',hash_preset=''):
|
||||
dstart = salt_len + g.aesctr_iv_len
|
||||
salt,iv,enc_d = data[:salt_len],data[salt_len:dstart],data[dstart:]
|
||||
|
|
|
|||
|
|
@ -64,5 +64,5 @@ if cmd_args and cmd_args[0] == '--help':
|
|||
sys.exit()
|
||||
|
||||
args,kwargs = tool.process_args(g.prog_name, command, cmd_args)
|
||||
|
||||
tool.__dict__[command](*args,**kwargs)
|
||||
ret = tool.__dict__[command](*args,**kwargs)
|
||||
sys.exit(0 if ret in (None,True) else 1) # some commands die, some return False on failure
|
||||
|
|
|
|||
|
|
@ -98,22 +98,14 @@ was specified.
|
|||
}
|
||||
|
||||
def select_unspent(unspent,prompt):
|
||||
|
||||
while True:
|
||||
reply = my_raw_input(prompt).strip()
|
||||
|
||||
if not reply: continue
|
||||
|
||||
selected = parse_addr_idxs(reply,sep=None)
|
||||
|
||||
if not selected: continue
|
||||
|
||||
if selected[-1] > len(unspent):
|
||||
msg('Inputs must be less than %s' % len(unspent))
|
||||
continue
|
||||
|
||||
return selected
|
||||
|
||||
if reply:
|
||||
selected = AddrIdxList(fmt_str=','.join(reply.split()),on_fail='return')
|
||||
if selected:
|
||||
if selected[-1] <= len(unspent):
|
||||
return selected
|
||||
msg('Unspent output number must be <= %s' % len(unspent))
|
||||
|
||||
def mmaddr2baddr(c,mmaddr,ad_w,ad_f):
|
||||
|
||||
|
|
|
|||
|
|
@ -164,7 +164,14 @@ def add_keys(tx,src,infiles=None,saved_seeds=None,keyaddr_list=None):
|
|||
vmsg('Added %s wif key%s from %s' % (len(new_keys),suf(new_keys,'k'),desc))
|
||||
return new_keys
|
||||
|
||||
# # function unneeded - use bitcoin-cli walletdump instead
|
||||
# # functions unneeded - use bitcoin-cli walletdump instead
|
||||
# def get_bitcoind_passphrase(prompt):
|
||||
# if opt.passwd_file:
|
||||
# pwfile_reuse_warning()
|
||||
# return get_data_from_file(opt.passwd_file,'passphrase').strip('\r\n')
|
||||
# else:
|
||||
# return my_raw_input(prompt, echo=opt.echo_passphrase)
|
||||
#
|
||||
# def sign_tx_with_bitcoind_wallet(c,tx,tx_num_str,keys):
|
||||
# ok = tx.sign(c,tx_num_str,keys) # returns false on failure
|
||||
# if ok:
|
||||
|
|
@ -174,7 +181,7 @@ def add_keys(tx,src,infiles=None,saved_seeds=None,keyaddr_list=None):
|
|||
# prompt = 'Enter passphrase for bitcoind wallet: '
|
||||
# while True:
|
||||
# passwd = get_bitcoind_passphrase(prompt)
|
||||
# ret = c.walletpassphrase(passwd, 9999,ret_on_error=True)
|
||||
# ret = c.walletpassphrase(passwd, 9999,on_fail='return')
|
||||
# if rpc_error(ret):
|
||||
# if rpc_errmsg(ret,'unencrypted wallet, but walletpassphrase was called'):
|
||||
# msg('Wallet is unencrypted'); break
|
||||
|
|
@ -184,12 +191,11 @@ def add_keys(tx,src,infiles=None,saved_seeds=None,keyaddr_list=None):
|
|||
# ok = tx.sign(c,tx_num_str,keys)
|
||||
#
|
||||
# msg('Locking wallet')
|
||||
# ret = c.walletlock(ret_on_error=True)
|
||||
# ret = c.walletlock(on_fail='return')
|
||||
# if rpc_error(ret):
|
||||
# msg('Failed to lock wallet')
|
||||
#
|
||||
# return ok
|
||||
#
|
||||
|
||||
# main(): execution begins here
|
||||
|
||||
|
|
|
|||
44
mmgen/obj.py
44
mmgen/obj.py
|
|
@ -187,7 +187,7 @@ class AddrIdx(int,InitErrors):
|
|||
assert type(num) is not float
|
||||
me = int.__new__(cls,num)
|
||||
except:
|
||||
m = "'%s': value cannot be converted to addr idx" % num
|
||||
m = "'%s': value cannot be converted to address index" % num
|
||||
else:
|
||||
if len(str(me)) > cls.max_digits:
|
||||
m = "'%s': too many digits in addr idx" % num
|
||||
|
|
@ -205,9 +205,11 @@ class AddrIdxList(list,InitErrors):
|
|||
self.arg_chk(type(self),on_fail)
|
||||
assert fmt_str or idx_list
|
||||
if idx_list:
|
||||
return list.__init__(self,sorted(set(idx_list)))
|
||||
# dies on failure
|
||||
return list.__init__(self,sorted(set([AddrIdx(i) for i in idx_list])))
|
||||
elif fmt_str:
|
||||
ret,fs = [],"'%s': value cannot be converted to addr idx"
|
||||
desc = fmt_str
|
||||
ret,fs = [],"'%s': value cannot be converted to address index"
|
||||
from mmgen.util import msg
|
||||
for i in (fmt_str.split(sep)):
|
||||
j = i.split('-')
|
||||
|
|
@ -228,7 +230,7 @@ class AddrIdxList(list,InitErrors):
|
|||
else:
|
||||
return list.__init__(self,sorted(set(ret))) # fell off end of loop - success
|
||||
|
||||
return self.init_fail(fs % err,on_fail,silent=True)
|
||||
return self.init_fail((fs + ' list') % desc,on_fail)
|
||||
|
||||
class Hilite(object):
|
||||
|
||||
|
|
@ -238,19 +240,21 @@ class Hilite(object):
|
|||
trunc_ok = True
|
||||
|
||||
@classmethod
|
||||
def fmtc(cls,s,width=None,color=False,encl='',trunc_ok=None):
|
||||
def fmtc(cls,s,width=None,color=False,encl='',trunc_ok=None,center=False,nullrepl=''):
|
||||
if width == None: width = cls.width
|
||||
if trunc_ok == None: trunc_ok = cls.trunc_ok
|
||||
assert width > 0
|
||||
if s == '' and nullrepl:
|
||||
s,center = nullrepl,True
|
||||
if center: s = s.center(width)
|
||||
assert type(encl) is str and len(encl) in (0,2)
|
||||
a,b = list(encl) if encl else ('','')
|
||||
if trunc_ok and len(s) > width: s = s[:width]
|
||||
return cls.colorize((a+s+b).ljust(width),color=color)
|
||||
|
||||
def fmt(self,width=None,color=False,encl='',trunc_ok=None):
|
||||
if width == None: width = self.width
|
||||
if trunc_ok == None: trunc_ok = self.trunc_ok
|
||||
return self.fmtc(self,width=width,color=color,encl=encl,trunc_ok=trunc_ok)
|
||||
def fmt(self,*args,**kwargs):
|
||||
assert args == () # forbid invocation w/o keywords
|
||||
return self.fmtc(self,*args,**kwargs)
|
||||
|
||||
@classmethod
|
||||
def hlc(cls,s,color=True):
|
||||
|
|
@ -266,7 +270,8 @@ class Hilite(object):
|
|||
def colorize(cls,s,color=True):
|
||||
import mmgen.globalvars as g
|
||||
from mmgen.util import red,blue,green,yellow,pink,cyan,gray,orange,magenta
|
||||
return locals()[cls.color](s) if (color or cls.color_always) and g.color else s
|
||||
k = color if type(color) is str else cls.color # hack: override color with str value
|
||||
return locals()[k](s) if (color or cls.color_always) and g.color else s
|
||||
|
||||
class BTCAmt(Decimal,Hilite,InitErrors):
|
||||
color = 'yellow'
|
||||
|
|
@ -337,7 +342,6 @@ class BTCAmt(Decimal,Hilite,InitErrors):
|
|||
def __neg__(self,other,context=None):
|
||||
return type(self)(Decimal.__neg__(self,other,context))
|
||||
|
||||
|
||||
class BTCAddr(str,Hilite,InitErrors):
|
||||
color = 'cyan'
|
||||
width = 34
|
||||
|
|
@ -351,16 +355,15 @@ class BTCAddr(str,Hilite,InitErrors):
|
|||
m = "'%s': value is not a Bitcoin address" % s
|
||||
return cls.init_fail(m,on_fail)
|
||||
|
||||
def fmt(self,width=width,color=False):
|
||||
return self.fmtc(self,width=width,color=color)
|
||||
|
||||
@classmethod
|
||||
def fmtc(cls,s,width=width,color=False):
|
||||
if width >= len(s):
|
||||
s = s.ljust(width)
|
||||
else:
|
||||
s = s[:width-2] + '..'
|
||||
return cls.colorize(s,color=color)
|
||||
def fmtc(cls,s,**kwargs):
|
||||
# True -> 'cyan': use the str value override hack
|
||||
if 'color' in kwargs and kwargs['color'] == True:
|
||||
kwargs['color'] = cls.color
|
||||
if not 'width' in kwargs: kwargs['width'] = cls.width
|
||||
if kwargs['width'] < len(s):
|
||||
s = s[:kwargs['width']-2] + '..'
|
||||
return Hilite.fmtc(s,**kwargs)
|
||||
|
||||
class SeedID(str,Hilite,InitErrors):
|
||||
color = 'blue'
|
||||
|
|
@ -431,6 +434,7 @@ class MMGenWalletLabel(MMGenLabel):
|
|||
|
||||
class MMGenAddrLabel(MMGenLabel):
|
||||
max_len = 32
|
||||
allowed = [chr(i+32) for i in range(95)]
|
||||
desc = 'address label'
|
||||
|
||||
class MMGenTXLabel(MMGenLabel):
|
||||
|
|
|
|||
16
mmgen/rpc.py
16
mmgen/rpc.py
|
|
@ -56,10 +56,10 @@ class BitcoinRPCConnection(object):
|
|||
# kwargs are for local use and are not passed to server
|
||||
|
||||
# By default, dies with an error msg on all errors and exceptions
|
||||
# With ret_on_error=True, returns 'rpcfail',(resp_object,(die_args))
|
||||
# With on_fail='return', returns 'rpcfail',(resp_object,(die_args))
|
||||
def request(self,cmd,*args,**kwargs):
|
||||
|
||||
cf = { 'timeout':g.http_timeout, 'batch':False, 'ret_on_error':False }
|
||||
cf = { 'timeout':g.http_timeout, 'batch':False, 'on_fail':'die' }
|
||||
|
||||
for k in cf:
|
||||
if k in kwargs and kwargs[k]: cf[k] = kwargs[k]
|
||||
|
|
@ -72,7 +72,7 @@ class BitcoinRPCConnection(object):
|
|||
p = {'method':cmd,'params':args,'id':1}
|
||||
|
||||
def die_maybe(*args):
|
||||
if cf['ret_on_error']:
|
||||
if cf['on_fail'] == 'return':
|
||||
return 'rpcfail',args
|
||||
else:
|
||||
die(*args[1:])
|
||||
|
|
@ -88,7 +88,10 @@ class BitcoinRPCConnection(object):
|
|||
return (float,str)[caller.client_version>=120000](obj)
|
||||
return json.JSONEncoder.default(self, obj)
|
||||
|
||||
# pp_msg(json.dumps(p,cls=MyJSONEncoder))
|
||||
# Can't do UTF-8 labels yet: httplib only ascii?
|
||||
# if type(p) != list and p['method'] == 'importaddress':
|
||||
# dump = json.dumps(p,cls=MyJSONEncoder,ensure_ascii=False)
|
||||
# print(dump)
|
||||
|
||||
try:
|
||||
c.request('POST', '/', json.dumps(p,cls=MyJSONEncoder), {
|
||||
|
|
@ -153,7 +156,6 @@ class BitcoinRPCConnection(object):
|
|||
exec "def {n}(self,*a,**k):return self.request('{n}',*a,**k)\n".format(n=name)
|
||||
|
||||
def rpc_error(ret):
|
||||
return ret is list and ret and ret[0] == 'rpcfail'
|
||||
return type(ret) is tuple and ret and ret[0] == 'rpcfail'
|
||||
|
||||
def rpc_errmsg(ret,e):
|
||||
return (False,True)[ret[1][2].find(e) == -1]
|
||||
def rpc_errmsg(ret): return ret[1][2]
|
||||
|
|
|
|||
|
|
@ -53,7 +53,6 @@ class Seed(MMGenObject):
|
|||
def get_data(self):
|
||||
return self.data
|
||||
|
||||
|
||||
class SeedSource(MMGenObject):
|
||||
|
||||
desc = g.proj_name + ' seed source'
|
||||
|
|
@ -216,13 +215,11 @@ class SeedSource(MMGenObject):
|
|||
}
|
||||
write_data_to_file(self._filename(),self.fmt_data,**kwargs)
|
||||
|
||||
|
||||
class SeedSourceUnenc(SeedSource):
|
||||
|
||||
def _decrypt_retry(self): pass
|
||||
def _encrypt(self): pass
|
||||
|
||||
|
||||
class SeedSourceEnc(SeedSource):
|
||||
|
||||
_msg = {
|
||||
|
|
@ -346,7 +343,6 @@ an empty passphrase, just hit ENTER twice.
|
|||
d.key_id = make_chksum_8(key)
|
||||
d.enc_seed = encrypt_seed(self.seed.data,key)
|
||||
|
||||
|
||||
class Mnemonic (SeedSourceUnenc):
|
||||
|
||||
stdin_ok = True
|
||||
|
|
@ -466,7 +462,6 @@ class Mnemonic (SeedSourceUnenc):
|
|||
def _filename(self):
|
||||
return '%s[%s].%s' % (self.seed.sid,self.seed.length,self.ext)
|
||||
|
||||
|
||||
class SeedFile (SeedSourceUnenc):
|
||||
|
||||
stdin_ok = True
|
||||
|
|
@ -524,7 +519,6 @@ class SeedFile (SeedSourceUnenc):
|
|||
def _filename(self):
|
||||
return '%s[%s].%s' % (self.seed.sid,self.seed.length,self.ext)
|
||||
|
||||
|
||||
class Wallet (SeedSourceEnc):
|
||||
|
||||
fmt_codes = 'wallet','w'
|
||||
|
|
@ -687,7 +681,6 @@ class Wallet (SeedSourceEnc):
|
|||
self.ext
|
||||
)
|
||||
|
||||
|
||||
class Brainwallet (SeedSourceEnc):
|
||||
|
||||
stdin_ok = True
|
||||
|
|
@ -727,7 +720,6 @@ class Brainwallet (SeedSourceEnc):
|
|||
qmsg('Check this value against your records')
|
||||
return True
|
||||
|
||||
|
||||
class IncogWallet (SeedSourceEnc):
|
||||
|
||||
file_mode = 'binary'
|
||||
|
|
@ -883,7 +875,6 @@ to exit and re-run the program with the '--old-incog-fmt' option.
|
|||
else:
|
||||
return False
|
||||
|
||||
|
||||
class IncogWalletHex (IncogWallet):
|
||||
|
||||
file_mode = 'text'
|
||||
|
|
@ -904,7 +895,6 @@ class IncogWalletHex (IncogWallet):
|
|||
IncogWallet._format(self)
|
||||
self.fmt_data = pretty_hexdump(self.fmt_data)
|
||||
|
||||
|
||||
class IncogWalletHidden (IncogWallet):
|
||||
|
||||
desc = 'hidden incognito data'
|
||||
|
|
@ -931,7 +921,6 @@ harder to find, you're advised to choose a much larger file size than this.
|
|||
'dec_chk': ', hash preset, offset %s seed length'
|
||||
}
|
||||
|
||||
|
||||
def _get_hincog_params(self,wtype):
|
||||
p = getattr(opt,'hidden_incog_'+ wtype +'_params')
|
||||
a,b = p.split(',')
|
||||
|
|
|
|||
|
|
@ -23,6 +23,9 @@ term.py: Terminal-handling routines for the MMGen suite
|
|||
import os,struct
|
||||
from mmgen.common import *
|
||||
|
||||
CUR_SHOW = '\033[?25h'
|
||||
CUR_HIDE = '\033[?25l'
|
||||
|
||||
def _kb_hold_protect_unix():
|
||||
|
||||
fd = sys.stdin.fileno()
|
||||
|
|
|
|||
104
mmgen/tool.py
104
mmgen/tool.py
|
|
@ -71,7 +71,6 @@ cmd_data = OrderedDict([
|
|||
('mn_stats', ["wordlist [str='electrum']"]),
|
||||
('mn_printlist', ["wordlist [str='electrum']"]),
|
||||
|
||||
|
||||
('listaddresses',["addrs [str='']",'minconf [int=1]','showempty [bool=False]','pager [bool=False]','showbtcaddrs [bool=False]']),
|
||||
('getbalance', ['minconf [int=1]']),
|
||||
('txview', ['<{} TX file> [str]'.format(pnm),'pager [bool=False]','terse [bool=False]']),
|
||||
|
|
@ -102,7 +101,7 @@ cmd_help = """
|
|||
wif2hex - convert a private key from WIF to hex format
|
||||
|
||||
Wallet/TX operations (bitcoind must be running):
|
||||
getbalance - like 'bitcoind getbalance' but shows confirmed/unconfirmed,
|
||||
getbalance - like 'bitcoin-cli getbalance' but shows confirmed/unconfirmed,
|
||||
spendable/unspendable balances for individual {pnm} wallets
|
||||
listaddresses - list {pnm} addresses and their balances
|
||||
txview - show raw/signed {pnm} transaction in human-readable form
|
||||
|
|
@ -229,15 +228,12 @@ def are_equal(a,b,dtype=''):
|
|||
else: return a == b
|
||||
|
||||
def print_convert_results(indata,enc,dec,dtype):
|
||||
|
||||
error = (True,False)[are_equal(indata,dec,dtype)]
|
||||
|
||||
if error or opt.verbose:
|
||||
Msg('Input: %s' % repr(indata))
|
||||
Msg('Encoded data: %s' % repr(enc))
|
||||
Msg('Recoded data: %s' % repr(dec))
|
||||
else: Msg(enc)
|
||||
|
||||
if error:
|
||||
die(3,"Error! Recoded data doesn't match input!")
|
||||
|
||||
|
|
@ -364,23 +360,15 @@ def str2id6(s): Msg(make_chksum_6(''.join(s.split())))
|
|||
# List MMGen addresses and their balances:
|
||||
def listaddresses(addrs='',minconf=1,showempty=False,pager=False,showbtcaddrs=False):
|
||||
|
||||
# TODO - move some or all of this code to AddrList
|
||||
usr_addr_list = []
|
||||
if addrs:
|
||||
sid,idxs = split2(addrs,':')
|
||||
if not idxs:
|
||||
s1 = "'%s': invalid address argument\n"
|
||||
s2 = "Address argument format: <%s Seed ID>':'<index or range>"
|
||||
die(1, (s1+s2) % (addrs,g.proj_name))
|
||||
if not is_mmgen_seed_id(sid):
|
||||
die(1,'%s: invalid %s Seed ID' % (g.proj_name,sid))
|
||||
tmp = parse_addr_idxs(idxs)
|
||||
if not tmp: return False
|
||||
usr_addr_list = ['%s:%s' % (sid,i) for i in tmp]
|
||||
sid_s,idxs = split2(addrs,':')
|
||||
sid = SeedID(sid=sid_s)
|
||||
usr_addr_list = ['{}:{}'.format(sid,a) for a in AddrIdxList(idxs)]
|
||||
|
||||
c = bitcoin_connection()
|
||||
|
||||
addrs = {} # reusing variable name!
|
||||
from mmgen.obj import BTCAmt
|
||||
total = BTCAmt('0')
|
||||
for d in c.listunspent(0):
|
||||
mmaddr,comment = split2(d['account'])
|
||||
|
|
@ -392,7 +380,7 @@ def listaddresses(addrs='',minconf=1,showempty=False,pager=False,showbtcaddrs=Fa
|
|||
die(2,'duplicate BTC address ({}) for this MMGen address! ({})'.format(
|
||||
(d['address'], addrs[key][2])))
|
||||
else:
|
||||
addrs[key] = [BTCAmt('0'),comment,d['address']]
|
||||
addrs[key] = [BTCAmt('0'),MMGenAddrLabel(comment),BTCAddr(d['address'])]
|
||||
addrs[key][0] += d['amount']
|
||||
total += d['amount']
|
||||
|
||||
|
|
@ -407,45 +395,47 @@ def listaddresses(addrs='',minconf=1,showempty=False,pager=False,showbtcaddrs=Fa
|
|||
key = mmaddr.replace(':','_')
|
||||
if key not in addrs:
|
||||
if showbtcaddrs: save_a.append([acct])
|
||||
addrs[key] = [BTCAmt('0'),comment,'']
|
||||
addrs[key] = [BTCAmt('0'),MMGenAddrLabel(comment),'']
|
||||
|
||||
for acct,addr in zip(save_a,c.getaddressesbyaccount(save_a,batch=True)):
|
||||
if len(addr) != 1:
|
||||
die(2,"Account '%s' has more or less than one BTC address!" % addr)
|
||||
key = split2(acct[0])[0].replace(':','_')
|
||||
addrs[key][2] = addr[0]
|
||||
addrs[key][2] = BTCAddr(addr[0])
|
||||
|
||||
if not addrs:
|
||||
die(0,('No addresses with balances!','No tracked addresses!')[showempty])
|
||||
|
||||
fs = '%-{}s %-{}s %-{}s %s'.format(
|
||||
max(len(k) for k in addrs),
|
||||
(0,36)[showbtcaddrs],
|
||||
max(max(len(addrs[k][1]) for k in addrs) + 1,8) # pad 8 if no comments
|
||||
)
|
||||
fs = ('{mid} {lbl} {amt}','{mid} {addr} {lbl} {amt}')[showbtcaddrs]
|
||||
max_mmid_len = max(len(k) for k in addrs) or 10
|
||||
max_lbl_len = max(len(addrs[k][1]) for k in addrs) or 7
|
||||
out = [fs.format(
|
||||
mid=MMGenID.fmtc('MMGenID',width=max_mmid_len),
|
||||
addr=BTCAddr.fmtc('ADDRESS'),
|
||||
lbl=MMGenAddrLabel.fmtc('COMMENT',width=max_lbl_len),
|
||||
amt='BALANCE'
|
||||
)]
|
||||
|
||||
def s_mmgen(key): # TODO
|
||||
return '{}:{:>0{w}}'.format(w=AddrIdx.max_digits, *key.split('_'))
|
||||
|
||||
out = []
|
||||
old_sid = ''
|
||||
def s_mmgen(k): return '{:>0{w}}'.format(k,w=AddrIdx.max_digits+9) # TODO
|
||||
for k in sorted(addrs,key=s_mmgen):
|
||||
if out and k.split('_')[0] != out[-1].split(':')[0]: out.append('')
|
||||
baddr = ' ' + addrs[k][2] if showbtcaddrs else ''
|
||||
out.append(fs % (k.replace('_',':'), baddr, addrs[k][1], addrs[k][0].fmt('3.0',color=1)))
|
||||
if old_sid and old_sid != k[:8]: out.append('')
|
||||
old_sid = k[:8]
|
||||
out.append(fs.format(
|
||||
mid=MMGenID(k.replace('_',':')).fmt(width=max_mmid_len,color=True),
|
||||
addr=(addrs[k][2].fmt(color=True) if showbtcaddrs else None),
|
||||
lbl=addrs[k][1].fmt(width=max_lbl_len,color=True,nullrepl='-'),
|
||||
amt=addrs[k][0].fmt('3.0',color=True)))
|
||||
|
||||
o = (fs + '\n%s\nTOTAL: %s BTC') % (
|
||||
'ADDRESS','','COMMENT',' BALANCE', '\n'.join(out), total.hl()
|
||||
)
|
||||
out.append('\nTOTAL: %s BTC' % total.hl(color=True))
|
||||
o = '\n'.join(out)
|
||||
if pager: do_pager(o)
|
||||
else: Msg(o)
|
||||
|
||||
|
||||
def getbalance(minconf=1):
|
||||
|
||||
accts = {}
|
||||
us = bitcoin_connection().listunspent(0)
|
||||
# pp_die(us)
|
||||
for d in us:
|
||||
for d in bitcoin_connection().listunspent(0):
|
||||
ma = split2(d['account'])[0]
|
||||
keys = ['TOTAL']
|
||||
if d['spendable']: keys += ['SPENDABLE']
|
||||
|
|
@ -477,30 +467,16 @@ def twview(pager=False,reverse=False,wide=False,sort='age'):
|
|||
out = tw.format_for_printing(color=True) if wide else tw.format_for_display()
|
||||
do_pager(out) if pager else sys.stdout.write(out)
|
||||
|
||||
def add_label(mmaddr,label,remove=False):
|
||||
if not is_mmgen_id(mmaddr):
|
||||
die(1,'{a}: not a valid {pnm} address'.format(pnm=pnm,a=mmaddr))
|
||||
MMGenAddrLabel(label) # Exits on failure
|
||||
def add_label(mmaddr,label):
|
||||
from mmgen.tw import MMGenTrackingWallet
|
||||
if MMGenTrackingWallet.add_label(mmaddr,label): # returns on failure
|
||||
s = '{pnm} address {a} in tracking wallet'.format(a=mmaddr,pnm=pnm)
|
||||
if label: msg("Added label '{}' for {}".format(label,s))
|
||||
else: msg('Removed label for {}'.format(s))
|
||||
else:
|
||||
die(1,'Label could not be %s' % ('removed','added')[bool(label)])
|
||||
|
||||
from mmgen.addr import AddrData
|
||||
btcaddr = AddrData(source='tw').mmaddr2btcaddr(mmaddr)
|
||||
|
||||
if not btcaddr:
|
||||
die(1,'{pnm} address {a} not found in tracking wallet'.format(
|
||||
pnm=pnm,a=mmaddr))
|
||||
|
||||
c = bitcoin_connection()
|
||||
try:
|
||||
l = ' ' + label if label else ''
|
||||
c.importaddress(btcaddr,mmaddr+l,False) # addr,label,rescan,p2sh
|
||||
except:
|
||||
die(1,'Unable to add label')
|
||||
|
||||
s = '{pnm} address {a} in tracking wallet'.format(a=mmaddr,pnm=pnm)
|
||||
if remove: msg('Removed label from {}'.format(s))
|
||||
else: msg("Added label '{}' for {}".format(label,s))
|
||||
|
||||
def remove_label(mmaddr): add_label(mmaddr,'',remove=True)
|
||||
def remove_label(mmaddr): add_label(mmaddr,'')
|
||||
|
||||
def addrfile_chksum(infile):
|
||||
from mmgen.addr import AddrList
|
||||
|
|
@ -544,7 +520,6 @@ def wif2hex(wif,compressed=False):
|
|||
def hex2wif(hexpriv,compressed=False):
|
||||
Msg(bitcoin.hextowif(hexpriv,compressed))
|
||||
|
||||
|
||||
def encrypt(infile,outfile='',hash_preset=''):
|
||||
data = get_data_from_file(infile,'data for encryption',binary=True)
|
||||
enc_d = mmgen_encrypt(data,'user data',hash_preset)
|
||||
|
|
@ -553,7 +528,6 @@ def encrypt(infile,outfile='',hash_preset=''):
|
|||
|
||||
write_data_to_file(outfile,enc_d,'encrypted data',binary=True)
|
||||
|
||||
|
||||
def decrypt(infile,outfile='',hash_preset=''):
|
||||
enc_d = get_data_from_file(infile,'encrypted data',binary=True)
|
||||
while True:
|
||||
|
|
@ -568,7 +542,6 @@ def decrypt(infile,outfile='',hash_preset=''):
|
|||
|
||||
write_data_to_file(outfile,dec_d,'decrypted data',binary=True)
|
||||
|
||||
|
||||
def find_incog_data(filename,iv_id,keep_searching=False):
|
||||
ivsize,bsize,mod = g.aesctr_iv_len,4096,4096*8
|
||||
n,carry = 0,' '*ivsize
|
||||
|
|
@ -594,7 +567,6 @@ def find_incog_data(filename,iv_id,keep_searching=False):
|
|||
msg('')
|
||||
os.close(f)
|
||||
|
||||
|
||||
def rand2file(outfile, nbytes, threads=4, silent=False):
|
||||
nbytes = parse_nbytes(nbytes)
|
||||
from Crypto import Random
|
||||
|
|
|
|||
142
mmgen/tw.py
142
mmgen/tw.py
|
|
@ -46,7 +46,26 @@ No spendable outputs found! Import addresses with balances into your
|
|||
watch-only wallet using '{}-addrimport' and then re-run this program.
|
||||
""".strip().format(g.proj_name)
|
||||
}
|
||||
sort_keys = 'addr','age','amt','txid','mmid'
|
||||
|
||||
def __init__(self):
|
||||
self.unspent = []
|
||||
self.fmt_display = ''
|
||||
self.fmt_print = ''
|
||||
self.cols = None
|
||||
self.reverse = False
|
||||
self.group = False
|
||||
self.show_days = True
|
||||
self.show_mmid = True
|
||||
self.get_data()
|
||||
self.sort_key = 'age'
|
||||
self.do_sort()
|
||||
self.total = self.get_total_btc()
|
||||
|
||||
def get_total_btc(self):
|
||||
return sum([i.amt for i in self.unspent])
|
||||
|
||||
def get_data(self):
|
||||
if g.bogus_wallet_data: # for debugging purposes only
|
||||
us_rpc = eval(get_data_from_file(g.bogus_wallet_data))
|
||||
else:
|
||||
|
|
@ -56,27 +75,15 @@ watch-only wallet using '{}-addrimport' and then re-run this program.
|
|||
|
||||
if not us_rpc: die(2,self.wmsg['no_spendable_outputs'])
|
||||
for o in us_rpc:
|
||||
o['mmid'],o['label'] = parse_tw_acct_label(o['account'])
|
||||
o['mmid'],o['label'] = parse_tw_acct_label(o['account']) if 'account' in o else ('','')
|
||||
o['days'] = int(o['confirmations'] * g.mins_per_block / (60*24))
|
||||
o['amt'] = o['amount'] # TODO
|
||||
o['addr'] = o['address']
|
||||
o['confs'] = o['confirmations']
|
||||
us = [MMGenTWOutput(**dict([(k,v) for k,v in o.items() if k in MMGenTWOutput.attrs and o[k] not in (None,'')])) for o in us_rpc]
|
||||
# die(1,''.join([str(i)+'\n' for i in us]))
|
||||
self.unspent = [MMGenTWOutput(**dict([(k,v) for k,v in o.items() if k in MMGenTWOutput.attrs and o[k] not in (None,'')])) for o in us_rpc]
|
||||
# die(1,''.join([pp_format(i)+'\n' for i in us_rpc]))
|
||||
# die(1,''.join([str(i)+'\n' for i in self.unspent]))
|
||||
|
||||
self.unspent = us
|
||||
self.fmt_display = ''
|
||||
self.fmt_print = ''
|
||||
self.cols = None
|
||||
self.reverse = False
|
||||
self.group = False
|
||||
self.show_days = True
|
||||
self.show_mmid = True
|
||||
self.do_sort('age')
|
||||
self.total = sum([i.amt for i in self.unspent])
|
||||
|
||||
sort_keys = 'addr','age','amt','txid','mmid'
|
||||
def s_addr(self,i): return i.addr
|
||||
def s_age(self,i): return 0 - i.confs
|
||||
def s_amt(self,i): return i.amt
|
||||
|
|
@ -87,18 +94,20 @@ watch-only wallet using '{}-addrimport' and then re-run this program.
|
|||
*i.mmid.split(':'), w=AddrIdx.max_digits)
|
||||
else: return 'G' + (i.label or '')
|
||||
|
||||
def do_sort(self,key,reverse=None):
|
||||
def do_sort(self,key=None,reverse=None):
|
||||
if not key: key = self.sort_key
|
||||
assert key
|
||||
self.sort_key = key
|
||||
if key not in self.sort_keys:
|
||||
fs = "'{}': invalid sort key. Valid keys: [{}]"
|
||||
die(2,fs.format(key,' '.join(self.sort_keys)))
|
||||
if reverse == None: reverse = self.reverse
|
||||
self.sort = key
|
||||
self.unspent.sort(key=getattr(self,'s_'+key),reverse=reverse)
|
||||
|
||||
def sort_info(self,include_group=True):
|
||||
ret = ([],['Reverse'])[self.reverse]
|
||||
ret.append(self.sort.capitalize().replace('Mmid','MMGenID'))
|
||||
if include_group and self.group and (self.sort in ('addr','txid','mmid')):
|
||||
ret.append(self.sort_key.capitalize().replace('Mmid','MMGenID'))
|
||||
if include_group and self.group and (self.sort_key in ('addr','txid','mmid')):
|
||||
ret.append('Grouped')
|
||||
return ret
|
||||
|
||||
|
|
@ -122,7 +131,7 @@ watch-only wallet using '{}-addrimport' and then re-run this program.
|
|||
if i.label == None: i.label = ''
|
||||
i.skip = ''
|
||||
|
||||
mmid_w = max(len(i.mmid or '') for i in unsp)
|
||||
mmid_w = max(len(i.mmid or '') for i in unsp) or 10
|
||||
max_acct_len = max([len((i.mmid or '')+i.label)+1 for i in unsp])
|
||||
addr_w = min(34+((1+max_acct_len) if self.show_mmid else 0),self.cols-46) + 6
|
||||
acct_w = min(max_acct_len, max(24,int(addr_w-10)))
|
||||
|
|
@ -138,10 +147,10 @@ watch-only wallet using '{}-addrimport' and then re-run this program.
|
|||
'Amt(BTC) ',
|
||||
('Conf.','Age(d)')[self.show_days])
|
||||
|
||||
if self.group and (self.sort in ('addr','txid','mmid')):
|
||||
if self.group and (self.sort_key in ('addr','txid','mmid')):
|
||||
for a,b in [(unsp[i],unsp[i+1]) for i in range(len(unsp)-1)]:
|
||||
for k in ('addr','txid','mmid'):
|
||||
if self.sort == k and getattr(a,k) == getattr(b,k):
|
||||
if self.sort_key == k and getattr(a,k) == getattr(b,k):
|
||||
b.skip = (k,'addr')[k=='mmid']
|
||||
|
||||
hdr_fmt = 'UNSPENT OUTPUTS (sort order: %s) Total BTC: %s'
|
||||
|
|
@ -156,7 +165,7 @@ watch-only wallet using '{}-addrimport' and then re-run this program.
|
|||
addr_out = '%s %s' % (
|
||||
type(i.addr).fmtc(addr_dots,width=btaddr_w,color=True) if i.skip == 'addr' \
|
||||
else i.addr.fmt(width=btaddr_w,color=True),
|
||||
'{} {}'.format(mmid_disp,i.label.fmt(width=label_w,color=True))
|
||||
'{} {}'.format(mmid_disp,i.label.fmt(width=label_w,color=True) if label_w > 0 else '')
|
||||
)
|
||||
else:
|
||||
addr_out = type(i.addr).fmtc(addr_dots,width=addr_w,color=True) if i.skip=='addr' \
|
||||
|
|
@ -177,12 +186,16 @@ watch-only wallet using '{}-addrimport' and then re-run this program.
|
|||
out = [fs % ('Num','Tx ID,Vout','Address'.ljust(34),'MMGen ID'.ljust(15),
|
||||
'Amount(BTC)','Conf.','Age(d)', 'Label')]
|
||||
|
||||
max_lbl_len = max(len(i.label) for i in self.unspent if i.label) or 1
|
||||
for n,i in enumerate(self.unspent):
|
||||
addr = '=' if i.skip == 'addr' and self.group else i.addr.fmt(color=color)
|
||||
tx = ' ' * 63 + '=' if i.skip == 'txid' and self.group else str(i.txid)
|
||||
s = fs % (str(n+1)+')', tx+','+str(i.vout),addr,
|
||||
(i.mmid.fmt(14,color=color) if i.mmid else ''.ljust(14)),
|
||||
i.amt.fmt(color=color),i.confs,i.days,i.label.hl(color=color) if i.label else '')
|
||||
(i.mmid.fmt(width=14,color=color) if i.mmid else
|
||||
MMGenID.fmtc('',width=14,nullrepl='-',color=color)),
|
||||
i.amt.fmt(color=color),i.confs,i.days,
|
||||
i.label.hl(color=color) if i.label else
|
||||
MMGenAddrLabel.fmtc('',color=color,nullrepl='-',width=max_lbl_len))
|
||||
out.append(s.rstrip())
|
||||
|
||||
fs = 'Unspent outputs ({} UTC)\nSort order: {}\n\n{}\n\nTotal BTC: {}\n'
|
||||
|
|
@ -197,6 +210,30 @@ watch-only wallet using '{}-addrimport' and then re-run this program.
|
|||
fs = '\nTotal unspent: %s BTC (%s outputs)'
|
||||
msg(fs % (self.total.hl(),len(self.unspent)))
|
||||
|
||||
def get_idx_and_label_from_user(self):
|
||||
msg('')
|
||||
while True:
|
||||
ret = my_raw_input("Enter unspent output number (or 'q' to return to main menu): ")
|
||||
if ret == 'q': return None,None
|
||||
n = AddrIdx(ret,on_fail='silent') # hacky way to test and convert to integer
|
||||
if not n or n < 1 or n > len(self.unspent):
|
||||
msg('Choice must be a single number between 1 and %s' % len(self.unspent))
|
||||
elif not self.unspent[n-1].mmid:
|
||||
msg('Address #%s is not an %s address. No label can be added to it' %
|
||||
(n,g.proj_name))
|
||||
else:
|
||||
while True:
|
||||
s = my_raw_input("Enter label text (or 'q' to return to main menu): ")
|
||||
if s == 'q':
|
||||
return None,None
|
||||
elif s == '':
|
||||
if keypress_confirm(
|
||||
"Removing label for address #%s. Is this what you want?" % n):
|
||||
return n,s
|
||||
elif s:
|
||||
if MMGenAddrLabel(s,on_fail='return'):
|
||||
return n,s
|
||||
|
||||
def view_and_sort(self):
|
||||
from mmgen.term import do_pager
|
||||
prompt = """
|
||||
|
|
@ -206,27 +243,37 @@ Display options: show [D]ays, [g]roup, show [m]mgen addr, r[e]draw screen
|
|||
self.display()
|
||||
msg(prompt)
|
||||
|
||||
p = "('q' = quit sorting, 'p' = print to file, 'v' = pager view, 'w' = wide view): "
|
||||
p = "'q'=quit view, 'p'=print to file, 'v'=pager view, 'w'=wide view, 'l'=add label:\b"
|
||||
while True:
|
||||
reply = get_char(p, immed_chars='atDdAMrgmeqpvw')
|
||||
if reply == 'a': self.do_sort('amt')
|
||||
elif reply == 't': self.do_sort('txid')
|
||||
elif reply == 'D': self.show_days = not self.show_days
|
||||
elif reply == 'd': self.do_sort('addr')
|
||||
elif reply == 'A': self.do_sort('age')
|
||||
elif reply == 'M': self.do_sort('mmid'); self.show_mmid = True
|
||||
elif reply == 'r': self.unspent.reverse(); self.reverse = not self.reverse
|
||||
elif reply == 'd': self.do_sort('addr')
|
||||
elif reply == 'D': self.show_days = not self.show_days
|
||||
elif reply == 'e': msg('\n%s\n%s\n%s' % (self.fmt_display,prompt,p))
|
||||
elif reply == 'g': self.group = not self.group
|
||||
elif reply == 'l':
|
||||
idx,lbl = self.get_idx_and_label_from_user()
|
||||
if idx:
|
||||
e = self.unspent[idx-1]
|
||||
if type(self).add_label(e.mmid,lbl,addr=e.addr):
|
||||
self.get_data()
|
||||
self.do_sort()
|
||||
msg('%s\n%s\n%s' % (self.fmt_display,prompt,p))
|
||||
else:
|
||||
msg('Label could not be added\n%s\n%s' % (prompt,p))
|
||||
elif reply == 'M': self.do_sort('mmid'); self.show_mmid = True
|
||||
elif reply == 'm': self.show_mmid = not self.show_mmid
|
||||
elif reply == 'e': msg("\n%s\n%s\n%s" % (self.fmt_display,prompt,p))
|
||||
elif reply == 'q': return self.unspent
|
||||
elif reply == 'p':
|
||||
of = 'listunspent[%s].out' % ','.join(self.sort_info(include_group=False))
|
||||
msg('')
|
||||
of = 'listunspent[%s].out' % ','.join(self.sort_info(include_group=False)).lower()
|
||||
write_data_to_file(of,self.format_for_printing(),'unspent outputs listing')
|
||||
m = yellow("Data written to '%s'" % of)
|
||||
msg('\n%s\n%s\n\n%s' % (self.fmt_display,m,prompt))
|
||||
continue
|
||||
elif reply == 'q': return self.unspent
|
||||
elif reply == 'r': self.unspent.reverse(); self.reverse = not self.reverse
|
||||
elif reply == 't': self.do_sort('txid')
|
||||
elif reply == 'v':
|
||||
do_pager(self.fmt_display)
|
||||
continue
|
||||
|
|
@ -240,3 +287,30 @@ Display options: show [D]ays, [g]roup, show [m]mgen addr, r[e]draw screen
|
|||
msg('\n')
|
||||
self.display()
|
||||
msg(prompt)
|
||||
|
||||
# returns on failure
|
||||
@classmethod
|
||||
def add_label(cls,mmaddr,label='',addr=None):
|
||||
mmaddr = MMGenID(mmaddr)
|
||||
|
||||
if addr: # called from view_and_sort()
|
||||
if not BTCAddr(addr,on_fail='return'): return False
|
||||
else:
|
||||
from mmgen.addr import AddrData
|
||||
addr = AddrData(source='tw').mmaddr2btcaddr(mmaddr)
|
||||
if not addr:
|
||||
msg('{} address {} not found in tracking wallet'.format(g.proj_name,mmaddr))
|
||||
return False
|
||||
|
||||
label = MMGenAddrLabel(label,on_fail='return')
|
||||
if not label and label != '': return False
|
||||
|
||||
acct = mmaddr + (' ' + label if label else '') # label is ASCII for now
|
||||
# return on failure - args: addr,label,rescan,p2sh
|
||||
ret = bitcoin_connection().importaddress(addr,acct,False,on_fail='return')
|
||||
from mmgen.rpc import rpc_error,rpc_errmsg
|
||||
if rpc_error(ret): msg('From bitcoind: ' + rpc_errmsg(ret))
|
||||
return not rpc_error(ret)
|
||||
|
||||
@classmethod
|
||||
def remove_label(cls,mmaddr): cls.add_label(mmaddr,'')
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
util.py: Low-level routines imported by other modules for the MMGen suite
|
||||
util.py: Low-level routines imported by other modules in the MMGen suite
|
||||
"""
|
||||
|
||||
import sys,os,time,stat,re
|
||||
|
|
@ -136,12 +136,10 @@ def vmsg(s):
|
|||
if opt.verbose: sys.stderr.write(s + '\n')
|
||||
def vmsg_r(s):
|
||||
if opt.verbose: sys.stderr.write(s)
|
||||
|
||||
def Vmsg(s):
|
||||
if opt.verbose: sys.stdout.write(s + '\n')
|
||||
def Vmsg_r(s):
|
||||
if opt.verbose: sys.stdout.write(s)
|
||||
|
||||
def dmsg(s):
|
||||
if opt.debug: sys.stdout.write(s + '\n')
|
||||
|
||||
|
|
@ -190,7 +188,7 @@ def split_into_cols(col_wid,s):
|
|||
return ' '.join([s[col_wid*i:col_wid*(i+1)]
|
||||
for i in range(len(s)/col_wid+1)]).rstrip()
|
||||
|
||||
def capfirst(s):
|
||||
def capfirst(s): # different from str.capitalize() - doesn't downcase any uc in string
|
||||
return s if len(s) == 0 else s[0].upper() + s[1:]
|
||||
|
||||
def decode_timestamp(s):
|
||||
|
|
@ -338,7 +336,6 @@ def open_file_or_exit(filename,mode):
|
|||
die(2,"Unable to open file '%s' for %s" % (filename,op))
|
||||
return f
|
||||
|
||||
|
||||
def check_file_type_and_access(fname,ftype,blkdev_ok=False):
|
||||
|
||||
a = ((os.R_OK,'read'),(os.W_OK,'writ'))
|
||||
|
|
@ -372,46 +369,9 @@ def check_outfile(f,blkdev_ok=False):
|
|||
return check_file_type_and_access(f,'output file',blkdev_ok=blkdev_ok)
|
||||
def check_outdir(f):
|
||||
return check_file_type_and_access(f,'output directory')
|
||||
|
||||
def make_full_path(outdir,outfile):
|
||||
return os.path.normpath(os.path.join(outdir, os.path.basename(outfile)))
|
||||
|
||||
def _validate_addr_num(n):
|
||||
from mmgen.tx import is_mmgen_idx
|
||||
if is_mmgen_idx(n):
|
||||
return int(n)
|
||||
else:
|
||||
msg("'%s': invalid %s address index" % (n,g.proj_name))
|
||||
return False
|
||||
|
||||
def parse_addr_idxs(arg,sep=','): # TODO - delete
|
||||
|
||||
ret = []
|
||||
|
||||
for i in (arg.split(sep)):
|
||||
|
||||
j = i.split('-')
|
||||
|
||||
if len(j) == 1:
|
||||
i = _validate_addr_num(i)
|
||||
if not i: return False
|
||||
ret.append(i)
|
||||
elif len(j) == 2:
|
||||
beg = _validate_addr_num(j[0])
|
||||
if not beg: return False
|
||||
end = _validate_addr_num(j[1])
|
||||
if not end: return False
|
||||
if end < beg:
|
||||
msg("'%s-%s': invalid range (end is less than beginning)" % (beg,end))
|
||||
return False
|
||||
ret.extend(range(beg,end+1))
|
||||
else:
|
||||
msg("'%s': invalid address range argument" % i)
|
||||
return False
|
||||
|
||||
return sorted(set(ret))
|
||||
|
||||
|
||||
def get_new_passphrase(desc,passchg=False):
|
||||
|
||||
w = '{}passphrase for {}'.format(('','new ')[bool(passchg)], desc)
|
||||
|
|
@ -586,17 +546,13 @@ def get_data_from_file(infile,desc='data',dash=False,silent=False,binary=False):
|
|||
f.close()
|
||||
return data
|
||||
|
||||
passwd_file_used = False
|
||||
|
||||
def pwfile_reuse_warning():
|
||||
global passwd_file_used
|
||||
if passwd_file_used:
|
||||
if 'passwd_file_used' in globals():
|
||||
qmsg("Reusing passphrase from file '%s' at user request" % opt.passwd_file)
|
||||
return True
|
||||
passwd_file_used = True
|
||||
globals()['passwd_file_used'] = True
|
||||
return False
|
||||
|
||||
|
||||
def get_mmgen_passphrase(desc,passchg=False):
|
||||
prompt ='Enter {}passphrase for {}: '.format(('','old ')[bool(passchg)],desc)
|
||||
if opt.passwd_file:
|
||||
|
|
@ -605,28 +561,6 @@ def get_mmgen_passphrase(desc,passchg=False):
|
|||
else:
|
||||
return ' '.join(get_words_from_user(prompt))
|
||||
|
||||
def get_bitcoind_passphrase(prompt):
|
||||
if opt.passwd_file:
|
||||
pwfile_reuse_warning()
|
||||
return get_data_from_file(opt.passwd_file,'passphrase').strip('\r\n')
|
||||
else:
|
||||
return my_raw_input(prompt, echo=opt.echo_passphrase)
|
||||
|
||||
|
||||
def get_hash_preset_from_user(hp=g.hash_preset,desc='data'):
|
||||
p = """Enter hash preset for %s,
|
||||
or hit ENTER to accept the default value ('%s'): """ % (desc,hp)
|
||||
while True:
|
||||
ret = my_raw_input(p)
|
||||
if ret:
|
||||
if ret in g.hash_presets.keys(): return ret
|
||||
else:
|
||||
msg('Invalid input. Valid choices are %s' %
|
||||
', '.join(sorted(g.hash_presets.keys())))
|
||||
continue
|
||||
else: return hp
|
||||
|
||||
|
||||
def my_raw_input(prompt,echo=True,insert_txt='',use_readline=True):
|
||||
|
||||
try: import readline
|
||||
|
|
@ -651,7 +585,6 @@ def my_raw_input(prompt,echo=True,insert_txt='',use_readline=True):
|
|||
|
||||
return reply.strip()
|
||||
|
||||
|
||||
def keypress_confirm(prompt,default_yes=False,verbose=False):
|
||||
|
||||
from mmgen.term import get_char
|
||||
|
|
@ -670,7 +603,6 @@ def keypress_confirm(prompt,default_yes=False,verbose=False):
|
|||
if verbose: msg('\nInvalid reply')
|
||||
else: msg_r('\r')
|
||||
|
||||
|
||||
def prompt_and_get_char(prompt,chars,enter_ok=False,verbose=False):
|
||||
|
||||
from mmgen.term import get_char
|
||||
|
|
@ -685,7 +617,6 @@ def prompt_and_get_char(prompt,chars,enter_ok=False,verbose=False):
|
|||
if verbose: msg('\nInvalid reply')
|
||||
else: msg_r('\r')
|
||||
|
||||
|
||||
def do_license_msg(immed=False):
|
||||
|
||||
if opt.quiet or g.no_license: return
|
||||
|
|
@ -708,7 +639,6 @@ def do_license_msg(immed=False):
|
|||
msg_r('\r')
|
||||
msg('')
|
||||
|
||||
|
||||
def get_bitcoind_cfg_options(cfg_keys):
|
||||
|
||||
cfg_file = os.path.join(get_homedir(), get_datadir(), 'bitcoin.conf')
|
||||
|
|
|
|||
|
|
@ -1176,13 +1176,13 @@ class MMGenTestSuite(object):
|
|||
if opt.verbose or opt.exact_output:
|
||||
sys.stderr.write(green('Generating fake tracking wallet info\n'))
|
||||
silence()
|
||||
from mmgen.addr import AddrList,AddrData
|
||||
from mmgen.addr import AddrList,AddrData,AddrIdxList
|
||||
tx_data,ad = {},AddrData()
|
||||
for s in sources:
|
||||
afile = get_file_with_ext('addrs',cfgs[s]['tmpdir'])
|
||||
ai = AddrList(afile)
|
||||
ad.add(ai)
|
||||
aix = parse_addr_idxs(cfgs[s]['addr_idx_list'])
|
||||
aix = AddrIdxList(fmt_str=cfgs[s]['addr_idx_list'])
|
||||
if len(aix) != addrs_per_wallet:
|
||||
errmsg(red('Address index list length != %s: %s' %
|
||||
(addrs_per_wallet,repr(aix))))
|
||||
|
|
@ -1244,8 +1244,8 @@ class MMGenTestSuite(object):
|
|||
|
||||
for num in tx_data:
|
||||
t.expect('Continue anyway? (y/N): ','y')
|
||||
t.expect(r"'q' = quit sorting, .*?: ",'M', regex=True)
|
||||
t.expect(r"'q' = quit sorting, .*?: ",'q', regex=True)
|
||||
t.expect(r"'q'=quit view, .*?:.",'M', regex=True)
|
||||
t.expect(r"'q'=quit view, .*?:.",'q', regex=True)
|
||||
outputs_list = [(addrs_per_wallet+1)*i + 1 for i in range(len(tx_data))]
|
||||
if non_mmgen_input: outputs_list.append(len(tx_data)*(addrs_per_wallet+1) + 1)
|
||||
t.expect('Enter a range or space-separated list of outputs to spend: ',
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue