@@ -92,6 +92,7 @@ module Chainweb.BlockHeader.Internal
9292, blockEpochStart
9393, blockNonce
9494, blockHash
95+
9596-- ** Utilities
9697, _blockPow
9798, blockPow
@@ -130,6 +131,19 @@ module Chainweb.BlockHeader.Internal
130131-- * Create a new BlockHeader
131132, newBlockHeader
132133
134+ -- * Fork State
135+ , blockForkState
136+ , _blockForkNumber
137+ , blockForkNumber
138+ , _blockForkVotes
139+ , blockForkVotes
140+ , forkEpochLength
141+ , isForkEpochStart
142+ , isForkCountBlock
143+ , isForkVoteBlock
144+ , newForkState
145+ , isLastForkEpochBlock
146+
133147-- * CAS Constraint
134148, BlockHeaderCas
135149
@@ -186,6 +200,7 @@ import Numeric.AffineSpace
186200import Numeric.Natural
187201import System.IO.Unsafe
188202import Text.Read (readEither )
203+ import Chainweb.ForkState
189204
190205-- -------------------------------------------------------------------------- --
191206-- Nonce
@@ -244,6 +259,8 @@ decodeEpochStartTime = EpochStartTime <$> decodeTime
244259
245260-- -----------------------------------------------------------------------------
246261-- Feature Flags
262+ --
263+ -- Deprecated: renamed into 'blockForkState'
247264
248265newtype FeatureFlags = FeatureFlags Word64
249266 deriving stock (Show , Eq , Generic )
@@ -600,7 +617,7 @@ epochStart ph@(ParentHeader p) adj (BlockCreationTime bt)
600617 --
601618 -- The result is guaranteed to be non-empty
602619 --
603- adjCreationTimes = fmap ( _blockCreationTime)
620+ adjCreationTimes = fmap _blockCreationTime
604621 $ HM. insert cid (_parentHeader ph)
605622 $ HM. filter (not . isGenesisBlockHeader)
606623 $ fmap _parentHeader adj
@@ -693,7 +710,7 @@ genesisBlockHeaders = \v ->
693710 testnetGenesisHeaders = makeGenesisBlockHeaders testnet04
694711
695712genesisBlockHeader :: (HasCallStack , HasChainId p ) => ChainwebVersion -> p -> BlockHeader
696- genesisBlockHeader v p = genesisBlockHeaders v ^?! at (_chainId p) . _Just
713+ genesisBlockHeader v p = genesisBlockHeaders v ^?! ix (_chainId p)
697714
698715makeGenesisBlockHeaders :: ChainwebVersion -> HashMap ChainId BlockHeader
699716makeGenesisBlockHeaders v = HM. fromList [ (cid, makeGenesisBlockHeader v cid) | cid <- HS. toList (chainIds v)]
@@ -1049,6 +1066,87 @@ instance FromJSON (ObjectEncoded BlockHeader) where
10491066 $ fmap ObjectEncoded . parseBlockHeaderObject
10501067 {-# INLINE parseJSON #-}
10511068
1069+ -- -------------------------------------------------------------------------- --
1070+ -- Fork State
1071+
1072+ blockForkState :: Lens' BlockHeader ForkState
1073+ blockForkState = blockFlags . coerced
1074+
1075+ _blockForkNumber :: BlockHeader -> ForkNumber
1076+ _blockForkNumber = view (blockForkState . forkNumber)
1077+
1078+ _blockForkVotes :: BlockHeader -> ForkVotes
1079+ _blockForkVotes = view (blockForkState . forkVotes)
1080+
1081+ blockForkNumber :: Lens' BlockHeader ForkNumber
1082+ blockForkNumber = blockForkState . forkNumber
1083+
1084+ blockForkVotes :: Lens' BlockHeader ForkVotes
1085+ blockForkVotes = blockForkState . forkVotes
1086+
1087+ -- | Returns whether a block is the first block in a fork epoch.
1088+ --
1089+ isForkEpochStart :: BlockHeader -> Bool
1090+ isForkEpochStart hdr =
1091+ rem (int $ view blockHeight hdr) forkEpochLength == 0
1092+
1093+ isLastForkEpochBlock :: BlockHeader -> Bool
1094+ isLastForkEpochBlock hdr =
1095+ rem (1 + int (view blockHeight hdr)) forkEpochLength == 0
1096+
1097+ -- | Returns whether a block is at a height at which voting occurs.
1098+ --
1099+ isForkVoteBlock :: BlockHeader -> Bool
1100+ isForkVoteBlock hdr =
1101+ rem (int $ view blockHeight hdr) forkEpochLength < (forkEpochLength - 120 )
1102+
1103+ -- | Returns whether a block is at a height at which vote couting occurs.
1104+ --
1105+ isForkCountBlock :: BlockHeader -> Bool
1106+ isForkCountBlock hdr = not (isForkVoteBlock hdr)
1107+
1108+ -- | New Fork State computation
1109+ --
1110+ -- The Boolean parameter indicates whether the block votes "yes" (True) to
1111+ -- increasing the fork number.
1112+ --
1113+ -- Callers of this function must not just unconditionally vote "yes". Instead,
1114+ -- they should vote "yes" only if the current fork number is less than the
1115+ -- maximum fork number that the version of the code supports.
1116+ --
1117+ -- TODO: replace the Boolean parameter with a 'maxSupportedForkNumber'
1118+ -- parameter.
1119+ --
1120+ -- * isForkEpochStart -> forkNumber is deterministically increased
1121+ -- * isForkEpochStart -> forkVote is nondeterministically reset to 0 or forkStep
1122+ -- * forkVoteBlock && not isForkVoteStart -> forkVotes are non-deterministically monotonicly increasing
1123+ -- * forkCountBlock -> forkVotes are deterministically set
1124+ --
1125+ newForkState
1126+ :: HM. HashMap ChainId ParentHeader
1127+ -- ^ Adjacent parent headers
1128+ -> ParentHeader
1129+ -- Parent block header
1130+ -> Bool
1131+ -- ^ Non-deterministcally selected vote (True = yes, False = no)
1132+ -> ForkState
1133+ newForkState as p vote
1134+ | isLastForkEpochBlock (view parentHeader p) = cur
1135+ -- reset votes and vote
1136+ & forkVotes .~ (if vote then addVote else id ) resetVotes
1137+ -- based on current vote count decide whether to increase fork number
1138+ & forkNumber %~ (if decideVotes curVotes then succ else id )
1139+ | isForkVoteBlock (view parentHeader p) = cur
1140+ -- vote
1141+ & forkVotes %~ (if vote then addVote else id )
1142+ | otherwise = cur
1143+ -- do one vote counting step
1144+ & forkVotes .~ countVotes allParentVotes
1145+ where
1146+ cur = view (parentHeader . blockForkState) p
1147+ curVotes = view (parentHeader . blockForkVotes ) p
1148+ allParentVotes = view (parentHeader . blockForkVotes) <$> (p : HM. elems as)
1149+
10521150-- -------------------------------------------------------------------------- --
10531151-- IsBlockHeader
10541152
@@ -1114,7 +1212,7 @@ newBlockHeader adj pay nonce t p@(ParentHeader b) =
11141212 cid = _chainId p
11151213 v = _chainwebVersion p
11161214 target = powTarget p adj t
1117- adjHashes = BlockHashRecord $ ( _blockHash . _parentHeader) <$> adj
1215+ adjHashes = BlockHashRecord $ _blockHash . _parentHeader <$> adj
11181216
11191217-- -------------------------------------------------------------------------- --
11201218-- TreeDBEntry instance
@@ -1141,7 +1239,8 @@ instance TreeDbEntry BlockHeader where
11411239-- on the graph of the parent.
11421240--
11431241headerSizes :: ChainwebVersion -> Rule BlockHeight Natural
1144- headerSizes v = fmap (\ g -> _versionHeaderBaseSizeBytes v + 36 * degree g + 2 ) $ _versionGraphs v
1242+ headerSizes v = (\ g -> _versionHeaderBaseSizeBytes v + 36 * degree g + 2 )
1243+ <$> _versionGraphs v
11451244
11461245-- | The size of the serialized block header.
11471246--
0 commit comments