Maintenance Guide for Cardano Stake Pool    
βœ… Updated for cardano-node 1.35.7
❀️ Big thanks to the excellent guide from CoinCashew, which part of this guide took reference from. I really learnt a lot from them.
To make it easy to follow and work smoothly, I had to work extremely hard to refine the details and test every single command. Any support is greatly appreciated πŸ™ Thanks to your supports, I can continue updating this guide.
❀️ Stake with my pool, ticker: STAY
❀️ Donate to my address: addr1qyxd2rpa3fwxpj739zcddwac5knke4s2casn0czfr32v5zf6g5qmpfv40fal70xtuqc4wx4h708h26l78eajgmhuvntqhkhr4z
Update cardano-node
πŸ“’ You can check for the lastest release here. Click the "Watch" button to be notified on new releases.
  • Download cardano-node source
mkdir -p $HOME/src
cd $HOME/src
git clone https://github.com/input-output-hk/cardano-node.git
  • Checkout with the lastest cardano-node release.
cd $HOME/src/cardano-node
git fetch --all --recurse-submodules --tags
git stash
git checkout tags/1.35.7
  • Configure Cabal to use the correct GHC version.
cabal configure --with-compiler=ghc-8.10.7
πŸ•š It may take a while.
  • Update the local project file to use the correct VRF library.
echo "package cardano-crypto-praos" >>  cabal.project.local
echo "  flags: -external-libsodium-vrf" >>  cabal.project.local
  • Clean and update Cabal
cabal clean
cabal update
πŸ•š It may take a while.
  • Built Cardano node with cabal
cabal build all
πŸ•š It may take up to a few hours.
⚠️ Wait for the build to finish to continue.
  • Check the versions of cardano-node and cardano-cli that's just been built.
echo ;\
$(find $HOME/src/cardano-node/dist-newstyle/build -type f -name "cardano-node") version ;\
echo ;\
$(find $HOME/src/cardano-node/dist-newstyle/build -type f -name "cardano-cli") version
The versions should be 1.35.7
  • Stop running cardano-node now
sudo systemctl stop cardano-node
  • Install the newly built cardano-node and cardano-cli
mkdir -p ~/.local/bin
cp -p "$(./scripts/bin-path.sh cardano-node)" ~/.local/bin/
cp -p "$(./scripts/bin-path.sh cardano-cli)" ~/.local/bin/
Note, we avoid using cabal install because that method prevents the installed binaries from reporting the git revision with the --version switch.
  • Check the version that has been installed
echo ;\
cardano-cli --version ;\
echo ;\
cardano-node version
The versions should be 1.35.7
  • Update mainnet-config.json
cd $NODE_HOME
wget https://raw.githubusercontent.com/input-output-hk/cardano-node/master/configuration/cardano/mainnet-config.json
# Change "TraceBlockFetchDecisions" to "true" to enable live view.
sed -i mainnet-config.json \
    -e "s/TraceBlockFetchDecisions\": false/TraceBlockFetchDecisions\": true/g"
# Change Prometheus address so Monitoring relay node can scrape data from cardano-node.
sed -i $NODE_HOME/mainnet-config.json -e "s/127.0.0.1/0.0.0.0/g"  
  • Start the node now
sudo systemctl start cardano-node
or just type startnode
πŸ“’ When the node starts the first time after updating, it may perform database processing.

Update protocol parameters

cardano-cli query protocol-parameters \
    --mainnet \
    --out-file $NODE_HOME/params.json

Copy cardano-cli to Airgapped Computer

πŸ’» On local computer
  • Download cardano-cli from any node to local computer
