diff --git a/cardano-api/cardano-api.cabal b/cardano-api/cardano-api.cabal index cf9744630..e9d60457c 100644 --- a/cardano-api/cardano-api.cabal +++ b/cardano-api/cardano-api.cabal @@ -77,8 +77,6 @@ library Cardano.Api.Internal.Eon.ShelleyBasedEra Cardano.Api.Internal.Eras Cardano.Api.Internal.Error - Cardano.Api.Internal.Experimental.Eras - Cardano.Api.Internal.Experimental.Tx Cardano.Api.Internal.Fees Cardano.Api.Internal.Genesis Cardano.Api.Internal.GenesisParameters @@ -200,6 +198,15 @@ library Cardano.Api.Internal.Eon.ShelleyToMaryEra Cardano.Api.Internal.Eras.Case Cardano.Api.Internal.Eras.Core + Cardano.Api.Internal.Experimental.Eras + Cardano.Api.Internal.Experimental.Plutus.IndexedPlutusScriptWitness + Cardano.Api.Internal.Experimental.Plutus.Shim.LegacyScripts + Cardano.Api.Internal.Experimental.Plutus.Script + Cardano.Api.Internal.Experimental.Plutus.ScriptWitness + Cardano.Api.Internal.Experimental.Simple.Script + Cardano.Api.Internal.Experimental.Tx + Cardano.Api.Internal.Experimental.Witness.TxScriptWitnessRequirements + Cardano.Api.Internal.Experimental.Witness.AnyWitness Cardano.Api.Internal.Feature Cardano.Api.Internal.Governance.Actions.ProposalProcedure Cardano.Api.Internal.Governance.Actions.VotingProcedure @@ -248,6 +255,7 @@ library Cardano.Api.Internal.SerialiseUsing Cardano.Api.Internal.SpecialByron Cardano.Api.Internal.StakePoolMetadata + Cardano.Api.Internal.Tx.BuildTxWith Cardano.Api.Internal.Tx.UTxO Cardano.Api.Internal.TxIn Cardano.Api.Internal.TxMetadata diff --git a/cardano-api/gen/Test/Gen/Cardano/Api/Typed.hs b/cardano-api/gen/Test/Gen/Cardano/Api/Typed.hs index 15ca77016..01f8ca355 100644 --- a/cardano-api/gen/Test/Gen/Cardano/Api/Typed.hs +++ b/cardano-api/gen/Test/Gen/Cardano/Api/Typed.hs @@ -169,6 +169,7 @@ import qualified Data.ByteString.Base16 as Base16 import Data.Ratio (Ratio, (%)) import Data.String import Test.Gen.Cardano.Api.Hardcoded +import Data.Typeable import Data.Word (Word16, Word32, Word64) import GHC.Exts (IsList (..)) import GHC.Stack @@ -707,7 +708,7 @@ genTxWithdrawals = ] ) -genTxCertificates :: CardanoEra era -> Gen (TxCertificates BuildTx era) +genTxCertificates :: Typeable era => CardanoEra era -> Gen (TxCertificates BuildTx era) genTxCertificates = inEonForEra (pure TxCertificatesNone) @@ -720,7 +721,7 @@ genTxCertificates = ] ) -genCertificate :: forall era. ShelleyBasedEra era -> Gen (Certificate era) +genCertificate :: forall era. Typeable era => ShelleyBasedEra era -> Gen (Certificate era) genCertificate sbe = Gen.choice $ catMaybes @@ -871,7 +872,7 @@ genTxMintValue = , pure $ TxMintValue w (fromList assets) ] -genTxBodyContent :: ShelleyBasedEra era -> Gen (TxBodyContent BuildTx era) +genTxBodyContent :: Typeable era => ShelleyBasedEra era -> Gen (TxBodyContent BuildTx era) genTxBodyContent sbe = do let era = toCardanoEra sbe txIns <- @@ -992,7 +993,8 @@ genWitnessesByron = Gen.list (Range.constant 1 10) genByronKeyWitness -- | This generator validates generated 'TxBodyContent' and backtracks when the generated body -- fails the validation. That also means that it is quite slow. -genValidTxBody :: ShelleyBasedEra era +genValidTxBody :: Typeable era + => ShelleyBasedEra era -> Gen (TxBody era, TxBodyContent BuildTx era) -- ^ validated 'TxBody' and 'TxBodyContent' genValidTxBody sbe = Gen.mapMaybe @@ -1003,7 +1005,7 @@ genValidTxBody sbe = (genTxBodyContent sbe) -- | Partial! This function will throw an error when the generated transaction is invalid. -genTxBody :: HasCallStack => ShelleyBasedEra era -> Gen (TxBody era) +genTxBody :: (HasCallStack, Typeable era) => ShelleyBasedEra era -> Gen (TxBody era) genTxBody era = do res <- Api.createTransactionBody era <$> genTxBodyContent era case res of @@ -1042,7 +1044,7 @@ genScriptValidity :: Gen ScriptValidity genScriptValidity = Gen.element [ScriptInvalid, ScriptValid] genTx - :: () + :: Typeable era => ShelleyBasedEra era -> Gen (Tx era) genTx era = @@ -1050,7 +1052,7 @@ genTx era = <$> genWitnesses era <*> (fst <$> genValidTxBody era) -genWitnesses :: ShelleyBasedEra era -> Gen [KeyWitness era] +genWitnesses :: Typeable era => ShelleyBasedEra era -> Gen [KeyWitness era] genWitnesses sbe = do bsWits <- Gen.list (Range.constant 0 10) (genShelleyBootstrapWitness sbe) keyWits <- Gen.list (Range.constant 0 10) (genShelleyKeyWitness sbe) @@ -1095,7 +1097,7 @@ genWitnessNetworkIdOrByronAddress = ] genShelleyBootstrapWitness - :: () + :: Typeable era => ShelleyBasedEra era -> Gen (KeyWitness era) genShelleyBootstrapWitness sbe = @@ -1104,8 +1106,10 @@ genShelleyBootstrapWitness sbe = <*> (fst <$> genValidTxBody sbe) <*> genSigningKey AsByronKey + genShelleyKeyWitness :: () + => Typeable era => ShelleyBasedEra era -> Gen (KeyWitness era) genShelleyKeyWitness sbe = @@ -1114,7 +1118,7 @@ genShelleyKeyWitness sbe = <*> genShelleyWitnessSigningKey genShelleyWitness - :: () + :: Typeable era => ShelleyBasedEra era -> Gen (KeyWitness era) genShelleyWitness sbe = @@ -1135,7 +1139,7 @@ genShelleyWitnessSigningKey = ] genCardanoKeyWitness - :: () + :: Typeable era => ShelleyBasedEra era -> Gen (KeyWitness era) genCardanoKeyWitness = genShelleyWitness diff --git a/cardano-api/src/Cardano/Api/Internal/Eon/AlonzoEraOnwards.hs b/cardano-api/src/Cardano/Api/Internal/Eon/AlonzoEraOnwards.hs index c936271a9..988f0ced3 100644 --- a/cardano-api/src/Cardano/Api/Internal/Eon/AlonzoEraOnwards.hs +++ b/cardano-api/src/Cardano/Api/Internal/Eon/AlonzoEraOnwards.hs @@ -29,6 +29,7 @@ import Cardano.Binary import Cardano.Crypto.Hash.Blake2b qualified as Blake2b import Cardano.Crypto.Hash.Class qualified as C import Cardano.Crypto.VRF qualified as C +import Cardano.Ledger.Allegra.Scripts qualified as L import Cardano.Ledger.Alonzo.Plutus.Context qualified as Plutus import Cardano.Ledger.Alonzo.Scripts qualified as L import Cardano.Ledger.Alonzo.Tx qualified as L @@ -103,6 +104,7 @@ type AlonzoEraOnwardsConstraints era = , L.EraUTxO (ShelleyLedgerEra era) , L.HashAnnotated (L.TxBody (ShelleyLedgerEra era)) L.EraIndependentTxBody L.StandardCrypto , L.MaryEraTxBody (ShelleyLedgerEra era) + , L.NativeScript (ShelleyLedgerEra era) ~ L.Timelock (ShelleyLedgerEra era) , Plutus.EraPlutusContext (ShelleyLedgerEra era) , L.Script (ShelleyLedgerEra era) ~ L.AlonzoScript (ShelleyLedgerEra era) , L.ScriptsNeeded (ShelleyLedgerEra era) ~ L.AlonzoScriptsNeeded (ShelleyLedgerEra era) diff --git a/cardano-api/src/Cardano/Api/Internal/Experimental/Eras.hs b/cardano-api/src/Cardano/Api/Internal/Experimental/Eras.hs index 66bcd5ca3..e5390f08d 100644 --- a/cardano-api/src/Cardano/Api/Internal/Experimental/Eras.hs +++ b/cardano-api/src/Cardano/Api/Internal/Experimental/Eras.hs @@ -32,6 +32,7 @@ module Cardano.Api.Internal.Experimental.Eras ) where +import Cardano.Api.Internal.Eon.AlonzoEraOnwards import Cardano.Api.Internal.Eon.BabbageEraOnwards import Cardano.Api.Internal.Eon.Convert import Cardano.Api.Internal.Eon.ShelleyBasedEra (ShelleyBasedEra (..), ShelleyLedgerEra) @@ -206,6 +207,11 @@ instance Convert Era ShelleyBasedEra where BabbageEra -> ShelleyBasedEraBabbage ConwayEra -> ShelleyBasedEraConway +instance Convert Era AlonzoEraOnwards where + convert = \case + BabbageEra -> AlonzoEraOnwardsBabbage + ConwayEra -> AlonzoEraOnwardsConway + instance Convert Era BabbageEraOnwards where convert = \case BabbageEra -> BabbageEraOnwardsBabbage diff --git a/cardano-api/src/Cardano/Api/Internal/Experimental/Plutus/IndexedPlutusScriptWitness.hs b/cardano-api/src/Cardano/Api/Internal/Experimental/Plutus/IndexedPlutusScriptWitness.hs new file mode 100644 index 000000000..cf17c470d --- /dev/null +++ b/cardano-api/src/Cardano/Api/Internal/Experimental/Plutus/IndexedPlutusScriptWitness.hs @@ -0,0 +1,91 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE KindSignatures #-} + +module Cardano.Api.Internal.Experimental.Plutus.IndexedPlutusScriptWitness + ( -- * Constuct an indexed plutus script witness. + AnyIndexedPlutusScriptWitness (..) + , IndexedPlutusScriptWitness (..) + , createIndexedPlutusScriptWitnesses + , getAnyWitnessRedeemerPointerMap + ) +where + +import Cardano.Api.Internal.Eon.AlonzoEraOnwards +import Cardano.Api.Internal.Eon.ShelleyBasedEra +import Cardano.Api.Internal.Experimental.Plutus.ScriptWitness +import Cardano.Api.Internal.Experimental.Witness.AnyWitness +import Cardano.Api.Internal.Script (toAlonzoExUnits) +import Cardano.Api.Internal.ScriptData +import Cardano.Api.Ledger qualified as L + +import Cardano.Ledger.Alonzo.TxWits qualified as L + +import Data.Word +import GHC.Exts + +-- | A Plutus script witness along the thing it is witnessing and the index of that thing. +-- E.g transaction input, certificate, withdrawal, minting policy, etc. +-- A Plutus script witness only makes sense in the context of what it is witnessing +-- and the index of the thing it is witnessing. +data IndexedPlutusScriptWitness witnessable (lang :: L.Language) (purpose :: PlutusScriptPurpose) era where + IndexedPlutusScriptWitness + :: Witnessable witnessable era + -> (L.PlutusPurpose L.AsIx era) + -> (PlutusScriptWitness lang purpose era) + -> IndexedPlutusScriptWitness witnessable lang purpose era + +data AnyIndexedPlutusScriptWitness era where + AnyIndexedPlutusScriptWitness + :: GetPlutusScriptPurpose era + => IndexedPlutusScriptWitness witnessable lang purpose era + -> AnyIndexedPlutusScriptWitness era + +createIndexedPlutusScriptWitness + :: Word32 + -> Witnessable witnessable era + -> PlutusScriptWitness lang purpose era + -> IndexedPlutusScriptWitness witnessable lang purpose era +createIndexedPlutusScriptWitness index witnessable = + IndexedPlutusScriptWitness witnessable (toPlutusScriptPurpose index witnessable) + +createIndexedPlutusScriptWitnesses + :: [(Witnessable witnessable era, AnyWitness era)] + -> [AnyIndexedPlutusScriptWitness era] +createIndexedPlutusScriptWitnesses witnessableThings = + [ AnyIndexedPlutusScriptWitness $ createIndexedPlutusScriptWitness index thing sWit + | (index, (thing, AnyPlutusScriptWitness sWit)) <- zip [0 ..] witnessableThings + ] + +-- | The transaction's redeemer pointer map allows the ledger to connect a redeemer and execution unit pairing to the relevant +-- script. The ledger basically reconstructs the indicies (redeemer pointers) of this map can then look up the relevant +-- execution units/redeemer pairing. NB the redeemer pointer has been renamed to 'PlutusPurpose AsIndex' in the ledger. +getAnyWitnessRedeemerPointerMap + :: AlonzoEraOnwards era + -> (Witnessable witnessable (ShelleyLedgerEra era), AnyWitness (ShelleyLedgerEra era)) + -> L.Redeemers (ShelleyLedgerEra era) +getAnyWitnessRedeemerPointerMap eon (_, AnyKeyWitness) = alonzoEraOnwardsConstraints eon mempty +getAnyWitnessRedeemerPointerMap eon (_, AnySimpleScriptWitness{}) = alonzoEraOnwardsConstraints eon mempty +getAnyWitnessRedeemerPointerMap eon anyWit = + constructRedeeemerPointerMap eon $ + createIndexedPlutusScriptWitnesses [anyWit] + +-- | An 'IndexedPlutusScriptWitness' contains everything we need to construct a single +-- entry in the redeemer pointer map. +constructRedeemerPointer + :: AlonzoEraOnwards era + -> AnyIndexedPlutusScriptWitness (ShelleyLedgerEra era) + -> L.Redeemers (ShelleyLedgerEra era) +constructRedeemerPointer eon (AnyIndexedPlutusScriptWitness (IndexedPlutusScriptWitness _ purpose scriptWit)) = + let PlutusScriptWitness _ _ _ redeemer execUnits = scriptWit + in alonzoEraOnwardsConstraints eon $ + L.Redeemers $ + fromList [(purpose, (toAlonzoData redeemer, toAlonzoExUnits execUnits))] + +constructRedeeemerPointerMap + :: AlonzoEraOnwards era + -> [AnyIndexedPlutusScriptWitness ((ShelleyLedgerEra era))] + -> L.Redeemers (ShelleyLedgerEra era) +constructRedeeemerPointerMap eon scriptWits = + let redeemerPointers = map (constructRedeemerPointer eon) scriptWits + in alonzoEraOnwardsConstraints eon $ mconcat redeemerPointers diff --git a/cardano-api/src/Cardano/Api/Internal/Experimental/Plutus/Script.hs b/cardano-api/src/Cardano/Api/Internal/Experimental/Plutus/Script.hs new file mode 100644 index 000000000..9bc87d920 --- /dev/null +++ b/cardano-api/src/Cardano/Api/Internal/Experimental/Plutus/Script.hs @@ -0,0 +1,45 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE StandaloneDeriving #-} + +module Cardano.Api.Internal.Experimental.Plutus.Script + ( PlutusScriptInEra (..) + , PlutusScriptOrReferenceInput (..) + ) +where + +import Cardano.Api.Internal.TxIn (TxIn) + +import Cardano.Ledger.Plutus.Language (PlutusRunnable) +import Cardano.Ledger.Plutus.Language qualified as L + +-- | A Plutus script in a particular era. +-- Why PlutusRunnable? Mainly for deserialization benefits. +-- The deserialization of this type looks at the +-- major protocol version and the script language to determine if +-- indeed the script is runnable. This is a dramatic improvement over the old api +-- which essentially read a 'ByteString' and hoped for the best. +-- Any failures due to malformed/invalid scripts were caught upon transaction +-- submission or running the script when attempting to predict the necessary execution units. +-- +-- Where do we get the major protocol version from? +-- In order to access the major protocol version we pass in an 'era` type parameter which +-- can be translated to the major protocol version. +-- +-- Where do we get the script language from? +-- The serialized version of 'PlutusRunnable' encodes the script language. +-- See `DecCBOR (PlutusRunnable l)` in cardano-ledger for more details. +data PlutusScriptInEra (lang :: L.Language) era where + PlutusScriptInEra :: PlutusRunnable lang -> PlutusScriptInEra lang era + +deriving instance Show (PlutusScriptInEra lang era) + +-- | You can provide the plutus script directly in the transaction +-- or a reference input that points to the script in the UTxO. +-- Using a reference script saves space in your transaction. +data PlutusScriptOrReferenceInput lang era + = PScript (PlutusScriptInEra lang era) + | PReferenceScript TxIn + deriving Show diff --git a/cardano-api/src/Cardano/Api/Internal/Experimental/Plutus/ScriptWitness.hs b/cardano-api/src/Cardano/Api/Internal/Experimental/Plutus/ScriptWitness.hs new file mode 100644 index 000000000..c6db0707b --- /dev/null +++ b/cardano-api/src/Cardano/Api/Internal/Experimental/Plutus/ScriptWitness.hs @@ -0,0 +1,226 @@ +{-# LANGUAGE AllowAmbiguousTypes #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE PolyKinds #-} +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE StandaloneDeriving #-} +{-# LANGUAGE TypeFamilyDependencies #-} +{-# LANGUAGE UndecidableInstances #-} + +module Cardano.Api.Internal.Experimental.Plutus.ScriptWitness + ( PlutusScriptWitness (..) + + -- * Plutus script witnessable things. + , TxIn + , Mint + , Withdrawal + , Cert + , Voter + , Proposal + + -- * Witnessable things + , Witnessable (..) + + -- * Constructing a plutus script witness. + , GetPlutusScriptPurpose (..) + , PlutusScriptOrReferenceInput (..) + , ScriptRedeemer + , PlutusScriptPurpose (..) + , PlutusScriptDatum (..) + , NoScriptDatum (..) + , mkPlutusScriptWitness + + -- * Helpers + , obtainAlonzoScriptPurposeConstraints + , getPlutusScriptWitnessLanguage + ) +where + +import Cardano.Api.Internal.Address +import Cardano.Api.Internal.Certificate +import Cardano.Api.Internal.Eon.AlonzoEraOnwards +import Cardano.Api.Internal.Eon.ShelleyBasedEra +import Cardano.Api.Internal.Experimental.Plutus.Script +import Cardano.Api.Internal.Governance.Actions.ProposalProcedure qualified as Api +import Cardano.Api.Internal.Governance.Actions.VotingProcedure qualified as Api +import Cardano.Api.Internal.Script (ExecutionUnits) +import Cardano.Api.Internal.ScriptData +import Cardano.Api.Internal.TxIn +import Cardano.Api.Internal.Value +import Cardano.Api.Ledger qualified as L + +import Cardano.Ledger.Conway.Scripts qualified as L +import Cardano.Ledger.Plutus.Language qualified as L + +import Data.Word + +{- +To construct a plutus script witness you need: +1. The plutus script or reference input +2. A redeemer +3. The thing being witnessed + +This is true regardless of the plutus script version. + +-} + +type ScriptRedeemer = HashableScriptData + +-- | This is a Plutus script witness. It possesses: +-- 1. The plutus script or reference input +-- 2. The script redeemer +-- 3. The execution units +-- 4. Potentially a script datum. See the PlutusScriptDatum type family for more details. +-- +-- Note that Plutus script witnesses do not exist on their own. They must witness something +-- and a redeemer pointer must be constucted to point to the thing being witnessed. +-- See 'IndexedPlutusScriptWitness' for more details. +data PlutusScriptWitness (lang :: L.Language) (purpose :: PlutusScriptPurpose) era where + PlutusScriptWitness + :: L.SLanguage lang + -> (PlutusScriptOrReferenceInput lang era) + -> (PlutusScriptDatum lang purpose) + -> ScriptRedeemer + -> ExecutionUnits + -> PlutusScriptWitness lang purpose era + +getPlutusScriptWitnessLanguage :: PlutusScriptWitness lang purpose era -> L.Language +getPlutusScriptWitnessLanguage (PlutusScriptWitness l _ _ _ _) = + case l of + L.SPlutusV1 -> L.plutusLanguage l + L.SPlutusV2 -> L.plutusLanguage l + L.SPlutusV3 -> L.plutusLanguage l + +-- | Every Plutus script has a purpose that indicates +-- what that script is witnessing. +data PlutusScriptPurpose + = -- | Witnesses a transaction input + SpendingScript + | -- | Witnesses a minting policy + MintingScript + | -- | Witnesses a withdrawal + WithdrawingScript + | -- | Witnesses a certificate + CertifyingScript + | -- | Witnesses a proposal + ProposingScript + | -- | Witnesses a vote + VotingScript + +data NoScriptDatum = NoScriptDatumAllowed deriving Show + +-- | The PlutusScriptDatum type family is used to determine if a script datum is allowed +-- for a given plutus script purpose and version. This change was proposed in CIP-69 +-- https://github.com/cardano-foundation/CIPs/tree/master/CIP-0069 +type family PlutusScriptDatumF (lang :: L.Language) (purpose :: PlutusScriptPurpose) where + PlutusScriptDatumF L.PlutusV1 SpendingScript = HashableScriptData + PlutusScriptDatumF L.PlutusV2 SpendingScript = HashableScriptData + PlutusScriptDatumF L.PlutusV3 SpendingScript = Maybe HashableScriptData -- CIP-69 + PlutusScriptDatumF L.PlutusV1 MintingScript = NoScriptDatum + PlutusScriptDatumF L.PlutusV2 MintingScript = NoScriptDatum + PlutusScriptDatumF L.PlutusV3 MintingScript = NoScriptDatum + PlutusScriptDatumF L.PlutusV1 WithdrawingScript = NoScriptDatum + PlutusScriptDatumF L.PlutusV2 WithdrawingScript = NoScriptDatum + PlutusScriptDatumF L.PlutusV3 WithdrawingScript = NoScriptDatum + PlutusScriptDatumF L.PlutusV1 CertifyingScript = NoScriptDatum + PlutusScriptDatumF L.PlutusV2 CertifyingScript = NoScriptDatum + PlutusScriptDatumF L.PlutusV3 CertifyingScript = NoScriptDatum + PlutusScriptDatumF L.PlutusV1 ProposingScript = NoScriptDatum + PlutusScriptDatumF L.PlutusV2 ProposingScript = NoScriptDatum + PlutusScriptDatumF L.PlutusV3 ProposingScript = NoScriptDatum + PlutusScriptDatumF L.PlutusV1 VotingScript = NoScriptDatum + PlutusScriptDatumF L.PlutusV2 VotingScript = NoScriptDatum + PlutusScriptDatumF L.PlutusV3 VotingScript = NoScriptDatum + +data PlutusScriptDatum (lang :: L.Language) (purpose :: PlutusScriptPurpose) where + SpendingScriptDatum + :: PlutusScriptDatumF lang SpendingScript -> PlutusScriptDatum lang SpendingScript + InlineDatum :: PlutusScriptDatum lang purpose + NoScriptDatum + :: PlutusScriptDatum lang purpose + +instance Show (PlutusScriptDatum lang purpose) where + show = \case + SpendingScriptDatum _d -> "Datum" + InlineDatum -> "InlineDatum" + NoScriptDatum -> "NoScriptDatum" + +-- | These are all of the "things" a plutus script can witness. We include the relevant +-- type class constraint to avoid boilerplate when creating the 'PlutusPurpose' in +-- the 'GetPlutusScriptPurpose' instances. +data Witnessable thing era where + WitTxIn :: L.AlonzoEraScript era => TxIn -> Witnessable TxIn era + WitTxCert :: L.AlonzoEraScript era => Cert -> Witnessable Cert era + WitMint :: L.AlonzoEraScript era => Mint -> Witnessable Mint era + WitWithdrawal + :: L.AlonzoEraScript era => Withdrawal -> Witnessable Withdrawal era + WitVote + :: L.ConwayEraScript era + => Voter -> Witnessable Voter era + WitProposal :: L.ConwayEraScript era => Proposal -> Witnessable Proposal era + +deriving instance Show (Witnessable thing era) + +deriving instance Eq (Witnessable thing era) + +type Mint = (PolicyId, AssetName, Quantity) + +type Withdrawal = (StakeAddress, L.Coin) + +type Cert = (AnyCertificate, StakeCredential) + +type Voter = Api.AnyVoter + +type Proposal = Api.AnyProposal + +-- | To reduce boilerplate, we reuse the `PlutusPurpose` type from `cardano-ledger`. +-- This type is utilized in constructing the redeemer pointers map, which +-- links the redeemer and execution units with the entity being witnessed. +-- The map is indexed by the redeemer pointer. +-- +-- A natural question arises: How do Plutus scripts determine which +-- execution units and redeemer are paired with them? The ledger constructs a redeemer pointer +-- for every Plutus script, and this pointer corresponds to the one in the transaction's +-- redeemer pointers map. For more details, refer to `collectPlutusScriptsWithContext` +-- in `cardano-ledger`. +class GetPlutusScriptPurpose era where + toPlutusScriptPurpose + :: Word32 + -> Witnessable thing era + -> L.PlutusPurpose L.AsIx era + +instance GetPlutusScriptPurpose era where + toPlutusScriptPurpose index WitTxIn{} = L.mkSpendingPurpose (L.AsIx index) + toPlutusScriptPurpose index WitWithdrawal{} = L.mkRewardingPurpose (L.AsIx index) + toPlutusScriptPurpose index WitMint{} = L.mkMintingPurpose (L.AsIx index) + toPlutusScriptPurpose index WitTxCert{} = L.mkCertifyingPurpose (L.AsIx index) + toPlutusScriptPurpose index WitVote{} = L.mkVotingPurpose (L.AsIx index) + toPlutusScriptPurpose index WitProposal{} = L.mkProposingPurpose (L.AsIx index) + +obtainAlonzoScriptPurposeConstraints + :: AlonzoEraOnwards era + -> ((GetPlutusScriptPurpose era, L.AlonzoEraScript (ShelleyLedgerEra era)) => a) + -> a +obtainAlonzoScriptPurposeConstraints v = + case v of + AlonzoEraOnwardsAlonzo -> id + AlonzoEraOnwardsBabbage -> id + AlonzoEraOnwardsConway -> id + +mkPlutusScriptWitness + :: AlonzoEraOnwards era + -> L.SLanguage plutuslang + -> L.PlutusRunnable plutuslang + -> PlutusScriptDatum plutuslang purpose + -> ScriptRedeemer + -> ExecutionUnits + -> PlutusScriptWitness plutuslang purpose (ShelleyLedgerEra era) +mkPlutusScriptWitness _ l plutusScriptRunnable = + PlutusScriptWitness + l + (PScript $ PlutusScriptInEra plutusScriptRunnable) diff --git a/cardano-api/src/Cardano/Api/Internal/Experimental/Plutus/Shim/LegacyScripts.hs b/cardano-api/src/Cardano/Api/Internal/Experimental/Plutus/Shim/LegacyScripts.hs new file mode 100644 index 000000000..1872b40b4 --- /dev/null +++ b/cardano-api/src/Cardano/Api/Internal/Experimental/Plutus/Shim/LegacyScripts.hs @@ -0,0 +1,222 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE InstanceSigs #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE PolyKinds #-} +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE TypeFamilyDependencies #-} + +module Cardano.Api.Internal.Experimental.Plutus.Shim.LegacyScripts + ( legacyWitnessToScriptRequirements + ) +where + +import Cardano.Api.Internal.Eon.AlonzoEraOnwards +import Cardano.Api.Internal.Eon.ShelleyBasedEra +import Cardano.Api.Internal.Experimental.Plutus.Script +import Cardano.Api.Internal.Experimental.Plutus.ScriptWitness +import Cardano.Api.Internal.Experimental.Simple.Script +import Cardano.Api.Internal.Experimental.Witness.AnyWitness +import Cardano.Api.Internal.Experimental.Witness.TxScriptWitnessRequirements +import Cardano.Api.Internal.Pretty +import Cardano.Api.Internal.Script + ( ExecutionUnits + , Witness + ) +import Cardano.Api.Internal.Script qualified as Old +import Cardano.Api.Internal.Tx.BuildTxWith + +import Cardano.Binary qualified as CBOR +import Cardano.Ledger.Alonzo.Scripts qualified as L +import Cardano.Ledger.BaseTypes (Version) +import Cardano.Ledger.Core qualified as L +import Cardano.Ledger.Plutus.Language qualified as L + +import Data.Text qualified as Text + +-- | This module is concerned with converting legacy api scripts and by extension +-- script witnesses to the new api. + +-- Remember we don't care about simple script witnesses beyond the fact that they require key witnesses +-- and therefore contribute to the determination of the script witness index. +fromExistingApi + :: AlonzoEraOnwards era + -> (Witnessable thing (ShelleyLedgerEra era), BuildTxWith BuildTx (Witness witctx era)) + -> Either + CBOR.DecoderError + (Witnessable thing (ShelleyLedgerEra era), AnyWitness (ShelleyLedgerEra era)) +fromExistingApi _ (witnessable, BuildTxWith (Old.KeyWitness _)) = + return (witnessable, AnyKeyWitness) +fromExistingApi _ (witnessable, BuildTxWith (Old.ScriptWitness _ Old.SimpleScriptWitness{})) = + return (witnessable, AnyKeyWitness) +fromExistingApi eon (witnessable, BuildTxWith (Old.ScriptWitness _ oldApiPlutusScriptWitness)) = + convertToNewPlutusScriptWitness eon oldApiPlutusScriptWitness witnessable + +type family ToPlutusScriptPurpose witnessable = (purpose :: PlutusScriptPurpose) | purpose -> witnessable where + ToPlutusScriptPurpose TxIn = SpendingScript + ToPlutusScriptPurpose Mint = MintingScript + ToPlutusScriptPurpose Cert = CertifyingScript + ToPlutusScriptPurpose Withdrawal = WithdrawingScript + ToPlutusScriptPurpose Proposal = ProposingScript + ToPlutusScriptPurpose Voter = VotingScript + +convertToNewPlutusScriptWitness + :: AlonzoEraOnwards era + -> Old.ScriptWitness witctx era + -> Witnessable thing (ShelleyLedgerEra era) + -> Either + CBOR.DecoderError + (Witnessable thing (ShelleyLedgerEra era), AnyWitness (ShelleyLedgerEra era)) +convertToNewPlutusScriptWitness eon (Old.PlutusScriptWitness _ v scriptOrRefInput datum scriptRedeemer execUnits) witnessable = do + let d = createDatum witnessable v datum + newScriptWitness <- + obtainConstraints v $ + toNewPlutusScriptWitness + eon + v + scriptOrRefInput + scriptRedeemer + execUnits + d + return (witnessable, newScriptWitness) +convertToNewPlutusScriptWitness eon (Old.SimpleScriptWitness _ scriptOrRefInput) witnessable = + case scriptOrRefInput of + Old.SScript simpleScript -> do + let timelock = convertTotimelock eon simpleScript + return (witnessable, AnySimpleScriptWitness $ SScript $ SimpleScript timelock) + Old.SReferenceScript txIn -> + return (witnessable, AnySimpleScriptWitness $ SReferenceScript txIn) + +convertTotimelock + :: AlonzoEraOnwards era -> Old.SimpleScript -> L.NativeScript (ShelleyLedgerEra era) +convertTotimelock eon s = alonzoEraOnwardsConstraints eon $ Old.toAllegraTimelock s + +createDatum + :: Witnessable thing era + -> Old.PlutusScriptVersion lang + -> Old.ScriptDatum witctx + -> PlutusScriptDatum (Old.ToLedgerPlutusLanguage lang) SpendingScript +createDatum missingContext plutusVersion oldDatum = + case (missingContext, oldDatum) of + (w@WitTxIn{}, d@Old.ScriptDatumForTxIn{}) -> toCip69Datum w plutusVersion d + (WitTxIn{}, Old.InlineScriptDatum) -> NoScriptDatum + (WitTxIn{}, _) -> + error $ + unlines + [ "createDatum: invalid combination of witnessable and script datum" + , "Witnessable: " ++ show missingContext + , "Script Datum: " ++ show oldDatum + ] + (WitMint{}, _) -> NoScriptDatum + (WitWithdrawal{}, _) -> NoScriptDatum + (WitProposal{}, _) -> NoScriptDatum + (WitVote{}, _) -> NoScriptDatum + (WitTxCert{}, _) -> NoScriptDatum + +toCip69Datum + :: Witnessable TxIn era + -> Old.PlutusScriptVersion lang + -> Old.ScriptDatum Old.WitCtxTxIn + -> PlutusScriptDatum (Old.ToLedgerPlutusLanguage lang) (ToPlutusScriptPurpose TxIn) +-- ^ Encapsulates CIP-69: V3 spending script datums are optional +toCip69Datum WitTxIn{} Old.PlutusScriptV3 (Old.ScriptDatumForTxIn r) = SpendingScriptDatum r +-- \^ V2 and V1 spending script datums are required +toCip69Datum WitTxIn{} Old.PlutusScriptV2 (Old.ScriptDatumForTxIn (Just r)) = SpendingScriptDatum r +toCip69Datum WitTxIn{} Old.PlutusScriptV1 (Old.ScriptDatumForTxIn (Just r)) = SpendingScriptDatum r +-- \^ V2 and V3 scripts can have inline datums +toCip69Datum WitTxIn{} Old.PlutusScriptV3 Old.InlineScriptDatum = InlineDatum +toCip69Datum WitTxIn{} Old.PlutusScriptV2 Old.InlineScriptDatum = InlineDatum +-- \^ Everything else is not allowed. The old api does not prevent these invalid combinations +-- so we are forced to error here. The valid combinations are enforced in the PlutusScriptDatum +-- type family within the resultant PlutusScriptDatum GADT. +toCip69Datum WitTxIn{} scriptVersion datum = + error $ + unlines + [ "toCip69Datum: invalid combination of script version and script datum" + , "Script Version: " ++ show scriptVersion + , "Script Datum: " ++ show datum + ] + +toNewPlutusScriptWitness + :: forall era lang purpose + . L.PlutusLanguage (Old.ToLedgerPlutusLanguage lang) + => AlonzoEraOnwards era + -> Old.PlutusScriptVersion lang + -> Old.PlutusScriptOrReferenceInput lang + -> ScriptRedeemer + -> ExecutionUnits + -> PlutusScriptDatum (Old.ToLedgerPlutusLanguage lang) purpose + -> Either + CBOR.DecoderError + ( AnyWitness + (ShelleyLedgerEra era) + ) +toNewPlutusScriptWitness eon l (Old.PScript (Old.PlutusScriptSerialised scriptShortBs)) scriptRedeemer execUnits datum = do + let protocolVersion = getVersion eon + plutusScript = L.Plutus $ L.PlutusBinary scriptShortBs + + case L.decodePlutusRunnable @(Old.ToLedgerPlutusLanguage lang) protocolVersion plutusScript of + Left e -> + Left $ + CBOR.DecoderErrorCustom "PlutusLedgerApi.Common.ScriptDecodeError" (Text.pack . show $ pretty e) + Right plutusScriptRunnable -> + return + . AnyPlutusScriptWitness + $ mkPlutusScriptWitness + eon + (toPlutusSLanguage l) + plutusScriptRunnable + datum + scriptRedeemer + execUnits +toNewPlutusScriptWitness _ l (Old.PReferenceScript refInput) scriptRedeemer execUnits datum = + return . AnyPlutusScriptWitness $ + PlutusScriptWitness (toPlutusSLanguage l) (PReferenceScript refInput) datum scriptRedeemer execUnits + +getVersion :: forall era. AlonzoEraOnwards era -> Version +getVersion eon = alonzoEraOnwardsConstraints eon $ L.eraProtVerLow @(ShelleyLedgerEra era) + +obtainConstraints + :: Old.PlutusScriptVersion lang + -> (L.PlutusLanguage (Old.ToLedgerPlutusLanguage lang) => a) + -> a +obtainConstraints v = + case v of + Old.PlutusScriptV1 -> id + Old.PlutusScriptV2 -> id + Old.PlutusScriptV3 -> id + +toPlutusSLanguage + :: Old.PlutusScriptVersion lang -> L.SLanguage (Old.ToLedgerPlutusLanguage lang) +toPlutusSLanguage = \case + Old.PlutusScriptV1 -> L.SPlutusV1 + Old.PlutusScriptV2 -> L.SPlutusV2 + Old.PlutusScriptV3 -> L.SPlutusV3 + +-- | When it comes to using plutus scripts we need to provide +-- the following to the tx: +-- +-- 1. The redeemer pointer map +-- 2. The set of plutus languages in use +-- 3. The set of plutus scripts in use (present in the t) +-- 4. The datum map +legacyWitnessConversion + :: AlonzoEraOnwards era + -> [(Witnessable witnessable (ShelleyLedgerEra era), BuildTxWith BuildTx (Witness ctx era))] + -> Either + CBOR.DecoderError + [(Witnessable witnessable (ShelleyLedgerEra era), AnyWitness (ShelleyLedgerEra era))] +legacyWitnessConversion eon = mapM (fromExistingApi eon) + +legacyWitnessToScriptRequirements + :: AlonzoEraOnwards era + -> [(Witnessable witnessable (ShelleyLedgerEra era), BuildTxWith BuildTx (Witness ctx era))] + -> Either CBOR.DecoderError (TxScriptWitnessRequirements (ShelleyLedgerEra era)) +legacyWitnessToScriptRequirements eon wits = do + r <- legacyWitnessConversion eon wits + return $ getTxScriptWitnessesRequirements eon r diff --git a/cardano-api/src/Cardano/Api/Internal/Experimental/Simple/Script.hs b/cardano-api/src/Cardano/Api/Internal/Experimental/Simple/Script.hs new file mode 100644 index 000000000..eb73b3f3b --- /dev/null +++ b/cardano-api/src/Cardano/Api/Internal/Experimental/Simple/Script.hs @@ -0,0 +1,22 @@ +{-# LANGUAGE GADTs #-} + +module Cardano.Api.Internal.Experimental.Simple.Script + ( SimpleScript (..) + , SimpleScriptOrReferenceInput (..) + ) +where + +import Cardano.Api.Internal.TxIn (TxIn) + +import Cardano.Ledger.Core qualified as Ledger + +-- | A simple script in a particular era. We leverage ledger's Cardano.Api.Experimental.ErasraScript +-- type class methods to work with the script. +data SimpleScript era where + SimpleScript :: Ledger.NativeScript era -> SimpleScript era + +-- SimpleScript :: Ledger.EraScript era => Ledger.Timelock (LedgerEra era) -> SimpleScript era + +data SimpleScriptOrReferenceInput era + = SScript (SimpleScript era) + | SReferenceScript TxIn diff --git a/cardano-api/src/Cardano/Api/Internal/Experimental/Tx.hs b/cardano-api/src/Cardano/Api/Internal/Experimental/Tx.hs index 8057b3cb2..b2df97d67 100644 --- a/cardano-api/src/Cardano/Api/Internal/Experimental/Tx.hs +++ b/cardano-api/src/Cardano/Api/Internal/Experimental/Tx.hs @@ -127,6 +127,7 @@ import Cardano.Api.Internal.Eon.Convert import Cardano.Api.Internal.Eon.ShelleyBasedEra import Cardano.Api.Internal.Eras.Core (ToCardanoEra (toCardanoEra), forEraInEon) import Cardano.Api.Internal.Experimental.Eras +import Cardano.Api.Internal.Experimental.Witness.TxScriptWitnessRequirements import Cardano.Api.Internal.Feature import Cardano.Api.Internal.Pretty (docToString, pretty) import Cardano.Api.Internal.ReexposeLedger (StrictMaybe (..), maybeToStrictMaybe) @@ -168,10 +169,13 @@ makeUnsignedTx -> Either TxBodyError (UnsignedTx era) makeUnsignedTx era bc = obtainCommonConstraints era $ do let sbe = convert era + aeon = convert era + TxScriptWitnessRequirements languages scripts datums redeemers <- + shelleyBasedEraConstraints sbe $ + collectTxBodyScriptWitnessRequirements (convert era) bc -- cardano-api types let apiTxOuts = txOuts bc - apiScriptWitnesses = collectTxBodyScriptWitnesses sbe bc apiScriptValidity = txScriptValidity bc apiMintValue = txMintValue bc apiProtocolParameters = txProtocolParams bc @@ -192,12 +196,13 @@ makeUnsignedTx era bc = obtainCommonConstraints era $ do totalCollateral = convTotalCollateral apiTotalCollateral certs = convCertificates sbe $ txCertificates bc txAuxData = toAuxiliaryData sbe (txMetadata bc) (txAuxScripts bc) - scripts = convScripts apiScriptWitnesses - languages = convLanguages apiScriptWitnesses - sData = convScriptData sbe apiTxOuts apiScriptWitnesses - (datums, redeemers) = case sData of - TxBodyScriptData _ ds rs -> (ds, rs) - TxBodyNoScriptData -> (mempty, L.Redeemers mempty) + scriptIntegrityHash = + convPParamsToScriptIntegrityHash + aeon + apiProtocolParameters + redeemers + datums + languages let setMint = convMintValue apiMintValue setReqSignerHashes = convExtraKeyWitnesses apiExtraKeyWitnesses @@ -213,7 +218,7 @@ makeUnsignedTx era bc = obtainCommonConstraints era $ do & L.vldtTxBodyL . L.invalidBeforeL .~ convValidityLowerBound (txValidityLowerBound bc) & L.vldtTxBodyL . L.invalidHereAfterL .~ convValidityUpperBound sbe (txValidityUpperBound bc) & L.reqSignerHashesTxBodyL .~ setReqSignerHashes - & L.scriptIntegrityHashTxBodyL .~ getScriptIntegrityHash apiProtocolParameters languages sData + & L.scriptIntegrityHashTxBodyL .~ scriptIntegrityHash & L.withdrawalsTxBodyL .~ withdrawals & L.certsTxBodyL .~ certs & L.mintTxBodyL .~ setMint diff --git a/cardano-api/src/Cardano/Api/Internal/Experimental/Witness/AnyWitness.hs b/cardano-api/src/Cardano/Api/Internal/Experimental/Witness/AnyWitness.hs new file mode 100644 index 000000000..99d7d5b36 --- /dev/null +++ b/cardano-api/src/Cardano/Api/Internal/Experimental/Witness/AnyWitness.hs @@ -0,0 +1,156 @@ +{-# LANGUAGE GADTs #-} + +module Cardano.Api.Internal.Experimental.Witness.AnyWitness + ( + -- * Any witness (key, simple script, plutus script). + AnyWitness (..) + , getAnyWitnessScript + , getAnyWitnessPlutusLanguage + , getAnyWitnessScriptData + + ) where + + +import Cardano.Api.Internal.Eon.AlonzoEraOnwards +import Cardano.Api.Internal.Eon.ShelleyBasedEra +import Cardano.Api.Internal.Experimental.Plutus.Script +import Cardano.Api.Internal.Experimental.Simple.Script + ( SimpleScript (SimpleScript) + , SimpleScriptOrReferenceInput (..) + ) +import Cardano.Api.Internal.ScriptData +import Cardano.Api.Ledger qualified as L + +import Cardano.Ledger.Alonzo.Scripts qualified as L +import Cardano.Ledger.Babbage.Scripts qualified as L +import Cardano.Ledger.Conway.Scripts qualified as L +import Cardano.Ledger.Core qualified as L +import Cardano.Ledger.Plutus.Data qualified as L +import Cardano.Ledger.Plutus.Language qualified as L +import Cardano.Api.Internal.Experimental.Plutus.ScriptWitness + +import GHC.Exts + +-- | Here we consider three types of witnesses in Cardano: key witnesses, simple script witnesses and +-- Plutus script witnesses. Note that AnyKeyWitness does not contain a key witness. This is because +-- that witness is provided in the signing stage of the transaction. AnyWitness is used to contruct +-- the transaction body. +data AnyWitness era where + AnyKeyWitness :: AnyWitness era + AnySimpleScriptWitness :: SimpleScriptOrReferenceInput era -> AnyWitness era + AnyPlutusScriptWitness :: PlutusScriptWitness lang purpose era -> AnyWitness era + +getAnyWitnessPlutusLanguage :: AnyWitness era -> Maybe L.Language +getAnyWitnessPlutusLanguage AnyKeyWitness = Nothing +getAnyWitnessPlutusLanguage (AnySimpleScriptWitness _) = Nothing +getAnyWitnessPlutusLanguage (AnyPlutusScriptWitness swit) = Just $ getPlutusScriptWitnessLanguage swit + +getAnyWitnessSimpleScript + :: AnyWitness (ShelleyLedgerEra era) -> Maybe (L.NativeScript (ShelleyLedgerEra era)) +getAnyWitnessSimpleScript AnyKeyWitness = Nothing +getAnyWitnessSimpleScript (AnySimpleScriptWitness simpleScriptOrRefInput) = + case simpleScriptOrRefInput of + SScript (SimpleScript simpleScript) -> Just simpleScript + SReferenceScript{} -> Nothing +getAnyWitnessSimpleScript (AnyPlutusScriptWitness _) = Nothing + +getAnyWitnessPlutusScript + :: AlonzoEraOnwards era + -> AnyWitness (ShelleyLedgerEra era) + -> Maybe (L.PlutusScript (ShelleyLedgerEra era)) +getAnyWitnessPlutusScript _ AnyKeyWitness = Nothing +getAnyWitnessPlutusScript _ (AnySimpleScriptWitness _) = Nothing +getAnyWitnessPlutusScript + eon + ( AnyPlutusScriptWitness + (PlutusScriptWitness l (PScript (PlutusScriptInEra plutusScriptRunnable)) _ _ _) + ) = fromPlutusRunnable l eon plutusScriptRunnable +getAnyWitnessPlutusScript _ (AnyPlutusScriptWitness (PlutusScriptWitness _ (PReferenceScript{}) _ _ _)) = + Nothing + +-- | NB this does not include datums from inline datums existing at tx outputs! +getAnyWitnessScriptData + :: AlonzoEraOnwards era -> AnyWitness (ShelleyLedgerEra era) -> L.TxDats (ShelleyLedgerEra era) +getAnyWitnessScriptData eon AnyKeyWitness = alonzoEraOnwardsConstraints eon mempty +getAnyWitnessScriptData eon AnySimpleScriptWitness{} = alonzoEraOnwardsConstraints eon mempty +getAnyWitnessScriptData eon (AnyPlutusScriptWitness (PlutusScriptWitness l _ scriptDatum _ _)) = + let alonzoSdat = toAlonzoDatum eon l scriptDatum + in alonzoEraOnwardsConstraints eon $ + case alonzoSdat of + Nothing -> alonzoEraOnwardsConstraints eon mempty + Just d -> alonzoEraOnwardsConstraints eon $ L.TxDats $ fromList [(L.hashData d, d)] + + +getAnyWitnessScript + :: ShelleyBasedEra era -> AnyWitness (ShelleyLedgerEra era) -> Maybe (L.Script (ShelleyLedgerEra era)) +getAnyWitnessScript _ AnyKeyWitness = Nothing +getAnyWitnessScript era ss@(AnySimpleScriptWitness{}) = + case era of + ShelleyBasedEraShelley -> getAnyWitnessSimpleScript ss + ShelleyBasedEraAllegra -> getAnyWitnessSimpleScript ss + ShelleyBasedEraMary -> getAnyWitnessSimpleScript ss + ShelleyBasedEraAlonzo -> L.TimelockScript <$> getAnyWitnessSimpleScript ss + ShelleyBasedEraBabbage -> L.TimelockScript <$> getAnyWitnessSimpleScript ss + ShelleyBasedEraConway -> L.TimelockScript <$> getAnyWitnessSimpleScript ss +getAnyWitnessScript era ps@(AnyPlutusScriptWitness{}) = + forShelleyBasedEraInEon era Nothing $ \aEon -> + case aEon of + AlonzoEraOnwardsAlonzo -> L.PlutusScript <$> getAnyWitnessPlutusScript aEon ps + AlonzoEraOnwardsBabbage -> L.PlutusScript <$> getAnyWitnessPlutusScript aEon ps + AlonzoEraOnwardsConway -> L.PlutusScript <$> getAnyWitnessPlutusScript aEon ps + + +-- It should be noted that 'PlutusRunnable' is constructed via deserialization. The deserialization +-- instance lives in ledger and will fail for an invalid script language/era pairing. Therefore +-- this function should never return 'Nothing'. +fromPlutusRunnable + :: L.SLanguage lang + -> AlonzoEraOnwards era + -> L.PlutusRunnable lang + -> Maybe (L.PlutusScript (ShelleyLedgerEra era)) +fromPlutusRunnable L.SPlutusV1 eon runnable = + case eon of + AlonzoEraOnwardsAlonzo -> + let plutusScript = L.plutusFromRunnable runnable + in Just $ L.AlonzoPlutusV1 plutusScript + AlonzoEraOnwardsBabbage -> + let plutusScript = L.plutusFromRunnable runnable + in Just $ L.BabbagePlutusV1 plutusScript + AlonzoEraOnwardsConway -> + let plutusScript = L.plutusFromRunnable runnable + in Just $ L.ConwayPlutusV1 plutusScript +fromPlutusRunnable L.SPlutusV2 eon runnable = + case eon of + AlonzoEraOnwardsAlonzo -> Nothing + AlonzoEraOnwardsBabbage -> + let plutusScript = L.plutusFromRunnable runnable + in Just $ L.BabbagePlutusV2 plutusScript + AlonzoEraOnwardsConway -> + let plutusScript = L.plutusFromRunnable runnable + in Just $ L.ConwayPlutusV2 plutusScript +fromPlutusRunnable L.SPlutusV3 eon runnable = + case eon of + AlonzoEraOnwardsAlonzo -> Nothing + AlonzoEraOnwardsBabbage -> Nothing + AlonzoEraOnwardsConway -> + let plutusScript = L.plutusFromRunnable runnable + in Just $ L.ConwayPlutusV3 plutusScript + +toAlonzoDatum + :: AlonzoEraOnwards era + -> L.SLanguage lang + -> PlutusScriptDatum lang purpose + -> Maybe (L.Data (ShelleyLedgerEra era)) +toAlonzoDatum eon l d = + let mHashableData = getPlutusDatum l d + in case mHashableData of + Just h -> Just $ alonzoEraOnwardsConstraints eon $ toAlonzoData h + Nothing -> Nothing + +getPlutusDatum + :: L.SLanguage lang -> PlutusScriptDatum lang purpose -> Maybe HashableScriptData +getPlutusDatum L.SPlutusV1 (SpendingScriptDatum d) = Just d +getPlutusDatum L.SPlutusV2 (SpendingScriptDatum d) = Just d +getPlutusDatum L.SPlutusV3 (SpendingScriptDatum d) = d +getPlutusDatum _ InlineDatum = Nothing +getPlutusDatum _ NoScriptDatum = Nothing \ No newline at end of file diff --git a/cardano-api/src/Cardano/Api/Internal/Experimental/Witness/TxScriptWitnessRequirements.hs b/cardano-api/src/Cardano/Api/Internal/Experimental/Witness/TxScriptWitnessRequirements.hs new file mode 100644 index 000000000..adb622b02 --- /dev/null +++ b/cardano-api/src/Cardano/Api/Internal/Experimental/Witness/TxScriptWitnessRequirements.hs @@ -0,0 +1,94 @@ +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE RankNTypes #-} + +module Cardano.Api.Internal.Experimental.Witness.TxScriptWitnessRequirements + ( -- * All the parts that constitute a plutus script witness but also including simple scripts + TxScriptWitnessRequirements (..) + + -- * Collecting plutus script witness related transaction requirements. + , getTxScriptWitnessesRequirements + , obtainMonoidConstraint + + -- * For testing only + , extractExecutionUnits + ) +where + +import Cardano.Api.Internal.Eon.AlonzoEraOnwards +import Cardano.Api.Internal.Eon.Convert (Convert (convert)) +import Cardano.Api.Internal.Eon.ShelleyBasedEra +import Cardano.Api.Internal.Experimental.Plutus.IndexedPlutusScriptWitness +import Cardano.Api.Internal.Experimental.Plutus.ScriptWitness +import Cardano.Api.Internal.Experimental.Witness.AnyWitness +import Cardano.Api.Internal.Script (ExecutionUnits, fromAlonzoExUnits) +import Cardano.Api.Ledger qualified as L + +import Cardano.Ledger.Alonzo.TxWits qualified as L +import Ouroboros.Consensus.Shelley.Eras qualified as Consensus + +import Data.Map.Strict qualified as Map +import Data.Set (Set) +import Data.Set qualified as Set + +-- | This type collects all the requirements for script witnesses in a transaction. +data TxScriptWitnessRequirements era + = TxScriptWitnessRequirements + (Set L.Language) + [L.Script era] + (L.TxDats era) + (L.Redeemers era) + +instance Semigroup (TxScriptWitnessRequirements Consensus.StandardAlonzo) where + (<>) (TxScriptWitnessRequirements l1 s1 d1 r1) (TxScriptWitnessRequirements l2 s2 d2 r2) = + TxScriptWitnessRequirements (l1 <> l2) (s1 <> s2) (d1 <> d2) (r1 <> r2) + +instance Monoid (TxScriptWitnessRequirements Consensus.StandardAlonzo) where + mempty = TxScriptWitnessRequirements mempty mempty mempty mempty + +instance Semigroup (TxScriptWitnessRequirements Consensus.StandardBabbage) where + (<>) (TxScriptWitnessRequirements l1 s1 d1 r1) (TxScriptWitnessRequirements l2 s2 d2 r2) = + TxScriptWitnessRequirements (l1 <> l2) (s1 <> s2) (d1 <> d2) (r1 <> r2) + +instance Monoid (TxScriptWitnessRequirements Consensus.StandardBabbage) where + mempty = TxScriptWitnessRequirements mempty mempty mempty mempty + +instance Semigroup (TxScriptWitnessRequirements Consensus.StandardConway) where + (<>) (TxScriptWitnessRequirements l1 s1 d1 r1) (TxScriptWitnessRequirements l2 s2 d2 r2) = + TxScriptWitnessRequirements (l1 <> l2) (s1 <> s2) (d1 <> d2) (r1 <> r2) + +instance Monoid (TxScriptWitnessRequirements Consensus.StandardConway) where + mempty = TxScriptWitnessRequirements mempty mempty mempty mempty + +getTxScriptWitnessRequirements + :: AlonzoEraOnwards era + -> (Witnessable witnessable (ShelleyLedgerEra era), AnyWitness (ShelleyLedgerEra era)) + -> TxScriptWitnessRequirements (ShelleyLedgerEra era) +getTxScriptWitnessRequirements era (thing, anyWit) = + TxScriptWitnessRequirements + (maybe mempty Set.singleton $ getAnyWitnessPlutusLanguage anyWit) + (maybe mempty return $ getAnyWitnessScript (convert era) anyWit) + (getAnyWitnessScriptData era anyWit) + (getAnyWitnessRedeemerPointerMap era (thing, anyWit)) + +getTxScriptWitnessesRequirements + :: AlonzoEraOnwards era + -> [(Witnessable witnessable (ShelleyLedgerEra era), AnyWitness (ShelleyLedgerEra era))] + -> TxScriptWitnessRequirements (ShelleyLedgerEra era) +getTxScriptWitnessesRequirements eon wits = + obtainMonoidConstraint eon $ mconcat $ map (getTxScriptWitnessRequirements eon) wits + +obtainMonoidConstraint + :: AlonzoEraOnwards era + -> (Monoid (TxScriptWitnessRequirements (ShelleyLedgerEra era)) => a) + -> a +obtainMonoidConstraint eon = case eon of + AlonzoEraOnwardsAlonzo -> id + AlonzoEraOnwardsBabbage -> id + AlonzoEraOnwardsConway -> id + +extractExecutionUnits :: TxScriptWitnessRequirements era -> [ExecutionUnits] +extractExecutionUnits (TxScriptWitnessRequirements _ _ _ redeemers) = + let m = L.unRedeemers redeemers + in [fromAlonzoExUnits exUnits | (_, exUnits) <- Map.elems m] diff --git a/cardano-api/src/Cardano/Api/Internal/ScriptData.hs b/cardano-api/src/Cardano/Api/Internal/ScriptData.hs index 15fd0aec5..610fb9e01 100644 --- a/cardano-api/src/Cardano/Api/Internal/ScriptData.hs +++ b/cardano-api/src/Cardano/Api/Internal/ScriptData.hs @@ -10,7 +10,7 @@ module Cardano.Api.Internal.ScriptData ( -- * Script data - HashableScriptData + HashableScriptData (..) , hashScriptDataBytes , getOriginalScriptDataBytes , getScriptData diff --git a/cardano-api/src/Cardano/Api/Internal/Tx/Body.hs b/cardano-api/src/Cardano/Api/Internal/Tx/Body.hs index dc5a26f6f..7911a90b2 100644 --- a/cardano-api/src/Cardano/Api/Internal/Tx/Body.hs +++ b/cardano-api/src/Cardano/Api/Internal/Tx/Body.hs @@ -336,6 +336,7 @@ module Cardano.Api.Internal.Tx.Body , ScriptWitnessIndex (..) , renderScriptWitnessIndex , collectTxBodyScriptWitnesses + , collectTxBodyScriptWitnessRequirements , toScriptIndex -- ** Conversion to inline data @@ -347,6 +348,7 @@ module Cardano.Api.Internal.Tx.Body , convExtraKeyWitnesses , convLanguages , convMintValue + , convPParamsToScriptIntegrityHash , convReferenceInputs , convReturnCollateral , convScripts @@ -404,7 +406,18 @@ import Cardano.Api.Internal.Eon.ShelleyToBabbageEra import Cardano.Api.Internal.Eras.Case import Cardano.Api.Internal.Eras.Core import Cardano.Api.Internal.Error (Error (..), displayError) +import Cardano.Api.Internal.Experimental.Plutus.ScriptWitness + ( Cert + , Mint + , Withdrawal + , Witnessable (..) + , obtainAlonzoScriptPurposeConstraints + ) +import Cardano.Api.Internal.Experimental.Plutus.ScriptWitness qualified as NewSWit +import Cardano.Api.Internal.Experimental.Plutus.Shim.LegacyScripts +import Cardano.Api.Internal.Experimental.Witness.TxScriptWitnessRequirements import Cardano.Api.Internal.Feature +import Cardano.Api.Internal.Governance.Actions.ProposalProcedure import Cardano.Api.Internal.Governance.Actions.VotingProcedure import Cardano.Api.Internal.Hash import Cardano.Api.Internal.Keys.Byron @@ -418,6 +431,7 @@ import Cardano.Api.Internal.ScriptData import Cardano.Api.Internal.SerialiseCBOR import Cardano.Api.Internal.SerialiseJSON import Cardano.Api.Internal.SerialiseRaw +import Cardano.Api.Internal.Tx.BuildTxWith import Cardano.Api.Internal.Tx.Sign import Cardano.Api.Internal.TxIn import Cardano.Api.Internal.TxMetadata @@ -498,9 +512,13 @@ import Data.String import Data.Text (Text) import Data.Text qualified as Text import Data.Text.Encoding qualified as Text +import Data.Text.Lazy qualified as LText +import Data.Text.Lazy.Builder qualified as LText import Data.Type.Equality import Data.Typeable import Data.Word (Word16, Word32, Word64) +import Formatting.Buildable (Buildable) +import Formatting.Buildable qualified as Build import GHC.Exts (IsList (..)) import GHC.Stack import Lens.Micro hiding (ix) @@ -2016,7 +2034,8 @@ getTxIdShelley _ tx = -- data TxBodyError - = TxBodyEmptyTxIns + = TxBodyPlutusScriptDecodeError CBOR.DecoderError + | TxBodyEmptyTxIns | TxBodyEmptyTxInsCollateral | TxBodyEmptyTxOuts | TxBodyOutputNegative !Quantity !TxOutInAnyEra @@ -2027,8 +2046,13 @@ data TxBodyError | TxBodyProtocolParamsConversionError !ProtocolParametersConversionError deriving (Eq, Show) +renderBuildable :: Buildable a => a -> Text +renderBuildable e = LText.toStrict . LText.toLazyText $ Build.build e + instance Error TxBodyError where prettyError = \case + TxBodyPlutusScriptDecodeError err -> + "Error decoding Plutus script: " <> pretty (renderBuildable err) TxBodyEmptyTxIns -> "Transaction body has no inputs" TxBodyEmptyTxInsCollateral -> @@ -2069,16 +2093,21 @@ instance Error TxBodyError where "Errors in protocol parameters conversion: " <> prettyError ppces createTransactionBody - :: () - => HasCallStack + :: forall era + . HasCallStack => ShelleyBasedEra era -> TxBodyContent BuildTx era -> Either TxBodyError (TxBody era) createTransactionBody sbe bc = shelleyBasedEraConstraints sbe $ do + TxScriptWitnessRequirements languages scripts dats redeemers + :: TxScriptWitnessRequirements (ShelleyLedgerEra era) <- + caseShelleyToMaryOrAlonzoEraOnwards + (const $ error "Impossible to construct TxScriptWitnessRequirements in pre-Alonzo era") + (\eon -> collectTxBodyScriptWitnessRequirements eon bc) + sbe let era = toCardanoEra sbe - apiTxOuts = txOuts bc - apiScriptWitnesses = collectTxBodyScriptWitnesses sbe bc + apiScriptValidity = txScriptValidity bc apiMintValue = txMintValue bc apiProtocolParameters = txProtocolParams bc @@ -2095,9 +2124,11 @@ createTransactionBody sbe bc = totalCollateral = convTotalCollateral apiTotalCollateral certs = convCertificates sbe $ txCertificates bc txAuxData = toAuxiliaryData sbe (txMetadata bc) (txAuxScripts bc) - scripts = convScripts apiScriptWitnesses - languages = convLanguages apiScriptWitnesses - sData = convScriptData sbe apiTxOuts apiScriptWitnesses + sData = + caseShelleyToMaryOrAlonzoEraOnwards + (const TxBodyNoScriptData) + (\eon -> TxBodyScriptData eon dats redeemers) + sbe proposalProcedures = convProposalProcedures $ maybe TxProposalProceduresNone unFeatured (txProposalProcedures bc) votingProcedures = convVotingProcedures $ maybe TxVotingProceduresNone unFeatured (txVotingProcedures bc) currentTreasuryValue = Ledger.maybeToStrictMaybe $ unFeatured =<< txCurrentTreasuryValue bc @@ -3814,7 +3845,187 @@ collectTxBodyScriptWitnesses | (ix, _, witness) <- indexTxProposalProcedures txp ] --- TODO: Investigate if we need +getSupplementalDatums + :: AlonzoEraOnwards era -> [TxOut CtxTx era] -> L.TxDats (ShelleyLedgerEra era) +getSupplementalDatums eon [] = alonzoEraOnwardsConstraints eon mempty +getSupplementalDatums eon txouts = + alonzoEraOnwardsConstraints eon $ + L.TxDats $ + fromList + [ (L.hashData ledgerData, ledgerData) + | TxOut _ _ (TxOutSupplementalDatum _ d) _ <- txouts + , let ledgerData = toAlonzoData d + ] + +collectTxBodyScriptWitnessRequirements + :: forall era + . IsShelleyBasedEra era + => AlonzoEraOnwards era + -> TxBodyContent BuildTx era + -> Either + TxBodyError + (TxScriptWitnessRequirements (ShelleyLedgerEra era)) +collectTxBodyScriptWitnessRequirements + aEon + bc@TxBodyContent + { txOuts + } = + obtainAlonzoScriptPurposeConstraints aEon $ do + let sbe = shelleyBasedEra @era + supplementaldatums = TxScriptWitnessRequirements mempty mempty (getSupplementalDatums aEon txOuts) mempty + txInWits <- + first TxBodyPlutusScriptDecodeError $ + legacyWitnessToScriptRequirements aEon $ + extractWitnessableTxIns aEon bc + + txWithdrawalWits <- + first TxBodyPlutusScriptDecodeError $ + legacyWitnessToScriptRequirements aEon $ + extractWitnessableWithdrawals aEon bc + + txCertWits <- + first TxBodyPlutusScriptDecodeError $ + legacyWitnessToScriptRequirements aEon $ + extractWitnessableCertificates aEon bc + + txMintWits <- + first TxBodyPlutusScriptDecodeError $ + legacyWitnessToScriptRequirements aEon $ + extractWitnessableMints aEon bc + + txVotingWits <- + caseShelleyToBabbageOrConwayEraOnwards + ( \w -> + shelleyToBabbageEraConstraints w $ Right $ TxScriptWitnessRequirements mempty mempty mempty mempty + ) + ( \eon -> + first TxBodyPlutusScriptDecodeError $ + legacyWitnessToScriptRequirements aEon $ + extractWitnessableVotes eon bc + ) + sbe + txProposalWits <- + caseShelleyToBabbageOrConwayEraOnwards + (const $ Right $ TxScriptWitnessRequirements mempty mempty mempty mempty) + ( \eon -> + first TxBodyPlutusScriptDecodeError $ + legacyWitnessToScriptRequirements aEon $ + extractWitnessableProposals eon bc + ) + sbe + + return $ + obtainMonoidConstraint aEon $ + mconcat + [ supplementaldatums + , txInWits + , txWithdrawalWits + , txCertWits + , txMintWits + , txVotingWits + , txProposalWits + ] + +extractWitnessableTxIns + :: AlonzoEraOnwards era + -> TxBodyContent BuildTx era + -> [(Witnessable TxIn (ShelleyLedgerEra era), BuildTxWith BuildTx (Witness WitCtxTxIn era))] +extractWitnessableTxIns aeon TxBodyContent{txIns} = + alonzoEraOnwardsConstraints aeon $ + List.nub [(WitTxIn txin, wit) | (txin, wit) <- txIns] + +extractWitnessableWithdrawals + :: AlonzoEraOnwards era + -> TxBodyContent BuildTx era + -> [(Witnessable Withdrawal (ShelleyLedgerEra era), BuildTxWith BuildTx (Witness WitCtxStake era))] +extractWitnessableWithdrawals aeon TxBodyContent{txWithdrawals} = + alonzoEraOnwardsConstraints aeon $ + List.nub + [ (WitWithdrawal (addr, withAmt), wit) + | (addr, withAmt, wit) <- getWithdrawals txWithdrawals + ] + where + getWithdrawals TxWithdrawalsNone = [] + getWithdrawals (TxWithdrawals _ txws) = txws + +extractWitnessableCertificates + :: AlonzoEraOnwards era + -> TxBodyContent BuildTx era + -> [(Witnessable Cert (ShelleyLedgerEra era), BuildTxWith BuildTx (Witness WitCtxStake era))] +extractWitnessableCertificates aeon TxBodyContent{txCertificates} = + alonzoEraOnwardsConstraints aeon $ + List.nub + [ ( WitTxCert (AnyCertificate cert, stakeCred) + , BuildTxWith wit + ) + | (cert, BuildTxWith (Just (stakeCred, wit))) <- getCertificates txCertificates + ] + where + getCertificates TxCertificatesNone = [] + getCertificates (TxCertificates _ txcs) = toList txcs + +extractWitnessableMints + :: AlonzoEraOnwards era + -> TxBodyContent BuildTx era + -> [(Witnessable Mint (ShelleyLedgerEra era), BuildTxWith BuildTx (Witness WitCtxMint era))] +extractWitnessableMints aeon TxBodyContent{txMintValue} = + alonzoEraOnwardsConstraints aeon $ + List.nub + [ (WitMint (policyId, assetName, quantity), BuildTxWith $ ScriptWitness ScriptWitnessForMinting wit) + | (policyId, assetsWithWits) <- getMints txMintValue + , (assetName, quantity, BuildTxWith wit) <- assetsWithWits + ] + where + getMints TxMintNone = [] + getMints (TxMintValue _ txms) = toList txms + +extractWitnessableVotes + :: ConwayEraOnwards era + -> TxBodyContent BuildTx era + -> [(Witnessable NewSWit.Voter (ShelleyLedgerEra era), BuildTxWith BuildTx (Witness WitCtxStake era))] +extractWitnessableVotes e@ConwayEraOnwardsConway TxBodyContent{txVotingProcedures} = + List.nub + [ (WitVote $ AnyVoter vote, BuildTxWith wit) + | (vote, wit) <- getVotes e $ maybe TxVotingProceduresNone unFeatured txVotingProcedures + ] + where + getVotes + :: ConwayEraOnwards era + -> TxVotingProcedures BuildTx era + -> [(Voter era, Witness WitCtxStake era)] + getVotes ConwayEraOnwardsConway TxVotingProceduresNone = [] + getVotes ConwayEraOnwardsConway (TxVotingProcedures allVotingProcedures (BuildTxWith scriptWitnessedVotes)) = + [ (Voter singleVoter, wit) + | (singleVoter, _) <- toList $ L.unVotingProcedures allVotingProcedures + , let wit = case Map.lookup singleVoter scriptWitnessedVotes of + Just sWit -> ScriptWitness ScriptWitnessForStakeAddr sWit + Nothing -> KeyWitness KeyWitnessForStakeAddr + ] + +extractWitnessableProposals + :: ConwayEraOnwards era + -> TxBodyContent BuildTx era + -> [(Witnessable NewSWit.Proposal (ShelleyLedgerEra era), BuildTxWith BuildTx (Witness WitCtxStake era))] +extractWitnessableProposals e@ConwayEraOnwardsConway TxBodyContent{txProposalProcedures} = + List.nub + [ (WitProposal $ AnyProposal prop, BuildTxWith wit) + | (prop, wit) <- + getProposals e $ maybe TxProposalProceduresNone unFeatured txProposalProcedures + ] + where + getProposals + :: ConwayEraOnwards era + -> TxProposalProcedures BuildTx era + -> [(Proposal era, Witness WitCtxStake era)] + getProposals ConwayEraOnwardsConway TxProposalProceduresNone = [] + getProposals ConwayEraOnwardsConway (TxProposalProcedures txps) = + [ (Proposal p, wit) + | (p, BuildTxWith mScriptWit) <- toList txps + , let wit = case mScriptWit of + Just sWit -> ScriptWitness ScriptWitnessForStakeAddr sWit + Nothing -> KeyWitness KeyWitnessForStakeAddr + ] + toShelleyWithdrawal :: [(StakeAddress, L.Coin, a)] -> L.Withdrawals StandardCrypto toShelleyWithdrawal withdrawals = L.Withdrawals $ diff --git a/cardano-api/src/Cardano/Api/Shelley.hs b/cardano-api/src/Cardano/Api/Shelley.hs index 5974be435..aedb1f484 100644 --- a/cardano-api/src/Cardano/Api/Shelley.hs +++ b/cardano-api/src/Cardano/Api/Shelley.hs @@ -280,6 +280,7 @@ module Cardano.Api.Shelley , toConsensusGenTx , fromAlonzoCostModels -- TODO: arrange not to export these + , collectTxBodyScriptWitnessRequirements , toLedgerNonce , toShelleyNetwork , fromShelleyPoolParams @@ -287,6 +288,7 @@ module Cardano.Api.Shelley , emptyVotingProcedures , mergeVotingProcedures , singletonVotingProcedures + , extractExecutionUnits , VotesMergingConflict (..) ) where @@ -297,6 +299,7 @@ import Cardano.Api.Internal.Block import Cardano.Api.Internal.Certificate import Cardano.Api.Internal.DRepMetadata import Cardano.Api.Internal.Eon.ShelleyBasedEra +import Cardano.Api.Internal.Experimental.Witness.TxScriptWitnessRequirements import Cardano.Api.Internal.Fees import Cardano.Api.Internal.Genesis import Cardano.Api.Internal.Governance.Actions.ProposalProcedure diff --git a/cardano-api/test/cardano-api-test/Test/Cardano/Api/Transaction/Autobalance.hs b/cardano-api/test/cardano-api-test/Test/Cardano/Api/Transaction/Autobalance.hs index a3602c87a..7d949fd9c 100644 --- a/cardano-api/test/cardano-api-test/Test/Cardano/Api/Transaction/Autobalance.hs +++ b/cardano-api/test/cardano-api-test/Test/Cardano/Api/Transaction/Autobalance.hs @@ -20,7 +20,11 @@ import Cardano.Api.Internal.Fees import Cardano.Api.Internal.Script import Cardano.Api.Ledger qualified as L import Cardano.Api.Ledger.Lens qualified as L -import Cardano.Api.Shelley (LedgerProtocolParameters (..)) +import Cardano.Api.Shelley + ( LedgerProtocolParameters (..) + , collectTxBodyScriptWitnessRequirements + , extractExecutionUnits + ) import Cardano.Ledger.Alonzo.Core qualified as L import Cardano.Ledger.Coin qualified as L @@ -111,16 +115,16 @@ prop_make_transaction_body_autobalance_return_correct_fee_for_multi_asset = H.pr address Nothing + scriptWitReqsWithAsset <- + H.evalEither $ collectTxBodyScriptWitnessRequirements aeo balancedContentWithTxoutAsset + -- check if execution units have changed [ ExecutionUnits { executionSteps = 84_851_308 , executionMemory = 325_610 } ] - === [ exUnits - | (_, AnyScriptWitness (PlutusScriptWitness _ _ _ _ _ exUnits)) <- - collectTxBodyScriptWitnesses sbe balancedContentWithTxoutAsset - ] + === extractExecutionUnits scriptWitReqsWithAsset -- the correct amount with manual balancing of assets 335_299 === feeWithTxoutAsset @@ -141,16 +145,16 @@ prop_make_transaction_body_autobalance_return_correct_fee_for_multi_asset = H.pr address Nothing + scriptWitReqsBalanced <- + H.evalEither $ collectTxBodyScriptWitnessRequirements aeo balancedContent + -- check if execution units have changed [ ExecutionUnits { executionSteps = 84_851_308 , executionMemory = 325_610 } ] - === [ exUnits - | (_, AnyScriptWitness (PlutusScriptWitness _ _ _ _ _ exUnits)) <- - collectTxBodyScriptWitnesses sbe balancedContent - ] + === extractExecutionUnits scriptWitReqsBalanced H.noteShow_ feeWithTxoutAsset H.noteShow_ fee @@ -270,16 +274,16 @@ prop_make_transaction_body_autobalance_multi_asset_collateral = H.propertyOnce $ address Nothing + scriptWitReqsBalanced <- + H.evalEither $ collectTxBodyScriptWitnessRequirements aeo balancedContent + -- check if execution units have changed [ ExecutionUnits { executionSteps = 84_851_308 , executionMemory = 325_610 } ] - === [ exUnits - | (_, AnyScriptWitness (PlutusScriptWitness _ _ _ _ _ exUnits)) <- - collectTxBodyScriptWitnesses sbe balancedContent - ] + === extractExecutionUnits scriptWitReqsBalanced 335_299 === fee TxReturnCollateral _ (TxOut _ txOutValue _ _) <- H.noteShow $ txReturnCollateral balancedContent