diff --git a/charts/dv-pod/QUICKSTART.md b/charts/dv-pod/QUICKSTART.md index efbb4db..61581d4 100644 --- a/charts/dv-pod/QUICKSTART.md +++ b/charts/dv-pod/QUICKSTART.md @@ -20,7 +20,7 @@ Each operator deploys their node: ```bash helm upgrade --install my-dv-pod charts/dv-pod/ \ --set charon.operatorAddress=0xYOUR_OPERATOR_ADDRESS \ - --set chainId=1 \ + --set network=mainnet \ --namespace=dv-pod \ --timeout=10m --create-namespace ``` @@ -122,7 +122,7 @@ Expected output: `["charon-enr-private-key", "enr"]` helm upgrade --install my-dv-pod charts/dv-pod/ \ --set charon.operatorAddress=0xYOUR_OPERATOR_ADDRESS \ --set charon.dkgSidecar.targetConfigHash=0xYOUR_CONFIG_HASH \ - --set chainId=1 \ + --set network=mainnet \ --set 'charon.beaconNodeEndpoints[0]=http://YOUR_BEACON_NODE:5052' \ --set charon.enr.existingSecret.name=charon-enr-private-key \ --namespace=dv-pod \ @@ -156,16 +156,13 @@ kubectl exec -n dv-pod my-dv-pod-dv-pod-0 -- ls -la /charon-data/cluster-lock.js ```bash # Mainnet (default) ---set chainId=1 +--set network=mainnet # Sepolia testnet ---set chainId=11155111 +--set network=sepolia # Hoodi testnet ---set chainId=560048 - -# Gnosis Chain ---set chainId=100 +--set network=hoodi ``` ### Custom DKG Sidecar Image @@ -198,8 +195,7 @@ kubectl exec -n dv-pod my-dv-pod-dv-pod-0 -- ls -la /charon-data/cluster-lock.js --set validatorClient.type=teku # Prysm ---set validatorClient.type=prysm \ ---set validatorClient.config.prysm.acceptTermsOfUse=true +--set validatorClient.type=prysm # Lodestar --set validatorClient.type=lodestar @@ -293,6 +289,73 @@ kubectl port-forward -n dv-pod my-dv-pod-dv-pod-0 3620:3620 --- +## Challenge & Testing Environments + +For testing, development, or challenge environments, you may need to override the default API endpoint or use a specific DKG sidecar version. + +### Override API Endpoint + +To point to a different Obol API (e.g., dev, staging, or local): + +```bash +# Using development API +helm upgrade --install my-dv-pod charts/dv-pod/ \ + --set charon.operatorAddress=0xYOUR_OPERATOR_ADDRESS \ + --set charon.dkgSidecar.apiEndpoint=https://obol-api-nonprod-dev.dev.obol.tech \ + --set network=hoodi \ + --namespace=dv-pod \ + --timeout=10m --create-namespace + +# Using local API for development +helm upgrade --install my-dv-pod charts/dv-pod/ \ + --set charon.operatorAddress=0xYOUR_OPERATOR_ADDRESS \ + --set charon.dkgSidecar.apiEndpoint=http://localhost:3000 \ + --set network=hoodi \ + --namespace=dv-pod \ + --timeout=10m --create-namespace +``` + +### Use Specific DKG Sidecar Commit + +To test with a specific DKG sidecar commit or branch: + +```bash +# Using a specific commit SHA +helm upgrade --install my-dv-pod charts/dv-pod/ \ + --set charon.operatorAddress=0xYOUR_OPERATOR_ADDRESS \ + --set charon.dkgSidecar.image.tag=90a1656 \ + --set charon.dkgSidecar.image.pullPolicy=Always \ + --set network=hoodi \ + --namespace=dv-pod \ + --timeout=10m --create-namespace + +### Complete Challenge Example + +Full example combining custom API endpoint, specific DKG sidecar commit, and hoodi testnet: + +```bash +helm upgrade --install challenge-dv-pod charts/dv-pod/ \ + --set charon.operatorAddress=0xYOUR_OPERATOR_ADDRESS \ + --set network=hoodi \ + --set 'charon.fallbackBeaconNodeEndpoints[0]=https://ethereum-hoodi-beacon-api.publicnode.com' \ + --set charon.dkgSidecar.apiEndpoint=https://obol-api-nonprod-dev.dev.obol.tech \ + --set charon.dkgSidecar.image.tag=90a1656 \ + --set charon.dkgSidecar.image.pullPolicy=Always \ + --set validatorClient.type=prysm \ + --set centralMonitoring.enabled=true \ + --set-string centralMonitoring.token='YOUR_MONITORING_TOKEN' \ + --namespace=dv-pod \ + --timeout=10m --create-namespace +``` + +**Key Parameters for Challenges:** +- `network=hoodi` - Use Hoodi testnet for testing +- `charon.dkgSidecar.apiEndpoint` - Point to dev/staging API +- `charon.dkgSidecar.image.tag` - Specific commit SHA or branch name +- `charon.dkgSidecar.image.pullPolicy=Always` - Force pull latest image + +--- + ## Next Steps After successful DKG: diff --git a/charts/dv-pod/README.md b/charts/dv-pod/README.md index c102925..50596aa 100644 --- a/charts/dv-pod/README.md +++ b/charts/dv-pod/README.md @@ -66,7 +66,7 @@ kubectl create secret generic validator-keys \ kubectl create configmap cluster-lock --from-file=cluster/node0/cluster-lock.json # Install the chart, referencing your ConfigMap and Secrets -helm install my-dv-pod obol/dv-pod \ +helm upgrade --install my-dv-pod obol/dv-pod \ --set configMaps.clusterLock=cluster-lock \ --set validatorClient.keystores.secretName=validator-keys ``` @@ -102,7 +102,7 @@ kubectl create configmap cluster-lock \ --from-file=.charon/cluster-lock.json # Install the chart -helm install my-dv-pod obol/dv-pod \ +helm upgrade --install my-dv-pod obol/dv-pod \ --set configMaps.clusterLock=cluster-lock \ --set validatorClient.keystores.secretName=validator-keys ``` @@ -119,7 +119,7 @@ If you don't have pre-existing artifacts, the chart can automatically: Simply deploy the chart with your operator address: ```sh -helm install my-dv-pod obol/dv-pod \ +helm upgrade --install my-dv-pod obol/dv-pod \ --set charon.operatorAddress=0xYOUR_OPERATOR_ADDRESS ``` @@ -168,13 +168,13 @@ validatorClient: - --suggested-fee-recipient=0xYOUR_FEE_RECIPIENT_ADDRESS ``` -With a `helm install` command for pre-existing artifacts. +With a `helm upgrade --install` command for pre-existing artifacts. ```sh -helm install my-dv-pod obol/dv-pod \ +helm upgrade --install my-dv-pod obol/dv-pod \ --set configMaps.clusterLock=cluster-lock \ --set validatorClient.keystores.secretName=validator-keys \ - --set validatorClient.type=prysm --set validatorClient.config.prysm.acceptTermsOfUse=true + --set validatorClient.type=prysm ``` > [!NOTE] @@ -197,7 +197,7 @@ kubectl create secret generic validator-keys \ --from-file=keystore-1.txt # Deploy the chart with the keystore secret -helm install my-dv-pod obol/dv-pod \ +helm upgrade --install my-dv-pod obol/dv-pod \ --set validatorClient.keystores.secretName=validator-keys \ --set configMaps.clusterLock=my-cluster-lock ``` @@ -225,11 +225,11 @@ The ENR (Ethereum Node Record) secret **MUST** be created in the same namespace ```bash # Wrong approach - secret and chart in different namespaces kubectl create secret generic charon-enr-private-key -n dv-pod --from-literal=... -helm install my-dv-pod obol/dv-pod # Installs in default namespace - ENR will be regenerated! +helm upgrade --install my-dv-pod obol/dv-pod # Installs in default namespace - ENR will be regenerated! # Correct approach - both in same namespace kubectl create secret generic charon-enr-private-key -n dv-pod --from-literal=... -helm install my-dv-pod obol/dv-pod -n dv-pod # Both in dv-pod namespace +helm upgrade --install my-dv-pod obol/dv-pod -n dv-pod # Both in dv-pod namespace ``` ## Advanced Usage @@ -276,7 +276,7 @@ In this case, you have two options: 2. Install the chart with the lockHash value: ```sh - helm install my-dv-pod obol/dv-pod \ + helm upgrade --install my-dv-pod obol/dv-pod \ --set charon.lockHash=$LOCK_HASH \ --set charon.operatorAddress= ``` @@ -292,7 +292,7 @@ In this case, you have two options: 2. Install the chart referencing the ConfigMap: ```sh - helm install my-dv-pod obol/dv-pod \ + helm upgrade --install my-dv-pod obol/dv-pod \ --set configMaps.lockHash=cluster-lock-hash \ --set charon.operatorAddress= ``` @@ -344,7 +344,7 @@ The command removes all the Kubernetes components associated with the chart and | charon.enr.privateKey | string | `""` | Provide the ENR private key directly (hex format, e.g., 0x...). If set, 'generate' and 'existingSecret' are ignored. | | charon.enrJob.enabled | bool | `true` | Enable or disable the Kubernetes Job that generates/manages the ENR. Note: This is typically not needed as the job automatically detects existing secrets. The job will check if the ENR secret already exists and skip generation if found. Only set to false for advanced use cases where you need to completely disable the job. | | charon.executionClientRpcEndpoint | string | `""` | Optional: Execution client RPC endpoint URL (e.g., your Ethereum execution client) Note: Charon currently only supports a single execution endpoint This is only needed if an operator in the cluster uses a smart contract wallet for an operator signature. If that does not apply to this cluster, this field can be left unset. | -| charon.fallbackBeaconNodeEndpoints | list | `["https://ethereum-beacon-api.publicnode.com"]` | Fallback beacon node endpoints (optional) These will be used if the primary beaconNodeEndpoints are unavailable | +| charon.fallbackBeaconNodeEndpoints | list | `["https://ethereum-beacon-api.publicnode.com"]` | Fallback beacon node endpoints (optional) These will be used if the primary beaconNodeEndpoints are unavailable If not specified, intelligent defaults based on network will be used: - mainnet: https://ethereum-beacon-api.publicnode.com - sepolia: https://ethereum-sepolia-beacon-api.publicnode.com - hoodi: https://ethereum-hoodi-beacon-api.publicnode.com | | charon.featureSet | string | `"stable"` | Minimum feature set to enable by default: alpha, beta, or stable. Warning: modify at own risk. (default "stable") | | charon.featureSetDisable | string | `""` | Comma-separated list of features to disable, overriding the default minimum feature set. | | charon.featureSetEnable | string | `""` | Comma-separated list of features to enable, overriding the default minimum feature set. | diff --git a/charts/dv-pod/README.md.gotmpl b/charts/dv-pod/README.md.gotmpl index 4f0d20c..334e27b 100644 --- a/charts/dv-pod/README.md.gotmpl +++ b/charts/dv-pod/README.md.gotmpl @@ -67,7 +67,7 @@ kubectl create secret generic validator-keys \ kubectl create configmap cluster-lock --from-file=cluster/node0/cluster-lock.json # Install the chart, referencing your ConfigMap and Secrets -helm install my-dv-pod obol/dv-pod \ +helm upgrade --install my-dv-pod obol/dv-pod \ --set configMaps.clusterLock=cluster-lock \ --set validatorClient.keystores.secretName=validator-keys ``` @@ -103,7 +103,7 @@ kubectl create configmap cluster-lock \ --from-file=.charon/cluster-lock.json # Install the chart -helm install my-dv-pod obol/dv-pod \ +helm upgrade --install my-dv-pod obol/dv-pod \ --set configMaps.clusterLock=cluster-lock \ --set validatorClient.keystores.secretName=validator-keys ``` @@ -121,7 +121,7 @@ If you don't have pre-existing artifacts, the chart can automatically: Simply deploy the chart with your operator address: ```sh -helm install my-dv-pod obol/dv-pod \ +helm upgrade --install my-dv-pod obol/dv-pod \ --set charon.operatorAddress=0xYOUR_OPERATOR_ADDRESS ``` @@ -170,13 +170,13 @@ validatorClient: - --suggested-fee-recipient=0xYOUR_FEE_RECIPIENT_ADDRESS ``` -With a `helm install` command for pre-existing artifacts. +With a `helm upgrade --install` command for pre-existing artifacts. ```sh -helm install my-dv-pod obol/dv-pod \ +helm upgrade --install my-dv-pod obol/dv-pod \ --set configMaps.clusterLock=cluster-lock \ --set validatorClient.keystores.secretName=validator-keys \ - --set validatorClient.type=prysm --set validatorClient.config.prysm.acceptTermsOfUse=true + --set validatorClient.type=prysm ``` > [!NOTE] @@ -199,7 +199,7 @@ kubectl create secret generic validator-keys \ --from-file=keystore-1.txt # Deploy the chart with the keystore secret -helm install my-dv-pod obol/dv-pod \ +helm upgrade --install my-dv-pod obol/dv-pod \ --set validatorClient.keystores.secretName=validator-keys \ --set configMaps.clusterLock=my-cluster-lock ``` @@ -227,11 +227,11 @@ The ENR (Ethereum Node Record) secret **MUST** be created in the same namespace ```bash # Wrong approach - secret and chart in different namespaces kubectl create secret generic charon-enr-private-key -n dv-pod --from-literal=... -helm install my-dv-pod obol/dv-pod # Installs in default namespace - ENR will be regenerated! +helm upgrade --install my-dv-pod obol/dv-pod # Installs in default namespace - ENR will be regenerated! # Correct approach - both in same namespace kubectl create secret generic charon-enr-private-key -n dv-pod --from-literal=... -helm install my-dv-pod obol/dv-pod -n dv-pod # Both in dv-pod namespace +helm upgrade --install my-dv-pod obol/dv-pod -n dv-pod # Both in dv-pod namespace ``` ## Advanced Usage @@ -278,7 +278,7 @@ In this case, you have two options: 2. Install the chart with the lockHash value: ```sh - helm install my-dv-pod obol/dv-pod \ + helm upgrade --install my-dv-pod obol/dv-pod \ --set charon.lockHash=$LOCK_HASH \ --set charon.operatorAddress= ``` @@ -294,7 +294,7 @@ In this case, you have two options: 2. Install the chart referencing the ConfigMap: ```sh - helm install my-dv-pod obol/dv-pod \ + helm upgrade --install my-dv-pod obol/dv-pod \ --set configMaps.lockHash=cluster-lock-hash \ --set charon.operatorAddress= ``` diff --git a/charts/dv-pod/templates/_helpers.tpl b/charts/dv-pod/templates/_helpers.tpl index 1305d76..ea317e4 100644 --- a/charts/dv-pod/templates/_helpers.tpl +++ b/charts/dv-pod/templates/_helpers.tpl @@ -141,10 +141,20 @@ Create comma-separated list of primary beacon node endpoints {{/* Create comma-separated list of fallback beacon node endpoints +Returns user-specified endpoints or intelligent defaults based on network */}} {{- define "dv-pod.fallbackBeaconNodeEndpoints" -}} {{- if .Values.charon.fallbackBeaconNodeEndpoints -}} {{- join "," .Values.charon.fallbackBeaconNodeEndpoints -}} +{{- else -}} +{{- $network := .Values.network -}} +{{- if eq $network "mainnet" -}} +https://ethereum-beacon-api.publicnode.com +{{- else if eq $network "sepolia" -}} +https://ethereum-sepolia-beacon-api.publicnode.com +{{- else if eq $network "hoodi" -}} +https://ethereum-hoodi-beacon-api.publicnode.com +{{- end -}} {{- end -}} {{- end -}} @@ -156,7 +166,44 @@ Validate validator client type {{- $validTypes := list "lighthouse" "lodestar" "teku" "prysm" "nimbus" -}} {{- $currentType := .Values.validatorClient.type -}} {{- if not (has $currentType $validTypes) -}} -{{- fail (printf "ERROR: Invalid validator client type '%s'. Valid options are: %s" $currentType (join ", " $validTypes)) -}} +{{- $errorMsg := printf "\n\nERROR: Invalid validator client type '%s'.\n\nValid options: %s" $currentType (join ", " $validTypes) -}} +{{- /* Try to find the closest match using simple heuristics */ -}} +{{- $suggestion := "" -}} +{{- $currentLower := lower $currentType -}} +{{- /* Check for common typos and confusions */ -}} +{{- $typoMap := dict "loki" "lodestar" "lodestar-" "lodestar" "lighthose" "lighthouse" "lighthouse-" "lighthouse" "tekku" "teku" "teku-" "teku" "prisim" "prysm" "prism" "prysm" "prysm-" "prysm" "nimbos" "nimbus" "nimbus-" "nimbus" -}} +{{- if hasKey $typoMap $currentLower -}} +{{- $suggestion = get $typoMap $currentLower -}} +{{- else -}} +{{- /* Check for prefix matches (at least 2 characters) */ -}} +{{- range $validType := $validTypes -}} +{{- $validLower := lower $validType -}} +{{- if and (gt (len $currentLower) 1) (hasPrefix $validLower (substr 0 2 $currentLower)) -}} +{{- $suggestion = $validType -}} +{{- end -}} +{{- if and (gt (len $currentLower) 2) (hasPrefix $validLower (substr 0 3 $currentLower)) -}} +{{- $suggestion = $validType -}} +{{- end -}} +{{- /* Check if valid type contains the input or vice versa */ -}} +{{- if and (gt (len $currentLower) 2) (contains $validLower $currentLower) -}} +{{- $suggestion = $validType -}} +{{- end -}} +{{- if and (gt (len $currentLower) 2) (contains $currentLower $validLower) -}} +{{- $suggestion = $validType -}} +{{- end -}} +{{- end -}} +{{- end -}} +{{- /* Add suggestion to error message if found */ -}} +{{- if $suggestion -}} +{{- $errorMsg = printf "%s\n\n⚠️ Did you mean '%s'?" $errorMsg $suggestion -}} +{{- end -}} +{{- /* Use suggestion if available, otherwise use lodestar as example */ -}} +{{- $exampleType := "lodestar" -}} +{{- if $suggestion -}} +{{- $exampleType = $suggestion -}} +{{- end -}} +{{- $errorMsg = printf "%s\n\nHow to fix this:\n • Using --set flag:\n --set validatorClient.type=%s\n\n • In your values.yaml file:\n validatorClient:\n type: %s\n\n • Using custom values file:\n helm install my-dv-pod obol/dv-pod -f myvalues.yaml\n\nFor more information, see the Validator Client section in charts/dv-pod/README.md\n" $errorMsg $exampleType $exampleType -}} +{{- fail $errorMsg -}} {{- end -}} {{- end -}} {{- end -}} diff --git a/charts/dv-pod/templates/hooks-enr-job.yaml b/charts/dv-pod/templates/hooks-enr-job.yaml index 6fd1c8e..7db3ff4 100644 --- a/charts/dv-pod/templates/hooks-enr-job.yaml +++ b/charts/dv-pod/templates/hooks-enr-job.yaml @@ -207,7 +207,7 @@ spec: if [ -f /enr-shared/generate-enr-only ]; then echo "INFO: Adding missing public ENR field to existing secret '$SECRET_NAME'..." # Use kubectl patch to add only the missing field - kubectl patch secret "${SECRET_NAME}" -n "${NAMESPACE}" --type='merge' -p="{\"data\":{\"${ENR_PUBLIC_ADDRESS_DATA_NAME}\":\"$(echo -n "$PUBLIC_ENR" | base64 -w 0)\"}}" + kubectl patch secret "${SECRET_NAME}" -n "${NAMESPACE}" --type='merge' --validate=false -p="{\"data\":{\"${ENR_PUBLIC_ADDRESS_DATA_NAME}\":\"$(echo -n "$PUBLIC_ENR" | base64 -w 0)\"}}" echo "Secret '$SECRET_NAME' updated with missing ENR field successfully." else # Check again if the secret already exists (race condition protection) @@ -223,7 +223,7 @@ spec: --namespace="${NAMESPACE}" \ --from-literal=charon-enr-private-key="${PRIVATE_KEY}" \ --from-literal=enr="${PUBLIC_ENR}" \ - --dry-run=client -o yaml | kubectl apply -f - + --dry-run=client -o yaml | kubectl apply --validate=false -f - echo "Secret '$SECRET_NAME' created successfully." fi diff --git a/charts/dv-pod/templates/statefulset.yaml b/charts/dv-pod/templates/statefulset.yaml index bbf48a4..827d90c 100644 --- a/charts/dv-pod/templates/statefulset.yaml +++ b/charts/dv-pod/templates/statefulset.yaml @@ -517,8 +517,9 @@ spec: {{- if and .Values.charon.beaconNodeHeaders (not .Values.charon.beaconNodeHeadersSecretName) }} --beacon-node-headers={{ .Values.charon.beaconNodeHeaders | quote }} {{- end }} - {{- if .Values.charon.fallbackBeaconNodeEndpoints }} - --fallback-beacon-node-endpoints={{ include "dv-pod.fallbackBeaconNodeEndpoints" . }} + {{- $fallbackEndpoints := include "dv-pod.fallbackBeaconNodeEndpoints" . }} + {{- if $fallbackEndpoints }} + --fallback-beacon-node-endpoints={{ $fallbackEndpoints }} {{- end }} {{- if .Values.charon.lockFile }} --lock-file={{ .Values.charon.lockFile }} diff --git a/charts/dv-pod/values-examples/with-target-config-hash.yaml b/charts/dv-pod/values-examples/with-target-config-hash.yaml index b25e9b7..10465af 100644 --- a/charts/dv-pod/values-examples/with-target-config-hash.yaml +++ b/charts/dv-pod/values-examples/with-target-config-hash.yaml @@ -42,4 +42,4 @@ charon: retryDelayFactor: 2 # Other standard configurations -chainId: "11155111" # Sepolia +network: sepolia diff --git a/charts/dv-pod/values.yaml b/charts/dv-pod/values.yaml index fbc4aad..6b21332 100644 --- a/charts/dv-pod/values.yaml +++ b/charts/dv-pod/values.yaml @@ -46,8 +46,12 @@ charon: # -- Fallback beacon node endpoints (optional) # These will be used if the primary beaconNodeEndpoints are unavailable + # If not specified, intelligent defaults based on network will be used: + # - mainnet: https://ethereum-beacon-api.publicnode.com + # - sepolia: https://ethereum-sepolia-beacon-api.publicnode.com + # - hoodi: https://ethereum-hoodi-beacon-api.publicnode.com fallbackBeaconNodeEndpoints: - # Example: + # Example for custom fallback endpoints: - "https://ethereum-beacon-api.publicnode.com" # - "http://backup-beacon-2:5052"