Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions cluster/scripts/charon-add-validator/main.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#!/bin/bash

# VARS
SERVICE_OK=0
ATTEMPTS=0
MAX_ATTEMPTS=10
INFO="[ INFO | container-add-validator:]"

# Function that runs the validator addition logic
run_validator_logic() {
echo "${INFO} Add validators for $CHARON_SERVICE_NAME"

# Check if ADD_VALIDATOR is setting in config for current running service
if [[ "$ADD_VALIDATOR_TARGET_CLUSTER" =~ (^|,)($CHARON_SERVICE_NAME)(,|$) ]]; then
echo "${INFO} Start running charon add-validators command"

charon alpha add-validators \
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please override the default relays list (see --p2p-relays) to use just https://4.relay.obol.dev - we keep this as an isolated relay for DKGs to not interfere with the default [0, 1 & 2] which are used for running clusters.

The command uses p2p network under the hood. An isolated relay ensures that add-validators instances will not interfere with charon run instances (those running a live cluster).
Otherwise, relay starts rejecting new connections because theses are the same peers having the same peer ID and they cannot connect twice to the same relay.

Hope this makes sense.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for your feedback! Do we need to have option to set any other relays in future? I mean, do I need to add optional not required (by default) field for setting non default (https://4.relay.obol.dev in our case) p2p-relays during add-validators?

--data-dir="$CHARON_ROOT_DIR" \
--num-validators "$ADD_VALIDATOR_NUM_VALIDATORS" \
--withdrawal-addresses="$ADD_VALIDATOR_WITHDRAWAL_ADDRESS" \
--fee-recipient-addresses="$ADD_VALIDATOR_FEE_RECEPIENT_ADDRESS" \
--p2p-relays https://4.relay.obol.dev \
--output-dir=/tmp/.charon

if [[ $? -ne 0 ]]; then
echo "${INFO} charon add-validators failed. Exiting..."
rm -f /import/add_validator
rm -rf /tmp/.charon
exit 1
fi

echo "${INFO} Stopping charon and lodestar during upgrade processes..."
supervisorctl stop charon lodestar

echo "${INFO} Upgrade .charon directory with backing up previous .charon to /tmp/.charon"
cp -r "$CHARON_ROOT_DIR" /tmp/.charon.bck && mv /tmp/.charon "$CHARON_ROOT_DIR"

echo "${INFO} Starting charon and lodestar processes..."
supervisorctl start charon lodestar

while [[ "$ATTEMPTS" -lt "$MAX_ATTEMPTS" ]]; do
if supervisorctl status charon | grep -q "RUNNING"; then
SERVICE_OK=1
break
fi

echo "${INFO} charon not ready, waiting 2 seconds... (Attempt ${ATTEMPTS}/${MAX_ATTEMPTS})"
sleep 3
ATTEMPTS=$((ATTEMPTS + 1))
done

if [[ "$SERVICE_OK" -eq 1 ]]; then
echo "${INFO} Validator(s) added, charon is running."
touch "$CHARON_ROOT_DIR/.charon_added_validator_state"
else
echo "${INFO} Validator(s) was not added, restoring .charon state folder to previous one and restart cluster. Check logs for more details"
mv /tmp/.charon.bck "$CHARON_ROOT_DIR"
supervisorctl restart charon lodestar
fi
fi
}

# Watch for the creation of /import/add_validator and trigger logic
inotifywait -m /import -e create |
while read path action file; do
if [ "$file" == "add_validator" ]; then
echo "${INFO} Trigger file detected, executing validator logic..."
run_validator_logic
rm -f /import/add_validator
fi
done
14 changes: 10 additions & 4 deletions cluster/scripts/charon/run-charon.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ ENR_FILE=${CHARON_ROOT_DIR}/enr
DEFINITION_FILE_URL_FILE=${CHARON_ROOT_DIR}/definition_file_url.txt

CHARON_LOCK_FILE=${CHARON_ROOT_DIR}/cluster-lock.json
CHARON_ADDED_VALIDATOR_STATE_FILE=${CHARON_ROOT_DIR}/.charon_added_validator_state

if [ -n "$DEFINITION_FILE_URL" ]; then
echo "$DEFINITION_FILE_URL" >$DEFINITION_FILE_URL_FILE
Expand All @@ -36,7 +37,7 @@ function get_beacon_node_endpoint() {
if [ -n "$CUSTOM_BEACON_NODE_URLS" ]; then

if [ -n "$local_beacon_api" ]; then
CHARON_BEACON_NODE_ENDPOINTS="$CHARON_BEACON_NODE_ENDPOINTS,$local_beacon_api"
CHARON_BEACON_NODE_ENDPOINTS="$CUSTOM_BEACON_NODE_URLS,$local_beacon_api"
else
CHARON_BEACON_NODE_ENDPOINTS=$CUSTOM_BEACON_NODE_URLS
fi
Expand Down Expand Up @@ -106,11 +107,16 @@ function check_DKG() {
}

function run_charon() {
if [ "$ENABLE_MEV_BOOST" = true ]; then
CHARON_EXTRA_OPTS="--builder-api $CHARON_EXTRA_OPTS"
if [ "$ENABLE_MEV_BOOST" = true ] && [ -f "$CHARON_ADDED_VALIDATOR_STATE_FILE" ]; then
CHARON_EXTRA_OPTS="--builder-api --no-verify ${CHARON_EXTRA_OPTS}"

elif [ "$ENABLE_MEV_BOOST" = true ]; then
CHARON_EXTRA_OPTS="--builder-api ${CHARON_EXTRA_OPTS}"
fi

exec charon run --private-key-file=$ENR_PRIVATE_KEY_FILE --lock-file=$CHARON_LOCK_FILE ${CHARON_EXTRA_OPTS}
if [ "$ENABLE_MEV_BOOST" = true ] || [ -f $CHARON_ADDED_VALIDATOR_STATE_FILE ]; then
exec charon run --private-key-file=$ENR_PRIVATE_KEY_FILE --lock-file=$CHARON_LOCK_FILE ${CHARON_EXTRA_OPTS}
fi
}

########
Expand Down
12 changes: 11 additions & 1 deletion cluster/supervisord.conf
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,14 @@ autorestart = true # Should not be required to start automatically
stdout_logfile = /dev/stdout
stdout_logfile_maxbytes = 0
stderr_logfile = /dev/stderr
stderr_logfile_maxbytes = 0
stderr_logfile_maxbytes = 0

[program:addvalidator]
command = /usr/local/bin/scripts/charon-add-validator/main.sh
priority = 3
autostart = true
autorestart = true
stdout_logfile = /dev/stdout
stdout_logfile_maxbytes = 0
stderr_logfile = /dev/stderr
stderr_logfile_maxbytes = 0
5 changes: 5 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ services:
CHARON_LOG_LEVEL: info
CHARON_LOKI_ADDRESSES: http://loki.dms.dappnode:3100/loki/api/v1/push
CHARON_P2P_RELAYS: https://0.relay.obol.tech,https://1.relay.obol.tech/
CHARON_SERVICE_NAME: cluster-1
CHARON_LOKI_SERVICE: cluster-1
ENABLE_MEV_BOOST: "false"
CHARON_EXTRA_OPTS: ""
Expand Down Expand Up @@ -51,6 +52,7 @@ services:
CHARON_P2P_RELAYS: https://0.relay.obol.tech,https://1.relay.obol.tech/
CHARON_P2P_TCP_ADDRESS: ""
CHARON_P2P_UDP_ADDRESS: ""
CHARON_SERVICE_NAME: cluster-2
CHARON_LOKI_SERVICE: cluster-2
ENABLE_MEV_BOOST: "false"
CHARON_EXTRA_OPTS: ""
Expand Down Expand Up @@ -83,6 +85,7 @@ services:
CHARON_LOG_LEVEL: info
CHARON_LOKI_ADDRESSES: http://loki.dms.dappnode:3100/loki/api/v1/push
CHARON_P2P_RELAYS: https://0.relay.obol.tech,https://1.relay.obol.tech/
CHARON_SERVICE_NAME: cluster-3
CHARON_LOKI_SERVICE: cluster-3
ENABLE_MEV_BOOST: "false"
CHARON_EXTRA_OPTS: ""
Expand Down Expand Up @@ -115,6 +118,7 @@ services:
CHARON_LOG_LEVEL: info
CHARON_LOKI_ADDRESSES: http://loki.dms.dappnode:3100/loki/api/v1/push
CHARON_P2P_RELAYS: https://0.relay.obol.tech,https://1.relay.obol.tech/
CHARON_SERVICE_NAME: cluster-4
CHARON_LOKI_SERVICE: cluster-4
ENABLE_MEV_BOOST: "false"
CHARON_EXTRA_OPTS: ""
Expand Down Expand Up @@ -147,6 +151,7 @@ services:
CHARON_LOG_LEVEL: info
CHARON_LOKI_ADDRESSES: http://loki.dms.dappnode:3100/loki/api/v1/push
CHARON_P2P_RELAYS: https://0.relay.obol.tech,https://1.relay.obol.tech/
CHARON_SERVICE_NAME: cluster-5
CHARON_LOKI_SERVICE: cluster-5
ENABLE_MEV_BOOST: "false"
CHARON_EXTRA_OPTS: ""
Expand Down
136 changes: 133 additions & 3 deletions setup-wizard.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ fields:
**READ BEFORE PROCEEDING**

This package contains 5 instances of Obol, so you can run up to 5 clusters with different people. Each cluster will have its own configuration. You can have a Cluster being part of Lido's SDVTM and another one with your friends and up to 5 different clusters. Each cluster can handle hundreds of validators.

It is **highly recommended** that you backup the package every time you make a change to the configuration of any of the clusters!

---
Expand All @@ -17,13 +17,16 @@ fields:

Select 'New cluster / Simple update' if this is your initial setup or if you are updating an existing setup.

For importing configurations, choose 'URL' to specify definition file URLs or 'File' to upload a compressed file containing a node artifact. Select 'URL' for SDVTM.
For importing configurations, choose 'URL' to specify definition file URLs or 'File' to upload a compressed file containing a node artifact. Select 'URL' for SDVTM.

To add new validators, fill in the required fields: *Number of validators*, *Target cluster*, *Withdrawal address*, and *Fee Recipient Addresses*. Afterward, use the DAppNode file manager to upload an empty file named `add_validator` (with no extension) to the /import target folder.

Note file upload will not be available on config tab after install, but in the file manager tab.
enum:
- "New cluster / Simple update"
- "URL"
- "File"
- "Add Validators"
target:
type: environment
name: CONFIG_MODE
Expand All @@ -34,7 +37,7 @@ fields:
title: Charons to monitor by Obol (optional)
description: |
Leave blank if you haven't been asked to do this.

Comma separated list of charon services to monitor by Obol team for performance and reliability. The prometheus service will send the metrics to the server defined by the monitoring URL.

Example: "1,2,3"
Expand Down Expand Up @@ -190,3 +193,130 @@ fields:
pattern: "(\\.tar.xz|\\.tar.gz|\\.zip)"
required: false
if: { "config_mode": { "enum": ["File"] } }

- id: validator-switch-1
target:
type: fileUpload
path: /import/add_validator
service: "cluster-1"
title: Add validator trigger file
description: |
Created empty file without extensions named: add_validator to trigger adding validator logic
pattern: "^add_validator$"
patternErrorMessage: |
"Can't upload provided file. Make sure you are uploading file without any additional extensions like .txt, upload add_validator file without any extenions and try again."
required: false
if: { "config_mode": { "enum": ["Add Validators"] } }

- id: validator-switch-2
target:
type: fileUpload
path: /import/add_validator
service: "cluster-2"
title: Add validator trigger file
description: |
Created empty file without extensions named: add_validator to trigger adding validator logic
pattern: "^add_validator$"
patternErrorMessage: |
"Can't upload provided file. Make sure you are uploading file without any additional extensions like .txt, upload add_validator file without any extenions and try again."
required: false
if: { "config_mode": { "enum": ["Add Validators"] } }

- id: validator-switch-3
target:
type: fileUpload
path: /import/add_validator
service: "cluster-3"
title: Add validator trigger file
description: |
Created empty file without extensions named: add_validator to trigger adding validator logic
pattern: "^add_validator$"
patternErrorMessage: |
"Can't upload provided file. Make sure you are uploading file without any additional extensions like .txt, upload add_validator file without any extenions and try again."
required: false
if: { "config_mode": { "enum": ["Add Validators"] } }

- id: validator-switch-4
target:
type: fileUpload
path: /import/add_validator
service: "cluster-4"
title: Add validator trigger file
description: |
Created empty file without extensions named: add_validator to trigger adding validator logic
pattern: "^add_validator$"
patternErrorMessage: |
"Can't upload provided file. Make sure you are uploading file without any additional extensions like .txt, upload add_validator file without any extenions and try again."
required: true
if: { "config_mode": { "enum": ["Add Validators"] } }

- id: validator-switch-5
target:
type: fileUpload
path: /import/add_validator
service: "cluster-5"
title: Add validator trigger file
description: |
Created empty file without extensions named: add_validator to trigger adding validator logic
pattern: "^add_validator$"
patternErrorMessage: |
"Can't upload provided file. Make sure you are uploading file without any additional extensions like .txt, upload add_validator file without any extenions and try again."
required: true
if: { "config_mode": { "enum": ["Add Validators"] } }

- id: num_validators
target:
type: environment
name: ADD_VALIDATOR_NUM_VALIDATORS
service: ["cluster-1", "cluster-2", "cluster-3", "cluster-4", "cluster-5"]
title: Number of validators
description: |
Enter the number of validators to add (1-100).
type: string
required: true
pattern: "^(100|[1-9][0-9]?)$"
patternErrorMessage: "Please enter a number between 1 and 100."
if: { "config_mode": { "enum": ["Add Validators"] } }

- id: target_cluster
target:
type: environment
name: ADD_VALIDATOR_TARGET_CLUSTER
service: ["cluster-1", "cluster-2", "cluster-3", "cluster-4", "cluster-5"]
title: Target clusters to process add-validators procedure
description: |
Set list of target clusters to add-validators separated by commas with np spaces.
Examples: `cluster-1` or `cluster-1,cluster-3`
default: "cluster-1"
required: true
pattern: "^cluster-\\d+(,cluster-\\d+)*$"
patternErrorMessage: Must be a valid set of clusters, for e.g. cluster-1 or cluster-1,cluster-2 *without spaces!*
if: { "config_mode": { "enum": ["Add Validators"] } }

- id: withdrawal_address
target:
type: environment
name: ADD_VALIDATOR_WITHDRAWAL_ADDRESS
# Use the target_cluster value to construct the service name
service: ["cluster-1", "cluster-2", "cluster-3", "cluster-4", "cluster-5"]
title: Withdrawal address
description: |
Enter the withdrawal address (0x...).
required: true
pattern: "^0x[a-fA-F0-9]{40}$"
patternErrorMessage: Must be a valid Ethereum address (0x...)
if: { "config_mode": { "enum": ["Add Validators"] } }

- id: fee_recipient_address
target:
type: environment
name: ADD_VALIDATOR_FEE_RECEPIENT_ADDRESS
# Use the target_cluster value to construct the service name
service: ["cluster-1", "cluster-2", "cluster-3", "cluster-4", "cluster-5"]
title: Fee Recipient Addresses
description: |
Enter the fee recipient address (0x...).
required: true
pattern: "^0x[a-fA-F0-9]{40}$"
patternErrorMessage: Must be a valid Ethereum address (0x...)
if: { "config_mode": { "enum": ["Add Validators"] } }