|
|
@@ -142,6 +142,7 @@ class Signable:
|
|
|
clean_all = False
|
|
|
multiple_ok = True
|
|
|
action_desc = 'signed'
|
|
|
+ fail_msg = 'failed to sign'
|
|
|
|
|
|
def __init__(self, parent):
|
|
|
self.parent = parent
|
|
|
@@ -149,31 +150,10 @@ class Signable:
|
|
|
self.dir = getattr(parent, self.dir_name)
|
|
|
self.name = type(self).__name__
|
|
|
|
|
|
- @property
|
|
|
- def submitted(self):
|
|
|
- return self._processed('_submitted', self.subext)
|
|
|
-
|
|
|
- def _processed(self, attrname, ext):
|
|
|
- if not hasattr(self, attrname):
|
|
|
- setattr(self, attrname, tuple(f for f in sorted(self.dir.iterdir())
|
|
|
- if f.name.endswith('.' + ext)))
|
|
|
- return getattr(self, attrname)
|
|
|
-
|
|
|
@property
|
|
|
def unsigned(self):
|
|
|
return self._unprocessed('_unsigned', self.rawext, self.sigext)
|
|
|
|
|
|
- @property
|
|
|
- def unsubmitted(self):
|
|
|
- return self._unprocessed('_unsubmitted', self.sigext, self.subext)
|
|
|
-
|
|
|
- @property
|
|
|
- def unsubmitted_raw(self):
|
|
|
- return self._unprocessed('_unsubmitted_raw', self.rawext, self.subext)
|
|
|
-
|
|
|
- unsent = unsubmitted
|
|
|
- unsent_raw = unsubmitted_raw
|
|
|
-
|
|
|
def _unprocessed(self, attrname, rawext, sigext):
|
|
|
if not hasattr(self, attrname):
|
|
|
dirlist = sorted(self.dir.iterdir())
|
|
|
@@ -192,75 +172,15 @@ class Signable:
|
|
|
b = ' {}\n'.format('\n '.join(
|
|
|
self.gen_bad_list(sorted(bad_files, key=lambda f: f.name))))))
|
|
|
|
|
|
- def die_wrong_num_txs(self, tx_type, *, msg=None, desc=None, show_dir=False):
|
|
|
- num_txs = len(getattr(self, tx_type))
|
|
|
- die('AutosignTXError', '{m}{a} {b} transaction{c} {d} {e}!'.format(
|
|
|
- m = msg + '\n' if msg else '',
|
|
|
- a = 'One' if num_txs == 1 else 'More than one' if num_txs else 'No',
|
|
|
- b = desc or tx_type,
|
|
|
- c = suf(num_txs),
|
|
|
- d = 'already present' if num_txs else 'present',
|
|
|
- e = f'in ‘{getattr(self.parent, self.dir_name)}’'
|
|
|
- if show_dir else 'on removable device'))
|
|
|
-
|
|
|
- def check_create_ok(self):
|
|
|
- if len(self.unsigned):
|
|
|
- self.die_wrong_num_txs('unsigned', msg='Cannot create transaction')
|
|
|
- if len(self.unsent):
|
|
|
- die('AutosignTXError', 'Cannot create transaction: you have an unsent transaction')
|
|
|
-
|
|
|
- def get_unsubmitted(self, tx_type='unsubmitted'):
|
|
|
- if len(self.unsubmitted) == 1:
|
|
|
- return self.unsubmitted[0]
|
|
|
- else:
|
|
|
- self.die_wrong_num_txs(tx_type)
|
|
|
-
|
|
|
- def get_unsent(self):
|
|
|
- return self.get_unsubmitted('unsent')
|
|
|
-
|
|
|
- def get_submitted(self):
|
|
|
- if len(self.submitted) == 0:
|
|
|
- self.die_wrong_num_txs('submitted')
|
|
|
- else:
|
|
|
- return self.submitted
|
|
|
-
|
|
|
- def get_abortable(self):
|
|
|
- if len(self.unsent_raw) != 1:
|
|
|
- self.die_wrong_num_txs('unsent_raw', desc='unsent')
|
|
|
- if len(self.unsent) > 1:
|
|
|
- self.die_wrong_num_txs('unsent')
|
|
|
- if self.unsent:
|
|
|
- if self.unsent[0].stem != self.unsent_raw[0].stem:
|
|
|
- die(1, f'{self.unsent[0]}, {self.unsent_raw[0]}: file mismatch')
|
|
|
- return self.unsent_raw + self.unsent
|
|
|
-
|
|
|
- def shred_abortable(self):
|
|
|
- files = self.get_abortable() # raises AutosignTXError if no unsent TXs available
|
|
|
- keypress_confirm(
|
|
|
- self.cfg,
|
|
|
- 'The following file{} will be securely deleted:\n{}\nOK?'.format(
|
|
|
- suf(files),
|
|
|
- fmt_list(map(str, files), fmt='col', indent=' ')),
|
|
|
- do_exit = True)
|
|
|
- for fn in files:
|
|
|
- msg(f'Shredding file ‘{fn}’')
|
|
|
- shred_file(self.cfg, fn, iterations=15)
|
|
|
- sys.exit(0)
|
|
|
-
|
|
|
- async def get_last_created(self):
|
|
|
- from .tx import CompletedTX
|
|
|
- files = [f for f in self.dir.iterdir() if f.name.endswith(self.subext)]
|
|
|
- return sorted(
|
|
|
- [await CompletedTX(cfg=self.cfg, filename=str(txfile), quiet_open=True)
|
|
|
- for txfile in files],
|
|
|
- key = lambda x: x.timestamp)[-1]
|
|
|
+ def gen_bad_list(self, bad_files):
|
|
|
+ for f in bad_files:
|
|
|
+ yield red(f.name)
|
|
|
|
|
|
class transaction(base):
|
|
|
desc = 'non-automount transaction'
|
|
|
rawext = 'rawtx'
|
|
|
sigext = 'sigtx'
|
|
|
dir_name = 'tx_dir'
|
|
|
- fail_msg = 'failed to sign'
|
|
|
automount = False
|
|
|
|
|
|
async def sign(self, f):
|
|
|
@@ -321,12 +241,8 @@ class Signable:
|
|
|
else:
|
|
|
msg('\nNo non-MMGen outputs')
|
|
|
|
|
|
- def gen_bad_list(self, bad_files):
|
|
|
- for f in bad_files:
|
|
|
- yield red(f.name)
|
|
|
-
|
|
|
class automount_transaction(transaction):
|
|
|
- desc = 'automount transaction'
|
|
|
+ desc = 'automount transaction'
|
|
|
dir_name = 'txauto_dir'
|
|
|
rawext = 'arawtx'
|
|
|
sigext = 'asigtx'
|
|
|
@@ -334,7 +250,91 @@ class Signable:
|
|
|
multiple_ok = False
|
|
|
automount = True
|
|
|
|
|
|
- class xmr_signable(transaction): # mixin class
|
|
|
+ @property
|
|
|
+ def unsubmitted(self):
|
|
|
+ return self._unprocessed('_unsubmitted', self.sigext, self.subext)
|
|
|
+
|
|
|
+ @property
|
|
|
+ def unsubmitted_raw(self):
|
|
|
+ return self._unprocessed('_unsubmitted_raw', self.rawext, self.subext)
|
|
|
+
|
|
|
+ unsent = unsubmitted
|
|
|
+ unsent_raw = unsubmitted_raw
|
|
|
+
|
|
|
+ @property
|
|
|
+ def submitted(self):
|
|
|
+ return self._processed('_submitted', self.subext)
|
|
|
+
|
|
|
+ def _processed(self, attrname, ext):
|
|
|
+ if not hasattr(self, attrname):
|
|
|
+ setattr(self, attrname, tuple(f for f in sorted(self.dir.iterdir())
|
|
|
+ if f.name.endswith('.' + ext)))
|
|
|
+ return getattr(self, attrname)
|
|
|
+
|
|
|
+ def die_wrong_num_txs(self, tx_type, *, msg=None, desc=None, show_dir=False):
|
|
|
+ num_txs = len(getattr(self, tx_type))
|
|
|
+ die('AutosignTXError', '{m}{a} {b} transaction{c} {d} {e}!'.format(
|
|
|
+ m = msg + '\n' if msg else '',
|
|
|
+ a = 'One' if num_txs == 1 else 'More than one' if num_txs else 'No',
|
|
|
+ b = desc or tx_type,
|
|
|
+ c = suf(num_txs),
|
|
|
+ d = 'already present' if num_txs else 'present',
|
|
|
+ e = f'in ‘{getattr(self.parent, self.dir_name)}’'
|
|
|
+ if show_dir else 'on removable device'))
|
|
|
+
|
|
|
+ def check_create_ok(self):
|
|
|
+ if len(self.unsigned):
|
|
|
+ self.die_wrong_num_txs('unsigned', msg='Cannot create transaction')
|
|
|
+ if len(self.unsent):
|
|
|
+ die('AutosignTXError', 'Cannot create transaction: you have an unsent transaction')
|
|
|
+
|
|
|
+ def get_unsubmitted(self, tx_type='unsubmitted'):
|
|
|
+ if len(self.unsubmitted) == 1:
|
|
|
+ return self.unsubmitted[0]
|
|
|
+ else:
|
|
|
+ self.die_wrong_num_txs(tx_type)
|
|
|
+
|
|
|
+ def get_unsent(self):
|
|
|
+ return self.get_unsubmitted('unsent')
|
|
|
+
|
|
|
+ def get_submitted(self):
|
|
|
+ if len(self.submitted) == 0:
|
|
|
+ self.die_wrong_num_txs('submitted')
|
|
|
+ else:
|
|
|
+ return self.submitted
|
|
|
+
|
|
|
+ def get_abortable(self):
|
|
|
+ if len(self.unsent_raw) != 1:
|
|
|
+ self.die_wrong_num_txs('unsent_raw', desc='unsent')
|
|
|
+ if len(self.unsent) > 1:
|
|
|
+ self.die_wrong_num_txs('unsent')
|
|
|
+ if self.unsent:
|
|
|
+ if self.unsent[0].stem != self.unsent_raw[0].stem:
|
|
|
+ die(1, f'{self.unsent[0]}, {self.unsent_raw[0]}: file mismatch')
|
|
|
+ return self.unsent_raw + self.unsent
|
|
|
+
|
|
|
+ def shred_abortable(self):
|
|
|
+ files = self.get_abortable() # raises AutosignTXError if no unsent TXs available
|
|
|
+ keypress_confirm(
|
|
|
+ self.cfg,
|
|
|
+ 'The following file{} will be securely deleted:\n{}\nOK?'.format(
|
|
|
+ suf(files),
|
|
|
+ fmt_list(map(str, files), fmt='col', indent=' ')),
|
|
|
+ do_exit = True)
|
|
|
+ for fn in files:
|
|
|
+ msg(f'Shredding file ‘{fn}’')
|
|
|
+ shred_file(self.cfg, fn, iterations=15)
|
|
|
+ sys.exit(0)
|
|
|
+
|
|
|
+ async def get_last_created(self):
|
|
|
+ from .tx import CompletedTX
|
|
|
+ files = [f for f in self.dir.iterdir() if f.name.endswith(self.subext)]
|
|
|
+ return sorted(
|
|
|
+ [await CompletedTX(cfg=self.cfg, filename=str(txfile), quiet_open=True)
|
|
|
+ for txfile in files],
|
|
|
+ key = lambda x: x.timestamp)[-1]
|
|
|
+
|
|
|
+ class xmr_signable: # mixin class
|
|
|
automount = True
|
|
|
|
|
|
def need_daemon_restart(self, m, new_idx):
|
|
|
@@ -346,11 +346,12 @@ class Signable:
|
|
|
bmsg('\nAutosign summary:')
|
|
|
msg('\n'.join(s.get_info(indent=' ') for s in signables) + self.summary_footer)
|
|
|
|
|
|
- class xmr_transaction(xmr_signable):
|
|
|
+ class xmr_transaction(xmr_signable, automount_transaction):
|
|
|
dir_name = 'xmr_tx_dir'
|
|
|
desc = 'Monero transaction'
|
|
|
+ rawext = 'rawtx'
|
|
|
+ sigext = 'sigtx'
|
|
|
subext = 'subtx'
|
|
|
- multiple_ok = False
|
|
|
summary_footer = ''
|
|
|
|
|
|
async def sign(self, f):
|
|
|
@@ -366,7 +367,7 @@ class Signable:
|
|
|
tx2.write(ask_write=False)
|
|
|
return tx2
|
|
|
|
|
|
- class xmr_wallet_outputs_file(xmr_signable):
|
|
|
+ class xmr_wallet_outputs_file(xmr_signable, base):
|
|
|
desc = 'Monero wallet outputs file'
|
|
|
rawext = 'raw'
|
|
|
sigext = 'sig'
|