chainqueue/chainqueue/sql/tx.py

126 lines
3.6 KiB
Python

# standard imports
import logging
import copy
# external imports
from hexathon import (
uniform as hex_uniform,
add_0x,
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 (
StatusBits,
)
from chainqueue.error import TxStateChangeError
logg = logging.getLogger().getChild(__name__)
def create(chain_spec, nonce, holder_address, tx_hash, signed_tx, obsolete_predecessors=True, session=None):
"""Create a new transaction queue record.
:param chain_spec: Chain spec of transaction network
:type chain_spec: chainlib.chain.ChainSpec
:param nonce: Transaction nonce
:type nonce: int
:param holder_address: Sender address
:type holder_address: str, 0x-hex
:param tx_hash: Transaction hash
:type tx_hash: str, 0x-hex
:param signed_tx: Signed raw transaction
:type signed_tx: str, 0x-hex
:param obsolete_predecessors: If true will mark all other transactions with the same nonce as obsolete (should not be retried)
:type obsolete_predecessors: bool
:param session: Backend state integrity session
:type session: varies
:returns: transaction hash, in hex
:rtype: str
"""
session = SessionBase.bind_session(session)
holder_address = holder_address.lower()
tx_hash = tx_hash.lower()
signed_tx = signed_tx.lower()
o = Otx.add(
nonce=nonce,
tx_hash=tx_hash,
signed_tx=signed_tx,
session=session,
)
session.flush()
# TODO: No magic, please, should be separate step
if obsolete_predecessors:
q = session.query(Otx)
q = q.join(TxCache)
q = q.filter(Otx.nonce==nonce)
q = q.filter(TxCache.sender==holder_address)
q = q.filter(Otx.tx_hash!=tx_hash)
q = q.filter(Otx.status.op('&')(StatusBits.FINAL)==0)
for otx in q.all():
logg.info('otx {} obsoleted by {}'.format(otx.tx_hash, tx_hash))
try:
otx.cancel(confirmed=False, session=session)
except TxStateChangeError as e:
logg.exception('obsolete fail: {}'.format(e))
session.close()
raise(e)
except Exception as e:
logg.exception('obsolete UNEXPECTED fail: {}'.format(e))
session.close()
raise(e)
session.commit()
SessionBase.release_session(session)
logg.debug('queue created nonce {} from {} hash {}'.format(nonce, holder_address, tx_hash))
return tx_hash
def cache_tx_dict(tx_dict, session=None):
"""Add a transaction cache entry to backend.
:param tx_dict: Transaction cache details
:type tx_dict: dict
:param session: Backend state integrity session
:type session: varies
:rtype: tuple
:returns: original transaction, backend insertion id
"""
session = SessionBase.bind_session(session)
ntx = copy.copy(tx_dict)
for k in [
'hash',
'from',
'to',
'source_token',
'destination_token',
]:
ntx[k] = add_0x(hex_uniform(strip_0x(ntx[k])))
txc = TxCache(
ntx['hash'],
ntx['from'],
ntx['to'],
ntx['source_token'],
ntx['destination_token'],
ntx['from_value'],
ntx['to_value'],
session=session
)
session.add(txc)
session.commit()
insert_id = txc.id
SessionBase.release_session(session)
return (ntx, insert_id)