@@ -35,11 +35,10 @@ import Data.List.NonEmpty (NonEmpty)
3535import qualified Data.List.NonEmpty as NE
3636import Data.Map.Strict (Map )
3737import qualified Data.Map.Strict as Map
38- import Data.Maybe (isJust , isNothing )
38+ import Data.Maybe (fromJust , isJust , isNothing )
3939import Data.Maybe.Strict (StrictMaybe (.. ), strictMaybeToMaybe )
4040import Data.Set (Set )
4141import qualified Data.Set as Set
42- import Data.Word (Word64 )
4342import GHC.Stack (HasCallStack )
4443import Ouroboros.Consensus.Block
4544import 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
0 commit comments