diff --git a/.changes/20260514_cardano_api_remove_legacy_txout_from_compat_and_experimental.yml b/.changes/20260514_cardano_api_remove_legacy_txout_from_compat_and_experimental.yml new file mode 100644 index 0000000000..9b06019656 --- /dev/null +++ b/.changes/20260514_cardano_api_remove_legacy_txout_from_compat_and_experimental.yml @@ -0,0 +1,20 @@ +project: cardano-api +pr: 1209 +kind: + - breaking +description: | + Remove legacy `TxOut ctx era` from the Compatible and Experimental APIs. + + `createCompatibleTx` now takes `[Exp.TxOut (ShelleyLedgerEra era)]` plus an explicit `Map L.DataHash (L.Data (ShelleyLedgerEra era))` for supplemental datums, mirroring how it already takes `Exp.TxCertificates` / `Exp.TxVotingProcedures` / `Exp.TxProposalProcedures`. The legacy `TxOut CtxTx era` bundled supplemental datums inside outputs; `Exp.TxOut` only carries the datum hash (the full datum lives in the witness set), so callers now thread them in explicitly. + + Deletes the legacy bridge helpers re-exported from `Cardano.Api.Experimental.Tx`: + + - `fromLegacyTxOut` + - `legacyDatumToDatum` + - `supplementalDatumFromLegacy` + - `toLedgerDatum` + - `DatumDecodingError` + + These existed solely to convert legacy `TxOut CtxTx era` / `TxOutDatum CtxTx era` values into the experimental world. With the Compatible API no longer accepting the legacy type, they had no remaining in-repo callers. They are not re-exported from top-level `Cardano.Api`. + + The legacy `TxOut ctx era` type itself is unchanged — `UTxO`, `Tx` body construction, fee/balancing, `LedgerState`, and Byron all still depend on it. diff --git a/cardano-api/src/Cardano/Api/Compatible/Tx.hs b/cardano-api/src/Cardano/Api/Compatible/Tx.hs index d95d684530..16c24eb9ef 100644 --- a/cardano-api/src/Cardano/Api/Compatible/Tx.hs +++ b/cardano-api/src/Cardano/Api/Compatible/Tx.hs @@ -37,6 +37,7 @@ import Cardano.Ledger.Alonzo.TxWits qualified as Alonzo import Cardano.Ledger.Api qualified as L import Cardano.Ledger.Core qualified as L +import Data.Map.Strict (Map) import Data.Map.Strict qualified as Map import Data.Maybe import Data.Maybe.Strict @@ -44,7 +45,6 @@ import Data.Monoid import Data.OSet.Strict (OSet) import Data.Sequence.Strict qualified as Seq import GHC.Exts (IsList (..)) -import GHC.Stack import Lens.Micro hiding (ix) data AnyProtocolUpdate era where @@ -72,14 +72,19 @@ createCompatibleTx :: forall era . ShelleyBasedEra era -> [TxIn] - -> [TxOut CtxTx era] + -> [Exp.TxOut (ShelleyLedgerEra era)] + -> Map L.DataHash (L.Data (ShelleyLedgerEra era)) + -- ^ Supplemental datums to include in the witness set. Use 'mempty' if + -- none are required. The legacy 'TxOut CtxTx era' bundled supplemental + -- datums inside outputs; 'Exp.TxOut' only carries the datum hash, so + -- callers thread the full datum bodies in here explicitly. -> Lovelace -- ^ Fee -> AnyProtocolUpdate era -> AnyVote era -> Exp.TxCertificates (ShelleyLedgerEra era) -> Either ProtocolParametersConversionError (Tx era) -createCompatibleTx sbe ins outs txFee' anyProtocolUpdate anyVote txCertificates' = +createCompatibleTx sbe ins outs extraDatums txFee' anyProtocolUpdate anyVote txCertificates' = shelleyBasedEraConstraints sbe $ do (updateTxBody, extraScriptWitnesses) <- case anyProtocolUpdate of @@ -186,7 +191,7 @@ createCompatibleTx sbe ins outs txFee' anyProtocolUpdate anyVote txCertificates' [ monoidForEraInEon era ( \aeo -> alonzoEraOnwardsConstraints aeo $ Endo $ do - let sData = convScriptData' sbe outs scriptWitnesses + let sData = convScriptData' sbe extraDatums scriptWitnesses let (datums, redeemers) = case sData of TxBodyScriptData _ ds rs -> (ds, rs) TxBodyNoScriptData -> (mempty, L.Redeemers mempty) @@ -223,25 +228,16 @@ convCertificates (Exp.TxCertificates cs) = convScriptData' :: ShelleyBasedEra era - -> [TxOut CtxTx era] + -> Map L.DataHash (L.Data (ShelleyLedgerEra era)) -> [(ScriptWitnessIndex, AnyWitness (ShelleyLedgerEra era))] -> TxBodyScriptData era -convScriptData' sbe txOuts' scriptWitnesses = +convScriptData' sbe extraDatums scriptWitnesses = caseShelleyToMaryOrAlonzoEraOnwards (const TxBodyNoScriptData) ( \w -> let redeemers = getAnyPlutusScriptWitnessRedeemerPointerMap w scriptWitnesses - datums = mconcat [getAnyWitnessScriptData wit | (_, wit) <- scriptWitnesses] - - supplementalDatums = - let ds = [d | TxOut _ _ (TxOutSupplementalDatum _ d) _ <- txOuts'] - in Alonzo.TxDats $ - fromList - [ (L.hashData d', d') - | d <- ds - , let d' = toAlonzoData d - ] + supplementalDatums = alonzoEraOnwardsConstraints w $ Alonzo.TxDats extraDatums in TxBodyScriptData w (datums <> supplementalDatums) redeemers ) sbe @@ -268,17 +264,16 @@ getAnyPlutusScriptWitnessRedeemerPointerMap w wits = ] createCommonTxBody - :: HasCallStack - => ShelleyBasedEra era + :: ShelleyBasedEra era -> [TxIn] - -> [TxOut ctx era] + -> [Exp.TxOut (ShelleyLedgerEra era)] -> Lovelace -> L.TxBody L.TopTx (ShelleyLedgerEra era) createCommonTxBody era ins outs txFee' = - let txIns' = map toShelleyTxIn ins - txOuts' = map (toShelleyTxOutAny era) outs - in shelleyBasedEraConstraints era $ - L.mkBasicTxBody + shelleyBasedEraConstraints era $ + let txIns' = map toShelleyTxIn ins + txOuts' = map (\(Exp.TxOut o) -> o) outs + in L.mkBasicTxBody & L.inputsTxBodyL .~ fromList txIns' & L.outputsTxBodyL diff --git a/cardano-api/src/Cardano/Api/Experimental/Tx.hs b/cardano-api/src/Cardano/Api/Experimental/Tx.hs index 2e8d487520..779934ad1f 100644 --- a/cardano-api/src/Cardano/Api/Experimental/Tx.hs +++ b/cardano-api/src/Cardano/Api/Experimental/Tx.hs @@ -158,12 +158,6 @@ module Cardano.Api.Experimental.Tx , setTxVotingProcedures , setTxWithdrawals - -- * Legacy Conversions - , DatumDecodingError (..) - , legacyDatumToDatum - , fromLegacyTxOut - , supplementalDatumFromLegacy - -- * TxBodyContent sub type , TxCertificates (..) , TxMintValue (..) diff --git a/cardano-api/src/Cardano/Api/Experimental/Tx/Internal/BodyContent/New.hs b/cardano-api/src/Cardano/Api/Experimental/Tx/Internal/BodyContent/New.hs index 7ef0d706a2..281a68ea50 100644 --- a/cardano-api/src/Cardano/Api/Experimental/Tx/Internal/BodyContent/New.hs +++ b/cardano-api/src/Cardano/Api/Experimental/Tx/Internal/BodyContent/New.hs @@ -69,12 +69,6 @@ module Cardano.Api.Experimental.Tx.Internal.BodyContent.New , extractWitnessableWithdrawals , extractWitnessableVotes , extractWitnessableProposals - - -- * Legacy conversions - , DatumDecodingError (..) - , legacyDatumToDatum - , fromLegacyTxOut - , supplementalDatumFromLegacy ) where @@ -117,7 +111,6 @@ import Cardano.Api.Plutus.Internal.Script ) import Cardano.Api.Plutus.Internal.Script qualified as OldScript import Cardano.Api.Plutus.Internal.ScriptData qualified as Api -import Cardano.Api.Pretty import Cardano.Api.Serialise.Cbor (serialiseToCBOR) import Cardano.Api.Tx.Internal.Body ( CtxTx @@ -126,7 +119,6 @@ import Cardano.Api.Tx.Internal.Body , toShelleyTxIn , toShelleyWithdrawal ) -import Cardano.Api.Tx.Internal.Output qualified as OldApi import Cardano.Api.Tx.Internal.Sign import Cardano.Api.Tx.Internal.TxMetadata import Cardano.Api.Value.Internal @@ -153,7 +145,6 @@ import Control.Monad import Data.Aeson (ToJSON (..), (.=)) import Data.Aeson qualified as Aeson import Data.ByteString.Base16 qualified as Base16 -import Data.ByteString.Short qualified as SBS import Data.Functor import Data.List qualified as List import Data.Map.Ordered.Strict (OMap) @@ -169,7 +160,6 @@ import Data.Set qualified as Set import Data.Text.Encoding qualified as Text import Data.Typeable (cast) import GHC.Exts (IsList (..)) -import GHC.Stack (HasCallStack) import Lens.Micro -- | Error that can occur when constructing an unsigned transaction. @@ -521,63 +511,6 @@ extractDatumsAndHashes TxOutDatumHash{} = Nothing extractDatumsAndHashes (TxOutSupplementalDatum h d) = Just (h, d) extractDatumsAndHashes (TxOutDatumInline h d) = Just (h, d) -hashableScriptDatumToDatumAndHash :: L.Era era => Api.HashableScriptData -> (L.DataHash, L.Data era) -hashableScriptDatumToDatumAndHash sd = - (Api.unScriptDataHash $ Api.hashScriptDataBytes sd, Api.toAlonzoData sd) - -legacyDatumToDatum - :: forall era. IsEra era => OldApi.TxOutDatum CtxTx era -> Maybe (Datum CtxTx (LedgerEra era)) -legacyDatumToDatum (OldApi.TxOutDatumHash _ h) = Just (TxOutDatumHash $ Api.unScriptDataHash h) -legacyDatumToDatum (OldApi.TxOutSupplementalDatum _ hd) = do - let (hash, d) = obtainCommonConstraints (useEra @era) $ hashableScriptDatumToDatumAndHash hd - Just (TxOutSupplementalDatum hash d) -legacyDatumToDatum (OldApi.TxOutDatumInline _ hd) = do - let (hash, d) = obtainCommonConstraints (useEra @era) $ hashableScriptDatumToDatumAndHash hd - Just (TxOutDatumInline hash d) -legacyDatumToDatum OldApi.TxOutDatumNone = Nothing - -fromLegacyTxOut - :: forall era - . HasCallStack - => IsEra era - => OldApi.TxOut CtxTx era - -> Either DatumDecodingError (TxOut (LedgerEra era), Map L.DataHash (L.Data (LedgerEra era))) -fromLegacyTxOut tOut@(OldApi.TxOut _ _ d _) = do - let o = OldApi.toShelleyTxOutAny (convert $ useEra @era) tOut - newDatum :: L.Datum (LedgerEra era) <- obtainCommonConstraints (useEra @era) $ toLedgerDatum d - let txOut = obtainCommonConstraints (useEra @era) $ TxOut $ o & L.datumTxOutL .~ newDatum - suppDats = obtainCommonConstraints (useEra @era) $ supplementalDatumFromLegacy d - return (txOut, suppDats) - --- | Extract supplemental datum data from a legacy 'TxOutDatum'. --- Returns 'mempty' for non-supplemental datums. -supplementalDatumFromLegacy - :: L.Era (LedgerEra era) - => OldApi.TxOutDatum CtxTx era - -> Map L.DataHash (L.Data (LedgerEra era)) -supplementalDatumFromLegacy (OldApi.TxOutSupplementalDatum _ h) = - let ledgerData = Api.toAlonzoData h - in fromList [(L.hashData ledgerData, ledgerData)] -supplementalDatumFromLegacy _ = mempty - -newtype DatumDecodingError = DatumDecodingError String - deriving (Show, Eq) - -instance Error DatumDecodingError where - prettyError (DatumDecodingError msg) = "Datum decoding error: " <> pshow msg - -toLedgerDatum - :: L.Era (LedgerEra era) - => OldApi.TxOutDatum CtxTx era -> Either DatumDecodingError (L.Datum (LedgerEra era)) -toLedgerDatum OldApi.TxOutDatumNone = Right L.NoDatum -toLedgerDatum (OldApi.TxOutDatumHash _ (Api.ScriptDataHash h)) = Right $ L.DatumHash h -toLedgerDatum (OldApi.TxOutSupplementalDatum _ h) = - Right $ L.DatumHash (Api.unScriptDataHash $ Api.hashScriptDataBytes h) -toLedgerDatum (OldApi.TxOutDatumInline _ h) = - case L.makeBinaryData $ SBS.toShort $ Api.getOriginalScriptDataBytes h of - Left e -> Left $ DatumDecodingError e - Right bd -> Right $ L.Datum bd - data TxInsReference era = TxInsReference [TxIn] (Set (Datum CtxTx era)) newtype TxTotalCollateral = TxTotalCollateral {unTxTotalCollateral :: L.Coin} diff --git a/cardano-api/test/cardano-api-test/Test/Cardano/Api/Experimental/Fee.hs b/cardano-api/test/cardano-api-test/Test/Cardano/Api/Experimental/Fee.hs index 6c6d57027c..398ca83c96 100644 --- a/cardano-api/test/cardano-api-test/Test/Cardano/Api/Experimental/Fee.hs +++ b/cardano-api/test/cardano-api-test/Test/Cardano/Api/Experimental/Fee.hs @@ -775,7 +775,7 @@ prop_createCompatibleTx_preserves_all_certs = H.property $ do let sbe = convert Exp.ConwayEra inputCerts = Exp.mkTxCertificates Exp.ConwayEra allCerts Api.ShelleyTx _ ledgerTx <- - H.evalEither $ createCompatibleTx sbe [] [] 0 (NoPParamsUpdate sbe) NoVotes inputCerts + H.evalEither $ createCompatibleTx sbe [] [] mempty 0 (NoPParamsUpdate sbe) NoVotes inputCerts let bodyCerts = ledgerTx ^. L.bodyTxL . L.certsTxBodyL Seq.length bodyCerts H.=== expectedCount