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
8 changes: 8 additions & 0 deletions sei-db/common/evm/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ const (
slotLen = 32
)

// EVMStoreKey is the cosmos store/module name for EVM state.
const EVMStoreKey = "evm"

// EVMFlatKVStoreKey is the module name used when exporting/importing FlatKV
// EVM data as a separate module in state-sync snapshots. Both the SC and SS
// layers need to recognise this name and treat it as EVM data.
const EVMFlatKVStoreKey = "evm_flatkv"

// EVM key prefixes — mirrored from x/evm/types/keys.go.
// These are immutable on-disk format markers; changing them would break
// all existing state, so duplicating here is safe and avoids pulling in the
Expand Down
7 changes: 4 additions & 3 deletions sei-db/state_db/bench/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (

"github.com/sei-protocol/sei-chain/sei-cosmos/snapshots"
snapshottypes "github.com/sei-protocol/sei-chain/sei-cosmos/snapshots/types"
commonevm "github.com/sei-protocol/sei-chain/sei-db/common/evm"
"github.com/sei-protocol/sei-chain/sei-db/proto"
"github.com/sei-protocol/sei-chain/sei-db/state_db/bench/wrappers"
sctypes "github.com/sei-protocol/sei-chain/sei-db/state_db/sc/types"
Expand All @@ -27,7 +28,7 @@ import (

const (
// EVMStoreName simulates the EVM module store
EVMStoreName = "evm"
EVMStoreName = commonevm.EVMStoreKey

// KeySize EVM storage key: 0x03 prefix + 20-byte address + 32-byte slot = 53 bytes
KeySize = 53
Expand Down Expand Up @@ -336,7 +337,7 @@ func importSnapshot(chunksDir string, importer sctypes.Importer) error {
switch i := item.Item.(type) {
case *snapshottypes.SnapshotItem_Store:
currModule = i.Store.Name
if currModule == "evm" {
if currModule == commonevm.EVMStoreKey {
if err := importer.AddModule(i.Store.Name); err != nil {
return fmt.Errorf("add module %s: %w", i.Store.Name, err)
}
Expand All @@ -345,7 +346,7 @@ func importSnapshot(chunksDir string, importer sctypes.Importer) error {
fmt.Printf("[Snapshot] Skipping store: %s\n", i.Store.Name)
}
case *snapshottypes.SnapshotItem_IAVL:
if currModule != "evm" {
if currModule != commonevm.EVMStoreKey {
continue
}
if i.IAVL.Height > math.MaxInt8 {
Expand Down
3 changes: 2 additions & 1 deletion sei-db/state_db/bench/wrappers/db_implementations.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@ import (
"fmt"
"path/filepath"

commonevm "github.com/sei-protocol/sei-chain/sei-db/common/evm"
"github.com/sei-protocol/sei-chain/sei-db/config"
"github.com/sei-protocol/sei-chain/sei-db/state_db/sc/composite"
"github.com/sei-protocol/sei-chain/sei-db/state_db/sc/flatkv"
"github.com/sei-protocol/sei-chain/sei-db/state_db/sc/memiavl"
ssComposite "github.com/sei-protocol/sei-chain/sei-db/state_db/ss/composite"
)

const EVMStoreName = "evm"
const EVMStoreName = commonevm.EVMStoreKey

type DBType string

Expand Down
114 changes: 114 additions & 0 deletions sei-db/state_db/sc/composite/exporter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package composite

import (
"errors"
"fmt"

errorutils "github.com/sei-protocol/sei-chain/sei-db/common/errors"
"github.com/sei-protocol/sei-chain/sei-db/state_db/sc/types"
)

var _ types.Exporter = (*SnapshotExporter)(nil)

type exportPhase int

const (
phaseCosmos exportPhase = iota
phaseFlatKV
phaseDone
)

// SnapshotExporter coordinates export from cosmos (memiavl) and flatKV backends.
//
// Next() returns items in stream order. Each item is either:
// - string: a module name header that starts a new module section
// - *types.SnapshotNode: a leaf key/value belonging to the current module
//
// FlatKV data is exported as a separate "evm_flatkv" module appended after all
// cosmos modules complete. This keeps the two backends fully independent in the
// snapshot stream.
type SnapshotExporter struct {
cosmosExporter types.Exporter
evmExporter types.Exporter
phase exportPhase
}

// NewExporter creates a composite exporter. cosmosExporter must not be nil.
// evmExporter may be nil when FlatKV is not active.
func NewExporter(cosmosExporter types.Exporter, evmExporter types.Exporter) (*SnapshotExporter, error) {
if cosmosExporter == nil {
return nil, fmt.Errorf("cosmosExporter must not be nil")
}
return &SnapshotExporter{
cosmosExporter: cosmosExporter,
evmExporter: evmExporter,
phase: phaseCosmos,
}, nil
}

// Next returns the next item in the composite snapshot stream.
//
// The stream is split into two sequential phases:
// 1. phaseCosmos — drains all items from the cosmos (memiavl) exporter.
// When the cosmos exporter is exhausted, if a FlatKV exporter is present,
// the phase transitions to phaseFlatKV and emits the EVMFlatKVStoreName
// module header as the first item.
// 2. phaseFlatKV — drains all items from the FlatKV exporter.
//
// Returns ErrorExportDone when both phases are complete.
func (s *SnapshotExporter) Next() (interface{}, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Short godoc explaining the methods in struct would be helpful. Purpose of these methods wasn't immediately obvious based on function names and context.

switch s.phase {
case phaseCosmos:
return s.nextFromCosmos()
case phaseFlatKV:
return s.nextFromFlatKV()
default:
return nil, errorutils.ErrorExportDone
}
}

// nextFromCosmos pulls items from the cosmos exporter. On exhaustion it
// transitions to phaseFlatKV (emitting the module header) or phaseDone.
func (s *SnapshotExporter) nextFromCosmos() (interface{}, error) {
item, err := s.cosmosExporter.Next()
if err != nil {
if !errors.Is(err, errorutils.ErrorExportDone) {
return nil, err
}

// Cosmos done. Append flatKV as a separate module.
if s.evmExporter != nil {
s.phase = phaseFlatKV
return EVMFlatKVStoreName, nil
}

s.phase = phaseDone
return nil, errorutils.ErrorExportDone
}
return item, nil
}

// nextFromFlatKV pulls items from the FlatKV exporter. On exhaustion it
// transitions to phaseDone.
func (s *SnapshotExporter) nextFromFlatKV() (interface{}, error) {
item, err := s.evmExporter.Next()
if err != nil {
if !errors.Is(err, errorutils.ErrorExportDone) {
return nil, err
}
s.phase = phaseDone
return nil, errorutils.ErrorExportDone
}
return item, nil
}

func (s *SnapshotExporter) Close() error {
var errCosmos, errEVM error
if s.cosmosExporter != nil {
errCosmos = s.cosmosExporter.Close()
}
if s.evmExporter != nil {
errEVM = s.evmExporter.Close()
}
return errors.Join(errCosmos, errEVM)
}
15 changes: 12 additions & 3 deletions sei-db/state_db/sc/composite/importer.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,26 @@ func (si *SnapshotImporter) Close() error {

func (si *SnapshotImporter) AddModule(name string) error {
si.currentModule = name
if name == EVMFlatKVStoreName {
if si.evmImporter != nil {
return si.evmImporter.AddModule(name)
}
return nil
}
if si.cosmosImporter != nil {
return si.cosmosImporter.AddModule(name)
}
return nil
}

func (si *SnapshotImporter) AddNode(node *types.SnapshotNode) {
if si.currentModule == EVMFlatKVStoreName {
if si.evmImporter != nil {
si.evmImporter.AddNode(node)
}
return
}
if si.cosmosImporter != nil {
si.cosmosImporter.AddNode(node)
}
if si.evmImporter != nil && si.currentModule == "evm" && node.Height == 0 {
si.evmImporter.AddNode(node)
}
}
31 changes: 26 additions & 5 deletions sei-db/state_db/sc/composite/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"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/config"
"github.com/sei-protocol/sei-chain/sei-db/proto"
"github.com/sei-protocol/sei-chain/sei-db/state_db/sc/flatkv"
Expand All @@ -19,8 +20,13 @@ import (

var logger = seilog.NewLogger("db", "state-db", "sc", "composite")

// EVMStoreName is the module name for the EVM store
const EVMStoreName = "evm"
// EVMStoreName is the module name for the EVM store in memiavl.
const EVMStoreName = commonevm.EVMStoreKey

// EVMFlatKVStoreName is the module name used when exporting/importing
// EVM data from the FlatKV backend. Treated as a separate module in
// state-sync snapshots so that import routes data exclusively to FlatKV.
const EVMFlatKVStoreName = commonevm.EVMFlatKVStoreKey

// For backward compatibility purpose reuse current interface
var _ types.Committer = (*CompositeCommitStore)(nil)
Expand Down Expand Up @@ -298,8 +304,22 @@ func (cs *CompositeCommitStore) Exporter(version int64) (types.Exporter, error)
if version < 0 || version > math.MaxUint32 {
return nil, fmt.Errorf("version %d out of range", version)
}
// TODO: Add evm committer for exporter
return cs.cosmosCommitter.Exporter(version)

cosmosExporter, err := cs.cosmosCommitter.Exporter(version)
if err != nil {
return nil, fmt.Errorf("failed to create cosmos exporter: %w", err)
}

var evmExporter types.Exporter
if cs.evmCommitter != nil && (cs.config.WriteMode == config.SplitWrite || cs.config.WriteMode == config.DualWrite) {
evmExporter, err = cs.evmCommitter.Exporter(version)
if err != nil {
_ = cosmosExporter.Close()
return nil, fmt.Errorf("failed to create evm exporter: %w", err)
}
}

return NewExporter(cosmosExporter, evmExporter)
}

// Importer returns an importer for state sync
Expand All @@ -312,7 +332,8 @@ func (cs *CompositeCommitStore) Importer(version int64) (types.Importer, error)
if cs.evmCommitter != nil {
evmImporter, err = cs.evmCommitter.Importer(version)
if err != nil {
return nil, err
_ = cosmosImporter.Close()
return nil, fmt.Errorf("failed to create evm importer: %w", err)
}
}
compositeImporter := NewImporter(cosmosImporter, evmImporter)
Expand Down
Loading
Loading