@@ -34,13 +34,15 @@ module Ouroboros.Consensus.Peras.Weight
3434import Data.Foldable as Foldable (foldl' )
3535import Data.Map.Strict (Map )
3636import qualified Data.Map.Strict as Map
37- import Data.Word ( Word64 )
37+ import Data.Maybe ( fromJust )
3838import GHC.Generics (Generic )
3939import NoThunks.Class
4040import Ouroboros.Consensus.Block
4141import Ouroboros.Consensus.Config.SecurityParam
4242import Ouroboros.Network.AnchoredFragment (AnchoredFragment )
4343import qualified Ouroboros.Network.AnchoredFragment as AF
44+ import Ouroboros.Network.AnchoredSeq (AnchoredSeq )
45+ import qualified Ouroboros.Network.AnchoredSeq as AS
4446
4547-- | Data structure for tracking the weight of blocks due to Peras boosts.
4648newtype PerasWeightSnapshot blk = PerasWeightSnapshot
@@ -367,34 +369,67 @@ takeVolatileSuffix ::
367369 SecurityParam ->
368370 AnchoredFragment h ->
369371 AnchoredFragment h
370- takeVolatileSuffix snap secParam frag
372+ takeVolatileSuffix snap secParam
371373 | Map. null $ getPerasWeightSnapshot snap =
372374 -- Optimize the case where Peras is disabled.
373- AF. anchorNewest (unPerasWeight k) frag
374- | hasAtMostWeightK frag = frag
375- | otherwise = go 0 lenFrag ( AF. Empty $ AF. headAnchor frag )
375+ AF. anchorNewest (unPerasWeight k)
376+ | otherwise =
377+ takeLongestSuffix (totalWeightOfFragment snap) ( <= k )
376378 where
377379 k :: PerasWeight
378380 k = maxRollbackWeight secParam
379381
380- hasAtMostWeightK :: AnchoredFragment h -> Bool
381- hasAtMostWeightK f = totalWeightOfFragment snap f <= k
382-
383- lenFrag = fromIntegral $ AF. length frag
384-
385- -- Binary search for the longest suffix of @frag@ which 'hasAtMostWeightK'.
382+ -- | Take the longest suffix of an 'AnchoredSeq' @as@ satisfying the given
383+ -- predicate @p@ on the monoidal summary given by @f@.
384+ --
385+ -- TODO: upstream this function
386+ --
387+ -- === PRECONDITIONS:
388+ --
389+ -- For @as0, as1@ such that @AS.join as0 as1 = Just as2@, we must have the
390+ -- following homomorphism property:
391+ --
392+ -- > f as0 <> f as1 ≡ f as2
393+ --
394+ -- For empty @ase@, we must have @f ase ≡ mempty@.
395+ --
396+ -- The predicate must be monotonic, ie when @suf0@ is a suffix of @as@ and
397+ -- @suf1@ is a suffix of @suf0@, then @p (f suf0)@ must imply @p (f suf1)@.
398+ -- Furthermore, we must have @p mempty@.
399+ takeLongestSuffix ::
400+ forall s v a b .
401+ (Monoid s , AS. Anchorable v a b ) =>
402+ -- | @f@: Compute a monoidal summary of a fragment.
403+ (AnchoredSeq v a b -> s ) ->
404+ -- | @p@: Predicate on the summary of a fragment.
405+ (s -> Bool ) ->
406+ -- | Input sequence @as@.
407+ AnchoredSeq v a b ->
408+ -- | A suffix of the input sequence.
409+ AnchoredSeq v a b
410+ takeLongestSuffix f p as =
411+ go (AS. Empty $ AS. headAnchor as) mempty as
412+ where
386413 go ::
387- Word64 -> -- lb. The length lb suffix satisfies 'hasAtMostWeightK'.
388- Word64 -> -- ub. The length ub suffix does not satisfy 'hasAtMostWeightK'.
389- AnchoredFragment h -> -- The length lb suffix.
390- AnchoredFragment h
391- go lb ub lbFrag
392- | lb + 1 == ub = lbFrag
393- | hasAtMostWeightK midFrag = go mid ub midFrag
394- | otherwise = go lb mid lbFrag
414+ -- @suf@: the longest suffix of @as@ for which we currently know that @p (f
415+ -- suf)@.
416+ AnchoredSeq v a b ->
417+ -- Equal to @f suf@.
418+ s ->
419+ -- @pre@: longest infix of @as@ ending just before @suf@ such that we don't
420+ -- know whether @p (f (AS.join pre suf))@.
421+ AnchoredSeq v a b ->
422+ -- Longest suffix of @as@ satisfying @p . f@.
423+ AnchoredSeq v a b
424+ go suf sufS pre
425+ | AS. null pre = suf
426+ | p suf'S = go suf' suf'S pre0
427+ | AS. null pre0 = suf
428+ | otherwise = go suf sufS pre1
395429 where
396- mid = (lb + ub) `div` 2
397- midFrag = AF. anchorNewest mid frag
430+ (pre0, pre1) = AS. splitAt (AS. length pre `div` 2 ) pre
431+ suf' = fromJust (AS. join (\ _ _ -> True ) pre1 suf)
432+ suf'S = f pre1 <> sufS
398433
399434-- $setup
400435-- >>> import Cardano.Ledger.BaseTypes
0 commit comments