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
98 changes: 98 additions & 0 deletions sei-tendermint/config/statesync_compat_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package config_test

import (
"os"
"path/filepath"
"testing"
"time"

"github.com/spf13/viper"
"github.com/stretchr/testify/require"

"github.com/sei-protocol/sei-chain/sei-tendermint/cmd/tendermint/commands"
tmconfig "github.com/sei-protocol/sei-chain/sei-tendermint/config"
)

// This test (and TestFreshStateSyncConfigValidatesWithoutHiddenKnobs) mutate the
// global viper singleton via commands.ParseConfig, so they must not run in
// parallel with other tests in this package.

func TestHiddenStateSyncKnobsStillParseFromExistingConfig(t *testing.T) {
viper.Reset()
t.Cleanup(viper.Reset)

configPath := filepath.Join(t.TempDir(), "config.toml")
err := os.WriteFile(configPath, []byte(`
[statesync]
enable = true
rpc-servers = "localhost:26657,localhost:26659"
trust-height = 10
trust-hash = "AABB"
trust-period = "168h0m0s"
backfill-blocks = "100"
backfill-duration = "5m0s"
discovery-time = "20s"
temp-dir = "/tmp/statesync"
chunk-request-timeout = "30s"
fetchers = "5"
verify-light-block-timeout = "2m0s"
blacklist-ttl = "10m0s"
`), 0600)
require.NoError(t, err)

viper.SetConfigFile(configPath)
require.NoError(t, viper.ReadInConfig())

cfg, err := commands.ParseConfig(tmconfig.DefaultConfig())
require.NoError(t, err)
require.True(t, cfg.StateSync.Enable)
require.Equal(t, []string{"localhost:26657", "localhost:26659"}, cfg.StateSync.RPCServers)
require.Equal(t, int64(10), cfg.StateSync.TrustHeight)
require.Equal(t, "AABB", cfg.StateSync.TrustHash)
require.Equal(t, int64(100), cfg.StateSync.BackfillBlocks)
require.Equal(t, 5*time.Minute, cfg.StateSync.BackfillDuration)
require.Equal(t, 20*time.Second, cfg.StateSync.DiscoveryTime)
require.Equal(t, "/tmp/statesync", cfg.StateSync.TempDir)
require.Equal(t, 30*time.Second, cfg.StateSync.ChunkRequestTimeout)
require.Equal(t, int32(5), cfg.StateSync.Fetchers)
require.Equal(t, 2*time.Minute, cfg.StateSync.VerifyLightBlockTimeout)
require.Equal(t, 10*time.Minute, cfg.StateSync.BlacklistTTL)
}

// TestFreshStateSyncConfigValidatesWithoutHiddenKnobs mirrors the freshly-rendered
// template (no hidden tuning knobs in the file) and verifies that ParseConfig
// still produces a valid StateSyncConfig that passes ValidateBasic. This guards
// against future regressions where someone removes or zeros a default in
// DefaultStateSyncConfig without realizing the template no longer carries it.
func TestFreshStateSyncConfigValidatesWithoutHiddenKnobs(t *testing.T) {
viper.Reset()
t.Cleanup(viper.Reset)

configPath := filepath.Join(t.TempDir(), "config.toml")
err := os.WriteFile(configPath, []byte(`
[statesync]
enable = true
rpc-servers = "localhost:26657,localhost:26659"
trust-height = 10
trust-hash = "AABB"
trust-period = "168h0m0s"
`), 0600)
require.NoError(t, err)

viper.SetConfigFile(configPath)
require.NoError(t, viper.ReadInConfig())

cfg, err := commands.ParseConfig(tmconfig.DefaultConfig())
require.NoError(t, err)

defaults := tmconfig.DefaultStateSyncConfig()
require.Equal(t, defaults.BackfillBlocks, cfg.StateSync.BackfillBlocks)
require.Equal(t, defaults.BackfillDuration, cfg.StateSync.BackfillDuration)
require.Equal(t, defaults.DiscoveryTime, cfg.StateSync.DiscoveryTime)
require.Equal(t, defaults.ChunkRequestTimeout, cfg.StateSync.ChunkRequestTimeout)
require.Equal(t, defaults.Fetchers, cfg.StateSync.Fetchers)
require.Equal(t, defaults.VerifyLightBlockTimeout, cfg.StateSync.VerifyLightBlockTimeout)
require.Equal(t, defaults.BlacklistTTL, cfg.StateSync.BlacklistTTL)

require.NoError(t, cfg.StateSync.ValidateBasic())
}
34 changes: 7 additions & 27 deletions sei-tendermint/config/toml.go
Original file line number Diff line number Diff line change
Expand Up @@ -458,38 +458,18 @@ trust-hash = "{{ .StateSync.TrustHash }}"
# period should suffice.
trust-period = "{{ .StateSync.TrustPeriod }}"

