{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE ViewPatterns #-}

-- | Snapshots
--
-- Snapshotting a ledger state means saving a copy of the state to disk, so that
-- a later start of a cardano-node can use such a snapshot as a starting point
-- instead of having to replay from Genesis.
--
-- A snapshot is identified by the slot number of the ledger state it contains
-- and possibly has a suffix in the name. The consensus logic will not delete a
-- snapshot if it has a suffix. This can be used to store important
-- snapshots. The suffix can be manually added to the snapshot by renaming the
-- folder (see the caveats in 'snapshotManager' for the LSM backend). It will
-- also be added automatically by some tools such as db-analyser.
--
-- In general snapshots will be stored in the @./ledger@ directory inside the
-- ChainDB directory, but each LedgerDB backend is free to store it somewhere
-- else. Management of snapshots is done through the 'SnapshotManager'
-- record (see the 'snapshotManager' functions on each backend).
--
-- Snapshots cosists of two parts:
--
--  - the ledger state tables: location and format differs among backends,
--
--  - the rest of the ledger state: a CBOR serialization of an @ExtLedgerState
--    blk EmptyMK@, stored in the @./state@ file in the snapshot directory.
--
-- V2 backends will provide means of loading a snapshot via the method
-- 'newHandleFromSnapshot'. V1 backends load the snapshot directly in
-- 'initFromSnapshot'.
module Ouroboros.Consensus.Storage.LedgerDB.Snapshots
  ( -- * Snapshots
    CRCError (..)
  , DiskSnapshot (..)
  , MetadataErr (..)
  , ReadSnapshotErr (..)
  , SnapshotBackend (..)
  , SnapshotFailure (..)
  , SnapshotMetadata (..)
  , SnapshotPolicyArgs (..)
  , TablesCodecVersion (..)
  , NumOfDiskSnapshots (..)
  , defaultSnapshotPolicyArgs

    -- * Codec
  , readExtLedgerState
  , writeExtLedgerState

    -- * Paths
  , diskSnapshotIsTemporary
  , snapshotFromPath
  , snapshotToChecksumPath
  , snapshotToStatePath
  , snapshotToDirName
  , snapshotToDirPath
  , snapshotToMetadataPath

    -- * Management
  , SnapshotManager (..)
  , defaultDeleteSnapshotIfTemporary
  , defaultListSnapshots
  , trimSnapshots
  , loadSnapshotMetadata
  , writeSnapshotMetadata

    -- * Policy
  , SnapshotPolicy (..)
  , SnapshotDelayRange (..)
  , SnapshotSelectorContext (..)
  , SnapshotFrequency (..)
  , SnapshotFrequencyArgs (..)
  , defaultSnapshotPolicy
  , mithrilEpochSize
  , sanityCheckSnapshotPolicyArgs
  , pattern DoDiskSnapshotChecksum
  , pattern NoDoDiskSnapshotChecksum

    -- * Tracing
  , TraceSnapshotEvent (..)

    -- * Re-exports
  , Flag (..)

    -- * Testing
  , decodeLBackwardsCompatible
  , destroySnapshots
  , encodeL
  , snapshotsMapM_
  ) where

import Cardano.Ledger.BaseTypes
import Codec.CBOR.Decoding
import Codec.CBOR.Encoding
import qualified Codec.CBOR.Write as CBOR
import qualified Codec.Serialise.Decoding as Dec
import Control.Monad
import qualified Control.Monad as Monad
import Control.Monad.Class.MonadTime.SI
import Control.Monad.Except
import Control.Tracer
import Data.Aeson (FromJSON (..), ToJSON (..), (.:), (.=))
import qualified Data.Aeson as Aeson
import Data.Aeson.Types (Parser)
import Data.Functor.Identity
import qualified Data.List as List
import Data.List.NonEmpty (NonEmpty)
import Data.Maybe (catMaybes, isJust, mapMaybe, maybeToList)
import Data.Ord
import Data.Set (Set)
import qualified Data.Set as Set
import Data.Time.Clock (secondsToDiffTime)
import Data.Word
import GHC.Generics
import NoThunks.Class
import Ouroboros.Consensus.Block
import Ouroboros.Consensus.Config
import Ouroboros.Consensus.Ledger.Abstract (EmptyMK)
import Ouroboros.Consensus.Ledger.Extended
import Ouroboros.Consensus.Util (Flag (..), lastMaybe)
import Ouroboros.Consensus.Util.Args (OverrideOrDefault (..), provideDefault)
import Ouroboros.Consensus.Util.CBOR
  ( ReadIncrementalErr
  , decodeWithOrigin
  , readIncremental
  )
import Ouroboros.Consensus.Util.CRC
import Ouroboros.Consensus.Util.CallStack
import Ouroboros.Consensus.Util.Enclose
import Ouroboros.Consensus.Util.IOLike
import Ouroboros.Consensus.Util.Versioned
import System.FS.API
import System.FS.API.Lazy
import System.FS.CRC
import Text.Read (readMaybe)

-- | Name of a disk snapshot.
--
--   The snapshot itself might not yet exist on disk.
data DiskSnapshot = DiskSnapshot
  { DiskSnapshot -> Word64
dsNumber :: Word64
  -- ^ Snapshots are numbered. We will try the snapshots with the highest
  -- number first.
  --
  -- When creating a snapshot, we use the slot number of the ledger state it
  -- corresponds to as the snapshot number. This gives an indication of how
  -- recent the snapshot is.
  --
  -- Note that the snapshot names are only indicative, we don't rely on the
  -- snapshot number matching the slot number of the corresponding ledger
  -- state. We only use the snapshots numbers to determine the order in
  -- which we try them.
  , DiskSnapshot -> Maybe String
dsSuffix :: Maybe String
  -- ^ Snapshots can optionally have a suffix, separated by the snapshot
  -- number with an underscore, e.g., @4492799_last_Byron@. This suffix acts
  -- as metadata for the operator of the node. Snapshots with a suffix will
  -- /not be deleted/.
  }
  deriving (Int -> DiskSnapshot -> ShowS
[DiskSnapshot] -> ShowS
DiskSnapshot -> String
(Int -> DiskSnapshot -> ShowS)
-> (DiskSnapshot -> String)
-> ([DiskSnapshot] -> ShowS)
-> Show DiskSnapshot
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> DiskSnapshot -> ShowS
showsPrec :: Int -> DiskSnapshot -> ShowS
$cshow :: DiskSnapshot -> String
show :: DiskSnapshot -> String
$cshowList :: [DiskSnapshot] -> ShowS
showList :: [DiskSnapshot] -> ShowS
Show, DiskSnapshot -> DiskSnapshot -> Bool
(DiskSnapshot -> DiskSnapshot -> Bool)
-> (DiskSnapshot -> DiskSnapshot -> Bool) -> Eq DiskSnapshot
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: DiskSnapshot -> DiskSnapshot -> Bool
== :: DiskSnapshot -> DiskSnapshot -> Bool
$c/= :: DiskSnapshot -> DiskSnapshot -> Bool
/= :: DiskSnapshot -> DiskSnapshot -> Bool
Eq, (forall x. DiskSnapshot -> Rep DiskSnapshot x)
-> (forall x. Rep DiskSnapshot x -> DiskSnapshot)
-> Generic DiskSnapshot
forall x. Rep DiskSnapshot x -> DiskSnapshot
forall x. DiskSnapshot -> Rep DiskSnapshot x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. DiskSnapshot -> Rep DiskSnapshot x
from :: forall x. DiskSnapshot -> Rep DiskSnapshot x
$cto :: forall x. Rep DiskSnapshot x -> DiskSnapshot
to :: forall x. Rep DiskSnapshot x -> DiskSnapshot
Generic)

data SnapshotFailure blk
  = -- | We failed to deserialise the snapshot
    --
    -- This can happen due to data corruption in the ledger DB or if the codecs
    -- changed.
    InitFailureRead ReadSnapshotErr
  | -- | This snapshot is too recent (ahead of the tip of the immutable chain)
    InitFailureTooRecent DiskSnapshot (Point blk)
  | -- | This snapshot was of the ledger state at genesis, even though we never
    -- take snapshots at genesis, so this is unexpected.
    InitFailureGenesis
  deriving (Int -> SnapshotFailure blk -> ShowS
[SnapshotFailure blk] -> ShowS
SnapshotFailure blk -> String
(Int -> SnapshotFailure blk -> ShowS)
-> (SnapshotFailure blk -> String)
-> ([SnapshotFailure blk] -> ShowS)
-> Show (SnapshotFailure blk)
forall blk. StandardHash blk => Int -> SnapshotFailure blk -> ShowS
forall blk. StandardHash blk => [SnapshotFailure blk] -> ShowS
forall blk. StandardHash blk => SnapshotFailure blk -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: forall blk. StandardHash blk => Int -> SnapshotFailure blk -> ShowS
showsPrec :: Int -> SnapshotFailure blk -> ShowS
$cshow :: forall blk. StandardHash blk => SnapshotFailure blk -> String
show :: SnapshotFailure blk -> String
$cshowList :: forall blk. StandardHash blk => [SnapshotFailure blk] -> ShowS
showList :: [SnapshotFailure blk] -> ShowS
Show, SnapshotFailure blk -> SnapshotFailure blk -> Bool
(SnapshotFailure blk -> SnapshotFailure blk -> Bool)
-> (SnapshotFailure blk -> SnapshotFailure blk -> Bool)
-> Eq (SnapshotFailure blk)
forall blk.
StandardHash blk =>
SnapshotFailure blk -> SnapshotFailure blk -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: forall blk.
StandardHash blk =>
SnapshotFailure blk -> SnapshotFailure blk -> Bool
== :: SnapshotFailure blk -> SnapshotFailure blk -> Bool
$c/= :: forall blk.
StandardHash blk =>
SnapshotFailure blk -> SnapshotFailure blk -> Bool
/= :: SnapshotFailure blk -> SnapshotFailure blk -> Bool
Eq, (forall x. SnapshotFailure blk -> Rep (SnapshotFailure blk) x)
-> (forall x. Rep (SnapshotFailure blk) x -> SnapshotFailure blk)
-> Generic (SnapshotFailure blk)
forall x. Rep (SnapshotFailure blk) x -> SnapshotFailure blk
forall x. SnapshotFailure blk -> Rep (SnapshotFailure blk) x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
forall blk x. Rep (SnapshotFailure blk) x -> SnapshotFailure blk
forall blk x. SnapshotFailure blk -> Rep (SnapshotFailure blk) x
$cfrom :: forall blk x. SnapshotFailure blk -> Rep (SnapshotFailure blk) x
from :: forall x. SnapshotFailure blk -> Rep (SnapshotFailure blk) x
$cto :: forall blk x. Rep (SnapshotFailure blk) x -> SnapshotFailure blk
to :: forall x. Rep (SnapshotFailure blk) x -> SnapshotFailure blk
Generic)

data ReadSnapshotErr
  = -- | Error while de-serialising data
    ReadSnapshotFailed ReadIncrementalErr
  | -- | Checksum of read snapshot differs from the one tracked by
    --   its corresponding metadata file
    ReadSnapshotDataCorruption
  | -- | An error occurred while reading the snapshot metadata file
    ReadMetadataError FsPath MetadataErr
  | -- | We were given a legacy snapshot
    ReadSnapshotIsLegacy
  deriving (ReadSnapshotErr -> ReadSnapshotErr -> Bool
(ReadSnapshotErr -> ReadSnapshotErr -> Bool)
-> (ReadSnapshotErr -> ReadSnapshotErr -> Bool)
-> Eq ReadSnapshotErr
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: ReadSnapshotErr -> ReadSnapshotErr -> Bool
== :: ReadSnapshotErr -> ReadSnapshotErr -> Bool
$c/= :: ReadSnapshotErr -> ReadSnapshotErr -> Bool
/= :: ReadSnapshotErr -> ReadSnapshotErr -> Bool
Eq, Int -> ReadSnapshotErr -> ShowS
[ReadSnapshotErr] -> ShowS
ReadSnapshotErr -> String
(Int -> ReadSnapshotErr -> ShowS)
-> (ReadSnapshotErr -> String)
-> ([ReadSnapshotErr] -> ShowS)
-> Show ReadSnapshotErr
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> ReadSnapshotErr -> ShowS
showsPrec :: Int -> ReadSnapshotErr -> ShowS
$cshow :: ReadSnapshotErr -> String
show :: ReadSnapshotErr -> String
$cshowList :: [ReadSnapshotErr] -> ShowS
showList :: [ReadSnapshotErr] -> ShowS
Show)

data TablesCodecVersion
  = -- | Used in cardano-node 10.7. Previous versions have no codec version.
    -- [ {_ (txid, big-endian txix) => txout} ]
    TablesCodecVersion1
  deriving (TablesCodecVersion -> TablesCodecVersion -> Bool
(TablesCodecVersion -> TablesCodecVersion -> Bool)
-> (TablesCodecVersion -> TablesCodecVersion -> Bool)
-> Eq TablesCodecVersion
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: TablesCodecVersion -> TablesCodecVersion -> Bool
== :: TablesCodecVersion -> TablesCodecVersion -> Bool
$c/= :: TablesCodecVersion -> TablesCodecVersion -> Bool
/= :: TablesCodecVersion -> TablesCodecVersion -> Bool
Eq, Int -> TablesCodecVersion -> ShowS
[TablesCodecVersion] -> ShowS
TablesCodecVersion -> String
(Int -> TablesCodecVersion -> ShowS)
-> (TablesCodecVersion -> String)
-> ([TablesCodecVersion] -> ShowS)
-> Show TablesCodecVersion
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> TablesCodecVersion -> ShowS
showsPrec :: Int -> TablesCodecVersion -> ShowS
$cshow :: TablesCodecVersion -> String
show :: TablesCodecVersion -> String
$cshowList :: [TablesCodecVersion] -> ShowS
showList :: [TablesCodecVersion] -> ShowS
Show)

instance ToJSON TablesCodecVersion where
  toJSON :: TablesCodecVersion -> Value
toJSON TablesCodecVersion
TablesCodecVersion1 = Scientific -> Value
Aeson.Number Scientific
1

instance FromJSON TablesCodecVersion where
  parseJSON :: Value -> Parser TablesCodecVersion
parseJSON Value
v = Word8 -> Parser TablesCodecVersion
enforceVersion (Word8 -> Parser TablesCodecVersion)
-> Parser Word8 -> Parser TablesCodecVersion
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< Value -> Parser Word8
forall a. FromJSON a => Value -> Parser a
parseJSON Value
v

enforceVersion :: Word8 -> Parser TablesCodecVersion
enforceVersion :: Word8 -> Parser TablesCodecVersion
enforceVersion Word8
v = case Word8
v of
  Word8
1 -> TablesCodecVersion -> Parser TablesCodecVersion
forall a. a -> Parser a
forall (f :: * -> *) a. Applicative f => a -> f a
pure TablesCodecVersion
TablesCodecVersion1
  Word8
_ -> String -> Parser TablesCodecVersion
forall a. String -> Parser a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"Unknown or outdated tables codec version"

data SnapshotMetadata = SnapshotMetadata
  { SnapshotMetadata -> SnapshotBackend
snapshotBackend :: SnapshotBackend
  , SnapshotMetadata -> CRC
snapshotChecksum :: CRC
  , SnapshotMetadata -> TablesCodecVersion
snapshotTablesCodecVersion :: TablesCodecVersion
  }
  deriving (SnapshotMetadata -> SnapshotMetadata -> Bool
(SnapshotMetadata -> SnapshotMetadata -> Bool)
-> (SnapshotMetadata -> SnapshotMetadata -> Bool)
-> Eq SnapshotMetadata
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: SnapshotMetadata -> SnapshotMetadata -> Bool
== :: SnapshotMetadata -> SnapshotMetadata -> Bool
$c/= :: SnapshotMetadata -> SnapshotMetadata -> Bool
/= :: SnapshotMetadata -> SnapshotMetadata -> Bool
Eq, Int -> SnapshotMetadata -> ShowS
[SnapshotMetadata] -> ShowS
SnapshotMetadata -> String
(Int -> SnapshotMetadata -> ShowS)
-> (SnapshotMetadata -> String)
-> ([SnapshotMetadata] -> ShowS)
-> Show SnapshotMetadata
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> SnapshotMetadata -> ShowS
showsPrec :: Int -> SnapshotMetadata -> ShowS
$cshow :: SnapshotMetadata -> String
show :: SnapshotMetadata -> String
$cshowList :: [SnapshotMetadata] -> ShowS
showList :: [SnapshotMetadata] -> ShowS
Show)

instance ToJSON SnapshotMetadata where
  toJSON :: SnapshotMetadata -> Value
toJSON SnapshotMetadata
sm =
    [Pair] -> Value
Aeson.object
      [ Key
"backend" Key -> SnapshotBackend -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= SnapshotMetadata -> SnapshotBackend
snapshotBackend SnapshotMetadata
sm
      , Key
"checksum" Key -> Word32 -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= CRC -> Word32
getCRC (SnapshotMetadata -> CRC
snapshotChecksum SnapshotMetadata
sm)
      , Key
"tablesCodecVersion" Key -> TablesCodecVersion -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= SnapshotMetadata -> TablesCodecVersion
snapshotTablesCodecVersion SnapshotMetadata
sm
      ]

instance FromJSON SnapshotMetadata where
  parseJSON :: Value -> Parser SnapshotMetadata
parseJSON = String
-> (Object -> Parser SnapshotMetadata)
-> Value
-> Parser SnapshotMetadata
forall a. String -> (Object -> Parser a) -> Value -> Parser a
Aeson.withObject String
"SnapshotMetadata" ((Object -> Parser SnapshotMetadata)
 -> Value -> Parser SnapshotMetadata)
-> (Object -> Parser SnapshotMetadata)
-> Value
-> Parser SnapshotMetadata
forall a b. (a -> b) -> a -> b
$ \Object
o ->
    SnapshotBackend -> CRC -> TablesCodecVersion -> SnapshotMetadata
SnapshotMetadata
      (SnapshotBackend -> CRC -> TablesCodecVersion -> SnapshotMetadata)
-> Parser SnapshotBackend
-> Parser (CRC -> TablesCodecVersion -> SnapshotMetadata)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Object
o Object -> Key -> Parser SnapshotBackend
forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"backend"
      Parser (CRC -> TablesCodecVersion -> SnapshotMetadata)
-> Parser CRC -> Parser (TablesCodecVersion -> SnapshotMetadata)
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> (Word32 -> CRC) -> Parser Word32 -> Parser CRC
forall a b. (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Word32 -> CRC
CRC (Object
o Object -> Key -> Parser Word32
forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"checksum")
      Parser (TablesCodecVersion -> SnapshotMetadata)
-> Parser TablesCodecVersion -> Parser SnapshotMetadata
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
o Object -> Key -> Parser TablesCodecVersion
forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"tablesCodecVersion"

data SnapshotBackend
  = UTxOHDMemSnapshot
  | UTxOHDLMDBSnapshot
  | UTxOHDLSMSnapshot
  deriving (SnapshotBackend -> SnapshotBackend -> Bool
(SnapshotBackend -> SnapshotBackend -> Bool)
-> (SnapshotBackend -> SnapshotBackend -> Bool)
-> Eq SnapshotBackend
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: SnapshotBackend -> SnapshotBackend -> Bool
== :: SnapshotBackend -> SnapshotBackend -> Bool
$c/= :: SnapshotBackend -> SnapshotBackend -> Bool
/= :: SnapshotBackend -> SnapshotBackend -> Bool
Eq, Int -> SnapshotBackend -> ShowS
[SnapshotBackend] -> ShowS
SnapshotBackend -> String
(Int -> SnapshotBackend -> ShowS)
-> (SnapshotBackend -> String)
-> ([SnapshotBackend] -> ShowS)
-> Show SnapshotBackend
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> SnapshotBackend -> ShowS
showsPrec :: Int -> SnapshotBackend -> ShowS
$cshow :: SnapshotBackend -> String
show :: SnapshotBackend -> String
$cshowList :: [SnapshotBackend] -> ShowS
showList :: [SnapshotBackend] -> ShowS
Show)

instance ToJSON SnapshotBackend where
  toJSON :: SnapshotBackend -> Value
toJSON = \case
    SnapshotBackend
UTxOHDMemSnapshot -> Value
"utxohd-mem"
    SnapshotBackend
UTxOHDLMDBSnapshot -> Value
"utxohd-lmdb"
    SnapshotBackend
UTxOHDLSMSnapshot -> Value
"utxohd-lsm"

instance FromJSON SnapshotBackend where
  parseJSON :: Value -> Parser SnapshotBackend
parseJSON = String
-> (Text -> Parser SnapshotBackend)
-> Value
-> Parser SnapshotBackend
forall a. String -> (Text -> Parser a) -> Value -> Parser a
Aeson.withText String
"SnapshotBackend" ((Text -> Parser SnapshotBackend)
 -> Value -> Parser SnapshotBackend)
-> (Text -> Parser SnapshotBackend)
-> Value
-> Parser SnapshotBackend
forall a b. (a -> b) -> a -> b
$ \case
    Text
"utxohd-mem" -> SnapshotBackend -> Parser SnapshotBackend
forall a. a -> Parser a
forall (f :: * -> *) a. Applicative f => a -> f a
pure SnapshotBackend
UTxOHDMemSnapshot
    Text
"utxohd-lmdb" -> SnapshotBackend -> Parser SnapshotBackend
forall a. a -> Parser a
forall (f :: * -> *) a. Applicative f => a -> f a
pure SnapshotBackend
UTxOHDLMDBSnapshot
    Text
"utxohd-lsm" -> SnapshotBackend -> Parser SnapshotBackend
forall a. a -> Parser a
forall (f :: * -> *) a. Applicative f => a -> f a
pure SnapshotBackend
UTxOHDLSMSnapshot
    Text
_ -> String -> Parser SnapshotBackend
forall a. String -> Parser a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"unknown SnapshotBackend"

data MetadataErr
  = -- | The metadata file does not exist
    MetadataFileDoesNotExist
  | -- | The metadata file is invalid and does not deserialize
    MetadataInvalid String
  | -- | The metadata file has the incorrect backend
    MetadataBackendMismatch
  deriving (MetadataErr -> MetadataErr -> Bool
(MetadataErr -> MetadataErr -> Bool)
-> (MetadataErr -> MetadataErr -> Bool) -> Eq MetadataErr
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: MetadataErr -> MetadataErr -> Bool
== :: MetadataErr -> MetadataErr -> Bool
$c/= :: MetadataErr -> MetadataErr -> Bool
/= :: MetadataErr -> MetadataErr -> Bool
Eq, Int -> MetadataErr -> ShowS
[MetadataErr] -> ShowS
MetadataErr -> String
(Int -> MetadataErr -> ShowS)
-> (MetadataErr -> String)
-> ([MetadataErr] -> ShowS)
-> Show MetadataErr
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> MetadataErr -> ShowS
showsPrec :: Int -> MetadataErr -> ShowS
$cshow :: MetadataErr -> String
show :: MetadataErr -> String
$cshowList :: [MetadataErr] -> ShowS
showList :: [MetadataErr] -> ShowS
Show)

-- | Management of snapshots for the different LedgerDB backends.
--
-- The LedgerDB V1 takes snapshots in @ReadLocked m@, hence the two different
-- @m@ and @n@ monad types.
data SnapshotManager m n blk st = SnapshotManager
  { forall (m :: * -> *) (n :: * -> *) blk st.
SnapshotManager m n blk st -> m [DiskSnapshot]
listSnapshots :: m [DiskSnapshot]
  , forall (m :: * -> *) (n :: * -> *) blk st.
SnapshotManager m n blk st -> DiskSnapshot -> m ()
deleteSnapshotIfTemporary :: DiskSnapshot -> m ()
  , forall (m :: * -> *) (n :: * -> *) blk st.
SnapshotManager m n blk st
-> Maybe String -> st -> n (Maybe (DiskSnapshot, RealPoint blk))
takeSnapshot ::
      Maybe String ->
      -- \^ The (possibly empty) suffix for the snapshot name
      st ->
      -- \^ The state needed for taking the snapshot:
      -- - In V1: this will be the DbChangelog and the Backing store
      -- - In V2: this will be a StateRef
      n (Maybe (DiskSnapshot, RealPoint blk))
      -- \^ If a Snapshot was taken, its information and the point at which it
      -- was taken.
  }

-- | Named snapshot are permanent, they will never be deleted even if failing to
-- deserialize.
diskSnapshotIsPermanent :: DiskSnapshot -> Bool
diskSnapshotIsPermanent :: DiskSnapshot -> Bool
diskSnapshotIsPermanent = Maybe String -> Bool
forall a. Maybe a -> Bool
isJust (Maybe String -> Bool)
-> (DiskSnapshot -> Maybe String) -> DiskSnapshot -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. DiskSnapshot -> Maybe String
dsSuffix

-- | The snapshots that are periodically created are temporary, they will be
-- deleted when trimming or if they fail to deserialize.
diskSnapshotIsTemporary :: DiskSnapshot -> Bool
diskSnapshotIsTemporary :: DiskSnapshot -> Bool
diskSnapshotIsTemporary = Bool -> Bool
not (Bool -> Bool) -> (DiskSnapshot -> Bool) -> DiskSnapshot -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. DiskSnapshot -> Bool
diskSnapshotIsPermanent

snapshotFromPath :: String -> Maybe DiskSnapshot
snapshotFromPath :: String -> Maybe DiskSnapshot
snapshotFromPath String
fileName = do
  number <- String -> Maybe Word64
forall a. Read a => String -> Maybe a
readMaybe String
prefix
  return $ DiskSnapshot number suffix'
 where
  (String
prefix, String
suffix) = (Char -> Bool) -> String -> (String, String)
forall a. (a -> Bool) -> [a] -> ([a], [a])
break (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
'_') String
fileName

  suffix' :: Maybe String
  suffix' :: Maybe String
suffix' = case String
suffix of
    String
"" -> Maybe String
forall a. Maybe a
Nothing
    Char
_ : String
str -> String -> Maybe String
forall a. a -> Maybe a
Just String
str

-- | List on-disk snapshots, highest number first.
defaultListSnapshots :: Monad m => SomeHasFS m -> m [DiskSnapshot]
defaultListSnapshots :: forall (m :: * -> *). Monad m => SomeHasFS m -> m [DiskSnapshot]
defaultListSnapshots (SomeHasFS HasFS{HasCallStack => FsPath -> m (Set String)
listDirectory :: HasCallStack => FsPath -> m (Set String)
listDirectory :: forall (m :: * -> *) h.
HasFS m h -> HasCallStack => FsPath -> m (Set String)
listDirectory}) =
  Set String -> [DiskSnapshot]
aux (Set String -> [DiskSnapshot])
-> m (Set String) -> m [DiskSnapshot]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> HasCallStack => FsPath -> m (Set String)
FsPath -> m (Set String)
listDirectory ([String] -> FsPath
mkFsPath [])
 where
  aux :: Set String -> [DiskSnapshot]
  aux :: Set String -> [DiskSnapshot]
aux = (DiskSnapshot -> Down Word64) -> [DiskSnapshot] -> [DiskSnapshot]
forall b a. Ord b => (a -> b) -> [a] -> [a]
List.sortOn (Word64 -> Down Word64
forall a. a -> Down a
Down (Word64 -> Down Word64)
-> (DiskSnapshot -> Word64) -> DiskSnapshot -> Down Word64
forall b c a. (b -> c) -> (a -> b) -> a -> c
. DiskSnapshot -> Word64
dsNumber) ([DiskSnapshot] -> [DiskSnapshot])
-> (Set String -> [DiskSnapshot]) -> Set String -> [DiskSnapshot]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (String -> Maybe DiskSnapshot) -> [String] -> [DiskSnapshot]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe String -> Maybe DiskSnapshot
snapshotFromPath ([String] -> [DiskSnapshot])
-> (Set String -> [String]) -> Set String -> [DiskSnapshot]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Set String -> [String]
forall a. Set a -> [a]
Set.toList

-- | Delete snapshot from disk
defaultDeleteSnapshotIfTemporary ::
  forall m blk.
  (MonadCatch m, HasCallStack) =>
  SomeHasFS m -> Tracer m (TraceSnapshotEvent blk) -> DiskSnapshot -> m ()
defaultDeleteSnapshotIfTemporary :: forall (m :: * -> *) blk.
(MonadCatch m, HasCallStack) =>
SomeHasFS m
-> Tracer m (TraceSnapshotEvent blk) -> DiskSnapshot -> m ()
defaultDeleteSnapshotIfTemporary (SomeHasFS HasFS{HasCallStack => FsPath -> m Bool
doesDirectoryExist :: HasCallStack => FsPath -> m Bool
doesDirectoryExist :: forall (m :: * -> *) h.
HasFS m h -> HasCallStack => FsPath -> m Bool
doesDirectoryExist, HasCallStack => FsPath -> m ()
removeDirectoryRecursive :: HasCallStack => FsPath -> m ()
removeDirectoryRecursive :: forall (m :: * -> *) h. HasFS m h -> HasCallStack => FsPath -> m ()
removeDirectoryRecursive}) Tracer m (TraceSnapshotEvent blk)
tracer DiskSnapshot
ss =
  Bool -> m () -> m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (DiskSnapshot -> Bool
diskSnapshotIsTemporary DiskSnapshot
ss) (m () -> m ()) -> m () -> m ()
forall a b. (a -> b) -> a -> b
$ m (Either SomeException ()) -> m ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (m (Either SomeException ()) -> m ())
-> m (Either SomeException ()) -> m ()
forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) e a.
(MonadCatch m, Exception e) =>
m a -> m (Either e a)
try @m @SomeException (m () -> m (Either SomeException ()))
-> m () -> m (Either SomeException ())
forall a b. (a -> b) -> a -> b
$ do
    let p :: FsPath
p = DiskSnapshot -> FsPath
snapshotToDirPath DiskSnapshot
ss
    exists <- HasCallStack => FsPath -> m Bool
FsPath -> m Bool
doesDirectoryExist FsPath
p
    when exists (removeDirectoryRecursive p)
    traceWith tracer (DeletedSnapshot ss)

-- | Write a snapshot metadata JSON file.
writeSnapshotMetadata ::
  MonadThrow m =>
  SomeHasFS m ->
  DiskSnapshot ->
  SnapshotMetadata ->
  m ()
writeSnapshotMetadata :: forall (m :: * -> *).
MonadThrow m =>
SomeHasFS m -> DiskSnapshot -> SnapshotMetadata -> m ()
writeSnapshotMetadata (SomeHasFS HasFS m h
hasFS) DiskSnapshot
ds SnapshotMetadata
meta = do
  let metadataPath :: FsPath
metadataPath = DiskSnapshot -> FsPath
snapshotToMetadataPath DiskSnapshot
ds
  HasFS m h -> FsPath -> OpenMode -> (Handle h -> m ()) -> m ()
forall (m :: * -> *) h a.
(HasCallStack, MonadThrow m) =>
HasFS m h -> FsPath -> OpenMode -> (Handle h -> m a) -> m a
withFile HasFS m h
hasFS FsPath
metadataPath (AllowExisting -> OpenMode
WriteMode AllowExisting
MustBeNew) ((Handle h -> m ()) -> m ()) -> (Handle h -> m ()) -> m ()
forall a b. (a -> b) -> a -> b
$ \Handle h
h ->
    m Word64 -> m ()
forall (f :: * -> *) a. Functor f => f a -> f ()
Monad.void (m Word64 -> m ()) -> m Word64 -> m ()
forall a b. (a -> b) -> a -> b
$ HasFS m h -> Handle h -> ByteString -> m Word64
forall (m :: * -> *) h.
(HasCallStack, Monad m) =>
HasFS m h -> Handle h -> ByteString -> m Word64
hPutAll HasFS m h
hasFS Handle h
h (ByteString -> m Word64) -> ByteString -> m Word64
forall a b. (a -> b) -> a -> b
$ SnapshotMetadata -> ByteString
forall a. ToJSON a => a -> ByteString
Aeson.encode SnapshotMetadata
meta

-- | Load a snapshot metadata JSON file.
--
--   - Fails with 'MetadataFileDoesNotExist' when the file doesn't exist;
--   - Fails with 'MetadataInvalid' when the contents of the file cannot be
--     deserialised correctly
loadSnapshotMetadata ::
  IOLike m =>
  SomeHasFS m ->
  DiskSnapshot ->
  ExceptT MetadataErr m SnapshotMetadata
loadSnapshotMetadata :: forall (m :: * -> *).
IOLike m =>
SomeHasFS m
-> DiskSnapshot -> ExceptT MetadataErr m SnapshotMetadata
loadSnapshotMetadata (SomeHasFS HasFS m h
hasFS) DiskSnapshot
ds = m (Either MetadataErr SnapshotMetadata)
-> ExceptT MetadataErr m SnapshotMetadata
forall e (m :: * -> *) a. m (Either e a) -> ExceptT e m a
ExceptT (m (Either MetadataErr SnapshotMetadata)
 -> ExceptT MetadataErr m SnapshotMetadata)
-> m (Either MetadataErr SnapshotMetadata)
-> ExceptT MetadataErr m SnapshotMetadata
forall a b. (a -> b) -> a -> b
$ do
  let metadataPath :: FsPath
metadataPath = DiskSnapshot -> FsPath
snapshotToMetadataPath DiskSnapshot
ds
  exists <- HasFS m h -> HasCallStack => FsPath -> m Bool
forall (m :: * -> *) h.
HasFS m h -> HasCallStack => FsPath -> m Bool
doesFileExist HasFS m h
hasFS FsPath
metadataPath
  if not exists
    then pure $ Left MetadataFileDoesNotExist
    else do
      withFile hasFS metadataPath ReadMode $ \Handle h
h -> do
        bs <- HasFS m h -> Handle h -> m ByteString
forall (m :: * -> *) h.
Monad m =>
HasFS m h -> Handle h -> m ByteString
hGetAll HasFS m h
hasFS Handle h
h
        case Aeson.eitherDecode bs of
          Left String
decodeErr -> Either MetadataErr SnapshotMetadata
-> m (Either MetadataErr SnapshotMetadata)
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Either MetadataErr SnapshotMetadata
 -> m (Either MetadataErr SnapshotMetadata))
