eth-stat-syncer

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

commit 2e687114af2bb5bff129ed0f04a36c3ac7c3d09c
parent a7f859b952ce758e8546e52d83410f05cb951975
Author: nolash <dev@holbrook.no>
Date:   Fri,  9 Apr 2021 17:39:00 +0200

Add http server jsonrpc code

Diffstat:
A.gitignore | 5+++++
Aconfig/httpd.ini | 3+++
Aconfig/store.ini | 2++
Aeth_stat_syncer/runnable/server.py | 144+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Meth_stat_syncer/runnable/syncer.py | 1-
5 files changed, 154 insertions(+), 1 deletion(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1,5 @@ +__pycache__ +*.pyc +dist/ +*.egg-info +gmon.out diff --git a/config/httpd.ini b/config/httpd.ini @@ -0,0 +1,3 @@ +[httpd] +host = +port = diff --git a/config/store.ini b/config/store.ini @@ -0,0 +1,2 @@ +[store] +base_dir = /run diff --git a/eth_stat_syncer/runnable/server.py b/eth_stat_syncer/runnable/server.py @@ -0,0 +1,144 @@ +# standard import +import json +import os +import logging +import argparse +import sys +from http.server import ( + HTTPServer, + BaseHTTPRequestHandler, + ) + +# external imports +import confini +from jsonrpc_std.parse import jsonrpc_from_str +from jsonrpc_std.interface import jsonrpc_response + +# local imports +from eth_stat_syncer.store import RunStore + +logging.basicConfig(level=logging.WARNING) +logg = logging.getLogger() + +default_config_dir = os.environ.get('CONFINI_DIR', './config') + +argparser = argparse.ArgumentParser() +argparser.add_argument('-c', '--config', dest='c', default=default_config_dir, type=str, help='rpc provider') +argparser.add_argument('--host', type=str, help='httpd host') +argparser.add_argument('--port', type=str, help='httpd port') +argparser.add_argument('--store-dir', dest='store_dir', type=str, help='syncerd data store base directory') +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 = { + 'HTTPD_HOST': getattr(args, 'host'), + 'HTTPD_PORT': getattr(args, 'port'), + 'STORE_BASE_DIR': getattr(args, 'store_dir'), + } +config.dict_override(args_override, 'cli flag') +logg.debug('loaded config: {}\n'.format(config)) + +class StatRequestHandler(BaseHTTPRequestHandler): + + runstore = None + server_version = 'eth_stat_syncer/0.0.1' + + + def do_POST(self): + + if self.headers.get('Content-Type') != 'application/json': + self.send_response(400, 'me read json only') + self.end_headers() + return + + if 'application/json' not in self.headers.get('Accept').split(','): + self.send_response(400, 'me json only speak') + self.end_headers() + return + + l = self.headers.get('Content-Length') + try: + l = int(l) + except ValueError: + self.send_response(400, 'content length must be integer') + self.end_headers() + return + if l > 4096: + self.send_response(400, 'too much information') + self.end_headers() + return + if l < 0: + self.send_response(400, 'you are too negative') + self.end_headers() + return + + b = b'' + c = 0 + while c < l: + d = self.rfile.read(l-c) + if d == None: + break + b += d + c += len(d) + if c > 4096: + self.send_response(413, 'i should slap you around for lying about your size') + self.end_headers() + return + + o = None + err = None + try: + o = jsonrpc_from_str(b.decode('utf-8')) + except JSONRPCException as e: + err = e + + if err != None: + res = e + self.send_response(200, 'alas with jsonrpc error') + + else: + r = self.runstore.get('high', 'minute') + r = int(r) + if r == 0: + r = 1 + res = jsonrpc_response(o['id'], r) + logg.debug('returning {} for req {} {}'.format(r, o, self.requestline)) + self.send_response(200, 'It\'s a gas') + + b = json.dumps(res).encode('utf-8') + l = len(b) + self.send_header('Content-Length', str(l)) + self.send_header('Cache-Control', 'no-cache') + self.send_header('Content-Type', 'application/json') + self.end_headers() + + c = 0 + while c < l: + n = self.wfile.write(b[c:]) + c += n + + +def run(store, host=None, port=None): + if host == None: + host = '' + if port == None: + port = 8000 + StatRequestHandler.runstore = store + server_address = (host, port) + httpd = HTTPServer(server_address, StatRequestHandler) + httpd.serve_forever() + + +if __name__ == '__main__': + store = RunStore(basedir=config.get('STORE_BASE_DIR')) + run(store, host=config.get('HTTPD_HOST'), port=config.get('HTTPD_PORT')) diff --git a/eth_stat_syncer/runnable/syncer.py b/eth_stat_syncer/runnable/syncer.py @@ -91,7 +91,6 @@ def main(): 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)