Skip to content

Commit

Permalink
ChainSync client test: enrich with historical rollback check
Browse files Browse the repository at this point in the history
We only test for false positives, not for false negatives.
  • Loading branch information
amesgen committed Jul 25, 2024
1 parent 3b7679b commit 0e1d128
Showing 1 changed file with 74 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,13 @@ import Control.Monad.Class.MonadTimer (MonadTimer)
import Control.Monad.IOSim (runSimOrThrow)
import Control.Tracer (contramap, contramapM, nullTracer)
import Data.DerivingVia (InstantiatedAt (InstantiatedAt))
import Data.List (intercalate)
import Data.List (foldl', intercalate)
import qualified Data.Map.Merge.Strict as Map
import qualified Data.Map.Strict as Map
import Data.Maybe (isJust)
import Data.Semigroup (Max (Max), getMax)
import qualified Data.Set as Set
import Data.Time (diffUTCTime)
import Data.Time (NominalDiffTime, diffUTCTime)
import Data.Typeable
import GHC.Generics (Generic)
import Network.TypedProtocol.Channel
Expand All @@ -88,6 +88,8 @@ import Ouroboros.Consensus.MiniProtocol.ChainSync.Client
DynamicEnv (..), Our (..), Their (..),
TraceChainSyncClientEvent (..), bracketChainSyncClient,
chainSyncClient, chainSyncStateFor, viewChainSyncState)
import Ouroboros.Consensus.MiniProtocol.ChainSync.Client.HistoricalRollbacks
(HistoricalRollbackCheck, MaxRollbackAge (..))
import qualified Ouroboros.Consensus.MiniProtocol.ChainSync.Client.HistoricalRollbacks as HistoricalRollbacks
import qualified Ouroboros.Consensus.MiniProtocol.ChainSync.Client.InFutureCheck as InFutureCheck
import Ouroboros.Consensus.Node.GsmState (GsmState (Syncing))
Expand Down Expand Up @@ -343,11 +345,6 @@ runChainSync skew securityParam (ClientUpdates clientUpdates)
let _ = clientSystemTime :: SystemTime m

varCurrentLogicalTick <- uncheckedNewTVarM (Tick 0)
let clockUpdates :: Schedule NewMaxSlot
clockUpdates =
mkClockUpdates
(ClientUpdates clientUpdates)
(ServerUpdates serverUpdates)

-- Set up the client
varClientState <- uncheckedNewTVarM Genesis
Expand Down Expand Up @@ -404,6 +401,15 @@ runChainSync skew securityParam (ClientUpdates clientUpdates)
-- Note that this tests passes in the exact difference between the
-- client's and server's clock as the tolerable clock skew.

historicalRollbackCheck :: HistoricalRollbackCheck m TestBlock
historicalRollbackCheck =
HistoricalRollbacks.mkCheck
clientSystemTime
-- The historical rollback check is disabled when we use
-- 'CaughtUp' here, so we use 'Syncing'.
(pure Syncing)
maxRollbackAge

lopBucketConfig :: ChainSyncLoPBucketConfig
lopBucketConfig = ChainSyncLoPBucketDisabled

Expand All @@ -421,8 +427,7 @@ runChainSync skew securityParam (ClientUpdates clientUpdates)
, cfg = nodeCfg
, tracer = chainSyncTracer
, someHeaderInFutureCheck = headerInFutureCheck
-- TODO this will be changed in the next commit
, historicalRollbackCheck = HistoricalRollbacks.noCheck
, historicalRollbackCheck
, mkPipelineDecision0 =
pipelineDecisionLowHighMark 10 20
}
Expand All @@ -447,12 +452,7 @@ runChainSync skew securityParam (ClientUpdates clientUpdates)
advanceWallClockForTick tick = do
doTick clockUpdates tick $ \case
[newMaxSlot] -> do
let target = case newMaxSlot of
NewMaxClientSlot slot -> toOnset slot
NewMaxServerSlot slot -> toSkewedOnset slot

NewMaxClientAndServerSlot cslot sslot ->
toOnset cslot `max` toSkewedOnset sslot
let target = clientTimeForNewMaxSlot newMaxSlot
now <- systemTimeCurrent clientSystemTime
threadDelay $ nominalDelay $ target `diffRelTime` now

Expand Down Expand Up @@ -597,6 +597,65 @@ runChainSync skew securityParam (ClientUpdates clientUpdates)
in
RelativeTime $ onset - unClockSkew skew

-- The target time (as reported by 'clientSystemTime') for the given
-- 'NewMaxSlot'.
clientTimeForNewMaxSlot :: NewMaxSlot -> RelativeTime
clientTimeForNewMaxSlot = \case
NewMaxClientSlot slot -> toOnset slot
NewMaxServerSlot slot -> toSkewedOnset slot

NewMaxClientAndServerSlot cslot sslot ->
toOnset cslot `max` toSkewedOnset sslot

clockUpdates :: Schedule NewMaxSlot
clockUpdates =
mkClockUpdates
(ClientUpdates clientUpdates)
(ServerUpdates serverUpdates)

-- Also see the module header for how ticks/time/clock skew are working in
-- this test.
clientTimeForTick :: Tick -> RelativeTime
clientTimeForTick = \tick -> case Map.lookupLE tick clientTimes of
Just (_, time) -> time
-- For every tick, there is a clock update before or at that tick.
Nothing -> error "unreachable"
where
clientTimes :: Map.Map Tick RelativeTime
clientTimes =
Map.foldlWithKey' f Map.empty (getSchedule clockUpdates)
where
f acc t [newMaxSlot] = case Map.lookupMax acc of
Just (_, time')
| time' < time -> Map.insert t time acc
| otherwise -> acc
Nothing -> Map.singleton t time
where
time = clientTimeForNewMaxSlot newMaxSlot
f _ _ _ = error "bad clockUpdates"

-- For the historical rollback check. This is calculated by considering the
-- 'ChainUpdate' which rewinds the oldest block relative to the
-- client wallclock time in the given tick.
maxRollbackAge :: MaxRollbackAge
maxRollbackAge =
-- If there are no rollbacks at all, any value will do.
MaxRollbackAge $ foldl' max 0 rollbackAges
where
rollbackAges :: [NominalDiffTime]
rollbackAges =
[ clientTime `diffRelTime` toSkewedOnset oldestRewound
| (tick, updates) <- Map.toList $ getSchedule serverUpdates
, let clientTime = clientTimeForTick tick
, SwitchFork rollbackPoint _blks <- updates
, -- Here, we make use of the fact that the blocks generated for this
-- test have dense slot numbers (ie there are no empty slots).
let oldestRewound =
withOrigin firstSlot succ $ pointSlot rollbackPoint
]

firstSlot = blockSlot $ firstBlock 0

doTick :: Schedule a -> Tick -> ([a] -> m ()) -> m ()
doTick sched tick kont = whenJust (Map.lookup tick (getSchedule sched)) kont

Expand Down

0 comments on commit 0e1d128

Please sign in to comment.