Obol Setup

Setting Up a Distributed Validator Cluster with Obol Network and StakeWise Vaults

Follow these steps to set up a Distributed Validator Cluster using Obol Network and integrate it with StakeWise Vaults.

Prerequisites

  1. Ensure Docker is running. Check:

$ docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
 ...

IMPORTANT! When using the create-keys command, add the --per-keystore-password flag to generate a keystore with a separate password file for each keystore. This is necessary for the normal operation of the Charon CLI, especially when splitting keys.

Step 1. Setup Obol Cluster

First, create .env file with charon settings:

Addresses must be in lowercase

cat <<EOF > .env
export VAULT_CONTRACT_ADDR=[ENTER YOUR VAULT CONTRACT ADDRESS HERE]
export FEE_RECIPIENT_ADDR=[ENTER YOUR VAULT FEE RECIPIENT ADDRESS HERE]
export NETWORK=[ENTER YOUR NETWORK NAME]
EOF

Next create cluster obol cluster:

source .env
docker run --rm -v "$(pwd):/opt/charon" -v "$HOME/.stakewise:/.stakewise" obolnetwork/charon:v0.19.2 \
  create cluster \
  --name="cluster-name" \
  --cluster-dir=".charon/cluster/" \
  --withdrawal-addresses=$VAULT_CONTRACT_ADDR \
  --fee-recipient-addresses=$FEE_RECIPIENT_ADDR \
  --nodes 3 \
  --network $NETWORK \
  --split-existing-keys \
  --split-keys-dir /.stakewise/$VAULT_CONTRACT_ADDR/keystores


~~~~~ OUTPUT:

***************** WARNING: Splitting keys **********************
 Please make sure any existing validator has been shut down for
 at least 2 finalised epochs before starting the charon cluster,
 otherwise slashing could occur.                               
****************************************************************

Created charon cluster:
 --split-existing-keys=true

/opt/charon/.charon/cluster/
├─ node[0-2]/			Directory for each node
  ├─ charon-enr-private-key	Charon networking private key for node authentication
  ├─ cluster-lock.json		Cluster lock defines the cluster lock file which is signed by all nodes
  ├─ deposit-data.json		Deposit data file is used to activate a Distributed Validator on DV Launchpad
  ├─ validator_keys		Validator keystores and password
    ├─ keystore-*.json	Validator private share key for duty signing
    ├─ keystore-*.txt		Keystore password files for keystore-*.json

You should now have multiple folders within ./.charon/cluster/, one for each node created. Backup the ./.charon folder, then move on to deploying the cluster physically.

Step 2. Run Nodes, Validators and Charon client

After initial setup run 3 charon client and 3 validators client (you can run it on single server or distribute across multiple), copy below docker-compose.yml, file to the server the same dir with .charon and run docker compose up -d

Replace [ENTER YOUR VAULT FEE RECIPIENT ADDRESS HERE] with your vault fee recipient address

docker-compose.yml
x-logging: &logging
  logging:
    driver: json-file
    options:
      max-size: 10m
      max-file: "3"
      tag: "{{.ImageName}}|{{.Name}}|{{.ImageFullID}}|{{.FullID}}"

networks:
  cluster:

x-node-base:
  # Pegged charon version (update this for each release).
  &node-base
  image: obolnetwork/charon:${CHARON_VERSION:-v0.19.2}
  restart: unless-stopped
  networks: [ cluster ]
  depends_on: [ relay ]
  volumes:
    - ./.charon:/opt/charon/.charon/

