Skip to content
Open
53 changes: 34 additions & 19 deletions compose-vc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ services:
vc-lodestar:
profiles: [vc-lodestar]
image: chainsafe/lodestar:${VC_LODESTAR_VERSION:-v1.46.0}
depends_on: [charon]
entrypoint: /opt/lodestar/run.sh
depends_on:
charon:
condition: service_healthy
networks: [dvnode]
entrypoint: /opt/lodestar/run.sh
environment:
BEACON_NODE_ADDRESS: http://charon:3600
NETWORK: ${NETWORK}
Expand All @@ -25,6 +27,7 @@ services:
volumes:
- ./lodestar/run.sh:/opt/lodestar/run.sh
- .charon/validator_keys:/home/charon/validator_keys
- .charon/vc-config:/home/charon/vc-config:ro
- ./data/lodestar:/opt/data # Keep data in lodestar and not vc-lodestar for backwards compatibility
restart: unless-stopped

Expand All @@ -41,7 +44,9 @@ services:
context: nimbus
args:
VERSION: ${VC_NIMBUS_VERSION:-multiarch-v26.7.0}
depends_on: [charon]
depends_on:
charon:
condition: service_healthy
networks: [dvnode]
environment:
BEACON_NODE_ADDRESS: http://charon:3600
Expand All @@ -51,6 +56,7 @@ services:
volumes:
- ./nimbus/run.sh:/home/user/data/run.sh
- .charon/validator_keys:/home/validator_keys
- .charon/vc-config:/home/charon/vc-config:ro
- ./data/vc-nimbus:/home/user/data
restart: unless-stopped

Expand All @@ -62,20 +68,28 @@ services:

vc-prysm:
profiles: [vc-prysm]
image: offchainlabs/prysm-validator:${VC_PRYSM_VERSION:-v7.1.8}
image: "prysm-validator-client:${VC_PRYSM_VERSION:-v7.1.8}"
build:
context: prysm
args:
VERSION: ${VC_PRYSM_VERSION:-v7.1.8}
platform: "linux/amd64"
depends_on: [charon]
depends_on:
charon:
condition: service_healthy
networks: [dvnode]
entrypoint: /home/prysm/run.sh
environment:
BEACON_NODE_ADDRESS: http://charon:3600
NETWORK: ${NETWORK}
BUILDER_API_ENABLED: ${BUILDER_API_ENABLED:-true}
labels:
- "alloy-monitored=${VC_PRYSM_ALLOY_MONITORED:-true}"
volumes:
- ./prysm/run.sh:/home/prysm/run.sh
- ./data/vc-prysm:/data/vc
- .charon/validator_keys:/home/charon/validator_keys
- .charon/vc-config:/home/charon/vc-config:ro
restart: unless-stopped

# _ _
Expand All @@ -86,24 +100,25 @@ services:

vc-teku:
profiles: [vc-teku]
image: consensys/teku:${VC_TEKU_VERSION:-26.8.0}
command: |
validator-client
--beacon-node-api-endpoint "http://charon:3600"
--network="${NETWORK}"
--data-base-path=/home/data
--validator-keys="/opt/charon/validator_keys:/opt/charon/validator_keys"
--validators-keystore-locking-enabled false
--validators-external-signer-slashing-protection-enabled true
--validators-builder-registration-default-enabled ${BUILDER_API_ENABLED:-true}
--validators-proposer-default-fee-recipient "0x0000000000000000000000000000000000000000"
--Xobol-dvt-integration-enabled true
--Xvalidator-client-beacon-api-executor-threads=50
depends_on: [charon]
image: "teku-validator-client:${VC_TEKU_VERSION:-26.8.0}"
build:
context: teku
args:
VERSION: ${VC_TEKU_VERSION:-26.8.0}
depends_on:
charon:
condition: service_healthy
networks: [dvnode]
entrypoint: /opt/teku/run.sh
environment:
BEACON_NODE_ADDRESS: http://charon:3600
NETWORK: ${NETWORK}
BUILDER_API_ENABLED: ${BUILDER_API_ENABLED:-true}
labels:
- "alloy-monitored=${VC_TEKU_ALLOY_MONITORED:-true}"
volumes:
- ./teku/run.sh:/opt/teku/run.sh
- .charon/validator_keys:/opt/charon/validator_keys
- .charon/vc-config:/opt/charon/vc-config:ro
- ./data/vc-teku:/home/data
restart: unless-stopped
32 changes: 31 additions & 1 deletion lodestar/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,35 @@ done

echo "Processed all keys imported=${IMPORTED_COUNT}, existing=${EXISTING_COUNT}, total=$(ls /home/charon/validator_keys/keystore-*.json | wc -l)"

# Render Lodestar's proposer settings from the charon-generated canonical config when
# available: entries only carry fields diverging from default_config, absent fields
# fall back to it. Lodestar only accepts yml/yaml file extensions; JSON is valid YAML.
PROPOSER_SETTINGS=""
if [ -f /home/charon/vc-config/proposer-config.json ]; then
echo "proposer-config.json found, rendering lodestar proposer settings"
node -e '
const fs = require("fs");
const src = JSON.parse(fs.readFileSync(process.argv[1], "utf8"));
const d = src.default_config;
const out = {
proposer_config: {},
default_config: {fee_recipient: d.fee_recipient, builder: {gas_limit: d.gas_limit}},
};
for (const [pubkey, entry] of Object.entries(src.proposer_config || {})) {
out.proposer_config[pubkey] = {
fee_recipient: entry.fee_recipient ?? d.fee_recipient,
builder: {gas_limit: entry.gas_limit ?? d.gas_limit},
};
}
fs.writeFileSync(process.argv[2], JSON.stringify(out));
' /home/charon/vc-config/proposer-config.json /tmp/proposer-config.yml
PROPOSER_SETTINGS="--proposerSettingsFile=/tmp/proposer-config.yml"
else
echo "proposer-config.json not found, running without proposer settings"
fi

# Word splitting of $PROPOSER_SETTINGS is intentional, it is empty or a single flag.
# shellcheck disable=SC2086
exec node /usr/app/packages/cli/bin/lodestar validator \
--dataDir="$DATA_DIR" \
--keystoresDir="$KEYSTORES_DIR" \
Expand All @@ -51,4 +80,5 @@ exec node /usr/app/packages/cli/bin/lodestar validator \
--beaconNodes="$BEACON_NODE_ADDRESS" \
--builder="$BUILDER_API_ENABLED" \
--builder.selection="$BUILDER_SELECTION" \
--distributed
--distributed \
$PROPOSER_SETTINGS
5 changes: 5 additions & 0 deletions nimbus/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,9 @@ FROM statusim/nimbus-validator-client:${VERSION}

COPY --from=nimbusbn /home/user/nimbus_beacon_node /home/user/nimbus_beacon_node

