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
10 changes: 9 additions & 1 deletion cmd/mithril/configcmd/configcmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,16 @@ cluster = "mainnet-beta" # Required: "mainnet-beta" | "testnet" | "devnet"
rpc = ["https://api.mainnet-beta.solana.com"]

[block]
source = "rpc" # "rpc" | "lightbringer"
source = "rpc" # "rpc" | "lightbringer" | "turbine"
# lightbringer_endpoint = "localhost:9000"
# turbine_bind_addr = "0.0.0.0:8001"

# [turbine]
# bind_addr = "0.0.0.0:8001"
# gossip_entrypoint = "1.2.3.4:8000"
# gossip_bind_addr = "0.0.0.0:65401"
# advertised_ip = "203.0.113.10"
# shred_version = 0

# [lightbringer]
# enabled = false
Expand Down
53 changes: 53 additions & 0 deletions cmd/mithril/dashboardcmd/dashboard.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,11 @@ func newModel(cf string) model {
{section: "storage", key: "logs", label: "Logs Path"},
{isSep: true},
{section: "block", key: "source", label: "Block Source"},
{section: "block", key: "turbine_bind_addr", label: "Turbine UDP"},
{section: "turbine", key: "gossip_entrypoint", label: "Turbine Gossip"},
{section: "turbine", key: "gossip_bind_addr", label: "Gossip UDP"},
{section: "turbine", key: "advertised_ip", label: "Advertised IP"},
{section: "turbine", key: "shred_version", label: "Shred Version"},
{section: "block", key: "max_rps", label: "Block Max RPS"},
{section: "block", key: "max_inflight", label: "Block Max Inflight"},
{isSep: true},
Expand Down Expand Up @@ -714,6 +719,16 @@ func (m model) getFieldValue(f editFieldDef) string {
return m.cfg.logsPath
case "block.source":
return m.cfg.blockSource
case "block.turbine_bind_addr":
return m.cfg.turbineBindAddr
case "turbine.gossip_entrypoint":
return m.cfg.turbineGossip
case "turbine.gossip_bind_addr":
return m.cfg.turbineGossipBind
case "turbine.advertised_ip":
return m.cfg.turbineAdvertisedIP
case "turbine.shred_version":
return m.cfg.turbineShredVersion
case "block.max_rps":
return m.cfg.blockMaxRPS
case "block.max_inflight":
Expand Down Expand Up @@ -759,6 +774,7 @@ func menuOptionsFor(section, key string) []editOption {
return []editOption{
{label: "rpc", value: "rpc", desc: "Fetch blocks via RPC"},
{label: "lightbringer", value: "lightbringer", desc: "Sidecar streaming"},
{label: "turbine", value: "turbine", desc: "Native shred receiver"},
}
case "lightbringer.enabled":
return []editOption{
Expand Down Expand Up @@ -877,6 +893,43 @@ func (m *model) applyEditField() {
m.editErr = "Must be a number"
return
}
case key == "turbine.shred_version":
if value != "" {
n, err := strconv.Atoi(value)
if err != nil || n < 0 || n > 65535 {
m.editErr = "Must be 0-65535"
return
}
}
case key == "block.turbine_bind_addr" || key == "turbine.gossip_bind_addr":
if value != "" {
_, portStr, err := net.SplitHostPort(value)
if err != nil {
m.editErr = "Format: host:port or :port"
return
}
if p, perr := strconv.Atoi(portStr); perr != nil || p < 1 || p > 65535 {
m.editErr = "Port must be 1-65535"
return
}
}
case key == "turbine.gossip_entrypoint":
if value != "" {
host, portStr, err := net.SplitHostPort(value)
if err != nil || host == "" {
m.editErr = "Format: host:port"
return
}
if p, perr := strconv.Atoi(portStr); perr != nil || p < 1 || p > 65535 {
m.editErr = "Port must be 1-65535"
return
}
}
case key == "turbine.advertised_ip":
if value != "" && net.ParseIP(value) == nil {
m.editErr = "Must be an IP address"
return
}
case key == "tuning.txpar":
if value != "" { // empty = sequential (runtime default 0)
if _, err := strconv.Atoi(value); err != nil {
Expand Down
119 changes: 78 additions & 41 deletions cmd/mithril/dashboardcmd/data.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,26 +61,31 @@ func readState(accountsPath string) *nodeState {
// ── Config reading ──────────────────────────────────────────────────────

type configData struct {
cluster string
rpcEndpoints []string
blockSource string
lbEnabled bool
lbGossip string
lbGrpcAddr string
lbRpcAddr string
lbExternalEndpoint string // block.lightbringer_endpoint for external LB mode
lbBinaryPath string
lbQuiet bool
accountsPath string
snapshotsPath string
shredstorePath string
logsPath string
txpar string
blockMaxRPS string
blockInflight string
rpcPort string
logLevel string
bootstrapMode string
cluster string
rpcEndpoints []string
blockSource string
lbEnabled bool
lbGossip string
lbGrpcAddr string
lbRpcAddr string
lbExternalEndpoint string // block.lightbringer_endpoint for external LB mode
lbBinaryPath string
lbQuiet bool
turbineBindAddr string
turbineGossip string
turbineGossipBind string
turbineAdvertisedIP string
turbineShredVersion string
accountsPath string
snapshotsPath string
shredstorePath string
logsPath string
txpar string
blockMaxRPS string
blockInflight string
rpcPort string
logLevel string
bootstrapMode string
}

func readConfig(configFile string) *configData {
Expand All @@ -106,28 +111,37 @@ func readConfig(configFile string) *configData {
if logsPath == "" {
logsPath = v.GetString("log.dir")
}
turbineBindAddr := v.GetString("block.turbine_bind_addr")
if turbineBindAddr == "" {
turbineBindAddr = v.GetString("turbine.bind_addr")
}

return &configData{
cluster: cluster,
rpcEndpoints: v.GetStringSlice("network.rpc"),
blockSource: v.GetString("block.source"),
lbEnabled: v.GetBool("lightbringer.enabled"),
lbGossip: v.GetString("lightbringer.gossip_entrypoint"),
lbGrpcAddr: v.GetString("lightbringer.grpc_addr"),
lbRpcAddr: v.GetString("lightbringer.rpc_addr"),
lbQuiet: v.GetBool("lightbringer.quiet"),
lbExternalEndpoint: v.GetString("block.lightbringer_endpoint"),
lbBinaryPath: v.GetString("lightbringer.binary_path"),
accountsPath: v.GetString("storage.accounts"),
snapshotsPath: v.GetString("storage.snapshots"),
shredstorePath: v.GetString("storage.shredstore"),
logsPath: logsPath,
txpar: txpar,
blockMaxRPS: v.GetString("block.max_rps"),
blockInflight: v.GetString("block.max_inflight"),
rpcPort: v.GetString("rpc.port"),
logLevel: v.GetString("log.level"),
bootstrapMode: v.GetString("bootstrap.mode"),
cluster: cluster,
rpcEndpoints: v.GetStringSlice("network.rpc"),
blockSource: v.GetString("block.source"),
lbEnabled: v.GetBool("lightbringer.enabled"),
lbGossip: v.GetString("lightbringer.gossip_entrypoint"),
lbGrpcAddr: v.GetString("lightbringer.grpc_addr"),
lbRpcAddr: v.GetString("lightbringer.rpc_addr"),
lbQuiet: v.GetBool("lightbringer.quiet"),
lbExternalEndpoint: v.GetString("block.lightbringer_endpoint"),
lbBinaryPath: v.GetString("lightbringer.binary_path"),
turbineBindAddr: turbineBindAddr,
turbineGossip: v.GetString("turbine.gossip_entrypoint"),
turbineGossipBind: v.GetString("turbine.gossip_bind_addr"),
turbineAdvertisedIP: v.GetString("turbine.advertised_ip"),
turbineShredVersion: v.GetString("turbine.shred_version"),
accountsPath: v.GetString("storage.accounts"),
snapshotsPath: v.GetString("storage.snapshots"),
shredstorePath: v.GetString("storage.shredstore"),
logsPath: logsPath,
txpar: txpar,
blockMaxRPS: v.GetString("block.max_rps"),
blockInflight: v.GetString("block.max_inflight"),
rpcPort: v.GetString("rpc.port"),
logLevel: v.GetString("log.level"),
bootstrapMode: v.GetString("bootstrap.mode"),
}
}

Expand Down Expand Up @@ -392,6 +406,28 @@ func runDoctorChecks(configFile string, cfg *configData) []checkResult {
} else if cfg.blockSource == "lightbringer" && cfg.lbExternalEndpoint == "" {
// Invalid: source=lightbringer but no sidecar and no endpoint
results = append(results, checkResult{"Lightbringer", "fail", "block.source=lightbringer requires enabled sidecar or endpoint"})
} else if cfg.blockSource == "turbine" {
if cfg.turbineBindAddr == "" {
results = append(results, checkResult{"Turbine UDP", "fail", "block.source=turbine requires block.turbine_bind_addr or turbine.bind_addr"})
} else if _, _, err := net.SplitHostPort(cfg.turbineBindAddr); err != nil {
results = append(results, checkResult{"Turbine UDP", "fail", "invalid format: " + cfg.turbineBindAddr})
} else {
results = append(results, checkResult{"Turbine UDP", "pass", cfg.turbineBindAddr})
}
if cfg.turbineGossip == "" {
results = append(results, checkResult{"Turbine gossip", "warn", "empty; UDP-only receiver mode"})
} else if _, _, err := net.SplitHostPort(cfg.turbineGossip); err != nil {
results = append(results, checkResult{"Turbine gossip", "fail", "invalid format: " + cfg.turbineGossip})
} else {
results = append(results, checkResult{"Turbine gossip", "pass", cfg.turbineGossip})
}
if cfg.turbineGossipBind != "" {
if _, _, err := net.SplitHostPort(cfg.turbineGossipBind); err != nil {
results = append(results, checkResult{"Turbine gossip UDP", "fail", "invalid format: " + cfg.turbineGossipBind})
} else {
results = append(results, checkResult{"Turbine gossip UDP", "pass", cfg.turbineGossipBind})
}
}
} else {
results = append(results, checkResult{"Lightbringer", "pass", "disabled"})
}
Expand Down Expand Up @@ -485,7 +521,8 @@ func saveConfigValue(configFile, section, key, value string) error {
var tomlValue string
switch {
case fullKey == "block.max_rps" || fullKey == "block.max_inflight" ||
fullKey == "tuning.txpar" || fullKey == "rpc.port":
fullKey == "tuning.txpar" || fullKey == "rpc.port" ||
fullKey == "turbine.shred_version":
tomlValue = value // numeric — no quoting
case fullKey == "lightbringer.enabled" || fullKey == "lightbringer.quiet":
tomlValue = value // boolean — no quoting
Expand Down
Loading
Loading