-> Either MetadataErr SnapshotMetadata
-> m (Either MetadataErr SnapshotMetadata)
forall a b. (a -> b) -> a -> b
$ MetadataErr -> Either MetadataErr SnapshotMetadata
forall a b. a -> Either a b
Left (MetadataErr -> Either MetadataErr SnapshotMetadata)
-> MetadataErr -> Either MetadataErr SnapshotMetadata
forall a b. (a -> b) -> a -> b
$ String -> MetadataErr
MetadataInvalid String
decodeErr
          Right SnapshotMetadata
meta -> Either MetadataErr SnapshotMetadata
-> m (Either MetadataErr SnapshotMetadata)
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Either MetadataErr SnapshotMetadata
 -> m (Either MetadataErr SnapshotMetadata))
-> Either MetadataErr SnapshotMetadata
-> m (Either MetadataErr SnapshotMetadata)
forall a b. (a -> b) -> a -> b
$ SnapshotMetadata -> Either MetadataErr SnapshotMetadata
forall a b. b -> Either a b
Right SnapshotMetadata
meta

snapshotsMapM_ :: Monad m => SnapshotManager m n blk st -> (DiskSnapshot -> m a) -> m ()
snapshotsMapM_ :: forall (m :: * -> *) (n :: * -> *) blk st a.
Monad m =>
SnapshotManager m n blk st -> (DiskSnapshot -> m a) -> m ()
snapshotsMapM_ SnapshotManager m n blk st
snapManager DiskSnapshot -> m a
f =
  (DiskSnapshot -> m a) -> [DiskSnapshot] -> m ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ DiskSnapshot -> m a
