Skip to content

Commit

Permalink
New algorithm to detect invalid eth_getLogs responses (#880)
Browse files Browse the repository at this point in the history
* feat: new realtime eth_getLogs validation

* fix: type and test errors

* request logs by block number in realtime

* Revert "request logs by block number in realtime"

This reverts commit 2dad0a2.

* chore: changeset

* chore: changeset
  • Loading branch information
kyscott18 committed May 13, 2024
1 parent 24e7bf5 commit 10e2548
Show file tree
Hide file tree
Showing 5 changed files with 30 additions and 250 deletions.
5 changes: 5 additions & 0 deletions .changeset/lemon-nails-invite.md
@@ -0,0 +1,5 @@
---
"@ponder/core": patch
---

Improved realtime detection for invalid "eth_getLogs" responses by comparing response length with a block's bloom filter.
25 changes: 0 additions & 25 deletions packages/core/src/sync-realtime/format.test.ts

This file was deleted.

38 changes: 2 additions & 36 deletions packages/core/src/sync-realtime/format.ts
@@ -1,20 +1,10 @@
import type { SyncBlock, SyncLog } from "@/sync/index.js";
import {
type Block,
type BlockTag,
type Hex,
type Log,
hexToNumber,
} from "viem";
import type { SyncBlock } from "@/sync/index.js";
import { type Block, type BlockTag, hexToNumber } from "viem";

export type LightBlock = Pick<
Block<number, boolean, Exclude<BlockTag, "pending">>,
"hash" | "parentHash" | "number" | "timestamp" | "logsBloom"
>;
export type LightLog = Pick<
Log<number, Hex, false>,
"blockHash" | "blockNumber" | "transactionHash" | "logIndex"
>;

export const syncBlockToLightBlock = ({
hash,
Expand All @@ -29,27 +19,3 @@ export const syncBlockToLightBlock = ({
timestamp: hexToNumber(timestamp),
logsBloom,
});

export const syncLogToLightLog = ({
blockHash,
blockNumber,
transactionHash,
logIndex,
}: SyncLog): LightLog => ({
blockHash,
blockNumber: hexToNumber(blockNumber),
transactionHash,
logIndex,
});

export const sortLogs = <log extends SyncLog | LightLog>(
logs: log[],
): log[] => {
return logs.sort((a, b) => {
if (a.blockNumber < b.blockNumber) return -1;
if (a.blockNumber > b.blockNumber) return 1;
if (a.logIndex < b.logIndex) return -1;
if (a.logIndex > b.logIndex) return 1;
return 0;
});
};
69 changes: 4 additions & 65 deletions packages/core/src/sync-realtime/service.test.ts
@@ -1,4 +1,3 @@
import { erc20ABI, factoryABI, pairABI } from "@/_test/generated.js";
import {
setupAnvil,
setupCommon,
Expand All @@ -9,17 +8,8 @@ import { testClient } from "@/_test/utils.js";
import type { EventSource } from "@/config/sources.js";
import { type SyncBlock, _eth_getBlockByNumber } from "@/sync/index.js";
import { maxCheckpoint } from "@/utils/checkpoint.js";
import { getAbiItem, getEventSelector } from "viem";
import { beforeEach, expect, test, vi } from "vitest";
import { syncBlockToLightBlock } from "./format.js";
import {
create,
handleBlock,
handleReorg,
kill,
start,
validateLocalBlockchainState,
} from "./service.js";
import { create, handleBlock, handleReorg, kill, start } from "./service.js";

beforeEach(setupCommon);
beforeEach(setupAnvil);
Expand Down Expand Up @@ -47,11 +37,6 @@ test("createRealtimeSyncService()", async (context) => {

expect(realtimeSyncService.finalizedBlock.number).toBe(0);
expect(realtimeSyncService.localChain).toHaveLength(0);
expect(realtimeSyncService.eventSelectors).toStrictEqual([
getEventSelector(getAbiItem({ abi: erc20ABI, name: "Transfer" })),
getEventSelector(getAbiItem({ abi: factoryABI, name: "PairCreated" })),
getEventSelector(getAbiItem({ abi: pairABI, name: "Swap" })),
]);

await cleanup();
});
Expand Down Expand Up @@ -225,7 +210,7 @@ test("start() finds reorg with block hash", async (context) => {
queue.add({
...block,
number: "0x6",
parentHash: realtimeSyncService.localChain[3].block.hash,
parentHash: realtimeSyncService.localChain[3].hash,
hash: "0x0000000000000000000000000000000000000000000000000000000000000000",
});
},
Expand Down Expand Up @@ -594,7 +579,7 @@ test("handleReorg() finds common ancestor", async (context) => {
});
}

realtimeSyncService.localChain[2].block.hash = "0x0";
realtimeSyncService.localChain[2].hash = "0x0";

await handleReorg(
realtimeSyncService,
Expand Down Expand Up @@ -658,7 +643,7 @@ test("handleReorg() emits fatal error for deep reorg", async (context) => {

realtimeSyncService.finalizedBlock.hash = "0x1";
for (let i = 0; i < 3; i++) {
realtimeSyncService.localChain[i].block.hash = "0x0";
realtimeSyncService.localChain[i].hash = "0x0";
}

await handleReorg(
Expand All @@ -677,49 +662,3 @@ test("handleReorg() emits fatal error for deep reorg", async (context) => {

await cleanup();
});

test("validateLocalBlockchainState()", async (context) => {
const { common, networks, requestQueues, sources } = context;
const { syncStore, cleanup } = await setupDatabaseServices(context);

const finalizedBlock = await requestQueues[0].request({
method: "eth_getBlockByNumber",
params: ["0x0", false],
});

const realtimeSyncService = create({
common,
syncStore,
network: networks[0],
requestQueue: requestQueues[0],
sources,
finalizedBlock: finalizedBlock as SyncBlock,
onEvent: vi.fn(),
onFatalError: vi.fn(),
});

for (let i = 1; i <= 4; i++) {
await handleBlock(realtimeSyncService, {
newHeadBlock: await _eth_getBlockByNumber(
{ requestQueue: requestQueues[0] },
{ blockNumber: i },
),
});
}

realtimeSyncService.localChain[1].logs[1].logIndex = "0x0";

const isInvalid = await validateLocalBlockchainState(
realtimeSyncService,
await _eth_getBlockByNumber(
{ requestQueue: requestQueues[0] },
{ blockNumber: 4 },
).then(syncBlockToLightBlock),
);

expect(isInvalid).toBe(true);

await kill(realtimeSyncService);

await cleanup();
});

0 comments on commit 10e2548

Please sign in to comment.