Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
ad7b1cd
Implement public key posting to Dappmanager
chuygarcia92 Jun 1, 2026
fe3dd94
Merge pull request #1 from chuygarcia92/add-operator-pubkey-to-dappma…
chuygarcia92 Jun 1, 2026
a0a34f3
Add Dockerfile for DKG service setup
chuygarcia92 Jun 1, 2026
6cc443b
Add entrypoint script for DKG initialization
chuygarcia92 Jun 1, 2026
96dc7dd
Add DKG service configuration to docker-compose
chuygarcia92 Jun 1, 2026
22ef8b4
Add DKG repository and update architectures
chuygarcia92 Jun 1, 2026
57e49cb
Add DKG service to docker-compose configuration
chuygarcia92 Jun 1, 2026
e312cf3
Add DKG service configuration to docker-compose
chuygarcia92 Jun 1, 2026
fe081ab
add-dkg-service
chuygarcia92 Jun 1, 2026
2ea7e99
Update Dockerfile
chuygarcia92 Jun 1, 2026
27a1894
Fix typo in DKG_UPSTREAM_VERSION argument
chuygarcia92 Jun 1, 2026
edc6a03
Fix typo in DKG_UPSTREAM_VERSION variable
chuygarcia92 Jun 1, 2026
4a2b27c
Merge pull request #3 from chuygarcia92/add-dkg-service
chuygarcia92 Jun 1, 2026
e44f628
Fix typo in Dockerfile ARG variable name
chuygarcia92 Jun 1, 2026
ecb0a05
Create dkg-config.yml
chuygarcia92 Jun 1, 2026
ea4f236
Add executable permission to entrypoint.sh
chuygarcia92 Jun 1, 2026
36ffb01
Update OPERATOR_CONFIG_DIR path in entrypoint.sh
chuygarcia92 Jun 2, 2026
43f1d51
Update dappnode_package.json
chuygarcia92 Jun 3, 2026
6cbef41
Update dappnode_package.json
chuygarcia92 Jun 3, 2026
364fff2
Update docker-compose.yml
chuygarcia92 Jun 3, 2026
a0c2378
Fix typo in Dockerfile ARG declaration
chuygarcia92 Jun 3, 2026
0fd73e9
Fix typo in Dockerfile variable name
chuygarcia92 Jun 3, 2026
ebc82d2
Downgrade ssv-dkg version from v3.1.0 to v3.0.3
chuygarcia92 Jun 12, 2026
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
11 changes: 11 additions & 0 deletions dappnode_package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@
"version": "v1.2.3",
"arg": "UPSTREAM_VERSION"
},
{
"repo": "ssvlabs/ssv-dkg",
"version": "v3.0.3",
"arg": "DKG_UPSTREAM_VERSION"
},
{
"repo": "dappnode/staker-package-scripts",
"version": "v0.1.2",
Expand Down Expand Up @@ -32,6 +37,7 @@
"minimumDappnodeVersion": "0.2.101"
},
"license": "Apache-2.0",
"mainService": "operator",
"links": {
"Readme": "https://github.com/dappnode/DAppNodePackage-Anchor",
"Documentation": "https://anchor.sigmaprime.io/introduction/",
Expand All @@ -46,6 +52,11 @@
"name": "keys",
"path": "/root/keys",
"service": "operator"
},
{
"name": "dkg-output",
"path": "/data/dkg/output",
"service": "dkg"
}
]
}
27 changes: 27 additions & 0 deletions dkg/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
ARG DKG_UPSTREAM_VERSION
FROM ssvlabs/ssv-dkg:${DKG_UPSTREAM_VERSION}

WORKDIR /

ARG NETWORK
ARG DKG_PORT
ARG STAKER_SCRIPTS_VERSION

RUN apk add --no-cache --repository=http://dl-cdn.alpinelinux.org/alpine/edge/community \
yq inotify-tools jq curl

COPY dkg-config.yml /ssv-dkg/config/dkg-config.yml
COPY entrypoint.sh /usr/local/bin/entrypoint.sh

RUN chmod +x /usr/local/bin/entrypoint.sh

ENV DKG_PORT=${DKG_PORT} \
DKG_DATA_DIR=/data/dkg \
OPERATOR_DATA_DIR=/root/keys \
NETWORK=${NETWORK} \
METRICS_PORT=15000 \
DKG_CONFIG_DIR=/ssv-dkg/config \
STAKER_SCRIPTS_URL=https://github.com/dappnode/staker-package-scripts/releases/download/${STAKER_SCRIPTS_VERSION}

