Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Annotate headers with time #1288

Draft
wants to merge 14 commits into
base: main
Choose a base branch
from
Draft
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,9 @@ initNodeKernel args@NodeKernelArgs { registry, cfg, tracers
, gsmTracer tracers
)

-- TODO: this should be a library function if the idea of dropping time on the candidate fragment makes sense.
dropTime = undefined

let gsm = GSM.realGsmEntryPoints gsmTracerArgs GSM.GsmView
{ GSM.antiThunderingHerd = Just gsmAntiThunderingHerd
, GSM.candidateOverSelection = \(headers, _lst) state ->
Expand All @@ -241,7 +244,8 @@ initNodeKernel args@NodeKernelArgs { registry, cfg, tracers
preferAnchoredCandidate
(configBlock cfg)
headers
(csCandidate state)
-- REVIEW: is it ok to drop the times here because we're initializing the node kernel?
(dropTime $ csCandidate state)
dnadales marked this conversation as resolved.
Show resolved Hide resolved
, GSM.peerIsIdle = csIdling
, GSM.durationUntilTooOld =
gsmDurationUntilTooOld
Expand Down Expand Up @@ -388,7 +392,7 @@ initInternalState NodeKernelArgs { tracers, chainDB, registry, cfg

fetchClientRegistry <- newFetchClientRegistry

let getCandidates :: STM m (Map (ConnectionId addrNTN) (AnchoredFragment (Header blk)))
let getCandidates :: STM m (Map (ConnectionId addrNTN) (AnchoredFragment (HeaderWithTime blk)))
getCandidates = viewChainSyncState varChainSyncHandles csCandidate

slotForgeTimeOracle <- BlockFetchClientInterface.initSlotForgeTimeOracle cfg chainDB
Expand All @@ -401,7 +405,7 @@ initInternalState NodeKernelArgs { tracers, chainDB, registry, cfg
blockFetchInterface = BlockFetchClientInterface.mkBlockFetchConsensusInterface
(configBlock cfg)
(BlockFetchClientInterface.defaultChainDbView chainDB)
getCandidates
getCandidates -- REVIEW: note for myself. This is what enables the use of 'HeaderWithTime' to obtain the time slot of the header, removing the need of using the HFC to compute the header time.
blockFetchSize
slotForgeTimeOracle
readFetchMode
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import Ouroboros.Consensus.Config.SecurityParam
(SecurityParam (SecurityParam), maxRollbacks)
import Ouroboros.Consensus.Genesis.Governor (DensityBounds,
densityDisconnect, sharedCandidatePrefix)
import Ouroboros.Consensus.HeaderValidation (HeaderWithTime)
import Ouroboros.Consensus.MiniProtocol.ChainSync.Client
(ChainSyncClientException (DensityTooLow),
ChainSyncState (..))
Expand Down Expand Up @@ -93,6 +94,16 @@ data StaticCandidates =
}
deriving Show

addTime ::
AnchoredFragment (Header TestBlock)
-> AnchoredFragment (HeaderWithTime TestBlock)
addTime = undefined

dropTime ::
AnchoredFragment (HeaderWithTime blk)
-> AnchoredFragment (Header blk)
dropTime = undefined

-- | Define one selection for each branch of the given block tree, consisting of the first @k@ blocks (or what's
-- available) of the branch's suffix.
--
Expand All @@ -106,13 +117,13 @@ staticCandidates GenesisTest {gtSecurityParam, gtGenesisWindow, gtBlockTree} =
StaticCandidates {
k = gtSecurityParam,
sgen = gtGenesisWindow,
suffixes,
suffixes = fmap (second dropTime) suffixes,
tips,
loeFrag
loeFrag = dropTime loeFrag
}
where
(loeFrag, suffixes) =
sharedCandidatePrefix curChain (second toHeaders <$> candidates)
sharedCandidatePrefix curChain (second (addTime . toHeaders) <$> candidates)

selections = selection <$> branches

Expand All @@ -133,7 +144,7 @@ staticCandidates GenesisTest {gtSecurityParam, gtGenesisWindow, gtBlockTree} =
prop_densityDisconnectStatic :: Property
prop_densityDisconnectStatic =
forAll gen $ \ StaticCandidates {k, sgen, suffixes, loeFrag} -> do
let (disconnect, _) = densityDisconnect sgen k (mkState <$> Map.fromList suffixes) suffixes loeFrag
let (disconnect, _) = densityDisconnect sgen k (mkState <$> Map.fromList suffixes) (fmap (second addTime) suffixes) (addTime loeFrag)
counterexample "it should disconnect some node" (not (null disconnect))
.&&.
counterexample "it should not disconnect the honest peers"
Expand All @@ -142,7 +153,7 @@ prop_densityDisconnectStatic =
mkState :: AnchoredFragment (Header TestBlock) -> ChainSyncState TestBlock
mkState frag =
ChainSyncState {
csCandidate = frag,
csCandidate = addTime frag,
csLatestSlot = SJust (AF.headSlot frag),
csIdling = False
}
Expand Down Expand Up @@ -377,26 +388,26 @@ evolveBranches EvolvingPeers {k, sgen, peers = initialPeers, fullTree} =
states =
candidates <&> \ csCandidate ->
ChainSyncState {
csCandidate,
csCandidate = addTime csCandidate,
csIdling = False,
csLatestSlot = SJust (AF.headSlot csCandidate)
}
-- Run GDD.
(loeFrag, suffixes) = sharedCandidatePrefix curChain (Map.toList candidates)
(loeFrag, suffixes) = sharedCandidatePrefix curChain (Map.toList $ fmap addTime candidates)
(killedNow, bounds) = first Set.fromList $ densityDisconnect sgen k states suffixes loeFrag
event = UpdateEvent {
target,
added,
killed = killedNow,
bounds,
tree = snapshotTree nextPeers,
loeFrag,
loeFrag = dropTime loeFrag,
curChain
}
newEvents = event : events
-- Check the termination condition and remove exhausted peers.
updated = updatePeers sgen nextPeers killedBefore event
either (pure . result newEvents loeFrag) (step newEvents) updated
either (pure . result newEvents (dropTime loeFrag)) (step newEvents) updated
where
result evs f (res, final) = (res, EvolvingPeers {k, sgen, peers = final, loeFrag = f, fullTree}, reverse evs)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import Network.TypedProtocol.Codec (AnyMessage, PeerHasAgency (..),
PeerRole)
import Ouroboros.Consensus.Block (HasHeader)
import Ouroboros.Consensus.Block.Abstract (Header, Point (..))
import Ouroboros.Consensus.HeaderValidation (HeaderWithTime (..))
import qualified Ouroboros.Consensus.MiniProtocol.BlockFetch.ClientInterface as BlockFetchClientInterface
import Ouroboros.Consensus.Node.ProtocolInfo
(NumCoreNodes (NumCoreNodes))
Expand Down Expand Up @@ -78,7 +79,7 @@ startBlockFetchLogic ::
-> Tracer m (TraceEvent TestBlock)
-> ChainDB m TestBlock
-> FetchClientRegistry PeerId (Header TestBlock) TestBlock m
-> STM m (Map PeerId (AnchoredFragment (Header TestBlock)))
-> STM m (Map PeerId (AnchoredFragment (HeaderWithTime TestBlock)))
-> m ()
startBlockFetchLogic registry tracer chainDb fetchClientRegistry getCandidates = do
let slotForgeTime :: BlockFetchClientInterface.SlotForgeTimeOracle m blk
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,8 @@ mkStateTracer schedulerConfig GenesisTest {gtBlockTree} PeerSimulatorResources {
, let getCandidates = viewChainSyncState psrHandles CSClient.csCandidate
getCurrentChain = ChainDB.getCurrentChain chainDb
getPoints = traverse readTVar (srCurrentState . prShared <$> psrPeers)
= peerSimStateDiagramSTMTracerDebug gtBlockTree getCurrentChain getCandidates getPoints
-- REVIEW: is it ok to drop time here?
= peerSimStateDiagramSTMTracerDebug gtBlockTree getCurrentChain (undefined getCandidates) getPoints
Comment on lines +321 to +322
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think so; only performance could theoretically be a concern here, but I don't think so.

| otherwise
= pure nullTracer

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import Ouroboros.Consensus.Block (GenesisWindow (..), Header, Point,
WithOrigin (NotOrigin, Origin), succWithOrigin)
import Ouroboros.Consensus.Genesis.Governor (DensityBounds (..),
GDDDebugInfo (..), TraceGDDEvent (..))
import Ouroboros.Consensus.HeaderValidation (HeaderWithTime (..))
import Ouroboros.Consensus.MiniProtocol.ChainSync.Client
(TraceChainSyncClientEvent (..))
import Ouroboros.Consensus.MiniProtocol.ChainSync.Client.Jumping
Expand All @@ -52,8 +53,9 @@ import Ouroboros.Network.Block (SlotNo (SlotNo), Tip, castPoint)
import Test.Consensus.PointSchedule.NodeState (NodeState)
import Test.Consensus.PointSchedule.Peers (Peer (Peer), PeerId)
import Test.Util.TersePrinting (terseAnchor, terseBlock,
terseFragment, terseHFragment, terseHeader, tersePoint,
terseRealPoint, terseTip, terseWithOrigin)
terseFragment, terseHFragment, terseHWTFragment,
terseHeader, tersePoint, terseRealPoint, terseTip,
terseWithOrigin)
import Test.Util.TestBlock (TestBlock)
import Text.Printf (printf)

Expand All @@ -75,7 +77,7 @@ data TraceSchedulerEvent blk
DiffTime
(Peer (NodeState blk))
(AnchoredFragment (Header blk))
(Maybe (AnchoredFragment (Header blk)))
(Maybe (AnchoredFragment (HeaderWithTime blk)))
[(PeerId, ChainSyncJumpingState m blk)]
| TraceNodeShutdownStart (WithOrigin SlotNo)
| TraceNodeShutdownComplete
Expand Down Expand Up @@ -218,7 +220,7 @@ traceSchedulerEventTestBlockWith setTickTime tracer0 _tracer = \case
" peer: " ++ condense pid,
" state: " ++ condense state,
" current chain: " ++ terseHFragment currentChain,
" candidate fragment: " ++ maybe "Nothing" terseHFragment mCandidateFrag,
" candidate fragment: " ++ maybe "Nothing" terseHWTFragment mCandidateFrag,
" jumping states:\n" ++ traceJumpingStates jumpingStates
]
TraceNodeShutdownStart immTip ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ module Test.Util.TersePrinting (
, terseBlock
, terseFragment
, terseHFragment
, terseHWTFragment
, terseHeader
, terseMaybe
, tersePoint
Expand All @@ -24,12 +25,13 @@ import Ouroboros.Consensus.Block (Header,
Point (BlockPoint, GenesisPoint), RealPoint,
SlotNo (SlotNo), blockHash, blockNo, blockSlot,
realPointToPoint)
import Ouroboros.Consensus.HeaderValidation (HeaderWithTime (..))
import Ouroboros.Network.AnchoredFragment (Anchor, AnchoredFragment,
anchor, anchorToPoint, mapAnchoredFragment, toOldestFirst)
import Ouroboros.Network.Block (Tip (..))
import Ouroboros.Network.Point (WithOrigin (..))
import Test.Util.TestBlock (Header (TestHeader), TestBlock,
TestHash (TestHash), unTestHash)
import Test.Util.TestBlock (Header (TestHeader, testHeader),
TestBlock, TestHash (TestHash), unTestHash)

-- | Run-length encoding of a list. This groups consecutive duplicate elements,
-- counting them. Only the first element of the equality is kept. For instance:
Expand Down Expand Up @@ -118,6 +120,12 @@ terseFragment fragment =
terseHFragment :: AnchoredFragment (Header TestBlock) -> String
terseHFragment = terseFragment . mapAnchoredFragment (\(TestHeader block) -> block)

-- | Same as 'terseFragment' for fragments of headers with time.
--
-- TOOD: factor out common functionality if this function will stay.
terseHWTFragment :: AnchoredFragment (HeaderWithTime TestBlock) -> String
terseHWTFragment = terseFragment . mapAnchoredFragment (testHeader . hwtHeader)

-- | Same as 'terseWithOrigin' for 'Maybe'.
terseMaybe :: (a -> String) -> Maybe a -> String
terseMaybe _ Nothing = "X"
Expand Down
2 changes: 1 addition & 1 deletion ouroboros-consensus/bench/ChainSync-client-bench/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ main = withStdTerminalHandles $ mainWith $ \n -> do

{-# INLINE oneBenchRun #-}
oneBenchRun ::
StrictTVar IO (AnchoredFragment H)
StrictTVar IO (AnchoredFragment (HV.HeaderWithTime B))
-> StrictTVar IO (Tip B)
-> ChainDB.Follower IO B (ChainDB.WithPoint B H)
-> Int
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import Data.Map.Strict (Map)
import qualified Data.Map.Strict as Map
import Data.Maybe (mapMaybe, maybeToList)
import Data.Maybe.Strict (StrictMaybe)
import Data.Typeable (Typeable)
import Data.Word (Word64)
import Ouroboros.Consensus.Block
import Ouroboros.Consensus.Config (TopLevelConfig, configLedger,
Expand All @@ -54,6 +55,7 @@ import Ouroboros.Consensus.Config.SecurityParam
import Ouroboros.Consensus.HardFork.Abstract (HasHardForkHistory (..))
import Ouroboros.Consensus.HardFork.History.Qry (qryFromExpr,
runQuery, slotToGenesisWindow)
import Ouroboros.Consensus.HeaderValidation (HeaderWithTime (..))
import Ouroboros.Consensus.Ledger.Extended (ExtLedgerState,
ledgerState)
import Ouroboros.Consensus.Ledger.SupportsProtocol
Expand Down Expand Up @@ -216,7 +218,7 @@ evaluateGDD cfg tracer stateView = do
let
(losingPeers, bounds) =
densityDisconnect sgen (configSecurityParam cfg) states candidateSuffixes loeFrag
loeHead = AF.headAnchor loeFrag
loeHead = AF.castAnchor $ AF.headAnchor loeFrag

traceWith tracer $ TraceGDDDebug
GDDDebugInfo {sgen, curChain, bounds, candidates, candidateSuffixes, losingPeers, loeHead}
Expand All @@ -225,7 +227,15 @@ evaluateGDD cfg tracer stateView = do
for_ losingPeersNE $ \peer -> killActions Map.! peer
traceWith tracer $ TraceGDDDisconnected losingPeersNE

pure loeFrag
-- REVIEW: we should avoid this linear computation, but I don't see an alternative to changing the type of the LoE fragment.
pure $ dropTime loeFrag
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the approach here would be to make the logic work on AnchoredFragment (HeaderWithTime blk) (or even AnchoredFragment a where a is suitably constrained). This will e.g. also require changing the type of cdbLoE in the ChainDB.



-- REVIEW: If dropping time is correct, this function will go into HeaderValidation most likely.
dropTime ::
AnchoredFragment (HeaderWithTime blk)
-> AnchoredFragment (Header blk)
dropTime = undefined

-- | Compute the fragment @loeFrag@ between the immutable tip and the
-- earliest intersection between @curChain@ and any of the @candidates@.
Expand All @@ -235,14 +245,14 @@ evaluateGDD cfg tracer stateView = do
-- The function also yields the suffixes of the intersection of @loeFrag@ with
-- every candidate fragment.
sharedCandidatePrefix ::
GetHeader blk =>
(Typeable blk, GetHeader blk) =>
AnchoredFragment (Header blk) ->
[(peer, AnchoredFragment (Header blk))] ->
(AnchoredFragment (Header blk), [(peer, AnchoredFragment (Header blk))])
[(peer, AnchoredFragment (HeaderWithTime blk))] ->
(AnchoredFragment (HeaderWithTime blk), [(peer, AnchoredFragment (HeaderWithTime blk))])
sharedCandidatePrefix curChain candidates =
second getCompose $
stripCommonPrefix (AF.anchor curChain) $
Compose immutableTipSuffixes
stripCommonPrefix (AF.castAnchor $ AF.anchor curChain) $
Compose immutableTipSuffixes
where
immutableTip = AF.anchorPoint curChain

Expand Down Expand Up @@ -300,8 +310,8 @@ densityDisconnect ::
=> GenesisWindow
-> SecurityParam
-> Map peer (ChainSyncState blk)
-> [(peer, AnchoredFragment (Header blk))]
-> AnchoredFragment (Header blk)
-> [(peer, AnchoredFragment (HeaderWithTime blk))] -- REVIEW: add a comment here? (in addition to the haddock above)
-> AnchoredFragment (HeaderWithTime blk) -- REVIEW: add a comment here?
Comment on lines +313 to +314
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, moving the Haddocks to the individual parameters from the docs above sounds good, but seems orthogonal to this PR.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is indeed; however, I don't think we'll ever do if we don't improve the haddocks while working on other tasks. I can use a separate commit for this, of course :)

-> ([peer], [(peer, DensityBounds blk)])
densityDisconnect (GenesisWindow sgen) (SecurityParam k) states candidateSuffixes loeFrag =
(losingPeers, densityBounds)
Expand Down Expand Up @@ -349,7 +359,9 @@ densityDisconnect (GenesisWindow sgen) (SecurityParam k) states candidateSuffixe
-- If not, it is not qualified to compete by density (yet).
offersMoreThanK = totalBlockCount > k

pure (peer, DensityBounds {clippedFragment, offersMoreThanK, lowerBound, upperBound, hasBlockAfter, latestSlot, idling})
pure (peer, DensityBounds {
clippedFragment = (dropTime clippedFragment), -- REVIEW: should we change the type of the clipped fragment?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes 👍

offersMoreThanK, lowerBound, upperBound, hasBlockAfter, latestSlot, idling})

losingPeers = nubOrd $ densityBounds >>= \
(peer0 , DensityBounds { clippedFragment = frag0
Expand Down Expand Up @@ -434,8 +446,8 @@ data GDDDebugInfo peer blk =
GDDDebugInfo {
bounds :: [(peer, DensityBounds blk)],
curChain :: AnchoredFragment (Header blk),
candidates :: [(peer, AnchoredFragment (Header blk))],
candidateSuffixes :: [(peer, AnchoredFragment (Header blk))],
candidates :: [(peer, AnchoredFragment (HeaderWithTime blk))],
candidateSuffixes :: [(peer, AnchoredFragment (HeaderWithTime blk))],
losingPeers :: [peer],
loeHead :: AF.Anchor (Header blk),
sgen :: GenesisWindow
Expand Down
Loading