From e98385a1864f2146eb31cbec66395087f38e4dc7 Mon Sep 17 00:00:00 2001 From: Jordan Millar Date: Thu, 9 Apr 2026 15:33:19 -0400 Subject: [PATCH 1/2] Add integration test for spending reference script with build-raw Add hprop_build_raw_ref_script_spend test that validates spending a UTxO locked at a PlutusV3 script address via a reference script using cardano-cli transaction build-raw instead of the auto-balancing transaction build command. --- cardano-testnet/cardano-testnet.cabal | 1 + .../Testnet/Test/Cli/Plutus/BuildRaw.hs | 184 ++++++++++++++++++ .../cardano-testnet-test.hs | 2 + 3 files changed, 187 insertions(+) create mode 100644 cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Cli/Plutus/BuildRaw.hs diff --git a/cardano-testnet/cardano-testnet.cabal b/cardano-testnet/cardano-testnet.cabal index ea0b0acc4fd..884201d5f43 100644 --- a/cardano-testnet/cardano-testnet.cabal +++ b/cardano-testnet/cardano-testnet.cabal @@ -210,6 +210,7 @@ test-suite cardano-testnet-test Cardano.Testnet.Test.Cli.QuerySlotNumber Cardano.Testnet.Test.Cli.Plutus.Scripts Cardano.Testnet.Test.Cli.Plutus.CostCalculation + Cardano.Testnet.Test.Cli.Plutus.BuildRaw Cardano.Testnet.Test.Cli.Plutus.MultiAssetReturnCollateral Cardano.Testnet.Test.Cli.Scripts.Simple.CostCalculation Cardano.Testnet.Test.Cli.Scripts.Simple.Mint diff --git a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Cli/Plutus/BuildRaw.hs b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Cli/Plutus/BuildRaw.hs new file mode 100644 index 00000000000..7f1c228e453 --- /dev/null +++ b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Cli/Plutus/BuildRaw.hs @@ -0,0 +1,184 @@ +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE NumericUnderscores #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} + +module Cardano.Testnet.Test.Cli.Plutus.BuildRaw + ( hprop_build_raw_ref_script_spend + -- | Execute tests in this module with: + -- @DISABLE_RETRIES=1 cabal test cardano-testnet-test -- -p "/Build Raw Ref Script/"@ + ) +where + +import Cardano.Api hiding (Value) +import Cardano.Api.Experimental (Some (Some)) +import Cardano.Api.Ledger (EpochInterval (..)) + +import Cardano.Testnet + +import Prelude + +import Control.Monad (void) +import Data.Default.Class (Default (def)) +import qualified Data.Text as Text +import System.FilePath (()) +import qualified System.Info as SYS + +import Testnet.Components.Query (findLargestUtxoForPaymentKey, getEpochStateView, getTxIx, + watchEpochStateUpdate) +import qualified Testnet.Defaults as Defaults +import Testnet.Process.Cli.Transaction (TxOutAddress (..), mkSpendOutputsOnlyTx, + retrieveTransactionId, signTx, submitTx) +import Testnet.Process.Run (execCli', mkExecConfig) +import Testnet.Property.Util (integrationRetryWorkspace) +import Testnet.Start.Types (eraToString) +import Testnet.Types + +import Hedgehog (Property) +import qualified Hedgehog as H +import qualified Hedgehog.Extras.Test.Base as H +import qualified Hedgehog.Extras.Test.File as H +import qualified Hedgehog.Extras.Test.TestWatchdog as H + +-- | Test spending a reference script UTxO using @transaction build-raw@. +-- @DISABLE_RETRIES=1 cabal test cardano-testnet-test --test-options '-p "/Build Raw Ref Script/"'@ +hprop_build_raw_ref_script_spend :: Property +hprop_build_raw_ref_script_spend = integrationRetryWorkspace 2 "build-raw-ref-script" $ \tempAbsBasePath' -> H.runWithDefaultWatchdog_ $ do + H.note_ SYS.os + conf@Conf{tempAbsPath} <- mkConf tempAbsBasePath' + let tempAbsPath' = unTmpAbsPath tempAbsPath + work <- H.createDirectoryIfMissing $ tempAbsPath' "work" + + let + sbe = ShelleyBasedEraConway + era = toCardanoEra sbe + cEra = AnyCardanoEra era + eraName = eraToString era + tempBaseAbsPath = makeTmpBaseAbsPath $ TmpAbsolutePath tempAbsPath' + options = def{cardanoNodeEra = AnyShelleyBasedEra sbe} + + TestnetRuntime + { configurationFile + , testnetMagic + , testnetNodes + , wallets = wallet0 : wallet1 : _ + } <- + createAndRunTestnet options def conf + + poolNode1 <- H.headM testnetNodes + poolSprocket1 <- H.noteShow $ nodeSprocket poolNode1 + execConfig <- mkExecConfig tempBaseAbsPath poolSprocket1 testnetMagic + epochStateView <- getEpochStateView configurationFile (nodeSocketPath poolNode1) + + -- Write PlutusV3 always-succeeds script to file + let plutusScriptFp = work "always-succeeds-script.plutusV3" + H.writeFile plutusScriptFp $ Text.unpack Defaults.plutusV3Script + let plutusV3Script = File plutusScriptFp + + -- Step 1: Publish reference script at wallet0's address (not the script address) + refScriptPublishWork <- H.createDirectoryIfMissing $ work "ref-script-publish" + let scriptPublishUTxOAmount = 10_000_000 + + txinPublish <- findLargestUtxoForPaymentKey epochStateView sbe wallet0 + let txBodyPublishFp = File $ refScriptPublishWork "tx-body.txbody" + void $ execCli' execConfig + [ eraName + , "transaction", "build" + , "--change-address", Text.unpack $ paymentKeyInfoAddr wallet0 + , "--tx-in", Text.unpack $ renderTxIn txinPublish + , "--tx-out", Text.unpack (paymentKeyInfoAddr wallet0) <> "+" <> show (unCoin scriptPublishUTxOAmount) + , "--tx-out-reference-script-file", unFile plutusV3Script + , "--out-file", unFile txBodyPublishFp + ] + signedTxPublishRefScript <- + signTx + execConfig + cEra + refScriptPublishWork + "signed-tx" + txBodyPublishFp + [Some $ paymentKeyInfoPair wallet0] + submitTx execConfig cEra signedTxPublishRefScript + + txIdPublishRefScript <- retrieveTransactionId execConfig signedTxPublishRefScript + txIxPublishRefScript <- + H.evalMaybeM $ + watchEpochStateUpdate + epochStateView + (EpochInterval 2) + (getTxIx sbe txIdPublishRefScript scriptPublishUTxOAmount) + + -- Step 2: Lock funds at script address + refScriptLock <- H.createDirectoryIfMissing $ work "ref-script-lock" + let transferAmount = 20_000_000 + + txBodyLock <- + mkSpendOutputsOnlyTx + execConfig + epochStateView + sbe + refScriptLock + "tx-body" + wallet0 + [(ScriptAddress plutusV3Script, transferAmount, Nothing)] + signedTxLock <- + signTx execConfig cEra refScriptLock "signed-tx" txBodyLock [Some $ paymentKeyInfoPair wallet0] + submitTx execConfig cEra signedTxLock + + txIdLock <- retrieveTransactionId execConfig signedTxLock + txIxLock <- + H.evalMaybeM $ + watchEpochStateUpdate epochStateView (EpochInterval 2) (getTxIx sbe txIdLock transferAmount) + + -- Step 3: Query protocol parameters + void $ execCli' execConfig + [ eraName, "query", "protocol-parameters" + , "--cardano-mode" + , "--out-file", work "pparams.json" + ] + + -- Step 4: Build raw transaction to unlock the script UTxO + refScriptUnlock <- H.createDirectoryIfMissing $ work "ref-script-unlock" + let unsignedUnlockTx = File $ refScriptUnlock "unsigned-tx.tx" + fee = 500_000 :: Coin + + collateralUTxO <- findLargestUtxoForPaymentKey epochStateView sbe wallet1 + + void $ + execCli' + execConfig + [ eraName + , "transaction", "build-raw" + , "--tx-in", prettyShow (TxIn txIdLock txIxLock) + , "--spending-reference-tx-in-inline-datum-present" + , "--spending-tx-in-reference", prettyShow (TxIn txIdPublishRefScript txIxPublishRefScript) + , "--spending-plutus-script-v3" + , "--spending-reference-tx-in-redeemer-value", "42" + , "--spending-reference-tx-in-execution-units", "(200000000, 200000)" + , "--tx-in-collateral", prettyShow collateralUTxO + , "--tx-out", Text.unpack (paymentKeyInfoAddr wallet1) <> "+" <> show (unCoin (transferAmount - fee)) + , "--fee", show (unCoin fee) + , "--protocol-params-file", work "pparams.json" + , "--out-file", unFile unsignedUnlockTx + ] + + -- Step 5: Sign and submit + signedUnlockTx <- + signTx + execConfig + cEra + refScriptUnlock + "signed-tx" + unsignedUnlockTx + [Some $ paymentKeyInfoPair wallet1] + + submitTx execConfig cEra signedUnlockTx + + -- Verify the transaction landed on chain + txIdUnlock <- retrieveTransactionId execConfig signedUnlockTx + void $ + H.evalMaybeM $ + watchEpochStateUpdate + epochStateView + (EpochInterval 2) + (getTxIx sbe txIdUnlock (transferAmount - fee)) diff --git a/cardano-testnet/test/cardano-testnet-test/cardano-testnet-test.hs b/cardano-testnet/test/cardano-testnet-test/cardano-testnet-test.hs index 90080114c26..3d95a98bdab 100644 --- a/cardano-testnet/test/cardano-testnet-test/cardano-testnet-test.hs +++ b/cardano-testnet/test/cardano-testnet-test/cardano-testnet-test.hs @@ -7,6 +7,7 @@ module Main import qualified Cardano.Crypto.Init as Crypto import qualified Cardano.Testnet.Test.Api.TxReferenceInputDatum import qualified Cardano.Testnet.Test.Cli.KesPeriodInfo +import qualified Cardano.Testnet.Test.Cli.Plutus.BuildRaw import qualified Cardano.Testnet.Test.Cli.Plutus.CostCalculation import qualified Cardano.Testnet.Test.Cli.Plutus.Scripts import qualified Cardano.Testnet.Test.Cli.Query @@ -94,6 +95,7 @@ tests = do [ ignoreOnWindows "PlutusV3 purposes" Cardano.Testnet.Test.Cli.Plutus.Scripts.hprop_plutus_purposes_v3 , ignoreOnWindows "PlutusV2 transaction with two script certs" Cardano.Testnet.Test.Cli.Plutus.Scripts.hprop_tx_two_script_certs_v2 , ignoreOnWindows "Collateral With Multiassets" Cardano.Testnet.Test.Cli.Plutus.MultiAssetReturnCollateral.hprop_collateral_with_tokens + , ignoreOnWindows "Build Raw Ref Script" Cardano.Testnet.Test.Cli.Plutus.BuildRaw.hprop_build_raw_ref_script_spend , T.testGroup "Cost Calc" [ ignoreOnWindows "Ref Script" Cardano.Testnet.Test.Cli.Plutus.CostCalculation.hprop_ref_plutus_cost_calculation , ignoreOnWindows "Normal Script" Cardano.Testnet.Test.Cli.Plutus.CostCalculation.hprop_included_plutus_cost_calculation From 974048248c38ef9aa309fbc3ac08ec9168ef0ff1 Mon Sep 17 00:00:00 2001 From: Jordan Millar Date: Thu, 9 Apr 2026 15:42:13 -0400 Subject: [PATCH 2/2] Add scriv changelog fragment for build-raw reference script test --- ...0260409_200000_jordan.millar_build_raw_ref_script_test.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 cardano-testnet/changelog.d/20260409_200000_jordan.millar_build_raw_ref_script_test.md diff --git a/cardano-testnet/changelog.d/20260409_200000_jordan.millar_build_raw_ref_script_test.md b/cardano-testnet/changelog.d/20260409_200000_jordan.millar_build_raw_ref_script_test.md new file mode 100644 index 00000000000..3e2c3eaffcf --- /dev/null +++ b/cardano-testnet/changelog.d/20260409_200000_jordan.millar_build_raw_ref_script_test.md @@ -0,0 +1,5 @@ + +### Tests + +- Added integration test for spending a reference script using `transaction build-raw` +