x-node-env:
  &node-env
  CHARON_BEACON_NODE_ENDPOINTS: ${CHARON_BEACON_NODE_ENDPOINTS:-http://lighthouse:6000}
  CHARON_LOG_LEVEL: ${CHARON_LOG_LEVEL:-info}
  CHARON_LOG_FORMAT: ${CHARON_LOG_FORMAT:-console}
  CHARON_P2P_EXTERNAL_HOSTNAME: ${CHARON_P2P_EXTERNAL_HOSTNAME:-} # Empty default required to avoid warnings.
  CHARON_P2P_RELAYS: ${CHARON_P2P_RELAYS:-http://relay:3640/enr}
  CHARON_P2P_TCP_ADDRESS: ${CHARON_P2P_TCP_ADDRESS:-0.0.0.0:3610}
  CHARON_VALIDATOR_API_ADDRESS: ${CHARON_VALIDATOR_API_ADDRESS:-0.0.0.0:3600}

services:
  mev-boost:
    image: flashbots/mev-boost:1.7.0
    restart: always
    command: >
      -mainnet
      -relays
      https://0xac6e77dfe25ecd6110b8e780608cce0dab71fdd5ebea22a16c0205200f2f8e2e3ad3b71d3499c54ad14d6c21b41a37ae@boost-relay.flashbots.net,https://0xa1559ace749633b997cb3fdacffb890aeebdb0f5a3b6aaa7eeeaf1a38af0a8fe88b9e4b1f61f236d2e64d95733327a62@relay.ultrasound.money,https://0x8b5d2e73e2a3a55c6c87b8b6eb92e0149a125c852751db1422fa951e42a09b82c142c3ea98d0d9930b056a3bc9896b8f@bloxroute.max-profit.blxrbdn.com,https://0xa7ab7a996c8584251c8f925da3170bdfd6ebc75d50f5ddc4050a6fdc77f2a3b5fce2cc750d0865e05d7228af97d69561@agnostic-relay.net,https://0xb0b07cd0abef743db4260b0ed50619cf6ad4d82064cb4fbec9d3ec530f7c5e6793d9f286c4e082c0244ffb9f2658fe88@bloxroute.regulated.blxrbdn.com,https://0xa15b52576bcbf1072f4a011c0f99f9fb6c66f3e1ff321f11f461d15e31b1cb359caa092c71bbded0bae5b5ea401aab7e@aestus.live
      -addr
      127.0.0.1:18551
      -loglevel
      info
      -json
    networks: [ cluster ]

  geth:
    image: ethereum/client-go:v1.13.14
    restart: always
    command: >
      --mainnet
      --syncmode=snap
      --datadir=/data
      --db.engine=pebble
      --authrpc.jwtsecret=/data/jwtsecret --authrpc.addr=0.0.0.0 --authrpc.port=8551 --authrpc.vhosts=*
      --http --http.addr=0.0.0.0 --http.port=8445 --http.corsdomain=* --http.vhosts=*
      --port=30303
      --ipcdisable
    volumes: ["./data/geth:/data"]
    ports:
      - 30303:30303/tcp
      - 30303:30303/udp
    networks: [ cluster ]

  lighthouse:
    image: sigp/lighthouse:v5.1.2
    restart: always
    command: >
      lighthouse
      bn
      --staking
      --datadir=/data
      --network=mainnet
      --execution-endpoint=http://geth:8551
      --execution-jwt=/data/jwtsecret
      --checkpoint-sync-url=https://mainnet-checkpoint-sync.attestant.io/
      --slots-per-restore-point=8192
      --http
      --http-port=6000
      --http-address=0.0.0.0
      --http-allow-origin=*
      --builder http://localhost:18551
      --port=30304
      --enr-udp-port=30305
      --disable-upnp
    ulimits:
      nofile:
        soft: "1000000"
        hard: "1000000"
    volumes: ["./data/lighthouse:/data"]
    ports:
      - 30304:30304/tcp
      - 30304:30304/udp
      - 30305:30305/udp
    networks: [ cluster ]

  relay:
    <<: *node-base
    command: relay
    depends_on: []
    environment:
      <<: *node-env
      CHARON_HTTP_ADDRESS: 0.0.0.0:3640
      CHARON_DATA_DIR: /opt/charon/relay
      CHARON_P2P_RELAYS: ""
      CHARON_P2P_EXTERNAL_HOSTNAME: relay
    volumes:
      - ./data/relay:/opt/charon/relay:rw

  node0:
    <<: *node-base
    environment:
      <<: *node-env
      CHARON_PRIVATE_KEY_FILE: /opt/charon/.charon/cluster/node0/charon-enr-private-key
      CHARON_LOCK_FILE: /opt/charon/.charon/cluster/node0/cluster-lock.json
      CHARON_P2P_EXTERNAL_HOSTNAME: node0

  node1:
    <<: *node-base
    environment:
      <<: *node-env
      CHARON_PRIVATE_KEY_FILE: /opt/charon/.charon/cluster/node1/charon-enr-private-key
      CHARON_LOCK_FILE: /opt/charon/.charon/cluster/node1/cluster-lock.json
      CHARON_P2P_EXTERNAL_HOSTNAME: node1

  node2:
    <<: *node-base
    environment:
      <<: *node-env
      CHARON_PRIVATE_KEY_FILE: /opt/charon/.charon/cluster/node2/charon-enr-private-key
      CHARON_LOCK_FILE: /opt/charon/.charon/cluster/node2/cluster-lock.json
      CHARON_P2P_EXTERNAL_HOSTNAME: node2

  vc0-teku:
    image: consensys/teku:${TEKU_VERSION:-24.3.0}
    networks: [ cluster ]
    depends_on: [ node0 ]
    restart: unless-stopped
    command: |
      validator-client
      --network=auto
      --beacon-node-api-endpoint="http://node0:3600"
      --Xblock-v3-enabled=false
      --validators-proposer-default-fee-recipient=[ENTER YOUR VAULT FEE RECIPIENT ADDRESS HERE]
      --validator-keys="/opt/charon/validator_keys:/opt/charon/validator_keys"
      --validators-keystore-locking-enabled=false
    volumes:
      - .charon/cluster/node0/validator_keys:/opt/charon/validator_keys
      - ./data/vc0:/opt/charon/teku

  vc1-teku:
    image: consensys/teku:${TEKU_VERSION:-24.3.0}
    networks: [ cluster ]
    depends_on: [ node1 ]
    restart: unless-stopped
    command: |
      validator-client
      --network=auto
      --beacon-node-api-endpoint="http://node1:3600"
      --Xblock-v3-enabled=false
      --validators-proposer-default-fee-recipient=[ENTER YOUR VAULT FEE RECIPIENT ADDRESS HERE]
      --validator-keys="/opt/charon/validator_keys:/opt/charon/validator_keys"
      --validators-keystore-locking-enabled=false
    volumes:
      - .charon/cluster/node1/validator_keys:/opt/charon/validator_keys
      - ./data/vc1:/opt/charon/teku

  vc2-teku:
    image: consensys/teku:${TEKU_VERSION:-24.3.0}
    networks: [ cluster ]
    depends_on: [ node2 ]
    restart: unless-stopped
    command: |
      validator-client
      --network=auto
      --beacon-node-api-endpoint="http://node2:3600"
      --Xblock-v3-enabled=false
      --validators-proposer-default-fee-recipient=[ENTER YOUR VAULT FEE RECIPIENT ADDRESS HERE]
      --validator-keys="/opt/charon/validator_keys:/opt/charon/validator_keys"
      --validators-keystore-locking-enabled=false
    volumes:
      - .charon/cluster/node2/validator_keys:/opt/charon/validator_keys
      - ./data/vc2:/opt/charon/teku

Last updated