autosign,xmrwallet: various fixes and cleanups
This commit is contained in:
parent
1996ecab4a
commit
ca8657d4cd
7 changed files with 82 additions and 33 deletions
|
|
@ -188,9 +188,9 @@ class Autosign:
|
|||
if 'coin' in cfg._uopts:
|
||||
die(1,'--coin option not supported with this command. Use --coins instead')
|
||||
|
||||
if cfg.coins:
|
||||
self.coins = cfg.coins.upper().split(',')
|
||||
else:
|
||||
self.coins = cfg.coins.upper().split(',') if cfg.coins else []
|
||||
|
||||
if not self.coins:
|
||||
ymsg('Warning: no coins specified, defaulting to BTC')
|
||||
self.coins = ['BTC']
|
||||
|
||||
|
|
@ -227,6 +227,16 @@ class Autosign:
|
|||
|
||||
def do_mount(self):
|
||||
|
||||
from stat import S_ISDIR,S_IWUSR,S_IRUSR
|
||||
|
||||
def check_dir(cdir):
|
||||
try:
|
||||
ds = os.stat(cdir)
|
||||
assert S_ISDIR(ds.st_mode), f'{cdir!r} is not a directory!'
|
||||
assert ds.st_mode & S_IWUSR|S_IRUSR == S_IWUSR|S_IRUSR, f'{cdir!r} is not read/write for this user!'
|
||||
except:
|
||||
die(1,f'{cdir!r} missing or not read/writable by user!')
|
||||
|
||||
if not os.path.isdir(self.mountpoint):
|
||||
def do_die(m):
|
||||
die(1,'\n' + yellow(fmt(m.strip(),indent=' ')))
|
||||
|
|
@ -241,14 +251,10 @@ class Autosign:
|
|||
|
||||
self.have_msg_dir = os.path.isdir(self.msg_dir)
|
||||
|
||||
from stat import S_ISDIR,S_IWUSR,S_IRUSR
|
||||
for cdir in [self.tx_dir] + ([self.msg_dir] if self.have_msg_dir else []):
|
||||
try:
|
||||
ds = os.stat(cdir)
|
||||
assert S_ISDIR(ds.st_mode), f'{cdir!r} is not a directory!'
|
||||
assert ds.st_mode & S_IWUSR|S_IRUSR == S_IWUSR|S_IRUSR, f'{cdir!r} is not read/write for this user!'
|
||||
except:
|
||||
die(1,f'{cdir!r} missing or not read/writable by user!')
|
||||
check_dir(self.tx_dir)
|
||||
|
||||
if self.have_msg_dir:
|
||||
check_dir(self.msg_dir)
|
||||
|
||||
def do_umount(self):
|
||||
if os.path.ismount(self.mountpoint):
|
||||
|
|
|
|||
|
|
@ -405,7 +405,9 @@ class Config(Lockable):
|
|||
# Step 1: get user-supplied configuration data from a) command line, or b) first argument
|
||||
# to constructor; save to self._uopts:
|
||||
if opts_data or parsed_opts or process_opts:
|
||||
assert cfg is None
|
||||
assert cfg is None, (
|
||||
'Config(): ‘cfg’ cannot be used simultaneously with ' +
|
||||
'‘opts_data’, ‘parsed_opts’ or ‘process_opts’' )
|
||||
from mmgen.opts import UserOpts
|
||||
UserOpts(
|
||||
cfg = self,
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ from decimal import Decimal
|
|||
from collections import namedtuple
|
||||
|
||||
from .cfg import gc
|
||||
from .util import msg,die,fmt,fmt_list,pp_fmt
|
||||
from .util import msg,die,fmt,fmt_list,pp_fmt,oneshot_warning
|
||||
from .base_obj import AsyncInit
|
||||
from .obj import NonNegativeInt
|
||||
from .objmethods import Hilite,InitErrors,MMGenObject
|
||||
|
|
|
|||
|
|
@ -115,6 +115,9 @@ class MoneroMMGenTX:
|
|||
|
||||
class Base:
|
||||
|
||||
def __init__(self):
|
||||
self.name = type(self).__name__
|
||||
|
||||
def make_chksum(self,keys=None):
|
||||
res = json.dumps(
|
||||
dict( (k,v) for k,v in self.data._asdict().items() if (not keys or k in keys) ),
|
||||
|
|
@ -195,7 +198,7 @@ class MoneroMMGenTX:
|
|||
pmid = pink(pmid.hex()) if pmid else None
|
||||
)
|
||||
|
||||
def write(self,delete_metadata=False):
|
||||
def write(self,delete_metadata=False,ask_write=True,ask_overwrite=True):
|
||||
dict_data = self.data._asdict()
|
||||
if delete_metadata:
|
||||
dict_data['metadata'] = None
|
||||
|
|
@ -224,16 +227,16 @@ class MoneroMMGenTX:
|
|||
outfile = fn,
|
||||
data = out,
|
||||
desc = self.desc,
|
||||
ask_write = True,
|
||||
ask_write_default_yes = False )
|
||||
ask_write = ask_write,
|
||||
ask_write_default_yes = not ask_write,
|
||||
ask_overwrite = ask_overwrite )
|
||||
|
||||
class NewSigned(Base):
|
||||
signed = True
|
||||
desc = 'signed transaction data'
|
||||
ext = 'sigtx'
|
||||
class New(Base):
|
||||
|
||||
def __init__(self,*args,**kwargs):
|
||||
|
||||
super().__init__()
|
||||
|
||||
assert not args, 'Non-keyword args not permitted'
|
||||
|
||||
d = namedtuple('kwargs_tuple',kwargs)(**kwargs)
|
||||
|
|
@ -259,11 +262,17 @@ class MoneroMMGenTX:
|
|||
metadata = d.metadata,
|
||||
)
|
||||
|
||||
class NewSigned(New):
|
||||
desc = 'signed transaction data'
|
||||
ext = 'sigtx'
|
||||
signed = True
|
||||
|
||||
class Completed(Base):
|
||||
name = 'completed'
|
||||
|
||||
def __init__(self,cfg,fn):
|
||||
|
||||
super().__init__()
|
||||
|
||||
self.cfg = cfg
|
||||
self.fn = fn
|
||||
|
||||
|
|
@ -276,6 +285,10 @@ class MoneroMMGenTX:
|
|||
|
||||
d = self.xmrwallet_tx_data(**d_wrap['data'])
|
||||
|
||||
if self.name != 'Completed':
|
||||
assert fn.endswith('.'+self.ext), 'TX filename {fn!r} has incorrect extension (not {self.ext!r})'
|
||||
assert getattr(d,self.req_field), f'{self.name} TX missing required field {self.req_field!r}'
|
||||
|
||||
proto = init_proto( cfg, 'xmr', network=d.network, need_amt=True )
|
||||
|
||||
self.data = self.xmrwallet_tx_data(
|
||||
|
|
@ -293,13 +306,17 @@ class MoneroMMGenTX:
|
|||
blob = d.blob,
|
||||
metadata = d.metadata,
|
||||
)
|
||||
|
||||
for k in ('base_chksum','full_chksum'):
|
||||
a = getattr(self,k)
|
||||
b = d_wrap[k]
|
||||
assert a == b, f'{k} mismatch: {a} != {b}'
|
||||
|
||||
class Signed(Completed):
|
||||
name = 'signed'
|
||||
desc = 'signed transaction'
|
||||
ext = 'sigtx'
|
||||
signed = True
|
||||
req_field = 'blob'
|
||||
|
||||
class MoneroWalletOps:
|
||||
|
||||
|
|
@ -505,6 +522,7 @@ class MoneroWalletOps:
|
|||
|
||||
def __init__(self,parent,d):
|
||||
self.parent = parent
|
||||
self.cfg = parent.cfg
|
||||
self.c = parent.c
|
||||
self.d = d
|
||||
self.fn = parent.get_wallet_fn(d)
|
||||
|
|
@ -637,7 +655,7 @@ class MoneroWalletOps:
|
|||
get_tx_metadata = True
|
||||
)
|
||||
return MoneroMMGenTX.NewSigned(
|
||||
cfg = self.parent.cfg,
|
||||
cfg = self.cfg,
|
||||
op = self.parent.name,
|
||||
network = self.parent.proto.network,
|
||||
seed_id = self.parent.kal.al_id.sid,
|
||||
|
|
@ -665,7 +683,7 @@ class MoneroWalletOps:
|
|||
die(3,'More than one TX required. Cannot perform this sweep')
|
||||
|
||||
return MoneroMMGenTX.NewSigned(
|
||||
cfg = self.parent.cfg,
|
||||
cfg = self.cfg,
|
||||
op = self.parent.name,
|
||||
network = self.parent.proto.network,
|
||||
seed_id = self.parent.kal.al_id.sid,
|
||||
|
|
@ -858,7 +876,7 @@ class MoneroWalletOps:
|
|||
else:
|
||||
idx = int(m[i])
|
||||
try:
|
||||
res = [d for d in self.kal.data if d.idx == idx][0]
|
||||
res = self.kal.entry(idx)
|
||||
except:
|
||||
die(1,'Supplied key-address file does not contain address {}:{}'.format(
|
||||
self.kal.al_id.sid,
|
||||
|
|
@ -1095,6 +1113,8 @@ class MoneroWalletOps:
|
|||
|
||||
super().__init__(cfg,uarg_tuple)
|
||||
|
||||
self.tx = MoneroMMGenTX.Signed( self.cfg, uarg.infile )
|
||||
|
||||
if self.cfg.tx_relay_daemon:
|
||||
m = re.fullmatch(
|
||||
uarg_info['tx_relay_daemon'].pat,
|
||||
|
|
@ -1120,8 +1140,6 @@ class MoneroWalletOps:
|
|||
test_connection = False, # relay is presumably a public node, so avoid extra connections
|
||||
proxy = proxy )
|
||||
|
||||
self.tx = MoneroMMGenTX.Signed( self.cfg, uarg.infile )
|
||||
|
||||
async def main(self):
|
||||
msg('\n' + self.tx.get_info())
|
||||
|
||||
|
|
@ -1150,6 +1168,6 @@ class MoneroWalletOps:
|
|||
'\n'.join(
|
||||
tx.get_info() for tx in
|
||||
sorted(
|
||||
(MoneroMMGenTX.Signed( self.cfg, fn ) for fn in uarg.infile),
|
||||
(MoneroMMGenTX.Completed( self.cfg, fn ) for fn in uarg.infile),
|
||||
key = lambda x: x.data.sign_time )
|
||||
))
|
||||
|
|
|
|||
|
|
@ -97,6 +97,7 @@ class TestSuiteAutosignBase(TestSuiteBase):
|
|||
|
||||
self.asi = Autosign(
|
||||
AutosignConfig({
|
||||
'coins': ','.join(self.coins),
|
||||
'mountpoint': (
|
||||
None if self.live else
|
||||
os.path.join(self.tmpdir,self.mountpoint_basename)
|
||||
|
|
@ -298,20 +299,23 @@ class TestSuiteAutosignBase(TestSuiteBase):
|
|||
def do_sign(self,args,have_msg=False):
|
||||
t = self.spawn('mmgen-autosign', self.opts + args )
|
||||
t.expect(
|
||||
f'{self.tx_count} transactions signed' if self.tx_count else
|
||||
f'{self.tx_count} transaction{suf(self.tx_count)} signed' if self.tx_count else
|
||||
'No unsigned transactions' )
|
||||
|
||||
if self.bad_tx_count:
|
||||
t.expect(f'{self.bad_tx_count} transactions failed to sign')
|
||||
t.expect(f'{self.bad_tx_count} transaction{suf(self.bad_tx_count)} failed to sign')
|
||||
t.req_exit_val = 1
|
||||
|
||||
if have_msg:
|
||||
t.expect(
|
||||
f'{self.good_msg_count} message files{{0,1}} signed' if self.good_msg_count else
|
||||
f'{self.good_msg_count} message file{suf(self.good_msg_count)}{{0,1}} signed'
|
||||
if self.good_msg_count else
|
||||
'No unsigned message files', regex=True )
|
||||
|
||||
if self.bad_msg_count:
|
||||
t.expect(f'{self.bad_msg_count} message files{{0,1}} failed to sign', regex=True)
|
||||
t.expect(
|
||||
f'{self.bad_msg_count} message file{suf(self.bad_msg_count)}{{0,1}} failed to sign',
|
||||
regex = True )
|
||||
t.req_exit_val = 1
|
||||
|
||||
if 'wait' in args:
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ from mmgen.cfg import gc
|
|||
from ..include.common import *
|
||||
from .common import *
|
||||
|
||||
class TestSuiteBase(object):
|
||||
class TestSuiteBase:
|
||||
'initializer class for the test.py test suite'
|
||||
base_passthru_opts = ('data_dir','skip_cfg_file')
|
||||
passthru_opts = ()
|
||||
|
|
@ -36,6 +36,8 @@ class TestSuiteBase(object):
|
|||
need_daemon = False
|
||||
|
||||
def __init__(self,trunner,cfgs,spawn):
|
||||
if hasattr(self,'tr'): # init will be called multiple times for classes with multiple inheritance
|
||||
return
|
||||
self.proto = cfg._proto
|
||||
self.tr = trunner
|
||||
self.cfgs = cfgs
|
||||
|
|
|
|||
|
|
@ -615,6 +615,23 @@ class TestSuiteXMRWallet(TestSuiteBase):
|
|||
|
||||
# mining methods
|
||||
|
||||
async def mine5(self):
|
||||
return await self.mine(5)
|
||||
|
||||
async def mine10(self):
|
||||
return await self.mine(10)
|
||||
|
||||
async def mine60(self):
|
||||
return await self.mine(60)
|
||||
|
||||
async def mine(self,nsecs):
|
||||
imsg_r(f'Mining for {nsecs} seconds...')
|
||||
await self.start_mining()
|
||||
await asyncio.sleep(nsecs)
|
||||
ret = await self.stop_mining()
|
||||
imsg('done')
|
||||
return ret
|
||||
|
||||
async def start_mining(self):
|
||||
data = self.users['miner']
|
||||
addr = read_from_file(data.addrfile_fs.format(1)) # mine to wallet #1, account 0
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue