Skip to content

Commit cd032b1

Browse files
committed
Rework followsLoEFrag into trimToLoE
This commit brings several LoE-related improvements, namely: - The precondition of `followsLoEFrag` stipulating that the given candidate fragment had to intersect with the LoE fragment was often violated in the code, because the candidate fragments were anchored at the tip of the selection. - `followsLoEFrag` used to accept or reject fragments depending on whether they were LoE-compliant or not. The new verson trims them to their longest LoE-compliant prefix. Following this change, `followsLoEFrag` has been renamed to `timToLoE`. It does not filter candidates out anymore. - This last change makes `computeLoEMaxExtra` redundant. We get rid of it entirely and clean up the functions `maximalCandidates` and `extendWithSuccessors` accordingly.
1 parent e882d90 commit cd032b1

File tree

2 files changed

+69
-92
lines changed
  • ouroboros-consensus/src/ouroboros-consensus/Ouroboros/Consensus/Storage/ChainDB/Impl

2 files changed

+69
-92
lines changed

ouroboros-consensus/src/ouroboros-consensus/Ouroboros/Consensus/Storage/ChainDB/Impl/ChainSel.hs

Lines changed: 60 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,10 @@ import Data.List.NonEmpty (NonEmpty)
3535
import qualified Data.List.NonEmpty as NE
3636
import Data.Map.Strict (Map)
3737
import qualified Data.Map.Strict as Map
38-
import Data.Maybe (isJust, isNothing)
38+
import Data.Maybe (fromJust, isJust, isNothing)
3939
import Data.Maybe.Strict (StrictMaybe (..), strictMaybeToMaybe)
4040
import Data.Set (Set)
4141
import qualified Data.Set as Set
42-
import Data.Word (Word64)
4342
import GHC.Stack (HasCallStack)
4443
import Ouroboros.Consensus.Block
4544
import Ouroboros.Consensus.Config
@@ -176,7 +175,9 @@ initialChainSelection immutableDB volatileDB lgrDB tracer cfg varInvalid
176175
suffixesAfterI :: [NonEmpty (HeaderHash blk)]
177176
suffixesAfterI = Paths.maximalCandidates succsOf limit (AF.anchorToPoint i)
178177
where
179-
limit = k <$ loE
178+
limit = case loE of
179+
LoEDisabled -> Nothing
180+
LoEEnabled () -> Just k
180181

181182
constructChain ::
182183
NonEmpty (HeaderHash blk)
@@ -579,25 +580,17 @@ chainSelectionForBlock cdb@CDB{..} blockCache hdr punish = electric $ do
579580
return tipPoint
580581

581582
-- The block fits onto the end of our current chain
582-
| pointHash tipPoint == headerPrevHash hdr
583-
-- TODO could be optimized if necessary/easy enough
584-
, let newBlockFrag = curChain AF.:> hdr
585-
, Just maxExtra <- computeLoEMaxExtra loeFrag newBlockFrag -> do
583+
| pointHash tipPoint == headerPrevHash hdr -> do
586584
-- ### Add to current chain
587585
traceWith addBlockTracer (TryAddToCurrentChain p)
588-
addToCurrentChain succsOf' curChainAndLedger loeFrag maxExtra
586+
addToCurrentChain succsOf' curChainAndLedger loeFrag
589587

590588
-- The block is reachable from the current selection
591589
-- and it doesn't fit after the current selection
592-
| Just diff <- Paths.isReachable lookupBlockInfo' curChain p
593-
-- TODO could be optimized if necessary/easy enough
594-
, let curChain' =
595-
AF.mapAnchoredFragment (castHeaderFields . getHeaderFields) curChain
596-
, Just newBlockFrag <- Diff.apply curChain' diff
597-
, Just maxExtra <- computeLoEMaxExtra loeFrag newBlockFrag -> do
590+
| Just diff <- Paths.isReachable lookupBlockInfo' curChain p -> do
598591
-- ### Switch to a fork
599592
traceWith addBlockTracer (TrySwitchToAFork p diff)
600-
switchToAFork succsOf' lookupBlockInfo' curChainAndLedger loeFrag maxExtra diff
593+
switchToAFork succsOf' lookupBlockInfo' curChainAndLedger loeFrag diff
601594

602595
-- We cannot reach the block from the current selection
603596
| otherwise -> do
@@ -651,12 +644,10 @@ chainSelectionForBlock cdb@CDB{..} blockCache hdr punish = electric $ do
651644
-- ^ The current chain and ledger
652645
-> LoE (AnchoredFragment (Header blk))
653646
-- ^ LoE fragment
654-
-> LoE Word64
655-
-- ^ How many extra blocks to select after @b@ at most.
656647
-> m (Point blk)
657-
addToCurrentChain succsOf curChainAndLedger loeFrag maxExtra = do
648+
addToCurrentChain succsOf curChainAndLedger loeFrag = do
658649
-- Extensions of @B@ that do not exceed the LoE
659-
let suffixesAfterB = Paths.maximalCandidates succsOf maxExtra (realPointToPoint p)
650+
let suffixesAfterB = Paths.maximalCandidates succsOf Nothing (realPointToPoint p)
660651

661652
-- Fragments that are anchored at @curHead@, i.e. suffixes of the
662653
-- current chain.
@@ -675,10 +666,10 @@ chainSelectionForBlock cdb@CDB{..} blockCache hdr punish = electric $ do
675666
return $ AF.fromOldestFirst curHead (hdr : hdrs)
676667

677668
let chainDiffs = NE.nonEmpty
678-
$ map Diff.extend
679-
$ filter (followsLoEFrag loeFrag)
680-
$ NE.filter (preferAnchoredCandidate (bcfg chainSelEnv) curChain)
681-
candidates
669+
$ filter (preferAnchoredCandidate (bcfg chainSelEnv) curChain . Diff.getSuffix)
670+
$ fmap (trimToLoE loeFrag curChainAndLedger)
671+
$ fmap Diff.extend
672+
$ NE.toList candidates
682673
-- All candidates are longer than the current chain, so they will be
683674
-- preferred over it, /unless/ the block we just added is an EBB,
684675
-- which has the same 'BlockNo' as the block before it, so when
@@ -709,16 +700,45 @@ chainSelectionForBlock cdb@CDB{..} blockCache hdr punish = electric $ do
709700
curTip = castPoint $ AF.headPoint curChain
710701
curHead = AF.headAnchor curChain
711702

712-
-- Either frag extends loe or loe extends frag
703+
-- | Trim the given candidate fragment to respect the LoE.
713704
--
714-
-- PRECONDITION: @AF.withinFragmentBounds (AF.anchorPoint frag) loe@
715-
followsLoEFrag :: LoE (AnchoredFragment (Header blk))
716-
-> AnchoredFragment (Header blk)
717-
-> Bool
718-
followsLoEFrag LoEDisabled _ = True
719-
followsLoEFrag (LoEEnabled loe) frag =
720-
AF.withinFragmentBounds (AF.headPoint loe) frag
721-
|| AF.withinFragmentBounds (AF.headPoint frag) loe
705+
-- The returned fragment is such that:
706+
--
707+
-- - It is a prefix of the given fragment.
708+
-- - If it contains the tip of the LoE fragment, then it contains at most
709+
-- @k@ block after it.
710+
-- - If it does not contain the tip of the LoE fragment, then it is included
711+
-- in the LoE fragment.
712+
--
713+
-- The fragment is represented by the current chain and a diff with that
714+
-- current chain. It is tempting to only consider the suffix of the diff,
715+
-- but that would be incorrect, because the diff might not intersect with
716+
-- the LoE fragment, because the diff suffix is anchored somewhere on the
717+
-- current chain and LoE frag's tip might be older than that anchor.
718+
--
719+
-- PRECONDITIONS:
720+
--
721+
-- 1. The given 'ChainDiff' can apply on top of the given 'ChainAndLedger'.
722+
-- 2. The LoE fragment intersects with the current selection.
723+
trimToLoE ::
724+
LoE (AnchoredFragment (Header blk)) ->
725+
ChainAndLedger blk ->
726+
ChainDiff (Header blk) ->
727+
ChainDiff (Header blk)
728+
trimToLoE LoEDisabled _ diff = diff
729+
trimToLoE (LoEEnabled loe) curChain diff =
730+
case Diff.apply (VF.validatedFragment curChain) diff of
731+
Nothing -> error "trimToLoE: precondition 1 violated: the given 'ChainDiff' must apply on top of the given 'ChainAndLedger'"
732+
Just cand ->
733+
case AF.intersect cand loe of
734+
Nothing -> error "trimToLoE: precondition 2 violated: the LoE fragment must intersect with the current selection"
735+
Just (candPrefix, _, candSuffix, loeSuffix) ->
736+
let trimmedCandSuffix = AF.takeOldest (fromIntegral k) candSuffix
737+
trimmedCand =
738+
if AF.null loeSuffix
739+
then fromJust $ AF.join candPrefix trimmedCandSuffix
740+
else candPrefix
741+
in Diff.diff (VF.validatedFragment curChain) trimmedCand
722742

723743
-- | We have found a 'ChainDiff' through the VolatileDB connecting the new
724744
-- block to the current chain. We'll call the intersection/anchor @x@.
@@ -734,29 +754,29 @@ chainSelectionForBlock cdb@CDB{..} blockCache hdr punish = electric $ do
734754
-- ^ The current chain (anchored at @i@) and ledger
735755
-> LoE (AnchoredFragment (Header blk))
736756
-- ^ LoE fragment
737-
-> LoE Word64
738-
-- ^ How many extra blocks to select after @b@ at most.
739757
-> ChainDiff (HeaderFields blk)
740758
-- ^ Header fields for @(x,b]@
741759
-> m (Point blk)
742-
switchToAFork succsOf lookupBlockInfo curChainAndLedger loeFrag maxExtra diff = do
760+
switchToAFork succsOf lookupBlockInfo curChainAndLedger loeFrag diff = do
743761
-- We use a cache to avoid reading the headers from disk multiple
744762
-- times in case they're part of multiple forks that go through @b@.
745763
let initCache = Map.singleton (headerHash hdr) hdr
746764
chainDiffs <-
747-
fmap (filter (followsLoEFrag loeFrag . Diff.getSuffix))
748-
749-
-- 4. Filter out candidates that are not preferred over the current
765+
-- 5. Filter out candidates that are not preferred over the current
750766
-- chain.
751767
--
752768
-- The suffixes all fork off from the current chain within @k@
753769
-- blocks, so it satisfies the precondition of 'preferCandidate'.
754-
. fmap
770+
fmap
755771
( filter
756772
( preferAnchoredCandidate (bcfg chainSelEnv) curChain
757773
. Diff.getSuffix
758774
)
759775
)
776+
-- 4. Trim fragments so that they follow the LoE, that is, they
777+
-- extend the LoE or are extended by the LoE. Filter them out
778+
-- otherwise.
779+
. fmap (fmap (trimToLoE loeFrag curChainAndLedger))
760780
-- 3. Translate the 'HeaderFields' to 'Header' by reading the
761781
-- headers from disk.
762782
. flip evalStateT initCache
@@ -766,7 +786,7 @@ chainSelectionForBlock cdb@CDB{..} blockCache hdr punish = electric $ do
766786
-- for those candidates.
767787
. NE.filter (not . Diff.rollbackExceedsSuffix)
768788
-- 1. Extend the diff with candidates fitting on @B@ and not exceeding the LoE
769-
. Paths.extendWithSuccessors succsOf lookupBlockInfo maxExtra
789+
. Paths.extendWithSuccessors succsOf lookupBlockInfo
770790
$ diff
771791

772792
case NE.nonEmpty chainDiffs of
@@ -786,45 +806,6 @@ chainSelectionForBlock cdb@CDB{..} blockCache hdr punish = electric $ do
786806
curChain = VF.validatedFragment curChainAndLedger
787807
curTip = castPoint $ AF.headPoint curChain
788808

789-
-- | How many extra blocks to select at most after the tip of @newBlockFrag@
790-
-- according to the LoE.
791-
--
792-
-- There are two cases to consider:
793-
--
794-
-- 1. If @newBlockFrag@ and @loeFrag@ are on the same chain, then we cannot
795-
-- select more than @loeLimit@ blocks after @loeFrag@.
796-
--
797-
-- 2. If @newBlockFrag@ and @loeFrag@ are on different chains, then we
798-
-- cannot select more than @loeLimit@ blocks after their intersection.
799-
--
800-
-- In any case, 'Nothing' is returned if @newBlockFrag@ extends beyond
801-
-- what LoE allows.
802-
computeLoEMaxExtra ::
803-
(HasHeader x, HeaderHash x ~ HeaderHash blk)
804-
=> LoE (AnchoredFragment (Header blk))
805-
-- ^ The fragment with the LoE as its tip, with the same anchor as
806-
-- @curChain@.
807-
-> AnchoredFragment x
808-
-- ^ The fragment with the new block @b@ as its tip, with the same
809-
-- anchor as @curChain@.
810-
-> Maybe (LoE Word64)
811-
computeLoEMaxExtra (LoEEnabled loeFrag) newBlockFrag =
812-
-- Both fragments are on the same chain
813-
if loeSuffixLength == 0 || rollback == 0 then
814-
if rollback > k + loeSuffixLength
815-
then Nothing
816-
else Just $ LoEEnabled $ k + loeSuffixLength - rollback
817-
else
818-
if rollback > k
819-
then Nothing
820-
else Just $ LoEEnabled $ k - rollback
821-
where
822-
d = Diff.diff newBlockFrag loeFrag
823-
rollback = Diff.getRollback d
824-
loeSuffixLength = fromIntegral $ AF.length (Diff.getSuffix d)
825-
computeLoEMaxExtra LoEDisabled _ =
826-
Just LoEDisabled
827-
828809
mkSelectionChangedInfo ::
829810
AnchoredFragment (Header blk) -- ^ old chain
830811
-> AnchoredFragment (Header blk) -- ^ new chain

