Skip to content

Commit

Permalink
Merge pull request #932 from IntersectMBO/add-hash-validation4
Browse files Browse the repository at this point in the history
Add metadata validation, hash checking, and URL support to `stake-pool registration-certificate`, and hash checking and URL support to `stake-pool metadata-hash`
  • Loading branch information
palas authored Oct 11, 2024
2 parents 184b794 + ddfa0a2 commit a3f93bc
Show file tree
Hide file tree
Showing 45 changed files with 730 additions and 153 deletions.
1 change: 1 addition & 0 deletions cardano-cli/cardano-cli.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,7 @@ test-suite cardano-cli-test
Test.Cli.Pioneers.Exercise5
Test.Cli.Pioneers.Exercise6
Test.Cli.Pipes
Test.Cli.Shelley.Certificates.StakePool
Test.Cli.Shelley.Run.Hash
Test.Cli.Shelley.Run.Query
Test.Cli.Shelley.Transaction.Build
Expand Down
6 changes: 3 additions & 3 deletions cardano-cli/src/Cardano/CLI/Commands/Hash.hs
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,19 @@ data HashCmds
= HashAnchorDataCmd !HashAnchorDataCmdArgs
| HashScriptCmd !HashScriptCmdArgs

data HashGoal
data HashGoal hash
= -- | The hash is written to stdout
HashToStdout
| -- | The hash to check against
CheckHash !(L.SafeHash L.StandardCrypto L.AnchorData)
CheckHash !hash
| -- | The output file to which the hash is written
HashToFile !(File () Out)
deriving Show

data HashAnchorDataCmdArgs
= HashAnchorDataCmdArgs
{ toHash :: !AnchorDataHashSource
, hashGoal :: !HashGoal
, hashGoal :: !(HashGoal (L.SafeHash L.StandardCrypto L.AnchorData))
}
deriving Show

Expand Down
15 changes: 4 additions & 11 deletions cardano-cli/src/Cardano/CLI/EraBased/Commands/Governance/DRep.hs
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ module Cardano.CLI.EraBased.Commands.Governance.DRep
, GovernanceDRepUpdateCertificateCmdArgs (..)
, GovernanceDRepMetadataHashCmdArgs (..)
, DRepMetadataSource (..)
, DRepHashGoal (..)
)
where

import Cardano.Api
import qualified Cardano.Api.Ledger as L

import Cardano.CLI.Commands.Hash (HashGoal)
import Cardano.CLI.Types.Common
import Cardano.CLI.Types.Key

Expand Down Expand Up @@ -56,6 +56,7 @@ data GovernanceDRepRegistrationCertificateCmdArgs era
:: !( Maybe
( PotentiallyCheckedAnchor
DRepMetadataUrl
(L.Anchor L.StandardCrypto)
)
)
, outFile :: !(File () Out)
Expand All @@ -77,6 +78,7 @@ data GovernanceDRepUpdateCertificateCmdArgs era
:: Maybe
( PotentiallyCheckedAnchor
DRepMetadataUrl
(L.Anchor L.StandardCrypto)
)
, outFile :: !(File () Out)
}
Expand All @@ -85,23 +87,14 @@ data GovernanceDRepMetadataHashCmdArgs era
= GovernanceDRepMetadataHashCmdArgs
{ eon :: !(ConwayEraOnwards era)
, drepMetadataSource :: !DRepMetadataSource
, hashGoal :: !DRepHashGoal
, hashGoal :: !(HashGoal (Hash DRepMetadata))
}

data DRepMetadataSource
= DrepMetadataFileIn !(DRepMetadataFile In)
| DrepMetadataURL !L.Url
deriving Show

data DRepHashGoal
= -- | The hash is written to stdout
DRepHashToStdout
| -- | The hash to check against
CheckDRepHash !(Hash DRepMetadata)
| -- | The output file to which the hash is written
DRepHashToFile !(File () Out)
deriving Show

renderGovernanceDRepCmds
:: ()
=> GovernanceDRepCmds era
Expand Down
15 changes: 12 additions & 3 deletions cardano-cli/src/Cardano/CLI/EraBased/Commands/StakePool.hs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,15 @@ module Cardano.CLI.EraBased.Commands.StakePool
, StakePoolIdCmdArgs (..)
, StakePoolMetadataHashCmdArgs (..)
, StakePoolRegistrationCertificateCmdArgs (..)
, StakePoolMetadataSource (..)
)
where

import Cardano.Api.Ledger (Coin)
import qualified Cardano.Api.Ledger as L
import Cardano.Api.Shelley hiding (QueryInShelleyBasedEra (..))

import Cardano.CLI.Commands.Hash (HashGoal)
import Cardano.CLI.Types.Common
import Cardano.CLI.Types.Key

Expand Down Expand Up @@ -48,11 +51,16 @@ data StakePoolIdCmdArgs era

data StakePoolMetadataHashCmdArgs era
= StakePoolMetadataHashCmdArgs
{ poolMetadataFile :: !(StakePoolMetadataFile In)
, mOutFile :: !(Maybe (File () Out))
{ poolMetadataSource :: !StakePoolMetadataSource
, hashGoal :: !(HashGoal (Hash StakePoolMetadata))
}
deriving Show

data StakePoolMetadataSource
= StakePoolMetadataFileIn !(StakePoolMetadataFile In)
| StakePoolMetadataURL !L.Url
deriving Show

data StakePoolRegistrationCertificateCmdArgs era
= StakePoolRegistrationCertificateCmdArgs
{ sbe :: !(ShelleyBasedEra era)
Expand All @@ -73,7 +81,8 @@ data StakePoolRegistrationCertificateCmdArgs era
-- ^ Pool owner verification staking key(s).
, relays :: ![StakePoolRelay]
-- ^ Stake pool relays.
, mMetadata :: !(Maybe StakePoolMetadataReference)
, mMetadata
:: !(Maybe (PotentiallyCheckedAnchor StakePoolMetadataReference StakePoolMetadataReference))
-- ^ Stake pool metadata.
, network :: !NetworkId
, outFile :: !(File () Out)
Expand Down
37 changes: 23 additions & 14 deletions cardano-cli/src/Cardano/CLI/EraBased/Options/Common.hs
Original file line number Diff line number Diff line change
Expand Up @@ -2683,12 +2683,11 @@ pPort =
, Opt.help "The stake pool relay's port"
]

pStakePoolMetadataReference :: Parser (Maybe StakePoolMetadataReference)
pStakePoolMetadataReference :: Parser StakePoolMetadataReference
pStakePoolMetadataReference =
optional $
StakePoolMetadataReference
<$> pStakePoolMetadataUrl
<*> pStakePoolMetadataHash
StakePoolMetadataReference
<$> pStakePoolMetadataUrl
<*> pStakePoolMetadataHash

pStakePoolMetadataUrl :: Parser Text
pStakePoolMetadataUrl =
Expand Down Expand Up @@ -2724,7 +2723,11 @@ pStakePoolRegistrationParserRequirements envCli =
<*> pRewardAcctVerificationKeyOrFile
<*> some pPoolOwnerVerificationKeyOrFile
<*> many pPoolRelay
<*> pStakePoolMetadataReference
<*> optional
( pPotentiallyCheckedAnchorData
pMustCheckStakeMetadataHash
pStakePoolMetadataReference
)
<*> pNetworkId envCli

pProtocolParametersUpdate :: Parser ProtocolParametersUpdate
Expand Down Expand Up @@ -3547,16 +3550,19 @@ pAnchorUrl =
ProposalUrl
<$> pUrl "anchor-url" "Anchor URL"

pExpectedHash :: Parser (L.SafeHash L.StandardCrypto L.AnchorData)
pExpectedHash =
Opt.option readSafeHash $
pExpectedAnchorDataHash :: Parser (L.SafeHash L.StandardCrypto L.AnchorData)
pExpectedAnchorDataHash = pExpectedHash id "anchor data"

pExpectedHash :: (L.SafeHash L.StandardCrypto L.AnchorData -> a) -> String -> Parser a
pExpectedHash adaptor hashedDataName =
Opt.option (adaptor <$> 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."
[ "Expected hash for the " ++ hashedDataName ++ ", for verification purposes. "
, "If provided, the hash of the " ++ hashedDataName ++ " will be compared to this value."
]
]

Expand Down Expand Up @@ -3586,9 +3592,9 @@ pMustCheckHash flagSuffix' dataName' hashParamName' urlParamName' =
]

pPotentiallyCheckedAnchorData
:: Parser (MustCheckHash anchorDataType)
-> Parser (L.Anchor L.StandardCrypto)
-> Parser (PotentiallyCheckedAnchor anchorDataType)
:: Parser (MustCheckHash anchorType)
-> Parser anchor
-> Parser (PotentiallyCheckedAnchor anchorType anchor)
pPotentiallyCheckedAnchorData mustCheckHash anchorData =
PotentiallyCheckedAnchor
<$> anchorData
Expand All @@ -3603,6 +3609,9 @@ pMustCheckConstitutionHash = pMustCheckHash "constitution-hash" "constitution" "
pMustCheckMetadataHash :: Parser (MustCheckHash DRepMetadataUrl)
pMustCheckMetadataHash = pMustCheckHash "drep-metadata-hash" "DRep metadata" "--drep-metadata-hash" "--drep-metadata-url"

pMustCheckStakeMetadataHash :: Parser (MustCheckHash StakePoolMetadataReference)
pMustCheckStakeMetadataHash = pMustCheckHash "metadata-hash" "stake pool metadata" "--metadata-hash" "--metadata-url"

pPreviousGovernanceAction :: Parser (Maybe (TxId, Word16))
pPreviousGovernanceAction =
optional $
Expand Down
22 changes: 7 additions & 15 deletions cardano-cli/src/Cardano/CLI/EraBased/Options/Governance/DRep.hs
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@ import Cardano.Api.Ledger (extractHash)
import qualified Cardano.Api.Ledger as L
import Cardano.Api.Shelley (Hash (DRepMetadataHash))

import Cardano.CLI.Commands.Hash (HashGoal (..))
import Cardano.CLI.Environment
import Cardano.CLI.EraBased.Commands.Governance.DRep
import Cardano.CLI.EraBased.Options.Common
import Cardano.CLI.Parser
import Cardano.CLI.Read
import Cardano.CLI.Types.Common
import Cardano.CLI.Types.Common hiding (CheckHash)
import Cardano.CLI.Types.Key
import Cardano.Ledger.SafeHash (castSafeHash)

Expand Down Expand Up @@ -134,16 +135,7 @@ pDrepMetadataUrl =

pExpectedDrepMetadataHash :: Parser (Hash DRepMetadata)
pExpectedDrepMetadataHash =
Opt.option (DRepMetadataHash . extractHash . castSafeHash <$> readSafeHash) $
mconcat
[ Opt.long "expected-hash"
, Opt.metavar "HASH"
, Opt.help $
mconcat
[ "Expected hash for the DRep metadata, for verification purposes. "
, "If provided, the hash of the DRep metadata is compared to this value."
]
]
pExpectedHash (DRepMetadataHash . extractHash . castSafeHash) "DRep metadata"

pDrepMetadataHash :: Parser (L.SafeHash L.StandardCrypto L.AnchorData)
pDrepMetadataHash =
Expand Down Expand Up @@ -210,13 +202,13 @@ pGovernanceDrepMetadataHashCmd era = do
$ Opt.progDesc
"Calculate the hash of a metadata file, optionally checking the obtained hash against an expected value."

pDRepHashGoal :: Parser DRepHashGoal
pDRepHashGoal :: Parser (HashGoal (Hash DRepMetadata))
pDRepHashGoal =
asum
[ CheckDRepHash <$> pExpectedDrepMetadataHash
, DRepHashToFile <$> pOutputFile
[ CheckHash <$> pExpectedDrepMetadataHash
, HashToFile <$> pOutputFile
]
<|> pure DRepHashToStdout
<|> pure HashToStdout

pDRepMetadataSource :: Parser DRepMetadataSource
pDRepMetadataSource =
Expand Down
40 changes: 36 additions & 4 deletions cardano-cli/src/Cardano/CLI/EraBased/Options/StakePool.hs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,16 @@ module Cardano.CLI.EraBased.Options.StakePool
where

import Cardano.Api
import qualified Cardano.Api.Ledger as L
import Cardano.Api.Shelley (Hash (StakePoolMetadataHash))

import qualified Cardano.CLI.Commands.Hash as Cmd
import Cardano.CLI.Environment (EnvCli (..))
import qualified Cardano.CLI.EraBased.Commands.StakePool as Cmd
import Cardano.CLI.EraBased.Options.Common
import qualified Cardano.Ledger.SafeHash as L

import qualified Data.Foldable as F
import Options.Applicative hiding (help, str)
import qualified Options.Applicative as Opt

Expand All @@ -42,7 +47,10 @@ pStakePoolCmds era envCli =
, Just $
subParser "metadata-hash" $
Opt.info pStakePoolMetadataHashCmd $
Opt.progDesc "Print the hash of pool metadata."
Opt.progDesc
( "Calculate the hash of a stake pool metadata file,"
<> " optionally checking the obtained hash against an expected value."
)
]

pStakePoolId
Expand All @@ -61,8 +69,28 @@ pStakePoolMetadataHashCmd
pStakePoolMetadataHashCmd =
fmap Cmd.StakePoolMetadataHashCmd $
Cmd.StakePoolMetadataHashCmdArgs
<$> pPoolMetadataFile
<*> pMaybeOutputFile
<$> pPoolMetadataSource
<*> pPoolMetadataHashGoal

pPoolMetadataSource :: Parser Cmd.StakePoolMetadataSource
pPoolMetadataSource =
F.asum
[ Cmd.StakePoolMetadataFileIn <$> pPoolMetadataFile
, Cmd.StakePoolMetadataURL
<$> pUrl "pool-metadata-url" "URL pointing to the JSON Metadata file to hash."
]

pPoolMetadataHashGoal :: Parser (Cmd.HashGoal (Hash StakePoolMetadata))
pPoolMetadataHashGoal =
F.asum
[ Cmd.CheckHash <$> pExpectedStakePoolMetadataHash
, Cmd.HashToFile <$> pOutputFile
]
<|> pure Cmd.HashToStdout

pExpectedStakePoolMetadataHash :: Parser (Hash StakePoolMetadata)
pExpectedStakePoolMetadataHash =
pExpectedHash (StakePoolMetadataHash . L.extractHash . L.castSafeHash) "stake pool metadata"

pStakePoolRegistrationCertificateCmd
:: ()
Expand All @@ -84,7 +112,11 @@ pStakePoolRegistrationCertificateCmd era envCli = do
<*> pRewardAcctVerificationKeyOrFile
<*> some pPoolOwnerVerificationKeyOrFile
<*> many pPoolRelay
<*> pStakePoolMetadataReference
<*> optional
( pPotentiallyCheckedAnchorData
pMustCheckStakeMetadataHash
pStakePoolMetadataReference
)
<*> pNetworkId envCli
<*> pOutputFile
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -526,7 +526,7 @@ carryHashChecks checkHash anchor checkType =
L.AnchorData
<$> fetchURLErrorToGovernanceActionError
checkType
(getByteStringFromURL httpsAndIpfsSchemas $ L.anchorUrl anchor)
(getByteStringFromURL httpsAndIpfsSchemas $ L.urlToText $ L.anchorUrl anchor)
let hash = L.hashAnchorData anchorData
when (hash /= L.anchorDataHash anchor) $
left $
Expand Down
14 changes: 7 additions & 7 deletions cardano-cli/src/Cardano/CLI/EraBased/Run/Governance/DRep.hs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ where
import Cardano.Api
import qualified Cardano.Api.Ledger as L

import Cardano.CLI.EraBased.Commands.Governance.DRep (DRepHashGoal (..))
import qualified Cardano.CLI.Commands.Hash as Cmd
import qualified Cardano.CLI.EraBased.Commands.Governance.DRep as Cmd
import Cardano.CLI.Run.Hash (allSchemas, getByteStringFromURL, httpsAndIpfsSchemas)
import qualified Cardano.CLI.Run.Key as Key
Expand Down Expand Up @@ -187,15 +187,15 @@ runGovernanceDRepMetadataHashCmd
Cmd.DrepMetadataFileIn metadataFile ->
firstExceptT ReadFileError . newExceptT $ readByteStringFile metadataFile
Cmd.DrepMetadataURL urlText ->
fetchURLToGovernanceCmdError $ getByteStringFromURL allSchemas urlText
fetchURLToGovernanceCmdError $ getByteStringFromURL allSchemas $ L.urlToText urlText
let (_metadata, metadataHash) = hashDRepMetadata metadataBytes
case hashGoal of
CheckDRepHash expectedHash
Cmd.CheckHash expectedHash
| metadataHash /= expectedHash ->
left $ GovernanceCmdHashMismatchError expectedHash metadataHash
| otherwise -> liftIO $ putStrLn "Hashes match!"
DRepHashToFile outFile -> writeOutput (Just outFile) metadataHash
DRepHashToStdout -> writeOutput Nothing metadataHash
Cmd.HashToFile outFile -> writeOutput (Just outFile) metadataHash
Cmd.HashToStdout -> writeOutput Nothing metadataHash
where
writeOutput
:: MonadIO m
Expand All @@ -215,7 +215,7 @@ runGovernanceDRepMetadataHashCmd
-- | Check the hash of the anchor data against the hash in the anchor if
-- checkHash is set to CheckHash.
carryHashChecks
:: PotentiallyCheckedAnchor DRepMetadataUrl
:: PotentiallyCheckedAnchor DRepMetadataUrl (L.Anchor L.StandardCrypto)
-- ^ The information about anchor data and whether to check the hash (see 'PotentiallyCheckedAnchor')
-> ExceptT HashCheckError IO ()
carryHashChecks potentiallyCheckedAnchor =
Expand All @@ -225,7 +225,7 @@ carryHashChecks potentiallyCheckedAnchor =
L.AnchorData
<$> withExceptT
FetchURLError
(getByteStringFromURL httpsAndIpfsSchemas $ L.anchorUrl anchor)
(getByteStringFromURL httpsAndIpfsSchemas $ L.urlToText $ L.anchorUrl anchor)
let hash = L.hashAnchorData anchorData
when (hash /= L.anchorDataHash anchor) $
left $
Expand Down
Loading

0 comments on commit a3f93bc

Please sign in to comment.