From 23f11f4532eddeca7b36291f63a6e878d05f435c Mon Sep 17 00:00:00 2001 From: Pablo Lamela Date: Tue, 10 Sep 2024 03:11:40 +0200 Subject: [PATCH 01/18] Added `--expected-hash` to `cardano-cli hash anchor-data` --- cardano-cli/src/Cardano/CLI/Commands/Hash.hs | 2 ++ .../Cardano/CLI/EraBased/Options/Common.hs | 13 ++++++++ cardano-cli/src/Cardano/CLI/Options/Hash.hs | 1 + cardano-cli/src/Cardano/CLI/Run/Hash.hs | 33 ++++++++++--------- .../Cardano/CLI/Types/Errors/HashCmdError.hs | 13 +++++++- .../cardano-cli-golden/files/golden/help.cli | 1 + .../files/golden/help/hash_anchor-data.cli | 4 +++ 7 files changed, 51 insertions(+), 16 deletions(-) diff --git a/cardano-cli/src/Cardano/CLI/Commands/Hash.hs b/cardano-cli/src/Cardano/CLI/Commands/Hash.hs index f0509c74e..3265dbaf5 100644 --- a/cardano-cli/src/Cardano/CLI/Commands/Hash.hs +++ b/cardano-cli/src/Cardano/CLI/Commands/Hash.hs @@ -12,6 +12,7 @@ module Cardano.CLI.Commands.Hash where import Cardano.Api +import qualified Cardano.Api.Ledger as L import Cardano.CLI.Types.Common @@ -24,6 +25,7 @@ data HashCmds data HashAnchorDataCmdArgs = HashAnchorDataCmdArgs { toHash :: !AnchorDataHashSource + , mExpectedHash :: !(Maybe (L.SafeHash L.StandardCrypto L.AnchorData)) , mOutFile :: !(Maybe (File () Out)) -- ^ The output file to which the hash is written } diff --git a/cardano-cli/src/Cardano/CLI/EraBased/Options/Common.hs b/cardano-cli/src/Cardano/CLI/EraBased/Options/Common.hs index 0d4e8679c..b6c09081b 100644 --- a/cardano-cli/src/Cardano/CLI/EraBased/Options/Common.hs +++ b/cardano-cli/src/Cardano/CLI/EraBased/Options/Common.hs @@ -3618,6 +3618,19 @@ pAnchorUrl = ProposalUrl <$> pUrl "anchor-url" "Anchor URL" +pExpectedHash :: Parser (L.SafeHash L.StandardCrypto L.AnchorData) +pExpectedHash = + Opt.option readSafeHash $ + mconcat + [ Opt.long "expected-hash" + , Opt.metavar "HASH" + , Opt.help $ + mconcat + [ "Expected hash for the anchor data for verification purposes. " + , "If provided, the hash of the anchor data will be compared to this value." + ] + ] + pAnchorDataHash :: Parser (L.SafeHash L.StandardCrypto L.AnchorData) pAnchorDataHash = Opt.option readSafeHash $ diff --git a/cardano-cli/src/Cardano/CLI/Options/Hash.hs b/cardano-cli/src/Cardano/CLI/Options/Hash.hs index 80ef0602d..b1e54ff7f 100644 --- a/cardano-cli/src/Cardano/CLI/Options/Hash.hs +++ b/cardano-cli/src/Cardano/CLI/Options/Hash.hs @@ -32,6 +32,7 @@ pHashAnchorDataCmd = do Cmd.HashAnchorDataCmd ( Cmd.HashAnchorDataCmdArgs <$> pAnchorDataHashSource + <*> optional pExpectedHash <*> optional pOutputFile ) ) diff --git a/cardano-cli/src/Cardano/CLI/Run/Hash.hs b/cardano-cli/src/Cardano/CLI/Run/Hash.hs index 301df84fe..6a700b2c4 100644 --- a/cardano-cli/src/Cardano/CLI/Run/Hash.hs +++ b/cardano-cli/src/Cardano/CLI/Run/Hash.hs @@ -36,21 +36,24 @@ runHashAnchorDataCmd :: () => Cmd.HashAnchorDataCmdArgs -> ExceptT HashCmdError IO () -runHashAnchorDataCmd Cmd.HashAnchorDataCmdArgs{toHash, mOutFile} = - case toHash of - Cmd.AnchorDataHashSourceBinaryFile fp -> do - let path = unFile fp - bytes <- handleIOExceptT (HashReadFileError path) $ BS.readFile path - let hash = L.hashAnchorData $ L.AnchorData bytes - writeHash hash - Cmd.AnchorDataHashSourceTextFile fp -> do - let path = unFile fp - text <- handleIOExceptT (HashReadFileError path) $ Text.readFile path - let hash = L.hashAnchorData $ L.AnchorData $ Text.encodeUtf8 text - writeHash hash - Cmd.AnchorDataHashSourceText text -> do - let hash = L.hashAnchorData $ L.AnchorData $ Text.encodeUtf8 text - writeHash hash +runHashAnchorDataCmd Cmd.HashAnchorDataCmdArgs{toHash, mExpectedHash, mOutFile} = do + anchorData <- + L.AnchorData <$> case toHash of + Cmd.AnchorDataHashSourceBinaryFile fp -> do + let path = unFile fp + handleIOExceptT (HashReadFileError path) $ BS.readFile path + Cmd.AnchorDataHashSourceTextFile fp -> do + let path = unFile fp + text <- handleIOExceptT (HashReadFileError path) $ Text.readFile path + return $ Text.encodeUtf8 text + Cmd.AnchorDataHashSourceText text -> return $ Text.encodeUtf8 text + let hash = L.hashAnchorData anchorData + case mExpectedHash of + Just expectedHash + | hash /= expectedHash -> + left $ HashMismatchedHashError expectedHash hash + | otherwise -> liftIO $ putStrLn "Hashes match!" + Nothing -> writeHash hash where writeHash :: L.SafeHash L.StandardCrypto i -> ExceptT HashCmdError IO () writeHash hash = do diff --git a/cardano-cli/src/Cardano/CLI/Types/Errors/HashCmdError.hs b/cardano-cli/src/Cardano/CLI/Types/Errors/HashCmdError.hs index b2cad2576..af1f01b1a 100644 --- a/cardano-cli/src/Cardano/CLI/Types/Errors/HashCmdError.hs +++ b/cardano-cli/src/Cardano/CLI/Types/Errors/HashCmdError.hs @@ -6,18 +6,29 @@ module Cardano.CLI.Types.Errors.HashCmdError where import Cardano.Api +import qualified Cardano.Api.Ledger as L import Cardano.CLI.Read (ScriptDecodeError) +import Cardano.Ledger.SafeHash (extractHash) import Cardano.Prelude (Exception (displayException), IOException) data HashCmdError - = HashReadFileError !FilePath !IOException + = HashMismatchedHashError + !(L.SafeHash L.StandardCrypto L.AnchorData) + !(L.SafeHash L.StandardCrypto L.AnchorData) + | HashReadFileError !FilePath !IOException | HashWriteFileError !(FileError ()) | HashReadScriptError !FilePath !(FileError ScriptDecodeError) deriving Show instance Error HashCmdError where prettyError = \case + HashMismatchedHashError expectedHash actualHash -> + "Hashes do not match! \n" + <> "Expected: " + <> pretty (show (extractHash expectedHash)) + <> "\n Actual: " + <> pretty (show (extractHash actualHash)) HashReadFileError filepath exc -> "Cannot read " <> pretty filepath <> ": " <> pretty (displayException exc) HashWriteFileError fileErr -> diff --git a/cardano-cli/test/cardano-cli-golden/files/golden/help.cli b/cardano-cli/test/cardano-cli-golden/files/golden/help.cli index a42022da6..5c6709537 100644 --- a/cardano-cli/test/cardano-cli-golden/files/golden/help.cli +++ b/cardano-cli/test/cardano-cli-golden/files/golden/help.cli @@ -12537,6 +12537,7 @@ Usage: cardano-cli hash anchor-data | --file-binary FILEPATH | --file-text FILEPATH ) + [--expected-hash HASH] [--out-file FILEPATH] Compute the hash of some anchor data (to then pass it to other commands). diff --git a/cardano-cli/test/cardano-cli-golden/files/golden/help/hash_anchor-data.cli b/cardano-cli/test/cardano-cli-golden/files/golden/help/hash_anchor-data.cli index 184ba93ce..a7bbb5270 100644 --- a/cardano-cli/test/cardano-cli-golden/files/golden/help/hash_anchor-data.cli +++ b/cardano-cli/test/cardano-cli-golden/files/golden/help/hash_anchor-data.cli @@ -3,6 +3,7 @@ Usage: cardano-cli hash anchor-data | --file-binary FILEPATH | --file-text FILEPATH ) + [--expected-hash HASH] [--out-file FILEPATH] Compute the hash of some anchor data (to then pass it to other commands). @@ -11,5 +12,8 @@ Available options: --text TEXT Text to hash as UTF-8 --file-binary FILEPATH Binary file to hash --file-text FILEPATH Text file to hash + --expected-hash HASH Expected hash for the anchor data for verification + purposes. If provided, the hash of the anchor data + will be compared to this value. --out-file FILEPATH The output file. -h,--help Show this help text From 0d1293ec3f521d18e8a05d024bd35ad4940bc744 Mon Sep 17 00:00:00 2001 From: Pablo Lamela Date: Tue, 10 Sep 2024 21:30:46 +0200 Subject: [PATCH 02/18] Add support for HTTP(S) URLs to `hash anchor-data` --- cardano-cli/cardano-cli.cabal | 5 ++ cardano-cli/src/Cardano/CLI/Commands/Hash.hs | 1 + cardano-cli/src/Cardano/CLI/Options/Hash.hs | 2 + cardano-cli/src/Cardano/CLI/Run/Hash.hs | 55 +++++++++++++++++++ .../Cardano/CLI/Types/Errors/HashCmdError.hs | 22 ++++++++ .../cardano-cli-golden/files/golden/help.cli | 1 + .../files/golden/help/hash_anchor-data.cli | 2 + 7 files changed, 88 insertions(+) diff --git a/cardano-cli/cardano-cli.cabal b/cardano-cli/cardano-cli.cabal index 9b187f99c..228c3e352 100644 --- a/cardano-cli/cardano-cli.cabal +++ b/cardano-cli/cardano-cli.cabal @@ -235,13 +235,18 @@ library cryptonite, deepseq, directory, + exceptions, filepath, formatting, + http-client, + http-client-tls, + http-types, io-classes, iproute, microlens, mtl, network, + network-uri, optparse-applicative-fork, ouroboros-consensus ^>=0.20, ouroboros-consensus-cardano ^>=0.19, diff --git a/cardano-cli/src/Cardano/CLI/Commands/Hash.hs b/cardano-cli/src/Cardano/CLI/Commands/Hash.hs index 3265dbaf5..5b4afbf2d 100644 --- a/cardano-cli/src/Cardano/CLI/Commands/Hash.hs +++ b/cardano-cli/src/Cardano/CLI/Commands/Hash.hs @@ -35,6 +35,7 @@ data AnchorDataHashSource = AnchorDataHashSourceBinaryFile (File ProposalBinary In) | AnchorDataHashSourceTextFile (File ProposalText In) | AnchorDataHashSourceText Text + | AnchorDataHashSourceURL L.Url deriving Show data HashScriptCmdArgs diff --git a/cardano-cli/src/Cardano/CLI/Options/Hash.hs b/cardano-cli/src/Cardano/CLI/Options/Hash.hs index b1e54ff7f..2cbfb3cd1 100644 --- a/cardano-cli/src/Cardano/CLI/Options/Hash.hs +++ b/cardano-cli/src/Cardano/CLI/Options/Hash.hs @@ -53,6 +53,8 @@ pAnchorDataHashSource = <$> pFileInDirection "file-binary" "Binary file to hash" , Cmd.AnchorDataHashSourceTextFile <$> pFileInDirection "file-text" "Text file to hash" + , Cmd.AnchorDataHashSourceURL + <$> pUrl "url" "A URL to the file to hash (HTTP(S) only)" ] pHashScriptCmd :: Parser Cmd.HashCmds diff --git a/cardano-cli/src/Cardano/CLI/Run/Hash.hs b/cardano-cli/src/Cardano/CLI/Run/Hash.hs index 6a700b2c4..34e1ee290 100644 --- a/cardano-cli/src/Cardano/CLI/Run/Hash.hs +++ b/cardano-cli/src/Cardano/CLI/Run/Hash.hs @@ -19,10 +19,22 @@ import Cardano.CLI.Read import Cardano.CLI.Types.Errors.HashCmdError import Cardano.Crypto.Hash (hashToTextAsHex) +import Control.Exception (throw) +import Control.Monad.Catch (Exception, Handler (Handler)) import qualified Data.ByteString as BS +import qualified Data.ByteString.Char8 as BS8 +import qualified Data.ByteString.Lazy as BSL +import Data.Char (toLower) import Data.Function +import qualified Data.Text as Text import qualified Data.Text.Encoding as Text import qualified Data.Text.IO as Text +import Network.HTTP.Client (Response (..), httpLbs, newManager, requestFromURI) +import Network.HTTP.Client.TLS (tlsManagerSettings) +import Network.HTTP.Types (Status (statusCode), statusMessage) +import Network.URI (URI (..), parseAbsoluteURI, pathSegments) +import System.FilePath (()) +import System.FilePath.Posix (isDrive) runHashCmds :: () @@ -47,6 +59,8 @@ runHashAnchorDataCmd Cmd.HashAnchorDataCmdArgs{toHash, mExpectedHash, mOutFile} text <- handleIOExceptT (HashReadFileError path) $ Text.readFile path return $ Text.encodeUtf8 text Cmd.AnchorDataHashSourceText text -> return $ Text.encodeUtf8 text + Cmd.AnchorDataHashSourceURL urlText -> + getByteStringFromURL urlText let hash = L.hashAnchorData anchorData case mExpectedHash of Just expectedHash @@ -63,6 +77,47 @@ runHashAnchorDataCmd Cmd.HashAnchorDataCmdArgs{toHash, mExpectedHash, mOutFile} where text = hashToTextAsHex . L.extractHash $ hash + getByteStringFromURL :: L.Url -> ExceptT HashCmdError IO BS.ByteString + getByteStringFromURL urlText = + let urlString = Text.unpack $ L.urlToText urlText + in case parseAbsoluteURI urlString of + Just uri -> do + case map toLower $ uriScheme uri of + "file:" -> + let path = uriPathToFilePath (pathSegments uri) + in handleIOExceptT (HashReadFileError path) $ BS.readFile path + "http:" -> getFileFromHttp uri + "https:" -> getFileFromHttp uri + unsupportedScheme -> left $ HashUnsupportedURLSchemeError unsupportedScheme + Nothing -> left $ HashInvalidURLError urlString + where + uriPathToFilePath :: [String] -> FilePath + uriPathToFilePath allPath@(letter : path) = + if isDrive letter + then foldl () letter path + else foldl () "/" allPath + uriPathToFilePath [] = "/" + + getFileFromHttp :: URI -> ExceptT HashCmdError IO BS.ByteString + getFileFromHttp uri = handlesExceptT handlers $ liftIO $ do + request <- requestFromURI uri + manager <- newManager tlsManagerSettings + response <- httpLbs request manager + let status = responseStatus response + if statusCode status /= 200 + then throw $ BadStatusCodeHRE (statusCode status) (BS8.unpack $ statusMessage status) + else return $ BS.concat . BSL.toChunks $ responseBody response + where + handlers :: [Handler IO HashCmdError] + handlers = + [ mkHandler id + , mkHandler HttpExceptionHRE + , mkHandler IOExceptionHRE + ] + where + mkHandler :: (Monad m, Exception e) => (e -> HttpRequestError) -> Handler m HashCmdError + mkHandler x = Handler $ return . HashGetFileFromHttpError . x + runHashScriptCmd :: () => Cmd.HashScriptCmdArgs diff --git a/cardano-cli/src/Cardano/CLI/Types/Errors/HashCmdError.hs b/cardano-cli/src/Cardano/CLI/Types/Errors/HashCmdError.hs index af1f01b1a..7912c57eb 100644 --- a/cardano-cli/src/Cardano/CLI/Types/Errors/HashCmdError.hs +++ b/cardano-cli/src/Cardano/CLI/Types/Errors/HashCmdError.hs @@ -1,7 +1,9 @@ +{-# LANGUAGE InstanceSigs #-} {-# LANGUAGE LambdaCase #-} module Cardano.CLI.Types.Errors.HashCmdError ( HashCmdError (..) + , HttpRequestError (..) ) where @@ -12,6 +14,8 @@ import Cardano.CLI.Read (ScriptDecodeError) import Cardano.Ledger.SafeHash (extractHash) import Cardano.Prelude (Exception (displayException), IOException) +import Network.HTTP.Client (HttpException) + data HashCmdError = HashMismatchedHashError !(L.SafeHash L.StandardCrypto L.AnchorData) @@ -19,6 +23,9 @@ data HashCmdError | HashReadFileError !FilePath !IOException | HashWriteFileError !(FileError ()) | HashReadScriptError !FilePath !(FileError ScriptDecodeError) + | HashInvalidURLError !String + | HashUnsupportedURLSchemeError !String + | HashGetFileFromHttpError !HttpRequestError deriving Show instance Error HashCmdError where @@ -35,3 +42,18 @@ instance Error HashCmdError where prettyError fileErr HashReadScriptError filepath err -> "Cannot read script at " <> pretty filepath <> ": " <> prettyError err + HashInvalidURLError text -> "Cannot parse URL: " <> pretty text + HashUnsupportedURLSchemeError text -> "Unsupported URL scheme: " <> pretty text + HashGetFileFromHttpError err -> pretty $ displayException err + +data HttpRequestError + = BadStatusCodeHRE !Int !String + | HttpExceptionHRE !HttpException + | IOExceptionHRE !IOException + deriving Show + +instance Exception HttpRequestError where + displayException :: HttpRequestError -> String + displayException (BadStatusCodeHRE code description) = "Bad status code when downloading anchor data: " <> show code <> " (" <> description <> ")" + displayException (HttpExceptionHRE exc) = "HTTP request error when downloading anchor data: " <> displayException exc + displayException (IOExceptionHRE exc) = "I/O error when downloading anchor data: " <> displayException exc diff --git a/cardano-cli/test/cardano-cli-golden/files/golden/help.cli b/cardano-cli/test/cardano-cli-golden/files/golden/help.cli index 5c6709537..62d980527 100644 --- a/cardano-cli/test/cardano-cli-golden/files/golden/help.cli +++ b/cardano-cli/test/cardano-cli-golden/files/golden/help.cli @@ -12536,6 +12536,7 @@ Usage: cardano-cli hash anchor-data ( --text TEXT | --file-binary FILEPATH | --file-text FILEPATH + | --url TEXT ) [--expected-hash HASH] [--out-file FILEPATH] diff --git a/cardano-cli/test/cardano-cli-golden/files/golden/help/hash_anchor-data.cli b/cardano-cli/test/cardano-cli-golden/files/golden/help/hash_anchor-data.cli index a7bbb5270..1bf970d12 100644 --- a/cardano-cli/test/cardano-cli-golden/files/golden/help/hash_anchor-data.cli +++ b/cardano-cli/test/cardano-cli-golden/files/golden/help/hash_anchor-data.cli @@ -2,6 +2,7 @@ Usage: cardano-cli hash anchor-data ( --text TEXT | --file-binary FILEPATH | --file-text FILEPATH + | --url TEXT ) [--expected-hash HASH] [--out-file FILEPATH] @@ -12,6 +13,7 @@ Available options: --text TEXT Text to hash as UTF-8 --file-binary FILEPATH Binary file to hash --file-text FILEPATH Text file to hash + --url TEXT A URL to the file to hash (HTTP(S) only) --expected-hash HASH Expected hash for the anchor data for verification purposes. If provided, the hash of the anchor data will be compared to this value. From 7463163982812f2b292b75ab75ce549da9d2c555 Mon Sep 17 00:00:00 2001 From: Pablo Lamela Date: Tue, 10 Sep 2024 23:12:03 +0200 Subject: [PATCH 03/18] Add IPFS schema support to `hash anchor-data` --- cardano-cli/src/Cardano/CLI/Options/Hash.hs | 2 +- cardano-cli/src/Cardano/CLI/Run/Hash.hs | 50 ++++++++++++++----- .../Cardano/CLI/Types/Errors/HashCmdError.hs | 8 ++- .../files/golden/help/hash_anchor-data.cli | 2 +- 4 files changed, 46 insertions(+), 16 deletions(-) diff --git a/cardano-cli/src/Cardano/CLI/Options/Hash.hs b/cardano-cli/src/Cardano/CLI/Options/Hash.hs index 2cbfb3cd1..551425286 100644 --- a/cardano-cli/src/Cardano/CLI/Options/Hash.hs +++ b/cardano-cli/src/Cardano/CLI/Options/Hash.hs @@ -54,7 +54,7 @@ pAnchorDataHashSource = , Cmd.AnchorDataHashSourceTextFile <$> pFileInDirection "file-text" "Text file to hash" , Cmd.AnchorDataHashSourceURL - <$> pUrl "url" "A URL to the file to hash (HTTP(S) only)" + <$> pUrl "url" "A URL to the file to hash (HTTP(S) and IPFS only)" ] pHashScriptCmd :: Parser Cmd.HashCmds diff --git a/cardano-cli/src/Cardano/CLI/Run/Hash.hs b/cardano-cli/src/Cardano/CLI/Run/Hash.hs index 34e1ee290..39d7c4c55 100644 --- a/cardano-cli/src/Cardano/CLI/Run/Hash.hs +++ b/cardano-cli/src/Cardano/CLI/Run/Hash.hs @@ -26,13 +26,15 @@ import qualified Data.ByteString.Char8 as BS8 import qualified Data.ByteString.Lazy as BSL import Data.Char (toLower) import Data.Function +import Data.List (intercalate) import qualified Data.Text as Text import qualified Data.Text.Encoding as Text import qualified Data.Text.IO as Text import Network.HTTP.Client (Response (..), httpLbs, newManager, requestFromURI) import Network.HTTP.Client.TLS (tlsManagerSettings) import Network.HTTP.Types (Status (statusCode), statusMessage) -import Network.URI (URI (..), parseAbsoluteURI, pathSegments) +import Network.URI (URI (..), URIAuth (..), parseAbsoluteURI, pathSegments) +import qualified System.Environment as IO import System.FilePath (()) import System.FilePath.Posix (isDrive) @@ -78,18 +80,19 @@ runHashAnchorDataCmd Cmd.HashAnchorDataCmdArgs{toHash, mExpectedHash, mOutFile} text = hashToTextAsHex . L.extractHash $ hash getByteStringFromURL :: L.Url -> ExceptT HashCmdError IO BS.ByteString - getByteStringFromURL urlText = + getByteStringFromURL urlText = do let urlString = Text.unpack $ L.urlToText urlText - in case parseAbsoluteURI urlString of - Just uri -> do - case map toLower $ uriScheme uri of - "file:" -> - let path = uriPathToFilePath (pathSegments uri) - in handleIOExceptT (HashReadFileError path) $ BS.readFile path - "http:" -> getFileFromHttp uri - "https:" -> getFileFromHttp uri - unsupportedScheme -> left $ HashUnsupportedURLSchemeError unsupportedScheme - Nothing -> left $ HashInvalidURLError urlString + uri <- expectJustExceptT (HashInvalidURLError urlString) $ parseAbsoluteURI urlString + case map toLower $ uriScheme uri of + "file:" -> + let path = uriPathToFilePath (pathSegments uri) + in handleIOExceptT (HashReadFileError path) $ BS.readFile path + "http:" -> getFileFromHttp uri + "https:" -> getFileFromHttp uri + "ipfs:" -> do + httpUri <- convertToHttp uri + getFileFromHttp httpUri + unsupportedScheme -> left $ HashUnsupportedURLSchemeError unsupportedScheme where uriPathToFilePath :: [String] -> FilePath uriPathToFilePath allPath@(letter : path) = @@ -118,6 +121,29 @@ runHashAnchorDataCmd Cmd.HashAnchorDataCmdArgs{toHash, mExpectedHash, mOutFile} mkHandler :: (Monad m, Exception e) => (e -> HttpRequestError) -> Handler m HashCmdError mkHandler x = Handler $ return . HashGetFileFromHttpError . x + convertToHttp :: URI -> ExceptT HashCmdError IO URI + convertToHttp ipfsUri = do + mIpfsGatewayUriString <- handleIOExceptT HashReadEnvVarError $ IO.lookupEnv "IPFS_GATEWAY_URI" + ipfsGatewayUriString <- expectJustExceptT HashIpfsGatewayNotSetError mIpfsGatewayUriString + ipfsGatewayUri <- + expectJustExceptT (HashInvalidURLError ipfsGatewayUriString) $ parseAbsoluteURI ipfsGatewayUriString + return $ + ipfsGatewayUri + { uriPath = + '/' + : intercalate + "/" + ( pathSegments ipfsGatewayUri + ++ ["ipfs"] + ++ maybe [] (\ipfsAuthority -> [uriRegName ipfsAuthority]) (uriAuthority ipfsUri) + ++ pathSegments ipfsUri + ) + } + + expectJustExceptT :: e -> Maybe a -> ExceptT e IO a + expectJustExceptT e Nothing = left e + expectJustExceptT _ (Just a) = return a + runHashScriptCmd :: () => Cmd.HashScriptCmdArgs diff --git a/cardano-cli/src/Cardano/CLI/Types/Errors/HashCmdError.hs b/cardano-cli/src/Cardano/CLI/Types/Errors/HashCmdError.hs index 7912c57eb..3e510d5a1 100644 --- a/cardano-cli/src/Cardano/CLI/Types/Errors/HashCmdError.hs +++ b/cardano-cli/src/Cardano/CLI/Types/Errors/HashCmdError.hs @@ -24,6 +24,8 @@ data HashCmdError | HashWriteFileError !(FileError ()) | HashReadScriptError !FilePath !(FileError ScriptDecodeError) | HashInvalidURLError !String + | HashReadEnvVarError !IOException + | HashIpfsGatewayNotSetError | HashUnsupportedURLSchemeError !String | HashGetFileFromHttpError !HttpRequestError deriving Show @@ -42,8 +44,10 @@ instance Error HashCmdError where prettyError fileErr HashReadScriptError filepath err -> "Cannot read script at " <> pretty filepath <> ": " <> prettyError err - HashInvalidURLError text -> "Cannot parse URL: " <> pretty text + HashInvalidURLError text -> "Cannot parse URI: " <> pretty text HashUnsupportedURLSchemeError text -> "Unsupported URL scheme: " <> pretty text + HashReadEnvVarError exc -> "Cannot read environment variable: " <> pretty (displayException exc) + HashIpfsGatewayNotSetError -> "IPFS schema requires IPFS_GATEWAY_URI environment variable to be set." HashGetFileFromHttpError err -> pretty $ displayException err data HttpRequestError @@ -55,5 +59,5 @@ data HttpRequestError instance Exception HttpRequestError where displayException :: HttpRequestError -> String displayException (BadStatusCodeHRE code description) = "Bad status code when downloading anchor data: " <> show code <> " (" <> description <> ")" - displayException (HttpExceptionHRE exc) = "HTTP request error when downloading anchor data: " <> displayException exc + displayException (HttpExceptionHRE exc) = "HTTP(S) request error when downloading anchor data: " <> displayException exc displayException (IOExceptionHRE exc) = "I/O error when downloading anchor data: " <> displayException exc diff --git a/cardano-cli/test/cardano-cli-golden/files/golden/help/hash_anchor-data.cli b/cardano-cli/test/cardano-cli-golden/files/golden/help/hash_anchor-data.cli index 1bf970d12..d3cf66364 100644 --- a/cardano-cli/test/cardano-cli-golden/files/golden/help/hash_anchor-data.cli +++ b/cardano-cli/test/cardano-cli-golden/files/golden/help/hash_anchor-data.cli @@ -13,7 +13,7 @@ Available options: --text TEXT Text to hash as UTF-8 --file-binary FILEPATH Binary file to hash --file-text FILEPATH Text file to hash - --url TEXT A URL to the file to hash (HTTP(S) only) + --url TEXT A URL to the file to hash (HTTP(S) and IPFS only) --expected-hash HASH Expected hash for the anchor data for verification purposes. If provided, the hash of the anchor data will be compared to this value. From ed9178e119f49bdbf4698df23fe3108be30f8800 Mon Sep 17 00:00:00 2001 From: Pablo Lamela Date: Thu, 12 Sep 2024 02:56:25 +0200 Subject: [PATCH 04/18] Add tests for local file hashing in `hash anchor-data` --- .gitattributes | 1 + cardano-cli/cardano-cli.cabal | 2 + .../test/cardano-cli-test/Test/Cli/Hash.hs | 95 +++++++++++++++++++ .../files/input/example_anchor_data.txt | 2 + 4 files changed, 100 insertions(+) create mode 100644 .gitattributes create mode 100644 cardano-cli/test/cardano-cli-test/Test/Cli/Hash.hs create mode 100644 cardano-cli/test/cardano-cli-test/files/input/example_anchor_data.txt diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..e6ade18bb --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +cardano-cli/test/cardano-cli-test/files/input/example_anchor_data.txt -text diff --git a/cardano-cli/cardano-cli.cabal b/cardano-cli/cardano-cli.cabal index 228c3e352..666bdabd2 100644 --- a/cardano-cli/cardano-cli.cabal +++ b/cardano-cli/cardano-cli.cabal @@ -326,6 +326,7 @@ test-suite cardano-cli-test cardano-ledger-alonzo, cardano-slotting, containers, + directory, exceptions, filepath, hedgehog, @@ -345,6 +346,7 @@ test-suite cardano-cli-test Test.Cli.FilePermissions Test.Cli.Governance.DRep Test.Cli.Governance.Hash + Test.Cli.Hash Test.Cli.ITN Test.Cli.Json Test.Cli.MonadWarning diff --git a/cardano-cli/test/cardano-cli-test/Test/Cli/Hash.hs b/cardano-cli/test/cardano-cli-test/Test/Cli/Hash.hs new file mode 100644 index 000000000..e09bac993 --- /dev/null +++ b/cardano-cli/test/cardano-cli-test/Test/Cli/Hash.hs @@ -0,0 +1,95 @@ +module Test.Cli.Hash where + +import Control.Monad (void) +import Data.List (intercalate) +import GHC.IO.Exception (ExitCode (ExitFailure)) +import System.Directory (getCurrentDirectory) +import System.FilePath (dropTrailingPathSeparator) +import System.FilePath.Posix (splitDirectories) + +import Test.Cardano.CLI.Util + +import Hedgehog as H +import qualified Hedgehog.Extras as H + +exampleAnchorDataHash :: String +exampleAnchorDataHash = "de38a4f5b8b9d8372386cc923bad19d1a0662298cf355bbe947e5eedf127fa9c" + +exampleAnchorDataPath :: String +exampleAnchorDataPath = "test/cardano-cli-test/files/input/example_anchor_data.txt" + +-- | Execute me with: +-- @cabal test cardano-cli-test --test-options '-p "/generate anchor data hash from file/"'@ +hprop_generate_anchor_data_hash_from_file :: Property +hprop_generate_anchor_data_hash_from_file = + propertyOnce $ do + result <- + execCardanoCLI + [ "hash" + , "anchor-data" + , "--file-binary" + , exampleAnchorDataPath + ] + result === exampleAnchorDataHash + +-- | Execute me with: +-- @cabal test cardano-cli-test --test-options '-p "/check anchor data hash from file/"'@ +hprop_check_anchor_data_hash_from_file :: Property +hprop_check_anchor_data_hash_from_file = + propertyOnce $ do + void $ + execCardanoCLI + [ "hash" + , "anchor-data" + , "--file-binary" + , exampleAnchorDataPath + , "--expected-hash" + , exampleAnchorDataHash + ] + +-- | Execute me with: +-- @cabal test cardano-cli-test --test-options '-p "/check anchor data hash from file fails/"'@ +hprop_check_anchor_data_hash_from_file_fails :: Property +hprop_check_anchor_data_hash_from_file_fails = + propertyOnce $ do + (ec, _, _) <- + execDetailCardanoCLI + [ "hash" + , "anchor-data" + , "--file-binary" + , exampleAnchorDataPath + , "--expected-hash" + , 'c' : drop 1 exampleAnchorDataHash + ] + ec === ExitFailure 1 + +-- | Execute me with: +-- @cabal test cardano-cli-test --test-options '-p "/generate anchor data hash from file uri/"'@ +hprop_generate_anchor_data_hash_from_file_uri :: Property +hprop_generate_anchor_data_hash_from_file_uri = + propertyOnce $ do + cwd <- H.evalIO getCurrentDirectory + posixCwd <- toPOSIX cwd + result <- + execCardanoCLI + [ "hash" + , "anchor-data" + , "--url" + , "file://" ++ posixCwd ++ "/" ++ exampleAnchorDataPath + ] + result === exampleAnchorDataHash + where + toPOSIX :: FilePath -> PropertyT IO [Char] + toPOSIX path = + case map dropTrailingPathSeparator (splitDirectories path) of + letter : restOfPath -> do + fixedLetter <- case letter of + '/' : _ -> return "" + l : ':' : _ -> return $ l : ":/" + wrongLetter -> do + H.note_ ("Unexpected letter: " ++ wrongLetter) + H.failure + return $ fixedLetter ++ intercalate "/" (fixedLetter : restOfPath) + [] -> do + H.note_ ("Path doesn't split:" ++ path) + H.failure diff --git a/cardano-cli/test/cardano-cli-test/files/input/example_anchor_data.txt b/cardano-cli/test/cardano-cli-test/files/input/example_anchor_data.txt new file mode 100644 index 000000000..5d83630ef --- /dev/null +++ b/cardano-cli/test/cardano-cli-test/files/input/example_anchor_data.txt @@ -0,0 +1,2 @@ +This is just a random file with content that is used for +testing the hashing of anchor data files. \ No newline at end of file From ba8601295160622a027b2bd53da57b0a17476278 Mon Sep 17 00:00:00 2001 From: Pablo Lamela Date: Thu, 12 Sep 2024 04:25:29 +0200 Subject: [PATCH 05/18] Add test for `hash anchor-data` with HTTP url --- cardano-cli/cardano-cli.cabal | 6 ++ .../test/cardano-cli-test/Test/Cli/Hash.hs | 74 +++++++++++++++++++ 2 files changed, 80 insertions(+) diff --git a/cardano-cli/cardano-cli.cabal b/cardano-cli/cardano-cli.cabal index 666bdabd2..38d044002 100644 --- a/cardano-cli/cardano-cli.cabal +++ b/cardano-cli/cardano-cli.cabal @@ -331,6 +331,10 @@ test-suite cardano-cli-test filepath, hedgehog, hedgehog-extras ^>=0.6.1.0, + http-types, + lifted-base, + monad-control, + network, parsec, regex-tdfa, tasty, @@ -338,6 +342,8 @@ test-suite cardano-cli-test text, time, transformers, + wai, + warp, build-tool-depends: tasty-discover:tasty-discover other-modules: diff --git a/cardano-cli/test/cardano-cli-test/Test/Cli/Hash.hs b/cardano-cli/test/cardano-cli-test/Test/Cli/Hash.hs index e09bac993..b769f2550 100644 --- a/cardano-cli/test/cardano-cli-test/Test/Cli/Hash.hs +++ b/cardano-cli/test/cardano-cli-test/Test/Cli/Hash.hs @@ -1,8 +1,21 @@ +{-# LANGUAGE FlexibleContexts #-} + module Test.Cli.Hash where +import Cardano.Api (MonadIO) + +import Control.Concurrent (forkOS) +import Control.Exception.Lifted (bracket) import Control.Monad (void) +import Control.Monad.Trans.Control (MonadBaseControl) import Data.List (intercalate) +import qualified Data.Text as T import GHC.IO.Exception (ExitCode (ExitFailure)) +import Network.HTTP.Types.Status (status200, status404) +import Network.Socket (close) +import Network.Wai (Request, Response, ResponseReceived, pathInfo, responseFile, + responseLBS) +import Network.Wai.Handler.Warp (defaultSettings, openFreePort, runSettingsSocket) import System.Directory (getCurrentDirectory) import System.FilePath (dropTrailingPathSeparator) import System.FilePath.Posix (splitDirectories) @@ -11,6 +24,7 @@ import Test.Cardano.CLI.Util import Hedgehog as H import qualified Hedgehog.Extras as H +import Hedgehog.Internal.Source (HasCallStack) exampleAnchorDataHash :: String exampleAnchorDataHash = "de38a4f5b8b9d8372386cc923bad19d1a0662298cf355bbe947e5eedf127fa9c" @@ -93,3 +107,63 @@ hprop_generate_anchor_data_hash_from_file_uri = [] -> do H.note_ ("Path doesn't split:" ++ path) H.failure + +-- | Execute me with: +-- @cabal test cardano-cli-test --test-options '-p "/check anchor data hash from http uri/"'@ +hprop_check_anchor_data_hash_from_http_uri :: Property +hprop_check_anchor_data_hash_from_http_uri = + propertyOnce $ do + let relativeUrl = ["example", "url", "file.txt"] + serveFileWhile + relativeUrl + exampleAnchorDataPath + ( \port -> + void $ + execCardanoCLI + [ "hash" + , "anchor-data" + , "--url" + , "http://localhost:" ++ show port ++ "/" ++ intercalate "/" relativeUrl + , "--expected-hash" + , exampleAnchorDataHash + ] + ) + +-- | Takes a relative url (as a list of segments), a file path, and an action, and it serves +-- the file in the url provided in a random free port that is passed as a parameter to the +-- action. After the action returns, it shuts down the server. It returns the result of the +-- action. It also ensures the server is shut down even if the action throws an exception. +serveFileWhile + :: (MonadBaseControl IO m, MonadTest m, MonadIO m, HasCallStack) + => [String] + -- ^ Relative URL where the file will be served. + -- Each element is a segment of the URL. + -> FilePath + -- ^ File path for the file to serve + -> (Int -> m a) + -- ^ Action to run while the file is being served. + -- It receives the port the server is listening on + -> m a +serveFileWhile relativeUrl filePath action = + bracket + -- Server setup (resource acquisition) + ( do + -- Get the port the server is listening on + (port, socket) <- H.evalIO openFreePort + -- Serve the file + let app :: Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived + app req respond = do + let path = T.unpack <$> pathInfo req + if path == relativeUrl + -- Status -> ResponseHeaders -> FilePath -> Maybe FilePart -> Response + then respond $ responseFile status200 [("Content-Type", "text/plain")] filePath Nothing + else respond $ responseLBS status404 [("Content-Type", "text/plain")] "404 - Not Found" + + -- Run server asynchronously in a separate thread + void $ H.evalIO $ forkOS $ runSettingsSocket defaultSettings socket app + return (port, socket) + ) + -- Server teardown (resource release) + (\(_, socket) -> H.evalIO $ close socket) + -- Test action + (\(port, _) -> action port) From b2e63de42e651b7f830cb0c4f32b3af1b91a2705 Mon Sep 17 00:00:00 2001 From: Pablo Lamela Date: Thu, 12 Sep 2024 22:08:25 +0200 Subject: [PATCH 06/18] Add test for `hash anchor-data` with IPFS url --- .../Test/Cardano/CLI/Util.hs | 16 ++++++- .../test/cardano-cli-test/Test/Cli/Hash.hs | 44 ++++++++++++++++++- 2 files changed, 58 insertions(+), 2 deletions(-) diff --git a/cardano-cli/test/cardano-cli-test-lib/Test/Cardano/CLI/Util.hs b/cardano-cli/test/cardano-cli-test-lib/Test/Cardano/CLI/Util.hs index d73125d96..d1d4182f5 100644 --- a/cardano-cli/test/cardano-cli-test-lib/Test/Cardano/CLI/Util.hs +++ b/cardano-cli/test/cardano-cli-test-lib/Test/Cardano/CLI/Util.hs @@ -11,6 +11,7 @@ module Test.Cardano.CLI.Util , equivalence , execCardanoCLI , execDetailCardanoCLI + , execDetailCfgCardanoCLI , tryExecCardanoCLI , propertyOnce , withSnd @@ -82,7 +83,20 @@ execDetailCardanoCLI -- ^ Arguments to the CLI command -> m (IO.ExitCode, String, String) -- ^ Captured stdout -execDetailCardanoCLI = GHC.withFrozenCallStack $ execDetailFlex H.defaultExecConfig "cardano-cli" "CARDANO_CLI" +execDetailCardanoCLI params = GHC.withFrozenCallStack $ execDetailCfgCardanoCLI H.defaultExecConfig params + +-- | Execute cardano-cli via the command line, expecting it to fail, and accepting custom config. +-- +-- Waits for the process to finish and returns the exit code, stdout and stderr. +execDetailCfgCardanoCLI + :: (MonadTest m, MonadCatch m, MonadIO m, HasCallStack) + => ExecConfig + -- ^ Configuration for the execution + -> [String] + -- ^ Arguments to the CLI command + -> m (IO.ExitCode, String, String) + -- ^ Captured stdout +execDetailCfgCardanoCLI cfg = GHC.withFrozenCallStack $ execDetailFlex cfg "cardano-cli" "CARDANO_CLI" procFlex' :: (MonadTest m, MonadCatch m, MonadIO m, HasCallStack) diff --git a/cardano-cli/test/cardano-cli-test/Test/Cli/Hash.hs b/cardano-cli/test/cardano-cli-test/Test/Cli/Hash.hs index b769f2550..7f80feb3c 100644 --- a/cardano-cli/test/cardano-cli-test/Test/Cli/Hash.hs +++ b/cardano-cli/test/cardano-cli-test/Test/Cli/Hash.hs @@ -9,14 +9,16 @@ import Control.Exception.Lifted (bracket) import Control.Monad (void) import Control.Monad.Trans.Control (MonadBaseControl) import Data.List (intercalate) +import Data.Monoid (Last (..)) import qualified Data.Text as T -import GHC.IO.Exception (ExitCode (ExitFailure)) +import GHC.IO.Exception (ExitCode (..)) import Network.HTTP.Types.Status (status200, status404) import Network.Socket (close) import Network.Wai (Request, Response, ResponseReceived, pathInfo, responseFile, responseLBS) import Network.Wai.Handler.Warp (defaultSettings, openFreePort, runSettingsSocket) import System.Directory (getCurrentDirectory) +import System.Environment (getEnvironment) import System.FilePath (dropTrailingPathSeparator) import System.FilePath.Posix (splitDirectories) @@ -32,6 +34,9 @@ exampleAnchorDataHash = "de38a4f5b8b9d8372386cc923bad19d1a0662298cf355bbe947e5ee exampleAnchorDataPath :: String exampleAnchorDataPath = "test/cardano-cli-test/files/input/example_anchor_data.txt" +exampleAchorDataIpfsHash :: String +exampleAchorDataIpfsHash = "QmbL5EBFJLf8DdPkWAskG3Euin9tHY8naqQ2JDoHnWHHXJ" + -- | Execute me with: -- @cabal test cardano-cli-test --test-options '-p "/generate anchor data hash from file/"'@ hprop_generate_anchor_data_hash_from_file :: Property @@ -129,6 +134,43 @@ hprop_check_anchor_data_hash_from_http_uri = ] ) +-- | Execute me with: +-- @cabal test cardano-cli-test --test-options '-p "/check anchor data hash from ipfs uri/"'@ +hprop_check_anchor_data_hash_from_ipfs_uri :: Property +hprop_check_anchor_data_hash_from_ipfs_uri = + propertyOnce $ do + let relativeUrl = ["ipfs", exampleAchorDataIpfsHash] + serveFileWhile + relativeUrl + exampleAnchorDataPath + ( \port -> do + env <- H.evalIO getEnvironment + result <- + execDetailCfgCardanoCLI + H.defaultExecConfig + { H.execConfigEnv = + Last $ + Just + ( ( "IPFS_GATEWAY_URI" + , "http://localhost:" ++ show port ++ "/" + ) + : env + ) + } + [ "hash" + , "anchor-data" + , "--url" + , "ipfs://" ++ exampleAchorDataIpfsHash + , "--expected-hash" + , exampleAnchorDataHash + ] + case result of + (ExitFailure _, _, stderr) -> do + H.note_ stderr + failure + (ExitSuccess, _, _) -> success + ) + -- | Takes a relative url (as a list of segments), a file path, and an action, and it serves -- the file in the url provided in a random free port that is passed as a parameter to the -- action. After the action returns, it shuts down the server. It returns the result of the From b1542aa1a72cc7f204dbcfab09d837ea3b59f4f4 Mon Sep 17 00:00:00 2001 From: Pablo Lamela Date: Fri, 13 Sep 2024 03:27:02 +0200 Subject: [PATCH 07/18] Patch for CI --- flake.nix | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/flake.nix b/flake.nix index aad8fbc0b..14c51c02c 100644 --- a/flake.nix +++ b/flake.nix @@ -67,7 +67,11 @@ inherit (inputs.haskellNix) config; }; inherit (nixpkgs) lib; - + macOS-security = + # make `/usr/bin/security` available in `PATH`, which is needed for stack + # on darwin which calls this binary to find certificates + nixpkgs.writeScriptBin "security" ''exec /usr/bin/security "$@"''; + isDarwin = (system == "x86_64-darwin") || (system == "aarch64-darwin"); gitRevFlag = if inputs.self ? rev then [("--ghc-option=-D__GIT_REV__=\\\"" + inputs.self.rev + "\\\"")] @@ -114,7 +118,7 @@ stylish-haskell = "0.14.6.0"; }; # and from nixpkgs or other inputs - shell.nativeBuildInputs = with nixpkgs; [gh jq yq-go actionlint shellcheck cabal-head]; + shell.nativeBuildInputs = with nixpkgs; [gh jq yq-go actionlint shellcheck cabal-head] ++ (lib.optional isDarwin macOS-security); # disable Hoogle until someone request it shell.withHoogle = false; # Skip cross compilers for the shell @@ -161,7 +165,11 @@ in '' ${exportCliPath} cp -r ${filteredProjectBase}/* .. - ''; + '' + (if isDarwin + then '' + export PATH=${macOS-security}/bin:$PATH + '' + else ''''); }) { packages.crypton-x509-system.postPatch = '' From 432dedf3c2a6dbf00dde43721f210de1448c5392 Mon Sep 17 00:00:00 2001 From: Pablo Lamela Date: Mon, 16 Sep 2024 22:13:31 +0200 Subject: [PATCH 08/18] Update flake.lock --- flake.lock | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/flake.lock b/flake.lock index e6a76a06b..3bf375dbd 100644 --- a/flake.lock +++ b/flake.lock @@ -173,11 +173,11 @@ "hackage": { "flake": false, "locked": { - "lastModified": 1722472127, - "narHash": "sha256-nYv7VbYAgo45CtXBX5Kkskyqi6Oh4coLgVmuYa/Hmqw=", + "lastModified": 1726446873, + "narHash": "sha256-dWdiphXwkk4qQVFkQHuUysphOb0XO8EHJlk/8Km/7q0=", "owner": "input-output-hk", "repo": "hackage.nix", - "rev": "adb966c62bed389945cb5d964d9bf5a02bf64a2e", + "rev": "3126b966be7409ebfd61c88f85dbfb6ec2a51338", "type": "github" }, "original": { @@ -226,11 +226,11 @@ "stackage": "stackage" }, "locked": { - "lastModified": 1722473442, - "narHash": "sha256-Yc0ntATTg9mqTj5MVONFvyntEWXSStZYlBSEyAFMS9I=", + "lastModified": 1726447863, + "narHash": "sha256-bI1GMzozXWQ/Ckukr8bXnH3QzWZ+vZC0o5RkblCXIyI=", "owner": "input-output-hk", "repo": "haskell.nix", - "rev": "d501b2b44438a7cd329396240fc21fa4b42c6d42", + "rev": "d259dac293df908b6749653122cca88b5e459c30", "type": "github" }, "original": { @@ -815,11 +815,11 @@ "stackage": { "flake": false, "locked": { - "lastModified": 1722384610, - "narHash": "sha256-cIJRuPF7y/wf9C6jMMXcC77RbjLJMlrjCZyyV9ln7kY=", + "lastModified": 1726445918, + "narHash": "sha256-M34goAxhRqzDaVXqUo8lLnjZpppJYpr26c+X1Lhj5hU=", "owner": "input-output-hk", "repo": "stackage.nix", - "rev": "21c6eb2e51ba4ae8d3cdb8d2bfd7f2aa8ca2724d", + "rev": "8299f8d17eef21ec8365536ee9705ff66a3504f3", "type": "github" }, "original": { From 4f7cdac9c1ac2962dc5ac92f50a3e52e5c03219d Mon Sep 17 00:00:00 2001 From: Pablo Lamela Date: Tue, 17 Sep 2024 04:17:06 +0200 Subject: [PATCH 09/18] Output hash to file even in case of `expected-hash` flag --- cardano-cli/src/Cardano/CLI/Run/Hash.hs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cardano-cli/src/Cardano/CLI/Run/Hash.hs b/cardano-cli/src/Cardano/CLI/Run/Hash.hs index 39d7c4c55..fe2b2f763 100644 --- a/cardano-cli/src/Cardano/CLI/Run/Hash.hs +++ b/cardano-cli/src/Cardano/CLI/Run/Hash.hs @@ -20,6 +20,7 @@ import Cardano.CLI.Types.Errors.HashCmdError import Cardano.Crypto.Hash (hashToTextAsHex) import Control.Exception (throw) +import Control.Monad (when) import Control.Monad.Catch (Exception, Handler (Handler)) import qualified Data.ByteString as BS import qualified Data.ByteString.Char8 as BS8 @@ -27,6 +28,7 @@ import qualified Data.ByteString.Lazy as BSL import Data.Char (toLower) import Data.Function import Data.List (intercalate) +import Data.Maybe (isJust) import qualified Data.Text as Text import qualified Data.Text.Encoding as Text import qualified Data.Text.IO as Text @@ -68,7 +70,9 @@ runHashAnchorDataCmd Cmd.HashAnchorDataCmdArgs{toHash, mExpectedHash, mOutFile} Just expectedHash | hash /= expectedHash -> left $ HashMismatchedHashError expectedHash hash - | otherwise -> liftIO $ putStrLn "Hashes match!" + | otherwise -> do + liftIO $ putStrLn "Hashes match!" + when (isJust mOutFile) $ writeHash hash Nothing -> writeHash hash where writeHash :: L.SafeHash L.StandardCrypto i -> ExceptT HashCmdError IO () From cabb16af758f58e03844918855725f2e88c26b6b Mon Sep 17 00:00:00 2001 From: Pablo Lamela Date: Thu, 19 Sep 2024 03:39:20 +0200 Subject: [PATCH 10/18] Make `--out-file` and `--expect-hash` mutually exclusive --- cardano-cli/src/Cardano/CLI/Commands/Hash.hs | 14 +++++++++++--- cardano-cli/src/Cardano/CLI/Options/Hash.hs | 11 +++++++++-- cardano-cli/src/Cardano/CLI/Run/Hash.hs | 17 ++++++++--------- .../cardano-cli-golden/files/golden/help.cli | 5 +++-- .../files/golden/help/hash_anchor-data.cli | 5 +++-- 5 files changed, 34 insertions(+), 18 deletions(-) diff --git a/cardano-cli/src/Cardano/CLI/Commands/Hash.hs b/cardano-cli/src/Cardano/CLI/Commands/Hash.hs index 5b4afbf2d..0672f8ee5 100644 --- a/cardano-cli/src/Cardano/CLI/Commands/Hash.hs +++ b/cardano-cli/src/Cardano/CLI/Commands/Hash.hs @@ -4,6 +4,7 @@ module Cardano.CLI.Commands.Hash ( HashCmds (..) + , HashGoal (..) , HashAnchorDataCmdArgs (..) , HashScriptCmdArgs (..) , AnchorDataHashSource (..) @@ -22,12 +23,19 @@ data HashCmds = HashAnchorDataCmd !HashAnchorDataCmdArgs | HashScriptCmd !HashScriptCmdArgs +data HashGoal + = -- | The hash is written to stdout + HashToStdout + | -- | The hash to check against + CheckHash !(L.SafeHash L.StandardCrypto L.AnchorData) + | -- | The output file to which the hash is written + HashToFile !(File () Out) + deriving Show + data HashAnchorDataCmdArgs = HashAnchorDataCmdArgs { toHash :: !AnchorDataHashSource - , mExpectedHash :: !(Maybe (L.SafeHash L.StandardCrypto L.AnchorData)) - , mOutFile :: !(Maybe (File () Out)) - -- ^ The output file to which the hash is written + , hashGoal :: !HashGoal } deriving Show diff --git a/cardano-cli/src/Cardano/CLI/Options/Hash.hs b/cardano-cli/src/Cardano/CLI/Options/Hash.hs index 551425286..7af0003f4 100644 --- a/cardano-cli/src/Cardano/CLI/Options/Hash.hs +++ b/cardano-cli/src/Cardano/CLI/Options/Hash.hs @@ -32,12 +32,19 @@ pHashAnchorDataCmd = do Cmd.HashAnchorDataCmd ( Cmd.HashAnchorDataCmdArgs <$> pAnchorDataHashSource - <*> optional pExpectedHash - <*> optional pOutputFile + <*> pHashGoal ) ) $ Opt.progDesc "Compute the hash of some anchor data (to then pass it to other commands)." +pHashGoal :: Parser Cmd.HashGoal +pHashGoal = + asum + [ Cmd.CheckHash <$> pExpectedHash + , Cmd.HashToFile <$> pOutputFile + ] + <|> pure Cmd.HashToStdout + pAnchorDataHashSource :: Parser Cmd.AnchorDataHashSource pAnchorDataHashSource = asum diff --git a/cardano-cli/src/Cardano/CLI/Run/Hash.hs b/cardano-cli/src/Cardano/CLI/Run/Hash.hs index fe2b2f763..5144d065e 100644 --- a/cardano-cli/src/Cardano/CLI/Run/Hash.hs +++ b/cardano-cli/src/Cardano/CLI/Run/Hash.hs @@ -14,13 +14,13 @@ where import Cardano.Api import qualified Cardano.Api.Ledger as L +import Cardano.CLI.Commands.Hash (HashGoal (..)) import qualified Cardano.CLI.Commands.Hash as Cmd import Cardano.CLI.Read import Cardano.CLI.Types.Errors.HashCmdError import Cardano.Crypto.Hash (hashToTextAsHex) import Control.Exception (throw) -import Control.Monad (when) import Control.Monad.Catch (Exception, Handler (Handler)) import qualified Data.ByteString as BS import qualified Data.ByteString.Char8 as BS8 @@ -28,7 +28,6 @@ import qualified Data.ByteString.Lazy as BSL import Data.Char (toLower) import Data.Function import Data.List (intercalate) -import Data.Maybe (isJust) import qualified Data.Text as Text import qualified Data.Text.Encoding as Text import qualified Data.Text.IO as Text @@ -52,7 +51,7 @@ runHashAnchorDataCmd :: () => Cmd.HashAnchorDataCmdArgs -> ExceptT HashCmdError IO () -runHashAnchorDataCmd Cmd.HashAnchorDataCmdArgs{toHash, mExpectedHash, mOutFile} = do +runHashAnchorDataCmd Cmd.HashAnchorDataCmdArgs{toHash, hashGoal} = do anchorData <- L.AnchorData <$> case toHash of Cmd.AnchorDataHashSourceBinaryFile fp -> do @@ -66,17 +65,17 @@ runHashAnchorDataCmd Cmd.HashAnchorDataCmdArgs{toHash, mExpectedHash, mOutFile} Cmd.AnchorDataHashSourceURL urlText -> getByteStringFromURL urlText let hash = L.hashAnchorData anchorData - case mExpectedHash of - Just expectedHash + case hashGoal of + CheckHash expectedHash | hash /= expectedHash -> left $ HashMismatchedHashError expectedHash hash | otherwise -> do liftIO $ putStrLn "Hashes match!" - when (isJust mOutFile) $ writeHash hash - Nothing -> writeHash hash + HashToFile outFile -> writeHash (Just outFile) hash + HashToStdout -> writeHash Nothing hash where - writeHash :: L.SafeHash L.StandardCrypto i -> ExceptT HashCmdError IO () - writeHash hash = do + writeHash :: Maybe (File () Out) -> L.SafeHash L.StandardCrypto i -> ExceptT HashCmdError IO () + writeHash mOutFile hash = do firstExceptT HashWriteFileError $ newExceptT $ writeTextOutput mOutFile text diff --git a/cardano-cli/test/cardano-cli-golden/files/golden/help.cli b/cardano-cli/test/cardano-cli-golden/files/golden/help.cli index 62d980527..e3a2f0eb8 100644 --- a/cardano-cli/test/cardano-cli-golden/files/golden/help.cli +++ b/cardano-cli/test/cardano-cli-golden/files/golden/help.cli @@ -12538,8 +12538,9 @@ Usage: cardano-cli hash anchor-data | --file-text FILEPATH | --url TEXT ) - [--expected-hash HASH] - [--out-file FILEPATH] + [ --expected-hash HASH + | --out-file FILEPATH + ] Compute the hash of some anchor data (to then pass it to other commands). diff --git a/cardano-cli/test/cardano-cli-golden/files/golden/help/hash_anchor-data.cli b/cardano-cli/test/cardano-cli-golden/files/golden/help/hash_anchor-data.cli index d3cf66364..4fb88d13b 100644 --- a/cardano-cli/test/cardano-cli-golden/files/golden/help/hash_anchor-data.cli +++ b/cardano-cli/test/cardano-cli-golden/files/golden/help/hash_anchor-data.cli @@ -4,8 +4,9 @@ Usage: cardano-cli hash anchor-data | --file-text FILEPATH | --url TEXT ) - [--expected-hash HASH] - [--out-file FILEPATH] + [ --expected-hash HASH + | --out-file FILEPATH + ] Compute the hash of some anchor data (to then pass it to other commands). From 82d5e121b0d344f8810073ce4a1e097a1b48fc9a Mon Sep 17 00:00:00 2001 From: Pablo Lamela Date: Thu, 19 Sep 2024 03:46:13 +0200 Subject: [PATCH 11/18] Add comment to specify which hash is expeced and which actual --- cardano-cli/src/Cardano/CLI/Types/Errors/HashCmdError.hs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cardano-cli/src/Cardano/CLI/Types/Errors/HashCmdError.hs b/cardano-cli/src/Cardano/CLI/Types/Errors/HashCmdError.hs index 3e510d5a1..5c8420b09 100644 --- a/cardano-cli/src/Cardano/CLI/Types/Errors/HashCmdError.hs +++ b/cardano-cli/src/Cardano/CLI/Types/Errors/HashCmdError.hs @@ -19,7 +19,9 @@ import Network.HTTP.Client (HttpException) data HashCmdError = HashMismatchedHashError !(L.SafeHash L.StandardCrypto L.AnchorData) + -- ^ Expected hash !(L.SafeHash L.StandardCrypto L.AnchorData) + -- ^ Actual hash | HashReadFileError !FilePath !IOException | HashWriteFileError !(FileError ()) | HashReadScriptError !FilePath !(FileError ScriptDecodeError) From 77430a66d9bb2349c310cb0033482c634ae0195e Mon Sep 17 00:00:00 2001 From: Pablo Lamela Date: Thu, 19 Sep 2024 03:48:15 +0200 Subject: [PATCH 12/18] Move unnecessarily nested `when` up one level --- cardano-cli/src/Cardano/CLI/Run/Hash.hs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/cardano-cli/src/Cardano/CLI/Run/Hash.hs b/cardano-cli/src/Cardano/CLI/Run/Hash.hs index 5144d065e..07606c667 100644 --- a/cardano-cli/src/Cardano/CLI/Run/Hash.hs +++ b/cardano-cli/src/Cardano/CLI/Run/Hash.hs @@ -113,16 +113,16 @@ runHashAnchorDataCmd Cmd.HashAnchorDataCmdArgs{toHash, hashGoal} = do if statusCode status /= 200 then throw $ BadStatusCodeHRE (statusCode status) (BS8.unpack $ statusMessage status) else return $ BS.concat . BSL.toChunks $ responseBody response + + handlers :: [Handler IO HashCmdError] + handlers = + [ mkHandler id + , mkHandler HttpExceptionHRE + , mkHandler IOExceptionHRE + ] where - handlers :: [Handler IO HashCmdError] - handlers = - [ mkHandler id - , mkHandler HttpExceptionHRE - , mkHandler IOExceptionHRE - ] - where - mkHandler :: (Monad m, Exception e) => (e -> HttpRequestError) -> Handler m HashCmdError - mkHandler x = Handler $ return . HashGetFileFromHttpError . x + mkHandler :: (Monad m, Exception e) => (e -> HttpRequestError) -> Handler m HashCmdError + mkHandler x = Handler $ return . HashGetFileFromHttpError . x convertToHttp :: URI -> ExceptT HashCmdError IO URI convertToHttp ipfsUri = do From fe49f9b37e43a0bfa070802421b37b891f0bea73 Mon Sep 17 00:00:00 2001 From: Pablo Lamela Date: Thu, 19 Sep 2024 03:51:03 +0200 Subject: [PATCH 13/18] Used `hoistMaybe` insetad of custom function --- cardano-cli/src/Cardano/CLI/Run/Hash.hs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/cardano-cli/src/Cardano/CLI/Run/Hash.hs b/cardano-cli/src/Cardano/CLI/Run/Hash.hs index 07606c667..a0799d993 100644 --- a/cardano-cli/src/Cardano/CLI/Run/Hash.hs +++ b/cardano-cli/src/Cardano/CLI/Run/Hash.hs @@ -85,7 +85,7 @@ runHashAnchorDataCmd Cmd.HashAnchorDataCmdArgs{toHash, hashGoal} = do getByteStringFromURL :: L.Url -> ExceptT HashCmdError IO BS.ByteString getByteStringFromURL urlText = do let urlString = Text.unpack $ L.urlToText urlText - uri <- expectJustExceptT (HashInvalidURLError urlString) $ parseAbsoluteURI urlString + uri <- hoistMaybe (HashInvalidURLError urlString) $ parseAbsoluteURI urlString case map toLower $ uriScheme uri of "file:" -> let path = uriPathToFilePath (pathSegments uri) @@ -127,9 +127,9 @@ runHashAnchorDataCmd Cmd.HashAnchorDataCmdArgs{toHash, hashGoal} = do convertToHttp :: URI -> ExceptT HashCmdError IO URI convertToHttp ipfsUri = do mIpfsGatewayUriString <- handleIOExceptT HashReadEnvVarError $ IO.lookupEnv "IPFS_GATEWAY_URI" - ipfsGatewayUriString <- expectJustExceptT HashIpfsGatewayNotSetError mIpfsGatewayUriString + ipfsGatewayUriString <- hoistMaybe HashIpfsGatewayNotSetError mIpfsGatewayUriString ipfsGatewayUri <- - expectJustExceptT (HashInvalidURLError ipfsGatewayUriString) $ parseAbsoluteURI ipfsGatewayUriString + hoistMaybe (HashInvalidURLError ipfsGatewayUriString) $ parseAbsoluteURI ipfsGatewayUriString return $ ipfsGatewayUri { uriPath = @@ -143,10 +143,6 @@ runHashAnchorDataCmd Cmd.HashAnchorDataCmdArgs{toHash, hashGoal} = do ) } - expectJustExceptT :: e -> Maybe a -> ExceptT e IO a - expectJustExceptT e Nothing = left e - expectJustExceptT _ (Just a) = return a - runHashScriptCmd :: () => Cmd.HashScriptCmdArgs From b07b0e5f4c68c201718252506a3b89c33a0fdb79 Mon Sep 17 00:00:00 2001 From: Pablo Lamela Date: Thu, 19 Sep 2024 03:53:30 +0200 Subject: [PATCH 14/18] Move `convertToHttp` to top level --- cardano-cli/src/Cardano/CLI/Run/Hash.hs | 36 ++++++++++++------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/cardano-cli/src/Cardano/CLI/Run/Hash.hs b/cardano-cli/src/Cardano/CLI/Run/Hash.hs index a0799d993..a8b2ee674 100644 --- a/cardano-cli/src/Cardano/CLI/Run/Hash.hs +++ b/cardano-cli/src/Cardano/CLI/Run/Hash.hs @@ -124,24 +124,24 @@ runHashAnchorDataCmd Cmd.HashAnchorDataCmdArgs{toHash, hashGoal} = do mkHandler :: (Monad m, Exception e) => (e -> HttpRequestError) -> Handler m HashCmdError mkHandler x = Handler $ return . HashGetFileFromHttpError . x - convertToHttp :: URI -> ExceptT HashCmdError IO URI - convertToHttp ipfsUri = do - mIpfsGatewayUriString <- handleIOExceptT HashReadEnvVarError $ IO.lookupEnv "IPFS_GATEWAY_URI" - ipfsGatewayUriString <- hoistMaybe HashIpfsGatewayNotSetError mIpfsGatewayUriString - ipfsGatewayUri <- - hoistMaybe (HashInvalidURLError ipfsGatewayUriString) $ parseAbsoluteURI ipfsGatewayUriString - return $ - ipfsGatewayUri - { uriPath = - '/' - : intercalate - "/" - ( pathSegments ipfsGatewayUri - ++ ["ipfs"] - ++ maybe [] (\ipfsAuthority -> [uriRegName ipfsAuthority]) (uriAuthority ipfsUri) - ++ pathSegments ipfsUri - ) - } +convertToHttp :: URI -> ExceptT HashCmdError IO URI +convertToHttp ipfsUri = do + mIpfsGatewayUriString <- handleIOExceptT HashReadEnvVarError $ IO.lookupEnv "IPFS_GATEWAY_URI" + ipfsGatewayUriString <- hoistMaybe HashIpfsGatewayNotSetError mIpfsGatewayUriString + ipfsGatewayUri <- + hoistMaybe (HashInvalidURLError ipfsGatewayUriString) $ parseAbsoluteURI ipfsGatewayUriString + return $ + ipfsGatewayUri + { uriPath = + '/' + : intercalate + "/" + ( pathSegments ipfsGatewayUri + ++ ["ipfs"] + ++ maybe [] (\ipfsAuthority -> [uriRegName ipfsAuthority]) (uriAuthority ipfsUri) + ++ pathSegments ipfsUri + ) + } runHashScriptCmd :: () From 86b89e70f4e9abdeb2dcc5602d9b4443a56b1f0f Mon Sep 17 00:00:00 2001 From: Pablo Lamela Date: Thu, 19 Sep 2024 03:56:00 +0200 Subject: [PATCH 15/18] Remove spurious comment --- cardano-cli/test/cardano-cli-test/Test/Cli/Hash.hs | 1 - 1 file changed, 1 deletion(-) diff --git a/cardano-cli/test/cardano-cli-test/Test/Cli/Hash.hs b/cardano-cli/test/cardano-cli-test/Test/Cli/Hash.hs index 7f80feb3c..2618d6ec5 100644 --- a/cardano-cli/test/cardano-cli-test/Test/Cli/Hash.hs +++ b/cardano-cli/test/cardano-cli-test/Test/Cli/Hash.hs @@ -197,7 +197,6 @@ serveFileWhile relativeUrl filePath action = app req respond = do let path = T.unpack <$> pathInfo req if path == relativeUrl - -- Status -> ResponseHeaders -> FilePath -> Maybe FilePart -> Response then respond $ responseFile status200 [("Content-Type", "text/plain")] filePath Nothing else respond $ responseLBS status404 [("Content-Type", "text/plain")] "404 - Not Found" From 198dcbce37e5a3db3e66311d025a9d6b452ebf74 Mon Sep 17 00:00:00 2001 From: Pablo Lamela Date: Thu, 19 Sep 2024 04:42:35 +0200 Subject: [PATCH 16/18] Add URL to the 404 result of `serveFileWhile` server --- cardano-cli/cardano-cli.cabal | 1 + .../test/cardano-cli-test/Test/Cli/Hash.hs | 21 ++++++++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/cardano-cli/cardano-cli.cabal b/cardano-cli/cardano-cli.cabal index 38d044002..322d2bcd7 100644 --- a/cardano-cli/cardano-cli.cabal +++ b/cardano-cli/cardano-cli.cabal @@ -342,6 +342,7 @@ test-suite cardano-cli-test text, time, transformers, + utf8-string, wai, warp, diff --git a/cardano-cli/test/cardano-cli-test/Test/Cli/Hash.hs b/cardano-cli/test/cardano-cli-test/Test/Cli/Hash.hs index 2618d6ec5..7e4cf55af 100644 --- a/cardano-cli/test/cardano-cli-test/Test/Cli/Hash.hs +++ b/cardano-cli/test/cardano-cli-test/Test/Cli/Hash.hs @@ -8,13 +8,17 @@ import Control.Concurrent (forkOS) import Control.Exception.Lifted (bracket) import Control.Monad (void) import Control.Monad.Trans.Control (MonadBaseControl) +import qualified Data.ByteString.UTF8 as BSU8 import Data.List (intercalate) import Data.Monoid (Last (..)) +import Data.String (IsString (fromString)) +import Data.Text (unpack) import qualified Data.Text as T import GHC.IO.Exception (ExitCode (..)) import Network.HTTP.Types.Status (status200, status404) +import Network.HTTP.Types.URI (renderQuery) import Network.Socket (close) -import Network.Wai (Request, Response, ResponseReceived, pathInfo, responseFile, +import Network.Wai (Request (..), Response, ResponseReceived, pathInfo, responseFile, responseLBS) import Network.Wai.Handler.Warp (defaultSettings, openFreePort, runSettingsSocket) import System.Directory (getCurrentDirectory) @@ -122,7 +126,7 @@ hprop_check_anchor_data_hash_from_http_uri = serveFileWhile relativeUrl exampleAnchorDataPath - ( \port -> + ( \port -> do void $ execCardanoCLI [ "hash" @@ -198,7 +202,10 @@ serveFileWhile relativeUrl filePath action = let path = T.unpack <$> pathInfo req if path == relativeUrl then respond $ responseFile status200 [("Content-Type", "text/plain")] filePath Nothing - else respond $ responseLBS status404 [("Content-Type", "text/plain")] "404 - Not Found" + else + respond $ + responseLBS status404 [("Content-Type", "text/plain")] $ + fromString ("404 - Url \"" ++ urlFromRequest req ++ "\" - Not Found") -- Run server asynchronously in a separate thread void $ H.evalIO $ forkOS $ runSettingsSocket defaultSettings socket app @@ -208,3 +215,11 @@ serveFileWhile relativeUrl filePath action = (\(_, socket) -> H.evalIO $ close socket) -- Test action (\(port, _) -> action port) + where + urlFromRequest :: Request -> String + urlFromRequest req = + "http://" + ++ maybe "localhost" BSU8.toString (requestHeaderHost req) + ++ "/" + ++ intercalate "/" (unpack <$> pathInfo req) + ++ BSU8.toString (renderQuery True (queryString req)) From 09affb66b15d9691f6d0541d5b4e6a892edf14fe Mon Sep 17 00:00:00 2001 From: Pablo Lamela Date: Thu, 19 Sep 2024 04:44:30 +0200 Subject: [PATCH 17/18] Rename `execDetailCfgCardanoCLI` to `execDetailConfigCardanoCLI` --- .../test/cardano-cli-test-lib/Test/Cardano/CLI/Util.hs | 8 ++++---- cardano-cli/test/cardano-cli-test/Test/Cli/Hash.hs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cardano-cli/test/cardano-cli-test-lib/Test/Cardano/CLI/Util.hs b/cardano-cli/test/cardano-cli-test-lib/Test/Cardano/CLI/Util.hs index d1d4182f5..60d7b0238 100644 --- a/cardano-cli/test/cardano-cli-test-lib/Test/Cardano/CLI/Util.hs +++ b/cardano-cli/test/cardano-cli-test-lib/Test/Cardano/CLI/Util.hs @@ -11,7 +11,7 @@ module Test.Cardano.CLI.Util , equivalence , execCardanoCLI , execDetailCardanoCLI - , execDetailCfgCardanoCLI + , execDetailConfigCardanoCLI , tryExecCardanoCLI , propertyOnce , withSnd @@ -83,12 +83,12 @@ execDetailCardanoCLI -- ^ Arguments to the CLI command -> m (IO.ExitCode, String, String) -- ^ Captured stdout -execDetailCardanoCLI params = GHC.withFrozenCallStack $ execDetailCfgCardanoCLI H.defaultExecConfig params +execDetailCardanoCLI params = GHC.withFrozenCallStack $ execDetailConfigCardanoCLI H.defaultExecConfig params -- | Execute cardano-cli via the command line, expecting it to fail, and accepting custom config. -- -- Waits for the process to finish and returns the exit code, stdout and stderr. -execDetailCfgCardanoCLI +execDetailConfigCardanoCLI :: (MonadTest m, MonadCatch m, MonadIO m, HasCallStack) => ExecConfig -- ^ Configuration for the execution @@ -96,7 +96,7 @@ execDetailCfgCardanoCLI -- ^ Arguments to the CLI command -> m (IO.ExitCode, String, String) -- ^ Captured stdout -execDetailCfgCardanoCLI cfg = GHC.withFrozenCallStack $ execDetailFlex cfg "cardano-cli" "CARDANO_CLI" +execDetailConfigCardanoCLI cfg = GHC.withFrozenCallStack $ execDetailFlex cfg "cardano-cli" "CARDANO_CLI" procFlex' :: (MonadTest m, MonadCatch m, MonadIO m, HasCallStack) diff --git a/cardano-cli/test/cardano-cli-test/Test/Cli/Hash.hs b/cardano-cli/test/cardano-cli-test/Test/Cli/Hash.hs index 7e4cf55af..f9580f9c3 100644 --- a/cardano-cli/test/cardano-cli-test/Test/Cli/Hash.hs +++ b/cardano-cli/test/cardano-cli-test/Test/Cli/Hash.hs @@ -150,7 +150,7 @@ hprop_check_anchor_data_hash_from_ipfs_uri = ( \port -> do env <- H.evalIO getEnvironment result <- - execDetailCfgCardanoCLI + execDetailConfigCardanoCLI H.defaultExecConfig { H.execConfigEnv = Last $ From d0df4c0535cee2076b0081d6d47ff28ced77779a Mon Sep 17 00:00:00 2001 From: Pablo Lamela Date: Thu, 19 Sep 2024 18:30:58 +0200 Subject: [PATCH 18/18] Add comment with error before darwin CI patch --- flake.nix | 2 ++ 1 file changed, 2 insertions(+) diff --git a/flake.nix b/flake.nix index 14c51c02c..b61c56b66 100644 --- a/flake.nix +++ b/flake.nix @@ -70,6 +70,8 @@ macOS-security = # make `/usr/bin/security` available in `PATH`, which is needed for stack # on darwin which calls this binary to find certificates + # Without this, we get the following error when compiling darwin in Hydra: + # I/O error when downloading anchor data: security: createProcess: posix_spawnp: does not exist (No such file or directory) nixpkgs.writeScriptBin "security" ''exec /usr/bin/security "$@"''; isDarwin = (system == "x86_64-darwin") || (system == "aarch64-darwin"); gitRevFlag =