From 7700412f3289640eb0a21295a9a9c183cacd17b8 Mon Sep 17 00:00:00 2001 From: Sjoerd Visscher Date: Thu, 9 Jun 2022 18:08:12 +0200 Subject: [PATCH] plutus-chain-index: Use fromCardanoTx in fromOnChainTx --- .../src/Plutus/ChainIndex/Tx.hs | 116 ++++++------------ .../src/Plutus/ChainIndex/Types.hs | 75 ++++++++++- .../src/Plutus/Contract/CardanoAPI.hs | 14 ++- plutus-ledger/src/Ledger/Tx.hs | 6 +- plutus-ledger/src/Ledger/Tx/CardanoAPI.hs | 5 + plutus-pab/src/Cardano/Node/Client.hs | 2 +- 6 files changed, 125 insertions(+), 93 deletions(-) diff --git a/plutus-chain-index-core/src/Plutus/ChainIndex/Tx.hs b/plutus-chain-index-core/src/Plutus/ChainIndex/Tx.hs index a0c15d59ed..42313e72e3 100644 --- a/plutus-chain-index-core/src/Plutus/ChainIndex/Tx.hs +++ b/plutus-chain-index-core/src/Plutus/ChainIndex/Tx.hs @@ -31,79 +31,20 @@ module Plutus.ChainIndex.Tx( , _ValidTx ) where -import Codec.Serialise (Serialise) -import Control.Lens (makeLenses, makePrisms) -import Data.Aeson (FromJSON, ToJSON) import Data.Map (Map) import Data.Map qualified as Map -import Data.OpenApi qualified as OpenApi import Data.Set (Set) import Data.Set qualified as Set import Data.Tuple (swap) -import GHC.Generics (Generic) -import Ledger (Address, CardanoTx (..), OnChainTx (..), SlotRange, SomeCardanoApiTx, TxId, TxIn (txInType), - TxInType (..), TxOut (txOutAddress), TxOutRef (..), consumableInputs, eitherTx, getCardanoTxData, - getCardanoTxId, getCardanoTxMintScripts, getCardanoTxOutputs, getCardanoTxValidityRange) +import Ledger (Address, CardanoTx (..), OnChainTx (..), SomeCardanoApiTx (SomeTx), Tx (..), TxIn (txInType), + TxInType (..), TxOut (txOutAddress), TxOutRef (..), txId) +import Plutus.Contract.CardanoAPI (fromCardanoTx, setValidity) import Plutus.Script.Utils.V1.Scripts (datumHash, mintingPolicyHash, redeemerHash, validatorHash) import Plutus.V1.Ledger.Scripts (Datum, DatumHash, MintingPolicy (getMintingPolicy), MintingPolicyHash (MintingPolicyHash), Redeemer (..), RedeemerHash, Script, ScriptHash (..), Validator (getValidator), ValidatorHash (ValidatorHash)) -import Prettyprinter --- | List of outputs of a transaction. There are no outputs if the transaction --- is invalid. -data ChainIndexTxOutputs = - InvalidTx -- ^ The transaction is invalid so there is no outputs - | ValidTx [TxOut] - deriving (Show, Eq, Generic, ToJSON, FromJSON, Serialise, OpenApi.ToSchema) - -makePrisms ''ChainIndexTxOutputs - -data ChainIndexTx = ChainIndexTx { - _citxTxId :: TxId, - -- ^ The id of this transaction. - _citxInputs :: Set TxIn, - -- ^ The inputs to this transaction. - _citxOutputs :: ChainIndexTxOutputs, - -- ^ The outputs of this transaction, ordered so they can be referenced by index. - _citxValidRange :: !SlotRange, - -- ^ The 'SlotRange' during which this transaction may be validated. - _citxData :: Map DatumHash Datum, - -- ^ Datum objects recorded on this transaction. - _citxRedeemers :: Map RedeemerHash Redeemer, - -- ^ Redeemers of the minting scripts. - _citxScripts :: Map ScriptHash Script, - -- ^ The scripts (validator, stake validator or minting) part of cardano tx. - _citxCardanoTx :: Maybe SomeCardanoApiTx - -- ^ The full Cardano API tx which was used to populate the rest of the - -- 'ChainIndexTx' fields. Useful because 'ChainIndexTx' doesn't have all the - -- details of the tx, so we keep it as a safety net. Might be Nothing if we - -- are in the emulator. - } deriving (Show, Eq, Generic, ToJSON, FromJSON, Serialise, OpenApi.ToSchema) - -makeLenses ''ChainIndexTx - -instance Pretty ChainIndexTx where - pretty ChainIndexTx{_citxTxId, _citxInputs, _citxOutputs = ValidTx outputs, _citxValidRange, _citxData, _citxRedeemers, _citxScripts} = - let lines' = - [ hang 2 (vsep ("inputs:" : fmap pretty (Set.toList _citxInputs))) - , hang 2 (vsep ("outputs:" : fmap pretty outputs)) - , hang 2 (vsep ("scripts hashes:": fmap (pretty . fst) (Map.toList _citxScripts))) - , "validity range:" <+> viaShow _citxValidRange - , hang 2 (vsep ("data:": fmap (pretty . snd) (Map.toList _citxData) )) - , hang 2 (vsep ("redeemers:": fmap (pretty . snd) (Map.toList _citxRedeemers) )) - ] - in nest 2 $ vsep ["Valid tx" <+> pretty _citxTxId <> colon, braces (vsep lines')] - pretty ChainIndexTx{_citxTxId, _citxInputs, _citxOutputs = InvalidTx, _citxValidRange, _citxData, _citxRedeemers, _citxScripts} = - let lines' = - [ hang 2 (vsep ("inputs:" : fmap pretty (Set.toList _citxInputs))) - , hang 2 (vsep ["no outputs:"]) - , hang 2 (vsep ("scripts hashes:": fmap (pretty . fst) (Map.toList _citxScripts))) - , "validity range:" <+> viaShow _citxValidRange - , hang 2 (vsep ("data:": fmap (pretty . snd) (Map.toList _citxData) )) - , hang 2 (vsep ("redeemers:": fmap (pretty . snd) (Map.toList _citxRedeemers) )) - ] - in nest 2 $ vsep ["Invalid tx" <+> pretty _citxTxId <> colon, braces (vsep lines')] +import Plutus.ChainIndex.Types -- | Get tx output references from tx. txOutRefs :: ChainIndexTx -> [TxOutRef] @@ -130,22 +71,39 @@ txOutRefMapForAddr addr tx = -- produce any 'ChainIndexTx' outputs and the collateral inputs of the -- 'OnChainTx' will be the inputs of the 'ChainIndexTx'. fromOnChainTx :: OnChainTx -> ChainIndexTx -fromOnChainTx tx = - let inputs = consumableInputs tx - (validatorHashes, otherDataHashes, redeemers) = validators inputs - getCardanoApiTx (CardanoApiTx ctx) = Just ctx - getCardanoApiTx (Both _ ctx) = Just ctx - getCardanoApiTx (EmulatorTx _) = Nothing - in ChainIndexTx - { _citxTxId = eitherTx getCardanoTxId getCardanoTxId tx - , _citxInputs = inputs - , _citxOutputs = eitherTx (const InvalidTx) (ValidTx . getCardanoTxOutputs) tx - , _citxValidRange = eitherTx getCardanoTxValidityRange getCardanoTxValidityRange tx - , _citxData = eitherTx getCardanoTxData getCardanoTxData tx <> otherDataHashes - , _citxRedeemers = redeemers - , _citxScripts = mintingPolicies (eitherTx getCardanoTxMintScripts getCardanoTxMintScripts tx) <> validatorHashes - , _citxCardanoTx = eitherTx getCardanoApiTx getCardanoApiTx tx - } +fromOnChainTx = \case + Valid (EmulatorTx tx@Tx{txInputs, txOutputs, txValidRange, txData, txMintScripts}) -> + let (validatorHashes, otherDataHashes, redeemers) = validators txInputs in + ChainIndexTx + { _citxTxId = txId tx + , _citxInputs = txInputs + , _citxOutputs = ValidTx txOutputs + , _citxValidRange = txValidRange + , _citxData = txData <> otherDataHashes + , _citxRedeemers = redeemers + , _citxScripts = mintingPolicies txMintScripts <> validatorHashes + , _citxCardanoTx = Nothing + } + Invalid (EmulatorTx tx@Tx{txCollateral, txValidRange, txData, txInputs, txMintScripts}) -> + let (validatorHashes, otherDataHashes, redeemers) = validators txInputs in + ChainIndexTx + { _citxTxId = txId tx + , _citxInputs = txCollateral + , _citxOutputs = InvalidTx + , _citxValidRange = txValidRange + , _citxData = txData <> otherDataHashes + , _citxRedeemers = redeemers + , _citxScripts = mintingPolicies txMintScripts <> validatorHashes + , _citxCardanoTx = Nothing + } + Valid (CardanoApiTx someTx) -> fromOnChainCardanoTx True someTx + Invalid (CardanoApiTx someTx) -> fromOnChainCardanoTx False someTx + Valid (Both _ someTx) -> fromOnChainCardanoTx True someTx + Invalid (Both _ someTx) -> fromOnChainCardanoTx False someTx + +fromOnChainCardanoTx :: Bool -> SomeCardanoApiTx -> ChainIndexTx +fromOnChainCardanoTx validity (SomeTx tx era) = + either (error . ("Plutus.ChainIndex.Tx.fromOnChainCardanoTx: " ++) . show) id $ fromCardanoTx era $ setValidity validity tx mintingPolicies :: Set MintingPolicy -> Map ScriptHash Script mintingPolicies = Map.fromList . fmap withHash . Set.toList diff --git a/plutus-chain-index-core/src/Plutus/ChainIndex/Types.hs b/plutus-chain-index-core/src/Plutus/ChainIndex/Types.hs index f33ca2af99..bc96bfe7de 100644 --- a/plutus-chain-index-core/src/Plutus/ChainIndex/Types.hs +++ b/plutus-chain-index-core/src/Plutus/ChainIndex/Types.hs @@ -8,7 +8,9 @@ {-| Misc. types used in this package -} module Plutus.ChainIndex.Types( - BlockId(..) + ChainIndexTx(..) + , ChainIndexTxOutputs(..) + , BlockId(..) , blockId , Tip(..) , Point(..) @@ -37,6 +39,17 @@ module Plutus.ChainIndex.Types( , tobSpentOutputs , ChainSyncBlock(..) , TxProcessOption(..) + -- ** Lenses + , citxTxId + , citxInputs + , citxOutputs + , citxValidRange + , citxData + , citxRedeemers + , citxScripts + , citxCardanoTx + , _InvalidTx + , _ValidTx ) where import Codec.Serialise (Serialise) @@ -57,16 +70,70 @@ import Data.Set (Set) import Data.Set qualified as Set import Data.Word (Word64) import GHC.Generics (Generic) -import Ledger (TxOutRef (..)) +import Ledger (SlotRange, SomeCardanoApiTx, TxIn, TxOut, TxOutRef (..)) import Ledger.Blockchain (BlockId (..)) import Ledger.Blockchain qualified as Ledger import Ledger.Slot (Slot) import Ledger.TxId (TxId) +import Plutus.V1.Ledger.Scripts (Datum, DatumHash, Redeemer, RedeemerHash, Script, ScriptHash) import PlutusTx.Lattice (MeetSemiLattice (..)) -import Prettyprinter (Pretty (..), comma, (<+>)) +import Prettyprinter import Prettyprinter.Extras (PrettyShow (..)) -import Plutus.ChainIndex.Tx (ChainIndexTx) +-- | List of outputs of a transaction. There are no outputs if the transaction +-- is invalid. +data ChainIndexTxOutputs = + InvalidTx -- ^ The transaction is invalid so there is no outputs + | ValidTx [TxOut] + deriving (Show, Eq, Generic, ToJSON, FromJSON, Serialise, OpenApi.ToSchema) + +makePrisms ''ChainIndexTxOutputs + +data ChainIndexTx = ChainIndexTx { + _citxTxId :: TxId, + -- ^ The id of this transaction. + _citxInputs :: Set TxIn, + -- ^ The inputs to this transaction. + _citxOutputs :: ChainIndexTxOutputs, + -- ^ The outputs of this transaction, ordered so they can be referenced by index. + _citxValidRange :: !SlotRange, + -- ^ The 'SlotRange' during which this transaction may be validated. + _citxData :: Map DatumHash Datum, + -- ^ Datum objects recorded on this transaction. + _citxRedeemers :: Map RedeemerHash Redeemer, + -- ^ Redeemers of the minting scripts. + _citxScripts :: Map ScriptHash Script, + -- ^ The scripts (validator, stake validator or minting) part of cardano tx. + _citxCardanoTx :: Maybe SomeCardanoApiTx + -- ^ The full Cardano API tx which was used to populate the rest of the + -- 'ChainIndexTx' fields. Useful because 'ChainIndexTx' doesn't have all the + -- details of the tx, so we keep it as a safety net. Might be Nothing if we + -- are in the emulator. + } deriving (Show, Eq, Generic, ToJSON, FromJSON, Serialise, OpenApi.ToSchema) + +makeLenses ''ChainIndexTx + +instance Pretty ChainIndexTx where + pretty ChainIndexTx{_citxTxId, _citxInputs, _citxOutputs = ValidTx outputs, _citxValidRange, _citxData, _citxRedeemers, _citxScripts} = + let lines' = + [ hang 2 (vsep ("inputs:" : fmap pretty (Set.toList _citxInputs))) + , hang 2 (vsep ("outputs:" : fmap pretty outputs)) + , hang 2 (vsep ("scripts hashes:": fmap (pretty . fst) (Map.toList _citxScripts))) + , "validity range:" <+> viaShow _citxValidRange + , hang 2 (vsep ("data:": fmap (pretty . snd) (Map.toList _citxData) )) + , hang 2 (vsep ("redeemers:": fmap (pretty . snd) (Map.toList _citxRedeemers) )) + ] + in nest 2 $ vsep ["Valid tx" <+> pretty _citxTxId <> colon, braces (vsep lines')] + pretty ChainIndexTx{_citxTxId, _citxInputs, _citxOutputs = InvalidTx, _citxValidRange, _citxData, _citxRedeemers, _citxScripts} = + let lines' = + [ hang 2 (vsep ("inputs:" : fmap pretty (Set.toList _citxInputs))) + , hang 2 (vsep ["no outputs:"]) + , hang 2 (vsep ("scripts hashes:": fmap (pretty . fst) (Map.toList _citxScripts))) + , "validity range:" <+> viaShow _citxValidRange + , hang 2 (vsep ("data:": fmap (pretty . snd) (Map.toList _citxData) )) + , hang 2 (vsep ("redeemers:": fmap (pretty . snd) (Map.toList _citxRedeemers) )) + ] + in nest 2 $ vsep ["Invalid tx" <+> pretty _citxTxId <> colon, braces (vsep lines')] -- | Compute a hash of the block's contents. blockId :: Ledger.Block -> BlockId diff --git a/plutus-chain-index-core/src/Plutus/Contract/CardanoAPI.hs b/plutus-chain-index-core/src/Plutus/Contract/CardanoAPI.hs index ff326ef0f9..80e926e0b1 100644 --- a/plutus-chain-index-core/src/Plutus/Contract/CardanoAPI.hs +++ b/plutus-chain-index-core/src/Plutus/Contract/CardanoAPI.hs @@ -11,15 +11,16 @@ Interface to the transaction types from 'cardano-api' module Plutus.Contract.CardanoAPI( fromCardanoBlock , fromCardanoTx + , setValidity , module Export ) where import Cardano.Api qualified as C +import Cardano.Api.Shelley qualified as C import Data.Set qualified as Set import Ledger qualified as P import Ledger.Tx.CardanoAPI as Export -import Plutus.ChainIndex.Tx (ChainIndexTx (..)) -import Plutus.ChainIndex.Tx qualified as ChainIndex.Tx +import Plutus.ChainIndex.Types (ChainIndexTx (..), ChainIndexTxOutputs (..)) fromCardanoBlock :: C.BlockInMode C.CardanoMode -> Either FromCardanoError [ChainIndexTx] fromCardanoBlock (C.BlockInMode (C.Block C.BlockHeader {} txs) eraInMode) = @@ -49,10 +50,15 @@ fromCardanoTx eraInMode tx@(C.Tx txBody@(C.TxBody C.TxBodyContent{..}) _) = do -- If the transaction is invalid, we use collateral inputs , _citxInputs = Set.fromList $ fmap ((`P.TxIn` Nothing) . fromCardanoTxIn) inputs -- No outputs if the one of scripts failed - , _citxOutputs = if isTxScriptValid then ChainIndex.Tx.ValidTx txOutputs - else ChainIndex.Tx.InvalidTx + , _citxOutputs = if isTxScriptValid then ValidTx txOutputs + else InvalidTx , _citxData = datums , _citxRedeemers = redeemers , _citxScripts = scriptMap , _citxCardanoTx = Just $ SomeTx tx eraInMode } + +setValidity :: Bool -> C.Tx era -> C.Tx era +setValidity validity (C.Tx (C.ShelleyTxBody C.ShelleyBasedEraAlonzo txBody scripts dat aux _) era) = + C.Tx (C.ShelleyTxBody C.ShelleyBasedEraAlonzo txBody scripts dat aux (toTxScriptValidity validity)) era +setValidity _ tx = tx -- only applies in Alonzo era diff --git a/plutus-ledger/src/Ledger/Tx.hs b/plutus-ledger/src/Ledger/Tx.hs index 60d391cf5a..c598d36fb1 100644 --- a/plutus-ledger/src/Ledger/Tx.hs +++ b/plutus-ledger/src/Ledger/Tx.hs @@ -41,7 +41,6 @@ module Ledger.Tx , getCardanoTxUnspentOutputsTx , getCardanoTxFee , getCardanoTxMint - , getCardanoTxMintScripts , getCardanoTxValidityRange , getCardanoTxData , SomeCardanoApiTx(.., CardanoApiEmulatorEraTx) @@ -79,7 +78,7 @@ import Ledger.Crypto (Passphrase, PrivateKey, signTx, signTx', toPublicKey) import Ledger.Orphans () import Ledger.Tx.CardanoAPI (SomeCardanoApiTx (SomeTx), ToCardanoError (..)) import Ledger.Tx.CardanoAPI qualified as CardanoAPI -import Plutus.Script.Utils.V1.Scripts (MintingPolicy, datumHash) +import Plutus.Script.Utils.V1.Scripts (datumHash) import Plutus.V1.Ledger.Api (Credential (PubKeyCredential, ScriptCredential), Datum (Datum), DatumHash, TxId (TxId), Validator, ValidatorHash, Value, addressCredential, toBuiltin) import Plutus.V1.Ledger.Slot (SlotRange) @@ -217,9 +216,6 @@ getCardanoTxFee = onCardanoTx txFee (\(SomeTx (C.Tx (C.TxBody C.TxBodyContent {. getCardanoTxMint :: CardanoTx -> Value getCardanoTxMint = onCardanoTx txMint (\(SomeTx (C.Tx (C.TxBody C.TxBodyContent {..}) _) _) -> CardanoAPI.fromCardanoMintValue txMintValue) -getCardanoTxMintScripts :: CardanoTx -> Set MintingPolicy -getCardanoTxMintScripts = onCardanoTx txMintScripts (const mempty) -- (error "getCardanoTxMintScripts: TODO") - getCardanoTxValidityRange :: CardanoTx -> SlotRange getCardanoTxValidityRange = onCardanoTx txValidRange (\(SomeTx (C.Tx (C.TxBody C.TxBodyContent {..}) _) _) -> CardanoAPI.fromCardanoValidityRange txValidityRange) diff --git a/plutus-ledger/src/Ledger/Tx/CardanoAPI.hs b/plutus-ledger/src/Ledger/Tx/CardanoAPI.hs index 64a55ed551..92b50d23f2 100644 --- a/plutus-ledger/src/Ledger/Tx/CardanoAPI.hs +++ b/plutus-ledger/src/Ledger/Tx/CardanoAPI.hs @@ -38,6 +38,7 @@ module Ledger.Tx.CardanoAPI( , fromCardanoPaymentKeyHash , fromCardanoScriptData , fromTxScriptValidity + , toTxScriptValidity , scriptDataFromCardanoTxBody , plutusScriptsFromTxBody , makeTransactionBody @@ -282,6 +283,10 @@ fromTxScriptValidity C.TxScriptValidityNone fromTxScriptValidity (C.TxScriptValidity C.TxScriptValiditySupportedInAlonzoEra C.ScriptValid) = True fromTxScriptValidity (C.TxScriptValidity C.TxScriptValiditySupportedInAlonzoEra C.ScriptInvalid) = False +toTxScriptValidity :: Bool -> C.TxScriptValidity C.AlonzoEra +toTxScriptValidity True = C.TxScriptValidity C.TxScriptValiditySupportedInAlonzoEra C.ScriptValid +toTxScriptValidity False = C.TxScriptValidity C.TxScriptValiditySupportedInAlonzoEra C.ScriptInvalid + -- | Given a 'C.TxBody from a 'C.Tx era', return the datums and redeemers along -- with their hashes. scriptDataFromCardanoTxBody diff --git a/plutus-pab/src/Cardano/Node/Client.hs b/plutus-pab/src/Cardano/Node/Client.hs index 787366d34c..45d64f48bc 100644 --- a/plutus-pab/src/Cardano/Node/Client.hs +++ b/plutus-pab/src/Cardano/Node/Client.hs @@ -61,7 +61,7 @@ handleNodeClientClient params e = do Just handle -> liftIO $ onCardanoTx (MockClient.queueTx handle) - (error "Cardano.Node.Client: Expecting a mock tx, not an Alonzo tx when publishing it.") + (const $ error "Cardano.Node.Client: Expecting a mock tx, not an Alonzo tx when publishing it.") tx GetClientSlot -> either (liftIO . MockClient.getCurrentSlot)