Skip to content

Commit

Permalink
Lightweight checkpointing for all block types (#898)
Browse files Browse the repository at this point in the history
Lightweight checkpointing is a mechanism to ensure that new nodes end up
in the correct chain even when the chain is too sparse for normal
operation of Praos and Genesis.

The idea is to supply the new node with a list of block hashes that
should be present on the chain at specific block numbers. This list is
provided in the TopLevelConfig record, and it is used during validation
of headers in validateIfCheckpoint called by validateEnvelope.

If the hashes of checkpoints don't match the hashes of a supplied header
for a given block number, then validation of the header fails.

The substance of the change is modifying TopLevelConfig and
validateEnvelope. Most other changes derive from these modifications.
There are three new unit tests of validateIfCheckpoint added in
ouroboros-consensus:test:consensus-test.

Implements
#453 (comment)
  • Loading branch information
facundominguez authored Mar 22, 2024
2 parents c1b7426 + 4673971 commit 488bfc6
Show file tree
Hide file tree
Showing 31 changed files with 343 additions and 77 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<!--
A new scriv changelog fragment.
Uncomment the section that is right (remove the HTML comment wrapper).
-->

<!--
### Patch
- A bullet item for the Patch category.
-->
<!--
### Non-Breaking
- A bullet item for the Non-Breaking category.
-->
### Breaking

- Implement lightweight checkpointing [#449](https://github.com/IntersectMBO/ouroboros-consensus/issues/449).
A validation to help nodes follow the historical chain. A new field `cardanoCheckpoints`
has been added to the record `ProtocolParams (CardanoBlock c)` record, with a new type `CheckpointsMap`.

Original file line number Diff line number Diff line change
Expand Up @@ -192,10 +192,11 @@ protocolInfoByron ProtocolParamsByron {
topLevelConfigProtocol = PBftConfig {
pbftParams = byronPBftParams compactedGenesisConfig mSigThresh
}
, topLevelConfigLedger = compactedGenesisConfig
, topLevelConfigBlock = blockConfig
, topLevelConfigCodec = mkByronCodecConfig compactedGenesisConfig
, topLevelConfigStorage = ByronStorageConfig blockConfig
, topLevelConfigLedger = compactedGenesisConfig
, topLevelConfigBlock = blockConfig
, topLevelConfigCodec = mkByronCodecConfig compactedGenesisConfig
, topLevelConfigStorage = ByronStorageConfig blockConfig
, topLevelConfigCheckpoints = emptyCheckpointsMap
}
, pInfoInitLedger = ExtLedgerState {
-- Important: don't pass the compacted genesis config to
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,7 @@ data instance ProtocolParams (CardanoBlock c) = ProtocolParamsCardano {
, shelleyBasedProtocolParams :: ProtocolParamsShelleyBased c
, cardanoHardForkTriggers :: CardanoHardForkTriggers
, cardanoLedgerTransitionConfig :: L.TransitionConfig (L.LatestKnownEra c)
, cardanoCheckpoints :: CheckpointsMap (CardanoBlock c)
}

type CardanoProtocolParams c = ProtocolParams (CardanoBlock c)
Expand All @@ -539,6 +540,7 @@ pattern CardanoProtocolParams ::
-> ProtocolParams (ShelleyBlock (Praos c) (ConwayEra c))
-> CardanoHardForkTriggers
-> L.TransitionConfig (L.LatestKnownEra c)
-> CheckpointsMap (CardanoBlock c)
-> CardanoProtocolParams c
pattern CardanoProtocolParams {
paramsByron
Expand All @@ -551,6 +553,7 @@ pattern CardanoProtocolParams {
, paramsConway
, hardForkTriggers
, ledgerTransitionConfig
, checkpoints
} =
ProtocolParamsCardano {
cardanoProtocolParamsPerEra = PerEraProtocolParams
Expand All @@ -566,6 +569,7 @@ pattern CardanoProtocolParams {
, shelleyBasedProtocolParams = paramsShelleyBased
, cardanoHardForkTriggers = hardForkTriggers
, cardanoLedgerTransitionConfig = ledgerTransitionConfig
, cardanoCheckpoints = checkpoints
}

{-# COMPLETE CardanoProtocolParams #-}
Expand Down Expand Up @@ -618,6 +622,7 @@ protocolInfoCardano paramsCardano
, triggerHardForkConway
}
, ledgerTransitionConfig
, checkpoints
} = paramsCardano

genesisShelley = ledgerTransitionConfig ^. L.tcShelleyGenesisL
Expand Down Expand Up @@ -944,6 +949,7 @@ protocolInfoCardano paramsCardano
(Shelley.ShelleyStorageConfig tpraosSlotsPerKESPeriod k)
(Shelley.ShelleyStorageConfig tpraosSlotsPerKESPeriod k)
(Shelley.ShelleyStorageConfig tpraosSlotsPerKESPeriod k)
, topLevelConfigCheckpoints = checkpoints
}

-- When the initial ledger state is not in the Byron era, register the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -297,11 +297,12 @@ protocolInfoTPraosShelleyBased ProtocolParamsShelleyBased {

topLevelConfig :: TopLevelConfig (ShelleyBlock (TPraos c) era)
topLevelConfig = TopLevelConfig {
topLevelConfigProtocol = consensusConfig
, topLevelConfigLedger = ledgerConfig
, topLevelConfigBlock = blockConfig
, topLevelConfigCodec = ShelleyCodecConfig
, topLevelConfigStorage = storageConfig
topLevelConfigProtocol = consensusConfig
, topLevelConfigLedger = ledgerConfig
, topLevelConfigBlock = blockConfig
, topLevelConfigCodec = ShelleyCodecConfig
, topLevelConfigStorage = storageConfig
, topLevelConfigCheckpoints = emptyCheckpointsMap
}

consensusConfig :: ConsensusConfig (BlockProtocol (ShelleyBlock (TPraos c) era))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ protocolInfoDualByron abstractGenesis@ByronSpecGenesis{..} params credss =
dualStorageConfigMain = ByronStorageConfig concreteConfig
, dualStorageConfigAux = ByronSpecStorageConfig
}
, topLevelConfigCheckpoints = emptyCheckpointsMap
}
, pInfoInitLedger = ExtLedgerState {
ledgerState = DualLedgerState {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import Ouroboros.Consensus.Cardano.Node (CardanoHardForkConstraints,
CardanoHardForkTriggers (..), ProtocolParams (..),
TriggerHardFork (TriggerHardForkAtEpoch, TriggerHardForkNotDuringThisExecution),
protocolInfoCardano)
import Ouroboros.Consensus.Config (emptyCheckpointsMap)
import Ouroboros.Consensus.Config.SecurityParam (SecurityParam (..))
import qualified Ouroboros.Consensus.Mempool as Mempool
import Ouroboros.Consensus.Node.ProtocolInfo (NumCoreNodes (..),
Expand Down Expand Up @@ -326,6 +327,7 @@ mkTestProtocolInfo
SL.exampleAlonzoGenesis
SL.exampleConwayGenesis
)
emptyCheckpointsMap
)

where
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import Ouroboros.Consensus.Cardano
import qualified Ouroboros.Consensus.Cardano as Consensus
import qualified Ouroboros.Consensus.Cardano.CanHardFork as Consensus
import Ouroboros.Consensus.Cardano.Condense ()
import Ouroboros.Consensus.Config (emptyCheckpointsMap)
import Ouroboros.Consensus.HardFork.Combinator.Condense ()
import qualified Ouroboros.Consensus.Mempool as Mempool

Expand Down Expand Up @@ -296,6 +297,7 @@ mkSomeConsensusProtocolCardano NodeByronProtocolConfiguration {
Just epochNo -> Consensus.TriggerHardForkAtEpoch epochNo
}
transitionLedgerConfig
emptyCheckpointsMap
)

------------------------------------------------------------------------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ import Ouroboros.Consensus.Cardano.Block (CardanoEras)
import qualified Ouroboros.Consensus.Cardano.Block as Cardano.Block
import Ouroboros.Consensus.Cardano.Node (TriggerHardFork (..),
protocolInfoCardano)
import Ouroboros.Consensus.Config (emptyCheckpointsMap)
import Ouroboros.Consensus.HardFork.Combinator (HardForkBlock (..),
OneEraBlock (..), OneEraHash (..), getHardForkState,
hardForkLedgerStatePerEra)
Expand Down Expand Up @@ -417,6 +418,7 @@ mkCardanoProtocolInfo genesisByron signatureThreshold transitionConfig initialNo
}
triggers
transitionConfig
emptyCheckpointsMap
)
where

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<!--
A new scriv changelog fragment.
Uncomment the section that is right (remove the HTML comment wrapper).
-->

