Files
zeekayandHanzo Dev 7cbe76b386 Go 1.27.1, and the code it can say more simply
Modules declare go 1.27.1 and the build images follow. go fix applied the
modernizers, and int32Ptr went once new(expr) could say it; omitzero is not
applied, since it changes what the JSON says.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-16 19:07:47 -07:00

195 lines
5.2 KiB
Go

package protocol
import (
"fmt"
"strings"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
"github.com/hanzoai/nchain/api/v1alpha1"
)
const (
solDefaultImage = "ghcr.io/hanzoai/solana-validator:latest"
solDataDir = "/data"
solLedgerDir = "/data/ledger"
solAccountsDir = "/data/accounts"
solKeysDir = "/keys"
solRPCPort = int32(8899)
solP2PPort = int32(8001)
solGossipPort = int32(8000)
solMetricsPort = int32(9090)
solHealthPath = "/health"
)
// SolanaDriver implements the Driver interface for the Solana blockchain protocol.
// Solana requires bare-metal-like specs. NVMe SSDs are mandatory.
// RAM-disk for accounts DB is recommended for production validators.
type SolanaDriver struct{}
func (d *SolanaDriver) Name() string { return "solana" }
func (d *SolanaDriver) DefaultImage() string { return solDefaultImage }
func (d *SolanaDriver) DefaultPorts() v1alpha1.PortConfig {
return v1alpha1.PortConfig{
RPC: solRPCPort,
P2P: solP2PPort,
Metrics: solMetricsPort,
Additional: []v1alpha1.NamedPort{
{Name: "gossip", Port: solGossipPort, Protocol: corev1.ProtocolUDP},
},
}
}
func (d *SolanaDriver) BuildCommand(spec *v1alpha1.NodeClusterSpec) ([]string, []string) {
args := []string{
"--identity", fmt.Sprintf("%s/validator-keypair.json", solKeysDir),
"--ledger", solLedgerDir,
"--accounts", solAccountsDir,
"--rpc-port", fmt.Sprintf("%d", d.rpcPort(spec)),
"--dynamic-port-range", "8000-8020",
"--limit-ledger-size",
"--no-poh-speed-test",
"--full-rpc-api",
"--rpc-bind-address", "0.0.0.0",
}
// Bootstrap entrypoints.
if len(spec.P2P.BootstrapNodes) > 0 {
for _, node := range spec.P2P.BootstrapNodes {
args = append(args, "--entrypoint", node)
}
}
// Network-specific expected genesis hash via protocolConfig is left to the user.
return []string{"agave-validator"}, args
}
func (d *SolanaDriver) BuildEnv(spec *v1alpha1.NodeClusterSpec) []corev1.EnvVar {
return []corev1.EnvVar{
{Name: "SOLANA_METRICS_CONFIG", Value: ""},
{
Name: "POD_NAME",
ValueFrom: &corev1.EnvVarSource{
FieldRef: &corev1.ObjectFieldSelector{FieldPath: "metadata.name"},
},
},
}
}
func (d *SolanaDriver) BuildVolumeMounts(spec *v1alpha1.NodeClusterSpec) []corev1.VolumeMount {
mounts := []corev1.VolumeMount{
{Name: "data", MountPath: solDataDir},
}
if spec.Keys != nil && spec.Keys.SecretName != "" {
mounts = append(mounts, corev1.VolumeMount{
Name: "keys",
MountPath: solKeysDir,
ReadOnly: true,
})
}
return mounts
}
func (d *SolanaDriver) BuildVolumes(spec *v1alpha1.NodeClusterSpec) []corev1.Volume {
var volumes []corev1.Volume
if spec.Keys != nil && spec.Keys.SecretName != "" {
volumes = append(volumes, corev1.Volume{
Name: "keys",
Secret: &corev1.SecretVolumeSource{
SecretName: spec.Keys.SecretName,
DefaultMode: new(int32(0400)),
},
})
}
return volumes
}
func (d *SolanaDriver) HealthEndpoint(spec *v1alpha1.NodeClusterSpec) (string, int32) {
return solHealthPath, d.rpcPort(spec)
}
func (d *SolanaDriver) ReadinessEndpoint(spec *v1alpha1.NodeClusterSpec) (string, int32) {
return solHealthPath, d.rpcPort(spec)
}
func (d *SolanaDriver) BuildInitContainers(spec *v1alpha1.NodeClusterSpec) []corev1.Container {
var initContainers []corev1.Container
// Create ledger and accounts directories.
initContainers = append(initContainers, corev1.Container{
Name: "init-dirs",
Image: "alpine:3",
Command: []string{"/bin/sh", "-c"},
Args: []string{strings.Join([]string{
fmt.Sprintf("mkdir -p %s", solLedgerDir),
fmt.Sprintf("mkdir -p %s", solAccountsDir),
}, " && ")},
VolumeMounts: []corev1.VolumeMount{
{Name: "data", MountPath: solDataDir},
},
})
return initContainers
}
func (d *SolanaDriver) NeedsConfigMap(_ *v1alpha1.NodeClusterSpec) bool {
return false
}
func (d *SolanaDriver) BuildConfigMap(_ *v1alpha1.NodeClusterSpec) map[string]string {
return nil
}
func (d *SolanaDriver) RecommendedResources(role string) (corev1.ResourceList, corev1.ResourceList) {
switch role {
case "fullnode":
return corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("16"),
corev1.ResourceMemory: resource.MustParse("128Gi"),
}, corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("24"),
corev1.ResourceMemory: resource.MustParse("256Gi"),
}
case "archive":
return corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("24"),
corev1.ResourceMemory: resource.MustParse("256Gi"),
}, corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("32"),
corev1.ResourceMemory: resource.MustParse("512Gi"),
}
default: // validator
return corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("24"),
corev1.ResourceMemory: resource.MustParse("256Gi"),
}, corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("32"),
corev1.ResourceMemory: resource.MustParse("512Gi"),
}
}
}
func (d *SolanaDriver) RecommendedStorage(role string) string {
switch role {
case "archive":
return "4Ti"
default:
return "2Ti"
}
}
func (d *SolanaDriver) rpcPort(spec *v1alpha1.NodeClusterSpec) int32 {
if spec.Ports.RPC > 0 {
return spec.Ports.RPC
}
return solRPCPort
}