From 74d902195baee5631528adfa68be09df8a9be1ac Mon Sep 17 00:00:00 2001 From: Mateusz Galazyn Date: Thu, 15 Oct 2026 09:34:19 +0200 Subject: [PATCH] Complete Plutus V4 support and remove Conway hardcodes A transaction output whose reference script is in a Plutus language the era does not support used to lose the script silently. Fail with cardano-api's `TxOutputReferenceScriptLanguageNotSupportedInEra` error instead, using the same `toScriptInEra` check as cardano-api's `validateTxOutsReferenceScripts`, which is not exported. Read script files for `hash script`, `transaction policyid` and `address build` `--payment-script-file` and `--stake-script-file` in cardano-api's newest era, so they accept Plutus V4 scripts instead of failing with "not supported in the Conway era". Add Plutus V4 golden tests: - a V4 script file as a direct witness for all six script purposes in `dijkstra transaction build-raw`, checked with `debug transaction view` - hashing a V4 script with `hash script` - `transaction policyid` and `address build` with a V4 script - the error for a V4 reference script in a Conway transaction Parse `CARDANO_ERA` by reusing cardano-api's era enumeration and rendering instead of a hand-written string table, so newer eras such as dijkstra are accepted. An unknown value still stops the command, now with an error that lists the valid eras. The latest command group and the top-level node and query command groups follow the era running on mainnet. A MainnetEra alias names that choice, so the hard fork changes one line. --- ...i_mateusz.galazyn_read-any-script-hash.yml | 6 + .changes/cardano_era_all_eras.yml | 6 + .changes/plutus-v4-script-readers.yml | 6 + .changes/plutus-v4-script-witnesses.yml | 6 + .../reference-script-unsupported-language.yml | 6 + cardano-cli/cardano-cli.cabal | 2 + .../CLI/Compatible/Transaction/TxOut.hs | 6 +- cardano-cli/src/Cardano/CLI/Environment.hs | 46 ++++--- .../src/Cardano/CLI/EraBased/Option.hs | 10 +- .../Cardano/CLI/EraBased/Transaction/Run.hs | 9 +- .../Cardano/CLI/EraIndependent/Address/Run.hs | 26 ++-- .../Cardano/CLI/EraIndependent/Hash/Run.hs | 10 +- cardano-cli/src/Cardano/CLI/Option.hs | 6 +- cardano-cli/src/Cardano/CLI/Read.hs | 10 ++ .../Conway/Transaction/ReferenceScript.hs | 43 ++++++ .../Golden/Dijkstra/Transaction/BuildRaw.hs | 126 +++++++++++++++++ .../Test/Golden/Hash/Hash.hs | 22 +++ .../Test/Golden/Shelley/Address/Build.hs | 43 ++++++ .../Test/Golden/Shelley/Transaction/Build.hs | 19 +++ ...-reference-script-unsupported-language.out | 2 + .../address/build-payment-script-v4.addr | 1 + .../address/build-stake-script-v4.addr | 1 + .../dijkstra/plutus/v4-always-succeeds.hash | 1 + .../build-raw-plutus-v4-script-witnesses.out | 127 ++++++++++++++++++ .../dijkstra/plutus/v4-always-succeeds.plutus | 5 + 25 files changed, 491 insertions(+), 54 deletions(-) create mode 100644 .changes/20261015_120100_cardano-cli_mateusz.galazyn_read-any-script-hash.yml create mode 100644 .changes/cardano_era_all_eras.yml create mode 100644 .changes/plutus-v4-script-readers.yml create mode 100644 .changes/plutus-v4-script-witnesses.yml create mode 100644 .changes/reference-script-unsupported-language.yml create mode 100644 cardano-cli/test/cardano-cli-golden/Test/Golden/Conway/Transaction/ReferenceScript.hs create mode 100644 cardano-cli/test/cardano-cli-golden/Test/Golden/Dijkstra/Transaction/BuildRaw.hs create mode 100644 cardano-cli/test/cardano-cli-golden/files/golden/conway/transaction/build-raw-reference-script-unsupported-language.out create mode 100644 cardano-cli/test/cardano-cli-golden/files/golden/dijkstra/address/build-payment-script-v4.addr create mode 100644 cardano-cli/test/cardano-cli-golden/files/golden/dijkstra/address/build-stake-script-v4.addr create mode 100644 cardano-cli/test/cardano-cli-golden/files/golden/dijkstra/plutus/v4-always-succeeds.hash create mode 100644 cardano-cli/test/cardano-cli-golden/files/golden/dijkstra/transaction/build-raw-plutus-v4-script-witnesses.out create mode 100644 cardano-cli/test/cardano-cli-golden/files/input/dijkstra/plutus/v4-always-succeeds.plutus diff --git a/.changes/20261015_120100_cardano-cli_mateusz.galazyn_read-any-script-hash.yml b/.changes/20261015_120100_cardano-cli_mateusz.galazyn_read-any-script-hash.yml new file mode 100644 index 0000000000..a256facce8 --- /dev/null +++ b/.changes/20261015_120100_cardano-cli_mateusz.galazyn_read-any-script-hash.yml @@ -0,0 +1,6 @@ +project: cardano-cli +pr: 1452 +kind: + - refactoring +description: | + Add `readAnyScriptHash` to `Cardano.CLI.Read`, which returns the hash of a script file in any script language cardano-api supports, including Plutus V4. Add `MainnetEra` to `Cardano.CLI.EraBased.Option`, the era used by the `latest` command group and the top-level `node` and `query` commands. diff --git a/.changes/cardano_era_all_eras.yml b/.changes/cardano_era_all_eras.yml new file mode 100644 index 0000000000..eb625c8791 --- /dev/null +++ b/.changes/cardano_era_all_eras.yml @@ -0,0 +1,6 @@ +project: cardano-cli +pr: 1452 +kind: + - bugfix +description: | + `CARDANO_ERA` now accepts every era cardano-api supports, including `dijkstra`, in any letter case, and an unknown value stops the command with an error that lists the valid eras. diff --git a/.changes/plutus-v4-script-readers.yml b/.changes/plutus-v4-script-readers.yml new file mode 100644 index 0000000000..18baf3a0e4 --- /dev/null +++ b/.changes/plutus-v4-script-readers.yml @@ -0,0 +1,6 @@ +project: cardano-cli +pr: 1452 +kind: + - bugfix +description: | + `transaction policyid` and `address build` with `--payment-script-file` or `--stake-script-file` now accept Plutus V4 scripts, instead of failing with "Plutus language PlutusScriptV4 is not supported in the Conway era". They also accept simple scripts with a `guard`, which used to crash them. diff --git a/.changes/plutus-v4-script-witnesses.yml b/.changes/plutus-v4-script-witnesses.yml new file mode 100644 index 0000000000..b9235b7bd0 --- /dev/null +++ b/.changes/plutus-v4-script-witnesses.yml @@ -0,0 +1,6 @@ +project: cardano-cli +pr: 1452 +kind: + - test +description: | + Add golden tests for Plutus V4 scripts: script files as direct witnesses for every script purpose in `dijkstra transaction build-raw`, `hash script`, `transaction policyid`, `address build` with payment and stake scripts, and the error for a V4 reference script in a Conway transaction. diff --git a/.changes/reference-script-unsupported-language.yml b/.changes/reference-script-unsupported-language.yml new file mode 100644 index 0000000000..e0a5fe6ba0 --- /dev/null +++ b/.changes/reference-script-unsupported-language.yml @@ -0,0 +1,6 @@ +project: cardano-cli +pr: 1452 +kind: + - bugfix +description: | + A transaction output whose reference script is written in a Plutus language not supported by the transaction's era now makes the command fail with a clear error, instead of silently building the transaction with that reference script dropped from the output. diff --git a/cardano-cli/cardano-cli.cabal b/cardano-cli/cardano-cli.cabal index 9f47dcc52d..a8d709cb36 100644 --- a/cardano-cli/cardano-cli.cabal +++ b/cardano-cli/cardano-cli.cabal @@ -487,11 +487,13 @@ test-suite cardano-cli-golden Test.Golden.Conway.Transaction.Assemble Test.Golden.Conway.Transaction.BuildRaw Test.Golden.Conway.Transaction.CreateWitness + Test.Golden.Conway.Transaction.ReferenceScript Test.Golden.CreateStaked Test.Golden.CreateTestnetData Test.Golden.Debug.TransactionView Test.Golden.Dijkstra.StakePool.RegistrationCertificate Test.Golden.Dijkstra.Transaction.Assemble + Test.Golden.Dijkstra.Transaction.BuildRaw Test.Golden.Dijkstra.Transaction.SubTransaction Test.Golden.ErrorsSpec Test.Golden.Genesis.Common diff --git a/cardano-cli/src/Cardano/CLI/Compatible/Transaction/TxOut.hs b/cardano-cli/src/Cardano/CLI/Compatible/Transaction/TxOut.hs index 18eaacbe71..4b210aeb64 100644 --- a/cardano-cli/src/Cardano/CLI/Compatible/Transaction/TxOut.hs +++ b/cardano-cli/src/Cardano/CLI/Compatible/Transaction/TxOut.hs @@ -139,7 +139,11 @@ readRefScript sbe = \case ReferenceScriptAnyEraNone -> pure L.SNothing ReferenceScriptAnyEra fp -> do script <- readFileScriptInAnyLang fp - pure $ maybe L.SNothing (L.SJust . toShelleyScript) (toScriptInEra sbe script) + scriptInEra <- + fromMaybeCli + (TxOutputReferenceScriptLanguageNotSupportedInEra script (anyCardanoEra $ toCardanoEra sbe)) + $ toScriptInEra sbe script + pure . L.SJust $ toShelleyScript scriptInEra toTxOutValueInShelleyBasedEra :: ShelleyBasedEra era diff --git a/cardano-cli/src/Cardano/CLI/Environment.hs b/cardano-cli/src/Cardano/CLI/Environment.hs index c7c9503d93..58f90dde5a 100644 --- a/cardano-cli/src/Cardano/CLI/Environment.hs +++ b/cardano-cli/src/Cardano/CLI/Environment.hs @@ -14,15 +14,21 @@ where import Cardano.Api ( AnyCardanoEra (..) - , CardanoEra (..) , NetworkId (..) , NetworkMagic (..) , forEraMaybeEon + , pretty ) import Cardano.Api.Experimental qualified as Exp +import Control.Monad.IO.Class (MonadIO (..)) +import Data.Char (toLower) +import Data.Foldable (find) +import Data.List (intercalate) +import Data.Traversable (forM) import Data.Type.Equality import Data.Word (Word32) +import GHC.Stack (HasCallStack) import System.Environment qualified as IO import System.IO qualified as IO import Text.Read (readMaybe) @@ -84,21 +90,29 @@ getEnvNetworkId = do getEnvSocketPath :: IO (Maybe FilePath) getEnvSocketPath = IO.lookupEnv "CARDANO_NODE_SOCKET_PATH" --- | If the environment variable @CARDANO_ERA@ is set, then return the set value. +-- | If the environment variable @CARDANO_ERA@ is set to a known era, then return that era. +-- If it is set to an unknown era, fail with an error listing the valid eras. -- Otherwise, return 'Nothing'. -getCardanoEra :: IO (Maybe AnyCardanoEra) +getCardanoEra :: (HasCallStack, MonadIO m) => m (Maybe AnyCardanoEra) getCardanoEra = do - mEraString <- IO.lookupEnv "CARDANO_ERA" + mEraString <- liftIO $ IO.lookupEnv "CARDANO_ERA" + forM mEraString $ \eraString -> + maybe (error $ invalidEraMessage eraString) pure $ parseCardanoEra eraString + where + invalidEraMessage :: String -> String + invalidEraMessage eraString = + mconcat + [ "The era specified in CARDANO_ERA is invalid: " <> eraString <> "." + , " It should be one of: " <> intercalate ", " (cardanoEraName <$> allCardanoEras) <> "." + ] - case mEraString of - Just eraString -> - case eraString of - "byron" -> pure $ Just $ AnyCardanoEra ByronEra - "shelley" -> pure $ Just $ AnyCardanoEra ShelleyEra - "allegra" -> pure $ Just $ AnyCardanoEra AllegraEra - "mary" -> pure $ Just $ AnyCardanoEra MaryEra - "alonzo" -> pure $ Just $ AnyCardanoEra AlonzoEra - "babbage" -> pure $ Just $ AnyCardanoEra BabbageEra - "conway" -> pure $ Just $ AnyCardanoEra ConwayEra - unknown -> error $ "Unknown era: " <> unknown -- TODO improve error handling - Nothing -> pure Nothing + -- Parse an era name, ignoring case, using the names that cardano-api renders for its eras. + parseCardanoEra :: String -> Maybe AnyCardanoEra + parseCardanoEra eraString = + find ((== map toLower eraString) . cardanoEraName) allCardanoEras + + allCardanoEras :: [AnyCardanoEra] + allCardanoEras = enumFrom minBound + + cardanoEraName :: AnyCardanoEra -> String + cardanoEraName = map toLower . show . pretty diff --git a/cardano-cli/src/Cardano/CLI/EraBased/Option.hs b/cardano-cli/src/Cardano/CLI/EraBased/Option.hs index 5d9be2fce5..ea7f14667d 100644 --- a/cardano-cli/src/Cardano/CLI/EraBased/Option.hs +++ b/cardano-cli/src/Cardano/CLI/EraBased/Option.hs @@ -4,10 +4,12 @@ module Cardano.CLI.EraBased.Option ( pCmds , pAnyEraCommand + , MainnetEra ) where import Cardano.Api.Experimental +import Cardano.Api.Pretty (prettyShow) import Cardano.CLI.Environment import Cardano.CLI.EraBased.Command @@ -29,6 +31,9 @@ import Data.Maybe import Options.Applicative (Parser) import Options.Applicative qualified as Opt +-- | The era running on mainnet, used by the latest, top-level node and top-level query commands. +type MainnetEra = ConwayEra + pCmds :: forall era. IsEra era => EnvCli -> Parser (Cmds era) pCmds envCli = do asum $ @@ -59,6 +64,7 @@ pAnyEraCommand envCli = Opt.progDesc "Dijkstra era commands" , Opt.hsubparser $ commandWithMetavar "latest" $ - Opt.info (AnyEraCommandOf ConwayEra <$> pCmds @ConwayEra envCli) $ - Opt.progDesc "Latest era commands (Conway)" + Opt.info (AnyEraCommandOf (useEra @MainnetEra) <$> pCmds @MainnetEra envCli) $ + Opt.progDesc $ + "Latest era commands (" <> prettyShow (useEra @MainnetEra) <> ")" ] diff --git a/cardano-cli/src/Cardano/CLI/EraBased/Transaction/Run.hs b/cardano-cli/src/Cardano/CLI/EraBased/Transaction/Run.hs index 831320d89d..aef33e5c08 100644 --- a/cardano-cli/src/Cardano/CLI/EraBased/Transaction/Run.hs +++ b/cardano-cli/src/Cardano/CLI/EraBased/Transaction/Run.hs @@ -1530,12 +1530,9 @@ runTransactionPolicyIdCmd -> CIO e () runTransactionPolicyIdCmd Cmd.TransactionPolicyIdCmdArgs - { scriptFile = File sFile - } = do - script <- - readAnyScript @_ @ConwayEra sFile - let hash = fromShelleyScriptHash $ Exp.hashAnyScript script - liftIO . Text.putStrLn $ serialiseToRawBytesHexText hash + { scriptFile + } = + liftIO . Text.putStrLn . serialiseToRawBytesHexText =<< readAnyScriptHash scriptFile -- ---------------------------------------------------------------------------- -- Other misc small commands diff --git a/cardano-cli/src/Cardano/CLI/EraIndependent/Address/Run.hs b/cardano-cli/src/Cardano/CLI/EraIndependent/Address/Run.hs index 4012f01619..88f94be2b8 100644 --- a/cardano-cli/src/Cardano/CLI/EraIndependent/Address/Run.hs +++ b/cardano-cli/src/Cardano/CLI/EraIndependent/Address/Run.hs @@ -17,14 +17,12 @@ module Cardano.CLI.EraIndependent.Address.Run where import Cardano.Api -import Cardano.Api.Experimental.AnyScript qualified as Exp import Cardano.CLI.Compatible.Exception import Cardano.CLI.EraIndependent.Address.Command import Cardano.CLI.EraIndependent.Address.Info.Run import Cardano.CLI.EraIndependent.Key.Run qualified as Key import Cardano.CLI.Read -import Cardano.CLI.Read qualified as Exp import Cardano.CLI.Type.Common import Cardano.CLI.Type.Error.AddressCmdError import Cardano.CLI.Type.Key @@ -40,6 +38,7 @@ import Cardano.CLI.Type.Key import Control.Monad (void) import Data.ByteString.Char8 qualified as BS import Data.Function +import Data.Maybe (fromMaybe) import Data.Text.IO qualified as Text import Vary (Vary) import Vary qualified @@ -206,15 +205,11 @@ runAddressBuildCmd paymentVerifier mbStakeVerifier nw mOutFp = do nonPaymentKey -> throwCliError $ AddressCmdExpectedPaymentVerificationKey nonPaymentKey return $ serialiseAddress (addr :: AddressAny) - PaymentVerifierScriptFile (File fp) -> do - script <- - readAnyScript @_ @ConwayEra fp - - let hash = fromShelleyScriptHash $ Exp.hashAnyScript script - payCred = PaymentCredentialByScript hash + PaymentVerifierScriptFile scriptFile -> do + payCred <- PaymentCredentialByScript <$> readAnyScriptHash scriptFile stakeAddressReference <- - maybe (return NoStakeAddress) makeStakeAddressRef mbStakeVerifier + fromMaybe NoStakeAddress <$> traverse makeStakeAddressRef mbStakeVerifier return $ serialiseAddress . makeShelleyAddress nw payCred $ stakeAddressReference @@ -233,14 +228,8 @@ makeStakeAddressRef stakeIdentifier = stakeVKeyHash <- readVerificationKeyOrHashOrFile stkVkeyOrFile return . StakeAddressByValue $ StakeCredentialByKey stakeVKeyHash - StakeVerifierScriptFile (File fp) -> do - script <- - Exp.readAnyScript @_ @ConwayEra fp - - let hash = fromShelleyScriptHash $ Exp.hashAnyScript script - stakeCred = StakeCredentialByScript hash - - return (StakeAddressByValue stakeCred) + StakeVerifierScriptFile scriptFile -> + StakeAddressByValue . StakeCredentialByScript <$> readAnyScriptHash scriptFile StakeIdentifierAddress stakeAddr -> pure $ StakeAddressByValue $ stakeAddressCredential stakeAddr @@ -251,4 +240,5 @@ buildShelleyAddress -> CIO e (Address ShelleyAddr) buildShelleyAddress vkey mbStakeVerifier nw = makeShelleyAddress nw (PaymentCredentialByKey (verificationKeyHash vkey)) - <$> maybe (return NoStakeAddress) makeStakeAddressRef mbStakeVerifier + . fromMaybe NoStakeAddress + <$> traverse makeStakeAddressRef mbStakeVerifier diff --git a/cardano-cli/src/Cardano/CLI/EraIndependent/Hash/Run.hs b/cardano-cli/src/Cardano/CLI/EraIndependent/Hash/Run.hs index 2424002568..d56398571e 100644 --- a/cardano-cli/src/Cardano/CLI/EraIndependent/Hash/Run.hs +++ b/cardano-cli/src/Cardano/CLI/EraIndependent/Hash/Run.hs @@ -13,7 +13,6 @@ module Cardano.CLI.EraIndependent.Hash.Run where import Cardano.Api -import Cardano.Api.Experimental.AnyScript qualified as Exp import Cardano.Api.Ledger qualified as L import Cardano.CLI.Compatible.Exception @@ -88,15 +87,12 @@ runHashScriptCmd :: forall e . Cmd.HashScriptCmdArgs -> CIO e () -runHashScriptCmd Cmd.HashScriptCmdArgs{Cmd.toHash = File toHash, mOutFile} = +runHashScriptCmd Cmd.HashScriptCmdArgs{Cmd.toHash, mOutFile} = do - script <- - readAnyScript @_ @DijkstraEra toHash - let hash = Exp.hashAnyScript script + hash <- readAnyScriptHash toHash fromEitherIOCli @(FileError ()) $ writeTextOutput mOutFile $ - serialiseToRawBytesHexText $ - fromShelleyScriptHash hash + serialiseToRawBytesHexText hash runHashGenesisFile :: GenesisFile -> CIO e () runHashGenesisFile (GenesisFile fpath) = do diff --git a/cardano-cli/src/Cardano/CLI/Option.hs b/cardano-cli/src/Cardano/CLI/Option.hs index 328f78722f..743ccf2d75 100644 --- a/cardano-cli/src/Cardano/CLI/Option.hs +++ b/cardano-cli/src/Cardano/CLI/Option.hs @@ -8,8 +8,6 @@ module Cardano.CLI.Option ) where -import Cardano.Api.Experimental (ConwayEra) - import Cardano.CLI.Byron.Parser (backwardsCompatibilityCommands, parseByronCommands) import Cardano.CLI.Compatible.Option import Cardano.CLI.Environment (EnvCli) @@ -61,14 +59,14 @@ addressCmdsTopLevel envCli = AddressCommand <$> pAddressCmds envCli -- The node related commands are shelley era agnostic for the time being. -- There is no need to guard them by the era argument. nodeCmdsTopLevel :: Parser ClientCommand -nodeCmdsTopLevel = NodeCommands <$> pNodeCmds @ConwayEra +nodeCmdsTopLevel = NodeCommands <$> pNodeCmds @MainnetEra -- Queries actually depend on the node to client version which may coincide -- with a hardfork but not necessarily. We will expose commands at the top level -- regardless if they are compatible with the era or not. The help text should be -- updated to make this clear. Gating commands behind eras queryCmdsTopLevel :: EnvCli -> Parser ClientCommand -queryCmdsTopLevel envCli = QueryCommands @ConwayEra <$> pQueryCmdsTopLevel envCli +queryCmdsTopLevel envCli = QueryCommands @MainnetEra <$> pQueryCmdsTopLevel envCli keyCmdsTopLevel :: Parser ClientCommand keyCmdsTopLevel = KeyCommands <$> pKeyCmds diff --git a/cardano-cli/src/Cardano/CLI/Read.hs b/cardano-cli/src/Cardano/CLI/Read.hs index ece394ef91..4fe381577d 100644 --- a/cardano-cli/src/Cardano/CLI/Read.hs +++ b/cardano-cli/src/Cardano/CLI/Read.hs @@ -16,6 +16,7 @@ module Cardano.CLI.Read -- * Script , ScriptDecodeError (..) , readAnyScript + , readAnyScriptHash , deserialiseScriptInAnyLang , readFileScriptInAnyLang , PlutusScriptDecodeError (..) @@ -200,6 +201,15 @@ readVerificationKeySource extractHash = \case VksKeyHashFile vKeyOrHashOrFile -> L.KeyHashObj . extractHash <$> readVerificationKeyOrHashOrTextEnvFile vKeyOrHashOrFile +-- | Reads the script in cardano-api's newest era, so every script language decodes. +-- Returns only the era-independent script hash. +readAnyScriptHash :: MonadIO m => ScriptFile -> m ScriptHash +readAnyScriptHash scriptFile = + case maxBound of + Exp.Some (era :: Exp.Era era) -> + Exp.obtainCommonConstraints era $ + fromShelleyScriptHash . Exp.hashAnyScript <$> readAnyScript @_ @era (unFile scriptFile) + readAnyScript :: forall m era . (MonadIO m, Exp.IsEra era) diff --git a/cardano-cli/test/cardano-cli-golden/Test/Golden/Conway/Transaction/ReferenceScript.hs b/cardano-cli/test/cardano-cli-golden/Test/Golden/Conway/Transaction/ReferenceScript.hs new file mode 100644 index 0000000000..573abc64e9 --- /dev/null +++ b/cardano-cli/test/cardano-cli-golden/Test/Golden/Conway/Transaction/ReferenceScript.hs @@ -0,0 +1,43 @@ +{-# LANGUAGE OverloadedStrings #-} + +module Test.Golden.Conway.Transaction.ReferenceScript where + +import GHC.IO.Exception (ExitCode (ExitFailure)) + +import Test.Cardano.CLI.Util + +import Hedgehog (Property, (===)) +import Hedgehog.Extras.Test qualified as H + +-- A reference script in a Plutus language the era does not support must fail +-- the command instead of being silently dropped from the output. + +-- | Execute me with: +-- @cabal test cardano-cli-golden --test-options '-p "/golden conway build raw reference script unsupported language/"'@ +hprop_golden_conway_build_raw_reference_script_unsupported_language :: Property +hprop_golden_conway_build_raw_reference_script_unsupported_language = + watchdogProp . propertyOnce . H.moduleWorkspace "tmp" $ \tempDir -> do + outFile <- noteTempFile tempDir "out.json" + + (exitCode, _stdout, stderr) <- + execDetailCardanoCLI + [ "conway" + , "transaction" + , "build-raw" + , "--tx-in" + , "f62cd7bc15d8c6d2c8519fb8d13c57c0157ab6bab50af62bc63706feb966393d#0" + , "--tx-out" + , "addr_test1qpmxr8d8jcl25kyz2tz9a9sxv7jxglhddyf475045y8j3zxjcg9vquzkljyfn3rasfwwlkwu7hhm59gzxmsyxf3w9dps8832xh+5000000" + , "--tx-out-reference-script-file" + , "test/cardano-cli-golden/files/input/dijkstra/plutus/v4-always-succeeds.plutus" + , "--fee" + , "166777" + , "--out-file" + , outFile + ] + + exitCode === ExitFailure 1 + + H.diffVsGoldenFileExcludeTrace + stderr + "test/cardano-cli-golden/files/golden/conway/transaction/build-raw-reference-script-unsupported-language.out" diff --git a/cardano-cli/test/cardano-cli-golden/Test/Golden/Dijkstra/Transaction/BuildRaw.hs b/cardano-cli/test/cardano-cli-golden/Test/Golden/Dijkstra/Transaction/BuildRaw.hs new file mode 100644 index 0000000000..7a67a2fdd1 --- /dev/null +++ b/cardano-cli/test/cardano-cli-golden/Test/Golden/Dijkstra/Transaction/BuildRaw.hs @@ -0,0 +1,126 @@ +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TypeApplications #-} + +module Test.Golden.Dijkstra.Transaction.BuildRaw where + +import Cardano.Api.Experimental qualified as Exp +import Cardano.Api.Ledger qualified as L + +import Control.Monad (void) +import Data.Aeson qualified as Aeson + +import Test.Cardano.CLI.Util + +import Hedgehog (Property) +import Hedgehog qualified as H +import Hedgehog.Extras.Test qualified as H + +-- A Plutus V4 script file must work as a direct witness for every script purpose. +-- Regression test for https://github.com/IntersectMBO/cardano-cli/issues/1448 + +-- | Execute me with: +-- @cabal test cardano-cli-golden --test-options '-p "/golden dijkstra build raw plutus v4 script witnesses/"'@ +hprop_golden_dijkstra_build_raw_plutus_v4_script_witnesses :: Property +hprop_golden_dijkstra_build_raw_plutus_v4_script_witnesses = + watchdogProp . propertyOnce . H.moduleWorkspace "tmp" $ \tempDir -> do + pparamsFile <- noteTempFile tempDir "protocol-params.json" + proposalFile <- noteTempFile tempDir "info.action" + txBodyFile <- noteTempFile tempDir "tx-body" + + let v4Script = "test/cardano-cli-golden/files/input/dijkstra/plutus/v4-always-succeeds.plutus" + v4ScriptHash = "31a78786b5989dc6fd2d4ab15b297069e94106ecacceb8eb29e1b681" + + -- build-raw does not evaluate scripts, so empty protocol parameters suffice. + H.evalIO . Aeson.encodeFile pparamsFile . L.PParams $ + L.emptyPParamsIdentity @(Exp.LedgerEra Exp.DijkstraEra) + + void $ + execCardanoCLI + [ "dijkstra" + , "governance" + , "action" + , "create-info" + , "--testnet" + , "--governance-action-deposit" + , "12345" + , "--deposit-return-stake-verification-key-file" + , "test/cardano-cli-golden/files/input/governance/stake-address.vkey" + , "--anchor-url" + , "https://example.com" + , "--anchor-data-hash" + , "52e69500a92d80f2126c836a4903dc582006709f004cf7a28ed648f732dff8d2" + , "--out-file" + , proposalFile + ] + + void $ + execCardanoCLI + [ "dijkstra" + , "transaction" + , "build-raw" + , "--tx-in" + , "fe5dd07fb576bff960d6e066eade5b26cdb5afebe29f76ea58d0a098bce5d891#200" + , "--tx-in-script-file" + , v4Script + , "--tx-in-datum-value" + , "24" + , "--tx-in-redeemer-value" + , "42" + , "--tx-in-execution-units" + , "(100,110)" + , "--mint" + , "1000 " <> v4ScriptHash + , "--mint-script-file" + , v4Script + , "--mint-redeemer-value" + , "42" + , "--mint-execution-units" + , "(100,110)" + , "--withdrawal" + , "stake_test17qvxuvh64q9zdqgrjt76d42eclk5wgdxtnsun4808cwg0dqxv5r99+10000" + , "--withdrawal-script-file" + , v4Script + , "--withdrawal-redeemer-value" + , "83" + , "--withdrawal-execution-units" + , "(100,110)" + , "--certificate-file" + , "test/cardano-cli-golden/files/input/stake-address-registration.json" + , "--certificate-script-file" + , v4Script + , "--certificate-redeemer-value" + , "63" + , "--certificate-execution-units" + , "(100,110)" + , "--vote-file" + , "test/cardano-cli-golden/files/input/conway/vote1.drep.json" + , "--vote-script-file" + , v4Script + , "--vote-redeemer-value" + , "1" + , "--vote-execution-units" + , "(100,110)" + , "--proposal-file" + , proposalFile + , "--proposal-script-file" + , v4Script + , "--proposal-redeemer-value" + , "1" + , "--proposal-execution-units" + , "(100,110)" + , "--tx-out" + , "addr_test1vp0t4dfa9ktc2uvv7sg9leafuhtwyu0xcj4q4kf5pqkpjwqhklklg+15000000" + , "--fee" + , "200000" + , "--protocol-params-file" + , pparamsFile + , "--out-file" + , txBodyFile + ] + + result <- + execCardanoCLI + ["debug", "transaction", "view", "--tx-body-file", txBodyFile, "--output-yaml"] + H.diffVsGoldenFile + result + "test/cardano-cli-golden/files/golden/dijkstra/transaction/build-raw-plutus-v4-script-witnesses.out" diff --git a/cardano-cli/test/cardano-cli-golden/Test/Golden/Hash/Hash.hs b/cardano-cli/test/cardano-cli-golden/Test/Golden/Hash/Hash.hs index ec8e40f3db..96f682e400 100644 --- a/cardano-cli/test/cardano-cli-golden/Test/Golden/Hash/Hash.hs +++ b/cardano-cli/test/cardano-cli-golden/Test/Golden/Hash/Hash.hs @@ -25,3 +25,25 @@ hprop_golden_governance_hash_script = ] H.diffFileVsGoldenFile hashFile hashGold + +-- Check that `hash script` can hash a Plutus V4 script envelope. +-- Regression test for https://github.com/IntersectMBO/cardano-cli/issues/1448 +hprop_golden_hash_script_plutus_v4 :: Property +hprop_golden_hash_script_plutus_v4 = + watchdogProp . H.propertyOnce . H.moduleWorkspace "tmp" $ \tempDir -> do + scriptFile <- + noteInputFile "test/cardano-cli-golden/files/input/dijkstra/plutus/v4-always-succeeds.plutus" + hashFile <- H.noteTempFile tempDir "v4-always-succeeds.hash" + hashGold <- H.note "test/cardano-cli-golden/files/golden/dijkstra/plutus/v4-always-succeeds.hash" + + void $ + execCardanoCLI + [ "hash" + , "script" + , "--script-file" + , scriptFile + , "--out-file" + , hashFile + ] + + H.diffFileVsGoldenFile hashFile hashGold diff --git a/cardano-cli/test/cardano-cli-golden/Test/Golden/Shelley/Address/Build.hs b/cardano-cli/test/cardano-cli-golden/Test/Golden/Shelley/Address/Build.hs index a8787cd0ce..29357790f9 100644 --- a/cardano-cli/test/cardano-cli-golden/Test/Golden/Shelley/Address/Build.hs +++ b/cardano-cli/test/cardano-cli-golden/Test/Golden/Shelley/Address/Build.hs @@ -9,6 +9,7 @@ import Test.Cardano.CLI.Util as OP import Hedgehog (Property) import Hedgehog.Extras.Test.Base qualified as H import Hedgehog.Extras.Test.File qualified as H +import Hedgehog.Extras.Test.Golden qualified as H hprop_golden_shelleyAddressBuild :: Property hprop_golden_shelleyAddressBuild = @@ -65,3 +66,45 @@ hprop_golden_shelleyAddressBuild = H.writeFile enterpriseAddressHexFile enterpriseAddressText equivalence enterpriseAddressText goldenEnterpriseAddressHex + +-- Regression tests for https://github.com/IntersectMBO/cardano-cli/issues/1448 +hprop_golden_addressBuildPlutusV4PaymentScript :: Property +hprop_golden_addressBuildPlutusV4PaymentScript = + watchdogProp . propertyOnce $ do + address <- + execCardanoCLI + [ "latest" + , "address" + , "build" + , "--testnet-magic" + , "14" + , "--payment-script-file" + , plutusV4ScriptFile + ] + + H.diffVsGoldenFile + address + "test/cardano-cli-golden/files/golden/dijkstra/address/build-payment-script-v4.addr" + +hprop_golden_addressBuildPlutusV4StakeScript :: Property +hprop_golden_addressBuildPlutusV4StakeScript = + watchdogProp . propertyOnce $ do + address <- + execCardanoCLI + [ "latest" + , "address" + , "build" + , "--testnet-magic" + , "14" + , "--payment-verification-key-file" + , "test/cardano-cli-golden/files/input/shelley/keys/payment_keys/verification_key" + , "--stake-script-file" + , plutusV4ScriptFile + ] + + H.diffVsGoldenFile + address + "test/cardano-cli-golden/files/golden/dijkstra/address/build-stake-script-v4.addr" + +plutusV4ScriptFile :: FilePath +plutusV4ScriptFile = "test/cardano-cli-golden/files/input/dijkstra/plutus/v4-always-succeeds.plutus" diff --git a/cardano-cli/test/cardano-cli-golden/Test/Golden/Shelley/Transaction/Build.hs b/cardano-cli/test/cardano-cli-golden/Test/Golden/Shelley/Transaction/Build.hs index 43ddf1f5be..5aeb4a132e 100644 --- a/cardano-cli/test/cardano-cli-golden/Test/Golden/Shelley/Transaction/Build.hs +++ b/cardano-cli/test/cardano-cli-golden/Test/Golden/Shelley/Transaction/Build.hs @@ -83,6 +83,25 @@ hprop_golden_shelley_transaction_build_minting = goldenFile <- H.note "test/cardano-cli-golden/files/golden/shelley/build-raw-tx-body-out-3.json" H.diffFileVsGoldenFile txBodyOutFile goldenFile +-- The policy id of a Plutus V4 script is its script hash. +-- Regression test for https://github.com/IntersectMBO/cardano-cli/issues/1448 +hprop_golden_transaction_policyid_plutus_v4 :: Property +hprop_golden_transaction_policyid_plutus_v4 = + watchdogProp . propertyOnce $ do + policyId <- + execCardanoCLI + [ "latest" + , "transaction" + , "policyid" + , "--script-file" + , "test/cardano-cli-golden/files/input/dijkstra/plutus/v4-always-succeeds.plutus" + ] + + scriptHash <- + H.readFile "test/cardano-cli-golden/files/golden/dijkstra/plutus/v4-always-succeeds.hash" + + equivalence (filter (/= '\n') policyId) scriptHash + hprop_golden_shelley_transaction_build_withdrawal_script_witnessed :: Property hprop_golden_shelley_transaction_build_withdrawal_script_witnessed = watchdogProp . propertyOnce $ H.moduleWorkspace "tmp" $ \tempDir -> do diff --git a/cardano-cli/test/cardano-cli-golden/files/golden/conway/transaction/build-raw-reference-script-unsupported-language.out b/cardano-cli/test/cardano-cli-golden/files/golden/conway/transaction/build-raw-reference-script-unsupported-language.out new file mode 100644 index 0000000000..81a0e7d9f4 --- /dev/null +++ b/cardano-cli/test/cardano-cli-golden/files/golden/conway/transaction/build-raw-reference-script-unsupported-language.out @@ -0,0 +1,2 @@ +Command failed: transaction build-raw +Error: Reference script language PlutusV4 is not supported in the Conway era. diff --git a/cardano-cli/test/cardano-cli-golden/files/golden/dijkstra/address/build-payment-script-v4.addr b/cardano-cli/test/cardano-cli-golden/files/golden/dijkstra/address/build-payment-script-v4.addr new file mode 100644 index 0000000000..cfaf21fb02 --- /dev/null +++ b/cardano-cli/test/cardano-cli-golden/files/golden/dijkstra/address/build-payment-script-v4.addr @@ -0,0 +1 @@ +addr_test1wqc60puxkkvfm3ha949tzkefwp57jsgxajkvaw8t98smdqghg77g0 \ No newline at end of file diff --git a/cardano-cli/test/cardano-cli-golden/files/golden/dijkstra/address/build-stake-script-v4.addr b/cardano-cli/test/cardano-cli-golden/files/golden/dijkstra/address/build-stake-script-v4.addr new file mode 100644 index 0000000000..c02708bcda --- /dev/null +++ b/cardano-cli/test/cardano-cli-golden/files/golden/dijkstra/address/build-stake-script-v4.addr @@ -0,0 +1 @@ +addr_test1ypfqwx0t6v5d80f6gqaznkk676vph0wmt68lfera8am87f3357rcddvcnhr06t22k9djjurfa9qsdm9ve6uwk20pk6qsjutxhj \ No newline at end of file diff --git a/cardano-cli/test/cardano-cli-golden/files/golden/dijkstra/plutus/v4-always-succeeds.hash b/cardano-cli/test/cardano-cli-golden/files/golden/dijkstra/plutus/v4-always-succeeds.hash new file mode 100644 index 0000000000..8534ad9b2e --- /dev/null +++ b/cardano-cli/test/cardano-cli-golden/files/golden/dijkstra/plutus/v4-always-succeeds.hash @@ -0,0 +1 @@ +31a78786b5989dc6fd2d4ab15b297069e94106ecacceb8eb29e1b681 \ No newline at end of file diff --git a/cardano-cli/test/cardano-cli-golden/files/golden/dijkstra/transaction/build-raw-plutus-v4-script-witnesses.out b/cardano-cli/test/cardano-cli-golden/files/golden/dijkstra/transaction/build-raw-plutus-v4-script-witnesses.out new file mode 100644 index 0000000000..60db910a31 --- /dev/null +++ b/cardano-cli/test/cardano-cli-golden/files/golden/dijkstra/transaction/build-raw-plutus-v4-script-witnesses.out @@ -0,0 +1,127 @@ +auxiliary scripts: null +certificates: +- Stake address registration: + deposit: 2000000 + stake credential: + keyHash: 81b4245f84593f63c0ff2296930254f77a0690fa9de5f969cac27cb0 +collateral inputs: [] +currentTreasuryValue: null +datums: +- datum: I 24 + datum hash: df5078aee07dd171a343fb99d5fc1b5462fb3c94d82bf72dc1b77d9c0aceec29 +era: Dijkstra +fee: 200000 Lovelace +governance actions: +- anchor: + dataHash: 52e69500a92d80f2126c836a4903dc582006709f004cf7a28ed648f732dff8d2 + url: https://example.com + deposit: 12345 + governance action: + tag: InfoAction + return address: + credential: + keyHash: 8f4a3466a404c11eb410313015b88e447d81b60089e25f611600e605 + network: Testnet +inputs: +- fe5dd07fb576bff960d6e066eade5b26cdb5afebe29f76ea58d0a098bce5d891#200 +metadata: null +mint: + policy 31a78786b5989dc6fd2d4ab15b297069e94106ecacceb8eb29e1b681: + default asset: 1000 +outputs: +- address: addr_test1vp0t4dfa9ktc2uvv7sg9leafuhtwyu0xcj4q4kf5pqkpjwqhklklg + address era: Shelley + amount: + lovelace: 15000000 + datum: null + network: Testnet + payment credential key hash: 5ebab53d2d9785718cf4105fe7a9e5d6e271e6c4aa0ad934082c1938 + reference script: null + stake reference: null +redeemers: +- purpose: + spending script witnessed input: fe5dd07fb576bff960d6e066eade5b26cdb5afebe29f76ea58d0a098bce5d891#200 + redeemer: + data: I 42 + execution units: + memory: 110 + steps: 100 +- purpose: + minting currency with policy id: 31a78786b5989dc6fd2d4ab15b297069e94106ecacceb8eb29e1b681 + redeemer: + data: I 42 + execution units: + memory: 110 + steps: 100 +- purpose: + validating certificate with script credentials: + credential: + keyHash: 81b4245f84593f63c0ff2296930254f77a0690fa9de5f969cac27cb0 + deposit: 2000000 + kind: RegCert + redeemer: + data: I 63 + execution units: + memory: 110 + steps: 100 +- purpose: + withdrawing reward from script address: + credential: + scriptHash: 186e32faa80a26810392fda6d559c7ed4721a65ce1c9d4ef3e1c87b4 + network: Testnet + redeemer: + data: I 83 + execution units: + memory: 110 + steps: 100 +- purpose: + voting using script protected voter credentials: + contents: + keyHash: 8f4fefcf28017a57b41517a67d56ef4c0dc04181a11d35178dd53f4c + tag: DRepVoter + redeemer: + data: I 1 + execution units: + memory: 110 + steps: 100 +- purpose: + submitting a proposal following proposal policy: + anchor: + dataHash: 52e69500a92d80f2126c836a4903dc582006709f004cf7a28ed648f732dff8d2 + url: https://example.com + deposit: 12345 + govAction: + tag: InfoAction + returnAddr: + credential: + keyHash: 8f4a3466a404c11eb410313015b88e447d81b60089e25f611600e605 + network: Testnet + redeemer: + data: I 1 + execution units: + memory: 110 + steps: 100 +reference inputs: [] +required signers (payment key hashes needed for scripts): null +return collateral: null +scripts: +- script data: + plutus version: PlutusV4 + script: '46450101002499' + type: plutus + script hash: 31a78786b5989dc6fd2d4ab15b297069e94106ecacceb8eb29e1b681 +total collateral: null +treasuryDonation: 0 +validity range: + lower bound: null + upper bound: null +voters: + drep-keyHash-8f4fefcf28017a57b41517a67d56ef4c0dc04181a11d35178dd53f4c: + 704e5c60c51dd0f3732991980b273402fbad0581c3407d682f8ea5b808a530fc#0: + anchor: null + decision: VoteYes +withdrawals: +- address: stake_test17qvxuvh64q9zdqgrjt76d42eclk5wgdxtnsun4808cwg0dqxv5r99 + amount: 10000 Lovelace + network: Testnet + stake credential script hash: 186e32faa80a26810392fda6d559c7ed4721a65ce1c9d4ef3e1c87b4 diff --git a/cardano-cli/test/cardano-cli-golden/files/input/dijkstra/plutus/v4-always-succeeds.plutus b/cardano-cli/test/cardano-cli-golden/files/input/dijkstra/plutus/v4-always-succeeds.plutus new file mode 100644 index 0000000000..43cc31ebed --- /dev/null +++ b/cardano-cli/test/cardano-cli-golden/files/input/dijkstra/plutus/v4-always-succeeds.plutus @@ -0,0 +1,5 @@ +{ + "type": "PlutusScriptV4", + "description": "Always succeeds", + "cborHex": "46450101002499" +}