commit 5c6b65dd50842e5a3aa26fbfbd28639e8d1e5fc7
parent e06ab63d8e7285d2428c00e2e0b71f1bd66c3b0c
Author: nolash <dev@holbrook.no>
Date: Tue, 26 Jan 2021 11:38:13 +0100
Add web3 middleware
Diffstat:
5 files changed, 131 insertions(+), 12 deletions(-)
diff --git a/gas_proxy/proxy.py b/gas_proxy/proxy.py
@@ -3,7 +3,6 @@ import logging
import websocket
-logging.basicConfig(level=logging.DEBUG)
logg = logging.getLogger()
@@ -12,30 +11,36 @@ class Oracle:
def get(self):
raise NotImplementedError
-class Web3WebsocketOracle(Oracle):
+class JSONRPCOracle(Oracle):
- def __init__(self, connection_string, cache=None):
- logg.debug('connecting to {}'.format(connection_string))
- self.ws = websocket.create_connection(connection_string)
+ def __init__(self, socket, cache=None):
self.query = {
"jsonrpc": "2.0",
"method": "eth_gasPrice",
"params": [],
"id": 0,
}
+ self.id_seq = 0
+ self.socket = socket
self.cache = cache
def __del__(self):
- self.ws.close()
+ self.socket.close()
- def get(self):
+ def get(self, request_id=None):
o = None
+ if request_id == None:
+ request_id = str(self.id_seq)
+ self.id_seq += 1
+
+ self.query['id'] = request_id
+
try:
- self.ws.send(json.dumps(self.query))
- result = self.ws.recv()
+ self.socket.send(json.dumps(self.query))
+ result = self.socket.recv()
o = json.loads(result)
result_hex = o['result'][2:]
if len(result_hex) % 2 != 0:
@@ -43,6 +48,7 @@ class Web3WebsocketOracle(Oracle):
result_bytes = bytes.fromhex(result_hex)
result_num = int.from_bytes(result_bytes, 'big')
except Exception as e:
+ logg.exception('backend error')
if self.cache == None:
raise(e)
result_num = self.cache.get()
diff --git a/gas_proxy/runnable/server.py b/gas_proxy/runnable/server.py
@@ -1,13 +1,16 @@
+import re
import os
import argparse
import logging
import socket
import json
-from gas_proxy.proxy import Web3WebsocketOracle as Oracle
+import websocket
+
+from gas_proxy.proxy import JSONRPCOracle as Oracle
from gas_proxy.cache.mem import MemCache
-logging.basicConfig(level=logging.DEBUG)
+logging.basicConfig(level=logging.WARNING)
logg = logging.getLogger()
@@ -26,9 +29,19 @@ if args.vv:
elif args.v:
logging.getLogger().setLevel(logging.INFO)
+re_websocket = r'^wss?://(.+):(\d+)/?$'
+m = re.match(re_websocket, args.provider)
+if m == None:
+ raise ValueError('websocket providers only')
+
+logg.debug('using websocket host {} port {}'.format(m.group(1), m.group(2)))
+connection_string = args.provider
+
if __name__ == '__main__':
memcache = MemCache()
- ws = Oracle(args.provider, cache=memcache)
+
+ ws = websocket.create_connection(connection_string)
+ ws = Oracle(ws, cache=memcache)
s = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
s.bind((args.host, args.port))
diff --git a/requirements.txt b/requirements.txt
@@ -0,0 +1,2 @@
+web3==5.12.2
+websocket-client==0.57.0
diff --git a/scripts/test.py b/scripts/test.py
@@ -0,0 +1,25 @@
+import logging
+import time
+
+import web3
+import websocket
+
+from gas_proxy.web3 import GasMiddleware
+
+logging.basicConfig(level=logging.DEBUG)
+logg = logging.getLogger()
+
+re_websocket = r'wss?://'
+
+def socket_constructor():
+ return websocket.create_connection('ws://localhost:8545')
+
+GasMiddleware.socket_constructor = socket_constructor
+GasMiddleware.last_gas = "0x1000"
+
+w3 = web3.Web3(web3.Web3.WebsocketProvider('ws://localhost:8545'))
+w3.middleware_onion.add(GasMiddleware)
+
+while True:
+ print(w3.eth.gas_price())
+ time.sleep(1)
diff --git a/tests/test_middleware.py b/tests/test_middleware.py
@@ -0,0 +1,73 @@
+import json
+import logging
+
+import unittest
+
+from gas_proxy.web3 import GasMiddleware
+
+logging.basicConfig(level=logging.DEBUG)
+
+
+class Mocket:
+
+
+ def __init__(self):
+ self.last_id = None
+ self.response = None
+ self.active = True
+
+
+ def toggle(self):
+ self.active = not self.active
+
+
+ def send(self, d):
+ if not self.active:
+ raise ConnectionError('socket deactivated')
+ o = json.loads(d)
+ self.last_id = o['id']
+ r = {
+ "jsonrpc": "2.0",
+ "id": self.last_id,
+ "result": "0x1324",
+ }
+ self.response = json.dumps(r)
+
+ def recv(self, c=0):
+ return self.response
+
+
+class MiddlewareTest(unittest.TestCase):
+
+
+ def setUp(self):
+ self.socket = Mocket()
+
+ def socket_constructor():
+ return self.socket
+
+ GasMiddleware.socket_constructor = socket_constructor
+
+
+ def tearDown(self):
+ pass
+
+
+ def test_middleware(self):
+ GasMiddleware.last_gas = "0x2a"
+ middleware = GasMiddleware(None, None)
+
+ self.socket.toggle()
+ r = middleware.__call__("eth_gasPrice", [])
+ self.assertEqual(r['result'], "0x2a")
+
+ self.socket.toggle()
+ r = middleware.__call__("eth_gasPrice", [])
+ self.assertEqual(r['result'], "0x1324")
+
+ self.socket.toggle()
+ r = middleware.__call__("eth_gasPrice", [])
+ self.assertEqual(r['result'], "0x1324")
+
+if __name__ == '__main__':
+ unittest.main()