diff --git a/infrastructure/charts/mev-commit-erigon/Chart.yaml b/infrastructure/charts/mev-commit-erigon/Chart.yaml new file mode 100644 index 000000000..d347fd51f --- /dev/null +++ b/infrastructure/charts/mev-commit-erigon/Chart.yaml @@ -0,0 +1,7 @@ +apiVersion: v2 +name: erigon-snode +description: A Helm chart for deploying MEV Commit chain with Erigon blockchain client +type: application +version: 0.1.0 +appVersion: "v3.0.7" + diff --git a/infrastructure/charts/mev-commit-erigon/templates/_helpers.tpl b/infrastructure/charts/mev-commit-erigon/templates/_helpers.tpl new file mode 100644 index 000000000..65a48a37f --- /dev/null +++ b/infrastructure/charts/mev-commit-erigon/templates/_helpers.tpl @@ -0,0 +1,51 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "erigon-snode.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "erigon-snode.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "erigon-snode.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "erigon-snode.labels" -}} +helm.sh/chart: {{ include "erigon-snode.chart" . }} +{{ include "erigon-snode.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "erigon-snode.selectorLabels" -}} +app.kubernetes.io/name: {{ include "erigon-snode.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} diff --git a/infrastructure/charts/mev-commit-erigon/templates/configmap.yaml b/infrastructure/charts/mev-commit-erigon/templates/configmap.yaml new file mode 100644 index 000000000..e002edaf0 --- /dev/null +++ b/infrastructure/charts/mev-commit-erigon/templates/configmap.yaml @@ -0,0 +1,43 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "erigon-snode.fullname" . }}-scripts + labels: + {{- include "erigon-snode.labels" . | nindent 4 }} +data: + create-jwt.sh: | + #!/bin/sh + set -e + if [ -f /shared/jwt ]; then + echo "[create-jwt] JWT already exists, skipping creation" + exit 0 + fi + echo "[create-jwt] copying JWT from secret to /shared/jwt" + cp /secrets/jwt /shared/jwt + chmod 600 /shared/jwt + download-genesis.sh: | + #!/bin/sh + set -e + echo "[download-genesis] ensuring /shared permissions" + mkdir -p /shared + chmod 777 /shared + + if [ -f /shared/genesis.json ]; then + echo "[download-genesis] genesis.json already exists, skipping download" + exit 0 + fi + + echo "[download-genesis] downloading genesis.json" + wget -O /shared/genesis.json {{ .Values.genesis.url }} + init-erigon.sh: | + #!/bin/sh + set -e + + # Check if erigon has already been initialized + if [ -f {{ .Values.erigon.datadir }}/chaindata/CURRENT ] || [ -f {{ .Values.erigon.datadir }}/chaindata/LOCK ]; then + echo "[init-erigon] Erigon already initialized, skipping init" + exit 0 + fi + + echo "[init-erigon] initializing erigon with genesis.json" + erigon --datadir {{ .Values.erigon.datadir }} init /shared/genesis.json diff --git a/infrastructure/charts/mev-commit-erigon/templates/ingress-rpc.yaml b/infrastructure/charts/mev-commit-erigon/templates/ingress-rpc.yaml new file mode 100644 index 000000000..0237dba4d --- /dev/null +++ b/infrastructure/charts/mev-commit-erigon/templates/ingress-rpc.yaml @@ -0,0 +1,34 @@ +{{- if .Values.ingress.enabled -}} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ include "erigon-snode.fullname" . }}-rpc + labels: + {{- include "erigon-snode.labels" . | nindent 4 }} + app.kubernetes.io/component: rpc-ingress + {{- with .Values.ingress.rpc.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if .Values.ingress.className }} + ingressClassName: {{ .Values.ingress.className }} + {{- end }} + {{- if .Values.ingress.tls }} + tls: + - hosts: + - {{ .Values.ingress.rpc.host | quote }} + secretName: {{ .Values.ingress.tls.secretName }} + {{- end }} + rules: + - host: {{ .Values.ingress.rpc.host | quote }} + http: + paths: + - path: {{ .Values.ingress.rpc.path }} + pathType: {{ .Values.ingress.rpc.pathType }} + backend: + service: + name: {{ include "erigon-snode.fullname" . }}-erigon + port: + name: http +{{- end }} diff --git a/infrastructure/charts/mev-commit-erigon/templates/ingress-ws.yaml b/infrastructure/charts/mev-commit-erigon/templates/ingress-ws.yaml new file mode 100644 index 000000000..cee056c19 --- /dev/null +++ b/infrastructure/charts/mev-commit-erigon/templates/ingress-ws.yaml @@ -0,0 +1,34 @@ +{{- if .Values.ingress.enabled -}} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ include "erigon-snode.fullname" . }}-websocket + labels: + {{- include "erigon-snode.labels" . | nindent 4 }} + app.kubernetes.io/component: websocket-ingress + {{- with .Values.ingress.websocket.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if .Values.ingress.className }} + ingressClassName: {{ .Values.ingress.className }} + {{- end }} + {{- if .Values.ingress.tls }} + tls: + - hosts: + - {{ .Values.ingress.websocket.host | quote }} + secretName: {{ .Values.ingress.tls.secretName }} + {{- end }} + rules: + - host: {{ .Values.ingress.websocket.host | quote }} + http: + paths: + - path: {{ .Values.ingress.websocket.path }} + pathType: {{ .Values.ingress.websocket.pathType }} + backend: + service: + name: {{ include "erigon-snode.fullname" . }}-erigon + port: + name: ws +{{- end }} diff --git a/infrastructure/charts/mev-commit-erigon/templates/secret.yaml b/infrastructure/charts/mev-commit-erigon/templates/secret.yaml new file mode 100644 index 000000000..47bae568e --- /dev/null +++ b/infrastructure/charts/mev-commit-erigon/templates/secret.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "erigon-snode.fullname" . }}-jwt + labels: + {{- include "erigon-snode.labels" . | nindent 4 }} +type: Opaque +stringData: + jwt: {{ .Values.jwt.token | quote }} diff --git a/infrastructure/charts/mev-commit-erigon/templates/service-erigon.yaml b/infrastructure/charts/mev-commit-erigon/templates/service-erigon.yaml new file mode 100644 index 000000000..ef4b66399 --- /dev/null +++ b/infrastructure/charts/mev-commit-erigon/templates/service-erigon.yaml @@ -0,0 +1,28 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "erigon-snode.fullname" . }}-erigon + labels: + {{- include "erigon-snode.labels" . | nindent 4 }} + app.kubernetes.io/component: erigon +spec: + type: {{ .Values.service.erigon.type }} + ports: + - port: {{ .Values.service.erigon.ports.http }} + targetPort: http + protocol: TCP + name: http + - port: {{ .Values.service.erigon.ports.ws }} + targetPort: ws + protocol: TCP + name: ws + - port: {{ .Values.service.erigon.ports.metrics }} + targetPort: metrics + protocol: TCP + name: metrics + selector: + {{- if .Values.service.erigon.customSelector }} + {{- toYaml .Values.service.erigon.customSelector | nindent 4 }} + {{- else }} + {{- include "erigon-snode.selectorLabels" . | nindent 4 }} + {{- end }} diff --git a/infrastructure/charts/mev-commit-erigon/templates/service-snode.yaml b/infrastructure/charts/mev-commit-erigon/templates/service-snode.yaml new file mode 100644 index 000000000..adfb57bd8 --- /dev/null +++ b/infrastructure/charts/mev-commit-erigon/templates/service-snode.yaml @@ -0,0 +1,24 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "erigon-snode.fullname" . }}-snode + labels: + {{- include "erigon-snode.labels" . | nindent 4 }} + app.kubernetes.io/component: snode +spec: + type: {{ .Values.service.snode.type }} + ports: + - port: {{ .Values.service.snode.ports.api }} + targetPort: api + protocol: TCP + name: api + - port: {{ .Values.service.snode.ports.health }} + targetPort: health + protocol: TCP + name: health + selector: + {{- if .Values.service.snode.customSelector }} + {{- toYaml .Values.service.snode.customSelector | nindent 4 }} + {{- else }} + {{- include "erigon-snode.selectorLabels" . | nindent 4 }} + {{- end }} diff --git a/infrastructure/charts/mev-commit-erigon/templates/statefulset.yaml b/infrastructure/charts/mev-commit-erigon/templates/statefulset.yaml new file mode 100644 index 000000000..332ecef86 --- /dev/null +++ b/infrastructure/charts/mev-commit-erigon/templates/statefulset.yaml @@ -0,0 +1,215 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: {{ include "erigon-snode.fullname" . }} + labels: + {{- include "erigon-snode.labels" . | nindent 4 }} + {{- with .Values.additionalLabels }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + serviceName: {{ include "erigon-snode.fullname" . }} + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + {{- include "erigon-snode.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "erigon-snode.selectorLabels" . | nindent 8 }} + {{- with .Values.additionalLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.topologySpreadConstraints }} + topologySpreadConstraints: + {{- toYaml . | nindent 8 }} + {{- end }} + terminationGracePeriodSeconds: {{ .Values.terminationGracePeriodSeconds }} + volumes: + - name: scripts-volume + configMap: + name: {{ include "erigon-snode.fullname" . }}-scripts + defaultMode: 0755 + - name: jwt-secret + secret: + secretName: {{ include "erigon-snode.fullname" . }}-jwt + initContainers: + - name: create-jwt + image: busybox:latest + command: ["/bin/sh", "-c"] + args: ["/scripts/create-jwt.sh"] + {{- with .Values.initContainerResources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + securityContext: + runAsUser: 0 + runAsGroup: 0 + volumeMounts: + - name: scripts-volume + mountPath: /scripts + - name: erigon-volume + mountPath: /shared + subPath: shared + - name: jwt-secret + mountPath: /secrets + - name: download-genesis + image: alpine:latest + command: ["/bin/sh", "-c"] + args: ["/scripts/download-genesis.sh"] + {{- with .Values.initContainerResources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + securityContext: + runAsUser: 0 + runAsGroup: 0 + volumeMounts: + - name: scripts-volume + mountPath: /scripts + - name: erigon-volume + mountPath: /shared + subPath: shared + - name: init-erigon + image: "{{ .Values.image.erigon.repository }}:{{ .Values.image.erigon.tag }}" + imagePullPolicy: {{ .Values.image.erigon.pullPolicy }} + command: ["/bin/sh", "-c"] + args: ["/scripts/init-erigon.sh"] + {{- with .Values.initContainerResources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + securityContext: + runAsUser: 0 + runAsGroup: 0 + volumeMounts: + - name: scripts-volume + mountPath: /scripts + - name: erigon-volume + mountPath: /shared + subPath: shared + - name: erigon-volume + mountPath: {{ .Values.erigon.datadir }} + subPath: erigon-data + containers: + - name: erigon + image: "{{ .Values.image.erigon.repository }}:{{ .Values.image.erigon.tag }}" + imagePullPolicy: {{ .Values.image.erigon.pullPolicy }} + command: ["erigon"] + {{- with .Values.resources.erigon }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + securityContext: + runAsUser: 0 + runAsGroup: 0 + args: + - --datadir={{ .Values.erigon.datadir }} + - --authrpc.jwtsecret=/shared/jwt + - --authrpc.addr=0.0.0.0 + - --authrpc.port={{ .Values.erigon.ports.authrpc }} + - --authrpc.vhosts=* + {{- if .Values.erigon.zeroFeeTxList }} + - --zerofee.tx.list={{ join "," .Values.erigon.zeroFeeTxList }} + {{- end }} + {{- if .Values.erigon.api.http.enabled }} + - --http + - --http.addr={{ .Values.erigon.api.http.addr }} + - --http.port={{ .Values.erigon.ports.http }} + - --http.vhosts={{ .Values.erigon.api.http.vhosts }} + - --http.corsdomain={{ .Values.erigon.api.http.corsdomain }} + - --http.api={{ .Values.erigon.api.http.api }} + {{- end }} + {{- if .Values.erigon.api.ws.enabled }} + - --ws + - --ws.port={{ .Values.erigon.ports.ws }} + {{- end }} + - --networkid={{ .Values.erigon.networkId }} + {{- if .Values.erigon.api.metrics.enabled }} + - --metrics + - --metrics.addr={{ .Values.erigon.api.metrics.addr }} + - --metrics.port={{ .Values.erigon.ports.metrics }} + {{- end }} + - --private.api.addr=0.0.0.0:{{ .Values.erigon.ports.privateApi }} + {{- if .Values.erigon.nodiscover }} + - --nodiscover + {{- end }} + - --prune.mode={{ .Values.erigon.pruneMode }} + {{- range .Values.erigon.extraArgs }} + - {{ . }} + {{- end }} + ports: + - name: http + containerPort: {{ .Values.erigon.ports.http }} + - name: ws + containerPort: {{ .Values.erigon.ports.ws }} + - name: authrpc + containerPort: {{ .Values.erigon.ports.authrpc }} + - name: metrics + containerPort: {{ .Values.erigon.ports.metrics }} + volumeMounts: + - name: erigon-volume + mountPath: {{ .Values.erigon.datadir }} + subPath: erigon-data + - name: erigon-volume + mountPath: /shared + subPath: shared + - name: snode + image: "{{ .Values.image.snode.repository }}:{{ .Values.image.snode.tag }}" + imagePullPolicy: {{ .Values.image.snode.pullPolicy }} + command: ["./snode"] + {{- with .Values.resources.snode }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + securityContext: + runAsUser: 0 + runAsGroup: 0 + args: + - "leader" + - --instance-id={{ .Values.snode.instanceId }} + - --eth-client-url=http://localhost:{{ .Values.erigon.ports.authrpc }} + - --jwt-secret={{ .Values.jwt.token }} + - --priority-fee-recipient={{ .Values.snode.priorityFeeRecipient }} + - --api-addr=:{{ .Values.snode.ports.api }} + - --health-addr=:{{ .Values.snode.ports.health }} + - --log-level={{ .Values.snode.logLevel }} + {{- range .Values.snode.extraArgs }} + - {{ . }} + {{- end }} + ports: + - name: api + containerPort: {{ .Values.snode.ports.api }} + - name: health + containerPort: {{ .Values.snode.ports.health }} + volumeMounts: + - name: erigon-volume + mountPath: /data/snode + subPath: snode-data + - name: erigon-volume + mountPath: /shared + subPath: shared + volumeClaimTemplates: + - metadata: + name: erigon-volume + spec: + accessModes: ["{{ .Values.storage.accessMode }}"] + resources: + requests: + storage: {{ .Values.storage.size }} + {{- if .Values.storage.storageClassName }} + storageClassName: {{ .Values.storage.storageClassName }} + {{- end }} diff --git a/infrastructure/charts/mev-commit-erigon/values.yaml b/infrastructure/charts/mev-commit-erigon/values.yaml new file mode 100644 index 000000000..a614cab44 --- /dev/null +++ b/infrastructure/charts/mev-commit-erigon/values.yaml @@ -0,0 +1,238 @@ + +erigon: + networkId: 141414 + datadir: "/data/erigon" + ports: + http: 8545 + ws: 8546 + authrpc: 8551 + metrics: 6061 + privateApi: 9095 + api: + http: + enabled: true + addr: "0.0.0.0" + vhosts: "*" + corsdomain: "*" + api: "eth,erigon,engine,net,web3,txpool" + ws: + enabled: true + addr: "0.0.0.0" + vhosts: "*" + metrics: + enabled: true + addr: "0.0.0.0" + zeroFeeTxList: + - "0x00" # Orcale Keystore + - "0x00" # Contracts Deployer Keystore + - "0x00" # Bridge Relayer Keystore + pruneMode: "archive" + nodiscover: true + # Additional flags to pass to erigon + extraArgs: [] + # Example: + # extraArgs: + # - "--trace.config=/path/to/trace.json" + # - "--state.scheme=path" + +snode: + instanceId: "snode1" + priorityFeeRecipient: "0x00" + ports: + api: 9090 + health: 8080 + logLevel: "info" + # Additional arguments to pass to snode + extraArgs: [] + # Example: + # extraArgs: + # - "--metrics-enabled" + + +jwt: + token: "" + +genesis: + url: "" + + +image: + erigon: + repository: + tag: + pullPolicy: IfNotPresent + snode: + repository: + tag: + pullPolicy: IfNotPresent + +storage: + size: 500Gi + storageClassName: + accessMode: ReadWriteOnce + +security: + runAsUser: 0 + runAsGroup: 0 + runAsNonRoot: false + fsGroup: 0 + +replicaCount: 1 +terminationGracePeriodSeconds: 60 + +# Resource requests and limits +resources: + erigon: + requests: + cpu: "48" + memory: "128Gi" + limits: + cpu: "52" + memory: "152Gi" + snode: + requests: + cpu: "8" + memory: "48Gi" + limits: + cpu: "12" + memory: "64Gi" + +# Init container resources +initContainerResources: + requests: + cpu: "500m" + memory: "500Mi" + limits: + cpu: "1000m" + memory: "1Gi" + +service: + erigon: + type: ClusterIP + ports: + http: 8545 + ws: 8546 + authrpc: 8551 + metrics: 6061 + # Custom selector for erigon service - if provided, overrides default selector + customSelector: {} + # Example: + # customSelector: + # app: erigon + # version: v1.0.0 + snode: + type: ClusterIP + ports: + api: 9090 + health: 8080 + # Custom selector for snode service - if provided, overrides default selector + customSelector: {} + # Example: + # customSelector: + # app: snode + # component: consensus-layer + +nodeSelector: + workload-type: erigon + + +tolerations: [] +# Example: +# tolerations: +# - key: "erigon-dedicated" +# operator: "Equal" +# value: "true" +# effect: "NoSchedule" +# - key: "node.kubernetes.io/disk-pressure" +# operator: "Exists" +# effect: "NoSchedule" + +affinity: {} +# Example: +# affinity: +# nodeAffinity: +# requiredDuringSchedulingIgnoredDuringExecution: +# nodeSelectorTerms: +# - matchExpressions: +# - key: node-type +# operator: In +# values: +# - high-memory +# preferredDuringSchedulingIgnoredDuringExecution: +# - weight: 100 +# preference: +# matchExpressions: +# - key: zone +# operator: In +# values: +# - zone-1 +# podAntiAffinity: +# preferredDuringSchedulingIgnoredDuringExecution: +# - weight: 100 +# podAffinityTerm: +# labelSelector: +# matchExpressions: +# - key: app.kubernetes.io/name +# operator: In +# values: +# - erigon-snode +# topologyKey: kubernetes.io/hostname + +topologySpreadConstraints: [] +# Example: +# topologySpreadConstraints: +# - maxSkew: 1 +# topologyKey: topology.kubernetes.io/zone +# whenUnsatisfiable: DoNotSchedule +# labelSelector: +# matchLabels: +# app.kubernetes.io/name: erigon-snode + + +# Additional labels for StatefulSet +additionalLabels: {} +# Example: +# additionalLabels: +# environment: production +# team: blockchain +# version: v1.0.0 + +ingress: + enabled: true + className: "" + + # RPC Configuration + rpc: + host: + path: / + pathType: Prefix + annotations: + nginx.ingress.kubernetes.io/backend-protocol: "HTTP" + nginx.ingress.kubernetes.io/force-ssl-redirect: "true" + nginx.ingress.kubernetes.io/proxy-body-size: "0" + nginx.ingress.kubernetes.io/proxy-read-timeout: "1200" + nginx.ingress.kubernetes.io/proxy-send-timeout: "1200" + nginx.ingress.kubernetes.io/ssl-redirect: "true" + + # WebSocket Configuration + websocket: + host: + path: / + pathType: Prefix + annotations: + nginx.ingress.kubernetes.io/backend-protocol: "HTTP" + nginx.ingress.kubernetes.io/force-ssl-redirect: "true" + nginx.ingress.kubernetes.io/proxy-body-size: "0" + nginx.ingress.kubernetes.io/proxy-read-timeout: "3600" + nginx.ingress.kubernetes.io/proxy-send-timeout: "3600" + nginx.ingress.kubernetes.io/ssl-redirect: "true" + # WebSocket specific annotations + nginx.ingress.kubernetes.io/proxy-http-version: "1.1" + nginx.ingress.kubernetes.io/proxy-set-headers: | + Connection "upgrade" + Upgrade $http_upgrade + + + tls: + secretName: +