From 60e7b6a33f3280eb08412cbec742e3d23d0d4404 Mon Sep 17 00:00:00 2001 From: Kevin <90073088+typedarray@users.noreply.github.com> Date: Wed, 8 Jan 2025 17:05:23 -0500 Subject: [PATCH] fix: indexing error regression (#1424) * fix: indexing error log detail regression * chore: changeset * fix: review, and handle trown objects that are not Errors --------- Co-authored-by: typedarray <90073088+0xOlias@users.noreply.github.com> --- .changeset/unlucky-wasps-poke.md | 5 + packages/core/src/indexing/service.ts | 131 ++++++++++++++++++-------- packages/core/src/utils/print.ts | 4 + 3 files changed, 103 insertions(+), 37 deletions(-) create mode 100644 .changeset/unlucky-wasps-poke.md diff --git a/.changeset/unlucky-wasps-poke.md b/.changeset/unlucky-wasps-poke.md new file mode 100644 index 000000000..5e4c0ebac --- /dev/null +++ b/.changeset/unlucky-wasps-poke.md @@ -0,0 +1,5 @@ +--- +"ponder": patch +--- + +Fixed a bug where indexing errors did not include the block number and transaction hash of the event being indexed. diff --git a/packages/core/src/indexing/service.ts b/packages/core/src/indexing/service.ts index 267bc7713..cff2a39e9 100644 --- a/packages/core/src/indexing/service.ts +++ b/packages/core/src/indexing/service.ts @@ -1,5 +1,6 @@ import type { IndexingFunctions } from "@/build/configAndIndexingFunctions.js"; import type { Common } from "@/common/common.js"; +import { BaseError } from "@/common/errors.js"; import type { Network } from "@/config/networks.js"; import type { Schema } from "@/drizzle/index.js"; import type { IndexingStore } from "@/indexing-store/index.js"; @@ -10,6 +11,7 @@ import { isAddressFactory, } from "@/sync/source.js"; import type { Db } from "@/types/db.js"; +import type { Block, Log, Trace, Transaction } from "@/types/eth.js"; import { type Checkpoint, decodeCheckpoint, @@ -377,54 +379,32 @@ const executeSetup = async ( ); } catch (_error) { if (indexingService.isKilled) return { status: "killed" }; - const error = _error as Error; - - const decodedCheckpoint = decodeCheckpoint(event.checkpoint); + const error = _error instanceof Error ? _error : new Error(String(_error)); addStackTrace(error, common.options); - common.metrics.ponder_indexing_has_error.set(1); + if (error instanceof BaseError) { + error.meta.push(toErrorMeta(event)); + } else { + // @ts-expect-error + error.meta = [toErrorMeta(event)]; + } + const decodedCheckpoint = decodeCheckpoint(event.checkpoint); common.logger.error({ service: "indexing", msg: `Error while processing '${event.name}' event in '${networkByChainId[event.chainId]!.name}' block ${decodedCheckpoint.blockNumber}`, error, }); + common.metrics.ponder_indexing_has_error.set(1); + return { status: "error", error: error }; } return { status: "success" }; }; -const toErrorMeta = (event: Event) => { - switch (event.type) { - case "log": - case "trace": { - return `Event arguments:\n${prettyPrint(event.event.args)}`; - } - - case "transfer": { - return `Event arguments:\n${prettyPrint(event.event.transfer)}`; - } - - case "block": { - return `Block:\n${prettyPrint({ - hash: event.event.block.hash, - number: event.event.block.number, - timestamp: event.event.block.timestamp, - })}`; - } - - case "transaction": { - return `Transaction:\n${prettyPrint({ - hash: event.event.transaction.hash, - block: event.event.block.number, - })}`; - } - } -}; - const executeEvent = async ( indexingService: Service, { event }: { event: Event }, @@ -465,17 +445,19 @@ const executeEvent = async ( ); } catch (_error) { if (indexingService.isKilled) return { status: "killed" }; - const error = _error as Error & { meta?: string[] }; - - const decodedCheckpoint = decodeCheckpoint(event.checkpoint); + const error = _error instanceof Error ? _error : new Error(String(_error)); addStackTrace(error, common.options); - error.meta = Array.isArray(error.meta) ? error.meta : []; - if (error.meta.length === 0) { + if (error instanceof BaseError) { error.meta.push(toErrorMeta(event)); + } else { + // @ts-expect-error + error.meta = [toErrorMeta(event)]; } + const decodedCheckpoint = decodeCheckpoint(event.checkpoint); + common.logger.error({ service: "indexing", msg: `Error while processing '${event.name}' event in '${networkByChainId[event.chainId]!.name}' block ${decodedCheckpoint.blockNumber}`, @@ -489,3 +471,78 @@ const executeEvent = async ( return { status: "success" }; }; + +const blockText = (block: Block) => + `Block:\n${prettyPrint({ + hash: block.hash, + number: block.number, + timestamp: block.timestamp, + })}`; + +const transactionText = (transaction: Transaction) => + `Transaction:\n${prettyPrint({ + hash: transaction.hash, + from: transaction.from, + to: transaction.to, + })}`; + +const logText = (log: Log) => + `Log:\n${prettyPrint({ + index: log.logIndex, + address: log.address, + })}`; + +const traceText = (trace: Trace) => + `Trace:\n${prettyPrint({ + traceIndex: trace.traceIndex, + from: trace.from, + to: trace.to, + })}`; + +const toErrorMeta = (event: Event | SetupEvent) => { + switch (event.type) { + case "setup": { + return `Block:\n${prettyPrint({ + number: event.block, + })}`; + } + + case "log": { + return [ + `Event arguments:\n${prettyPrint(event.event.args)}`, + logText(event.event.log), + transactionText(event.event.transaction), + blockText(event.event.block), + ].join("\n"); + } + + case "trace": { + return [ + `Call trace arguments:\n${prettyPrint(event.event.args)}`, + traceText(event.event.trace), + transactionText(event.event.transaction), + blockText(event.event.block), + ].join("\n"); + } + + case "transfer": { + return [ + `Transfer arguments:\n${prettyPrint(event.event.transfer)}`, + traceText(event.event.trace), + transactionText(event.event.transaction), + blockText(event.event.block), + ].join("\n"); + } + + case "block": { + return blockText(event.event.block); + } + + case "transaction": { + return [ + transactionText(event.event.transaction), + blockText(event.event.block), + ].join("\n"); + } + } +}; diff --git a/packages/core/src/utils/print.ts b/packages/core/src/utils/print.ts index b7e3c5f86..f849bf88f 100644 --- a/packages/core/src/utils/print.ts +++ b/packages/core/src/utils/print.ts @@ -15,10 +15,14 @@ export function prettyPrint( return [key, trimmedValue]; }) .filter(Boolean) as [string, string][]; + + if (entries.length === 0) return " (empty object)"; + const maxLength = entries.reduce( (acc, [key]) => Math.max(acc, key.length), 0, ); + return entries .map(([key, value]) => ` ${`${key}`.padEnd(maxLength + 1)} ${value}`) .join("\n");