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
11 changes: 11 additions & 0 deletions charts/network/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,14 @@ A Helm chart for a blockchain network on Kubernetes
| | network-bootstrapper | * |
| | network-nodes | * |

## Values

| Key | Type | Default | Description |
|-----|------|---------|-------------|
| global | object | `{"networkNodes":{"faucetArtifactPrefix":"besu-faucet","genesisConfigMapName":"besu-genesis","podPrefix":"","serviceName":"","staticNodesConfigMapName":"besu-static-nodes"}}` | Global configuration shared across subcharts. |
| global.networkNodes | object | `{"faucetArtifactPrefix":"besu-faucet","genesisConfigMapName":"besu-genesis","podPrefix":"","serviceName":"","staticNodesConfigMapName":"besu-static-nodes"}` | Defaults consumed by Besu network node workloads. |
| global.networkNodes.faucetArtifactPrefix | string | `"besu-faucet"` | Prefix used for faucet ConfigMaps and Secrets. |
| global.networkNodes.genesisConfigMapName | string | `"besu-genesis"` | ConfigMap name storing the generated genesis.json artifact. |
| global.networkNodes.podPrefix | string | `""` | StatefulSet prefix used for validator pod hostnames. |
| global.networkNodes.serviceName | string | `""` | Kubernetes Service name fronting validator pods to align bootstrapper static-nodes output. |
| global.networkNodes.staticNodesConfigMapName | string | `"besu-static-nodes"` | ConfigMap name storing static-nodes.json entries. |
5 changes: 5 additions & 0 deletions charts/network/charts/network-bootstrapper/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,19 @@ A Helm chart for Kubernetes
| settings.defaultStaticNodeDiscoveryPort | int | `30303` | Default UDP discovery port used in generated enode URIs when no override is provided. |
| settings.defaultStaticNodePort | int | `30303` | Default TCP P2P port used in generated enode URIs when no override is provided. |
| settings.evmStackSize | int | `nil` | Maximum EVM stack size permitted for contract execution. |
| settings.faucetArtifactPrefix | string | `nil` | Prefix used for faucet ConfigMaps and Secrets. |
| settings.gasLimit | int | `nil` | Genesis block gas limit expressed as a decimal number. |
| settings.gasPrice | int | `nil` | Base gas price in wei applied across the network. |
| settings.genesisConfigMapName | string | `nil` | ConfigMap name storing the generated genesis.json payload. |
| settings.outputType | string | `"kubernetes"` | Output target for generated artefacts: screen, file, or kubernetes. |
| settings.secondsPerBlock | int | `nil` | Block interval in seconds for the genesis configuration. |
| settings.staticNodeDiscoveryPort | int | `nil` | UDP discovery port embedded in static node enode URIs. |
| settings.staticNodeDomain | string | `nil` | DNS suffix appended to generated static node hostnames. |
| settings.staticNodeNamespace | string | `nil` | Namespace component inserted between service name and domain in static node hostnames. |
| settings.staticNodePodPrefix | string | `nil` | StatefulSet prefix applied to validator pod hostnames. |
| settings.staticNodePort | int | `nil` | TCP P2P port embedded in static node enode URIs. |
| settings.staticNodeServiceName | string | `nil` | Headless Service name used when constructing static node hostnames. |
| settings.staticNodesConfigMapName | string | `nil` | ConfigMap name storing the generated static-nodes.json payload. |
| settings.validators | int | `nil` | Number of validator identities to generate (default 4). |
| tolerations | list | `[]` | Tolerations allowing the bootstrapper pod onto tainted nodes. |
| volumeMounts | list | `[]` | Extra volume mounts applied to the bootstrapper container. |
Expand Down
19 changes: 19 additions & 0 deletions charts/network/charts/network-bootstrapper/templates/job.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,20 @@ spec:
{{- $resolvedStaticDomain := default $clusterDomain .Values.settings.staticNodeDomain }}
{{- $resolvedStaticPort := default $defaultStaticPort .Values.settings.staticNodePort }}
{{- $resolvedStaticDiscovery := default $defaultStaticDiscovery .Values.settings.staticNodeDiscoveryPort }}
{{- $globalNodes := default (dict) .Values.global.networkNodes }}
{{- $autoNames := dict "service" "besu-node" "podPrefix" "besu-node-validator" }}
{{- with (index $.Subcharts "network-nodes") }}
{{- $service := include "nodes.fullname" . }}
{{- $_ := set $autoNames "service" $service }}
{{- $_ := set $autoNames "podPrefix" (printf "%s-validator" $service) }}
{{- end }}
{{- $serviceOverride := coalesce .Values.settings.staticNodeServiceName (get $globalNodes "serviceName") }}
{{- $resolvedServiceName := default (index $autoNames "service") $serviceOverride }}
{{- $podOverride := coalesce .Values.settings.staticNodePodPrefix (get $globalNodes "podPrefix") }}
{{- $resolvedPodPrefix := default (index $autoNames "podPrefix") $podOverride }}
{{- $resolvedGenesisName := default "besu-genesis" (default (get $globalNodes "genesisConfigMapName") .Values.settings.genesisConfigMapName) }}
{{- $resolvedStaticNodesName := default "besu-static-nodes" (default (get $globalNodes "staticNodesConfigMapName") .Values.settings.staticNodesConfigMapName) }}
{{- $resolvedFaucetPrefix := default "besu-faucet" (default (get $globalNodes "faucetArtifactPrefix") .Values.settings.faucetArtifactPrefix) }}
{{- with .Values.settings.validators }}
- --validators={{ . }}
{{- end }}
Expand All @@ -56,6 +70,11 @@ spec:
{{- end }}
- --static-node-port={{ $resolvedStaticPort }}
- --static-node-discovery-port={{ $resolvedStaticDiscovery }}
- --static-node-service-name={{ $resolvedServiceName }}
- --static-node-pod-prefix={{ $resolvedPodPrefix }}
- --genesis-configmap-name={{ $resolvedGenesisName }}
- --static-nodes-configmap-name={{ $resolvedStaticNodesName }}
- --faucet-artifact-prefix={{ $resolvedFaucetPrefix }}
{{- with .Values.settings.allocations }}
- --allocations={{ . }}
{{- end }}
Expand Down
10 changes: 10 additions & 0 deletions charts/network/charts/network-bootstrapper/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,16 @@ settings:
staticNodePort:
# -- (int) UDP discovery port embedded in static node enode URIs.
staticNodeDiscoveryPort:
# -- (string) Headless Service name used when constructing static node hostnames.
staticNodeServiceName:
# -- (string) StatefulSet prefix applied to validator pod hostnames.
staticNodePodPrefix:
# -- (string) ConfigMap name storing the generated genesis.json payload.
genesisConfigMapName:
# -- (string) ConfigMap name storing the generated static-nodes.json payload.
staticNodesConfigMapName:
# -- (string) Prefix used for faucet ConfigMaps and Secrets.
faucetArtifactPrefix:
# -- (string) Path to a JSON allocations file providing pre-funded accounts.
allocations:
# -- (string) Output target for generated artefacts: screen, file, or kubernetes.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ metadata:
app.kubernetes.io/component: rpc
spec:
{{- $root := . }}
{{- $globalNodes := .Values.global.networkNodes | default (dict) }}
{{- $genesisConfigMapName := default "besu-genesis" (get $globalNodes "genesisConfigMapName") }}
{{- $staticNodesConfigMapName := default "besu-static-nodes" (get $globalNodes "staticNodesConfigMapName") }}
{{- $persistence := .Values.persistence | default (dict) }}
{{- $persistenceEnabled := default false (get $persistence "enabled") }}
{{- $existingClaim := default "" (get $persistence "existingClaim") }}
Expand Down Expand Up @@ -161,10 +164,10 @@ spec:
name: {{ include "nodes.fullname" . }}-config
- name: besu-genesis
configMap:
name: besu-genesis
name: {{ $genesisConfigMapName }}
- name: besu-static-nodes
configMap:
name: besu-static-nodes
name: {{ $staticNodesConfigMapName }}
{{- if $useExistingClaim }}
- name: {{ $volumeName }}
persistentVolumeClaim:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ spec:
{{- $useClaimTemplate := and $persistenceEnabled (not $useExistingClaim) }}
{{- $privateKeyFilename := default "privateKey" .Values.config.privateKeyFilename }}
{{- $shouldMountPersistence := $persistenceEnabled }}
{{- $globalNodes := .Values.global.networkNodes | default (dict) }}
{{- $genesisConfigMapName := default "besu-genesis" (get $globalNodes "genesisConfigMapName") }}
{{- $staticNodesConfigMapName := default "besu-static-nodes" (get $globalNodes "staticNodesConfigMapName") }}
{{- $validatorPodPrefix := default "besu-node-validator" (get $globalNodes "podPrefix") }}
{{- $validatorReplicaBudget := (include "nodes.validatorReplicaCount" . | int) }}
replicas: {{ $validatorReplicaBudget }}
serviceName: {{ include "nodes.fullname" . }}
Expand Down Expand Up @@ -164,17 +168,17 @@ spec:
name: {{ include "nodes.fullname" . }}-config
- name: besu-genesis
configMap:
name: besu-genesis
name: {{ $genesisConfigMapName }}
- name: besu-static-nodes
configMap:
name: besu-static-nodes
name: {{ $staticNodesConfigMapName }}
- name: besu-private-key
projected:
{{- if gt $validatorReplicaBudget 0 }}
sources:
{{- range $i, $_ := until $validatorReplicaBudget }}
- secret:
name: {{ printf "besu-node-validator-%d-private-key" $i }}
name: {{ printf "%s-%d-private-key" $validatorPodPrefix $i }}
items:
- key: privateKey
path: {{ printf "%s-validator-%d/privateKey" (include "nodes.fullname" $root) $i }}
Expand Down
15 changes: 15 additions & 0 deletions charts/network/values.yaml
Original file line number Diff line number Diff line change
@@ -1 +1,16 @@
# This parent chart does not define additional values; override subchart defaults here when packaging composite releases.

