IntersectMBO / IntersectMBO/ouroboros-consensus

[BUG] - Mismatch in ChainDB.q-s-m wrt MaxSlotNo

Open
#1,870 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
Haskell
Stars
67
Forks
43
Avg merge
5d 13h
Merged PRs (30d)
43

Description

https://github.com/IntersectMBO/ouroboros-consensus/actions/runs/21832592717/job/62994870493?pr=1826#logs

The chain is:
```
Genesis -- A (0, -8986190201926803368, Valid)
\- B (2, -1486433721842374672, Invalid)
```

When the block B arrives, it is popped from the queue
```
TraceAddBlockEvent (PoppedBlockFromQueue (RealPoint (SlotNo 2) (TestHeaderHash (-1486433721842374672))))
```
and then it is ignored, because the block A has been boosted with Peras certificates, and the chainsel logic sees that it is at the same block number than the immutable tip so we should not switch:
```
TraceAddBlockEvent (IgnoreBlockOlderThanImmTip (RealPoint (SlotNo 2) (TestHeaderHash (-1486433721842374672))))
```

However when the ChainDB is then asked what is the MaxSlotNo, it reports 2. However this answer comes from:
```haskell
curChainMaxSlotNo <-
maxSlotNoFromWithOrigin . AF.headSlot . icWithoutTime
<$> readTVar cdbChain
volatileDbMaxSlotNo <- VolatileDB.getMaxSlotNo cdbVolatileDB
queuedMaxSlotNo <- getMaxSlotNoChainSelQueue cdbChainSelQueue
return $ curChainMaxSlotNo `max` volatileDbMaxSlotNo `max` queuedMaxSlotNo
```
And the block is not in the cdbChain (as we saw no `AddedToCurrentChain` for this block), nor in the cdbVolatileDB (as we saw no `AddedBlockToVolatileDB` traces for this block) so it has to be in the chain sel queue? but it was unqueued precisely by the existance of the `PoppedBlockFromQueue` so something fishy is going on here.

Moreover, I cannot reproduce this test failure on my machine with the same seed and GHC
```
❯ cabal test storage-test --test-option="-p /ChainDB q-s-m.sequential/" --test-option="--quickcheck-replay=(SMGen 15194541575572225434 13540340026014931941,80)" --test-option="--hide-successes"
Build profile: -w ghc-9.12.2 -O1
In order, the following will be built (use -v for more details):
- ouroboros-consensus-0.28.0.0 (test:storage-test) (ephemeral targets)
Preprocessing test suite 'storage-test' for ouroboros-consensus-0.28.0.0...
Building test suite 'storage-test' for ouroboros-consensus-0.28.0.0...
Running 1 test suites...
Test suite storage-test: RUNNING...

All 1 tests passed (22.08s)
Test suite storage-test: PASS
Test suite logged to:
/home/javier/code/cardano/ouroboros-consensus/dist-newstyle/build/x86_64-linux/ghc-9.12.2/ouroboros-consensus-0.28.0.0/t/storage-test/test/ouroboros-consensus-0.28.0.0-storage-test.log
1 of 1 test suites (1 of 1 test cases) passed.
```

EDIT: actually, the reading from the queue happens from the set of known points:
```
data ChainSelQueue m blk = ChainSelQueue
{ varChainSelQueue :: TBQueue m (ChainSelMessage m blk)
, varChainSelPoints :: StrictTVar m (MultiSet (RealPoint blk))
}
```
but we only add when we add a block to the queue:
```
addBlockToAdd tracer (ChainSelQueue{varChainSelQueue, varChainSelPoints}) punish blk = do
...
modifyTVar varChainSelPoints $ MultiSet.insert pt
```
and we delete when we processed a block:
```
processedChainSelMessage ChainSelQueue{varChainSelPoints} = \case
ChainSelAddBlock BlockToAdd{blockToAdd = blk} ->
modifyTVar varChainSelPoints $ MultiSet.delete (blockRealPoint blk)
```
which gets called immediately after the chainSelSync:
```
( \message -> do
lift $ case message of
ChainSelReprocessLoEBlocks _ ->
trace PoppedReprocessLoEBlocksFromQueue
ChainSelAddBlock BlockToAdd{blockToAdd} ->
trace $ PoppedBlockFromQueue $ blockRealPoint blockToAdd
ChainSelAddPerasCert cert _varProcessed ->
traceWith cdbTracer $
TraceAddPerasCertEvent $
PoppedPerasCertFromQueue (getPerasCertRound cert) (getPerasCertBoostedBlock cert)
chainSelSync cdb message
lift $ atomically $ processedChainSelMessage cdbChainSelQueue message
)
```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.