ouroboros-consensus/src/ouroboros-consensus/Ouroboros/Consensus/Storage/ChainDB/Impl/Paths.hs

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -64,19 +64,18 @@ type LookupBlockInfo blk = HeaderHash blk -> Maybe (VolatileDB.BlockInfo blk)
6464
-- NOTE: it is possible that no candidates are found, but don't forget that
6565
-- the chain (fragment) ending with @B@ is also a potential candidate.
6666
--
67-
-- If ChainSel is using the LoE, the value passed in @lenLimit@ will be used
68-
-- to truncate the candidates so that no more than @k@ blocks can be selected
69-
-- beyond the LoE fragment.
7067
maximalCandidates ::
7168
forall blk.
7269
(ChainHash blk -> Set (HeaderHash blk))
7370
-- ^ @filterByPredecessor@
74-
-> LoE Word64 -- ^ Max length of any candidate
71+
-> Maybe Word64 -- ^ Optional max length of any candidate, used during initial
72+
-- chain selection when LoE is enabled.
7573
-> Point blk -- ^ @B@
7674
-> [NonEmpty (HeaderHash blk)]
7775
-- ^ Each element in the list is a list of hashes from which we can
7876
-- construct a fragment anchored at the point @B@.
79-
maximalCandidates succsOf loeLimit b = mapMaybe (NE.nonEmpty . applyLoE) $ go (pointHash b)
77+
maximalCandidates succsOf sizeLimit b =
78+
mapMaybe (NE.nonEmpty . trimToSizeLimit) $ go (pointHash b)
8079
where
8180
go :: ChainHash blk -> [[HeaderHash blk]]
8281
go mbHash = case Set.toList $ succsOf mbHash of
@@ -85,11 +84,9 @@ maximalCandidates succsOf loeLimit b = mapMaybe (NE.nonEmpty . applyLoE) $ go (p
8584
| next <- succs
8685
, candidate <- go (BlockHash next)
8786
]
88-
applyLoE
89-
| LoEEnabled limit <- loeLimit
90-
= take (fromIntegral limit)
91-
| otherwise
92-
= id
87+
trimToSizeLimit = case sizeLimit of
88+
Just limit -> take (fromIntegral limit)
89+
Nothing -> id
9390

9491
-- | Extend the 'ChainDiff' with the successors found by 'maximalCandidates'.
9592
--
@@ -105,17 +102,16 @@ extendWithSuccessors ::
105102
forall blk. HasHeader blk
106103
=> (ChainHash blk -> Set (HeaderHash blk))
107104
-> LookupBlockInfo blk
108-
-> LoE Word64 -- ^ Max extra length for any suffix
109105
-> ChainDiff (HeaderFields blk)
110106
-> NonEmpty (ChainDiff (HeaderFields blk))
111-
extendWithSuccessors succsOf lookupBlockInfo loeLimit diff =
107+
extendWithSuccessors succsOf lookupBlockInfo diff =
112108
case NE.nonEmpty extensions of
113109
Nothing -> diff NE.:| []
114110
Just extensions' -> extensions'
115111
where
116112
extensions =
117113
[ foldl' Diff.append diff (lookupHeaderFields <$> candHashes)
118-
| candHashes <- maximalCandidates succsOf loeLimit (castPoint (Diff.getTip diff))
114+
| candHashes <- maximalCandidates succsOf Nothing (castPoint (Diff.getTip diff))
119115
]
120116

121117
lookupHeaderFields :: HeaderHash blk -> HeaderFields blk

0 commit comments

Comments
 (0)