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/
|
build/
|
||||||
dist/
|
dist/
|
||||||
*.sqlite
|
*.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):
|
def loop(self, conn, item, interval=1):
|
||||||
logg.debug('started loop')
|
logg.debug('started loop')
|
||||||
tx_start = item.tx_cursor
|
|
||||||
while self.running and SyncDriver.running_global:
|
while self.running and SyncDriver.running_global:
|
||||||
self.last_start = time.clock_gettime_ns(self.clock_id)
|
self.last_start = time.clock_gettime_ns(self.clock_id)
|
||||||
|
|
||||||
if self.pre_callback != None:
|
if self.pre_callback != None:
|
||||||
self.pre_callback()
|
self.pre_callback()
|
||||||
|
|
||||||
while True and self.running:
|
while True and self.running:
|
||||||
try:
|
try:
|
||||||
block = self.get(conn, item)
|
block = self.get(conn, item)
|
||||||
@ -111,27 +112,26 @@ class SyncDriver:
|
|||||||
self.block_callback(block, None)
|
self.block_callback(block, None)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.process(conn, item, block, tx_start)
|
self.process(conn, item, block)
|
||||||
except IndexError:
|
except IndexError:
|
||||||
item.next(advance_block=True)
|
item.next(advance_block=True)
|
||||||
tx_start = 0
|
|
||||||
time.sleep(self.yield_delay)
|
time.sleep(self.yield_delay)
|
||||||
|
|
||||||
|
if self.store.target > -1 and block.number >= self.store.target:
|
||||||
|
self.running = False
|
||||||
|
|
||||||
if self.post_callback != None:
|
if self.post_callback != None:
|
||||||
self.post_callback()
|
self.post_callback()
|
||||||
|
|
||||||
logg.debug('fooo')
|
|
||||||
if self.store.target > -1 and block.number >= self.store.target:
|
|
||||||
self.running = False
|
|
||||||
|
|
||||||
self.idle(interval)
|
self.idle(interval)
|
||||||
|
|
||||||
|
|
||||||
def process_single(self, conn, block, tx):
|
def process_single(self, conn, block, tx):
|
||||||
logg.debug('single')
|
|
||||||
self.session.filter(conn, block, tx)
|
self.session.filter(conn, block, tx)
|
||||||
|
|
||||||
|
|
||||||
def process(self, conn, block, tx_start):
|
def process(self, conn, item, block):
|
||||||
raise NotImplementedError()
|
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
|
:param i: Transaction index
|
||||||
:type i: int
|
:type i: int
|
||||||
"""
|
"""
|
||||||
return MockTx(i, self.txs[i])
|
return MockTx(i, self.txs[i].hash)
|
||||||
|
|
||||||
|
|
||||||
class MockStore(State):
|
class MockStore(State):
|
||||||
@ -170,7 +170,7 @@ class MockFilter:
|
|||||||
r = True
|
r = True
|
||||||
self.brk -= 1
|
self.brk -= 1
|
||||||
self.contents.append((block.number, tx.index, tx.hash,))
|
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
|
return r
|
||||||
|
|
||||||
|
|
||||||
@ -202,8 +202,8 @@ class MockDriver(SyncDriver):
|
|||||||
raise NoBlockForYou()
|
raise NoBlockForYou()
|
||||||
|
|
||||||
|
|
||||||
def process(self, conn, item, block, tx_start):
|
def process(self, conn, item, block):
|
||||||
i = tx_start
|
i = item.tx_cursor
|
||||||
while self.running:
|
while self.running:
|
||||||
if self.interrupt != None:
|
if self.interrupt != None:
|
||||||
if self.interrupt[0] == block.number and self.interrupt[1] == i:
|
if self.interrupt[0] == block.number and self.interrupt[1] == i:
|
||||||
@ -216,3 +216,54 @@ class MockDriver(SyncDriver):
|
|||||||
self.process_single(conn, block, tx)
|
self.process_single(conn, block, tx)
|
||||||
item.next()
|
item.next()
|
||||||
i += 1
|
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]
|
[metadata]
|
||||||
name = chainsyncer
|
name = chainsyncer
|
||||||
version = 0.2.0
|
version = 0.3.0
|
||||||
description = Generic blockchain syncer driver
|
description = Generic blockchain syncer driver
|
||||||
author = Louis Holbrook
|
author = Louis Holbrook
|
||||||
author_email = dev@holbrook.no
|
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')
|
fltr_three = MockFilter('baz')
|
||||||
self.store.register(fltr_three)
|
self.store.register(fltr_three)
|
||||||
|
|
||||||
|
|
||||||
store = SyncFsStore(self.path, state_event_callback=state_event_handler, filter_state_event_callback=filter_state_event_handler)
|
store = SyncFsStore(self.path, state_event_callback=state_event_handler, filter_state_event_callback=filter_state_event_handler)
|
||||||
|
|
||||||
with self.assertRaises(SyncDone):
|
with self.assertRaises(SyncDone):
|
||||||
|
Loading…
Reference in New Issue
Block a user