Chain ID Latest Version Tag Sidecar version Custom Port
gardia-4 v5.16.10 v5.16.9 182

Setup validator name

Replace YOUR_MONIKER_GOES_HERE with your validator name

MONIKER="YOUR_MONIKER_GOES_HERE"

Install dependencies

Update system and install build tools

sudo apt -q update
sudo apt -qy install curl git jq lz4 build-essential
sudo apt -qy upgrade

Install Go

sudo rm -rf /usr/local/go
curl -Ls https://go.dev/dl/go1.23.5.linux-amd64.tar.gz | sudo tar -xzf - -C /usr/local
eval $(echo 'export PATH=$PATH:/usr/local/go/bin' | sudo tee /etc/profile.d/golang.sh)
eval $(echo 'export PATH=$PATH:$HOME/go/bin' | tee -a $HOME/.profile)

Download and build binaries

# Clone project repository
cd $HOME
rm -rf zrchain
git clone https://github.com/zenrocklabs/zrchain.git
cd zrchain
git checkout v5.16.10

# Build binaries
make build

# Prepare binaries for Cosmovisor
mkdir -p $HOME/.zrchain/cosmovisor/genesis/bin
mv build/zenrockd $HOME/.zrchain/cosmovisor/genesis/bin/
rm -rf build

# Create application symlinks
ln -s $HOME/.zrchain/cosmovisor/genesis $HOME/.zrchain/cosmovisor/current -f
sudo ln -s $HOME/.zrchain/cosmovisor/current/bin/zenrockd /usr/local/bin/zenrockd -f

Install Cosmovisor and create a service

# Download and install Cosmovisor
go install cosmossdk.io/tools/cosmovisor/cmd/cosmovisor@v1.6.0

# Create service
sudo tee /etc/systemd/system/zenrock-testnet.service > /dev/null << EOF
[Unit]
Description=zenrock node service
After=network-online.target

[Service]
User=$USER
ExecStart=$(which cosmovisor) run start
Restart=on-failure
RestartSec=10
LimitNOFILE=65535
Environment="DAEMON_HOME=$HOME/.zrchain"
Environment="DAEMON_NAME=zenrockd"
Environment="UNSAFE_SKIP_BACKUP=true"
Environment="PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:$HOME/.zrchain/cosmovisor/current/bin"

[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable zenrock-testnet.service

Set node configuration

# Set node configuration
zenrockd config set client chain-id gardia-4
zenrockd config set client keyring-backend test
zenrockd config set client node tcp://localhost:18257

Initialize the node

# Initialize the node
zenrockd init $MONIKER --chain-id gardia-4

# Download genesis and addrbook
curl -Ls https://snapshots.kjnodes.com/zenrock-testnet/genesis.json > $HOME/.zrchain/config/genesis.json
curl -Ls https://snapshots.kjnodes.com/zenrock-testnet/addrbook.json > $HOME/.zrchain/config/addrbook.json

# Add seeds
sed -i -e "s|^seeds *=.*|seeds = \"3f472746f46493309650e5a033076689996c8881@zenrock-testnet.rpc.kjnodes.com:18259\"|" $HOME/.zrchain/config/config.toml

# Set minimum gas price
sed -i -e "s|^minimum-gas-prices *=.*|minimum-gas-prices = \"2.5urock\"|" $HOME/.zrchain/config/app.toml

# Set pruning
sed -i \
  -e 's|^pruning *=.*|pruning = "custom"|' \
  -e 's|^pruning-keep-recent *=.*|pruning-keep-recent = "100"|' \
  -e 's|^pruning-keep-every *=.*|pruning-keep-every = "0"|' \
  -e 's|^pruning-interval *=.*|pruning-interval = "19"|' \
  $HOME/.zrchain/config/app.toml

# Set custom ports
sed -i -e "s%^proxy_app = \"tcp://127.0.0.1:26658\"%proxy_app = \"tcp://127.0.0.1:18258\"%; s%^laddr = \"tcp://127.0.0.1:26657\"%laddr = \"tcp://127.0.0.1:18257\"%; s%^pprof_laddr = \"localhost:6060\"%pprof_laddr = \"localhost:18260\"%; s%^laddr = \"tcp://0.0.0.0:26656\"%laddr = \"tcp://0.0.0.0:18256\"%; s%^prometheus_listen_addr = \":26660\"%prometheus_listen_addr = \":18266\"%" $HOME/.zrchain/config/config.toml
sed -i -e "s%^address = \"tcp://0.0.0.0:1317\"%address = \"tcp://0.0.0.0:18217\"%; s%^address = \":8080\"%address = \":18280\"%; s%^address = \"0.0.0.0:9090\"%address = \"0.0.0.0:18290\"%; s%^address = \"0.0.0.0:9091\"%address = \"0.0.0.0:18291\"%; s%:8545%:18245%; s%:8546%:18246%; s%:6065%:18265%" $HOME/.zrchain/config/app.toml

Download latest chain snapshot

curl -L https://snapshots.kjnodes.com/zenrock-testnet/snapshot_latest.tar.lz4 | tar -Ilz4 -xf - -C $HOME/.zrchain
[[ -f $HOME/.zrchain/data/upgrade-info.json ]] && cp $HOME/.zrchain/data/upgrade-info.json $HOME/.zrchain/cosmovisor/genesis/upgrade-info.json

Start service and check the logs

sudo systemctl start zenrock-testnet.service && sudo journalctl -u zenrock-testnet.service -f --no-hostname -o cat

Validator setup

To set up a validator, follow the steps below. Official validator setup instructions can be found at https://github.com/zenrocklabs/zenrock-validators.

Step 1: Create a wallet

First of all we will need to create wallet for our validator. You have two options for that.

Option 1 - Create new wallet

zenrockd keys add wallet

Option 2 - Recover existing wallet

zenrockd keys add wallet --recover

Save the mnemonic output as this is the only way to recover your validator wallet in case you lose it!

To list your wallets use command below

zenrockd keys list

Step 2: Fund a wallet

To create validator you have to top up previously created wallet with tokens.

To check wallet balance use command below

zenrockd q bank balances $(zenrockd keys show wallet -a)

Step 3: Create validator

Ensure that you have updated the validator details to match your own.

zenrockd tx validation create-validator <(cat <<EOF
{
  "pubkey": $(zenrockd comet show-validator),
  "amount": "1000000urock",
  "moniker": "YOUR_MONIKER_NAME",
  "identity": "YOUR_KEYBASE_ID",
  "website": "YOUR_WEBSITE_URL",
  "security": "YOUR_SECURITY_EMAIL",
  "details": "YOUR_DETAILS",
  "commission-rate": "0.05",
  "commission-max-rate": "0.20",
  "commission-max-change-rate": "0.05",
  "min-self-delegation": "1"
}
EOF
) \
--chain-id gardia-4 \
--from wallet \
--gas-adjustment 1.4 \
--gas auto \
--gas-prices 2.5urock \
-y

Save the $HOME/.zrchain/config/priv_validator_key.json file as this is the only way to recover your validator signing key in case you lose it!

Sidecar setup

The validator sidecar service allows validators to vote on oracle data during the consensus process in CometBFT, ensuring that oracle values are secured by the same economic security as the blockchain through a super-majority consensus mechanism.

Learn more about Validation Module.

Step 1: Clone zenrock-validators repository

cd $HOME
rm -rf zenrock-validators
git clone https://github.com/zenrocklabs/zenrock-validators

Step 2: Generate keys

# Set key password
read -p "Enter password for the keys: " key_pass

# Create sidecar directories
mkdir -p $HOME/.zrchain/sidecar/bin
mkdir -p $HOME/.zrchain/sidecar/keys

# Build ecdsa binary
cd $HOME/zenrock-validators/utils/keygen/ecdsa && go build

# Build bls binary
cd $HOME/zenrock-validators/utils/keygen/bls && go build

# Generate ecdsa key
ecdsa_output_file=$HOME/.zrchain/sidecar/keys/ecdsa.key.json
ecdsa_creation=$($HOME/zenrock-validators/utils/keygen/ecdsa/ecdsa --password $key_pass -output-file $ecdsa_output_file)
ecdsa_address=$(echo "$ecdsa_creation" | grep "Public address" | cut -d: -f2)

# Generate bls key
bls_output_file=$HOME/.zrchain/sidecar/keys/bls.key.json
$HOME/zenrock-validators/utils/keygen/bls/bls --password $key_pass -output-file $bls_output_file

# Output
echo "ecdsa address: $ecdsa_address"

Step 3: Top up your wallet address

Please fund your wallet addresses with Holesky $ETH before proceeding further.

Step 4: Set operator configuration

Ensure that you have configured TESTNET_HOLESKY_ENDPOINT, MAINNET_ENDPOINT, ETH_RPC_URL, ETH_WS_URL with your specific values. You can use Quicknode.com to get api keys.

Declare variables

EIGEN_OPERATOR_CONFIG="$HOME/.zrchain/sidecar/eigen_operator_config.yaml"
TESTNET_HOLESKY_ENDPOINT="YOUR_TESTNET_HOLESKY_ENDPOINT"
MAINNET_ENDPOINT="YOUR_ETH_MAINNET_ENDPOINT"
OPERATOR_VALIDATOR_ADDRESS=$(zenrockd keys show wallet --bech val -a)
OPERATOR_ADDRESS=$ecdsa_address
ETH_RPC_URL="YOUR_TESTNET_HOLESKY_RPC"
ETH_WS_URL="YOUR_TESTNET_HOLESKY_WS"
ECDSA_KEY_PATH=$ecdsa_output_file
BLS_KEY_PATH=$bls_output_file

Generate eigen_operator_config.yaml configuration file

tee $HOME/.zrchain/sidecar/eigen_operator_config.yaml > /dev/null <<EOF
register_operator_on_startup: true
# this sets the logger level (true = info, false = debug)
production: true
operator_address: OPERATOR_ADDRESS
operator_validator_address: OPERATOR_VALIDATOR_ADDRESS

# EigenLayer Slasher contract address
# This is the address of the contracts which are deployed in the anvil saved state
# The saved eigenlayer state is located in tests/anvil/credible_squaring_avs_deployment_output.json
avs_registry_coordinator_address: 0xdc3A1b2a44D18c6B98a1d6c8C042247d2F5AC722
operator_state_retriever_address: 0xdB55356826a16DfFBD86ba334b84fC4E37113d97

# ETH RPC URL
eth_rpc_url: ETH_RPC_URL
eth_ws_url: ETH_WS_URL
# ECDSA key
ecdsa_private_key_store_path: ECDSA_KEY_PATH

# We are using bn254 curve for bls keys
bls_private_key_store_path: BLS_KEY_PATH

# address which the aggregator listens on for operator signed messages
aggregator_server_ip_port_address: avs-aggregator.gardia.zenrocklabs.io:8090

# avs node spec compliance https://eigen.nethermind.io/docs/spec/intro
eigen_metrics_ip_port_address: 0.0.0.0:9292
enable_metrics: true
node_api_ip_port_address: 0.0.0.0:9191
enable_node_api: true

# address of token to deposit tokens into when registering on startup
token_strategy_addr: 0x80528D6e9A2BAbFc766965E0E26d5aB08D9CFaF9
EOF

Generate config.yaml configuration file

tee $HOME/.zrchain/sidecar/config.yaml > /dev/null <<EOF
enabled: true
grpc_port: 9191
zrchain_rpc: "localhost:18290"
state_file: "cache.json"
operator_config: "$HOME/.zrchain/sidecar/eigen_operator_config.yaml"
network: "testnet"
eth_oracle:
  rpc:
    local: "http://127.0.0.1:8545"
    testnet: TESTNET_HOLESKY_ENDPOINT  # Replace this endpoint with a valid one
    mainnet: MAINNET_ENDPOINT  # Replace this endpoint with a valid one
  contract_addrs:
    service_manager: "0xa559CDb9e029fc4078170122eBf7A3e622a764E4"
    price_feeds:
      btc: "0xF4030086522a5bEEa4988F8cA5B36dbC97BeE88c"
      eth: "0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419"
    zenbtc:
      controller:
        testnet: "0xaCE3634AAd9bCC48ef6A194f360F7ACe51F7d9f1"
      token:
        ethereum:
          testnet: "0xfA32a2D7546f8C7c229F94E693422A786DaE5E18"
  network_name:
    mainnet: "Ethereum Mainnet"
    testnet: "Holesky Ethereum Testnet"
solana_rpc:
  testnet: "https://api.testnet.solana.com"
  mainnet: ""
proxy_rpc:
  url: #To be provided by the Zenrock team
  user: #To be provided by the Zenrock team
  password: #To be provided by the Zenrock team
neutrino:
  path: "$HOME/.zrchain/neutrino"
EOF

Replace variables in config.yaml

sed -i "s|TESTNET_HOLESKY_ENDPOINT|$TESTNET_HOLESKY_ENDPOINT|g" "$HOME/.zrchain/sidecar/config.yaml"
sed -i "s|MAINNET_ENDPOINT|$MAINNET_ENDPOINT|g" "$HOME/.zrchain/sidecar/config.yaml"

Replace variables in eigen_operator_config.yaml

sed -i "s|OPERATOR_VALIDATOR_ADDRESS|$OPERATOR_VALIDATOR_ADDRESS|g" "$HOME/.zrchain/sidecar/eigen_operator_config.yaml"
sed -i "s|OPERATOR_ADDRESS|$OPERATOR_ADDRESS|g" "$HOME/.zrchain/sidecar/eigen_operator_config.yaml"
sed -i "s|ETH_RPC_URL|$ETH_RPC_URL|g" "$HOME/.zrchain/sidecar/eigen_operator_config.yaml"
sed -i "s|ETH_WS_URL|$ETH_WS_URL|g" "$HOME/.zrchain/sidecar/eigen_operator_config.yaml"
sed -i "s|ECDSA_KEY_PATH|$ECDSA_KEY_PATH|g" "$HOME/.zrchain/sidecar/eigen_operator_config.yaml"
sed -i "s|BLS_KEY_PATH|$BLS_KEY_PATH|g" "$HOME/.zrchain/sidecar/eigen_operator_config.yaml"

Step 5: Download sidecar binary

wget -O $HOME/.zrchain/sidecar/bin/validator_sidecar https://github.com/zenrocklabs/zrchain/releases/download/v5.16.9/validator_sidecar
chmod +x $HOME/.zrchain/sidecar/bin/validator_sidecar

Step 6: Create and run sidecar service

Create service

sudo tee /etc/systemd/system/zenrock-testnet-sidecar.service > /dev/null <<EOF
[Unit]
Description=Validator Sidecar
After=network-online.target

[Service]
User=$USER
ExecStart=$HOME/.zrchain/sidecar/bin/validator_sidecar
WorkingDirectory=$HOME/.zrchain/sidecar
Restart=on-failure
RestartSec=30
LimitNOFILE=65535
Environment="OPERATOR_BLS_KEY_PASSWORD=$key_pass"
Environment="OPERATOR_ECDSA_KEY_PASSWORD=$key_pass"
Environment="SIDECAR_CONFIG_FILE=$HOME/.zrchain/sidecar/config.yaml"

[Install]
WantedBy=multi-user.target
EOF

Enable and start service

sudo systemctl daemon-reload
sudo systemctl enable zenrock-testnet-sidecar.service
sudo systemctl start zenrock-testnet-sidecar.service

Step 7: Check the service logs

To check service logs use command below:

journalctl -fu zenrock-testnet-sidecar.service -o cat

Successfull Log examples:

{"level":"info","ts":1727294139.4385705,"caller":"operator/operator.go:250","msg":"Operator info","operatorId":[144,89,34,19,95,158,123,120,47,228,59,114,85,73,150,39,84,119,143,77,154,173,85,210,132,206,213,195,7,190,250,142],"operatorAddr":"0x68e305548619Ce71D562b851ff1adfb7e5369DB3","operatorG1Pubkey":"E([20150260775620749168755223143919346367674724303860875751315024817211815113340,5713528518001336848987890055463332760351549074437348780451495739349376234320])","operatorG2Pubkey":"E([9400781597017099172228313635710883835447541071342639102400258381712924127278+17588199816725806065286885136698384247231626887315665039610705603333445204237*u,21507870890336379219932542686750816691453493327464905543429790078496913285917+3341052853010856303076683482759015503204225193006075252832111461015148222443*u])"}
2024/09/25 19:55:39 initialized operator
2024/09/25 19:55:39 starting operator
{"level":"info","ts":1727294139.4387212,"caller":"operator/operator.go:262","msg":"Starting operator."}
{"level":"info","ts":1727294139.438742,"caller":"nodeapi/nodeapi.go:104","msg":"Starting node api server at address 0.0.0.0:9191"}
{"level":"info","ts":1727294139.4388723,"caller":"metrics/eigenmetrics.go:81","msg":"Starting metrics server at port 0.0.0.0:9292"}
{"level":"info","ts":1727294139.4389389,"caller":"nodeapi/nodeapi.go:238","msg":"node api server running","addr":"0.0.0.0:9191"}
{"level":"info","ts":1727294139.5475569,"caller":"chainio/avs_subscriber.go:63","msg":"Subscribed to new TaskManager tasks"}
2024/09/25 19:55:45 Received AVS contract state for  block 2407883
2024/09/25 19:55:45 Received prices: ETH/USD 2583.166191, ROCK/USD 0.000000
2024/09/25 19:56:00 Received prices: ETH/USD 2583.166191, ROCK/USD 0.000000
2024/09/25 19:56:00 Received AVS contract state for  block 2407884
2024/09/25 19:56:15 Received prices: ETH/USD 2583.166191, ROCK/USD 0.000000
2024/09/25 19:56:15 Received AVS contract state for  block 2407885

Step 8: Backup operator config and keys

To back up your sidecar configuration and keys, ensure you have a copy of the $HOME/.zrchain/sidecar directory.