Skip to content

fix: prevent unlock indexer quorum future-checkpoint crash#644

Merged
fewensa merged 3 commits into
mainfrom
ohh-97-unlock-indexer-stop-fix
Apr 3, 2026
Merged

fix: prevent unlock indexer quorum future-checkpoint crash#644
fewensa merged 3 commits into
mainfrom
ohh-97-unlock-indexer-stop-fix

Conversation

@fewensa
Copy link
Copy Markdown
Collaborator

@fewensa fewensa commented Apr 2, 2026

Summary

  • clamp ChainTool.quorum() to a safe past checkpoint when the requested quorum timepoint is still in the future
  • reuse the existing near-head safety margin for future-checkpoint retries so RPC clock skew cannot reintroduce the fatal revert path
  • keep stale data under the effective clamped cache key available as fetch-failure fallback
  • add regression coverage for the future-checkpoint path

Problem

  • unlock-dao was stuck at block 44021688 in production
  • the next ProposalCreated at block 44022375 triggered quorum(proposalSnapshot) with a future timestamp checkpoint (1775179697)
  • the governor deterministically reverted on that future checkpoint, and the indexer treated the revert as fatal, so the processor died and restarted in a loop

Validation

  • ./node_modules/.bin/jest --runInBand __tests__/unit/chaintool.test.ts
  • npx -y tsx -e 'import { ChainTool } from "./src/internal/chaintool.ts"; void (async () => { const tool = new ChainTool(); const result = await tool.quorum({ chainId: 8453, contractAddress: "0x65bA0624403Fc5Ca2b20479e9F626eD4D78E0aD9", governorTokenAddress: "0xaC27fa800955849d6D17cC8952Ba9dD6EAA66187", governorTokenStandard: "ERC20", timepoint: 1775179697n }); console.log(JSON.stringify({ clockMode: result.clockMode, quorum: result.quorum.toString(), decimals: result.decimals.toString() })); })();'
  • ./node_modules/.bin/tsc -p tsconfig.json --noEmit currently fails on pre-existing generated model/type baseline issues unrelated to this PR

Summary:
- clamp quorum lookups to a safe past checkpoint when a requested
  proposal snapshot is still in the future
- log the clamp decision and cover the future-checkpoint behavior in
  unit tests

Rationale:
- unlock-dao was dying on ProposalCreated because quorum(proposalSnapshot)
  deterministically reverted before the snapshot checkpoint existed
- clamping preserves proposal indexing progress without changing the
  existing near-head fallback behavior for normal quorum reads

Tests:
- ./node_modules/.bin/jest --runInBand __tests__/unit/chaintool.test.ts
- npx -y tsx -e 'import { ChainTool } from "./src/internal/chaintool.ts"; void (async () => { const tool = new ChainTool(); const result = await tool.quorum({ chainId: 8453, contractAddress: "0x65bA0624403Fc5Ca2b20479e9F626eD4D78E0aD9", governorTokenAddress: "0xaC27fa800955849d6D17cC8952Ba9dD6EAA66187", governorTokenStandard: "ERC20", timepoint: 1775179697n }); console.log(JSON.stringify({ clockMode: result.clockMode, quorum: result.quorum.toString(), decimals: result.decimals.toString() })); })();'
- ./node_modules/.bin/tsc -p tsconfig.json --noEmit (fails on existing generated model/type baseline unrelated to this change)
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 2, 2026

@fewensa fewensa marked this pull request as ready for review April 2, 2026 03:26
Copilot AI review requested due to automatic review settings April 2, 2026 03:26
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 642adcd576

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/indexer/src/internal/chaintool.ts
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Prevents the indexer from crashing when a Governor’s quorum(timepoint) is queried with a checkpoint that is still in the future (deterministic revert), by clamping to a safe past timepoint and adding a regression test to cover this path.

Changes:

  • Add helper functions to derive “stable” quorum timepoints and clamp future checkpoints to a past timepoint.
  • Allow currentClock() to accept an optional precomputed clockMode to avoid redundant reads.
  • Add a unit test asserting future quorum checkpoints are clamped and logged.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
