Searchโ€ฆ
Pi-Core/Cold
Create operational keys & certificates. Create wallet & register stake pool
You need to have a Pi-Node configured with a new static ip address on your LAN. A fully qualified domain name and cardano-service file set to start on port 3000. You also need to update the env file used by gLiveView.sh located in $NODE_HOME/scripts.
You do not enable the topology updater service on a core node so feel free to delete those two scripts and remove the commented out cron job.
Make sure your core node is synced to the tip of the blockchain.
There exists a way to create your pool wallets payment keypair by creating a wallet in Yoroi and using cardano-wallet to extract the key pair from the mnemonic seed from Yoroi. This allows you to have a seed backup of the wallet and to also easily extract rewards or send funds elsewhere. You can do this with any shelley era mnemonic seed. I prefer Yoroi because it is quick.
Cardano-wallet will not build on arm due to dependency failure. @ZW3RK tried to build it for us and it would not. You may want to install cardano-wallet on an offline x86 machine and go through this process. That is how I did it. You can get cardano-wallet binary below.

Enable blockfetch tracing

1
sed -i ${NODE_FILES}/mainnet-config.json \
2
-e "s/TraceBlockFetchDecisions\": false/TraceBlockFetchDecisions\": true/g"
Copied!

Generate Keys & Issue Operational Certificate

Rotating the KES keys

KES keys need to be regenerated and a new pool.cert needs to be issued and submitted to the chain every 90 days. The node.counter file keeps track of how many times this has been done.
Generate a KES key pair: kes.vkey & kes.skey
Core
1
cd $NODE_HOME
2
cardano-cli node key-gen-KES \
3
--verification-key-file kes.vkey \
4
--signing-key-file kes.skey
Copied!
Generate a node cold key pair: node.vkey, node.skey and node.counter file on your Cold Offline machine.
Cold Offline
1
mkdir $HOME/cold-keys
2
cd cold-keys
3
cardano-cli node key-gen \
4
--cold-verification-key-file node.vkey \
5
--cold-signing-key-file node.skey \
6
--operational-certificate-issue-counter node.counter
Copied!
Create variables with the number of slots per KES period from the genesis file and current tip of the chain.
Core
1
slotsPerKesPeriod=$(cat $NODE_FILES/mainnet-shelley-genesis.json | jq -r '.slotsPerKESPeriod')
2
slotNo=$(cardano-cli query tip --mainnet | jq -r '.slot')
3
echo slotsPerKesPeriod: ${slotsPerKesPeriod}
4
echo slotNo: ${slotNo}
Copied!
Set the startKesPeriod variable by dividing slotNo / slotsPerKESPeriod.
Core
1
startKesPeriod=$((${slotNo} / ${slotsPerKesPeriod}))
2
echo startKesPeriod: ${startKesPeriod}
Copied!
Write startKesPeriod value down & copy the kes.vkey to your cold offline machine.
Issue a node.cert certificate using: kes.vkey, node.skey, node.counter and startKesPeriod value.
Replace <startKesPeriod> with the value you wrote down.
Cold Offline
1
cardano-cli node issue-op-cert \
2
--kes-verification-key-file kes.vkey \
3
--cold-signing-key-file $HOME/cold-keys/node.skey \
4
--operational-certificate-issue-counter $HOME/cold-keys/node.counter
5
--kes-period <startKesPeriod> \
6
--out-file node.cert
Copied!
Copy node.cert to your Core machine.
Generate a VRF key pair.
Core
1
cardano-cli node key-gen-VRF \
2
--verification-key-file vrf.vkey \
3
--signing-key-file vrf.skey
Copied!
For security purposes the vrf.skey needs read only permissions or cardano-node will not start.
Core
1
chmod 400 vrf.skey
Copied!
Edit the cardano-service startup script by adding kes.skey, vrf.skey and node.cert to the cardano-node run command and changing the port it listens on.
Core
1
nano $HOME/.local/bin/cardano-service
Copied!
Core
1
#!/bin/bash
2
DIRECTORY=/home/ada/pi-pool
3
FILES=/home/ada/pi-pool/files
4
PORT=3000
5
HOSTADDR=0.0.0.0
6
TOPOLOGY=${FILES}/mainnet-topology.json
7
DB_PATH=${DIRECTORY}/db
8
SOCKET_PATH=${DIRECTORY}/db/socket
9
CONFIG=${FILES}/mainnet-config.json
10
KES=${DIRECTORY}/kes.skey
11
VRF=${DIRECTORY}/vrf.skey
12
CERT=${DIRECTORY}/node.cert
13
## +RTS -N4 -RTS = Multicore(4)
14
cardano-node run +RTS -N4 -RTS \
15
--topology ${TOPOLOGY} \
16
--database-path ${DB_PATH} \
17
--socket-path ${SOCKET_PATH} \
18
--host-addr ${HOSTADDR} \
19
--port ${PORT} \
20
--config ${CONFIG} \
21
--shelley-kes-key ${KES} \
22
--shelley-vrf-key ${VRF} \
23
--shelley-operational-certificate ${CERT}
Copied!
Add your relay(s) to mainnet-topolgy.json.
Core
1
nano $NODE_FILES/mainnet-topology.json
Copied!
Use your LAN IPv4 for addr field if you are not using domain DNS. Be sure to have proper records set with your registrar or DNS service. Below are some examples.
Valency greater than one is only used with DNS round robin srv records.
1 Relay DNS
2 Relays DNS
1 Relay IPv4
2 Relays IPv4
1
{
2
"Producers": [
3
{
4
"addr": "r1.example.com",
5
"port": 3001,
6
"valency": 1
7
}
8
]
9
}
Copied!
1
{
2
"Producers": [
3
{
4
"addr": "r1.example.com",
5
"port": 3001,
6
"valency": 1
7
},
8
{
9
"addr": "r2.example.com",
10
"port": 3002,
11
"valency": 1
12
}
13
]
14
}
Copied!
1
{
2
"Producers": [
3
{
4
"addr": "192.168.1.151",
5
"port": 3001,
6
"valency": 1
7
}
8
]
9
}
Copied!
1
{
2
"Producers": [
3
{
4
"addr": "192.168.1.151",
5
"port": 3001,
6
"valency": 1
7
},
8
{
9
"addr": "192.168.1.152",
10
"port": 3002,
11
"valency": 1
12
}
13
]
14
}
Copied!
Restart and your node is now running as a core.
Core
1
cardano-service restart
Copied!

