{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
module Cardano.Rpc.Server.Internal.UtxoRpc.Sync
( fetchBlockMethod
, followTipMethod
, followTipStream
, readTipMethod
)
where
import Cardano.Api
import Cardano.Api.Consensus qualified as Consensus
import Cardano.Rpc.Proto.Api.UtxoRpc.Sync qualified as U5c
import Cardano.Rpc.Server.Internal.Error
import Cardano.Rpc.Server.Internal.Monad
import Cardano.Rpc.Server.Internal.Tracing ()
import Cardano.Rpc.Server.Internal.UtxoRpc.Type.Block (mkAnyChainBlock)
import Cardano.Rpc.Server.Internal.UtxoRpc.Type.ChainPoint
( chainPointToBlockRef
, mkTipBlockRef
, tipHeaderPoint
)
import Cardano.Rpc.Server.NodeKernelAccess
import Cardano.Ledger.BaseTypes qualified as L
import RIO
import Data.ByteString qualified as BS
import Data.ProtoLens (defMessage)
import Data.Sequence qualified as Seq
import Data.Time.Clock (UTCTime)
import GHC.Stack (withFrozenCallStack)
import Network.GRPC.Spec
( GrpcError (GrpcInternal, GrpcInvalidArgument, GrpcNotFound)
, NextElem (NextElem)
, Proto
)
fetchBlockMethod
:: MonadRpc e m
=> Proto U5c.FetchBlockRequest
-> m (Proto U5c.FetchBlockResponse)
fetchBlockMethod :: forall e (m :: * -> *).
MonadRpc e m =>
Proto FetchBlockRequest -> m (Proto FetchBlockResponse)
fetchBlockMethod Proto FetchBlockRequest
request = do
nodeKernelAccess@NodeKernelAccess{systemStart, readEraHistory} <- m NodeKernelAccess
forall e (m :: * -> *). MonadRpc e m => m NodeKernelAccess
grabNodeKernelAccess
(slot, headerHash) <- blockRefToPoint (request ^. U5c.ref)
let throwNotFound =
GrpcError -> Text -> m (ByteString, BlockInMode)
forall (m :: * -> *) a. MonadIO m => GrpcError -> Text -> m a
throwGrpcErrorWithMessage GrpcError
GrpcNotFound (Text -> m (ByteString, BlockInMode))
-> Text -> m (ByteString, BlockInMode)
forall a b. (a -> b) -> a -> b
$
Text
"block not found at slot " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Word64 -> Text
forall a. Show a => a -> Text
tshow (SlotNo -> Word64
unSlotNo SlotNo
slot)
(rawBytes, blockInMode) <-
fetchBlock nodeKernelAccess slot headerHash >>= maybe throwNotFound pure
timestamp <- slotTimestampOrThrow systemStart readEraHistory slot
pure $ defMessage & U5c.block .~ mkAnyChainBlock rawBytes blockInMode timestamp
readTipMethod
:: MonadRpc e m
=> Proto U5c.ReadTipRequest
-> m (Proto U5c.ReadTipResponse)
readTipMethod :: forall e (m :: * -> *).
MonadRpc e m =>
Proto ReadTipRequest -> m (Proto ReadTipResponse)
readTipMethod Proto ReadTipRequest
_request = do
NodeKernelAccess{chainDb, systemStart, readEraHistory} <- m NodeKernelAccess
forall e (m :: * -> *). MonadRpc e m => m NodeKernelAccess
grabNodeKernelAccess
tip <- readTipBlockRef chainDb (slotTimestampOrThrow systemStart readEraHistory)
pure $ defMessage & U5c.maybe'tip .~ tip
followTipMethod
:: MonadRpc e m
=> Proto U5c.FollowTipRequest
-> (NextElem (Proto U5c.FollowTipResponse) -> IO ())
-> m ()
followTipMethod :: forall e (m :: * -> *).
MonadRpc e m =>
Proto FollowTipRequest
-> (NextElem (Proto FollowTipResponse) -> IO ()) -> m ()
followTipMethod Proto FollowTipRequest
request NextElem (Proto FollowTipResponse) -> IO ()
send = do
nodeKernelAccess@NodeKernelAccess{chainDb, systemStart, readEraHistory, securityParam} <-
m NodeKernelAccess
forall e (m :: * -> *). MonadRpc e m => m NodeKernelAccess
grabNodeKernelAccess
requestedPoints <- traverse blockRefToIntersectPoint (request ^. U5c.intersect)
withFollower nodeKernelAccess $ \ChainFollower
follower -> do
let slotTimestamp :: SlotNo -> m UTCTime
slotTimestamp = SystemStart -> m EraHistory -> SlotNo -> m UTCTime
forall (m :: * -> *).
MonadIO m =>
SystemStart -> m EraHistory -> SlotNo -> m UTCTime
slotTimestampOrThrow SystemStart
systemStart m EraHistory
forall (m :: * -> *). MonadIO m => m EraHistory
readEraHistory
startPoints <-
if [ChainPoint] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [ChainPoint]
requestedPoints
then do
tipHeader <- IO (Maybe (Header (CardanoBlock StandardCrypto)))
-> m (Maybe (Header (CardanoBlock StandardCrypto)))
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Maybe (Header (CardanoBlock StandardCrypto)))
-> m (Maybe (Header (CardanoBlock StandardCrypto))))
-> IO (Maybe (Header (CardanoBlock StandardCrypto)))
-> m (Maybe (Header (CardanoBlock StandardCrypto)))
forall a b. (a -> b) -> a -> b
$ ChainDB IO (CardanoBlock StandardCrypto)
-> IO (Maybe (Header (CardanoBlock StandardCrypto)))
forall (m :: * -> *) blk. ChainDB m blk -> m (Maybe (Header blk))
Consensus.getTipHeader ChainDB IO (CardanoBlock StandardCrypto)
chainDb
pure [maybe ChainPointAtGenesis tipHeaderPoint tipHeader]
else [ChainPoint] -> m [ChainPoint]
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure [ChainPoint]
requestedPoints
followTipStream
follower
(readTipBlockRef chainDb slotTimestamp)
slotTimestamp
(fetchBlockByChainPoint nodeKernelAccess)
(fromIntegral . L.unNonZero $ Consensus.maxRollbacks securityParam)
send
startPoints
blockRefToIntersectPoint
:: MonadRpc e m
=> Proto U5c.BlockRef
-> m ChainPoint
blockRefToIntersectPoint :: forall e (m :: * -> *).
MonadRpc e m =>
Proto BlockRef -> m ChainPoint
blockRefToIntersectPoint Proto BlockRef
blockRef
| ByteString -> Bool
BS.null (Proto BlockRef
blockRef Proto BlockRef
-> Getting ByteString (Proto BlockRef) ByteString -> ByteString
forall s a. s -> Getting a s a -> a
^. Getting ByteString (Proto BlockRef) ByteString
forall (f :: * -> *) s a.
(Functor f, HasField s "hash" a) =>
LensLike' f s a
U5c.hash) = ChainPoint -> m ChainPoint
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ChainPoint
ChainPointAtGenesis
| Bool
otherwise = (SlotNo -> Hash BlockHeader -> ChainPoint)
-> (SlotNo, Hash BlockHeader) -> ChainPoint
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry SlotNo -> Hash BlockHeader -> ChainPoint
ChainPoint ((SlotNo, Hash BlockHeader) -> ChainPoint)
-> m (SlotNo, Hash BlockHeader) -> m ChainPoint
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Proto BlockRef -> m (SlotNo, Hash BlockHeader)
forall e (m :: * -> *).
MonadRpc e m =>
Proto BlockRef -> m (SlotNo, Hash BlockHeader)
blockRefToPoint Proto BlockRef
blockRef
blockRefToPoint
:: MonadRpc e m
=> Proto U5c.BlockRef
-> m (SlotNo, Hash BlockHeader)
blockRefToPoint :: forall e (m :: * -> *).
MonadRpc e m =>
Proto BlockRef -> m (SlotNo, Hash BlockHeader)
blockRefToPoint Proto BlockRef
blockRef = do
let slot :: SlotNo
slot = Word64 -> SlotNo
SlotNo (Word64 -> SlotNo) -> Word64 -> SlotNo
forall a b. (a -> b) -> a -> b
$ Proto BlockRef
blockRef Proto BlockRef -> Getting Word64 (Proto BlockRef) Word64 -> Word64
forall s a. s -> Getting a s a -> a
^. Getting Word64 (Proto BlockRef) Word64
forall (f :: * -> *) s a.
(Functor f, HasField s "slot" a) =>
LensLike' f s a
U5c.slot
hashBytes :: ByteString
hashBytes = Proto BlockRef
blockRef Proto BlockRef
-> Getting ByteString (Proto BlockRef) ByteString -> ByteString
forall s a. s -> Getting a s a -> a
^. Getting ByteString (Proto BlockRef) ByteString
forall (f :: * -> *) s a.
(Functor f, HasField s "hash" a) =>
LensLike' f s a
U5c.hash
throwInvalidHash :: m (Hash BlockHeader)
throwInvalidHash =
GrpcError -> Text -> m (Hash BlockHeader)
forall (m :: * -> *) a. MonadIO m => GrpcError -> Text -> m a
throwGrpcErrorWithMessage GrpcError
GrpcInvalidArgument (Text -> m (Hash BlockHeader)) -> Text -> m (Hash BlockHeader)
forall a b. (a -> b) -> a -> b
$
Text
"invalid block header hash (" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Int -> Text
forall a. Show a => a -> Text
tshow (ByteString -> Int
BS.length ByteString
hashBytes) Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" bytes)"
headerHash <-
AsType (Hash BlockHeader)
-> ByteString -> Either SerialiseAsRawBytesError (Hash BlockHeader)
forall a.
SerialiseAsRawBytes a =>
AsType a -> ByteString -> Either SerialiseAsRawBytesError a
deserialiseFromRawBytes (Proxy (Hash BlockHeader) -> AsType (Hash BlockHeader)
forall t. HasTypeProxy t => Proxy t -> AsType t
proxyToAsType (forall t. Proxy t
forall {k} (t :: k). Proxy t
Proxy @(Hash BlockHeader))) ByteString
hashBytes
Either SerialiseAsRawBytesError (Hash BlockHeader)
-> (Either SerialiseAsRawBytesError (Hash BlockHeader)
-> m (Hash BlockHeader))
-> m (Hash BlockHeader)
forall a b. a -> (a -> b) -> b
& (SerialiseAsRawBytesError -> m (Hash BlockHeader))
-> (Hash BlockHeader -> m (Hash BlockHeader))
-> Either SerialiseAsRawBytesError (Hash BlockHeader)
-> m (Hash BlockHeader)
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either (m (Hash BlockHeader)
-> SerialiseAsRawBytesError -> m (Hash BlockHeader)
forall a b. a -> b -> a
const m (Hash BlockHeader)
throwInvalidHash) Hash BlockHeader -> m (Hash BlockHeader)
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure
pure (slot, headerHash)
fetchBlockByChainPoint
:: MonadIO m
=> NodeKernelAccess
-> ChainPoint
-> m (Maybe (ByteString, BlockInMode))
fetchBlockByChainPoint :: forall (m :: * -> *).
MonadIO m =>
NodeKernelAccess
-> ChainPoint -> m (Maybe (ByteString, BlockInMode))
fetchBlockByChainPoint NodeKernelAccess
_nodeKernelAccess ChainPoint
ChainPointAtGenesis = Maybe (ByteString, BlockInMode)
-> m (Maybe (ByteString, BlockInMode))
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Maybe (ByteString, BlockInMode)
forall a. Maybe a
Nothing
fetchBlockByChainPoint NodeKernelAccess
nodeKernelAccess (ChainPoint SlotNo
slot Hash BlockHeader
headerHash) =
NodeKernelAccess
-> SlotNo
-> Hash BlockHeader
-> m (Maybe (ByteString, BlockInMode))
forall (m :: * -> *).
MonadIO m =>
NodeKernelAccess
-> SlotNo
-> Hash BlockHeader
-> m (Maybe (ByteString, BlockInMode))
fetchBlock NodeKernelAccess
nodeKernelAccess SlotNo
slot Hash BlockHeader
headerHash
type TrackedPoints = Seq.Seq (SlotNo, Hash BlockHeader)
followTipStream
:: forall m
. HasCallStack
=> MonadIO m
=> ChainFollower
-> m (Maybe (Proto U5c.BlockRef))
-> (SlotNo -> m UTCTime)
-> (ChainPoint -> m (Maybe (ByteString, BlockInMode)))
-> Int
-> (NextElem (Proto U5c.FollowTipResponse) -> IO ())
-> [ChainPoint]
-> m ()
followTipStream :: forall (m :: * -> *).
(HasCallStack, MonadIO m) =>
ChainFollower
-> m (Maybe (Proto BlockRef))
-> (SlotNo -> m UTCTime)
-> (ChainPoint -> m (Maybe (ByteString, BlockInMode)))
-> Int
-> (NextElem (Proto FollowTipResponse) -> IO ())
-> [ChainPoint]
-> m ()
followTipStream ChainFollower{forall (m :: * -> *). MonadIO m => m ChainChange
nextChange :: forall (m :: * -> *). MonadIO m => m ChainChange
nextChange :: ChainFollower -> forall (m :: * -> *). MonadIO m => m ChainChange
nextChange, forall (m :: * -> *).
MonadIO m =>
[ChainPoint] -> m (Maybe ChainPoint)
findIntersect :: forall (m :: * -> *).
MonadIO m =>
[ChainPoint] -> m (Maybe ChainPoint)
findIntersect :: ChainFollower
-> forall (m :: * -> *).
MonadIO m =>
[ChainPoint] -> m (Maybe ChainPoint)
findIntersect} m (Maybe (Proto BlockRef))
readTip SlotNo -> m UTCTime
slotTimestamp ChainPoint -> m (Maybe (ByteString, BlockInMode))
fetchBlockByPoint Int
trackingCap NextElem (Proto FollowTipResponse) -> IO ()
send [ChainPoint]
startPoints =
(HasCallStack => m ()) -> m ()
forall a. HasCallStack => (HasCallStack => a) -> a
withFrozenCallStack ((HasCallStack => m ()) -> m ()) -> (HasCallStack => m ()) -> m ()
forall a b. (a -> b) -> a -> b
$ do
resolvedIntersection <- [ChainPoint] -> m (Maybe ChainPoint)
forall (m :: * -> *).
MonadIO m =>
[ChainPoint] -> m (Maybe ChainPoint)
findIntersect [ChainPoint]
startPoints
startPoint <- case resolvedIntersection of
Maybe ChainPoint
Nothing ->
GrpcError -> Text -> m ChainPoint
forall (m :: * -> *) a. MonadIO m => GrpcError -> Text -> m a
throwGrpcErrorWithMessage GrpcError
GrpcNotFound (Text -> m ChainPoint) -> Text -> m ChainPoint
forall a b. (a -> b) -> a -> b
$
Text
"no intersection found: none of the "
Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Int -> Text
forall a. Show a => a -> Text
tshow ([ChainPoint] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [ChainPoint]
startPoints)
Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" intersect points are on the chain"
Just ChainPoint
point -> ChainPoint -> m ChainPoint
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ChainPoint
point
go startPoint Seq.empty
where
sendMessage :: Proto FollowTipResponse -> m ()
sendMessage Proto FollowTipResponse
action = do
tip <- m (Maybe (Proto BlockRef))
readTip
liftIO . send . NextElem $ action & U5c.maybe'tip .~ tip
go :: ChainPoint -> TrackedPoints -> m ()
go :: ChainPoint -> TrackedPoints -> m ()
go ChainPoint
floorPoint TrackedPoints
tracked = do
change <- m ChainChange
forall (m :: * -> *). MonadIO m => m ChainChange
nextChange
(floorPoint', tracked') <- case change of
ChainApply (ByteString
rawBytes, blockInMode :: BlockInMode
blockInMode@(BlockInMode CardanoEra era
_ Block era
block)) -> do
let BlockHeader SlotNo
slot Hash BlockHeader
headerHash BlockNo
_ = Block era -> BlockHeader
forall era. Block era -> BlockHeader
getBlockHeader Block era
block
timestamp <- SlotNo -> m UTCTime
slotTimestamp SlotNo
slot
sendMessage $ defMessage & U5c.apply .~ mkAnyChainBlock rawBytes blockInMode timestamp
pure (floorPoint, trackApplied trackingCap (slot, headerHash) tracked)
ChainRollBack ChainPoint
point -> ChainPoint
-> ChainPoint -> TrackedPoints -> m (ChainPoint, TrackedPoints)
handleRollback ChainPoint
point ChainPoint
floorPoint TrackedPoints
tracked
go floorPoint' tracked'
handleRollback :: ChainPoint -> ChainPoint -> TrackedPoints -> m (ChainPoint, TrackedPoints)
handleRollback :: ChainPoint
-> ChainPoint -> TrackedPoints -> m (ChainPoint, TrackedPoints)
handleRollback ChainPoint
point ChainPoint
floorPoint TrackedPoints
tracked =
case ChainPoint
-> ChainPoint
-> TrackedPoints
-> Maybe (TrackedPoints, TrackedPoints)
windowSplit ChainPoint
point ChainPoint
floorPoint TrackedPoints
tracked of
Maybe (TrackedPoints, TrackedPoints)
Nothing -> do
Proto FollowTipResponse -> m ()
sendMessage (Proto FollowTipResponse -> m ())
-> Proto FollowTipResponse -> m ()
forall a b. (a -> b) -> a -> b
$ Proto FollowTipResponse
forall msg. Message msg => msg
defMessage Proto FollowTipResponse
-> (Proto FollowTipResponse -> Proto FollowTipResponse)
-> Proto FollowTipResponse
forall a b. a -> (a -> b) -> b
& LensLike' Identity (Proto FollowTipResponse) (Proto BlockRef)
forall (f :: * -> *) s a.
(Functor f, HasField s "reset" a) =>
LensLike' f s a
U5c.reset LensLike' Identity (Proto FollowTipResponse) (Proto BlockRef)
-> Proto BlockRef
-> Proto FollowTipResponse
-> Proto FollowTipResponse
forall s t a b. ASetter s t a b -> b -> s -> t
.~ ChainPoint -> Proto BlockRef
chainPointToBlockRef ChainPoint
point
(ChainPoint, TrackedPoints) -> m (ChainPoint, TrackedPoints)
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (ChainPoint
point, TrackedPoints
forall a. Seq a
Seq.empty)
Just (TrackedPoints
undone, TrackedPoints
kept)
| TrackedPoints -> Bool
forall a. Seq a -> Bool
Seq.null TrackedPoints
undone ->
Proto FollowTipResponse -> m ()
sendMessage (Proto FollowTipResponse
forall msg. Message msg => msg
defMessage Proto FollowTipResponse
-> (Proto FollowTipResponse -> Proto FollowTipResponse)
-> Proto FollowTipResponse
forall a b. a -> (a -> b) -> b
& LensLike' Identity (Proto FollowTipResponse) (Proto BlockRef)
forall (f :: * -> *) s a.
(Functor f, HasField s "reset" a) =>
LensLike' f s a
U5c.reset LensLike' Identity (Proto FollowTipResponse) (Proto BlockRef)
-> Proto BlockRef
-> Proto FollowTipResponse
-> Proto FollowTipResponse
forall s t a b. ASetter s t a b -> b -> s -> t
.~ ChainPoint -> Proto BlockRef
chainPointToBlockRef ChainPoint
point)
m ()
-> (ChainPoint, TrackedPoints) -> m (ChainPoint, TrackedPoints)
forall (f :: * -> *) a b. Functor f => f a -> b -> f b
$> (ChainPoint
floorPoint, TrackedPoints
kept)
| Bool
otherwise -> do
kept' <- ChainPoint -> TrackedPoints -> TrackedPoints -> m TrackedPoints
undoNewestFirst ChainPoint
point TrackedPoints
kept TrackedPoints
undone
pure (floorPoint, kept')
windowSplit :: ChainPoint -> ChainPoint -> TrackedPoints -> Maybe (TrackedPoints, TrackedPoints)
windowSplit :: ChainPoint
-> ChainPoint
-> TrackedPoints
-> Maybe (TrackedPoints, TrackedPoints)
windowSplit ChainPoint
point ChainPoint
floorPoint TrackedPoints
tracked = case ChainPoint
point of
ChainPoint SlotNo
slot Hash BlockHeader
headerHash
| Just Int
i <- ((SlotNo, Hash BlockHeader) -> Bool) -> TrackedPoints -> Maybe Int
forall a. (a -> Bool) -> Seq a -> Maybe Int
Seq.findIndexL ((SlotNo, Hash BlockHeader) -> (SlotNo, Hash BlockHeader) -> Bool
forall a. Eq a => a -> a -> Bool
== (SlotNo
slot, Hash BlockHeader
headerHash)) TrackedPoints
tracked ->
(TrackedPoints, TrackedPoints)
-> Maybe (TrackedPoints, TrackedPoints)
forall a. a -> Maybe a
Just (Int -> TrackedPoints -> TrackedPoints
forall a. Int -> Seq a -> Seq a
Seq.take Int
i TrackedPoints
tracked, Int -> TrackedPoints -> TrackedPoints
forall a. Int -> Seq a -> Seq a
Seq.drop Int
i TrackedPoints
tracked)
ChainPoint
_
| ChainPoint
point ChainPoint -> ChainPoint -> Bool
forall a. Eq a => a -> a -> Bool
== ChainPoint
floorPoint -> (TrackedPoints, TrackedPoints)
-> Maybe (TrackedPoints, TrackedPoints)
forall a. a -> Maybe a
Just (TrackedPoints
tracked, TrackedPoints
forall a. Seq a
Seq.empty)
| Bool
otherwise -> Maybe (TrackedPoints, TrackedPoints)
forall a. Maybe a
Nothing
undoNewestFirst :: ChainPoint -> TrackedPoints -> TrackedPoints -> m TrackedPoints
undoNewestFirst :: ChainPoint -> TrackedPoints -> TrackedPoints -> m TrackedPoints
undoNewestFirst ChainPoint
point TrackedPoints
kept = TrackedPoints -> m TrackedPoints
loop
where
loop :: TrackedPoints -> m TrackedPoints
loop TrackedPoints
pending = case TrackedPoints -> ViewL (SlotNo, Hash BlockHeader)
forall a. Seq a -> ViewL a
Seq.viewl TrackedPoints
pending of
ViewL (SlotNo, Hash BlockHeader)
Seq.EmptyL -> TrackedPoints -> m TrackedPoints
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure TrackedPoints
kept
(SlotNo
slot, Hash BlockHeader
headerHash) Seq.:< TrackedPoints
rest -> do
fetched <- ChainPoint -> m (Maybe (ByteString, BlockInMode))
fetchBlockByPoint (SlotNo -> Hash BlockHeader -> ChainPoint
ChainPoint SlotNo
slot Hash BlockHeader
headerHash)
case fetched of
Just (ByteString
rawBytes, BlockInMode
blockInMode) -> do
timestamp <- SlotNo -> m UTCTime
slotTimestamp SlotNo
slot
sendMessage $ defMessage & U5c.undo .~ mkAnyChainBlock rawBytes blockInMode timestamp
loop rest
Maybe (ByteString, BlockInMode)
Nothing ->
Proto FollowTipResponse -> m ()
sendMessage (Proto FollowTipResponse
forall msg. Message msg => msg
defMessage Proto FollowTipResponse
-> (Proto FollowTipResponse -> Proto FollowTipResponse)
-> Proto FollowTipResponse
forall a b. a -> (a -> b) -> b
& LensLike' Identity (Proto FollowTipResponse) (Proto BlockRef)
forall (f :: * -> *) s a.
(Functor f, HasField s "reset" a) =>
LensLike' f s a
U5c.reset LensLike' Identity (Proto FollowTipResponse) (Proto BlockRef)
-> Proto BlockRef
-> Proto FollowTipResponse
-> Proto FollowTipResponse
forall s t a b. ASetter s t a b -> b -> s -> t
.~ ChainPoint -> Proto BlockRef
chainPointToBlockRef ChainPoint
point) m () -> TrackedPoints -> m TrackedPoints
forall (f :: * -> *) a b. Functor f => f a -> b -> f b
$> TrackedPoints
kept
trackApplied :: Int -> (SlotNo, Hash BlockHeader) -> TrackedPoints -> TrackedPoints
trackApplied :: Int -> (SlotNo, Hash BlockHeader) -> TrackedPoints -> TrackedPoints
trackApplied Int
cap (SlotNo, Hash BlockHeader)
entry TrackedPoints
tracked = Int -> TrackedPoints -> TrackedPoints
forall a. Int -> Seq a -> Seq a
Seq.take Int
cap ((SlotNo, Hash BlockHeader)
entry (SlotNo, Hash BlockHeader) -> TrackedPoints -> TrackedPoints
forall a. a -> Seq a -> Seq a
Seq.<| TrackedPoints
tracked)
readTipBlockRef
:: MonadIO m
=> Consensus.ChainDB IO (Consensus.CardanoBlock Consensus.StandardCrypto)
-> (SlotNo -> m UTCTime)
-> m (Maybe (Proto U5c.BlockRef))
readTipBlockRef :: forall (m :: * -> *).
MonadIO m =>
ChainDB IO (CardanoBlock StandardCrypto)
-> (SlotNo -> m UTCTime) -> m (Maybe (Proto BlockRef))
readTipBlockRef ChainDB IO (CardanoBlock StandardCrypto)
chainDb SlotNo -> m UTCTime
slotTimestamp = do
tipHeader <- IO (Maybe (Header (CardanoBlock StandardCrypto)))
-> m (Maybe (Header (CardanoBlock StandardCrypto)))
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Maybe (Header (CardanoBlock StandardCrypto)))
-> m (Maybe (Header (CardanoBlock StandardCrypto))))
-> IO (Maybe (Header (CardanoBlock StandardCrypto)))
-> m (Maybe (Header (CardanoBlock StandardCrypto)))
forall a b. (a -> b) -> a -> b
$ ChainDB IO (CardanoBlock StandardCrypto)
-> IO (Maybe (Header (CardanoBlock StandardCrypto)))
forall (m :: * -> *) blk. ChainDB m blk -> m (Maybe (Header blk))
Consensus.getTipHeader ChainDB IO (CardanoBlock StandardCrypto)
chainDb
forM tipHeader $ \Header (CardanoBlock StandardCrypto)
header ->
Header (CardanoBlock StandardCrypto) -> UTCTime -> Proto BlockRef
mkTipBlockRef Header (CardanoBlock StandardCrypto)
header (UTCTime -> Proto BlockRef) -> m UTCTime -> m (Proto BlockRef)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> SlotNo -> m UTCTime
slotTimestamp (Header (CardanoBlock StandardCrypto) -> SlotNo
forall b. HasHeader b => b -> SlotNo
Consensus.blockSlot Header (CardanoBlock StandardCrypto)
header)
slotTimestampOrThrow
:: MonadIO m
=> SystemStart
-> m EraHistory
-> SlotNo
-> m UTCTime
slotTimestampOrThrow :: forall (m :: * -> *).
MonadIO m =>
SystemStart -> m EraHistory -> SlotNo -> m UTCTime
slotTimestampOrThrow SystemStart
systemStart m EraHistory
readEraHistory SlotNo
slot = do
eraHistory <- m EraHistory
readEraHistory
slotToUTCTime systemStart eraHistory slot
& either (const throwPastHorizon) pure
where
throwPastHorizon :: m UTCTime
throwPastHorizon =
GrpcError -> Text -> m UTCTime
forall (m :: * -> *) a. MonadIO m => GrpcError -> Text -> m a
throwGrpcErrorWithMessage GrpcError
GrpcInternal (Text -> m UTCTime) -> Text -> m UTCTime
forall a b. (a -> b) -> a -> b
$
Text
"cannot convert slot "
Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Word64 -> Text
forall a. Show a => a -> Text
tshow (SlotNo -> Word64
unSlotNo SlotNo
slot)
Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" to timestamp: the slot is past the era history horizon;"
Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" check that the requested slot is correct and that the node is fully in sync"