Skip to content

dapi: final-window StoreCommit can land in the same block as the previous one and is rejected (only one commit per block allowed) #2201

Open @maria-mitina opened 2026-11-02 17:18 UTC 1 comment Updated 2026-11-02 17:22 UTC

Summary

In the last blocks of a PoC generation stage, the dapi can send a MsgPoCV2StoreCommit while its previous commit is still in the mempool. When both land in the same block, the chain rejects the second with only one commit per block allowed (code 1137). The fee grant still pays for the rejected tx, about 224,000 ngonka at 1 ngonka/gas.

Observed on testnet4 (build 70ceeb804, stage 32560)

The dapi normally sends one commit per block. When one block has no commit, the next block gets two:

Participant ML nodes Block without a commit Block with two commits Rejected count
jx6tad 2 32617 32618 (count 9408 ok, 9568 rejected) 9568
6jw2rm 1 32618 32619 (count 8768 ok, 8896 rejected) 8896

This does not depend on how many ML nodes a participant has. The rejected tx's fee came out of the ML-ops key's fee grant. No commit was lost: the next block carried a higher count.

Cause

decentralized-api/poc/commit_worker.go: during the final commit window (inFinalCommitWindow, the last 3 blocks before the deadline), the two guards that stop a second in-flight commit are skipped.

  • Line 383: if !finalWindow && height == w.lastAcceptedBroadcastHeight
  • Line 446: if pending, ok := w.pending[key]; ok && !finalWindow && ...

If the previous commit misses its block, the worker sends the next one anyway. Both are included together, and the keeper rejects the second (msg_server_poc_v2_commit.go:251, where existing.CommitBlockHeight == currentBlockHeight). The ante recheck in poc_period_validation.go:325 only catches this when entry.Count <= existing.Count, so a higher count gets through to DeliverTx.

Impact

Low. It costs about one rejected tx fee per participant per stage when a block is missed near the end of a stage, and adds one failed tx to the chain. Rewards and PoC weight are not affected.

Possible fix

In the final window, still skip a model that has a pending commit in the mempool, unless that commit's timeout height has passed. Or make sure at most one commit per (stage, model) is in flight at a time.


💬 Comments (1)

@maria-mitina commented 2026-11-02 17:22 UTC

@tcharchian FYI


🔄 Auto-synced from Issue #2201 every hour.