Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changes/20260720_cardano_api_chaindb_tip_reexports.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
project: cardano-api
pr: 1259
kind:
- compatible
description: |
Re-export getTipHeader, blockHash and blockSlot from Cardano.Api.Consensus.
6 changes: 6 additions & 0 deletions .changes/20260720_cardano_rpc_read_tip.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
project: cardano-rpc
pr: 1259
kind:
- feature
description: |
Implement the ReadTip SyncService method: returns the current chain tip as slot, block hash, height and timestamp.
3 changes: 3 additions & 0 deletions cardano-api/src/Cardano/Api/Consensus.hs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ module Cardano.Api.Consensus
, ChainDB.ChainDB
, ChainDB.getBlockComponent
, ChainDB.getCurrentLedger
, ChainDB.getTipHeader
, ConfigSupportsNode
, ChainDepState
, GenTx (..)
Expand All @@ -71,7 +72,9 @@ module Cardano.Api.Consensus
, StandardCrypto
, TopLevelConfig
, ledgerState
, blockHash
, blockNo
, blockSlot
, byronBlockRaw
, byronIdTx
, configBlock
Expand Down
11 changes: 10 additions & 1 deletion cardano-api/src/Cardano/Api/Consensus/Internal/Reexport.hs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ module Cardano.Api.Consensus.Internal.Reexport
, StandardCrypto
, TopLevelConfig
, ledgerState
, blockHash
, blockNo
, blockSlot
, byronBlockRaw
, byronIdTx
, configBlock
Expand All @@ -34,7 +36,14 @@ module Cardano.Api.Consensus.Internal.Reexport
where

import Cardano.Protocol.Crypto (StandardCrypto)
import Ouroboros.Consensus.Block (HasHeader, HeaderHash, RealPoint (..), blockNo)
import Ouroboros.Consensus.Block
( HasHeader
, HeaderHash
, RealPoint (..)
, blockHash
, blockNo
, blockSlot
)
import Ouroboros.Consensus.Byron.Ledger (ByronBlock (byronBlockRaw), GenTx (..), byronIdTx)
import Ouroboros.Consensus.Cardano.Block (CardanoBlock, EraMismatch (..))
import Ouroboros.Consensus.Config (TopLevelConfig, configBlock, configLedger)
Expand Down
2 changes: 1 addition & 1 deletion cardano-rpc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ It implements [UTxO RPC](https://utxorpc.org/introduction) protobuf communicatio
| [FetchBlock](https://utxorpc.org/sync/spec/#fetchblockrequest) | ✅ Supported |
| [DumpHistory](https://utxorpc.org/sync/spec/#dumphistoryrequest) | ⬜ Not supported |
| [FollowTip](https://utxorpc.org/sync/spec/#followtiprequest) | ⬜ Not supported |
| [ReadTip](https://utxorpc.org/sync/spec/#readtiprequest) | ⬜ Not supported |
| [ReadTip](https://utxorpc.org/sync/spec/#readtiprequest) | ✅ Supported |

### [WatchService](https://utxorpc.org/watch/spec/)

Expand Down
2 changes: 1 addition & 1 deletion cardano-rpc/src/Cardano/Rpc/Server.hs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ methodsSyncRpc =
Method (mkNonStreaming $ const unimplemented) -- dumpHistory
. Method (mkNonStreaming $ wrapInSpan TraceRpcFetchBlockSpan . fetchBlockMethod)
. Method (mkServerStreaming $ \_ _ -> unimplemented) -- followTip
. Method (mkNonStreaming $ const unimplemented) -- readTip
. Method (mkNonStreaming $ wrapInSpan TraceRpcReadTipSpan . readTipMethod)
$ NoMoreMethods
where
unimplemented =
Expand Down
6 changes: 5 additions & 1 deletion cardano-rpc/src/Cardano/Rpc/Server/Internal/Tracing.hs
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,12 @@ instance Pretty TraceRpcSubmit where
instance Error TraceRpcSubmit where
prettyError = pretty

-- | Traces used in SyncService (FetchBlock, FollowTip)
-- | Traces used in SyncService (FetchBlock, ReadTip, FollowTip)
data TraceRpcSync
= -- | FetchBlock span
TraceRpcFetchBlockSpan TraceSpanEvent
| -- | ReadTip span
TraceRpcReadTipSpan TraceSpanEvent
| -- | Requested block was not found
TraceRpcFetchBlockNotFound SlotNo
| -- | Node kernel access is not yet available
Expand All @@ -111,6 +113,8 @@ instance Pretty TraceRpcSync where
pretty = \case
TraceRpcFetchBlockSpan (SpanBegin _) -> "Started FetchBlock method"
TraceRpcFetchBlockSpan (SpanEnd _) -> "Finished FetchBlock method"
TraceRpcReadTipSpan (SpanBegin _) -> "Started ReadTip method"
TraceRpcReadTipSpan (SpanEnd _) -> "Finished ReadTip method"
TraceRpcFetchBlockNotFound slot -> "Block not found at slot " <> pshow slot
TraceRpcNodeKernelAccessUnavailable -> "Node kernel access not yet initialised"
TraceRpcForkerError e -> "Ledger forker error: " <> pretty e
Expand Down
35 changes: 35 additions & 0 deletions cardano-rpc/src/Cardano/Rpc/Server/Internal/UtxoRpc/Sync.hs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@
-- (fetching blocks, dumping history, following the tip).
module Cardano.Rpc.Server.Internal.UtxoRpc.Sync
( fetchBlockMethod
, 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
Expand All @@ -23,6 +25,7 @@ import Cardano.Rpc.Server.NodeKernelAccess
import RIO

import Data.ByteString qualified as BS
import Data.ByteString.Short qualified as SBS
import Data.ProtoLens (defMessage)
import Data.Time.Clock.POSIX (utcTimeToPOSIXSeconds)
import Network.GRPC.Spec (GrpcError (GrpcInternal, GrpcInvalidArgument, GrpcNotFound), Proto)
Expand Down Expand Up @@ -88,3 +91,35 @@ fetchBlockMethod request = do
& U5c.cardano . U5c.body . U5c.tx .~ txs
& U5c.cardano . U5c.timestamp .~ timestampMs
)

-- | Handle the @ReadTip@ SyncService RPC method.
-- Reads the current chain tip from ChainDB and returns it as slot, block
-- header hash, block height and slot timestamp.
-- When the chain is at origin, the tip field is left unset.
readTipMethod
:: MonadRpc e m
=> Proto U5c.ReadTipRequest
-> m (Proto U5c.ReadTipResponse)
readTipMethod _request = do
NodeKernelAccess{chainDb, systemStart, readEraHistory} <- grabNodeKernelAccess
tipHeader <- liftIO $ Consensus.getTipHeader chainDb
tip <- forM tipHeader $ \header -> do
let slot = Consensus.blockSlot header
Consensus.OneEraHash tipHash = Consensus.blockHash header
BlockNo height = Consensus.blockNo header
throwPastHorizon =
throwGrpcErrorWithMessage GrpcInternal $
"cannot convert tip slot "
<> tshow (unSlotNo slot)
<> " to timestamp: the slot is past the era history horizon"
eraHistory <- readEraHistory
timestampMs <-
slotToUTCTime systemStart eraHistory slot
& either (const throwPastHorizon) (pure . round . (* 1000) . utcTimeToPOSIXSeconds)
pure $
defMessage
& U5c.slot .~ unSlotNo slot
& U5c.hash .~ SBS.fromShort tipHash
& U5c.height .~ height
& U5c.timestamp .~ timestampMs
pure $ defMessage & U5c.maybe'tip .~ tip
Loading