Skip to content

Commit 8f7a5cc

Browse files
committed
db-sync: Add hash checking when maintaining ledger state
Maintenance of ledger state uses code in the consensus packages as a library. * There are two versions of a function to apply a block to a ledger state; a slow one that does full checking and fast one that does fewer checks. * Since db-sync is getting blocks that have already been validated by the node, it seemed sensible to use the fast version. * I had been told that the checks in the fast version included checking that the block's previous hash matches the value of the head hash in the ledger state, but this hash check was not being done. The previous lack of hash checking means the problem was not detected until the start of the next epoch.
1 parent ff8261c commit 8f7a5cc

File tree

3 files changed

+66
-29
lines changed

3 files changed

+66
-29
lines changed

cardano-db-sync/cardano-db-sync.cabal

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -37,33 +37,34 @@ library
3737
Cardano.DbSync.Config.Node
3838
Cardano.DbSync.Config.Shelley
3939
Cardano.DbSync.Config.Types
40-
Cardano.DbSync.Types
41-
Cardano.DbSync.Error
42-
Cardano.DbSync.Util
43-
Cardano.DbSync.Era
44-
Cardano.DbSync.Era.Byron.Util
45-
Cardano.DbSync.Era.Shelley.Util
46-
4740
Cardano.DbSync.Database
4841
Cardano.DbSync.DbAction
49-
Cardano.DbSync.Metrics
50-
Cardano.DbSync.Plugin
51-
Cardano.DbSync.Plugin.Default
52-
Cardano.DbSync.Plugin.Epoch
53-
54-
Cardano.DbSync.Rollback
55-
56-
Cardano.DbSync.Tracing.ToObjectOrphans
42+
Cardano.DbSync.Error
5743

44+
Cardano.DbSync.Era
5845
Cardano.DbSync.Era.Byron.Genesis
5946
Cardano.DbSync.Era.Byron.Insert
47+
Cardano.DbSync.Era.Byron.Util
48+
Cardano.DbSync.Era.Cardano.Util
6049
Cardano.DbSync.Era.Shelley.Genesis
6150
Cardano.DbSync.Era.Shelley.Insert
6251
Cardano.DbSync.Era.Shelley.Query
6352
Cardano.DbSync.Era.Shelley.Metadata
6453
Cardano.DbSync.Era.Shelley.Types
54+
Cardano.DbSync.Era.Shelley.Util
55+
6556
Cardano.DbSync.LedgerState
57+
Cardano.DbSync.Metrics
58+
59+
Cardano.DbSync.Plugin
60+
Cardano.DbSync.Plugin.Default
61+
Cardano.DbSync.Plugin.Epoch
62+
63+
Cardano.DbSync.Rollback
6664
Cardano.DbSync.StateQuery
65+
Cardano.DbSync.Tracing.ToObjectOrphans
66+
Cardano.DbSync.Types
67+
Cardano.DbSync.Util
6768

