From 22cc25f49ee7243b153c7466f8e9b26ce0609307 Mon Sep 17 00:00:00 2001 From: Agustin Mista Date: Fri, 5 Dec 2025 13:18:52 +0100 Subject: [PATCH 1/3] Add optional nonce to BHeaderView This commit add an optional Nonce to BHeaderView to carry the previous epoch nonce needed to validate Peras certificates embedded in blocks. Additionally, it tweaks 'makeHeaderView' to be able to pass this nonce from the consensus side. --- .../testlib/Test/Cardano/Ledger/Conway/Imp/BbodySpec.hs | 1 + eras/shelley/test-suite/bench/BenchValidation.hs | 6 +++--- .../src/Test/Cardano/Ledger/Shelley/Generator/Block.hs | 2 +- .../src/Test/Cardano/Ledger/Shelley/Rules/Chain.hs | 2 +- libs/cardano-ledger-core/src/Cardano/Ledger/BHeaderView.hs | 5 ++++- .../src/Test/Cardano/Ledger/Examples/AlonzoBBODY.hs | 1 + .../src/Cardano/Protocol/TPraos/BHeader.hs | 5 +++-- 7 files changed, 14 insertions(+), 8 deletions(-) diff --git a/eras/conway/impl/testlib/Test/Cardano/Ledger/Conway/Imp/BbodySpec.hs b/eras/conway/impl/testlib/Test/Cardano/Ledger/Conway/Imp/BbodySpec.hs index e33825bc741..005aefa8c18 100644 --- a/eras/conway/impl/testlib/Test/Cardano/Ledger/Conway/Imp/BbodySpec.hs +++ b/eras/conway/impl/testlib/Test/Cardano/Ledger/Conway/Imp/BbodySpec.hs @@ -281,6 +281,7 @@ spec = do , bhviewHSize = 0 , bhviewBHash = hashBlockBody blockBody , bhviewSlot = slotNo + , bhviewPrevEpochNonce = Nothing } tryRunImpRule @"BBODY" (BbodyEnv pp (nes ^. chainAccountStateL)) diff --git a/eras/shelley/test-suite/bench/BenchValidation.hs b/eras/shelley/test-suite/bench/BenchValidation.hs index a1b52a57213..bf935a8bde9 100644 --- a/eras/shelley/test-suite/bench/BenchValidation.hs +++ b/eras/shelley/test-suite/bench/BenchValidation.hs @@ -93,7 +93,7 @@ benchValidate :: ValidateInput era -> IO (NewEpochState era) benchValidate (ValidateInput globals state (Block bh txs)) = - let block = Block (makeHeaderView bh) txs + let block = Block (makeHeaderView bh Nothing) txs in case API.applyBlockEitherNoEvents ValidateAll globals state block of Right x -> pure x Left x -> error (show x) @@ -111,7 +111,7 @@ applyBlock :: Int -> Int applyBlock (ValidateInput globals state (Block bh txs)) n = - let block = Block (makeHeaderView bh) txs + let block = Block (makeHeaderView bh Nothing) txs in case API.applyBlockEitherNoEvents ValidateAll globals state block of Right x -> seq (rnf x) (n + 1) Left x -> error (show x) @@ -121,7 +121,7 @@ benchreValidate :: ValidateInput era -> NewEpochState era benchreValidate (ValidateInput globals state (Block bh txs)) = - API.applyBlockNoValidaton globals state (Block (makeHeaderView bh) txs) + API.applyBlockNoValidaton globals state (Block (makeHeaderView bh Nothing) txs) -- ============================================================== diff --git a/eras/shelley/test-suite/src/Test/Cardano/Ledger/Shelley/Generator/Block.hs b/eras/shelley/test-suite/src/Test/Cardano/Ledger/Shelley/Generator/Block.hs index cce2b8d7b89..7e341349c11 100644 --- a/eras/shelley/test-suite/src/Test/Cardano/Ledger/Shelley/Generator/Block.hs +++ b/eras/shelley/test-suite/src/Test/Cardano/Ledger/Shelley/Generator/Block.hs @@ -173,7 +173,7 @@ genBlockWithTxGen -- e.g. the KES period in which this key starts to be valid. <*> pure (fromIntegral (m * fromIntegral maxKESIterations)) <*> pure oCert - let hView = makeHeaderView (blockHeader theBlock) + let hView = makeHeaderView (blockHeader theBlock) Nothing unless (bhviewBSize hView <= pp ^. ppMaxBBSizeL) $ tracedDiscard $ "genBlockWithTxGen: bhviewBSize too large" diff --git a/eras/shelley/test-suite/src/Test/Cardano/Ledger/Shelley/Rules/Chain.hs b/eras/shelley/test-suite/src/Test/Cardano/Ledger/Shelley/Rules/Chain.hs index 02c5c2e925c..7f151ac0ba6 100644 --- a/eras/shelley/test-suite/src/Test/Cardano/Ledger/Shelley/Rules/Chain.hs +++ b/eras/shelley/test-suite/src/Test/Cardano/Ledger/Shelley/Rules/Chain.hs @@ -326,7 +326,7 @@ chainTransition = let pp = nes ^. nesEpochStateL . curPParamsEpochStateL chainChecksData = pparamsToChainChecksPParams pp - bhView = makeHeaderView bh + bhView = makeHeaderView bh Nothing -- We allow one protocol version higher than the current era's maximum, because -- that is the way we can get out of the current era into the next one. We test diff --git a/libs/cardano-ledger-core/src/Cardano/Ledger/BHeaderView.hs b/libs/cardano-ledger-core/src/Cardano/Ledger/BHeaderView.hs index bf7c0038d70..625e29c47f6 100644 --- a/libs/cardano-ledger-core/src/Cardano/Ledger/BHeaderView.hs +++ b/libs/cardano-ledger-core/src/Cardano/Ledger/BHeaderView.hs @@ -3,7 +3,7 @@ module Cardano.Ledger.BHeaderView where -import Cardano.Ledger.BaseTypes (BoundedRational (..), UnitInterval) +import Cardano.Ledger.BaseTypes (BoundedRational (..), Nonce, UnitInterval) import Cardano.Ledger.Hashes (EraIndependentBlockBody, HASH, Hash, KeyHash, KeyRole (..)) import Cardano.Ledger.Slot (SlotNo (..), (-*)) import Data.Word (Word32) @@ -29,6 +29,9 @@ data BHeaderView = BHeaderView -- ^ The purported hash of the block body. , bhviewSlot :: SlotNo -- ^ The slot for which this block was submitted to the chain. + , bhviewPrevEpochNonce :: Maybe Nonce + -- ^ The previous epoch nonce, needed to validate Peras certificates + -- contained in blocks. } -- | Determine if the given slot is reserved for the overlay schedule. diff --git a/libs/cardano-ledger-test/src/Test/Cardano/Ledger/Examples/AlonzoBBODY.hs b/libs/cardano-ledger-test/src/Test/Cardano/Ledger/Examples/AlonzoBBODY.hs index 2e0918ade1f..44eed1f544f 100644 --- a/libs/cardano-ledger-test/src/Test/Cardano/Ledger/Examples/AlonzoBBODY.hs +++ b/libs/cardano-ledger-test/src/Test/Cardano/Ledger/Examples/AlonzoBBODY.hs @@ -598,6 +598,7 @@ makeNaiveBlock txs = Block {blockHeader = bhView, blockBody} , bhviewHSize = 0 , bhviewBHash = hashBlockBody blockBody , bhviewSlot = SlotNo 0 + , bhviewPrevEpochNonce = Nothing } blockBody = mkBasicBlockBody & txSeqBlockBodyL .~ StrictSeq.fromList txs diff --git a/libs/cardano-protocol-tpraos/src/Cardano/Protocol/TPraos/BHeader.hs b/libs/cardano-protocol-tpraos/src/Cardano/Protocol/TPraos/BHeader.hs index 4ab0d286f25..1855bf7c9ae 100644 --- a/libs/cardano-protocol-tpraos/src/Cardano/Protocol/TPraos/BHeader.hs +++ b/libs/cardano-protocol-tpraos/src/Cardano/Protocol/TPraos/BHeader.hs @@ -454,11 +454,12 @@ lastAppliedHash (At lab) = BlockHash $ labHash lab bnonce :: BHBody c -> Nonce bnonce = mkNonceFromOutputVRF . VRF.certifiedOutput . bheaderEta -makeHeaderView :: Crypto c => BHeader c -> BHeaderView -makeHeaderView bh@(BHeader bhb _) = +makeHeaderView :: Crypto c => BHeader c -> Maybe Nonce -> BHeaderView +makeHeaderView bh@(BHeader bhb _) nonce = BHeaderView (hashKey . bheaderVk $ bhb) (bsize bhb) (originalBytesSize bh) (bhash bhb) (bheaderSlotNo bhb) + nonce From c3eeda0ca13542406f1e81db6035ed4a3fcd72d9 Mon Sep 17 00:00:00 2001 From: Agustin Mista Date: Fri, 5 Dec 2025 15:48:10 +0100 Subject: [PATCH 2/3] Require previous epoch nonce in Dijkstra BBODY transition rule This commit adds a check in the Dijkstra BBODY transition rule to enforce having a non-null previous epoch nonce so that Peras certificates can be validated should they appear in a block body. --- .../Cardano/Ledger/Dijkstra/Rules/Bbody.hs | 34 ++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/eras/dijkstra/impl/src/Cardano/Ledger/Dijkstra/Rules/Bbody.hs b/eras/dijkstra/impl/src/Cardano/Ledger/Dijkstra/Rules/Bbody.hs index 127f51998b3..bd967bf950c 100644 --- a/eras/dijkstra/impl/src/Cardano/Ledger/Dijkstra/Rules/Bbody.hs +++ b/eras/dijkstra/impl/src/Cardano/Ledger/Dijkstra/Rules/Bbody.hs @@ -82,7 +82,11 @@ import qualified Cardano.Ledger.Shelley.Rules as Shelley import Control.State.Transition ( Embed (..), STS (..), + TransitionRule, + judgmentContext, ) +import Control.State.Transition.Extended (TRC (..), (?!)) +import Data.Maybe (isNothing) import Data.Sequence (Seq) import GHC.Generics (Generic) import NoThunks.Class (NoThunks (..)) @@ -94,6 +98,7 @@ data DijkstraBbodyPredFailure era LedgersFailure (PredicateFailure (EraRule "LEDGERS" era)) | TooManyExUnits (Mismatch RelLTEQ ExUnits) | BodyRefScriptsSizeTooBig (Mismatch RelLTEQ Int) + | PrevEpochNonceNotPresent deriving (Generic) deriving instance @@ -121,6 +126,7 @@ instance LedgersFailure x -> Sum (LedgersFailure @era) 2 !> To x TooManyExUnits mm -> Sum TooManyExUnits 3 !> To mm BodyRefScriptsSizeTooBig mm -> Sum BodyRefScriptsSizeTooBig 4 !> To mm + PrevEpochNonceNotPresent -> Sum PrevEpochNonceNotPresent 5 instance ( Era era @@ -134,6 +140,7 @@ instance 2 -> SumD LedgersFailure SumD TooManyExUnits SumD BodyRefScriptsSizeTooBig SumD PrevEpochNonceNotPresent n -> Invalid n type instance EraRuleFailure "BBODY" DijkstraEra = DijkstraBbodyPredFailure DijkstraEra @@ -297,6 +304,7 @@ instance , AlonzoEraPParams era , InjectRuleFailure "BBODY" AlonzoBbodyPredFailure era , InjectRuleFailure "BBODY" ConwayBbodyPredFailure era + , InjectRuleFailure "BBODY" DijkstraBbodyPredFailure era , EraRule "BBODY" era ~ DijkstraBBODY era , AlonzoEraTx era , BabbageEraTxBody era @@ -317,7 +325,31 @@ instance type Event (DijkstraBBODY era) = AlonzoBbodyEvent era initialRules = [] - transitionRules = [Conway.conwayBbodyTransition @era >> alonzoBbodyTransition @era] + transitionRules = + [ dijkstraBbodyTransition @era + >> Conway.conwayBbodyTransition @era + >> alonzoBbodyTransition @era + ] + +dijkstraBbodyTransition :: + forall era. + ( Signal (EraRule "BBODY" era) ~ Block BHeaderView era + , State (EraRule "BBODY" era) ~ ShelleyBbodyState era + , InjectRuleFailure "BBODY" DijkstraBbodyPredFailure era + ) => + TransitionRule (EraRule "BBODY" era) +dijkstraBbodyTransition = do + judgmentContext + >>= \( TRC + ( _ + , state + , Block bh _ + ) + ) -> do + -- Check that the previous epoch nonce is present + isNothing (bhviewPrevEpochNonce bh) + ?! injectFailure PrevEpochNonceNotPresent + pure state conwayToDijkstraBbodyPredFailure :: forall era. ConwayBbodyPredFailure era -> DijkstraBbodyPredFailure era From c26e4ea1109e059bc34a13a435f7de58556258f2 Mon Sep 17 00:00:00 2001 From: Agustin Mista Date: Fri, 5 Dec 2025 16:29:11 +0100 Subject: [PATCH 3/3] Update Dijkstra CHANGELOG to reflect changes in BHeaderView --- eras/dijkstra/impl/CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/eras/dijkstra/impl/CHANGELOG.md b/eras/dijkstra/impl/CHANGELOG.md index 5425026f85d..0771b56ee62 100644 --- a/eras/dijkstra/impl/CHANGELOG.md +++ b/eras/dijkstra/impl/CHANGELOG.md @@ -2,6 +2,9 @@ ## 0.2.0.0 +* Add `bhviewPrevEpochNonce` to `BHeaderView` +* Change `makeHeaderView` to expect an additional `Maybe Nonce` +* Add `dijkstraBbodyTransition` to the BBODY rule * Add `DijkstraBlockBody` type and pattern * Add `mkBasicBlockBodyDijkstra` * Add `DijkstraEraBlockBody` class and instance for `DijkstraEraBlockBody`