From fef1c47230e1c17b24db73b6f61fea363d0090a3 Mon Sep 17 00:00:00 2001 From: Alexander Esgen Date: Tue, 19 Apr 2022 17:28:08 +0200 Subject: [PATCH] Add a basic ThreadNet test for pipelining of forged blocks --- .../src/Test/ThreadNet/General.hs | 35 +++++++++++++++++++ .../src/Test/ThreadNet/Network.hs | 25 +++++++++---- 2 files changed, 54 insertions(+), 6 deletions(-) diff --git a/ouroboros-consensus-test/src/Test/ThreadNet/General.hs b/ouroboros-consensus-test/src/Test/ThreadNet/General.hs index 7bd80ad84c3..7018949f5d7 100644 --- a/ouroboros-consensus-test/src/Test/ThreadNet/General.hs +++ b/ouroboros-consensus-test/src/Test/ThreadNet/General.hs @@ -57,6 +57,7 @@ import Ouroboros.Consensus.Node.Run import Ouroboros.Consensus.NodeId import Ouroboros.Consensus.Protocol.Abstract (LedgerView) import Ouroboros.Consensus.Protocol.LeaderSchedule +import qualified Ouroboros.Consensus.Storage.ChainDB as ChainDB import Ouroboros.Consensus.TypeFamilyWrappers import Ouroboros.Consensus.Util.Condense @@ -478,6 +479,7 @@ prop_general_internal syncity pga testOutput = prop_no_BlockRejections .&&. prop_no_unexpected_CannotForges .&&. prop_no_invalid_blocks .&&. + prop_pipelining .&&. propSync ( prop_all_common_prefix maxForkLength (Map.elems nodeChains) .&&. prop_all_growth .&&. @@ -854,6 +856,39 @@ prop_general_internal syncity pga testOutput = , (s, blk) <- Map.toAscList nodeOutputForges ] + -- Check that all self-issued blocks are pipelined. + prop_pipelining :: Property + prop_pipelining = conjoin + [ counterexample ("Node " <> condense nid <> " did not pipeline") $ + counterexample ("some of its blocks forged as the sole slot leader:") $ + counterexample (condense forgedButNotPipelined) $ + Set.null forgedButNotPipelined + | (nid, NodeOutput + { nodeOutputForges + , nodePipeliningEvents + }) <- Map.toList testOutputNodes + , CoreId cnid <- [nid] + , let tentativePoints = Set.fromList + [ headerPoint hdr + | ChainDB.SetTentativeHeader hdr <- nodePipeliningEvents + ] + forgedAsSoleLeaderPoints = Set.fromList $ + [ blockPoint blk + | blk <- Map.elems nodeOutputForges + , let s = blockSlot blk + NodeRestarts nrs = nodeRestarts + , getLeaderSchedule actualLeaderSchedule Map.! s == [cnid] + -- When the node is restarted while it is a slot + -- leader, this property is often not satisfied in + -- the Byron ThreadNet tests. As diffusion + -- pipelining is concerned with up-to-date, + -- long-running nodes, we ignore this edge case. + , cnid `Map.notMember` Map.findWithDefault mempty s nrs + ] + forgedButNotPipelined = + forgedAsSoleLeaderPoints Set.\\ tentativePoints + ] + {------------------------------------------------------------------------------- Final chains properties -------------------------------------------------------------------------------} diff --git a/ouroboros-consensus-test/src/Test/ThreadNet/Network.hs b/ouroboros-consensus-test/src/Test/ThreadNet/Network.hs index b66a14e5d61..0aa473b157a 100644 --- a/ouroboros-consensus-test/src/Test/ThreadNet/Network.hs +++ b/ouroboros-consensus-test/src/Test/ThreadNet/Network.hs @@ -692,13 +692,14 @@ runThreadNetwork systemTime ThreadNetworkArgs -- ^ block selection tracer -> Tracer m (LedgerUpdate blk) -- ^ ledger updates tracer + -> Tracer m (ChainDB.TracePipeliningEvent blk) -> NodeDBs (StrictTVar m MockFS) -> CoreNodeId -> ChainDbArgs Identity m blk mkArgs clock registry cfg initLedger - invalidTracer addTracer selTracer updatesTracer + invalidTracer addTracer selTracer updatesTracer pipeliningTracer nodeDBs _coreNodeId = ChainDbArgs { -- HasFS instances cdbHasFSImmutableDB = SomeHasFS $ simHasFS (nodeDBsImm nodeDBs) @@ -754,6 +755,10 @@ runThreadNetwork systemTime ThreadNetworkArgs mapM_ (traceWith updatesTracer) updates traceWith selTracer (ChainDB.newTipPoint p, prj new) + ChainDB.TraceAddBlockEvent + (ChainDB.PipeliningEvent e) + -> traceWith pipeliningTracer e + _ -> pure () -- We don't expect any ledger warnings @@ -794,9 +799,10 @@ runThreadNetwork systemTime ThreadNetworkArgs wrapTracer tr = Tracer $ \(p, bno) -> do s <- OracularClock.getCurrentSlot clock traceWith tr (s, p, bno) - addTracer = wrapTracer $ nodeEventsAdds nodeInfoEvents - selTracer = wrapTracer $ nodeEventsSelects nodeInfoEvents - headerAddTracer = wrapTracer $ nodeEventsHeaderAdds nodeInfoEvents + addTracer = wrapTracer $ nodeEventsAdds nodeInfoEvents + selTracer = wrapTracer $ nodeEventsSelects nodeInfoEvents + headerAddTracer = wrapTracer $ nodeEventsHeaderAdds nodeInfoEvents + pipeliningTracer = nodeEventsPipelining nodeInfoEvents let chainDbArgs = mkArgs clock registry pInfoConfig pInfoInitLedger @@ -804,6 +810,7 @@ runThreadNetwork systemTime ThreadNetworkArgs addTracer selTracer updatesTracer + pipeliningTracer nodeInfoDBs coreNodeId chainDB <- snd <$> @@ -1396,6 +1403,8 @@ data NodeEvents blk ev = NodeEvents -- ^ 'ChainDB.getTipBlockNo' for each node at the onset of each slot , nodeEventsUpdates :: ev (LedgerUpdate blk) -- ^ Ledger updates every time we adopt a block/switch to a fork + , nodeEventsPipelining :: ev (ChainDB.TracePipeliningEvent blk) + -- ^ Pipelining events tracking the tentative header } -- | A vector with an element for each database of a node @@ -1423,9 +1432,10 @@ newNodeInfo = do (t5, m5) <- recordingTracerTVar (t6, m6) <- recordingTracerTVar (t7, m7) <- recordingTracerTVar + (t8, m8) <- recordingTracerTVar pure - ( NodeEvents t1 t2 t3 t4 t5 t6 t7 - , NodeEvents <$> m1 <*> m2 <*> m3 <*> m4 <*> m5 <*> m6 <*> m7 + ( NodeEvents t1 t2 t3 t4 t5 t6 t7 t8 + , NodeEvents <$> m1 <*> m2 <*> m3 <*> m4 <*> m5 <*> m6 <*> m7 <*> m8 ) (nodeInfoDBs, readDBs) <- do @@ -1461,6 +1471,7 @@ data NodeOutput blk = NodeOutput , nodeOutputNodeDBs :: NodeDBs MockFS , nodeOutputSelects :: Map SlotNo [(RealPoint blk, BlockNo)] , nodeOutputUpdates :: [LedgerUpdate blk] + , nodePipeliningEvents :: [ChainDB.TracePipeliningEvent blk] } data TestOutput blk = TestOutput @@ -1494,6 +1505,7 @@ mkTestOutput vertexInfos = do , nodeEventsSelects , nodeEventsTipBlockNos , nodeEventsUpdates + , nodeEventsPipelining } = nodeInfoEvents let nodeOutput = NodeOutput { nodeOutputAdds = @@ -1520,6 +1532,7 @@ mkTestOutput vertexInfos = do , nodeOutputInvalids = (:[]) <$> Map.fromList nodeEventsInvalids , nodeOutputNodeDBs = nodeInfoDBs , nodeOutputUpdates = nodeEventsUpdates + , nodePipeliningEvents = nodeEventsPipelining } pure