Skip to content

Commit

Permalink
[PLT-81] plutus-chain-index: support inline scripts when querying TxO…
Browse files Browse the repository at this point in the history
…ut of a TxOutRef (#613)

* Make plutus-ledger-api version explicit in Ledger.Tx

* Make pattern match explicit

* Reorder ScriptChainIndexTxOut fields

To highlight they are the same as PublicKeyChainIndexTxOut.

* Add datum to both branches of ChainIndexTxOut

- Unfortunately we need to rename the fields because they have now
  different types.
- In the PublicKeyChainIndexTxOut case, the output datum is optional and
  we can use plutus-ledger-api OutputDatum type.
- In the ScriptChainIndexTxOut case, the output datum is required,
  nothing changes here but the name.

* Add ReferenceScript to ChainIndexTxOut

* Remove old comment

* Rename _ciTxOutDatumPublicKey and _ciTxOutDatumScript into _ciTxOutPublicKeyDatum and _ciTxOutScriptDatum

* Introduce fromReferenceScript

* Add comments to ChainIndexTxOut
  • Loading branch information
andreabedini authored and eyeinsky committed Jul 25, 2022
1 parent a01afb6 commit 6954a7c
Show file tree
Hide file tree
Showing 16 changed files with 153 additions and 92 deletions.
2 changes: 1 addition & 1 deletion doc/plutus/tutorials/EscrowImpl.hs
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@ refund ::
refund inst escrow = do
pk <- ownFirstPaymentPubKeyHash
unspentOutputs <- utxosAt (Scripts.validatorAddress inst)
let flt _ ciTxOut = either id Scripts.datumHash (Tx._ciTxOutDatum ciTxOut) == Scripts.datumHash (Datum (PlutusTx.toBuiltinData pk))
let flt _ ciTxOut = either id Scripts.datumHash (Tx._ciTxOutScriptDatum ciTxOut) == Scripts.datumHash (Datum (PlutusTx.toBuiltinData pk))
tx' = Typed.collectFromScriptFilter flt unspentOutputs Refund
<> Constraints.mustValidateIn (from (Haskell.succ $ escrowDeadline escrow))
if Constraints.modifiesUtxoSet tx'
Expand Down
29 changes: 17 additions & 12 deletions plutus-chain-index-core/src/Plutus/ChainIndex/Emulator/Handlers.hs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ import Plutus.ChainIndex.Emulator.DiskState qualified as DiskState
import Plutus.ChainIndex.Tx (ChainIndexTx, ChainIndexTxOut (..), _ValidTx, citxOutputs)
import Plutus.ChainIndex.TxUtxoBalance qualified as TxUtxoBalance
import Plutus.ChainIndex.Types (ChainSyncBlock (..), Diagnostics (..), Point (PointAtGenesis), Tip (..),
TxProcessOption (..), TxUtxoBalance (..))
TxProcessOption (..), TxUtxoBalance (..), fromReferenceScript)
import Plutus.ChainIndex.UtxoState (InsertUtxoSuccess (..), RollbackResult (..), UtxoIndex, tip, utxoState)
import Plutus.ChainIndex.UtxoState qualified as UtxoState
import Plutus.V1.Ledger.Api (Credential (PubKeyCredential, ScriptCredential), MintingPolicy (MintingPolicy),
Expand Down Expand Up @@ -93,25 +93,27 @@ getTxOutFromRef ref@TxOutRef{txOutRefId, txOutRefIdx} = do
-- Find the output in the tx matching the output ref
case preview (txMap . ix txOutRefId . citxOutputs . _ValidTx . ix (fromIntegral txOutRefIdx)) ds of
Nothing -> logWarn (TxOutNotFound ref) >> pure Nothing
Just txout@ChainIndexTxOut{..} -> do
Just txout@(ChainIndexTxOut address value datum refScript) -> do
-- The output might come from a public key address or a script address.
-- We need to handle them differently.
case addressCredential citoAddress of
case addressCredential address of
PubKeyCredential _ ->
pure $ Just $ L.PublicKeyChainIndexTxOut citoAddress citoValue
pure $ Just $ L.PublicKeyChainIndexTxOut address value datum script
ScriptCredential vh@(ValidatorHash h) -> do
case citoDatum of
case datum of
OutputDatumHash dh -> do
let v = maybe (Left vh) (Right . Validator) $ preview (scriptMap . ix (ScriptHash h)) ds
let d = maybe (Left dh) Right $ preview (dataMap . ix dh) ds
pure $ Just $ L.ScriptChainIndexTxOut citoAddress v d citoValue
pure $ Just $ L.ScriptChainIndexTxOut address value d script v
OutputDatum d -> do
let v = maybe (Left vh) (Right . Validator) $ preview (scriptMap . ix (ScriptHash h)) ds
pure $ Just $ L.ScriptChainIndexTxOut citoAddress v (Right d) citoValue
pure $ Just $ L.ScriptChainIndexTxOut address value (Right d) script v
NoOutputDatum -> do
-- If the txout comes from a script address, the Datum should not be Nothing
logWarn $ NoDatumScriptAddr txout
pure Nothing
where
script = fromReferenceScript refScript


-- | Get the 'ChainIndexTxOut' for a 'TxOutRef'.
Expand All @@ -127,25 +129,28 @@ getUtxoutFromRef ref@TxOutRef{txOutRefId, txOutRefIdx} = do
-- Find the output in the tx matching the output ref
case preview (txMap . ix txOutRefId . citxOutputs . _ValidTx . ix (fromIntegral txOutRefIdx)) ds of
Nothing -> logWarn (TxOutNotFound ref) >> pure Nothing
Just txout -> do
Just txout@(ChainIndexTxOut address value datum refScript) -> do
-- The output might come from a public key address or a script address.
-- We need to handle them differently.
case addressCredential $ citoAddress txout of
PubKeyCredential _ ->
pure $ Just $ L.PublicKeyChainIndexTxOut (citoAddress txout) (citoValue txout)
pure $ Just $ L.PublicKeyChainIndexTxOut address value datum script
ScriptCredential vh@(ValidatorHash h) -> do
case citoDatum txout of
case datum of
OutputDatumHash dh -> do
let v = maybe (Left vh) (Right . Validator) $ preview (scriptMap . ix (ScriptHash h)) ds
let d = maybe (Left dh) Right $ preview (dataMap . ix dh) ds
pure $ Just $ L.ScriptChainIndexTxOut (citoAddress txout) v d (citoValue txout)
pure $ Just $ L.ScriptChainIndexTxOut address value d script v
OutputDatum d -> do
let v = maybe (Left vh) (Right . Validator) $ preview (scriptMap . ix (ScriptHash h)) ds
pure $ Just $ L.ScriptChainIndexTxOut (citoAddress txout) v (Right d) (citoValue txout)
pure $ Just $ L.ScriptChainIndexTxOut address value (Right d) script v
NoOutputDatum -> do
-- If the txout comes from a script address, the Datum should not be Nothing
logWarn $ NoDatumScriptAddr txout
pure Nothing
where
script = fromReferenceScript refScript


-- | Unspent outputs located at addresses with the given credential.
getUtxoSetAtAddress ::
Expand Down
20 changes: 11 additions & 9 deletions plutus-chain-index-core/src/Plutus/ChainIndex/Handlers.hs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE QuantifiedConstraints #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
Expand Down Expand Up @@ -60,7 +59,7 @@ import Plutus.ChainIndex.Effects (ChainIndexControlEffect (..), ChainIndexQueryE
import Plutus.ChainIndex.Tx
import Plutus.ChainIndex.TxUtxoBalance qualified as TxUtxoBalance
import Plutus.ChainIndex.Types (ChainSyncBlock (..), Depth (..), Diagnostics (..), Point (..), Tip (..),
TxProcessOption (..), TxUtxoBalance (..), tipAsPoint)
TxProcessOption (..), TxUtxoBalance (..), fromReferenceScript, tipAsPoint)
import Plutus.ChainIndex.UtxoState (InsertUtxoSuccess (..), RollbackResult (..), UtxoIndex)
import Plutus.ChainIndex.UtxoState qualified as UtxoState
import Plutus.V2.Ledger.Api (Credential (..), Datum (..), DatumHash (..), TxOutRef (..))
Expand Down Expand Up @@ -192,22 +191,25 @@ makeChainIndexTxOut ::
)
=> ChainIndexTxOut
-> Eff effs (Maybe L.ChainIndexTxOut)
makeChainIndexTxOut txout@ChainIndexTxOut{..} =
case addressCredential citoAddress of
PubKeyCredential _ -> pure $ Just $ L.PublicKeyChainIndexTxOut citoAddress citoValue
makeChainIndexTxOut txout@(ChainIndexTxOut address value datum refScript) =
case addressCredential address of
PubKeyCredential _ ->
pure $ Just $ L.PublicKeyChainIndexTxOut address value datum script
ScriptCredential vh ->
case citoDatum of
case datum of
OutputDatumHash dh -> do
v <- maybe (Left vh) Right <$> getScriptFromHash vh
d <- maybe (Left dh) Right <$> getDatumFromHash dh
pure $ Just $ L.ScriptChainIndexTxOut citoAddress v d citoValue
pure $ Just $ L.ScriptChainIndexTxOut address value d script v
OutputDatum d -> do
v <- maybe (Left vh) Right <$> getScriptFromHash vh
pure $ Just $ L.ScriptChainIndexTxOut citoAddress v (Right d) citoValue
_ -> do
pure $ Just $ L.ScriptChainIndexTxOut address value (Right d) script v
NoOutputDatum -> do
-- If the txout comes from a script address, the Datum should not be Nothing
logWarn $ NoDatumScriptAddr txout
pure Nothing
where
script = fromReferenceScript refScript

