@@ -139,6 +139,7 @@ data LeiosNodeState m = LeiosNodeState
139139 -- ^ waiting for ledger state of RB block to be validated.
140140 , ledgerStateVar :: ! (TVar m (Map (HeaderHash RankingBlock ) LedgerState ))
141141 , ibsNeededForEBVar :: ! (TVar m (Map EndorseBlockId (Set InputBlockId )))
142+ , ibsValidationActionsVar :: ! (TVar m (Map InputBlockId (STM m () )))
142143 , votesForEBVar :: ! (TVar m (Map EndorseBlockId CertificateProgress ))
143144 }
144145
@@ -401,6 +402,7 @@ newLeiosNodeState cfg = do
401402 waitingForLedgerStateVar <- newTVarIO Map. empty
402403 taskQueue <- atomically $ newTaskMultiQueue cfg. processingQueueBound
403404 votesForEBVar <- newTVarIO Map. empty
405+ ibsValidationActionsVar <- newTVarIO Map. empty
404406 return $ LeiosNodeState {.. }
405407
406408leiosNode ::
@@ -495,7 +497,10 @@ leiosNode tracer cfg followers peers = do
495497
496498 let blockGenerationThreads = [generator tracer cfg leiosState]
497499
498- let computeLedgerStateThreads = [computeLedgerStateThread tracer cfg leiosState]
500+ let computeLedgerStateThreads =
501+ [ computeLedgerStateThread tracer cfg leiosState
502+ -- , validateIBsOfCertifiedEBs tracer cfg leiosState
503+ ]
499504
500505 let pruningThreads =
501506 concat
@@ -507,6 +512,9 @@ leiosNode tracer cfg followers peers = do
507512 ]
508513 , [ pruneExpiredUnadoptedEBs tracer cfg leiosState
509514 | CleanupExpiredUnadoptedEb `isEnabledIn` cfg. leios. cleanupPolicies
515+ , -- With Full a fresh EB might end up referencing all the way to Genesis.
516+ -- TODO: could expire EBs not referenced by young enough EBs.
517+ cfg. leios. variant /= Full
510518 ]
511519 , [ pruneExpiredIBs tracer cfg leiosState
512520 | CleanupExpiredIb `isEnabledIn` cfg. leios. cleanupPolicies
@@ -543,7 +551,7 @@ pruneExpiredIBs _tracer LeiosNodeConfig{leios, slotConfig} st = go (toEnum 0)
543551 -- traceWith tracer $! LeiosNodeEvent Pruned (EventIB ib)
544552 go (succ p)
545553
546- -- rEB slots after the end of vote-receiving ,
554+ -- rEB slots after the end of Endorse ,
547555-- prune EBs that became certified but were not adopted by an RB.
548556pruneExpiredUnadoptedEBs ::
549557 forall m .
@@ -673,44 +681,107 @@ pruneExpiredVotes _tracer LeiosNodeConfig{leios = leios@LeiosConfig{pipeline = _
673681 -- traceWith tracer $! LeiosNodeEvent Pruned (EventVote $ snd vt)
674682 go (succ p)
675683
684+ referencedEBs :: MonadSTM m => LeiosConfig -> LeiosNodeState m -> Set EndorseBlockId -> STM m [EndorseBlockId ]
685+ referencedEBs cfg st ebIds0
686+ | null ebIds0 = return []
687+ | Short <- cfg. variant = pure $ Set. toList ebIds0
688+ | otherwise = do
689+ ebBuffer <- readTVar st. relayEBState. relayBufferVar
690+ let
691+ ebsReferenced :: Set EndorseBlockId -> Set EndorseBlockId -> [EndorseBlockId ]
692+ ebsReferenced ! fetched ebIds
693+ | null ebIds = []
694+ | otherwise = do
695+ let ebs =
696+ [ snd $ fromMaybe (error $ " EB missing:" ++ show ebId) $ RB. lookup ebBuffer ebId
697+ | ebId <- Set. toList ebIds
698+ ]
699+ let fetched' = Set. union fetched ebIds
700+ let refs =
701+ Set. fromList
702+ [ refId
703+ | eb <- ebs
704+ , refId <- eb. endorseBlocksEarlierPipeline
705+ , Set. notMember refId fetched'
706+ ]
707+ map (. id ) ebs ++ ebsReferenced fetched' refs
708+ return $ ebsReferenced Set. empty ebIds0
709+
676710computeLedgerStateThread ::
677711 forall m .
678712 (MonadMVar m , MonadFork m , MonadAsync m , MonadSTM m , MonadTime m , MonadDelay m ) =>
679713 Tracer m LeiosNodeEvent ->
680714 LeiosNodeConfig ->
681715 LeiosNodeState m ->
682716 m ()
683- computeLedgerStateThread tracer _cfg st = forever $ do
717+ computeLedgerStateThread tracer cfg st = forever $ do
684718 readyLedgerState <- atomically $ do
685719 -- TODO: this will get more costly as the base chain grows,
686720 -- however it grows much more slowly than anything else.
687721 chain <- PraosNode. preferredChain st. praosState
688722 let rbsOnChain = Chain. toNewestFirst $ chain
689- let blocks = Map. fromList [(blockHash block, block) | block <- rbsOnChain]
690- when ( Map. null blocks) retry
723+ when ( null rbsOnChain) retry
724+ -- TODO: should we prune the ledger state to only cover RBs on the chain?
691725 ledgerState <- readTVar st. ledgerStateVar
692- let ledgerMissing = Map. elems $ blocks Map. \\ ledgerState
693- when (null ledgerMissing) retry
694- let ledgerEligible = flip filter ledgerMissing $ \ blk ->
695- case blockPrevHash blk of
696- GenesisHash -> True
697- BlockHash prev -> prev `Map.member` ledgerState
698- when (null ledgerEligible) retry
699-
700- ibsNeededForEB <- readTVar st. ibsNeededForEBVar
701- let readyLedgerState =
702- [ (blockHash rb, LedgerState )
703- | rb <- ledgerMissing
704- , flip all rb. blockBody. endorseBlocks $ \ (ebId, _) ->
705- Map. lookup ebId ibsNeededForEB == Just Set. empty
706- ]
707- when (null readyLedgerState) retry
708- modifyTVar' st. ledgerStateVar (`Map.union` Map. fromList readyLedgerState)
709- return readyLedgerState
726+ let oldestMissingLedgerState = go Nothing rbsOnChain
727+ where
728+ go acc [] = acc
729+ go acc (x : xs)
730+ | Map. member (blockHash x) ledgerState = acc
731+ | otherwise = go (Just x) xs
732+ ledgerEligible <- case oldestMissingLedgerState of
733+ Nothing -> retry
734+ Just block -> pure block
735+
736+ todo <- do
737+ let doLedgerState = Left (blockHash ledgerEligible, LedgerState )
738+ case (map fst $ ledgerEligible. blockBody. endorseBlocks) of
739+ [] -> return $ doLedgerState
740+ ebIds -> do
741+ ibsNeededForEB <- readTVar st. ibsNeededForEBVar
742+ ibsNeeded <- do
743+ ebs <- referencedEBs cfg. leios st (Set. fromList ebIds)
744+ return $ Set. unions <$> mapM (flip Map. lookup ibsNeededForEB) ebs
745+ case ibsNeeded of
746+ -- Some EB was missing ibsNeeded info
747+ Nothing -> undefined
748+ Just ibs
749+ | Set. null ibs -> pure $ doLedgerState
750+ | otherwise -> pure $ Right ibs
751+
752+ case todo of
753+ Left readyLedgerState -> do
754+ modifyTVar' st. ledgerStateVar (uncurry Map. insert readyLedgerState)
755+ return [readyLedgerState]
756+ Right ibsEligibleToValidate -> do
757+ ibValActions <- readTVar st. ibsValidationActionsVar
758+ let ibsReadyToValidate = Map. elems $ Map. restrictKeys ibValActions ibsEligibleToValidate
759+ if null ibsReadyToValidate
760+ then retry
761+ else do
762+ modifyTVar' st. ibsValidationActionsVar $ flip Map. withoutKeys ibsEligibleToValidate
763+ sequence_ ibsReadyToValidate
764+ return []
710765 for_ readyLedgerState $ \ (rb, _) -> do
711766 traceWith tracer $! LeiosNodeEventLedgerState rb
712767 return ()
713768
769+ -- TODO: Use or remove.
770+ -- Might be sensible to validate IBs as soon as we have a certified EB including them: the network managed to validate the IB, so a suitable ledger state is available.
771+ validateIBsOfCertifiedEBs :: MonadSTM m => Tracer m LeiosNodeEvent -> LeiosNodeConfig -> LeiosNodeState m -> m ()
772+ validateIBsOfCertifiedEBs _trace _cfg st = forever . atomically $ do
773+ ibsNeeded <- readTVar st. ibsNeededForEBVar
774+ ebs <- readTVar st. votesForEBVar
775+ let certEBs = Set. fromList [eb | (eb, Certified {}) <- Map. toList ebs]
776+ let ibsEligible = Set. unions $ Map. elems $ Map. restrictKeys ibsNeeded certEBs
777+ when (null ibsEligible) retry
778+ ibsValActions <- readTVar st. ibsValidationActionsVar
779+ let ibsToValidate = Map. toList $ Map. restrictKeys ibsValActions ibsEligible
780+ when (null ibsToValidate) $ retry
781+ forM_ ibsToValidate $ \ (ibId, m) -> do
782+ modifyTVar' st. ibsValidationActionsVar $ Map. delete ibId
783+ m
784+
714785adoptIB :: MonadSTM m => LeiosNodeState m -> InputBlock -> UTCTime -> STM m ()
715786adoptIB leiosState ib deliveryTime = do
716787 let ! ibSlot = ib. header. slot
@@ -787,12 +858,28 @@ dispatchValidation tracer cfg leiosState req =
787858 return []
788859 ValidateIBS ibs deliveryTime completion -> do
789860 -- NOTE: IBs with an RB reference have to wait for ledger state of that RB.
790- let waitingLedgerState =
791- [ (rbHash, [queue [valIB ib deliveryTime completion]])
792- | ib <- ibs
793- , BlockHash rbHash <- [(fst ib). rankingBlock]
794- ]
861+ -- However, if they get referenced by the chain they should be validated anyway.
862+ -- We use a map to store the validation logic, so we can force it happening in the latter case.
863+ let lookupValAction ibId = do
864+ ibValActions <- readTVar leiosState. ibsValidationActionsVar
865+ case Map. lookup ibId ibValActions of
866+ Just m -> do
867+ modifyTVar' leiosState. ibsValidationActionsVar $
868+ Map. delete ibId
869+ m
870+ Nothing -> pure ()
871+ let storeAction rbHash ib@ (h, _) = do
872+ modifyTVar' leiosState. ibsValidationActionsVar $
873+ Map. insert h. id (queue [valIB ib deliveryTime completion])
874+ return (rbHash, [lookupValAction $ (fst ib). id ])
875+ waitingLedgerState <-
876+ sequence $
877+ [ storeAction rbHash ib
878+ | ib <- ibs
879+ , BlockHash rbHash <- [(fst ib). rankingBlock]
880+ ]
795881
882+ -- TODO: cancel the ones forced by computeLedgerState
796883 waitFor
797884 leiosState. waitingForLedgerStateVar
798885 waitingLedgerState
0 commit comments