From 8d6f9f598b65b5681bfca58aa3d7b46a6730a00f Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Thu, 22 Feb 2024 12:48:14 +0000 Subject: [PATCH] tx: new `Sent` class; tx.send() clean up error handling --- mmgen/main_txbump.py | 4 ++-- mmgen/main_txdo.py | 12 +++++----- mmgen/main_txsend.py | 13 ++++++----- mmgen/proto/btc/tx/online.py | 23 +++++++++--------- mmgen/proto/eth/tx/online.py | 45 +++++++++++++++++------------------- mmgen/tx/__init__.py | 4 +++- mmgen/tx/base.py | 4 ++++ mmgen/tx/completed.py | 6 +++-- mmgen/tx/file.py | 8 +++++++ mmgen/tx/info.py | 2 +- mmgen/tx/online.py | 4 ++++ 11 files changed, 71 insertions(+), 54 deletions(-) diff --git a/mmgen/main_txbump.py b/mmgen/main_txbump.py index d8fd793d..2902b4d3 100755 --- a/mmgen/main_txbump.py +++ b/mmgen/main_txbump.py @@ -175,8 +175,8 @@ async def main(): if tx3: tx4 = await OnlineSignedTX(cfg=cfg,data=tx3.__dict__) tx4.file.write(ask_write=False) - await tx4.send(exit_on_fail=True) - tx4.file.write(ask_write=False) + if await tx4.send(): + tx4.file.write(ask_write=False) else: die(2,'Transaction could not be signed') else: diff --git a/mmgen/main_txdo.py b/mmgen/main_txdo.py index 1cebe3b1..15b4490d 100755 --- a/mmgen/main_txdo.py +++ b/mmgen/main_txdo.py @@ -124,7 +124,7 @@ FMT CODES: cfg = Config(opts_data=opts_data) -from .tx import NewTX,OnlineSignedTX +from .tx import NewTX, SentTX from .tx.sign import txsign,get_seed_files,get_keyaddrlist,get_keylist seed_files = get_seed_files(cfg,cfg._args) @@ -147,11 +147,11 @@ async def main(): tx3 = await txsign(cfg,tx2,seed_files,kl,kal) if tx3: - tx4 = await OnlineSignedTX(cfg=cfg,data=tx3.__dict__) - tx4.file.write(ask_write=False) - await tx4.send(exit_on_fail=True) - tx4.file.write(ask_overwrite=False,ask_write=False) - tx4.print_contract_addr() + tx3.file.write(ask_write=False) + tx4 = await SentTX(cfg=cfg, data=tx3.__dict__) + if await tx4.send(): + tx4.file.write(ask_overwrite=False,ask_write=False) + tx4.print_contract_addr() else: die(2,'Transaction could not be signed') diff --git a/mmgen/main_txsend.py b/mmgen/main_txsend.py index ca937368..e1e3a0c0 100755 --- a/mmgen/main_txsend.py +++ b/mmgen/main_txsend.py @@ -56,7 +56,7 @@ if not cfg.status: async def main(): - from .tx import OnlineSignedTX + from .tx import OnlineSignedTX, SentTX tx = await OnlineSignedTX( cfg = cfg, @@ -79,10 +79,11 @@ async def main(): if tx.add_comment(): # edits an existing comment, returns true if changed tx.file.write(ask_write_default_yes=True) - await tx.send(exit_on_fail=True) - tx.file.write( - ask_overwrite = False, - ask_write = False) - tx.print_contract_addr() + if await tx.send(): + tx2 = await SentTX(cfg=cfg, data=tx.__dict__) + tx2.file.write( + ask_overwrite = False, + ask_write = False) + tx2.print_contract_addr() async_run(main()) diff --git a/mmgen/proto/btc/tx/online.py b/mmgen/proto/btc/tx/online.py index 3ed3a893..a004b6c7 100755 --- a/mmgen/proto/btc/tx/online.py +++ b/mmgen/proto/btc/tx/online.py @@ -12,15 +12,14 @@ proto.btc.tx.online: Bitcoin online signed transaction class """ -import sys - from ....tx import online as TxBase -from ....util import msg,ymsg,rmsg,die +from ....util import msg,die +from ....color import orange from .signed import Signed class OnlineSigned(Signed,TxBase.OnlineSigned): - async def send(self,prompt_user=True,exit_on_fail=False): + async def send(self,prompt_user=True): self.check_correct_chain() @@ -49,6 +48,7 @@ class OnlineSigned(Signed,TxBase.OnlineSigned): ret = await self.rpc.call('sendrawtransaction',self.serialized) except Exception as e: errmsg = str(e) + nl = '\n' if errmsg.count('Signature must use SIGHASH_FORKID'): m = ( 'The Aug. 1 2017 UAHF has activated on this chain.\n' @@ -61,20 +61,19 @@ class OnlineSigned(Signed,TxBase.OnlineSigned): m = "Transaction with nLockTime {!r} can't be included in this block!".format( self.strfmt_locktime(self.get_serialized_locktime())) else: - m = errmsg - ymsg(m) - rmsg(f'Send of MMGen transaction {self.txid} failed') - if exit_on_fail: - sys.exit(1) - return False + m,nl = ('','') + msg(orange('\n'+errmsg)) + die(2, f'{m}{nl}Send of MMGen transaction {self.txid} failed') else: assert ret == self.coin_txid, 'txid mismatch (after sending)' msg(m.format(self.coin_txid.hl())) - self.add_timestamp() + self.add_sent_timestamp() self.add_blockcount() - self.desc = 'sent transaction' return True def print_contract_addr(self): pass + +class Sent(TxBase.Sent, OnlineSigned): + pass diff --git a/mmgen/proto/eth/tx/online.py b/mmgen/proto/eth/tx/online.py index dc473333..1f61f54d 100755 --- a/mmgen/proto/eth/tx/online.py +++ b/mmgen/proto/eth/tx/online.py @@ -12,14 +12,15 @@ proto.eth.tx.online: Ethereum online signed transaction class """ -from ....util import msg,rmsg,die +from ....util import msg,die +from ....color import orange from ....tx import online as TxBase from .. import erigon_sleep from .signed import Signed,TokenSigned class OnlineSigned(Signed,TxBase.OnlineSigned): - async def send(self,prompt_user=True,exit_on_fail=False): + async def send(self,prompt_user=True): self.check_correct_chain() @@ -36,32 +37,22 @@ class OnlineSigned(Signed,TxBase.OnlineSigned): self.confirm_send() if self.cfg.bogus_send: - ret = None + m = 'BOGUS transaction NOT sent: {}' else: try: ret = await self.rpc.call('eth_sendRawTransaction','0x'+self.serialized) - except: - raise # TODO: raises immediately - ret = False # TODO: unreachable code + except Exception as e: + msg(orange('\n'+str(e))) + die(2, f'Send of MMGen transaction {self.txid} failed') + m = 'Transaction sent: {}' + assert ret == '0x'+self.coin_txid,'txid mismatch (after sending)' + await erigon_sleep(self) - if ret is False: # TODO: unreachable code - rmsg(f'Send of MMGen transaction {self.txid} failed') - if exit_on_fail: - import sys - sys.exit(1) - return False - else: - if self.cfg.bogus_send: - m = 'BOGUS transaction NOT sent: {}' - else: - m = 'Transaction sent: {}' - assert ret == '0x'+self.coin_txid,'txid mismatch (after sending)' - await erigon_sleep(self) - self.desc = 'sent transaction' - msg(m.format(self.coin_txid.hl())) - self.add_timestamp() - self.add_blockcount() - return True + msg(m.format(self.coin_txid.hl())) + self.add_sent_timestamp() + self.add_blockcount() + + return True def print_contract_addr(self): if 'token_addr' in self.txobj: @@ -80,3 +71,9 @@ class TokenOnlineSigned(TokenSigned,OnlineSigned): t = Token(self.cfg,self.proto,o['token_addr'],o['decimals']) o['amt'] = t.transferdata2amt(o['data']) o['token_to'] = t.transferdata2sendaddr(o['data']) + +class Sent(TxBase.Sent, OnlineSigned): + pass + +class TokenSent(TxBase.Sent, TokenOnlineSigned): + pass diff --git a/mmgen/tx/__init__.py b/mmgen/tx/__init__.py index 68a7b687..f7c387ab 100755 --- a/mmgen/tx/__init__.py +++ b/mmgen/tx/__init__.py @@ -73,7 +73,8 @@ async def _get_obj_async( _clsname, _modname, *args, **kwargs ): # signing. if proto and proto.tokensym and clsname in ( 'New', - 'OnlineSigned'): + 'OnlineSigned', + 'Sent'): from ..tw.ctl import TwCtl kwargs['twctl'] = await TwCtl(cfg,proto) @@ -92,4 +93,5 @@ NewTX = _get_async('New', 'new') CompletedTX = _get_async('Completed', 'completed') SignedTX = _get_async('Signed', 'signed') OnlineSignedTX = _get_async('OnlineSigned', 'online') +SentTX = _get_async('Sent', 'online') BumpTX = _get_async('Bump', 'bump') diff --git a/mmgen/tx/base.py b/mmgen/tx/base.py index 69c82e36..d76ce644 100755 --- a/mmgen/tx/base.py +++ b/mmgen/tx/base.py @@ -77,6 +77,7 @@ class Base(MMGenObject): txid = None coin_txid = None timestamp = None + sent_timestamp = None blockcount = None locktime = None chain = None @@ -162,6 +163,9 @@ class Base(MMGenObject): def add_timestamp(self): self.timestamp = make_timestamp() + def add_sent_timestamp(self): + self.sent_timestamp = make_timestamp() + def add_blockcount(self): self.blockcount = self.rpc.blockcount diff --git a/mmgen/tx/completed.py b/mmgen/tx/completed.py index 08cbb46f..5b8fb0cf 100755 --- a/mmgen/tx/completed.py +++ b/mmgen/tx/completed.py @@ -58,8 +58,10 @@ class Completed(Base): see twctl:import_token() """ from .unsigned import Unsigned - if ext == Unsigned.ext: - return Unsigned + from .online import Sent + for cls in (Unsigned, Sent): + if ext == getattr(cls, 'ext'): + return cls if proto.tokensym: from .online import OnlineSigned as Signed diff --git a/mmgen/tx/file.py b/mmgen/tx/file.py index 51d098d6..fac6e339 100755 --- a/mmgen/tx/file.py +++ b/mmgen/tx/file.py @@ -72,6 +72,11 @@ class MMGenTxFile(MMGenObject): self.chksum = HexStr(tx_data.pop(0)) assert self.chksum == make_chksum_6(' '.join(tx_data)),'file data does not match checksum' + if len(tx_data) == 7: + desc = 'sent timestamp' + (_, tx.sent_timestamp) = tx_data.pop(-1).split() + assert _ == 'Sent', 'invalid sent timestamp line' + if len(tx_data) == 6: assert len(tx_data[-1]) == 64,'invalid coin TxID length' desc = 'coin TxID' @@ -186,6 +191,9 @@ class MMGenTxFile(MMGenObject): lines.append('-') # keep old tx files backwards compatible lines.append(tx.coin_txid) + if tx.sent_timestamp: + lines.append(f'Sent {tx.sent_timestamp}') + self.chksum = make_chksum_6(' '.join(lines)) fmt_data = '\n'.join([self.chksum] + lines) + '\n' if len(fmt_data) > tx.cfg.max_tx_file_size: diff --git a/mmgen/tx/info.py b/mmgen/tx/info.py index 56eccce0..fa1b85e4 100755 --- a/mmgen/tx/info.py +++ b/mmgen/tx/info.py @@ -56,7 +56,7 @@ class TxInfo: orange(self.strfmt_locktime(terse=True)) if tx.locktime else green('None') )) - for attr,label in [('timestamp','Created:')]: + for attr,label in [('timestamp','Created:'),('sent_timestamp','Sent:')]: if (val := getattr(tx,attr)) is not None: _ = decode_timestamp(val) yield f'{label:8} {make_timestr(_)} ({format_elapsed_hr(_)})\n' diff --git a/mmgen/tx/online.py b/mmgen/tx/online.py index 5918b6df..dce3c754 100755 --- a/mmgen/tx/online.py +++ b/mmgen/tx/online.py @@ -30,3 +30,7 @@ class OnlineSigned(Signed): action = f'broadcast this transaction to the {self.proto.coin} {self.proto.network.upper()} network', expect = 'YES' if self.cfg.quiet or self.cfg.yes else 'YES, I REALLY WANT TO DO THIS' ) msg('Sending transaction') + +class Sent(OnlineSigned): + desc = 'sent transaction' + ext = 'subtx'