Skip to content

Commit 91d36ca

Browse files
authored
db-analyser: support full block application in --store-ledger (#1155)
This PR adds support for full validation of blocks according to the ledger rules for `--store-ledger`. This is useful to validate changes against an existing DB. In particular, if there is an error, a ledger state just before the offending block will be stored. Also, in a separate commit, we address the third point of #35 in order to avoid users being confused when seeing both `--validate-all-blocks` and `--full-ledger-validation`.
2 parents 1cf0607 + 44aad2a commit 91d36ca

File tree

5 files changed

+87
-43
lines changed

5 files changed

+87
-43
lines changed

ouroboros-consensus-cardano/README.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,12 +91,12 @@ User should run this if they are dealing with a `cardano` chain.
9191

9292
The user can limit the maximum number of blocks that db-analyser will process.
9393

94-
### Database validation
94+
### Database validation, via --db-validation
9595

9696
The tool provides two database validation policies:
9797

9898
- `validate-all-blocks`, which will cause the tool to validate all chunks on the
99-
immutable and volatile databases.
99+
immutable database.
100100
- `minimum-block-validation`, which will cause the tool to validate only the
101101
most recent chunk in the immutable database.
102102

@@ -127,6 +127,12 @@ Lastly the user can provide the analysis that should be run on the chain:
127127
slot number, it will create one on the next available slot number (and issue a
128128
warning about this fact).
129129

130+
By default, for better performance, blocks are only *re*applied, skipping eg
131+
validation of signatures and Plutus scripts. If desired (eg when investigating
132+
a Ledger bug), one can use `--full-ledger-validation` to also perform these
133+
checks. If there is an error on block application, the previous ledger state
134+
is stored.
135+
130136
* `--count-blocks` prints out the number of blocks it saw on the chain
131137

132138
* `--benchmark-ledger-ops` applies the main ledger calculations to each block in

ouroboros-consensus-cardano/app/DBAnalyser/Parsers.hs

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{-# LANGUAGE ApplicativeDo #-}
22
{-# LANGUAGE CPP #-}
3+
{-# LANGUAGE LambdaCase #-}
34

45
module DBAnalyser.Parsers (
56
BlockType (..)
@@ -8,6 +9,7 @@ module DBAnalyser.Parsers (
89
) where
910

1011
import Cardano.Crypto (RequiresNetworkMagic (..))
12+
import Cardano.Tools.DBAnalyser.Analysis
1113
import Cardano.Tools.DBAnalyser.Block.Byron
1214
import Cardano.Tools.DBAnalyser.Block.Cardano
1315
import Cardano.Tools.DBAnalyser.Block.Shelley
@@ -55,16 +57,18 @@ parseSelectDB =
5557

5658

5759
parseValidationPolicy :: Parser (Maybe ValidateBlocks)
58-
parseValidationPolicy = optional $ asum [
59-
flag' ValidateAllBlocks $ mconcat [
60-
long "validate-all-blocks"
61-
, help "Validate all blocks of the Immutable DB"
62-
]
63-
, flag' MinimumBlockValidation $ mconcat [
64-
long "minimum-block-validation"
65-
, help "Validate a minimum part of the Immutable DB"
66-
]
67-
]
60+
parseValidationPolicy =
61+
optional $ option reader $ mconcat [
62+
long "db-validation"
63+
, help $ "The extent of the ChainDB on-disk files validation. This is "
64+
<> "completely unrelated to validation of the ledger rules. "
65+
<> "Possible values: validate-all-blocks, minimum-block-validation."
66+
]
67+
where
68+
reader = maybeReader $ \case
69+
"validate-all-blocks" -> Just ValidateAllBlocks
70+
"minimum-block-validation" -> Just MinimumBlockValidation
71+
_ -> Nothing
6872

6973
parseAnalysis :: Parser AnalysisName
7074
parseAnalysis = asum [
@@ -114,10 +118,19 @@ parseAnalysis = asum [
114118
]
115119

116120
storeLedgerParser :: Parser AnalysisName
117-
storeLedgerParser = (StoreLedgerStateAt . SlotNo) <$> option auto
118-
( long "store-ledger"
119-
<> metavar "SLOT_NUMBER"
120-
<> help "Store ledger state at specific slot number" )
121+
storeLedgerParser = do
122+
slot <- SlotNo <$> option auto
123+
( long "store-ledger"
124+
<> metavar "SLOT_NUMBER"
125+
<> help "Store ledger state at specific slot number" )
126+
ledgerValidation <- flag LedgerReapply LedgerApply
127+
( long "full-ledger-validation"
128+
<> help ( "Use full block application while applying blocks to ledger states, "
129+
<> "also validating signatures and scripts. "
130+
<> "This is much slower than block reapplication (the default)."
131+
)
132+
)
133+
pure $ StoreLedgerStateAt slot ledgerValidation
121134

122135
checkNoThunksParser :: Parser AnalysisName
123136
checkNoThunksParser = CheckNoThunksEvery <$> option auto

ouroboros-consensus-cardano/app/db-analyser.hs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,15 @@
44

55
-- | Database analysis tool.
66
--
7-
-- Usage: db-analyser --db PATH [--analyse-from SLOT_NUMBER]
8-
-- [--validate-all-blocks | --minimum-block-validation]
7+
-- Usage: db-analyser --db PATH [--verbose] [--analyse-from SLOT_NUMBER]
8+
-- [--db-validation ARG]
99
-- [--show-slot-block-no | --count-tx-outputs |
1010
-- --show-block-header-size | --show-block-txs-size |
11-
-- --show-ebbs | --store-ledger SLOT_NUMBER | --count-blocks |
12-
-- --checkThunks BLOCK_COUNT | --trace-ledger |
13-
-- --repro-mempool-and-forge INT | --benchmark-ledger-ops
14-
-- [--out-file FILE] |
11+
-- --show-ebbs | --store-ledger SLOT_NUMBER
12+
-- [--full-ledger-validation] |
13+
-- --count-blocks | --checkThunks BLOCK_COUNT |
14+
-- --trace-ledger | --repro-mempool-and-forge INT |
15+
-- --benchmark-ledger-ops [--out-file FILE] |
1516
-- --get-block-application-metrics NUM [--out-file FILE]]
1617
-- [--num-blocks-to-process INT] COMMAND
1718
module Main (main) where

ouroboros-consensus-cardano/src/unstable-cardano-tools/Cardano/Tools/DBAnalyser/Analysis.hs

Lines changed: 43 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ module Cardano.Tools.DBAnalyser.Analysis (
1717
, AnalysisName (..)
1818
, AnalysisResult (..)
1919
, AnalysisStartFrom (..)
20+
, LedgerApplicationMode (..)
2021
, Limit (..)
2122
, NumberOfBlocks (..)
2223
, SStartFrom (..)
@@ -51,7 +52,7 @@ import Ouroboros.Consensus.HeaderValidation (HasAnnTip (..),
5152
HeaderState (..), headerStatePoint, tickHeaderState,
5253
validateHeader)
5354
import Ouroboros.Consensus.Ledger.Abstract (LedgerCfg, LedgerConfig,
54-
applyBlockLedgerResult, applyChainTick,
55+
applyBlockLedgerResult, applyChainTick, tickThenApply,
5556
tickThenApplyLedgerResult, tickThenReapply)
5657
import Ouroboros.Consensus.Ledger.Basics (LedgerResult (..),
5758
LedgerState, getTipSlot)
@@ -72,6 +73,7 @@ import Ouroboros.Consensus.Storage.LedgerDB (DiskSnapshot (..),
7273
writeSnapshot)
7374
import Ouroboros.Consensus.Storage.Serialisation (SizeInBytes,
7475
encodeDisk)
76+
import Ouroboros.Consensus.Util ((..:))
7577
import qualified Ouroboros.Consensus.Util.IOLike as IOLike
7678
import Ouroboros.Consensus.Util.ResourceRegistry
7779
import System.FS.API (SomeHasFS (..))
@@ -88,7 +90,7 @@ data AnalysisName =
8890
| ShowBlockTxsSize
8991
| ShowEBBs
9092
| OnlyValidation
91-
| StoreLedgerStateAt SlotNo
93+
| StoreLedgerStateAt SlotNo LedgerApplicationMode
9294
| CountBlocks
9395
| CheckNoThunksEvery Word64
9496
| TraceLedgerProcessing
@@ -109,6 +111,11 @@ data AnalysisResult =
109111
newtype NumberOfBlocks = NumberOfBlocks { unNumberOfBlocks :: Word64 }
110112
deriving (Eq, Show, Num, Read)
111113

114+
-- | Whether to apply blocks to a ledger state via /reapplication/ (eg skipping
115+
-- signature checks/Plutus scripts) or full /application/ (much slower).
116+
data LedgerApplicationMode = LedgerReapply | LedgerApply
117+
deriving (Eq, Show)
118+
112119
runAnalysis ::
113120
forall blk.
114121
( HasAnalysis blk
@@ -133,7 +140,7 @@ runAnalysis analysisName = case go analysisName of
133140
go ShowBlockTxsSize = mkAnalysis $ showBlockTxsSize
134141
go ShowEBBs = mkAnalysis $ showEBBs
135142
go OnlyValidation = mkAnalysis @StartFromPoint $ \_ -> pure Nothing
136-
go (StoreLedgerStateAt slotNo) = mkAnalysis $ storeLedgerStateAt slotNo
143+
go (StoreLedgerStateAt slotNo lgrAppMode) = mkAnalysis $ storeLedgerStateAt slotNo lgrAppMode
137144
go CountBlocks = mkAnalysis $ countBlocks
138145
go (CheckNoThunksEvery nBks) = mkAnalysis $ checkNoThunksEvery nBks
139146
go TraceLedgerProcessing = mkAnalysis $ traceLedgerProcessing
@@ -221,6 +228,8 @@ data TraceEvent blk =
221228
-- ^ triggered once during StoreLedgerStateAt analysis,
222229
-- when snapshot was created in slot proceeding the
223230
-- requested one
231+
| LedgerErrorEvent (Point blk) (ExtValidationError blk)
232+
-- ^ triggered when applying a block with the given point failed
224233
| BlockTxSizeEvent SlotNo Int SizeInBytes
225234
-- ^ triggered for all blocks during ShowBlockTxsSize analysis,
226235
-- it holds:
@@ -241,7 +250,7 @@ data TraceEvent blk =
241250
-- * total time spent in the mutator when calling 'Mempool.getSnapshotFor'
242251
-- * total time spent in gc when calling 'Mempool.getSnapshotFor'
243252

244-
instance HasAnalysis blk => Show (TraceEvent blk) where
253+
instance (HasAnalysis blk, LedgerSupportsProtocol blk) => Show (TraceEvent blk) where
245254
show (StartedEvent analysisName) = "Started " <> (show analysisName)
246255
show DoneEvent = "Done"
247256
show (BlockSlotEvent bn sn h) = intercalate "\t" $ [
@@ -273,6 +282,8 @@ instance HasAnalysis blk => Show (TraceEvent blk) where
273282
show (SnapshotWarningEvent requested actual) =
274283
"Snapshot was created at " <> show actual <> " " <>
275284
"because there was no block forged at requested " <> show requested
285+
show (LedgerErrorEvent pt err) =
286+
"Applying block at " <> show pt <> " failed: " <> show err
276287
show (BlockTxSizeEvent slot numBlocks txsSize) = intercalate "\t" [
277288
show slot
278289
, "Num txs in block = " <> show numBlocks
@@ -398,21 +409,33 @@ storeLedgerStateAt ::
398409
, HasAnalysis blk
399410
, LedgerSupportsProtocol blk
400411
)
401-
=> SlotNo -> Analysis blk StartFromLedgerState
402-
storeLedgerStateAt slotNo (AnalysisEnv { db, registry, startFrom, cfg, limit, ledgerDbFS, tracer }) = do
412+
=> SlotNo
413+
-> LedgerApplicationMode
414+
-> Analysis blk StartFromLedgerState
415+
storeLedgerStateAt slotNo ledgerAppMode env = do
403416
void $ processAllUntil db registry GetBlock startFrom limit initLedger process
404417
pure Nothing
405418
where
419+
AnalysisEnv { db, registry, startFrom, cfg, limit, ledgerDbFS, tracer } = env
406420
FromLedgerState initLedger = startFrom
407421

408422
process :: ExtLedgerState blk -> blk -> IO (NextStep, ExtLedgerState blk)
409423
process oldLedger blk = do
410424
let ledgerCfg = ExtLedgerCfg cfg
411-
newLedger = tickThenReapply ledgerCfg blk oldLedger
412-
when (blockSlot blk >= slotNo) $ storeLedgerState blk newLedger
413-
when (blockSlot blk > slotNo) $ issueWarning blk
414-
when ((unBlockNo $ blockNo blk) `mod` 1000 == 0) $ reportProgress blk
415-
return (continue blk, newLedger)
425+
case runExcept $ tickThenXApply ledgerCfg blk oldLedger of
426+
Right newLedger -> do
427+
when (blockSlot blk >= slotNo) $ storeLedgerState newLedger
428+
when (blockSlot blk > slotNo) $ issueWarning blk
429+
when ((unBlockNo $ blockNo blk) `mod` 1000 == 0) $ reportProgress blk
430+
return (continue blk, newLedger)
431+
Left err -> do
432+
traceWith tracer $ LedgerErrorEvent (blockPoint blk) err
433+
storeLedgerState oldLedger
434+
pure (Stop, oldLedger)
435+
436+
tickThenXApply = case ledgerAppMode of
437+
LedgerReapply -> pure ..: tickThenReapply
438+
LedgerApply -> tickThenApply
416439

417440
continue :: blk -> NextStep
418441
continue blk
@@ -424,16 +447,15 @@ storeLedgerStateAt slotNo (AnalysisEnv { db, registry, startFrom, cfg, limit, le
424447
reportProgress blk = let event = BlockSlotEvent (blockNo blk) (blockSlot blk) (blockHash blk)
425448
in traceWith tracer event
426449

427-
storeLedgerState ::
428-
blk
429-
-> ExtLedgerState blk
430-
-> IO ()
431-
storeLedgerState blk ledgerState = do
432-
let snapshot = DiskSnapshot
433-
(unSlotNo $ blockSlot blk)
434-
(Just $ "db-analyser")
435-
writeSnapshot ledgerDbFS encLedger snapshot ledgerState
436-
traceWith tracer $ SnapshotStoredEvent (blockSlot blk)
450+
storeLedgerState :: ExtLedgerState blk -> IO ()
451+
storeLedgerState ledgerState = case pointSlot pt of
452+
NotOrigin slot -> do
453+
let snapshot = DiskSnapshot (unSlotNo slot) (Just "db-analyser")
454+
writeSnapshot ledgerDbFS encLedger snapshot ledgerState
455+
traceWith tracer $ SnapshotStoredEvent slot
456+
Origin -> pure ()
457+
where
458+
pt = headerStatePoint $ headerState ledgerState
437459

438460
encLedger :: ExtLedgerState blk -> Encoding
439461
encLedger =

ouroboros-consensus-cardano/src/unstable-cardano-tools/Cardano/Tools/DBAnalyser/Types.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,6 @@ data DBAnalyserConfig = DBAnalyserConfig {
2020
, confLimit :: Limit
2121
}
2222

23+
-- | The extent of the ChainDB on-disk files validation. This is completely
24+
-- unrelated to validation of the ledger rules.
2325
data ValidateBlocks = ValidateAllBlocks | MinimumBlockValidation

0 commit comments

Comments
 (0)