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
19 changes: 12 additions & 7 deletions cmd/mithril/configcmd/configcmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"strconv"
"strings"

"github.com/Overclock-Validator/mithril/pkg/config"
"github.com/Overclock-Validator/mithril/pkg/tui"
"github.com/spf13/cobra"
)
Expand Down Expand Up @@ -116,7 +117,11 @@ func runConfigInit() {
}

func generateStarterConfig() string {
return `# Mithril Configuration
// Pick storage paths that work for the current environment: production
// /mnt/mithril-* when scripts/disk-setup.sh has been run, ~/.mithril/*
// otherwise. See pkg/config/defaults.go for detection details.
s := config.DefaultStoragePaths()
return fmt.Sprintf(`# Mithril Configuration
# Generated by: mithril config init
# See config.example.toml for detailed documentation of all options.

Expand All @@ -126,10 +131,10 @@ name = "mithril"
mode = "auto" # "auto" | "snapshot" | "new-snapshot" | "accountsdb"

[storage]
accounts = "/mnt/mithril-accounts" # AccountsDB (~500GB, use fastest NVMe)
shredstore = "/mnt/mithril-ledger/shredstore" # Lightbringer shred storage
snapshots = "/mnt/mithril-ledger/snapshots" # ~100GB for full + incremental
logs = "/mnt/mithril-logs" # Log files (created if missing)
accounts = %q # AccountsDB (~500GB, use fastest NVMe)
shredstore = %q # Lightbringer shred storage
snapshots = %q # ~100GB for full + incremental
logs = %q # Log files (created if missing)

[network]
cluster = "mainnet-beta" # Required: "mainnet-beta" | "testnet" | "devnet"
Expand All @@ -155,15 +160,15 @@ txpar = 24 # Recommended: 2x your CPU core count
port = 8899 # Mithril's RPC server (binds to all interfaces)

[log]
dir = "/mnt/mithril-logs" # Log files (created if missing)
dir = %q # Log files (created if missing)
level = "info" # "debug" | "info" | "warn" | "error"
to_stdout = true # Also write to stdout
max_size_mb = 100 # Max log file size before rotation
max_age_days = 7 # Delete logs older than this

# Advanced options (defaults work well for most setups)
# See config.example.toml for: [tuning], [debug], [snapshot], [reporting]
`
`, s.Accounts, s.Shredstore, s.Snapshots, s.Logs, s.Logs)
}

