diff --git a/config.go b/config.go index 19c8ea3..e13c62c 100644 --- a/config.go +++ b/config.go @@ -328,6 +328,7 @@ type MemIAVLConfig struct { type StateStoreConfig struct { Enable bool `toml:"enable"` + EVMSSSplit bool `toml:"evm_ss_split"` DBDirectory string `toml:"db_directory"` Backend string `toml:"backend"` AsyncWriteBuffer int `toml:"async_write_buffer"` diff --git a/defaults.go b/defaults.go index f9bc7bd..e7b24af 100644 --- a/defaults.go +++ b/defaults.go @@ -296,6 +296,13 @@ func applyFullOverrides(cfg *SeiConfig) { cfg.API.GRPC.Enable = true cfg.API.GRPCWeb.Enable = true cfg.Storage.StateStore.Enable = true + // RPC/full nodes default to the split SS layout (EVM state in its own SS DB). + // Scoped here, not baseDefaults: only modes that run the SS store may set it, + // and per giga_store_migration.md the split is supported for RPC nodes only + // (validator/seed run no SS store; archive resets it below). A fresh node + // builds the split on bootstrap; an existing unsplit node adopts it only via a + // state-sync, so this default touches new nodes, not a migration path. + cfg.Storage.StateStore.EVMSSSplit = true cfg.Storage.StateStore.KeepRecent = 100_000 cfg.Storage.StateCommit.AsyncCommitBuffer = 100 cfg.Chain.MinRetainBlocks = 100_000 @@ -309,6 +316,9 @@ func applyArchiveOverrides(cfg *SeiConfig) { cfg.Storage.PruningStrategy = PruningNothing cfg.Storage.StateStore.KeepRecent = 0 + // Archive inherits full's overrides but the giga SS split is not supported for + // archive nodes yet (giga_store_migration.md: RPC nodes only), so reset it. + cfg.Storage.StateStore.EVMSSSplit = false // Only MinRetainBlocks disables receipt pruning at runtime; the next two // are emitted to document intent (see ReceiptStoreConfig). cfg.Chain.MinRetainBlocks = 0 diff --git a/enrichments.go b/enrichments.go index 5ba6ed2..be23c08 100644 --- a/enrichments.go +++ b/enrichments.go @@ -223,6 +223,9 @@ func DefaultEnrichments() map[string][]FieldOption { "storage.state_store.enable": { WithDescription("Enable SeiDB state-store for historical queries."), }, + "storage.state_store.evm_ss_split": { + WithDescription("Store EVM state in the SeiDB state-store (app.toml [state-store] evm-ss-split)."), + }, "storage.state_store.backend": { WithDescription("State store backend: pebbledb, rocksdb."), }, diff --git a/io_write_test.go b/io_write_test.go index 9b0c4a8..277fe06 100644 --- a/io_write_test.go +++ b/io_write_test.go @@ -107,3 +107,49 @@ func TestWriteConfigToDir_ReceiptStoreUsesRSBackend(t *testing.T) { } } } + +// TestWriteConfigToDir_EVMSSSplit guards the giga state-store default per mode: only +// the full/RPC mode renders app.toml [state-store] evm-ss-split=true (the one flag +// distinguishing a giga SS node from a plain one). validator/seed run no SS store and +// archive is unsupported, so all three omit the key entirely (omitempty) — leaving +// their rendered app.toml unchanged. The key is bare evm-ss-split, not ss-prefixed +// like its siblings, because the binary reads it unprefixed. +func TestWriteConfigToDir_EVMSSSplit(t *testing.T) { + readApp := func(home string) string { + b, err := os.ReadFile(filepath.Join(home, configDir, appTomlFile)) + if err != nil { + t.Fatalf("ReadFile app.toml: %v", err) + } + return string(b) + } + + // Mode matrix: full renders the split on; every other mode omits the key. + splitModes := map[NodeMode]bool{ModeFull: true} + for _, mode := range []NodeMode{ModeValidator, ModeSeed, ModeFull, ModeArchive} { + home := t.TempDir() + if err := WriteConfigToDir(DefaultForMode(mode), home); err != nil { + t.Fatalf("WriteConfigToDir(%s): %v", mode, err) + } + app := readApp(home) + switch { + case splitModes[mode]: + if !strings.Contains(app, "evm-ss-split = true") { + t.Errorf("%s: app.toml must render evm-ss-split = true", mode) + } + got, err := ReadConfigFromDir(home) + if err != nil { + t.Fatalf("ReadConfigFromDir(%s): %v", mode, err) + } + if !got.Storage.StateStore.EVMSSSplit { + t.Errorf("%s round-trip: state_store.evm_ss_split got false, want true", mode) + } + default: + if strings.Contains(app, "evm-ss-split") { + t.Errorf("%s: must omit evm-ss-split (mode runs no split SS store), but the key is present", mode) + } + } + if strings.Contains(app, "ss-evm-ss-split") { + t.Errorf("%s: evm-ss-split must be the bare key, not ss-evm-ss-split — the binary reads it unprefixed", mode) + } + } +} diff --git a/legacy.go b/legacy.go index dc2f1da..849402d 100644 --- a/legacy.go +++ b/legacy.go @@ -268,7 +268,12 @@ type legacyStateCommit struct { } type legacyStateStore struct { - Enable bool `toml:"ss-enable"` + Enable bool `toml:"ss-enable"` + // evm-ss-split is bare, NOT ss-prefixed — the binary reads it unprefixed + // under [state-store]. Do not add the ss- prefix (cf. rs-backend below). + // omitempty so a false value omits the key: an existing (v2) node's app.toml + // stays unchanged; only a node with the split on emits evm-ss-split = true. + EVMSSSplit bool `toml:"evm-ss-split,omitempty"` DBDirectory string `toml:"ss-db-directory"` Backend string `toml:"ss-backend"` AsyncWriteBuffer int `toml:"ss-async-write-buffer"` @@ -589,6 +594,7 @@ func (cfg *SeiConfig) toLegacyApp() legacyAppConfig { StateStore: legacyStateStore{ Enable: cfg.Storage.StateStore.Enable, + EVMSSSplit: cfg.Storage.StateStore.EVMSSSplit, DBDirectory: cfg.Storage.StateStore.DBDirectory, Backend: cfg.Storage.StateStore.Backend, AsyncWriteBuffer: cfg.Storage.StateStore.AsyncWriteBuffer, @@ -852,6 +858,7 @@ func fromLegacy(tm legacyTendermintConfig, app legacyAppConfig) *SeiConfig { }, StateStore: StateStoreConfig{ Enable: app.StateStore.Enable, + EVMSSSplit: app.StateStore.EVMSSSplit, DBDirectory: app.StateStore.DBDirectory, Backend: app.StateStore.Backend, AsyncWriteBuffer: app.StateStore.AsyncWriteBuffer, diff --git a/validate.go b/validate.go index 1fb2426..8ba1ad6 100644 --- a/validate.go +++ b/validate.go @@ -278,6 +278,10 @@ func validateStorage(r *ValidationResult, cfg *SeiConfig) { r.addWarning("storage.state_store.backend", fmt.Sprintf( "unusual backend %q; expected pebbledb or rocksdb", ss.Backend)) } + if ss.EVMSSSplit && !ss.Enable { + r.addError("storage.state_store.evm_ss_split", + "evm_ss_split requires state_store.enable=true (the split routes EVM state into the state store)") + } } func validateEVM(r *ValidationResult, cfg *SeiConfig) {