tx.online.OnlineSigned: new send() method
- common exit point for all scripts that broadcast transactions to the network - supports transaction files with multiple txhex, required for upcoming ERC20 THORChain swap support
This commit is contained in:
parent
026f4a4c1d
commit
48660c3189
12 changed files with 109 additions and 124 deletions
|
|
@ -1 +1 @@
|
|||
15.1.dev27
|
||||
15.1.dev28
|
||||
|
|
|
|||
|
|
@ -205,8 +205,7 @@ async def main():
|
|||
if tx3:
|
||||
tx4 = await OnlineSignedTX(cfg=cfg, data=tx3.__dict__)
|
||||
tx4.file.write(ask_write=False)
|
||||
if await tx4.send():
|
||||
tx4.file.write(ask_write=False)
|
||||
await tx4.send(cfg, asi if cfg.autosign else None)
|
||||
else:
|
||||
die(2, 'Transaction could not be signed')
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -176,9 +176,7 @@ async def main():
|
|||
if tx3:
|
||||
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.post_write()
|
||||
await tx4.send(cfg, asi=None)
|
||||
else:
|
||||
die(2, 'Transaction could not be signed')
|
||||
|
||||
|
|
|
|||
|
|
@ -83,6 +83,8 @@ if cfg.dump_hex and cfg.dump_hex != '-':
|
|||
from .fileutil import check_outfile_dir
|
||||
check_outfile_dir(cfg.dump_hex)
|
||||
|
||||
asi = None
|
||||
|
||||
if len(cfg._args) == 1:
|
||||
infile = cfg._args[0]
|
||||
from .fileutil import check_infile
|
||||
|
|
@ -109,16 +111,7 @@ if not cfg.status:
|
|||
from .ui import do_license_msg
|
||||
do_license_msg(cfg)
|
||||
|
||||
from .tx import OnlineSignedTX, SentTX
|
||||
from .ui import keypress_confirm
|
||||
|
||||
async def post_send(tx):
|
||||
tx2 = await SentTX(cfg=cfg, data=tx.__dict__, automount=cfg.autosign)
|
||||
tx2.file.write(
|
||||
outdir = asi.txauto_dir if cfg.autosign else None,
|
||||
ask_overwrite = False,
|
||||
ask_write = False)
|
||||
tx2.post_write()
|
||||
from .tx import OnlineSignedTX
|
||||
|
||||
async def main():
|
||||
|
||||
|
|
@ -145,12 +138,9 @@ async def main():
|
|||
cfg._util.vmsg(f'Getting {tx.desc} ‘{tx.infile}’')
|
||||
|
||||
if cfg.mark_sent:
|
||||
await post_send(tx)
|
||||
await tx.post_send(asi)
|
||||
sys.exit(0)
|
||||
|
||||
if cfg.receipt:
|
||||
sys.exit(await tx.status.display(print_receipt=True))
|
||||
|
||||
if cfg.status:
|
||||
if tx.coin_txid:
|
||||
cfg._util.qmsg(f'{tx.proto.coin} txid: {tx.coin_txid.hl()}')
|
||||
|
|
@ -162,35 +152,12 @@ async def main():
|
|||
if tx.is_swap and not tx.check_swap_expiry():
|
||||
die(1, 'Swap quote has expired. Please re-create the transaction')
|
||||
|
||||
if not cfg.yes:
|
||||
if not (cfg.yes or cfg.receipt):
|
||||
tx.info.view_with_prompt('View transaction details?')
|
||||
if tx.add_comment(): # edits an existing comment, returns true if changed
|
||||
if not cfg.autosign:
|
||||
tx.file.write(ask_write_default_yes=True)
|
||||
|
||||
if cfg.dump_hex:
|
||||
from .fileutil import write_data_to_file
|
||||
write_data_to_file(
|
||||
cfg,
|
||||
cfg.dump_hex,
|
||||
tx.serialized + '\n',
|
||||
desc = 'serialized transaction hex data',
|
||||
ask_overwrite = False,
|
||||
ask_tty = False)
|
||||
if cfg.autosign:
|
||||
if keypress_confirm(cfg, 'Mark transaction as sent on removable device?'):
|
||||
await post_send(tx)
|
||||
else:
|
||||
await post_send(tx)
|
||||
elif cfg.tx_proxy:
|
||||
from .tx.tx_proxy import send_tx
|
||||
if send_tx(cfg, tx):
|
||||
if (not cfg.autosign or
|
||||
keypress_confirm(cfg, 'Mark transaction as sent on removable device?')):
|
||||
await post_send(tx)
|
||||
elif cfg.test:
|
||||
await tx.test_sendable()
|
||||
elif await tx.send():
|
||||
await post_send(tx)
|
||||
await tx.send(cfg, asi)
|
||||
|
||||
async_run(main())
|
||||
|
|
|
|||
|
|
@ -37,11 +37,11 @@ class OnlineSigned(Signed, TxBase.OnlineSigned):
|
|||
|
||||
await self.status.display()
|
||||
|
||||
async def test_sendable(self):
|
||||
async def test_sendable(self, txhex):
|
||||
|
||||
await self.send_checks()
|
||||
|
||||
res = await self.rpc.call('testmempoolaccept', (self.serialized,))
|
||||
res = await self.rpc.call('testmempoolaccept', (txhex,))
|
||||
ret = res[0]
|
||||
|
||||
if ret['allowed']:
|
||||
|
|
@ -54,44 +54,27 @@ class OnlineSigned(Signed, TxBase.OnlineSigned):
|
|||
msg(ret['reject-reason'])
|
||||
return False
|
||||
|
||||
async def send(self, *, prompt_user=True):
|
||||
|
||||
await self.send_checks()
|
||||
|
||||
if prompt_user:
|
||||
self.confirm_send()
|
||||
|
||||
if self.cfg.bogus_send:
|
||||
m = 'BOGUS transaction NOT sent: {}'
|
||||
else:
|
||||
m = 'Transaction sent: {}'
|
||||
try:
|
||||
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'
|
||||
'Re-run the script with the --coin=bch option.')
|
||||
elif errmsg.count('Illegal use of SIGHASH_FORKID'):
|
||||
m = (
|
||||
'The Aug. 1 2017 UAHF is not yet active on this chain.\n'
|
||||
'Re-run the script without the --coin=bch option.')
|
||||
elif errmsg.count('non-final'):
|
||||
m = "Transaction with nLockTime {!r} can’t be included in this block!".format(
|
||||
self.info.strfmt_locktime(self.get_serialized_locktime()))
|
||||
else:
|
||||
m, nl = ('', '')
|
||||
msg(orange('\n'+errmsg))
|
||||
die(2, f'{m}{nl}Send of MMGen transaction {self.txid} failed')
|
||||
async def send_with_node(self, txhex):
|
||||
try:
|
||||
return await self.rpc.call('sendrawtransaction', txhex)
|
||||
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'
|
||||
'Re-run the script with the --coin=bch option.')
|
||||
elif errmsg.count('Illegal use of SIGHASH_FORKID'):
|
||||
m = (
|
||||
'The Aug. 1 2017 UAHF is not yet active on this chain.\n'
|
||||
'Re-run the script without the --coin=bch option.')
|
||||
elif errmsg.count('non-final'):
|
||||
m = "Transaction with nLockTime {!r} can’t be included in this block!".format(
|
||||
self.info.strfmt_locktime(self.get_serialized_locktime()))
|
||||
else:
|
||||
assert ret == self.coin_txid, 'txid mismatch (after sending)'
|
||||
|
||||
msg(m.format(self.coin_txid.hl()))
|
||||
self.add_sent_timestamp()
|
||||
self.add_blockcount()
|
||||
return True
|
||||
m, nl = ('', '')
|
||||
msg(orange('\n'+errmsg))
|
||||
die(2, f'{m}{nl}Send of MMGen transaction {self.txid} failed')
|
||||
|
||||
def post_write(self):
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -20,42 +20,27 @@ from .signed import Signed, TokenSigned
|
|||
|
||||
class OnlineSigned(Signed, TxBase.OnlineSigned):
|
||||
|
||||
async def test_sendable(self):
|
||||
async def test_sendable(self, txhex):
|
||||
raise NotImplementedError('transaction testing not implemented for Ethereum')
|
||||
|
||||
async def send(self, *, prompt_user=True):
|
||||
|
||||
async def send_checks(self):
|
||||
self.check_correct_chain()
|
||||
|
||||
if not self.disable_fee_check and (self.fee > self.proto.max_tx_fee):
|
||||
die(2, 'Transaction fee ({}) greater than {} max_tx_fee ({} {})!'.format(
|
||||
self.fee,
|
||||
self.proto.name,
|
||||
self.proto.max_tx_fee,
|
||||
self.proto.coin))
|
||||
|
||||
await self.status.display()
|
||||
|
||||
if prompt_user:
|
||||
self.confirm_send()
|
||||
|
||||
if self.cfg.bogus_send:
|
||||
m = 'BOGUS transaction NOT sent: {}'
|
||||
else:
|
||||
try:
|
||||
ret = await self.rpc.call('eth_sendRawTransaction', '0x'+self.serialized)
|
||||
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)
|
||||
|
||||
msg(m.format(self.coin_txid.hl()))
|
||||
self.add_sent_timestamp()
|
||||
self.add_blockcount()
|
||||
|
||||
return True
|
||||
async def send_with_node(self, txhex):
|
||||
try:
|
||||
ret = await self.rpc.call('eth_sendRawTransaction', '0x' + txhex)
|
||||
except Exception as e:
|
||||
msg(orange('\n'+str(e)))
|
||||
die(2, f'Send of MMGen transaction {self.txid} failed')
|
||||
await erigon_sleep(self)
|
||||
return ret.removeprefix('0x')
|
||||
|
||||
def post_write(self):
|
||||
if 'token_addr' in self.txobj and not self.txobj['to']:
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ from ....util import msg, Msg, die, suf, capfirst
|
|||
|
||||
class Status(TxBase.Status):
|
||||
|
||||
async def display(self, *, usr_req=False, return_exit_val=False, print_receipt=False):
|
||||
async def display(self, *, usr_req=False, return_exit_val=False, print_receipt=False, idx=''):
|
||||
|
||||
def do_exit(retval, message):
|
||||
if return_exit_val:
|
||||
|
|
@ -27,6 +27,7 @@ class Status(TxBase.Status):
|
|||
die(retval, message)
|
||||
|
||||
tx = self.tx
|
||||
coin_txid = '0x' + getattr(tx, f'coin_txid{idx}')
|
||||
|
||||
async def is_in_mempool():
|
||||
if not 'full_node' in tx.rpc.caps:
|
||||
|
|
@ -36,12 +37,10 @@ class Status(TxBase.Status):
|
|||
elif tx.rpc.daemon.id in ('geth', 'reth', 'erigon'):
|
||||
res = await tx.rpc.call('txpool_content')
|
||||
pool = list(res['pending']) + list(res['queued'])
|
||||
return '0x'+tx.coin_txid in pool
|
||||
return coin_txid in pool
|
||||
|
||||
async def is_in_wallet():
|
||||
d = await tx.rpc.call(
|
||||
'eth_getTransactionReceipt',
|
||||
'0x' + tx.coin_txid)
|
||||
d = await tx.rpc.call('eth_getTransactionReceipt', coin_txid)
|
||||
if d and 'blockNumber' in d and d['blockNumber'] is not None:
|
||||
from collections import namedtuple
|
||||
receipt_info = namedtuple('receipt_info', ['confs', 'exec_status', 'rx'])
|
||||
|
|
|
|||
|
|
@ -46,6 +46,63 @@ class OnlineSigned(Signed):
|
|||
expect = 'YES' if self.cfg.quiet or self.cfg.yes else 'YES, I REALLY WANT TO DO THIS')
|
||||
msg('Sending transaction')
|
||||
|
||||
async def post_send(self, asi):
|
||||
from . import SentTX
|
||||
tx2 = await SentTX(cfg=self.cfg, data=self.__dict__, automount=bool(asi))
|
||||
tx2.add_sent_timestamp()
|
||||
tx2.add_blockcount()
|
||||
tx2.file.write(
|
||||
outdir = asi.txauto_dir if asi else None,
|
||||
ask_overwrite = False,
|
||||
ask_write = False)
|
||||
tx2.post_write()
|
||||
|
||||
async def send(self, cfg, asi):
|
||||
|
||||
if not (cfg.receipt or cfg.dump_hex or cfg.test):
|
||||
self.confirm_send()
|
||||
|
||||
do_post_send = False
|
||||
|
||||
for idx in ('', '2'):
|
||||
if coin_txid := getattr(self, f'coin_txid{idx}', None):
|
||||
txhex = getattr(self, f'serialized{idx}')
|
||||
if cfg.receipt:
|
||||
import sys
|
||||
sys.exit(await self.status.display(print_receipt=True, idx=idx))
|
||||
elif cfg.dump_hex:
|
||||
from ..fileutil import write_data_to_file
|
||||
write_data_to_file(
|
||||
cfg,
|
||||
cfg.dump_hex + idx,
|
||||
txhex + '\n',
|
||||
desc = 'serialized transaction hex data',
|
||||
ask_overwrite = False,
|
||||
ask_tty = False)
|
||||
elif cfg.tx_proxy:
|
||||
from .tx_proxy import send_tx
|
||||
if ret := send_tx(cfg, txhex):
|
||||
if ret != coin_txid:
|
||||
from ..util import ymsg
|
||||
ymsg(f'Warning: txid mismatch (after sending) ({ret} != {coin_txid})')
|
||||
do_post_send = 'confirm'
|
||||
elif cfg.test:
|
||||
await self.test_sendable(txhex)
|
||||
else: # node send
|
||||
if not cfg.bogus_send:
|
||||
ret = await self.send_with_node(txhex)
|
||||
assert ret == coin_txid, f'txid mismatch (after sending) ({ret} != {coin_txid})'
|
||||
desc = 'BOGUS transaction NOT' if cfg.bogus_send else 'Transaction'
|
||||
from ..util import msg
|
||||
msg(desc + ' sent: ' + coin_txid.hl())
|
||||
do_post_send = 'no_confirm'
|
||||
|
||||
if do_post_send:
|
||||
from ..ui import keypress_confirm
|
||||
if do_post_send == 'no_confirm' or not asi or keypress_confirm(
|
||||
cfg, 'Mark transaction as sent on removable device?'):
|
||||
await self.post_send(asi)
|
||||
|
||||
class AutomountOnlineSigned(AutomountSigned, OnlineSigned):
|
||||
pass
|
||||
|
||||
|
|
|
|||
|
|
@ -170,14 +170,11 @@ class EtherscanTxProxyClient(TxProxyClient):
|
|||
else:
|
||||
return False
|
||||
|
||||
def send_tx(cfg, tx):
|
||||
def send_tx(cfg, txhex):
|
||||
|
||||
c = get_client(cfg)
|
||||
msg(f'Using {pink(cfg.tx_proxy.upper())} tx proxy')
|
||||
|
||||
if not cfg.test:
|
||||
tx.confirm_send()
|
||||
|
||||
msg_r(f'Retrieving form from {orange(c.host)}...')
|
||||
form_text = c.get_form(timeout=180)
|
||||
msg('done')
|
||||
|
|
@ -186,7 +183,7 @@ def send_tx(cfg, tx):
|
|||
post_data = c.create_post_data(
|
||||
form_text = form_text,
|
||||
coin = cfg.coin,
|
||||
tx_hex = tx.serialized)
|
||||
tx_hex = txhex)
|
||||
msg('done')
|
||||
|
||||
if cfg.test:
|
||||
|
|
@ -205,7 +202,7 @@ def send_tx(cfg, tx):
|
|||
msg('Transaction ' + (f'sent: {txid.hl()}' if txid else 'send failed'))
|
||||
c.save_response(result_text, 'result')
|
||||
|
||||
return bool(txid)
|
||||
return txid
|
||||
|
||||
tx_proxies = {
|
||||
'blockchair': BlockchairTxProxyClient,
|
||||
|
|
|
|||
|
|
@ -524,7 +524,6 @@ class CmdTestAutosignThreaded(CmdTestAutosignBase):
|
|||
t.do_comment(comment)
|
||||
if dump_hex:
|
||||
t.written_to_file('Serialized transaction hex data')
|
||||
t.expect('(y/N): ', 'n') # mark as sent?
|
||||
else:
|
||||
self._do_confirm_send(t, quiet=True)
|
||||
t.written_to_file('Sent automount transaction')
|
||||
|
|
|
|||
|
|
@ -308,7 +308,7 @@ class CmdTestEthBump(CmdTestEthBumpMethods, CmdTestEthSwapMethods, CmdTestSwapMe
|
|||
add_opts = ['--token=MM1'],
|
||||
add_args = [dfl_words_file])
|
||||
t.expect('to confirm: ', 'YES\n')
|
||||
t.written_to_file('Signed transaction')
|
||||
t.written_to_file('Sent transaction')
|
||||
return t
|
||||
|
||||
def token_bal2(self):
|
||||
|
|
|
|||
|
|
@ -1207,7 +1207,7 @@ class CmdTestRegtest(CmdTestBase, CmdTestShared):
|
|||
if signed_tx:
|
||||
t.passphrase(dfl_wcls.desc, rt_pw)
|
||||
t.written_to_file('Signed transaction')
|
||||
self.txsend_ui_common(t, caller='txdo', bogus_send=False, file_desc='Signed transaction')
|
||||
self.txsend_ui_common(t, caller='txdo', bogus_send=False)
|
||||
else:
|
||||
t.expect('Save fee-bumped transaction? (y/N): ', 'y')
|
||||
t.written_to_file('Fee-bumped transaction')
|
||||
|
|
@ -2230,7 +2230,8 @@ class CmdTestRegtest(CmdTestBase, CmdTestShared):
|
|||
['-d', self.dump_hex_subdir, f'--dump-hex={file}', '--bob', txfile], no_passthru_opts=['coin'])
|
||||
t.expect('view: ', '\n')
|
||||
t.expect('(y/N): ', '\n') # add comment?
|
||||
t.written_to_file('Sent transaction')
|
||||
if file != '-':
|
||||
t.written_to_file('Serialized transaction hex data')
|
||||
return t
|
||||
|
||||
def bob_dump_hex_dump(self):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue