From 903a8ac4aa83a9f3561e1a497c245c5786d998e1 Mon Sep 17 00:00:00 2001 From: bussyjd Date: Mon, 29 Sep 2025 22:41:39 +0400 Subject: [PATCH 01/10] feat: add support for DKG sidecar target config hash Add ability to specify target config hash via --set charon.dkgSidecar.targetConfigHash which exposes TARGET_CONFIG_HASH environment variable to the DKG sidecar runtime. --- charts/dv-pod/templates/statefulset.yaml | 4 ++++ charts/dv-pod/values.yaml | 2 ++ 2 files changed, 6 insertions(+) diff --git a/charts/dv-pod/templates/statefulset.yaml b/charts/dv-pod/templates/statefulset.yaml index 08970d0b..e0e7a2ce 100644 --- a/charts/dv-pod/templates/statefulset.yaml +++ b/charts/dv-pod/templates/statefulset.yaml @@ -106,6 +106,10 @@ spec: value: "/charon-data/charon-enr-private-key" - name: CHAIN_ID value: "{{ .Values.chainId }}" + {{- if .Values.charon.dkgSidecar.targetConfigHash }} + - name: TARGET_CONFIG_HASH + value: "{{ .Values.charon.dkgSidecar.targetConfigHash }}" + {{- end }} volumeMounts: - name: enr-data mountPath: /enr-from-job diff --git a/charts/dv-pod/values.yaml b/charts/dv-pod/values.yaml index 2f5ce6f4..cacb2f62 100644 --- a/charts/dv-pod/values.yaml +++ b/charts/dv-pod/values.yaml @@ -272,6 +272,8 @@ charon: pageLimit: 10 # -- API endpoint for the Obol network to fetch cluster definitions apiEndpoint: "https://api.obol.tech" + # -- Target config hash for DKG validation (optional) + targetConfigHash: "" # -- Resources for the cluster poller init container resources: requests: From 8018b3ee26fbddc42eeed1ccf7ac796ae6b5b9f1 Mon Sep 17 00:00:00 2001 From: bussyjd Date: Mon, 29 Sep 2025 22:51:31 +0400 Subject: [PATCH 02/10] docs: update helm-docs generated README files --- charts/charon-cluster/README.md | 4 ++-- charts/charon/README.md | 4 ++-- charts/dv-pod/README.md | 3 ++- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/charts/charon-cluster/README.md b/charts/charon-cluster/README.md index b91e4b11..a2c49caf 100755 --- a/charts/charon-cluster/README.md +++ b/charts/charon-cluster/README.md @@ -2,7 +2,7 @@ Charon Cluster =========== -![Version: 0.3.6](https://img.shields.io/badge/Version-0.3.6-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.5.1](https://img.shields.io/badge/AppVersion-1.5.1-informational?style=flat-square) +![Version: 0.3.8](https://img.shields.io/badge/Version-0.3.8-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.6.1](https://img.shields.io/badge/AppVersion-1.6.1-informational?style=flat-square) Charon is an open-source Ethereum Distributed validator middleware written in golang. This chart deploys a full Charon cluster. @@ -52,7 +52,7 @@ Charon is an open-source Ethereum Distributed validator middleware written in go | config.validatorApiAddress | string | `"0.0.0.0:3600"` | Listening address (ip and port) for validator-facing traffic proxying the beacon-node API. (default "127.0.0.1:3600") | | containerSecurityContext | object | See `values.yaml` | The security context for containers | | fullnameOverride | string | `""` | Provide a name to substitute for the full names of resources | -| image | object | `{"pullPolicy":"IfNotPresent","repository":"obolnetwork/charon","tag":"v1.5.1"}` | Charon image repository, pull policy, and tag version | +| image | object | `{"pullPolicy":"IfNotPresent","repository":"obolnetwork/charon","tag":"v1.6.1"}` | Charon image repository, pull policy, and tag version | | imagePullSecrets | list | `[]` | Credentials to fetch images from private registry # ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ | | livenessProbe | object | `{"enabled":false,"httpGet":{"path":"/livez"},"initialDelaySeconds":10,"periodSeconds":5}` | Configure liveness probes # ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/ | | nameOverride | string | `""` | Provide a name in place of lighthouse for `app:` labels | diff --git a/charts/charon/README.md b/charts/charon/README.md index 24fe2636..2fc85158 100755 --- a/charts/charon/README.md +++ b/charts/charon/README.md @@ -2,7 +2,7 @@ Charon =========== -![Version: 0.4.6](https://img.shields.io/badge/Version-0.4.6-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.5.1](https://img.shields.io/badge/AppVersion-1.5.1-informational?style=flat-square) +![Version: 0.4.8](https://img.shields.io/badge/Version-0.4.8-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.6.1](https://img.shields.io/badge/AppVersion-1.6.1-informational?style=flat-square) Charon is an open-source Ethereum Distributed validator middleware written in golang. @@ -56,7 +56,7 @@ Charon is an open-source Ethereum Distributed validator middleware written in go | extraVolumes | list | `[]` | Additional volumes | | fullnameOverride | string | `""` | Provide a name to substitute for the full names of resources | | httpPort | int | `3600` | HTTP Port | -| image | object | `{"pullPolicy":"IfNotPresent","repository":"obolnetwork/charon","tag":"v1.5.1"}` | Charon image repository, pull policy, and tag version | +| image | object | `{"pullPolicy":"IfNotPresent","repository":"obolnetwork/charon","tag":"v1.6.1"}` | Charon image repository, pull policy, and tag version | | imagePullSecrets | list | `[]` | Credentials to fetch images from private registry # ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ | | initContainers | list | `[]` | Additional init containers | | jaegerPort | int | `6831` | Jaeger Port | diff --git a/charts/dv-pod/README.md b/charts/dv-pod/README.md index d6ec5c3e..40fa2b23 100644 --- a/charts/dv-pod/README.md +++ b/charts/dv-pod/README.md @@ -28,7 +28,7 @@ A Helm chart for deploying a single distributed validator pod with Charon middle | charon.beaconNodeHeadersSecretName | string | `""` | Optional: Name of the secret containing beacon node headers Use this for secure credential storage instead of beaconNodeHeaders The secret should contain a key specified by beaconNodeHeadersSecretKey | | charon.builderApi | string | `""` | Enables the builder api. Will only produce builder blocks. Builder API must also be enabled on the validator client. Beacon node must be connected to a builder-relay to access the builder network. | | charon.directConnectionEnabled | string | `"true"` | If enabled, it will set p2pExternalHostname value to the pod name and enable direct connection between cluster nodes. This is useful for deployments where pods can directly communicate with each other. | -| charon.dkgSidecar | object | `{"apiEndpoint":"https://api.obol.tech","enabled":true,"image":{"pullPolicy":"IfNotPresent","repository":"ghcr.io/obolnetwork/charon-dkg-sidecar","tag":"v1.0.0"},"initialRetryDelaySeconds":10,"maxRetryDelaySeconds":300,"pageLimit":10,"resources":{"limits":{"cpu":"200m","memory":"256Mi"},"requests":{"cpu":"50m","memory":"128Mi"}},"retryDelayFactor":2,"retryDelaySeconds":10,"serviceAccount":{"create":true}}` | Configuration for the DKG Sidecar init container This init container orchestrates the Distributed Key Generation (DKG) process for Charon clusters. The sidecar has three operating modes: 1. If cluster-lock.json exists: Exits immediately (cluster already initialized) 2. If cluster-definition.json exists: Attempts DKG with the existing definition 3. If neither exists: Polls the Obol API for cluster invites and runs DKG when ready To provide a pre-existing cluster-lock and skip DKG: 1. Create a configMap: kubectl create configmap cluster-lock --from-file=cluster-lock.json 2. The sidecar will detect the lock file and exit, allowing Charon to start immediately To provide a cluster-definition without running DKG through the API: 1. Mount your cluster-definition.json in /charon-data/ 2. The sidecar will run 'charon dkg' to generate the cluster-lock.json Note: When providing a pre-existing cluster-lock.json, you must also ensure the associated validator keys are available in the charon-data volume. | +| charon.dkgSidecar | object | `{"apiEndpoint":"https://api.obol.tech","enabled":true,"image":{"pullPolicy":"IfNotPresent","repository":"ghcr.io/obolnetwork/charon-dkg-sidecar","tag":"v1.0.0"},"initialRetryDelaySeconds":10,"maxRetryDelaySeconds":300,"pageLimit":10,"resources":{"limits":{"cpu":"200m","memory":"256Mi"},"requests":{"cpu":"50m","memory":"128Mi"}},"retryDelayFactor":2,"retryDelaySeconds":10,"serviceAccount":{"create":true},"targetConfigHash":""}` | Configuration for the DKG Sidecar init container This init container orchestrates the Distributed Key Generation (DKG) process for Charon clusters. The sidecar has three operating modes: 1. If cluster-lock.json exists: Exits immediately (cluster already initialized) 2. If cluster-definition.json exists: Attempts DKG with the existing definition 3. If neither exists: Polls the Obol API for cluster invites and runs DKG when ready To provide a pre-existing cluster-lock and skip DKG: 1. Create a configMap: kubectl create configmap cluster-lock --from-file=cluster-lock.json 2. The sidecar will detect the lock file and exit, allowing Charon to start immediately To provide a cluster-definition without running DKG through the API: 1. Mount your cluster-definition.json in /charon-data/ 2. The sidecar will run 'charon dkg' to generate the cluster-lock.json Note: When providing a pre-existing cluster-lock.json, you must also ensure the associated validator keys are available in the charon-data volume. | | charon.dkgSidecar.apiEndpoint | string | `"https://api.obol.tech"` | API endpoint for the Obol network to fetch cluster definitions | | charon.dkgSidecar.image.repository | string | `"ghcr.io/obolnetwork/charon-dkg-sidecar"` | Image repository for the DKG sidecar | | charon.dkgSidecar.initialRetryDelaySeconds | int | `10` | Initial delay in seconds before the first retry of a polling cycle. | @@ -38,6 +38,7 @@ A Helm chart for deploying a single distributed validator pod with Charon middle | charon.dkgSidecar.retryDelayFactor | int | `2` | Factor by which the retry delay increases after each polling cycle (e.g., 2 for doubling). | | charon.dkgSidecar.retryDelaySeconds | int | `10` | Delay in seconds between polling retries | | charon.dkgSidecar.serviceAccount | object | `{"create":true}` | Service account settings for test pods | +| charon.dkgSidecar.targetConfigHash | string | `""` | Target config hash for DKG validation (optional) | | charon.enr.existingSecret | object | `{"dataKey":"private-key","name":""}` | Point to an existing Kubernetes secret that holds the ENR private key. If 'privateKey' above is not set and this 'existingSecret.name' is provided, 'generate' is ignored. NOTE: If not set, the chart will automatically check for a secret named 'charon-enr-private-key' (configurable via secrets.defaultEnrPrivateKey) and use it if it exists. | | charon.enr.generate | object | `{"enabled":true,"image":{"pullPolicy":"IfNotPresent","repository":"obolnetwork/charon","tag":"v1.5.1"},"kubectlImage":{"pullPolicy":"IfNotPresent","repository":"bitnami/kubectl","tag":"1.33.3"}}` | Enable automatic generation of an ENR private key. This is active only if 'privateKey' and 'existingSecret.name' are NOT set. The generated key will be stored in a secret (e.g., "{{ .Release.Name }}-dv-pod-enr-key") with data keys 'private-key' (for the hex key) and 'public-enr' (for the ENR string). | | charon.enr.generate.kubectlImage | object | `{"pullPolicy":"IfNotPresent","repository":"bitnami/kubectl","tag":"1.33.3"}` | Image to use for kubectl operations within the ENR generation job This image must contain a compatible kubectl binary. | From 3acec644e54c6496bef9e795430e7695d0032c96 Mon Sep 17 00:00:00 2001 From: JeanDaniel Bussy Date: Tue, 30 Sep 2025 11:05:36 +0400 Subject: [PATCH 03/10] Update charts/dv-pod/README.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Oisín Kyne <4981644+OisinKyne@users.noreply.github.com> Signed-off-by: JeanDaniel Bussy --- charts/dv-pod/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/dv-pod/README.md b/charts/dv-pod/README.md index 40fa2b23..09be7f0f 100644 --- a/charts/dv-pod/README.md +++ b/charts/dv-pod/README.md @@ -38,7 +38,7 @@ A Helm chart for deploying a single distributed validator pod with Charon middle | charon.dkgSidecar.retryDelayFactor | int | `2` | Factor by which the retry delay increases after each polling cycle (e.g., 2 for doubling). | | charon.dkgSidecar.retryDelaySeconds | int | `10` | Delay in seconds between polling retries | | charon.dkgSidecar.serviceAccount | object | `{"create":true}` | Service account settings for test pods | -| charon.dkgSidecar.targetConfigHash | string | `""` | Target config hash for DKG validation (optional) | +| charon.dkgSidecar.targetConfigHash | string | `""` | Target cluster definition config hash for the DKG sidecar to partake in (optional) | | charon.enr.existingSecret | object | `{"dataKey":"private-key","name":""}` | Point to an existing Kubernetes secret that holds the ENR private key. If 'privateKey' above is not set and this 'existingSecret.name' is provided, 'generate' is ignored. NOTE: If not set, the chart will automatically check for a secret named 'charon-enr-private-key' (configurable via secrets.defaultEnrPrivateKey) and use it if it exists. | | charon.enr.generate | object | `{"enabled":true,"image":{"pullPolicy":"IfNotPresent","repository":"obolnetwork/charon","tag":"v1.5.1"},"kubectlImage":{"pullPolicy":"IfNotPresent","repository":"bitnami/kubectl","tag":"1.33.3"}}` | Enable automatic generation of an ENR private key. This is active only if 'privateKey' and 'existingSecret.name' are NOT set. The generated key will be stored in a secret (e.g., "{{ .Release.Name }}-dv-pod-enr-key") with data keys 'private-key' (for the hex key) and 'public-enr' (for the ENR string). | | charon.enr.generate.kubectlImage | object | `{"pullPolicy":"IfNotPresent","repository":"bitnami/kubectl","tag":"1.33.3"}` | Image to use for kubectl operations within the ENR generation job This image must contain a compatible kubectl binary. | From ed14e56f8c7e79701c90db76def6e8954f922154 Mon Sep 17 00:00:00 2001 From: bussyjd Date: Tue, 30 Sep 2025 11:43:51 +0400 Subject: [PATCH 04/10] docs: improve targetConfigHash description --- charts/dv-pod/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/dv-pod/values.yaml b/charts/dv-pod/values.yaml index cacb2f62..7171c6ab 100644 --- a/charts/dv-pod/values.yaml +++ b/charts/dv-pod/values.yaml @@ -272,7 +272,7 @@ charon: pageLimit: 10 # -- API endpoint for the Obol network to fetch cluster definitions apiEndpoint: "https://api.obol.tech" - # -- Target config hash for DKG validation (optional) + # -- Target cluster definition config hash for the DKG sidecar to partake in (optional) targetConfigHash: "" # -- Resources for the cluster poller init container resources: From 073da61ecd0d6efdf6931a1b698bcaa0575d289e Mon Sep 17 00:00:00 2001 From: bussyjd Date: Wed, 1 Oct 2025 19:15:39 +0400 Subject: [PATCH 05/10] fix: replace invalid kubectl image with alpine/k8s Replace bitnami/kubectl:1.33.3 (non-existent version) with alpine/k8s:1.31.4. The bitnami/kubectl image was causing ErrImagePull failures in the ENR generation job. alpine/k8s provides both kubectl binary and shell required by the init containers. --- charts/dv-pod/values.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/charts/dv-pod/values.yaml b/charts/dv-pod/values.yaml index 7171c6ab..b2742e80 100644 --- a/charts/dv-pod/values.yaml +++ b/charts/dv-pod/values.yaml @@ -319,10 +319,10 @@ charon: tag: "v1.5.1" pullPolicy: "IfNotPresent" # -- Image to use for kubectl operations within the ENR generation job - # This image must contain a compatible kubectl binary. + # This image must contain a compatible kubectl binary and shell. kubectlImage: - repository: "bitnami/kubectl" - tag: "1.33.3" # Pinned to match stable Kubernetes v1.33.3 + repository: "alpine/k8s" + tag: "1.31.4" pullPolicy: "IfNotPresent" # --- Charon command line options --- From 02d157ce3344ef2633c74587c5aa1a54149bdcaf Mon Sep 17 00:00:00 2001 From: bussyjd Date: Thu, 9 Oct 2025 12:40:36 +0400 Subject: [PATCH 06/10] Merge main --- charts/dv-pod/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/charts/dv-pod/README.md b/charts/dv-pod/README.md index 09be7f0f..0ea657f8 100644 --- a/charts/dv-pod/README.md +++ b/charts/dv-pod/README.md @@ -40,8 +40,8 @@ A Helm chart for deploying a single distributed validator pod with Charon middle | charon.dkgSidecar.serviceAccount | object | `{"create":true}` | Service account settings for test pods | | charon.dkgSidecar.targetConfigHash | string | `""` | Target cluster definition config hash for the DKG sidecar to partake in (optional) | | charon.enr.existingSecret | object | `{"dataKey":"private-key","name":""}` | Point to an existing Kubernetes secret that holds the ENR private key. If 'privateKey' above is not set and this 'existingSecret.name' is provided, 'generate' is ignored. NOTE: If not set, the chart will automatically check for a secret named 'charon-enr-private-key' (configurable via secrets.defaultEnrPrivateKey) and use it if it exists. | -| charon.enr.generate | object | `{"enabled":true,"image":{"pullPolicy":"IfNotPresent","repository":"obolnetwork/charon","tag":"v1.5.1"},"kubectlImage":{"pullPolicy":"IfNotPresent","repository":"bitnami/kubectl","tag":"1.33.3"}}` | Enable automatic generation of an ENR private key. This is active only if 'privateKey' and 'existingSecret.name' are NOT set. The generated key will be stored in a secret (e.g., "{{ .Release.Name }}-dv-pod-enr-key") with data keys 'private-key' (for the hex key) and 'public-enr' (for the ENR string). | -| charon.enr.generate.kubectlImage | object | `{"pullPolicy":"IfNotPresent","repository":"bitnami/kubectl","tag":"1.33.3"}` | Image to use for kubectl operations within the ENR generation job This image must contain a compatible kubectl binary. | +| charon.enr.generate | object | `{"enabled":true,"image":{"pullPolicy":"IfNotPresent","repository":"obolnetwork/charon","tag":"v1.5.1"},"kubectlImage":{"pullPolicy":"IfNotPresent","repository":"alpine/k8s","tag":"1.31.4"}}` | Enable automatic generation of an ENR private key. This is active only if 'privateKey' and 'existingSecret.name' are NOT set. The generated key will be stored in a secret (e.g., "{{ .Release.Name }}-dv-pod-enr-key") with data keys 'private-key' (for the hex key) and 'public-enr' (for the ENR string). | +| charon.enr.generate.kubectlImage | object | `{"pullPolicy":"IfNotPresent","repository":"alpine/k8s","tag":"1.31.4"}` | Image to use for kubectl operations within the ENR generation job This image must contain a compatible kubectl binary and shell. | | 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 | `""` | Execution client RPC endpoint URL (e.g., your Ethereum execution client) Note: Charon currently only supports a single execution endpoint | From 2c97bcad23e006e3db48bb448dc9cc4101e8de05 Mon Sep 17 00:00:00 2001 From: bussyjd Date: Thu, 9 Oct 2025 12:41:10 +0400 Subject: [PATCH 07/10] Merge main --- charts/charon-cluster/README.md | 4 ++-- charts/charon/README.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/charts/charon-cluster/README.md b/charts/charon-cluster/README.md index a2c49caf..f8dbffa3 100755 --- a/charts/charon-cluster/README.md +++ b/charts/charon-cluster/README.md @@ -2,7 +2,7 @@ Charon Cluster =========== -![Version: 0.3.8](https://img.shields.io/badge/Version-0.3.8-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.6.1](https://img.shields.io/badge/AppVersion-1.6.1-informational?style=flat-square) +![Version: 0.3.9](https://img.shields.io/badge/Version-0.3.9-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.7.0](https://img.shields.io/badge/AppVersion-1.7.0-informational?style=flat-square) Charon is an open-source Ethereum Distributed validator middleware written in golang. This chart deploys a full Charon cluster. @@ -52,7 +52,7 @@ Charon is an open-source Ethereum Distributed validator middleware written in go | config.validatorApiAddress | string | `"0.0.0.0:3600"` | Listening address (ip and port) for validator-facing traffic proxying the beacon-node API. (default "127.0.0.1:3600") | | containerSecurityContext | object | See `values.yaml` | The security context for containers | | fullnameOverride | string | `""` | Provide a name to substitute for the full names of resources | -| image | object | `{"pullPolicy":"IfNotPresent","repository":"obolnetwork/charon","tag":"v1.6.1"}` | Charon image repository, pull policy, and tag version | +| image | object | `{"pullPolicy":"IfNotPresent","repository":"obolnetwork/charon","tag":"v1.7.0"}` | Charon image repository, pull policy, and tag version | | imagePullSecrets | list | `[]` | Credentials to fetch images from private registry # ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ | | livenessProbe | object | `{"enabled":false,"httpGet":{"path":"/livez"},"initialDelaySeconds":10,"periodSeconds":5}` | Configure liveness probes # ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/ | | nameOverride | string | `""` | Provide a name in place of lighthouse for `app:` labels | diff --git a/charts/charon/README.md b/charts/charon/README.md index 2fc85158..406544a0 100755 --- a/charts/charon/README.md +++ b/charts/charon/README.md @@ -2,7 +2,7 @@ Charon =========== -![Version: 0.4.8](https://img.shields.io/badge/Version-0.4.8-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.6.1](https://img.shields.io/badge/AppVersion-1.6.1-informational?style=flat-square) +![Version: 0.4.9](https://img.shields.io/badge/Version-0.4.9-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.7.0](https://img.shields.io/badge/AppVersion-1.7.0-informational?style=flat-square) Charon is an open-source Ethereum Distributed validator middleware written in golang. @@ -56,7 +56,7 @@ Charon is an open-source Ethereum Distributed validator middleware written in go | extraVolumes | list | `[]` | Additional volumes | | fullnameOverride | string | `""` | Provide a name to substitute for the full names of resources | | httpPort | int | `3600` | HTTP Port | -| image | object | `{"pullPolicy":"IfNotPresent","repository":"obolnetwork/charon","tag":"v1.6.1"}` | Charon image repository, pull policy, and tag version | +| image | object | `{"pullPolicy":"IfNotPresent","repository":"obolnetwork/charon","tag":"v1.7.0"}` | Charon image repository, pull policy, and tag version | | imagePullSecrets | list | `[]` | Credentials to fetch images from private registry # ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ | | initContainers | list | `[]` | Additional init containers | | jaegerPort | int | `6831` | Jaeger Port | From 3df8ea09c12747867357b0eb129178af6a9fd1d0 Mon Sep 17 00:00:00 2001 From: bussyjd Date: Thu, 9 Oct 2025 13:11:39 +0400 Subject: [PATCH 08/10] feat(dv-pod): add validation for targetConfigHash parameter Add comprehensive format validation for charon.dkgSidecar.targetConfigHash: - Must start with '0x' prefix - Must be exactly 66 characters (0x + 64 hex digits) - Must contain only valid hexadecimal characters (0-9, a-f, A-F) Changes: - Add validation rules in validate.yaml using hasPrefix, len, and regexMatch - Update values.yaml documentation with format requirements and example - Create example values file showing proper usage - Regenerate README with helm-docs to reflect new documentation Format: 0x7f0fd29abb11674b4e61000de26bff3600237aab0402427bd1409756665c2115 Represents: 32-byte (256-bit) Keccak256 hash Validation catches common errors: - Missing 0x prefix - Wrong length - Invalid hex characters - Empty string is allowed (optional parameter) --- charts/dv-pod/README.md | 2 +- charts/dv-pod/templates/validate.yaml | 12 +++++ .../with-target-config-hash.yaml | 45 +++++++++++++++++++ charts/dv-pod/values.yaml | 4 +- 4 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 charts/dv-pod/values-examples/with-target-config-hash.yaml diff --git a/charts/dv-pod/README.md b/charts/dv-pod/README.md index 0ea657f8..0d7d07ab 100644 --- a/charts/dv-pod/README.md +++ b/charts/dv-pod/README.md @@ -38,7 +38,7 @@ A Helm chart for deploying a single distributed validator pod with Charon middle | charon.dkgSidecar.retryDelayFactor | int | `2` | Factor by which the retry delay increases after each polling cycle (e.g., 2 for doubling). | | charon.dkgSidecar.retryDelaySeconds | int | `10` | Delay in seconds between polling retries | | charon.dkgSidecar.serviceAccount | object | `{"create":true}` | Service account settings for test pods | -| charon.dkgSidecar.targetConfigHash | string | `""` | Target cluster definition config hash for the DKG sidecar to partake in (optional) | +| charon.dkgSidecar.targetConfigHash | string | `""` | Target cluster definition config hash for the DKG sidecar to partake in (optional). Must be a valid 32-byte hex hash with 0x prefix (66 characters total). Example: "0x7f0fd29abb11674b4e61000de26bff3600237aab0402427bd1409756665c2115" | | charon.enr.existingSecret | object | `{"dataKey":"private-key","name":""}` | Point to an existing Kubernetes secret that holds the ENR private key. If 'privateKey' above is not set and this 'existingSecret.name' is provided, 'generate' is ignored. NOTE: If not set, the chart will automatically check for a secret named 'charon-enr-private-key' (configurable via secrets.defaultEnrPrivateKey) and use it if it exists. | | charon.enr.generate | object | `{"enabled":true,"image":{"pullPolicy":"IfNotPresent","repository":"obolnetwork/charon","tag":"v1.5.1"},"kubectlImage":{"pullPolicy":"IfNotPresent","repository":"alpine/k8s","tag":"1.31.4"}}` | Enable automatic generation of an ENR private key. This is active only if 'privateKey' and 'existingSecret.name' are NOT set. The generated key will be stored in a secret (e.g., "{{ .Release.Name }}-dv-pod-enr-key") with data keys 'private-key' (for the hex key) and 'public-enr' (for the ENR string). | | charon.enr.generate.kubectlImage | object | `{"pullPolicy":"IfNotPresent","repository":"alpine/k8s","tag":"1.31.4"}` | Image to use for kubectl operations within the ENR generation job This image must contain a compatible kubectl binary and shell. | diff --git a/charts/dv-pod/templates/validate.yaml b/charts/dv-pod/templates/validate.yaml index 7c161cab..c8b139b8 100644 --- a/charts/dv-pod/templates/validate.yaml +++ b/charts/dv-pod/templates/validate.yaml @@ -13,3 +13,15 @@ {{- fail ".Values.centralMonitoring.token is required" }} {{- end }} {{- end }} + +{{- if .Values.charon.dkgSidecar.targetConfigHash }} + {{- if not (hasPrefix "0x" .Values.charon.dkgSidecar.targetConfigHash) }} + {{- fail "charon.dkgSidecar.targetConfigHash must start with '0x'" }} + {{- end }} + {{- if ne (len .Values.charon.dkgSidecar.targetConfigHash) 66 }} + {{- fail "charon.dkgSidecar.targetConfigHash must be exactly 66 characters (0x + 64 hex digits)" }} + {{- end }} + {{- if not (regexMatch "^0x[0-9a-fA-F]{64}$" .Values.charon.dkgSidecar.targetConfigHash) }} + {{- fail "charon.dkgSidecar.targetConfigHash must be a valid hex hash (0x followed by 64 hexadecimal characters)" }} + {{- end }} +{{- end }} diff --git a/charts/dv-pod/values-examples/with-target-config-hash.yaml b/charts/dv-pod/values-examples/with-target-config-hash.yaml new file mode 100644 index 00000000..b25e9b75 --- /dev/null +++ b/charts/dv-pod/values-examples/with-target-config-hash.yaml @@ -0,0 +1,45 @@ +# Example: Using targetConfigHash to participate in a specific cluster +# +# The targetConfigHash parameter allows your DKG sidecar to automatically join a specific +# cluster definition when polling the Obol API. This is useful when: +# +# 1. Multiple clusters are available for your operator address +# 2. You want to ensure the sidecar joins the correct cluster +# 3. You're coordinating with other operators on a specific cluster configuration +# +# How to get your targetConfigHash: +# - Visit https://launchpad.obol.tech +# - Create or view your cluster definition +# - Copy the config hash from the cluster details +# +# Format Requirements: +# - Must start with "0x" +# - Must be exactly 66 characters total (0x + 64 hex digits) +# - Represents a 32-byte Keccak256 hash +# +# Example deployment: +# helm upgrade --install my-dv-pod obol/dv-pod \ +# --set charon.dkgSidecar.targetConfigHash="0x7f0fd29abb11674b4e61000de26bff3600237aab0402427bd1409756665c2115" \ +# --set charon.operatorAddress="0xYourOperatorAddress" \ +# -n dv-namespace --create-namespace + +# Standard Charon configuration +charon: + operatorAddress: "0x0000000000000000000000000000000000000000" # Replace with your operator address + beaconNodeEndpoints: + - "http://your-beacon-node:5052" + + # DKG Sidecar configuration with target config hash + dkgSidecar: + enabled: true + # Specify the exact cluster definition to join + targetConfigHash: "0x7f0fd29abb11674b4e61000de26bff3600237aab0402427bd1409756665c2115" + apiEndpoint: "https://api.obol.tech" + + # Adjust polling intervals if needed + initialRetryDelaySeconds: 10 + maxRetryDelaySeconds: 300 + retryDelayFactor: 2 + +# Other standard configurations +chainId: "11155111" # Sepolia diff --git a/charts/dv-pod/values.yaml b/charts/dv-pod/values.yaml index b2742e80..a13d459c 100644 --- a/charts/dv-pod/values.yaml +++ b/charts/dv-pod/values.yaml @@ -272,7 +272,9 @@ charon: pageLimit: 10 # -- API endpoint for the Obol network to fetch cluster definitions apiEndpoint: "https://api.obol.tech" - # -- Target cluster definition config hash for the DKG sidecar to partake in (optional) + # -- Target cluster definition config hash for the DKG sidecar to partake in (optional). + # Must be a valid 32-byte hex hash with 0x prefix (66 characters total). + # Example: "0x7f0fd29abb11674b4e61000de26bff3600237aab0402427bd1409756665c2115" targetConfigHash: "" # -- Resources for the cluster poller init container resources: From 9e8a4dcb1d86a11b8eb69ee5786794dcbbe10f4e Mon Sep 17 00:00:00 2001 From: bussyjd Date: Thu, 9 Oct 2025 14:08:04 +0400 Subject: [PATCH 09/10] test(dv-pod): add test coverage for targetConfigHash parameter Add comprehensive test coverage for charon.dkgSidecar.targetConfigHash: 1. New test file: test-target-config-hash-set.yaml - Dedicated test to verify TARGET_CONFIG_HASH env var is set correctly - Validates env var matches expected value - Checks format: 0x prefix, 66 character length - Uses busybox for lightweight verification - Only runs when tests.dkgSidecar.targetConfigHash is set 2. Updated test-dkg-sidecar-fresh-state.yaml - Added TARGET_CONFIG_HASH env var to DKG sidecar init container - Ensures test environment matches production StatefulSet configuration - Conditional on tests.dkgSidecar.targetConfigHash value 3. Added test configuration to values.yaml - New tests.dkgSidecar.targetConfigHash field - Default test hash: 0x7f0fd29abb11674b4e61000de26bff3600237aab0402427bd1409756665c2115 - Enables both new test and passes hash to existing DKG sidecar tests Test coverage: Validates runtime behavior of targetConfigHash parameter, complements existing template-time validation in validate.yaml. --- .../tests/test-dkg-sidecar-fresh-state.yaml | 4 ++ .../tests/test-target-config-hash-set.yaml | 59 +++++++++++++++++++ charts/dv-pod/values.yaml | 3 + 3 files changed, 66 insertions(+) create mode 100644 charts/dv-pod/templates/tests/test-target-config-hash-set.yaml diff --git a/charts/dv-pod/templates/tests/test-dkg-sidecar-fresh-state.yaml b/charts/dv-pod/templates/tests/test-dkg-sidecar-fresh-state.yaml index 82866f37..cb1c6540 100644 --- a/charts/dv-pod/templates/tests/test-dkg-sidecar-fresh-state.yaml +++ b/charts/dv-pod/templates/tests/test-dkg-sidecar-fresh-state.yaml @@ -69,6 +69,10 @@ spec: valueFrom: fieldRef: fieldPath: metadata.namespace + {{- if .Values.tests.dkgSidecar.targetConfigHash }} + - name: TARGET_CONFIG_HASH + value: "{{ .Values.tests.dkgSidecar.targetConfigHash }}" + {{- end }} volumeMounts: - name: charon-data mountPath: /charon-data diff --git a/charts/dv-pod/templates/tests/test-target-config-hash-set.yaml b/charts/dv-pod/templates/tests/test-target-config-hash-set.yaml new file mode 100644 index 00000000..7a3efbcd --- /dev/null +++ b/charts/dv-pod/templates/tests/test-target-config-hash-set.yaml @@ -0,0 +1,59 @@ +{{- if and .Values.tests.dkgSidecar.enabled .Values.tests.dkgSidecar.targetConfigHash }} +apiVersion: v1 +kind: Pod +metadata: + name: "{{ .Release.Name }}-target-config-hash-test" + namespace: {{ .Release.Namespace }} + annotations: + "helm.sh/hook": test + "helm.sh/hook-delete-policy": hook-succeeded + "helm.sh/hook-weight": "1" + labels: + app.kubernetes.io/name: {{ template "dv-pod.name" . }} + helm.sh/chart: {{ template "dv-pod.chart" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + app.kubernetes.io/component: test + test-scenario: target-config-hash +spec: + restartPolicy: Never + containers: + - name: verify-target-config-hash + image: busybox + command: + - sh + - -c + - | + echo "========================================" + echo "Testing TARGET_CONFIG_HASH Environment Variable" + echo "========================================" + echo "" + echo "Expected hash: {{ .Values.tests.dkgSidecar.targetConfigHash }}" + echo "Actual TARGET_CONFIG_HASH: ${TARGET_CONFIG_HASH:-}" + echo "" + + if [ -z "$TARGET_CONFIG_HASH" ]; then + echo "❌ FAIL: TARGET_CONFIG_HASH environment variable is not set" + exit 1 + fi + + EXPECTED_HASH="{{ .Values.tests.dkgSidecar.targetConfigHash }}" + + if [ "$TARGET_CONFIG_HASH" = "$EXPECTED_HASH" ]; then + echo "✅ PASS: TARGET_CONFIG_HASH matches expected value" + echo "" + echo "Hash format validation:" + echo " - Starts with 0x: $(echo $TARGET_CONFIG_HASH | grep -q '^0x' && echo 'YES' || echo 'NO')" + echo " - Length: $(echo -n $TARGET_CONFIG_HASH | wc -c) characters (expected: 66)" + echo "" + exit 0 + else + echo "❌ FAIL: TARGET_CONFIG_HASH does not match expected value" + echo " Expected: $EXPECTED_HASH" + echo " Got: $TARGET_CONFIG_HASH" + exit 1 + fi + env: + - name: TARGET_CONFIG_HASH + value: "{{ .Values.tests.dkgSidecar.targetConfigHash }}" +{{- end }} diff --git a/charts/dv-pod/values.yaml b/charts/dv-pod/values.yaml index a13d459c..84615937 100644 --- a/charts/dv-pod/values.yaml +++ b/charts/dv-pod/values.yaml @@ -503,6 +503,9 @@ tests: dkgSidecar: enabled: true operatorAddress: "0x3D1f0598943239806A251899016EAf4920d4726d" + # -- Target config hash for testing (optional) + # When set, enables test-target-config-hash-set.yaml test and passes hash to DKG sidecar tests + targetConfigHash: "0x7f0fd29abb11674b4e61000de26bff3600237aab0402427bd1409756665c2115" mockApi: port: 3001 image: From 7df826729e2168231da127eed4d4d5c6caf78178 Mon Sep 17 00:00:00 2001 From: bussyjd Date: Thu, 9 Oct 2025 14:39:33 +0400 Subject: [PATCH 10/10] docs(dv-pod): update README with test targetConfigHash parameter Regenerated README with helm-docs to include new test configuration: - tests.dkgSidecar.targetConfigHash parameter (line 140) - Documents it's for testing purposes - Shows default test hash value - Enables test-target-config-hash-set.yaml test when set --- charts/dv-pod/README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/charts/dv-pod/README.md b/charts/dv-pod/README.md index 0d7d07ab..3ae3a2ca 100644 --- a/charts/dv-pod/README.md +++ b/charts/dv-pod/README.md @@ -133,10 +133,11 @@ A Helm chart for deploying a single distributed validator pod with Charon middle | serviceMonitor.scheme | string | `"http"` | ServiceMonitor scheme | | serviceMonitor.scrapeTimeout | string | `"30s"` | ServiceMonitor scrape timeout | | serviceMonitor.tlsConfig | object | `{}` | ServiceMonitor TLS configuration | -| tests | object | `{"dkgSidecar":{"enabled":true,"hostNetwork":false,"mockApi":{"image":{"pullPolicy":"Always"},"port":3001},"operatorAddress":"0x3D1f0598943239806A251899016EAf4920d4726d","serviceAccount":{"create":true}}}` | Configuration for running Helm tests. These values are typically only used when `helm test` is run. | -| tests.dkgSidecar | object | `{"enabled":true,"hostNetwork":false,"mockApi":{"image":{"pullPolicy":"Always"},"port":3001},"operatorAddress":"0x3D1f0598943239806A251899016EAf4920d4726d","serviceAccount":{"create":true}}` | The operator address to use for DKG sidecar tests. This should be a valid Ethereum address (0x...). | +| tests | object | `{"dkgSidecar":{"enabled":true,"hostNetwork":false,"mockApi":{"image":{"pullPolicy":"Always"},"port":3001},"operatorAddress":"0x3D1f0598943239806A251899016EAf4920d4726d","serviceAccount":{"create":true},"targetConfigHash":"0x7f0fd29abb11674b4e61000de26bff3600237aab0402427bd1409756665c2115"}}` | Configuration for running Helm tests. These values are typically only used when `helm test` is run. | +| tests.dkgSidecar | object | `{"enabled":true,"hostNetwork":false,"mockApi":{"image":{"pullPolicy":"Always"},"port":3001},"operatorAddress":"0x3D1f0598943239806A251899016EAf4920d4726d","serviceAccount":{"create":true},"targetConfigHash":"0x7f0fd29abb11674b4e61000de26bff3600237aab0402427bd1409756665c2115"}` | The operator address to use for DKG sidecar tests. This should be a valid Ethereum address (0x...). | | tests.dkgSidecar.hostNetwork | bool | `false` | Host network setting for dkgSidecar test pods | | tests.dkgSidecar.serviceAccount | object | `{"create":true}` | Service account settings for test pods | +| tests.dkgSidecar.targetConfigHash | string | `"0x7f0fd29abb11674b4e61000de26bff3600237aab0402427bd1409756665c2115"` | Target config hash for testing (optional) When set, enables test-target-config-hash-set.yaml test and passes hash to DKG sidecar tests | | tolerations | object | `{}` | Tolerations for pod assignment # ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ | | updateStrategy | string | `"RollingUpdate"` | allows you to configure and disable automated rolling updates for containers, labels, resource request/limits, and annotations for the Pods in a StatefulSet. | | validatorClient | object | `{"config":{"extraArgs":[],"graffiti":"DV-Pod"},"enabled":true,"image":{"pullPolicy":"IfNotPresent","repository":"","tag":""},"keystores":{"autoCreate":true,"secretName":""},"resources":{"limits":{"cpu":"1000m","memory":"2Gi"},"requests":{"cpu":"500m","memory":"1Gi"}},"type":"lighthouse"}` | Validator client configuration |