rpc.py: new calls() method; tw.py: batch date RPC calls; related fixes/cleanups

This commit is contained in:
The MMGen Project 2020-05-01 08:28:22 +00:00
commit 744c7b0d54
Signed by: mmgen
GPG key ID: 3F8B1861E32B7DA2
3 changed files with 58 additions and 51 deletions

View file

@ -164,8 +164,8 @@ common_opts_data = {
--, --no-license Suppress the GPL license prompt
--, --rpc-host=h Communicate with {dn} running on host 'h'
--, --rpc-port=p Communicate with {dn} listening on port 'p'
--, --rpc-user=user Override 'rpcuser' in {pn}.conf
--, --rpc-password=pass Override 'rpcpassword' in {pn}.conf
--, --rpc-user=user Override 'rpc_user' in mmgen.cfg
--, --rpc-password=pass Override 'rpc_password' in mmgen.cfg
--, --monero-wallet-rpc-host=host Override 'monero_wallet_rpc_host' in mmgen.cfg
--, --monero-wallet-rpc-user=user Override 'monero_wallet_rpc_user' in mmgen.cfg
--, --monero-wallet-rpc-password=pass Override 'monero_wallet_rpc_password' in mmgen.cfg

View file

@ -46,13 +46,11 @@ class RPCConnection(MMGenObject):
except:
raise SocketError('Unable to connect to {}:{}'.format(host,port))
if not self.auth:
if user and passwd: # user/pass overrides cookie
pass
elif user and passwd:
self.auth_str = '{}:{}'.format(user,passwd)
elif auth_cookie:
self.auth_str = auth_cookie
else:
user,passwd = auth_cookie.split(':')
elif self.auth:
msg('Error: no {} RPC authentication method found'.format(g.proto.name.capitalize()))
if passwd: die(1,"'rpcuser' entry not found in {}.conf or mmgen.cfg".format(g.proto.name))
elif user: die(1,"'rpcpassword' entry not found in {}.conf or mmgen.cfg".format(g.proto.name))
@ -67,10 +65,11 @@ class RPCConnection(MMGenObject):
pnm=g.proj_name))
if self.auth:
fs = ' RPC AUTHORIZATION data ==> raw: [{}]\n{:>31}enc: [Basic {}]\n'
as_enc = base64.b64encode(self.auth_str.encode()).decode()
dmsg_rpc(fs.format(self.auth_str,'',as_enc))
self.http_hdrs.update({ 'Host':host, 'Authorization':'Basic {}'.format(as_enc) })
fs = ' RPC AUTHORIZATION data ==> raw: [{}]\n{:>31}enc: [{}]\n'
auth_str = f'{user}:{passwd}'
auth_str_b64 = 'Basic ' + base64.b64encode(auth_str.encode()).decode()
dmsg_rpc(fs.format(auth_str,'',auth_str_b64))
self.http_hdrs.update({ 'Host': host, 'Authorization': auth_str_b64 })
self.host = host
self.port = port
@ -81,6 +80,20 @@ class RPCConnection(MMGenObject):
exec('{c}.{m} = lambda self,*args,**kwargs: self.request("{m}",*args,**kwargs)'.format(
c=type(self).__name__,m=method))
def calls(self,method,args_list):
"""
Perform a list of RPC calls, returning results in a list
Can be called two ways:
1) method = methodname, args_list = [args_tuple1, args_tuple2,...]
2) method = None, args_list = [(methodname1,args_tuple1), (methodname2,args_tuple2), ...]
"""
cmd_list = args_list if method == None else tuple(zip([method] * len(args_list), args_list))
if True:
return [self.request(method,*params) for method,params in cmd_list]
# Normal mode: call with arg list unrolled, exactly as with cli
# Batch mode: call with list of arg lists as first argument
# kwargs are for local use and are not passed to server
@ -199,7 +212,7 @@ class RPCConnection(MMGenObject):
for k,v in self.http_hdrs.items():
exec_cmd += ['--header', '{}: {}'.format(k,v)]
if self.auth:
exec_cmd += ['--user', self.auth_str]
exec_cmd += ['--user', self.user + ':' + self.passwd]
exec_cmd += ['http://{}:{}/'.format(self.host,self.port)]
cp = run(exec_cmd,stdout=PIPE,check=True)
@ -233,6 +246,7 @@ class RPCConnection(MMGenObject):
'getblockcount',
'getblockhash',
'getblockheader',
'getblockstats', # mmgen-node-tools
'getmempoolinfo',
'getmempoolentry',
'getnettotals',