# -- (object) Global configuration shared across subcharts.
global:
# -- (object) Defaults consumed by Besu network node workloads.
networkNodes:
# -- (string) Kubernetes Service name fronting validator pods to align bootstrapper static-nodes output.
serviceName: ""
# -- (string) StatefulSet prefix used for validator pod hostnames.
podPrefix: ""
# -- (string) ConfigMap name storing the generated genesis.json artifact.
genesisConfigMapName: besu-genesis
# -- (string) ConfigMap name storing static-nodes.json entries.
staticNodesConfigMapName: besu-static-nodes
# -- (string) Prefix used for faucet ConfigMaps and Secrets.
faucetArtifactPrefix: besu-faucet
69 changes: 65 additions & 4 deletions src/cli/build-command.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { afterEach, beforeEach, describe, expect, test } from "bun:test";

import { getAddress } from "viem";
import { ARTIFACT_DEFAULTS } from "../constants/artifact-defaults.ts";
import type { BesuAllocAccount } from "../genesis/besu-genesis.service.ts";
import { ALGORITHM } from "../genesis/besu-genesis.service.ts";
import type { GeneratedNodeKey } from "../keys/node-key-factory.ts";
Expand All @@ -16,6 +17,13 @@ const EXPECTED_DEFAULT_VALIDATOR = 4;
const DEFAULT_STATIC_NODE_PORT = 30_303;
const CUSTOM_STATIC_NODE_PORT = 40_000;
const LEADING_DOT_REGEX = /^\./u;
const {
staticNodeServiceName: DEFAULT_SERVICE_NAME,
staticNodePodPrefix: DEFAULT_POD_PREFIX,
genesisConfigMapName: DEFAULT_GENESIS_CONFIGMAP_NAME,
staticNodesConfigMapName: DEFAULT_STATIC_NODES_CONFIGMAP_NAME,
faucetArtifactPrefix: DEFAULT_FAUCET_PREFIX,
} = ARTIFACT_DEFAULTS;
const UNCOMPRESSED_PUBLIC_KEY_PREFIX = "04";
const UNCOMPRESSED_PUBLIC_KEY_LENGTH = 130;
const HEX_RADIX = 16;
Expand Down Expand Up @@ -46,6 +54,10 @@ const createFactoryStub = () => {
};
};