// runConfigSet updates a key in the config file
Expand Down
44 changes: 40 additions & 4 deletions cmd/mithril/configcmd/edit.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ const (
edScrRPC
edScrLightbringer
edScrGossip
edScrLightbringerQuiet
edScrStorage
edScrAccountsPath
edScrSnapshotsPath
Expand Down Expand Up @@ -89,6 +90,7 @@ type editModel struct {
rpcEndpoint string
lbEnabled bool
gossipEntry string
lbQuiet bool
accountsPath string
snapshotsPath string
logsPath string
Expand Down Expand Up @@ -162,6 +164,7 @@ func newEditModel(cf string, v *viper.Viper) editModel {
txparWasSet: txparWasSet,
lbEnabled: v.GetBool("lightbringer.enabled"),
gossipEntry: v.GetString("lightbringer.gossip_entrypoint"),
lbQuiet: v.GetBool("lightbringer.quiet"),
accountsPath: v.GetString("storage.accounts"),
snapshotsPath: v.GetString("storage.snapshots"),
logsPath: logsPath,
Expand Down Expand Up @@ -253,6 +256,9 @@ func (m editModel) currentItems() []edItem {
lbStatus := "disabled"
if m.lbEnabled {
lbStatus = "enabled"
if m.lbQuiet {
lbStatus += ", quiet"
}
}
return []edItem{
{label: "Network", value: "network", desc: fmt.Sprintf("cluster=%s rpc=%s", m.cluster, truncate(m.rpcEndpoint, 35))},
Expand All @@ -275,9 +281,23 @@ func (m editModel) currentItems() []edItem {
{label: "← Back", value: "_back"},
}
case edScrLightbringer:
return []edItem{
items := []edItem{
{label: "Disable", value: "disable", desc: "Use RPC only"},
{label: "Enable", value: "enable", desc: "Sidecar for lower-latency block streaming"},
}
if m.lbEnabled {
quietDesc := "off"
if m.lbQuiet {
quietDesc = "on (only warn/error in lightbringer.log)"
}
items = append(items, edItem{label: "Quiet logs", value: "quiet", desc: quietDesc})
}
items = append(items, edItem{isSep: true}, edItem{label: "← Back", value: "_back"})
return items
case edScrLightbringerQuiet:
return []edItem{
{label: "Normal logs", value: "false", desc: "Show all info messages (default)"},
{label: "Quiet mode", value: "true", desc: "Only warnings and errors — recommended for long runs"},
{isSep: true},
{label: "← Back", value: "_back"},
}
Expand Down Expand Up @@ -415,13 +435,22 @@ func (m *editModel) handleSelect(value string) {
m.pushInput(edScrRPC)

case edScrLightbringer:
m.lbEnabled = value == "enable"
if m.lbEnabled {
switch value {
case "enable":
m.lbEnabled = true
m.pushInput(edScrGossip)
} else {
case "disable":
m.lbEnabled = false
m.lbQuiet = false // Reset dependent state so disable→re-enable starts clean.
m.goBack()
case "quiet":
m.pushMenu(edScrLightbringerQuiet)
}

case edScrLightbringerQuiet:
m.lbQuiet = value == "true"
m.goBack()

case edScrStorage:
switch value {
case "accounts":
Expand Down Expand Up @@ -638,6 +667,11 @@ func (m *editModel) saveConfig() {
content = setTomlValue(content, "lightbringer", "gossip_entrypoint", fmt.Sprintf("%q", m.gossipEntry))
}
}
if m.lbQuiet {
content = setTomlValue(content, "lightbringer", "quiet", "true")
} else {
content = setTomlValue(content, "lightbringer", "quiet", "false")
}
} else {
// Only force block.source="rpc" if no external lightbringer_endpoint is configured.
// External LB mode (enabled=false + endpoint set) is a valid runtime config.
Expand Down Expand Up @@ -693,6 +727,8 @@ func (m editModel) menuTitleDesc() (string, string) {
return "Solana Cluster", ""
case edScrLightbringer:
return "Lightbringer Sidecar", ""
case edScrLightbringerQuiet:
return "Lightbringer Log Verbosity", "Quiet mode suppresses Lightbringer info/debug logs (only warnings and errors)."
case edScrStorage:
return "Storage Paths", ""
case edScrLogLevel:
Expand Down
68 changes: 68 additions & 0 deletions cmd/mithril/configcmd/edit_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package configcmd

import (
"testing"

"github.com/stretchr/testify/assert"
)

// TestEditor_ScrLightbringerQuiet sets m.lbQuiet from a menu selection.
func TestEditor_ScrLightbringerQuiet_True(t *testing.T) {
m := &editModel{screen: edScrLightbringerQuiet, lbEnabled: true, lbQuiet: false}
m.handleSelect("true")
assert.True(t, m.lbQuiet, "picking 'true' should set lbQuiet=true")
}

func TestEditor_ScrLightbringerQuiet_False(t *testing.T) {
m := &editModel{screen: edScrLightbringerQuiet, lbEnabled: true, lbQuiet: true}
m.handleSelect("false")
assert.False(t, m.lbQuiet, "picking 'false' should set lbQuiet=false")
}

// TestEditor_DisableLB_ResetsQuiet verifies that "disable" resets m.lbQuiet
// so a later re-enable starts clean (no stale quiet state carried over).
func TestEditor_DisableLB_ResetsQuiet(t *testing.T) {
m := &editModel{
screen: edScrLightbringer,
lbEnabled: true,
lbQuiet: true, // previously enabled quiet
}
m.handleSelect("disable")
assert.False(t, m.lbEnabled, "disable should set lbEnabled=false")
assert.False(t, m.lbQuiet, "disable should reset lbQuiet to avoid stale state on re-enable")
}

// TestEditor_EnableLB_PreservesQuiet ensures enable does not clobber quiet.
func TestEditor_EnableLB_PreservesQuiet(t *testing.T) {
m := &editModel{
screen: edScrLightbringer,
lbEnabled: false,
lbQuiet: true,
}
m.handleSelect("enable")
assert.True(t, m.lbEnabled)
assert.True(t, m.lbQuiet, "enable should not modify lbQuiet")
}

// TestEditor_QuietMenuItem_ShownOnlyWhenLBEnabled verifies the conditional
// menu rendering — the "Quiet logs" entry must not appear when LB is off.
func TestEditor_QuietMenuItem_HiddenWhenLBDisabled(t *testing.T) {
m := editModel{screen: edScrLightbringer, lbEnabled: false}
items := m.currentItems()
for _, it := range items {
assert.NotEqual(t, "quiet", it.value, "Quiet logs entry must not appear when lbEnabled=false")
}
}

func TestEditor_QuietMenuItem_ShownWhenLBEnabled(t *testing.T) {
m := editModel{screen: edScrLightbringer, lbEnabled: true}
items := m.currentItems()
found := false
for _, it := range items {
if it.value == "quiet" {
found = true
break
}
}
assert.True(t, found, "Quiet logs entry should appear when lbEnabled=true")
}
11 changes: 11 additions & 0 deletions cmd/mithril/dashboardcmd/dashboard.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ func newModel(cf string) model {
{section: "lightbringer", key: "gossip_entrypoint", label: "Gossip Entrypoint"},
{section: "lightbringer", key: "grpc_addr", label: "LB gRPC Address"},
{section: "lightbringer", key: "rpc_addr", label: "LB HTTP Address"},
{section: "lightbringer", key: "quiet", label: "LB Quiet Logs"},
{isSep: true},
{section: "tuning", key: "txpar", label: "TX Parallelism"},
{section: "rpc", key: "port", label: "RPC Port"},
Expand Down Expand Up @@ -728,6 +729,11 @@ func (m model) getFieldValue(f editFieldDef) string {
return m.cfg.lbGrpcAddr
case "lightbringer.rpc_addr":
return m.cfg.lbRpcAddr
case "lightbringer.quiet":
if m.cfg.lbQuiet {
return "true"
}
return "false"
case "tuning.txpar":
return m.cfg.txpar
case "rpc.port":
Expand Down Expand Up @@ -759,6 +765,11 @@ func menuOptionsFor(section, key string) []editOption {
{label: "false", value: "false", desc: "Disabled"},
{label: "true", value: "true", desc: "Enabled"},
}
case "lightbringer.quiet":
return []editOption{
{label: "false", value: "false", desc: "Show all info messages (default)"},
{label: "true", value: "true", desc: "Only warnings and errors — recommended for long runs"},
}
case "log.level":
return []editOption{
{label: "debug", value: "debug"},
Expand Down
11 changes: 10 additions & 1 deletion cmd/mithril/dashboardcmd/data.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ type configData struct {
lbRpcAddr string
lbExternalEndpoint string // block.lightbringer_endpoint for external LB mode
lbBinaryPath string
lbQuiet bool
accountsPath string
snapshotsPath string
shredstorePath string
Expand Down Expand Up @@ -112,6 +113,7 @@ func readConfig(configFile string) *configData {
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"),
Expand Down Expand Up @@ -375,6 +377,13 @@ func runDoctorChecks(configFile string, cfg *configData) []checkResult {
} else {
results = append(results, checkResult{"Gossip entrypoint", "fail", "not set"})
}

// Quiet mode (informational)
if cfg.lbQuiet {
results = append(results, checkResult{"Lightbringer logs", "pass", "quiet (warn/error only)"})
} else {
results = append(results, checkResult{"Lightbringer logs", "pass", "normal (info)"})
}
} else if cfg.blockSource == "lightbringer" && cfg.lbExternalEndpoint != "" {
// External Lightbringer mode — sidecar disabled but endpoint configured
results = append(results, checkResult{"Lightbringer", "pass", "external at " + cfg.lbExternalEndpoint})
Expand Down Expand Up @@ -476,7 +485,7 @@ func saveConfigValue(configFile, section, key, value string) error {
case fullKey == "block.max_rps" || fullKey == "block.max_inflight" ||
fullKey == "tuning.txpar" || fullKey == "rpc.port":
tomlValue = value // numeric — no quoting
case fullKey == "lightbringer.enabled":
case fullKey == "lightbringer.enabled" || fullKey == "lightbringer.quiet":
tomlValue = value // boolean — no quoting
case fullKey == "network.rpc":
// Preserve failover endpoints — read existing array, update first element
Expand Down
3 changes: 3 additions & 0 deletions cmd/mithril/node/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ var (
lightbringerInfluxdbToken string
lightbringerBlockConfirmHTTP string
lightbringerBlockConfirmWS string
lightbringerQuiet bool
)

func init() {
Expand Down Expand Up @@ -426,6 +427,7 @@ func initConfigAndBindFlags(cmd *cobra.Command) error {
lightbringerInfluxdbToken = config.GetString("lightbringer.influxdb_token")
lightbringerBlockConfirmHTTP = config.GetString("lightbringer.block_confirmation_rpc_http")
lightbringerBlockConfirmWS = config.GetString("lightbringer.block_confirmation_rpc_websocket")
lightbringerQuiet = config.GetBool("lightbringer.quiet")

// Auto-sync: when lightbringer is enabled, override block source settings
if lightbringerEnabled {
Expand Down Expand Up @@ -747,6 +749,7 @@ func runLive(c *cobra.Command, args []string) {
InfluxdbToken: lightbringerInfluxdbToken,
BlockConfirmRpcHTTP: lightbringerBlockConfirmHTTP,
BlockConfirmRpcWS: lightbringerBlockConfirmWS,
Quiet: lightbringerQuiet,
},
LogWriter: lbLogWriter,
})
Expand Down
Loading
Loading