Skip to content

Commit

Permalink
Add some helper functions for Plutus V3 ScriptContext (#5688)
Browse files Browse the repository at this point in the history
  • Loading branch information
zliu41 authored Dec 20, 2023
1 parent 3b6eb36 commit ff55dcc
Show file tree
Hide file tree
Showing 2 changed files with 145 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

### Added

- Added some helper functions for Plutus V3 ScriptContext.
144 changes: 141 additions & 3 deletions plutus-ledger-api/src/PlutusLedgerApi/V3/Contexts.hs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
Expand Down Expand Up @@ -243,9 +244,10 @@ instance PlutusTx.Eq ProtocolVersion where
ProtocolVersion a b == ProtocolVersion a' b' =
a PlutusTx.== a' PlutusTx.&& b PlutusTx.== b'

-- | A Plutus Data object containing proposed parameter changes. The Data object contains
-- a @Map@ with one entry per changed parameter, from the parameter name to the new value.
-- Unchanged parameters are not included.
{- | A Plutus Data object containing proposed parameter changes. The Data object contains
a @Map@ with one entry per changed parameter, from the parameter name to the new value.
Unchanged parameters are not included.
-}
newtype ChangedParameters = ChangedParameters {getChangedParameters :: PlutusTx.BuiltinData}
deriving stock (Generic, Haskell.Show)
deriving newtype
Expand Down Expand Up @@ -421,6 +423,142 @@ instance PlutusTx.Eq ScriptContext where
ScriptContext a b == ScriptContext a' b' =
a PlutusTx.== a' PlutusTx.&& b PlutusTx.== b'

{-# INLINEABLE findOwnInput #-}

-- | Find the input currently being validated.
findOwnInput :: ScriptContext -> Haskell.Maybe V2.TxInInfo
findOwnInput
ScriptContext
{ scriptContextTxInfo = TxInfo{txInfoInputs}
, scriptContextPurpose = Spending txOutRef
} =
PlutusTx.find
(\V2.TxInInfo{txInInfoOutRef} -> txInInfoOutRef PlutusTx.== txOutRef)
txInfoInputs
findOwnInput _ = Haskell.Nothing

{-# INLINEABLE findDatum #-}

-- | Find the data corresponding to a data hash, if there is one
findDatum :: V2.DatumHash -> TxInfo -> Haskell.Maybe V2.Datum
findDatum dsh TxInfo{txInfoData} = lookup dsh txInfoData

{-# INLINEABLE findDatumHash #-}

{- | Find the hash of a datum, if it is part of the pending transaction's
hashes
-}
findDatumHash :: V2.Datum -> TxInfo -> Haskell.Maybe V2.DatumHash
findDatumHash ds TxInfo{txInfoData} =
PlutusTx.fst PlutusTx.<$> PlutusTx.find f (toList txInfoData)
where
f (_, ds') = ds' PlutusTx.== ds

{-# INLINEABLE findTxInByTxOutRef #-}

{- | Given a UTXO reference and a transaction (`TxInfo`), resolve it to one of the
transaction's inputs (`TxInInfo`).
Note: this only searches the true transaction inputs and not the referenced transaction inputs.
-}
findTxInByTxOutRef :: V2.TxOutRef -> TxInfo -> Haskell.Maybe V2.TxInInfo
findTxInByTxOutRef outRef TxInfo{txInfoInputs} =
PlutusTx.find
(\V2.TxInInfo{txInInfoOutRef} -> txInInfoOutRef PlutusTx.== outRef)
txInfoInputs

{-# INLINEABLE findContinuingOutputs #-}

{- | Find the indices of all the outputs that pay to the same script address we are
currently spending from, if any.
-}
findContinuingOutputs :: ScriptContext -> [Haskell.Integer]
findContinuingOutputs ctx
| Haskell.Just V2.TxInInfo{txInInfoResolved = V2.TxOut{txOutAddress}} <-
findOwnInput ctx =
PlutusTx.findIndices
(f txOutAddress)
(txInfoOutputs (scriptContextTxInfo ctx))
where
f addr V2.TxOut{txOutAddress = otherAddress} = addr PlutusTx.== otherAddress
findContinuingOutputs _ = PlutusTx.traceError "Le" -- "Can't find any continuing outputs"

{-# INLINEABLE getContinuingOutputs #-}

{- | Get all the outputs that pay to the same script address we are currently spending
from, if any.
-}
getContinuingOutputs :: ScriptContext -> [V2.TxOut]
getContinuingOutputs ctx
| Haskell.Just V2.TxInInfo{txInInfoResolved = V2.TxOut{txOutAddress}} <-
findOwnInput ctx =
PlutusTx.filter (f txOutAddress) (txInfoOutputs (scriptContextTxInfo ctx))
where
f addr V2.TxOut{txOutAddress = otherAddress} = addr PlutusTx.== otherAddress
getContinuingOutputs _ = PlutusTx.traceError "Lf" -- "Can't get any continuing outputs"

{-# INLINEABLE txSignedBy #-}

-- | Check if a transaction was signed by the given public key.
txSignedBy :: TxInfo -> V2.PubKeyHash -> Haskell.Bool
txSignedBy TxInfo{txInfoSignatories} k = case PlutusTx.find ((PlutusTx.==) k) txInfoSignatories of
Haskell.Just _ -> Haskell.True
Haskell.Nothing -> Haskell.False

{-# INLINEABLE pubKeyOutputsAt #-}

-- | Get the values paid to a public key address by a pending transaction.
pubKeyOutputsAt :: V2.PubKeyHash -> TxInfo -> [V2.Value]
pubKeyOutputsAt pk p =
let flt V2.TxOut{txOutAddress = V2.Address (V2.PubKeyCredential pk') _, txOutValue}
| pk PlutusTx.== pk' = Haskell.Just txOutValue
flt _ = Haskell.Nothing
in PlutusTx.mapMaybe flt (txInfoOutputs p)

{-# INLINEABLE valuePaidTo #-}

-- | Get the total value paid to a public key address by a pending transaction.
valuePaidTo :: TxInfo -> V2.PubKeyHash -> V2.Value
valuePaidTo ptx pkh = PlutusTx.mconcat (pubKeyOutputsAt pkh ptx)

{-# INLINEABLE valueSpent #-}

-- | Get the total value of inputs spent by this transaction.
valueSpent :: TxInfo -> V2.Value
valueSpent =
PlutusTx.foldMap
(V2.txOutValue PlutusTx.. V2.txInInfoResolved)
PlutusTx.. txInfoInputs

{-# INLINEABLE valueProduced #-}

-- | Get the total value of outputs produced by this transaction.
valueProduced :: TxInfo -> V2.Value
valueProduced = PlutusTx.foldMap V2.txOutValue PlutusTx.. txInfoOutputs

{-# INLINEABLE ownCurrencySymbol #-}

-- | The 'CurrencySymbol' of the current validator script.
ownCurrencySymbol :: ScriptContext -> V2.CurrencySymbol
ownCurrencySymbol ScriptContext{scriptContextPurpose = Minting cs} = cs
ownCurrencySymbol _ =
-- "Can't get currency symbol of the current validator script"
PlutusTx.traceError "Lh"

{-# INLINEABLE spendsOutput #-}

{- | Check if the pending transaction spends a specific transaction output
(identified by the hash of a transaction and an index into that
transactions' outputs)
-}
spendsOutput :: TxInfo -> V2.TxId -> Haskell.Integer -> Haskell.Bool
spendsOutput p h i =
let spendsOutRef inp =
let outRef = V2.txInInfoOutRef inp
in h PlutusTx.== V2.txOutRefId outRef
PlutusTx.&& i PlutusTx.== V2.txOutRefIdx outRef
in PlutusTx.any spendsOutRef (txInfoInputs p)

PlutusTx.makeLift ''ColdCommitteeCredential
PlutusTx.makeLift ''HotCommitteeCredential
PlutusTx.makeLift ''DRepCredential
Expand Down

0 comments on commit ff55dcc

Please sign in to comment.