Skip to content
This repository was archived by the owner on Dec 2, 2024. It is now read-only.

Commit f54eca8

Browse files
authored
Add V2 TXInfo test to plutus e2e tests (#1002)
Add V2 TXInfo e2e test
1 parent cb4854f commit f54eca8

File tree

9 files changed

+305
-14
lines changed

9 files changed

+305
-14
lines changed

plutus-e2e-tests/plutus-e2e-tests.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ test-suite plutus-e2e-tests-test
9999
PlutusScripts.Helpers
100100
PlutusScripts.SECP256k1
101101
PlutusScripts.V1TxInfo
102+
PlutusScripts.V2TxInfo
102103
Spec.AlonzoFeatures
103104
Spec.BabbageFeatures
104105
Spec.Builtins.SECP256k1

plutus-e2e-tests/test/Helpers/Utils.hs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module Helpers.Utils where
33
import Cardano.Api qualified as C
44
import Control.Monad (when)
55
import Control.Monad.IO.Class (MonadIO, liftIO)
6+
import Data.Time.Clock.POSIX qualified as Time
67
import GHC.Stack qualified as GHC
78
import Hedgehog (MonadTest)
89
import Hedgehog qualified as H
@@ -59,3 +60,7 @@ maybeReadAs as path = do
5960
maybeEither . liftIO $ C.readFileTextEnvelope as path'
6061
where
6162
maybeEither m = m >>= return . either (const Nothing) Just
63+
64+
-- | Convert a 'POSIXTime' to the number of milliseconds since the Unix epoch.
65+
posixToMilliseconds :: Time.POSIXTime -> Integer
66+
posixToMilliseconds posixTime = round $ 1000 * (realToFrac posixTime :: Double)

plutus-e2e-tests/test/PlutusScripts/AlwaysSucceeds.hs

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,26 +12,42 @@ module PlutusScripts.AlwaysSucceeds (
1212
alwaysSucceedPolicyScriptV2
1313
, alwaysSucceedAssetIdV2
1414
, alwaysSucceedMintWitnessV2
15+
, alwaysSucceedMintWitnessV2'
16+
, alwaysSucceedPolicyTxInfoRedeemerV2
1517

1618
, alwaysSucceedSpendScriptV2
1719
, alwaysSucceedSpendScriptHashV2
1820
, alwaysSucceedSpendWitnessV2
1921
) where
2022

2123
import Cardano.Api qualified as C
22-
import Plutus.V1.Ledger.Api (MintingPolicy, Validator, mkMintingPolicyScript, mkValidatorScript)
23-
import PlutusScripts.Helpers (mintScriptWitness, plutusL2, policyIdV2, policyScript, spendScriptWitness, toScriptData,
24-
validatorScript)
24+
import Plutus.V1.Ledger.Api (MintingPolicy, Redeemer, ScriptPurpose (Minting), Validator, mkMintingPolicyScript,
25+
mkValidatorScript)
26+
import Plutus.V1.Ledger.Api qualified as BI
27+
import Plutus.V2.Ledger.Api qualified as PlutusV2 (Map)
28+
import PlutusScripts.Helpers (asRedeemer, fromPolicyId, mintScriptWitness, mintScriptWitness', plutusL2, policyIdV2,
29+
policyScript, spendScriptWitness, toScriptData, validatorScript)
2530
import PlutusTx qualified
31+
import PlutusTx.AssocMap qualified as AMap
32+
33+
-- AlwaysSucceeds minting policy --
2634

2735
alwaysSucceedPolicy :: MintingPolicy
2836
alwaysSucceedPolicy = mkMintingPolicyScript $$(PlutusTx.compile [|| \_ _ -> () ||])
2937

3038
alwaysSucceedPolicyScriptV2 :: C.PlutusScript C.PlutusScriptV2
3139
alwaysSucceedPolicyScriptV2 = policyScript alwaysSucceedPolicy
3240

41+
alwaysSucceedPolicyIdV2 :: C.PolicyId
42+
alwaysSucceedPolicyIdV2 = policyIdV2 alwaysSucceedPolicy
43+
3344
alwaysSucceedAssetIdV2 :: C.AssetId
34-
alwaysSucceedAssetIdV2 = C.AssetId (policyIdV2 alwaysSucceedPolicy) ""
45+
alwaysSucceedAssetIdV2 = C.AssetId alwaysSucceedPolicyIdV2 ""
46+
47+
alwaysSucceedPolicyTxInfoRedeemerV2 :: PlutusV2.Map ScriptPurpose Redeemer
48+
alwaysSucceedPolicyTxInfoRedeemerV2 = AMap.singleton
49+
(Minting $ fromPolicyId alwaysSucceedPolicyIdV2)
50+
(asRedeemer $ BI.toBuiltinData ())
3551

3652
-- | Witness token mint for including in txbody's txMintValue
3753
-- Use Nothing to include script in witness, else provide TxIn to reference script
@@ -45,6 +61,13 @@ alwaysSucceedMintWitnessV2 era (Just refTxIn) =
4561
(policyIdV2 alwaysSucceedPolicy,
4662
mintScriptWitness era plutusL2 (Right refTxIn) (toScriptData ()))
4763

64+
alwaysSucceedMintWitnessV2' :: C.CardanoEra era
65+
-> C.ExecutionUnits
66+
-> (C.PolicyId, C.ScriptWitness C.WitCtxMint era)
67+
alwaysSucceedMintWitnessV2' era exunits =
68+
(policyIdV2 alwaysSucceedPolicy,
69+
mintScriptWitness' era plutusL2 (Left alwaysSucceedPolicyScriptV2) (toScriptData ()) exunits)
70+
4871
-- AlwaysSucceeds validator --
4972

5073
alwaysSucceedSpend :: Validator

plutus-e2e-tests/test/PlutusScripts/Helpers.hs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,13 @@ import Codec.Serialise (serialise)
1212
import Data.ByteString qualified as BS (ByteString)
1313
import Data.ByteString.Lazy qualified as LBS
1414
import Data.ByteString.Short qualified as SBS
15+
import Plutus.Script.Utils.Value (CurrencySymbol)
1516
import Plutus.V1.Ledger.Api (MintingPolicy, Validator, unMintingPolicyScript, unValidatorScript)
17+
import Plutus.V1.Ledger.Api qualified as PlutusV1
1618
import Plutus.V1.Ledger.Bytes qualified as P (bytes, fromHex)
19+
import Plutus.V1.Ledger.Scripts (Datum (Datum), Redeemer (Redeemer))
1720
import PlutusTx qualified
21+
import PlutusTx.Builtins qualified as BI
1822

1923
-- | Treat string of hexidecimal bytes literally, without encoding. Useful for hashes.
2024
bytesFromHex :: BS.ByteString -> BS.ByteString
@@ -32,6 +36,12 @@ defExecutionUnits = C.ExecutionUnits {C.executionSteps = 0, C.executionMemory =
3236
toScriptData :: PlutusTx.ToData a => a -> C.ScriptData
3337
toScriptData a = C.fromPlutusData $ PlutusTx.toData a
3438

39+
asRedeemer :: PlutusTx.ToData a => a -> Redeemer
40+
asRedeemer a = Redeemer $ PlutusTx.dataToBuiltinData $ PlutusTx.toData a
41+
42+
asDatum :: PlutusTx.ToData a => a -> Datum
43+
asDatum a = Datum $ PlutusTx.dataToBuiltinData $ PlutusTx.toData a
44+
3545
plutusL1 :: C.ScriptLanguage C.PlutusScriptV1
3646
plutusL1 = C.PlutusScriptLanguage C.PlutusScriptV1
3747

@@ -121,3 +131,6 @@ policyIdV1 = C.scriptPolicyId . unPlutusScriptV1 . policyScript
121131
-- | PolicyId of a V2 minting policy
122132
policyIdV2 :: MintingPolicy -> C.PolicyId
123133
policyIdV2 = C.scriptPolicyId . unPlutusScriptV2 . policyScript
134+
135+
fromPolicyId :: C.PolicyId -> CurrencySymbol
136+
fromPolicyId (C.PolicyId hash) = PlutusV1.CurrencySymbol . BI.toBuiltin $ C.serialiseToRawBytes hash

plutus-e2e-tests/test/PlutusScripts/V1TxInfo.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
{-# LANGUAGE TypeApplications #-}
88

99
{-# OPTIONS_GHC -Wno-missing-fields #-}
10+
{-# OPTIONS_GHC -Wno-deprecations #-}
1011

1112
module PlutusScripts.V1TxInfo (
1213
txInfoInputs
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
{-# LANGUAGE DataKinds #-}
2+
{-# LANGUAGE NumericUnderscores #-}
3+
{-# LANGUAGE OverloadedStrings #-}
4+
{-# LANGUAGE RecordWildCards #-}
5+
{-# LANGUAGE ScopedTypeVariables #-}
6+
{-# LANGUAGE TemplateHaskell #-}
7+
{-# LANGUAGE TypeApplications #-}
8+
9+
{-# OPTIONS_GHC -Wno-missing-fields #-}
10+
{-# OPTIONS_GHC -Wno-deprecations #-}
11+
12+
module PlutusScripts.V2TxInfo (
13+
txInfoInputs
14+
, txInfoOutputs
15+
, txInfoFee
16+
, txInfoMint
17+
, txInfoSigs
18+
, txInfoData
19+
, checkV2TxInfoScriptV2
20+
, checkV2TxInfoAssetIdV2
21+
, checkV2TxInfoRedeemer
22+
, checkV2TxInfoMintWitnessV2
23+
) where
24+
25+
import Cardano.Api qualified as C
26+
import Ledger.Tx.CardanoAPI.Internal (fromCardanoPaymentKeyHash, fromCardanoScriptData, fromCardanoTxIn,
27+
fromCardanoTxOutToPV2TxInfoTxOut, fromCardanoTxOutToPV2TxInfoTxOut',
28+
fromCardanoValue)
29+
import Plutus.Script.Utils.Typed (IsScriptContext (mkUntypedMintingPolicy))
30+
import Plutus.V1.Ledger.Api (mkMintingPolicyScript)
31+
import Plutus.V1.Ledger.Interval qualified as P
32+
import Plutus.V2.Ledger.Api qualified as PlutusV2
33+
import Plutus.V2.Ledger.Contexts (ownCurrencySymbol)
34+
import PlutusScripts.Helpers (mintScriptWitness', plutusL2, policyIdV2, policyScript, toScriptData)
35+
import PlutusTx qualified
36+
import PlutusTx.AssocMap qualified as AMap
37+
import PlutusTx.Builtins qualified as P
38+
import PlutusTx.Prelude qualified as P
39+
40+
data V2TxInfo = V2TxInfo
41+
{ expTxInfoInputs :: [PlutusV2.TxInInfo] -- ^ Transaction inputs; cannot be an empty list
42+
, expTxInfoReferenceInputs :: [PlutusV2.TxInInfo] -- ^ /Added in V2:/ Transaction reference inputs
43+
, expTxInfoOutputs :: [PlutusV2.TxOut] -- ^ Transaction outputs
44+
, expTxInfoFee :: PlutusV2.Value -- ^ The fee paid by this transaction.
45+
, expTxInfoMint :: PlutusV2.Value -- ^ The 'Value' minted by this transaction.
46+
, expTxInfoDCert :: [PlutusV2.DCert] -- ^ Digests of certificates included in this transaction
47+
, expTxInfoWdrl :: PlutusV2.Map PlutusV2.StakingCredential Integer -- ^ Withdrawals
48+
, expTxInfoValidRange :: PlutusV2.POSIXTimeRange -- ^ The valid range for the transaction.
49+
, expTxInfoSignatories :: [PlutusV2.PubKeyHash] -- ^ Signatures provided with the transaction, attested that they all signed the tx
50+
, expTxInfoRedeemers :: PlutusV2.Map PlutusV2.ScriptPurpose PlutusV2.Redeemer
51+
, expTxInfoData :: PlutusV2.Map PlutusV2.DatumHash PlutusV2.Datum -- ^ The lookup table of datums attached to the transaction
52+
-- , expTxInfoId :: PlutusV2.TxId -- ^ Hash of the pending transaction body (i.e. transaction excluding witnesses). Cannot be verified onchain.
53+
}
54+
PlutusTx.unstableMakeIsData ''V2TxInfo
55+
56+
checkV2TxInfoRedeemer ::
57+
[PlutusV2.TxInInfo] ->
58+
[PlutusV2.TxInInfo] ->
59+
[PlutusV2.TxOut] ->
60+
PlutusV2.Value ->
61+
PlutusV2.Value ->
62+
[PlutusV2.DCert] ->
63+
PlutusV2.Map PlutusV2.StakingCredential Integer ->
64+
PlutusV2.POSIXTimeRange ->
65+
[PlutusV2.PubKeyHash] ->
66+
PlutusV2.Map PlutusV2.ScriptPurpose PlutusV2.Redeemer ->
67+
PlutusV2.Map PlutusV2.DatumHash PlutusV2.Datum ->
68+
C.ScriptData
69+
checkV2TxInfoRedeemer expIns expRefIns expOuts expFee expMint expDCert expWdrl expRange expSigs expReds expData =
70+
toScriptData $ V2TxInfo expIns expRefIns expOuts expFee expMint expDCert expWdrl expRange expSigs expReds expData
71+
72+
txInfoInputs :: (C.TxIn, C.TxOut C.CtxUTxO era) -> PlutusV2.TxInInfo
73+
txInfoInputs (txIn, txOut) = do PlutusV2.TxInInfo {
74+
PlutusV2.txInInfoOutRef = fromCardanoTxIn txIn
75+
, PlutusV2.txInInfoResolved = fromCardanoTxOutToPV2TxInfoTxOut' txOut
76+
}
77+
78+
txInfoOutputs :: [C.TxOut C.CtxTx era] -> [PlutusV2.TxOut]
79+
txInfoOutputs = map fromCardanoTxOutToPV2TxInfoTxOut
80+
81+
txInfoFee :: C.Lovelace -> PlutusV2.Value
82+
txInfoFee = fromCardanoValue . C.lovelaceToValue
83+
84+
txInfoMint :: C.Value -> PlutusV2.Value
85+
txInfoMint = fromCardanoValue
86+
87+
txInfoSigs :: [C.VerificationKey C.PaymentKey] -> [PlutusV2.PubKeyHash]
88+
txInfoSigs = map (fromCardanoPaymentKeyHash . C.verificationKeyHash)
89+
90+
txInfoData :: [C.ScriptData] -> PlutusV2.Map PlutusV2.DatumHash PlutusV2.Datum
91+
txInfoData = PlutusV2.fromList . map (\ datum ->
92+
(PlutusV2.DatumHash $ PlutusV2.toBuiltin $ C.serialiseToRawBytes $ C.hashScriptData datum,
93+
PlutusV2.Datum $ fromCardanoScriptData datum))
94+
95+
-- minting policy --
96+
97+
{-# INLINABLE mkCheckV2TxInfo #-}
98+
mkCheckV2TxInfo :: V2TxInfo -> PlutusV2.ScriptContext -> Bool
99+
mkCheckV2TxInfo V2TxInfo{..} ctx =
100+
P.traceIfFalse "unexpected txInfoInputs" checkTxInfoInputs &&
101+
P.traceIfFalse "unexpected txInfoReferenceInputs" checkTxInfoReferenceInputs &&
102+
P.traceIfFalse "unexpected txInfoOutputs" checkTxInfoOutputs &&
103+
P.traceIfFalse "unexpected txInfoFee" checkTxInfoFee &&
104+
P.traceIfFalse "unexpected txInfoMint" checkTxInfoMint &&
105+
P.traceIfFalse "unexpected txInfoDCert" checkTxInfoDCert &&
106+
P.traceIfFalse "unexpected txInfoWdrl" checkTxInfoWdrl &&
107+
P.traceIfFalse "provided range doesn't contain txInfoValidRange" checkTxInfoValidRange &&
108+
P.traceIfFalse "unexpected txInfoSignatories" checkTxInfoSignatories &&
109+
P.traceIfFalse "unexpected txInfoRedeemers" checkTxInfoRedeemers &&
110+
P.traceIfFalse "unexpected txInfoData" checkTxInfoData &&
111+
P.traceIfFalse "txInfoId isn't the expected TxId length" checkTxInfoId
112+
where
113+
info :: PlutusV2.TxInfo
114+
info = PlutusV2.scriptContextTxInfo ctx
115+
116+
checkTxInfoInputs = expTxInfoInputs P.== PlutusV2.txInfoInputs info
117+
checkTxInfoReferenceInputs = expTxInfoReferenceInputs P.== PlutusV2.txInfoReferenceInputs info
118+
checkTxInfoOutputs = expTxInfoOutputs P.== PlutusV2.txInfoOutputs info
119+
checkTxInfoFee = expTxInfoFee P.== PlutusV2.txInfoFee info
120+
checkTxInfoMint = expTxInfoMint P.== PlutusV2.txInfoMint info
121+
checkTxInfoDCert = expTxInfoDCert P.== PlutusV2.txInfoDCert info
122+
checkTxInfoWdrl = expTxInfoWdrl P.== PlutusV2.txInfoWdrl info
123+
checkTxInfoValidRange = expTxInfoValidRange `P.contains` PlutusV2.txInfoValidRange info
124+
checkTxInfoSignatories = expTxInfoSignatories P.== PlutusV2.txInfoSignatories info
125+
checkTxInfoRedeemers = do
126+
let ownScriptPurpose = PlutusV2.Minting (ownCurrencySymbol ctx)
127+
withoutOwnRedeemer = AMap.delete ownScriptPurpose (PlutusV2.txInfoRedeemers info)
128+
expTxInfoRedeemers P.== withoutOwnRedeemer -- cannot check own redeemer so only check other script's redeemer
129+
checkTxInfoData = expTxInfoData P.== PlutusV2.txInfoData info
130+
checkTxInfoId = P.equalsInteger 32 (P.lengthOfByteString P.$ PlutusV2.getTxId P.$ PlutusV2.txInfoId info)
131+
132+
checkV2TxInfoV2 :: PlutusV2.MintingPolicy
133+
checkV2TxInfoV2 = mkMintingPolicyScript
134+
$$(PlutusTx.compile [|| wrap ||])
135+
where
136+
wrap = mkUntypedMintingPolicy @PlutusV2.ScriptContext mkCheckV2TxInfo
137+
138+
checkV2TxInfoScriptV2 :: C.PlutusScript C.PlutusScriptV2
139+
checkV2TxInfoScriptV2 = policyScript checkV2TxInfoV2
140+
141+
checkV2TxInfoAssetIdV2 :: C.AssetId
142+
checkV2TxInfoAssetIdV2 = C.AssetId (policyIdV2 checkV2TxInfoV2) "V2TxInfo"
143+
144+
checkV2TxInfoMintWitnessV2 :: C.CardanoEra era
145+
-> C.ScriptData
146+
-> C.ExecutionUnits
147+
-> (C.PolicyId, C.ScriptWitness C.WitCtxMint era)
148+
checkV2TxInfoMintWitnessV2 era redeemer exunits =
149+
(policyIdV2 checkV2TxInfoV2,
150+
mintScriptWitness' era plutusL2 (Left checkV2TxInfoScriptV2) redeemer exunits)

plutus-e2e-tests/test/Spec/AlonzoFeatures.hs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import Helpers.Query qualified as Q
1919
import Helpers.Testnet (testnetOptionsAlonzo6, testnetOptionsBabbage7, testnetOptionsBabbage8)
2020
import Helpers.Testnet qualified as TN
2121
import Helpers.Tx qualified as Tx
22-
import Helpers.Utils qualified as U (workspace)
22+
import Helpers.Utils qualified as U (posixToMilliseconds, workspace)
2323
import Plutus.V1.Ledger.Api qualified as PlutusV1
2424
import Plutus.V1.Ledger.Interval qualified as PlutusV1
2525
import Plutus.V1.Ledger.Time qualified as PlutusV1
@@ -30,10 +30,6 @@ import Test.Base qualified as H
3030
import Test.Tasty (TestTree, testGroup)
3131
import Test.Tasty.Hedgehog (testProperty)
3232

33-
-- | Convert a 'POSIXTime' to the number of milliseconds since the Unix epoch.
34-
posixToMilliseconds :: Time.POSIXTime -> Integer
35-
posixToMilliseconds posixTime = round $ 1000 * (realToFrac posixTime :: Double)
36-
3733
tests :: TestTree
3834
tests =
3935
testGroup
@@ -73,9 +69,9 @@ checkTxInfoV1Test networkOptions = H.integration . HE.runFinallies . U.workspace
7369
txOut2 = Tx.txOut era (C.lovelaceToValue amountReturned) w1Address
7470

7571
lowerBound = PlutusV1.fromMilliSeconds
76-
$ PlutusV1.DiffMilliSeconds $ posixToMilliseconds preTestnetTime -- before slot 1
72+
$ PlutusV1.DiffMilliSeconds $ U.posixToMilliseconds preTestnetTime -- before slot 1
7773
upperBound = PlutusV1.fromMilliSeconds
78-
$ PlutusV1.DiffMilliSeconds $ posixToMilliseconds startTime + 600_000 -- ~10mins after slot 1 (to account for testnet init time)
74+
$ PlutusV1.DiffMilliSeconds $ U.posixToMilliseconds startTime + 600_000 -- ~10mins after slot 1 (to account for testnet init time)
7975
timeRange = PlutusV1.interval lowerBound upperBound :: PlutusV1.POSIXTimeRange
8076

8177
expTxInfoInputs = txInfoInputs (txIn, txInAsTxOut)
@@ -114,4 +110,3 @@ checkTxInfoV1Test networkOptions = H.integration . HE.runFinallies . U.workspace
114110
H.success
115111

116112
-- TODO: datumHashSpendTest
117-
-- TODO: mintTest

0 commit comments

Comments
 (0)