Create the pool wallet payment & staking key pairs

Cold offlline machine. Take the time to visualize the operations here.
    1.
    Generate a wallet key pair named payment. = payment.vkey & payment.skey
    2.
    Generate staking key pair. = stake.vkey & stake.skey
    3.
    Build a stake address from the newly created stake.vkey. = stake.addr
    4.
    Build a wallet address from the payment.vkey & delegate with stake.vkey. = payment.addr
    5.
    Add funds to the wallet by sending ada to payment.addr
    6.
    Check balance.

1. Generate wallet key pair

Cold Offline
1
cd $NODE_HOME
2
cardano-cli address key-gen \
3
--verification-key-file payment.vkey \
4
--signing-key-file payment.skey
Copied!

2. Generate staking key pair

Cold Offline
1
cardano-cli stake-address key-gen \
2
--verification-key-file stake.vkey \
3
--signing-key-file stake.skey
Copied!

3. Build stake address

Cold Offline
1
cardano-cli stake-address build \
2
--stake-verification-key-file stake.vkey \
3
--out-file stake.addr \
4
--mainnet
Copied!

4. Build payment address

Cold Offline
1
cardano-cli address build \
2
--payment-verification-key-file payment.vkey \
3
--stake-verification-key-file stake.vkey \
4
--out-file payment.addr \
5
--mainnet
Copied!

5. Fund wallet

1
cat payment.addr
Copied!
Copy payment.addr to a thumb drive and move it to the core nodes pi-pool folder.
Add funds to the wallet. This is the only wallet the pool uses so your pledge goes here as well. There is a 2 ada staking registration fee and a 500 ada pool registration deposit that you can get back when retiring your pool.
Test the wallet by sending a small amount waiting a few minutes and querying it's balance.
Core node needs to be synced to the tip of the blockchain.

6. Check balance

Core
1
cardano-cli query utxo \
2
--address $(cat payment.addr) \
3
--mainnet
Copied!

Register stake address

