@@ -23,6 +23,7 @@ module Cardano.Wallet.Kernel.NodeStateAdaptor (
2323 , getSlotCount
2424 , getSlotStart
2525 , getNextEpochSlotDuration
26+ , getNodeSyncProgress
2627 , curSoftwareVersion
2728 , compileInfo
2829 , getNtpDrift
@@ -42,10 +43,10 @@ module Cardano.Wallet.Kernel.NodeStateAdaptor (
4243
4344import Universum
4445
45- import Control.Lens (lens )
46+ import Control.Lens (lens , to )
4647import Control.Monad.IO.Unlift (MonadUnliftIO , UnliftIO (UnliftIO ),
4748 askUnliftIO , unliftIO , withUnliftIO )
48- import Control.Monad.STM (retry )
49+ import Control.Monad.STM (orElse , retry )
4950import Data.Conduit (mapOutputMaybe , runConduitRes , (.|) )
5051import qualified Data.Conduit.List as Conduit
5152import Data.SafeCopy (base , deriveSafeCopy )
@@ -57,14 +58,16 @@ import Ntp.Packet (NtpOffset)
5758import Serokell.Data.Memory.Units (Byte )
5859
5960import qualified Cardano.Wallet.API.V1.Types as V1
60- import Pos.Chain.Block (Block , HeaderHash , MainBlock , blockHeader ,
61- headerHash , mainBlockSlot , prevBlockL )
61+ import Pos.Chain.Block (Block , HeaderHash , LastKnownHeader ,
62+ LastKnownHeaderTag , MainBlock , blockHeader , headerHash ,
63+ mainBlockSlot , prevBlockL )
6264import Pos.Chain.Update (ConfirmedProposalState ,
6365 HasUpdateConfiguration , SoftwareVersion , bvdMaxTxSize )
6466import qualified Pos.Chain.Update as Upd
6567import Pos.Context (NodeContext (.. ))
66- import Pos.Core (ProtocolConstants (pcK ), SlotCount , Timestamp ,
67- genesisBlockVersionData , pcEpochSlots )
68+ import Pos.Core (BlockCount , ProtocolConstants (pcK ), SlotCount ,
69+ Timestamp , difficultyL , genesisBlockVersionData ,
70+ getChainDifficulty , pcEpochSlots )
6871import Pos.Core.Configuration (HasConfiguration , genesisHash ,
6972 protocolConstants )
7073import Pos.Core.Slotting (EpochIndex (.. ), HasSlottingVar (.. ),
@@ -257,6 +260,18 @@ data NodeStateAdaptor m = Adaptor {
257260 -- | Get last known slot duration.
258261 , getNextEpochSlotDuration :: m Millisecond
259262
263+ -- | Get the "sync progress". This term is desperately overloaded but
264+ -- in this context we need something very simple: a tuple containing the
265+ -- "global blockchain height" and the "node blockchain height". The
266+ -- former is the maximum between the biggest height we observed from an
267+ -- unsolicited block we received and the current local tip:
268+ --
269+ -- global_height = max (last_known_header, node_local_tip)
270+ --
271+ -- The latter is simply the node local tip, i.e. "how far we went into
272+ -- chasing the global blockchain height during syncing".
273+ , getNodeSyncProgress :: LockContext -> m (Maybe BlockCount , BlockCount )
274+
260275 -- | Version of application (code running)
261276 , curSoftwareVersion :: m SoftwareVersion
262277
@@ -265,6 +280,7 @@ data NodeStateAdaptor m = Adaptor {
265280
266281 -- | Ask the NTP client for the status
267282 , getNtpDrift :: V1. ForceNtpCheck -> m V1. TimeInfo
283+
268284 }
269285
270286{- ------------------------------------------------------------------------------
@@ -298,6 +314,9 @@ instance HasLens S.SimpleSlottingStateVar Res S.SimpleSlottingStateVar where
298314instance HasLens UpdateContext Res UpdateContext where
299315 lensOf = mkResLens (nrContextLens . lensOf')
300316
317+ instance HasLens LastKnownHeaderTag Res LastKnownHeader where
318+ lensOf = mkResLens (nrContextLens . lensOf @ LastKnownHeaderTag )
319+
301320instance HasSlottingVar Res where
302321 slottingTimestamp = mkResLens (nrContextLens . slottingTimestamp)
303322 slottingVar = mkResLens (nrContextLens . slottingVar)
@@ -346,11 +365,12 @@ newNodeStateAdaptor :: forall m ext. (NodeConstraints, MonadIO m, MonadMask m)
346365 -> TVar NtpStatus
347366 -> NodeStateAdaptor m
348367newNodeStateAdaptor nr ntpStatus = Adaptor {
349- withNodeState = run
350- , getTipSlotId = run $ \ _lock -> defaultGetTipSlotId
351- , getMaxTxSize = run $ \ _lock -> defaultGetMaxTxSize
352- , getSlotStart = \ slotId -> run $ \ _lock -> defaultGetSlotStart slotId
353- , getNextEpochSlotDuration = run $ \ _lock -> defaultGetNextEpochSlotDuration
368+ withNodeState = run
369+ , getTipSlotId = run $ \ _lock -> defaultGetTipSlotId
370+ , getMaxTxSize = run $ \ _lock -> defaultGetMaxTxSize
371+ , getSlotStart = \ slotId -> run $ \ _lock -> defaultGetSlotStart slotId
372+ , getNextEpochSlotDuration = run $ \ _lock -> defaultGetNextEpochSlotDuration
373+ , getNodeSyncProgress = \ lockCtx -> run $ defaultSyncProgress lockCtx
354374 , getSecurityParameter = return $ pcK' protocolConstants
355375 , getSlotCount = return $ pcEpochSlots protocolConstants
356376 , curSoftwareVersion = return $ Upd. curSoftwareVersion
@@ -405,6 +425,23 @@ defaultGetSlotStart slotId =
405425defaultGetNextEpochSlotDuration :: MonadIO m => WithNodeState m Millisecond
406426defaultGetNextEpochSlotDuration = Slotting. getNextEpochSlotDuration
407427
428+ defaultSyncProgress :: (MonadIO m , MonadMask m , NodeConstraints )
429+ => LockContext
430+ -> Lock (WithNodeState m )
431+ -> WithNodeState m (Maybe BlockCount , BlockCount )
432+ defaultSyncProgress lockContext lock = do
433+ (globalHeight, localHeight) <- lock lockContext $ \ _localTipHash -> do
434+ -- We need to grab the localTip again as '_localTip' has type
435+ -- 'HeaderHash' but we cannot grab the difficulty out of it.
436+ headerRef <- view (lensOf @ LastKnownHeaderTag )
437+ localTip <- getTipHeader
438+ mbHeader <- atomically $ readTVar headerRef `orElse` pure Nothing
439+ pure (view (difficultyL . to getChainDifficulty) <$> mbHeader
440+ ,view (difficultyL . to getChainDifficulty) localTip
441+ )
442+ return (max localHeight <$> globalHeight, localHeight)
443+
444+
408445{- ------------------------------------------------------------------------------
409446 Non-mockable functions
410447-------------------------------------------------------------------------------}
@@ -511,6 +548,7 @@ mockNodeState MockNodeStateParams{..} =
511548 , getTipSlotId = return mockNodeStateTipSlotId
512549 , getSecurityParameter = return mockNodeStateSecurityParameter
513550 , getNextEpochSlotDuration = return mockNodeStateNextEpochSlotDuration
551+ , getNodeSyncProgress = \ _ -> return mockNodeStateSyncProgress
514552 , getSlotStart = return . mockNodeStateSlotStart
515553 , getMaxTxSize = return $ bvdMaxTxSize genesisBlockVersionData
516554 , getSlotCount = return $ pcEpochSlots protocolConstants
@@ -540,6 +578,9 @@ data MockNodeStateParams = NodeConstraints => MockNodeStateParams {
540578 -- | Value for 'getNextEpochSlotDuration'
541579 , mockNodeStateNextEpochSlotDuration :: Millisecond
542580
581+ -- | Value for 'getNodeSyncProgress'
582+ , mockNodeStateSyncProgress :: (Maybe BlockCount , BlockCount )
583+
543584 -- | Value for 'getNtpDrift'
544585 , mockNodeStateNtpDrift :: V1. ForceNtpCheck -> V1. TimeInfo
545586 }
@@ -563,6 +604,7 @@ defMockNodeStateParams =
563604 mockNodeStateTipSlotId = notDefined " mockNodeStateTipSlotId"
564605 , mockNodeStateSlotStart = notDefined " mockNodeStateSlotStart"
565606 , mockNodeStateNextEpochSlotDuration = notDefined " mockNodeStateNextEpochSlotDuration"
607+ , mockNodeStateSyncProgress = notDefined " mockNodeStateSyncProgress"
566608 , mockNodeStateSecurityParameter = SecurityParameter 2160
567609 , mockNodeStateNtpDrift = const (V1. TimeInfo Nothing )
568610 }
0 commit comments