diff --git a/.changes/20260813_120000_cardano-cli_sebastian.nagel_pool_reg_bls_key.yml b/.changes/20260813_120000_cardano-cli_sebastian.nagel_pool_reg_bls_key.yml new file mode 100644 index 0000000000..0de548ce3f --- /dev/null +++ b/.changes/20260813_120000_cardano-cli_sebastian.nagel_pool_reg_bls_key.yml @@ -0,0 +1,10 @@ +description: | + `stake-pool registration-certificate` can now register a stake pool's BLS voting key. The new `--bls-signing-key-file FILEPATH` option is era-gated: it is mandatory from the Dijkstra era onwards and not offered in Conway or earlier, so existing Conway invocations are unaffected. The signing key is read as a text envelope (produce one with `node key-gen-BLS`) and converted to the registered verification key plus proof of possession via `createBlsKeyRegistration`. + + The `dijkstra` subcommand now also exposes the `stake-pool` command group, so `cardano-cli dijkstra stake-pool registration-certificate` is reachable. Previously only `node` was wired up for that era. + + Genesis pool parameters built by `genesis create-staked` and `genesis create-testnet-data` set the new ledger `sppBlsKey` field to `SNothing`. +kind: + - feature +pr: 0 +project: cardano-cli diff --git a/cardano-cli/src/Cardano/CLI/Compatible/StakePool/Run.hs b/cardano-cli/src/Cardano/CLI/Compatible/StakePool/Run.hs index 16730d4a69..26058b6d1f 100644 --- a/cardano-cli/src/Cardano/CLI/Compatible/StakePool/Run.hs +++ b/cardano-cli/src/Cardano/CLI/Compatible/StakePool/Run.hs @@ -84,6 +84,7 @@ runStakePoolRegistrationCertificateCmd StakePoolParameters { stakePoolId = stakePoolId' , stakePoolVRF = vrfKeyHash' + , stakePoolBlsKey = Nothing , stakePoolCost = poolCost , stakePoolMargin = poolMargin , stakePoolRewardAccount = rewardAccountAddr diff --git a/cardano-cli/src/Cardano/CLI/EraBased/Genesis/CreateTestnetData/Run.hs b/cardano-cli/src/Cardano/CLI/EraBased/Genesis/CreateTestnetData/Run.hs index d43852bf05..e44156145a 100644 --- a/cardano-cli/src/Cardano/CLI/EraBased/Genesis/CreateTestnetData/Run.hs +++ b/cardano-cli/src/Cardano/CLI/EraBased/Genesis/CreateTestnetData/Run.hs @@ -805,6 +805,7 @@ buildPoolParams nw dir index specifiedRelays = do , L.sppOwners = mempty , L.sppRelays = lookupPoolRelay specifiedRelays , L.sppMetadata = L.SNothing + , L.sppBlsKey = L.SNothing } where lookupPoolRelay :: Map Word [L.StakePoolRelay] -> Seq.StrictSeq L.StakePoolRelay diff --git a/cardano-cli/src/Cardano/CLI/EraBased/Genesis/Run.hs b/cardano-cli/src/Cardano/CLI/EraBased/Genesis/Run.hs index 093c768e38..5790354f82 100644 --- a/cardano-cli/src/Cardano/CLI/EraBased/Genesis/Run.hs +++ b/cardano-cli/src/Cardano/CLI/EraBased/Genesis/Run.hs @@ -989,6 +989,7 @@ buildPoolParams nw dir index specifiedRelays = do , L.sppOwners = mempty , L.sppRelays = lookupPoolRelay specifiedRelays , L.sppMetadata = L.SNothing + , L.sppBlsKey = L.SNothing } where lookupPoolRelay diff --git a/cardano-cli/src/Cardano/CLI/EraBased/Option.hs b/cardano-cli/src/Cardano/CLI/EraBased/Option.hs index adf63519da..10175edef8 100644 --- a/cardano-cli/src/Cardano/CLI/EraBased/Option.hs +++ b/cardano-cli/src/Cardano/CLI/EraBased/Option.hs @@ -45,6 +45,18 @@ pCmds envCli = do , fmap TransactionCmds <$> pTransactionCmds envCli ] +-- | The subset of era-based commands wired up for Dijkstra so far. Grows towards +-- 'pCmds' as each command group is checked over for the new era; a pool's BLS +-- voting key can only be registered from Dijkstra onwards, so @stake-pool@ has to +-- be reachable here. +pDijkstraCmds :: EnvCli -> Parser (Cmds DijkstraEra) +pDijkstraCmds envCli = + asum $ + catMaybes + [ Just (NodeCmds <$> pNodeCmds @DijkstraEra) + , fmap StakePoolCmds <$> pStakePoolCmds envCli + ] + pAnyEraCommand :: EnvCli -> Parser AnyEraCommand pAnyEraCommand envCli = asum @@ -54,7 +66,7 @@ pAnyEraCommand envCli = Opt.progDesc "Conway era commands" , Opt.hsubparser $ commandWithMetavar "dijkstra" $ - Opt.info (AnyEraCommandOf DijkstraEra <$> asum [NodeCmds <$> pNodeCmds @DijkstraEra]) $ + Opt.info (AnyEraCommandOf DijkstraEra <$> pDijkstraCmds envCli) $ Opt.progDesc "Dijkstra era commands" , Opt.hsubparser $ commandWithMetavar "latest" $ diff --git a/cardano-cli/src/Cardano/CLI/EraBased/StakePool/Command.hs b/cardano-cli/src/Cardano/CLI/EraBased/StakePool/Command.hs index 5c2a530bfa..48a4f6109f 100644 --- a/cardano-cli/src/Cardano/CLI/EraBased/StakePool/Command.hs +++ b/cardano-cli/src/Cardano/CLI/EraBased/StakePool/Command.hs @@ -74,6 +74,9 @@ data StakePoolRegistrationCertificateCmdArgs era -- ^ Stake pool verification key. , vrfVerificationKeyOrFile :: !(VerificationKeyOrFile VrfKey) -- ^ VRF Verification key. + , blsSkeyFile :: !(Maybe (SigningKeyFile In)) + -- ^ BLS signing key, from which the registered voting key and its proof of + -- possession are derived. Required from Dijkstra onwards, unavailable before. , poolPledge :: !Coin -- ^ Pool pledge. , poolCost :: !Coin diff --git a/cardano-cli/src/Cardano/CLI/EraBased/StakePool/Option.hs b/cardano-cli/src/Cardano/CLI/EraBased/StakePool/Option.hs index b5802be05c..a6f7787081 100644 --- a/cardano-cli/src/Cardano/CLI/EraBased/StakePool/Option.hs +++ b/cardano-cli/src/Cardano/CLI/EraBased/StakePool/Option.hs @@ -17,6 +17,7 @@ import Cardano.CLI.Environment (EnvCli (..)) import Cardano.CLI.EraBased.Common.Option import Cardano.CLI.EraBased.StakePool.Command qualified as Cmd import Cardano.CLI.EraIndependent.Hash.Command qualified as Cmd +import Cardano.CLI.EraIndependent.Node.Option (pBlsSigningKeyFile) import Cardano.CLI.Parser import Data.Foldable qualified as F @@ -107,6 +108,7 @@ pStakePoolRegistrationCertificateCmd envCli = do Cmd.StakePoolRegistrationCertificateCmdArgs (convert useEra) <$> pStakePoolVerificationKeyOrFile Nothing <*> pVrfVerificationKeyOrFile + <*> pBlsSigningKeyFileForEra <*> pPoolPledge <*> pPoolCost <*> pPoolMargin @@ -124,6 +126,16 @@ pStakePoolRegistrationCertificateCmd envCli = do ) $ Opt.progDesc "Create a stake pool registration certificate" +-- | A pool registers its voting key from Dijkstra onwards, so the BLS signing +-- key is mandatory there and not offered at all in earlier eras. +pBlsSigningKeyFileForEra + :: forall era + . IsEra era + => Parser (Maybe (SigningKeyFile In)) +pBlsSigningKeyFileForEra = case useEra @era of + ConwayEra -> pure Nothing + DijkstraEra -> Just <$> pBlsSigningKeyFile + pStakePoolDeregistrationCertificateCmd :: IsEra era => Maybe (Parser (Cmd.StakePoolCmds era)) pStakePoolDeregistrationCertificateCmd = do diff --git a/cardano-cli/src/Cardano/CLI/EraBased/StakePool/Run.hs b/cardano-cli/src/Cardano/CLI/EraBased/StakePool/Run.hs index a5a5ee4e5e..298880fb1f 100644 --- a/cardano-cli/src/Cardano/CLI/EraBased/StakePool/Run.hs +++ b/cardano-cli/src/Cardano/CLI/EraBased/StakePool/Run.hs @@ -43,7 +43,7 @@ import Cardano.CLI.Type.Error.HashCmdError (FetchURLError (..)) import Cardano.CLI.Type.Error.StakePoolCmdError import Cardano.CLI.Type.Key (readVerificationKeyOrFile) -import Control.Monad (when) +import Control.Monad (forM, when) import Data.ByteString.Char8 qualified as BS import Data.ByteString.Lazy qualified as LBS import Data.Function ((&)) @@ -77,6 +77,7 @@ runStakePoolRegistrationCertificateCmd { era , poolVerificationKeyOrFile , vrfVerificationKeyOrFile + , blsSkeyFile , poolPledge , poolCost , poolMargin @@ -114,10 +115,19 @@ runStakePoolRegistrationCertificateCmd ownerStakeVerificationKeyOrFiles let stakePoolOwners' = map verificationKeyHash sPoolOwnerVkeys + -- BLS voting key, registered from Dijkstra onwards + mBlsKey <- + forM blsSkeyFile $ \skeyFile -> do + blsSkey <- + fromEitherIOCli @(FileError TextEnvelopeError) $ + readFileTextEnvelope @(SigningKey BlsKey) skeyFile + pure $ createBlsKeyRegistration blsSkey + let stakePoolParams = StakePoolParameters { stakePoolId = stakePoolId' , stakePoolVRF = vrfKeyHash' + , stakePoolBlsKey = mBlsKey , stakePoolCost = poolCost , stakePoolMargin = poolMargin , stakePoolRewardAccount = rewardAccountAddr diff --git a/cardano-cli/src/Cardano/CLI/EraIndependent/Node/Option.hs b/cardano-cli/src/Cardano/CLI/EraIndependent/Node/Option.hs index ae8be2d3f5..551fb25c64 100644 --- a/cardano-cli/src/Cardano/CLI/EraIndependent/Node/Option.hs +++ b/cardano-cli/src/Cardano/CLI/EraIndependent/Node/Option.hs @@ -7,6 +7,7 @@ module Cardano.CLI.EraIndependent.Node.Option ( pNodeCmds + , pBlsSigningKeyFile ) where