@@ -20,6 +20,7 @@ def upgrade():
|
||||
op.create_table(
|
||||
'nonce_task_reservation',
|
||||
sa.Column('id', sa.Integer, primary_key=True),
|
||||
sa.Column('address_hex', sa.String(42), nullable=False),
|
||||
sa.Column('nonce', sa.Integer, nullable=False),
|
||||
sa.Column('key', sa.String, nullable=False),
|
||||
sa.Column('date_created', sa.DateTime, nullable=False),
|
||||
|
||||
@@ -20,6 +20,7 @@ def upgrade():
|
||||
op.create_table(
|
||||
'nonce_task_reservation',
|
||||
sa.Column('id', sa.Integer, primary_key=True),
|
||||
sa.Column('address_hex', sa.String(42), nullable=False),
|
||||
sa.Column('nonce', sa.Integer, nullable=False),
|
||||
sa.Column('key', sa.String, nullable=False),
|
||||
sa.Column('date_created', sa.DateTime, nullable=False),
|
||||
|
||||
@@ -24,6 +24,7 @@ def upgrade():
|
||||
sa.Column('blockchain', sa.String),
|
||||
sa.Column("flags", sa.BIGINT(), nullable=False, default=0),
|
||||
sa.Column("date_created", sa.DateTime, nullable=False),
|
||||
sa.Column("otx_id", sa.Integer, nullable=True),
|
||||
)
|
||||
op.create_index('idx_chain_address', 'lock', ['blockchain', 'address'], unique=True)
|
||||
|
||||
|
||||
@@ -116,6 +116,6 @@ class SessionBase(Model):
|
||||
def release_session(session=None):
|
||||
session_key = str(id(session))
|
||||
if SessionBase.localsessions.get(session_key) != None:
|
||||
logg.debug('destroying session {}'.format(session_key))
|
||||
logg.debug('commit and destroy session {}'.format(session_key))
|
||||
session.commit()
|
||||
session.close()
|
||||
|
||||
@@ -4,7 +4,7 @@ import logging
|
||||
|
||||
# third-party imports
|
||||
from sqlalchemy import Column, String, Integer, DateTime, ForeignKey
|
||||
from cic_registry import zero_address
|
||||
from chainlib.eth.constant import ZERO_ADDRESS
|
||||
|
||||
# local imports
|
||||
from cic_eth.db.models.base import SessionBase
|
||||
@@ -35,7 +35,7 @@ class Lock(SessionBase):
|
||||
|
||||
|
||||
@staticmethod
|
||||
def set(chain_str, flags, address=zero_address, session=None, tx_hash=None):
|
||||
def set(chain_str, flags, address=ZERO_ADDRESS, session=None, tx_hash=None):
|
||||
"""Sets flags associated with the given address and chain.
|
||||
|
||||
If a flags entry does not exist it is created.
|
||||
@@ -88,7 +88,7 @@ class Lock(SessionBase):
|
||||
|
||||
|
||||
@staticmethod
|
||||
def reset(chain_str, flags, address=zero_address, session=None):
|
||||
def reset(chain_str, flags, address=ZERO_ADDRESS, session=None):
|
||||
"""Resets flags associated with the given address and chain.
|
||||
|
||||
If the resulting flags entry value is 0, the entry will be deleted.
|
||||
@@ -132,7 +132,7 @@ class Lock(SessionBase):
|
||||
|
||||
|
||||
@staticmethod
|
||||
def check(chain_str, flags, address=zero_address, session=None):
|
||||
def check(chain_str, flags, address=ZERO_ADDRESS, session=None):
|
||||
"""Checks whether all given flags are set for given address and chain.
|
||||
|
||||
Does not validate the address against any other tables or components.
|
||||
|
||||
@@ -55,6 +55,20 @@ class Nonce(SessionBase):
|
||||
conn.execute("UPDATE nonce set nonce = {} WHERE address_hex = '{}'".format(nonce, address))
|
||||
|
||||
|
||||
@staticmethod
|
||||
def __inc(conn, address):
|
||||
#conn.execute("UPDATE nonce set nonce = nonce + 1 WHERE address_hex = '{}'".format(address))
|
||||
q = conn.query(Nonce)
|
||||
q = q.filter(Nonce.address_hex==address)
|
||||
q = q.with_for_update()
|
||||
o = q.first()
|
||||
nonce = o.nonce
|
||||
o.nonce += 1
|
||||
conn.add(o)
|
||||
conn.flush()
|
||||
return nonce
|
||||
|
||||
|
||||
@staticmethod
|
||||
def __init(conn, address, nonce):
|
||||
conn.execute("INSERT INTO nonce (nonce, address_hex) VALUES ({}, '{}')".format(nonce, address))
|
||||
@@ -78,7 +92,7 @@ class Nonce(SessionBase):
|
||||
|
||||
# TODO: Incrementing nonce MUST be done by separate tasks.
|
||||
@staticmethod
|
||||
def next(address, initial_if_not_exists=0):
|
||||
def next(address, initial_if_not_exists=0, session=None):
|
||||
"""Generate next nonce for the given address.
|
||||
|
||||
If there is no previous nonce record for the address, the nonce may be initialized to a specified value, or 0 if no value has been given.
|
||||
@@ -90,28 +104,31 @@ class Nonce(SessionBase):
|
||||
:returns: Nonce
|
||||
:rtype: number
|
||||
"""
|
||||
#session = SessionBase.bind_session(session)
|
||||
session = SessionBase.bind_session(session)
|
||||
|
||||
#session.begin_nested()
|
||||
conn = Nonce.engine.connect()
|
||||
if Nonce.transactional:
|
||||
conn.execute('BEGIN')
|
||||
conn.execute('LOCK TABLE nonce IN SHARE ROW EXCLUSIVE MODE')
|
||||
logg.debug('locking nonce table for address {}'.format(address))
|
||||
nonce = Nonce.__get(conn, address)
|
||||
#conn = Nonce.engine.connect()
|
||||
#if Nonce.transactional:
|
||||
# conn.execute('BEGIN')
|
||||
# conn.execute('LOCK TABLE nonce IN SHARE ROW EXCLUSIVE MODE')
|
||||
# logg.debug('locking nonce table for address {}'.format(address))
|
||||
#nonce = Nonce.__get(conn, address)
|
||||
nonce = Nonce.__get(session, address)
|
||||
logg.debug('get nonce {} for address {}'.format(nonce, address))
|
||||
if nonce == None:
|
||||
nonce = initial_if_not_exists
|
||||
logg.debug('setting default nonce to {} for address {}'.format(nonce, address))
|
||||
Nonce.__init(conn, address, nonce)
|
||||
Nonce.__set(conn, address, nonce+1)
|
||||
if Nonce.transactional:
|
||||
conn.execute('COMMIT')
|
||||
logg.debug('unlocking nonce table for address {}'.format(address))
|
||||
conn.close()
|
||||
#Nonce.__init(conn, address, nonce)
|
||||
Nonce.__init(session, address, nonce)
|
||||
#Nonce.__set(conn, address, nonce+1)
|
||||
nonce = Nonce.__inc(session, address)
|
||||
#if Nonce.transactional:
|
||||
#conn.execute('COMMIT')
|
||||
# logg.debug('unlocking nonce table for address {}'.format(address))
|
||||
#conn.close()
|
||||
#session.commit()
|
||||
|
||||
#SessionBase.release_session(session)
|
||||
SessionBase.release_session(session)
|
||||
return nonce
|
||||
|
||||
|
||||
@@ -119,67 +136,74 @@ class NonceReservation(SessionBase):
|
||||
|
||||
__tablename__ = 'nonce_task_reservation'
|
||||
|
||||
address_hex = Column(String(42))
|
||||
nonce = Column(Integer)
|
||||
key = Column(String)
|
||||
date_created = Column(DateTime, default=datetime.datetime.utcnow)
|
||||
|
||||
|
||||
@staticmethod
|
||||
def peek(key, session=None):
|
||||
def peek(address, key, session=None):
|
||||
session = SessionBase.bind_session(session)
|
||||
|
||||
q = session.query(NonceReservation)
|
||||
q = q.filter(NonceReservation.key==key)
|
||||
q = q.filter(NonceReservation.address_hex==address)
|
||||
o = q.first()
|
||||
|
||||
nonce = None
|
||||
r = None
|
||||
if o != None:
|
||||
nonce = o.nonce
|
||||
r = (o.key, o.nonce)
|
||||
|
||||
session.flush()
|
||||
|
||||
SessionBase.release_session(session)
|
||||
|
||||
return nonce
|
||||
return r
|
||||
|
||||
|
||||
@staticmethod
|
||||
def release(key, session=None):
|
||||
def release(address, key, session=None):
|
||||
|
||||
session = SessionBase.bind_session(session)
|
||||
|
||||
nonce = NonceReservation.peek(key, session=session)
|
||||
o = NonceReservation.peek(address, key, session=session)
|
||||
|
||||
if o == None:
|
||||
SessionBase.release_session(session)
|
||||
raise IntegrityError('"release" called on key {} address {} which does not exists'.format(key, address))
|
||||
|
||||
q = session.query(NonceReservation)
|
||||
q = q.filter(NonceReservation.key==key)
|
||||
q = q.filter(NonceReservation.address_hex==address)
|
||||
o = q.first()
|
||||
|
||||
if o == None:
|
||||
raise IntegrityError('nonce for key {}'.format(nonce))
|
||||
SessionBase.release_session(session)
|
||||
r = (o.key, o.nonce)
|
||||
|
||||
session.delete(o)
|
||||
session.flush()
|
||||
|
||||
SessionBase.release_session(session)
|
||||
|
||||
return nonce
|
||||
return r
|
||||
|
||||
|
||||
@staticmethod
|
||||
def next(address, key, session=None):
|
||||
session = SessionBase.bind_session(session)
|
||||
|
||||
if NonceReservation.peek(key, session) != None:
|
||||
raise IntegrityError('nonce for key {}'.format(key))
|
||||
o = NonceReservation.peek(address, key, session)
|
||||
if o != None:
|
||||
raise IntegrityError('"next" called on nonce for key {} address {} during active key {}'.format(key, address, o[0]))
|
||||
|
||||
nonce = Nonce.next(address)
|
||||
nonce = Nonce.next(address, session=session)
|
||||
|
||||
o = NonceReservation()
|
||||
o.nonce = nonce
|
||||
o.key = key
|
||||
o.address_hex = address
|
||||
session.add(o)
|
||||
r = (key, nonce)
|
||||
|
||||
SessionBase.release_session(session)
|
||||
|
||||
return nonce
|
||||
return r
|
||||
|
||||
@@ -15,7 +15,6 @@ from cic_eth.db.enum import (
|
||||
is_error_status,
|
||||
)
|
||||
from cic_eth.db.error import TxStateChangeError
|
||||
#from cic_eth.eth.util import address_hex_from_signed_tx
|
||||
|
||||
logg = logging.getLogger()
|
||||
|
||||
@@ -95,19 +94,16 @@ class Otx(SessionBase):
|
||||
:type block: number
|
||||
:raises cic_eth.db.error.TxStateChangeError: State change represents a sequence of events that should not exist.
|
||||
"""
|
||||
localsession = session
|
||||
if localsession == None:
|
||||
localsession = SessionBase.create_session()
|
||||
session = SessionBase.bind_session(session)
|
||||
|
||||
if self.block != None:
|
||||
SessionBase.release_session(session)
|
||||
raise TxStateChangeError('Attempted set block {} when block was already {}'.format(block, self.block))
|
||||
self.block = block
|
||||
localsession.add(self)
|
||||
localsession.flush()
|
||||
session.add(self)
|
||||
session.flush()
|
||||
|
||||
if session==None:
|
||||
localsession.commit()
|
||||
localsession.close()
|
||||
SessionBase.release_session(session)
|
||||
|
||||
|
||||
def waitforgas(self, session=None):
|
||||
@@ -123,8 +119,10 @@ class Otx(SessionBase):
|
||||
session = SessionBase.bind_session(session)
|
||||
|
||||
if self.status & StatusBits.FINAL:
|
||||
SessionBase.release_session(session)
|
||||
raise TxStateChangeError('GAS_ISSUES cannot be set on an entry with FINAL state set ({})'.format(status_str(self.status)))
|
||||
if self.status & StatusBits.IN_NETWORK:
|
||||
SessionBase.release_session(session)
|
||||
raise TxStateChangeError('GAS_ISSUES cannot be set on an entry with IN_NETWORK state set ({})'.format(status_str(self.status)))
|
||||
|
||||
self.__set_status(StatusBits.GAS_ISSUES, session)
|
||||
@@ -147,8 +145,10 @@ class Otx(SessionBase):
|
||||
session = SessionBase.bind_session(session)
|
||||
|
||||
if self.status & StatusBits.FINAL:
|
||||
SessionBase.release_session(session)
|
||||
raise TxStateChangeError('FUBAR cannot be set on an entry with FINAL state set ({})'.format(status_str(self.status)))
|
||||
if is_error_status(self.status):
|
||||
SessionBase.release_session(session)
|
||||
raise TxStateChangeError('FUBAR cannot be set on an entry with an error state already set ({})'.format(status_str(self.status)))
|
||||
|
||||
self.__set_status(StatusBits.UNKNOWN_ERROR | StatusBits.FINAL, session)
|
||||
@@ -170,10 +170,13 @@ class Otx(SessionBase):
|
||||
session = SessionBase.bind_session(session)
|
||||
|
||||
if self.status & StatusBits.FINAL:
|
||||
SessionBase.release_session(session)
|
||||
raise TxStateChangeError('REJECTED cannot be set on an entry with FINAL state set ({})'.format(status_str(self.status)))
|
||||
if self.status & StatusBits.IN_NETWORK:
|
||||
SessionBase.release_session(session)
|
||||
raise TxStateChangeError('REJECTED cannot be set on an entry already IN_NETWORK ({})'.format(status_str(self.status)))
|
||||
if is_error_status(self.status):
|
||||
SessionBase.release_session(session)
|
||||
raise TxStateChangeError('REJECTED cannot be set on an entry with an error state already set ({})'.format(status_str(self.status)))
|
||||
|
||||
self.__set_status(StatusBits.NODE_ERROR | StatusBits.FINAL, session)
|
||||
@@ -193,10 +196,13 @@ class Otx(SessionBase):
|
||||
session = SessionBase.bind_session(session)
|
||||
|
||||
if self.status & StatusBits.FINAL:
|
||||
SessionBase.release_session(session)
|
||||
raise TxStateChangeError('OVERRIDDEN/OBSOLETED cannot be set on an entry with FINAL state set ({})'.format(status_str(self.status)))
|
||||
if self.status & StatusBits.IN_NETWORK:
|
||||
SessionBase.release_session(session)
|
||||
raise TxStateChangeError('OVERRIDDEN/OBSOLETED cannot be set on an entry already IN_NETWORK ({})'.format(status_str(self.status)))
|
||||
if self.status & StatusBits.OBSOLETE:
|
||||
SessionBase.release_session(session)
|
||||
raise TxStateChangeError('OVERRIDDEN/OBSOLETED cannot be set on an entry already OBSOLETE ({})'.format(status_str(self.status)))
|
||||
|
||||
self.__set_status(StatusBits.OBSOLETE, session)
|
||||
@@ -216,6 +222,7 @@ class Otx(SessionBase):
|
||||
|
||||
|
||||
if self.status & StatusBits.FINAL:
|
||||
SessionBase.release_session(session)
|
||||
raise TxStateChangeError('OVERRIDDEN/OBSOLETED cannot be set on an entry with FINAL state set ({})'.format(status_str(self.status)))
|
||||
|
||||
self.__set_status(StatusBits.MANUAL, session)
|
||||
@@ -238,8 +245,10 @@ class Otx(SessionBase):
|
||||
session = SessionBase.bind_session(session)
|
||||
|
||||
if self.status & StatusBits.FINAL:
|
||||
SessionBase.release_session(session)
|
||||
raise TxStateChangeError('RETRY cannot be set on an entry with FINAL state set ({})'.format(status_str(self.status)))
|
||||
if not is_error_status(self.status) and not StatusBits.IN_NETWORK & self.status > 0:
|
||||
SessionBase.release_session(session)
|
||||
raise TxStateChangeError('RETRY cannot be set on an entry that has no error ({})'.format(status_str(self.status)))
|
||||
|
||||
self.__set_status(StatusBits.QUEUED, session)
|
||||
@@ -264,8 +273,10 @@ class Otx(SessionBase):
|
||||
session = SessionBase.bind_session(session)
|
||||
|
||||
if self.status & StatusBits.FINAL:
|
||||
SessionBase.release_session(session)
|
||||
raise TxStateChangeError('READYSEND cannot be set on an entry with FINAL state set ({})'.format(status_str(self.status)))
|
||||
if is_error_status(self.status):
|
||||
SessionBase.release_session(session)
|
||||
raise TxStateChangeError('READYSEND cannot be set on an errored state ({})'.format(status_str(self.status)))
|
||||
|
||||
self.__set_status(StatusBits.QUEUED, session)
|
||||
@@ -290,6 +301,7 @@ class Otx(SessionBase):
|
||||
session = SessionBase.bind_session(session)
|
||||
|
||||
if self.status & StatusBits.FINAL:
|
||||
SessionBase.release_session(session)
|
||||
raise TxStateChangeError('SENT cannot be set on an entry with FINAL state set ({})'.format(status_str(self.status)))
|
||||
|
||||
self.__set_status(StatusBits.IN_NETWORK, session)
|
||||
@@ -314,8 +326,10 @@ class Otx(SessionBase):
|
||||
session = SessionBase.bind_session(session)
|
||||
|
||||
if self.status & StatusBits.FINAL:
|
||||
SessionBase.release_session(session)
|
||||
raise TxStateChangeError('SENDFAIL cannot be set on an entry with FINAL state set ({})'.format(status_str(self.status)))
|
||||
if self.status & StatusBits.IN_NETWORK:
|
||||
SessionBase.release_session(session)
|
||||
raise TxStateChangeError('SENDFAIL cannot be set on an entry with IN_NETWORK state set ({})'.format(status_str(self.status)))
|
||||
|
||||
self.__set_status(StatusBits.LOCAL_ERROR | StatusBits.DEFERRED, session)
|
||||
@@ -340,9 +354,11 @@ class Otx(SessionBase):
|
||||
session = SessionBase.bind_session(session)
|
||||
|
||||
if self.status & StatusBits.FINAL:
|
||||
raise TxStateChangeError('SENDFAIL cannot be set on an entry with FINAL state set ({})'.format(status_str(self.status)))
|
||||
SessionBase.release_session(session)
|
||||
raise TxStateChangeError('QUEUED cannot be unset on an entry with FINAL state set ({})'.format(status_str(self.status)))
|
||||
if self.status & StatusBits.IN_NETWORK:
|
||||
raise TxStateChangeError('SENDFAIL cannot be set on an entry with IN_NETWORK state set ({})'.format(status_str(self.status)))
|
||||
SessionBase.release_session(session)
|
||||
raise TxStateChangeError('QUEUED cannot be unset on an entry with IN_NETWORK state set ({})'.format(status_str(self.status)))
|
||||
|
||||
self.__reset_status(StatusBits.QUEUED, session)
|
||||
|
||||
@@ -368,8 +384,10 @@ class Otx(SessionBase):
|
||||
session = SessionBase.bind_session(session)
|
||||
|
||||
if self.status & StatusBits.FINAL:
|
||||
SessionBase.release_session(session)
|
||||
raise TxStateChangeError('REVERTED cannot be set on an entry with FINAL state set ({})'.format(status_str(self.status)))
|
||||
if not self.status & StatusBits.IN_NETWORK:
|
||||
SessionBase.release_session(session)
|
||||
raise TxStateChangeError('REVERTED cannot be set on an entry without IN_NETWORK state set ({})'.format(status_str(self.status)))
|
||||
|
||||
if block != None:
|
||||
@@ -397,10 +415,12 @@ class Otx(SessionBase):
|
||||
session = SessionBase.bind_session(session)
|
||||
|
||||
if self.status & StatusBits.FINAL:
|
||||
SessionBase.release_session(session)
|
||||
raise TxStateChangeError('CANCEL cannot be set on an entry with FINAL state set ({})'.format(status_str(self.status)))
|
||||
|
||||
if confirmed:
|
||||
if self.status > 0 and not self.status & StatusBits.OBSOLETE:
|
||||
SessionBase.release_session(session)
|
||||
raise TxStateChangeError('CANCEL can only be set on an entry marked OBSOLETE ({})'.format(status_str(self.status)))
|
||||
self.__set_status(StatusEnum.CANCELLED, session)
|
||||
else:
|
||||
@@ -425,10 +445,13 @@ class Otx(SessionBase):
|
||||
session = SessionBase.bind_session(session)
|
||||
|
||||
if self.status & StatusBits.FINAL:
|
||||
SessionBase.release_session(session)
|
||||
raise TxStateChangeError('SUCCESS cannot be set on an entry with FINAL state set ({})'.format(status_str(self.status)))
|
||||
if not self.status & StatusBits.IN_NETWORK:
|
||||
SessionBase.release_session(session)
|
||||
raise TxStateChangeError('SUCCESS cannot be set on an entry without IN_NETWORK state set ({})'.format(status_str(self.status)))
|
||||
if is_error_status(self.status):
|
||||
SessionBase.release_session(session)
|
||||
raise TxStateChangeError('SUCCESS cannot be set on an entry with error state set ({})'.format(status_str(self.status)))
|
||||
|
||||
if block != None:
|
||||
@@ -509,22 +532,23 @@ class Otx(SessionBase):
|
||||
session.add(l)
|
||||
|
||||
|
||||
# TODO: it is not safe to return otx here unless session has been passed in
|
||||
@staticmethod
|
||||
def add(nonce, address, tx_hash, signed_tx, session=None):
|
||||
localsession = session
|
||||
if localsession == None:
|
||||
localsession = SessionBase.create_session()
|
||||
external_session = session != None
|
||||
|
||||
session = SessionBase.bind_session(session)
|
||||
|
||||
otx = Otx(nonce, address, tx_hash, signed_tx)
|
||||
localsession.add(otx)
|
||||
localsession.flush()
|
||||
session.add(otx)
|
||||
session.flush()
|
||||
if otx.tracing:
|
||||
otx.__state_log(session=localsession)
|
||||
localsession.flush()
|
||||
otx.__state_log(session=session)
|
||||
session.flush()
|
||||
|
||||
if session==None:
|
||||
localsession.commit()
|
||||
localsession.close()
|
||||
SessionBase.release_session(session)
|
||||
|
||||
if not external_session:
|
||||
return None
|
||||
|
||||
return otx
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
# standard imports
|
||||
import logging
|
||||
|
||||
# third-party imports
|
||||
# external imports
|
||||
from sqlalchemy import Column, String, Text
|
||||
from cic_registry import zero_address
|
||||
from chainlib.eth.constant import ZERO_ADDRESS
|
||||
|
||||
# local imports
|
||||
from .base import SessionBase
|
||||
@@ -42,7 +42,7 @@ class AccountRole(SessionBase):
|
||||
|
||||
role = AccountRole.__get_role(tag, session)
|
||||
|
||||
r = zero_address
|
||||
r = ZERO_ADDRESS
|
||||
if role != None:
|
||||
r = role.address_hex
|
||||
|
||||
@@ -133,4 +133,4 @@ class AccountRole(SessionBase):
|
||||
|
||||
def __init__(self, tag):
|
||||
self.tag = tag
|
||||
self.address_hex = zero_address
|
||||
self.address_hex = ZERO_ADDRESS
|
||||
|
||||
Reference in New Issue
Block a user