View file

@ -34,33 +34,28 @@ def get_tw_label(s):
except BadTwComment: raise
except: return None
_date2days = lambda date: (g.rpch.cur_date - date) // 86400
_date_formatter = {
'days': lambda secs: (g.rpch.cur_date - secs) // 86400,
'date': lambda secs: '{}-{:02}-{:02}'.format(*time.gmtime(secs)[:3])[2:],
'date_time': lambda secs: '{}-{:02}-{:02} {:02}:{:02}'.format(*time.gmtime(secs)[:5]),
}
_confs2date_approx = lambda o: g.rpch.cur_date - int(g.proto.avg_bdi * (o.confs - 1))
_confs2date_exact = lambda o: (
# g.rpch.getblockheader(g.rpch.getblockhash(g.rpch.blockcount - (o.confs - 1)))['time']
g.rpch.gettransaction(o.txid)['blocktime'] # same as above, differs from 'time'
if o.confs
else g.rpch.cur_date )
def _set_dates(us):
if us and us[0].date is None:
# 'blocktime' differs from 'time', is same as getblockheader['time']
dates = [o['blocktime'] for o in g.rpch.calls('gettransaction',[(o.txid,) for o in us])]
for o,date in zip(us,dates):
o.date = date
if os.getenv('MMGEN_BOGUS_WALLET_DATA'):
# 1831006505 (09 Jan 2028) = projected time of block 1000000
_date2days = lambda date: (1831006505 - date) // 86400
_date_formatter['days'] = lambda date: (1831006505 - date) // 86400
_confs2date_approx = lambda o: 1831006505 - (10 * 60 * (o.confs - 1))
_confs2date_exact = lambda o: 1831006505 - int(9.7 * 60 * (o.confs - 1))
def _format_date(secs):
t = time.gmtime(secs)
return '{}-{:02}-{:02}'.format(str(t.tm_year)[2:],t.tm_mon,t.tm_mday)
def _format_date_time(secs):
t = time.gmtime(secs)
return '{}-{:02}-{:02} {:02}:{:02}'.format(
t.tm_year,
t.tm_mon,
t.tm_mday,
t.tm_hour,
t.tm_min,
)
def _set_dates(us):
for o in us:
o.date = 1831006505 - int(9.7 * 60 * (o.confs - 1))
class TwUnspentOutputs(MMGenObject):
@ -86,7 +81,8 @@ Actions: [q]uit view, [p]rint to file, pager [v]iew, [w]ide view, add [l]abel:
'q':'a_quit','p':'a_print','v':'a_view','w':'a_view_wide','l':'a_lbl_add' }
col_adj = 38
age_fmts = ('confs','block','days','date','date_time')
age_fmts_ia = ('confs','block','days','date')
age_fmts_date_dependent = ('days','date','date_time')
age_fmts_interactive = ('confs','block','days','date')
_age_fmt = 'confs'
age_precs = ('approx','exact')
age_prec = 'approx'
@ -227,6 +223,8 @@ watch-only wallet using '{}-addrimport' and then re-run this program.
def format_for_display(self):
unsp = self.unspent
if self.age_fmt in self.age_fmts_date_dependent and self.age_prec == 'exact':
_set_dates(unsp)
self.set_term_columns()
# allow for 7-digit confirmation nums
@ -301,7 +299,8 @@ watch-only wallet using '{}-addrimport' and then re-run this program.
return self.fmt_display
def format_for_printing(self,color=False,show_confs=True):
if self.age_fmt in self.age_fmts_date_dependent and self.age_prec == 'exact':
_set_dates(self.unspent)
addr_w = max(len(i.addr) for i in self.unspent)
mmid_w = max(len(('',i.twmmid)[i.twmmid.type=='mmgen']) for i in self.unspent) or 12 # DEADBEEF:S:1
amt_w = g.proto.coin_amt.max_prec + 5
@ -404,7 +403,7 @@ watch-only wallet using '{}-addrimport' and then re-run this program.
self.do_sort(action[2:])
if action == 's_twmmid': self.show_mmid = True
elif action == 'd_days':
af = self.age_fmts_ia
af = self.age_fmts_interactive
self.age_fmt = af[(af.index(self.age_fmt) + 1) % len(af)]
elif action == 'd_mmid': self.show_mmid = not self.show_mmid
elif action == 'd_group':
@ -467,22 +466,13 @@ watch-only wallet using '{}-addrimport' and then re-run this program.
elif age_fmt == 'block':
return g.rpch.blockcount - (o.confs - 1)
else:
if self.age_prec == 'approx':
date = _confs2date_approx(o)
else:
if o.date == None:
o.date = _confs2date_exact(o)
date = o.date
return {
'days': _date2days(date),
'date': _format_date(date),
'date_time': _format_date_time(date),
}[age_fmt]
return _date_formatter[age_fmt](_confs2date_approx(o) if self.age_prec == 'approx' else o.date)
class TwAddrList(MMGenDict):
age_fmts = TwUnspentOutputs.age_fmts
age_disp = TwUnspentOutputs.age_disp
age_prec_disp = TwUnspentOutputs.age_prec_disp
def __new__(cls,*args,**kwargs):
return MMGenDict.__new__(altcoin_subclass(cls,'tw','TwAddrList'),*args,**kwargs)
@ -584,7 +574,7 @@ class TwAddrList(MMGenDict):
addr=(CoinAddr.fmtc('ADDRESS',width=addr_width) if showbtcaddrs else None),
cmt=TwComment.fmtc('COMMENT',width=max_cmt_width+1),
amt='BALANCE'.ljust(max_fp_len+4),
age=age_fmt.upper()
age=age_fmt.upper()+self.age_prec_disp[self.age_prec],
).rstrip()]
def sort_algo(j):
@ -598,7 +588,10 @@ class TwAddrList(MMGenDict):
return j.sort_key
al_id_save = None
for mmid in sorted(self,key=sort_algo,reverse=bool(sort and 'reverse' in sort)):
mmids = sorted(self,key=sort_algo,reverse=bool(sort and 'reverse' in sort))
if show_age and self.age_prec == 'exact':
_set_dates([o for o in mmids if hasattr(o,'confs')])
for mmid in mmids:
if mmid.type == 'mmgen':
if al_id_save and al_id_save != mmid.obj.al_id:
out.append('')
@ -615,7 +608,7 @@ class TwAddrList(MMGenDict):
addr=(e['addr'].fmt(color=True,width=addr_width) if showbtcaddrs else None),
cmt=e['lbl'].comment.fmt(width=max_cmt_width,color=True,nullrepl='-'),
amt=e['amt'].fmt('4.{}'.format(max(max_fp_len,3)),color=True),
age=self.age_disp(mmid,age_fmt) if hasattr(mmid,'confs') and mmid.confs != None else '-'
age=self.age_disp(mmid,age_fmt) if show_age and hasattr(mmid,'confs') else '-'
).rstrip())
return '\n'.join(out + ['\nTOTAL: {} {}'.format(self.total.hl(color=True),g.dcoin)])