scp -r -P SSH_port -i /path/to/id_rsa username@ip_of_node:.local/bin/cardano-cli /path/to/Downloads/folder
πŸ’‘ Tip: Drag and drop a file into terminal to get its path.
Example:
scp -r -P 1234 -i /Users/Charles/RSA/id_rsa charles@12.34.56.78:.local/bin/cardano-cli /Users/Charles/Downloads
  • Save the customized command to a text file to use when you update cardano-cli later.
  • Copy cardano-cli to a USB stick (It's best to format the USB first)
πŸ”’ ON YOUR AIR-GAPPED COMPUTER
πŸ’‘ You may want to save this web page as a HTML file and copy it to your πŸ”’ Airgapped Computer so you can copy and paste the commands.
  • Copy cardano-cli from USB stick to "cardano" folder.
πŸ”’ ON YOUR AIR-GAPPED COMPUTER
  • Give execution permission
sudo chmod +x $HOME/cardano/cardano-cli
  • Check the version of cardano-cli
cardano-cli --version
The version should be 1.35.7
πŸ”· On Block-producing Node
  • (Optional) Check prequently-used parameters to see if their names are still the same.
This is optional because I will do it for you and update the whole guide every time there is a new cardano-node release.
echo ;\
echo ● stakePoolDeposit: $(cat $NODE_HOME/params.json | jq -r '.stakePoolDeposit') ;\
echo ● minPoolCost: $(cat $NODE_HOME/params.json | jq -r '.minPoolCost') ;\
echo ● stakeAddressDeposit: $(cat $NODE_HOME/params.json | jq -r '.stakeAddressDeposit') ;\
echo ● currentSlot: $(cardano-cli query tip --mainnet | jq -r '.slot') ;\
echo ● currentBlock: $(cardano-cli query tip --mainnet | jq -r '.block')
If any parameter returns null, that means its name has been changed. Check for the new name with these commands:
cat $NODE_HOME/params.json ;\
cardano-cli query tip --mainnet
Renew KES keys
πŸ“’ How to know when to renew KES keys
  • Option 1: Look at the "KES RENEWAL" panel on Grafana Dashboard.
  • Option 2: Check directly from cardano_node_metrics
πŸ”· On Block-producing Node
kes_remaining=$(curl -s localhost:12798/metrics | grep cardano_node_metrics_remainingKESPeriods_int | awk '{print $2}') ;\
slotsPerKESPeriod=$(cat $NODE_HOME/mainnet-shelley-genesis.json | jq -r '.slotsPerKESPeriod') ;\
echo ;\
echo ● Days left: $((${kes_remaining} * ${slotsPerKESPeriod} / 86400))
πŸ”· On Block-producing Node
  • Get the current pool-operation.cert counter number
πŸ”΄ This step is important because the counter number must increase after rotating KES keys. If not, your blocks will be invalid.
❀️ Thanks to CHRTY pool for this addition.
cardano-cli text-view decode-cbor --in-file $NODE_HOME/pool-operation.cert | grep int | head -1
Output should look like this
01  # int(0)
  • Take note of the int value, which is 0 in this example.

πŸ”΄ Important Housecleaning

πŸ”· On Block-producing Node
  • Rename old keys to prevent confusion with new keys
πŸ”΄ This step is important to make sure you don't mistakenly use your old KES keys. If you do, all your produced blocks will be invalid.
mv $NODE_HOME/kes.skey $NODE_HOME/kes-old-dont-use.skey ;\
mv $NODE_HOME/pool-operation.cert $NODE_HOME/pool-operation-old-dont-use.cert
  • Make sure the old keys have been renamed
cat $NODE_HOME/kes.skey ;\
cat $NODE_HOME/pool-operation.cert
Output should look like this
cat: /home/.../cardano/kes.skey: No such file or directory
cat: /home/.../cardano/pool-operation.cert: No such file or directory
πŸ”’ ON YOUR AIR-GAPPED COMPUTER
  • Rename old keys to prevent confusion with new keys
πŸ”΄ This step is important to make sure you don't mistakenly use your old KES keys. If you do, all your produced blocks will be invalid.
mv $HOME/cardano/cold-keys/cold-kes.vkey $HOME/cardano/cold-keys/cold-kes-old-dont-use.vkey ;\
mv $HOME/cardano/kes.skey $HOME/cardano/kes-old-dont-use.skey ;\
mv $HOME/cardano/pool-operation.cert $HOME/cardano/pool-operation-old-dont-use.cert
  • Make sure the old keys have been renamed
cat $HOME/cardano/kes.skey ;\
cat $HOME/cardano/cold-keys/cold-kes.vkey ;\
cat $HOME/cardano/pool-operation.cert
Output should look like this
cat: /home/.../cardano/kes.skey: No such file or directory
cat: /home/.../cardano/cold-keys/cold-kes.vkey: No such file or directory
cat: /home/.../cardano/pool-operation.cert: No such file or directory
πŸ”΄ Also delete all kes.skey and pool-operation.cert files on your Local Computer and USB stick.

Generate New Keys

πŸ”· On Block-producing Node
  • Get current KES period
slotsPerKESPeriod=$(cat $NODE_HOME/mainnet-shelley-genesis.json | jq -r '.slotsPerKESPeriod') ;\
slotNo=$(cardano-cli query tip --mainnet | jq -r '.slot') ;\
kesPeriod=$((${slotNo} / ${slotsPerKESPeriod})) ;\
echo ;\
echo ● slotsPerKESPeriod: ${slotsPerKESPeriod} ;\
echo ● slotNo: ${slotNo} ;\
echo ● kesPeriod: ${kesPeriod}
  • Take note of the kesPeriod
πŸ”’ On Air-gapped Computer
  • Create new KES key pair
cardano-cli node key-gen-KES \
  --verification-key-file $HOME/cardano/cold-keys/cold-kes.vkey \
  --signing-key-file $HOME/cardano/kes.skey
πŸ”’ On Air-gapped Computer
Generate New Operational Certificate
  • Replace kesPeriod with the value taken earlier.
cardano-cli node issue-op-cert \
    --kes-verification-key-file $HOME/cardano/cold-keys/cold-kes.vkey \
    --cold-signing-key-file $HOME/cardano/cold-keys/cold-pool.skey \
    --operational-certificate-issue-counter $HOME/cardano/cold-keys/cold-op-cert-issue.counter \
    --kes-period kesPeriod \
    --out-file $HOME/cardano/pool-operation.cert
πŸ”’ On Air-gapped Computer
  • Copy
  1. pool-operation.cert
  2. kes.skey
from cardano folder to your πŸ’» Local computer via a USB stick.
  • Upload them from πŸ’» Local computer to πŸ”· Block-producing Node
πŸ”· On Block-producing Node
  • Check if files has been uploaded
ls -lh $NODE_HOME | grep 'pool-operation.cert\|kes.skey'
Output should contains
  1. pool-operation.cert
  2. kes.skey
πŸ”΄ The modification date of the two files must be today (in your server's timezone).
-rw-------  1 1.3K Mar 25 23:07 kes.skey
-rwxrwxr-x  1  365 Mar 25 23:40 pool-operation.cert
πŸ”· On Block-producing Node
  • Get the current pool-operation.cert counter number
cardano-cli text-view decode-cbor --in-file $NODE_HOME/pool-operation.cert | grep int | head -1
Output should look like this
01  # int(1)
πŸ”΄ The int number must be higher than the number before rotating taken earlier.
πŸ”· On Block-producing Node
  • Check read permission of files
ls $NODE_HOME -l
Output should look like this
-rw------- ....
  • Make sure you have at least read permission for every file. If not, cardano-node may not be able to produce block.
  • Go to cardanoscan.io, search for your pool and take note of the Vrf Hash registered on blockchain.
πŸ”’ On Air-gapped Computer
  • Get the vrf hash of your vrf key.
cardano-cli node key-hash-VRF \
  --verification-key-file $HOME/cardano/cold-keys/cold-vrf.vkey
  • The two vrf hashes must be the same.
πŸ”· On Block-producing Node
  • Restart node for changes to take effect
sudo systemctl restart cardano-node
or just type restartnode
πŸ•š Wait a few minutes for cardano-node to start.
πŸ”· On Block-producing Node
  • Check KES days left again
kes_remaining=$(curl -s localhost:12798/metrics | grep cardano_node_metrics_remainingKESPeriods_int | awk '{print $2}') ;\
slotsPerKESPeriod=$(cat $NODE_HOME/mainnet-shelley-genesis.json | jq -r '.slotsPerKESPeriod') ;\
echo ;\
echo ● Days left: $((${kes_remaining} * ${slotsPerKESPeriod} / 86400))
Days left should be 93 days.
Add a New Relay
  • Follow all the steps in the CREATE page that relate to Relay node. After that, do the following additional steps:

Update firewall

πŸ”· On Block-producing Node
Allow your new Relay Node to access your Block-producing Node
  • Replace 1.2.3.4 with your new ✳️ Relay node's IPv4.
sudo ufw allow proto tcp from 1.2.3.4 to any port ${POOL_RELAY_PORT}
  • Check firewall status
sudo ufw status numbered
Output should contain this line
[ x] POOL_RELAY_PORT/tcp   ALLOW IN   NEW_RELAY_NODE_IP

Update Topology

πŸ”· On Block-producing Node
  • Edit topology
sudo nano $NODE_HOME/mainnet-topology.json
  • Add your new relay to the "Producers" list.
Your mainnet-topology.json may look like this:
{
    "Producers": [
        {
            "addr": "relay_1_IPv4",
            "port": ${POOL_RELAY_PORT},
            "valency": 1
        },
        {
            "addr": "relay_2_IPv4",
            "port": ${POOL_RELAY_PORT},
            "valency": 1
        }
    ]
}
  • Press Ctrl-X to exit.
  • Press Y then Enter/Return to confirm saving.
πŸ”· On Block-producing Node
  • Restart your node for changes to take effect.
sudo systemctl restart cardano-node
or just type restartnode

Update Prometheus

πŸ“Šβœ³οΈ On Monitoring Relay Node
  • Edit prometheus.yml
sudo nano /etc/prometheus/prometheus.yml
  • Add new targets for your new relay node.
The file may look like this:
global:
  scrape_interval: 10s
  external_labels:
    monitor: 'codelab-monitor'
scrape_configs:
  # Scrape data from cardano-node
  - job_name: 'cardano-node'
    static_configs:
      - targets: ['localhost:12798']
      - targets: ['$(cat $NODE_HOME/bp-node-ip.txt):12798']
      - targets: ['new_relay_ip:12798']
      # Add more relay nodes here if needed
  # Scrape data from prometheus-node-exporter
  - job_name: 'node-exporter'
    static_configs:
      - targets: ['localhost:9100']
      - targets: ['$(cat $NODE_HOME/bp-node-ip.txt):9100']
      - targets: ['new_relay_ip:9100']
      # Add more relay nodes here if needed
πŸ“Šβœ³οΈ On Monitoring Relay Node
  • Restart prometheus for changes to take effect
sudo systemctl restart prometheus
  • Check if the service is running
sudo systemctl status prometheus
Output should look like this
🟒 prometheus.service - Monitoring system and time series database
     Loaded: loaded (/lib/systemd/system/prometheus.service; enabled; vendor preset: enab>
     Active: active (running)
  • Press ctrl-C to exit

Resubmit Pool Registration If Necessary

Depending on the way you chose to register your relays, you may need to re-register your pool on the blockchain. Please refer to:
Change poolMetadata.json and get new hash
πŸ”· On Block-producing Node
πŸ”΄ Increase poolMetadata version count.
This version count will be attached to poolMetadata file name to create a new file every time you change pool metadata.
⚠️ If you only edit the old file and use the same URL for poolMetadata.json, the file content will be updated immediately while the registered hash may not be updated yet. That will cause a hash mismatch and may affect your pool's appearance on wallets and websites.
if [ ! -e $NODE_HOME/poolMetadata_VersionCount.txt ]
then
    echo "1" > $NODE_HOME/poolMetadata_VersionCount.txt
fi ;\
poolMetadataVersion=$(cat $NODE_HOME/poolMetadata_VersionCount.txt) ;\
poolMetadataVersion=$(($poolMetadataVersion + 1)) ;\
echo $poolMetadataVersion > $NODE_HOME/poolMetadata_VersionCount.txt ;\
echo ;\
echo ● Current Pool Metadata Version: $(tput bold)$poolMetadataVersion$(tput sgr0)
πŸ”· On Block-producing Node
  • Fill in the correct information to create poolMetadataVersionX.json
poolMetadataVersion=$(cat $NODE_HOME/poolMetadata_VersionCount.txt)
cat > $NODE_HOME/poolMetadataVersion${poolMetadataVersion}.json << EOF
{
"name": "Cool Pool name",
"description": "Here's why you should stake with us",
"ticker": "3-5 CHARACTERS",
"homepage": "https://www.examplepooldomain.com Just leave blank if don't use",
"extended": "https://link/to/poolExtendedMetadata.json"
}
EOF
echo ;\
echo ● File name: $(tput bold)poolMetadataVersion${poolMetadataVersion}.json$(tput sgr0) ;\
echo ● Content: ;\
cat $NODE_HOME/poolMetadataVersion${poolMetadataVersion}.json
πŸ’» On local computer
  • Download poolMetadataVersionX.json to your πŸ’» Local computer.
  • Upload poolMetadataVersionX.json to your website or Github and get its URL. The URL must be HTTPS and no longer than 64 characters.
⚠️ If you choose to upload file to GitHub
See how to upload file to Github.
Because GitHub may modify the file content to optimize it, which in turn may change the hash of the file, you need to download it from GitHub to get the correct hash.
πŸ”· On Block-producing Node
⚠️ Only do this if you choose to upload file to GitHub
  • Download poolMetadataVersionX.json from GitHub, replacing git.io/abcde with the actual link.
poolMetadataVersion=$(cat $NODE_HOME/poolMetadata_VersionCount.txt) ;\
wget -O $NODE_HOME/poolMetadataVersion${poolMetadataVersion}.json https://git.io/abcde ;\
echo ;\
echo ● File name: $(tput bold)poolMetadataVersion${poolMetadataVersion}.json$(tput sgr0) ;\
echo ● Content: ;\
cat $NODE_HOME/poolMetadataVersion${poolMetadataVersion}.json
πŸ”· On Block-producing Node
  • Get the hash of poolMetadataVersionX.json
poolMetadataVersion=$(cat $NODE_HOME/poolMetadata_VersionCount.txt) ;\
cardano-cli stake-pool metadata-hash \
  --pool-metadata-file $NODE_HOME/poolMetadataVersion${poolMetadataVersion}.json > $NODE_HOME/poolMetadataHash.txt ;\
echo ;\
echo ● Hash is: $(cat $NODE_HOME/poolMetadataHash.txt)
πŸ“’ The commands above will validate the correctness of your JSON file before hashing it.
πŸ’» On local computer
  • Download poolMetadataHash.txt to your πŸ’» Local computer.
  • Copy it to cardano folder on your πŸ”’ Air-gapped computer via a USB stick, replacing the old file.
  • Continue to the next section.
Update Pool Metadata, Pledge, Fee, Margin, Relays, Owner

Create New Pool Registration Certificate

πŸ”· On Block-producing Node
  • Rename pool-registration.cert to avoid confusion.
mv $NODE_HOME/pool-registration.cert $NODE_HOME/pool-registration-old.cert
πŸ”’ On Air-gapped Computer
  • Specify the right values for the first 6 options
mv $HOME/cardano/pool-registration.cert $HOME/cardano/pool-registration-old.cert ;\
cardano-cli stake-pool registration-certificate \
  --pool-pledge YOUR_PLEDGE_IN_LOVELACE \
  --pool-cost 340000000 \
  --pool-margin 0.01 \
  --single-host-pool-relay relays.examplepooldomain.com \
  --pool-relay-port 6000 \
  --metadata-url https://link/to/poolMetadata.json \
  \
  \
  --metadata-hash $(cat $HOME/cardano/poolMetadataHash.txt) \
  --cold-verification-key-file $HOME/cardano/cold-keys/cold-pool.vkey \
  --vrf-verification-key-file $HOME/cardano/cold-keys/cold-vrf.vkey \
  --pool-reward-account-verification-key-file $HOME/cardano/cold-keys/cold-stake.vkey \
  --pool-owner-stake-verification-key-file $HOME/cardano/cold-keys/cold-stake.vkey \
  --mainnet \
  --out-file $HOME/cardano/pool-registration.cert ;\
echo ;\
echo ● pool-registration.cert content: ;\
cat $HOME/cardano/pool-registration.cert
pool-registration.cert should look like this
type: CertificateShelley
description: Stake Pool Registration Certificate
cborHex:
885e22d5d63...
  • Copy pool-registration.cert to πŸ’» Local computer and upload it to πŸ”· Block-producing node.
πŸ”· On Block-producing Node
  • Get information for transaction. Copy all the command lines below and paste into terminal at the same time.
mv $NODE_HOME/tx.draft $NODE_HOME/tx-old.draft 2>/dev/null ;\
mv $NODE_HOME/tx.raw $NODE_HOME/tx-old.raw 2>/dev/null ;\
mv $NODE_HOME/tx.signed $NODE_HOME/tx-old.signed 2>/dev/null ;\
cd $NODE_HOME ;\
\
cardano-cli query utxo \
    --address $(cat $NODE_HOME/payment-with-stake.addr) \
    --mainnet > fullUtxo.out ;\
tail -n +3 fullUtxo.out | sort -k3 -nr > balance.out ;\
tx_in="" ;\
lovelace_total_balance=0 ;\
while read -r utxo; do
    in_addr=$(awk '{ print $1 }' <<< "${utxo}")
    idx=$(awk '{ print $2 }' <<< "${utxo}")
    utxo_balance=$(awk '{ print $3 }' <<< "${utxo}")
    lovelace_total_balance=$((${lovelace_total_balance}+${utxo_balance}))
    tx_in="${tx_in} --tx-in ${in_addr}#${idx}"
done < balance.out ;\
tx_in_count=$(cat balance.out | wc -l) ;\
currentSlot=$(cardano-cli query tip --mainnet | jq -r '.slot') ;\
invalidHereafter=$((${currentSlot} + 10000)) ;\
\
echo ;\
echo βœ… VERIFY THE INFORMATION BELOW: ;\
echo ● UTxOs List: ; \
cat balance.out ; \
echo ● Total Lovelace balance: ${lovelace_total_balance} ;\
echo ● Number of UTxOs: ${tx_in_count} ;\
echo ● Transaction Input: ${tx_in} ;\
echo ● Current Slot: $currentSlot ;\
echo ● Transaction Invalid Hereafter: $invalidHereafter ;\
\
rm fullUtxo.out ;\
rm balance.out
πŸ”· On Block-producing Node
  • Build transaction draft
cardano-cli transaction build-raw \
  ${tx_in} \
  --tx-out $(cat $NODE_HOME/payment-with-stake.addr)+${lovelace_total_balance}  \
  --invalid-hereafter ${invalidHereafter} \
  --fee 0 \
  --certificate-file $NODE_HOME/pool-registration.cert \
  --certificate-file $NODE_HOME/delegation.cert \
  --out-file $NODE_HOME/tx.draft
πŸ”· On Block-producing Node
  • Calculate fee & transaction output
fee=$(cardano-cli transaction calculate-min-fee \
  --tx-body-file $NODE_HOME/tx.draft \
  --tx-in-count ${tx_in_count} \
  --tx-out-count 1 \
  --witness-count 3 \
  --byron-witness-count 0 \
  --mainnet \
  --protocol-params-file $NODE_HOME/params.json | awk '{ print $1 }') ;\
tx_out_change=$(($lovelace_total_balance - $fee)) ;\
tx_out_with_fee="$(cat $NODE_HOME/payment-with-stake.addr)+${tx_out_change}" ;\
echo ;\
echo βœ… VERIFY THE INFORMATION BELOW: ;\
echo ● fee: $fee ;\
echo ● Transaction Output Change: ${tx_out_change} ;\
echo ● Transaction Output WITH Fee: ${tx_out_with_fee}
πŸ”· On Block-producing Node
  • Build raw transaction
cardano-cli transaction build-raw \
  ${tx_in} \
  --tx-out ${tx_out_with_fee}  \
  --invalid-hereafter ${invalidHereafter} \
  --fee ${fee} \
  --certificate-file $NODE_HOME/pool-registration.cert \
  --certificate-file $NODE_HOME/delegation.cert \
  --out-file $NODE_HOME/tx.raw
  • Download tx.raw to πŸ’» Local computer and copy to πŸ”’ Air-gapped computer via a USB stick.
πŸ”’ On Air-gapped Computer
  • Sign the transaction
mv $HOME/cardano/tx.signed $HOME/cardano/tx-old.signed ;\
cardano-cli transaction sign \
  --tx-body-file $HOME/cardano/tx.raw \
  --signing-key-file $HOME/cardano/cold-keys/cold-payment.skey \
  --signing-key-file $HOME/cardano/cold-keys/cold-pool.skey \
  --signing-key-file $HOME/cardano/cold-keys/cold-stake.skey \
  --mainnet \
  --out-file $HOME/cardano/tx.signed
  • Copy tx.signed to πŸ’» Local computer and upload it to πŸ”· Block-producing node.
πŸ”· On Block-producing Node
  • Submit transaction
cardano-cli transaction submit \
--tx-file $NODE_HOME/tx.signed \
--mainnet
πŸ”· On Block-producing Node
  • Check if transaction is confirmed by blockchain.
cardano-cli query utxo --address $(cat $NODE_HOME/payment-with-stake.addr) --mainnet ;\
echo ● Expected total balance: ${tx_out_change}
The balance of payment-with-stake.addr should equal to Expected total balance. If not, wait a few minute for the transaction to be included in a block then recheck.
After the transaction has been processed, go to pool.vet and enter you pool's ticker to check if everything's ok. It may take a while for pool.vet to update your information.
Withdraw Your rewards
πŸ“’ All rewards will be sent to stake.addr
πŸ”· On Block-producing Node
  • Check your reward
reward_lovelace=$(cardano-cli query stake-address-info \
    --mainnet \
    --address $(cat $NODE_HOME/stake.addr) | jq -r ".[0].rewardAccountBalance") ;\
echo ;\
echo ● Rewards in lovelace: $(tput bold)$reward_lovelace$(tput sgr0)
πŸ”· On Block-producing Node
  • Set the reward recipient address.
Use the commands below to send rewards to payment-with-stake.addr or change $(cat payment-with-stake.addr) to any address you want. Just make sure the address is correct.
reward_recipient_address=$(cat payment-with-stake.addr) ;\
echo ;\
echo ● Reward recipient address: $(tput bold)$reward_recipient_address$(tput sgr0)
πŸ”· On Block-producing Node
  • Get information for transaction. Copy all the command lines below and paste into terminal at the same time.
mv $NODE_HOME/tx.draft $NODE_HOME/tx-old.draft 2>/dev/null ;\
mv $NODE_HOME/tx.raw $NODE_HOME/tx-old.raw 2>/dev/null ;\
mv $NODE_HOME/tx.signed $NODE_HOME/tx-old.signed 2>/dev/null ;\
cd $NODE_HOME ;\
\
cardano-cli query utxo \
    --address $reward_recipient_address \
    --mainnet > fullUtxo.out ;\
tail -n +3 fullUtxo.out | sort -k3 -nr > balance.out ;\
tx_in="" ;\
lovelace_total_balance=0 ;\
while read -r utxo; do
    in_addr=$(awk '{ print $1 }' <<< "${utxo}")
    idx=$(awk '{ print $2 }' <<< "${utxo}")
    utxo_balance=$(awk '{ print $3 }' <<< "${utxo}")
    lovelace_total_balance=$((${lovelace_total_balance}+${utxo_balance}))
    tx_in="${tx_in} --tx-in ${in_addr}#${idx}"
done < balance.out ;\
tx_in_count=$(cat balance.out | wc -l) ;\
currentSlot=$(cardano-cli query tip --mainnet | jq -r '.slot') ;\
invalidHereafter=$((${currentSlot} + 10000)) ;\
withdrawal_input=$(cat $NODE_HOME/stake.addr)+${reward_lovelace} ;\
\
echo ;\
echo βœ… VERIFY THE INFORMATION BELOW: ;\
echo ● UTxOs List: ; \
cat balance.out ; \
echo ● Total Lovelace balance: ${lovelace_total_balance} ;\
echo ● Number of UTxOs: ${tx_in_count} ;\
echo ● Transaction Input: ${tx_in} ;\
echo ● Current Slot: $currentSlot ;\
echo ● Transaction Invalid Hereafter: $invalidHereafter ;\
echo ● Withdrawal Input: $withdrawal_input ;\
\
rm fullUtxo.out ;\
rm balance.out
πŸ”· On Block-producing Node
  • Build transaction draft
cardano-cli transaction build-raw \
  ${tx_in} \
  --tx-out ${reward_recipient_address}+0  \
  --invalid-hereafter ${invalidHereafter} \
  --fee 0 \
  --withdrawal ${withdrawal_input} \
  --out-file $NODE_HOME/tx.draft
πŸ”· On Block-producing Node
  • Calculate fee & transaction output
fee=$(cardano-cli transaction calculate-min-fee \
  --tx-body-file $NODE_HOME/tx.draft \
  --tx-in-count ${tx_in_count} \
  --tx-out-count 1 \
  --witness-count 2 \
  --byron-witness-count 0 \
  --mainnet \
  --protocol-params-file $NODE_HOME/params.json | awk '{ print $1 }') ;\
tx_out_change=$(($lovelace_total_balance - $fee + $reward_lovelace)) ;\
tx_out_with_fee="${reward_recipient_address}+${tx_out_change}" ;\
echo ;\
echo βœ… VERIFY THE INFORMATION BELOW: ;\
echo ● fee: $fee ;\
echo ● Transaction Output Change: ${tx_out_change} ;\
echo ● Transaction Output WITH Fee: ${tx_out_with_fee}
πŸ”· On Block-producing Node
  • Build raw transaction
cardano-cli transaction build-raw \
  ${tx_in} \
  --tx-out ${tx_out_with_fee} \
  --invalid-hereafter ${invalidHereafter} \
  --fee ${fee} \
  --withdrawal ${withdrawal_input} \
  --out-file $NODE_HOME/tx.raw
  • Download tx.raw to πŸ’» Local computer and copy to πŸ”’ Air-gapped computer via a USB stick.
πŸ”’ On Air-gapped Computer
  • Sign the transaction
mv $HOME/cardano/tx.signed $HOME/cardano/tx-old.signed ;\
cardano-cli transaction sign \
  --tx-body-file $HOME/cardano/tx.raw \
  --signing-key-file $HOME/cardano/cold-keys/cold-payment.skey \
  --signing-key-file $HOME/cardano/cold-keys/cold-stake.skey \
  --mainnet \
  --out-file $HOME/cardano/tx.signed
  • Copy tx.signed to πŸ’» Local computer and upload it to πŸ”· Block-producing node.
πŸ”· On Block-producing Node
  • Submit transaction
cardano-cli transaction submit \
--tx-file $NODE_HOME/tx.signed \
--mainnet
πŸ”· On Block-producing Node
  • Check if transaction is confirmed by blockchain.
cardano-cli query utxo --address ${reward_recipient_address} --mainnet ;\
echo ● Expected total balance: ${tx_out_change}
The balance of reward_recipient_address should equal to Expected total balance. If not, wait a few minute for the transaction to be included in a block then recheck.
Check Your Slot Leader Schedule
About 1.5 day before the end of the current epoch, you can check your slot leader schedule for the next epoch. This is useful to schedule your server maintainance.
⚠️ Your slot leader schedule is confidential. Do not expose it to the public. Bad actors may use that information to attack your pool.
❀️ Special thanks to Andrew Westberg (BCSH) for making this possible.
πŸ”· On Block-producing Node
  • Install cncli
CNCLI_RELEASE_TAG=$(curl -s https://api.github.com/repos/AndrewWestberg/cncli/releases/latest | jq -r .tag_name)
CNCLI_VERSION=$(echo ${CNCLI_RELEASE_TAG} | cut -c 2-)
curl -sLJ https://github.com/AndrewWestberg/cncli/releases/download/${CNCLI_RELEASE_TAG}/cncli-${CNCLI_VERSION}-x86_64-unknown-linux-gnu.tar.gz -o /tmp/cncli-${CNCLI_VERSION}-x86_64-unknown-linux-gnu.tar.gz
sudo tar xzvf /tmp/cncli-${CNCLI_VERSION}-x86_64-unknown-linux-gnu.tar.gz -C /usr/local/bin/
πŸ”· On Block-producing Node
  • Check if cncli is properly installed.
command -v cncli
It should return /usr/local/bin/cncli
πŸ”· On Block-producing Node
  • Sync database. Replacing 0.0.0.0 with the IP of πŸ”· Block-producing Node
/usr/local/bin/cncli sync --host 0.0.0.0 --port 6000 --no-service
Wait until the sync is 100% completed.
  • Take snapshot
POOL_ID=$(cat $NODE_HOME/pool-id.txt)
echo "POOL ID: $POOL_ID"
SNAPSHOT=$(cardano-cli query stake-snapshot --stake-pool-id $POOL_ID --mainnet)
πŸ”· On Block-producing Node
  • Get leader log for next epoch.
POOL_STAKE=$(jq .poolStakeMark <<< $SNAPSHOT)
ACTIVE_STAKE=$(jq .activeStakeMark <<< $SNAPSHOT)
/usr/local/bin/cncli leaderlog --pool-id $POOL_ID --pool-vrf-skey ${NODE_HOME}/vrf.skey --byron-genesis ${NODE_HOME}/mainnet-byron-genesis.json --shelley-genesis ${NODE_HOME}/mainnet-shelley-genesis.json --pool-stake $POOL_STAKE --active-stake $ACTIVE_STAKE --ledger-set next
  • Get leader log for current epoch.
POOL_STAKE=$(jq .poolStakeSet <<< $SNAPSHOT)
ACTIVE_STAKE=$(jq .activeStakeSet <<< $SNAPSHOT)
/usr/local/bin/cncli leaderlog --pool-id $POOL_ID --pool-vrf-skey ${NODE_HOME}/vrf.skey --byron-genesis ${NODE_HOME}/mainnet-byron-genesis.json --shelley-genesis ${NODE_HOME}/mainnet-shelley-genesis.json --pool-stake $POOL_STAKE --active-stake $ACTIVE_STAKE --ledger-set current
  • Get leader log for previous epoch.
POOL_STAKE=$(jq .poolStakeGo <<< $SNAPSHOT)
ACTIVE_STAKE=$(jq .activeStakeGo <<< $SNAPSHOT)
/usr/local/bin/cncli leaderlog --pool-id $POOL_ID --pool-vrf-skey ${NODE_HOME}/vrf.skey --byron-genesis ${NODE_HOME}/mainnet-byron-genesis.json --shelley-genesis ${NODE_HOME}/mainnet-shelley-genesis.json --pool-stake $POOL_STAKE --active-stake $ACTIVE_STAKE --ledger-set prev
❀️ Donate to my address: addr1qyxd2rpa3fwxpj739zcddwac5knke4s2casn0czfr32v5zf6g5qmpfv40fal70xtuqc4wx4h708h26l78eajgmhuvntqhkhr4z