{-# LANGUAGE NamedFieldPuns #-}
module Test.ThreadNet.Util.Expectations (
NumBlocks (..)
, determineForkLength
) where
import Cardano.Ledger.BaseTypes (unNonZero)
import Data.Foldable as Foldable (foldl')
import qualified Data.Map.Strict as Map
import Data.Word (Word64)
import Ouroboros.Consensus.Block
import Ouroboros.Consensus.Config.SecurityParam
import Ouroboros.Consensus.NodeId (CoreNodeId (..))
import Ouroboros.Consensus.Protocol.LeaderSchedule
import Test.ThreadNet.Util.NodeJoinPlan
newtype NumBlocks = NumBlocks Word64
deriving (NumBlocks -> NumBlocks -> Bool
(NumBlocks -> NumBlocks -> Bool)
-> (NumBlocks -> NumBlocks -> Bool) -> Eq NumBlocks
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: NumBlocks -> NumBlocks -> Bool
== :: NumBlocks -> NumBlocks -> Bool
$c/= :: NumBlocks -> NumBlocks -> Bool
/= :: NumBlocks -> NumBlocks -> Bool
Eq, Int -> NumBlocks -> ShowS
[NumBlocks] -> ShowS
NumBlocks -> String
(Int -> NumBlocks -> ShowS)
-> (NumBlocks -> String)
-> ([NumBlocks] -> ShowS)
-> Show NumBlocks
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> NumBlocks -> ShowS
showsPrec :: Int -> NumBlocks -> ShowS
$cshow :: NumBlocks -> String
show :: NumBlocks -> String
$cshowList :: [NumBlocks] -> ShowS
showList :: [NumBlocks] -> ShowS
Show)
data Acc = Acc
{ Acc -> Word64
maxChainLength :: !Word64
, Acc -> Word64
maxForkLength :: !Word64
}
determineForkLength ::
SecurityParam
-> NodeJoinPlan
-> LeaderSchedule
-> NumBlocks
determineForkLength :: SecurityParam -> NodeJoinPlan -> LeaderSchedule -> NumBlocks
determineForkLength SecurityParam
k (NodeJoinPlan Map CoreNodeId SlotNo
joinPlan) (LeaderSchedule Map SlotNo [CoreNodeId]
sched) =
Acc -> NumBlocks
prj (Acc -> NumBlocks) -> Acc -> NumBlocks
forall a b. (a -> b) -> a -> b
$ (Acc -> (SlotNo, [CoreNodeId]) -> Acc)
-> Acc -> [(SlotNo, [CoreNodeId])] -> Acc
forall b a. (b -> a -> b) -> b -> [a] -> b
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
Foldable.foldl' Acc -> (SlotNo, [CoreNodeId]) -> Acc
step Acc
initial (Map SlotNo [CoreNodeId] -> [(SlotNo, [CoreNodeId])]
forall k a. Map k a -> [(k, a)]
Map.toAscList Map SlotNo [CoreNodeId]
sched)
where
prj :: Acc -> NumBlocks
prj Acc{Word64
maxForkLength :: Acc -> Word64
maxForkLength :: Word64
maxForkLength} = Word64 -> NumBlocks
NumBlocks Word64
maxForkLength
initial :: Acc
initial = Acc
{ maxChainLength :: Word64
maxChainLength = Word64
0
, maxForkLength :: Word64
maxForkLength = Word64
0
}
step :: Acc -> (SlotNo, [CoreNodeId]) -> Acc
step Acc{Word64
maxChainLength :: Acc -> Word64
maxChainLength :: Word64
maxChainLength, Word64
maxForkLength :: Acc -> Word64
maxForkLength :: Word64
maxForkLength} (SlotNo
slot, [CoreNodeId]
leaders) =
Acc
{ maxChainLength :: Word64
maxChainLength = Word64 -> Word64
grow Word64
maxChainLength
, maxForkLength :: Word64
maxForkLength = Word64 -> Word64
update Word64
maxForkLength
}
where
grow :: Word64 -> Word64
grow = if Int
0 Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
pullingAhead then Word64 -> Word64
forall a. a -> a
id else (Word64 -> Word64 -> Word64
forall a. Num a => a -> a -> a
+ Word64
1)
update :: Word64 -> Word64
update
| Word64
maxForkLength Word64 -> Word64 -> Bool
forall a. Ord a => a -> a -> Bool
> NonZero Word64 -> Word64
forall a. NonZero a -> a
unNonZero (SecurityParam -> NonZero Word64
maxRollbacks SecurityParam
k) = Word64 -> Word64
grow
| Int
pullingAhead Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
1 = (Word64 -> Word64 -> Word64
forall a. Num a => a -> a -> a
+ Word64
1)
| Int
pullingAhead Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
1 = Word64 -> Word64 -> Word64
forall a b. a -> b -> a
const Word64
0
| Int
pullingEven Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
0 = Word64 -> Word64 -> Word64
forall a. Ord a => a -> a -> a
max Word64
1
| Bool
otherwise = Word64 -> Word64
forall a. a -> a
id
pullingAhead :: Int
pullingAhead = Int
nlOld Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Bool -> Int
nlNew (Word64
maxChainLength Word64 -> Word64 -> Bool
forall a. Eq a => a -> a -> Bool
== Word64
0)
pullingEven :: Int
pullingEven = Bool -> Int
nlNew (Word64
maxChainLength Word64 -> Word64 -> Bool
forall a. Eq a => a -> a -> Bool
== Word64
1)
nlOld :: Int
nlOld = [CoreNodeId] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length ([CoreNodeId] -> Int) -> [CoreNodeId] -> Int
forall a b. (a -> b) -> a -> b
$ (CoreNodeId -> Bool) -> [CoreNodeId] -> [CoreNodeId]
forall a. (a -> Bool) -> [a] -> [a]
filter ((SlotNo -> SlotNo -> Bool
forall a. Ord a => a -> a -> Bool
< SlotNo
slot) (SlotNo -> Bool) -> (CoreNodeId -> SlotNo) -> CoreNodeId -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CoreNodeId -> SlotNo
joinSlot) [CoreNodeId]
leaders
nlNew :: Bool -> Int
nlNew Bool
b
| Bool -> Bool
not Bool
b = Int
0
| Bool
otherwise = [CoreNodeId] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length ([CoreNodeId] -> Int) -> [CoreNodeId] -> Int
forall a b. (a -> b) -> a -> b
$ (CoreNodeId -> Bool) -> [CoreNodeId] -> [CoreNodeId]
forall a. (a -> Bool) -> [a] -> [a]
filter ((SlotNo -> SlotNo -> Bool
forall a. Eq a => a -> a -> Bool
== SlotNo
slot) (SlotNo -> Bool) -> (CoreNodeId -> SlotNo) -> CoreNodeId -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CoreNodeId -> SlotNo
joinSlot) [CoreNodeId]
leaders
joinSlot :: CoreNodeId -> SlotNo
joinSlot :: CoreNodeId -> SlotNo
joinSlot CoreNodeId
nid = case CoreNodeId -> Map CoreNodeId SlotNo -> Maybe SlotNo
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup CoreNodeId
nid Map CoreNodeId SlotNo
joinPlan of
Maybe SlotNo
Nothing -> String -> SlotNo
forall a. HasCallStack => String -> a
error String
"determineForkLength: incomplete node join plan"
Just SlotNo
slot' -> SlotNo
slot'