<!--
### Patch
- A bullet item for the Patch category.
-->
<!--
### Non-Breaking
- A bullet item for the Non-Breaking category.
-->
### Breaking

- Integrate changes for lightweight checkpointing [#449](https://github.com/IntersectMBO/ouroboros-consensus/issues/449),
which required adding a field to `TopLevelConfig`.

Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,7 @@ prop_simple_hfc_convergence testSetup@TestSetup{..} =
:* SCfgB
:* Nil
}
, topLevelConfigCheckpoints = emptyCheckpointsMap
}

consensusConfigA :: CoreNodeId -> ConsensusConfig ProtocolA
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ module Test.Consensus.PeerSimulator.Config (defaultCfg) where
import Cardano.Crypto.DSIGN (SignKeyDSIGN (..), VerKeyDSIGN (..))
import Cardano.Slotting.Time (SlotLength, slotLengthFromSec)
import qualified Data.Map.Strict as Map
import Ouroboros.Consensus.Config (SecurityParam, TopLevelConfig (..))
import Ouroboros.Consensus.Config (SecurityParam, TopLevelConfig (..),
emptyCheckpointsMap)
import qualified Ouroboros.Consensus.HardFork.History.EraParams as HardFork
import Ouroboros.Consensus.Node.ProtocolInfo
(NumCoreNodes (NumCoreNodes))
Expand Down Expand Up @@ -33,10 +34,11 @@ defaultCfg secParam = TopLevelConfig {
, (CoreId (CoreNodeId 1), VerKeyMockDSIGN 1)
]
}
, topLevelConfigLedger = eraParams
, topLevelConfigBlock = TestBlockConfig numCoreNodes
, topLevelConfigCodec = TestBlockCodecConfig
, topLevelConfigStorage = TestBlockStorageConfig
, topLevelConfigLedger = eraParams
, topLevelConfigBlock = TestBlockConfig numCoreNodes
, topLevelConfigCodec = TestBlockCodecConfig
, topLevelConfigStorage = TestBlockStorageConfig
, topLevelConfigCheckpoints = emptyCheckpointsMap
}
where
-- REVIEW: Make it 1s or a parameter?
Expand Down
9 changes: 5 additions & 4 deletions ouroboros-consensus/bench/ChainSync-client-bench/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -235,10 +235,11 @@ topConfig = TopLevelConfig {
, (CoreId (CoreNodeId 1), VerKeyMockDSIGN 1)
]
}
, topLevelConfigLedger = eraParams
, topLevelConfigBlock = TB.TestBlockConfig numCoreNodes
, topLevelConfigCodec = TB.TestBlockCodecConfig
, topLevelConfigStorage = TB.TestBlockStorageConfig
, topLevelConfigLedger = eraParams
, topLevelConfigBlock = TB.TestBlockConfig numCoreNodes
, topLevelConfigCodec = TB.TestBlockCodecConfig
, topLevelConfigStorage = TB.TestBlockStorageConfig
, topLevelConfigCheckpoints = emptyCheckpointsMap
}
where
eraParams :: HardFork.EraParams
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<!--
A new scriv changelog fragment.
Uncomment the section that is right (remove the HTML comment wrapper).
-->

