-
Notifications
You must be signed in to change notification settings - Fork 86
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Introduce header validation as concept
This then paves the way for introducing additional checks (which this doesn't do yet).
- Loading branch information
Showing
18 changed files
with
291 additions
and
114 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
{-# LANGUAGE DeriveAnyClass #-} | ||
{-# LANGUAGE DeriveGeneric #-} | ||
{-# LANGUAGE FlexibleContexts #-} | ||
{-# LANGUAGE RankNTypes #-} | ||
{-# LANGUAGE RecordWildCards #-} | ||
{-# LANGUAGE StandaloneDeriving #-} | ||
{-# LANGUAGE TypeFamilies #-} | ||
{-# LANGUAGE UndecidableInstances #-} | ||
|
||
-- | Header validation | ||
module Ouroboros.Consensus.Header ( | ||
validateHeader | ||
-- * State | ||
, HeaderState(..) | ||
, initHeaderState | ||
, rewindHeaderState | ||
, castHeaderState | ||
-- * Errors | ||
, HeaderError(..) | ||
, castHeaderError | ||
-- * Serialisation | ||
, encodeHeaderState | ||
, decodeHeaderState | ||
) where | ||
|
||
import Codec.CBOR.Decoding (Decoder) | ||
import Codec.CBOR.Encoding (Encoding) | ||
import Codec.Serialise (Serialise) | ||
import Control.Monad.Except | ||
import GHC.Generics (Generic) | ||
|
||
import Cardano.Prelude (NoUnexpectedThunks) | ||
|
||
import Ouroboros.Network.Block (HeaderHash, Point) | ||
|
||
import Ouroboros.Consensus.Block | ||
import Ouroboros.Consensus.Protocol.Abstract | ||
|
||
{------------------------------------------------------------------------------- | ||
State | ||
-------------------------------------------------------------------------------} | ||
|
||
-- | State required to validate the header | ||
-- | ||
-- See 'validateHeader' for details | ||
data HeaderState blk = HeaderState { | ||
headerChainState :: ChainState (BlockProtocol blk) | ||
} | ||
deriving (Generic) | ||
|
||
deriving instance SupportedBlock blk => Show (HeaderState blk) | ||
deriving instance SupportedBlock blk => NoUnexpectedThunks (HeaderState blk) | ||
deriving instance Eq (ChainState (BlockProtocol blk)) => Eq (HeaderState blk) | ||
|
||
initHeaderState :: ChainState (BlockProtocol blk) | ||
-> HeaderState blk | ||
initHeaderState = HeaderState | ||
|
||
castHeaderState :: ( ChainState (BlockProtocol blk ) | ||
~ ChainState (BlockProtocol blk') | ||
) | ||
=> HeaderState blk -> HeaderState blk' | ||
castHeaderState HeaderState{..} = HeaderState{..} | ||
|
||
rewindHeaderState :: ( SupportedBlock blk | ||
, Serialise (HeaderHash blk) | ||
) | ||
=> NodeConfig (BlockProtocol blk) | ||
-> Point blk | ||
-> HeaderState blk -> Maybe (HeaderState blk) | ||
rewindHeaderState cfg p HeaderState{..} = do | ||
chainState' <- rewindChainState cfg headerChainState p | ||
return $ HeaderState { | ||
headerChainState = chainState' | ||
} | ||
|
||
{------------------------------------------------------------------------------- | ||
Errors | ||
-------------------------------------------------------------------------------} | ||
|
||
-- | Invalid header | ||
data HeaderError blk = | ||
-- | Invalid consensus protocol fields | ||
HeaderProtocolError (ValidationErr (BlockProtocol blk)) | ||
deriving (Generic) | ||
|
||
deriving instance SupportedBlock blk => Eq (HeaderError blk) | ||
deriving instance SupportedBlock blk => Show (HeaderError blk) | ||
deriving instance SupportedBlock blk => NoUnexpectedThunks (HeaderError blk) | ||
|
||
castHeaderError :: ( ValidationErr (BlockProtocol blk ) | ||
~ ValidationErr (BlockProtocol blk') | ||
) | ||
=> HeaderError blk -> HeaderError blk' | ||
castHeaderError (HeaderProtocolError e) = HeaderProtocolError e | ||
|
||
{------------------------------------------------------------------------------- | ||
Validation proper | ||
-------------------------------------------------------------------------------} | ||
|
||
-- | Header validation | ||
-- | ||
-- Header validation (as opposed to block validation) is done by the chain sync | ||
-- client: as we download headers from other network nodes, we validate those | ||
-- headers before deciding whether or not to download the corresponding blocks. | ||
-- | ||
-- Before we /adopt/ any blocks we download, however, we will do a full block | ||
-- validation. As such, the header validation check can omit some checks | ||
-- (provided that we do those checks when we do the full validation); at worst, | ||
-- this would mean we might download some blocks that we will reject as being | ||
-- invalid where we could have detected that sooner. | ||
-- | ||
-- For this reason, the header validation currently only checks two things: | ||
-- | ||
-- o It verifies the consensus part of the header. | ||
-- | ||
-- For example, for Praos this means checking the VRF proofs. | ||
-- | ||
-- o It verifies the 'HasHeader' part of the header. | ||
-- | ||
-- Specifically, we verify that | ||
-- | ||
-- x Block numbers are consecutive | ||
-- x The block number of the first block is 'genesisBlockNo' | ||
-- x Hashes line up | ||
-- | ||
-- Note that this check is independent from both the consensus protocol and | ||
-- from the choice of ledger. | ||
-- | ||
-- /If/ a particular ledger wants to verify additional fields in the header, | ||
-- it will get the chance to do so in 'applyLedgerBlock', which is passed the | ||
-- entire block (not just the block body). | ||
validateHeader :: SupportedBlock blk | ||
=> NodeConfig (BlockProtocol blk) | ||
-> LedgerView (BlockProtocol blk) | ||
-> Header blk | ||
-> HeaderState blk | ||
-> Except (HeaderError blk) (HeaderState blk) | ||
validateHeader cfg ledgerView hdr HeaderState{..} = do | ||
chainState' <- withExcept HeaderProtocolError $ | ||
applyChainState | ||
cfg | ||
ledgerView | ||
(validateView cfg hdr) | ||
headerChainState | ||
return HeaderState { | ||
headerChainState = chainState' | ||
} | ||
|
||
{------------------------------------------------------------------------------- | ||
Serialisation | ||
-------------------------------------------------------------------------------} | ||
|
||
encodeHeaderState :: (ChainState (BlockProtocol blk) -> Encoding) | ||
-> (HeaderState blk -> Encoding) | ||
encodeHeaderState encodeChainState (HeaderState x) = encodeChainState x | ||
|
||
decodeHeaderState :: (forall s. Decoder s (ChainState (BlockProtocol blk))) | ||
-> (forall s. Decoder s (HeaderState blk)) | ||
decodeHeaderState decodeChainState = HeaderState <$> decodeChainState |
Oops, something went wrong.