Skip to content

Commit

Permalink
plutus-chain-index: Use fromCardanoTx in fromOnChainTx
Browse files Browse the repository at this point in the history
  • Loading branch information
sjoerdvisscher committed Jun 9, 2022
1 parent 6eeb972 commit 7700412
Show file tree
Hide file tree
Showing 6 changed files with 125 additions and 93 deletions.
116 changes: 37 additions & 79 deletions plutus-chain-index-core/src/Plutus/ChainIndex/Tx.hs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand All @@ -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
Expand Down
75 changes: 71 additions & 4 deletions plutus-chain-index-core/src/Plutus/ChainIndex/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
{-| Misc. types used in this package
-}
module Plutus.ChainIndex.Types(
BlockId(..)
ChainIndexTx(..)
, ChainIndexTxOutputs(..)
, BlockId(..)
, blockId
, Tip(..)
, Point(..)
Expand Down Expand Up @@ -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)
Expand All @@ -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
Expand Down
14 changes: 10 additions & 4 deletions plutus-chain-index-core/src/Plutus/Contract/CardanoAPI.hs
Original file line number Diff line number Diff line change
Expand Up @@ -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) =
Expand Down Expand Up @@ -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
6 changes: 1 addition & 5 deletions plutus-ledger/src/Ledger/Tx.hs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ module Ledger.Tx
, getCardanoTxUnspentOutputsTx
, getCardanoTxFee
, getCardanoTxMint
, getCardanoTxMintScripts
, getCardanoTxValidityRange
, getCardanoTxData
, SomeCardanoApiTx(.., CardanoApiEmulatorEraTx)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
5 changes: 5 additions & 0 deletions plutus-ledger/src/Ledger/Tx/CardanoAPI.hs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ module Ledger.Tx.CardanoAPI(
, fromCardanoPaymentKeyHash
, fromCardanoScriptData
, fromTxScriptValidity
, toTxScriptValidity
, scriptDataFromCardanoTxBody
, plutusScriptsFromTxBody
, makeTransactionBody
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion plutus-pab/src/Cardano/Node/Client.hs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down

0 comments on commit 7700412

Please sign in to comment.