ADD ${STAKER_SCRIPTS_URL}/dvt_lsd_tools.sh /etc/profile.d/
ENTRYPOINT [ "entrypoint.sh" ]
8 changes: 8 additions & 0 deletions dkg/dkg-config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
privKey:
privKeyPassword:
port:
storeShare: true
logLevel:
logFormat: json
logLevelFormat: capitalColor
logFilePath: /data/dkg.log
128 changes: 128 additions & 0 deletions dkg/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
#!/bin/sh

OPERATOR_CONFIG_DIR=${OPERATOR_DATA_DIR}
DKG_LOGS_DIR=${DKG_DATA_DIR}/logs
DKG_OUTPUT_DIR=${DKG_DATA_DIR}/output
DKG_CERT_DIR=${DKG_DATA_DIR}/ssl

PRIVATE_KEY_FILE=${OPERATOR_CONFIG_DIR}/encrypted_private_key.json
PRIVATE_KEY_PASSWORD_FILE=${OPERATOR_CONFIG_DIR}/password.txt
OPERATOR_ID_FILE=/data/dkg/operator_id.txt
DKG_CONFIG_FILE=${DKG_CONFIG_DIR}/dkg-config.yml
DKG_LOG_FILE=${DKG_LOGS_DIR}/dkg.log

CERT_FILE="$DKG_CERT_DIR/tls.crt"
KEY_FILE="$DKG_CERT_DIR/tls.key"

# To use staker scripts
# shellcheck disable=SC1091
. /etc/profile

assign_execution_endpoint() {
EXECUTION_LAYER=$(get_execution_rpc_api_url_from_global_env "$NETWORK")
export EXECUTION_LAYER
}

create_directories() {
mkdir -p "${DKG_CONFIG_DIR}" "${DKG_LOGS_DIR}" "${DKG_OUTPUT_DIR}"
}

wait_for_private_key() {
echo "[INFO] Waiting for the operator service to create the private key file..."
while [ ! -f "${PRIVATE_KEY_FILE}" ]; do
echo "[INFO] Waiting for ${PRIVATE_KEY_FILE} to be created..."
inotifywait -e create -qq "$(dirname "${PRIVATE_KEY_FILE}")"
done

echo "[INFO] Private key file found."

if [ ! -f "${PRIVATE_KEY_PASSWORD_FILE}" ]; then
echo "[ERROR] ${PRIVATE_KEY_PASSWORD_FILE} not found. Cannot continue without the private key password file. Restarting dkg service..."
exit 1 # To avoid restart loop
fi
}

get_operator_id() {
if [ -z "${OPERATOR_ID}" ]; then

# Read operator ID from the file if it exists and is not empty
if [ -f "${OPERATOR_ID_FILE}" ] && [ -s "${OPERATOR_ID_FILE}" ]; then
OPERATOR_ID=$(cat "${OPERATOR_ID_FILE}")
echo "[INFO] Using OPERATOR_ID from the file: ${OPERATOR_ID}"
else
fetch_operator_id_from_api
fi
else
echo "[INFO] Using provided OPERATOR_ID: ${OPERATOR_ID}"
echo "${OPERATOR_ID}" >"${OPERATOR_ID_FILE}"
fi
}

fetch_operator_id_from_api() {
echo "[INFO] OPERATOR_ID not provided. Fetching OPERATOR_ID from the API..."

PUBLIC_KEY=$(jq -r '.pubKey' "${PRIVATE_KEY_FILE}")

# If the PUBLIC_KEY is empty, try extracting using the '.publicKey' field (for previous SSV versions)
if [ -z "$PUBLIC_KEY" ] || [ "$PUBLIC_KEY" = "null" ]; then
PUBLIC_KEY=$(jq -r '.publicKey' "${PRIVATE_KEY_FILE}")
fi

# Fetch the operator ID using the public key (retry 50 times with a delay of 10mins)
RESPONSE=$(curl --retry 50 --retry-delay 600 "https://api.ssv.network/api/v4/${NETWORK}/operators/public_key/${PUBLIC_KEY}")

OPERATOR_ID=$(echo "${RESPONSE}" | jq -r '.data.id')

# Check if OPERATOR_ID is successfully retrieved
if [ -z "${OPERATOR_ID}" ] || [ "${OPERATOR_ID}" = "null" ]; then
echo "[ERROR] Failed to fetch OPERATOR_ID from the API. Is your operator registered on the SSV network?"
echo "[INFO] Once registered, set OPERATOR_ID in the package config to perform the DKG or restart the dkg service to retry fetching it from the SSV API."
sleep 10m # To allow restoring backup
exit 0
else
echo "[INFO] Successfully fetched OPERATOR_ID: ${OPERATOR_ID}"
echo "${OPERATOR_ID}" >"${OPERATOR_ID_FILE}"
fi
}

