Skip to content
Open
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
28 changes: 14 additions & 14 deletions supernode/config/config_lep6_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -97,7 +97,7 @@ storage_challenge:
}
}

func TestLoadConfig_LEP6EmergencyDisablesRemainFalse(t *testing.T) {
func TestLoadConfig_LEP6ExplicitFalseForcedOn(t *testing.T) {
t.Parallel()

cfg := loadConfigFromBody(t, `
Expand Down Expand Up @@ -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")
}
}

Expand Down
31 changes: 10 additions & 21 deletions supernode/config/lep6.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -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
}
Expand All @@ -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)
}
Expand Down
103 changes: 31 additions & 72 deletions supernode/config/lep6_config_regression_test.go
Original file line number Diff line number Diff line change
@@ -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()
Expand Down Expand Up @@ -60,93 +56,56 @@ 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:
enabled: false
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)
}
Loading