From 7c6ac8f5b2aac217fe0925f6d177997748737a74 Mon Sep 17 00:00:00 2001 From: yzang2019 Date: Tue, 31 Mar 2026 16:40:36 -0700 Subject: [PATCH 1/5] Restructure sei data folder for Giga --- app/receipt_store_config.go | 6 +- app/seidb_test.go | 3 +- sei-db/common/utils/path.go | 62 ++++++- sei-db/common/utils/path_test.go | 170 ++++++++++++++++++ sei-db/state_db/sc/composite/store.go | 4 +- sei-db/state_db/ss/composite/store.go | 3 +- .../cmd/tendermint/commands/reindex_event.go | 14 +- .../cmd/tendermint/commands/reset.go | 86 ++++----- sei-tendermint/config/config.go | 19 +- sei-tendermint/config/config_test.go | 88 +++++++++ sei-tendermint/config/db.go | 41 ++++- sei-tendermint/config/db_test.go | 120 +++++++++++++ 12 files changed, 546 insertions(+), 70 deletions(-) create mode 100644 sei-db/common/utils/path_test.go create mode 100644 sei-tendermint/config/db_test.go diff --git a/app/receipt_store_config.go b/app/receipt_store_config.go index 8d2acfacf2..d5398eaf6b 100644 --- a/app/receipt_store_config.go +++ b/app/receipt_store_config.go @@ -1,9 +1,9 @@ package app import ( - "path/filepath" - seidbconfig "github.com/sei-protocol/sei-chain/sei-db/config" + + "github.com/sei-protocol/sei-chain/sei-db/common/utils" ) const ( @@ -20,7 +20,7 @@ func readReceiptStoreConfig(homePath string, appOpts seidbconfig.AppOptions) (se return receiptConfig, err } if receiptConfig.DBDirectory == "" { - receiptConfig.DBDirectory = filepath.Join(homePath, "data", "receipt.db") + receiptConfig.DBDirectory = utils.GetReceiptStorePath(homePath) } return receiptConfig, nil } diff --git a/app/seidb_test.go b/app/seidb_test.go index 8504f32848..046ab4520c 100644 --- a/app/seidb_test.go +++ b/app/seidb_test.go @@ -155,7 +155,8 @@ func TestReadReceiptStoreConfigUsesDefaultDirectoryWhenUnset(t *testing.T) { homePath := t.TempDir() receiptConfig, err := readReceiptStoreConfig(homePath, mapAppOpts{}) require.NoError(t, err) - assert.Equal(t, filepath.Join(homePath, "data", "receipt.db"), receiptConfig.DBDirectory) + // New nodes (no legacy data/receipt.db) get the new ledger/ layout + assert.Equal(t, filepath.Join(homePath, "data", "ledger", "receipt.db"), receiptConfig.DBDirectory) } // TestFullAppPathWithParquetReceiptStore exercises the full app.New path with rs-backend = "parquet" diff --git a/sei-db/common/utils/path.go b/sei-db/common/utils/path.go index aad7207e19..fea01b5102 100644 --- a/sei-db/common/utils/path.go +++ b/sei-db/common/utils/path.go @@ -1,13 +1,69 @@ package utils -import "path/filepath" +import ( + "os" + "path/filepath" +) +// PathExists returns true if the given path exists on disk. +func PathExists(path string) bool { + _, err := os.Stat(path) + return err == nil +} + +// GetCommitStorePath returns the path for the memiavl state commitment store. +// New nodes use data/state_commit/memiavl; existing nodes with data/committer.db +// continue using the legacy path for backward compatibility. func GetCommitStorePath(homePath string) string { - return filepath.Join(homePath, "data", "committer.db") + legacyPath := filepath.Join(homePath, "data", "committer.db") + if PathExists(legacyPath) { + return legacyPath + } + return filepath.Join(homePath, "data", "state_commit", "memiavl") +} + +// GetFlatKVPath returns the path for the FlatKV EVM commit store. +// New nodes use data/state_commit/flatkv; existing nodes with data/flatkv +// continue using the legacy path for backward compatibility. +func GetFlatKVPath(homePath string) string { + legacyPath := filepath.Join(homePath, "data", "flatkv") + if PathExists(legacyPath) { + return legacyPath + } + return filepath.Join(homePath, "data", "state_commit", "flatkv") } +// GetStateStorePath returns the path for the Cosmos state store (SS). +// New nodes use data/state_store/cosmos_ss/{backend}; existing nodes with +// data/{backend} continue using the legacy path for backward compatibility. func GetStateStorePath(homePath string, backend string) string { - return filepath.Join(homePath, "data", backend) + legacyPath := filepath.Join(homePath, "data", backend) + if PathExists(legacyPath) { + return legacyPath + } + return filepath.Join(homePath, "data", "state_store", "cosmos_ss", backend) +} + +// GetEVMStateStorePath returns the path for the EVM state store. +// New nodes use data/state_store/evm_ss; existing nodes with data/evm_ss +// continue using the legacy path for backward compatibility. +func GetEVMStateStorePath(homePath string) string { + legacyPath := filepath.Join(homePath, "data", "evm_ss") + if PathExists(legacyPath) { + return legacyPath + } + return filepath.Join(homePath, "data", "state_store", "evm_ss") +} + +// GetReceiptStorePath returns the path for the receipt store. +// New nodes use data/ledger/receipt.db; existing nodes with data/receipt.db +// continue using the legacy path for backward compatibility. +func GetReceiptStorePath(homePath string) string { + legacyPath := filepath.Join(homePath, "data", "receipt.db") + if PathExists(legacyPath) { + return legacyPath + } + return filepath.Join(homePath, "data", "ledger", "receipt.db") } func GetChangelogPath(dbPath string) string { diff --git a/sei-db/common/utils/path_test.go b/sei-db/common/utils/path_test.go new file mode 100644 index 0000000000..acd94902a0 --- /dev/null +++ b/sei-db/common/utils/path_test.go @@ -0,0 +1,170 @@ +package utils + +import ( + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestPathExists(t *testing.T) { + dir := t.TempDir() + assert.True(t, PathExists(dir)) + assert.False(t, PathExists(filepath.Join(dir, "nonexistent"))) + + f := filepath.Join(dir, "file.txt") + require.NoError(t, os.WriteFile(f, []byte("hi"), 0644)) + assert.True(t, PathExists(f)) +} + +// --- GetCommitStorePath --- + +func TestGetCommitStorePath_NewNode(t *testing.T) { + home := t.TempDir() + got := GetCommitStorePath(home) + assert.Equal(t, filepath.Join(home, "data", "state_commit", "memiavl"), got) +} + +func TestGetCommitStorePath_LegacyExists(t *testing.T) { + home := t.TempDir() + legacy := filepath.Join(home, "data", "committer.db") + require.NoError(t, os.MkdirAll(legacy, 0755)) + + got := GetCommitStorePath(home) + assert.Equal(t, legacy, got) +} + +// --- GetFlatKVPath --- + +func TestGetFlatKVPath_NewNode(t *testing.T) { + home := t.TempDir() + got := GetFlatKVPath(home) + assert.Equal(t, filepath.Join(home, "data", "state_commit", "flatkv"), got) +} + +func TestGetFlatKVPath_LegacyExists(t *testing.T) { + home := t.TempDir() + legacy := filepath.Join(home, "data", "flatkv") + require.NoError(t, os.MkdirAll(legacy, 0755)) + + got := GetFlatKVPath(home) + assert.Equal(t, legacy, got) +} + +// --- GetStateStorePath --- + +func TestGetStateStorePath_NewNode_Pebble(t *testing.T) { + home := t.TempDir() + got := GetStateStorePath(home, "pebbledb") + assert.Equal(t, filepath.Join(home, "data", "state_store", "cosmos_ss", "pebbledb"), got) +} + +func TestGetStateStorePath_NewNode_RocksDB(t *testing.T) { + home := t.TempDir() + got := GetStateStorePath(home, "rocksdb") + assert.Equal(t, filepath.Join(home, "data", "state_store", "cosmos_ss", "rocksdb"), got) +} + +func TestGetStateStorePath_LegacyExists(t *testing.T) { + home := t.TempDir() + legacy := filepath.Join(home, "data", "pebbledb") + require.NoError(t, os.MkdirAll(legacy, 0755)) + + got := GetStateStorePath(home, "pebbledb") + assert.Equal(t, legacy, got) +} + +func TestGetStateStorePath_LegacyForDifferentBackend(t *testing.T) { + home := t.TempDir() + // Legacy rocksdb dir exists but we ask for pebbledb — no legacy match + require.NoError(t, os.MkdirAll(filepath.Join(home, "data", "rocksdb"), 0755)) + + got := GetStateStorePath(home, "pebbledb") + assert.Equal(t, filepath.Join(home, "data", "state_store", "cosmos_ss", "pebbledb"), got) +} + +// --- GetEVMStateStorePath --- + +func TestGetEVMStateStorePath_NewNode(t *testing.T) { + home := t.TempDir() + got := GetEVMStateStorePath(home) + assert.Equal(t, filepath.Join(home, "data", "state_store", "evm_ss"), got) +} + +func TestGetEVMStateStorePath_LegacyExists(t *testing.T) { + home := t.TempDir() + legacy := filepath.Join(home, "data", "evm_ss") + require.NoError(t, os.MkdirAll(legacy, 0755)) + + got := GetEVMStateStorePath(home) + assert.Equal(t, legacy, got) +} + +// --- GetReceiptStorePath --- + +func TestGetReceiptStorePath_NewNode(t *testing.T) { + home := t.TempDir() + got := GetReceiptStorePath(home) + assert.Equal(t, filepath.Join(home, "data", "ledger", "receipt.db"), got) +} + +func TestGetReceiptStorePath_LegacyExists(t *testing.T) { + home := t.TempDir() + legacy := filepath.Join(home, "data", "receipt.db") + require.NoError(t, os.MkdirAll(legacy, 0755)) + + got := GetReceiptStorePath(home) + assert.Equal(t, legacy, got) +} + +// --- GetChangelogPath (unchanged, but verify) --- + +func TestGetChangelogPath(t *testing.T) { + assert.Equal(t, "/foo/bar/changelog", GetChangelogPath("/foo/bar")) +} + +// --- Edge: new path already has data (second run of new node) --- + +func TestGetCommitStorePath_NewDataAlreadyExists(t *testing.T) { + home := t.TempDir() + newPath := filepath.Join(home, "data", "state_commit", "memiavl") + require.NoError(t, os.MkdirAll(newPath, 0755)) + + got := GetCommitStorePath(home) + assert.Equal(t, newPath, got, "should use new path when legacy is absent even if new path already exists") +} + +func TestGetStateStorePath_NewDataAlreadyExists(t *testing.T) { + home := t.TempDir() + newPath := filepath.Join(home, "data", "state_store", "cosmos_ss", "pebbledb") + require.NoError(t, os.MkdirAll(newPath, 0755)) + + got := GetStateStorePath(home, "pebbledb") + assert.Equal(t, newPath, got, "should use new path when legacy is absent even if new path already exists") +} + +// --- Edge: both legacy and new exist (legacy wins) --- + +func TestGetCommitStorePath_BothExist(t *testing.T) { + home := t.TempDir() + legacy := filepath.Join(home, "data", "committer.db") + require.NoError(t, os.MkdirAll(legacy, 0755)) + newPath := filepath.Join(home, "data", "state_commit", "memiavl") + require.NoError(t, os.MkdirAll(newPath, 0755)) + + got := GetCommitStorePath(home) + assert.Equal(t, legacy, got, "legacy should take precedence when both exist") +} + +func TestGetReceiptStorePath_BothExist(t *testing.T) { + home := t.TempDir() + legacy := filepath.Join(home, "data", "receipt.db") + require.NoError(t, os.MkdirAll(legacy, 0755)) + newPath := filepath.Join(home, "data", "ledger", "receipt.db") + require.NoError(t, os.MkdirAll(newPath, 0755)) + + got := GetReceiptStorePath(home) + assert.Equal(t, legacy, got, "legacy should take precedence when both exist") +} diff --git a/sei-db/state_db/sc/composite/store.go b/sei-db/state_db/sc/composite/store.go index c4e200c822..ff1b6b3305 100644 --- a/sei-db/state_db/sc/composite/store.go +++ b/sei-db/state_db/sc/composite/store.go @@ -6,10 +6,10 @@ import ( "context" "fmt" "math" - "path/filepath" commonerrors "github.com/sei-protocol/sei-chain/sei-db/common/errors" commonevm "github.com/sei-protocol/sei-chain/sei-db/common/evm" + "github.com/sei-protocol/sei-chain/sei-db/common/utils" "github.com/sei-protocol/sei-chain/sei-db/config" "github.com/sei-protocol/sei-chain/sei-db/proto" "github.com/sei-protocol/sei-chain/sei-db/state_db/sc/flatkv" @@ -72,7 +72,7 @@ func NewCompositeCommitStore( // Initialize FlatKV store struct if write mode requires it // Note: DB is NOT opened here, will be opened in LoadVersion if cfg.WriteMode == config.DualWrite || cfg.WriteMode == config.SplitWrite { - flatkvPath := filepath.Join(homeDir, "data", "flatkv") + flatkvPath := utils.GetFlatKVPath(homeDir) store.evmCommitter = flatkv.NewCommitStore(ctx, flatkvPath, cfg.FlatKVConfig) } diff --git a/sei-db/state_db/ss/composite/store.go b/sei-db/state_db/ss/composite/store.go index 41b3c137e9..609b78ea96 100644 --- a/sei-db/state_db/ss/composite/store.go +++ b/sei-db/state_db/ss/composite/store.go @@ -2,7 +2,6 @@ package composite import ( "fmt" - "path/filepath" "sync" commonevm "github.com/sei-protocol/sei-chain/sei-db/common/evm" @@ -59,7 +58,7 @@ func NewCompositeStateStore( if ssConfig.EVMEnabled() { evmDir := ssConfig.EVMDBDirectory if evmDir == "" { - evmDir = filepath.Join(homeDir, "data", "evm_ss") + evmDir = utils.GetEVMStateStorePath(homeDir) } evmStore, err := evm.NewEVMStateStore(evmDir, ssConfig) diff --git a/sei-tendermint/cmd/tendermint/commands/reindex_event.go b/sei-tendermint/cmd/tendermint/commands/reindex_event.go index e3b33f3d81..b8c85e9713 100644 --- a/sei-tendermint/cmd/tendermint/commands/reindex_event.go +++ b/sei-tendermint/cmd/tendermint/commands/reindex_event.go @@ -149,23 +149,23 @@ func loadEventSinks(cfg *tmcfg.Config) ([]indexer.EventSink, error) { func loadStateAndBlockStore(cfg *tmcfg.Config) (*store.BlockStore, state.Store, error) { dbType := dbm.BackendType(cfg.DBBackend) - if !os.FileExists(filepath.Join(cfg.DBDir(), "blockstore.db")) { + blockstoreDir := tmcfg.ResolveDBDir("blockstore", cfg.DBDir()) + if !os.FileExists(filepath.Join(blockstoreDir, "blockstore.db")) { return nil, nil, fmt.Errorf("no blockstore found in %v", cfg.DBDir()) } - // Get BlockStore - blockStoreDB, err := dbm.NewDB("blockstore", dbType, cfg.DBDir()) + blockStoreDB, err := dbm.NewDB("blockstore", dbType, blockstoreDir) if err != nil { return nil, nil, err } blockStore := store.NewBlockStore(blockStoreDB) - if !os.FileExists(filepath.Join(cfg.DBDir(), "state.db")) { - return nil, nil, fmt.Errorf("no blockstore found in %v", cfg.DBDir()) + stateDir := tmcfg.ResolveDBDir("state", cfg.DBDir()) + if !os.FileExists(filepath.Join(stateDir, "state.db")) { + return nil, nil, fmt.Errorf("no state store found in %v", cfg.DBDir()) } - // Get StateStore - stateDB, err := dbm.NewDB("state", dbType, cfg.DBDir()) + stateDB, err := dbm.NewDB("state", dbType, stateDir) if err != nil { return nil, nil, err } diff --git a/sei-tendermint/cmd/tendermint/commands/reset.go b/sei-tendermint/cmd/tendermint/commands/reset.go index 7e994b9ee7..280415ee54 100644 --- a/sei-tendermint/cmd/tendermint/commands/reset.go +++ b/sei-tendermint/cmd/tendermint/commands/reset.go @@ -110,53 +110,34 @@ func ResetAll(dbDir, privValKeyFile, privValStateFile string, keyType string, ho return ResetFilePV(filepath.Join(homeDir, privValKeyFile), filepath.Join(homeDir, privValStateFile), keyType) } -// ResetState removes all blocks, tendermint state, indexed transactions and evidence. -func ResetState(dbDir string) error { - blockdb := filepath.Join(dbDir, "blockstore.db") - state := filepath.Join(dbDir, "state.db") - wal := filepath.Join(dbDir, "cs.wal") - evidence := filepath.Join(dbDir, "evidence.db") - txIndex := filepath.Join(dbDir, "tx_index.db") - - if tmos.FileExists(blockdb) { - if err := os.RemoveAll(blockdb); err == nil { - logger.Info("Removed all blockstore.db", "dir", blockdb) +// removeIfExists removes a path if it exists, logging the result. +func removeIfExists(path, label string) { + if tmos.FileExists(path) { + if err := os.RemoveAll(path); err == nil { + logger.Info("Removed "+label, "dir", path) } else { - logger.Error("error removing all blockstore.db", "dir", blockdb, "err", err) - } - } - - if tmos.FileExists(state) { - if err := os.RemoveAll(state); err == nil { - logger.Info("Removed all state.db", "dir", state) - } else { - logger.Error("error removing all state.db", "dir", state, "err", err) - } - } - - if tmos.FileExists(wal) { - if err := os.RemoveAll(wal); err == nil { - logger.Info("Removed all cs.wal", "dir", wal) - } else { - logger.Error("error removing all cs.wal", "dir", wal, "err", err) - } - } - - if tmos.FileExists(evidence) { - if err := os.RemoveAll(evidence); err == nil { - logger.Info("Removed all evidence.db", "dir", evidence) - } else { - logger.Error("error removing all evidence.db", "dir", evidence, "err", err) + logger.Error("error removing "+label, "dir", path, "err", err) } } +} - if tmos.FileExists(txIndex) { - if err := os.RemoveAll(txIndex); err == nil { - logger.Info("Removed tx_index.db", "dir", txIndex) - } else { - logger.Error("error removing tx_index.db", "dir", txIndex, "err", err) - } - } +// ResetState removes all blocks, tendermint state, indexed transactions and evidence. +// It handles both the legacy flat layout and the new subdirectory layout. +func ResetState(dbDir string) error { + // Legacy paths (flat under data/) + removeIfExists(filepath.Join(dbDir, "blockstore.db"), "blockstore.db") + removeIfExists(filepath.Join(dbDir, "state.db"), "state.db") + removeIfExists(filepath.Join(dbDir, "cs.wal"), "cs.wal") + removeIfExists(filepath.Join(dbDir, "evidence.db"), "evidence.db") + removeIfExists(filepath.Join(dbDir, "tx_index.db"), "tx_index.db") + + // New paths (subdirectory layout — all tendermint DBs under data/tendermint/) + removeIfExists(filepath.Join(dbDir, "tendermint", "blockstore.db"), "tendermint/blockstore.db") + removeIfExists(filepath.Join(dbDir, "tendermint", "tx_index.db"), "tendermint/tx_index.db") + removeIfExists(filepath.Join(dbDir, "tendermint", "state.db"), "tendermint/state.db") + removeIfExists(filepath.Join(dbDir, "tendermint", "cs.wal"), "tendermint/cs.wal") + removeIfExists(filepath.Join(dbDir, "tendermint", "evidence.db"), "tendermint/evidence.db") + removeIfExists(filepath.Join(dbDir, "tendermint", "peerstore.db"), "tendermint/peerstore.db") return tmos.EnsureDir(dbDir, 0700) } @@ -189,12 +170,21 @@ func ResetFilePV(privValKeyFile, privValStateFile string, keyType string) error return nil } -// ResetPeerStore removes the peer store containing all information used by the tendermint networking layer -// In the case of a reset, new peers will need to be set either via the config or through the discovery mechanism +// ResetPeerStore removes the peer store containing all information used by the tendermint networking layer. +// In the case of a reset, new peers will need to be set either via the config or through the discovery mechanism. +// It checks both legacy (data/peerstore.db) and new (data/tendermint/peerstore.db) locations. func ResetPeerStore(dbDir string) error { - peerstore := filepath.Join(dbDir, "peerstore.db") - if tmos.FileExists(peerstore) { - return os.RemoveAll(peerstore) + legacy := filepath.Join(dbDir, "peerstore.db") + if tmos.FileExists(legacy) { + if err := os.RemoveAll(legacy); err != nil { + return err + } + } + newPath := filepath.Join(dbDir, "tendermint", "peerstore.db") + if tmos.FileExists(newPath) { + if err := os.RemoveAll(newPath); err != nil { + return err + } } return nil } diff --git a/sei-tendermint/config/config.go b/sei-tendermint/config/config.go index a435d41fed..0adbeca577 100644 --- a/sei-tendermint/config/config.go +++ b/sei-tendermint/config/config.go @@ -1164,7 +1164,7 @@ type ConsensusConfig struct { // DefaultConsensusConfig returns a default configuration for the consensus service func DefaultConsensusConfig() *ConsensusConfig { return &ConsensusConfig{ - WalPath: filepath.Join(defaultDataDir, "cs.wal", "wal"), + WalPath: filepath.Join(defaultDataDir, "tendermint", "cs.wal", "wal"), CreateEmptyBlocks: true, CreateEmptyBlocksInterval: 0 * time.Second, PeerGossipSleepDuration: 100 * time.Millisecond, @@ -1191,8 +1191,23 @@ func (cfg *ConsensusConfig) WaitForTxs() bool { return !cfg.CreateEmptyBlocks || cfg.CreateEmptyBlocksInterval > 0 } -// WalFile returns the full path to the write-ahead log file +// WalFile returns the full path to the write-ahead log file. +// When either the old default (data/cs.wal/wal) or the new default +// (data/tendermint/cs.wal/wal) is configured, the directory is chosen +// automatically: legacy data/cs.wal/ is used when it exists on disk, +// otherwise data/tendermint/cs.wal/ is used. Custom or absolute paths +// are returned as-is. func (cfg *ConsensusConfig) WalFile() string { + oldDefault := filepath.Join(defaultDataDir, "cs.wal", "wal") + newDefault := filepath.Join(defaultDataDir, "tendermint", "cs.wal", "wal") + + if cfg.WalPath == oldDefault || cfg.WalPath == newDefault { + legacyDir := filepath.Join(rootify(defaultDataDir, cfg.RootDir), "cs.wal") + if pathExists(legacyDir) { + return filepath.Join(legacyDir, "wal") + } + return filepath.Join(rootify(defaultDataDir, cfg.RootDir), "tendermint", "cs.wal", "wal") + } return rootify(cfg.WalPath, cfg.RootDir) } diff --git a/sei-tendermint/config/config_test.go b/sei-tendermint/config/config_test.go index 82fdd6606e..77db8be3f5 100644 --- a/sei-tendermint/config/config_test.go +++ b/sei-tendermint/config/config_test.go @@ -1,6 +1,8 @@ package config import ( + "os" + "path/filepath" "reflect" "testing" "time" @@ -164,3 +166,89 @@ func TestP2PConfigValidateBasic(t *testing.T) { reflect.ValueOf(cfg).Elem().FieldByName(fieldName).SetInt(0) } } + +// --- WalFile legacy fallback tests --- + +func TestWalFile_NewDefault_NoLegacy(t *testing.T) { + root := t.TempDir() + cfg := DefaultConsensusConfig() + cfg.RootDir = root + + expected := filepath.Join(root, "data", "tendermint", "cs.wal", "wal") + assert.Equal(t, expected, cfg.WalFile(), + "new node with new default should use data/tendermint/cs.wal/wal") +} + +func TestWalFile_NewDefault_LegacyExists(t *testing.T) { + root := t.TempDir() + legacyDir := filepath.Join(root, "data", "cs.wal") + require.NoError(t, os.MkdirAll(legacyDir, 0755)) + + cfg := DefaultConsensusConfig() + cfg.RootDir = root + + expected := filepath.Join(legacyDir, "wal") + assert.Equal(t, expected, cfg.WalFile(), + "should fall back to legacy cs.wal when it exists on disk") +} + +func TestWalFile_OldDefault_NoLegacy(t *testing.T) { + root := t.TempDir() + cfg := DefaultConsensusConfig() + cfg.RootDir = root + cfg.WalPath = filepath.Join("data", "cs.wal", "wal") + + expected := filepath.Join(root, "data", "tendermint", "cs.wal", "wal") + assert.Equal(t, expected, cfg.WalFile(), + "old default in config.toml on a new node should redirect to new path") +} + +func TestWalFile_OldDefault_LegacyExists(t *testing.T) { + root := t.TempDir() + legacyDir := filepath.Join(root, "data", "cs.wal") + require.NoError(t, os.MkdirAll(legacyDir, 0755)) + + cfg := DefaultConsensusConfig() + cfg.RootDir = root + cfg.WalPath = filepath.Join("data", "cs.wal", "wal") + + expected := filepath.Join(legacyDir, "wal") + assert.Equal(t, expected, cfg.WalFile(), + "old default in config.toml with legacy data should use legacy path") +} + +func TestWalFile_CustomPath(t *testing.T) { + root := t.TempDir() + cfg := DefaultConsensusConfig() + cfg.RootDir = root + cfg.WalPath = "/custom/wal/path" + + assert.Equal(t, "/custom/wal/path", cfg.WalFile(), + "absolute custom path should be returned unchanged") +} + +func TestWalFile_CustomRelativePath(t *testing.T) { + root := t.TempDir() + cfg := DefaultConsensusConfig() + cfg.RootDir = root + cfg.WalPath = filepath.Join("data", "mywal", "wal") + + expected := filepath.Join(root, "data", "mywal", "wal") + assert.Equal(t, expected, cfg.WalFile(), + "non-default custom relative path should be resolved normally") +} + +func TestWalFile_BothExist_LegacyWins(t *testing.T) { + root := t.TempDir() + legacyDir := filepath.Join(root, "data", "cs.wal") + require.NoError(t, os.MkdirAll(legacyDir, 0755)) + newDir := filepath.Join(root, "data", "tendermint", "cs.wal") + require.NoError(t, os.MkdirAll(newDir, 0755)) + + cfg := DefaultConsensusConfig() + cfg.RootDir = root + + expected := filepath.Join(legacyDir, "wal") + assert.Equal(t, expected, cfg.WalFile(), + "legacy should win when both locations exist") +} diff --git a/sei-tendermint/config/db.go b/sei-tendermint/config/db.go index 3f851b4e6c..ebbb18db2a 100644 --- a/sei-tendermint/config/db.go +++ b/sei-tendermint/config/db.go @@ -1,6 +1,9 @@ package config import ( + "os" + "path/filepath" + dbm "github.com/tendermint/tm-db" ) @@ -14,8 +17,42 @@ type DBContext struct { type DBProvider func(*DBContext) (dbm.DB, error) // DefaultDBProvider returns a database using the DBBackend and DBDir -// specified in the Config. +// specified in the Config. It routes each DB to its appropriate +// subdirectory under the data folder, with backward compatibility +// for existing nodes that have data in the legacy flat layout. func DefaultDBProvider(ctx *DBContext) (dbm.DB, error) { dbType := dbm.BackendType(ctx.Config.DBBackend) - return dbm.NewDB(ctx.ID, dbType, ctx.Config.DBDir()) + dbDir := ResolveDBDir(ctx.ID, ctx.Config.DBDir()) + return dbm.NewDB(ctx.ID, dbType, dbDir) +} + +// dbSubDir returns the new subdirectory for a given DB identifier. +func dbSubDir(dbID string) string { + switch dbID { + case "blockstore", "tx_index", "state", "evidence", "peerstore": + return "tendermint" + default: + return "" + } +} + +// ResolveDBDir returns the directory in which the given DB should be opened. +// If legacy data exists directly under baseDir (e.g. baseDir/blockstore.db), +// baseDir is returned for backward compatibility. Otherwise the new +// subdirectory layout (e.g. baseDir/ledger) is used. +func ResolveDBDir(dbID string, baseDir string) string { + subDir := dbSubDir(dbID) + if subDir == "" { + return baseDir + } + legacyPath := filepath.Join(baseDir, dbID+".db") + if pathExists(legacyPath) { + return baseDir + } + return filepath.Join(baseDir, subDir) +} + +func pathExists(path string) bool { + _, err := os.Stat(path) + return err == nil } diff --git a/sei-tendermint/config/db_test.go b/sei-tendermint/config/db_test.go new file mode 100644 index 0000000000..ba275e6729 --- /dev/null +++ b/sei-tendermint/config/db_test.go @@ -0,0 +1,120 @@ +package config + +import ( + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// --- ResolveDBDir --- + +func TestResolveDBDir_NewNode_Blockstore(t *testing.T) { + base := t.TempDir() + got := ResolveDBDir("blockstore", base) + assert.Equal(t, filepath.Join(base, "tendermint"), got) +} + +func TestResolveDBDir_NewNode_TxIndex(t *testing.T) { + base := t.TempDir() + got := ResolveDBDir("tx_index", base) + assert.Equal(t, filepath.Join(base, "tendermint"), got) +} + +func TestResolveDBDir_NewNode_State(t *testing.T) { + base := t.TempDir() + got := ResolveDBDir("state", base) + assert.Equal(t, filepath.Join(base, "tendermint"), got) +} + +func TestResolveDBDir_NewNode_Evidence(t *testing.T) { + base := t.TempDir() + got := ResolveDBDir("evidence", base) + assert.Equal(t, filepath.Join(base, "tendermint"), got) +} + +func TestResolveDBDir_NewNode_Peerstore(t *testing.T) { + base := t.TempDir() + got := ResolveDBDir("peerstore", base) + assert.Equal(t, filepath.Join(base, "tendermint"), got) +} + +func TestResolveDBDir_LegacyBlockstore(t *testing.T) { + base := t.TempDir() + require.NoError(t, os.MkdirAll(filepath.Join(base, "blockstore.db"), 0755)) + + got := ResolveDBDir("blockstore", base) + assert.Equal(t, base, got, "should return base dir when legacy blockstore.db exists") +} + +func TestResolveDBDir_LegacyState(t *testing.T) { + base := t.TempDir() + require.NoError(t, os.MkdirAll(filepath.Join(base, "state.db"), 0755)) + + got := ResolveDBDir("state", base) + assert.Equal(t, base, got, "should return base dir when legacy state.db exists") +} + +func TestResolveDBDir_LegacyTxIndex(t *testing.T) { + base := t.TempDir() + require.NoError(t, os.MkdirAll(filepath.Join(base, "tx_index.db"), 0755)) + + got := ResolveDBDir("tx_index", base) + assert.Equal(t, base, got, "should return base dir when legacy tx_index.db exists") +} + +func TestResolveDBDir_LegacyEvidence(t *testing.T) { + base := t.TempDir() + require.NoError(t, os.MkdirAll(filepath.Join(base, "evidence.db"), 0755)) + + got := ResolveDBDir("evidence", base) + assert.Equal(t, base, got, "should return base dir when legacy evidence.db exists") +} + +func TestResolveDBDir_LegacyPeerstore(t *testing.T) { + base := t.TempDir() + require.NoError(t, os.MkdirAll(filepath.Join(base, "peerstore.db"), 0755)) + + got := ResolveDBDir("peerstore", base) + assert.Equal(t, base, got, "should return base dir when legacy peerstore.db exists") +} + +func TestResolveDBDir_UnknownID(t *testing.T) { + base := t.TempDir() + got := ResolveDBDir("something_unknown", base) + assert.Equal(t, base, got, "unknown DB IDs should fall through to base dir") +} + +func TestResolveDBDir_NewDataAlreadyExists(t *testing.T) { + base := t.TempDir() + newDir := filepath.Join(base, "tendermint", "blockstore.db") + require.NoError(t, os.MkdirAll(newDir, 0755)) + + got := ResolveDBDir("blockstore", base) + assert.Equal(t, filepath.Join(base, "tendermint"), got, + "should use new path on subsequent runs when legacy is absent") +} + +func TestResolveDBDir_BothExist_LegacyWins(t *testing.T) { + base := t.TempDir() + require.NoError(t, os.MkdirAll(filepath.Join(base, "state.db"), 0755)) + require.NoError(t, os.MkdirAll(filepath.Join(base, "tendermint", "state.db"), 0755)) + + got := ResolveDBDir("state", base) + assert.Equal(t, base, got, "legacy should win when both exist") +} + +// Ensure that one DB having legacy data does not affect resolution of another DB. +func TestResolveDBDir_IndependentResolution(t *testing.T) { + base := t.TempDir() + // blockstore has legacy data, but state does not + require.NoError(t, os.MkdirAll(filepath.Join(base, "blockstore.db"), 0755)) + + gotBlock := ResolveDBDir("blockstore", base) + gotState := ResolveDBDir("state", base) + + assert.Equal(t, base, gotBlock, "blockstore should resolve to legacy") + assert.Equal(t, filepath.Join(base, "tendermint"), gotState, "state should resolve to new path") +} From 88daf32a84076ae2799d0d9686e3a7c919c39471 Mon Sep 17 00:00:00 2001 From: yzang2019 Date: Tue, 31 Mar 2026 16:53:49 -0700 Subject: [PATCH 2/5] Fix unit test --- sei-db/state_db/sc/composite/store_test.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sei-db/state_db/sc/composite/store_test.go b/sei-db/state_db/sc/composite/store_test.go index c872f1d84d..4f495d27a9 100644 --- a/sei-db/state_db/sc/composite/store_test.go +++ b/sei-db/state_db/sc/composite/store_test.go @@ -10,6 +10,7 @@ import ( errorutils "github.com/sei-protocol/sei-chain/sei-db/common/errors" "github.com/sei-protocol/sei-chain/sei-db/common/evm" "github.com/sei-protocol/sei-chain/sei-db/common/metrics" + "github.com/sei-protocol/sei-chain/sei-db/common/utils" "github.com/sei-protocol/sei-chain/sei-db/config" "github.com/sei-protocol/sei-chain/sei-db/proto" "github.com/sei-protocol/sei-chain/sei-db/state_db/sc/flatkv" @@ -716,7 +717,7 @@ func TestReconcileVersionsAfterCrash(t *testing.T) { // Simulate crash: rollback FlatKV to version 2 independently, leaving // cosmos at version 3. This mirrors a crash after cosmos Commit but // before FlatKV Commit completes. - flatkvPath := dir + "/data/flatkv" + flatkvPath := utils.GetFlatKVPath(dir) evmStore := flatkv.NewCommitStore(t.Context(), flatkvPath, cfg.FlatKVConfig) _, err = evmStore.LoadVersion(0, false) require.NoError(t, err) @@ -774,7 +775,7 @@ func TestReconcileVersionsThenContinueCommitting(t *testing.T) { require.NoError(t, cs.Close()) // Simulate crash: roll FlatKV back to version 2. - flatkvPath := dir + "/data/flatkv" + flatkvPath := utils.GetFlatKVPath(dir) evmStore := flatkv.NewCommitStore(t.Context(), flatkvPath, cfg.FlatKVConfig) _, err = evmStore.LoadVersion(0, false) require.NoError(t, err) @@ -869,7 +870,7 @@ func TestReconcileVersionsCosmosAheadByMultiple(t *testing.T) { require.NoError(t, cs.Close()) // Rollback FlatKV to version 3 (simulating 2 lost commits) - flatkvPath := dir + "/data/flatkv" + flatkvPath := utils.GetFlatKVPath(dir) evmStore := flatkv.NewCommitStore(t.Context(), flatkvPath, cfg.FlatKVConfig) _, err = evmStore.LoadVersion(0, false) require.NoError(t, err) From 0df74b4aeb45726db51096e7522273bbe3c6f9ab Mon Sep 17 00:00:00 2001 From: yzang2019 Date: Wed, 1 Apr 2026 08:01:53 -0700 Subject: [PATCH 3/5] Change method name --- sei-db/common/utils/path.go | 12 +++++----- sei-db/common/utils/path_test.go | 32 ++++++++++++++++----------- sei-db/state_db/sc/memiavl/store.go | 2 +- sei-db/state_db/ss/composite/store.go | 2 +- 4 files changed, 27 insertions(+), 21 deletions(-) diff --git a/sei-db/common/utils/path.go b/sei-db/common/utils/path.go index fea01b5102..08da79a05c 100644 --- a/sei-db/common/utils/path.go +++ b/sei-db/common/utils/path.go @@ -11,10 +11,10 @@ func PathExists(path string) bool { return err == nil } -// GetCommitStorePath returns the path for the memiavl state commitment store. +// GetCosmosSCStorePath returns the path for the memiavl state commitment store. // New nodes use data/state_commit/memiavl; existing nodes with data/committer.db // continue using the legacy path for backward compatibility. -func GetCommitStorePath(homePath string) string { +func GetCosmosSCStorePath(homePath string) string { legacyPath := filepath.Join(homePath, "data", "committer.db") if PathExists(legacyPath) { return legacyPath @@ -45,14 +45,14 @@ func GetStateStorePath(homePath string, backend string) string { } // GetEVMStateStorePath returns the path for the EVM state store. -// New nodes use data/state_store/evm_ss; existing nodes with data/evm_ss -// continue using the legacy path for backward compatibility. -func GetEVMStateStorePath(homePath string) string { +// New nodes use data/state_store/evm_ss/{backend}; existing nodes with +// data/evm_ss continue using the legacy path for backward compatibility. +func GetEVMStateStorePath(homePath string, backend string) string { legacyPath := filepath.Join(homePath, "data", "evm_ss") if PathExists(legacyPath) { return legacyPath } - return filepath.Join(homePath, "data", "state_store", "evm_ss") + return filepath.Join(homePath, "data", "state_store", "evm_ss", backend) } // GetReceiptStorePath returns the path for the receipt store. diff --git a/sei-db/common/utils/path_test.go b/sei-db/common/utils/path_test.go index acd94902a0..3a2253191c 100644 --- a/sei-db/common/utils/path_test.go +++ b/sei-db/common/utils/path_test.go @@ -19,20 +19,20 @@ func TestPathExists(t *testing.T) { assert.True(t, PathExists(f)) } -// --- GetCommitStorePath --- +// --- GetCosmosSCStorePath --- -func TestGetCommitStorePath_NewNode(t *testing.T) { +func TestGetCosmosSCStorePath_NewNode(t *testing.T) { home := t.TempDir() - got := GetCommitStorePath(home) + got := GetCosmosSCStorePath(home) assert.Equal(t, filepath.Join(home, "data", "state_commit", "memiavl"), got) } -func TestGetCommitStorePath_LegacyExists(t *testing.T) { +func TestGetCosmosSCStorePath_LegacyExists(t *testing.T) { home := t.TempDir() legacy := filepath.Join(home, "data", "committer.db") require.NoError(t, os.MkdirAll(legacy, 0755)) - got := GetCommitStorePath(home) + got := GetCosmosSCStorePath(home) assert.Equal(t, legacy, got) } @@ -87,10 +87,16 @@ func TestGetStateStorePath_LegacyForDifferentBackend(t *testing.T) { // --- GetEVMStateStorePath --- -func TestGetEVMStateStorePath_NewNode(t *testing.T) { +func TestGetEVMStateStorePath_NewNode_Pebble(t *testing.T) { home := t.TempDir() - got := GetEVMStateStorePath(home) - assert.Equal(t, filepath.Join(home, "data", "state_store", "evm_ss"), got) + got := GetEVMStateStorePath(home, "pebbledb") + assert.Equal(t, filepath.Join(home, "data", "state_store", "evm_ss", "pebbledb"), got) +} + +func TestGetEVMStateStorePath_NewNode_RocksDB(t *testing.T) { + home := t.TempDir() + got := GetEVMStateStorePath(home, "rocksdb") + assert.Equal(t, filepath.Join(home, "data", "state_store", "evm_ss", "rocksdb"), got) } func TestGetEVMStateStorePath_LegacyExists(t *testing.T) { @@ -98,7 +104,7 @@ func TestGetEVMStateStorePath_LegacyExists(t *testing.T) { legacy := filepath.Join(home, "data", "evm_ss") require.NoError(t, os.MkdirAll(legacy, 0755)) - got := GetEVMStateStorePath(home) + got := GetEVMStateStorePath(home, "pebbledb") assert.Equal(t, legacy, got) } @@ -127,12 +133,12 @@ func TestGetChangelogPath(t *testing.T) { // --- Edge: new path already has data (second run of new node) --- -func TestGetCommitStorePath_NewDataAlreadyExists(t *testing.T) { +func TestGetCosmosSCStorePath_NewDataAlreadyExists(t *testing.T) { home := t.TempDir() newPath := filepath.Join(home, "data", "state_commit", "memiavl") require.NoError(t, os.MkdirAll(newPath, 0755)) - got := GetCommitStorePath(home) + got := GetCosmosSCStorePath(home) assert.Equal(t, newPath, got, "should use new path when legacy is absent even if new path already exists") } @@ -147,14 +153,14 @@ func TestGetStateStorePath_NewDataAlreadyExists(t *testing.T) { // --- Edge: both legacy and new exist (legacy wins) --- -func TestGetCommitStorePath_BothExist(t *testing.T) { +func TestGetCosmosSCStorePath_BothExist(t *testing.T) { home := t.TempDir() legacy := filepath.Join(home, "data", "committer.db") require.NoError(t, os.MkdirAll(legacy, 0755)) newPath := filepath.Join(home, "data", "state_commit", "memiavl") require.NoError(t, os.MkdirAll(newPath, 0755)) - got := GetCommitStorePath(home) + got := GetCosmosSCStorePath(home) assert.Equal(t, legacy, got, "legacy should take precedence when both exist") } diff --git a/sei-db/state_db/sc/memiavl/store.go b/sei-db/state_db/sc/memiavl/store.go index 7667772b9f..32d9ffc8cb 100644 --- a/sei-db/state_db/sc/memiavl/store.go +++ b/sei-db/state_db/sc/memiavl/store.go @@ -19,7 +19,7 @@ type CommitStore struct { } func NewCommitStore(homeDir string, config Config) *CommitStore { - commitDBPath := utils.GetCommitStorePath(homeDir) + commitDBPath := utils.GetCosmosSCStorePath(homeDir) opts := Options{ Config: config, // Embed the config directly Dir: commitDBPath, diff --git a/sei-db/state_db/ss/composite/store.go b/sei-db/state_db/ss/composite/store.go index 609b78ea96..65d00a8969 100644 --- a/sei-db/state_db/ss/composite/store.go +++ b/sei-db/state_db/ss/composite/store.go @@ -58,7 +58,7 @@ func NewCompositeStateStore( if ssConfig.EVMEnabled() { evmDir := ssConfig.EVMDBDirectory if evmDir == "" { - evmDir = utils.GetEVMStateStorePath(homeDir) + evmDir = utils.GetEVMStateStorePath(homeDir, ssConfig.Backend) } evmStore, err := evm.NewEVMStateStore(evmDir, ssConfig) From 23d7a917d232cd76df543c5150964113301e6492 Mon Sep 17 00:00:00 2001 From: yzang2019 Date: Wed, 1 Apr 2026 13:07:34 -0700 Subject: [PATCH 4/5] Fix path --- app/receipt_store_config.go | 2 +- app/seidb_test.go | 4 +- sei-db/common/utils/path.go | 16 +-- sei-db/common/utils/path_test.go | 30 +++-- sei-db/config/ss_config.go | 2 +- sei-db/config/toml.go | 2 +- .../cmd/tendermint/commands/reindex_event.go | 4 +- .../cmd/tendermint/commands/reset_test.go | 105 ++++++++++++++++++ 8 files changed, 138 insertions(+), 27 deletions(-) diff --git a/app/receipt_store_config.go b/app/receipt_store_config.go index d5398eaf6b..96591e46ec 100644 --- a/app/receipt_store_config.go +++ b/app/receipt_store_config.go @@ -20,7 +20,7 @@ func readReceiptStoreConfig(homePath string, appOpts seidbconfig.AppOptions) (se return receiptConfig, err } if receiptConfig.DBDirectory == "" { - receiptConfig.DBDirectory = utils.GetReceiptStorePath(homePath) + receiptConfig.DBDirectory = utils.GetReceiptStorePath(homePath, receiptConfig.Backend) } return receiptConfig, nil } diff --git a/app/seidb_test.go b/app/seidb_test.go index 046ab4520c..504f6a6163 100644 --- a/app/seidb_test.go +++ b/app/seidb_test.go @@ -155,8 +155,8 @@ func TestReadReceiptStoreConfigUsesDefaultDirectoryWhenUnset(t *testing.T) { homePath := t.TempDir() receiptConfig, err := readReceiptStoreConfig(homePath, mapAppOpts{}) require.NoError(t, err) - // New nodes (no legacy data/receipt.db) get the new ledger/ layout - assert.Equal(t, filepath.Join(homePath, "data", "ledger", "receipt.db"), receiptConfig.DBDirectory) + // New nodes (no legacy data/receipt.db) get the new ledger/ layout with backend + assert.Equal(t, filepath.Join(homePath, "data", "ledger", "receipt", "pebbledb"), receiptConfig.DBDirectory) } // TestFullAppPathWithParquetReceiptStore exercises the full app.New path with rs-backend = "parquet" diff --git a/sei-db/common/utils/path.go b/sei-db/common/utils/path.go index 08da79a05c..a95a36cb59 100644 --- a/sei-db/common/utils/path.go +++ b/sei-db/common/utils/path.go @@ -34,36 +34,36 @@ func GetFlatKVPath(homePath string) string { } // GetStateStorePath returns the path for the Cosmos state store (SS). -// New nodes use data/state_store/cosmos_ss/{backend}; existing nodes with +// New nodes use data/state_store/cosmos/{backend}; existing nodes with // data/{backend} continue using the legacy path for backward compatibility. func GetStateStorePath(homePath string, backend string) string { legacyPath := filepath.Join(homePath, "data", backend) if PathExists(legacyPath) { return legacyPath } - return filepath.Join(homePath, "data", "state_store", "cosmos_ss", backend) + return filepath.Join(homePath, "data", "state_store", "cosmos", backend) } // GetEVMStateStorePath returns the path for the EVM state store. -// New nodes use data/state_store/evm_ss/{backend}; existing nodes with +// New nodes use data/state_store/evm/{backend}; existing nodes with // data/evm_ss continue using the legacy path for backward compatibility. func GetEVMStateStorePath(homePath string, backend string) string { legacyPath := filepath.Join(homePath, "data", "evm_ss") if PathExists(legacyPath) { return legacyPath } - return filepath.Join(homePath, "data", "state_store", "evm_ss", backend) + return filepath.Join(homePath, "data", "state_store", "evm", backend) } // GetReceiptStorePath returns the path for the receipt store. -// New nodes use data/ledger/receipt.db; existing nodes with data/receipt.db -// continue using the legacy path for backward compatibility. -func GetReceiptStorePath(homePath string) string { +// New nodes use data/ledger/receipt/{backend}; existing nodes with +// data/receipt.db continue using the legacy path for backward compatibility. +func GetReceiptStorePath(homePath string, backend string) string { legacyPath := filepath.Join(homePath, "data", "receipt.db") if PathExists(legacyPath) { return legacyPath } - return filepath.Join(homePath, "data", "ledger", "receipt.db") + return filepath.Join(homePath, "data", "ledger", "receipt", backend) } func GetChangelogPath(dbPath string) string { diff --git a/sei-db/common/utils/path_test.go b/sei-db/common/utils/path_test.go index 3a2253191c..78697bd21c 100644 --- a/sei-db/common/utils/path_test.go +++ b/sei-db/common/utils/path_test.go @@ -58,13 +58,13 @@ func TestGetFlatKVPath_LegacyExists(t *testing.T) { func TestGetStateStorePath_NewNode_Pebble(t *testing.T) { home := t.TempDir() got := GetStateStorePath(home, "pebbledb") - assert.Equal(t, filepath.Join(home, "data", "state_store", "cosmos_ss", "pebbledb"), got) + assert.Equal(t, filepath.Join(home, "data", "state_store", "cosmos", "pebbledb"), got) } func TestGetStateStorePath_NewNode_RocksDB(t *testing.T) { home := t.TempDir() got := GetStateStorePath(home, "rocksdb") - assert.Equal(t, filepath.Join(home, "data", "state_store", "cosmos_ss", "rocksdb"), got) + assert.Equal(t, filepath.Join(home, "data", "state_store", "cosmos", "rocksdb"), got) } func TestGetStateStorePath_LegacyExists(t *testing.T) { @@ -82,7 +82,7 @@ func TestGetStateStorePath_LegacyForDifferentBackend(t *testing.T) { require.NoError(t, os.MkdirAll(filepath.Join(home, "data", "rocksdb"), 0755)) got := GetStateStorePath(home, "pebbledb") - assert.Equal(t, filepath.Join(home, "data", "state_store", "cosmos_ss", "pebbledb"), got) + assert.Equal(t, filepath.Join(home, "data", "state_store", "cosmos", "pebbledb"), got) } // --- GetEVMStateStorePath --- @@ -90,13 +90,13 @@ func TestGetStateStorePath_LegacyForDifferentBackend(t *testing.T) { func TestGetEVMStateStorePath_NewNode_Pebble(t *testing.T) { home := t.TempDir() got := GetEVMStateStorePath(home, "pebbledb") - assert.Equal(t, filepath.Join(home, "data", "state_store", "evm_ss", "pebbledb"), got) + assert.Equal(t, filepath.Join(home, "data", "state_store", "evm", "pebbledb"), got) } func TestGetEVMStateStorePath_NewNode_RocksDB(t *testing.T) { home := t.TempDir() got := GetEVMStateStorePath(home, "rocksdb") - assert.Equal(t, filepath.Join(home, "data", "state_store", "evm_ss", "rocksdb"), got) + assert.Equal(t, filepath.Join(home, "data", "state_store", "evm", "rocksdb"), got) } func TestGetEVMStateStorePath_LegacyExists(t *testing.T) { @@ -110,10 +110,16 @@ func TestGetEVMStateStorePath_LegacyExists(t *testing.T) { // --- GetReceiptStorePath --- -func TestGetReceiptStorePath_NewNode(t *testing.T) { +func TestGetReceiptStorePath_NewNode_Pebble(t *testing.T) { home := t.TempDir() - got := GetReceiptStorePath(home) - assert.Equal(t, filepath.Join(home, "data", "ledger", "receipt.db"), got) + got := GetReceiptStorePath(home, "pebbledb") + assert.Equal(t, filepath.Join(home, "data", "ledger", "receipt", "pebbledb"), got) +} + +func TestGetReceiptStorePath_NewNode_Parquet(t *testing.T) { + home := t.TempDir() + got := GetReceiptStorePath(home, "parquet") + assert.Equal(t, filepath.Join(home, "data", "ledger", "receipt", "parquet"), got) } func TestGetReceiptStorePath_LegacyExists(t *testing.T) { @@ -121,7 +127,7 @@ func TestGetReceiptStorePath_LegacyExists(t *testing.T) { legacy := filepath.Join(home, "data", "receipt.db") require.NoError(t, os.MkdirAll(legacy, 0755)) - got := GetReceiptStorePath(home) + got := GetReceiptStorePath(home, "pebbledb") assert.Equal(t, legacy, got) } @@ -144,7 +150,7 @@ func TestGetCosmosSCStorePath_NewDataAlreadyExists(t *testing.T) { func TestGetStateStorePath_NewDataAlreadyExists(t *testing.T) { home := t.TempDir() - newPath := filepath.Join(home, "data", "state_store", "cosmos_ss", "pebbledb") + newPath := filepath.Join(home, "data", "state_store", "cosmos", "pebbledb") require.NoError(t, os.MkdirAll(newPath, 0755)) got := GetStateStorePath(home, "pebbledb") @@ -168,9 +174,9 @@ func TestGetReceiptStorePath_BothExist(t *testing.T) { home := t.TempDir() legacy := filepath.Join(home, "data", "receipt.db") require.NoError(t, os.MkdirAll(legacy, 0755)) - newPath := filepath.Join(home, "data", "ledger", "receipt.db") + newPath := filepath.Join(home, "data", "ledger", "receipt", "pebbledb") require.NoError(t, os.MkdirAll(newPath, 0755)) - got := GetReceiptStorePath(home) + got := GetReceiptStorePath(home, "pebbledb") assert.Equal(t, legacy, got, "legacy should take precedence when both exist") } diff --git a/sei-db/config/ss_config.go b/sei-db/config/ss_config.go index 1cda8d2ae0..097d8fec3f 100644 --- a/sei-db/config/ss_config.go +++ b/sei-db/config/ss_config.go @@ -74,7 +74,7 @@ type StateStoreConfig struct { ReadMode ReadMode `mapstructure:"read-mode"` // EVMDBDirectory defines the directory for EVM state store db files. - // If not set, defaults to /data/evm_ss + // If not set, defaults to /data/state_store/evm/{backend} EVMDBDirectory string `mapstructure:"evm-db-directory"` } diff --git a/sei-db/config/toml.go b/sei-db/config/toml.go index 9e48436e9b..b07199f63f 100644 --- a/sei-db/config/toml.go +++ b/sei-db/config/toml.go @@ -126,7 +126,7 @@ const ReceiptStoreConfigTemplate = ` # defaults to pebbledb rs-backend = "{{ .ReceiptStore.Backend }}" -# Defines the receipt store directory. If unset, defaults to /data/receipt.db +# Defines the receipt store directory. If unset, defaults to /data/ledger/receipt/{backend} db-directory = "{{ .ReceiptStore.DBDirectory }}" # AsyncWriteBuffer defines the async queue length for commits to be applied to receipt store. diff --git a/sei-tendermint/cmd/tendermint/commands/reindex_event.go b/sei-tendermint/cmd/tendermint/commands/reindex_event.go index b8c85e9713..72dc2817ff 100644 --- a/sei-tendermint/cmd/tendermint/commands/reindex_event.go +++ b/sei-tendermint/cmd/tendermint/commands/reindex_event.go @@ -151,7 +151,7 @@ func loadStateAndBlockStore(cfg *tmcfg.Config) (*store.BlockStore, state.Store, blockstoreDir := tmcfg.ResolveDBDir("blockstore", cfg.DBDir()) if !os.FileExists(filepath.Join(blockstoreDir, "blockstore.db")) { - return nil, nil, fmt.Errorf("no blockstore found in %v", cfg.DBDir()) + return nil, nil, fmt.Errorf("no blockstore found in %v", blockstoreDir) } blockStoreDB, err := dbm.NewDB("blockstore", dbType, blockstoreDir) @@ -162,7 +162,7 @@ func loadStateAndBlockStore(cfg *tmcfg.Config) (*store.BlockStore, state.Store, stateDir := tmcfg.ResolveDBDir("state", cfg.DBDir()) if !os.FileExists(filepath.Join(stateDir, "state.db")) { - return nil, nil, fmt.Errorf("no state store found in %v", cfg.DBDir()) + return nil, nil, fmt.Errorf("no state store found in %v", stateDir) } stateDB, err := dbm.NewDB("state", dbType, stateDir) diff --git a/sei-tendermint/cmd/tendermint/commands/reset_test.go b/sei-tendermint/cmd/tendermint/commands/reset_test.go index 95587f5163..ab804a1331 100644 --- a/sei-tendermint/cmd/tendermint/commands/reset_test.go +++ b/sei-tendermint/cmd/tendermint/commands/reset_test.go @@ -96,3 +96,108 @@ func initTestFiles(t *testing.T, config *cfg.Config) { require.NoError(t, err) require.NoError(t, pv.Save()) } + +// createDirs is a test helper that creates the given directories under base. +func createDirs(t *testing.T, base string, dirs []string) { + t.Helper() + for _, d := range dirs { + require.NoError(t, os.MkdirAll(filepath.Join(base, d), 0o755)) + } +} + +// legacyDBs are the flat data/ DB directories that ResetState should remove. +var legacyDBs = []string{ + "blockstore.db", "state.db", "cs.wal", "evidence.db", "tx_index.db", +} + +// newTendermintDBs are the data/tendermint/ DB directories that ResetState should remove. +var newTendermintDBs = []string{ + "tendermint/blockstore.db", "tendermint/state.db", "tendermint/cs.wal", + "tendermint/evidence.db", "tendermint/tx_index.db", "tendermint/peerstore.db", +} + +func TestResetState_LegacyLayout(t *testing.T) { + dbDir := t.TempDir() + createDirs(t, dbDir, legacyDBs) + + for _, d := range legacyDBs { + require.DirExists(t, filepath.Join(dbDir, d)) + } + + require.NoError(t, ResetState(dbDir)) + + for _, d := range legacyDBs { + require.NoDirExists(t, filepath.Join(dbDir, d)) + } + require.DirExists(t, dbDir) +} + +func TestResetState_NewLayout(t *testing.T) { + dbDir := t.TempDir() + createDirs(t, dbDir, newTendermintDBs) + + for _, d := range newTendermintDBs { + require.DirExists(t, filepath.Join(dbDir, d)) + } + + require.NoError(t, ResetState(dbDir)) + + for _, d := range newTendermintDBs { + require.NoDirExists(t, filepath.Join(dbDir, d)) + } + require.DirExists(t, dbDir) +} + +func TestResetState_BothLayouts(t *testing.T) { + dbDir := t.TempDir() + all := append(legacyDBs, newTendermintDBs...) + createDirs(t, dbDir, all) + + require.NoError(t, ResetState(dbDir)) + + for _, d := range all { + require.NoDirExists(t, filepath.Join(dbDir, d)) + } + require.DirExists(t, dbDir) +} + +func TestResetState_EmptyDir(t *testing.T) { + dbDir := t.TempDir() + require.NoError(t, ResetState(dbDir)) + require.DirExists(t, dbDir) +} + +func TestResetPeerStore_LegacyOnly(t *testing.T) { + dbDir := t.TempDir() + legacy := filepath.Join(dbDir, "peerstore.db") + require.NoError(t, os.MkdirAll(legacy, 0o755)) + + require.NoError(t, ResetPeerStore(dbDir)) + require.NoDirExists(t, legacy) +} + +func TestResetPeerStore_NewOnly(t *testing.T) { + dbDir := t.TempDir() + newPath := filepath.Join(dbDir, "tendermint", "peerstore.db") + require.NoError(t, os.MkdirAll(newPath, 0o755)) + + require.NoError(t, ResetPeerStore(dbDir)) + require.NoDirExists(t, newPath) +} + +func TestResetPeerStore_BothLayouts(t *testing.T) { + dbDir := t.TempDir() + legacy := filepath.Join(dbDir, "peerstore.db") + newPath := filepath.Join(dbDir, "tendermint", "peerstore.db") + require.NoError(t, os.MkdirAll(legacy, 0o755)) + require.NoError(t, os.MkdirAll(newPath, 0o755)) + + require.NoError(t, ResetPeerStore(dbDir)) + require.NoDirExists(t, legacy) + require.NoDirExists(t, newPath) +} + +func TestResetPeerStore_NeitherExists(t *testing.T) { + dbDir := t.TempDir() + require.NoError(t, ResetPeerStore(dbDir)) +} From 3ebd0aefcc404fbe849d16385df2f020e648bb66 Mon Sep 17 00:00:00 2001 From: yzang2019 Date: Fri, 3 Apr 2026 09:51:40 -0700 Subject: [PATCH 5/5] Change to dirExist --- sei-db/common/utils/path.go | 24 +++++++++++++++--------- sei-db/common/utils/path_test.go | 18 ++++++++++++++---- sei-tendermint/config/config.go | 2 +- sei-tendermint/config/db.go | 8 ++++---- 4 files changed, 34 insertions(+), 18 deletions(-) diff --git a/sei-db/common/utils/path.go b/sei-db/common/utils/path.go index a95a36cb59..a8d64e7852 100644 --- a/sei-db/common/utils/path.go +++ b/sei-db/common/utils/path.go @@ -5,10 +5,16 @@ import ( "path/filepath" ) -// PathExists returns true if the given path exists on disk. -func PathExists(path string) bool { - _, err := os.Stat(path) - return err == nil +// DirExists returns true if path exists and is a directory. +func DirExists(path string) bool { + info, err := os.Stat(path) + return err == nil && info.IsDir() +} + +// FileExists returns true if path exists and is a regular file. +func FileExists(path string) bool { + info, err := os.Stat(path) + return err == nil && !info.IsDir() } // GetCosmosSCStorePath returns the path for the memiavl state commitment store. @@ -16,7 +22,7 @@ func PathExists(path string) bool { // continue using the legacy path for backward compatibility. func GetCosmosSCStorePath(homePath string) string { legacyPath := filepath.Join(homePath, "data", "committer.db") - if PathExists(legacyPath) { + if DirExists(legacyPath) { return legacyPath } return filepath.Join(homePath, "data", "state_commit", "memiavl") @@ -27,7 +33,7 @@ func GetCosmosSCStorePath(homePath string) string { // continue using the legacy path for backward compatibility. func GetFlatKVPath(homePath string) string { legacyPath := filepath.Join(homePath, "data", "flatkv") - if PathExists(legacyPath) { + if DirExists(legacyPath) { return legacyPath } return filepath.Join(homePath, "data", "state_commit", "flatkv") @@ -38,7 +44,7 @@ func GetFlatKVPath(homePath string) string { // data/{backend} continue using the legacy path for backward compatibility. func GetStateStorePath(homePath string, backend string) string { legacyPath := filepath.Join(homePath, "data", backend) - if PathExists(legacyPath) { + if DirExists(legacyPath) { return legacyPath } return filepath.Join(homePath, "data", "state_store", "cosmos", backend) @@ -49,7 +55,7 @@ func GetStateStorePath(homePath string, backend string) string { // data/evm_ss continue using the legacy path for backward compatibility. func GetEVMStateStorePath(homePath string, backend string) string { legacyPath := filepath.Join(homePath, "data", "evm_ss") - if PathExists(legacyPath) { + if DirExists(legacyPath) { return legacyPath } return filepath.Join(homePath, "data", "state_store", "evm", backend) @@ -60,7 +66,7 @@ func GetEVMStateStorePath(homePath string, backend string) string { // data/receipt.db continue using the legacy path for backward compatibility. func GetReceiptStorePath(homePath string, backend string) string { legacyPath := filepath.Join(homePath, "data", "receipt.db") - if PathExists(legacyPath) { + if DirExists(legacyPath) { return legacyPath } return filepath.Join(homePath, "data", "ledger", "receipt", backend) diff --git a/sei-db/common/utils/path_test.go b/sei-db/common/utils/path_test.go index 78697bd21c..bbd8daffc1 100644 --- a/sei-db/common/utils/path_test.go +++ b/sei-db/common/utils/path_test.go @@ -9,14 +9,24 @@ import ( "github.com/stretchr/testify/require" ) -func TestPathExists(t *testing.T) { +func TestDirExists(t *testing.T) { dir := t.TempDir() - assert.True(t, PathExists(dir)) - assert.False(t, PathExists(filepath.Join(dir, "nonexistent"))) + assert.True(t, DirExists(dir)) + assert.False(t, DirExists(filepath.Join(dir, "nonexistent"))) f := filepath.Join(dir, "file.txt") require.NoError(t, os.WriteFile(f, []byte("hi"), 0644)) - assert.True(t, PathExists(f)) + assert.False(t, DirExists(f), "regular file should not match DirExists") +} + +func TestFileExists(t *testing.T) { + dir := t.TempDir() + assert.False(t, FileExists(dir), "directory should not match FileExists") + assert.False(t, FileExists(filepath.Join(dir, "nonexistent"))) + + f := filepath.Join(dir, "file.txt") + require.NoError(t, os.WriteFile(f, []byte("hi"), 0644)) + assert.True(t, FileExists(f)) } // --- GetCosmosSCStorePath --- diff --git a/sei-tendermint/config/config.go b/sei-tendermint/config/config.go index 0adbeca577..e1cca4f2f6 100644 --- a/sei-tendermint/config/config.go +++ b/sei-tendermint/config/config.go @@ -1203,7 +1203,7 @@ func (cfg *ConsensusConfig) WalFile() string { if cfg.WalPath == oldDefault || cfg.WalPath == newDefault { legacyDir := filepath.Join(rootify(defaultDataDir, cfg.RootDir), "cs.wal") - if pathExists(legacyDir) { + if dirExists(legacyDir) { return filepath.Join(legacyDir, "wal") } return filepath.Join(rootify(defaultDataDir, cfg.RootDir), "tendermint", "cs.wal", "wal") diff --git a/sei-tendermint/config/db.go b/sei-tendermint/config/db.go index ebbb18db2a..4d4bd92ef5 100644 --- a/sei-tendermint/config/db.go +++ b/sei-tendermint/config/db.go @@ -46,13 +46,13 @@ func ResolveDBDir(dbID string, baseDir string) string { return baseDir } legacyPath := filepath.Join(baseDir, dbID+".db") - if pathExists(legacyPath) { + if dirExists(legacyPath) { return baseDir } return filepath.Join(baseDir, subDir) } -func pathExists(path string) bool { - _, err := os.Stat(path) - return err == nil +func dirExists(path string) bool { + info, err := os.Stat(path) + return err == nil && info.IsDir() }