# Backfill sequentially fetches after state sync completes, verifies and stores light blocks in reverse order.
# backfill-blocks means it will keep reverse fetching up to backfill-blocks number of blocks behind state sync position
# backfill-duration means it will keep fetching up to backfill-duration old time
# The actual backfill process will take at backfill-blocks as priority:
# - If backfill-blocks is set, use backfill-blocks to backfill
# - If backfill-blocks is not set to be greater than 0, use backfill-duration to backfill
backfill-blocks = "{{ .StateSync.BackfillBlocks }}"
backfill-duration = "{{ .StateSync.BackfillDuration }}"

# Time to spend discovering snapshots before initiating a restore.
discovery-time = "{{ .StateSync.DiscoveryTime }}"

# Temporary directory for state sync snapshot chunks, defaults to os.TempDir().
# The synchronizer will create a new, randomly named directory within this directory
# and remove it when the sync is complete.
temp-dir = "{{ .StateSync.TempDir }}"

# Whether to use local snapshot only for state sync or not.
# If this is true, then state sync will look for existing snapshots
# which are located in the snapshot-dir configured in app.toml (default to [home-dir]/data/snapshots)
use-local-snapshot = {{ .StateSync.UseLocalSnapshot }}

# The timeout duration before re-requesting a chunk, possibly from a different
# peer (default: 15 seconds).
chunk-request-timeout = "{{ .StateSync.ChunkRequestTimeout }}"

# The number of concurrent chunk and block fetchers to run (default: 4).
fetchers = "{{ .StateSync.Fetchers }}"

verify-light-block-timeout = "{{ .StateSync.VerifyLightBlockTimeout }}"

blacklist-ttl = "{{ .StateSync.BlacklistTTL }}"
# Advanced state-sync tuning knobs are intentionally omitted from this template:
# - backfill-blocks, backfill-duration: post-sync historical light-block backfill
# - discovery-time, temp-dir: snapshot discovery and chunk staging
# - chunk-request-timeout, fetchers: chunk transfer behavior
# - verify-light-block-timeout, blacklist-ttl: light client verification
# They are still parsed if set explicitly here; see the Sei docs for the full list
# and recommended values.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is good but how the code on the latest version behave when these configs are still present?
This is likely the case for most nodes, where they upgrade the binary but rarely change the config.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Backward compatible: only the rendered template hides these keys. The StateSyncConfig struct fields and their mapstructure tags (backfill-blocks, backfill-duration, discovery-time, temp-dir, chunk-request-timeout, fetchers, verify-light-block-timeout, blacklist-ttl) are unchanged, so existing config.toml files continue to parse and honor those values exactly as before.

Covered by TestHiddenStateSyncKnobsStillParseFromExistingConfig in sei-tendermint/config/statesync_compat_test.go (existing config path) and TestFreshStateSyncConfigValidatesWithoutHiddenKnobs (fresh config falls back to DefaultStateSyncConfig() defaults).


#######################################################################
### Consensus Configuration Options ###
Expand Down
36 changes: 36 additions & 0 deletions sei-tendermint/config/toml_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,40 @@ func checkConfig(t *testing.T, configFile string) {
t.Errorf("config file was expected to contain %s but did not", e)
}
}

// Expert-only state sync tuning knobs are intentionally left out of the
// generated template, while still being parsed from existing config files.
var hiddenStateSyncElems = []string{
"backfill-blocks",
"backfill-duration",
"discovery-time",
"temp-dir",
"chunk-request-timeout",
"fetchers",
"verify-light-block-timeout",
"blacklist-ttl",
}
for _, e := range hiddenStateSyncElems {
if configContainsKey(configFile, e) {
t.Errorf("config file was not expected to contain %s", e)
}
}
}

func configContainsKey(configFile string, key string) bool {
for _, line := range strings.Split(configFile, "\n") {
line = strings.TrimSpace(line)
rest, ok := strings.CutPrefix(line, key)
if !ok {
continue
}
// Require the next character to be the assignment '=' or any whitespace
// followed by '=', so we don't match keys whose name happens to be a
// prefix of another key (e.g. "fetchers" vs "fetchers-extra").
rest = strings.TrimLeft(rest, " \t")
if strings.HasPrefix(rest, "=") {
return true
}
}
return false
}
Loading