Add filtering
parent
2f22d6df1a
commit
da54a077e5
@ -1,16 +0,0 @@
|
||||
class Block:
|
||||
|
||||
def __init__(self, hsh, obj):
|
||||
self.hash = hsh
|
||||
self.obj = obj
|
||||
|
||||
|
||||
def tx(self, idx):
|
||||
return NotImplementedError
|
||||
|
||||
|
||||
def number(self):
|
||||
return NotImplementedError
|
||||
|
||||
|
||||
|
@ -1,52 +0,0 @@
|
||||
import json
|
||||
|
||||
from chainsyncer.client import translate
|
||||
from chainsyncer.client.block import Block
|
||||
from chainsyncer.client.tx import Tx
|
||||
|
||||
|
||||
translations = {
|
||||
'block_number': translate.hex_to_int,
|
||||
'get_block': json.dumps,
|
||||
'number': translate.hex_to_int,
|
||||
}
|
||||
|
||||
|
||||
class EVMResponse:
|
||||
|
||||
def __init__(self, item, response_object):
|
||||
self.response_object = response_object
|
||||
self.item = item
|
||||
self.fn = translations[self.item]
|
||||
|
||||
|
||||
def get_error(self):
|
||||
return self.response_object.get('error')
|
||||
|
||||
|
||||
def get_result(self):
|
||||
r = self.fn(self.response_object.get('result'))
|
||||
if r == 'null':
|
||||
return None
|
||||
return r
|
||||
|
||||
|
||||
class EVMTx(Tx):
|
||||
|
||||
def __init__(self, block, tx_number, obj):
|
||||
super(EVMTx, self).__init__(block, tx_number, obj)
|
||||
|
||||
|
||||
class EVMBlock(Block):
|
||||
|
||||
def tx(self, idx):
|
||||
o = self.obj['transactions'][idx]
|
||||
return Tx(self, idx, o)
|
||||
|
||||
|
||||
def number(self):
|
||||
return translate.hex_to_int(self.obj['number'])
|
||||
|
||||
|
||||
def __str__(self):
|
||||
return str('block {} {}'.format(self.number(), self.hash))
|
@ -1,90 +0,0 @@
|
||||
# standard imports
|
||||
import logging
|
||||
import uuid
|
||||
import json
|
||||
|
||||
# third-party imports
|
||||
import websocket
|
||||
from hexathon import add_0x
|
||||
|
||||
# local imports
|
||||
from .response import EVMResponse
|
||||
from chainsyncer.error import RequestError
|
||||
from chainsyncer.client.evm.response import EVMBlock
|
||||
|
||||
logg = logging.getLogger()
|
||||
|
||||
|
||||
class EVMWebsocketClient:
|
||||
|
||||
def __init__(self, url):
|
||||
self.url = url
|
||||
self.conn = websocket.create_connection(url)
|
||||
|
||||
|
||||
def __del__(self):
|
||||
self.conn.close()
|
||||
|
||||
|
||||
def block_number(self):
|
||||
req_id = str(uuid.uuid4())
|
||||
req = {
|
||||
'jsonrpc': '2.0',
|
||||
'method': 'eth_blockNumber',
|
||||
'id': str(req_id),
|
||||
'params': [],
|
||||
}
|
||||
self.conn.send(json.dumps(req))
|
||||
r = self.conn.recv()
|
||||
res = EVMResponse('block_number', json.loads(r))
|
||||
err = res.get_error()
|
||||
if err != None:
|
||||
raise RequestError(err)
|
||||
|
||||
return res.get_result()
|
||||
|
||||
|
||||
def block_by_integer(self, n):
|
||||
req_id = str(uuid.uuid4())
|
||||
nhx = '0x' + n.to_bytes(8, 'big').hex()
|
||||
req = {
|
||||
'jsonrpc': '2.0',
|
||||
'method': 'eth_getBlockByNumber',
|
||||
'id': str(req_id),
|
||||
'params': [nhx, False],
|
||||
}
|
||||
self.conn.send(json.dumps(req))
|
||||
r = self.conn.recv()
|
||||
res = EVMResponse('get_block', json.loads(r))
|
||||
err = res.get_error()
|
||||
if err != None:
|
||||
raise RequestError(err)
|
||||
|
||||
j = res.get_result()
|
||||
if j == None:
|
||||
return None
|
||||
o = json.loads(j)
|
||||
return EVMBlock(o['hash'], o)
|
||||
|
||||
|
||||
def block_by_hash(self, hx_in):
|
||||
req_id = str(uuid.uuid4())
|
||||
hx = add_0x(hx_in)
|
||||
req ={
|
||||
'jsonrpc': '2.0',
|
||||
'method': 'eth_getBlockByHash',
|
||||
'id': str(req_id),
|
||||
'params': [hx, False],
|
||||
}
|
||||
self.conn.send(json.dumps(req))
|
||||
r = self.conn.recv()
|
||||
res = EVMResponse('get_block', json.loads(r))
|
||||
err = res.get_error()
|
||||
if err != None:
|
||||
raise RequestError(err)
|
||||
|
||||
j = res.get_result()
|
||||
if j == None:
|
||||
return None
|
||||
o = json.loads(j)
|
||||
return EVMBlock(o['hash'], o)
|
@ -1,10 +0,0 @@
|
||||
# third-party imports
|
||||
from hexathon import strip_0x
|
||||
|
||||
|
||||
def hex_to_int(hx, endianness='big'):
|
||||
hx = strip_0x(hx)
|
||||
if len(hx) % 2 == 1:
|
||||
hx = '0' + hx
|
||||
b = bytes.fromhex(hx)
|
||||
return int.from_bytes(b, endianness)
|
@ -1,6 +0,0 @@
|
||||
class Tx:
|
||||
|
||||
def __init__(self, block, tx_number, obj):
|
||||
self.block = block
|
||||
self.tx_number = tx_number
|
||||
self.obj = obj
|
@ -0,0 +1,40 @@
|
||||
# standard imports
|
||||
import hashlib
|
||||
|
||||
# third-party imports
|
||||
from sqlalchemy import Column, String, Integer, BLOB
|
||||
from sqlalchemy.ext.hybrid import hybrid_property, hybrid_method
|
||||
|
||||
# local imports
|
||||
from .base import SessionBase
|
||||
|
||||
|
||||
zero_digest = '{:<064s'.format('0')
|
||||
|
||||
|
||||
class BlockchainSyncFilter(SessionBase):
|
||||
|
||||
__tablename__ = 'chain_sync_filter'
|
||||
|
||||
chain_sync_id = Column(Integer, ForeignKey='chain_sync.id')
|
||||
flags = Column(BLOB)
|
||||
digest = Column(String)
|
||||
count = Column(Integer)
|
||||
|
||||
@staticmethod
|
||||
def set(self, names):
|
||||
|
||||
|
||||
def __init__(self, names, chain_sync, digest=None):
|
||||
if len(names) == 0:
|
||||
digest = zero_digest
|
||||
elif digest == None:
|
||||
h = hashlib.new('sha256')
|
||||
for n in names:
|
||||
h.update(n.encode('utf-8') + b'\x00')
|
||||
z = h.digest()
|
||||
digest = z.hex()
|
||||
self.digest = digest
|
||||
self.count = len(names)
|
||||
self.flags = bytearray((len(names) -1 ) / 8 + 1)
|
||||
self.chain_sync_id = chain_sync.id
|
@ -1,2 +1,3 @@
|
||||
[syncer]
|
||||
loop_interval = 1
|
||||
chain_spec =
|
||||
|
@ -1,8 +1,6 @@
|
||||
psycopg2==2.8.6
|
||||
SQLAlchemy==1.3.20
|
||||
py-evm==0.3.0a20
|
||||
eth-tester==0.5.0b3
|
||||
confini==0.3.6b2
|
||||
confini~=0.3.6b2
|
||||
semver==2.13.0
|
||||
hexathon==0.0.1a2
|
||||
chainlib~=0.0.1a7
|
||||
hexathon~=0.0.1a3
|
||||
chainlib~=0.0.1a13
|
||||
|
@ -0,0 +1,13 @@
|
||||
CREATE TABLE IF NOT EXISTS chain_sync (
|
||||
id serial primary key not null,
|
||||
blockchain varchar not null,
|
||||
block_start int not null default 0,
|
||||
tx_start int not null default 0,
|
||||
block_cursor int not null default 0,
|
||||
tx_cursor int not null default 0,
|
||||
flags bytea not null,
|
||||
num_flags int not null,
|
||||
block_target int default null,
|
||||
date_created timestamp not null,
|
||||
date_updated timestamp default null
|
||||
);
|
@ -0,0 +1,10 @@
|
||||
CREATE TABLE IF NOT EXISTS chain_sync_filter (
|
||||
id serial primary key not null,
|
||||
chain_sync_id int not null,
|
||||
flags bytea default null,
|
||||
count int not null default 0,
|
||||
digest char(64) not null default '0000000000000000000000000000000000000000000000000000000000000000',
|
||||
CONSTRAINT fk_chain_sync
|
||||
FOREIGN KEY(chain_sync_id)
|
||||
REFERENCES chain_sync(id)
|
||||
);
|
@ -0,0 +1,46 @@
|
||||
import unittest
|
||||
import tempfile
|
||||
import os
|
||||
#import pysqlite
|
||||
|
||||
from chainsyncer.db import dsn_from_config
|
||||
from chainsyncer.db.models.base import SessionBase
|
||||
|
||||
script_dir = os.path.realpath(os.path.dirname(__file__))
|
||||
|
||||
|
||||
class TestBase(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
db_dir = tempfile.mkdtemp()
|
||||
self.db_path = os.path.join(db_dir, 'test.sqlite')
|
||||
config = {
|
||||
'DATABASE_ENGINE': 'sqlite',
|
||||
'DATABASE_DRIVER': 'pysqlite',
|
||||
'DATABASE_NAME': self.db_path,
|
||||
}
|
||||
dsn = dsn_from_config(config)
|
||||
SessionBase.poolable = False
|
||||
SessionBase.transactional = False
|
||||
SessionBase.procedural = False
|
||||
SessionBase.connect(dsn, debug=True)
|
||||
|
||||
f = open(os.path.join(script_dir, '..', 'sql', 'sqlite', '1.sql'), 'r')
|
||||
sql = f.read()
|
||||
f.close()
|
||||
|
||||
conn = SessionBase.engine.connect()
|
||||
conn.execute(sql)
|
||||
|
||||
f = open(os.path.join(script_dir, '..', 'sql', 'sqlite', '2.sql'), 'r')
|
||||
sql = f.read()
|
||||
f.close()
|
||||
|
||||
conn = SessionBase.engine.connect()
|
||||
conn.execute(sql)
|
||||
|
||||
def tearDown(self):
|
||||
SessionBase.disconnect()
|
||||
os.unlink(self.db_path)
|
||||
|
||||
|
@ -0,0 +1,22 @@
|
||||
# standard imports
|
||||
import unittest
|
||||
|
||||
# external imports
|
||||
from chainlib.chain import ChainSpec
|
||||
|
||||
# local imports
|
||||
from chainsyncer.backend import SyncerBackend
|
||||
|
||||
# testutil imports
|
||||
from tests.base import TestBase
|
||||
|
||||
|
||||
class TestBasic(TestBase):
|
||||
|
||||
def test_hello(self):
|
||||
chain_spec = ChainSpec('evm', 'bloxberg', 8996, 'foo')
|
||||
backend = SyncerBackend(chain_spec, 'foo')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
Loading…
Reference in New Issue