getUtxoSetAtAddress
:: forall effs.
Expand Down
6 changes: 6 additions & 0 deletions plutus-chain-index-core/src/Plutus/ChainIndex/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ module Plutus.ChainIndex.Types(
, citxCardanoTx
, _InvalidTx
, _ValidTx
, fromReferenceScript
) where

import Cardano.Api qualified as C
Expand Down Expand Up @@ -88,6 +89,7 @@ import Ledger (Address, SlotRange, SomeCardanoApiTx, TxIn (..), TxOutRef (..))
import Ledger.Blockchain (BlockId (..))
import Ledger.Blockchain qualified as Ledger
import Ledger.Slot (Slot)
import Ledger.Tx.CardanoAPI (fromCardanoScriptInAnyLang)
import Plutus.V1.Ledger.Scripts (Datum, DatumHash, Script, ScriptHash)
import Plutus.V1.Ledger.Tx (Redeemers, TxId)
import Plutus.V2.Ledger.Api (OutputDatum (..), Value (..))
Expand Down Expand Up @@ -140,6 +142,10 @@ instance Serialise C.ScriptInAnyLang where
instance OpenApi.ToSchema C.ScriptInAnyLang where
declareNamedSchema _ = pure $ OpenApi.NamedSchema (Just "ScriptInAnyLang") mempty

