You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
proofOfIndexing returns an incorrect POI for pruned blocks instead of an error #6751
proofOfIndexing returns a wrong POI, with no error, when the requested block is below the deployment's earliest retained block (that is, the block has been pruned).
DeploymentStore::get_proof_of_indexing (store/postgres/src/deployment_store.rs) only checks that the block is not above the subgraph head:
It never compares the block to earliest_block_number. If the block has been pruned, it still runs the Poi$ query at that block. Pruning has removed the digest rows, or the remaining rows no longer cover the block, so the query returns an incomplete set of causality-region digests. ProofOfIndexingFinisher hashes them into a well-formed but wrong POI, and it is returned as a normal result.
publicProofsOfIndexing uses the same code path (get_public_proof_of_indexing → get_proof_of_indexing), so it has the same problem.
Why this matters
When an allocation closes, the indexer submits the POI for the current epoch's start block. A deployment with indexerHints.prune: auto keeps GRAPH_MIN_HISTORY_BLOCKS blocks of history. This defaults to 2 * reorg_threshold, so 500 blocks, about 100 minutes on mainnet. After that, the epoch block is pruned, and closing the allocation submits a wrong POI that can be disputed. Nothing in graph-node shows that the value is wrong. All pruned nodes agree with each other, so cross-checking between an operator's own nodes does not catch it either.
In get_proof_of_indexing, refuse to compute a POI for a block below the deployment's earliest retained block:
if block.number < earliest_block_number {returnErr(StoreError::...(/* "block {} has been pruned; earliest available block is {}" */));}
earliest_block_number is already tracked in deployment state.
Out of scope (possible follow-ups)
The prune: auto default (500 blocks) is much shorter than an epoch on every chain. One global block count can't fit all chains (one epoch is about 7.2k blocks on mainnet, about 43k on Base, and about 345k on Arbitrum), so a fix probably needs a minimum per chain or based on time.
Activity