# jq is used by run.sh to render per-validator proposer config files.
USER root
RUN apt-get update && apt-get install -y --no-install-recommends jq && rm -rf /var/lib/apt/lists/*
USER user

ENTRYPOINT ["/home/user/data/run.sh"]
24 changes: 23 additions & 1 deletion nimbus/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,34 @@ rm -r ${tmpkeys}

echo "Imported all keys"

if [[ -f /home/charon/vc-config/proposer-config.json ]]; then
echo "proposer-config.json found, rendering per-validator proposer settings"

# Resolve each imported validator's settings: proposer_config entries only carry
# fields diverging from default_config, absent fields fall back to it.
config=/home/charon/vc-config/proposer-config.json
for f in /home/validator_keys/keystore-*.json; do
pubkey="0x$(jq -r .pubkey "${f}")"
fee_recipient=$(jq -r --arg pk "${pubkey}" '.proposer_config[$pk].fee_recipient // .default_config.fee_recipient' "${config}")
gas_limit=$(jq -r --arg pk "${pubkey}" '.proposer_config[$pk].gas_limit // .default_config.gas_limit' "${config}")

for dir in "/home/user/data/validators/${pubkey}" "/home/user/data/validators/${pubkey#0x}"; do
if [[ -d "${dir}" ]]; then
echo "${fee_recipient}" >"${dir}/suggested_fee_recipient.hex"
echo "${gas_limit}" >"${dir}/suggested_gas_limit.json"
fi
done
done
else
echo "proposer-config.json not found, running without proposer settings"
fi

# Now run nimbus VC
exec /home/user/nimbus_validator_client \
--data-dir=/home/user/data \
--beacon-node="${BEACON_NODE_ADDRESS}" \
--doppelganger-detection=false \
--metrics \
--metrics-address=0.0.0.0 \
--payload-builder=${BUILDER_API_ENABLED} \
--payload-builder="${BUILDER_API_ENABLED}" \
--distributed
7 changes: 6 additions & 1 deletion prometheus/prometheus.yml.example
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,12 @@ scrape_configs:

- job_name: "validator-client"
static_configs:
- targets: ["lodestar:5064","vc-lodestar:5064"]
- targets:
- "lodestar:5064"
- "vc-lodestar:5064"
- "vc-teku:8008"
- "vc-prysm:8081"
- "vc-nimbus:8108"
# Debug
- job_name: "node-exporter"
static_configs:
Expand Down
8 changes: 8 additions & 0 deletions prysm/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
ARG VERSION

FROM ghcr.io/jqlang/jq:1.8.0 AS jq

FROM offchainlabs/prysm-validator:${VERSION}

# jq is used by run.sh to render Prysm's proposer settings from the charon-generated config.
COPY --from=jq /jq /usr/local/bin/jq
28 changes: 26 additions & 2 deletions prysm/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,37 @@ rm -r ${tmpkeys}

echo "Imported all keys"

# Render Prysm's proposer settings from the charon-generated canonical config when available:
# entries only carry fields diverging from default_config, absent fields fall back to it.
PROPOSER_SETTINGS=()
if [[ -f /home/charon/vc-config/proposer-config.json ]]; then
echo "proposer-config.json found, rendering prysm proposer settings"
jq --argjson enabled "${BUILDER_API_ENABLED}" '
.default_config as $d |
{
proposer_config: (.proposer_config | map_values({
fee_recipient: (.fee_recipient // $d.fee_recipient),
builder: {enabled: $enabled, gas_limit: (.gas_limit // $d.gas_limit)}
})),
default_config: {
fee_recipient: $d.fee_recipient,
builder: {enabled: $enabled, gas_limit: $d.gas_limit}
}
}' /home/charon/vc-config/proposer-config.json >/tmp/prysm-proposer-settings.json
PROPOSER_SETTINGS+=(--proposer-settings-file="/tmp/prysm-proposer-settings.json")
else
echo "proposer-config.json not found, running without proposer settings"
fi

# Now run prysm VC
/app/cmd/validator/validator --wallet-dir="$WALLET_DIR" \
exec /app/cmd/validator/validator --wallet-dir="$WALLET_DIR" \
--accept-terms-of-use=true \
--datadir="/data/vc" \
--wallet-password-file="/wallet-password.txt" \
--enable-beacon-rest-api \
--monitoring-host=0.0.0.0 \
--beacon-rest-api-provider="${BEACON_NODE_ADDRESS}" \
--beacon-rpc-provider="${BEACON_NODE_ADDRESS}" \
--"${NETWORK}" \
--distributed
--distributed \
"${PROPOSER_SETTINGS[@]}"
8 changes: 8 additions & 0 deletions teku/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
ARG VERSION

FROM ghcr.io/jqlang/jq:1.8.0 AS jq

FROM consensys/teku:${VERSION}

# jq is used by run.sh to render Teku's proposer config from the charon-generated one.
COPY --from=jq /jq /usr/local/bin/jq
41 changes: 41 additions & 0 deletions teku/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#!/bin/bash

# Render Teku's proposer config from the charon-generated canonical config when available:
# entries only carry fields diverging from default_config, absent fields fall back to it.
# Otherwise fall back to a zero default fee recipient, which charon overrides pre-gloas.
PROPOSER_CONFIG=()
if [[ -f /opt/charon/vc-config/proposer-config.json ]]; then
echo "proposer-config.json found, rendering teku proposer config"
jq --argjson enabled "${BUILDER_API_ENABLED}" '
.default_config as $d |
{
proposer_config: (.proposer_config | map_values({
fee_recipient: (.fee_recipient // $d.fee_recipient),
builder: {enabled: $enabled, gas_limit: (.gas_limit // $d.gas_limit)}
})),
default_config: {
fee_recipient: $d.fee_recipient,
builder: {enabled: $enabled, gas_limit: $d.gas_limit}
}
}' /opt/charon/vc-config/proposer-config.json >/tmp/teku-proposer-config.json
PROPOSER_CONFIG+=(--validators-proposer-config="/tmp/teku-proposer-config.json")
else
echo "proposer-config.json not found, running with zero default fee recipient"
PROPOSER_CONFIG+=(--validators-proposer-default-fee-recipient="0x0000000000000000000000000000000000000000")
fi

exec /opt/teku/bin/teku validator-client \
--beacon-node-api-endpoint "${BEACON_NODE_ADDRESS}" \
--network="${NETWORK}" \
--data-base-path=/home/data \
--validator-keys="/opt/charon/validator_keys:/opt/charon/validator_keys" \
--validators-keystore-locking-enabled false \
--validators-external-signer-slashing-protection-enabled true \
--validators-builder-registration-default-enabled "${BUILDER_API_ENABLED}" \
--Xobol-dvt-integration-enabled true \
--Xvalidator-client-beacon-api-executor-threads=50 \
--metrics-enabled=true \
--metrics-interface=0.0.0.0 \
--metrics-port=8008 \
--metrics-host-allowlist="*" \
"${PROPOSER_CONFIG[@]}"
Loading