chainqueue/chainqueue/sql/state.py

427 lines
13 KiB
Python
Raw Normal View History

2021-04-02 11:51:00 +02:00
# standard imports
import logging
# external imports
from hexathon import strip_0x
# local imports
from chainqueue.db.models.otx import Otx
from chainqueue.db.models.tx import TxCache
from chainqueue.db.models.base import SessionBase
from chainqueue.db.enum import (
StatusEnum,
StatusBits,
is_nascent,
2021-04-02 11:51:00 +02:00
)
from chainqueue.db.models.otx import OtxStateLog
from chainqueue.error import (
NotLocalTxError,
TxStateChangeError,
)
logg = logging.getLogger().getChild(__name__)
2021-04-03 00:17:05 +02:00
def set_sent(chain_spec, tx_hash, fail=False, session=None):
2021-04-02 11:51:00 +02:00
"""Used to set the status after a send attempt
2021-08-26 10:05:56 +02:00
:param chain_spec: Chain spec for transaction network
:type chain_spec: chainlib.chain.ChainSpec
2021-04-02 11:51:00 +02:00
:param tx_hash: Transaction hash of record to modify
:type tx_hash: str, 0x-hex
:param fail: if True, will set a SENDFAIL status, otherwise a SENT status. (Default: False)
:type fail: boolean
2021-08-26 10:05:56 +02:00
:param session: Backend state integrity session
:type session: varies
2021-04-02 11:51:00 +02:00
:raises NotLocalTxError: If transaction not found in queue.
:returns: True if tx is known, False otherwise
:rtype: boolean
"""
2021-04-03 00:17:05 +02:00
session = SessionBase.bind_session(session)
2021-04-02 11:51:00 +02:00
o = Otx.load(tx_hash, session=session)
if o == None:
logg.warning('not local tx, skipping {}'.format(tx_hash))
2021-04-03 00:17:05 +02:00
SessionBase.release_session(session)
2021-04-02 11:51:00 +02:00
return False
try:
if fail:
o.sendfail(session=session)
else:
o.sent(session=session)
except TxStateChangeError as e:
logg.exception('set sent fail: {}'.format(e))
2021-04-03 00:17:05 +02:00
SessionBase.release_session(session)
2021-04-02 11:51:00 +02:00
raise(e)
except Exception as e:
logg.exception('set sent UNEXPECED fail: {}'.format(e))
2021-04-03 00:17:05 +02:00
SessionBase.release_session(session)
2021-04-02 11:51:00 +02:00
raise(e)
session.commit()
2021-04-03 00:17:05 +02:00
SessionBase.release_session(session)
2021-04-02 11:51:00 +02:00
return tx_hash
def set_final(chain_spec, tx_hash, block=None, tx_index=None, fail=False, session=None):
2021-04-02 11:51:00 +02:00
"""Used to set the status of an incoming transaction result.
2021-08-26 10:05:56 +02:00
:param chain_spec: Chain spec for transaction network
:type chain_spec: chainlib.chain.ChainSpec
2021-04-02 11:51:00 +02:00
:param tx_hash: Transaction hash of record to modify
:type tx_hash: str, 0x-hex
:param block: Block number if final status represents a confirmation on the network
:type block: number
2021-08-26 10:05:56 +02:00
:param tx_index: Transaction index if final status represents a confirmation on the network
:type tx_index: number
2021-04-02 11:51:00 +02:00
:param fail: if True, will set a SUCCESS status, otherwise a REVERTED status. (Default: False)
:type fail: boolean
2021-08-26 10:05:56 +02:00
:param session: Backend state integrity session
:type session: varies
2021-04-02 11:51:00 +02:00
:raises NotLocalTxError: If transaction not found in queue.
2021-08-26 10:05:56 +02:00
:rtype: str
:returns: Transaction hash, in hex
2021-04-02 11:51:00 +02:00
"""
2021-04-03 00:17:05 +02:00
session = SessionBase.bind_session(session)
2021-04-02 11:51:00 +02:00
o = Otx.load(tx_hash, session=session)
if o == None:
2021-04-03 00:17:05 +02:00
SessionBase.release_session(session)
2021-04-02 11:51:00 +02:00
raise NotLocalTxError('queue does not contain tx hash {}'.format(tx_hash))
session.flush()
try:
if fail:
o.minefail(block, session=session)
else:
o.success(block, session=session)
except TxStateChangeError as e:
logg.exception('set final fail: {}'.format(e))
2021-04-03 00:17:05 +02:00
SessionBase.release_session(session)
2021-04-02 11:51:00 +02:00
raise(e)
except Exception as e:
logg.exception('set final UNEXPECTED fail: {}'.format(e))
2021-04-03 00:17:05 +02:00
SessionBase.release_session(session)
2021-04-02 11:51:00 +02:00
raise(e)
if block != None:
try:
TxCache.set_final(o.tx_hash, block, tx_index, session=session)
except NotLocalTxError:
logg.debug('otx for {} does not have cache complement'.format(tx_hash))
session.commit()
2021-04-03 00:17:05 +02:00
SessionBase.release_session(session)
2021-04-02 11:51:00 +02:00
return tx_hash
2021-04-03 00:17:05 +02:00
def set_cancel(chain_spec, tx_hash, manual=False, session=None):
2021-04-02 11:51:00 +02:00
"""Used to set the status when a transaction is cancelled.
Will set the state to CANCELLED or OVERRIDDEN
2021-08-26 10:05:56 +02:00
:param chain_spec: Chain spec for transaction network
:type chain_spec: chainlib.chain.ChainSpec
2021-04-02 11:51:00 +02:00
:param tx_hash: Transaction hash of record to modify
:type tx_hash: str, 0x-hex
:param manual: If set, status will be OVERRIDDEN. Otherwise CANCELLED.
:type manual: boolean
2021-08-26 10:05:56 +02:00
:param session: Backend state integrity session
:type session: varies
2021-04-02 11:51:00 +02:00
:raises NotLocalTxError: If transaction not found in queue.
2021-08-26 10:05:56 +02:00
:rtype: str
:returns: Transaction hash, in hex
2021-04-02 11:51:00 +02:00
"""
2021-04-03 00:17:05 +02:00
session = SessionBase.bind_session(session)
2021-04-02 13:04:31 +02:00
o = Otx.load(tx_hash, session=session)
2021-04-02 11:51:00 +02:00
if o == None:
2021-04-03 00:17:05 +02:00
SessionBase.release_session(session)
2021-04-02 11:51:00 +02:00
raise NotLocalTxError('queue does not contain tx hash {}'.format(tx_hash))
session.flush()
try:
if manual:
o.override(session=session)
else:
o.cancel(session=session)
session.commit()
except TxStateChangeError as e:
logg.exception('set cancel fail: {}'.format(e))
except Exception as e:
logg.exception('set cancel UNEXPECTED fail: {}'.format(e))
2021-04-03 00:17:05 +02:00
SessionBase.release_session(session)
2021-04-02 11:51:00 +02:00
return tx_hash
2021-04-03 00:17:05 +02:00
def set_rejected(chain_spec, tx_hash, session=None):
2021-04-02 11:51:00 +02:00
"""Used to set the status when the node rejects sending a transaction to network
Will set the state to REJECTED
2021-08-26 10:05:56 +02:00
:param chain_spec: Chain spec for transaction network
:type chain_spec: chainlib.chain.ChainSpec
2021-04-02 11:51:00 +02:00
:param tx_hash: Transaction hash of record to modify
:type tx_hash: str, 0x-hex
2021-08-26 10:05:56 +02:00
:param session: Backend state integrity session
:type session: varies
2021-04-02 11:51:00 +02:00
:raises NotLocalTxError: If transaction not found in queue.
2021-08-26 10:05:56 +02:00
:rtype: str
:returns: Transaction hash, in hex
2021-04-02 11:51:00 +02:00
"""
2021-04-03 00:17:05 +02:00
session = SessionBase.bind_session(session)
2021-04-02 13:04:31 +02:00
o = Otx.load(tx_hash, session=session)
2021-04-02 11:51:00 +02:00
if o == None:
2021-04-03 00:17:05 +02:00
SessionBase.release_session(session)
2021-04-02 11:51:00 +02:00
raise NotLocalTxError('queue does not contain tx hash {}'.format(tx_hash))
session.flush()
o.reject(session=session)
session.commit()
2021-04-03 00:17:05 +02:00
SessionBase.release_session(session)
2021-04-02 11:51:00 +02:00
return tx_hash
2021-04-03 00:17:05 +02:00
def set_fubar(chain_spec, tx_hash, session=None):
2021-04-02 11:51:00 +02:00
"""Used to set the status when an unexpected error occurs.
Will set the state to FUBAR
2021-08-26 10:05:56 +02:00
:param chain_spec: Chain spec for transaction network
:type chain_spec: chainlib.chain.ChainSpec
2021-04-02 11:51:00 +02:00
:param tx_hash: Transaction hash of record to modify
:type tx_hash: str, 0x-hex
2021-08-26 10:05:56 +02:00
:param session: Backend state integrity session
:type session: varies
2021-04-02 11:51:00 +02:00
:raises NotLocalTxError: If transaction not found in queue.
2021-08-26 10:05:56 +02:00
:rtype: str
:returns: Transaction hash, in hex
2021-04-02 11:51:00 +02:00
"""
2021-04-03 00:17:05 +02:00
session = SessionBase.bind_session(session)
2021-04-02 13:04:31 +02:00
o = Otx.load(tx_hash, session=session)
2021-04-02 11:51:00 +02:00
if o == None:
2021-04-03 00:17:05 +02:00
SessionBase.release_session(session)
2021-04-02 11:51:00 +02:00
raise NotLocalTxError('queue does not contain tx hash {}'.format(tx_hash))
session.flush()
o.fubar(session=session)
session.commit()
2021-04-03 00:17:05 +02:00
SessionBase.release_session(session)
2021-04-02 11:51:00 +02:00
return tx_hash
2021-04-03 00:17:05 +02:00
def set_manual(chain_spec, tx_hash, session=None):
2021-04-02 11:51:00 +02:00
"""Used to set the status when queue is manually changed
Will set the state to MANUAL
2021-08-26 10:05:56 +02:00
:param chain_spec: Chain spec for transaction network
:type chain_spec: chainlib.chain.ChainSpec
2021-04-02 11:51:00 +02:00
:param tx_hash: Transaction hash of record to modify
:type tx_hash: str, 0x-hex
2021-08-26 10:05:56 +02:00
:param session: Backend state integrity session
:type session: varies
2021-04-02 11:51:00 +02:00
:raises NotLocalTxError: If transaction not found in queue.
2021-08-26 10:05:56 +02:00
:rtype: str
:returns: Transaction hash, in hex
2021-04-02 11:51:00 +02:00
"""
2021-04-03 00:17:05 +02:00
session = SessionBase.bind_session(session)
2021-04-02 11:51:00 +02:00
o = Otx.load(tx_hash, session=session)
if o == None:
2021-04-03 00:17:05 +02:00
SessionBase.release_session(session)
2021-04-02 11:51:00 +02:00
raise NotLocalTxError('queue does not contain tx hash {}'.format(tx_hash))
session.flush()
o.manual(session=session)
session.commit()
2021-04-03 00:17:05 +02:00
SessionBase.release_session(session)
2021-04-02 11:51:00 +02:00
return tx_hash
2021-04-03 00:17:05 +02:00
def set_ready(chain_spec, tx_hash, session=None):
2021-04-02 11:51:00 +02:00
"""Used to mark a transaction as ready to be sent to network
2021-08-26 10:05:56 +02:00
:param chain_spec: Chain spec for transaction network
:type chain_spec: chainlib.chain.ChainSpec
2021-04-02 11:51:00 +02:00
:param tx_hash: Transaction hash of record to modify
:type tx_hash: str, 0x-hex
2021-08-26 10:05:56 +02:00
:param session: Backend state integrity session
:type session: varies
2021-04-02 11:51:00 +02:00
:raises NotLocalTxError: If transaction not found in queue.
2021-08-26 10:05:56 +02:00
:rtype: str
:returns: Transaction hash, in hex
2021-04-02 11:51:00 +02:00
"""
2021-04-03 00:17:05 +02:00
session = SessionBase.bind_session(session)
2021-04-02 11:51:00 +02:00
o = Otx.load(tx_hash, session=session)
if o == None:
2021-04-03 00:17:05 +02:00
SessionBase.release_session(session)
2021-04-02 11:51:00 +02:00
raise NotLocalTxError('queue does not contain tx hash {}'.format(tx_hash))
session.flush()
if o.status & StatusBits.GAS_ISSUES or is_nascent(o.status):
2021-04-02 11:51:00 +02:00
o.readysend(session=session)
else:
o.retry(session=session)
session.commit()
2021-04-03 00:17:05 +02:00
SessionBase.release_session(session)
2021-04-02 11:51:00 +02:00
return tx_hash
2021-04-03 00:17:05 +02:00
def set_reserved(chain_spec, tx_hash, session=None):
2021-08-26 10:05:56 +02:00
"""Used to mark a transaction as reserved by a worker for processing.
:param chain_spec: Chain spec for transaction network
:type chain_spec: chainlib.chain.ChainSpec
:param tx_hash: Transaction hash of record to modify
:type tx_hash: str, 0x-hex
:param session: Backend state integrity session
:type session: varies
:raises NotLocalTxError: If transaction not found in queue.
"""
2021-04-03 00:17:05 +02:00
session = SessionBase.bind_session(session)
2021-04-02 11:51:00 +02:00
o = Otx.load(tx_hash, session=session)
if o == None:
2021-04-03 00:17:05 +02:00
SessionBase.release_session(session)
2021-04-02 11:51:00 +02:00
raise NotLocalTxError('queue does not contain tx hash {}'.format(tx_hash))
session.flush()
o.reserve(session=session)
session.commit()
2021-04-03 00:17:05 +02:00
SessionBase.release_session(session)
2021-04-02 11:51:00 +02:00
return tx_hash
2021-04-03 00:17:05 +02:00
def set_waitforgas(chain_spec, tx_hash, session=None):
2021-04-02 11:51:00 +02:00
"""Used to set the status when a transaction must be deferred due to gas refill
Will set the state to WAITFORGAS
2021-08-26 10:05:56 +02:00
:param chain_spec: Chain spec for transaction network
:type chain_spec: chainlib.chain.ChainSpec
2021-04-02 11:51:00 +02:00
:param tx_hash: Transaction hash of record to modify
:type tx_hash: str, 0x-hex
2021-08-26 10:05:56 +02:00
:param session: Backend state integrity session
:type session: varies
2021-04-02 11:51:00 +02:00
:raises NotLocalTxError: If transaction not found in queue.
2021-08-26 10:05:56 +02:00
:rtype: str
:returns: Transaction hash, in hex
2021-04-02 11:51:00 +02:00
"""
2021-04-03 00:17:05 +02:00
session = SessionBase.bind_session(session)
2021-04-02 11:51:00 +02:00
o = Otx.load(tx_hash, session=session)
if o == None:
2021-04-03 00:17:05 +02:00
SessionBase.release_session(session)
2021-04-02 11:51:00 +02:00
raise NotLocalTxError('queue does not contain tx hash {}'.format(tx_hash))
session.flush()
o.waitforgas(session=session)
session.commit()
2021-04-03 00:17:05 +02:00
SessionBase.release_session(session)
2021-04-02 11:51:00 +02:00
return tx_hash
2021-04-03 00:17:05 +02:00
def get_state_log(chain_spec, tx_hash, session=None):
2021-08-26 10:05:56 +02:00
"""If state log is activated, retrieves all state log changes for the given transaction.
2021-04-02 11:51:00 +02:00
2021-08-26 10:05:56 +02:00
:param chain_spec: Chain spec for transaction network
:type chain_spec: chainlib.chain.ChainSpec
:param tx_hash: Transaction hash of record to modify
:type tx_hash: str, 0x-hex
:param session: Backend state integrity session
:type session: varies
:raises NotLocalTxError: If transaction not found in queue.
:rtype: list
:returns: Log items
"""
2021-04-02 11:51:00 +02:00
logs = []
2021-04-03 00:17:05 +02:00
session = SessionBase.bind_session(session)
2021-04-02 11:51:00 +02:00
q = session.query(OtxStateLog)
q = q.join(Otx)
q = q.filter(Otx.tx_hash==strip_0x(tx_hash))
2021-04-02 11:51:00 +02:00
q = q.order_by(OtxStateLog.date.asc())
for l in q.all():
logs.append((l.date, l.status,))
2021-04-03 00:17:05 +02:00
SessionBase.release_session(session)
2021-04-02 11:51:00 +02:00
return logs
def obsolete_by_cache(chain_spec, tx_hash, final, session=None):
2021-08-26 10:05:56 +02:00
"""Explicitly obsolete single transaction by transaction with same nonce.
:param chain_spec: Chain spec for transaction network
:type chain_spec: chainlib.chain.ChainSpec
:param tx_hash: Transaction hash of record to modify, in hex
:type tx_hash: str
:param final: Transaction hash superseding record, in hex
:type tx_hash: str
:param session: Backend state integrity session
:type session: varies
:raises TxStateChangeError: Transaction is not obsoletable
:rtype: str
:returns: Transaction hash, in hex
"""
session = SessionBase.bind_session(session)
2021-04-02 11:51:00 +02:00
q = session.query(
Otx.nonce.label('nonce'),
TxCache.sender.label('sender'),
Otx.id.label('otxid'),
)
q = q.join(TxCache)
q = q.filter(Otx.tx_hash==strip_0x(tx_hash))
o = q.first()
nonce = o.nonce
sender = o.sender
otxid = o.otxid
q = session.query(Otx)
q = q.join(TxCache)
q = q.filter(Otx.nonce==nonce)
q = q.filter(TxCache.sender==sender)
q = q.filter(Otx.tx_hash!=strip_0x(tx_hash))
for otwo in q.all():
try:
otwo.cancel(final, session=session)
logg.debug('cancel {} final {}'.format(tx_hash, final))
2021-04-02 11:51:00 +02:00
except TxStateChangeError as e:
logg.exception('cancel non-final fail: {}'.format(e))
session.close()
raise(e)
except Exception as e:
logg.exception('cancel non-final UNEXPECTED fail: {}'.format(e))
session.close()
raise(e)
session.commit()
SessionBase.release_session(session)
2021-04-02 11:51:00 +02:00
return tx_hash