fromReferenceScript :: ReferenceScript -> Maybe Script
fromReferenceScript ReferenceScriptNone = Nothing
fromReferenceScript (ReferenceScriptInAnyLang sial) = fromCardanoScriptInAnyLang sial

data ChainIndexTxOut = ChainIndexTxOut
{ citoAddress :: Address -- ^ We can't use AddressInAnyEra here because of missing FromJson instance for Byron era
, citoValue :: Value
Expand Down
2 changes: 1 addition & 1 deletion plutus-contract/src/Plutus/Contract/StateMachine.hs
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ getStates
getStates (SM.StateMachineInstance _ si) refMap =
flip mapMaybe (Map.toList refMap) $ \(txOutRef, ciTxOut) -> do
let txOut = Tx.toTxOut ciTxOut
datum <- ciTxOut ^? Tx.ciTxOutDatum . _Right
datum <- ciTxOut ^? Tx.ciTxOutScriptDatum . _Right
ocsTxOutRef <- either (const Nothing) Just $ Typed.typeScriptTxOutRef si txOutRef txOut datum
pure OnChainState{ocsTxOutRef}

Expand Down
6 changes: 3 additions & 3 deletions plutus-ledger-constraints/src/Ledger/Constraints/OffChain.hs
Original file line number Diff line number Diff line change
Expand Up @@ -500,7 +500,7 @@ addOwnInput ScriptInputConstraint{icRedeemer, icTxOutRef} = do
$ do
(txOut, datum) <- maybe (throwError UnknownRef) pure $ do
ciTxOut <- Map.lookup icTxOutRef slTxOutputs
datum <- ciTxOut ^? Tx.ciTxOutDatum . _Right
datum <- ciTxOut ^? Tx.ciTxOutScriptDatum . _Right
pure (Tx.toTxOut ciTxOut, datum)
Typed.typeScriptTxOutRef inst icTxOutRef txOut datum
let txIn = Typed.makeTypedScriptTxIn inst icRedeemer typedOutRef
Expand Down Expand Up @@ -711,14 +711,14 @@ resolveScriptTxOut
, MonadError MkTxError m
)
=> ChainIndexTxOut -> m (Maybe (Validator, Datum, Value))
resolveScriptTxOut Tx.ScriptChainIndexTxOut { Tx._ciTxOutValidator, Tx._ciTxOutDatum, Tx._ciTxOutValue } = do
resolveScriptTxOut Tx.ScriptChainIndexTxOut { Tx._ciTxOutValidator, Tx._ciTxOutScriptDatum, Tx._ciTxOutValue } = do
-- first check in the 'ChainIndexTx' for the validator, then
-- look for it in the 'slOtherScripts map.
validator <- either lookupValidator pure _ciTxOutValidator