f ([DiskSnapshot] -> m ()) -> m [DiskSnapshot] -> m ()
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< SnapshotManager m n blk st -> m [DiskSnapshot]
forall (m :: * -> *) (n :: * -> *) blk st.
SnapshotManager m n blk st -> m [DiskSnapshot]
listSnapshots SnapshotManager m n blk st
snapManager

-- | Testing only! Destroy all snapshots in the DB.
destroySnapshots :: Monad m => SnapshotManager m n blk st -> m ()
destroySnapshots :: forall (m :: * -> *) (n :: * -> *) blk st.
Monad m =>
SnapshotManager m n blk st -> m ()
destroySnapshots SnapshotManager m n blk st
snapManager =
  SnapshotManager m n blk st -> (DiskSnapshot -> m ()) -> m ()
forall (m :: * -> *) (n :: * -> *) blk st a.
Monad m =>
SnapshotManager m n blk st -> (DiskSnapshot -> m a) -> m ()
snapshotsMapM_
    SnapshotManager m n blk st
snapManager
    (SnapshotManager m n blk st -> DiskSnapshot -> m ()
forall (m :: * -> *) (n :: * -> *) blk st.
SnapshotManager m n blk st -> DiskSnapshot -> m ()
deleteSnapshotIfTemporary SnapshotManager m n blk st
snapManager)