packages/indexer/src/internal/chaintool.ts Clamp future quorum() timepoints; refactor stable timepoint calculation; optimize currentClock() by accepting optional clockMode.
packages/indexer/tests/unit/chaintool.test.ts Add regression coverage for the future-checkpoint clamping path and adjust existing tests for the new currentClock() usage.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/indexer/src/internal/chaintool.ts Outdated
Comment thread packages/indexer/src/internal/chaintool.ts Outdated
Summary:
- retry explicit snapshot quorum reads only after a deterministic revert proves the checkpoint is too new
- cache clamped quorum data under the effective queried checkpoint and cover the behavior in unit tests

Rationale:
- review feedback identified that the first fix always paid an extra currentClock RPC and could cache clamped quorum data under the original future snapshot key
- this keeps the unlock-dao crash fix while preserving correct snapshot semantics for repeated quorum reads

Tests:
- ./node_modules/.bin/jest --runInBand __tests__/unit/chaintool.test.ts
- npx -y tsx -e 'import { ChainTool } from ./src/internal/chaintool.ts; void (async () => { const tool = new ChainTool(); const result = await tool.quorum({ chainId: 8453, contractAddress: 0x65bA0624403Fc5Ca2b20479e9F626eD4D78E0aD9, governorTokenAddress: 0xaC27fa800955849d6D17cC8952Ba9dD6EAA66187, governorTokenStandard: ERC20, timepoint: 1775179697n }); console.log(JSON.stringify({ clockMode: result.clockMode, quorum: result.quorum.toString(), decimals: result.decimals.toString() })); })();'
@fewensa
Copy link
Copy Markdown
Collaborator Author

fewensa commented Apr 2, 2026

@codex review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cd109b919a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/indexer/src/internal/chaintool.ts Outdated
Comment thread packages/indexer/src/internal/chaintool.ts
@fewensa
Copy link
Copy Markdown
Collaborator Author

fewensa commented Apr 2, 2026

fix #642

Summary:
- widen future-checkpoint clamping to the same safety margin used by the
  near-head quorum path for timestamp and block clocks
- keep stale entries under the effective clamped cache key available when a
  retry fetch fails
- extend quorum unit coverage for the wider clamp target and stale clamped
  cache fallback behavior

Rationale:
- a one-second timestamp clamp could still be future on a lagging RPC, which
  reintroduced the fatal deterministic revert path for unlock-dao indexing
- clamped cache entries should remain usable as stale fallback data so
  transient retry failures do not bubble up as processor-fatal errors

Tests:
- ./node_modules/.bin/jest --runInBand __tests__/unit/chaintool.test.ts
- npx -y tsx -e 'import { ChainTool } from "./src/internal/chaintool.ts"; void (async () => { const tool = new ChainTool(); const result = await tool.quorum({ chainId: 8453, contractAddress: "0x65bA0624403Fc5Ca2b20479e9F626eD4D78E0aD9", governorTokenAddress: "0xaC27fa800955849d6D17cC8952Ba9dD6EAA66187", governorTokenStandard: "ERC20", timepoint: 1775179697n }); console.log(JSON.stringify({ clockMode: result.clockMode, quorum: result.quorum.toString(), decimals: result.decimals.toString() })); })();'
@fewensa
Copy link
Copy Markdown
Collaborator Author

fewensa commented Apr 3, 2026

[codex] Changes since last review:

  • widened the future-checkpoint clamp to the same 3-minute / 15-block safety buffer used by the near-head quorum path
  • preserved stale entries under the effective clamped cache key as fetch-failure fallback
  • added a regression test for stale clamped-cache fallback and updated the clamp target assertion
    Commits: 19eb892
    Tests: ./node_modules/.bin/jest --runInBand tests/unit/chaintool.test.ts; npx -y tsx -e "import { ChainTool } from "./src/internal/chaintool.ts"; void (async () => { const tool = new ChainTool(); const result = await tool.quorum({ chainId: 8453, contractAddress: "0x65bA0624403Fc5Ca2b20479e9F626eD4D78E0aD9", governorTokenAddress: "0xaC27fa800955849d6D17cC8952Ba9dD6EAA66187", governorTokenStandard: "ERC20", timepoint: 1775179697n }); console.log(JSON.stringify({ clockMode: result.clockMode, quorum: result.quorum.toString(), decimals: result.decimals.toString() })); })();"

@fewensa fewensa merged commit db3d63e into main Apr 3, 2026
4 checks passed
@fewensa fewensa deleted the ohh-97-unlock-indexer-stop-fix branch April 3, 2026 07:35
@fewensa fewensa mentioned this pull request Apr 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants