@@ -15,14 +15,16 @@ module Concordium.KonsensusV1.Types where
1515import Control.Monad
1616import Data.Bits
1717import qualified Data.ByteString as BS
18+ import qualified Data.ByteString.Short as BSS
1819import qualified Data.Map.Strict as Map
1920import Data.Maybe
21+ import qualified Data.ProtoLens.Combinators as Proto
2022import Data.Serialize
2123import qualified Data.Set as Set
2224import Data.Singletons
2325import qualified Data.Vector as Vector
2426import Data.Word
25- import Numeric.Natural
27+ import Lens.Micro.Platform
2628
2729import qualified Concordium.Crypto.BlockSignature as BlockSig
2830import qualified Concordium.Crypto.BlsSignature as Bls
@@ -43,8 +45,6 @@ import Concordium.Types.TransactionOutcomes
4345import Concordium.Types.Transactions
4446import Concordium.Utils.BinarySearch
4547import Concordium.Utils.Serialization
46- import qualified Data.ProtoLens.Combinators as Proto
47- import Lens.Micro.Platform
4848import qualified Proto.V2.Concordium.Types as Proto
4949import qualified Proto.V2.Concordium.Types_Fields as ProtoFields
5050
@@ -241,69 +241,80 @@ computeFinalizationCommitteeHash FinalizationCommittee{..} =
241241-- | A set of 'FinalizerIndex'es.
242242-- This is represented as a bit vector, where the bit @i@ is set iff the finalizer index @i@ is
243243-- in the set.
244- newtype FinalizerSet = FinalizerSet { theFinalizerSet :: Natural }
244+ newtype FinalizerSet = FinalizerSet { theFinalizerSet :: BSS. ShortByteString }
245245 deriving (Eq )
246246
247247-- | The serialization of a 'FinalizerSet' consists of a length (Word32, big-endian), followed by
248248-- that many bytes, the first of which (if any) must be non-zero. These bytes encode the bit-vector
249249-- in big-endian. This enforces that the serialization of a finalizer set is unique.
250+ --
251+ -- Internally, the bit-vector is stored little-endian, so finalizer index @i@ is represented by
252+ -- bit @i mod 8@ of byte @i div 8@. This makes membership tests constant time and keeps decoding
253+ -- linear in the number of serialized bytes.
250254instance Serialize FinalizerSet where
251- put fs = do
252- let (byteCount, putBytes) = unroll 0 (return () ) (theFinalizerSet fs)
253- putWord32be byteCount
254- putBytes
255- where
256- unroll :: Word32 -> Put -> Natural -> (Word32 , Put )
257- -- Compute the number of bytes and construct a 'Put' that serializes in big-endian.
258- -- We do this by adding the low order byte to the accumulated 'Put' (at the start)
259- -- and recursing with the bitvector shifted right 8 bits.
260- unroll bc cont 0 = (bc, cont)
261- unroll bc cont n = unroll (bc + 1 ) (putWord8 (fromIntegral n) >> cont) (shiftR n 8 )
255+ put (FinalizerSet fs) = do
256+ putWord32be $ fromIntegral $ BSS. length fs
257+ putByteString $ BS. reverse $ BSS. fromShort fs
262258 get = label " FinalizerSet" $ do
263259 byteCount <- getWord32be
264- FinalizerSet <$> roll1 byteCount
265- where
266- roll1 0 = return 0
267- roll1 bc = do
268- b <- getWord8
269- when (b == 0 ) $ fail " unexpected 0 byte"
270- roll (bc - 1 ) (fromIntegral b)
271- roll 0 n = return n
272- roll bc n = do
273- b <- getWord8
274- roll (bc - 1 ) (shiftL n 8 .|. fromIntegral b)
260+ remainingBytes <- remaining
261+ when (toInteger byteCount > toInteger remainingBytes) $
262+ fail " FinalizerSet length exceeds remaining input"
263+ bytes <- getByteString (fromIntegral byteCount)
264+ when (not (BS. null bytes) && BS. head bytes == 0 ) $
265+ fail " unexpected 0 byte"
266+ return $! FinalizerSet $! BSS. toShort $! BS. reverse bytes
275267
276268-- | Convert a 'FinalizerSet' to a list of 'FinalizerIndex', in ascending order.
277269finalizerList :: FinalizerSet -> [FinalizerIndex ]
278- finalizerList = unroll 0 . theFinalizerSet
270+ finalizerList ( FinalizerSet fs) = concat $ zipWith finalizersInByte [ 0 , 8 .. ] ( BSS. unpack fs)
279271 where
280- unroll _ 0 = []
281- unroll i x
282- | testBit x 0 = FinalizerIndex i : r
283- | otherwise = r
284- where
285- r = unroll (i + 1 ) (shiftR x 1 )
272+ finalizersInByte base byte =
273+ [ FinalizerIndex (base + bitIndex)
274+ | bitIndex <- [0 .. 7 ],
275+ testBit byte (fromIntegral bitIndex)
276+ ]
286277
287278-- | The empty set of finalizers
288279emptyFinalizerSet :: FinalizerSet
289- emptyFinalizerSet = FinalizerSet 0
280+ emptyFinalizerSet = FinalizerSet BSS. empty
290281
291282-- | Add a finalizer to a 'FinalizerSet'.
292283addFinalizer :: FinalizerSet -> FinalizerIndex -> FinalizerSet
293- addFinalizer (FinalizerSet setOfFinalizers) (FinalizerIndex i) = FinalizerSet $ setBit setOfFinalizers (fromIntegral i)
284+ addFinalizer (FinalizerSet setOfFinalizers) (FinalizerIndex i) =
285+ FinalizerSet $ BSS. pack $ case splitAt byteIndex paddedBytes of
286+ (prefix, oldByte : suffix) -> prefix ++ setBit oldByte bitIndex : suffix
287+ -- This case cannot occur because 'paddedBytes' has length at least @byteIndex + 1@.
288+ _ -> paddedBytes
289+ where
290+ byteIndex = fromIntegral (i `div` 8 )
291+ bitIndex = fromIntegral (i `mod` 8 )
292+ bytes = BSS. unpack setOfFinalizers
293+ paddedBytes = bytes ++ replicate (byteIndex + 1 - length bytes) 0
294294
295295-- | Test whether a given finalizer index is present in a finalizer set.
296296memberFinalizerSet :: FinalizerIndex -> FinalizerSet -> Bool
297- memberFinalizerSet (FinalizerIndex fi) (FinalizerSet setOfFinalizers) =
298- testBit setOfFinalizers (fromIntegral fi)
297+ memberFinalizerSet (FinalizerIndex fi) (FinalizerSet setOfFinalizers)
298+ | byteIndex >= BSS. length setOfFinalizers = False
299+ | otherwise = testBit (BSS. index setOfFinalizers byteIndex) bitIndex
300+ where
301+ byteIndex = fromIntegral (fi `div` 8 )
302+ bitIndex = fromIntegral (fi `mod` 8 )
299303
300304-- | Convert a list of [FinalizerIndex] to a 'FinalizerSet'.
301305finalizerSet :: [FinalizerIndex ] -> FinalizerSet
302- finalizerSet = foldl' addFinalizer ( FinalizerSet 0 )
306+ finalizerSet = foldl' addFinalizer emptyFinalizerSet
303307
304308-- | Test if the first finalizer set is a subset of the second.
305309subsetFinalizerSet :: FinalizerSet -> FinalizerSet -> Bool
306- subsetFinalizerSet (FinalizerSet s1) (FinalizerSet s2) = s1 .&. s2 == s1
310+ subsetFinalizerSet (FinalizerSet s1) (FinalizerSet s2) = go 0 (BSS. unpack s1)
311+ where
312+ go _ [] = True
313+ go byteIndex (b1 : rest) = b1 .&. b2 == b1 && go (byteIndex + 1 ) rest
314+ where
315+ b2
316+ | byteIndex < BSS. length s2 = BSS. index s2 byteIndex
317+ | otherwise = 0
307318
308319instance Show FinalizerSet where
309320 show = show . finalizerList
@@ -329,7 +340,7 @@ data QuorumCertificate = QuorumCertificate
329340
330341-- | For generating a genesis quorum certificate with empty signature and empty finalizer set.
331342genesisQuorumCertificate :: BlockHash -> QuorumCertificate
332- genesisQuorumCertificate genesisHash = QuorumCertificate genesisHash 0 0 mempty $ FinalizerSet 0
343+ genesisQuorumCertificate genesisHash = QuorumCertificate genesisHash 0 0 mempty emptyFinalizerSet
333344
334345instance Serialize QuorumCertificate where
335346 put QuorumCertificate {.. } = do
@@ -1091,6 +1102,7 @@ data DerivableBlockHashesBHV (bhv :: BlockHashVersion) where
10911102 DerivableBlockHashesBHV 'BlockHashVersion1
10921103
10931104deriving instance Show (DerivableBlockHashesBHV bhv )
1105+
10941106deriving instance Eq (DerivableBlockHashesBHV bhv )
10951107
10961108-- | Serialize derivable hashes.
0 commit comments