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
100 changes: 100 additions & 0 deletions sei-db/state_db/sc/composite/exporter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
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
}

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
}
}

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
}

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)
}
}
28 changes: 24 additions & 4 deletions sei-db/state_db/sc/composite/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,14 @@ import (

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

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

// 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 = "evm_flatkv"

// For backward compatibility purpose reuse current interface
var _ types.Committer = (*CompositeCommitStore)(nil)

Expand Down Expand Up @@ -270,8 +275,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 @@ -284,7 +303,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