-
Couldn't load subscription status.
- Fork 934
Description
Noticed that calling eth_getLogs immediately after receiving a newHead is often missing events. Running on a base node, with exact setup described here
This is very frequent when calling for a large range of blocks (ie the last 100) rather than for just the last block - it will however be missing most recent events, so should be unaffected by the range. Have also seen instances of it missing events near the end of the block range but including the latest events, which seems very odd. Haven't been able to confirm if it will happen when calling for 1 block at a time yet.
Also noticing occasional huge latencies (double digit seconds) on this call, when made with sub100 block ranges, with all other calls we make to our node performing fine. Latency spikes are occurring during volatility == more on-chain activity.
Here's a simple python script which reproduces the issue very consistently on our node - usually seeing an error within 5-10 blocks, although sometimes longer. Script just makes one call immediately after getting newHeads and again 500ms later, and compares the response.
import asyncio
import json
import websockets
WS_ENDPOINT = "ENTER_NODE_HERE"
ADDRESS = "0xb2cc224c1c9fee385f8ad6a55b4d94e92359dc59"
async def eth_call(ws, method, params):
req = {
"jsonrpc": "2.0",
"id": int(asyncio.get_event_loop().time() * 1000), # unique-ish ID
"method": method,
"params": params
}
await ws.send(json.dumps(req))
while True:
msg = await ws.recv()
data = json.loads(msg)
if "id" in data and data["id"] == req["id"]:
return data.get("result")
async def get_logs(ws, from_block, to_block):
params = [{
"fromBlock": hex(from_block),
"toBlock": hex(to_block),
"address": ADDRESS
}]
return await eth_call(ws, "eth_getLogs", params)
async def listen_new_heads():
async with websockets.connect(WS_ENDPOINT) as ws:
await ws.send(json.dumps({
"jsonrpc": "2.0",
"id": 1,
"method": "eth_subscribe",
"params": ["newHeads"]
}))
print("Subscribed to newHeads...")
while True:
msg = await ws.recv()
data = json.loads(msg)
if "params" in data and "result" in data["params"]:
block_number = int(data["params"]["result"]["number"], 16)
print(f"\n=== New block: {block_number} ===")
# First call
logs_1 = await get_logs(ws, block_number - 100, block_number)
print(f" First call for blocks {block_number-100}->{block_number} log count: {len(logs_1)}")
# Wait 500ms
await asyncio.sleep(0.5)
# Second call
logs_2 = await get_logs(ws, block_number - 100, block_number)
print(f" Second call for blocks {block_number-100}->{block_number} log count: {len(logs_2)}")
if logs_1 != logs_2:
print(" Logs differ between immediate and delayed call!")
set1 = {json.dumps(l, sort_keys=True) for l in logs_1}
set2 = {json.dumps(l, sort_keys=True) for l in logs_2}
print(" Added:", [json.loads(x) for x in set2 - set1])
print(" Removed:", [json.loads(x) for x in set1 - set2])
else:
print("Logs are consistent.")
asyncio.run(listen_new_heads())