Einen Cardano Stake Pool Aufsetzen

Diese Anleitung basiert auf dem Tutorial von Coincashew. Ich habe sie für dieses Tutorial übersetzt und leicht verändert, im Wesentlichen enspricht es jedoch der Coincashew Anleitung von Ende Mai 2021.

Installation

System vorbereiten

Als 1. werden einige Grundlagen installiert:

sudo apt-get install git jq bc make automake rsync htop curl build-essential pkg-config libffi-dev libgmp-dev libssl-dev libtinfo-dev libsystemd-dev zlib1g-dev make g++ wget libncursesw5 libtool autoconf -y

Libsodium installieren

Im 2. Schritt muss die Libsodium eingerichtet werden. Das ist eine kryptographie Bibliothek, welche einige Tools mitbringt:

mkdir $HOME/git
cd $HOME/git
git clone https://github.com/input-output-hk/libsodium
cd libsodium
git checkout 66f017f1
./autogen.sh
./configure
make
sudo make install

Für Debian basierte Systeme, wie das hier genutzte Ubuntu ist es möglicherweise erforderlich noch ein wenig zu verlinken:

sudo ln -s /usr/local/lib/libsodium.so.23.3.0 /usr/lib/libsodium.so.23

secp256k1 installieren

Ab node Version 1.35.0 brauchen wir die secp256k1 lib. Sonst bricht der Build Vorgang mit einer Fehlermeldung bezüglich cardano-crypto-class ab.

Es kommt zu folgender Meldung:

cabal: Could not resolve dependencies:
[__0] trying: cardano-crypto-class-2.0.0 (user goal)
[__1] rejecting: cardano-crypto-class:+secp256k1-support (conflict: pkg-config
package libsecp256k1-any, not found in the pkg-config database)
[__1] rejecting: cardano-crypto-class:-secp256k1-support (manual flag can only
be changed explicitly)
[__1] fail (backjumping, conflict set: cardano-crypto-class,
cardano-crypto-class:secp256k1-support)
After searching the rest of the dependency tree exhaustively, these were the
goals I've had most trouble fulfilling: cardano-crypto-class,
cardano-crypto-class:secp256k1-support

Um die fehlende Abhängigkeit zu lösen, brauchen wir die secp256k1, aus dem Bitcoin-Core Repository.

Um diese kompilieren zu können, werden wir unser Ubuntu zunächst mit einigen Abhängigkeiten aufrüsten:

sudo apt-get install nano llvm-12 numactl libnuma-dev autoconf automake libtool

Jetzt gehen wir in user bekanntes git-Verzeichnis und führen folgende Kommandos aus:

git clone https://github.com/bitcoin-core/secp256k1.git
cd secp256k1
git reset --hard ac83be33d0956faf6b7f61a60ab524ef7d6a473a
./autogen.sh
./configure --prefix=/usr --enable-module-schnorrsig --enable-experimental
make
make check
sudo make install
sudo reboot

Danach sollte die Cardano-Node auch in Version 1.35.0 und später kompilieren.

Cabal installieren

Als nächster Schritt werden wir Cabal installieren.

Wir beginnen mit den Abhängigkeiten, die sollten eigentlich schon installiert sein, aber sicher ist sicher:

sudo apt-get -y install pkg-config libgmp-dev libssl-dev libtinfo-dev libsystemd-dev zlib1g-dev build-essential curl libgmp-dev libffi-dev libncurses-dev libtinfo5

Im nächsten Schritt wird Cabal installiert.

Die Frage nach dem “haskell-language-server (HLS) beantworten wir mit NO.

Die Frage die PATH Variablen automatisch zum PATH in der .bashrc hinzuzufügen beantworten wir mit YES.

curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | sh

Die nachfolgenden Kommandos sollten bereits durch das Skript ausgeführt sein. Sicher ist sicher, wir führen sie ein weiteres mal aus, das schadet nicht:

cd $HOME
source .bashrc
ghcup upgrade
ghcup install cabal 3.4.0.0
ghcup set cabal 3.4.0.0

Nun installieren wir den GHC:

ghcup install ghc 8.10.7
ghcup set ghc 8.10.7

Jetzt updaten wir den PATH noch etwas weiter, damit wir nicht immer die Pfade referenzieren müssen:

echo PATH="$HOME/.local/bin:$PATH" >> $HOME/.bashrc
echo export LD_LIBRARY_PATH="/usr/local/lib:$LD_LIBRARY_PATH" >> $HOME/.bashrc
echo export NODE_HOME=$HOME/cardano-my-node >> $HOME/.bashrc
echo export NODE_CONFIG=mainnet>> $HOME/.bashrc
echo export NODE_BUILD_NUM=$(curl https://hydra.iohk.io/job/Cardano/iohk-nix/cardano-deployment/latest-finished/download/1/index.html | grep -e "build" | sed 's/.*build\/\([0-9]*\)\/download.*/\1/g') >> $HOME/.bashrc
source $HOME/.bashrc

Die Cardano Node bauen

Vorweg noch schnell alles auf den neuesten Stand bringen:

cabal update

Und schnell die Versionen abfragen:

cabal --version
ghc --version

Cabal muss 3.4.0.0 sein.

GHC sollte 8.10.7 sein.

Andernfalls muss sichergestellt werden, ob dieses Tutorial noch aktuell ist.

Um diese Anleitung im Testnet zu verwenden:

Einfach in den CLI Parametern

–mainnet

mit

–testnet-magic 1097911063

ersetzen.

Wir bauen eine Node

Schritt 1, herunterladen der Quellen und zum letzten TAG wechseln:

cd $HOME/git
git clone https://github.com/input-output-hk/cardano-node.git
cd cardano-node
git fetch --all --recurse-submodules --tags
git checkout $(curl -s https://api.github.com/repos/input-output-hk/cardano-node/releases/latest | jq -r .tag_name)

Schritt 2, konfigurieren der build Optionen. Also das, was bei C ./configure ist:

cabal configure -O0 -w ghc-8.10.7

Schritt 3, ein paar esoterische Konfigurationen:

echo -e "package cardano-crypto-praos\n flags: -external-libsodium-vrf" > cabal.project.local
sed -i $HOME/.cabal/config -e "s/overwrite-policy:/overwrite-policy: always/g"
rm -rf $HOME/git/cardano-node/dist-newstyle/build/x86_64-linux/ghc-8.10.7

Die 1. Zeile gibt an, dass die  libsodium von extern kommt.

Die 2. Zeile das die Override policy auf always gesetzt wird. Der Zweck ist es, das ein evtl. vorhandener alter Build überschrieben wird.

Die 3. Zeile löscht ein vorhandenes ghc aus dem build Ordner.

Schritt 4, jetzt werden wir die eigentliche Node bauen:

cabal build cardano-cli cardano-node

Das Kompilat verschieben wir in ein globales bin Verzeichnis:

sudo cp $(find $HOME/git/cardano-node/dist-newstyle/build -type f -name "cardano-cli") /usr/local/bin/cardano-cli
sudo cp $(find $HOME/git/cardano-node/dist-newstyle/build -type f -name "cardano-node") /usr/local/bin/cardano-node

Konfiguration der Nodes

Wir brauchen 5 json-Dateien zur Konfiguration der Node:

  • config.json
  • genesis.json (jeweils byron-genesis.json, shelley-genesis.json und alonzo-genesis.json)
  • topology.json

Achtung: Die hier heruntergeladenen config-Dateien sollten funktionieren, ob sie es wirklich tun, ist eine andere Frage. Weiter unten in diesem Tutorial gehe ich auf mögliche Probleme ein.

mkdir $NODE_HOME
cd $NODE_HOME
wget -N https://hydra.iohk.io/job/Cardano/cardano-node/cardano-deployment/latest-finished/download/1/${NODE_CONFIG}-config.json
wget -N https://hydra.iohk.io/job/Cardano/cardano-node/cardano-deployment/latest-finished/download/1/${NODE_CONFIG}-byron-genesis.json
wget -N https://hydra.iohk.io/job/Cardano/cardano-node/cardano-deployment/latest-finished/download/1/${NODE_CONFIG}-shelley-genesis.json
wget -N https://hydra.iohk.io/job/Cardano/cardano-node/cardano-deployment/latest-finished/download/1/${NODE_CONFIG}-alonzo-genesis.json
wget -N https://hydra.iohk.io/job/Cardano/cardano-node/cardano-deployment/latest-finished/download/1/${NODE_CONFIG}-topology.json

Wir legen jetzt eine mainnet-config.json an und updaten den Parameter TraceBlockFetchDecisions zu “true”:

sed -i ${NODE_CONFIG}-config.json \
-e "s/TraceBlockFetchDecisions\": false/TraceBlockFetchDecisions\": true/g"

Für die Relay Nodes interessant:
Der Speicherverbrauch einer Relay Node kann gesenkt werden, indem “TraceMemPool” auf “false” gesetzt wird.

Im nächsten Schritt updaten wir die .bashrc

echo export CARDANO_NODE_SOCKET_PATH="$NODE_HOME/db/socket" >> $HOME/.bashrc
source $HOME/.bashrc

Bis hierhin sind alle Konfigurationen gleich. Diesen Teil wiederholen wir auf allen Maschinen.

Konfiguration der block-producer node

Die block producer node benötigt diverse Schlüsselpaare für die Block Erzeugung.

  • cold keys
  • KES hot keys
  • VRF hot keys)

Die block producer node darf nur zu ihren relay nodes verbinden. Diese Node ist von der  Außenwelt abgeschirmt. Die Firewall muss entsprechend konfiguriert werden.

Die anderen Nodes, die Relay Nodes, besitzen keinerlei Schlüssel. Infolgedesssen können sie auch keine Blöcke erzeugen.

Die eigentliche Konfiguration

Zunächst erzeugen wir die Topologie:

cat > $NODE_HOME/${NODE_CONFIG}-topology.json << EOF 
{
    "Producers": [
    {
        "addr": "<RELAYNODE1'S PUBLIC IP ADDRESS>",
        "port": 6000,
        "valency": 1
    },
    {
        "addr": "<RELAYNODE2'S PUBLIC IP ADDRESS>", 
        "port": 6000, 
        "valency": 1 
    }
    ]
}
EOF

Bei mehreren Relays, fügen wir diese alle hinzu.

Konfiguration der relay node

cat > $NODE_HOME/${NODE_CONFIG}-topology.json << EOF 
 {
    "Producers": [
    {
        "addr": "<BLOCK PRODUCER NODE'S PUBLIC IP ADDRESS>",
        "port": 6000,
        "valency": 1
    },
    {
        "addr": "relays-new.cardano-mainnet.iohk.io",
        "port": 3001,
        "valency": 2
    }
    ]
 }
EOF

Konfiguration der offline Maschine

Wir verwenden ein vom Netz getrenntes Notebook als offline Maschine. Ziel dieser Maschine ist es die Schlüsselpaare des Block Producers zu erzeugen und uns vor diversen Bedrohungen zu schützen, indem diese Maschine physikalisch vom Netz getrennt ist.

Die offline Maschine hat folgende Eigenschaften:

  • Nicht mit dem Internet verbunden.
  • Physikalisch von jeder Internetverbindung abgekoppelt.
  • Keine Virtuelle Maschine auf einen Host der mit dem Internet verbunden ist.
  • Schutz vor Malware Bedrohungen
  • Schutz vor Sicherheitslücken

Bevor wir fortfahren nehmen wir die Grundeinrichtung vor. Das bedeutet, wir installieren die Abhängigkeiten.

Zunächst legen wir uns das “cardano-my-node” Verzeichnis an, fügen es zur .bashrc hinzu und fügen es direkt zum PATH hinzu.

echo export NODE_HOME=$HOME/cardano-my-node >> $HOME/.bashrc
source $HOME/.bashrc
mkdir -p $NODE_HOME

Jetzt die gebauten binaries der cardano-cli vom “hot environment”, dass ist der “block producer” kopieren.

Um die Regeln des “cold environment” korrekt einzuhalten verwenden wir dazu einen usb stick und kopieren die binaries über eine Drittmaschine, sofern wir keinen physischen Zugang zum Server haben.

Startskripte

Um uns das Leben zu erleichtern halten wir die Prozedur des Startens einer Node in Skripten fest.

Wir beginnen mit den Relay 1 (alternativ anpassen, für so viele Relays, wie wir haben):

cat > $NODE_HOME/startRelayNode1.sh << EOF 
#!/bin/bash
DIRECTORY=$NODE_HOME
PORT=6000
HOSTADDR=0.0.0.0
TOPOLOGY=\${DIRECTORY}/${NODE_CONFIG}-topology.json
DB_PATH=\${DIRECTORY}/db
SOCKET_PATH=\${DIRECTORY}/db/socket
CONFIG=\${DIRECTORY}/${NODE_CONFIG}-config.json
/usr/local/bin/cardano-node run +RTS -N -A16m -qg -qb -RTS --topology \${TOPOLOGY} --database-path \${DB_PATH} --socket-path \${SOCKET_PATH} --host-addr \${HOSTADDR} --port \${PORT} --config \${CONFIG}
EOF

Jetzt machen wir die angelegte Datei ausführbar:

chmod +x $NODE_HOME/startRelayNode1.sh

Im nächsten Schritt erstellen wir ein Skript für den Block Producer:

cat > $NODE_HOME/startBlockProducingNode.sh << EOF 
#!/bin/bash
DIRECTORY=$NODE_HOME
PORT=6000
HOSTADDR=0.0.0.0
TOPOLOGY=\${DIRECTORY}/${NODE_CONFIG}-topology.json
DB_PATH=\${DIRECTORY}/db
SOCKET_PATH=\${DIRECTORY}/db/socket
CONFIG=\${DIRECTORY}/${NODE_CONFIG}-config.json
/usr/local/bin/cardano-node run +RTS -N -A16m -qg -qb -RTS --topology \${TOPOLOGY} --database-path \${DB_PATH} --socket-path \${SOCKET_PATH} --host-addr \${HOSTADDR} --port \${PORT} --config \${CONFIG}
EOF

Auf den dieses Skript machen wir ausführbar:

chmod +x $NODE_HOME/startBlockProducingNode.sh

Systemd services für die Nodes erstellen

Systemd bietet uns diverse Vorteile in der Verwaltung der Nodes

  • Autostart der Nodes, wenn das System hochfährt oder durch Probleme zum Reboot gezwungen ist.
  • Automatischer Neustart, wenn die Node abstürzt.
  • Maximierte Performance der Nodes.

Im 1. Schritt beginnen wir wieder mit der Releay Node 1:

cat > $NODE_HOME/cardano-node.service << EOF 
# The Cardano node service (part of systemd) 
# file: /etc/systemd/system/cardano-node.service

[Unit] 
Description = Cardano node service 
Wants = network-online.target 
After = network-online.target

[Service] 
User = ${USER} 
Type = simple 
WorkingDirectory= ${NODE_HOME} 
ExecStart = /bin/bash -c '${NODE_HOME}/startRelayNode1.sh' 
KillSignal=SIGINT 
RestartKillSignal=SIGINT 
TimeoutStopSec=300 
LimitNOFILE=32768 
Restart=always 
RestartSec=5 
SyslogIdentifier=cardano-node

[Install] 
WantedBy = multi-user.target 
EOF

Wie zuvor auch, kann das obige für beliebig viele Relay Nodes angepasst werden.

Im 2. Schritt erstellen wir einen Service für die Block Producer Node:

cat > $NODE_HOME/cardano-node.service << EOF
# The Cardano node service (part of systemd)
# file: /etc/systemd/system/cardano-node.service

[Unit]
Description = Cardano node service
Wants = network-online.target
After = network-online.target

[Service]
User = ${USER}
Type = simple
WorkingDirectory= ${NODE_HOME}
ExecStart = /bin/bash -c '${NODE_HOME}/startBlockProducingNode.sh'
KillSignal=SIGINT
RestartKillSignal=SIGINT
TimeoutStopSec=300
LimitNOFILE=32768
Restart=always
RestartSec=5
SyslogIdentifier=cardano-node

[Install]
WantedBy = multi-user.target
EOF

Auf allen Systemen verschieben wir den unit file nun nach /etc/systemd/system und setzen die Zugriffsrechte:

sudo mv $NODE_HOME/cardano-node.service /etc/systemd/system/cardano-node.service
sudo chmod 644 /etc/systemd/system/cardano-node.service

Jetzt schalten wir den Autostart der Node Services ein:

sudo systemctl daemon-reload
sudo systemctl enable cardano-node

Nach dieser Maßnahme sind die Cardano Nodes von systemd verwaltet und profitieren von der automatischen Verwaltung durch diesen Dienst.

Systemd Kommandos

Den Status der Node einsehen:

sudo systemctl status cardano-node

Neustarten der Node:

sudo systemctl reload-or-restart cardano-node

Die Node stoppen:

sudo systemctl stop cardano-node

Anzeigen und filtern der Log-Files:

journalctl --unit=cardano-node --follow
journalctl --unit=cardano-node --since=yesterday
journalctl --unit=cardano-node --since=today
journalctl --unit=cardano-node --since='2020-07-29 00:00:00' --until='2020-07-29 12:00:00'

Starten der Nodes

Der Zeitpunkt die Nodes zu starten ist jetzt gekommen.

Folgendes Kommando wird auf beiden Maschinen ausgeführt:

sudo systemctl start cardano-node

Jetzt kann gLiveView eingerichtet werden. Die Einrichtung ist im Kapitel überwachen der Nodes erklärt.

Info:
Cardano ist noch in Entwicklung, es kann sein, dass die Synchronisierung der Blockchain abbricht. Dann müssen die Konfigurationsdateien eventuell gegen neuere Versionen ausgetauscht werden. Das sind:

  • mainnet-config.json
  • mainnet-byron-genesis.json
  • mainnet-shelley-genesis.json
  • mainnet-alonzo-genesis.json

Die Konfigurationsdateien sind hier aufbewahrt. Die Buildnummer muss angepasst werden, so das eine funktionierende Konfiguration gefunden wird. Meistens gibt es zu diesen Synchronisationsabbrüchen auch Forenbeiträge, von Menschen, die das Problem ebenfalls haben. Hier wäre beispielsweise so ein Fall.

Hier gibt es im Zweifel die letzten zutreffenden config-json-Dateien.

Die Block Producer Keys

Die Block Producer Node benötigt 3 Keys:

  • stake pool cold key (node.cert)

  • stake pool hot key (kes.skey)

  • stake pool VRF key (vrf.skey)

Auf dem Block Producer

Im 1. Schritt erzeugen wir die KES Keys auf der Block Producer Node:

cd $NODE_HOME
cardano-cli node key-gen-KES \
--verification-key-file kes.vkey \
--signing-key-file kes.skey

Die KES Keys müssen alle 90 Tage erneuert werden, wenn der Pool im Mainnet betrieben wird.

Auf der Offline Maschine

Die Cold-Keys müssen auf der Offline Maschine erzeugt und gespeichert werden.

Wir speichern diese Keys in einen Verzeichnis mit Namen Cold Keys:

mkdir $HOME/cold-keys
pushd $HOME/cold-keys

Jetzt werden die cold-keys erzeugt und ein counter file für die cold-keys:

cardano-cli node key-gen \
--cold-verification-key-file node.vkey \
--cold-signing-key-file node.skey \
--operational-certificate-issue-counter node.counter

Es ist extrem wichtig alle Keys auf einen weiteren Gerät zu sichern!

Auf dem Block Producer

Ab hier muss unsere Node voll synchronisiert sein. Herausfinden können wir das, indem wir auf https://pooltool.io/ (oder mein Favourit: https://cardanoscan.io/) die Epoche und slot Nummer mit der in gLiveView vergleichen.

Zunächst müssen wir die Anzahl der slots per KES Periode bestimmen:

pushd +1
slotsPerKESPeriod=$(cat $NODE_HOME/${NODE_CONFIG}-shelley-genesis.json | jq -r '.slotsPerKESPeriod')
echo slotsPerKESPeriod: ${slotsPerKESPeriod}

Das die Node voll synchronisiert st, finden wir heraus, wenn die Blocknummer und Slot Nummer in https://pooltool.io/ mit der von der Node übereinstimmt. Sonst werden wir nicht die korrekte KES-Periode herausfinden.

Zunächst die aktuelle Slot-Nr.:

slotNo=$(cardano-cli query tip --mainnet | jq -r '.slot')
echo slotNo: ${slotNo}

Den Start der aktuellen KES-Periode finden wir heraus, wenn wir die slotNr. durch die slotsPerKesPeriode teilen:

kesPeriod=$((${slotNo} / ${slotsPerKESPeriod}))
echo kesPeriod: ${kesPeriod}
startKesPeriod=${kesPeriod}
echo startKesPeriod: ${startKesPeriod}

Mit dieser Information können wir jetzt das operational Zertifikat für den Pool erzeugen.

Auf der Offline-Maschine

Dazu kopieren wird den key.vkey zum Cold-Environment.

Stake pool operators müssen ein “operational certificate” vorweisen, um nachweisen zu können, dass der Pool das Recht hat zu laufen.

Das Zertifikat beinhaltet:

  • Die operators signature
  • key information about the pool (addresses, keys, etc.)

Das “operational certificate” ist die Verbindung zwischen dem offline key des operators und seinem operational key.

Dieses Zertifikat erzeugen wir folgenden Kommando (<starKesPeriod> ersetzen wir durch den zuvor auf dem Block-Producer berechneten Wert):

cardano-cli node issue-op-cert \
--kes-verification-key-file kes.vkey \
--cold-signing-key-file $HOME/cold-keys/node.skey \
--operational-certificate-issue-counter $HOME/cold-keys/node.counter \
--kes-period <startKesPeriod> \
--out-file node.cert

Auf dem Block-Producer

Wir kopieren das node.cert auf den Block-Producer.

Alles, was wir hier machen, geschieht in $NODE_HOME.

Wir erzeugen jetzt das VRF-Key-Paar:

cardano-cli node key-gen-VRF \
--verification-key-file vrf.vkey \
--signing-key-file vrf.skey

Die richtigen Berechtigungen setzen:

chmod 400 vrf.skey

Jetzt stoppen wir den Stake-Pool:

sudo systemctl stop cardano-node

Wir updaten jetzt das Startup-Skript mit den neu erzeugten Keys:

cat > $NODE_HOME/startBlockProducingNode.sh << EOF 
DIRECTORY=$NODE_HOME
PORT=6000
HOSTADDR=0.0.0.0
TOPOLOGY=\${DIRECTORY}/${NODE_CONFIG}-topology.json
DB_PATH=\${DIRECTORY}/db
SOCKET_PATH=\${DIRECTORY}/db/socket
CONFIG=\${DIRECTORY}/${NODE_CONFIG}-config.json
KES=\${DIRECTORY}/kes.skey
VRF=\${DIRECTORY}/vrf.skey
CERT=\${DIRECTORY}/node.cert
cardano-node run --topology \${TOPOLOGY} --database-path \${DB_PATH} --socket-path \${SOCKET_PATH} --host-addr \${HOSTADDR} --port \${PORT} --config \${CONFIG} --shelley-kes-key \${KES} --shelley-vrf-key \${VRF} --shelley-operational-certificate \${CERT}
EOF

Immer dran denken, wir müssen die Keys alle 90 Tage erneuern!

Wir starten den Block-Producer jetzt neu:

sudo systemctl start cardano-node
Und Prüfen das Ergebnis in gLiveView:
./gLiveView.sh

Einrichten der Payment und Stake Keys

Payment Keys werden verwendet, um payments zu senden und empfangen und stake keys um stake delegations zu verwalten.

Zuerst beschaffen wir uns die Blockchain Parameter:

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

Es ist extrem wichtig, die payment und stake keys nur und ausschließlich auf der offline Maschine zu bewahren.

Sie werden verwendet um:

  • Transaktionen in einen Cold-Environment zu erzeugen.

Die einzigen Schritte, die im Hot-Environment durchgeführt werden sind:

  • Den momentanen Slot-Tip herausfinden.
  • Die balance einer Adresse herausfinden.
  • Eine Transaktion abschicken.

Das heißt, ausschließlich live data.

Es gibt jetzt 2 Methoden, um um die Schlüsselpaare für payments und stakes zu erzeugen. Eine CLI Methode und eine Mnemonic Methode.

Option 1: CLI-Methode – Auf der offline-Maschine

Das folgende geschieht komplett auf der offline-Maschine und im Verzeichnis $NODE_HOME.

Als 1. erzeugen wir die Schlüsselpaare payment.skey & payment.vkey:

###
### On air-gapped offline machine,
###
cd $NODE_HOME
cardano-cli address key-gen \
--verification-key-file payment.vkey \
--signing-key-file payment.skey

Als 2. erzeugen wir das stake Adressen Schlüsselpaar stake.skey & stake.vkey:

###
### On air-gapped offline machine,
###
cardano-cli stake-address key-gen \
--verification-key-file stake.vkey \
--signing-key-file stake.skey

Jetzt erzeugen wir die stake.addr aus dem stake address verification key stake.vkey:

###
### On air-gapped offline machine,
###
cardano-cli stake-address build \
--stake-verification-key-file stake.vkey \
--out-file stake.addr \
--mainnet

Zuletzt erzeugen wir die payment.addr für den payment.vkey

###
### On air-gapped offline machine,
###
cardano-cli address build \
--payment-verification-key-file payment.vkey \
--stake-verification-key-file stake.vkey \
--out-file payment.addr \
--mainnet

Von dieser Adresse werden die Funds zur stake-Adresse delegiert.

Option 2: Mnemonic Methode – Auf der offline-Maschine

<TODO>

Funding der Payment Adresse

1. wir kopieren die payment.addr zum Hot-Environment. Das heißt, nach $NODE_HOME.

Wir können jetzt Adas zur Payment Adresse senden, die Payment-Adresse bekommen wir mit folgenden Kommando heraus:

cat payment.addr

Nachdem wir Geld zur Adresse gesendet haben, müsen wir warten, bis alle unsere Nodes voll synchronisiert sind, dann können wir unsere Adas mit folgenden Kommando abfragen:

cardano-cli query utxo \
--address $(cat payment.addr) \
--mainnet

In der Ausgabe sehen wir die Lovelace, welche wir zu der Adresse gesand haben:

 TxHash TxIx Lovelace
----------------------------------------------------------------------------------------
100322a39d02c2ead.... 0 1000000000

Registrierung der Stake Adresse

1. Wir erstellen auf der Offline-Maschine ein stake.cert, mittels des stake.vkey:

# offline Maschine
cardano-cli stake-address registration-certificate \
--stake-verification-key-file stake.vkey \
--out-file stake.cert

Das stake.cert wird jetzt zur block producer node übertragen.

2. Es folgen jetzt die Schritte um die Registrierung der Stake Adresse durchzuführen, dies geschieht mittels einer Transaktion:

currentSlot=$(cardano-cli query tip --mainnet | jq -r '.slot')
echo Current Slot: $currentSlot

3 .Wir stellen fest, wie viele Transaktionen es bisher auf der payment-Adresse gab und wie die Total Balance gerade ist:

cardano-cli query utxo \
    --address $(cat payment.addr) \
    --mainnet > fullUtxo.out

tail -n +3 fullUtxo.out | sort -k3 -nr > balance.out

cat balance.out

tx_in=""
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}")
    total_balance=$((${total_balance}+${utxo_balance}))
    echo TxHash: ${in_addr}#${idx}
    echo ADA: ${utxo_balance}
    tx_in="${tx_in} --tx-in ${in_addr}#${idx}"
