Skip to main content
Validator key types are a consensus parameter, so allowing post-quantum validator keys is a chain-level change. This guide adds ml_dsa_65 to the accepted consensus key types: on a new chain through genesis, or on a live chain through a governance proposal, with no coordinated restart. For background on ML-DSA and the key types, see Post-quantum keys.
Before any validator rotates to an ML-DSA key, every counterparty chain that verifies this chain over IBC must run CometBFT v0.40 or later. See IBC considerations.

Prerequisites

  • The chain runs Cosmos SDK 0.55 and CometBFT 0.40 or later. See the release notes.
  • jq and curl. The commands derive the proposal payload with jq and read the validator set with curl.
  • The chain’s CLI binary on your PATH, with RPC access to a node. To stand up a local chain, see Run a node.
  • For a live chain: the ability to pass a governance proposal, and an account funded for the proposal deposit and gas.
Commands use simd; substitute your chain’s binary, and adjust key names and denoms to your own.

Check the current state

Consensus params list the allowed key types under validator.pub_key_types. Query them:
The default is ed25519 only. To see which types the validator set is currently running, list the validators and read each one’s pubkey type:
The output shows registered type names, one per validator: tendermint/PubKeyEd25519 for ed25519 keys, cometbft/PubKeyMlDsa65 for ML-DSA keys.

New chain: set the types in genesis

Add ml_dsa_65 to consensus.params.validator.pub_key_types in genesis.json before launch:
Keep ed25519 in the list unless every genesis validator starts on an ML-DSA key. Validators whose key type is not in the list cannot join the set. A genesis validator starts on an ML-DSA key by initializing its node with simd init <moniker> --consensus-key-algo ml_dsa_65, and a local ML-DSA testnet comes from the same flag on simd testnet init-files or simd testnet start.
simd init --consensus-key-algo replaces the genesis pub_key_types list with only the chosen algorithm. A chain that should accept both types must re-add ed25519 to the list after init, or ed25519 validators cannot join.

Live chain: expand the types through governance

The change is a parameter update executed by governance. It takes effect when the proposal passes, with no node restarts and no coordinated upgrade. All steps read and write params.json in the current directory, so run them from one place.
  1. Build the proposal params from the live chain state. The update message replaces the entire params object, so it must carry every current value. The following command derives the params from a query, adds ml_dsa_65 to pub_key_types, and converts the evidence duration to the format the proposal parser accepts:
  1. You can then submit the file’s contents as a governance proposal. The command takes the four param groups as separate arguments, sliced from the same file. Make sure to update the following command to include your key and correct deposit amount/denomination. Add --home pointing at your node’s home, since the transaction commands on this page read the keyring from it. Also add --chain-id and --keyring-backend if your client config does not supply them, and --fees (or --gas-prices) to meet the chain’s minimum gas price:
  1. Vote as with any governance proposal, using the proposal ID from simd query gov proposals.
When it passes, the new list is live.
The update replaces the entire params object, so every group must be present. If params.json is missing block, evidence, or validator, the jq -c substitution above emits null and the CLI rejects the command before it is submitted, with invalid argument "null": proto: syntax error. Always start from the queried current params and change only the key type list.

Verify

Query the params again and confirm the list includes ml_dsa_65:
New validators can now join with ML-DSA keys. Existing validators can migrate by rotation. See Key rotation.

Remove a key type

To remove a key type, update the params.json file to remove the key type from the pub_key_types array. Then, submit the updated params.json file as a governance proposal.
Do not remove a key type while validators still use it. Existing validators are not re-checked when a type leaves the list, but every later voting-power update for such a validator fails validation, and a failed validator update halts the chain. Routine activity is enough to trigger it: any delegation that changes the validator’s voting power emits one of these updates.

Next steps