<!--
### Patch
- A bullet item for the Patch category.
-->
<!--
### Non-Breaking
- A bullet item for the Non-Breaking category.
-->
### Breaking

- Implement lightweight checkpointing [#449](https://github.com/IntersectMBO/ouroboros-consensus/issues/449).
A validation to help nodes follow the historical chain. A new field `topLevelConfigCheckpoints`
has been added to the `TopLevelConfig` record, with a new type `CheckpointsMap`.

1 change: 1 addition & 0 deletions ouroboros-consensus/ouroboros-consensus.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,7 @@ test-suite consensus-test
Test.Consensus.HardFork.History
Test.Consensus.HardFork.Infra
Test.Consensus.HardFork.Summary
Test.Consensus.HeaderValidation
Test.Consensus.Mempool
Test.Consensus.Mempool.Fairness
Test.Consensus.Mempool.Fairness.TestBlock
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}

module Ouroboros.Consensus.Config (
-- * The top-level node configuration
TopLevelConfig (..)
, castTopLevelConfig
, mkTopLevelConfig
-- ** Checkpoints map
, CheckpointsMap (..)
, castCheckpointsMap
, emptyCheckpointsMap
-- ** Derived extraction functions
, configBlock
, configCodec
Expand All @@ -24,6 +29,7 @@ module Ouroboros.Consensus.Config (
) where

import Data.Coerce
import Data.Map.Strict (Map)
import GHC.Generics (Generic)
import NoThunks.Class (NoThunks)
import Ouroboros.Consensus.Block.Abstract
Expand All @@ -37,11 +43,12 @@ import Ouroboros.Consensus.Protocol.Abstract

-- | The top-level node configuration
data TopLevelConfig blk = TopLevelConfig {
topLevelConfigProtocol :: !(ConsensusConfig (BlockProtocol blk))
, topLevelConfigLedger :: !(LedgerConfig blk)
, topLevelConfigBlock :: !(BlockConfig blk)
, topLevelConfigCodec :: !(CodecConfig blk)
, topLevelConfigStorage :: !(StorageConfig blk)
topLevelConfigProtocol :: !(ConsensusConfig (BlockProtocol blk))
, topLevelConfigLedger :: !(LedgerConfig blk)
, topLevelConfigBlock :: !(BlockConfig blk)
, topLevelConfigCodec :: !(CodecConfig blk)
, topLevelConfigStorage :: !(StorageConfig blk)
, topLevelConfigCheckpoints :: !(CheckpointsMap blk)
}
deriving (Generic)

Expand All @@ -50,16 +57,36 @@ instance ( ConsensusProtocol (BlockProtocol blk)
, NoThunks (BlockConfig blk)
, NoThunks (CodecConfig blk)
, NoThunks (StorageConfig blk)
, NoThunks (HeaderHash blk)
) => NoThunks (TopLevelConfig blk)

-- | Checkpoints are block hashes that are expected to be present in the honest
-- historical chain.
--
-- Each checkpoint is associated with a 'BlockNo', and any block with a
-- 'BlockNo' in the checkpoints map is expected to have the corresponding hash.
--
newtype CheckpointsMap blk = CheckpointsMap {
unCheckpointsMap :: Map BlockNo (HeaderHash blk)
}
deriving (Generic, Monoid, Semigroup)

instance ( NoThunks (HeaderHash blk)
) => NoThunks (CheckpointsMap blk)

emptyCheckpointsMap :: CheckpointsMap blk
emptyCheckpointsMap = mempty

mkTopLevelConfig ::
ConsensusConfig (BlockProtocol blk)
-> LedgerConfig blk
-> BlockConfig blk
-> CodecConfig blk
-> StorageConfig blk
-> CheckpointsMap blk
-> TopLevelConfig blk
mkTopLevelConfig = TopLevelConfig
mkTopLevelConfig prtclCfg ledgerCfg blockCfg codecCfg storageCfg checkpointsMap =
TopLevelConfig prtclCfg ledgerCfg blockCfg codecCfg storageCfg checkpointsMap

configConsensus :: TopLevelConfig blk -> ConsensusConfig (BlockProtocol blk)
configConsensus = topLevelConfigProtocol
Expand Down Expand Up @@ -87,12 +114,19 @@ castTopLevelConfig ::
, Coercible (BlockConfig blk) (BlockConfig blk')
, Coercible (CodecConfig blk) (CodecConfig blk')
, Coercible (StorageConfig blk) (StorageConfig blk')
, Coercible (HeaderHash blk) (HeaderHash blk')
)
=> TopLevelConfig blk -> TopLevelConfig blk'
castTopLevelConfig TopLevelConfig{..} = TopLevelConfig{
topLevelConfigProtocol = coerce topLevelConfigProtocol
, topLevelConfigLedger = topLevelConfigLedger
, topLevelConfigBlock = coerce topLevelConfigBlock
, topLevelConfigCodec = coerce topLevelConfigCodec
, topLevelConfigStorage = coerce topLevelConfigStorage
topLevelConfigProtocol = coerce topLevelConfigProtocol
, topLevelConfigLedger = topLevelConfigLedger
, topLevelConfigBlock = coerce topLevelConfigBlock
, topLevelConfigCodec = coerce topLevelConfigCodec
, topLevelConfigStorage = coerce topLevelConfigStorage
, topLevelConfigCheckpoints = coerce topLevelConfigCheckpoints
}

castCheckpointsMap ::
Coercible (HeaderHash blk) (HeaderHash blk')
=> CheckpointsMap blk -> CheckpointsMap blk'
castCheckpointsMap = coerce
Loading

0 comments on commit 488bfc6

Please sign in to comment.