-- | Read an extended ledger state from disk
readExtLedgerState ::
  forall m blk.
  IOLike m =>
  SomeHasFS m ->
  (forall s. Decoder s (ExtLedgerState blk EmptyMK)) ->
  (forall s. Decoder s (HeaderHash blk)) ->
  FsPath ->
  ExceptT ReadIncrementalErr m (ExtLedgerState blk EmptyMK, CRC)
readExtLedgerState :: forall (m :: * -> *) blk.
IOLike m =>
SomeHasFS m
-> (forall s. Decoder s (ExtLedgerState blk EmptyMK))
-> (forall s. Decoder s (HeaderHash blk))
-> FsPath
-> ExceptT ReadIncrementalErr m (ExtLedgerState blk EmptyMK, CRC)
readExtLedgerState SomeHasFS m
hasFS forall s. Decoder s (ExtLedgerState blk EmptyMK)
decLedger forall s. Decoder s (HeaderHash blk)
decHash =
  do
    ExceptT
    (m (Either ReadIncrementalErr (ExtLedgerState blk EmptyMK, CRC))
 -> ExceptT ReadIncrementalErr m (ExtLedgerState blk EmptyMK, CRC))
-> (FsPath
    -> m (Either ReadIncrementalErr (ExtLedgerState blk EmptyMK, CRC)))
-> FsPath
-> ExceptT ReadIncrementalErr m (ExtLedgerState blk EmptyMK, CRC)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Either
   ReadIncrementalErr (ExtLedgerState blk EmptyMK, Identity CRC)
 -> Either ReadIncrementalErr (ExtLedgerState blk EmptyMK, CRC))
-> m (Either
        ReadIncrementalErr (ExtLedgerState blk EmptyMK, Identity CRC))
-> m (Either ReadIncrementalErr (ExtLedgerState blk EmptyMK, CRC))
forall a b. (a -> b) -> m a -> m b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (((ExtLedgerState blk EmptyMK, Identity CRC)
 -> (ExtLedgerState blk EmptyMK, CRC))
-> Either
     ReadIncrementalErr (ExtLedgerState blk EmptyMK, Identity CRC)
-> Either ReadIncrementalErr (ExtLedgerState blk EmptyMK, CRC)
forall a b.
(a -> b)
-> Either ReadIncrementalErr a -> Either ReadIncrementalErr b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((Identity CRC -> CRC)
-> (ExtLedgerState blk EmptyMK, Identity CRC)
-> (ExtLedgerState blk EmptyMK, CRC)
forall a b.
(a -> b)
-> (ExtLedgerState blk EmptyMK, a)
-> (ExtLedgerState blk EmptyMK, b)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Identity CRC -> CRC
forall a. Identity a -> a
runIdentity))
    (m (Either
      ReadIncrementalErr (ExtLedgerState blk EmptyMK, Identity CRC))
 -> m (Either ReadIncrementalErr (ExtLedgerState blk EmptyMK, CRC)))
-> (FsPath
    -> m (Either
            ReadIncrementalErr (ExtLedgerState blk EmptyMK, Identity CRC)))
-> FsPath
-> m (Either ReadIncrementalErr (ExtLedgerState blk EmptyMK, CRC))
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SomeHasFS m
-> (CRC -> Identity CRC)
-> Decoder (PrimState m) (ExtLedgerState blk EmptyMK)
-> FsPath
-> m (Either
        ReadIncrementalErr (ExtLedgerState blk EmptyMK, Identity CRC))
forall (m :: * -> *) (f :: * -> *) a.
(IOLike m, Functor f) =>
SomeHasFS m
-> (CRC -> f CRC)
-> Decoder (PrimState m) a
-> FsPath
-> m (Either ReadIncrementalErr (a, f CRC))
readIncremental SomeHasFS m
hasFS CRC -> Identity CRC
forall a. a -> Identity a
Identity Decoder (PrimState m) (ExtLedgerState blk EmptyMK)
forall s. Decoder s (ExtLedgerState blk EmptyMK)
decoder
 where
  decoder :: Decoder s (ExtLedgerState blk EmptyMK)
  decoder :: forall s. Decoder s (ExtLedgerState blk EmptyMK)
decoder = Proxy blk
-> (forall s. Decoder s (ExtLedgerState blk EmptyMK))
-> (forall s. Decoder s (HeaderHash blk))
-> forall s. Decoder s (ExtLedgerState blk EmptyMK)
forall l blk.
Proxy blk
-> (forall s. Decoder s l)
-> (forall s. Decoder s (HeaderHash blk))
-> forall s. Decoder s l
decodeLBackwardsCompatible (forall t. Proxy t
forall {k} (t :: k). Proxy t
Proxy @blk) Decoder s (ExtLedgerState blk EmptyMK)
forall s. Decoder s (ExtLedgerState blk EmptyMK)
decLedger Decoder s (HeaderHash blk)
forall s. Decoder s (HeaderHash blk)
decHash

-- | Write an extended ledger state to disk
writeExtLedgerState ::
  forall m blk.
  MonadThrow m =>
  SomeHasFS m ->
  (ExtLedgerState blk EmptyMK -> Encoding) ->
  FsPath ->
  ExtLedgerState blk EmptyMK ->
  m CRC
writeExtLedgerState :: forall (m :: * -> *) blk.
MonadThrow m =>
SomeHasFS m
-> (ExtLedgerState blk EmptyMK -> Encoding)
-> FsPath
-> ExtLedgerState blk EmptyMK
-> m CRC
writeExtLedgerState (SomeHasFS HasFS m h
hasFS) ExtLedgerState blk EmptyMK -> Encoding
encLedger FsPath
path ExtLedgerState blk EmptyMK
cs = do
  HasFS m h -> FsPath -> OpenMode -> (Handle h -> m CRC) -> m CRC
forall (m :: * -> *) h a.
(HasCallStack, MonadThrow m) =>
HasFS m h -> FsPath -> OpenMode -> (Handle h -> m a) -> m a
withFile HasFS m h
hasFS FsPath
path (AllowExisting -> OpenMode
WriteMode AllowExisting
MustBeNew) ((Handle h -> m CRC) -> m CRC) -> (Handle h -> m CRC) -> m CRC
forall a b. (a -> b) -> a -> b
$ \Handle h
h ->
    (Word64, CRC) -> CRC
forall a b. (a, b) -> b
snd ((Word64, CRC) -> CRC) -> m (Word64, CRC) -> m CRC
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> HasFS m h -> Handle h -> ByteString -> m (Word64, CRC)
forall (m :: * -> *) h.
(HasCallStack, Monad m) =>
HasFS m h -> Handle h -> ByteString -> m (Word64, CRC)
hPutAllCRC HasFS m h
hasFS Handle h
h (Encoding -> ByteString
CBOR.toLazyByteString (Encoding -> ByteString) -> Encoding -> ByteString
forall a b. (a -> b) -> a -> b
$ ExtLedgerState blk EmptyMK -> Encoding
encoder ExtLedgerState blk EmptyMK
cs)
 where
  encoder :: ExtLedgerState blk EmptyMK -> Encoding
  encoder :: ExtLedgerState blk EmptyMK -> Encoding
encoder = (ExtLedgerState blk EmptyMK -> Encoding)
-> ExtLedgerState blk EmptyMK -> Encoding
forall l. (l -> Encoding) -> l -> Encoding
encodeL ExtLedgerState blk EmptyMK -> Encoding
encLedger

-- | Trim the number of on disk snapshots so that at most 'onDiskNumSnapshots'
-- snapshots are stored on disk. The oldest snapshots are deleted.
--
-- The deleted snapshots are returned.
trimSnapshots ::
  Monad m =>
  SnapshotManager m n blk st ->
  SnapshotPolicy ->
  m [DiskSnapshot]
trimSnapshots :: forall (m :: * -> *) (n :: * -> *) blk st.
Monad m =>
SnapshotManager m n blk st -> SnapshotPolicy -> m [DiskSnapshot]
trimSnapshots SnapshotManager m n blk st
snapManager SnapshotPolicy{NumOfDiskSnapshots
onDiskNumSnapshots :: SnapshotPolicy -> NumOfDiskSnapshots
onDiskNumSnapshots :: NumOfDiskSnapshots
onDiskNumSnapshots} = do
  -- We only trim temporary snapshots
  ss <- (DiskSnapshot -> Bool) -> [DiskSnapshot] -> [DiskSnapshot]
forall a. (a -> Bool) -> [a] -> [a]
filter DiskSnapshot -> Bool
diskSnapshotIsTemporary ([DiskSnapshot] -> [DiskSnapshot])
-> m [DiskSnapshot] -> m [DiskSnapshot]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> SnapshotManager m n blk st -> m [DiskSnapshot]
forall (m :: * -> *) (n :: * -> *) blk st.
SnapshotManager m n blk st -> m [DiskSnapshot]
listSnapshots SnapshotManager m n blk st
snapManager
  -- The snapshot are most recent first, so we can simply drop from the
  -- front to get the snapshots that are "too" old.
  let ssTooOld = Int -> [DiskSnapshot] -> [DiskSnapshot]
forall a. Int -> [a] -> [a]
drop (Word -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Word -> Int)
-> (NumOfDiskSnapshots -> Word) -> NumOfDiskSnapshots -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. NumOfDiskSnapshots -> Word
getNumOfDiskSnapshots (NumOfDiskSnapshots -> Int) -> NumOfDiskSnapshots -> Int
forall a b. (a -> b) -> a -> b
$ NumOfDiskSnapshots
onDiskNumSnapshots) [DiskSnapshot]
ss
  mapM
    ( \DiskSnapshot
s -> do
        SnapshotManager m n blk st -> DiskSnapshot -> m ()
forall (m :: * -> *) (n :: * -> *) blk st.
SnapshotManager m n blk st -> DiskSnapshot -> m ()
deleteSnapshotIfTemporary SnapshotManager m n blk st
snapManager DiskSnapshot
s
        DiskSnapshot -> m DiskSnapshot
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure DiskSnapshot
s
    )
    ssTooOld

snapshotToDirName :: DiskSnapshot -> String
snapshotToDirName :: DiskSnapshot -> String
snapshotToDirName DiskSnapshot{Word64
dsNumber :: DiskSnapshot -> Word64
dsNumber :: Word64
dsNumber, Maybe String
dsSuffix :: DiskSnapshot -> Maybe String
dsSuffix :: Maybe String
dsSuffix} =
  Word64 -> String
forall a. Show a => a -> String
show Word64
dsNumber String -> ShowS
forall a. Semigroup a => a -> a -> a
<> String
suffix
 where
  suffix :: String
suffix = case Maybe String
dsSuffix of
    Maybe String
Nothing -> String
""
    Just String
s -> String
"_" String -> ShowS
forall a. Semigroup a => a -> a -> a
<> String
s

