202 lines
6.1 KiB
Python
202 lines
6.1 KiB
Python
|
# standard imports
|
|||
|
import logging
|
|||
|
|
|||
|
# local imports
|
|||
|
from cic_eth.db.models.sync import BlockchainSync
|
|||
|
from cic_eth.db.models.base import SessionBase
|
|||
|
|
|||
|
logg = logging.getLogger()
|
|||
|
|
|||
|
|
|||
|
class SyncerBackend:
|
|||
|
"""Interface to block and transaction sync state.
|
|||
|
|
|||
|
:param chain_spec: Chain spec for the chain that syncer is running for.
|
|||
|
:type chain_spec: cic_registry.chain.ChainSpec
|
|||
|
:param object_id: Unique id for the syncer session.
|
|||
|
:type object_id: number
|
|||
|
"""
|
|||
|
def __init__(self, chain_spec, object_id):
|
|||
|
self.db_session = None
|
|||
|
self.db_object = None
|
|||
|
self.chain_spec = chain_spec
|
|||
|
self.object_id = object_id
|
|||
|
self.connect()
|
|||
|
self.disconnect()
|
|||
|
|
|||
|
|
|||
|
def connect(self):
|
|||
|
"""Loads the state of the syncer session with the given id.
|
|||
|
"""
|
|||
|
if self.db_session == None:
|
|||
|
self.db_session = SessionBase.create_session()
|
|||
|
q = self.db_session.query(BlockchainSync)
|
|||
|
q = q.filter(BlockchainSync.id==self.object_id)
|
|||
|
self.db_object = q.first()
|
|||
|
if self.db_object == None:
|
|||
|
self.disconnect()
|
|||
|
raise ValueError('sync entry with id {} not found'.format(self.object_id))
|
|||
|
return self.db_session
|
|||
|
|
|||
|
|
|||
|
def disconnect(self):
|
|||
|
"""Commits state of sync to backend.
|
|||
|
"""
|
|||
|
if self.db_session != None:
|
|||
|
self.db_session.add(self.db_object)
|
|||
|
self.db_session.commit()
|
|||
|
self.db_session.close()
|
|||
|
self.db_session = None
|
|||
|
|
|||
|
|
|||
|
def chain(self):
|
|||
|
"""Returns chain spec for syncer
|
|||
|
|
|||
|
:returns: Chain spec
|
|||
|
:rtype chain_spec: cic_registry.chain.ChainSpec
|
|||
|
"""
|
|||
|
return self.chain_spec
|
|||
|
|
|||
|
|
|||
|
def get(self):
|
|||
|
"""Get the current state of the syncer cursor.
|
|||
|
|
|||
|
:returns: Block and block transaction height, respectively
|
|||
|
:rtype: tuple
|
|||
|
"""
|
|||
|
self.connect()
|
|||
|
pair = self.db_object.cursor()
|
|||
|
self.disconnect()
|
|||
|
return pair
|
|||
|
|
|||
|
|
|||
|
def set(self, block_height, tx_height):
|
|||
|
"""Update the state of the syncer cursor
|
|||
|
:param block_height: Block height of cursor
|
|||
|
:type block_height: number
|
|||
|
:param tx_height: Block transaction height of cursor
|
|||
|
:type tx_height: number
|
|||
|
:returns: Block and block transaction height, respectively
|
|||
|
:rtype: tuple
|
|||
|
"""
|
|||
|
self.connect()
|
|||
|
pair = self.db_object.set(block_height, tx_height)
|
|||
|
self.disconnect()
|
|||
|
return pair
|
|||
|
|
|||
|
|
|||
|
def start(self):
|
|||
|
"""Get the initial state of the syncer cursor.
|
|||
|
|
|||
|
:returns: Initial block and block transaction height, respectively
|
|||
|
:rtype: tuple
|
|||
|
"""
|
|||
|
self.connect()
|
|||
|
pair = self.db_object.start()
|
|||
|
self.disconnect()
|
|||
|
return pair
|
|||
|
|
|||
|
|
|||
|
def target(self):
|
|||
|
"""Get the target state (upper bound of sync) of the syncer cursor.
|
|||
|
|
|||
|
:returns: Target block height
|
|||
|
:rtype: number
|
|||
|
"""
|
|||
|
self.connect()
|
|||
|
target = self.db_object.target()
|
|||
|
self.disconnect()
|
|||
|
return target
|
|||
|
|
|||
|
|
|||
|
@staticmethod
|
|||
|
def first(chain):
|
|||
|
"""Returns the model object of the most recent syncer in backend.
|
|||
|
|
|||
|
:param chain: Chain spec of chain that syncer is running for.
|
|||
|
:type chain: cic_registry.chain.ChainSpec
|
|||
|
:returns: Last syncer object
|
|||
|
:rtype: cic_eth.db.models.BlockchainSync
|
|||
|
"""
|
|||
|
return BlockchainSync.first(chain)
|
|||
|
|
|||
|
|
|||
|
@staticmethod
|
|||
|
def initial(chain, block_height):
|
|||
|
"""Creates a new syncer session and commit its initial state to backend.
|
|||
|
|
|||
|
:param chain: Chain spec of chain that syncer is running for.
|
|||
|
:type chain: cic_registry.chain.ChainSpec
|
|||
|
:param block_height: Target block height
|
|||
|
:type block_height: number
|
|||
|
:returns: New syncer object
|
|||
|
:rtype: cic_eth.db.models.BlockchainSync
|
|||
|
"""
|
|||
|
object_id = None
|
|||
|
session = SessionBase.create_session()
|
|||
|
o = BlockchainSync(chain, 0, 0, block_height)
|
|||
|
session.add(o)
|
|||
|
session.commit()
|
|||
|
object_id = o.id
|
|||
|
session.close()
|
|||
|
|
|||
|
return SyncerBackend(chain, object_id)
|
|||
|
|
|||
|
|
|||
|
@staticmethod
|
|||
|
def resume(chain, block_height):
|
|||
|
"""Retrieves and returns all previously unfinished syncer sessions.
|
|||
|
|
|||
|
|
|||
|
:param chain: Chain spec of chain that syncer is running for.
|
|||
|
:type chain: cic_registry.chain.ChainSpec
|
|||
|
:param block_height: Target block height
|
|||
|
:type block_height: number
|
|||
|
:returns: Syncer objects of unfinished syncs
|
|||
|
:rtype: list of cic_eth.db.models.BlockchainSync
|
|||
|
"""
|
|||
|
syncers = []
|
|||
|
|
|||
|
session = SessionBase.create_session()
|
|||
|
|
|||
|
object_id = None
|
|||
|
|
|||
|
for object_id in BlockchainSync.get_unsynced(session=session):
|
|||
|
logg.debug('block syncer resume added previously unsynced sync entry id {}'.format(object_id))
|
|||
|
syncers.append(SyncerBackend(chain, object_id))
|
|||
|
|
|||
|
(block_resume, tx_resume) = BlockchainSync.get_last_live_height(block_height, session=session)
|
|||
|
if block_height != block_resume:
|
|||
|
o = BlockchainSync(chain, block_resume, tx_resume, block_height)
|
|||
|
session.add(o)
|
|||
|
session.commit()
|
|||
|
object_id = o.id
|
|||
|
syncers.append(SyncerBackend(chain, object_id))
|
|||
|
logg.debug('block syncer resume added new sync entry from previous run id {}, start{}:{} target {}'.format(object_id, block_resume, tx_resume, block_height))
|
|||
|
|
|||
|
session.close()
|
|||
|
|
|||
|
return syncers
|
|||
|
|
|||
|
|
|||
|
@staticmethod
|
|||
|
def live(chain, block_height):
|
|||
|
"""Creates a new open-ended syncer session starting at the given block height.
|
|||
|
|
|||
|
:param chain: Chain spec of chain that syncer is running for.
|
|||
|
:type chain: cic_registry.chain.ChainSpec
|
|||
|
:param block_height: Target block height
|
|||
|
:type block_height: number
|
|||
|
:returns: "Live" syncer object
|
|||
|
:rtype: cic_eth.db.models.BlockchainSync
|
|||
|
"""
|
|||
|
object_id = None
|
|||
|
session = SessionBase.create_session()
|
|||
|
o = BlockchainSync(chain, block_height, 0, None)
|
|||
|
session.add(o)
|
|||
|
session.commit()
|
|||
|
object_id = o.id
|
|||
|
session.close()
|
|||
|
|
|||
|
return SyncerBackend(chain, object_id)
|