22{-# LANGUAGE DeriveGeneric #-}
33{-# LANGUAGE FlexibleContexts #-}
44{-# LANGUAGE FlexibleInstances #-}
5+ {-# LANGUAGE LambdaCase #-}
56{-# LANGUAGE MultiParamTypeClasses #-}
67{-# LANGUAGE NamedFieldPuns #-}
78{-# LANGUAGE RecordWildCards #-}
89{-# LANGUAGE ScopedTypeVariables #-}
910{-# LANGUAGE StandaloneDeriving #-}
11+ {-# LANGUAGE TupleSections #-}
1012{-# LANGUAGE TypeFamilyDependencies #-}
1113{-# LANGUAGE TypeOperators #-}
1214{-# LANGUAGE UndecidableInstances #-}
@@ -35,7 +37,11 @@ import Crypto.Random (MonadRandom)
3537import Data.Bimap (Bimap )
3638import qualified Data.Bimap as Bimap
3739import Data.Constraint
40+ import Data.List (sortOn )
3841import Data.Reflection (Given (.. ), give )
42+ import Data.Map.Strict (Map )
43+ import qualified Data.Map.Strict as Map
44+ import Data.Maybe (catMaybes , fromJust )
3945import qualified Data.Set as Set
4046import Data.Sequence (Seq )
4147import qualified Data.Sequence as Seq
@@ -167,11 +173,10 @@ instance (PBftCrypto c, Typeable c) => OuroborosTag (PBft c) where
167173
168174 type IsLeader (PBft c ) = PBftIsLeader c
169175
170- -- | Chain state consists of two things:
171- -- - a list of the last 'pbftSignatureWindow' signatures.
172- -- - The last seen block slot
176+ -- | Chain state consists of a map from genesis keys to the list of blocks
177+ -- which they have issued.
173178 type ChainState (PBft c ) =
174- Seq (PBftVerKeyHash c , SlotNo )
179+ Map (PBftVerKeyHash c ) ( Seq SlotNo )
175180
176181 protocolSecurityParam = pbftSecurityParam . pbftParams
177182
@@ -201,9 +206,13 @@ instance (PBftCrypto c, Typeable c) => OuroborosTag (PBft c) where
201206 Right () -> return ()
202207 Left err -> throwError $ PBftInvalidSignature err
203208
204- let (signers, lastSlot) = ( takeR winSize $ fst <$> chainState
205- , maybe (SlotNo 0 ) snd $ Seq. lookup (Seq. length chainState) chainState
206- )
209+ -- We always include slot number 0 in case there are no signers yet.
210+ let lastSlot = maximum . (SlotNo 0 : )
211+ $ (\ case
212+ _ Seq. :|> l -> l
213+ _ -> SlotNo 0
214+ )
215+ <$> Map. elems chainState
207216
208217 -- FIXME confirm that non-strict inequality is ok in general.
209218 -- It's here because EBBs have the same slot as the first block of their
@@ -214,26 +223,101 @@ instance (PBftCrypto c, Typeable c) => OuroborosTag (PBft c) where
214223 case Bimap. lookupR (hashVerKey pbftIssuer) dms of
215224 Nothing -> throwError $ PBftNotGenesisDelegate (hashVerKey pbftIssuer) lv
216225 Just gk -> do
217- let totalSigners = Seq. length signers
218- gkSigners = Seq. length ( Seq. filter ( == gk) signers)
226+ let totalSigners = chainStateSize chainState
227+ gkSigners = maybe 0 Seq. length $ Map. lookup gk chainState
219228 when (totalSigners >= winSize && gkSigners > wt)
220229 $ throwError (PBftExceededSignThreshold totalSigners gkSigners)
221- return $! takeR ( winSize + 2 * k) chainState Seq. |> (gk, blockSlot b)
230+ return $! insertSigner gk (blockSlot b) $ pruneChainState ( winSize + 2 * k) chainState
222231 where
223232 PBftParams {.. } = pbftParams
224233 PBftFields {.. } = headerPBftFields cfg b
225234 winSize = fromIntegral pbftSignatureWindow
226235 SecurityParam (fromIntegral -> k) = pbftSecurityParam
227236 wt = floor $ pbftSignatureThreshold * fromIntegral winSize
228- -- Take the rightmost n elements of a sequence
229- takeR :: Integral i => i -> Seq a -> Seq a
230- takeR (fromIntegral -> n) s = Seq. drop (Seq. length s - n - 1 ) s
231237
232238 rewindChainState _ cs mSlot = case mSlot of
233- Origin -> Just Seq. empty
234- At slot -> case Seq. takeWhileL (\ (_, s) -> s <= slot) cs of
235- _ Seq. :<| _ -> Just cs
236- _ -> Nothing
239+ Origin -> Just Map. empty
240+ At slot -> let oldCs = Map. map (Seq. takeWhileL (\ s -> s <= slot)) cs in
241+ if (all Seq. null $ Map. elems oldCs) then Nothing else Just oldCs
242+
243+ -- | Prune the chain state to the given size by dropping the signers in the
244+ -- oldest slots.
245+ --
246+ -- Examples
247+ --
248+ -- $setup
249+ -- >>> :set -XOverloadedLists
250+ -- >>> :set -XScopedTypeVariables
251+ -- >>> import Test.QuickCheck
252+ -- >>> :{
253+ -- newtype TestChainState = TestChainState (Map Int (Seq Int))
254+ -- instance Arbitrary TestChainState where
255+ -- arbitrary = do
256+ -- numElts <- arbitrary
257+ -- ub <- arbitrary
258+ -- return . TestChainState $ Map.fromList
259+ -- [(idx, elts) | idx <- [0..numElts]
260+ -- , let elts = Seq.fromList [i | i <- [0..ub], i `mod` numElts == idx]
261+ -- ]
262+ -- :}
263+
264+ -- prop> \cs -> pruneChainState (chainStateSize cs) cs == cs
265+ --
266+ -- prop> \n (TestChainState cs) -> n > 0 && (chainStateSize cs) > n ==> (chainStateSize (pruneChainState n cs)) == n
267+ --
268+ -- >>> :{
269+ -- let (cs :: Map Char (Seq Int)) = [('a', [1,2,5]), ('b', [3, 4, 6])]
270+ -- in pruneChainState 2 cs
271+ -- :}
272+ -- fromList [('a',fromList [5]),('b',fromList [6])]
273+ --
274+ pruneChainState :: forall k v . (Ord k , Ord v ) => Int -> Map k (Seq v ) -> Map k (Seq v )
275+ pruneChainState toSize cs = go
276+ cs
277+ (sortOn snd . catMaybes $ strengthr . (fmap (Seq. lookup 0 )) <$> (Map. toAscList cs))
278+ (max 0 $ chainStateSize cs - toSize)
279+ where
280+ go :: Map k (Seq v )
281+ -> [(k , v )]
282+ -> Int
283+ -> Map k (Seq v )
284+ go fromCS idx toDrop = if toDrop <= 0 then fromCS else case idx of
285+ [] -> fromCS
286+ (gk,_): xs@ ((_,nextLowest): _) ->
287+ let (newSeq, numDropped) = fromJust $ dropWhileL (< nextLowest) <$> Map. lookup gk fromCS
288+ newIdx = case newSeq of
289+ x Seq. :<| _ -> sortOn snd $ (gk, x) : xs
290+ _ -> xs
291+ in go (Map. insert gk newSeq fromCS) newIdx (toDrop - numDropped)
292+ -- Only one genesis key
293+ (gk,_): [] ->
294+ let newSeq = fromJust $ Seq. drop toDrop <$> Map. lookup gk fromCS
295+ in Map. insert gk newSeq fromCS
296+
297+ chainStateSize :: Map k (Seq v ) -> Int
298+ chainStateSize cs = sum $ Seq. length <$> Map. elems cs
299+
300+ -- | Functorial strength on the right
301+ strengthr :: Functor f => (a , f b ) -> f (a , b )
302+ strengthr (a, fb) = fmap (a,) fb
303+
304+ -- | Variant of 'dropWhileL' which also returns the number of elements dropped
305+ dropWhileL :: (a -> Bool ) -> Seq a -> (Seq a , Int )
306+ dropWhileL f s = let res = Seq. dropWhileL f s in
307+ (res, Seq. length s - Seq. length res)
308+
309+ -- | Insert a signatory into the chain state.
310+ insertSigner
311+ :: PBftCrypto c
312+ => PBftVerKeyHash c
313+ -> SlotNo
314+ -> ChainState (PBft c )
315+ -> ChainState (PBft c )
316+ insertSigner gk s =
317+ Map. alter (\ case
318+ Just es -> Just $ es Seq. |> s
319+ Nothing -> Just $ Seq. singleton s
320+ ) gk
237321
238322{- ------------------------------------------------------------------------------
239323 PBFT node order
0 commit comments