Skip to content

Missing events on eth_getLogs calls immediately after newHeads + occasional high latency #667

@rebrax0

Description

@rebrax0

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())

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions