Skip to content

Add pool operator extended key support #781

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions cardano-api/gen/Test/Gen/Cardano/Api/Typed.hs
Original file line number Diff line number Diff line change
Expand Up @@ -197,8 +197,6 @@ import qualified Hedgehog.Gen as Gen
import qualified Hedgehog.Gen.QuickCheck as Q
import qualified Hedgehog.Range as Range



genAddressByron :: Gen (Address ByronAddr)
genAddressByron =
makeByronAddress
Expand Down Expand Up @@ -572,10 +570,13 @@ genOperationalCertificateWithCounter
genOperationalCertificateWithCounter = do
kesVKey <- genVerificationKey AsKesKey
stkPoolOrGenDelExtSign <-
Gen.either (genSigningKey AsStakePoolKey) (genSigningKey AsGenesisDelegateExtendedKey)
Gen.either (Gen.choice [ AnyStakePoolNormalSigningKey <$> genSigningKey AsStakePoolKey
, AnyStakePoolExtendedSigningKey <$> genSigningKey AsStakePoolExtendedKey
])
(genSigningKey AsGenesisDelegateExtendedKey)
kesP <- genKESPeriod
c <- Gen.integral $ Range.linear 0 1000
let stakePoolVer = either getVerificationKey (convert' . getVerificationKey) stkPoolOrGenDelExtSign
let stakePoolVer = either castAnyStakePoolSigningKeyToNormalVerificationKey (convert' . getVerificationKey) stkPoolOrGenDelExtSign
iCounter = OperationalCertificateIssueCounter c stakePoolVer

case issueOperationalCertificate kesVKey stkPoolOrGenDelExtSign kesP iCounter of
Expand All @@ -584,6 +585,15 @@ genOperationalCertificateWithCounter = do
Left err -> error $ docToString $ prettyError err
Right pair -> return pair
where
castAnyStakePoolSigningKeyToNormalVerificationKey
:: AnyStakePoolSigningKey
-> VerificationKey StakePoolKey
castAnyStakePoolSigningKeyToNormalVerificationKey anyStakePoolSKey =
case anyStakePoolSigningKeyToVerificationKey anyStakePoolSKey of
AnyStakePoolNormalVerificationKey normalStakePoolVKey -> normalStakePoolVKey
AnyStakePoolExtendedVerificationKey extendedStakePoolVKey ->
castVerificationKey extendedStakePoolVKey

convert'
:: VerificationKey GenesisDelegateExtendedKey
-> VerificationKey StakePoolKey
Expand Down
59 changes: 58 additions & 1 deletion cardano-api/src/Cardano/Api/Internal/Keys/Shelley.hs
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
-- The Shelley ledger uses promoted data kinds which we have to use, but we do
-- not export any from this API. We also use them unticked as nature intended.
{-# OPTIONS_GHC -Wno-unticked-promoted-constructors #-}
Expand Down Expand Up @@ -37,6 +41,10 @@ module Cardano.Api.Internal.Keys.Shelley
, VerificationKey (..)
, SigningKey (..)
, Hash (..)
, AnyStakePoolVerificationKey (..)
, anyStakePoolVerificationKeyHash
, AnyStakePoolSigningKey (..)
, anyStakePoolSigningKeyToVerificationKey
)
where

Expand All @@ -60,7 +68,11 @@ import Cardano.Crypto.Wallet qualified as Crypto.HD
import Cardano.Ledger.Keys (DSIGN)
import Cardano.Ledger.Keys qualified as Shelley

import Data.Aeson.Types (ToJSONKey (..), toJSONKeyText, withText)
import Data.Aeson.Types
( ToJSONKey (..)
, toJSONKeyText
, withText
)
import Data.Bifunctor (first)
import Data.ByteString (ByteString)
import Data.ByteString qualified as BS
Expand Down Expand Up @@ -1658,6 +1670,29 @@ instance CastSigningKeyRole GenesisUTxOKey PaymentKey where
-- stake pool keys
--

-- | Wrapper that handles both normal and extended StakePoolKeys VerificationKeys
data AnyStakePoolVerificationKey
= AnyStakePoolNormalVerificationKey (VerificationKey StakePoolKey)
| AnyStakePoolExtendedVerificationKey (VerificationKey StakePoolExtendedKey)
deriving (Show, Eq)

anyStakePoolVerificationKeyHash :: AnyStakePoolVerificationKey -> Hash StakePoolKey
anyStakePoolVerificationKeyHash (AnyStakePoolNormalVerificationKey vk) = verificationKeyHash vk
anyStakePoolVerificationKeyHash (AnyStakePoolExtendedVerificationKey vk) =
let StakePoolExtendedKeyHash hash = verificationKeyHash vk in StakePoolKeyHash hash

-- | Wrapper that handles both normal and extended StakePoolKeys SigningKeys
data AnyStakePoolSigningKey
= AnyStakePoolNormalSigningKey (SigningKey StakePoolKey)
| AnyStakePoolExtendedSigningKey (SigningKey StakePoolExtendedKey)
deriving Show

anyStakePoolSigningKeyToVerificationKey :: AnyStakePoolSigningKey -> AnyStakePoolVerificationKey
anyStakePoolSigningKeyToVerificationKey (AnyStakePoolNormalSigningKey sk) =
AnyStakePoolNormalVerificationKey (getVerificationKey sk)
anyStakePoolSigningKeyToVerificationKey (AnyStakePoolExtendedSigningKey vk) =
AnyStakePoolExtendedVerificationKey (getVerificationKey vk)

data StakePoolKey

instance HasTypeProxy StakePoolKey where
Expand Down Expand Up @@ -1892,6 +1927,10 @@ instance SerialiseAsRawBytes (Hash StakePoolExtendedKey) where
(SerialiseAsRawBytesError "Unable to deserialise Hash StakePoolExtendedKey")
(StakePoolExtendedKeyHash . Shelley.KeyHash <$> Crypto.hashFromBytes bs)

instance SerialiseAsBech32 (Hash StakePoolExtendedKey) where
bech32PrefixFor _ = "pool_xvkh"
bech32PrefixesPermitted _ = ["pool_xvkh"]

instance HasTextEnvelope (VerificationKey StakePoolExtendedKey) where
textEnvelopeType _ = "StakePoolExtendedVerificationKey_ed25519_bip32"

Expand All @@ -1906,6 +1945,24 @@ instance SerialiseAsBech32 (SigningKey StakePoolExtendedKey) where
bech32PrefixFor _ = "pool_xsk"
bech32PrefixesPermitted _ = ["pool_xsk"]

instance ToJSON (Hash StakePoolExtendedKey) where
toJSON = toJSON . serialiseToBech32

instance ToJSONKey (Hash StakePoolExtendedKey) where
toJSONKey = toJSONKeyText serialiseToBech32

instance FromJSON (Hash StakePoolExtendedKey) where
parseJSON = withText "PoolId" $ \str ->
case deserialiseFromBech32 (AsHash AsStakePoolExtendedKey) str of
Left err ->
fail $
docToString $
mconcat
[ "Error deserialising Hash StakePoolKey: " <> pretty str
, " Error: " <> prettyError err
]
Right h -> pure h

instance CastVerificationKeyRole StakePoolExtendedKey StakePoolKey where
castVerificationKey (StakePoolExtendedVerificationKey vk) =
StakePoolVerificationKey
Expand Down
24 changes: 21 additions & 3 deletions cardano-api/src/Cardano/Api/Internal/OperationalCertificate.hs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ instance Error OperationalCertIssueError where
issueOperationalCertificate
:: VerificationKey KesKey
-> Either
(SigningKey StakePoolKey)
AnyStakePoolSigningKey
(SigningKey GenesisDelegateExtendedKey)
-- TODO: this may be better with a type that
-- captured the three (four?) choices, stake pool
Expand All @@ -134,8 +134,21 @@ issueOperationalCertificate
, OperationalCertificateIssueCounter (succ counter) poolVKey
)
where
castAnyStakePoolSigningKeyToNormalVerificationKey
:: AnyStakePoolSigningKey
-> VerificationKey StakePoolKey
castAnyStakePoolSigningKeyToNormalVerificationKey anyStakePoolSKey =
case anyStakePoolSigningKeyToVerificationKey anyStakePoolSKey of
AnyStakePoolNormalVerificationKey normalStakePoolVKey -> normalStakePoolVKey
AnyStakePoolExtendedVerificationKey extendedStakePoolVKey ->
castVerificationKey extendedStakePoolVKey

poolVKey' :: VerificationKey StakePoolKey
poolVKey' = either getVerificationKey (convert . getVerificationKey) skey
poolVKey' =
either
castAnyStakePoolSigningKeyToNormalVerificationKey
(convert . getVerificationKey)
skey
where
convert
:: VerificationKey GenesisDelegateExtendedKey
Expand Down Expand Up @@ -164,8 +177,13 @@ issueOperationalCertificate
where
skey' :: ShelleySigningKey
skey' = case skey of
Left (StakePoolSigningKey poolSKey) ->
Left (AnyStakePoolNormalSigningKey (StakePoolSigningKey poolSKey)) ->
ShelleyNormalSigningKey poolSKey
Left
( AnyStakePoolExtendedSigningKey
(StakePoolExtendedSigningKey poolExtendedSKey)
) ->
ShelleyExtendedSigningKey poolExtendedSKey
Right (GenesisDelegateExtendedSigningKey delegSKey) ->
ShelleyExtendedSigningKey delegSKey

Expand Down
2 changes: 2 additions & 0 deletions cardano-api/src/Cardano/Api/Internal/Tx/Sign.hs
Original file line number Diff line number Diff line change
Expand Up @@ -790,6 +790,7 @@ data ShelleyWitnessSigningKey
| WitnessStakeKey (SigningKey StakeKey)
| WitnessStakeExtendedKey (SigningKey StakeExtendedKey)
| WitnessStakePoolKey (SigningKey StakePoolKey)
| WitnessStakePoolExtendedKey (SigningKey StakePoolExtendedKey)
| WitnessGenesisKey (SigningKey GenesisKey)
| WitnessGenesisExtendedKey (SigningKey GenesisExtendedKey)
| WitnessGenesisDelegateKey (SigningKey GenesisDelegateKey)
Expand Down Expand Up @@ -1163,6 +1164,7 @@ toShelleySigningKey key = case key of
-- The cases for extended keys
WitnessPaymentExtendedKey (PaymentExtendedSigningKey sk) -> ShelleyExtendedSigningKey sk
WitnessStakeExtendedKey (StakeExtendedSigningKey sk) -> ShelleyExtendedSigningKey sk
WitnessStakePoolExtendedKey (StakePoolExtendedSigningKey sk) -> ShelleyExtendedSigningKey sk
WitnessGenesisExtendedKey (GenesisExtendedSigningKey sk) -> ShelleyExtendedSigningKey sk
WitnessGenesisDelegateExtendedKey (GenesisDelegateExtendedSigningKey sk) -> ShelleyExtendedSigningKey sk
WitnessCommitteeColdExtendedKey (CommitteeColdExtendedSigningKey sk) -> ShelleyExtendedSigningKey sk
Expand Down
4 changes: 4 additions & 0 deletions cardano-api/src/Cardano/Api/Shelley.hs
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,10 @@ module Cardano.Api.Shelley
, DRepMetadataReference (DRepMetadataReference)

-- ** Stake pool operator's keys
, AnyStakePoolVerificationKey (..)
, anyStakePoolVerificationKeyHash
, AnyStakePoolSigningKey (..)
, anyStakePoolSigningKeyToVerificationKey
, StakePoolExtendedKey
, StakePoolKey
, PoolId
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,12 @@ test_OperationalCertIssueError =
testAllErrorMessages_
"Cardano.Api.OperationalCertificate"
"OperationalCertIssueError"
[ ("OperationalCertKeyMismatch", OperationalCertKeyMismatch stakePoolVerKey1 stakePoolVerKey2)
[
( "OperationalCertKeyMismatch"
, OperationalCertKeyMismatch
stakePoolVerKey1
stakePoolVerKey2
)
]

test_ProtocolParametersError :: TestTree
Expand Down
Loading