From 45b3774ef16042b4d634e66ed6d04bc512be3804 Mon Sep 17 00:00:00 2001 From: j-rafique Date: Mon, 21 Sep 2026 18:17:08 +0000 Subject: [PATCH] fix: force enable storage challenge LEP-6 --- supernode/config/config_lep6_test.go | 28 ++--- supernode/config/lep6.go | 31 ++---- .../config/lep6_config_regression_test.go | 103 ++++++------------ 3 files changed, 55 insertions(+), 107 deletions(-) diff --git a/supernode/config/config_lep6_test.go b/supernode/config/config_lep6_test.go index a7fa1c2e..7a624674 100644 --- a/supernode/config/config_lep6_test.go +++ b/supernode/config/config_lep6_test.go @@ -11,11 +11,11 @@ import ( func TestLoadConfig_LEP6DefaultEnabled(t *testing.T) { t.Parallel() - // Testnet rollout default: an operator who upgrades without adding the - // LEP-6 blocks should run storage challenge + LEP-6 while the chain - // StorageTruthEnforcementMode remains the protocol gate. Explicit - // enabled:false remains the emergency-disable path. Runtime knobs - // still receive defaults so no extra config edits are required. + // Testnet rollout policy: an operator who upgrades runs storage + // challenge + LEP-6 while the chain StorageTruthEnforcementMode remains + // the protocol gate. Local enabled:false is overridden so operators cannot + // bypass credibility checks. Runtime knobs still receive defaults so no + // extra config edits are required. cfg := loadConfigFromBody(t, ` supernode: key_name: test-key @@ -97,7 +97,7 @@ storage_challenge: } } -func TestLoadConfig_LEP6EmergencyDisablesRemainFalse(t *testing.T) { +func TestLoadConfig_LEP6ExplicitFalseForcedOn(t *testing.T) { t.Parallel() cfg := loadConfigFromBody(t, ` @@ -127,17 +127,17 @@ self_healing: enabled: false `) - if cfg.StorageChallengeConfig.Enabled { - t.Fatalf("storage_challenge.enabled = true, want explicit false emergency disable preserved") + if !cfg.StorageChallengeConfig.Enabled { + t.Fatalf("storage_challenge.enabled = false, want forced true") } - if cfg.StorageChallengeConfig.LEP6.Enabled { - t.Fatalf("storage_challenge.lep6.enabled = true, want explicit false emergency disable preserved") + if !cfg.StorageChallengeConfig.LEP6.Enabled { + t.Fatalf("storage_challenge.lep6.enabled = false, want forced true") } - if cfg.StorageChallengeConfig.LEP6.Recheck.Enabled { - t.Fatalf("storage_challenge.lep6.recheck.enabled = true, want explicit false emergency disable preserved") + if !cfg.StorageChallengeConfig.LEP6.Recheck.Enabled { + t.Fatalf("storage_challenge.lep6.recheck.enabled = false, want forced true") } - if cfg.SelfHealingConfig.Enabled { - t.Fatalf("self_healing.enabled = true, want explicit false emergency disable preserved") + if !cfg.SelfHealingConfig.Enabled { + t.Fatalf("self_healing.enabled = false, want forced true") } } diff --git a/supernode/config/lep6.go b/supernode/config/lep6.go index cf9f168b..93869356 100644 --- a/supernode/config/lep6.go +++ b/supernode/config/lep6.go @@ -88,23 +88,16 @@ func hasYAMLKey(value *yaml.Node, key string) bool { return false } -// applyLEP6DefaultsAndValidate applies testnet-ready defaults to LEP-6 -// toggles and runtime knobs, then runs validation. +// applyLEP6DefaultsAndValidate forces local LEP-6 storage-truth runtimes on, +// applies runtime knobs, then runs validation. // -// Storage truth remains chain-gated: even when local toggles default TRUE, -// every LEP-6 service no-ops while StorageTruthEnforcementMode is -// UNSPECIFIED. Operators can still emergency-disable any local runtime by -// setting the relevant `enabled: false` explicitly in YAML. +// Storage truth remains chain-gated: even when local toggles are forced TRUE, +// every LEP-6 service no-ops while StorageTruthEnforcementMode is UNSPECIFIED. +// Operators must not be able to bypass storage challenges with local YAML +// `enabled: false`; network credibility testing is protocol-owned. func (c *Config) applyLEP6DefaultsAndValidate() error { - // Local storage-truth runtimes default ON for testnet operators who update - // without adding new config blocks. enabledSet=true means the YAML had an - // explicit `enabled:` key — keep the operator's value verbatim. - if !c.StorageChallengeConfig.enabledSet { - c.StorageChallengeConfig.Enabled = true - } - if !c.StorageChallengeConfig.LEP6.enabledSet { - c.StorageChallengeConfig.LEP6.Enabled = true - } + c.StorageChallengeConfig.Enabled = true + c.StorageChallengeConfig.LEP6.Enabled = true if c.StorageChallengeConfig.LEP6.MaxConcurrentTargets == 0 { c.StorageChallengeConfig.LEP6.MaxConcurrentTargets = DefaultLEP6MaxConcurrentTargets } @@ -113,9 +106,7 @@ func (c *Config) applyLEP6DefaultsAndValidate() error { } recheck := &c.StorageChallengeConfig.LEP6.Recheck - if !recheck.enabledSet { - recheck.Enabled = true - } + recheck.Enabled = true if recheck.LookbackEpochs == 0 { recheck.LookbackEpochs = DefaultLEP6RecheckLookbackEpochs } @@ -132,9 +123,7 @@ func (c *Config) applyLEP6DefaultsAndValidate() error { recheck.FailureBackoffTTLms = int(DefaultLEP6RecheckFailureBackoffTTL / time.Millisecond) } - if !c.SelfHealingConfig.enabledSet { - c.SelfHealingConfig.Enabled = true - } + c.SelfHealingConfig.Enabled = true if c.SelfHealingConfig.PollIntervalMs == 0 { c.SelfHealingConfig.PollIntervalMs = int(DefaultSelfHealingPollInterval / time.Millisecond) } diff --git a/supernode/config/lep6_config_regression_test.go b/supernode/config/lep6_config_regression_test.go index 89ccdc8b..c422dc30 100644 --- a/supernode/config/lep6_config_regression_test.go +++ b/supernode/config/lep6_config_regression_test.go @@ -1,19 +1,15 @@ package config -import ( - "os" - "path/filepath" - "strings" - "testing" -) +import "testing" // LEP-6 config regression tests. // // Coverage: // - Missing-block defaults are ON for storage_challenge, LEP-6 dispatch, // recheck, and self-healing so testnet operators get storage-truth -// runtime after update unless they explicitly emergency-disable it. -// - L6: structural validator rejects recheck=true with disabled parents. +// runtime after update. +// - Explicit `enabled:false` is overridden so operators cannot bypass +// network credibility checks locally. func TestLoadConfig_MissingBlocksDefaultEnabled(t *testing.T) { t.Parallel() @@ -60,13 +56,12 @@ self_healing: } } -func TestLoadConfig_LEP6OperatorOptInAdvisory(t *testing.T) { +func TestLoadConfig_LEP6ExplicitFalseForcedOnRegression(t *testing.T) { t.Parallel() - // Explicitly opted out — advisory must mention each disabled service. - allOff := loadConfigFromBody(t, baseConfigYAML()+` + cfg := loadConfigFromBody(t, baseConfigYAML()+` storage_challenge: - enabled: true + enabled: false lep6: enabled: false recheck: @@ -74,79 +69,43 @@ storage_challenge: self_healing: enabled: false `) - advisory := allOff.LEP6OperatorOptInAdvisory() - if advisory == "" { - t.Fatalf("advisory must be non-empty when toggles are off") + + if !cfg.StorageChallengeConfig.Enabled { + t.Fatalf("explicit storage_challenge.enabled=false must be overridden") } - for _, want := range []string{ - "storage_challenge.lep6.enabled=false", - "storage_challenge.lep6.recheck.enabled=false", - "self_healing.enabled=false", - } { - if !strings.Contains(advisory, want) { - t.Fatalf("C1 advisory missing %q in:\n%s", want, advisory) - } + if !cfg.StorageChallengeConfig.LEP6.Enabled { + t.Fatalf("explicit storage_challenge.lep6.enabled=false must be overridden") } - - // Missing blocks now default on — advisory must be empty. - allOn := loadConfigFromBody(t, baseConfigYAML()) - if got := allOn.LEP6OperatorOptInAdvisory(); got != "" { - t.Fatalf("C1 advisory should be empty when all opted in; got %q", got) + if !cfg.StorageChallengeConfig.LEP6.Recheck.Enabled { + t.Fatalf("explicit recheck.enabled=false must be overridden") + } + if !cfg.SelfHealingConfig.Enabled { + t.Fatalf("explicit self_healing.enabled=false must be overridden") + } + if got := cfg.LEP6OperatorOptInAdvisory(); got != "" { + t.Fatalf("advisory should be empty after forced enablement; got %q", got) } } -func TestLoadConfig_L6_RecheckRequiresParents(t *testing.T) { +func TestLoadConfig_L6_ExplicitDisabledParentsForcedOnBeforeValidation(t *testing.T) { t.Parallel() - cases := map[string]struct { - body string - wantErrMatch string - }{ - "recheck_true_storage_disabled": { - body: baseConfigYAML() + ` + cfg := loadConfigFromBody(t, baseConfigYAML()+` storage_challenge: enabled: false - lep6: - enabled: true - recheck: - enabled: true -`, - wantErrMatch: "storage_challenge.enabled=true", - }, - "recheck_true_lep6_disabled": { - body: baseConfigYAML() + ` -storage_challenge: - enabled: true lep6: enabled: false recheck: enabled: true -`, - wantErrMatch: "storage_challenge.lep6.enabled=true", - }, - } +`) - for name, tc := range cases { - name, tc := name, tc - t.Run(name, func(t *testing.T) { - t.Parallel() - dir := t.TempDir() - path := filepath.Join(dir, "supernode.yml") - if err := writeFile(t, path, tc.body); err != nil { - t.Fatalf("write: %v", err) - } - _, err := LoadConfig(path, dir) - if err == nil { - t.Fatalf("L6: LoadConfig succeeded; want validator rejection for %s", name) - } - if !strings.Contains(err.Error(), tc.wantErrMatch) { - t.Fatalf("L6: error %q does not contain %q", err.Error(), tc.wantErrMatch) - } - }) + if !cfg.StorageChallengeConfig.Enabled { + t.Fatalf("storage_challenge.enabled=false should be forced true before validation") + } + if !cfg.StorageChallengeConfig.LEP6.Enabled { + t.Fatalf("storage_challenge.lep6.enabled=false should be forced true before validation") + } + if !cfg.StorageChallengeConfig.LEP6.Recheck.Enabled { + t.Fatalf("recheck.enabled=true should remain true") } -} - -func writeFile(t *testing.T, path, body string) error { - t.Helper() - return os.WriteFile(path, []byte(body), 0o600) }