diff --git a/cardano-cli/cardano-cli.cabal b/cardano-cli/cardano-cli.cabal index 554fb57152c..67afde40c87 100644 --- a/cardano-cli/cardano-cli.cabal +++ b/cardano-cli/cardano-cli.cabal @@ -205,6 +205,7 @@ test-suite cardano-cli-pioneers Test.CLI.Shelley.TextEnvelope.Golden.Tx.TxBody Test.CLI.Shelley.TextEnvelope.Golden.Tx.Witness Test.ITN + Test.Metadata Test.OptParse Test.Pioneers.Exercise1 Test.Pioneers.Exercise2 diff --git a/cardano-cli/src/Cardano/CLI/Shelley/Commands.hs b/cardano-cli/src/Cardano/CLI/Shelley/Commands.hs index f8cbb755d8c..cd1861bb452 100644 --- a/cardano-cli/src/Cardano/CLI/Shelley/Commands.hs +++ b/cardano-cli/src/Cardano/CLI/Shelley/Commands.hs @@ -162,7 +162,7 @@ data PoolCmd -- ^ Epoch in which to retire the stake pool. --TODO: Double check this OutputFile | PoolGetId VerificationKeyFile - | PoolMetaDataHash PoolMetaDataFile + | PoolMetaDataHash PoolMetaDataFile (Maybe OutputFile) deriving (Eq, Show) diff --git a/cardano-cli/src/Cardano/CLI/Shelley/Parsers.hs b/cardano-cli/src/Cardano/CLI/Shelley/Parsers.hs index bca56277692..3afa936acf0 100644 --- a/cardano-cli/src/Cardano/CLI/Shelley/Parsers.hs +++ b/cardano-cli/src/Cardano/CLI/Shelley/Parsers.hs @@ -392,7 +392,7 @@ pPoolCmd = pId = PoolGetId <$> pVerificationKeyFile Output pPoolMetaDataHashSubCmd :: Parser PoolCmd - pPoolMetaDataHashSubCmd = PoolMetaDataHash <$> pPoolMetaDataFile + pPoolMetaDataHashSubCmd = PoolMetaDataHash <$> pPoolMetaDataFile <*> pMaybeOutputFile pQueryCmd :: Parser QueryCmd diff --git a/cardano-cli/src/Cardano/CLI/Shelley/Run/Pool.hs b/cardano-cli/src/Cardano/CLI/Shelley/Run/Pool.hs index d8300df3dba..ef6e78ce8d1 100644 --- a/cardano-cli/src/Cardano/CLI/Shelley/Run/Pool.hs +++ b/cardano-cli/src/Cardano/CLI/Shelley/Run/Pool.hs @@ -18,7 +18,7 @@ import qualified Data.ByteString.Char8 as BS import Cardano.Api (StakePoolMetadataValidationError, Network(..), decodeAndValidateStakePoolMetadata, renderStakePoolMetadataValidationError) -import Cardano.Api.TextView (TextViewTitle (..)) +import Cardano.Api.TextView (TextViewTitle (..), textShow) import Cardano.Api.Typed (AsType (..), Error (..), FileError (..), Key (..), Lovelace, StakeCredential (..), StakePoolMetadataReference (..), StakePoolParameters (..), StakePoolRelay (..), TextEnvelopeError, @@ -39,6 +39,7 @@ import qualified Cardano.Crypto.Hash.Class as Crypto data ShelleyPoolCmdError = ShelleyPoolReadFileError !(FileError TextEnvelopeError) | ShelleyPoolWriteFileError !(FileError ()) + | ShelleyPoolWriteMetaDataHashError !FilePath !IOException | ShelleyPoolMetaDataValidationError !StakePoolMetadataValidationError deriving Show @@ -47,6 +48,8 @@ renderShelleyPoolCmdError err = case err of ShelleyPoolReadFileError fileErr -> Text.pack (displayError fileErr) ShelleyPoolWriteFileError fileErr -> Text.pack (displayError fileErr) + ShelleyPoolWriteMetaDataHashError fp ioException -> + "Error writing stake pool metadata hash at: " <> textShow fp <> " Error: " <> textShow ioException ShelleyPoolMetaDataValidationError validationErr -> "Error validating stake pool metadata: " <> renderStakePoolMetadataValidationError validationErr @@ -58,7 +61,7 @@ runPoolCmd (PoolRegistrationCert sPvkey vrfVkey pldg pCost pMrgn rwdVerFp ownerV runPoolCmd (PoolRetirementCert sPvkeyFp retireEpoch outfp) = runStakePoolRetirementCert sPvkeyFp retireEpoch outfp runPoolCmd (PoolGetId sPvkey) = runPoolId sPvkey -runPoolCmd (PoolMetaDataHash poolMdFile) = runPoolMetaDataHash poolMdFile +runPoolCmd (PoolMetaDataHash poolMdFile mOutFile) = runPoolMetaDataHash poolMdFile mOutFile runPoolCmd cmd = liftIO $ putStrLn $ "runPoolCmd: " ++ show cmd @@ -191,8 +194,8 @@ runPoolId (VerificationKeyFile vkeyPath) = do $ readFileTextEnvelope (AsVerificationKey AsStakePoolKey) vkeyPath liftIO $ BS.putStrLn $ serialiseToRawBytesHex (verificationKeyHash stakePoolVerKey) -runPoolMetaDataHash :: PoolMetaDataFile -> ExceptT ShelleyPoolCmdError IO () -runPoolMetaDataHash (PoolMetaDataFile poolMDPath) = do +runPoolMetaDataHash :: PoolMetaDataFile -> Maybe OutputFile -> ExceptT ShelleyPoolCmdError IO () +runPoolMetaDataHash (PoolMetaDataFile poolMDPath) mOutFile = do metaDataBytes <- handleIOExceptT (ShelleyPoolReadFileError . FileIOError poolMDPath) $ BS.readFile poolMDPath _ <- firstExceptT ShelleyPoolMetaDataValidationError @@ -200,4 +203,8 @@ runPoolMetaDataHash (PoolMetaDataFile poolMDPath) = do $ decodeAndValidateStakePoolMetadata metaDataBytes let metaDataHash :: Crypto.Hash Crypto.Blake2b_256 ByteString metaDataHash = Crypto.hashRaw (\x -> x) metaDataBytes - liftIO $ BS.putStrLn (Crypto.getHashBytesAsHex metaDataHash) + case mOutFile of + Nothing -> liftIO $ BS.putStrLn (Crypto.getHashBytesAsHex metaDataHash) + Just (OutputFile fpath) -> + handleIOExceptT (ShelleyPoolWriteMetaDataHashError fpath) + $ BS.writeFile fpath (Crypto.getHashBytesAsHex metaDataHash) diff --git a/cardano-cli/test/Test/Metadata.hs b/cardano-cli/test/Test/Metadata.hs new file mode 100644 index 00000000000..ef73163667d --- /dev/null +++ b/cardano-cli/test/Test/Metadata.hs @@ -0,0 +1,54 @@ +{-# LANGUAGE OverloadedStrings #-} + +module Test.Metadata + ( tests + ) where + +import Cardano.Prelude + +import Hedgehog (Property, (===)) +import qualified Hedgehog as H + +import Test.OptParse + +golden_stakePoolMetadataHash :: Property +golden_stakePoolMetadataHash = + propertyOnce $ do + let stakePoolMetadataFp = "stake-pool-metadata.json" + outputStakePoolMetadataHashFp = "stake-pool-metadata-hash.txt" + allFiles = [stakePoolMetadataFp, outputStakePoolMetadataHashFp] + + -- Write the example stake pool metadata to disk + liftIO $ writeFile stakePoolMetadataFp exampleStakePoolMetadata + assertFilesExist [stakePoolMetadataFp] + + -- Hash the stake pool metadata + execCardanoCLIParser + allFiles + $ evalCardanoCLIParser [ "shelley","stake-pool","metadata-hash" + , "--pool-metadata-file", stakePoolMetadataFp + , "--out-file", outputStakePoolMetadataHashFp + ] + + -- Check for existence of the stake pool metadata hash file. + assertFilesExist [outputStakePoolMetadataHashFp] + + -- Check that the stake pool metadata hash file content is correct. + expectedStakePoolMetadataHash <- liftIO $ + readFile "test/Test/golden/shelley/stake_pool_metadata/stake_pool_metadata_hash" + actualStakePoolMetadataHash <- liftIO $ readFile outputStakePoolMetadataHashFp + expectedStakePoolMetadataHash === actualStakePoolMetadataHash + + liftIO $ fileCleanup allFiles + + H.success + where + exampleStakePoolMetadata :: Text + exampleStakePoolMetadata = "{\"homepage\":\"https://iohk.io\",\"name\":\"Genesis Pool C\",\"ticker\":\"GPC\",\"description\":\"Lorem Ipsum Dolor Sit Amet.\"}" + +tests :: IO Bool +tests = + H.checkSequential + $ H.Group "Stake pool metadata" + [ ("golden_stakePoolMetadataHash", golden_stakePoolMetadataHash) + ] diff --git a/cardano-cli/test/Test/golden/shelley/stake_pool_metadata/stake_pool_metadata_hash b/cardano-cli/test/Test/golden/shelley/stake_pool_metadata/stake_pool_metadata_hash new file mode 100644 index 00000000000..d10fbdedfd9 --- /dev/null +++ b/cardano-cli/test/Test/golden/shelley/stake_pool_metadata/stake_pool_metadata_hash @@ -0,0 +1 @@ +8241de08075886a7d09c847c9bbd1719459dac0bd0a2f085e673611ebb9a5965 \ No newline at end of file diff --git a/cardano-cli/test/cardano-cli-pioneers.hs b/cardano-cli/test/cardano-cli-pioneers.hs index d8ccdc44907..c2447091e94 100644 --- a/cardano-cli/test/cardano-cli-pioneers.hs +++ b/cardano-cli/test/cardano-cli-pioneers.hs @@ -6,11 +6,12 @@ import System.IO (BufferMode (..)) import qualified System.IO as IO import qualified Test.CLI.Shelley.TextEnvelope.Golden.Tests +import qualified Test.ITN +import qualified Test.Metadata import qualified Test.Pioneers.Exercise1 import qualified Test.Pioneers.Exercise2 import qualified Test.Pioneers.Exercise3 import qualified Test.Pioneers.Exercise4 -import qualified Test.ITN main :: IO () main = do @@ -27,4 +28,5 @@ main = do , Test.Pioneers.Exercise4.tests , Test.ITN.tests + , Test.Metadata.tests ]