6869
build-depends: base >= 4.12 && < 4.13
6970
, aeson
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{-# LANGUAGE OverloadedStrings #-}
2+
module Cardano.DbSync.Era.Cardano.Util
3+
( unChainHash
4+
) where
5+
6+
import qualified Data.ByteString.Short as BSS
7+
8+
import Cardano.DbSync.Config.Types
9+
10+
import Cardano.Prelude
11+
12+
import qualified Ouroboros.Consensus.HardFork.Combinator as Consensus
13+
14+
import Ouroboros.Network.Block (ChainHash (..))
15+
16+
17+
unChainHash :: ChainHash CardanoBlock -> ByteString
18+
unChainHash ch =
19+
case ch of
20+
GenesisHash -> "genesis"
21+
BlockHash bh -> BSS.fromShort (Consensus.getOneEraHash bh)
22+
23+

cardano-db-sync/src/Cardano/DbSync/LedgerState.hs

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import qualified Cardano.Binary as Serialize
2323
import Cardano.DbSync.Config
2424
import Cardano.DbSync.Config.Cardano
2525
import Cardano.DbSync.Config.Types
26+
import qualified Cardano.DbSync.Era.Cardano.Util as Cardano
2627
import Cardano.DbSync.Types
2728
import Cardano.DbSync.Util
2829

@@ -38,20 +39,20 @@ import Control.Monad.Extra (firstJustM)
3839

3940
import qualified Data.ByteString.Char8 as BS
4041
import qualified Data.ByteString.Lazy.Char8 as LBS
42+
import qualified Data.ByteString.Short as BSS
4143
import Data.Either (partitionEithers)
4244
import qualified Data.List as List
4345

44-
import Ouroboros.Consensus.Block (CodecConfig, WithOrigin (..))
45-
import Ouroboros.Consensus.Cardano.Block
46-
(LedgerState (LedgerStateByron, LedgerStateShelley))
46+
import Ouroboros.Consensus.Block (CodecConfig, WithOrigin (..), blockHash, blockPrevHash)
47+
import Ouroboros.Consensus.Cardano.Block (LedgerState (..))
4748
import Ouroboros.Consensus.Cardano.CanHardFork ()
4849
import Ouroboros.Consensus.Config (TopLevelConfig (..), configCodec, configLedger)
4950
import qualified Ouroboros.Consensus.HardFork.Combinator as Consensus
5051
import Ouroboros.Consensus.HardFork.Combinator.Basics (LedgerState (..))
5152
import Ouroboros.Consensus.HardFork.Combinator.State (epochInfoLedger)
5253
import qualified Ouroboros.Consensus.HardFork.Combinator.State as Consensus
5354
import qualified Ouroboros.Consensus.HeaderValidation as Consensus
54-
import Ouroboros.Consensus.Ledger.Abstract (ledgerTipSlot, tickThenApply, tickThenReapply)
55+
import Ouroboros.Consensus.Ledger.Abstract (ledgerTipHash, ledgerTipSlot, tickThenReapply)
5556
import Ouroboros.Consensus.Ledger.Extended (ExtLedgerCfg (..), ExtLedgerState (..),
5657
decodeExtLedgerState, encodeExtLedgerState)
5758
import qualified Ouroboros.Consensus.Node.ProtocolInfo as Consensus
@@ -61,8 +62,6 @@ import qualified Ouroboros.Consensus.Shelley.Protocol as Consensus
6162
import Ouroboros.Consensus.Storage.Serialisation (DecodeDisk (..), EncodeDisk (..))
6263
import qualified Ouroboros.Consensus.TypeFamilyWrappers as Consensus
6364

64-
65-
6665
import qualified Shelley.Spec.Ledger.API.Protocol as Shelley
6766
import qualified Shelley.Spec.Ledger.BaseTypes as Shelley
6867
import qualified Shelley.Spec.Ledger.EpochBoundary as Shelley
@@ -137,12 +136,9 @@ applyBlock (LedgerStateVar stateVar) blk =
137136
where
138137
applyBlk :: ExtLedgerCfg CardanoBlock -> CardanoBlock -> ExtLedgerState CardanoBlock -> ExtLedgerState CardanoBlock
139138
applyBlk cfg block lsb =
140-
-- Set to False to get better error messages from Consensus (but slower block application).
141-
if True
142-
then tickThenReapply cfg block lsb
143-
else case runExcept $ tickThenApply cfg block lsb of
144-
Left err -> panic $ textShow err
145-
Right result -> result
139+
case tickThenReapplyCheckHash cfg block lsb of
140+
Left err -> panic err
141+
Right result -> result
146142

147143
saveLedgerState :: LedgerStateDir -> LedgerStateVar -> CardanoLedgerState -> SyncState -> IO ()
148144
saveLedgerState lsd@(LedgerStateDir stateDir) (LedgerStateVar stateVar) ledger synced = do
@@ -234,7 +230,6 @@ loadState stateDir ledger slotNo = do
234230
Left (_ :: IOException) -> pure Nothing
235231
Right bs -> pure $ decode bs
236232

237-
238233
codecConfig :: CodecConfig CardanoBlock
239234
codecConfig = configCodec (clsConfig ledger)
240235

@@ -297,7 +292,7 @@ ledgerEpochUpdate els mRewards =
297292
, euRewards = fromMaybe Shelley.emptyRewardUpdate mRewards
298293

299294
-- Use '_pstakeSet' here instead of '_pstateMark' because the stake addresses for the
300-
-- later may not have been added to the database yet. That means that whne these values
295+
-- later may not have been added to the database yet. That means that when these values
301296
-- are added to the database, the epoch number where they become active is the current
302297
-- epoch plus one.
303298
, euStakeDistribution = Shelley._stake . Shelley._pstakeSet . Shelley.esSnapshots
@@ -326,3 +321,21 @@ extractEpochNonce extLedgerState =
326321
$ Consensus.tpraosStateChainDepState (Consensus.unwrapChainDepState chainDepStateShelley)
327322
Consensus.TS {} ->
328323
Nothing
324+
325+
-- Like 'Consensus.tickThenReapply' but also checks that the previous hash from the block matches
326+
-- the head hash of the ledger state.
327+
tickThenReapplyCheckHash
328+
:: ExtLedgerCfg CardanoBlock -> CardanoBlock -> ExtLedgerState CardanoBlock
329+
-> Either Text (ExtLedgerState CardanoBlock)
330+
tickThenReapplyCheckHash cfg block lsb =
331+
if blockPrevHash block == ledgerTipHash (ledgerState lsb)
332+
then Right $ tickThenReapply cfg block lsb
333+
else Left $ mconcat
334+
[ "Ledger state hash mismatch. Ledger head is slot "
335+
, textShow (unSlotNo $ fromWithOrigin (SlotNo 0) (ledgerTipSlot $ ledgerState lsb))
336+
, " hash ", renderByteArray (Cardano.unChainHash (ledgerTipHash $ ledgerState lsb))
337+
, " but block previous hash is "
338+
, renderByteArray (Cardano.unChainHash $ blockPrevHash block)
339+
, " and block current hash is "
340+
, renderByteArray (BSS.fromShort . Consensus.getOneEraHash $ blockHash block), "."
341+
]

0 commit comments

Comments
 (0)