generate_tls_cert() {
echo "[INFO] Generating TLS certificates..."

mkdir -p "$DKG_CERT_DIR"

# Generate a self-signed SSL certificate only if it doesn't exist
if [ ! -f "$CERT_FILE" ] || [ ! -f "$KEY_FILE" ]; then
echo "[INFO] Certificate or key file not found. Generating new SSL certificate and key."
openssl req -x509 -newkey rsa:4096 -sha256 -days 3650 -nodes \
-keyout "$KEY_FILE" -out "$CERT_FILE" \
-subj "/C=IL/ST=Tel Aviv/L=Tel Aviv/O=Coin-Dash Ltd/CN=*.ssvlabs.io"
else
echo "[INFO] Existing SSL certificate and key found. Using them."
fi
}

start_dkg() {
exec /bin/ssv-dkg start-operator \
--operatorID "${OPERATOR_ID}" \
--configPath ".${DKG_CONFIG_FILE}" \
--logFilePath ".${DKG_LOG_FILE}" \
--logLevel "${LOG_LEVEL}" \
--outputPath ".${DKG_OUTPUT_DIR}" \
--port "${DKG_PORT}" \
--privKey ".${PRIVATE_KEY_FILE}" \
--privKeyPassword ".${PRIVATE_KEY_PASSWORD_FILE}" \
--serverTLSCertPath ".${CERT_FILE}" \
--ethEndpointURL "${EXECUTION_LAYER}" \
--serverTLSKeyPath ".${KEY_FILE}"
}

main() {
create_directories
assign_execution_endpoint
wait_for_private_key
get_operator_id
generate_tls_cert
start_dkg
}

main
15 changes: 15 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,21 @@ services:
EXTRA_OPTS: ""
EXISTING_PASSWORD: ""
NEW_PASSWORD: ""
dkg:
build:
context: dkg
args:
DKG_UPSTREAM_VERSION: v3.1.0
STAKER_SCRIPTS_VERSION: v0.1.2
restart: on-failure
volumes:
- operator-keys:/root/keys
- dkg-data:/data/dkg
environment:
LOG_LEVEL: info
OPERATOR_ID: ""
EXTRA_OPTS: ""
volumes:
operator-data: {}
operator-keys: {}
dkg-data: {}
26 changes: 26 additions & 0 deletions operator/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,32 @@ BEACON_NODES=$(get_beacon_api_url_from_global_env "$NETWORK")

PASSWORD_FILE_PATH="/root/keys/password.txt"
KEY_FILE_PATH="/root/keys/encrypted_private_key.json"
PUBLIC_KEY_FILE_PATH="/root/keys/public_key.txt"
sleep 1

post_pubkey_to_dappmanager() {
PUBLIC_KEY=$(cat "$PUBLIC_KEY_FILE_PATH")

if [ -z "$PUBLIC_KEY" ]; then
echo "[ERROR - entrypoint] Public key not found at ${PUBLIC_KEY_FILE_PATH}, skipping Dappmanager post"
return 1
fi

curl --connect-timeout 5 \
--max-time 10 \
--silent \
--retry 5 \
--retry-delay 0 \
--retry-max-time 40 \
-X POST "http://dappmanager.dappnode/data-send?key=OperatorPublicKey&data=${PUBLIC_KEY}" || \
{
echo "[ERROR - entrypoint] Failed to post public key to Dappmanager"
}

echo "[INFO - entrypoint] Use this public key to register your node on the SSV network:"
echo "PUBLIC_KEY=${PUBLIC_KEY}"
}

# If Import Operator, save the EXISTING_PASSWORD to the password.txt at first import
# Later when Anchor is starting it will use --password-file flag to decrypt the private key
if [ "${SETUP_MODE}" = "Import Operator" ]; then
Expand Down Expand Up @@ -51,6 +75,8 @@ else
KEY_FILE=""
fi

post_pubkey_to_dappmanager

FLAGS="--network=${NETWORK} \
--data-dir=/root/.anchor \
--beacon-nodes=${BEACON_NODES} \
Expand Down
7 changes: 7 additions & 0 deletions package_variants/hoodi/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,10 @@ services:
- "9102:9102/tcp"
- "9102:9102/udp"
- "9103:9103/udp"
dkg:
build:
args:
NETWORK: hoodi
DKG_PORT: 15514
ports:
- "15514:15514"
7 changes: 7 additions & 0 deletions package_variants/mainnet/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,10 @@ services:
- "9100:9100/tcp"
- "9100:9100/udp"
- "9101:9101/udp"
dkg:
build:
args:
NETWORK: mainnet
DKG_PORT: 15516
ports:
- "15516:15516"
Loading