Add chain interface driver
This commit is contained in:
parent
e8decb9cb7
commit
8527901e6c
1
.gitignore
vendored
1
.gitignore
vendored
@ -6,3 +6,4 @@ gmon.out
|
||||
build/
|
||||
dist/
|
||||
*.sqlite
|
||||
old/
|
||||
|
1
chainsyncer/driver/__init__.py
Normal file
1
chainsyncer/driver/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from .base import SyncDriver
|
@ -94,11 +94,12 @@ class SyncDriver:
|
||||
|
||||
def loop(self, conn, item, interval=1):
|
||||
logg.debug('started loop')
|
||||
tx_start = item.tx_cursor
|
||||
while self.running and SyncDriver.running_global:
|
||||
self.last_start = time.clock_gettime_ns(self.clock_id)
|
||||
|
||||
if self.pre_callback != None:
|
||||
self.pre_callback()
|
||||
|
||||
while True and self.running:
|
||||
try:
|
||||
block = self.get(conn, item)
|
||||
@ -111,27 +112,26 @@ class SyncDriver:
|
||||
self.block_callback(block, None)
|
||||
|
||||
try:
|
||||
self.process(conn, item, block, tx_start)
|
||||
self.process(conn, item, block)
|
||||
except IndexError:
|
||||
item.next(advance_block=True)
|
||||
tx_start = 0
|
||||
time.sleep(self.yield_delay)
|
||||
|
||||
if self.store.target > -1 and block.number >= self.store.target:
|
||||
self.running = False
|
||||
|
||||
if self.post_callback != None:
|
||||
self.post_callback()
|
||||
|
||||
logg.debug('fooo')
|
||||
if self.store.target > -1 and block.number >= self.store.target:
|
||||
self.running = False
|
||||
|
||||
self.idle(interval)
|
||||
|
||||
|
||||
def process_single(self, conn, block, tx):
|
||||
logg.debug('single')
|
||||
self.session.filter(conn, block, tx)
|
||||
|
||||
|
||||
def process(self, conn, block, tx_start):
|
||||
def process(self, conn, item, block):
|
||||
raise NotImplementedError()
|
||||
|
||||
|
57
chainsyncer/driver/chain_interface.py
Normal file
57
chainsyncer/driver/chain_interface.py
Normal file
@ -0,0 +1,57 @@
|
||||
# external imports
|
||||
from chainlib.error import RPCException
|
||||
|
||||
# local imports
|
||||
from chainsyncer.error import NoBlockForYou
|
||||
from chainsyncer.driver import SyncDriver
|
||||
|
||||
|
||||
class ChainInterfaceDriver(SyncDriver):
|
||||
|
||||
def __init__(self, store, chain_interface, offset=0, target=-1, pre_callback=None, post_callback=None, block_callback=None, idle_callback=None):
|
||||
super(ChainInterfaceDriver, self).__init__(store, offset=offset, target=target, pre_callback=pre_callback, post_callback=post_callback, block_callback=block_callback, idle_callback=idle_callback)
|
||||
self.chain_interface = chain_interface
|
||||
|
||||
|
||||
def get(self, conn, item):
|
||||
"""Retrieve the block currently defined by the syncer cursor from the RPC provider.
|
||||
|
||||
:param conn: RPC connection
|
||||
:type conn: chainlib.connectin.RPCConnection
|
||||
:raises NoBlockForYou: Block at the given height does not exist
|
||||
:rtype: chainlib.block.Block
|
||||
:returns: Block object
|
||||
"""
|
||||
o = self.chain_interface.block_by_number(item.cursor)
|
||||
try:
|
||||
r = conn.do(o)
|
||||
except RPCException:
|
||||
r = None
|
||||
if r == None:
|
||||
raise NoBlockForYou()
|
||||
b = self.chain_interface.block_from_src(r)
|
||||
b.txs = b.txs[item.tx_cursor:]
|
||||
|
||||
return b
|
||||
|
||||
|
||||
def process(self, conn, item, block):
|
||||
tx_src = None
|
||||
i = item.tx_cursor
|
||||
while True:
|
||||
# handle block objects regardless of whether the tx data is embedded or not
|
||||
try:
|
||||
tx = block.tx(i)
|
||||
except AttributeError:
|
||||
tx_hash = block.txs[i]
|
||||
o = self.chain_interface.tx_by_hash(tx_hash, block=block)
|
||||
r = conn.do(o)
|
||||
#tx = self.chain_interface.tx_from_src(tx_src, block=block)
|
||||
|
||||
rcpt = conn.do(self.chain_interface.tx_receipt(tx.hash))
|
||||
if rcpt != None:
|
||||
tx.apply_receipt(self.chain_interface.src_normalize(rcpt))
|
||||
|
||||
self.process_single(conn, block, tx)
|
||||
|
||||
i += 1
|
@ -121,7 +121,7 @@ class MockBlock:
|
||||
:param i: Transaction index
|
||||
:type i: int
|
||||
"""
|
||||
return MockTx(i, self.txs[i])
|
||||
return MockTx(i, self.txs[i].hash)
|
||||
|
||||
|
||||
class MockStore(State):
|
||||
@ -170,7 +170,7 @@ class MockFilter:
|
||||
r = True
|
||||
self.brk -= 1
|
||||
self.contents.append((block.number, tx.index, tx.hash,))
|
||||
logg.debug('filter {} result {} block {}'.format(self.common_name(), r, block.number))
|
||||
logg.debug('filter {} result {} block {} tx {} {}'.format(self.common_name(), r, block.number, tx.index, tx.hash))
|
||||
return r
|
||||
|
||||
|
||||
@ -202,8 +202,8 @@ class MockDriver(SyncDriver):
|
||||
raise NoBlockForYou()
|
||||
|
||||
|
||||
def process(self, conn, item, block, tx_start):
|
||||
i = tx_start
|
||||
def process(self, conn, item, block):
|
||||
i = item.tx_cursor
|
||||
while self.running:
|
||||
if self.interrupt != None:
|
||||
if self.interrupt[0] == block.number and self.interrupt[1] == i:
|
||||
@ -216,3 +216,54 @@ class MockDriver(SyncDriver):
|
||||
self.process_single(conn, block, tx)
|
||||
item.next()
|
||||
i += 1
|
||||
|
||||
|
||||
class MockChainInterface:
|
||||
|
||||
def block_by_number(self, number):
|
||||
return ('block_by_number', number,)
|
||||
|
||||
|
||||
def tx_by_hash(self, hsh):
|
||||
return ('tx_by_hash', hsh,)
|
||||
|
||||
|
||||
def block_from_src(self, src):
|
||||
return src
|
||||
|
||||
|
||||
def src_normalize(self, src):
|
||||
return src
|
||||
|
||||
|
||||
def tx_receipt(self, hsh):
|
||||
return ('receipt', hsh,)
|
||||
|
||||
|
||||
class MockChainInterfaceConn(MockConn):
|
||||
|
||||
def __init__(self, interface):
|
||||
self.ifc = interface
|
||||
self.blocks = {}
|
||||
self.txs = {}
|
||||
|
||||
|
||||
def add_block(self, block):
|
||||
logg.debug('add block {} {} with {} txs'.format(block.number, block.hash, len(block.txs)))
|
||||
self.blocks[block.number] = block
|
||||
for tx in block.txs:
|
||||
self.txs[tx.hash] = tx
|
||||
|
||||
|
||||
def do(self, o):
|
||||
m = getattr(self, 'handle_' + o[0])
|
||||
return m(o[1])
|
||||
|
||||
|
||||
def handle_block_by_number(self, number):
|
||||
return self.blocks[number]
|
||||
|
||||
|
||||
|
||||
def handle_receipt(self, hsh):
|
||||
return {}
|
||||
|
@ -1,6 +1,6 @@
|
||||
[metadata]
|
||||
name = chainsyncer
|
||||
version = 0.2.0
|
||||
version = 0.3.0
|
||||
description = Generic blockchain syncer driver
|
||||
author = Louis Holbrook
|
||||
author_email = dev@holbrook.no
|
||||
|
61
tests/test_driver.py
Normal file
61
tests/test_driver.py
Normal file
@ -0,0 +1,61 @@
|
||||
# standard imports
|
||||
import unittest
|
||||
import tempfile
|
||||
import shutil
|
||||
import logging
|
||||
import stat
|
||||
import os
|
||||
|
||||
# local imports
|
||||
from chainsyncer.store.fs import SyncFsStore
|
||||
from chainsyncer.session import SyncSession
|
||||
from chainsyncer.error import (
|
||||
LockError,
|
||||
FilterDone,
|
||||
IncompleteFilterError,
|
||||
SyncDone,
|
||||
)
|
||||
from chainsyncer.unittest import (
|
||||
MockBlockGenerator,
|
||||
MockFilter,
|
||||
MockChainInterfaceConn,
|
||||
MockTx,
|
||||
MockBlock,
|
||||
MockChainInterface,
|
||||
MockFilterError,
|
||||
)
|
||||
from chainsyncer.driver.chain_interface import ChainInterfaceDriver
|
||||
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
logg = logging.getLogger()
|
||||
|
||||
|
||||
class TestFilter(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.path = tempfile.mkdtemp()
|
||||
self.store = SyncFsStore(self.path)
|
||||
self.ifc = MockChainInterface()
|
||||
self.conn = MockChainInterfaceConn(self.ifc)
|
||||
|
||||
|
||||
def tearDown(self):
|
||||
shutil.rmtree(self.path)
|
||||
|
||||
|
||||
def test_driver(self):
|
||||
generator = MockBlockGenerator()
|
||||
generator.generate([1, 2], driver=self.conn)
|
||||
|
||||
drv = ChainInterfaceDriver(self.store, self.ifc, target=1)
|
||||
|
||||
fltr_one = MockFilter('foo')
|
||||
self.store.register(fltr_one)
|
||||
with self.assertRaises(SyncDone):
|
||||
drv.run(self.conn)
|
||||
|
||||
self.assertEqual(len(fltr_one.contents), 3)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
@ -117,7 +117,6 @@ class TestFilter(unittest.TestCase):
|
||||
fltr_three = MockFilter('baz')
|
||||
self.store.register(fltr_three)
|
||||
|
||||
|
||||
store = SyncFsStore(self.path, state_event_callback=state_event_handler, filter_state_event_callback=filter_state_event_handler)
|
||||
|
||||
with self.assertRaises(SyncDone):
|
||||
|
Loading…
Reference in New Issue
Block a user