Issue a staking registration certificate: stake.cert
Cold Offline
1
cardano-cli stake-address registration-certificate \
2
--stake-verification-key-file stake.vkey \
3
--out-file stake.cert
Copied!
Copy stake.cert to your core node's pi-pool folder.
Query current slot number or tip of the chain.
Core
1
slotNo=$(cardano-cli query tip --mainnet | jq -r '.slot')
2
echo slotNo: ${slotNo}
Copied!
Get the utxo or balance of the wallet.
Core
1
cardano-cli query utxo \
2
--address $(cat payment.addr) \
3
--mainnet > fullUtxo.out
4
โ€‹
5
tail -n +3 fullUtxo.out | sort -k3 -nr > balance.out
6
cat balance.out
7
tx_in=""
8
total_balance=0
9
โ€‹
10
while read -r utxo; do
11
in_addr=$(awk '{ print $1 }' <<< "${utxo}")
12
idx=$(awk '{ print $2 }' <<< "${utxo}")
13
utxo_balance=$(awk '{ print $3 }' <<< "${utxo}")
14
total_balance=$((${total_balance}+${utxo_balance}))
15
echo TxHash: ${in_addr}#${idx}
16
echo Lovelace: ${utxo_balance}
17
tx_in="${tx_in} --tx-in ${in_addr}#${idx}"
18
done < balance.out
19
txcnt=$(cat balance.out | wc -l)
20
echo Total ADA balance: $((${total_balance} / 1000000))
21
echo Number of UTXOs: ${txcnt}
Copied!
If you get
cardano-cli: Network.Socket.connect: : does not exist (No such file or directory)
It is because the core has not finished syncing to the tip of the blockchain. This can take a long time after a reboot. If you look in the db/ folder after cardano-service stop you will see a file named 'clean'. That is confirmation file of a clean database shutdown. It usually takes 5 to 10 minutes to sync back to the tip of the chain on Raspberry Pi as of epoch 267.
If however the cardano-node does not shutdown 'cleanly' for whatever reason it can take up to an hour to verify the database(chain) and create the socket file. Socket file is created once your synced.
Query mainnet for protocol parameters.
1
cardano-cli query protocol-parameters \
2
--mainnet \
3
--out-file params.json
Copied!
Retrieve stakeAddressDeposit value from params.json.
Core
1
stakeAddressDeposit=$(cat $NODE_HOME/params.json | jq -r '.stakeAddressDeposit')
2
echo stakeAddressDeposit : ${stakeAddressDeposit}
Copied!
Stake address registration is 2,000,000 lovelace or 2 ada.
Take note of the invalid-hereafter input. We are taking the current slot number(tip of the chain) and adding 1,000 slots. If we do not issue the signed transaction before the chain reaches this slot number the tx will be invalidated. A slot is one second so you have 16.666666667 minutes to get this done. ๐ŸŒ
Build tx.tmp file to hold some information.
Core
1
cardano-cli transaction build-raw \
2
${tx_in} \
3
--tx-out $(cat payment.addr)+0 \
4
--invalid-hereafter $(( ${slotNo} + 1000)) \
5
--fee 0 \
6
--certificate stake.cert \
7
--out-file tx.tmp
Copied!
Calculate the minimal fee.
Core
1
fee=$(cardano-cli transaction calculate-min-fee \
2
--tx-body-file tx.tmp \
3
--tx-in-count ${txcnt} \
4
--tx-out-count 1 \
5
--mainnet \
6
--witness-count 2 \
7
--byron-witness-count 0 \
8
--protocol-params-file params.json | awk '{ print $1 }')
9
echo fee: $fee
Copied!
Calculate txOut.
Core
1
txOut=$((${total_balance}-${stakeAddressDeposit}-${fee}))
2
echo Change Output: ${txOut}
Copied!
Build the full transaction to register your staking address.
Core
1
cardano-cli transaction build-raw \
2
${tx_in} \
3
--tx-out $(cat payment.addr)+${txOut} \
4
--invalid-hereafter $(( ${slotNo} + 10000)) \
5
--fee ${fee} \
6
--certificate-file stake.cert \
7
--out-file tx.raw
Copied!
Transfer tx.raw to your Cold offline machine and sign the transaction with the payment.skey and stake.skey.
Cold Offline
1
cardano-cli transaction sign \
2
--tx-body-file tx.raw \
3
--signing-key-file payment.skey \
4
--signing-key-file stake.skey \
5
--mainnet \
6
--out-file tx.signed
Copied!
Move tx.signed transaction file back to the core nodes pi-pool folder.
Submit the transaction to the blockchain.
Core
1
cardano-cli transaction submit \
2
--tx-file tx.signed \
3
--mainnet
Copied!

Register the pool ๐ŸŠ

Create a poolMetaData.json file. It will contain important information about your pool. You will need to host this file somewhere online forevermore. It must be online and you cannot edit it without resubmitting/updating your pool.cert. In the next couple steps we will hash
metadata-url must be less than 64 characters.
GitHub Pages
GitHub Pages
Hosting your poolMetaData.json on github is popular choice
I say host it on your Pi with NGINX.
Core
1
cd $NODE_HOME
2
nano poolMetaData.json
Copied!
The extendedPoolMetaData.json file is used by adapools and others to scrape information like where to find your pool logo and social media links. Unlike the poolMetaData.json this files hash is not stored in your registration certificate and can be edited without having to rehash and resubmit pool.cert.
Add the following and customize to your metadata.
Core
1
{
2
"name": "Pool Name",
3
"description": "Pool description, no longer than 255 characters.",
4
"ticker": "AARCH",
5
"homepage": "https://example.com/",
6
"extended": "https://example.com/extendedPoolMetaData.json"
7
}
Copied!
Core
1
cardano-cli stake-pool metadata-hash \
2
--pool-metadata-file poolMetaData.json > poolMetaDataHash.txt
Copied!
Copy poolMetaData.json to https://pages.github.io or host it yourself along with your website. Be careful not to accidentally insert a space or a new line, which would result in a different hash.
Here is my poolMetaData.json & extendedPoolMetaData.json as a reference and shameless links back to my site. ๐Ÿ˜ฐ
Core
1
minPoolCost=$(cat $NODE_HOME/params.json | jq -r .minPoolCost)
2
echo minPoolCost: ${minPoolCost}
Copied!
Use the format below to register single or multiple relays.
DNS Relay(1)
IPv4 Relay(1)
DNS Relay(2)
IPv4 Relay(2)
1
--single-host-pool-relay <r1.example.com> \
2
--pool-relay-port <R1 NODE PORT> \
Copied!
1
--pool-relay-ipv4 <RELAY NODE PUBLIC IP> \
2
--pool-relay-port <R1 NODE PORT> \
Copied!
1
--single-host-pool-relay <r1.example.com> \
2
--pool-relay-port <R1 NODE PORT> \
3
--single-host-pool-relay <r2.example.com> \
4
--pool-relay-port <R2 NODE PORT> \
Copied!
1
--pool-relay-ipv4 <R1 NODE PUBLIC IP> \
2
--pool-relay-port <R1 NODE PORT> \
3
--pool-relay-ipv4 <R2 NODE PUBLIC IP> \
4
--pool-relay-port <R2 NODE PORT> \
Copied!
Edit the information below to match your pools desired configuration.
Copy vrf.vkey and poolMetaDataHash.txt to your cold machine and issue a stake pool registration certificate.
Cold Offline
1
cardano-cli stake-pool registration-certificate \
2
--cold-verification-key-file $HOME/cold-keys/node.vkey \
3
--vrf-verification-key-file vrf.vkey \
4
--pool-pledge 10000000000 \
5
--pool-cost 340000000 \
6
--pool-margin 0.01 \
7
--pool-reward-account-verification-key-file stake.vkey \
8
--pool-owner-stake-verification-key-file stake.vkey \
9
--mainnet \
10
--single-host-pool-relay <r1.example.com> \
11
--pool-relay-port 3001 \
12
--metadata-url <https://example.com/poolMetaData.json> \
13
--metadata-hash $(cat poolMetaDataHash.txt) \
14
--out-file pool.cert
Copied!
Issue a delegation certificate from stake.skey & node.vkey.
Cold Offline
1
cardano-cli stake-address delegation-certificate \
2
--stake-verification-key-file stake.vkey \
3
--cold-verification-key-file $HOME/cold-keys/node.vkey \
4
--out-file deleg.cert
Copied!
Retrieve your stake pool id.
Cold Offline
1
cardano-cli stake-pool id --cold-verification-key-file $HOME/cold-keys/node.vkey --output-format hex > stakePoolId.txt
2
cat stakePoolId.txt
Copied!
Move pool.cert, deleg.cert & stakePoolId.txt to your online core machine.
Query the current slot number or tip of the chain.
Core
1
slotNo=$(cardano-cli query tip --mainnet | jq -r '.slot')
2
echo slotNo: ${slotNo}
Copied!
Get the utxo or balance of the wallet.
Core
1
cardano-cli query utxo \
2
--address $(cat payment.addr) \
3
--mainnet > fullUtxo.out
4
โ€‹
5
tail -n +3 fullUtxo.out | sort -k3 -nr > balance.out
6
cat balance.out
7
tx_in=""
8
total_balance=0
9
โ€‹
10
while read -r utxo; do
11
in_addr=$(awk '{ print $1 }' <<< "${utxo}")
12
idx=$(awk '{ print $2 }' <<< "${utxo}")
13
utxo_balance=$(awk '{ print $3 }' <<< "${utxo}")
14
total_balance=$((${total_balance}+${utxo_balance}))
15
echo TxHash: ${in_addr}#${idx}
16
echo Lovelace: ${utxo_balance}
17
tx_in="${tx_in} --tx-in ${in_addr}#${idx}"
18
done < balance.out
19
txcnt=$(cat balance.out | wc -l)
20
echo Total ADA balance: $((${total_balance} / 1000000))
21
echo Number of UTXOs: ${txcnt}
Copied!
Parse params.json for stake pool registration deposit value. Spoiler: it's 500 ada but that could change in the future.
Core
1
stakePoolDeposit=$(cat $NODE_HOME/params.json | jq -r '.stakePoolDeposit')
2
echo stakePoolDeposit: ${stakePoolDeposit}
Copied!
Build temporary tx.tmp to hold information while we build our raw transaction file.
Core
1
cardano-cli transaction build-raw \
2
${tx_in} \
3
--tx-out $(cat payment.addr)+$(( ${total_balance} - ${stakePoolDeposit})) \
4
--invalid-hereafter $(( ${slotNo} + 10000)) \
5
--fee 0 \
6
--certificate-file pool.cert \
7
--certificate-file deleg.cert \
8
--out-file tx.tmp
Copied!
Calculate the transaction fee.
Core
1
fee=$(cardano-cli transaction calculate-min-fee \
2
--tx-body-file tx.tmp \
3
--tx-in-count ${txcnt} \
4
--tx-out-count 1 \
5
--mainnet \
6
--witness-count 3 \
7
--byron-witness-count 0 \
8
--protocol-params-file params.json | awk '{ print $1 }')
9
echo fee: ${fee}
Copied!
Calculate your change output.
Core
1
txOut=$((${total_balance}-${stakePoolDeposit}-${fee}))
2
echo txOut: ${txOut}
Copied!
Build your tx.raw (unsigned) transaction file.
Core
1
cardano-cli transaction build-raw \
2
${tx_in} \
3
--tx-out $(cat payment.addr)+${txOut} \
4
--invalid-hereafter $(( ${slotNo} + 10000)) \
5
--fee ${fee} \
6
--certificate-file pool.cert \
7
--certificate-file deleg.cert \
8
--out-file tx.raw
Copied!
Move tx.raw to your cold offline machine.
Sign the transaction with your payment.skey, node.skey & stake.skey.
Cold Offline
1
cardano-cli transaction sign \
2
--tx-body-file tx.raw \
3
--signing-key-file payment.skey \
4
--signing-key-file $HOME/cold-keys/node.skey \
5
--signing-key-file stake.skey \
6
--mainnet \
7
--out-file tx.signed
Copied!
Move tx.signed back to your core node & submit the transaction to the blockchain.
Core
1
cardano-cli transaction submit \
2
--tx-file tx.signed \
3
--mainnet
Copied!

Confirm successful registration

pool.vet

pool.vet is a website for pool operators to check the validity of their stake pools on chain data. You can check this site for problems and clues as to how to fix them.
pool.vet - Cardano stake pool checker

adapools.org

You should create an account and claim your pool here.
https://adapools.org/

pooltool.io

You should create an account and claim your pool here.
Cardano PoolTool

Backups

Get a couple small usb sticks and backup all your files and folders(except the db/ folder). Backup your online Core first then the Cold offline files and folders. Do it now, not worth the risk! Do not plug the USB stick into anything online after Cold files are on it!
https://twitter.com/insaladaPool/status/1380087586509709312?s=19
Last modified 1mo ago