Register SPO as a candidate in the block producer committee
Prerequisites
-
Operate a Cardano SPO and have the following Cardano keys handy:
- The SPO
cold.skey. - A valid UTXO with associated
payment.vkeyandpayment.skey.
NEW: Cardano extended BIP32-Ed25519 are now supported!
- The SPO
-
Partner-chain-dependencies are fully synced (Step 2):
- Port to
OgmiosandPostgresmust be accessible.
- Port to
It's easier to perform the registration on the same instance as the partner-chain-dependencies. Especially if you're using the provided compose files within this guide.
1. Install midnight-node.
Midnight-node contains the Partner-chain CLI wizards that make interaction with the Midnight Partner-chain contracts, such as submitting a block producer candidacy, much easier.
Midnight-node is only available as a Docker image at the current stage of the Midnight project.
- Clone the
Midnight-node-dockerrepo (if applicable):
If you already cloned the midnight-node-docker repo in (Step 2), then you can simply skip cloning and use that instance.
git clone git@github.com:midnightntwrk/midnight-node-docker.git
cd midnight-node-docker
Upon entering the directory, direnv prompts you to allow the .envrc file.
- Using
midnight-node wizards:
Invoking the Partner-chains CLI wizards requires a file in current directory called, pc-chain-config.json. This file is located in the midnight-node-docker repository in envs/testnet-02/pc-chain-config.json. If you are using the midnight-node.sh script, then it will be automatically mounted from envs/testnet-02/pc-chain-config.json.
- Invoke the Partner-chain
wizardswith./midnight-node:
./midnight-node.sh wizards --help
Example output
Partner Chains text "wizards" for setting up chain
Usage: midnight-node wizards <COMMAND>
Commands:
generate-keys This wizard generates the keys required for operating a partner-chains node,
stores them in the keystore directory, and prints the public keys and keystore
location
prepare-configuration Wizard to obtain the configuration needed for the partner-chain governance
authority. This configuration should be shared with chain participants and used
to create the chain spec json file
create-chain-spec Wizard for creating a chain spec json file based on the chain configuration (see
`prepare-configuration`)
setup-main-chain-state Wizard for setting D-parameter and Permissioned Candidates list on the main
chain. Uses 'chain config' obtained after running `prepare-configuration`
start-node Wizard for starting a substrate node in the environment set up by
`generate-keys`, `prepare-config`, and `create-chain-spec`. It also assits in
setting the `resources configuration`
register1 The first step of registering as a committee candidate. Registration is split
into three steps to allow the user to use their cold keys on a cold machine
register2 The second step of registering as a committee candidate, using cold keys
register3 The final step of registering as a committee candidate, not using cold keys
deregister Deregister from the candidates set. This command requires chain config file
present in the running directory
help Print this message or the help of the given subcommand(s)
Options:
-h, --help
Print help (see a summary with '-h')
╔════════════════════════════════════════════════════════════════════════════════╗
║ Command groups by role ║
╠════════════════════════════════════════════════════════════════════════════════╣
║ The following sections outline the typical sequence of commands for each role. ║
║ The numbering indicates the recommended order of execution. Please note that ║
║ this order may vary depending on specific deployment scenarios. ║
╟────────────────────────────────────────────────────────────────────────────────╢
║ Governance Authority: ║
║ 1. generate-keys : generate necessary cryptographic keys ║
║ 2. prepare-configuration : set up the partner chain configuration ║
║ 3. create-chain-spec : create the chain specification file ║
║ 4. setup-main-chain-state: configure the main chain parameters ║
║ 5. start-node : start the validator node ║
╟────────────────────────────────────────────────────────────────────────────────╢
║ Registered Validator: ║
║ 1. generate-keys : generate validator keys ║
║ 2. register1 : initiate the registration process ║
║ 3. register2 : complete registration with cold keys ║
║ 4. register3 : finalize registration ║
║ 5. start-node : start the validator node ║
║ 6. deregister : cancel registration ║
║ ║
║ Note: This sequence assumes that the chain-spec.json and ║
║ pc-chain-config.json files have been obtained from ║
║ the Governance Authority and are present in the working directory. ║
╟────────────────────────────────────────────────────────────────────────────────╢
║ Permissioned Validator: ║
║ 1. generate-keys : generate validator keys ║
║ 2. start-node : start the validator node ║
║ ║
║ Note: After executing 'generate-keys', the generated keys must be shared ║
║ with the Governance Authority. The 'start-node' command can only be ║
║ executed after the Governance Authority has established the partner ║
║ chain on the main network. This sequence assumes that the ║
║ chain-spec.json and pc-chain-config.json files have ║
║ been obtained from the Governance Authority and are present in the ║
║ working directory. ║
╚════════════════════════════════════════════════════════════════════════════════╝
2. Generate Partner-chain keys
For this sequence we'll use the midnight-shell.sh script and invoke the wizards commands within the container.
- Enter
midnight-nodeshell:
./midnight-shell.sh
This will mount the ./data directory and put you in the midnight-node container where you can run midnight-node commands.
For example:
$ ./midnight-shell.sh
root@dcb8af95f166:/#
In the above example, dcb8af95f166 is the midnight-node's container ID. You can enter the container with the midnight-shell.sh script or more traditionally enter the container using docker commands:
docker container list -la # list the midnight-node container and it's "container ID"
docker exec -it <container-id> /bin/bash # enter the container's shell by ID
docker exec -it dcb /bin/bash # for example enter container "dcb8af95f166"
- Generate Partner-chain keys
The following sequence is performed within the midnight-node container. Make sure you're running ./midnight-node in the container.
- Invoke
./midnight-node wizards generate-keysand follow the prompts:
Example output (generate-keys)
./midnight-node wizards generate-keys
This 🧙 wizard will generate the following keys and save them to your node's keystore:
→ an ECDSA Cross-chain key
→ an ED25519 Grandpa key
→ an SR25519 Aura key
It will also generate a network key for your node if needed.
> node base path ./data
⚙️ Generating Cross-chain (ecdsa) key
running external command: /midnight-node key generate --scheme ecdsa --output-type json
💾 Inserting Cross-chain (ecdsa) key
running external command: /midnight-node key insert --base-path ./data --scheme ecdsa --key-type crch --suri 'legend remain sting admit kidney language sun student reject robust depth umbrella'
💾 Cross-chain key stored at ./data/chains/partner_chains_template/keystore/637263680x0378769bcea3d597048bb874b3671ed7bfccc86f5aa92beeb615e5f2621602c592
⚙️ Generating Grandpa (ed25519) key
running external command: /midnight-node key generate --scheme ed25519 --output-type json
💾 Inserting Grandpa (ed25519) key
running external command: /midnight-node key insert --base-path ./data --scheme ed25519 --key-type gran --suri 'stairs lend book century actor course intact sell popular hero drastic humor'
💾 Grandpa key stored at ./data/chains/partner_chains_template/keystore/6772616e0x67b3161c75d23fbbbc29dae8b937bf0a57bf5c7c9ebdef9be4d297812ac86de8
⚙️ Generating Aura (sr25519) key
running external command: /midnight-node key generate --scheme sr25519 --output-type json
💾 Inserting Aura (sr25519) key
running external command: /midnight-node key insert --base-path ./data --scheme sr25519 --key-type aura --suri 'unknown benefit exclude odor accident ribbon pond reduce rebuild rally prepare core'
💾 Aura key stored at ./data/chains/partner_chains_template/keystore/617572610x149b37993c43c2d70b09c7c7af29b36a9809f2ee076d01c624693f05e3768634
🔑 The following public keys were generated and saved to the partner-chains-public-keys.json file:
{
"sidechain_pub_key": "0x0378769bcea3d597048bb874b3671ed7bfccc86f5aa92beeb615e5f2621602c592",
"aura_pub_key": "0x149b37993c43c2d70b09c7c7af29b36a9809f2ee076d01c624693f05e3768634",
"grandpa_pub_key": "0x67b3161c75d23fbbbc29dae8b937bf0a57bf5c7c9ebdef9be4d297812ac86de8"
}
You may share them with your chain governance authority
if you wish to be included as a permissioned candidate.
⚙️ Generating network key
running external command: /midnight-node key generate-node-key --base-path ./data
command output: Generating key in "./data/chains/undeployed/network/secret_ed25519"
command output: 12D3KooWBAZC9CdRDPWB4trSfLJYDqqUVoZ3xFDcb2tF3M7vjL9F
🚀 All done!
The container will now have a data/ directory structure like so:
data/
└── chains
└── undeployed
├── keystore
│ ├── 6175728281...# sidechain key
│ ├── 63726367cd...# aura key
│ └── 677a322ca6...# grandpa key
└── network
└── secret_ed25519 # network (node) key
This data/ directory will be used for the Midnight-node in (Step 4).
- We need to move the keys from the generic partner chains directory to a keystore our node can find.
mv -R ./data/chains/undeployed/ ./data/chains/partner_chains_template
The file structure should now look like this:
data/
└── chains
└── partner_chains_template
├── keystore
│ ├── 6175728281...# sidechain key
│ ├── 63726367cd...# aura key
│ └── 677a322ca6...# grandpa key
└── network
└── secret_ed25519 # network (node) key
3. Make Cardano-keys accessible to container
The registration will be performed in the midnight-node container as done with the previous generate-keys step. However, now is a good time to ensure required Cardano keys are accessible to the container.
For example, let's assume your keys are in a cardano-keys directory on the host machine. This can be copied to the container using the docker cp command.
docker cp /path/to/cardano-keys/ <container-ID>:cardano-keys
This will place the cardano-keys directory and it's contents in the root directory of the midnight-node container.
For example:
$ docker cp cardano-keys/ cb5:cardano-keys/
Successfully copied 49.2kB to cb5:cardano-keys/
$ docker exec -it cb5 /bin/bash
root@cb52f4691afd:/# ls
artifacts-amd64 data lib mnt res srv var
bin dev lib64 opt root sys version
boot etc media pc-chain-config.json run tmp
cardano-keys home midnight-node proc sbin usr
To remove this directory from the container:
docker exec <container-ID> rm -r cardano-keys/
3. Register as a candidate
Registration is a 3 command process. You will have to:
- Initiate the registration process,
- prepare regsitration signature,
- and finally submit the registration.
This sequence is streamlined with the ./midnight-node wizards register1, register2 and register3 commands.
Again, perform this sequence in the container.
- Invoke
./midnight-node wizards register1and follow the prompts:
Example output (register1)
./midnight-node wizards register1
⚙️ Registering as a committee candidate (step 1/3)
This wizard will query your UTXOs using address derived from the payment verification key and Ogmios service
> Ogmios protocol (http/https) http
> Ogmios hostname cardano-ogmios
> Ogmios port 1337
> path to the payment verification file priv/wallet/mywallet/payment.vkey
⚙️ Querying UTXOs of addr_test1vz8jvjmn3zph7uj87nu6hdzkte7pwahjk4r864hug459ncsanyj7c from Ogmios at http://cardano-ogmios:1337...
> Select UTXO to use for registration d7ebd0b5f215ede15d25fc00c5e9fbfb24b36ffdf428f93fc83cde84440be376#0 (2212383 lovelace)
Please do not spend this UTXO, it needs to be consumed by the registration transaction.
Run the following command to generate signatures on the next step. It has to be executed on the machine with your SPO cold signing key.
/midnight-node wizards register2 \
--genesis-utxo 46876a2250ec0e523eccc30b0fc6d6fa55c61dd200b83140acaab291edeb0b11#0 \
--registration-utxo d7ebd0b5f215ede15d25fc00c5e9fbfb24b36ffdf428f93fc83cde84440be376#0 \
--aura-pub-key 0x0a66c6e1ca29d332a3b1738471bb4f27d4425b3001bbc9003757c2213b40b508 \
--grandpa-pub-key 0xca364cd1c9a63988d9da8adc5e336220ee93fc6b9d65bca280133a55e21dc5f1 \
--sidechain-pub-key 0x03de3d4544b1789d22aae9b4ae24d9e85282a300f690f7c0a534434f7f62362153 \
--sidechain-signature d03d7900805e9aeb465278ca2f67465521d8c2abc680a5650e85105f407825df75de19df6cc45bac92b66250ccd2b51e3d93922925aa7e09f3b0a3e53bf7763b
Common Errors
- Error: "⚠️ No UTXOs found for the given address"
- Cause:
- The address derived from
payment.vkeyhas no unspent transaction outputs (UTXOs), meaning it hasn’t been funded with testnet Ada (tAda). - The Cardano node might not be fully synced, so it can’t retrieve the latest balance.
- The address derived from
- Solution:
- Fund the Address: Send tAda to your payment address using a faucet (e.g., Cardano testnet faucet) or another funded wallet.
- Check Node Sync: Verify the node is fully synced by running:
Ensure the
./cardano-cli.sh query tipsyncProgressis100%. If not, wait for it to sync.
- Cause:
The register1 command will return the register2 command for you. You simply need to copy and paste this in the shell.
- Copy and paste the
register2command generated fromregister1:
Example output (register2)
/midnight-node wizards register2 \
--genesis-utxo 46876a2250ec0e523eccc30b0fc6d6fa55c61dd200b83140acaab291edeb0b11#0 \
--registration-utxo d7ebd0b5f215ede15d25fc00c5e9fbfb24b36ffdf428f93fc83cde84440be376#0 \
--aura-pub-key 0x0a66c6e1ca29d332a3b1738471bb4f27d4425b3001bbc9003757c2213b40b508 \
--grandpa-pub-key 0xca364cd1c9a63988d9da8adc5e336220ee93fc6b9d65bca280133a55e21dc5f1 \
--sidechain-pub-key 0x03de3d4544b1789d22aae9b4ae24d9e85282a300f690f7c0a534434f7f62362153 \
--sidechain-signature d03d7900805e9aeb465278ca2f67465521d8c2abc680a5650e85105f407825df75de19df6cc45bac92b66250ccd2b51e3d93922925aa7e09f3b0a3e53bf7763b
⚙️ Register as a committee candidate (step 2/3)
This command will use SPO cold signing key for signing the registration message.
> Path to mainchain signing key file priv/pool/WINTERGREENPOOL/cold.skey
To finish the registration process, run the following command on the machine with the partner chain dependencies running:
/midnight-node wizards register3 \
--genesis-utxo 46876a2250ec0e523eccc30b0fc6d6fa55c61dd200b83140acaab291edeb0b11#0 \
--registration-utxo d7ebd0b5f215ede15d25fc00c5e9fbfb24b36ffdf428f93fc83cde84440be376#0 \
--aura-pub-key 0x0a66c6e1ca29d332a3b1738471bb4f27d4425b3001bbc9003757c2213b40b508 \
--grandpa-pub-key 0xca364cd1c9a63988d9da8adc5e336220ee93fc6b9d65bca280133a55e21dc5f1 \
--partner-chain-pub-key 0x03de3d4544b1789d22aae9b4ae24d9e85282a300f690f7c0a534434f7f62362153 \
--partner-chain-signature d03d7900805e9aeb465278ca2f67465521d8c2abc680a5650e85105f407825df75de19df6cc45bac92b66250ccd2b51e3d93922925aa7e09f3b0a3e53bf7763b \
--spo-public-key 7ca8511e4b96c6d443304753685ea89660212e556a2c3c903fb10c99df292a72 \
--spo-signature 019ec2205223ba0f47a26f3e732e2c00e623eb1af546339a36c8136a792d37c6f2d36e76efa03a401e0be6559488d94e09368257f19886be95f8496f56460803
The register2 command will return the register3 command. Simply copy and paste this into shell.
- Copy and paste the register3 command and follow the prompts to submit registration:
Example output (register3)
root@cb52f4691afd:/# /midnight-node wizards register3 \
--genesis-utxo 46876a2250ec0e523eccc30b0fc6d6fa55c61dd200b83140acaab291edeb0b11#0 \
--registration-utxo d7ebd0b5f215ede15d25fc00c5e9fbfb24b36ffdf428f93fc83cde84440be376#0 \
--aura-pub-key 0x0a66c6e1ca29d332a3b1738471bb4f27d4425b3001bbc9003757c2213b40b508 \
--grandpa-pub-key 0xca364cd1c9a63988d9da8adc5e336220ee93fc6b9d65bca280133a55e21dc5f1 \
--partner-chain-pub-key 0x03de3d4544b1789d22aae9b4ae24d9e85282a300f690f7c0a534434f7f62362153 \
--partner-chain-signature d03d7900805e9aeb465278ca2f67465521d8c2abc680a5650e85105f407825df75de19df6cc45bac92b66250ccd2b51e3d93922925aa7e09f3b0a3e53bf7763b \
--spo-public-key 7ca8511e4b96c6d443304753685ea89660212e556a2c3c903fb10c99df292a72 \
--spo-signature 019ec2205223ba0f47a26f3e732e2c00e623eb1af546339a36c8136a792d37c6f2d36e76efa03a401e0be6559488d94e09368257f19886be95f8496f56460803
⚙️ Register as a committee candidate (step 3/3)
This command will submit the registration message to the mainchain.
To proceed with the next command, a payment signing key is required. Please note that this key will not be stored or communicated over the network.
> Path to mainchain payment signing key file priv/wallet/mywallet/payment.skey
Partner Chains Smart Contracts require access to Ogmios. Please provide its configuration.
> Ogmios protocol (http/https) http
> Ogmios hostname cardano-ogmios
> Ogmios port 1337
2025-05-20T03:48:25.486387217+00:00 INFO partner_chains_cardano_offchain::register - ✅ Transaction submitted. ID: ed829260c23f079a96dac6e6f4c48bf187066826c9d812fb87c6c9a9b7a832e7
2025-05-20T03:48:25.486403409+00:00 INFO partner_chains_cardano_offchain::await_tx - Probing for transaction output 'ed829260c23f079a96dac6e6f4c48bf187066826c9d812fb87c6c9a9b7a832e7#0'
2025-05-20T03:48:30.492834563+00:00 INFO partner_chains_cardano_offchain::await_tx - Probing for transaction output 'ed829260c23f079a96dac6e6f4c48bf187066826c9d812fb87c6c9a9b7a832e7#0'
2025-05-20T03:48:35.497173351+00:00 INFO partner_chains_cardano_offchain::await_tx - Probing for transaction output 'ed829260c23f079a96dac6e6f4c48bf187066826c9d812fb87c6c9a9b7a832e7#0'
2025-05-20T03:48:40.501445961+00:00 INFO partner_chains_cardano_offchain::await_tx - Probing for transaction output 'ed829260c23f079a96dac6e6f4c48bf187066826c9d812fb87c6c9a9b7a832e7#0'
2025-05-20T03:48:40.504456587+00:00 INFO partner_chains_cardano_offchain::await_tx - Transaction output 'ed829260c23f079a96dac6e6f4c48bf187066826c9d812fb87c6c9a9b7a832e7'
> Show registration status? Yes
The registration status will be queried from a db-sync instance for which a valid connection string is required. Please note that this db-sync instance needs to be up and synced with the main chain.
> DB-Sync Postgres connection string postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@$POSTGRES_HOST:$POSTGRES_PORT/$POSTGRES_DB
Registrations status for epoch 940:
There's a known issue with register3 querying the Postgres database, but don't worry - the registration was successful and can be validated by querying the committee in the next step.
- After registering query the current epoch:
curl -L -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"method": "sidechain_getStatus",
"params": [INSERT_EPOCH],
"id": 1
}' https://rpc.testnet-02.midnight.network | jq
Example output:
curl -L -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"method": "sidechain_getStatus",
"params": [],
"id": 1
}' https://rpc.testnet-02.midnight.network | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 297 100 193 100 104 395 213 --:--:-- --:--:-- --:--:-- 608
{
"jsonrpc": "2.0",
"result": {
"sidechain": {
"epoch": 239830,
"slot": 287797102,
"nextEpochTimestamp": 1726783200000
},
"mainchain": {
"epoch": 695,
"slot": 60126536,
"nextEpochTimestamp": 1726790400000
}
},
"id": 1
}
- Query validator committee for
n+ 2 epochs.
curl -L -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"method": "sidechain_getAriadneParameters",
"params": [697],
"id": 1
}' https://rpc.testnet-02.midnight.network | jq
Look for your registration under "candidateRegistrations": {). Ensure you see your Partner-chain keys in the registration and "isValid": true
🥳 At this point you are now registered as a candidate in the validator committee! Now, it's time to run a midnight-node in validator mode to produce Midnight blocks.
Deregister
Deregistration can be performed by invoking midnight-node wizards deregister. This should be performed in the container with access to keys used in registration.
- Enter the midnight-node container
- Invoke
midnight-node wizards deregisterand follow the prompts.
For example:
root@cb52f4691afd:/# ./midnight-node wizards deregister
This wizard will remove the specified candidate from the committee candidates based on the following chain parameters:
{
"genesis_utxo": "46876a2250ec0e523eccc30b0fc6d6fa55c61dd200b83140acaab291edeb0b11#0"
}.
Committee Candidate Validator Address is 'addr_test1wrdcz9xr2qmywev3rhegnyt0jvud45l2xk2lpkt29wwch2g9vngtm'
Payment signing key and cold verification key used for registration are required to deregister.
> path to the payment signing key file priv/wallet/mywallet/payment.skey
> path to the cold verification key file priv/pool/WINTERGREENPOOL/cold.vkey
Partner Chains Smart Contracts require access to Ogmios. Please provide its configuration.
> Ogmios protocol (http/https) http
> Ogmios hostname cardano-ogmios
> Ogmios port 1337
2025-05-20T02:22:08.482040253+00:00 INFO partner_chains_cardano_offchain::register - ✅ Transaction submitted. ID: d7ebd0b5f215ede15d25fc00c5e9fbfb24b36ffdf428f93fc83cde84440be376
2025-05-20T02:22:08.482055547+00:00 INFO partner_chains_cardano_offchain::await_tx - Probing for transaction output 'd7ebd0b5f215ede15d25fc00c5e9fbfb24b36ffdf428f93fc83cde84440be376#0'
2025-05-20T02:22:13.486015526+00:00 INFO partner_chains_cardano_offchain::await_tx - Probing for transaction output 'd7ebd0b5f215ede15d25fc00c5e9fbfb24b36ffdf428f93fc83cde84440be376#0'
2025-05-20T02:22:13.488982844+00:00 INFO partner_chains_cardano_offchain::await_tx - Transaction output 'd7ebd0b5f215ede15d25fc00c5e9fbfb24b36ffdf428f93fc83cde84440be376'
Deregistration will remove the candidate from the committee in n + 2 epochs.
- Query validator committee for
n+ 2 epochs since submitting deregistration:
curl -L -X POST -H "Content-Type: application/json" -d '{
"jsonrpc": "2.0",
"method": "sidechain_getAriadneParameters",
"params": [INSERT_EPOCH],
"id": 1
}' https://rpc.testnet-02.midnight.network | jq
You should see your candidate is no longer on the list.