done < balance.out
txcnt=$(cat balance.out | wc -l)
echo Total ADA balance: ${total_balance}
echo Number of UTXOs: ${txcnt}

4. Wir holen uns aus der param.json die Kosten zur Registrierung einer stake adresse:

stakeAddressDeposit=$(cat $NODE_HOME/params.json | jq -r '.stakeAddressDeposit')
echo stakeAddressDeposit : $stakeAddressDeposit

Zum Zeitpunkt, wo diese Anleitung verfasst wurde betrugen die Kosten zur registrierung des stake adressen Zeritifikats 2000000 = 2 Ada.

5. Wir erzeugen jetzt eine raw transaction, diese ist unvollständig, enthält aber genügend informationen, um weiterarbeiten zu können:

cardano-cli transaction build-raw \
${tx_in} \
--tx-out $(cat payment.addr)+0 \
--invalid-hereafter $(( ${currentSlot} + 10000)) \
--fee 0 \
--out-file tx.tmp \
--certificate stake.cert

invalid-herafter gibt an, wann die Transaktion ungültig wird, sofern sie nicht abgeschickt wird. Diese Zahl muss höher als die aktuelle Slot Nummer sein, daher in diesem Beispiel +1000.

6. Auf dieser Grundlage berechnen wir die aktuelle minimum fee:

fee=$(cardano-cli transaction calculate-min-fee \
    --tx-body-file tx.tmp \
    --tx-in-count ${txcnt} \
    --tx-out-count 1 \
    --mainnet \
    --witness-count 2 \
    --byron-witness-count 0 \
    --protocol-params-file params.json | awk '{ print $1 }')
echo fee: $fee

Die fee sollte sich in einem Bereich > 150000 und < 200000 bewegen.

7. Nun haben wir alle Daten beisammen und als Variablen gespeichert, um zu berechnen wie viel wir nach der Transaktion auf dem Konto übrig haben. Das ist txOut:

txOut=$((${total_balance}-${stakeAddressDeposit}-${fee}))
echo Change Output: ${txOut}

8. Jetzt können wir endlich die Transaktion erstellen, um das stake.cert für den Deposit wert von 2 Ada zu registrieren. Dazu erstellen wir folgende Transaktion:

cardano-cli transaction build-raw \
${tx_in} \
--tx-out $(cat payment.addr)+${txOut} \
--invalid-hereafter $(( ${currentSlot} + 10000)) \
--fee ${fee} \
--certificate-file stake.cert \
--out-file tx.raw

Wir kopieren die neue Datei tx.raw zur Offline-Maschine!

9. Auf der Offline-Maschine signieren wir jetzt die Transaktion:

# Offline-Maschine
cardano-cli transaction sign \
--tx-body-file tx.raw \
--signing-key-file payment.skey \
--signing-key-file stake.skey \
--mainnet \
--out-file tx.signed

Wir kopieren die neue Datei tx.signed zur Block Producer Node!

10. Wir senden die signierte Transaktion:

# block producer node
cardano-cli transaction submit \
--tx-file tx.signed \
--mainnet

Registrierung des Stake Pools

Die Registrierung eines Pools kostet 500 ADA deposit. Diese “Kaution” bekommen wir zwar zurück, wenn wir den Pool deregistrieren, aber man sollte sich darüber dennoch im Voraus im klaren sein. Bis hierhin sollte auch wirklich alles wie gewünscht laufen und man sollte alles verstanden haben.

1. Wir beginnen damit die poolMetaData.json Datei zu erstellen:

# block producer node
cat > poolMetaData.json << EOF
{
"name": "MyPoolName",
"description": "My pool description",
"ticker": "MPN",
"homepage": "https://myadapoolnamerocks.com"
}
EOF

Der Ticker mus zwischen 3-5 Zeichen lang sein!

Die Description darf nicht länger als 255 Zeichen sein!

Der Pool Name muss nicht pool enthalten und kann auch getrennt geschrieben sein. Zum Beispiel Financial Refuge.

2. Für die poolMetaData.json erzeugen wir jetzt einen Hash-Wert:

cardano-cli stake-pool metadata-hash --pool-metadata-file poolMetaData.json > poolMetaDataHash.txt

3. Hochladen der poolMetaData.json:

Die poolMetaData.json mus nun zu einen Ort hochgeladen werden, wo sie über eine normale URL erreichbar ist. In der Regel wird dies die Pool Website sein. Beispiel: https://financialrefuge.com/poolMetaData.json.

4. Die minimalen Betriebskosten eines Pools herausfinden:

# block producer node
minPoolCost=$(cat $NODE_HOME/params.json | jq -r .minPoolCost)
echo minPoolCost: ${minPoolCost}

Zum Zeitpunkt, wo diese Anleitung geschrieben wurde waren das 340 Ada, also 340000000 Lovelace.

Auf diesem Wert müssen die Poolkosten minimal sein.

Jetzt muss ein Registrierungszertifikat erstellt werden.

Zur angabe der Relays gibt es 3 Wege:

  • DNS basiert
 --single-host-pool-relay relaynode1.myadapoolnamerocks.com\
--pool-relay-port 6000 \
--single-host-pool-relay relaynode2.myadapoolnamerocks.com\
--pool-relay-port 6000 \
  • IP basiert
--pool-relay-port 6000 \
--pool-relay-ipv4 <your first relay node public IP address> \
--pool-relay-port 6000 \
--pool-relay-ipv4 <your second relay node public IP address> \
  • DNS round robin basiert
--multi-host-pool-relay relayNodes.myadapoolnamerocks.com\
--pool-relay-port 6000 \

Das Zertifikat werden wir auf der offline Maschine erstellen!

5. Erstellen des Zertifikats:

# offline Maschine
cardano-cli stake-pool registration-certificate \
    --cold-verification-key-file $HOME/cold-keys/node.vkey \
    --vrf-verification-key-file vrf.vkey \
    --pool-pledge 100000000 \
    --pool-cost 345000000 \
    --pool-margin 0.15 \
    --pool-reward-account-verification-key-file stake.vkey \
    --pool-owner-stake-verification-key-file stake.vkey \
    --mainnet \
    --single-host-pool-relay <dns based relay, example ~ relaynode1.myadapoolnamerocks.com> \
    --pool-relay-port 6000 \
    --metadata-url <url where you uploaded poolMetaData.json> \
    --metadata-hash $(cat poolMetaDataHash.txt) \
    --out-file pool.cert

Die Metadaten URL muss angepasst werden und die Relay Node Informationen. Außerdem natürlich die Daten des Pools, d.h. Kosten, Margin usw.

Dieses Pool Zeritifikat “pool.cert”, enthält die Basiskonfiguration des Pools. Vor dem Registrieren macht es vielleicht Sinn die Einstellungen entsprechend zu setzen und es sich gut zu überlegen. Die Registrierung kostet 500 Ada Kaution.

Gut ist, nachträgliche Änderungen kostet den Betrag nicht erneut. Registriert ist registriert.

Was wir jetzt noch brauchen ist ein Zertifikat zur Delegation:

6. Das “deleg.cert” erstellen:

# offline Maschine
cardano-cli stake-address delegation-certificate \
--stake-verification-key-file stake.vkey \
--cold-verification-key-file $HOME/cold-keys/node.vkey \
--out-file deleg.cert

Das deleg.cert ist aus dem stake.vkey heraus endstanden für die node, die auf den node.vkey erzeugt wurde. Damit wird alles, was auf den Stake Adressen, die mit dem stake.vkey verbunden sind zum Pool delegiert, der mit node.vkey erzeugt wurde.

Die Zertifikate “pool.cert” und “deleg.cert”  nun zum Block Producer kopieren!

Wir müssen diese beiden Zertifikate registrieren. Genau wie beim Stake-Key müssen wir dafür einige Adas hinterlegen. Zum Zeitpunkt dieses Tutorials kostete die Registrierung 500 Adas.

Die Registrierung geschieht mittels einer Transaktion, die wir nun auf dem block producer beginnen zu bauen.

1. Wir müssen herausfinden, welchen Slot und Tip wir gerade haben, um den Zeitraum festlegen zu können, wo die Transaktion gültig ist:

# block producer node
currentSlot=$(cardano-cli query tip --mainnet | jq -r '.slot')
echo Current Slot: $currentSlot

2. Wir benötigen die Account Balance der payment.addr und die Transaktionsnummer:

# block producer node
cardano-cli query utxo \
    --address $(cat payment.addr) \
    --mainnet > fullUtxo.out

tail -n +3 fullUtxo.out | sort -k3 -nr > balance.out

cat balance.out

tx_in=""
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}")
    total_balance=$((${total_balance}+${utxo_balance}))
    echo TxHash: ${in_addr}#${idx}
    echo ADA: ${utxo_balance}
    tx_in="${tx_in} --tx-in ${in_addr}#${idx}"
done < balance.out
txcnt=$(cat balance.out | wc -l)
echo Total ADA balance: ${total_balance}
echo Number of UTXOs: ${txcnt}

3. Jetzt holen wir uns die genaue Höhe der “Kaution” (Deposit), die wir brauchen, um das Zertifikat registrieren zu können:

# block producer node
stakePoolDeposit=$(cat $NODE_HOME/params.json | jq -r '.stakePoolDeposit')
echo stakePoolDeposit: $stakePoolDeposit

4. Nun wird eine einfache raw transaction erzeugt, welche uns zum berechnen der fee dienen wird:

# block producer node
cardano-cli transaction build-raw \
    ${tx_in} \
    --tx-out $(cat payment.addr)+$(( ${total_balance} - ${stakePoolDeposit})) \
    --invalid-hereafter $(( ${currentSlot} + 10000)) \
    --fee 0 \
    --certificate-file pool.cert \
    --certificate-file deleg.cert \
    --out-file tx.tmp

5. Damit können wir nun die Transaction fee berechnen:

# block producer node
fee=$(cardano-cli transaction calculate-min-fee \
    --tx-body-file tx.tmp \
    --tx-in-count ${txcnt} \
    --tx-out-count 1 \
    --mainnet \
    --witness-count 3 \
    --byron-witness-count 0 \
    --protocol-params-file params.json | awk '{ print $1 }')
echo fee: $fee

6. Damit haben wir alles zusammen, um die Balance unseres Accounts nach der Transaction zu bestimmen:

# block producer node
txOut=$((${total_balance}-${stakePoolDeposit}-${fee}))
echo txOut: ${txOut}

7. Jetzt bauen wir die Transaction, welche wir später signieren und abschicken:

cardano-cli transaction build-raw \
    ${tx_in} \
    --tx-out $(cat payment.addr)+${txOut} \
    --invalid-hereafter $(( ${currentSlot} + 10000)) \
    --fee ${fee} \
    --certificate-file pool.cert \
    --certificate-file deleg.cert \
    --out-file tx.raw

tx.raw werden wir jetzt zur Offline-Maschine kopieren!

8. Signieren der Transaktion:

# Offline Maschine
cardano-cli transaction sign \
    --tx-body-file tx.raw \
    --signing-key-file payment.skey \
    --signing-key-file $HOME/cold-keys/node.skey \
    --signing-key-file stake.skey \
    --mainnet \
    --out-file tx.signed

tx.signed werden wir jetzt zum Block-Producer kopieren!

9. Abschicken der Transaktion:

cardano-cli transaction submit \
--tx-file tx.signed \
--mainnet

Damit sollte der Stake Pool Registriert sein und laufen. Er ist bereit Blöcke zu produzieren.

Stake Pool ID herausfinden und Prüfen ob alles läuft

1. Auf der Offline-Maschine holen wir uns die Stake Pool ID:

# Offline-Maschine
cardano-cli stake-pool id --cold-verification-key-file $HOME/cold-keys/node.vkey --output-format hex > stakepoolid.txt
cat stakepoolid.txt

stakepoolid.txt kopieren wir zum Block-Producer!

2. Auf dem Block-Producer holen wir uns einige Stake Pool Daten:

cardano-cli query stake-snapshot --stake-pool-id $(cat stakepoolid.txt) --mainnet

Ein nicht leerer String bedeutet, dass alles geklappt hat. Gratulation Sie sind nun Besitzer eines eigenen Stake Pools!

Relay Node Topology Updaten

Das Cardano Netzwerk kann derzeit noch keine p2p Suche nach vertrauenswürdigen Nodes durchführen. Daher muss das Hinzufügen vertrauenswürdiger Relay Pools von Hand unternommen werden.

Wir erstellen hierzu ein Skript, welches ein Topology update bei einem (hoffentlich) vertrauenswürdigen Fremdserver abfragt.

1. Skript erstellen:

###
### On relaynode1
###
cat > $NODE_HOME/topologyUpdater.sh << EOF
#!/bin/bash
# shellcheck disable=SC2086,SC2034

USERNAME=$(whoami)
CNODE_PORT=6000 # must match your relay node port as set in the startup command
CNODE_HOSTNAME="CHANGE ME" # optional. must resolve to the IP you are requesting from
CNODE_BIN="/usr/local/bin"
CNODE_HOME=$NODE_HOME
CNODE_LOG_DIR="\${CNODE_HOME}/logs"
GENESIS_JSON="\${CNODE_HOME}/${NODE_CONFIG}-shelley-genesis.json"
NETWORKID=\$(jq -r .networkId \$GENESIS_JSON)
CNODE_VALENCY=1 # optional for multi-IP hostnames
NWMAGIC=\$(jq -r .networkMagic < \$GENESIS_JSON)
[[ "\${NETWORKID}" = "Mainnet" ]] && HASH_IDENTIFIER="--mainnet" || HASH_IDENTIFIER="--testnet-magic \${NWMAGIC}"
[[ "\${NWMAGIC}" = "1097911063" ]] && NETWORK_IDENTIFIER="--mainnet" || NETWORK_IDENTIFIER="--testnet-magic \${NWMAGIC}"

export PATH="\${CNODE_BIN}:\${PATH}"
export CARDANO_NODE_SOCKET_PATH="\${CNODE_HOME}/db/socket"

blockNo=\$(/usr/local/bin/cardano-cli query tip \${NETWORK_IDENTIFIER} | jq -r .block )

# Note:
# if you run your node in IPv4/IPv6 dual stack network configuration and want announced the
# IPv4 address only please add the -4 parameter to the curl command below (curl -4 -s ...)
if [ "\${CNODE_HOSTNAME}" != "CHANGE ME" ]; then
    T_HOSTNAME="&hostname=\${CNODE_HOSTNAME}"
else
    T_HOSTNAME=''
fi

if [ ! -d \${CNODE_LOG_DIR} ]; then
    mkdir -p \${CNODE_LOG_DIR};
fi

curl -s "https://api.clio.one/htopology/v1/?port=\${CNODE_PORT}&blockNo=\${blockNo}&valency=\${CNODE_VALENCY}&magic=\${NWMAGIC}\${T_HOSTNAME}" | tee -a \$CNODE_LOG_DIR/topologyUpdater_lastresult.json
EOF

Hier muss noch der DNS Name angepasst werden.

2. Jetzt das Skript ausführbar machen und ausführen:

###
### On relaynode1
###
cd $NODE_HOME
chmod +x topologyUpdater.sh
./topologyUpdater.sh

in $NODE_HOME wird ein Verzeichnis logs erstellt, wo das Resultat der letzten Ausführung abgelegt wird. Im Erfolgsfall sieht es so aus:

{ "resultcode": "201", "datetime":"2020-07-28 01:23:45", "clientIp": "1.2.3.4", "iptype": 4, "msg": "nice to meet you" }

3 Jetzt erstellen wir dafür einen Crontab eintrag, welcher dieses Skript jedes mal zur 33 Minute jeder Stunde ausführt:

###
### On relaynode1
###
cat > $NODE_HOME/crontab-fragment.txt << EOF
33 * * * * ${NODE_HOME}/topologyUpdater.sh
EOF
crontab -l | cat - ${NODE_HOME}/crontab-fragment.txt > ${NODE_HOME}/crontab.txt && crontab ${NODE_HOME}/crontab.txt
rm ${NODE_HOME}/crontab-fragment.txt

Nach 4 Stunden wird die Adresse des Relays registriert sein, dann kann mit den nächsten Kapitel fortgefahren werden!

Das Update der Topologie

Wir erzeugen jetzt ein relay-topology_pull.sh Skript.

1. Das Skript erzeugen:

###
### On relaynode1
###
cat > $NODE_HOME/relay-topology_pull.sh << EOF
#!/bin/bash
BLOCKPRODUCING_IP=<BLOCK PRODUCERS PUBLIC IP ADDRESS>
BLOCKPRODUCING_PORT=6000
curl -s -o $NODE_HOME/${NODE_CONFIG}-topology.json "https://api.clio.one/htopology/v1/fetch/?max=20&customPeers=\${BLOCKPRODUCING_IP}:\${BLOCKPRODUCING_PORT}:1|relays-new.cardano-mainnet.iohk.io:3001:2"
EOF

Hier natürlich unbedingt die eigene Block Producer public IP einfügen!

2. Berechtigungen setzen und das Skript ausführen:

###
### On relaynode1
###
chmod +x relay-topology_pull.sh
./relay-topology_pull.sh

3. Um die neue Topologie aktiv werden zu lassen, muss die Node neu gestartet werden:

###
### On relaynode1
###
sudo systemctl restart cardano-node

Jetzt sollte der Stake Pool wirklich dazu in der Lage sein Blöcke zu erzeugen!

Überwachen der Nodes

Es gibt einige gute und auch notwendige Werkzeuge, um eine Cardano Node überwachen zu können. Wir beginnen mit gLiveView, dem einfachsten und grundlegendsten dieser Werkzeuge.

gLiveView

Wir uns gLiveView und die environment files:

cd $NODE_HOME
sudo apt install bc tcptraceroute -y
curl -s -o gLiveView.sh https://raw.githubusercontent.com/cardano-community/guild-operators/master/scripts/cnode-helper-scripts/gLiveView.sh
curl -s -o env https://raw.githubusercontent.com/cardano-community/guild-operators/master/scripts/cnode-helper-scripts/env
chmod 755 gLiveView.sh

Noch ein paar Settings mit dem stream editor setzen:

sed -i env \
-e "s/\#CONFIG=\"\${CNODE_HOME}\/files\/config.json\"/CONFIG=\"\${NODE_HOME}\/mainnet-config.json\"/g" \
-e "s/\#SOCKET=\"\${CNODE_HOME}\/sockets\/node0.socket\"/SOCKET=\"\${NODE_HOME}\/db\/socket\"/g"

Das Tool arbeitet jetzt.

Scroll to Top
%d bloggers like this: