eth-monitor

Monitor and cache ethereum transactions with match filters
git clone git://git.defalsify.org/eth-monitor.git
Log | Files | Refs | README | LICENSE

commit 41d38d4eb8b4ab89f56695d5c1732caadf0c7745
Author: nolash <dev@holbrook.no>
Date:   Sat, 26 Jun 2021 14:04:34 +0200

Initial commit

Diffstat:
A.gitignore | 5+++++
Aconfig/syncer.ini | 2++
Aeth_monitor/chain.py | 18++++++++++++++++++
Aeth_monitor/runnable/sync.py | 125+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Arequirements.txt | 4++++
5 files changed, 154 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1,5 @@ +__pycache__ +build/ +dist/ +*.pyc +*.egg-info/ diff --git a/config/syncer.ini b/config/syncer.ini @@ -0,0 +1,2 @@ +[syncer] +loop_interval = 5 diff --git a/eth_monitor/chain.py b/eth_monitor/chain.py @@ -0,0 +1,18 @@ +# external imports +from chainlib.interface import ChainInterface +from chainlib.eth.block import ( + block_by_number, + Block, + ) +from chainlib.eth.tx import ( + receipt, + Tx, + ) + +class EthChainInterface(ChainInterface): + + def __init__(self): + self._block_by_number = block_by_number + self._block_from_src = Block.from_src + self._tx_receipt = receipt + self._src_normalize = Tx.src_normalize diff --git a/eth_monitor/runnable/sync.py b/eth_monitor/runnable/sync.py @@ -0,0 +1,125 @@ +# standard imports +import sys +import signal +import argparse +import confini +import logging +import os + +# external imports +from chainlib.chain import ChainSpec +from chainlib.eth.connection import EthHTTPConnection +from chainlib.eth.block import block_latest +from hexathon import ( + strip_0x, + add_0x, + ) +from chainsyncer.driver.head import HeadSyncer +from chainsyncer.driver.history import HistorySyncer +from chainsyncer.backend.file import FileBackend +from chainsyncer.filter import NoopFilter + +# local imports +from eth_monitor.chain import EthChainInterface + +logging.basicConfig(level=logging.WARNING) +logg = logging.getLogger() + +default_eth_provider = os.environ.get('RPC_PROVIDER') +if default_eth_provider == None: + default_eth_provider = os.environ.get('ETH_PROVIDER', 'http://localhost:8545') + +script_dir = os.path.realpath(os.path.dirname(__file__)) +exec_dir = os.path.realpath(os.getcwd()) +default_config_dir = os.environ.get('CONFINI_DIR', os.path.join(exec_dir, 'config')) + +argparser = argparse.ArgumentParser('master eth events monitor') +argparser.add_argument('-p', '--provider', dest='p', default=default_eth_provider, type=str, help='Web3 provider url (http only)') +argparser.add_argument('-c', type=str, default=default_config_dir, help='config file') +argparser.add_argument('-i', '--chain-spec', dest='i', type=str, default='evm:ethereum:1', help='Chain specification string') +argparser.add_argument('--offset', type=int, default=0, help='Use sequential rpc ids') +argparser.add_argument('--seq', action='store_true', help='Use sequential rpc ids') +argparser.add_argument('--skip-history', action='store_true', dest='skip_history', help='Skip history sync') +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(sys.argv[1:]) + +if args.vv: + logg.setLevel(logging.DEBUG) +elif args.v: + logg.setLevel(logging.INFO) + +config_dir = args.c +config = confini.Config(config_dir, os.environ.get('CONFINI_ENV_PREFIX')) +config.process() +args_override = { + 'CHAIN_SPEC': getattr(args, 'i'), + } +config.dict_override(args_override, 'cli') +config.add(args.offset, '_SYNC_OFFSET', True) +config.add(args.skip_history, '_NO_HISTORY', True) +logg.debug('config loaded:\n{}'.format(config)) + +chain_spec = ChainSpec.from_chain_str(args.i) + +state_dir = os.path.join(exec_dir, 'state') + +rpc_id_generator = None +if args.seq: + rpc_id_generator = IntSequenceGenerator() + +auth = None +if os.environ.get('RPC_AUTHENTICATION') == 'basic': + from chainlib.auth import BasicAuth + auth = BasicAuth(os.environ['RPC_USERNAME'], os.environ['RPC_PASSWORD']) +rpc = EthHTTPConnection(args.p) + +if __name__ == '__main__': + o = block_latest() + r = rpc.do(o) + block_offset = int(strip_0x(r), 16) + 1 + logg.debug('current block height {}'.format(block_offset)) + syncers = [] + + syncer_backends = FileBackend.resume(chain_spec, block_offset, base_dir=state_dir) + + if len(syncer_backends) == 0: + initial_block_start = block_offset - 1 + if config.get('_SYNC_OFFSET') != None: + initial_block_start = config.get('_SYNC_OFFSET') + initial_block_offset = block_offset + if config.get('_NO_HISTORY'): + initial_block_start = block_offset + initial_block_offset += 1 + syncer_backends.append(FileBackend.initial(chain_spec, initial_block_offset, start_block_height=initial_block_start, base_dir=state_dir)) + logg.info('found no backends to resume, adding initial sync from history start {} end {}'.format(initial_block_start, initial_block_offset)) + else: + for syncer_backend in syncer_backends: + logg.info('resuming sync session {}'.format(syncer_backend)) + + chain_interface = EthChainInterface() + for syncer_backend in syncer_backends: + syncers.append(HistorySyncer(syncer_backend, chain_interface)) + + #syncer_backend = FileBackend.live(chain_spec, block_offset+1, base_dir=state_dir) + #syncers.append(HeadSyncer(syncer_backend, chain_interface)) + + filters = [ + NoopFilter(), + ] + i = 0 + for syncer in syncers: + logg.debug('running syncer index {} {}'.format(i, str(syncer))) + for f in filters: + syncer.add_filter(f) + + r = syncer.loop(int(config.get('SYNCER_LOOP_INTERVAL')), rpc) + sys.stderr.write("sync {} done at block {}\n".format(syncer, r)) + + i += 1 + + +# if len(sys.argv) > 1: +# block_number = offset +# sys.stderr.write('starting on block {}\n'.format(block_number)) +# backend.set(block_number, 0) diff --git a/requirements.txt b/requirements.txt @@ -0,0 +1,4 @@ +chainlib>=0.0.4a1,<=0.0.4 +chainsyncer>=0.0.3a1, <=0.0.3 +crypto-dev-signer>=0.4.14a6,<0.5 +eth_erc20~=0.0.10a1