const passthroughTextPrompt: BootstrapDependencies["promptForText"] = ({
defaultValue,
}) => Promise.resolve(defaultValue);

const expectedAddress = (index: number) => {
const pattern = index.toString(HEX_RADIX).padStart(PAD_WIDTH, PAD_CHAR);
return getAddress(`0x${pattern.repeat(ADDRESS_REPEAT)}`);
Expand All @@ -61,7 +73,9 @@ const expectedStaticNodeUri = (
domain?: string,
port: number = DEFAULT_STATIC_NODE_PORT,
discoveryPort: number = DEFAULT_STATIC_NODE_PORT,
namespace?: string
namespace?: string,
serviceName: string = DEFAULT_SERVICE_NAME,
podPrefix: string = DEFAULT_POD_PREFIX
): string => {
const normalizedDomain =
domain === undefined || domain.trim().length === 0
Expand All @@ -72,8 +86,7 @@ const expectedStaticNodeUri = (
? undefined
: namespace.trim();
const ordinal = index - 1;
const podName = `besu-node-validator-${ordinal}`;
const serviceName = "besu-node";
const podName = `${podPrefix}-${ordinal}`;
const segments = [podName, serviceName];
if (normalizedNamespace) {
segments.push(normalizedNamespace);
Expand Down Expand Up @@ -121,6 +134,7 @@ describe("CLI command bootstrap", () => {
test("runBootstrap orchestrates prompts and writes genesis", async () => {
const factory = createFactoryStub();
const promptCalls: [string, number | undefined, number][] = [];
const textPromptCalls: [string, string][] = [];
let loadAllocationsPath: string | undefined;
let outputInvocation:
| {
Expand Down Expand Up @@ -174,6 +188,10 @@ describe("CLI command bootstrap", () => {
genesis: { config: {}, extraData: "0xextra" } as any,
});
},
promptForText: ({ labelText, defaultValue }) => {
textPromptCalls.push([labelText, defaultValue]);
return Promise.resolve(defaultValue);
},
service: {} as any,
loadAllocations: (path: string) => {
loadAllocationsPath = path;
Expand All @@ -194,6 +212,13 @@ describe("CLI command bootstrap", () => {
expect(promptCalls).toEqual([
[VALIDATOR_LABEL, undefined, EXPECTED_DEFAULT_VALIDATOR],
]);
expect(textPromptCalls).toEqual([
["Static node service name", DEFAULT_SERVICE_NAME],
["Static node pod prefix", DEFAULT_POD_PREFIX],
["Genesis ConfigMap name", DEFAULT_GENESIS_CONFIGMAP_NAME],
["Static nodes ConfigMap name", DEFAULT_STATIC_NODES_CONFIGMAP_NAME],
["Faucet artifact prefix", DEFAULT_FAUCET_PREFIX],
]);
const output = stdout.read();
expect(output).toContain("Genesis");
expect(output).toContain("Validator Nodes");
Expand All @@ -205,6 +230,12 @@ describe("CLI command bootstrap", () => {
expectedStaticNodeUri(FIRST_VALIDATOR_INDEX),
expectedStaticNodeUri(SECOND_VALIDATOR_INDEX),
]);
expect(outputInvocation?.payload.artifactNames).toEqual({
faucetPrefix: DEFAULT_FAUCET_PREFIX,
validatorPrefix: DEFAULT_POD_PREFIX,
genesisConfigMapName: DEFAULT_GENESIS_CONFIGMAP_NAME,
staticNodesConfigMapName: DEFAULT_STATIC_NODES_CONFIGMAP_NAME,
});
});

test("createCliCommand wires metadata", () => {
Expand Down Expand Up @@ -243,6 +274,7 @@ describe("CLI command bootstrap", () => {
genesis: { config: {}, extraData: "0xextra" } as any,
});
},
promptForText: passthroughTextPrompt,
service: {} as any,
loadAllocations: () =>
Promise.resolve({} satisfies Record<string, BesuAllocAccount>),
Expand Down Expand Up @@ -309,6 +341,7 @@ describe("CLI command bootstrap", () => {
},
genesis: { config: {}, extraData: "0xextra" } as any,
}),
promptForText: passthroughTextPrompt,
service: {} as any,
loadAllocations: () =>
Promise.resolve({} satisfies Record<string, BesuAllocAccount>),
Expand All @@ -334,6 +367,16 @@ describe("CLI command bootstrap", () => {
"40000",
"--static-node-discovery-port",
"0",
"--static-node-service-name",
"custom-service",
"--static-node-pod-prefix",
"custom-validator",
"--genesis-configmap-name",
"custom-genesis",
"--static-nodes-configmap-name",
"custom-static-nodes",
"--faucet-artifact-prefix",
"custom-faucet",
],
{ from: "node" }
);
Expand All @@ -344,9 +387,17 @@ describe("CLI command bootstrap", () => {
"svc.cluster.local",
CUSTOM_STATIC_NODE_PORT,
0,
"network"
"network",
"custom-service",
"custom-validator"
),
]);
expect(capturedPayload?.artifactNames).toEqual({
faucetPrefix: "custom-faucet",
validatorPrefix: "custom-validator",
genesisConfigMapName: "custom-genesis",
staticNodesConfigMapName: "custom-static-nodes",
});
});

test("runBootstrap builds static nodes with domain and custom ports", async () => {
Expand All @@ -371,6 +422,7 @@ describe("CLI command bootstrap", () => {
genesis: { config: {}, extraData: "0xextra" } as any,
});
},
promptForText: passthroughTextPrompt,
service: {} as any,
loadAllocations: () =>
Promise.resolve({} satisfies Record<string, BesuAllocAccount>),
Expand Down Expand Up @@ -438,6 +490,7 @@ describe("CLI command bootstrap", () => {
genesis: { config: {}, extraData: "0xextra" } as any,
});
},
promptForText: passthroughTextPrompt,
service: {} as any,
loadAllocations: () =>
Promise.resolve({} satisfies Record<string, BesuAllocAccount>),
Expand Down Expand Up @@ -519,6 +572,7 @@ describe("CLI command bootstrap", () => {
},
genesis: { config: {}, extraData: "0x" } as any,
}),
promptForText: passthroughTextPrompt,
service: {} as any,
loadAllocations: () =>
Promise.resolve({} satisfies Record<string, BesuAllocAccount>),
Expand Down Expand Up @@ -602,13 +656,20 @@ describe("CLI command bootstrap", () => {
genesis: { config: {}, extraData: "0xextra" } as any,
});
},
promptForText: passthroughTextPrompt,
service: {} as any,
loadAllocations: () => {
loadAllocationsInvoked = true;
return Promise.resolve({} as Record<string, BesuAllocAccount>);
},
outputResult: (_type, payload) => {
expect(payload.validators).toHaveLength(EXPECTED_DEFAULT_VALIDATOR);
expect(payload.artifactNames).toEqual({
faucetPrefix: DEFAULT_FAUCET_PREFIX,
validatorPrefix: DEFAULT_POD_PREFIX,
genesisConfigMapName: DEFAULT_GENESIS_CONFIGMAP_NAME,
staticNodesConfigMapName: DEFAULT_STATIC_NODES_CONFIGMAP_NAME,
});
return Promise.resolve();
},
};
Expand Down
Loading