-- first check in the 'ChainIndexTx' for the datum, then
-- look for it in the 'slOtherData' map.
dataValue <- either lookupDatum pure _ciTxOutDatum
dataValue <- either lookupDatum pure _ciTxOutScriptDatum

pure $ Just (validator, dataValue, _ciTxOutValue)
resolveScriptTxOut _ = pure Nothing
14 changes: 12 additions & 2 deletions plutus-ledger-constraints/test/Spec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,12 @@ testScriptInputs lookups txc = property $ do


txOut0 :: Ledger.ChainIndexTxOut
txOut0 = Ledger.ScriptChainIndexTxOut (Ledger.Address (ScriptCredential alwaysSucceedValidatorHash) Nothing) (Left alwaysSucceedValidatorHash) (Right Ledger.unitDatum) mempty
txOut0 = Ledger.ScriptChainIndexTxOut
(Ledger.Address (ScriptCredential alwaysSucceedValidatorHash) Nothing)
mempty
(Right Ledger.unitDatum)
Nothing
(Left alwaysSucceedValidatorHash)

txOutRef0 :: Ledger.TxOutRef
txOutRef0 = Ledger.TxOutRef (Ledger.TxId "") 0
Expand Down Expand Up @@ -198,7 +203,12 @@ validatorHash1 :: Ledger.ValidatorHash
validatorHash1 = Scripts.validatorHash validator1

txOut1 :: Ledger.ChainIndexTxOut
txOut1 = Ledger.ScriptChainIndexTxOut (Ledger.Address (ScriptCredential validatorHash1) Nothing) (Left validatorHash1) (Right Ledger.unitDatum) mempty
txOut1 = Ledger.ScriptChainIndexTxOut
(Ledger.Address (ScriptCredential validatorHash1) Nothing)
mempty
(Right Ledger.unitDatum)
Nothing
(Left validatorHash1)

txOutRef1 :: Ledger.TxOutRef
txOutRef1 = Ledger.TxOutRef (Ledger.TxId "") 1
Expand Down
Loading

0 comments on commit 6954a7c

Please sign in to comment.