{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE UndecidableInstances #-}
module Ouroboros.Consensus.MiniProtocol.LeiosNotify.Client.Announcements.Validation
( AnnouncementInvalidity (..)
, AnnouncingHeader (ancAnnouncement, ancElectionId, ancHeader)
, announcementValidity
, maxAnnouncementAgeRecv
, mkAnnouncingHeader
, validateAnnouncementHeader
) where
import Control.Monad (when)
import Control.Monad.Except (Except, ExceptT, liftEither, runExcept, throwError, withExcept)
import Control.Monad.Trans (lift)
import Data.Bifunctor (first)
import Data.Time.Clock (NominalDiffTime)
import GHC.Generics (Generic)
import NoThunks.Class (NoThunks)
import Ouroboros.Consensus.Block
import Ouroboros.Consensus.BlockchainTime.WallClock.Types
( SystemTime (..)
, diffRelTime
)
import Ouroboros.Consensus.Config
( TopLevelConfig
, configBlock
, configConsensus
, configLedger
)
import Ouroboros.Consensus.Forecast (OutsideForecastRange, forecastFor)
import Ouroboros.Consensus.HeaderValidation
( OtherHeaderEnvelopeError
, ValidateEnvelope (..)
, tickHeaderState
, tickedHeaderStateChainDep
)
import Ouroboros.Consensus.Ledger.Abstract (EmptyMK, getTipSlot)
import Ouroboros.Consensus.Ledger.Extended (ExtLedgerState (..))
import Ouroboros.Consensus.Ledger.SupportsProtocol
( LedgerSupportsProtocol
, ledgerViewForecastAt
)
import Ouroboros.Consensus.Leios.Protocol
import Ouroboros.Consensus.Leios.Types (ElectionId (..))
import qualified Ouroboros.Consensus.MiniProtocol.ChainSync.Client.InFutureCheck as InFutureCheck
import qualified Ouroboros.Consensus.MiniProtocol.LeiosNotify.Client.Announcements as Announcements
import Ouroboros.Consensus.Protocol.Abstract (ValidationErr)
import Ouroboros.Consensus.Util.IOLike (IOLike, throwIO)
data blk
=
{ :: !(Header blk)
, forall blk. AnnouncingHeader blk -> ElectionId
ancElectionId :: !ElectionId
, forall blk. AnnouncingHeader blk -> LeiosAnnouncement
ancAnnouncement :: !LeiosAnnouncement
}
deriving stock (forall x. AnnouncingHeader blk -> Rep (AnnouncingHeader blk) x)
-> (forall x. Rep (AnnouncingHeader blk) x -> AnnouncingHeader blk)
-> Generic (AnnouncingHeader blk)
forall x. Rep (AnnouncingHeader blk) x -> AnnouncingHeader blk
forall x. AnnouncingHeader blk -> Rep (AnnouncingHeader blk) x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
forall blk x. Rep (AnnouncingHeader blk) x -> AnnouncingHeader blk
forall blk x. AnnouncingHeader blk -> Rep (AnnouncingHeader blk) x
$cfrom :: forall blk x. AnnouncingHeader blk -> Rep (AnnouncingHeader blk) x
from :: forall x. AnnouncingHeader blk -> Rep (AnnouncingHeader blk) x
$cto :: forall blk x. Rep (AnnouncingHeader blk) x -> AnnouncingHeader blk
to :: forall x. Rep (AnnouncingHeader blk) x -> AnnouncingHeader blk
Generic
deriving anyclass instance NoThunks (Header blk) => NoThunks (AnnouncingHeader blk)
instance HasHeader (Header blk) => Eq (AnnouncingHeader blk) where
AnnouncingHeader blk
a == :: AnnouncingHeader blk -> AnnouncingHeader blk -> Bool
== AnnouncingHeader blk
b = Header blk -> HeaderHash blk
forall blk. HasHeader (Header blk) => Header blk -> HeaderHash blk
headerHash (AnnouncingHeader blk -> Header blk
forall blk. AnnouncingHeader blk -> Header blk
ancHeader AnnouncingHeader blk
a) HeaderHash blk -> HeaderHash blk -> Bool
forall a. Eq a => a -> a -> Bool
== Header blk -> HeaderHash blk
forall blk. HasHeader (Header blk) => Header blk -> HeaderHash blk
headerHash (AnnouncingHeader blk -> Header blk
forall blk. AnnouncingHeader blk -> Header blk
ancHeader AnnouncingHeader blk
b)
mkAnnouncingHeader ::
forall blk.
(BlockSupportsProtocol blk, LeiosProtocol (BlockProtocol blk)) =>
TopLevelConfig blk ->
Header blk ->
Maybe (AnnouncingHeader blk)
TopLevelConfig blk
cfg Header blk
hdr = do
ann <-
Proxy (BlockProtocol blk)
-> ValidateView (BlockProtocol blk) -> Maybe LeiosAnnouncement
forall p (proxy :: * -> *).
LeiosProtocol p =>
proxy p -> ValidateView p -> Maybe LeiosAnnouncement
forall (proxy :: * -> *).
proxy (BlockProtocol blk)
-> ValidateView (BlockProtocol blk) -> Maybe LeiosAnnouncement
getLeiosAnnouncement
(forall t. Proxy t
forall {k} (t :: k). Proxy t
Proxy @(BlockProtocol blk))
(BlockConfig blk -> Header blk -> ValidateView (BlockProtocol blk)
forall blk.
BlockSupportsProtocol blk =>
BlockConfig blk -> Header blk -> ValidateView (BlockProtocol blk)
validateView (TopLevelConfig blk -> BlockConfig blk
forall blk. TopLevelConfig blk -> BlockConfig blk
configBlock TopLevelConfig blk
cfg) Header blk
hdr)
pure $
UnsafeMkAnnouncingHeader
hdr
(ElectionId (blockSlot hdr) (announcementElectee ann))
ann
data AnnouncementInvalidity blk
=
OutsideHorizon !OutsideForecastRange
|
SlotBeforeImmutableTip
|
!(ValidationErr (BlockProtocol blk))
|
!(OtherHeaderEnvelopeError blk)
deriving stock instance
(Show (ValidationErr (BlockProtocol blk)), Show (OtherHeaderEnvelopeError blk)) =>
Show (AnnouncementInvalidity blk)
validateAnnouncementHeader ::
forall blk.
( LedgerSupportsProtocol blk
, LeiosProtocol (BlockProtocol blk)
) =>
TopLevelConfig blk ->
ExtLedgerState blk EmptyMK ->
AnnouncingHeader blk ->
Except (AnnouncementInvalidity blk) OCINStaleness
TopLevelConfig blk
cfg ExtLedgerState blk EmptyMK
extLedger AnnouncingHeader blk
ancHdr = do
Bool
-> ExceptT (AnnouncementInvalidity blk) Identity ()
-> ExceptT (AnnouncementInvalidity blk) Identity ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (SlotNo -> WithOrigin SlotNo
forall t. t -> WithOrigin t
NotOrigin SlotNo
slot WithOrigin SlotNo -> WithOrigin SlotNo -> Bool
forall a. Ord a => a -> a -> Bool
< LedgerState blk EmptyMK -> WithOrigin SlotNo
forall (l :: LedgerStateKind) (mk :: MapKind).
GetTip l =>
l mk -> WithOrigin SlotNo
getTipSlot (ExtLedgerState blk EmptyMK -> LedgerState blk EmptyMK
forall blk (mk :: MapKind).
ExtLedgerState blk mk -> LedgerState blk mk
ledgerState ExtLedgerState blk EmptyMK
extLedger)) (ExceptT (AnnouncementInvalidity blk) Identity ()
-> ExceptT (AnnouncementInvalidity blk) Identity ())
-> ExceptT (AnnouncementInvalidity blk) Identity ()
-> ExceptT (AnnouncementInvalidity blk) Identity ()
forall a b. (a -> b) -> a -> b
$
AnnouncementInvalidity blk
-> ExceptT (AnnouncementInvalidity blk) Identity ()
forall a.
AnnouncementInvalidity blk
-> ExceptT (AnnouncementInvalidity blk) Identity a
forall e (m :: * -> *) a. MonadError e m => e -> m a
throwError AnnouncementInvalidity blk
forall blk. AnnouncementInvalidity blk
SlotBeforeImmutableTip
ledgerView <-
(OutsideForecastRange -> AnnouncementInvalidity blk)
-> Except OutsideForecastRange (LedgerView (BlockProtocol blk))
-> Except
(AnnouncementInvalidity blk) (LedgerView (BlockProtocol blk))
forall e e' a. (e -> e') -> Except e a -> Except e' a
withExcept OutsideForecastRange -> AnnouncementInvalidity blk
forall blk. OutsideForecastRange -> AnnouncementInvalidity blk
OutsideHorizon (Except OutsideForecastRange (LedgerView (BlockProtocol blk))
-> Except
(AnnouncementInvalidity blk) (LedgerView (BlockProtocol blk)))
-> Except OutsideForecastRange (LedgerView (BlockProtocol blk))
-> Except
(AnnouncementInvalidity blk) (LedgerView (BlockProtocol blk))
forall a b. (a -> b) -> a -> b
$
Forecast (LedgerView (BlockProtocol blk))
-> SlotNo
-> Except OutsideForecastRange (LedgerView (BlockProtocol blk))
forall a. Forecast a -> SlotNo -> Except OutsideForecastRange a
forecastFor
(LedgerConfig blk
-> LedgerState blk EmptyMK
-> Forecast (LedgerView (BlockProtocol blk))
forall blk (mk :: MapKind).
(LedgerSupportsProtocol blk, HasCallStack) =>
LedgerConfig blk
-> LedgerState blk mk -> Forecast (LedgerView (BlockProtocol blk))
forall (mk :: MapKind).
HasCallStack =>
LedgerConfig blk
-> LedgerState blk mk -> Forecast (LedgerView (BlockProtocol blk))
ledgerViewForecastAt (TopLevelConfig blk -> LedgerConfig blk
forall blk. TopLevelConfig blk -> LedgerConfig blk
configLedger TopLevelConfig blk
cfg) (ExtLedgerState blk EmptyMK -> LedgerState blk EmptyMK
forall blk (mk :: MapKind).
ExtLedgerState blk mk -> LedgerState blk mk
ledgerState ExtLedgerState blk EmptyMK
extLedger))
SlotNo
slot
withExcept RbHeaderEnvelopeInvalid $
additionalEnvelopeChecks cfg ledgerView hdr
let tickedHeaderState =
ConsensusConfig (BlockProtocol blk)
-> LedgerView (BlockProtocol blk)
-> SlotNo
-> HeaderState blk
-> Ticked (HeaderState blk)
forall blk.
ConsensusProtocol (BlockProtocol blk) =>
ConsensusConfig (BlockProtocol blk)
-> LedgerView (BlockProtocol blk)
-> SlotNo
-> HeaderState blk
-> Ticked (HeaderState blk)
tickHeaderState (TopLevelConfig blk -> ConsensusConfig (BlockProtocol blk)
forall blk.
TopLevelConfig blk -> ConsensusConfig (BlockProtocol blk)
configConsensus TopLevelConfig blk
cfg) LedgerView (BlockProtocol blk)
ledgerView SlotNo
slot (ExtLedgerState blk EmptyMK -> HeaderState blk
forall blk (mk :: MapKind).
ExtLedgerState blk mk -> HeaderState blk
headerState ExtLedgerState blk EmptyMK
extLedger)
withExcept HeaderInvalid $
validateLeiosAnnouncement
(configConsensus cfg)
(validateView (configBlock cfg) hdr)
slot
(tickedHeaderStateChainDep tickedHeaderState)
where
hdr :: Header blk
hdr = AnnouncingHeader blk -> Header blk
forall blk. AnnouncingHeader blk -> Header blk
ancHeader AnnouncingHeader blk
ancHdr
slot :: SlotNo
slot = Header blk -> SlotNo
forall b. HasHeader b => b -> SlotNo
blockSlot Header blk
hdr
announcementValidity ::
( IOLike m
, LedgerSupportsProtocol blk
, LeiosProtocol (BlockProtocol blk)
) =>
SystemTime m ->
InFutureCheck.SomeHeaderInFutureCheck m blk ->
TopLevelConfig blk ->
ExtLedgerState blk EmptyMK ->
AnnouncingHeader blk ->
ExceptT (Announcements.Error (AnnouncementInvalidity blk)) m OCINStaleness
announcementValidity :: forall (m :: * -> *) blk.
(IOLike m, LedgerSupportsProtocol blk,
LeiosProtocol (BlockProtocol blk)) =>
SystemTime m
-> SomeHeaderInFutureCheck m blk
-> TopLevelConfig blk
-> ExtLedgerState blk EmptyMK
-> AnnouncingHeader blk
-> ExceptT (Error (AnnouncementInvalidity blk)) m OCINStaleness
announcementValidity SystemTime m
systemTime SomeHeaderInFutureCheck m blk
futureCheck TopLevelConfig blk
cfg ExtLedgerState blk EmptyMK
immLedger AnnouncingHeader blk
ancHdr = do
onset <- m RelativeTime
-> ExceptT (Error (AnnouncementInvalidity blk)) m RelativeTime
forall (m :: * -> *) a.
Monad m =>
m a -> ExceptT (Error (AnnouncementInvalidity blk)) m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m RelativeTime
-> ExceptT (Error (AnnouncementInvalidity blk)) m RelativeTime)
-> m RelativeTime
-> ExceptT (Error (AnnouncementInvalidity blk)) m RelativeTime
forall a b. (a -> b) -> a -> b
$ case SomeHeaderInFutureCheck m blk
futureCheck of
InFutureCheck.SomeHeaderInFutureCheck HeaderInFutureCheck m blk arrival judgment
hifc -> do
arrival <- HeaderInFutureCheck m blk arrival judgment
-> Header blk -> m arrival
forall (m :: * -> *) blk arrival judgment.
HeaderInFutureCheck m blk arrival judgment
-> Header blk -> m arrival
InFutureCheck.recordHeaderArrival HeaderInFutureCheck m blk arrival judgment
hifc (AnnouncingHeader blk -> Header blk
forall blk. AnnouncingHeader blk -> Header blk
ancHeader AnnouncingHeader blk
ancHdr)
judgment <-
either throwIO pure $
runExcept $
InFutureCheck.judgeHeaderArrival
hifc
(configLedger cfg)
(ledgerState immLedger)
arrival
arrivalResult <- InFutureCheck.handleHeaderArrival hifc judgment
either throwIO pure (runExcept arrivalResult)
now <- lift $ systemTimeCurrent systemTime
when (diffRelTime now onset > maxAnnouncementAgeRecv) $
throwError Announcements.ErrTooOld
liftEither . first Announcements.ErrInvalid . runExcept $
validateAnnouncementHeader cfg immLedger ancHdr
maxAnnouncementAgeRecv :: NominalDiffTime
maxAnnouncementAgeRecv :: NominalDiffTime
maxAnnouncementAgeRecv = NominalDiffTime
600