dapi: final-window StoreCommit can land in the same block as the previous one and is rejected (only one commit per block allowed) #2201
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)
🔄 Auto-synced from Issue #2201 every hour.
@tcharchian FYI