Indexing dispute: cana-graph (QmeTjg…) on mainnet, staked.cloud PoI presented after the block was pruned

Filed an indexing dispute, 0xf5c978c85e375487badc200c3734a65823acfd8092fa36e9156ad3051ad94676, against staked.cloud 0x090f7382f9ea85c733cd501f4d87f16cb5b83ed3 for the PoI it presented on cana-graph, QmeTjgLfbsnT4bFWEtJf29uHLqjyQnkoAaCASShL1UVPEi on mainnet, at epoch block 25,875,139.

The PoI was presented nineteen hours after the block, from a node that had already pruned it, and it is the value a pruned graph-node returns for a block it no longer holds. Another staked.cloud node presented the correct PoI for the same block three hours after it was mined, and an independent full-history sync reproduces that value exactly.

Same shape five times across three epochs on this deployment, always from the node that presented the next morning. Full table and reproduction steps: Indexing dispute: a PoI presented after the node had pruned the block, staked.cloud on cana-graph (mainnet) - HackMD

Separate from the ftnGO query dispute on the same operator, which is about missing entities. Happy to answer anything the arbitrators or Staked want checked.

Thanks @fishermanpete, we’ve reproduced it and agree with the technical finding.

**What we confirmed**

- For `QmeTjgLfbsnT4bFWEtJf29uHLqjyQnkoAaCASShL1UVPEi` at block 25,875,139 (hash `0x04726bee…`) and indexer `0x090f7382…`, our graph-node returns `proofOfIndexing` `0xfff20c85…`, the value presented on 2026-09-01. Its public PoIs at 25,867,938, 25,875,139 and 25,882,338 are `0xf2ebaea9…`, `0x78ef641a…` and `0x192a6a50…`, matching the report’s “wrong” values.

- The manifest sets `indexerHints.prune: auto`, so graph-node (we run v0.45.0) keeps its default minimum of 500 blocks of history, about two hours on mainnet. Our closes on this deployment came 3 to 21 hours after the epoch block.

- graph-node’s `proofOfIndexing` only rejects blocks above the subgraph head. For a block below the earliest retained block, it builds a PoI from whatever digest rows remain and returns it as a normal result (`get_proof_of_indexing` in `store/postgres/src/deployment_store.rs`). Nothing in graph-node or indexer-agent flags it.

Our indexed data is correct. Our PoI at block 26,242,728 matches three independent indexers, and, as the report notes, a full-history sync reproduces our correct presentations byte for byte. The other four wrong presentations in the report have the same cause.

**Our position**

We’d ask the Arbitrators to consider a Draw under §8. The incorrect PoI came from graph-node returning a value, rather than an error, for a block it no longer held, under the default history the subgraph’s own manifest requests. This is graph-node’s default behavior, not something specific to our setup: any node on default settings that is asked for the PoI of a prune: auto deployment after the epoch block has been pruned (about two hours on mainnet, minutes on faster chains) returns a wrong value if the subgraph has changed since. As the report shows, every node allocated to this deployment, including pinax2 and Ellipfra, returns the same values for these blocks today. Because pruned nodes agree with one another, comparing PoIs across indexers doesn’t reveal it; only a full-history sync does.

The report contrasts this with our other indexer presenting correctly hours earlier. All staked.cloud indexers query the same graph-node and store, so the correct and incorrect presentations came from the same data, computed at different times. Had graph-node returned no PoI, indexer-agent would have refused to present one.

**What we’re changing**

1. Raising retained history on every pruned deployment we index (133) to just over one epoch of blocks for its chain (8,000 blocks on mainnet), so the epoch block is still held whenever we close. New pruned deployments will also keep more than an epoch by default.

2. Holding closes on those deployments until the extra history has built up.

Is there any additional information you’d like from us?

Thanks,

Staked Team

PS I think this also brings up a larger discussion about how to handle POIs of pruned subgraphs. I’d like to raise the issue here with the graph-node maintainers, specifically I propose that proofOfIndexing return an error for blocks below the earliest retained block and that PoI digests be kept longer than entity history. In particular, it seems that the default value of GRAPH_MIN_HISTORY_BLOCKS=500 combined with the current graph-node behavior where it doesn’t refuse POI requests for pruned blocks, means that any indexer using the default will not produce valid POIs on pruned subgraphs. So an indexer on the default will submit an invalid POI whenever it closes or collects on such a subgraph more than about 500 blocks after the epoch’s start block: roughly 100 minutes later on mainnet, or 2 minutes on Arbitrum.

Note this response was generated with some AI help :slight_smile:

Thanks nate, sorry for the slow reply. Nothing more needed from you, and the retention change is the right fix.

On the default: agreed that this is a graph-node trap and not something only you walked into. proofOfIndexing should refuse a block below the earliest retained one, and the digest rows should outlive the entity history. I have the data from both sides of this thread ready for the issue, so do you want to open it or should I?

Whether that makes it a draw under §8 is for the arbitrators. The one fact I would leave with them is that the same store had produced the right number for the same block hours earlier.

Hi, thanks

Dispute information:

Dispute (0xf5c978c85e375487badc200c3734a65823acfd8092fa36e9156ad3051ad94676)
├─ Type: Indexing
├─ Status: Undecided (7.86 days ago)
├─ Indexer: 0x090f7382f9ea85c733cd501f4d87f16cb5b83ed3
├─ Fisherman: 0x083a086ca2ccec6526cbe97b2841a481f99834af
├─ SubgraphDeployment
│  └─ id: 0xef8bacf185bd0e6cc2be84c50397ac126bf042e64ad636e8157511bfdfa90b03 (QmeTjgLfbsnT4bFWEtJf29uHLqjyQnkoAaCASShL1UVPEi)
├─ Economics
│  ├─ indexerSlashableStake: 3704404.456401921578984404 GRT
│  └─ indexingRewardsCollected: 86.494212272746928951 GRT
├─ Allocation
│  ├─ id: 0xea7bd6c32966f7207bf86852660337a5fc926155
│  ├─ createdAtEpoch: 1365
│  ├─ createdAtBlock: 0x7dacc853daa580099f940a3d094989ae8ec340884b6866be9fe6796fcf100dfd
│  ├─ closedAtEpoch
│  │  ├─ id: 1368
│  │  └─ startBlock: 0x5fc1badc56f7ea8a6cb554a2135db3455f9d6dcd938dfdb338f3a54718b378cb (#25875137)
│  └─ closedAtBlock: 0xd73e62f143eed98b10edaad9fdeeb99aefbaa1b7d7e3cbf61bcafc739208a7ef (#25880875)
└─ POI
   ├─ submitted: 0xfff20c85a295cf7be0f78dcd50eab504f4c54b374a31bb98ab00e0895b6431db
   ├─ match: Not-Found
   ├─ previousEpochPOI: Not-Found
   └─ lastEpochPOI: Not-Found

Dispute resolution:
We agree with the conclusion reached by both fisherman and disputed indexer. In accordance with §8 this is a clear case of graph-node bug being the root cause for the POI discrepancy. We’ll be resolving this dispute as a draw. The issue has been reported to graph-node team (proofOfIndexing returns an incorrect POI for pruned blocks instead of an error · Issue #6751 · graphprotocol/graph-node · GitHub) and will be addressed in the next version.

Dispute drawn here: Arbitrum One Transaction Hash: 0x9440505d84... | Arbitrum One