eth-stat-syncer

Cache live EVM blockchain stats
git clone git://holbrook.no/eth-stat-syncer.git
Log | Files | Refs

commit 5b47ab566184a29a4407ff58b2edd71f543f9a48
Author: nolash <dev@holbrook.no>
Date:   Thu,  8 Apr 2021 19:53:36 +0200

Initial commit

Diffstat:
Aconfig/chain.ini | 2++
Aconfig/rpc.ini | 2++
Aeth_stat_syncer/runnable/syncer.py | 125+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Arequirements.txt | 2++
Asetup.cfg | 32++++++++++++++++++++++++++++++++
Asetup.py | 26++++++++++++++++++++++++++
6 files changed, 189 insertions(+), 0 deletions(-)

diff --git a/config/chain.ini b/config/chain.ini @@ -0,0 +1,2 @@ +[chain] +spec = diff --git a/config/rpc.ini b/config/rpc.ini @@ -0,0 +1,2 @@ +[rpc] +provider = diff --git a/eth_stat_syncer/runnable/syncer.py b/eth_stat_syncer/runnable/syncer.py @@ -0,0 +1,125 @@ +# standard imports +import sys +import os +import logging +import argparse +import datetime + +# external imports +import confini +from chainsyncer.backend import MemBackend +from chainsyncer.driver import ( + HeadSyncer, + HistorySyncer, + ) +from chainsyncer.filter import SyncFilter +from chainsyncer.error import NoBlockForYou +from chainlib.chain import ChainSpec +from chainlib.eth.connection import EthHTTPConnection +from chainlib.eth.block import block_latest + +logging.basicConfig(level=logging.WARNING) +logg = logging.getLogger() + +default_config_dir = os.environ.get('CONFINI_DIR', './config') + +argparser = argparse.ArgumentParser() +argparser.add_argument('-p', '--provider', dest='p', type=str, help='rpc provider') +argparser.add_argument('-c', '--config', dest='c', default=default_config_dir, type=str, help='rpc provider') +argparser.add_argument('-i', '--chain-spec', dest='i', default='evm:ethereum:1', type=str, help='chain spec') +argparser.add_argument('--start', type=int, help='number of blocks to sample at startup') +argparser.add_argument('--env-prefix', default=os.environ.get('CONFINI_ENV_PREFIX'), dest='env_prefix', type=str, help='environment prefix for variables to overwrite configuration') +argparser.add_argument('-v', action='store_true', help='be verbose') +argparser.add_argument('-vv', action='store_true', help='be more verbose') +args = argparser.parse_args() + +if args.vv: + logging.getLogger().setLevel(logging.DEBUG) +elif args.v: + logging.getLogger().setLevel(logging.INFO) + +config = confini.Config(args.c, args.env_prefix) +config.process() +# override args +args_override = { + 'CHAIN_SPEC': getattr(args, 'i'), + 'RPC_PROVIDER': getattr(args, 'p'), + } +config.dict_override(args_override, 'cli flag') +config.add(args.start, '_START', True) +logg.debug('loaded config: {}\n'.format(config)) + +chain_spec = ChainSpec.from_chain_str(config.get('CHAIN_SPEC')) + +conn = EthHTTPConnection(args.p) + + +class GasStore: + + def __init__(self, store): + self.store = store + self.avg = 0 + self.count = 0 + self.timestamp = datetime.datetime.utcnow() + + + def put(self, v): + aggr = self.avg * self.count + aggr += v + self.count += 1 + self.avg = int(aggr / self.count) + logg.info('added {} to aggregate {} new average {} from {} samples'.format(v, aggr, self.avg, self.count)) + return self.avg + + + def get(self): + return self.avg + + +class GasPriceFilter(SyncFilter): + + def __init__(self, chain_spec, gas_store): + self.chain_spec = chain_spec + self.gas_store = gas_store + + + def filter(self, conn, block, tx, db_session): + self.gas_store.put(tx.gas_price) + + +def block_callback(block, tx): + logg.info('synced {}'.format(block)) + + +def main(): + gas_store = GasStore(None) + gas_filter = GasPriceFilter(chain_spec, gas_store) + + o = block_latest() + r = conn.do(o) + n = int(r, 16) + start_block = n + logg.info('block height at start {}'.format(start_block)) + + if config.get('_START') != None: + offset = start_block - config.get('_START') + syncer_backend = MemBackend(chain_spec, None, target_block=start_block) + syncer_backend.set(offset, 0) + syncer = HistorySyncer(syncer_backend, block_callback=block_callback) + syncer.add_filter(gas_filter) + try: + syncer.loop(0.0, conn) + except NoBlockForYou: + logg.info('history done at {}'.format(syncer.backend.get())) + pass + + syncer_backend = MemBackend(chain_spec, None) + syncer_backend.set(start_block + 1, 0) + syncer = HeadSyncer(syncer_backend, block_callback=block_callback) + syncer.add_filter(gas_filter) + syncer.loop(1.0, conn) + + +if __name__ == '__main__': + main() + diff --git a/requirements.txt b/requirements.txt @@ -0,0 +1,2 @@ +chainsyncer==0.0.1a22 +chainlib==0.0.2a6 diff --git a/setup.cfg b/setup.cfg @@ -0,0 +1,32 @@ +[metadata] +name = eth-stat-syncer +version = 0.0.1a1 +description = Cache live EVM blockchain stats +author = Louis Holbrook +author_email = dev@holbrook.no +url = https://gitlab.com/nolash/giftable-erc-token +keywords = + ethereum +classifiers = + Programming Language :: Python :: 3 + Operating System :: OS Independent + Development Status :: 3 - Alpha + Environment :: No Input/Output (Daemon) + Intended Audience :: Developers + License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+) + Topic :: Internet + #Topic :: Blockchain :: EVM +license = GPL3 +licence_files = + LICENSE + +[options] +include_package_data = True +python_requires = >= 3.6 +packages = + eth_stat_syncer + eth_stat_syncer.runnable + +[options.entry_points] +console_scripts = + eth-stat-syncerd = eth_stat_tracker.runnable.syncer:main diff --git a/setup.py b/setup.py @@ -0,0 +1,26 @@ +from setuptools import setup + +requirements = [] +f = open('requirements.txt', 'r') +while True: + l = f.readline() + if l == '': + break + requirements.append(l.rstrip()) +f.close() + +test_requirements = [] +f = open('test_requirements.txt', 'r') +while True: + l = f.readline() + if l == '': + break + test_requirements.append(l.rstrip()) +f.close() + + +setup( + include_package_data=True, + install_requires=requirements, + tests_require=test_requirements, + )