From 7d02e2a2f62752c5cf671539461a0598be81f9b7 Mon Sep 17 00:00:00 2001 From: KadenaFriend <241389759+kdafriend@users.noreply.github.com> Date: Tue, 6 Jan 2026 15:15:14 +0000 Subject: [PATCH 1/8] Extend ForkHeight to manage by ForkNumber --- src/Chainweb/Chainweb/Configuration.hs | 6 ++- src/Chainweb/Pact4/ModuleCache.hs | 1 + src/Chainweb/Version.hs | 48 +++++++++++++++++++++--- src/Chainweb/Version/Guards.hs | 43 +++++++++++---------- src/Chainweb/Version/Mainnet.hs | 8 ++-- src/Chainweb/Version/RecapDevelopment.hs | 2 +- src/Chainweb/Version/Testnet04.hs | 6 +-- src/Chainweb/Version/Utils.hs | 2 +- test/lib/Chainweb/Test/TestVersions.hs | 2 +- 9 files changed, 82 insertions(+), 36 deletions(-) diff --git a/src/Chainweb/Chainweb/Configuration.hs b/src/Chainweb/Chainweb/Configuration.hs index 0f27adee78..2a65c6cc0e 100644 --- a/src/Chainweb/Chainweb/Configuration.hs +++ b/src/Chainweb/Chainweb/Configuration.hs @@ -605,10 +605,12 @@ parseVersion = constructVersion maybe (_versionUpgrades winningVersion) (\fub' -> OnChains $ HM.mapWithKey (\cid _ -> - case winningVersion ^?! versionForks . at fub' . _Just . atChain cid of + let currentUpgrades = winningVersion ^?! versionUpgrades . atChain cid + in case winningVersion ^?! versionForks . at fub' . _Just . atChain cid of ForkNever -> error "Chainweb.Chainweb.Configuration.parseVersion: the fork upper bound never occurs in this version." - ForkAtBlockHeight fubHeight -> HM.filterWithKey (\bh _ -> bh <= fubHeight) (winningVersion ^?! versionUpgrades . atChain cid) + ForkAtBlockHeight fubHeight -> HM.filterWithKey (\bh _ -> bh <= fubHeight) currentUpgrades ForkAtGenesis -> winningVersion ^?! versionUpgrades . atChain cid + ForkAtForkNumber _ -> currentUpgrades -- For now, version upgrades were only allowed at blok heights ) (HS.toMap (chainIds winningVersion)) ) fub diff --git a/src/Chainweb/Pact4/ModuleCache.hs b/src/Chainweb/Pact4/ModuleCache.hs index ea961e8b80..e6e047c619 100644 --- a/src/Chainweb/Pact4/ModuleCache.hs +++ b/src/Chainweb/Pact4/ModuleCache.hs @@ -72,3 +72,4 @@ cleanModuleCache v cid bh = ForkAtBlockHeight bh' -> bh == bh' ForkAtGenesis -> bh == genesisHeight v cid ForkNever -> False + ForkAtForkNumber _ -> error "ChainWeb217Pact is not supposed to be indexed by a ForkNumber" diff --git a/src/Chainweb/Version.hs b/src/Chainweb/Version.hs index 222f26887a..7680e6f293 100644 --- a/src/Chainweb/Version.hs +++ b/src/Chainweb/Version.hs @@ -37,6 +37,7 @@ module Chainweb.Version -- * Properties of Chainweb Version Fork(..) , ForkHeight(..) + , succByHeight , _ForkAtBlockHeight , _ForkAtGenesis , _ForkNever @@ -322,12 +323,49 @@ instance FromJSON Fork where instance FromJSONKey Fork where fromJSONKey = FromJSONKeyTextParser $ either fail return . eitherFromText -data ForkHeight = ForkAtBlockHeight !BlockHeight | ForkAtGenesis | ForkNever - deriving stock (Generic, Eq, Ord, Show) +data ForkHeight = ForkAtForkNumber !ForkNumber | ForkAtBlockHeight !BlockHeight | ForkAtGenesis | ForkNever + deriving stock (Generic, Eq, Show) deriving anyclass (Hashable, NFData) +instance Bounded ForkHeight where + minBound = ForkAtGenesis + maxBound = ForkNever + +instance Ord ForkHeight where + compare ForkAtGenesis ForkAtGenesis = EQ + compare ForkNever ForkNever = EQ + compare (ForkAtForkNumber a) (ForkAtForkNumber b) = compare a b + compare (ForkAtBlockHeight a) (ForkAtBlockHeight b) = compare a b + compare ForkAtGenesis _ = LT + compare _ ForkAtGenesis = GT + compare ForkNever _ = GT + compare _ ForkNever = LT + compare (ForkAtForkNumber fn) (ForkAtBlockHeight _) + | fn == 0 = LT + | otherwise = GT + compare (ForkAtBlockHeight _) (ForkAtForkNumber fn) + | fn == 0 = GT + | otherwise = LT + +-- We consider the following ordering for Forks: +-- - ForkAtGenesis +-- - ForkNumber = 0 (unusual case) +-- - BlockHeihgt = 0 (unusual case) +-- - Blockkheight = 1 +-- .. +-- - BlockHeight = n +-- - ForkNumber = 1 +-- .. +-- - ForkNumber = n +-- - ForkNever + makePrisms ''ForkHeight +succByHeight:: ForkHeight -> ForkHeight +succByHeight (ForkAtBlockHeight x) = ForkAtBlockHeight $ succ x +succByHeight ForkNever = ForkNever +succByHeight _ = error "Only a Blockheight defined fork can be succ'ed" + newtype ChainwebVersionName = ChainwebVersionName { getChainwebVersionName :: T.Text } deriving stock (Generic, Eq, Ord) @@ -491,9 +529,9 @@ data ChainwebVersion -- -- NOTE: This is internal. For the actual size of the serialized header -- use 'headerSizeBytes'. - , _versionMaxBlockGasLimit :: Rule BlockHeight (Maybe Natural) + , _versionMaxBlockGasLimit :: Rule ForkHeight (Maybe Natural) -- ^ The maximum gas limit for an entire block. - , _versionSpvProofRootValidWindow :: Rule BlockHeight (Maybe Word64) + , _versionSpvProofRootValidWindow :: Rule ForkHeight (Maybe Word64) -- ^ The minimum number of block headers a chainweb node should -- retain in its history at all times. , _versionBootstraps :: [PeerInfo] @@ -504,7 +542,7 @@ data ChainwebVersion -- ^ Whether to disable any core functionality. , _versionDefaults :: VersionDefaults -- ^ Version-specific defaults that can be overridden elsewhere. - , _versionVerifierPluginNames :: ChainMap (Rule BlockHeight (Set VerifierName)) + , _versionVerifierPluginNames :: ChainMap (Rule ForkHeight (Set VerifierName)) -- ^ Verifier plugins that can be run to verify transaction contents. , _versionQuirks :: VersionQuirks -- ^ Modifications to behavior at particular blockheights diff --git a/src/Chainweb/Version/Guards.hs b/src/Chainweb/Version/Guards.hs index 90cbd94321..8e12df44cc 100644 --- a/src/Chainweb/Version/Guards.hs +++ b/src/Chainweb/Version/Guards.hs @@ -2,6 +2,9 @@ {-# LANGUAGE ImportQualifiedPost #-} {-# LANGUAGE ScopedTypeVariables #-} +-- TODO Remove this when checkFork' will be used for real +{-# OPTIONS_GHC -Wno-unused-top-binds #-} + -- | -- Module: Chainweb.Version.Guards -- Copyright: Copyright © 2023 Kadena LLC. @@ -70,6 +73,7 @@ import Chainweb.BlockHeight import Chainweb.ChainId import Chainweb.Pact4.Transaction qualified as Pact4 import Chainweb.Utils.Rule +import Chainweb.ForkState import Chainweb.Version import Control.Lens import Data.Word (Word64) @@ -86,32 +90,33 @@ import Pact.Types.Scheme (PPKScheme(ED25519, WebAuthn)) getForkHeight :: Fork -> ChainwebVersion -> ChainId -> ForkHeight getForkHeight fork v cid = v ^?! versionForks . at fork . _Just . atChain cid +-- Check Fork by height checkFork - :: (BlockHeight -> ForkHeight -> Bool) + :: (ForkHeight -> ForkHeight -> Bool) -> Fork -> ChainwebVersion -> ChainId -> BlockHeight -> Bool -checkFork p f v cid h = p h (getForkHeight f v cid) +checkFork p f v cid h = p (ForkAtBlockHeight h) (getForkHeight f v cid) + +-- CheckFork by forkNumber +checkFork' + :: (ForkHeight -> ForkHeight -> Bool) + -> Fork -> ChainwebVersion -> ChainId -> ForkNumber -> Bool +checkFork' p f v cid fn = p (ForkAtForkNumber fn) (getForkHeight f v cid) + -after :: BlockHeight -> ForkHeight -> Bool -after bh (ForkAtBlockHeight bh') = bh > bh' -after _ ForkAtGenesis = True -after _ ForkNever = False +after :: ForkHeight -> ForkHeight -> Bool +after = (>) -atOrAfter :: BlockHeight -> ForkHeight -> Bool -atOrAfter bh (ForkAtBlockHeight bh') = bh >= bh' -atOrAfter _ ForkAtGenesis = True -atOrAfter _ ForkNever = False +atOrAfter :: ForkHeight -> ForkHeight -> Bool +atOrAfter = (>=) -before :: BlockHeight -> ForkHeight -> Bool -before bh (ForkAtBlockHeight bh') = bh < bh' -before _ ForkAtGenesis = False -before _ ForkNever = True +before :: ForkHeight -> ForkHeight -> Bool +before = (<) -- Intended for forks that intend to run upgrades at exactly one height, and so -- can't be "pre-activated" for genesis. -atNotGenesis :: BlockHeight -> ForkHeight -> Bool -atNotGenesis bh (ForkAtBlockHeight bh') = bh == bh' +atNotGenesis :: ForkHeight -> ForkHeight -> Bool atNotGenesis _ ForkAtGenesis = error "fork cannot be at genesis" -atNotGenesis _ ForkNever = False +atNotGenesis fh fh' = fh == fh' -- -------------------------------------------------------------------------- -- -- Header Validation Guards @@ -332,11 +337,11 @@ pact4ParserVersion v cid bh maxBlockGasLimit :: ChainwebVersion -> BlockHeight -> Maybe Natural maxBlockGasLimit v bh = snd $ ruleZipperHere $ snd - $ ruleSeek (\h _ -> bh >= h) (_versionMaxBlockGasLimit v) + $ ruleSeek (\h _ -> ForkAtBlockHeight bh >= h) (_versionMaxBlockGasLimit v) minimumBlockHeaderHistory :: ChainwebVersion -> BlockHeight -> Maybe Word64 minimumBlockHeaderHistory v bh = snd $ ruleZipperHere $ snd - $ ruleSeek (\h _ -> bh >= h) (_versionSpvProofRootValidWindow v) + $ ruleSeek (\h _ -> ForkAtBlockHeight bh >= h) (_versionSpvProofRootValidWindow v) -- | Different versions of Chainweb allow different PPKSchemes. -- diff --git a/src/Chainweb/Version/Mainnet.hs b/src/Chainweb/Version/Mainnet.hs index 52e0960dbb..d7a460da7e 100644 --- a/src/Chainweb/Version/Mainnet.hs +++ b/src/Chainweb/Version/Mainnet.hs @@ -164,11 +164,11 @@ mainnet = ChainwebVersion , _versionWindow = WindowWidth 120 , _versionHeaderBaseSizeBytes = 318 - 110 , _versionMaxBlockGasLimit = - (succ $ mainnet ^?! versionForks . at Chainweb216Pact . _Just . atChain (unsafeChainId 0) . _ForkAtBlockHeight, Just 180_000) `Above` + (succByHeight $ mainnet ^?! versionForks . at Chainweb216Pact . _Just . atChain (unsafeChainId 0), Just 180_000) `Above` Bottom (minBound, Nothing) , _versionSpvProofRootValidWindow = - (succ $ mainnet ^?! versionForks . at Chainweb31 . _Just . atChain (unsafeChainId 0) . _ForkAtBlockHeight, Nothing) `Above` - (succ $ mainnet ^?! versionForks . at Chainweb231Pact . _Just . atChain (unsafeChainId 0) . _ForkAtBlockHeight, Just 20_000) `Above` + (succByHeight $ mainnet ^?! versionForks . at Chainweb31 . _Just . atChain (unsafeChainId 0), Nothing) `Above` + (succByHeight $ mainnet ^?! versionForks . at Chainweb231Pact . _Just . atChain (unsafeChainId 0) , Just 20_000) `Above` Bottom (minBound, Nothing) , _versionBootstraps = domainAddr2PeerInfo mainnetBootstrapHosts , _versionGenesis = VersionGenesis @@ -223,7 +223,7 @@ mainnet = ChainwebVersion , _disableMempoolSync = False } , _versionVerifierPluginNames = AllChains $ - (4_577_530, Set.fromList [VerifierName "hyperlane_v3_message"]) `Above` + (ForkAtBlockHeight $ BlockHeight 4_577_530, Set.fromList [VerifierName "hyperlane_v3_message"]) `Above` Bottom (minBound, mempty) , _versionQuirks = VersionQuirks { _quirkGasFees = onChains diff --git a/src/Chainweb/Version/RecapDevelopment.hs b/src/Chainweb/Version/RecapDevelopment.hs index 4e7fc17fb6..7936994326 100644 --- a/src/Chainweb/Version/RecapDevelopment.hs +++ b/src/Chainweb/Version/RecapDevelopment.hs @@ -124,7 +124,7 @@ recapDevnet = ChainwebVersion , _disableMempoolSync = False } , _versionVerifierPluginNames = AllChains $ - (600, Set.fromList $ map VerifierName ["hyperlane_v3_message", "allow", "signed_list"]) `Above` + (ForkAtBlockHeight $ BlockHeight 600, Set.fromList $ map VerifierName ["hyperlane_v3_message", "allow", "signed_list"]) `Above` Bottom (minBound, mempty) , _versionQuirks = noQuirks , _versionForkNumber = 0 diff --git a/src/Chainweb/Version/Testnet04.hs b/src/Chainweb/Version/Testnet04.hs index 7a4da9db56..ef5ffe7b59 100644 --- a/src/Chainweb/Version/Testnet04.hs +++ b/src/Chainweb/Version/Testnet04.hs @@ -144,10 +144,10 @@ testnet04 = ChainwebVersion , _versionWindow = WindowWidth 120 , _versionHeaderBaseSizeBytes = 318 - 110 , _versionMaxBlockGasLimit = - (succ $ testnet04 ^?! versionForks . at Chainweb216Pact . _Just . atChain (unsafeChainId 0) . _ForkAtBlockHeight, Just 180_000) `Above` + (succByHeight $ testnet04 ^?! versionForks . at Chainweb216Pact . _Just . atChain (unsafeChainId 0) , Just 180_000) `Above` Bottom (minBound, Nothing) , _versionSpvProofRootValidWindow = - (succ $ testnet04 ^?! versionForks . at Chainweb231Pact . _Just . atChain (unsafeChainId 0) . _ForkAtBlockHeight, Just 20_000) `Above` + (succByHeight $ testnet04 ^?! versionForks . at Chainweb231Pact . _Just . atChain (unsafeChainId 0) , Just 20_000) `Above` Bottom (minBound, Nothing) , _versionBootstraps = domainAddr2PeerInfo testnet04BootstrapHosts , _versionGenesis = VersionGenesis @@ -190,7 +190,7 @@ testnet04 = ChainwebVersion { _disablePeerValidation = False , _disableMempoolSync = False } - , _versionVerifierPluginNames = AllChains $ (4_100_681, Set.fromList [VerifierName "hyperlane_v3_message"]) `Above` + , _versionVerifierPluginNames = AllChains $ (ForkAtBlockHeight $ BlockHeight $ 4_100_681, Set.fromList [VerifierName "hyperlane_v3_message"]) `Above` Bottom (minBound, mempty) , _versionQuirks = VersionQuirks { _quirkGasFees = onChains diff --git a/src/Chainweb/Version/Utils.hs b/src/Chainweb/Version/Utils.hs index b0cfb041ee..a7a32f781c 100644 --- a/src/Chainweb/Version/Utils.hs +++ b/src/Chainweb/Version/Utils.hs @@ -466,7 +466,7 @@ verifiersAt v cid bh = = snd $ ruleZipperHere $ snd - $ ruleSeek (\h _ -> bh >= h) + $ ruleSeek (\h _ -> ForkAtBlockHeight bh >= h) $ _versionVerifierPluginNames v ^?! atChain cid -- the mappings from names to verifier plugins is global. the list of verifier diff --git a/test/lib/Chainweb/Test/TestVersions.hs b/test/lib/Chainweb/Test/TestVersions.hs index b4a68d0ff4..f5afc26501 100644 --- a/test/lib/Chainweb/Test/TestVersions.hs +++ b/test/lib/Chainweb/Test/TestVersions.hs @@ -529,7 +529,7 @@ pact5InstantCpmTestVersionExpiryDisabled g = buildTestVersion $ \v -> v ) ) & versionSpvProofRootValidWindow .~ - ( (BlockHeight 5, Nothing) `Above` + ( (ForkAtBlockHeight $ BlockHeight 5, Nothing) `Above` Bottom (minBound, Just 20) ) From 489728fb94663a241b75c6beea174797added013 Mon Sep 17 00:00:00 2001 From: kdafriend <241389759+kdafriend@users.noreply.github.com> Date: Tue, 6 Jan 2026 18:43:06 +0100 Subject: [PATCH 2/8] Apply suggestions from code review Co-authored-by: Edmund Noble --- src/Chainweb/Version.hs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Chainweb/Version.hs b/src/Chainweb/Version.hs index 7680e6f293..85d3375b9b 100644 --- a/src/Chainweb/Version.hs +++ b/src/Chainweb/Version.hs @@ -350,8 +350,8 @@ instance Ord ForkHeight where -- We consider the following ordering for Forks: -- - ForkAtGenesis -- - ForkNumber = 0 (unusual case) --- - BlockHeihgt = 0 (unusual case) --- - Blockkheight = 1 +-- - BlockHeight = 0 (unusual case) +-- - BlockHeight = 1 -- .. -- - BlockHeight = n -- - ForkNumber = 1 @@ -361,7 +361,7 @@ instance Ord ForkHeight where makePrisms ''ForkHeight -succByHeight:: ForkHeight -> ForkHeight +succByHeight :: ForkHeight -> ForkHeight succByHeight (ForkAtBlockHeight x) = ForkAtBlockHeight $ succ x succByHeight ForkNever = ForkNever succByHeight _ = error "Only a Blockheight defined fork can be succ'ed" From 92fff2a0275c4db7e39eae3ec72a9464b4dba0da Mon Sep 17 00:00:00 2001 From: KadenaFriend <241389759+kdafriend@users.noreply.github.com> Date: Tue, 6 Jan 2026 19:17:28 +0000 Subject: [PATCH 3/8] Add comments --- src/Chainweb/Version.hs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Chainweb/Version.hs b/src/Chainweb/Version.hs index 85d3375b9b..0319d7610d 100644 --- a/src/Chainweb/Version.hs +++ b/src/Chainweb/Version.hs @@ -358,6 +358,11 @@ instance Ord ForkHeight where -- .. -- - ForkNumber = n -- - ForkNever +-- +-- During the LLC era, forks were triggered by block heights, with a fork number of 0 (called feature flag). +-- After version 3.1, forks are ONLY triggered by fork numbers, as soon as the fork number becomes equal to 1. +-- So the fork heights are sorted chronologically: first block heights, then fork numbers. + makePrisms ''ForkHeight From dd5a906b2c13b7933fa742427ad60d8c869e9018 Mon Sep 17 00:00:00 2001 From: KadenaFriend <241389759+kdafriend@users.noreply.github.com> Date: Wed, 7 Jan 2026 10:41:39 +0000 Subject: [PATCH 4/8] maxBlockGasLimit, minimumBlockHeaderHistory, verifiersAt use ForkNumber --- src/Chainweb/BlockHeaderDB/HeaderOracle.hs | 6 +++--- src/Chainweb/Chainweb.hs | 2 +- src/Chainweb/ForkState.hs | 6 ++++++ .../Pact/PactService/Pact4/ExecBlock.hs | 3 ++- .../Pact/PactService/Pact5/ExecBlock.hs | 6 +++--- src/Chainweb/Pact4/TransactionExec.hs | 6 ++++-- src/Chainweb/Pact5/TransactionExec.hs | 2 +- src/Chainweb/Pact5/Types.hs | 5 +++++ src/Chainweb/Version/Guards.hs | 19 ++++++++++++------- src/Chainweb/Version/Utils.hs | 8 +++++--- .../Chainweb/Test/Pact5/RemotePactTest.hs | 2 +- 11 files changed, 43 insertions(+), 22 deletions(-) diff --git a/src/Chainweb/BlockHeaderDB/HeaderOracle.hs b/src/Chainweb/BlockHeaderDB/HeaderOracle.hs index 7b3ba75456..6a6d2eb4d1 100644 --- a/src/Chainweb/BlockHeaderDB/HeaderOracle.hs +++ b/src/Chainweb/BlockHeaderDB/HeaderOracle.hs @@ -29,14 +29,14 @@ module Chainweb.BlockHeaderDB.HeaderOracle where import Chainweb.BlockHash (BlockHash) -import Chainweb.BlockHeader (BlockHeader, blockHash, blockHeight, genesisBlockHeader) +import Chainweb.BlockHeader (BlockHeader, blockHash, blockHeight, blockForkNumber, genesisBlockHeader) import Chainweb.BlockHeaderDB (BlockHeaderDb) import Chainweb.TreeDB (seekAncestor) import Chainweb.TreeDB qualified as TreeDB import Chainweb.Version (_chainwebVersion) import Chainweb.Version.Guards (minimumBlockHeaderHistory) import Control.Exception (Exception(..), throwIO) -import Control.Lens (view) +import Control.Lens import Numeric.Natural (Natural) -- | A 'HeaderOracle' is a 'BlockHeaderDb' with a lower and upper bound, and the only @@ -52,7 +52,7 @@ data HeaderOracle = HeaderOracle -- The lower bound of the oracle is determined by the 'spvProofExpirationWindow'. createSpv :: BlockHeaderDb -> BlockHeader -> IO HeaderOracle createSpv db upperBound = do - let mWindow = minimumBlockHeaderHistory (_chainwebVersion upperBound) (view blockHeight upperBound) + let mWindow = minimumBlockHeaderHistory (_chainwebVersion upperBound) (upperBound ^. blockForkNumber) (upperBound ^. blockHeight) let gh = genesisBlockHeader (_chainwebVersion upperBound) upperBound let defaultOracle = create db gh upperBound diff --git a/src/Chainweb/Chainweb.hs b/src/Chainweb/Chainweb.hs index e1ed1f0d54..28c6246db9 100644 --- a/src/Chainweb/Chainweb.hs +++ b/src/Chainweb/Chainweb.hs @@ -401,7 +401,7 @@ withChainwebInternal conf logger peer serviceSock rocksDb pactDbDir backupDir re (\cid x -> do let mcfg = validatingMempoolConfig cid v (_configBlockGasLimit conf) (_configMinGasPrice conf) -- NOTE: the gas limit may be set based on block height in future, so this approach may not be valid. - let maxGasLimit = fromIntegral <$> maxBlockGasLimit v maxBound + let maxGasLimit = fromIntegral <$> maxBlockGasLimit v maxBound maxBound case maxGasLimit of Just maxGasLimit' | _configBlockGasLimit conf > maxGasLimit' -> diff --git a/src/Chainweb/ForkState.hs b/src/Chainweb/ForkState.hs index dc793d341a..97d2dcf34f 100644 --- a/src/Chainweb/ForkState.hs +++ b/src/Chainweb/ForkState.hs @@ -25,6 +25,7 @@ module Chainweb.ForkState -- * Fork Number , ForkNumber(..) , forkNumber +, pact4ForkNumber -- * Fork Votes , ForkVotes(..) @@ -132,6 +133,11 @@ forkNumber = lens _forkNumber $ \(ForkState w) v -> ForkState $ (w .&. 0xFFFFFFFF00000000) .|. (fromIntegral v .&. 0xFFFFFFFF) + +-- Pact4 -> Pact5 transition happened during ForkNumber=0 era. +pact4ForkNumber:: ForkNumber +pact4ForkNumber = 0 + -- --------------------------------------------------------------------------- -- Fork Votes diff --git a/src/Chainweb/Pact/PactService/Pact4/ExecBlock.hs b/src/Chainweb/Pact/PactService/Pact4/ExecBlock.hs index b7e92d4487..a0fb97a8c1 100644 --- a/src/Chainweb/Pact/PactService/Pact4/ExecBlock.hs +++ b/src/Chainweb/Pact/PactService/Pact4/ExecBlock.hs @@ -95,6 +95,7 @@ import Chainweb.Payload.PayloadStore import Chainweb.Time import Chainweb.Utils hiding (check) import Chainweb.Version +import Chainweb.ForkState (pact4ForkNumber) import Chainweb.Version.Guards import Chainweb.Pact4.Backend.ChainwebPactDb import Data.Coerce @@ -163,7 +164,7 @@ execBlock currHeader payload = do return (totalGasUsed, pwo) where blockGasLimit = - fromIntegral <$> maxBlockGasLimit v (view blockHeight currHeader) + fromIntegral <$> maxBlockGasLimit v pact4ForkNumber (view blockHeight currHeader) logInitCache = liftPactServiceM $ do mc <- fmap (fmap instr . _getModuleCache) <$> use psInitCache diff --git a/src/Chainweb/Pact/PactService/Pact5/ExecBlock.hs b/src/Chainweb/Pact/PactService/Pact5/ExecBlock.hs index 557ff53ac2..44f5bc2b50 100644 --- a/src/Chainweb/Pact/PactService/Pact5/ExecBlock.hs +++ b/src/Chainweb/Pact/PactService/Pact5/ExecBlock.hs @@ -577,7 +577,7 @@ execExistingBlock -> CheckablePayload -> PactBlockM logger tbl (P.Gas, PayloadWithOutputs) execExistingBlock currHeader payload = do - parentBlockHeader <- view psParentHeader + parentBlockHeader <- _parentHeader <$> view psParentHeader let plData = checkablePayloadToPayloadData payload miner :: Miner <- decodeStrictOrThrow (_minerData $ view payloadDataMiner plData) txs <- liftIO $ pact5TransactionsFromPayload plData @@ -590,7 +590,7 @@ execExistingBlock currHeader payload = do isGenesis <- view psIsGenesis blockHandlePreCoinbase <- use pbBlockHandle let - txValidationTime = ParentCreationTime (view blockCreationTime $ _parentHeader parentBlockHeader) + txValidationTime = ParentCreationTime (parentBlockHeader ^. blockCreationTime) errors <- liftIO $ flip foldMap txs $ \tx -> do errorOrSuccess <- runExceptT $ validateParsedChainwebTx logger v cid db blockHandlePreCoinbase txValidationTime @@ -612,7 +612,7 @@ execExistingBlock currHeader payload = do postCoinbaseBlockHandle <- use pbBlockHandle let blockGasLimit = - Pact5.GasLimit . Pact5.Gas . fromIntegral <$> maxBlockGasLimit v (view blockHeight currHeader) + Pact5.GasLimit . Pact5.Gas . fromIntegral <$> maxBlockGasLimit v (parentBlockHeader ^. blockForkNumber) (currHeader ^. blockHeight) env <- ask (V.fromList -> results, (finalHandle, _finalBlockGasLimit)) <- diff --git a/src/Chainweb/Pact4/TransactionExec.hs b/src/Chainweb/Pact4/TransactionExec.hs index b877b1201b..7d324be85b 100644 --- a/src/Chainweb/Pact4/TransactionExec.hs +++ b/src/Chainweb/Pact4/TransactionExec.hs @@ -144,6 +144,7 @@ import qualified Pact.Utils.StableHashMap as SHM import Chainweb.BlockHeader import Chainweb.BlockHeight +import Chainweb.ForkState (pact4ForkNumber) import Chainweb.Logger import qualified Chainweb.ChainId as Chainweb import Chainweb.Mempool.Mempool (pact4RequestKeyToTransactionHash) @@ -371,7 +372,7 @@ applyCmd v logger gasLogger txFailuresCounter pdbenv miner gasModel txCtx txIdxI chainweb217Pact' = guardCtx chainweb217Pact txCtx chainweb219Pact' = guardCtx chainweb219Pact txCtx chainweb223Pact' = guardCtx chainweb223Pact txCtx - allVerifiers = verifiersAt v cid currHeight + allVerifiers = verifiersAt v cid pact4ForkNumber currHeight toEmptyPactError (PactError errty _ _ _) = PactError errty noInfo [] mempty toOldListErr pe = pe { peDoc = listErrMsg } @@ -671,7 +672,8 @@ applyLocal logger gasLogger dbEnv gasModel txCtx spv cmdIn mc execConfig = currHeight = ctxCurrentBlockHeight txCtx cid = V._chainId txCtx v = _chainwebVersion txCtx - allVerifiers = verifiersAt v cid currHeight + + allVerifiers = verifiersAt v cid pact4ForkNumber currHeight -- Note [Throw out verifier proofs eagerly] !verifiersWithNoProof = (fmap . fmap) (\_ -> ()) verifiers diff --git a/src/Chainweb/Pact5/TransactionExec.hs b/src/Chainweb/Pact5/TransactionExec.hs index a6e7fc8f0d..3be1e01d5b 100644 --- a/src/Chainweb/Pact5/TransactionExec.hs +++ b/src/Chainweb/Pact5/TransactionExec.hs @@ -183,7 +183,7 @@ runVerifiers txCtx cmd = do gasUsed <- liftIO . readIORef . _geGasRef . _txEnvGasEnv =<< ask let initGasRemaining = MilliGas $ case (gasToMilliGas (gasLimit ^. _GasLimit), gasUsed) of (MilliGas gasLimitMilliGasWord, MilliGas gasUsedMilliGasWord) -> gasLimitMilliGasWord - gasUsedMilliGasWord - let allVerifiers = verifiersAt v (_chainId txCtx) (ctxCurrentBlockHeight txCtx) + let allVerifiers = verifiersAt v (_chainId txCtx) (ctxParentForkNumber txCtx) (ctxCurrentBlockHeight txCtx) let toModuleName m = Pact4.ModuleName { Pact4._mnName = _mnName m diff --git a/src/Chainweb/Pact5/Types.hs b/src/Chainweb/Pact5/Types.hs index 785be552e8..fa02097fa0 100644 --- a/src/Chainweb/Pact5/Types.hs +++ b/src/Chainweb/Pact5/Types.hs @@ -11,6 +11,7 @@ module Chainweb.Pact5.Types ( TxContext(..) , guardCtx , ctxCurrentBlockHeight + , ctxParentForkNumber , GasSupply(..) , PactBlockM(..) , PactBlockState(..) @@ -34,6 +35,7 @@ import Chainweb.BlockHeader import Chainweb.Miner.Pact (Miner) import Chainweb.BlockHeight import Chainweb.Version +import Chainweb.ForkState import Chainweb.Pact.Types import qualified Chainweb.ChainId import Control.Lens @@ -84,6 +86,9 @@ ctxBlockHeader = _parentHeader . _tcParentHeader ctxCurrentBlockHeight :: TxContext -> BlockHeight ctxCurrentBlockHeight = succ . view blockHeight . ctxBlockHeader +ctxParentForkNumber :: TxContext -> ForkNumber +ctxParentForkNumber = view blockForkNumber . ctxBlockHeader + ctxChainId :: TxContext -> Chainweb.ChainId.ChainId ctxChainId = _chainId . ctxBlockHeader diff --git a/src/Chainweb/Version/Guards.hs b/src/Chainweb/Version/Guards.hs index 8e12df44cc..3c1e631409 100644 --- a/src/Chainweb/Version/Guards.hs +++ b/src/Chainweb/Version/Guards.hs @@ -335,13 +335,18 @@ pact4ParserVersion v cid bh | chainweb213Pact v cid bh = Pact4.PactParserChainweb213 | otherwise = Pact4.PactParserGenesis -maxBlockGasLimit :: ChainwebVersion -> BlockHeight -> Maybe Natural -maxBlockGasLimit v bh = snd $ ruleZipperHere $ snd - $ ruleSeek (\h _ -> ForkAtBlockHeight bh >= h) (_versionMaxBlockGasLimit v) - -minimumBlockHeaderHistory :: ChainwebVersion -> BlockHeight -> Maybe Word64 -minimumBlockHeaderHistory v bh = snd $ ruleZipperHere $ snd - $ ruleSeek (\h _ -> ForkAtBlockHeight bh >= h) (_versionSpvProofRootValidWindow v) +maxBlockGasLimit :: ChainwebVersion -> ForkNumber -> BlockHeight -> Maybe Natural +maxBlockGasLimit v fn bh = snd $ ruleZipperHere $ snd + $ ruleSeek (\h _ -> searchKey >= h) (_versionMaxBlockGasLimit v) + where + searchKey = ForkAtBlockHeight bh `max` ForkAtForkNumber fn + + +minimumBlockHeaderHistory :: ChainwebVersion -> ForkNumber -> BlockHeight -> Maybe Word64 +minimumBlockHeaderHistory v fn bh = snd $ ruleZipperHere $ snd + $ ruleSeek (\h _ -> searchKey >= h) (_versionSpvProofRootValidWindow v) + where + searchKey = ForkAtBlockHeight bh `max` ForkAtForkNumber fn -- | Different versions of Chainweb allow different PPKSchemes. -- diff --git a/src/Chainweb/Version/Utils.hs b/src/Chainweb/Version/Utils.hs index a7a32f781c..797003476d 100644 --- a/src/Chainweb/Version/Utils.hs +++ b/src/Chainweb/Version/Utils.hs @@ -64,6 +64,7 @@ import Chainweb.BlockHeight import Chainweb.ChainId import Chainweb.Difficulty import Chainweb.Time +import Chainweb.ForkState import Chainweb.VerifierPlugin import qualified Chainweb.VerifierPlugin.Allow import qualified Chainweb.VerifierPlugin.Hyperlane.Announcement @@ -458,16 +459,17 @@ expectedCutHeightAfterSeconds v s = eh * int (chainCountAt v (round eh)) eh = expectedBlockHeightAfterSeconds v s -- | The verifier plugins enabled for a particular block. -verifiersAt :: ChainwebVersion -> ChainId -> BlockHeight -> Map VerifierName VerifierPlugin -verifiersAt v cid bh = +verifiersAt :: ChainwebVersion -> ChainId -> ForkNumber -> BlockHeight -> Map VerifierName VerifierPlugin +verifiersAt v cid fn bh = M.restrictKeys allVerifierPlugins activeVerifierNames where activeVerifierNames = snd $ ruleZipperHere $ snd - $ ruleSeek (\h _ -> ForkAtBlockHeight bh >= h) + $ ruleSeek (\h _ -> searchKey >= h) $ _versionVerifierPluginNames v ^?! atChain cid + searchKey = ForkAtBlockHeight bh `max` ForkAtForkNumber fn -- the mappings from names to verifier plugins is global. the list of verifier -- plugins active in any particular block validation context is the only thing diff --git a/test/unit/Chainweb/Test/Pact5/RemotePactTest.hs b/test/unit/Chainweb/Test/Pact5/RemotePactTest.hs index 2a85b549db..c6db34af54 100644 --- a/test/unit/Chainweb/Test/Pact5/RemotePactTest.hs +++ b/test/unit/Chainweb/Test/Pact5/RemotePactTest.hs @@ -397,7 +397,7 @@ spvExpirationTest v baseRdb prop = runResourceT $ do -- disabled" case, we definitely don't want to use maxBound. let expirationWindow = fromMaybe (error "missing minimumBlockHeaderHistory") - (minimumBlockHeaderHistory v minBound) + (minimumBlockHeaderHistory v minBound minBound) when (int expirationWindow < waitBlocks + diameter (chainGraphAt v maxBound)) $ assertFailure "test version has a minimumBlockHeaderHistory that is too short to test" replicateM_ waitBlocks $ advanceAllChains_ fx From 4e7fb56b48aa139184fe68f2b159c942f0339628 Mon Sep 17 00:00:00 2001 From: KadenaFriend <241389759+kdafriend@users.noreply.github.com> Date: Wed, 7 Jan 2026 10:43:03 +0000 Subject: [PATCH 5/8] Headers compaction height depends on the ForkNumber + Rename some variables --- src/Chainweb/Pact/Backend/Compaction.hs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/Chainweb/Pact/Backend/Compaction.hs b/src/Chainweb/Pact/Backend/Compaction.hs index 186b9b8536..83c1f5e43f 100644 --- a/src/Chainweb/Pact/Backend/Compaction.hs +++ b/src/Chainweb/Pact/Backend/Compaction.hs @@ -68,7 +68,7 @@ import "yet-another-logger" System.Logger hiding (Logger) import "yet-another-logger" System.Logger qualified as YAL import "yet-another-logger" System.Logger.Backend.ColorOption (useColor) import Chainweb.BlockHash -import Chainweb.BlockHeader (blockHeight, blockHash, blockPayloadHash) +import Chainweb.BlockHeader (blockHeight, blockForkNumber, blockHash, blockPayloadHash) import Chainweb.BlockHeaderDB.Internal (BlockHeaderDb(..), RankedBlockHeader(..)) import Chainweb.BlockHeight (BlockHeight(..)) import Chainweb.Cut.CutHashes (cutIdToText) @@ -762,7 +762,7 @@ doCompactRocksDb logger cwVersion cids minBlockHeight srcDb targetDb = do iterLast it iterValue it >>= \case Nothing -> exitLog logger "Missing final payload. This is likely due to a corrupted database." - Just rbh -> pure (_getRankedBlockHeader rbh ^. blockHeight) + Just rbh -> pure $ _getRankedBlockHeader rbh -- The header that we start at depends on whether or not -- we have a minimal block header history window. @@ -772,7 +772,10 @@ doCompactRocksDb logger cwVersion cids minBlockHeight srcDb targetDb = do -- -- On new enough chainweb versions, we want to only copy over -- the minimal number of block headers. - case minimumBlockHeaderHistory cwVersion latestHeader of + -- Note, this behaviour may be dangerous in case of changes on the miniumum block history. + -- + -- TODO = Option to prune headers history to the minimum should be enabled by flag. + case minimumBlockHeaderHistory cwVersion (latestHeader ^. blockForkNumber) (latestHeader ^. blockHeight) of -- Go to the earliest possible entry. We migrate all BlockHeaders, for now. -- They are needed for SPV. -- @@ -791,16 +794,18 @@ doCompactRocksDb logger cwVersion cids minBlockHeight srcDb targetDb = do earliestHeader <- do iterValue it >>= \case Nothing -> exitLog logger "Missing first payload. This is likely due to a corrupted database." - Just rbh -> pure (_getRankedBlockHeader rbh ^. blockHeight) + Just rbh -> pure $ _getRankedBlockHeader rbh -- Ensure that we log progress 100 times per chain -- I just made this number up as something that felt somewhat sensible - let offset = (latestHeader - earliestHeader) `div` 100 - let headerProgressPoints = [earliestHeader + i * offset | i <- [1..100]] + let latestHeight = latestHeader ^. blockHeight + earliestHeight = earliestHeader ^. blockHeight + offset = (latestHeight - earliestHeight) `div` 100 + let headerProgressPoints = [earliestHeight + i * offset | i <- [1..100]] let logHeaderProgress bHeight = do when (bHeight `elem` headerProgressPoints) $ do - let percentDone = sshow $ 100 * fromIntegral @_ @Double (bHeight - earliestHeader) / fromIntegral @_ @Double (latestHeader - earliestHeader) + let percentDone = sshow $ 100 * fromIntegral @_ @Double (bHeight - earliestHeight) / fromIntegral @_ @Double (latestHeight - earliestHeight) log' LL.Info $ percentDone <> "% done." let go = do From e0f4b5f4c66bf863eb53945fcdeb8f2955e94a70 Mon Sep 17 00:00:00 2001 From: KadenaFriend <241389759+kdafriend@users.noreply.github.com> Date: Wed, 7 Jan 2026 17:49:57 +0000 Subject: [PATCH 6/8] Validate that no fork or rules application happens by height after Chainweb31 --- src/Chainweb/Version/RecapDevelopment.hs | 4 ++-- src/Chainweb/Version/Registry.hs | 26 +++++++++++++++++++++++- test/lib/Chainweb/Test/TestVersions.hs | 5 +++-- 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/src/Chainweb/Version/RecapDevelopment.hs b/src/Chainweb/Version/RecapDevelopment.hs index 7936994326..a21a423cbb 100644 --- a/src/Chainweb/Version/RecapDevelopment.hs +++ b/src/Chainweb/Version/RecapDevelopment.hs @@ -78,8 +78,8 @@ recapDevnet = ChainwebVersion Chainweb228Pact -> AllChains $ ForkAtBlockHeight $ BlockHeight 650 Chainweb230Pact -> AllChains $ ForkAtBlockHeight $ BlockHeight 680 Chainweb231Pact -> AllChains $ ForkAtBlockHeight $ BlockHeight 690 - Chainweb31 -> AllChains $ ForkAtBlockHeight $ BlockHeight 700 - MigratePlatformShare -> AllChains $ ForkAtBlockHeight $ BlockHeight 710 + MigratePlatformShare -> AllChains $ ForkAtBlockHeight $ BlockHeight 700 + Chainweb31 -> AllChains $ ForkAtBlockHeight $ BlockHeight 710 , _versionUpgrades = foldr (chainZip HM.union) (AllChains mempty) [ indexByForkHeights recapDevnet diff --git a/src/Chainweb/Version/Registry.hs b/src/Chainweb/Version/Registry.hs index f0653cdf37..aa7d3bfdfc 100644 --- a/src/Chainweb/Version/Registry.hs +++ b/src/Chainweb/Version/Registry.hs @@ -78,6 +78,24 @@ unregisterVersion v = do then error "You cannot unregister mainnet or testnet04 versions" else atomicModifyIORef' versionMap $ \m -> (HM.delete (_versionCode v) m, ()) +validateNoHeightAfterChainweb31' :: ChainwebVersion -> ForkHeight -> Either String () +validateNoHeightAfterChainweb31' v fh = + case (fork31Height, fh) of + (Just (ForkAtBlockHeight refAth), ForkAtBlockHeight ath) -> + if ath > (refAth + 1) + then Left ("validateVersion: Forking rule must only defined by Fork numbers after Chainweb31: " ++ show ath ++ " > " ++ show refAth) + else Right () + _ -> Right () + where + fork31Height = v ^? versionForks . at Chainweb31 . _Just . atChain (unsafeChainId 0) + +validateNoHeightAfterChainweb31 :: ChainwebVersion -> Either String () +validateNoHeightAfterChainweb31 v = + (mapM_ (mapM_ (validateNoHeightAfterChainweb31' v)) (v ^. versionForks)) + >> (mapM_ (validateNoHeightAfterChainweb31' v . fst) $ ruleElems $ v ^. versionMaxBlockGasLimit) + >> (mapM_ (validateNoHeightAfterChainweb31' v . fst) $ ruleElems $ v ^. versionSpvProofRootValidWindow) + >> (mapM_ (mapM_ (validateNoHeightAfterChainweb31' v . fst) . ruleElems) $ v ^. versionVerifierPluginNames) + validateVersion :: HasCallStack => ChainwebVersion -> IO () validateVersion v = do evaluate (rnf v) @@ -92,8 +110,12 @@ validateVersion v = do | not (all hasAllChains (_versionForks v)) ] , [ "validateVersion: chain graphs do not decrease in block height" | not (ruleValid (_versionGraphs v)) ] - , [ "validateVersion: block gas limits do not decrease in block height" + , [ "validateVersion: block gas limits rules do not decrease in fork number and height" | not (ruleValid (_versionMaxBlockGasLimit v)) ] + , [ "validateVersion: verifiers rules do not decrease in in fork number and height" + | not (all ruleValid (_versionVerifierPluginNames v)) ] + , [ "validateVersion: SPV valid window rules do not decrease in in fork number and height" + | not ( ruleValid (_versionSpvProofRootValidWindow v)) ] , [ "validateVersion: genesis data is missing for some chains" | not (and [ hasAllChains (_genesisBlockPayload $ _versionGenesis v) @@ -102,6 +124,8 @@ validateVersion v = do ])] , [ "validateVersion: some pact upgrade has no transactions" | any (any isUpgradeEmpty) (_versionUpgrades v) ] + , [ err + | Left err <- [validateNoHeightAfterChainweb31 v] ] -- TODO: check that pact 4/5 upgrades are only enabled when pact 4/5 is enabled ] unless (null errors) $ diff --git a/test/lib/Chainweb/Test/TestVersions.hs b/test/lib/Chainweb/Test/TestVersions.hs index f5afc26501..8f24d89ac9 100644 --- a/test/lib/Chainweb/Test/TestVersions.hs +++ b/test/lib/Chainweb/Test/TestVersions.hs @@ -330,8 +330,9 @@ slowForks = tabulateHashMap \case Chainweb228Pact -> AllChains $ ForkAtBlockHeight (BlockHeight 145) Chainweb230Pact -> AllChains $ ForkAtBlockHeight (BlockHeight 155) Chainweb231Pact -> AllChains $ ForkAtBlockHeight (BlockHeight 160) - Chainweb31 -> AllChains $ ForkAtBlockHeight (BlockHeight 165) - MigratePlatformShare -> AllChains $ ForkAtBlockHeight (BlockHeight 170) + MigratePlatformShare -> AllChains $ ForkAtBlockHeight (BlockHeight 165) + Chainweb31 -> AllChains $ ForkAtBlockHeight (BlockHeight 170) + -- | A set of fork heights which are relatively fast, but not fast enough to break anything. fastForks :: HashMap Fork (ChainMap ForkHeight) From e64444cd483cd129d121b16bb5560eea5a4689a1 Mon Sep 17 00:00:00 2001 From: kdafriend <241389759+kdafriend@users.noreply.github.com> Date: Thu, 8 Jan 2026 09:35:33 +0100 Subject: [PATCH 7/8] Apply suggestions from code review Co-authored-by: Edmund Noble --- src/Chainweb/Chainweb/Configuration.hs | 2 +- src/Chainweb/ForkState.hs | 2 +- src/Chainweb/Pact/Backend/Compaction.hs | 2 +- src/Chainweb/Pact5/Types.hs | 5 +++++ 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/Chainweb/Chainweb/Configuration.hs b/src/Chainweb/Chainweb/Configuration.hs index 2a65c6cc0e..fcbca2c303 100644 --- a/src/Chainweb/Chainweb/Configuration.hs +++ b/src/Chainweb/Chainweb/Configuration.hs @@ -610,7 +610,7 @@ parseVersion = constructVersion ForkNever -> error "Chainweb.Chainweb.Configuration.parseVersion: the fork upper bound never occurs in this version." ForkAtBlockHeight fubHeight -> HM.filterWithKey (\bh _ -> bh <= fubHeight) currentUpgrades ForkAtGenesis -> winningVersion ^?! versionUpgrades . atChain cid - ForkAtForkNumber _ -> currentUpgrades -- For now, version upgrades were only allowed at blok heights + ForkAtForkNumber _ -> currentUpgrades -- For now, version upgrades were only allowed at block heights ) (HS.toMap (chainIds winningVersion)) ) fub diff --git a/src/Chainweb/ForkState.hs b/src/Chainweb/ForkState.hs index 97d2dcf34f..7b7b61a8b3 100644 --- a/src/Chainweb/ForkState.hs +++ b/src/Chainweb/ForkState.hs @@ -135,7 +135,7 @@ forkNumber = lens _forkNumber $ \(ForkState w) v -> ForkState -- Pact4 -> Pact5 transition happened during ForkNumber=0 era. -pact4ForkNumber:: ForkNumber +pact4ForkNumber :: ForkNumber pact4ForkNumber = 0 -- --------------------------------------------------------------------------- diff --git a/src/Chainweb/Pact/Backend/Compaction.hs b/src/Chainweb/Pact/Backend/Compaction.hs index 83c1f5e43f..b0c1a9c116 100644 --- a/src/Chainweb/Pact/Backend/Compaction.hs +++ b/src/Chainweb/Pact/Backend/Compaction.hs @@ -772,7 +772,7 @@ doCompactRocksDb logger cwVersion cids minBlockHeight srcDb targetDb = do -- -- On new enough chainweb versions, we want to only copy over -- the minimal number of block headers. - -- Note, this behaviour may be dangerous in case of changes on the miniumum block history. + -- Note, this behaviour may be dangerous in case of changes on the minimum block history. -- -- TODO = Option to prune headers history to the minimum should be enabled by flag. case minimumBlockHeaderHistory cwVersion (latestHeader ^. blockForkNumber) (latestHeader ^. blockHeight) of diff --git a/src/Chainweb/Pact5/Types.hs b/src/Chainweb/Pact5/Types.hs index fa02097fa0..12e9aed687 100644 --- a/src/Chainweb/Pact5/Types.hs +++ b/src/Chainweb/Pact5/Types.hs @@ -86,6 +86,11 @@ ctxBlockHeader = _parentHeader . _tcParentHeader ctxCurrentBlockHeight :: TxContext -> BlockHeight ctxCurrentBlockHeight = succ . view blockHeight . ctxBlockHeader +-- We use the parent fork number in Pact, so when the fork +-- number is incremented, only that block's descendents will +-- have the forking behavior active. We do this because computing +-- the "currently active fork number" requires information from adjacent +-- headers, which is not actually available yet when we execute a new Pact payload. ctxParentForkNumber :: TxContext -> ForkNumber ctxParentForkNumber = view blockForkNumber . ctxBlockHeader From 52c1a46c2cceb0d000e0c1631924a059b17201f8 Mon Sep 17 00:00:00 2001 From: KadenaFriend <241389759+kdafriend@users.noreply.github.com> Date: Mon, 12 Jan 2026 19:55:16 +0000 Subject: [PATCH 8/8] Check that no fork use fork number of 0 --- src/Chainweb/Version/Registry.hs | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/src/Chainweb/Version/Registry.hs b/src/Chainweb/Version/Registry.hs index aa7d3bfdfc..5f5e1a5cb3 100644 --- a/src/Chainweb/Version/Registry.hs +++ b/src/Chainweb/Version/Registry.hs @@ -78,8 +78,8 @@ unregisterVersion v = do then error "You cannot unregister mainnet or testnet04 versions" else atomicModifyIORef' versionMap $ \m -> (HM.delete (_versionCode v) m, ()) -validateNoHeightAfterChainweb31' :: ChainwebVersion -> ForkHeight -> Either String () -validateNoHeightAfterChainweb31' v fh = +validateNoHeightAfterChainweb31 :: ChainwebVersion -> ForkHeight -> Either String () +validateNoHeightAfterChainweb31 v fh = case (fork31Height, fh) of (Just (ForkAtBlockHeight refAth), ForkAtBlockHeight ath) -> if ath > (refAth + 1) @@ -89,12 +89,21 @@ validateNoHeightAfterChainweb31' v fh = where fork31Height = v ^? versionForks . at Chainweb31 . _Just . atChain (unsafeChainId 0) -validateNoHeightAfterChainweb31 :: ChainwebVersion -> Either String () -validateNoHeightAfterChainweb31 v = - (mapM_ (mapM_ (validateNoHeightAfterChainweb31' v)) (v ^. versionForks)) - >> (mapM_ (validateNoHeightAfterChainweb31' v . fst) $ ruleElems $ v ^. versionMaxBlockGasLimit) - >> (mapM_ (validateNoHeightAfterChainweb31' v . fst) $ ruleElems $ v ^. versionSpvProofRootValidWindow) - >> (mapM_ (mapM_ (validateNoHeightAfterChainweb31' v . fst) . ruleElems) $ v ^. versionVerifierPluginNames) +validateNoForkAtZero :: ForkHeight -> Either String () +validateNoForkAtZero (ForkAtForkNumber atn) + | atn == 0 = Left ("ValidateSting: Fork Numbers can't be 0 ") + | otherwise = Right () +validateNoForkAtZero _ = Right () + + +validateForkHeights :: ChainwebVersion -> Either String () +validateForkHeights v = + (mapM_ (mapM_ doValidation) (v ^. versionForks)) + >> (mapM_ (doValidation . fst) $ ruleElems $ v ^. versionMaxBlockGasLimit) + >> (mapM_ (doValidation . fst) $ ruleElems $ v ^. versionSpvProofRootValidWindow) + >> (mapM_ (mapM_ (doValidation . fst) . ruleElems) $ v ^. versionVerifierPluginNames) + where + doValidation fh = validateNoHeightAfterChainweb31 v fh >> validateNoForkAtZero fh validateVersion :: HasCallStack => ChainwebVersion -> IO () validateVersion v = do @@ -125,7 +134,7 @@ validateVersion v = do , [ "validateVersion: some pact upgrade has no transactions" | any (any isUpgradeEmpty) (_versionUpgrades v) ] , [ err - | Left err <- [validateNoHeightAfterChainweb31 v] ] + | Left err <- [validateForkHeights v] ] -- TODO: check that pact 4/5 upgrades are only enabled when pact 4/5 is enabled ] unless (null errors) $