From c0b91de78ec9f9462af175a781a289fae474a557 Mon Sep 17 00:00:00 2001 From: Aniket Deshpande Date: Wed, 13 May 2026 20:20:52 +0530 Subject: [PATCH 01/13] Add decodeSparseKeyed; use for BabbageTxOut PV12+. --- eras/babbage/impl/cardano-ledger-babbage.cabal | 4 ++-- .../impl/src/Cardano/Ledger/Babbage/TxOut.hs | 18 +++++++++--------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/eras/babbage/impl/cardano-ledger-babbage.cabal b/eras/babbage/impl/cardano-ledger-babbage.cabal index fa720e8b829..ae7f66a1614 100644 --- a/eras/babbage/impl/cardano-ledger-babbage.cabal +++ b/eras/babbage/impl/cardano-ledger-babbage.cabal @@ -92,7 +92,7 @@ library cardano-data >=1.2, cardano-ledger-allegra ^>=1.10, cardano-ledger-alonzo ^>=1.16, - cardano-ledger-binary >=1.6, + cardano-ledger-binary >=1.9, cardano-ledger-core:{cardano-ledger-core, internal} >=1.20, cardano-ledger-mary ^>=1.11, cardano-ledger-shelley ^>=1.19, @@ -146,7 +146,7 @@ executable generate-cddl build-depends: base, - cardano-ledger-binary:testlib >=1.5, + cardano-ledger-binary:testlib >=1.9, cddl, library testlib diff --git a/eras/babbage/impl/src/Cardano/Ledger/Babbage/TxOut.hs b/eras/babbage/impl/src/Cardano/Ledger/Babbage/TxOut.hs index f910b8f9f9d..4e41eb5fd5c 100644 --- a/eras/babbage/impl/src/Cardano/Ledger/Babbage/TxOut.hs +++ b/eras/babbage/impl/src/Cardano/Ledger/Babbage/TxOut.hs @@ -621,37 +621,37 @@ decodeTxOut decAddr = do initial :: DecodingTxOut era initial = DecodingTxOut SNothing mempty NoDatum SNothing decoderForKey :: DecodingTxOut era -> Word -> Maybe (Decoder s (DecodingTxOut era)) - decoderForKey txo = \case + decoderForKey txOut = \case 0 -> Just $ do !x <- decAddr - pure txo {decodingTxOutAddr = SJust x} + pure txOut {decodingTxOutAddr = SJust x} 1 -> Just $ do !x <- decCBOR - pure txo {decodingTxOutVal = x} + pure txOut {decodingTxOutVal = x} 2 -> Just $ do !x <- decCBOR - pure txo {decodingTxOutDatum = x} + pure txOut {decodingTxOutDatum = x} 3 -> Just $ do !x <- decodeCIC "Script" - pure txo {decodingTxOutScript = SJust x} + pure txOut {decodingTxOutScript = SJust x} _ -> Nothing {-# INLINE decoderForKey #-} bodyFields :: (Word -> Field (DecodingTxOut era)) bodyFields 0 = field - (\x txo -> txo {decodingTxOutAddr = SJust x}) + (\x txOut -> txOut {decodingTxOutAddr = SJust x}) (D decAddr) bodyFields 1 = field - (\x txo -> txo {decodingTxOutVal = x}) + (\x txOut -> txOut {decodingTxOutVal = x}) From bodyFields 2 = field - (\x txo -> txo {decodingTxOutDatum = x}) + (\x txOut -> txOut {decodingTxOutDatum = x}) (D decCBOR) bodyFields 3 = ofield - (\x txo -> txo {decodingTxOutScript = x}) + (\x txOut -> txOut {decodingTxOutScript = x}) (D $ decodeCIC "Script") bodyFields n = invalidField n {-# INLINE bodyFields #-} From 5ca36f379a0318a836d91cefea5fee54c52771db Mon Sep 17 00:00:00 2001 From: Aniket Deshpande Date: Mon, 18 May 2026 19:42:39 +0530 Subject: [PATCH 02/13] Use decodeSparseKeyed for AlonzoTxAuxData PV12+. --- .../src/Cardano/Ledger/Alonzo/TxAuxData.hs | 49 ++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/eras/alonzo/impl/src/Cardano/Ledger/Alonzo/TxAuxData.hs b/eras/alonzo/impl/src/Cardano/Ledger/Alonzo/TxAuxData.hs index f47cb58c241..4d1610b03dc 100644 --- a/eras/alonzo/impl/src/Cardano/Ledger/Alonzo/TxAuxData.hs +++ b/eras/alonzo/impl/src/Cardano/Ledger/Alonzo/TxAuxData.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE BangPatterns #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DerivingVia #-} @@ -67,6 +68,8 @@ import Cardano.Ledger.Binary ( EncCBOR (..), ToCBOR, TokenType (..), + assertTag, + decodeSparseKeyed, decodeStrictSeq, ifDecoderVersionAtLeast, natVersion, @@ -202,7 +205,18 @@ instance decodeTxAuxDataByTokenType @(Annotator (AlonzoTxAuxDataRaw era)) decodeShelley (ifDecoderVersionAtLeast (natVersion @12) decodeDijkstra decodeAllegra) - decodeAlonzo + ( ifDecoderVersionAtLeast + (natVersion @12) + ( do + assertTag 259 + decodeSparseKeyed + "AlonzoTxAuxData" + [] + (pure emptyAlonzoTxAuxDataRaw) + decoderForKey + ) + decodeAlonzo + ) where decodeShelley = decode @@ -229,6 +243,39 @@ instance TagD 259 $ SparseKeyed "AlonzoTxAuxData" (pure emptyAlonzoTxAuxDataRaw) auxDataField [] + decoderForKey :: + Annotator (AlonzoTxAuxDataRaw era) -> + Word -> + Maybe (Decoder s (Annotator (AlonzoTxAuxDataRaw era))) + decoderForKey acc = \case + 0 -> Just $ do + !x <- decCBOR + pure $ (\ad -> ad {atadrMetadata = x}) <$> acc + 1 -> Just $ do + !x <- sequence <$> decodeStrictSeq decCBOR + pure $ + (\scripts ad -> ad {atadrNativeScripts = atadrNativeScripts ad <> scripts}) + <$> x + <*> acc + 2 -> Just $ do + guardPlutus PlutusV1 + !x <- decCBOR + pure $ addPlutusScripts PlutusV1 x <$> acc + 3 -> Just $ do + guardPlutus PlutusV2 + !x <- decCBOR + pure $ addPlutusScripts PlutusV2 x <$> acc + 4 -> Just $ do + guardPlutus PlutusV3 + !x <- decCBOR + pure $ addPlutusScripts PlutusV3 x <$> acc + 5 -> Just $ do + guardPlutus PlutusV4 + !x <- decCBOR + pure $ addPlutusScripts PlutusV4 x <$> acc + _ -> Nothing + {-# INLINE decoderForKey #-} + auxDataField :: Word -> Field (Annotator (AlonzoTxAuxDataRaw era)) auxDataField 0 = fieldA (\x ad -> ad {atadrMetadata = x}) From auxDataField 1 = From 6438b666d59c941d28045327cc7e50eb1297c550 Mon Sep 17 00:00:00 2001 From: Aniket Deshpande Date: Tue, 19 May 2026 20:00:29 +0530 Subject: [PATCH 03/13] Use decodeSparseKeyed for PParamsUpdate PV12+ --- .../src/Cardano/Ledger/Core/PParams.hs | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/libs/cardano-ledger-core/src/Cardano/Ledger/Core/PParams.hs b/libs/cardano-ledger-core/src/Cardano/Ledger/Core/PParams.hs index 9a9a413e4c8..9017ff0ee26 100644 --- a/libs/cardano-ledger-core/src/Cardano/Ledger/Core/PParams.hs +++ b/libs/cardano-ledger-core/src/Cardano/Ledger/Core/PParams.hs @@ -115,9 +115,12 @@ import Cardano.Ledger.Binary ( FromCBOR (..), ToCBOR (..), decodeRecordNamed, + decodeSparseKeyed, encodeListLen, encodeMapLen, encodeWord, + ifDecoderVersionAtLeast, + natVersion, ) import Cardano.Ledger.Binary.Coders (Decode (..), Field (..), decode, field, invalidField) import Cardano.Ledger.Coin ( @@ -252,13 +255,20 @@ instance EraPParams era => EncCBOR (PParamsUpdate era) where instance EraPParams era => DecCBOR (PParamsUpdate era) where decCBOR = - decode $ - SparseKeyed - (show . typeRep $ Proxy @(PParamsUpdate era)) - emptyPParamsUpdate - updateField - [] + ifDecoderVersionAtLeast + (natVersion @12) + (decodeSparseKeyed name [] emptyPParamsUpdate decoderForKey) + (decode $ SparseKeyed name emptyPParamsUpdate updateField []) where + name = show . typeRep $ Proxy @(PParamsUpdate era) + decoderForKey :: + PParamsUpdate era -> + Word -> + Maybe (Decoder s (PParamsUpdate era)) + decoderForKey acc k = + case IntMap.lookup (fromIntegral k) updateFieldMap of + Just (Field setter dec) -> Just $ (`setter` acc) <$> dec + Nothing -> Nothing updateField k = IntMap.findWithDefault (invalidField k) From 50d2be4636b274cb2495e8ca2fd25301053c12cf Mon Sep 17 00:00:00 2001 From: Aniket Deshpande Date: Tue, 19 May 2026 20:11:05 +0530 Subject: [PATCH 04/13] Add mapSparseField helpers for decodeSparseKeyed --- libs/cardano-ledger-binary/CHANGELOG.md | 1 + .../Cardano/Ledger/Binary/Decoding/Decoder.hs | 74 ++++++++++++++++++- 2 files changed, 73 insertions(+), 2 deletions(-) diff --git a/libs/cardano-ledger-binary/CHANGELOG.md b/libs/cardano-ledger-binary/CHANGELOG.md index d67e21e9fe6..49e39839fe1 100644 --- a/libs/cardano-ledger-binary/CHANGELOG.md +++ b/libs/cardano-ledger-binary/CHANGELOG.md @@ -2,6 +2,7 @@ ## 1.9.0.0 +* Add `mapSparseField`, `mapSparseFieldA`, `mapSparseFieldOptional`, `mapSparseFieldGuarded` * Add `decodeSparseKeyed` * Add `decodeIntegralRational` * Add `decodeNonEmptySetLikeEnforceNoDuplicates` diff --git a/libs/cardano-ledger-binary/src/Cardano/Ledger/Binary/Decoding/Decoder.hs b/libs/cardano-ledger-binary/src/Cardano/Ledger/Binary/Decoding/Decoder.hs index e8ea8dcd506..625264ceb27 100644 --- a/libs/cardano-ledger-binary/src/Cardano/Ledger/Binary/Decoding/Decoder.hs +++ b/libs/cardano-ledger-binary/src/Cardano/Ledger/Binary/Decoding/Decoder.hs @@ -80,6 +80,10 @@ module Cardano.Ledger.Binary.Decoding.Decoder ( decodeListLikeEnforceNoDuplicates, decodeMapContents, decodeSparseKeyed, + mapSparseField, + mapSparseFieldA, + mapSparseFieldOptional, + mapSparseFieldGuarded, -- **** Applicaitve decodeMapTraverse, @@ -1194,7 +1198,7 @@ decodeMapContentsTraverse decodeKey decodeValue = decodeSparseKeyed :: forall a s. -- | Type name used in error messages. - Text.Text -> + String -> -- | Required keys with friendly names per key. After the map is fully -- consumed, the absence of any key in this list is reported as a -- failure using its name and key. @@ -1222,7 +1226,7 @@ decodeSparseKeyed name requiredFields initial decoderForKey = do pure acc where failMsg :: String -> Decoder s b - failMsg msg = fail $ Text.unpack name <> ":" <> msg + failMsg msg = fail $ name <> ":" <> msg defLoop :: Set.Set Word -> a -> Int -> Decoder s (Set.Set Word, a) defLoop !seen !acc !i @@ -1252,6 +1256,72 @@ decodeSparseKeyed name requiredFields initial decoderForKey = do {-# INLINE step #-} {-# INLINE decodeSparseKeyed #-} +-- | Decode a value and apply it to a 'Functor'-wrapped accumulator +-- (typically 'Annotator'). Intended for use inside the per-key handler +-- of 'decodeSparseKeyed'. +mapSparseField :: + Functor ann => + -- | Setter that updates the underlying record with the decoded value. + (x -> t -> t) -> + -- | Decoder for the field value. + Decoder s x -> + -- | Current accumulator. + ann t -> + Decoder s (ann t) +mapSparseField setter dec acc = do + !x <- dec + pure $ setter x <$> acc +{-# INLINE mapSparseField #-} + +-- | Like 'mapSparseField' but the decoder yields an +-- 'Applicative'-wrapped value (e.g. @'Annotator' x@). +mapSparseFieldA :: + Applicative ann => + (x -> t -> t) -> + Decoder s (ann x) -> + ann t -> + Decoder s (ann t) +mapSparseFieldA setter dec acc = do + !x <- dec + pure $ setter <$> x <*> acc +{-# INLINE mapSparseFieldA #-} + +-- | Like 'mapSparseField' but the setter expects a 'StrictMaybe' value. +-- Useful when the underlying field is a @'Lens'' t ('StrictMaybe' x)@ +-- and the key's absence implies 'SNothing' (already set by the initial +-- accumulator). +mapSparseFieldOptional :: + Functor ann => + (StrictMaybe x -> t -> t) -> + Decoder s x -> + ann t -> + Decoder s (ann t) +mapSparseFieldOptional setter dec acc = do + !x <- dec + pure $ setter (SJust x) <$> acc +{-# INLINE mapSparseFieldOptional #-} + +-- | Like 'mapSparseField' but reject the value when the predicate is +-- satisfied. +mapSparseFieldGuarded :: + Functor ann => + -- | Type name; failure is prefixed with @name:@. + String -> + -- | Error message used when the predicate rejects the value. + String -> + -- | Predicate: 'True' means reject. + (x -> Bool) -> + (x -> t -> t) -> + Decoder s x -> + ann t -> + Decoder s (ann t) +mapSparseFieldGuarded name msg reject setter dec acc = do + !x <- dec + if reject x + then fail $ name <> ":" <> msg + else pure $ setter x <$> acc +{-# INLINE mapSparseFieldGuarded #-} + -------------------------------------------------------------------------------- -- Time -------------------------------------------------------------------------------- From 11d144726db09dd979423a932c694ba532616502 Mon Sep 17 00:00:00 2001 From: Aniket Deshpande Date: Tue, 19 May 2026 20:15:02 +0530 Subject: [PATCH 05/13] Use decodeSparseKeyed for AlonzoTxWits PV12+ --- .../src/Cardano/Ledger/Alonzo/TxAuxData.hs | 8 ++- .../impl/src/Cardano/Ledger/Alonzo/TxWits.hs | 58 +++++++++++++++---- 2 files changed, 51 insertions(+), 15 deletions(-) diff --git a/eras/alonzo/impl/src/Cardano/Ledger/Alonzo/TxAuxData.hs b/eras/alonzo/impl/src/Cardano/Ledger/Alonzo/TxAuxData.hs index 4d1610b03dc..ddf85d41215 100644 --- a/eras/alonzo/impl/src/Cardano/Ledger/Alonzo/TxAuxData.hs +++ b/eras/alonzo/impl/src/Cardano/Ledger/Alonzo/TxAuxData.hs @@ -96,9 +96,10 @@ import qualified Data.List.NonEmpty as NE import Data.Map (Map) import qualified Data.Map.Strict as Map import Data.Maybe (isNothing, mapMaybe) +import Data.Proxy (Proxy (..)) import Data.Sequence.Strict (StrictSeq ((:<|))) import qualified Data.Sequence.Strict as StrictSeq -import Data.Typeable (Typeable) +import Data.Typeable (Typeable, typeRep) import Data.Word (Word64) import GHC.Generics (Generic) import GHC.Stack @@ -210,7 +211,7 @@ instance ( do assertTag 259 decodeSparseKeyed - "AlonzoTxAuxData" + name [] (pure emptyAlonzoTxAuxDataRaw) decoderForKey @@ -218,6 +219,7 @@ instance decodeAlonzo ) where + name = show . typeRep $ Proxy @(AlonzoTxAuxDataRaw era) decodeShelley = decode ( Ann (Emit AlonzoTxAuxDataRaw) @@ -241,7 +243,7 @@ instance decodeAlonzo = decode $ TagD 259 $ - SparseKeyed "AlonzoTxAuxData" (pure emptyAlonzoTxAuxDataRaw) auxDataField [] + SparseKeyed name (pure emptyAlonzoTxAuxDataRaw) auxDataField [] decoderForKey :: Annotator (AlonzoTxAuxDataRaw era) -> diff --git a/eras/alonzo/impl/src/Cardano/Ledger/Alonzo/TxWits.hs b/eras/alonzo/impl/src/Cardano/Ledger/Alonzo/TxWits.hs index 52a0e3ca9b8..d41b8f33f74 100644 --- a/eras/alonzo/impl/src/Cardano/Ledger/Alonzo/TxWits.hs +++ b/eras/alonzo/impl/src/Cardano/Ledger/Alonzo/TxWits.hs @@ -83,12 +83,15 @@ import Cardano.Ledger.Binary ( decodeNonEmptyList, decodeNonEmptySetLikeEnforceNoDuplicates, decodeNonEmptySetLikeEnforceNoDuplicatesAnn, + decodeSparseKeyed, encodeFoldableEncoder, encodeListLen, encodeTag, ifDecoderVersionAtLeast, ifEncodingVersionAtLeast, listLenInt, + mapSparseField, + mapSparseFieldA, natVersion, peekTokenType, setTag, @@ -129,8 +132,10 @@ import qualified Data.Map.Strict as Map import Data.MapExtras (fromElems) import qualified Data.MapExtras as Map (fromElems) import Data.Maybe (mapMaybe) +import Data.Proxy (Proxy (..)) import Data.Set (Set) import qualified Data.Set as Set +import Data.Typeable (typeRep) import GHC.Generics (Generic) import Lens.Micro import NoThunks.Class (NoThunks) @@ -601,13 +606,12 @@ instance DecCBOR (Annotator (AlonzoTxWitsRaw era)) where decCBOR = - decode $ - SparseKeyed - "AlonzoTxWits" - (pure emptyTxWitsRaw) - txWitnessField - [] + ifDecoderVersionAtLeast + (natVersion @12) + (decodeSparseKeyed name [] (pure emptyTxWitsRaw) decoderForKey) + (decode $ SparseKeyed name (pure emptyTxWitsRaw) txWitnessField []) where + name = show . typeRep $ Proxy @(AlonzoTxWitsRaw era) addrWitsSetDecoder :: (Ord a, DecCBOR a) => Decoder s (Set a) addrWitsSetDecoder = do let @@ -621,6 +625,30 @@ instance ifDecoderVersionAtLeast (natVersion @12) nonEmptyNoDuplicatesDecoder nonEmptyDecoder {-# INLINE addrWitsSetDecoder #-} + setOrListDecoder :: (Ord a, DecCBOR a) => Decoder s (Set a) + setOrListDecoder = + ifDecoderVersionAtLeast + (natVersion @9) + addrWitsSetDecoder + (Set.fromList <$> decodeList decCBOR) + {-# INLINE setOrListDecoder #-} + + decoderForKey :: + Annotator (AlonzoTxWitsRaw era) -> + Word -> + Maybe (Decoder s (Annotator (AlonzoTxWitsRaw era))) + decoderForKey acc = \case + 0 -> Just $ mapSparseField (\x w -> w {atwrAddrTxWits = x}) setOrListDecoder acc + 1 -> Just $ mapSparseFieldA addScriptsTxWitsRaw nativeScriptsDecoder acc + 2 -> Just $ mapSparseField (\x w -> w {atwrBootAddrTxWits = x}) setOrListDecoder acc + 3 -> Just $ mapSparseField addScriptsTxWitsRaw (alonzoPlutusScriptDecoder SPlutusV1) acc + 4 -> Just $ mapSparseFieldA (\x w -> w {atwrDatsTxWits = x}) decCBOR acc + 5 -> Just $ mapSparseFieldA (\x w -> w {atwrRdmrsTxWits = x}) decCBOR acc + 6 -> Just $ mapSparseField addScriptsTxWitsRaw (alonzoPlutusScriptDecoder SPlutusV2) acc + 7 -> Just $ mapSparseField addScriptsTxWitsRaw (alonzoPlutusScriptDecoder SPlutusV3) acc + _ -> Nothing + {-# INLINE decoderForKey #-} + txWitnessField :: Word -> Field (Annotator (AlonzoTxWitsRaw era)) txWitnessField 0 = fieldA @@ -698,14 +726,20 @@ decodeAlonzoPlutusScript :: (AlonzoEraScript era, PlutusLanguage l) => SLanguage l -> Decode (Closed Dense) (Map ScriptHash (Script era)) -decodeAlonzoPlutusScript slang = - D $ - ifDecoderVersionAtLeast - (natVersion @9) - (scriptDecoderV9 (fromPlutusScript <$> decodePlutusScript slang)) - (scriptDecoder (fromPlutusScript <$> decodePlutusScript slang)) +decodeAlonzoPlutusScript slang = D (alonzoPlutusScriptDecoder slang) {-# INLINE decodeAlonzoPlutusScript #-} +alonzoPlutusScriptDecoder :: + (AlonzoEraScript era, PlutusLanguage l) => + SLanguage l -> + Decoder s (Map ScriptHash (Script era)) +alonzoPlutusScriptDecoder slang = + ifDecoderVersionAtLeast + (natVersion @9) + (scriptDecoderV9 (fromPlutusScript <$> decodePlutusScript slang)) + (scriptDecoder (fromPlutusScript <$> decodePlutusScript slang)) +{-# INLINE alonzoPlutusScriptDecoder #-} + scriptDecoderV9 :: EraScript era => Decoder s (Script era) -> From 45e4a2e3473ecac9b4fe37d23b1a7bc360a63c1f Mon Sep 17 00:00:00 2001 From: Aniket Deshpande Date: Tue, 19 May 2026 20:33:58 +0530 Subject: [PATCH 06/13] Use decodeSparseKeyed for DijkstraTxBody --- .../impl/src/Cardano/Ledger/Babbage/TxOut.hs | 7 +- .../src/Cardano/Ledger/Dijkstra/TxBody.hs | 215 +++++++++++------- 2 files changed, 140 insertions(+), 82 deletions(-) diff --git a/eras/babbage/impl/src/Cardano/Ledger/Babbage/TxOut.hs b/eras/babbage/impl/src/Cardano/Ledger/Babbage/TxOut.hs index 4e41eb5fd5c..41db07e242a 100644 --- a/eras/babbage/impl/src/Cardano/Ledger/Babbage/TxOut.hs +++ b/eras/babbage/impl/src/Cardano/Ledger/Babbage/TxOut.hs @@ -116,7 +116,7 @@ import qualified Data.ByteString.Lazy as LBS import Data.Maybe (fromMaybe) import Data.MemPack import qualified Data.Text as T -import Data.Typeable (Proxy (..)) +import Data.Typeable (Proxy (..), typeRep) import GHC.Generics (Generic) import GHC.Stack (HasCallStack) import Lens.Micro (Lens', lens, to, (^.)) @@ -610,14 +610,15 @@ decodeTxOut decAddr = do dtxo <- ifDecoderVersionAtLeast (natVersion @12) - (decodeSparseKeyed "TxOut" requiredFields initial decoderForKey) - (decode $ SparseKeyed "TxOut" initial bodyFields requiredFields) + (decodeSparseKeyed name requiredFields initial decoderForKey) + (decode $ SparseKeyed name initial bodyFields requiredFields) case dtxo of DecodingTxOut SNothing _ _ _ -> cborError $ DecoderErrorCustom "BabbageTxOut" "Impossible: no Addr" DecodingTxOut (SJust (addr, cAddr)) val d script -> pure $ mkTxOut addr cAddr val d script where + name = show . typeRep $ Proxy @(BabbageTxOut era) initial :: DecodingTxOut era initial = DecodingTxOut SNothing mempty NoDatum SNothing decoderForKey :: DecodingTxOut era -> Word -> Maybe (Decoder s (DecodingTxOut era)) diff --git a/eras/dijkstra/impl/src/Cardano/Ledger/Dijkstra/TxBody.hs b/eras/dijkstra/impl/src/Cardano/Ledger/Dijkstra/TxBody.hs index fedf5aba08b..1f573b35625 100644 --- a/eras/dijkstra/impl/src/Cardano/Ledger/Dijkstra/TxBody.hs +++ b/eras/dijkstra/impl/src/Cardano/Ledger/Dijkstra/TxBody.hs @@ -106,7 +106,7 @@ import Cardano.Ledger.Babbage.TxBody ( babbageAllInputsTxBodyF, babbageSpendableInputsTxBodyF, ) -import Cardano.Ledger.BaseTypes (Network, StrictMaybe (..), fromSMaybe) +import Cardano.Ledger.BaseTypes (Network, StrictMaybe (..)) import Cardano.Ledger.Binary import Cardano.Ledger.Binary.Coders import Cardano.Ledger.Coin (Coin, decodePositiveCoin) @@ -150,11 +150,12 @@ import Data.OMap.Strict (OMap) import qualified Data.OMap.Strict as OMap import Data.OSet.Strict (OSet, decodeOSet) import qualified Data.OSet.Strict as OSet +import Data.Proxy (Proxy (..)) import Data.STRef (newSTRef, readSTRef, writeSTRef) import Data.Sequence.Strict (StrictSeq) import Data.Set (Set, foldr') import qualified Data.Set as Set -import Data.Typeable (Typeable) +import Data.Typeable (Typeable, typeRep) import GHC.Generics (Generic) import Lens.Micro (Lens', lens, to, (.~), (^.)) import NoThunks.Class (InspectHeap (..), NoThunks) @@ -327,104 +328,160 @@ instance DecCBOR (Annotator (DijkstraTxBodyRaw l era)) where decCBOR = withSTxBothLevels @l $ \sTxLevel -> - decode $ - SparseKeyed - "TxBodyRaw" - (pure $ basicDijkstraTxBodyRaw sTxLevel) - (bodyFields sTxLevel) - (requiredFields sTxLevel) + decodeSparseKeyed + name + (requiredFields sTxLevel) + (pure $ basicDijkstraTxBodyRaw sTxLevel) + (decoderForKey sTxLevel) where - bodyFields :: STxBothLevels l era -> Word -> Field (Annotator (DijkstraTxBodyRaw l era)) - bodyFields sTxLevel = \case - 0 -> fieldA (inputsDijkstraTxBodyRawL .~) From - 1 -> fieldA (outputsDijkstraTxBodyRawL .~) From - 2 | STopTx <- sTxLevel -> fieldA (feeDijkstraTxBodyRawL .~) From - 3 -> ofieldA (vldtDijkstraTxBodyRawL . invalidHereAfterL .~) From + name = show . typeRep $ Proxy @(DijkstraTxBodyRaw l era) + decoderForKey :: + STxBothLevels l era -> + Annotator (DijkstraTxBodyRaw l era) -> + Word -> + Maybe (Decoder s (Annotator (DijkstraTxBodyRaw l era))) + decoderForKey sTxLevel acc = \case + 0 -> Just $ mapSparseField (inputsDijkstraTxBodyRawL .~) decCBOR acc + 1 -> Just $ mapSparseField (outputsDijkstraTxBodyRawL .~) decCBOR acc + 2 | STopTx <- sTxLevel -> Just $ mapSparseField (feeDijkstraTxBodyRawL .~) decCBOR acc + 3 -> Just $ mapSparseFieldOptional (vldtDijkstraTxBodyRawL . invalidHereAfterL .~) decCBOR acc 4 -> - fieldAGuarded - (emptyFailure "Certificates" "non-empty") - OSet.null - (certsDijkstraTxBodyRawL .~) - From + Just $ + mapSparseFieldGuarded + name + (emptyFailure "Certificates" "non-empty") + OSet.null + (certsDijkstraTxBodyRawL .~) + decCBOR + acc 5 -> - fieldAGuarded - (emptyFailure "Withdrawals" "non-empty") - (null . unWithdrawals) - (withdrawalsDijkstraTxBodyRawL .~) - From - 7 -> ofieldA (auxDataHashDijkstraTxBodyRawL .~) From - 8 -> ofieldA (vldtDijkstraTxBodyRawL . invalidBeforeL .~) From + Just $ + mapSparseFieldGuarded + name + (emptyFailure "Withdrawals" "non-empty") + (null . unWithdrawals) + (withdrawalsDijkstraTxBodyRawL .~) + decCBOR + acc + 7 -> Just $ mapSparseFieldOptional (auxDataHashDijkstraTxBodyRawL .~) decCBOR acc + 8 -> Just $ mapSparseFieldOptional (vldtDijkstraTxBodyRawL . invalidBeforeL .~) decCBOR acc 9 -> - fieldAGuarded - (emptyFailure "Mint" "non-empty") - (== mempty) - (mintDijkstraTxBodyRawL .~) - From - 11 -> ofieldA (scriptIntegrityHashDijkstraTxBodyRawL .~) From + Just $ + mapSparseFieldGuarded + name + (emptyFailure "Mint" "non-empty") + (== mempty) + (mintDijkstraTxBodyRawL .~) + decCBOR + acc + 11 -> Just $ mapSparseFieldOptional (scriptIntegrityHashDijkstraTxBodyRawL .~) decCBOR acc 13 | STopTx <- sTxLevel -> - fieldAGuarded - (emptyFailure "Collateral Inputs" "non-empty") - null - (collateralInputsDijkstraTxBodyRawL .~) - From + Just $ + mapSparseFieldGuarded + name + (emptyFailure "Collateral Inputs" "non-empty") + null + (collateralInputsDijkstraTxBodyRawL .~) + decCBOR + acc + -- Keys 14 and 22 are decoded with 'mapSparseField' directly. + -- Their lenses target plain values (@'OSet' (Credential Guard)@ + -- and @'Coin'@), so there's no 'StrictMaybe' to wrap or unwrap. + -- + -- The round-trip stays consistent and symmetric because + -- the encoder omits each key when its value equals the + -- default, and the decoder's initial accumulator (built by + -- 'basicDijkstraTxBodyRaw') starts each field at that same + -- default. + -- + -- For example: + -- * Key 14 — encoder: @Omit null (Key 14 (To dtbrGuards))@. + -- The key is in the serialised bits only when @dtbrGuards@ + -- is non-empty. The accumulator starts at @mempty@, so a + -- missing key keeps the field at @mempty@ — matching what + -- the sender skipped. + -- + -- * Key 22 — encoder: @Omit (== mempty) (Key 22 (To + -- dtbrTreasuryDonation))@. The key is in the serialised bits + -- only when the donation is non-zero. The accumulator starts + -- at @mempty@, so a missing key keeps the field at @zero@. 14 -> - ofieldA - (\x -> guardsDijkstraTxBodyRawL .~ fromSMaybe mempty x) - (D decodeGuards) - 15 -> ofieldA (networkIdDijkstraTxBodyRawL .~) From + Just $ mapSparseField (guardsDijkstraTxBodyRawL .~) decodeGuards acc + 15 -> Just $ mapSparseFieldOptional (networkIdDijkstraTxBodyRawL .~) decCBOR acc 16 | STopTx <- sTxLevel -> - ofieldA (collateralReturnDijkstraTxBodyRawL .~) From + Just $ mapSparseFieldOptional (collateralReturnDijkstraTxBodyRawL .~) decCBOR acc 17 | STopTx <- sTxLevel -> - ofieldA (totalCollateralDijkstraTxBodyRawL .~) From + Just $ mapSparseFieldOptional (totalCollateralDijkstraTxBodyRawL .~) decCBOR acc 18 -> - fieldAGuarded - (emptyFailure "Reference Inputs" "non-empty") - null - (referenceInputsDijkstraTxBodyRawL .~) - From + Just $ + mapSparseFieldGuarded + name + (emptyFailure "Reference Inputs" "non-empty") + null + (referenceInputsDijkstraTxBodyRawL .~) + decCBOR + acc 19 -> - fieldAGuarded - (emptyFailure "VotingProcedures" "non-empty") - (null . unVotingProcedures) - (votingProceduresDijkstraTxBodyRawL .~) - From + Just $ + mapSparseFieldGuarded + name + (emptyFailure "VotingProcedures" "non-empty") + (null . unVotingProcedures) + (votingProceduresDijkstraTxBodyRawL .~) + decCBOR + acc 20 -> - fieldAGuarded - (emptyFailure "ProposalProcedures" "non-empty") - OSet.null - (proposalProceduresDijkstraTxBodyRawL .~) - From - 21 -> ofieldA (currentTreasuryValueDijkstraTxBodyRawL .~) From + Just $ + mapSparseFieldGuarded + name + (emptyFailure "ProposalProcedures" "non-empty") + OSet.null + (proposalProceduresDijkstraTxBodyRawL .~) + decCBOR + acc + 21 -> Just $ mapSparseFieldOptional (currentTreasuryValueDijkstraTxBodyRawL .~) decCBOR acc 22 -> - ofieldA - (\x -> treasuryDonationDijkstraTxBodyRawL .~ fromSMaybe zero x) - (D (decodePositiveCoin $ emptyFailure "Treasury Donation" "non-zero")) + -- See comment about field 14. + Just $ + mapSparseField + (treasuryDonationDijkstraTxBodyRawL .~) + (decodePositiveCoin $ emptyFailure "Treasury Donation" "non-zero") + acc 23 | STopTx <- sTxLevel -> - fieldAA (subTransactionsDijkstraTxBodyRawL .~) (D decodeSubTransactions) + Just $ mapSparseFieldA (subTransactionsDijkstraTxBodyRawL .~) decodeSubTransactions acc 24 | SSubTx <- sTxLevel -> - fieldAGuarded - (emptyFailure "RequiredTopLevelGuards" "non-empty") - Map.null - (requiredTopLevelGuardsDijkstraTxBodyRawL .~) - (D (decodeMap decCBOR (decodeNullStrictMaybe decCBOR))) + Just $ + mapSparseFieldGuarded + name + (emptyFailure "RequiredTopLevelGuards" "non-empty") + Map.null + (requiredTopLevelGuardsDijkstraTxBodyRawL .~) + (decodeMap decCBOR (decodeNullStrictMaybe decCBOR)) + acc 25 -> - fieldAGuarded - (emptyFailure "DirectDeposits" "non-empty") - (null . unDirectDeposits) - (directDepositsDijkstraTxBodyRawL .~) - From + Just $ + mapSparseFieldGuarded + name + (emptyFailure "DirectDeposits" "non-empty") + (null . unDirectDeposits) + (directDepositsDijkstraTxBodyRawL .~) + decCBOR + acc 26 -> - fieldAGuarded - (emptyFailure "AccountBalanceIntervals" "non-empty") - (null . unAccountBalanceIntervals) - (accountBalanceIntervalsDijkstraTxBodyRawL .~) - From - n -> invalidField n + Just $ + mapSparseFieldGuarded + name + (emptyFailure "AccountBalanceIntervals" "non-empty") + (null . unAccountBalanceIntervals) + (accountBalanceIntervalsDijkstraTxBodyRawL .~) + decCBOR + acc + _ -> Nothing decodeSubTransactions :: Decoder s (Annotator (OMap TxId (Tx SubTx era))) decodeSubTransactions = decodeNonEmptySetLikeEnforceNoDuplicatesAnn From da6c95e909239c2832b0e07d96c53affb580458f Mon Sep 17 00:00:00 2001 From: Aniket Deshpande Date: Wed, 20 May 2026 16:20:28 +0530 Subject: [PATCH 07/13] Fix test with new error report --- .../Test/Cardano/Ledger/Alonzo/Binary/Golden.hs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/eras/alonzo/impl/testlib/Test/Cardano/Ledger/Alonzo/Binary/Golden.hs b/eras/alonzo/impl/testlib/Test/Cardano/Ledger/Alonzo/Binary/Golden.hs index 4ec1b3b3475..55ecb67b60d 100644 --- a/eras/alonzo/impl/testlib/Test/Cardano/Ledger/Alonzo/Binary/Golden.hs +++ b/eras/alonzo/impl/testlib/Test/Cardano/Ledger/Alonzo/Binary/Golden.hs @@ -15,16 +15,18 @@ module Test.Cardano.Ledger.Alonzo.Binary.Golden ( ) where import Cardano.Ledger.Alonzo.Core (EraTxWits (..), ShelleyEraTxCert) +import Cardano.Ledger.Alonzo.TxWits (AlonzoTxWitsRaw) import Cardano.Ledger.Binary ( Annotator, DecoderError (..), DeserialiseFailure (..), Tokens (..), Version, + natVersion, ) import qualified Cardano.Ledger.Binary as Binary import Cardano.Ledger.MemoBytes (EqRaw (..)) -import Data.Data (Proxy (..)) +import Data.Data (Proxy (..), typeRep) import Data.Void (Void) import Test.Cardano.Ledger.Allegra.Binary.Golden hiding (spec) import Test.Cardano.Ledger.Alonzo.Era (AlonzoEraTest) @@ -77,13 +79,16 @@ txWitsDecodingFailsOnInvalidField version validFields = DecoderErrorDeserialiseFailure lbl ( DeserialiseFailure (if n >= 24 then 3 else 2) $ - -- TODO fix the `occured` typo in the produced value - "An error occured while decoding (Int,Void) not a valid key:.\nError: " <> show n + if version >= natVersion @12 + then typeName <> ":Unknown field key " <> show n + -- TODO fix the `occured` typo in the produced value + else "An error occured while decoding (Int,Void) not a valid key:.\nError: " <> show n ) else DecoderErrorDeserialiseFailure lbl (DeserialiseFailure 1 "expected word") where lbl = Binary.label $ Proxy @(Annotator (TxWits era)) + typeName = show (typeRep (Proxy @(AlonzoTxWitsRaw era))) spec :: forall era. From 9260354246381100289d0241f51bd415557a0b4f Mon Sep 17 00:00:00 2001 From: Aniket Deshpande Date: Wed, 20 May 2026 19:37:03 +0530 Subject: [PATCH 08/13] Rename all decoderForKey to decoderByKey --- eras/alonzo/impl/src/Cardano/Ledger/Alonzo/TxAuxData.hs | 8 ++++---- eras/alonzo/impl/src/Cardano/Ledger/Alonzo/TxWits.hs | 8 ++++---- eras/babbage/impl/src/Cardano/Ledger/Babbage/TxOut.hs | 8 ++++---- eras/dijkstra/impl/src/Cardano/Ledger/Dijkstra/TxBody.hs | 6 +++--- .../src/Cardano/Ledger/Binary/Decoding/Decoder.hs | 4 ++-- .../src/Cardano/Ledger/Core/PParams.hs | 6 +++--- 6 files changed, 20 insertions(+), 20 deletions(-) diff --git a/eras/alonzo/impl/src/Cardano/Ledger/Alonzo/TxAuxData.hs b/eras/alonzo/impl/src/Cardano/Ledger/Alonzo/TxAuxData.hs index ddf85d41215..45cc56a94f9 100644 --- a/eras/alonzo/impl/src/Cardano/Ledger/Alonzo/TxAuxData.hs +++ b/eras/alonzo/impl/src/Cardano/Ledger/Alonzo/TxAuxData.hs @@ -214,7 +214,7 @@ instance name [] (pure emptyAlonzoTxAuxDataRaw) - decoderForKey + decoderByKey ) decodeAlonzo ) @@ -245,11 +245,11 @@ instance TagD 259 $ SparseKeyed name (pure emptyAlonzoTxAuxDataRaw) auxDataField [] - decoderForKey :: + decoderByKey :: Annotator (AlonzoTxAuxDataRaw era) -> Word -> Maybe (Decoder s (Annotator (AlonzoTxAuxDataRaw era))) - decoderForKey acc = \case + decoderByKey acc = \case 0 -> Just $ do !x <- decCBOR pure $ (\ad -> ad {atadrMetadata = x}) <$> acc @@ -276,7 +276,7 @@ instance !x <- decCBOR pure $ addPlutusScripts PlutusV4 x <$> acc _ -> Nothing - {-# INLINE decoderForKey #-} + {-# INLINE decoderByKey #-} auxDataField :: Word -> Field (Annotator (AlonzoTxAuxDataRaw era)) auxDataField 0 = fieldA (\x ad -> ad {atadrMetadata = x}) From diff --git a/eras/alonzo/impl/src/Cardano/Ledger/Alonzo/TxWits.hs b/eras/alonzo/impl/src/Cardano/Ledger/Alonzo/TxWits.hs index d41b8f33f74..887ce809c0c 100644 --- a/eras/alonzo/impl/src/Cardano/Ledger/Alonzo/TxWits.hs +++ b/eras/alonzo/impl/src/Cardano/Ledger/Alonzo/TxWits.hs @@ -608,7 +608,7 @@ instance decCBOR = ifDecoderVersionAtLeast (natVersion @12) - (decodeSparseKeyed name [] (pure emptyTxWitsRaw) decoderForKey) + (decodeSparseKeyed name [] (pure emptyTxWitsRaw) decoderByKey) (decode $ SparseKeyed name (pure emptyTxWitsRaw) txWitnessField []) where name = show . typeRep $ Proxy @(AlonzoTxWitsRaw era) @@ -633,11 +633,11 @@ instance (Set.fromList <$> decodeList decCBOR) {-# INLINE setOrListDecoder #-} - decoderForKey :: + decoderByKey :: Annotator (AlonzoTxWitsRaw era) -> Word -> Maybe (Decoder s (Annotator (AlonzoTxWitsRaw era))) - decoderForKey acc = \case + decoderByKey acc = \case 0 -> Just $ mapSparseField (\x w -> w {atwrAddrTxWits = x}) setOrListDecoder acc 1 -> Just $ mapSparseFieldA addScriptsTxWitsRaw nativeScriptsDecoder acc 2 -> Just $ mapSparseField (\x w -> w {atwrBootAddrTxWits = x}) setOrListDecoder acc @@ -647,7 +647,7 @@ instance 6 -> Just $ mapSparseField addScriptsTxWitsRaw (alonzoPlutusScriptDecoder SPlutusV2) acc 7 -> Just $ mapSparseField addScriptsTxWitsRaw (alonzoPlutusScriptDecoder SPlutusV3) acc _ -> Nothing - {-# INLINE decoderForKey #-} + {-# INLINE decoderByKey #-} txWitnessField :: Word -> Field (Annotator (AlonzoTxWitsRaw era)) txWitnessField 0 = diff --git a/eras/babbage/impl/src/Cardano/Ledger/Babbage/TxOut.hs b/eras/babbage/impl/src/Cardano/Ledger/Babbage/TxOut.hs index 41db07e242a..ccff38badfb 100644 --- a/eras/babbage/impl/src/Cardano/Ledger/Babbage/TxOut.hs +++ b/eras/babbage/impl/src/Cardano/Ledger/Babbage/TxOut.hs @@ -610,7 +610,7 @@ decodeTxOut decAddr = do dtxo <- ifDecoderVersionAtLeast (natVersion @12) - (decodeSparseKeyed name requiredFields initial decoderForKey) + (decodeSparseKeyed name requiredFields initial decoderByKey) (decode $ SparseKeyed name initial bodyFields requiredFields) case dtxo of DecodingTxOut SNothing _ _ _ -> @@ -621,8 +621,8 @@ decodeTxOut decAddr = do name = show . typeRep $ Proxy @(BabbageTxOut era) initial :: DecodingTxOut era initial = DecodingTxOut SNothing mempty NoDatum SNothing - decoderForKey :: DecodingTxOut era -> Word -> Maybe (Decoder s (DecodingTxOut era)) - decoderForKey txOut = \case + decoderByKey :: DecodingTxOut era -> Word -> Maybe (Decoder s (DecodingTxOut era)) + decoderByKey txOut = \case 0 -> Just $ do !x <- decAddr pure txOut {decodingTxOutAddr = SJust x} @@ -636,7 +636,7 @@ decodeTxOut decAddr = do !x <- decodeCIC "Script" pure txOut {decodingTxOutScript = SJust x} _ -> Nothing - {-# INLINE decoderForKey #-} + {-# INLINE decoderByKey #-} bodyFields :: (Word -> Field (DecodingTxOut era)) bodyFields 0 = field diff --git a/eras/dijkstra/impl/src/Cardano/Ledger/Dijkstra/TxBody.hs b/eras/dijkstra/impl/src/Cardano/Ledger/Dijkstra/TxBody.hs index 1f573b35625..e907a070e05 100644 --- a/eras/dijkstra/impl/src/Cardano/Ledger/Dijkstra/TxBody.hs +++ b/eras/dijkstra/impl/src/Cardano/Ledger/Dijkstra/TxBody.hs @@ -332,15 +332,15 @@ instance name (requiredFields sTxLevel) (pure $ basicDijkstraTxBodyRaw sTxLevel) - (decoderForKey sTxLevel) + (decoderByKey sTxLevel) where name = show . typeRep $ Proxy @(DijkstraTxBodyRaw l era) - decoderForKey :: + decoderByKey :: STxBothLevels l era -> Annotator (DijkstraTxBodyRaw l era) -> Word -> Maybe (Decoder s (Annotator (DijkstraTxBodyRaw l era))) - decoderForKey sTxLevel acc = \case + decoderByKey sTxLevel acc = \case 0 -> Just $ mapSparseField (inputsDijkstraTxBodyRawL .~) decCBOR acc 1 -> Just $ mapSparseField (outputsDijkstraTxBodyRawL .~) decCBOR acc 2 | STopTx <- sTxLevel -> Just $ mapSparseField (feeDijkstraTxBodyRawL .~) decCBOR acc diff --git a/libs/cardano-ledger-binary/src/Cardano/Ledger/Binary/Decoding/Decoder.hs b/libs/cardano-ledger-binary/src/Cardano/Ledger/Binary/Decoding/Decoder.hs index 625264ceb27..87ff8d43441 100644 --- a/libs/cardano-ledger-binary/src/Cardano/Ledger/Binary/Decoding/Decoder.hs +++ b/libs/cardano-ledger-binary/src/Cardano/Ledger/Binary/Decoding/Decoder.hs @@ -1210,7 +1210,7 @@ decodeSparseKeyed :: -- indicates the key as unknown and is reported as decoding failure. (a -> Word -> Maybe (Decoder s a)) -> Decoder s a -decodeSparseKeyed name requiredFields initial decoderForKey = do +decodeSparseKeyed name requiredFields initial decoderByKey = do (seen, acc) <- decodeMapLenOrIndef >>= \case Just len -> defLoop Set.empty initial len @@ -1248,7 +1248,7 @@ decodeSparseKeyed name requiredFields initial decoderForKey = do key <- decodeWord if Set.member key seen then failMsg $ "Duplicate field key " <> show key - else case decoderForKey acc key of + else case decoderByKey acc key of Nothing -> failMsg $ "Unknown field key " <> show key Just decoder -> do acc' <- decoder diff --git a/libs/cardano-ledger-core/src/Cardano/Ledger/Core/PParams.hs b/libs/cardano-ledger-core/src/Cardano/Ledger/Core/PParams.hs index 9017ff0ee26..ed2b920964d 100644 --- a/libs/cardano-ledger-core/src/Cardano/Ledger/Core/PParams.hs +++ b/libs/cardano-ledger-core/src/Cardano/Ledger/Core/PParams.hs @@ -257,15 +257,15 @@ instance EraPParams era => DecCBOR (PParamsUpdate era) where decCBOR = ifDecoderVersionAtLeast (natVersion @12) - (decodeSparseKeyed name [] emptyPParamsUpdate decoderForKey) + (decodeSparseKeyed name [] emptyPParamsUpdate decoderByKey) (decode $ SparseKeyed name emptyPParamsUpdate updateField []) where name = show . typeRep $ Proxy @(PParamsUpdate era) - decoderForKey :: + decoderByKey :: PParamsUpdate era -> Word -> Maybe (Decoder s (PParamsUpdate era)) - decoderForKey acc k = + decoderByKey acc k = case IntMap.lookup (fromIntegral k) updateFieldMap of Just (Field setter dec) -> Just $ (`setter` acc) <$> dec Nothing -> Nothing From 2a030c271b4a86d2ed6c7cf9017c3055bd15d91b Mon Sep 17 00:00:00 2001 From: Aniket Deshpande Date: Thu, 21 May 2026 21:23:14 +0530 Subject: [PATCH 09/13] decodeSparseKeyed take TypeName instead of String. Update AlonzoTxAuxData, AlonzoTxWits, BabbageTxOut, DijkstraTxBody, and PParamsUpdate. Add decodeAddPlutus helper for AlonzoTxAuxData. --- cabal.project | 7 +++++ .../src/Cardano/Ledger/Alonzo/TxAuxData.hs | 29 ++++++++----------- .../impl/src/Cardano/Ledger/Alonzo/TxWits.hs | 3 +- .../babbage/impl/cardano-ledger-babbage.cabal | 1 + .../impl/src/Cardano/Ledger/Babbage/TxOut.hs | 3 +- .../impl/cardano-ledger-dijkstra.cabal | 1 + .../src/Cardano/Ledger/Dijkstra/TxBody.hs | 3 +- .../Cardano/Ledger/Binary/Decoding/Decoder.hs | 5 ++-- .../src/Cardano/Ledger/Core/PParams.hs | 3 +- 9 files changed, 32 insertions(+), 23 deletions(-) diff --git a/cabal.project b/cabal.project index b4dfaf52d1b..a23063d9905 100644 --- a/cabal.project +++ b/cabal.project @@ -10,6 +10,13 @@ repository cardano-haskell-packages c00aae8461a256275598500ea0e187588c35a5d5d7454fb57eac18d9edb86a56 d4a35cd3121aa00d18544bb0ac01c3e1691d618f462c46129271bccf39f7e8ee +source-repository-package + type: git + location: https://github.com/IntersectMBO/cardano-base.git + subdir: cardano-base + --sha256: sha256-Gj8BPEew+UYkCWr3D0LgA6dvhYof49V20XRMRMf1FzY= + tag: efe33cb3739f5c90b733b730bdeffa11a75f291d + source-repository-package type: git location: https://github.com/IntersectMBO/formal-ledger-specifications.git diff --git a/eras/alonzo/impl/src/Cardano/Ledger/Alonzo/TxAuxData.hs b/eras/alonzo/impl/src/Cardano/Ledger/Alonzo/TxAuxData.hs index 45cc56a94f9..981a2e7ef3b 100644 --- a/eras/alonzo/impl/src/Cardano/Ledger/Alonzo/TxAuxData.hs +++ b/eras/alonzo/impl/src/Cardano/Ledger/Alonzo/TxAuxData.hs @@ -50,6 +50,7 @@ module Cardano.Ledger.Alonzo.TxAuxData ( atadPlutus', ) where +import Cardano.Base.Typeable (TypeName (TypeName)) import Cardano.Ledger.Allegra.TxAuxData (AllegraEraTxAuxData (..)) import Cardano.Ledger.Alonzo.Era import Cardano.Ledger.Alonzo.Scripts ( @@ -211,7 +212,7 @@ instance ( do assertTag 259 decodeSparseKeyed - name + TypeName [] (pure emptyAlonzoTxAuxDataRaw) decoderByKey @@ -259,23 +260,17 @@ instance (\scripts ad -> ad {atadrNativeScripts = atadrNativeScripts ad <> scripts}) <$> x <*> acc - 2 -> Just $ do - guardPlutus PlutusV1 - !x <- decCBOR - pure $ addPlutusScripts PlutusV1 x <$> acc - 3 -> Just $ do - guardPlutus PlutusV2 - !x <- decCBOR - pure $ addPlutusScripts PlutusV2 x <$> acc - 4 -> Just $ do - guardPlutus PlutusV3 - !x <- decCBOR - pure $ addPlutusScripts PlutusV3 x <$> acc - 5 -> Just $ do - guardPlutus PlutusV4 - !x <- decCBOR - pure $ addPlutusScripts PlutusV4 x <$> acc + 2 -> decodeAddPlutus PlutusV1 acc + 3 -> decodeAddPlutus PlutusV2 acc + 4 -> decodeAddPlutus PlutusV3 acc + 5 -> decodeAddPlutus PlutusV4 acc _ -> Nothing + where + decodeAddPlutus lang accu = Just $ do + guardPlutus lang + !x <- decCBOR + pure $ addPlutusScripts lang x <$> accu + {-# INLINE decodeAddPlutus #-} {-# INLINE decoderByKey #-} auxDataField :: Word -> Field (Annotator (AlonzoTxAuxDataRaw era)) diff --git a/eras/alonzo/impl/src/Cardano/Ledger/Alonzo/TxWits.hs b/eras/alonzo/impl/src/Cardano/Ledger/Alonzo/TxWits.hs index 887ce809c0c..996938fca6f 100644 --- a/eras/alonzo/impl/src/Cardano/Ledger/Alonzo/TxWits.hs +++ b/eras/alonzo/impl/src/Cardano/Ledger/Alonzo/TxWits.hs @@ -56,6 +56,7 @@ module Cardano.Ledger.Alonzo.TxWits ( ) where import Cardano.Base.Proxy (asProxy) +import Cardano.Base.Typeable (TypeName (TypeName)) import Cardano.Ledger.Alonzo.Era (AlonzoEra) import Cardano.Ledger.Alonzo.Scripts ( AlonzoEraScript (..), @@ -608,7 +609,7 @@ instance decCBOR = ifDecoderVersionAtLeast (natVersion @12) - (decodeSparseKeyed name [] (pure emptyTxWitsRaw) decoderByKey) + (decodeSparseKeyed TypeName [] (pure emptyTxWitsRaw) decoderByKey) (decode $ SparseKeyed name (pure emptyTxWitsRaw) txWitnessField []) where name = show . typeRep $ Proxy @(AlonzoTxWitsRaw era) diff --git a/eras/babbage/impl/cardano-ledger-babbage.cabal b/eras/babbage/impl/cardano-ledger-babbage.cabal index ae7f66a1614..79a76d36a54 100644 --- a/eras/babbage/impl/cardano-ledger-babbage.cabal +++ b/eras/babbage/impl/cardano-ledger-babbage.cabal @@ -89,6 +89,7 @@ library aeson >=2.2, base >=4.18 && <5, bytestring, + cardano-base >=0.1.4, cardano-data >=1.2, cardano-ledger-allegra ^>=1.10, cardano-ledger-alonzo ^>=1.16, diff --git a/eras/babbage/impl/src/Cardano/Ledger/Babbage/TxOut.hs b/eras/babbage/impl/src/Cardano/Ledger/Babbage/TxOut.hs index ccff38badfb..33c061ed932 100644 --- a/eras/babbage/impl/src/Cardano/Ledger/Babbage/TxOut.hs +++ b/eras/babbage/impl/src/Cardano/Ledger/Babbage/TxOut.hs @@ -42,6 +42,7 @@ module Cardano.Ledger.Babbage.TxOut ( internBabbageTxOut, ) where +import Cardano.Base.Typeable (TypeName (TypeName)) import Cardano.Ledger.Address ( CompactAddr, compactAddr, @@ -610,7 +611,7 @@ decodeTxOut decAddr = do dtxo <- ifDecoderVersionAtLeast (natVersion @12) - (decodeSparseKeyed name requiredFields initial decoderByKey) + (decodeSparseKeyed TypeName requiredFields initial decoderByKey) (decode $ SparseKeyed name initial bodyFields requiredFields) case dtxo of DecodingTxOut SNothing _ _ _ -> diff --git a/eras/dijkstra/impl/cardano-ledger-dijkstra.cabal b/eras/dijkstra/impl/cardano-ledger-dijkstra.cabal index b03f181b990..e9b4310ebdf 100644 --- a/eras/dijkstra/impl/cardano-ledger-dijkstra.cabal +++ b/eras/dijkstra/impl/cardano-ledger-dijkstra.cabal @@ -105,6 +105,7 @@ library aeson, base >=4.14 && <5, bytestring, + cardano-base >=0.1.4, cardano-crypto-class, cardano-data ^>=1.3, cardano-ledger-allegra, diff --git a/eras/dijkstra/impl/src/Cardano/Ledger/Dijkstra/TxBody.hs b/eras/dijkstra/impl/src/Cardano/Ledger/Dijkstra/TxBody.hs index e907a070e05..8ad4dc71659 100644 --- a/eras/dijkstra/impl/src/Cardano/Ledger/Dijkstra/TxBody.hs +++ b/eras/dijkstra/impl/src/Cardano/Ledger/Dijkstra/TxBody.hs @@ -98,6 +98,7 @@ module Cardano.Ledger.Dijkstra.TxBody ( accountBalanceIntervalsDijkstraTxBodyRawL, ) where +import Cardano.Base.Typeable (TypeName (TypeName)) import Cardano.Ledger.Address (DirectDeposits (..)) import Cardano.Ledger.Allegra.Scripts (invalidBeforeL, invalidHereAfterL) import Cardano.Ledger.Alonzo.TxBody (Indexable (..)) @@ -329,7 +330,7 @@ instance where decCBOR = withSTxBothLevels @l $ \sTxLevel -> decodeSparseKeyed - name + TypeName (requiredFields sTxLevel) (pure $ basicDijkstraTxBodyRaw sTxLevel) (decoderByKey sTxLevel) diff --git a/libs/cardano-ledger-binary/src/Cardano/Ledger/Binary/Decoding/Decoder.hs b/libs/cardano-ledger-binary/src/Cardano/Ledger/Binary/Decoding/Decoder.hs index 87ff8d43441..f8736795eda 100644 --- a/libs/cardano-ledger-binary/src/Cardano/Ledger/Binary/Decoding/Decoder.hs +++ b/libs/cardano-ledger-binary/src/Cardano/Ledger/Binary/Decoding/Decoder.hs @@ -171,6 +171,7 @@ module Cardano.Ledger.Binary.Decoding.Decoder ( ) where import Cardano.Base.IP (IPv4, IPv6, toIPv4w, toIPv6w) +import Cardano.Base.Typeable (TypeName) import Cardano.Ledger.Binary.Plain ( DecoderError (..), cborError, @@ -1198,7 +1199,7 @@ decodeMapContentsTraverse decodeKey decodeValue = decodeSparseKeyed :: forall a s. -- | Type name used in error messages. - String -> + TypeName a -> -- | Required keys with friendly names per key. After the map is fully -- consumed, the absence of any key in this list is reported as a -- failure using its name and key. @@ -1226,7 +1227,7 @@ decodeSparseKeyed name requiredFields initial decoderByKey = do pure acc where failMsg :: String -> Decoder s b - failMsg msg = fail $ name <> ":" <> msg + failMsg msg = fail $ show name <> ":" <> msg defLoop :: Set.Set Word -> a -> Int -> Decoder s (Set.Set Word, a) defLoop !seen !acc !i diff --git a/libs/cardano-ledger-core/src/Cardano/Ledger/Core/PParams.hs b/libs/cardano-ledger-core/src/Cardano/Ledger/Core/PParams.hs index ed2b920964d..74a5b4e482a 100644 --- a/libs/cardano-ledger-core/src/Cardano/Ledger/Core/PParams.hs +++ b/libs/cardano-ledger-core/src/Cardano/Ledger/Core/PParams.hs @@ -97,6 +97,7 @@ module Cardano.Ledger.Core.PParams ( ppuMinFeeBL, ) where +import Cardano.Base.Typeable (TypeName (TypeName)) import Cardano.Ledger.BaseTypes ( EpochInterval (..), KeyValuePairs (..), @@ -257,7 +258,7 @@ instance EraPParams era => DecCBOR (PParamsUpdate era) where decCBOR = ifDecoderVersionAtLeast (natVersion @12) - (decodeSparseKeyed name [] emptyPParamsUpdate decoderByKey) + (decodeSparseKeyed TypeName [] emptyPParamsUpdate decoderByKey) (decode $ SparseKeyed name emptyPParamsUpdate updateField []) where name = show . typeRep $ Proxy @(PParamsUpdate era) From b6277b5afee61f7ba0998e1cda21bf338c7175ba Mon Sep 17 00:00:00 2001 From: Aniket Deshpande Date: Thu, 21 May 2026 21:38:51 +0530 Subject: [PATCH 10/13] Replace mapSparseField helpers with decodeAccA. Migrate AlonzoTxWits and DijkstraTxBody to the new pattern. --- .../impl/src/Cardano/Ledger/Alonzo/TxWits.hs | 25 ++- .../src/Cardano/Ledger/Dijkstra/TxBody.hs | 179 +++++++----------- libs/cardano-ledger-binary/CHANGELOG.md | 2 +- .../Cardano/Ledger/Binary/Decoding/Decoder.hs | 87 ++------- 4 files changed, 104 insertions(+), 189 deletions(-) diff --git a/eras/alonzo/impl/src/Cardano/Ledger/Alonzo/TxWits.hs b/eras/alonzo/impl/src/Cardano/Ledger/Alonzo/TxWits.hs index 996938fca6f..9086a7067c9 100644 --- a/eras/alonzo/impl/src/Cardano/Ledger/Alonzo/TxWits.hs +++ b/eras/alonzo/impl/src/Cardano/Ledger/Alonzo/TxWits.hs @@ -76,6 +76,7 @@ import Cardano.Ledger.Binary ( ToCBOR (..), TokenType (..), allowTag, + decodeAccA, decodeList, decodeListLenOrIndef, decodeListLikeWithCount, @@ -91,8 +92,6 @@ import Cardano.Ledger.Binary ( ifDecoderVersionAtLeast, ifEncodingVersionAtLeast, listLenInt, - mapSparseField, - mapSparseFieldA, natVersion, peekTokenType, setTag, @@ -626,27 +625,27 @@ instance ifDecoderVersionAtLeast (natVersion @12) nonEmptyNoDuplicatesDecoder nonEmptyDecoder {-# INLINE addrWitsSetDecoder #-} - setOrListDecoder :: (Ord a, DecCBOR a) => Decoder s (Set a) - setOrListDecoder = + setOrListWitsDecoder :: (Ord a, DecCBOR a) => Decoder s (Set a) + setOrListWitsDecoder = ifDecoderVersionAtLeast (natVersion @9) addrWitsSetDecoder (Set.fromList <$> decodeList decCBOR) - {-# INLINE setOrListDecoder #-} + {-# INLINE setOrListWitsDecoder #-} decoderByKey :: Annotator (AlonzoTxWitsRaw era) -> Word -> Maybe (Decoder s (Annotator (AlonzoTxWitsRaw era))) decoderByKey acc = \case - 0 -> Just $ mapSparseField (\x w -> w {atwrAddrTxWits = x}) setOrListDecoder acc - 1 -> Just $ mapSparseFieldA addScriptsTxWitsRaw nativeScriptsDecoder acc - 2 -> Just $ mapSparseField (\x w -> w {atwrBootAddrTxWits = x}) setOrListDecoder acc - 3 -> Just $ mapSparseField addScriptsTxWitsRaw (alonzoPlutusScriptDecoder SPlutusV1) acc - 4 -> Just $ mapSparseFieldA (\x w -> w {atwrDatsTxWits = x}) decCBOR acc - 5 -> Just $ mapSparseFieldA (\x w -> w {atwrRdmrsTxWits = x}) decCBOR acc - 6 -> Just $ mapSparseField addScriptsTxWitsRaw (alonzoPlutusScriptDecoder SPlutusV2) acc - 7 -> Just $ mapSparseField addScriptsTxWitsRaw (alonzoPlutusScriptDecoder SPlutusV3) acc + 0 -> Just $ decodeAccA acc (\x w -> w {atwrAddrTxWits = x}) (pure <$> setOrListWitsDecoder) + 1 -> Just $ decodeAccA acc addScriptsTxWitsRaw nativeScriptsDecoder + 2 -> Just $ decodeAccA acc (\x w -> w {atwrBootAddrTxWits = x}) (pure <$> setOrListWitsDecoder) + 3 -> Just $ decodeAccA acc addScriptsTxWitsRaw (pure <$> alonzoPlutusScriptDecoder SPlutusV1) + 4 -> Just $ decodeAccA acc (\x w -> w {atwrDatsTxWits = x}) decCBOR + 5 -> Just $ decodeAccA acc (\x w -> w {atwrRdmrsTxWits = x}) decCBOR + 6 -> Just $ decodeAccA acc addScriptsTxWitsRaw (pure <$> alonzoPlutusScriptDecoder SPlutusV2) + 7 -> Just $ decodeAccA acc addScriptsTxWitsRaw (pure <$> alonzoPlutusScriptDecoder SPlutusV3) _ -> Nothing {-# INLINE decoderByKey #-} diff --git a/eras/dijkstra/impl/src/Cardano/Ledger/Dijkstra/TxBody.hs b/eras/dijkstra/impl/src/Cardano/Ledger/Dijkstra/TxBody.hs index 8ad4dc71659..f1b78f76cef 100644 --- a/eras/dijkstra/impl/src/Cardano/Ledger/Dijkstra/TxBody.hs +++ b/eras/dijkstra/impl/src/Cardano/Ledger/Dijkstra/TxBody.hs @@ -146,7 +146,6 @@ import Cardano.Ledger.Val (Val (..)) import Control.DeepSeq (NFData (..), deepseq) import Data.Coerce (coerce) import Data.Map.Strict (Map) -import qualified Data.Map.Strict as Map import Data.OMap.Strict (OMap) import qualified Data.OMap.Strict as OMap import Data.OSet.Strict (OSet, decodeOSet) @@ -336,152 +335,114 @@ instance (decoderByKey sTxLevel) where name = show . typeRep $ Proxy @(DijkstraTxBodyRaw l era) + emptyNamedFailure fieldName requirement = + name <> ": " <> emptyFailure fieldName requirement decoderByKey :: STxBothLevels l era -> Annotator (DijkstraTxBodyRaw l era) -> Word -> Maybe (Decoder s (Annotator (DijkstraTxBodyRaw l era))) decoderByKey sTxLevel acc = \case - 0 -> Just $ mapSparseField (inputsDijkstraTxBodyRawL .~) decCBOR acc - 1 -> Just $ mapSparseField (outputsDijkstraTxBodyRawL .~) decCBOR acc - 2 | STopTx <- sTxLevel -> Just $ mapSparseField (feeDijkstraTxBodyRawL .~) decCBOR acc - 3 -> Just $ mapSparseFieldOptional (vldtDijkstraTxBodyRawL . invalidHereAfterL .~) decCBOR acc + 0 -> Just $ decodeAccA acc (inputsDijkstraTxBodyRawL .~) (pure <$> decCBOR) + 1 -> Just $ decodeAccA acc (outputsDijkstraTxBodyRawL .~) (pure <$> decCBOR) + 2 | STopTx <- sTxLevel -> Just $ decodeAccA acc (feeDijkstraTxBodyRawL .~) (pure <$> decCBOR) + 3 -> Just $ decodeAccA acc (vldtDijkstraTxBodyRawL . invalidHereAfterL .~) (pure . SJust <$> decCBOR) 4 -> Just $ - mapSparseFieldGuarded - name - (emptyFailure "Certificates" "non-empty") - OSet.null - (certsDijkstraTxBodyRawL .~) - decCBOR - acc + decodeAccA acc (certsDijkstraTxBodyRawL .~) $ + pure <$> do + x <- decCBOR + failOnNull x $ emptyNamedFailure "Certificates" "non-empty" + pure x 5 -> Just $ - mapSparseFieldGuarded - name - (emptyFailure "Withdrawals" "non-empty") - (null . unWithdrawals) - (withdrawalsDijkstraTxBodyRawL .~) - decCBOR - acc - 7 -> Just $ mapSparseFieldOptional (auxDataHashDijkstraTxBodyRawL .~) decCBOR acc - 8 -> Just $ mapSparseFieldOptional (vldtDijkstraTxBodyRawL . invalidBeforeL .~) decCBOR acc + decodeAccA acc (withdrawalsDijkstraTxBodyRawL .~) $ + pure <$> do + x <- decCBOR + failOnNull (unWithdrawals x) $ emptyNamedFailure "Withdrawals" "non-empty" + pure x + 7 -> Just $ decodeAccA acc (auxDataHashDijkstraTxBodyRawL .~) (pure . SJust <$> decCBOR) + 8 -> Just $ decodeAccA acc (vldtDijkstraTxBodyRawL . invalidBeforeL .~) (pure . SJust <$> decCBOR) 9 -> Just $ - mapSparseFieldGuarded - name - (emptyFailure "Mint" "non-empty") - (== mempty) - (mintDijkstraTxBodyRawL .~) - decCBOR - acc - 11 -> Just $ mapSparseFieldOptional (scriptIntegrityHashDijkstraTxBodyRawL .~) decCBOR acc + decodeAccA acc (mintDijkstraTxBodyRawL .~) $ + pure <$> do + x <- decCBOR + failOnMempty x $ emptyNamedFailure "Mint" "non-empty" + pure x + 11 -> Just $ decodeAccA acc (scriptIntegrityHashDijkstraTxBodyRawL .~) (pure . SJust <$> decCBOR) 13 | STopTx <- sTxLevel -> Just $ - mapSparseFieldGuarded - name - (emptyFailure "Collateral Inputs" "non-empty") - null - (collateralInputsDijkstraTxBodyRawL .~) - decCBOR - acc - -- Keys 14 and 22 are decoded with 'mapSparseField' directly. - -- Their lenses target plain values (@'OSet' (Credential Guard)@ - -- and @'Coin'@), so there's no 'StrictMaybe' to wrap or unwrap. - -- - -- The round-trip stays consistent and symmetric because - -- the encoder omits each key when its value equals the - -- default, and the decoder's initial accumulator (built by - -- 'basicDijkstraTxBodyRaw') starts each field at that same - -- default. - -- - -- For example: - -- * Key 14 — encoder: @Omit null (Key 14 (To dtbrGuards))@. - -- The key is in the serialised bits only when @dtbrGuards@ - -- is non-empty. The accumulator starts at @mempty@, so a - -- missing key keeps the field at @mempty@ — matching what - -- the sender skipped. - -- - -- * Key 22 — encoder: @Omit (== mempty) (Key 22 (To - -- dtbrTreasuryDonation))@. The key is in the serialised bits - -- only when the donation is non-zero. The accumulator starts - -- at @mempty@, so a missing key keeps the field at @zero@. + decodeAccA acc (collateralInputsDijkstraTxBodyRawL .~) $ + pure <$> do + x <- decCBOR + failOnNull x $ emptyNamedFailure "Collateral Inputs" "non-empty" + pure x 14 -> - Just $ mapSparseField (guardsDijkstraTxBodyRawL .~) decodeGuards acc - 15 -> Just $ mapSparseFieldOptional (networkIdDijkstraTxBodyRawL .~) decCBOR acc + -- plain field - initial accumulator already holds the omit-default + Just $ decodeAccA acc (guardsDijkstraTxBodyRawL .~) (pure <$> decodeGuards) + 15 -> Just $ decodeAccA acc (networkIdDijkstraTxBodyRawL .~) (pure . SJust <$> decCBOR) 16 | STopTx <- sTxLevel -> - Just $ mapSparseFieldOptional (collateralReturnDijkstraTxBodyRawL .~) decCBOR acc + Just $ decodeAccA acc (collateralReturnDijkstraTxBodyRawL .~) (pure . SJust <$> decCBOR) 17 | STopTx <- sTxLevel -> - Just $ mapSparseFieldOptional (totalCollateralDijkstraTxBodyRawL .~) decCBOR acc + Just $ decodeAccA acc (totalCollateralDijkstraTxBodyRawL .~) (pure . SJust <$> decCBOR) 18 -> Just $ - mapSparseFieldGuarded - name - (emptyFailure "Reference Inputs" "non-empty") - null - (referenceInputsDijkstraTxBodyRawL .~) - decCBOR - acc + decodeAccA acc (referenceInputsDijkstraTxBodyRawL .~) $ + pure <$> do + x <- decCBOR + failOnNull x $ emptyNamedFailure "Reference Inputs" "non-empty" + pure x 19 -> Just $ - mapSparseFieldGuarded - name - (emptyFailure "VotingProcedures" "non-empty") - (null . unVotingProcedures) - (votingProceduresDijkstraTxBodyRawL .~) - decCBOR - acc + decodeAccA acc (votingProceduresDijkstraTxBodyRawL .~) $ + pure <$> do + x <- decCBOR + failOnNull (unVotingProcedures x) $ emptyNamedFailure "VotingProcedures" "non-empty" + pure x 20 -> Just $ - mapSparseFieldGuarded - name - (emptyFailure "ProposalProcedures" "non-empty") - OSet.null - (proposalProceduresDijkstraTxBodyRawL .~) - decCBOR - acc - 21 -> Just $ mapSparseFieldOptional (currentTreasuryValueDijkstraTxBodyRawL .~) decCBOR acc + decodeAccA acc (proposalProceduresDijkstraTxBodyRawL .~) $ + pure <$> do + x <- decCBOR + failOnNull x $ emptyNamedFailure "ProposalProcedures" "non-empty" + pure x + 21 -> Just $ decodeAccA acc (currentTreasuryValueDijkstraTxBodyRawL .~) (pure . SJust <$> decCBOR) 22 -> - -- See comment about field 14. + -- plain field - initial accumulator already holds the omit-default Just $ - mapSparseField - (treasuryDonationDijkstraTxBodyRawL .~) - (decodePositiveCoin $ emptyFailure "Treasury Donation" "non-zero") + decodeAccA acc + (treasuryDonationDijkstraTxBodyRawL .~) + (pure <$> decodePositiveCoin (emptyFailure "Treasury Donation" "non-zero")) 23 | STopTx <- sTxLevel -> - Just $ mapSparseFieldA (subTransactionsDijkstraTxBodyRawL .~) decodeSubTransactions acc + Just $ decodeAccA acc (subTransactionsDijkstraTxBodyRawL .~) decodeSubTransactions 24 | SSubTx <- sTxLevel -> Just $ - mapSparseFieldGuarded - name - (emptyFailure "RequiredTopLevelGuards" "non-empty") - Map.null - (requiredTopLevelGuardsDijkstraTxBodyRawL .~) - (decodeMap decCBOR (decodeNullStrictMaybe decCBOR)) - acc + decodeAccA acc (requiredTopLevelGuardsDijkstraTxBodyRawL .~) $ + pure <$> do + x <- decodeMap decCBOR (decodeNullStrictMaybe decCBOR) + failOnNull x $ emptyNamedFailure "RequiredTopLevelGuards" "non-empty" + pure x 25 -> Just $ - mapSparseFieldGuarded - name - (emptyFailure "DirectDeposits" "non-empty") - (null . unDirectDeposits) - (directDepositsDijkstraTxBodyRawL .~) - decCBOR - acc + decodeAccA acc (directDepositsDijkstraTxBodyRawL .~) $ + pure <$> do + x <- decCBOR + failOnNull (unDirectDeposits x) $ emptyNamedFailure "DirectDeposits" "non-empty" + pure x 26 -> Just $ - mapSparseFieldGuarded - name - (emptyFailure "AccountBalanceIntervals" "non-empty") - (null . unAccountBalanceIntervals) - (accountBalanceIntervalsDijkstraTxBodyRawL .~) - decCBOR - acc + decodeAccA acc (accountBalanceIntervalsDijkstraTxBodyRawL .~) $ + pure <$> do + x <- decCBOR + failOnNull (unAccountBalanceIntervals x) $ emptyNamedFailure "AccountBalanceIntervals" "non-empty" + pure x _ -> Nothing decodeSubTransactions :: Decoder s (Annotator (OMap TxId (Tx SubTx era))) decodeSubTransactions = diff --git a/libs/cardano-ledger-binary/CHANGELOG.md b/libs/cardano-ledger-binary/CHANGELOG.md index 49e39839fe1..c8b7a62a2ef 100644 --- a/libs/cardano-ledger-binary/CHANGELOG.md +++ b/libs/cardano-ledger-binary/CHANGELOG.md @@ -2,7 +2,7 @@ ## 1.9.0.0 -* Add `mapSparseField`, `mapSparseFieldA`, `mapSparseFieldOptional`, `mapSparseFieldGuarded` +* Add `decodeAccA`, `failOnNull`, `failOnMempty` * Add `decodeSparseKeyed` * Add `decodeIntegralRational` * Add `decodeNonEmptySetLikeEnforceNoDuplicates` diff --git a/libs/cardano-ledger-binary/src/Cardano/Ledger/Binary/Decoding/Decoder.hs b/libs/cardano-ledger-binary/src/Cardano/Ledger/Binary/Decoding/Decoder.hs index f8736795eda..cb13445bb7a 100644 --- a/libs/cardano-ledger-binary/src/Cardano/Ledger/Binary/Decoding/Decoder.hs +++ b/libs/cardano-ledger-binary/src/Cardano/Ledger/Binary/Decoding/Decoder.hs @@ -80,10 +80,9 @@ module Cardano.Ledger.Binary.Decoding.Decoder ( decodeListLikeEnforceNoDuplicates, decodeMapContents, decodeSparseKeyed, - mapSparseField, - mapSparseFieldA, - mapSparseFieldOptional, - mapSparseFieldGuarded, + decodeAccA, + failOnNull, + failOnMempty, -- **** Applicaitve decodeMapTraverse, @@ -1257,71 +1256,27 @@ decodeSparseKeyed name requiredFields initial decoderByKey = do {-# INLINE step #-} {-# INLINE decodeSparseKeyed #-} --- | Decode a value and apply it to a 'Functor'-wrapped accumulator --- (typically 'Annotator'). Intended for use inside the per-key handler --- of 'decodeSparseKeyed'. -mapSparseField :: - Functor ann => - -- | Setter that updates the underlying record with the decoded value. +-- | Decode a value and add it to the accumulator using the +-- `Applicative` instance. +decodeAccA :: + Applicative f => + -- | Applicative accumulator + f t -> + -- | Function to adds the decoded value to the accumulator (x -> t -> t) -> - -- | Decoder for the field value. - Decoder s x -> - -- | Current accumulator. - ann t -> - Decoder s (ann t) -mapSparseField setter dec acc = do - !x <- dec - pure $ setter x <$> acc -{-# INLINE mapSparseField #-} - --- | Like 'mapSparseField' but the decoder yields an --- 'Applicative'-wrapped value (e.g. @'Annotator' x@). -mapSparseFieldA :: - Applicative ann => - (x -> t -> t) -> - Decoder s (ann x) -> - ann t -> - Decoder s (ann t) -mapSparseFieldA setter dec acc = do + -- | Decoder of the Applicative value + Decoder s (f x) -> + Decoder s (f t) +decodeAccA acc setter dec = do !x <- dec pure $ setter <$> x <*> acc -{-# INLINE mapSparseFieldA #-} - --- | Like 'mapSparseField' but the setter expects a 'StrictMaybe' value. --- Useful when the underlying field is a @'Lens'' t ('StrictMaybe' x)@ --- and the key's absence implies 'SNothing' (already set by the initial --- accumulator). -mapSparseFieldOptional :: - Functor ann => - (StrictMaybe x -> t -> t) -> - Decoder s x -> - ann t -> - Decoder s (ann t) -mapSparseFieldOptional setter dec acc = do - !x <- dec - pure $ setter (SJust x) <$> acc -{-# INLINE mapSparseFieldOptional #-} - --- | Like 'mapSparseField' but reject the value when the predicate is --- satisfied. -mapSparseFieldGuarded :: - Functor ann => - -- | Type name; failure is prefixed with @name:@. - String -> - -- | Error message used when the predicate rejects the value. - String -> - -- | Predicate: 'True' means reject. - (x -> Bool) -> - (x -> t -> t) -> - Decoder s x -> - ann t -> - Decoder s (ann t) -mapSparseFieldGuarded name msg reject setter dec acc = do - !x <- dec - if reject x - then fail $ name <> ":" <> msg - else pure $ setter x <$> acc -{-# INLINE mapSparseFieldGuarded #-} +{-# INLINE decodeAccA #-} + +failOnNull :: (Foldable f, MonadFail m) => f a -> String -> m () +failOnNull f msg = when (null f) $ fail msg + +failOnMempty :: (Eq f, Monoid f, MonadFail m) => f -> String -> m () +failOnMempty f msg = when (f == mempty) $ fail msg -------------------------------------------------------------------------------- -- Time From 4d13e21e6287f4a76c9230fc6714313df82b7404 Mon Sep 17 00:00:00 2001 From: Aniket Deshpande Date: Fri, 22 May 2026 13:28:57 +0530 Subject: [PATCH 11/13] Fix TypeName in failing test --- .../impl/testlib/Test/Cardano/Ledger/Alonzo/Binary/Golden.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eras/alonzo/impl/testlib/Test/Cardano/Ledger/Alonzo/Binary/Golden.hs b/eras/alonzo/impl/testlib/Test/Cardano/Ledger/Alonzo/Binary/Golden.hs index 55ecb67b60d..6e52c34b0da 100644 --- a/eras/alonzo/impl/testlib/Test/Cardano/Ledger/Alonzo/Binary/Golden.hs +++ b/eras/alonzo/impl/testlib/Test/Cardano/Ledger/Alonzo/Binary/Golden.hs @@ -88,7 +88,7 @@ txWitsDecodingFailsOnInvalidField version validFields = DecoderErrorDeserialiseFailure lbl (DeserialiseFailure 1 "expected word") where lbl = Binary.label $ Proxy @(Annotator (TxWits era)) - typeName = show (typeRep (Proxy @(AlonzoTxWitsRaw era))) + typeName = show (typeRep (Proxy @(Annotator (AlonzoTxWitsRaw era)))) spec :: forall era. From d31f9552f273717a37d6e2d63cf043628886ca4f Mon Sep 17 00:00:00 2001 From: Aniket Deshpande Date: Mon, 25 May 2026 16:27:24 +0530 Subject: [PATCH 12/13] Remove SRP; update index-state --- cabal.project | 9 +-------- flake.lock | 6 +++--- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/cabal.project b/cabal.project index a23063d9905..deb7b5dfc67 100644 --- a/cabal.project +++ b/cabal.project @@ -10,13 +10,6 @@ repository cardano-haskell-packages c00aae8461a256275598500ea0e187588c35a5d5d7454fb57eac18d9edb86a56 d4a35cd3121aa00d18544bb0ac01c3e1691d618f462c46129271bccf39f7e8ee -source-repository-package - type: git - location: https://github.com/IntersectMBO/cardano-base.git - subdir: cardano-base - --sha256: sha256-Gj8BPEew+UYkCWr3D0LgA6dvhYof49V20XRMRMf1FzY= - tag: efe33cb3739f5c90b733b730bdeffa11a75f291d - source-repository-package type: git location: https://github.com/IntersectMBO/formal-ledger-specifications.git @@ -35,7 +28,7 @@ source-repository-package -- see CONTRIBUTING.md#to-update-the-referenced-agda-ledger-spec index-state: , hackage.haskell.org 2026-05-06T14:09:41Z - , cardano-haskell-packages 2026-04-11T06:29:42Z + , cardano-haskell-packages 2026-05-22T05:43:37Z packages: -- == Byron era == diff --git a/flake.lock b/flake.lock index 2741b7d461f..2b204fd3c27 100644 --- a/flake.lock +++ b/flake.lock @@ -3,11 +3,11 @@ "CHaP": { "flake": false, "locked": { - "lastModified": 1775892872, - "narHash": "sha256-8qwpclExAZYF5e35xqt9yQYcal3FJLirKNIMiomAIvs=", + "lastModified": 1779431600, + "narHash": "sha256-BveisGfAV1GBrwP83S5w0D7B1t3VPaoHGGlRcnHYN5E=", "owner": "intersectmbo", "repo": "cardano-haskell-packages", - "rev": "86660ad10909f521a9c42cb01e9626aefd3903cf", + "rev": "8479db771a3186eb326e42d8480eddc20a208275", "type": "github" }, "original": { From 7026d3748e930eba568d9ec8d9860b1cec7c7e86 Mon Sep 17 00:00:00 2001 From: Aniket Deshpande Date: Mon, 25 May 2026 16:27:44 +0530 Subject: [PATCH 13/13] Rm redundant argument; add spaces in error reports --- .../impl/src/Cardano/Ledger/Alonzo/TxAuxData.hs | 12 ++++++------ .../Test/Cardano/Ledger/Alonzo/Binary/Golden.hs | 2 +- .../src/Cardano/Ledger/Binary/Decoding/Decoder.hs | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/eras/alonzo/impl/src/Cardano/Ledger/Alonzo/TxAuxData.hs b/eras/alonzo/impl/src/Cardano/Ledger/Alonzo/TxAuxData.hs index 981a2e7ef3b..f84e6ec929b 100644 --- a/eras/alonzo/impl/src/Cardano/Ledger/Alonzo/TxAuxData.hs +++ b/eras/alonzo/impl/src/Cardano/Ledger/Alonzo/TxAuxData.hs @@ -260,16 +260,16 @@ instance (\scripts ad -> ad {atadrNativeScripts = atadrNativeScripts ad <> scripts}) <$> x <*> acc - 2 -> decodeAddPlutus PlutusV1 acc - 3 -> decodeAddPlutus PlutusV2 acc - 4 -> decodeAddPlutus PlutusV3 acc - 5 -> decodeAddPlutus PlutusV4 acc + 2 -> decodeAddPlutus PlutusV1 + 3 -> decodeAddPlutus PlutusV2 + 4 -> decodeAddPlutus PlutusV3 + 5 -> decodeAddPlutus PlutusV4 _ -> Nothing where - decodeAddPlutus lang accu = Just $ do + decodeAddPlutus lang = Just $ do guardPlutus lang !x <- decCBOR - pure $ addPlutusScripts lang x <$> accu + pure $ addPlutusScripts lang x <$> acc {-# INLINE decodeAddPlutus #-} {-# INLINE decoderByKey #-} diff --git a/eras/alonzo/impl/testlib/Test/Cardano/Ledger/Alonzo/Binary/Golden.hs b/eras/alonzo/impl/testlib/Test/Cardano/Ledger/Alonzo/Binary/Golden.hs index 6e52c34b0da..2a9f572c6c2 100644 --- a/eras/alonzo/impl/testlib/Test/Cardano/Ledger/Alonzo/Binary/Golden.hs +++ b/eras/alonzo/impl/testlib/Test/Cardano/Ledger/Alonzo/Binary/Golden.hs @@ -80,7 +80,7 @@ txWitsDecodingFailsOnInvalidField version validFields = lbl ( DeserialiseFailure (if n >= 24 then 3 else 2) $ if version >= natVersion @12 - then typeName <> ":Unknown field key " <> show n + then typeName <> ": Unknown field key " <> show n -- TODO fix the `occured` typo in the produced value else "An error occured while decoding (Int,Void) not a valid key:.\nError: " <> show n ) diff --git a/libs/cardano-ledger-binary/src/Cardano/Ledger/Binary/Decoding/Decoder.hs b/libs/cardano-ledger-binary/src/Cardano/Ledger/Binary/Decoding/Decoder.hs index cb13445bb7a..207186ad197 100644 --- a/libs/cardano-ledger-binary/src/Cardano/Ledger/Binary/Decoding/Decoder.hs +++ b/libs/cardano-ledger-binary/src/Cardano/Ledger/Binary/Decoding/Decoder.hs @@ -1216,7 +1216,7 @@ decodeSparseKeyed name requiredFields initial decoderByKey = do Just len -> defLoop Set.empty initial len Nothing -> indefLoop Set.empty initial let missing = - [ show n <> ":" <> show k + [ show n <> ": " <> show k | (k, n) <- requiredFields , not $ Set.member k seen ]