{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeApplications #-}

-- | Leios (CIP-0164) parts of the ledger state.
--
-- The voting committee: the pools entitled to vote on endorser blocks for one
-- epoch. The committee type itself is cardano-base's 'LeiosCommittee'; this
-- module re-exports it, gives it an empty value, teaches it to serialize as part
-- of the ledger state, and selects it from the per-epoch stake standing of the
-- pools ('LeiosCandidate').
--
-- The last endorser block announcement made by a block header ('EbAnnouncement').
module Cardano.Ledger.State.Leios (
  LeiosCommittee (..),
  LeiosSeat (..),
  emptyLeiosCommittee,
  LeiosCandidate (..),
  selectLeiosCommittee,
  leiosCommitteeToJSON,
  EbAnnouncement (..),
) where

import Cardano.Crypto.Leios (LeiosCommittee (..), LeiosSeat (..), Weight, mkLeiosCommittee)
import Cardano.Ledger.BaseTypes (
  EpochInterval,
  KeyValuePairs (..),
  StrictMaybe (..),
  ToKeyValuePairs (..),
  addEpochInterval,
  strictMaybeToMaybe,
 )
import Cardano.Ledger.Binary (DecCBOR (..), EncCBOR (..), decodeRecordNamed, encodeListLen)
import Cardano.Ledger.Block (EbReferencesAnnouncement)
import Cardano.Ledger.Coin (Coin, CompactForm)
import Cardano.Ledger.Hashes (EraIndependentBlockHeader, SafeHash)
import Cardano.Ledger.Keys (KeyHash, StakePool)
import Cardano.Ledger.Slot (EpochNo, SlotNo)
import Cardano.Ledger.State.StakePool (BlsKey (..), BlsKeyState (..))
import Control.DeepSeq (NFData)
import Data.Aeson (ToJSON (..), Value, object, toJSON, (.=))
import Data.Function ((&))
import Data.Ord (Down (..))
import Data.Vector (Vector)
import qualified Data.Vector as V
import qualified Data.Vector.Algorithms.Intro as Intro
import qualified Data.Vector.Strict as VS
import Data.Word (Word16)
import GHC.Generics (Generic)
import NoThunks.Class (NoThunks)

-- | The committee with no seats: the value for eras before Leios and for a
-- committee size of zero.
emptyLeiosCommittee :: LeiosCommittee
emptyLeiosCommittee :: LeiosCommittee
emptyLeiosCommittee = Vector LeiosSeat -> LeiosCommittee
UnsafeLeiosCommittee Vector LeiosSeat
forall a. Vector a
VS.empty

-- | A stake pool standing for committee selection. Keeps 'selectLeiosCommittee'
-- independent of the snapshot the candidates are projected from.
data LeiosCandidate = LeiosCandidate
  { LeiosCandidate -> KeyHash StakePool
lcPoolId :: !(KeyHash StakePool)
  -- ^ Tie-breaker: pools of equal stake are seated by ascending id.
  , LeiosCandidate -> CompactForm Coin
lcStake :: !(CompactForm Coin)
  -- ^ Ranking key: pools are seated in descending stake.
  , LeiosCandidate -> Weight
lcWeight :: !Weight
  -- ^ The seat's voting weight: the pool's share of the active stake.
  , LeiosCandidate -> StrictMaybe BlsKeyState
lcKey :: !(StrictMaybe BlsKeyState)
  -- ^ The registered voting key with its registration epoch, if the pool has one.
  }
  deriving (Int -> LeiosCandidate -> ShowS
[LeiosCandidate] -> ShowS
LeiosCandidate -> String
(Int -> LeiosCandidate -> ShowS)
-> (LeiosCandidate -> String)
-> ([LeiosCandidate] -> ShowS)
-> Show LeiosCandidate
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> LeiosCandidate -> ShowS
showsPrec :: Int -> LeiosCandidate -> ShowS
$cshow :: LeiosCandidate -> String
show :: LeiosCandidate -> String
$cshowList :: [LeiosCandidate] -> ShowS
showList :: [LeiosCandidate] -> ShowS
Show, LeiosCandidate -> LeiosCandidate -> Bool
(LeiosCandidate -> LeiosCandidate -> Bool)
-> (LeiosCandidate -> LeiosCandidate -> Bool) -> Eq LeiosCandidate
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: LeiosCandidate -> LeiosCandidate -> Bool
== :: LeiosCandidate -> LeiosCandidate -> Bool
$c/= :: LeiosCandidate -> LeiosCandidate -> Bool
/= :: LeiosCandidate -> LeiosCandidate -> Bool
Eq)

-- | Seat the @committeeSize@ pools with the most stake, largest first, ties
-- broken by ascending pool id. A pool with no registered key, one whose key has
-- aged out (CIP-0164: honoured for @maxKeyAge@ epochs after its registration,
-- judged against the epoch this committee is selected for), or one whose proof
-- of possession does not verify, is seated keyless. A size of zero yields the
-- empty committee without inspecting the candidates, so pre-Dijkstra snapshots
-- carry it for free even when forced.
selectLeiosCommittee ::
  EpochNo -> EpochInterval -> Word16 -> Vector LeiosCandidate -> LeiosCommittee
selectLeiosCommittee :: EpochNo
-> EpochInterval
-> Word16
-> Vector LeiosCandidate
-> LeiosCommittee
selectLeiosCommittee EpochNo
_ EpochInterval
_ Word16
0 Vector LeiosCandidate
_ = LeiosCommittee
emptyLeiosCommittee
selectLeiosCommittee EpochNo
epochNo EpochInterval
maxKeyAge Word16
committeeSize Vector LeiosCandidate
candidates =
  Vector LeiosCandidate
candidates
    Vector LeiosCandidate
-> (Vector LeiosCandidate -> Vector LeiosCandidate)
-> Vector LeiosCandidate
forall a b. a -> (a -> b) -> b
& Vector LeiosCandidate -> Vector LeiosCandidate
sortByStake
    Vector LeiosCandidate
-> (Vector LeiosCandidate -> Vector LeiosCandidate)
-> Vector LeiosCandidate
forall a b. a -> (a -> b) -> b
& Int -> Vector LeiosCandidate -> Vector LeiosCandidate
forall a. Int -> Vector a -> Vector a
V.take Int
size
    Vector LeiosCandidate
-> (Vector LeiosCandidate
    -> Vector
         (StrictMaybe
            (VerKeyDSIGN BLS12381MinSigDSIGN,
             PossessionProofDSIGN BLS12381MinSigDSIGN),
          Weight))
-> Vector
     (StrictMaybe
        (VerKeyDSIGN BLS12381MinSigDSIGN,
         PossessionProofDSIGN BLS12381MinSigDSIGN),
      Weight)
forall a b. a -> (a -> b) -> b
& (LeiosCandidate
 -> (StrictMaybe
       (VerKeyDSIGN BLS12381MinSigDSIGN,
        PossessionProofDSIGN BLS12381MinSigDSIGN),
     Weight))
-> Vector LeiosCandidate
-> Vector
     (StrictMaybe
        (VerKeyDSIGN BLS12381MinSigDSIGN,
         PossessionProofDSIGN BLS12381MinSigDSIGN),
      Weight)
forall a b. (a -> b) -> Vector a -> Vector b
V.map LeiosCandidate
-> (StrictMaybe
      (VerKeyDSIGN BLS12381MinSigDSIGN,
       PossessionProofDSIGN BLS12381MinSigDSIGN),
    Weight)
toSeat
    Vector
  (StrictMaybe
     (VerKeyDSIGN BLS12381MinSigDSIGN,
      PossessionProofDSIGN BLS12381MinSigDSIGN),
   Weight)
-> (Vector
      (StrictMaybe
         (VerKeyDSIGN BLS12381MinSigDSIGN,
          PossessionProofDSIGN BLS12381MinSigDSIGN),
       Weight)
    -> Vector
         (StrictMaybe
            (VerKeyDSIGN BLS12381MinSigDSIGN,
             PossessionProofDSIGN BLS12381MinSigDSIGN),
          Weight))
-> Vector
     (StrictMaybe
        (VerKeyDSIGN BLS12381MinSigDSIGN,
         PossessionProofDSIGN BLS12381MinSigDSIGN),
      Weight)
forall a b. a -> (a -> b) -> b
& Vector
  (StrictMaybe
     (VerKeyDSIGN BLS12381MinSigDSIGN,
      PossessionProofDSIGN BLS12381MinSigDSIGN),
   Weight)
-> Vector
     (StrictMaybe
        (VerKeyDSIGN BLS12381MinSigDSIGN,
         PossessionProofDSIGN BLS12381MinSigDSIGN),
      Weight)
forall (v :: * -> *) a (w :: * -> *).
(Vector v a, Vector w a) =>
v a -> w a
V.convert
    Vector
  (StrictMaybe
     (VerKeyDSIGN BLS12381MinSigDSIGN,
      PossessionProofDSIGN BLS12381MinSigDSIGN),
   Weight)
-> (Vector
      (StrictMaybe
         (VerKeyDSIGN BLS12381MinSigDSIGN,
          PossessionProofDSIGN BLS12381MinSigDSIGN),
       Weight)
    -> LeiosCommittee)
-> LeiosCommittee
forall a b. a -> (a -> b) -> b
& Vector
  (StrictMaybe
     (VerKeyDSIGN BLS12381MinSigDSIGN,
      PossessionProofDSIGN BLS12381MinSigDSIGN),
   Weight)
-> LeiosCommittee
mkLeiosCommittee
  where
    -- Only the top @size@ need to be in order, so partial-sort them in place
    -- and leave the rest untouched instead of ordering the whole vector.
    sortByStake :: Vector LeiosCandidate -> Vector LeiosCandidate
sortByStake = (forall s. MVector s LeiosCandidate -> ST s ())
-> Vector LeiosCandidate -> Vector LeiosCandidate
forall a.
(forall s. MVector s a -> ST s ()) -> Vector a -> Vector a
V.modify (\MVector s LeiosCandidate
mv -> Comparison LeiosCandidate
-> MVector (PrimState (ST s)) LeiosCandidate -> Int -> ST s ()
forall (m :: * -> *) (v :: * -> * -> *) e.
(PrimMonad m, MVector v e) =>
Comparison e -> v (PrimState m) e -> Int -> m ()
Intro.partialSortBy Comparison LeiosCandidate
higherStake MVector s LeiosCandidate
MVector (PrimState (ST s)) LeiosCandidate
mv Int
size)

    size :: Int
size = Int -> Int -> Int
forall a. Ord a => a -> a -> a
min (forall a b. (Integral a, Num b) => a -> b
fromIntegral @Word16 @Int Word16
committeeSize) (Vector LeiosCandidate -> Int
forall a. Vector a -> Int
V.length Vector LeiosCandidate
candidates)

    higherStake :: Comparison LeiosCandidate
higherStake LeiosCandidate
a LeiosCandidate
b =
      (Down (CompactForm Coin), KeyHash StakePool)
-> (Down (CompactForm Coin), KeyHash StakePool) -> Ordering
forall a. Ord a => a -> a -> Ordering
compare (CompactForm Coin -> Down (CompactForm Coin)
forall a. a -> Down a
Down (LeiosCandidate -> CompactForm Coin
lcStake LeiosCandidate
a), LeiosCandidate -> KeyHash StakePool
lcPoolId LeiosCandidate
a) (CompactForm Coin -> Down (CompactForm Coin)
forall a. a -> Down a
Down (LeiosCandidate -> CompactForm Coin
lcStake LeiosCandidate
b), LeiosCandidate -> KeyHash StakePool
lcPoolId LeiosCandidate
b)

    toSeat :: LeiosCandidate
-> (StrictMaybe
      (VerKeyDSIGN BLS12381MinSigDSIGN,
       PossessionProofDSIGN BLS12381MinSigDSIGN),
    Weight)
toSeat LeiosCandidate
c = (LeiosCandidate
-> StrictMaybe
     (VerKeyDSIGN BLS12381MinSigDSIGN,
      PossessionProofDSIGN BLS12381MinSigDSIGN)
honouredKey LeiosCandidate
c, LeiosCandidate -> Weight
lcWeight LeiosCandidate
c)

    -- The key is offered to the committee only while it is still honoured; an
    -- aged-out key leaves the pool seated but keyless.
    honouredKey :: LeiosCandidate
-> StrictMaybe
     (VerKeyDSIGN BLS12381MinSigDSIGN,
      PossessionProofDSIGN BLS12381MinSigDSIGN)
honouredKey LeiosCandidate
c = do
      bks <- LeiosCandidate -> StrictMaybe BlsKeyState
lcKey LeiosCandidate
c
      if epochNo < addEpochInterval (bksRegisteredIn bks) maxKeyAge
        then let BlsKey vk pop = bksKey bks in SJust (vk, pop)
        else SNothing

-- | Render a 'LeiosCommittee' as JSON for ledger purposes.
leiosCommitteeToJSON :: LeiosCommittee -> Value
leiosCommitteeToJSON :: LeiosCommittee -> Value
leiosCommitteeToJSON =
  [Value] -> Value
forall a. ToJSON a => a -> Value
toJSON ([Value] -> Value)
-> (LeiosCommittee -> [Value]) -> LeiosCommittee -> Value
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (LeiosSeat -> Value) -> [LeiosSeat] -> [Value]
forall a b. (a -> b) -> [a] -> [b]
map LeiosSeat -> Value
leiosSeatToJSON ([LeiosSeat] -> [Value])
-> (LeiosCommittee -> [LeiosSeat]) -> LeiosCommittee -> [Value]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Vector LeiosSeat -> [LeiosSeat]
forall a. Vector a -> [a]
VS.toList (Vector LeiosSeat -> [LeiosSeat])
-> (LeiosCommittee -> Vector LeiosSeat)
-> LeiosCommittee
-> [LeiosSeat]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. LeiosCommittee -> Vector LeiosSeat
leiosCommitteeSeats
  where
    leiosSeatToJSON :: LeiosSeat -> Value
leiosSeatToJSON (LeiosSeat Weight
weight StrictMaybe (VerKeyDSIGN BLS12381MinSigDSIGN)
vkey) =
      [Pair] -> Value
object
        [ Key
"seatWeight" Key -> Weight -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= Weight
weight
        , Key
"seatVKey" Key -> String -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= Maybe (VerKeyDSIGN BLS12381MinSigDSIGN) -> String
forall a. Show a => a -> String
show (StrictMaybe (VerKeyDSIGN BLS12381MinSigDSIGN)
-> Maybe (VerKeyDSIGN BLS12381MinSigDSIGN)
forall a. StrictMaybe a -> Maybe a
strictMaybeToMaybe StrictMaybe (VerKeyDSIGN BLS12381MinSigDSIGN)
vkey)
        ]

-- | Endorser Block (EB) references announcement together with the block header that made it.
data EbAnnouncement = EbAnnouncement
  { EbAnnouncement -> EbReferencesAnnouncement
ebAnnouncement :: !EbReferencesAnnouncement
  -- ^ Announcement of the Endorser Block references
  , EbAnnouncement -> SlotNo
ebAnnouncementSlotNo :: !SlotNo
  -- ^ Slot number of the block whose header made the announcement
  , EbAnnouncement -> SafeHash EraIndependentBlockHeader
ebAnnouncedInBlockHeader :: !(SafeHash EraIndependentBlockHeader)
  -- ^ Hash of the block header that made the announcement
  }
  deriving stock (Int -> EbAnnouncement -> ShowS
[EbAnnouncement] -> ShowS
EbAnnouncement -> String
(Int -> EbAnnouncement -> ShowS)
-> (EbAnnouncement -> String)
-> ([EbAnnouncement] -> ShowS)
-> Show EbAnnouncement
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> EbAnnouncement -> ShowS
showsPrec :: Int -> EbAnnouncement -> ShowS
$cshow :: EbAnnouncement -> String
show :: EbAnnouncement -> String
$cshowList :: [EbAnnouncement] -> ShowS
showList :: [EbAnnouncement] -> ShowS
Show, EbAnnouncement -> EbAnnouncement -> Bool
(EbAnnouncement -> EbAnnouncement -> Bool)
-> (EbAnnouncement -> EbAnnouncement -> Bool) -> Eq EbAnnouncement
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: EbAnnouncement -> EbAnnouncement -> Bool
== :: EbAnnouncement -> EbAnnouncement -> Bool
$c/= :: EbAnnouncement -> EbAnnouncement -> Bool
/= :: EbAnnouncement -> EbAnnouncement -> Bool
Eq, (forall x. EbAnnouncement -> Rep EbAnnouncement x)
-> (forall x. Rep EbAnnouncement x -> EbAnnouncement)
-> Generic EbAnnouncement
forall x. Rep EbAnnouncement x -> EbAnnouncement
forall x. EbAnnouncement -> Rep EbAnnouncement x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. EbAnnouncement -> Rep EbAnnouncement x
from :: forall x. EbAnnouncement -> Rep EbAnnouncement x
$cto :: forall x. Rep EbAnnouncement x -> EbAnnouncement
to :: forall x. Rep EbAnnouncement x -> EbAnnouncement
Generic)
  deriving anyclass (Context -> EbAnnouncement -> IO (Maybe ThunkInfo)
Proxy EbAnnouncement -> String
(Context -> EbAnnouncement -> IO (Maybe ThunkInfo))
-> (Context -> EbAnnouncement -> IO (Maybe ThunkInfo))
-> (Proxy EbAnnouncement -> String)
-> NoThunks EbAnnouncement
forall a.
(Context -> a -> IO (Maybe ThunkInfo))
-> (Context -> a -> IO (Maybe ThunkInfo))
-> (Proxy a -> String)
-> NoThunks a
$cnoThunks :: Context -> EbAnnouncement -> IO (Maybe ThunkInfo)
noThunks :: Context -> EbAnnouncement -> IO (Maybe ThunkInfo)
$cwNoThunks :: Context -> EbAnnouncement -> IO (Maybe ThunkInfo)
wNoThunks :: Context -> EbAnnouncement -> IO (Maybe ThunkInfo)
$cshowTypeOf :: Proxy EbAnnouncement -> String
showTypeOf :: Proxy EbAnnouncement -> String
NoThunks, EbAnnouncement -> ()
(EbAnnouncement -> ()) -> NFData EbAnnouncement
forall a. (a -> ()) -> NFData a
$crnf :: EbAnnouncement -> ()
rnf :: EbAnnouncement -> ()
NFData)
  deriving ([EbAnnouncement] -> Value
[EbAnnouncement] -> Encoding
EbAnnouncement -> Bool
EbAnnouncement -> Value
EbAnnouncement -> Encoding
(EbAnnouncement -> Value)
-> (EbAnnouncement -> Encoding)
-> ([EbAnnouncement] -> Value)
-> ([EbAnnouncement] -> Encoding)
-> (EbAnnouncement -> Bool)
-> ToJSON EbAnnouncement
forall a.
(a -> Value)
-> (a -> Encoding)
-> ([a] -> Value)
-> ([a] -> Encoding)
-> (a -> Bool)
-> ToJSON a
$ctoJSON :: EbAnnouncement -> Value
toJSON :: EbAnnouncement -> Value
$ctoEncoding :: EbAnnouncement -> Encoding
toEncoding :: EbAnnouncement -> Encoding
$ctoJSONList :: [EbAnnouncement] -> Value
toJSONList :: [EbAnnouncement] -> Value
$ctoEncodingList :: [EbAnnouncement] -> Encoding
toEncodingList :: [EbAnnouncement] -> Encoding
$comitField :: EbAnnouncement -> Bool
omitField :: EbAnnouncement -> Bool
ToJSON) via KeyValuePairs EbAnnouncement

instance EncCBOR EbAnnouncement where
  encCBOR :: EbAnnouncement -> Encoding
encCBOR (EbAnnouncement EbReferencesAnnouncement
a SlotNo
s SafeHash EraIndependentBlockHeader
h) =
    Word -> Encoding
encodeListLen Word
3
      Encoding -> Encoding -> Encoding
forall a. Semigroup a => a -> a -> a
<> EbReferencesAnnouncement -> Encoding
forall a. EncCBOR a => a -> Encoding
encCBOR EbReferencesAnnouncement
a
      Encoding -> Encoding -> Encoding
forall a. Semigroup a => a -> a -> a
<> SlotNo -> Encoding
forall a. EncCBOR a => a -> Encoding
encCBOR SlotNo
s
      Encoding -> Encoding -> Encoding
forall a. Semigroup a => a -> a -> a
<> SafeHash EraIndependentBlockHeader -> Encoding
forall a. EncCBOR a => a -> Encoding
encCBOR SafeHash EraIndependentBlockHeader
h

instance DecCBOR EbAnnouncement where
  decCBOR :: forall s. Decoder s EbAnnouncement
decCBOR =
    Text
-> (EbAnnouncement -> Int)
-> Decoder s EbAnnouncement
-> Decoder s EbAnnouncement
forall a s. Text -> (a -> Int) -> Decoder s a -> Decoder s a
decodeRecordNamed Text
"EbAnnouncement" (Int -> EbAnnouncement -> Int
forall a b. a -> b -> a
const Int
3) (Decoder s EbAnnouncement -> Decoder s EbAnnouncement)
-> Decoder s EbAnnouncement -> Decoder s EbAnnouncement
forall a b. (a -> b) -> a -> b
$
      EbReferencesAnnouncement
-> SlotNo -> SafeHash EraIndependentBlockHeader -> EbAnnouncement
EbAnnouncement
        (EbReferencesAnnouncement
 -> SlotNo -> SafeHash EraIndependentBlockHeader -> EbAnnouncement)
-> Decoder s EbReferencesAnnouncement
-> Decoder
     s (SlotNo -> SafeHash EraIndependentBlockHeader -> EbAnnouncement)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Decoder s EbReferencesAnnouncement
forall s. Decoder s EbReferencesAnnouncement
forall a s. DecCBOR a => Decoder s a
decCBOR
        Decoder
  s (SlotNo -> SafeHash EraIndependentBlockHeader -> EbAnnouncement)
-> Decoder s SlotNo
-> Decoder s (SafeHash EraIndependentBlockHeader -> EbAnnouncement)
forall a b. Decoder s (a -> b) -> Decoder s a -> Decoder s b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Decoder s SlotNo
forall s. Decoder s SlotNo
forall a s. DecCBOR a => Decoder s a
decCBOR
        Decoder s (SafeHash EraIndependentBlockHeader -> EbAnnouncement)
-> Decoder s (SafeHash EraIndependentBlockHeader)
-> Decoder s EbAnnouncement
forall a b. Decoder s (a -> b) -> Decoder s a -> Decoder s b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Decoder s (SafeHash EraIndependentBlockHeader)
forall s. Decoder s (SafeHash EraIndependentBlockHeader)
forall a s. DecCBOR a => Decoder s a
decCBOR

instance ToKeyValuePairs EbAnnouncement where
  toKeyValuePairs :: forall e kv. KeyValue e kv => EbAnnouncement -> [kv]
toKeyValuePairs (EbAnnouncement EbReferencesAnnouncement
a SlotNo
s SafeHash EraIndependentBlockHeader
h) =
    [ Key
"announcement" Key -> EbReferencesAnnouncement -> kv
forall v. ToJSON v => Key -> v -> kv
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= EbReferencesAnnouncement
a
    , Key
"slotNo" Key -> SlotNo -> kv
forall v. ToJSON v => Key -> v -> kv
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= SlotNo
s
    , Key
"announcedInBlockHeader" Key -> SafeHash EraIndependentBlockHeader -> kv
forall v. ToJSON v => Key -> v -> kv
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= SafeHash EraIndependentBlockHeader
h
    ]