snapshotToChecksumPath :: DiskSnapshot -> FsPath
snapshotToChecksumPath :: DiskSnapshot -> FsPath
snapshotToChecksumPath = [String] -> FsPath
mkFsPath ([String] -> FsPath)
-> (DiskSnapshot -> [String]) -> DiskSnapshot -> FsPath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (\String
x -> [String
x, String
"checksum"]) (String -> [String])
-> (DiskSnapshot -> String) -> DiskSnapshot -> [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. DiskSnapshot -> String
snapshotToDirName

snapshotToMetadataPath :: DiskSnapshot -> FsPath
snapshotToMetadataPath :: DiskSnapshot -> FsPath
snapshotToMetadataPath = [String] -> FsPath
mkFsPath ([String] -> FsPath)
-> (DiskSnapshot -> [String]) -> DiskSnapshot -> FsPath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (\String
x -> [String
x, String
"meta"]) (String -> [String])
-> (DiskSnapshot -> String) -> DiskSnapshot -> [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. DiskSnapshot -> String
snapshotToDirName

-- | The path within the LedgerDB's filesystem to the snapshot's directory
snapshotToDirPath :: DiskSnapshot -> FsPath
snapshotToDirPath :: DiskSnapshot -> FsPath
snapshotToDirPath = [String] -> FsPath
mkFsPath ([String] -> FsPath)
-> (DiskSnapshot -> [String]) -> DiskSnapshot -> FsPath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (String -> [String] -> [String]
forall a. a -> [a] -> [a]
: []) (String -> [String])
-> (DiskSnapshot -> String) -> DiskSnapshot -> [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. DiskSnapshot -> String
snapshotToDirName

-- | The path within the LedgerDB's filesystem to the file that contains the
-- snapshot's serialized ledger state
snapshotToStatePath :: DiskSnapshot -> FsPath
snapshotToStatePath :: DiskSnapshot -> FsPath
snapshotToStatePath = [String] -> FsPath
mkFsPath ([String] -> FsPath)
-> (DiskSnapshot -> [String]) -> DiskSnapshot -> FsPath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (\String
x -> [String
x, String
"state"]) (String -> [String])
-> (DiskSnapshot -> String) -> DiskSnapshot -> [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. DiskSnapshot -> String
snapshotToDirName

-- | Version 1: uses versioning ('Ouroboros.Consensus.Util.Versioned') and only
-- encodes the ledger state @l@.
snapshotEncodingVersion1 :: VersionNumber
snapshotEncodingVersion1 :: VersionNumber
snapshotEncodingVersion1 = VersionNumber
1

-- | Encoder to be used in combination with 'decodeSnapshotBackwardsCompatible'.
encodeL :: (l -> Encoding) -> l -> Encoding
encodeL :: forall l. (l -> Encoding) -> l -> Encoding
encodeL l -> Encoding
encodeLedger l
l =
  VersionNumber -> Encoding -> Encoding
encodeVersion VersionNumber
snapshotEncodingVersion1 (l -> Encoding
encodeLedger l
l)

-- | To remain backwards compatible with existing snapshots stored on disk, we
-- must accept the old format as well as the new format.
--
-- The old format:
--
-- * The tip: @WithOrigin (RealPoint blk)@
--
-- * The chain length: @Word64@
--
-- * The ledger state: @l@
--
-- The new format is described by 'snapshotEncodingVersion1'.
--
-- This decoder will accept and ignore them. The encoder ('encodeSnapshot') will
-- no longer encode them.
decodeLBackwardsCompatible ::
  forall l blk.
  Proxy blk ->
  (forall s. Decoder s l) ->
  (forall s. Decoder s (HeaderHash blk)) ->
  forall s.
  Decoder s l
decodeLBackwardsCompatible :: forall l blk.
Proxy blk
-> (forall s. Decoder s l)
-> (forall s. Decoder s (HeaderHash blk))
-> forall s. Decoder s l
decodeLBackwardsCompatible Proxy blk
_ forall s. Decoder s l
decodeLedger forall s. Decoder s (HeaderHash blk)
decodeHash =
  (forall s. Maybe Int -> Decoder s l)
-> [(VersionNumber, VersionDecoder l)] -> forall s. Decoder s l
forall a.
(forall s. Maybe Int -> Decoder s a)
-> [(VersionNumber, VersionDecoder a)] -> forall s. Decoder s a
decodeVersionWithHook
    Maybe Int -> Decoder s l
Maybe Int -> forall s. Decoder s l
forall s. Maybe Int -> Decoder s l
decodeOldFormat
    [(VersionNumber
snapshotEncodingVersion1, (forall s. Decoder s l) -> VersionDecoder l
forall a. (forall s. Decoder s a) -> VersionDecoder a
Decode Decoder s l
forall s. Decoder s l
decodeVersion1)]
 where
  decodeVersion1 :: forall s. Decoder s l
  decodeVersion1 :: forall s. Decoder s l
decodeVersion1 = Decoder s l
forall s. Decoder s l
decodeLedger

  decodeOldFormat :: Maybe Int -> forall s. Decoder s l
  decodeOldFormat :: Maybe Int -> forall s. Decoder s l
decodeOldFormat (Just Int
3) = do
    _ <-
      WithOrigin (RealPoint blk) -> Point blk
forall blk. WithOrigin (RealPoint blk) -> Point blk
withOriginRealPointToPoint
        (WithOrigin (RealPoint blk) -> Point blk)
-> Decoder s (WithOrigin (RealPoint blk)) -> Decoder s (Point blk)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Decoder s (RealPoint blk) -> Decoder s (WithOrigin (RealPoint blk))
forall s a. Decoder s a -> Decoder s (WithOrigin a)
decodeWithOrigin (forall blk.
(forall s. Decoder s (HeaderHash blk))
-> forall s. Decoder s (RealPoint blk)
decodeRealPoint @blk Decoder s (HeaderHash blk)
forall s. Decoder s (HeaderHash blk)
decodeHash)
    _ <- Dec.decodeWord64
    decodeLedger
  decodeOldFormat Maybe Int
mbListLen =
    String -> Decoder s l
forall a. String -> Decoder s a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail (String -> Decoder s l) -> String -> Decoder s l
forall a b. (a -> b) -> a -> b
$
      String
"decodeSnapshotBackwardsCompatible: invalid start "
        String -> ShowS
forall a. Semigroup a => a -> a -> a
<> Maybe Int -> String
forall a. Show a => a -> String
show Maybe Int
mbListLen

{-------------------------------------------------------------------------------
  Policy
-------------------------------------------------------------------------------}

-- | Number of snapshots to be stored on disk. See the
-- @'SnapshotPolicy'@ documentation for more information.
newtype NumOfDiskSnapshots
  = NumOfDiskSnapshots {NumOfDiskSnapshots -> Word
getNumOfDiskSnapshots :: Word}
  deriving stock (NumOfDiskSnapshots -> NumOfDiskSnapshots -> Bool
(NumOfDiskSnapshots -> NumOfDiskSnapshots -> Bool)
-> (NumOfDiskSnapshots -> NumOfDiskSnapshots -> Bool)
-> Eq NumOfDiskSnapshots
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: NumOfDiskSnapshots -> NumOfDiskSnapshots -> Bool
== :: NumOfDiskSnapshots -> NumOfDiskSnapshots -> Bool
$c/= :: NumOfDiskSnapshots -> NumOfDiskSnapshots -> Bool
/= :: NumOfDiskSnapshots -> NumOfDiskSnapshots -> Bool
Eq, (forall x. NumOfDiskSnapshots -> Rep NumOfDiskSnapshots x)
-> (forall x. Rep NumOfDiskSnapshots x -> NumOfDiskSnapshots)
-> Generic NumOfDiskSnapshots
forall x. Rep NumOfDiskSnapshots x -> NumOfDiskSnapshots
forall x. NumOfDiskSnapshots -> Rep NumOfDiskSnapshots x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. NumOfDiskSnapshots -> Rep NumOfDiskSnapshots x
from :: forall x. NumOfDiskSnapshots -> Rep NumOfDiskSnapshots x
$cto :: forall x. Rep NumOfDiskSnapshots x -> NumOfDiskSnapshots
to :: forall x. Rep NumOfDiskSnapshots x -> NumOfDiskSnapshots
Generic, Int -> NumOfDiskSnapshots -> ShowS
[NumOfDiskSnapshots] -> ShowS
NumOfDiskSnapshots -> String
(Int -> NumOfDiskSnapshots -> ShowS)
-> (NumOfDiskSnapshots -> String)
-> ([NumOfDiskSnapshots] -> ShowS)
-> Show NumOfDiskSnapshots
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> NumOfDiskSnapshots -> ShowS
showsPrec :: Int -> NumOfDiskSnapshots -> ShowS
$cshow :: NumOfDiskSnapshots -> String
show :: NumOfDiskSnapshots -> String
$cshowList :: [NumOfDiskSnapshots] -> ShowS
showList :: [NumOfDiskSnapshots] -> ShowS
Show)

-- | Type-safe flag to regulate the checksum policy of the ledger state snapshots.
--
-- These patterns are exposed to cardano-node and will be passed as part of @'SnapshotPolicy'@.
pattern DoDiskSnapshotChecksum, NoDoDiskSnapshotChecksum :: Flag "DoDiskSnapshotChecksum"
pattern $bDoDiskSnapshotChecksum :: Flag "DoDiskSnapshotChecksum"
$mDoDiskSnapshotChecksum :: forall {r}.
Flag "DoDiskSnapshotChecksum" -> ((# #) -> r) -> ((# #) -> r) -> r
DoDiskSnapshotChecksum = Flag True
pattern $bNoDoDiskSnapshotChecksum :: Flag "DoDiskSnapshotChecksum"
$mNoDoDiskSnapshotChecksum :: forall {r}.
Flag "DoDiskSnapshotChecksum" -> ((# #) -> r) -> ((# #) -> r) -> r
NoDoDiskSnapshotChecksum = Flag False

-- | Snapshots policy
--
-- We only write ledger states that are older than @k@ blocks to disk (that is,
-- snapshots that are guaranteed valid). The on-disk policy determines how often
-- we write to disk and how many checkpoints we keep.
data SnapshotPolicy = SnapshotPolicy
  { SnapshotPolicy -> NumOfDiskSnapshots
onDiskNumSnapshots :: NumOfDiskSnapshots
  -- ^ How many snapshots do we want to keep on disk?
  --
  -- A higher number of on-disk snapshots is primarily a safe-guard against
  -- disk corruption: it trades disk space for reliability.
  --
  -- Examples:
  --
  -- * @0@: Delete the snapshot immediately after writing.
  --        Probably not a useful value :-D
  -- * @1@: Delete the previous snapshot immediately after writing the next
  --        Dangerous policy: if for some reason the deletion happens before
  --        the new snapshot is written entirely to disk (we don't @fsync@),
  --        we have no choice but to start at the genesis snapshot on the
  --        next startup.
  -- * @2@: Always keep 2 snapshots around. This means that when we write
  --        the next snapshot, we delete the oldest one, leaving the middle
  --        one available in case of truncation of the write. This is
  --        probably a sane value in most circumstances.
  , SnapshotPolicy -> SnapshotSelectorContext -> [SlotNo]
onDiskSnapshotSelector :: SnapshotSelectorContext -> [SlotNo]
  -- ^ Select the slots to take a snapshot for, in increasing order. Must be a
  -- sublist of 'sscSnapshotSlots'.
  --
  -- See also 'defaultSnapshotPolicy'
  , SnapshotPolicy -> SnapshotDelayRange
onDiskSnapshotDelayRange :: SnapshotDelayRange
  -- ^ Minimum and maximum durations of the random delay between requesting
  -- a snapshot and taking that snapshot.
  }
  deriving [String] -> SnapshotPolicy -> IO (Maybe ThunkInfo)
Proxy SnapshotPolicy -> String
([String] -> SnapshotPolicy -> IO (Maybe ThunkInfo))
-> ([String] -> SnapshotPolicy -> IO (Maybe ThunkInfo))
-> (Proxy SnapshotPolicy -> String)
-> NoThunks SnapshotPolicy
forall a.
([String] -> a -> IO (Maybe ThunkInfo))
-> ([String] -> a -> IO (Maybe ThunkInfo))
-> (Proxy a -> String)
-> NoThunks a
$cnoThunks :: [String] -> SnapshotPolicy -> IO (Maybe ThunkInfo)
noThunks :: [String] -> SnapshotPolicy -> IO (Maybe ThunkInfo)
$cwNoThunks :: [String] -> SnapshotPolicy -> IO (Maybe ThunkInfo)
wNoThunks :: [String] -> SnapshotPolicy -> IO (Maybe ThunkInfo)
$cshowTypeOf :: Proxy SnapshotPolicy -> String
showTypeOf :: Proxy SnapshotPolicy -> String
NoThunks via OnlyCheckWhnf SnapshotPolicy

-- | Range from which the randomised snapshot delay will be taken. The randomly
-- chosen duration will be at least 'minimumDelay' and at most 'maximumDelay'.
data SnapshotDelayRange = SnapshotDelayRange
  { SnapshotDelayRange -> DiffTime
minimumDelay :: !DiffTime
  -- ^ minimum acceptable delay between requesting a snapshot and taking the
  -- snapshot
  , SnapshotDelayRange -> DiffTime
maximumDelay :: !DiffTime
  -- ^ maximum acceptable delay between requesting a snapshot and taking the
  -- snapshot
  }
  deriving (Int -> SnapshotDelayRange -> ShowS
[SnapshotDelayRange] -> ShowS
SnapshotDelayRange -> String
(Int -> SnapshotDelayRange -> ShowS)
-> (SnapshotDelayRange -> String)
-> ([SnapshotDelayRange] -> ShowS)
-> Show SnapshotDelayRange
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> SnapshotDelayRange -> ShowS
showsPrec :: Int -> SnapshotDelayRange -> ShowS
$cshow :: SnapshotDelayRange -> String
show :: SnapshotDelayRange -> String
$cshowList :: [SnapshotDelayRange] -> ShowS
showList :: [SnapshotDelayRange] -> ShowS
Show, SnapshotDelayRange -> SnapshotDelayRange -> Bool
(SnapshotDelayRange -> SnapshotDelayRange -> Bool)
-> (SnapshotDelayRange -> SnapshotDelayRange -> Bool)
-> Eq SnapshotDelayRange
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: SnapshotDelayRange -> SnapshotDelayRange -> Bool
== :: SnapshotDelayRange -> SnapshotDelayRange -> Bool
$c/= :: SnapshotDelayRange -> SnapshotDelayRange -> Bool
/= :: SnapshotDelayRange -> SnapshotDelayRange -> Bool
Eq, (forall x. SnapshotDelayRange -> Rep SnapshotDelayRange x)
-> (forall x. Rep SnapshotDelayRange x -> SnapshotDelayRange)
-> Generic SnapshotDelayRange
forall x. Rep SnapshotDelayRange x -> SnapshotDelayRange
forall x. SnapshotDelayRange -> Rep SnapshotDelayRange x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. SnapshotDelayRange -> Rep SnapshotDelayRange x
from :: forall x. SnapshotDelayRange -> Rep SnapshotDelayRange x
$cto :: forall x. Rep SnapshotDelayRange x -> SnapshotDelayRange
to :: forall x. Rep SnapshotDelayRange x -> SnapshotDelayRange
Generic, [String] -> SnapshotDelayRange -> IO (Maybe ThunkInfo)
Proxy SnapshotDelayRange -> String
([String] -> SnapshotDelayRange -> IO (Maybe ThunkInfo))
-> ([String] -> SnapshotDelayRange -> IO (Maybe ThunkInfo))
-> (Proxy SnapshotDelayRange -> String)
-> NoThunks SnapshotDelayRange
forall a.
([String] -> a -> IO (Maybe ThunkInfo))
-> ([String] -> a -> IO (Maybe ThunkInfo))
-> (Proxy a -> String)
-> NoThunks a
$cnoThunks :: [String] -> SnapshotDelayRange -> IO (Maybe ThunkInfo)
noThunks :: [String] -> SnapshotDelayRange -> IO (Maybe ThunkInfo)
$cwNoThunks :: [String] -> SnapshotDelayRange -> IO (Maybe ThunkInfo)
wNoThunks :: [String] -> SnapshotDelayRange -> IO (Maybe ThunkInfo)
$cshowTypeOf :: Proxy SnapshotDelayRange -> String
showTypeOf :: Proxy SnapshotDelayRange -> String
NoThunks)

data SnapshotSelectorContext = SnapshotSelectorContext
  { SnapshotSelectorContext -> Maybe DiffTime
sscTimeSinceLast :: Maybe DiffTime
  -- ^ The time since the last snapshot, or 'Nothing' if none was taken yet.
  -- Note that 'Nothing' merely means no snapshot had been taking yet since the
  -- node was started; it does not necessarily mean that none exist on disk.
  , SnapshotSelectorContext -> [SlotNo]
sscSnapshotSlots :: [SlotNo]
  -- ^ An increasing list of slots for which a snapshot can be taken (as the
  -- corresponding ledger state is immutable). The result of
  -- 'onDiskSnapshotSelector' must be a subset of this list.
  }
  deriving stock Int -> SnapshotSelectorContext -> ShowS
[SnapshotSelectorContext] -> ShowS
SnapshotSelectorContext -> String
(Int -> SnapshotSelectorContext -> ShowS)
-> (SnapshotSelectorContext -> String)
-> ([SnapshotSelectorContext] -> ShowS)
-> Show SnapshotSelectorContext
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> SnapshotSelectorContext -> ShowS
showsPrec :: Int -> SnapshotSelectorContext -> ShowS
$cshow :: SnapshotSelectorContext -> String
show :: SnapshotSelectorContext -> String
$cshowList :: [SnapshotSelectorContext] -> ShowS
showList :: [SnapshotSelectorContext] -> ShowS
Show

-- | Determines when/how often we take ledger snapshots.
--
-- We only write snapshots for ledger states that are /immutable/. Concretely,
-- for every slot @s@ out of
--
-- > sfaOffset, sfaOffset + sfaInterval, sfaOffset + 2 * sfaInterval, sfaOffset + 3 * sfaInterval, ...
--
-- we write a snapshot for the most recent immutable ledger state before @s@.
-- This way, nodes with the same @sfaInterval@/@sfaOffset@ configuration create
-- snapshots for precisely the same slots.
--
-- For example, on Cardano mainnet, where @k=2160@ and @f=1/20@, setting
-- @sfaInterval = 10*k/f = 432000@ (one epoch) and @sfaOffset = 0@ will cause
-- the node to create snapshots for the last block in every Shelley epoch. By
-- setting @sfaOffset@ to eg @5*k/f@ (half an epoch), snapshots are created just
-- before the midway point in each epoch.
--
-- Additionally, there is an (optional, opt-out) rate limit (useful while
-- bulk-syncing). When set to a given duration, we will skip writing a snapshot
-- if less time than the given duration has passed since we finished writing the
-- previous snapshot (if any).
--
-- To avoid skipping a snapshot write when caught-up, it is advisable to set
-- 'sfaRateLimit' to something significantly smaller than the wall-clock duration
-- of 'sfaInterval'.
data SnapshotFrequencyArgs = SnapshotFrequencyArgs
  { SnapshotFrequencyArgs -> OverrideOrDefault (NonZero Word64)
sfaInterval :: OverrideOrDefault (NonZero Word64)
  -- ^ Try to write snapshots every 'sfaInterval' many slots.
  , SnapshotFrequencyArgs -> OverrideOrDefault SlotNo
sfaOffset :: OverrideOrDefault SlotNo
  -- ^ An offset for when to write snapshots, see 'SnapshotFrequency'.
  , SnapshotFrequencyArgs -> OverrideOrDefault DiffTime
sfaRateLimit :: OverrideOrDefault DiffTime
  -- ^ Ensure (if present) that at least this amount of time passes between
  -- writing snapshots. Setting this to a non-positive value disable the rate
  -- limit.
  , SnapshotFrequencyArgs -> OverrideOrDefault SnapshotDelayRange
sfaDelaySnapshotRange :: OverrideOrDefault SnapshotDelayRange
  }
  deriving stock (Int -> SnapshotFrequencyArgs -> ShowS
[SnapshotFrequencyArgs] -> ShowS
SnapshotFrequencyArgs -> String
(Int -> SnapshotFrequencyArgs -> ShowS)
-> (SnapshotFrequencyArgs -> String)
-> ([SnapshotFrequencyArgs] -> ShowS)
-> Show SnapshotFrequencyArgs
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> SnapshotFrequencyArgs -> ShowS
showsPrec :: Int -> SnapshotFrequencyArgs -> ShowS
$cshow :: SnapshotFrequencyArgs -> String
show :: SnapshotFrequencyArgs -> String
$cshowList :: [SnapshotFrequencyArgs] -> ShowS
showList :: [SnapshotFrequencyArgs] -> ShowS
Show, SnapshotFrequencyArgs -> SnapshotFrequencyArgs -> Bool
(SnapshotFrequencyArgs -> SnapshotFrequencyArgs -> Bool)
-> (SnapshotFrequencyArgs -> SnapshotFrequencyArgs -> Bool)
-> Eq SnapshotFrequencyArgs
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: SnapshotFrequencyArgs -> SnapshotFrequencyArgs -> Bool
== :: SnapshotFrequencyArgs -> SnapshotFrequencyArgs -> Bool
$c/= :: SnapshotFrequencyArgs -> SnapshotFrequencyArgs -> Bool
/= :: SnapshotFrequencyArgs -> SnapshotFrequencyArgs -> Bool
Eq)

data SnapshotFrequency
  = SnapshotFrequency SnapshotFrequencyArgs
  | DisableSnapshots
  deriving stock (Int -> SnapshotFrequency -> ShowS
[SnapshotFrequency] -> ShowS
SnapshotFrequency -> String
(Int -> SnapshotFrequency -> ShowS)
-> (SnapshotFrequency -> String)
-> ([SnapshotFrequency] -> ShowS)
-> Show SnapshotFrequency
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> SnapshotFrequency -> ShowS
showsPrec :: Int -> SnapshotFrequency -> ShowS
$cshow :: SnapshotFrequency -> String
show :: SnapshotFrequency -> String
$cshowList :: [SnapshotFrequency] -> ShowS
showList :: [SnapshotFrequency] -> ShowS
Show, SnapshotFrequency -> SnapshotFrequency -> Bool
(SnapshotFrequency -> SnapshotFrequency -> Bool)
-> (SnapshotFrequency -> SnapshotFrequency -> Bool)
-> Eq SnapshotFrequency
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: SnapshotFrequency -> SnapshotFrequency -> Bool
== :: SnapshotFrequency -> SnapshotFrequency -> Bool
$c/= :: SnapshotFrequency -> SnapshotFrequency -> Bool
/= :: SnapshotFrequency -> SnapshotFrequency -> Bool
Eq)

data SnapshotPolicyArgs = SnapshotPolicyArgs
  { SnapshotPolicyArgs -> SnapshotFrequency
spaFrequency :: SnapshotFrequency
  , SnapshotPolicyArgs -> OverrideOrDefault NumOfDiskSnapshots
spaNum :: OverrideOrDefault NumOfDiskSnapshots
  -- ^ See 'onDiskNumSnapshots'.
  }
  deriving stock (Int -> SnapshotPolicyArgs -> ShowS
[SnapshotPolicyArgs] -> ShowS
SnapshotPolicyArgs -> String
(Int -> SnapshotPolicyArgs -> ShowS)
-> (SnapshotPolicyArgs -> String)
-> ([SnapshotPolicyArgs] -> ShowS)
-> Show SnapshotPolicyArgs
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> SnapshotPolicyArgs -> ShowS
showsPrec :: Int -> SnapshotPolicyArgs -> ShowS
$cshow :: SnapshotPolicyArgs -> String
show :: SnapshotPolicyArgs -> String
$cshowList :: [SnapshotPolicyArgs] -> ShowS
showList :: [SnapshotPolicyArgs] -> ShowS
Show, SnapshotPolicyArgs -> SnapshotPolicyArgs -> Bool
(SnapshotPolicyArgs -> SnapshotPolicyArgs -> Bool)
-> (SnapshotPolicyArgs -> SnapshotPolicyArgs -> Bool)
-> Eq SnapshotPolicyArgs
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: SnapshotPolicyArgs -> SnapshotPolicyArgs -> Bool
== :: SnapshotPolicyArgs -> SnapshotPolicyArgs -> Bool
$c/= :: SnapshotPolicyArgs -> SnapshotPolicyArgs -> Bool
/= :: SnapshotPolicyArgs -> SnapshotPolicyArgs -> Bool
Eq)

defaultSnapshotPolicyArgs :: SnapshotPolicyArgs
defaultSnapshotPolicyArgs :: SnapshotPolicyArgs
defaultSnapshotPolicyArgs =
  SnapshotFrequency
-> OverrideOrDefault NumOfDiskSnapshots -> SnapshotPolicyArgs
SnapshotPolicyArgs
    (SnapshotFrequencyArgs -> SnapshotFrequency
SnapshotFrequency (SnapshotFrequencyArgs -> SnapshotFrequency)
-> SnapshotFrequencyArgs -> SnapshotFrequency
forall a b. (a -> b) -> a -> b
$ OverrideOrDefault (NonZero Word64)
-> OverrideOrDefault SlotNo
-> OverrideOrDefault DiffTime
-> OverrideOrDefault SnapshotDelayRange
-> SnapshotFrequencyArgs
SnapshotFrequencyArgs OverrideOrDefault (NonZero Word64)
forall a. OverrideOrDefault a
UseDefault OverrideOrDefault SlotNo
forall a. OverrideOrDefault a
UseDefault OverrideOrDefault DiffTime
forall a. OverrideOrDefault a
UseDefault OverrideOrDefault SnapshotDelayRange
forall a. OverrideOrDefault a
UseDefault)
    OverrideOrDefault NumOfDiskSnapshots
forall a. OverrideOrDefault a
UseDefault

-- | Default on-disk policy suitable to use with cardano-node
defaultSnapshotPolicy ::
  SecurityParam ->
  SnapshotPolicyArgs ->
  SnapshotPolicy
defaultSnapshotPolicy :: SecurityParam -> SnapshotPolicyArgs -> SnapshotPolicy
defaultSnapshotPolicy (SecurityParam NonZero Word64
k) SnapshotPolicyArgs
args =
  SnapshotPolicy
    { NumOfDiskSnapshots
onDiskNumSnapshots :: NumOfDiskSnapshots
onDiskNumSnapshots :: NumOfDiskSnapshots
onDiskNumSnapshots
    , SnapshotSelectorContext -> [SlotNo]
onDiskSnapshotSelector :: SnapshotSelectorContext -> [SlotNo]
onDiskSnapshotSelector :: SnapshotSelectorContext -> [SlotNo]
onDiskSnapshotSelector
    , SnapshotDelayRange
onDiskSnapshotDelayRange :: SnapshotDelayRange
onDiskSnapshotDelayRange :: SnapshotDelayRange
onDiskSnapshotDelayRange
    }
 where
  SnapshotPolicyArgs
    { SnapshotFrequency
spaFrequency :: SnapshotPolicyArgs -> SnapshotFrequency
spaFrequency :: SnapshotFrequency
spaFrequency
    , spaNum :: SnapshotPolicyArgs -> OverrideOrDefault NumOfDiskSnapshots
spaNum = NumOfDiskSnapshots
-> OverrideOrDefault NumOfDiskSnapshots -> NumOfDiskSnapshots
forall a. a -> OverrideOrDefault a -> a
provideDefault (Word -> NumOfDiskSnapshots
NumOfDiskSnapshots Word
2) -> NumOfDiskSnapshots
onDiskNumSnapshots
    } = SnapshotPolicyArgs
args

  onDiskSnapshotSelector :: SnapshotSelectorContext -> [SlotNo]
  onDiskSnapshotSelector :: SnapshotSelectorContext -> [SlotNo]
onDiskSnapshotSelector SnapshotSelectorContext
ctx
    | Just DiffTime
timeSinceLast <- SnapshotSelectorContext -> Maybe DiffTime
sscTimeSinceLast SnapshotSelectorContext
ctx
    , Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ DiffTime -> Bool
passesRateLimitCheck DiffTime
timeSinceLast =
        []
    | Bool
otherwise = case SnapshotFrequency
spaFrequency of
        SnapshotFrequency
DisableSnapshots -> []
        SnapshotFrequency
          SnapshotFrequencyArgs
            { sfaInterval :: SnapshotFrequencyArgs -> OverrideOrDefault (NonZero Word64)
sfaInterval = NonZero Word64 -> Word64
forall a. NonZero a -> a
unNonZero (NonZero Word64 -> Word64)
-> (OverrideOrDefault (NonZero Word64) -> NonZero Word64)
-> OverrideOrDefault (NonZero Word64)
-> Word64
forall b c a. (b -> c) -> (a -> b) -> a -> c
. NonZero Word64
-> OverrideOrDefault (NonZero Word64) -> NonZero Word64
forall a. a -> OverrideOrDefault a -> a
provideDefault NonZero Word64
defInterval -> Word64
interval
            , sfaOffset :: SnapshotFrequencyArgs -> OverrideOrDefault SlotNo
sfaOffset = SlotNo -> OverrideOrDefault SlotNo -> SlotNo
forall a. a -> OverrideOrDefault a -> a
provideDefault SlotNo
0 -> SlotNo
offset
            , sfaRateLimit :: SnapshotFrequencyArgs -> OverrideOrDefault DiffTime
sfaRateLimit = DiffTime -> OverrideOrDefault DiffTime -> DiffTime
forall a. a -> OverrideOrDefault a -> a
provideDefault DiffTime
defRateLimit -> DiffTime
rateLimit
            } ->
            [SlotNo] -> [SlotNo]
applyRateLimit ([SlotNo] -> [SlotNo]) -> [SlotNo] -> [SlotNo]
forall a b. (a -> b) -> a -> b
$
              [Maybe SlotNo] -> [SlotNo]
forall a. [Maybe a] -> [a]
catMaybes ([Maybe SlotNo] -> [SlotNo]) -> [Maybe SlotNo] -> [SlotNo]
forall a b. (a -> b) -> a -> b
$
                (SlotNo -> SlotNo -> Maybe SlotNo)
-> [SlotNo] -> [SlotNo] -> [Maybe SlotNo]
forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith
                  SlotNo -> SlotNo -> Maybe SlotNo
shouldTakeSnapshot
                  (SnapshotSelectorContext -> [SlotNo]
sscSnapshotSlots SnapshotSelectorContext
ctx)
                  (Int -> [SlotNo] -> [SlotNo]
forall a. Int -> [a] -> [a]
drop Int
1 (SnapshotSelectorContext -> [SlotNo]
sscSnapshotSlots SnapshotSelectorContext
ctx))
           where
            -- Test whether there is a non-negative integer @n@ such that
            --
            -- > candidateSlot < offset + n * interval <= nextSlot
            --
            -- If so, return @'Just' 'candidateSlot'@ for snapshotting.
            shouldTakeSnapshot ::
              SlotNo -> -- The slot to potentially take a snapshot for.
              SlotNo -> -- The next slot in 'sscSnapshotSlots'.
              Maybe SlotNo
            shouldTakeSnapshot :: SlotNo -> SlotNo -> Maybe SlotNo
shouldTakeSnapshot SlotNo
candidateSlot SlotNo
nextSlot
              | SlotNo
nextSlot SlotNo -> SlotNo -> Bool
forall a. Ord a => a -> a -> Bool
< SlotNo
offset = Maybe SlotNo
forall a. Maybe a
Nothing
              | SlotNo
candidateSlot SlotNo -> SlotNo -> Bool
forall a. Ord a => a -> a -> Bool
< SlotNo
offset SlotNo -> SlotNo -> SlotNo
forall a. Num a => a -> a -> a
+ SlotNo
n SlotNo -> SlotNo -> SlotNo
forall a. Num a => a -> a -> a
* Word64 -> SlotNo
SlotNo Word64
interval = SlotNo -> Maybe SlotNo
forall a. a -> Maybe a
Just SlotNo
candidateSlot
              | Bool
otherwise = Maybe SlotNo
forall a. Maybe a
Nothing
             where
              n :: SlotNo
n = Word64 -> SlotNo
SlotNo (Word64 -> SlotNo) -> Word64 -> SlotNo
forall a b. (a -> b) -> a -> b
$ SlotNo -> Word64
unSlotNo (SlotNo
nextSlot SlotNo -> SlotNo -> SlotNo
forall a. Num a => a -> a -> a
- SlotNo
offset) Word64 -> Word64 -> Word64
forall a. Integral a => a -> a -> a
`div` Word64
interval

            -- When rate limiting is enabled, only return at most one (the last)
            -- of the slots satisfying 'shouldTakeSnapshot'.
            applyRateLimit :: [SlotNo] -> [SlotNo]
            applyRateLimit :: [SlotNo] -> [SlotNo]
applyRateLimit
              | DiffTime
rateLimit DiffTime -> DiffTime -> Bool
forall a. Ord a => a -> a -> Bool
> DiffTime
0 = Maybe SlotNo -> [SlotNo]
forall a. Maybe a -> [a]
maybeToList (Maybe SlotNo -> [SlotNo])
-> ([SlotNo] -> Maybe SlotNo) -> [SlotNo] -> [SlotNo]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [SlotNo] -> Maybe SlotNo
forall a. [a] -> Maybe a
lastMaybe
              | Bool
otherwise = [SlotNo] -> [SlotNo]
forall a. a -> a
id

  onDiskSnapshotDelayRange :: SnapshotDelayRange
onDiskSnapshotDelayRange = case SnapshotFrequency
spaFrequency of
    SnapshotFrequency
DisableSnapshots -> DiffTime -> DiffTime -> SnapshotDelayRange
SnapshotDelayRange DiffTime
0 DiffTime
0 -- snapshots are disabled, but we need to provide some value here
    SnapshotFrequency SnapshotFrequencyArgs
sfa -> SnapshotDelayRange
-> OverrideOrDefault SnapshotDelayRange -> SnapshotDelayRange
forall a. a -> OverrideOrDefault a -> a
provideDefault (DiffTime -> DiffTime -> SnapshotDelayRange
SnapshotDelayRange DiffTime
fiveMinutes DiffTime
tenMinutes) (OverrideOrDefault SnapshotDelayRange -> SnapshotDelayRange)
-> OverrideOrDefault SnapshotDelayRange -> SnapshotDelayRange
forall a b. (a -> b) -> a -> b
$ SnapshotFrequencyArgs -> OverrideOrDefault SnapshotDelayRange
sfaDelaySnapshotRange SnapshotFrequencyArgs
sfa

  fiveMinutes :: DiffTime
  fiveMinutes :: DiffTime
fiveMinutes = DiffTime
5 DiffTime -> DiffTime -> DiffTime
forall a. Num a => a -> a -> a
* DiffTime
60

  tenMinutes :: DiffTime
  tenMinutes :: DiffTime
tenMinutes = DiffTime
10 DiffTime -> DiffTime -> DiffTime
forall a. Num a => a -> a -> a
* DiffTime
60

  passesRateLimitCheck :: DiffTime -> Bool
passesRateLimitCheck DiffTime
t = case SnapshotFrequency
spaFrequency of
    SnapshotFrequency SnapshotFrequencyArgs{OverrideOrDefault DiffTime
sfaRateLimit :: SnapshotFrequencyArgs -> OverrideOrDefault DiffTime
sfaRateLimit :: OverrideOrDefault DiffTime
sfaRateLimit} ->
      DiffTime
t DiffTime -> DiffTime -> Bool
forall a. Ord a => a -> a -> Bool
>= DiffTime -> OverrideOrDefault DiffTime -> DiffTime
forall a. a -> OverrideOrDefault a -> a
provideDefault DiffTime
defRateLimit OverrideOrDefault DiffTime
sfaRateLimit
    SnapshotFrequency
DisableSnapshots -> Bool
False

  -- On mainnet, this is 72 min for @k=2160@ and a slot length of 1s.
  defInterval :: NonZero Word64
defInterval = Word64 -> NonZero Word64
forall a. a -> NonZero a
unsafeNonZero (Word64 -> NonZero Word64) -> Word64 -> NonZero Word64
forall a b. (a -> b) -> a -> b
$ NonZero Word64 -> Word64
forall a. NonZero a -> a
unNonZero NonZero Word64
k Word64 -> Word64 -> Word64
forall a. Num a => a -> a -> a
* Word64
2

  -- Most relevant during syncing.
  defRateLimit :: DiffTime
defRateLimit = Integer -> DiffTime
secondsToDiffTime (Integer -> DiffTime) -> Integer -> DiffTime
forall a b. (a -> b) -> a -> b
$ Integer
10 Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* Integer
60

-- | The Cardano mainnet epoch length in slots, used as the reference for the
-- Mithril snapshot compatibility check in 'sanityCheckSnapshotPolicyArgs'.
-- Mithril requires a ledger snapshot at each epoch boundary; for snapshots to
-- land on every epoch boundary, 'sfaInterval' must divide this value evenly.
mithrilEpochSize :: Word64
mithrilEpochSize :: Word64
mithrilEpochSize = Word64
432000

-- | Check a 'SnapshotPolicyArgs' for suspicious configurations and return a
-- (possibly empty) list of 'SanityCheckIssue's describing any problems found.
--
-- Only 'Override' values are checked — 'UseDefault' values are known-good and
-- are never flagged. Checks that are specific to 'SnapshotFrequency' are
-- skipped entirely when 'spaFrequency' is 'DisableSnapshots'.
sanityCheckSnapshotPolicyArgs :: SnapshotPolicyArgs -> [SanityCheckIssue]
sanityCheckSnapshotPolicyArgs :: SnapshotPolicyArgs -> [SanityCheckIssue]
sanityCheckSnapshotPolicyArgs SnapshotPolicyArgs{SnapshotFrequency
spaFrequency :: SnapshotPolicyArgs -> SnapshotFrequency
spaFrequency :: SnapshotFrequency
spaFrequency, OverrideOrDefault NumOfDiskSnapshots
spaNum :: SnapshotPolicyArgs -> OverrideOrDefault NumOfDiskSnapshots
spaNum :: OverrideOrDefault NumOfDiskSnapshots
spaNum} =
  [Maybe SanityCheckIssue] -> [SanityCheckIssue]
forall a. [Maybe a] -> [a]
catMaybes ([Maybe SanityCheckIssue] -> [SanityCheckIssue])
-> [Maybe SanityCheckIssue] -> [SanityCheckIssue]
forall a b. (a -> b) -> a -> b
$
    OverrideOrDefault NumOfDiskSnapshots -> Maybe SanityCheckIssue
checkNumZero OverrideOrDefault NumOfDiskSnapshots
spaNum
      Maybe SanityCheckIssue
-> [Maybe SanityCheckIssue] -> [Maybe SanityCheckIssue]
forall a. a -> [a] -> [a]
: case SnapshotFrequency
spaFrequency of
        SnapshotFrequency
DisableSnapshots -> []
        SnapshotFrequency SnapshotFrequencyArgs
sfa -> SnapshotFrequencyArgs -> [Maybe SanityCheckIssue]
checkFrequencyArgs SnapshotFrequencyArgs
sfa
 where
  checkNumZero :: OverrideOrDefault NumOfDiskSnapshots -> Maybe SanityCheckIssue
checkNumZero (Override (NumOfDiskSnapshots Word
0)) = SanityCheckIssue -> Maybe SanityCheckIssue
forall a. a -> Maybe a
Just SanityCheckIssue
SnapshotNumZero
  checkNumZero OverrideOrDefault NumOfDiskSnapshots
_ = Maybe SanityCheckIssue
forall a. Maybe a
Nothing

  checkFrequencyArgs :: SnapshotFrequencyArgs -> [Maybe SanityCheckIssue]
checkFrequencyArgs SnapshotFrequencyArgs{OverrideOrDefault SnapshotDelayRange
sfaDelaySnapshotRange :: SnapshotFrequencyArgs -> OverrideOrDefault SnapshotDelayRange
sfaDelaySnapshotRange :: OverrideOrDefault SnapshotDelayRange
sfaDelaySnapshotRange, OverrideOrDefault DiffTime
sfaRateLimit :: SnapshotFrequencyArgs -> OverrideOrDefault DiffTime
sfaRateLimit :: OverrideOrDefault DiffTime
sfaRateLimit, OverrideOrDefault (NonZero Word64)
sfaInterval :: SnapshotFrequencyArgs -> OverrideOrDefault (NonZero Word64)
sfaInterval :: OverrideOrDefault (NonZero Word64)
sfaInterval} =
    [ OverrideOrDefault SnapshotDelayRange -> Maybe SanityCheckIssue
checkDelayRange OverrideOrDefault SnapshotDelayRange
sfaDelaySnapshotRange
    , OverrideOrDefault DiffTime -> Maybe SanityCheckIssue
forall {a}.
(Ord a, Num a) =>
OverrideOrDefault a -> Maybe SanityCheckIssue
checkRateLimitDisabled OverrideOrDefault DiffTime
sfaRateLimit
    , OverrideOrDefault DiffTime -> Maybe SanityCheckIssue
checkRateLimitLarge OverrideOrDefault DiffTime
sfaRateLimit
    , OverrideOrDefault (NonZero Word64) -> Maybe SanityCheckIssue
checkMithrilDivisibility OverrideOrDefault (NonZero Word64)
sfaInterval
    ]

  checkDelayRange :: OverrideOrDefault SnapshotDelayRange -> Maybe SanityCheckIssue
checkDelayRange OverrideOrDefault SnapshotDelayRange
UseDefault = Maybe SanityCheckIssue
forall a. Maybe a
Nothing
  checkDelayRange (Override (SnapshotDelayRange DiffTime
mn DiffTime
mx))
    | DiffTime
mn DiffTime -> DiffTime -> Bool
forall a. Ord a => a -> a -> Bool
< DiffTime
0 = SanityCheckIssue -> Maybe SanityCheckIssue
forall a. a -> Maybe a
Just (DiffTime -> SanityCheckIssue
SnapshotDelayRangeNegativeMinimum DiffTime
mn)
    | DiffTime
mn DiffTime -> DiffTime -> Bool
forall a. Ord a => a -> a -> Bool
> DiffTime
mx = SanityCheckIssue -> Maybe SanityCheckIssue
forall a. a -> Maybe a
Just (DiffTime -> DiffTime -> SanityCheckIssue
SnapshotDelayRangeInverted DiffTime
mn DiffTime
mx)
    | Bool
otherwise = Maybe SanityCheckIssue
forall a. Maybe a
Nothing

  checkRateLimitDisabled :: OverrideOrDefault a -> Maybe SanityCheckIssue
checkRateLimitDisabled OverrideOrDefault a
UseDefault = Maybe SanityCheckIssue
forall a. Maybe a
Nothing
  checkRateLimitDisabled (Override a
rl)
    | a
rl a -> a -> Bool
forall a. Ord a => a -> a -> Bool
<= a
0 = SanityCheckIssue -> Maybe SanityCheckIssue
forall a. a -> Maybe a
Just SanityCheckIssue
SnapshotRateLimitDisabled
    | Bool
otherwise = Maybe SanityCheckIssue
forall a. Maybe a
Nothing

  checkRateLimitLarge :: OverrideOrDefault DiffTime -> Maybe SanityCheckIssue
checkRateLimitLarge OverrideOrDefault DiffTime
UseDefault = Maybe SanityCheckIssue
forall a. Maybe a
Nothing
  checkRateLimitLarge (Override DiffTime
rl)
    | DiffTime
rl DiffTime -> DiffTime -> Bool
forall a. Ord a => a -> a -> Bool
> DiffTime
86400 = SanityCheckIssue -> Maybe SanityCheckIssue
forall a. a -> Maybe a
Just (DiffTime -> SanityCheckIssue
SnapshotRateLimitSuspiciouslyLarge DiffTime
rl)
    | Bool
otherwise = Maybe SanityCheckIssue
forall a. Maybe a
Nothing

  checkMithrilDivisibility :: OverrideOrDefault (NonZero Word64) -> Maybe SanityCheckIssue
checkMithrilDivisibility OverrideOrDefault (NonZero Word64)
UseDefault = Maybe SanityCheckIssue
forall a. Maybe a
Nothing
  checkMithrilDivisibility (Override NonZero Word64
interval)
    | Word64
mithrilEpochSize Word64 -> Word64 -> Word64
forall a. Integral a => a -> a -> a
`mod` NonZero Word64 -> Word64
forall a. NonZero a -> a
unNonZero NonZero Word64
interval Word64 -> Word64 -> Bool
forall a. Eq a => a -> a -> Bool
/= Word64
0 =
        SanityCheckIssue -> Maybe SanityCheckIssue
forall a. a -> Maybe a
Just (Word64 -> SanityCheckIssue
SnapshotIntervalNotDivisorOfEpoch (NonZero Word64 -> Word64
forall a. NonZero a -> a
unNonZero NonZero Word64
interval))
    | Bool
otherwise = Maybe SanityCheckIssue
forall a. Maybe a
Nothing

{-------------------------------------------------------------------------------
  Tracing snapshot events
-------------------------------------------------------------------------------}

data TraceSnapshotEvent blk
  = -- | An on disk snapshot was skipped because it was invalid.
    InvalidSnapshot DiskSnapshot (SnapshotFailure blk)
  | -- | A delayed snapshot requested was issued at a timestamp,
    --   with a delay and for ledger states at the specified slot numbers
    SnapshotRequestDelayed Time DiffTime (NonEmpty SlotNo)
  | -- | A snapshot request was completed
    SnapshotRequestCompleted
  | -- | A snapshot was written to disk.
    TookSnapshot DiskSnapshot (RealPoint blk) EnclosingTimed
  | -- | An old or invalid on-disk snapshot was deleted
    DeletedSnapshot DiskSnapshot
  deriving ((forall x.
 TraceSnapshotEvent blk -> Rep (TraceSnapshotEvent blk) x)
-> (forall x.
    Rep (TraceSnapshotEvent blk) x -> TraceSnapshotEvent blk)
-> Generic (TraceSnapshotEvent blk)
forall x. Rep (TraceSnapshotEvent blk) x -> TraceSnapshotEvent blk
forall x. TraceSnapshotEvent blk -> Rep (TraceSnapshotEvent blk) x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
forall blk x.
Rep (TraceSnapshotEvent blk) x -> TraceSnapshotEvent blk
forall blk x.
TraceSnapshotEvent blk -> Rep (TraceSnapshotEvent blk) x
$cfrom :: forall blk x.
TraceSnapshotEvent blk -> Rep (TraceSnapshotEvent blk) x
from :: forall x. TraceSnapshotEvent blk -> Rep (TraceSnapshotEvent blk) x
$cto :: forall blk x.
Rep (TraceSnapshotEvent blk) x -> TraceSnapshotEvent blk
to :: forall x. Rep (TraceSnapshotEvent blk) x -> TraceSnapshotEvent blk
Generic, TraceSnapshotEvent blk -> TraceSnapshotEvent blk -> Bool
(TraceSnapshotEvent blk -> TraceSnapshotEvent blk -> Bool)
-> (TraceSnapshotEvent blk -> TraceSnapshotEvent blk -> Bool)
-> Eq (TraceSnapshotEvent blk)
forall blk.
StandardHash blk =>
TraceSnapshotEvent blk -> TraceSnapshotEvent blk -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: forall blk.
StandardHash blk =>
TraceSnapshotEvent blk -> TraceSnapshotEvent blk -> Bool
== :: TraceSnapshotEvent blk -> TraceSnapshotEvent blk -> Bool
$c/= :: forall blk.
StandardHash blk =>
TraceSnapshotEvent blk -> TraceSnapshotEvent blk -> Bool
/= :: TraceSnapshotEvent blk -> TraceSnapshotEvent blk -> Bool
Eq, Int -> TraceSnapshotEvent blk -> ShowS
[TraceSnapshotEvent blk] -> ShowS
TraceSnapshotEvent blk -> String
(Int -> TraceSnapshotEvent blk -> ShowS)
-> (TraceSnapshotEvent blk -> String)
-> ([TraceSnapshotEvent blk] -> ShowS)
-> Show (TraceSnapshotEvent blk)
forall blk.
StandardHash blk =>
Int -> TraceSnapshotEvent blk -> ShowS
forall blk. StandardHash blk => [TraceSnapshotEvent blk] -> ShowS
forall blk. StandardHash blk => TraceSnapshotEvent blk -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: forall blk.
StandardHash blk =>
Int -> TraceSnapshotEvent blk -> ShowS
showsPrec :: Int -> TraceSnapshotEvent blk -> ShowS
$cshow :: forall blk. StandardHash blk => TraceSnapshotEvent blk -> String
show :: TraceSnapshotEvent blk -> String
$cshowList :: forall blk. StandardHash blk => [TraceSnapshotEvent blk] -> ShowS
showList :: [TraceSnapshotEvent blk] -> ShowS
Show)