Skip to content
Merged
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
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ This repo contains Helm Charts for deploying Obol Distributed Validator [middlew

## List of charts

- [`charon`](charts/charon)
- [`charon-cluster`](charts/charon-cluster)
- [`charon-relay`](charts/charon-relay)
- [`charon`](charts/charon) - A chart for running a single charon container.
- [`charon-cluster`](charts/charon-cluster) - A chart for running a number of charon instances.
- [`charon-relay`](charts/charon-relay) - A chart for running a charon [relay](https://docs.obol.org/learn/charon/charon-cli-reference#host-a-relay).
- [`dv-pod`](charts/dv-pod) - A chart for running a Charon client + a Validator client, with automatic DKG completion as a feature.
- [`obol-app`](charts/obol-app) - A chart for running arbitrary docker images in the [Obol Stack](https://obol.org/stack).

## Before you begin

Expand Down
8 changes: 4 additions & 4 deletions charts/dv-pod/Chart.yaml
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
apiVersion: v2
name: dv-pod
description: A Helm chart for deploying a single distributed validator pod with Charon middleware and validator client
description: A Helm chart for deploying a single distributed validator pod consisting of a Charon middleware client and validator client.
icon: https://raw.githubusercontent.com/ObolNetwork/charon/main/docs/images/charonlogo.svg
home: https://obol.tech/
home: https://docs.obol.org/
sources:
- https://github.com/ObolNetwork/charon
maintainers:
- name: Eth2DevOps
- name: DV Labs
email: devops@obol.tech
keywords:
- ethereum
Expand All @@ -31,7 +31,7 @@ type: application
# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
version: 0.2.0
version: 0.3.0


# This is the version number of the application being deployed. This version number should be
Expand Down
449 changes: 233 additions & 216 deletions charts/dv-pod/README.md

Large diffs are not rendered by default.

155 changes: 81 additions & 74 deletions charts/dv-pod/README.md.gotmpl
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ Charon Cluster

{{ template "chart.requirementsSection" . }}

{{ template "chart.valuesSection" . }}

# How to use this chart

A distributed validator cluster is composed of the following containers:
Expand All @@ -31,13 +29,13 @@ A distributed validator cluster is composed of the following containers:

This chart deploys a single distributed validator pod. You have two options for providing the required artifacts:

### Option 1: Pre-existing DKG artifacts (skip DKG process)
### Option 1: Pre-created keys and artifacts (skips DKG process)

If you already have DKG artifacts from a ceremony, you can provide them to skip the DKG process entirely.
If you already have private keys and charon artifacts from a central key creation or a DKG ceremony, you can provide them to skip the automated DKG process entirely.

#### Example 1a: From a multi-node cluster output (./cluster/ structure)
#### Example 1a: From a `charon create cluster` command output (a `./cluster/nodeN` folder structure)

When you've run DKG for multiple nodes and have a `./cluster/` directory:
When you've created keystores for a full cluster locally and have a `./cluster/` directory:

```
./cluster/
Expand All @@ -57,21 +55,26 @@ When you've run DKG for multiple nodes and have a `./cluster/` directory:
```

To deploy node0 using this chart:
```console
# Create ENR private key secret
```sh
# Put the ENR private key in a secret
kubectl create secret generic charon-enr-private-key --from-file=cluster/node0/charon-enr-private-key

# Put the private keys in a kubernetes secret
kubectl create secret generic validator-keys \
--from-file=cluster/node0/validator_keys/keystore-0.json --from-file=cluster/node0/validator_keys/keystore-0.txt

# Create the cluster lock ConfigMap (same for all nodes)
kubectl create configmap my-cluster-lock --from-file=cluster/node0/cluster-lock.json
kubectl create configmap cluster-lock --from-file=cluster/node0/cluster-lock.json

# Install the chart, referencing your ConfigMap
# Install the chart, referencing your ConfigMap and Secrets
helm install my-dv-pod obol/dv-pod \
--set configMaps.clusterlock=my-cluster-lock
--set configMaps.clusterLock=cluster-lock \
--set validatorClient.keystores.secretName=validator-keys
```

Note: The validator keys will be loaded from the persistent volume created during DKG.
That should be enough to get your DV-Pod running, and loaded with the required artifacts, assuming the default beacon node endpoint of the Obol Stack works.

#### Example 1b: From a single node output (.charon/ structure)
#### Example 1b: From a Distributed Key Generation ceremony output (a `.charon/` folder structure)

When you have DKG artifacts in a `.charon/` directory:

Expand All @@ -86,70 +89,28 @@ When you have DKG artifacts in a `.charon/` directory:

Create the required resources:

> ⚠️ **IMPORTANT**: The ENR secret MUST be created in the same namespace where you plan to install the Helm chart. The ENR job will only look for secrets in its own namespace.

```console
# Create namespace (if needed)
kubectl create namespace dv-pod

# Create ENR private key secret IN THE SAME NAMESPACE
```sh
# Put the charon ENR private key in a kubernetes secret
kubectl create secret generic charon-enr-private-key \
--namespace dv-pod \
--from-file=.charon/charon-enr-private-key

# Create the cluster lock ConfigMap IN THE SAME NAMESPACE
kubectl create configmap my-cluster-lock \
--namespace dv-pod \
# Put the private keys in a kubernetes secret
kubectl create secret generic validator-keys \
--from-file=.charon/validator_keys/keystore-0.json --from-file=.charon/validator_keys/keystore-0.txt

# Put the cluster lock file in a kubernetes ConfigMap
kubectl create configmap cluster-lock \
--from-file=.charon/cluster-lock.json

# Install the chart IN THE SAME NAMESPACE
# Install the chart
helm install my-dv-pod obol/dv-pod \
--namespace dv-pod \
--set configMaps.clusterlock=my-cluster-lock
```

#### Handling Large Cluster-Lock Files (>1MB)

If your cluster-lock.json file is larger than 1MB, you may encounter errors when creating the ConfigMap:

```console
error validating data: ValidationError(ConfigMap.data.cluster-lock.json): invalid type for io.k8s.api.core.v1.ConfigMap.data: got "array", expected "string"
--set configMaps.clusterLock=cluster-lock \
--set validatorClient.keystores.secretName=validator-keys
```

In this case, you have two options:

**Option A: Direct lock hash (Recommended)**

1. Extract the lock_hash from your cluster-lock.json:
```console
LOCK_HASH=$(jq -r '.lock_hash' cluster-lock.json)
echo $LOCK_HASH
```
> [!IMPORTANT]
> The ENR secret MUST be created in the same namespace where you plan to install the Helm chart. The ENR job can only look for secrets in its own namespace.

2. Install the chart with the lockHash value:
```console
helm install my-dv-pod obol/dv-pod \
--set charon.lockHash=$LOCK_HASH \
--set charon.operatorAddress=<YOUR_OPERATOR_ADDRESS>
```

**Option B: ConfigMap approach**

1. Extract the lock_hash and create a ConfigMap:
```console
LOCK_HASH=$(jq -r '.lock_hash' cluster-lock.json)
kubectl create configmap cluster-lock-hash \
--from-literal=lock-hash=$LOCK_HASH
```

2. Install the chart referencing the ConfigMap:
```console
helm install my-dv-pod obol/dv-pod \
--set configMaps.lockHash=cluster-lock-hash \
--set charon.operatorAddress=<YOUR_OPERATOR_ADDRESS>
```

The DKG sidecar will use the lock hash to fetch the full cluster lock from the Obol API.

### Option 2: Run DKG through the chart (automatic)

Expand All @@ -159,7 +120,7 @@ If you don't have pre-existing artifacts, the chart can automatically:
3. Store the resulting artifacts in a persistent volume

Simply deploy the chart with your operator address:
```console
```sh
helm install my-dv-pod obol/dv-pod \
--set charon.operatorAddress=0xYOUR_OPERATOR_ADDRESS
```
Expand All @@ -179,9 +140,7 @@ Install a distributed validator pod:
```sh
helm upgrade --install my-dv-pod obol/dv-pod \
--set='charon.beaconNodeEndpoints[0]=<BEACON_NODE_ENDPOINT>' \
--set='charon.operatorAddress=<YOUR_OPERATOR_ADDRESS>' \
--create-namespace \
--namespace dv-pod
--set='charon.operatorAddress=<YOUR_OPERATOR_ADDRESS>'
```

## Validator Client
Expand Down Expand Up @@ -217,7 +176,7 @@ The chart supports two methods for providing validator keystores:

If you have existing keystores, create a Kubernetes secret and reference it:

```console
```sh
# Create secret with your keystores
kubectl create secret generic validator-keys \
--from-file=keystore-0.json \
Expand All @@ -228,7 +187,7 @@ kubectl create secret generic validator-keys \
# Deploy the chart with the keystore secret
helm install my-dv-pod obol/dv-pod \
--set validatorClient.keystores.secretName=validator-keys \
--set configMaps.clusterlock=my-cluster-lock
--set configMaps.clusterLock=my-cluster-lock
```

#### Option 2: DKG-Generated Keystores (Automatic)
Expand Down Expand Up @@ -285,9 +244,57 @@ For example, if you installed the chart as `my-dv-pod` in namespace `dv-pod`:

Note: The Charon validator API on port 3600 provides the same interface as a beacon node API, allowing standard validator clients to connect without modification.

### Handling Large Cluster-Lock Files (>1MB)

If your `cluster-lock.json` file is larger than 1MB, you may encounter errors when creating the ConfigMap:

```sh
error validating data: ValidationError(ConfigMap.data.cluster-lock.json): invalid type for io.k8s.api.core.v1.ConfigMap.data: got "array", expected "string"
```

In this case, you have two options:

**Option A: Direct lock hash (Recommended)**

1. Extract the lock_hash from your cluster-lock.json:
```sh
LOCK_HASH=$(jq -r '.lock_hash' cluster-lock.json)
echo $LOCK_HASH
```

2. Install the chart with the lockHash value:
```sh
helm install my-dv-pod obol/dv-pod \
--set charon.lockHash=$LOCK_HASH \
--set charon.operatorAddress=<YOUR_OPERATOR_ADDRESS>
```

**Option B: ConfigMap approach**

1. Extract the lock_hash and create a ConfigMap:
```sh
LOCK_HASH=$(jq -r '.lock_hash' cluster-lock.json)
kubectl create configmap cluster-lock-hash \
--from-literal=lock-hash=$LOCK_HASH
```

2. Install the chart referencing the ConfigMap:
```sh
helm install my-dv-pod obol/dv-pod \
--set configMaps.lockHash=cluster-lock-hash \
--set charon.operatorAddress=<YOUR_OPERATOR_ADDRESS>
```

The DKG sidecar will use the lock hash to fetch the full cluster lock from the Obol API.

## Uninstall the Chart
To uninstall and delete the `dv-pod` release:
```sh
helm uninstall dv-pod -n dv-pod
```
The command removes all the Kubernetes components associated with the chart and deletes the release.


## Full Chart Values Reference

{{ template "chart.valuesSection" . }}
2 changes: 1 addition & 1 deletion charts/dv-pod/templates/NOTES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Your Obol distributed validator pod "{{ template "dv-pod.fullname" . }}" has bee
{{- end }}

To retrieve the public ENR string:
kubectl get secret -n {{ .Release.Namespace }} {{ include "dv-pod.enrSecretName" . }} -o jsonpath='{.data.public-enr}' | base64 --decode
kubectl get secret -n {{ .Release.Namespace }} {{ include "dv-pod.enrSecretName" . }} -o jsonpath='{.data.enr}' | base64 --decode
echo ""

{{- if not .Values.configMaps.clusterlock }}
Expand Down
22 changes: 15 additions & 7 deletions charts/dv-pod/templates/_helpers.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -94,19 +94,27 @@ Logic:
Return the data key for the ENR private key within the shared secret.
*/}}
{{- define "dv-pod.enrSecretDataKey" -}}
{{- .Values.charon.enr.existingSecret.dataKey | default "private-key" -}}
{{- .Values.charon.enr.existingSecret.privateKeyDataKey | default "charon-enr-private-key" -}}
{{- end -}}

{{/*
Return the data key for the ENR public key within the shared secret.
*/}}
{{- define "dv-pod.enrSecretPublicDataKey" -}}
{{- .Values.charon.enr.existingSecret.publicKeyDataKey | default "enr" -}}
{{- end -}}

{{/*
Determine if the ENR generation job should be created.
The job should be created only if:
1. Generate is enabled
2. No private key is directly provided
3. No existing secret is explicitly configured
Note: The job will check at runtime if the default secret exists and skip generation if it does
The job should be created if:
1. Generate is enabled AND
2. No private key is directly provided AND
3. ENR job is enabled
Note: The job will run even if an existing secret is specified, as it can handle incomplete secrets
(secrets with only private key missing the public ENR field)
*/}}
{{- define "dv-pod.shouldCreateEnrJob" -}}
{{- if and .Values.charon.enr.generate.enabled (not .Values.charon.enr.privateKey) (not .Values.charon.enr.existingSecret.name) .Values.charon.enrJob.enabled -}}
{{- if and .Values.charon.enr.generate.enabled (not .Values.charon.enr.privateKey) .Values.charon.enrJob.enabled -}}
true
{{- end -}}
{{- end -}}
Expand Down
Loading