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
13 changes: 9 additions & 4 deletions app/seidb.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,10 @@ const (
FlagSSImportNumWorkers = "state-store.ss-import-num-workers"

// EVM SS optimization (embedded in SS config, controlled via write/read mode)
FlagEVMSSDirectory = "state-store.evm-ss-db-directory"
FlagEVMSSWriteMode = "state-store.evm-ss-write-mode"
FlagEVMSSReadMode = "state-store.evm-ss-read-mode"
FlagEVMSSDirectory = "state-store.evm-ss-db-directory"
FlagEVMSSWriteMode = "state-store.evm-ss-write-mode"
FlagEVMSSReadMode = "state-store.evm-ss-read-mode"
FlagEVMSSSeparateDBs = "state-store.evm-ss-separate-dbs"

// Other configs
FlagSnapshotInterval = "state-sync.snapshot-interval"
Expand All @@ -69,7 +70,10 @@ func SetupSeiDB(
}
if ssConfig.EVMEnabled() {
logger.Info("SeiDB EVM StateStore optimization is enabled",
"writeMode", ssConfig.WriteMode, "readMode", ssConfig.ReadMode)
"writeMode", ssConfig.WriteMode,
"readMode", ssConfig.ReadMode,
"separateDBs", ssConfig.SeparateEVMSubDBs,
)
}
validateConfigs(appOpts)
gigaExecutorConfig, err := gigaconfig.ReadConfig(appOpts)
Expand Down Expand Up @@ -146,6 +150,7 @@ func parseSSConfigs(appOpts servertypes.AppOptions) config.StateStoreConfig {

// EVM optimization fields (embedded in SS config)
ssConfig.EVMDBDirectory = cast.ToString(appOpts.Get(FlagEVMSSDirectory))
ssConfig.SeparateEVMSubDBs = cast.ToBool(appOpts.Get(FlagEVMSSSeparateDBs))
if wm := cast.ToString(appOpts.Get(FlagEVMSSWriteMode)); wm != "" {
parsedWM, err := config.ParseWriteMode(wm)
if err != nil {
Expand Down
20 changes: 20 additions & 0 deletions app/seidb_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ func (t TestSeiDBAppOpts) Get(s string) interface{} {
return "" // empty means use default
case FlagEVMSSReadMode:
return "" // empty means use default
case FlagEVMSSSeparateDBs:
return defaultSSConfig.SeparateEVMSubDBs
}
return nil
}
Expand Down Expand Up @@ -98,6 +100,24 @@ func TestParseSCConfigs_HistoricalProofFlags(t *testing.T) {
assert.Equal(t, 3, scConfig.HistoricalProofBurst)
}

func TestParseSSConfigs_EVMFlags(t *testing.T) {
appOpts := mapAppOpts{
FlagSSEnable: true,
FlagEVMSSDirectory: "/tmp/evm-ss",
FlagEVMSSWriteMode: string(config.SplitWrite),
FlagEVMSSReadMode: string(config.SplitRead),
FlagEVMSSSeparateDBs: true,
FlagSSAsyncWriterBuffer: 0,
}

ssConfig := parseSSConfigs(appOpts)
assert.True(t, ssConfig.Enable)
assert.Equal(t, "/tmp/evm-ss", ssConfig.EVMDBDirectory)
assert.Equal(t, config.SplitWrite, ssConfig.WriteMode)
assert.Equal(t, config.SplitRead, ssConfig.ReadMode)
assert.True(t, ssConfig.SeparateEVMSubDBs)
}

func TestParseReceiptConfigs_DefaultsToPebbleWhenUnset(t *testing.T) {
receiptConfig, err := config.ReadReceiptConfig(mapAppOpts{})
assert.NoError(t, err)
Expand Down
6 changes: 6 additions & 0 deletions docker/localnode/config/app.toml
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,12 @@ ss-prune-interval = 60
# defaults to 1
ss-import-num-workers = 1

# EVM state-store DB directory and routing mode controls.
evm-ss-db-directory = ""
evm-ss-write-mode = "cosmos_only"
evm-ss-read-mode = "cosmos_only"
evm-ss-separate-dbs = false

[evm]

# EnableTestAPI enables the EVM test API
Expand Down
6 changes: 6 additions & 0 deletions docker/rpcnode/config/app.toml
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,12 @@ ss-prune-interval = 60
# defaults to 1
ss-import-num-workers = 1

# EVM state-store DB directory and routing mode controls.
evm-ss-db-directory = ""
evm-ss-write-mode = "cosmos_only"
evm-ss-read-mode = "cosmos_only"
evm-ss-separate-dbs = false

[evm]

# EnableTestAPI enables the EVM test API
Expand Down
4 changes: 4 additions & 0 deletions sei-cosmos/server/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,10 @@ func GetConfig(v *viper.Viper) (Config, error) {
KeepRecent: v.GetInt("state-store.ss-keep-recent"),
PruneIntervalSeconds: v.GetInt("state-store.ss-prune-interval"),
ImportNumWorkers: v.GetInt("state-store.ss-import-num-workers"),
WriteMode: config.WriteMode(v.GetString("state-store.evm-ss-write-mode")),
ReadMode: config.ReadMode(v.GetString("state-store.evm-ss-read-mode")),
EVMDBDirectory: v.GetString("state-store.evm-ss-db-directory"),
SeparateEVMSubDBs: v.GetBool("state-store.evm-ss-separate-dbs"),
},
Genesis: GenesisConfig{
StreamImport: v.GetBool("genesis.stream-import"),
Expand Down
11 changes: 11 additions & 0 deletions sei-cosmos/server/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,10 @@ func TestGetConfigStateStore(t *testing.T) {
v.Set("state-store.ss-keep-recent", 50000)
v.Set("state-store.ss-prune-interval", 1200)
v.Set("state-store.ss-import-num-workers", 4)
v.Set("state-store.evm-ss-db-directory", "/custom/evm/ss/path")
v.Set("state-store.evm-ss-write-mode", "split_write")
v.Set("state-store.evm-ss-read-mode", "split_read")
v.Set("state-store.evm-ss-separate-dbs", true)

cfg, err := GetConfig(v)
require.NoError(t, err)
Expand All @@ -344,6 +348,10 @@ func TestGetConfigStateStore(t *testing.T) {
require.Equal(t, 50000, cfg.StateStore.KeepRecent)
require.Equal(t, 1200, cfg.StateStore.PruneIntervalSeconds)
require.Equal(t, 4, cfg.StateStore.ImportNumWorkers)
require.Equal(t, "/custom/evm/ss/path", cfg.StateStore.EVMDBDirectory)
require.Equal(t, seidbconfig.SplitWrite, cfg.StateStore.WriteMode)
require.Equal(t, seidbconfig.SplitRead, cfg.StateStore.ReadMode)
require.True(t, cfg.StateStore.SeparateEVMSubDBs)
}

func TestDefaultStateCommitConfig(t *testing.T) {
Expand All @@ -367,4 +375,7 @@ func TestDefaultStateStoreConfig(t *testing.T) {
require.Equal(t, seidbconfig.DefaultSSKeepRecent, cfg.StateStore.KeepRecent)
require.Equal(t, seidbconfig.DefaultSSPruneInterval, cfg.StateStore.PruneIntervalSeconds)
require.Equal(t, seidbconfig.DefaultSSImportWorkers, cfg.StateStore.ImportNumWorkers)
require.Equal(t, seidbconfig.CosmosOnlyWrite, cfg.StateStore.WriteMode)
require.Equal(t, seidbconfig.CosmosOnlyRead, cfg.StateStore.ReadMode)
require.False(t, cfg.StateStore.SeparateEVMSubDBs)
}
7 changes: 7 additions & 0 deletions sei-db/config/ss_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,12 @@ type StateStoreConfig struct {
// EVMDBDirectory defines the directory for EVM state store db files.
// If not set, defaults to <home>/data/evm_ss
EVMDBDirectory string `mapstructure:"evm-db-directory"`

// SeparateEVMSubDBs controls whether EVM data is physically split across
// per-type databases. When false (default), all EVM data stays in one DB.
// When true, data is routed to separate DBs by EVM key family while
// preserving the same logical store key and full key encoding inside each DB.
SeparateEVMSubDBs bool `mapstructure:"evm-separate-dbs"`
}

// EVMEnabled returns true if EVM state stores should be opened.
Expand Down Expand Up @@ -106,5 +112,6 @@ func DefaultStateStoreConfig() StateStoreConfig {
UseDefaultComparer: false,
WriteMode: CosmosOnlyWrite,
ReadMode: CosmosOnlyRead,
SeparateEVMSubDBs: false,
}
}
17 changes: 17 additions & 0 deletions sei-db/config/toml.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,23 @@ ss-prune-interval = {{ .StateStore.PruneIntervalSeconds }}
# ImportNumWorkers defines the concurrency for state sync import
# defaults to 1
ss-import-num-workers = {{ .StateStore.ImportNumWorkers }}

# EVMDBDirectory defines the directory for the optional EVM state-store DB(s).
# If unset, defaults to <home>/data/evm_ss when EVM SS is enabled.
evm-ss-db-directory = "{{ .StateStore.EVMDBDirectory }}"

# WriteMode controls how EVM data writes are routed.
# Supported values: "cosmos_only", "dual_write", "split_write"
evm-ss-write-mode = "{{ .StateStore.WriteMode }}"

# ReadMode controls how EVM data reads are routed.
# Supported values: "cosmos_only", "evm_first", "split_read"
evm-ss-read-mode = "{{ .StateStore.ReadMode }}"

# SeparateEVMSubDBs controls whether EVM data is split across per-type DBs.
# When false, all EVM data stays in one DB using the current unified layout.
# When true, data is routed to separate DBs while preserving the same evm key prefix format.
evm-ss-separate-dbs = {{ .StateStore.SeparateEVMSubDBs }}
`

// ReceiptStoreConfigTemplate defines the configuration template for receipt-store
Expand Down
4 changes: 4 additions & 0 deletions sei-db/config/toml_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ func TestStateStoreConfigTemplate(t *testing.T) {
require.Contains(t, output, "ss-keep-recent =", "Missing ss-keep-recent")
require.Contains(t, output, "ss-prune-interval =", "Missing ss-prune-interval")
require.Contains(t, output, "ss-import-num-workers =", "Missing ss-import-num-workers")
require.Contains(t, output, `evm-ss-db-directory = ""`, "Missing evm-ss-db-directory")
require.Contains(t, output, `evm-ss-write-mode = "cosmos_only"`, "Missing or incorrect evm-ss-write-mode")
require.Contains(t, output, `evm-ss-read-mode = "cosmos_only"`, "Missing or incorrect evm-ss-read-mode")
require.Contains(t, output, "evm-ss-separate-dbs = false", "Missing or incorrect evm-ss-separate-dbs")
}

// TestReceiptStoreConfigTemplate verifies that all field paths in the receipt-store TOML template
Expand Down
7 changes: 6 additions & 1 deletion sei-db/state_db/ss/composite/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,12 @@ func NewCompositeStateStore(
return nil, fmt.Errorf("failed to create EVM store: %w", err)
}
cs.evmStore = evmStore
logger.Info("EVM state store enabled", "dir", evmDir, "writeMode", ssConfig.WriteMode, "readMode", ssConfig.ReadMode)
logger.Info("EVM state store enabled",
"dir", evmDir,
"writeMode", ssConfig.WriteMode,
"readMode", ssConfig.ReadMode,
"separateDBs", ssConfig.SeparateEVMSubDBs,
)
}

changelogPath := utils.GetChangelogPath(dbHome)
Expand Down
78 changes: 78 additions & 0 deletions sei-db/state_db/ss/evm/db_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package evm

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

"github.com/stretchr/testify/require"
Expand All @@ -9,6 +11,7 @@ import (
"github.com/sei-protocol/sei-chain/sei-db/config"
"github.com/sei-protocol/sei-chain/sei-db/db_engine/types"
"github.com/sei-protocol/sei-chain/sei-db/proto"
"github.com/sei-protocol/sei-chain/sei-db/state_db/ss/backend"
iavl "github.com/sei-protocol/sei-chain/sei-iavl"
)

Expand All @@ -29,6 +32,81 @@ func openTestStore(t *testing.T) types.StateStore {
return store
}

func TestEVMStateStoreDefaultUsesUnifiedDB(t *testing.T) {
dir := t.TempDir()
cfg := testConfig()

store, err := NewEVMStateStore(dir, cfg)
require.NoError(t, err)
t.Cleanup(func() { _ = store.Close() })

require.False(t, store.separateDBs)
require.Len(t, store.managedDBs, 1)

for _, storeType := range AllEVMStoreTypes() {
require.Same(t, store.managedDBs[0], store.subDBs[storeType])
_, err := os.Stat(filepath.Join(dir, StoreTypeName(storeType)))
require.ErrorIs(t, err, os.ErrNotExist)
}
}

func TestEVMStateStoreSeparatedPreservesUnifiedKeyLayout(t *testing.T) {
dir := t.TempDir()
cfg := testConfig()
cfg.SeparateEVMSubDBs = true

store, err := NewEVMStateStore(dir, cfg)
require.NoError(t, err)

addr := make([]byte, 20)
addr[0] = 0x11
slot := make([]byte, 32)
slot[0] = 0x22
nonceKey := append([]byte{0x0a}, addr...)
storageKey := append([]byte{0x03}, append(addr, slot...)...)

cs := []*proto.NamedChangeSet{
{
Name: EVMStoreKey,
Changeset: iavl.ChangeSet{
Pairs: []*iavl.KVPair{
{Key: nonceKey, Value: []byte{0x09}},
{Key: storageKey, Value: []byte("slot_value")},
},
},
},
}
require.NoError(t, store.ApplyChangesetSync(7, cs))
require.NoError(t, store.SetLatestVersion(7))
require.NoError(t, store.Close())

opener := backend.ResolveBackend(cfg.Backend)

nonceDir := filepath.Join(dir, StoreTypeName(StoreNonce))
nonceDB, err := opener(nonceDir, subDBConfig(cfg, nonceDir))
require.NoError(t, err)
defer nonceDB.Close()

nonceVal, err := nonceDB.Get(EVMStoreKey, 7, nonceKey)
require.NoError(t, err)
require.Equal(t, []byte{0x09}, nonceVal)
require.Equal(t, int64(7), nonceDB.GetLatestVersion())

_, strippedNonceKey := commonevm.ParseEVMKey(nonceKey)
rewrittenNonceVal, err := nonceDB.Get(StoreTypeName(StoreNonce), 7, strippedNonceKey)
require.NoError(t, err)
require.Nil(t, rewrittenNonceVal, "separated DB should preserve evm store key and full key layout")

storageDir := filepath.Join(dir, StoreTypeName(StoreStorage))
storageDB, err := opener(storageDir, subDBConfig(cfg, storageDir))
require.NoError(t, err)
defer storageDB.Close()

storageVal, err := storageDB.Get(EVMStoreKey, 7, storageKey)
require.NoError(t, err)
require.Equal(t, []byte("slot_value"), storageVal)
}

// verifyStateStoreInterface ensures EVMStateStore satisfies db_engine.StateStore.
func TestEVMStateStoreImplementsInterface(t *testing.T) {
var _ types.StateStore = (*EVMStateStore)(nil)
Expand Down
Loading