|
| 1 | +package composite |
| 2 | + |
| 3 | +import ( |
| 4 | + "path/filepath" |
| 5 | + |
| 6 | + commonevm "github.com/sei-protocol/sei-chain/sei-db/common/evm" |
| 7 | + "github.com/sei-protocol/sei-chain/sei-db/common/logger" |
| 8 | + "github.com/sei-protocol/sei-chain/sei-db/config" |
| 9 | + "github.com/sei-protocol/sei-chain/sei-db/proto" |
| 10 | + "github.com/sei-protocol/sei-chain/sei-db/state_db/ss/evm" |
| 11 | + "github.com/sei-protocol/sei-chain/sei-db/state_db/ss/types" |
| 12 | +) |
| 13 | + |
| 14 | +// CompositeStateStore routes operations between Cosmos_SS (main state store) and EVM_SS (optimized EVM stores). |
| 15 | +// - Reads check EVM_SS first for EVM keys, then fallback to Cosmos_SS |
| 16 | +// - Writes go to both stores for EVM keys, only Cosmos_SS for others |
| 17 | +type CompositeStateStore struct { |
| 18 | + cosmosStore types.StateStore // Main MVCC PebbleDB for all modules |
| 19 | + evmStore *evm.EVMStateStore // Separate EVM DBs with default comparer (nil if disabled) |
| 20 | + logger logger.Logger |
| 21 | +} |
| 22 | + |
| 23 | +// NewCompositeStateStore creates a new composite state store |
| 24 | +// cosmosStore: the main state store (required) |
| 25 | +// evmConfig: configuration for EVM state stores (optional - if nil or disabled, only cosmosStore is used) |
| 26 | +// homeDir: base directory for data files |
| 27 | +func NewCompositeStateStore( |
| 28 | + cosmosStore types.StateStore, |
| 29 | + evmConfig *config.EVMStateStoreConfig, |
| 30 | + homeDir string, |
| 31 | + log logger.Logger, |
| 32 | +) (*CompositeStateStore, error) { |
| 33 | + cs := &CompositeStateStore{ |
| 34 | + cosmosStore: cosmosStore, |
| 35 | + logger: log, |
| 36 | + } |
| 37 | + |
| 38 | + // Initialize EVM stores if enabled |
| 39 | + if evmConfig != nil && evmConfig.Enable { |
| 40 | + evmDir := evmConfig.DBDirectory |
| 41 | + if evmDir == "" { |
| 42 | + evmDir = filepath.Join(homeDir, "data", "evm_ss") |
| 43 | + } |
| 44 | + |
| 45 | + evmStore, err := evm.NewEVMStateStore(evmDir) |
| 46 | + if err != nil { |
| 47 | + return nil, err |
| 48 | + } |
| 49 | + cs.evmStore = evmStore |
| 50 | + log.Info("EVM state store enabled", "dir", evmDir) |
| 51 | + } |
| 52 | + |
| 53 | + return cs, nil |
| 54 | +} |
| 55 | + |
| 56 | +// Get retrieves a value for a key at a specific version |
| 57 | +// For EVM keys: check EVM_SS first, fallback to Cosmos_SS |
| 58 | +// For non-EVM keys: use Cosmos_SS directly |
| 59 | +func (s *CompositeStateStore) Get(storeKey string, version int64, key []byte) ([]byte, error) { |
| 60 | + // Try EVM store first for EVM keys |
| 61 | + if s.evmStore != nil && storeKey == evm.EVMStoreKey { |
| 62 | + storeType, strippedKey := commonevm.ParseEVMKey(key) |
| 63 | + if storeType != evm.StoreUnknown { |
| 64 | + db := s.evmStore.GetDB(storeType) |
| 65 | + if db != nil { |
| 66 | + val, err := db.Get(strippedKey, version) |
| 67 | + if err != nil { |
| 68 | + return nil, err |
| 69 | + } |
| 70 | + if val != nil { |
| 71 | + return val, nil |
| 72 | + } |
| 73 | + // Fall through to Cosmos_SS if not found in EVM_SS |
| 74 | + } |
| 75 | + } |
| 76 | + } |
| 77 | + |
| 78 | + // Fallback to Cosmos store |
| 79 | + return s.cosmosStore.Get(storeKey, version, key) |
| 80 | +} |
| 81 | + |
| 82 | +// Has checks if a key exists at a specific version |
| 83 | +func (s *CompositeStateStore) Has(storeKey string, version int64, key []byte) (bool, error) { |
| 84 | + // Try EVM store first for EVM keys |
| 85 | + if s.evmStore != nil && storeKey == evm.EVMStoreKey { |
| 86 | + storeType, strippedKey := commonevm.ParseEVMKey(key) |
| 87 | + if storeType != evm.StoreUnknown { |
| 88 | + db := s.evmStore.GetDB(storeType) |
| 89 | + if db != nil { |
| 90 | + has, err := db.Has(strippedKey, version) |
| 91 | + if err != nil { |
| 92 | + return false, err |
| 93 | + } |
| 94 | + if has { |
| 95 | + return true, nil |
| 96 | + } |
| 97 | + // Fall through to check Cosmos_SS |
| 98 | + } |
| 99 | + } |
| 100 | + } |
| 101 | + |
| 102 | + // Fallback to Cosmos store |
| 103 | + return s.cosmosStore.Has(storeKey, version, key) |
| 104 | +} |
| 105 | + |
| 106 | +// Iterator returns an iterator over keys in the given range |
| 107 | +// For EVM store keys, we use Cosmos_SS iterator (EVM_SS is an optimization layer) |
| 108 | +func (s *CompositeStateStore) Iterator(storeKey string, version int64, start, end []byte) (types.DBIterator, error) { |
| 109 | + // Use Cosmos store for iteration (source of truth) |
| 110 | + return s.cosmosStore.Iterator(storeKey, version, start, end) |
| 111 | +} |
| 112 | + |
| 113 | +// ReverseIterator returns a reverse iterator over keys in the given range |
| 114 | +func (s *CompositeStateStore) ReverseIterator(storeKey string, version int64, start, end []byte) (types.DBIterator, error) { |
| 115 | + return s.cosmosStore.ReverseIterator(storeKey, version, start, end) |
| 116 | +} |
| 117 | + |
| 118 | +// RawIterate iterates over raw key-value pairs |
| 119 | +func (s *CompositeStateStore) RawIterate(storeKey string, fn func([]byte, []byte, int64) bool) (bool, error) { |
| 120 | + return s.cosmosStore.RawIterate(storeKey, fn) |
| 121 | +} |
| 122 | + |
| 123 | +// GetLatestVersion returns the latest version |
| 124 | +func (s *CompositeStateStore) GetLatestVersion() int64 { |
| 125 | + return s.cosmosStore.GetLatestVersion() |
| 126 | +} |
| 127 | + |
| 128 | +// GetEarliestVersion returns the earliest version |
| 129 | +func (s *CompositeStateStore) GetEarliestVersion() int64 { |
| 130 | + return s.cosmosStore.GetEarliestVersion() |
| 131 | +} |
| 132 | + |
| 133 | +// GetLatestMigratedKey returns the latest migrated key |
| 134 | +func (s *CompositeStateStore) GetLatestMigratedKey() ([]byte, error) { |
| 135 | + return s.cosmosStore.GetLatestMigratedKey() |
| 136 | +} |
| 137 | + |
| 138 | +// GetLatestMigratedModule returns the latest migrated module |
| 139 | +func (s *CompositeStateStore) GetLatestMigratedModule() (string, error) { |
| 140 | + return s.cosmosStore.GetLatestMigratedModule() |
| 141 | +} |
| 142 | + |
| 143 | +// Close closes all underlying stores |
| 144 | +func (s *CompositeStateStore) Close() error { |
| 145 | + var lastErr error |
| 146 | + |
| 147 | + if s.evmStore != nil { |
| 148 | + if err := s.evmStore.Close(); err != nil { |
| 149 | + s.logger.Error("failed to close EVM store", "error", err) |
| 150 | + lastErr = err |
| 151 | + } |
| 152 | + } |
| 153 | + |
| 154 | + if err := s.cosmosStore.Close(); err != nil { |
| 155 | + s.logger.Error("failed to close Cosmos store", "error", err) |
| 156 | + lastErr = err |
| 157 | + } |
| 158 | + |
| 159 | + return lastErr |
| 160 | +} |
| 161 | + |
| 162 | +// ============================================================================= |
| 163 | +// Write path methods - delegated to Cosmos store only in this PR |
| 164 | +// Full dual-write implementation will be added in the next PR |
| 165 | +// ============================================================================= |
| 166 | + |
| 167 | +// SetLatestVersion sets the latest version |
| 168 | +func (s *CompositeStateStore) SetLatestVersion(version int64) error { |
| 169 | + return s.cosmosStore.SetLatestVersion(version) |
| 170 | +} |
| 171 | + |
| 172 | +// SetEarliestVersion sets the earliest version |
| 173 | +func (s *CompositeStateStore) SetEarliestVersion(version int64, ignoreVersion bool) error { |
| 174 | + return s.cosmosStore.SetEarliestVersion(version, ignoreVersion) |
| 175 | +} |
| 176 | + |
| 177 | +// ApplyChangesetSync applies changeset synchronously (delegates to Cosmos store) |
| 178 | +func (s *CompositeStateStore) ApplyChangesetSync(version int64, changesets []*proto.NamedChangeSet) error { |
| 179 | + // TODO: Add dual-write to EVM_SS in next PR |
| 180 | + return s.cosmosStore.ApplyChangesetSync(version, changesets) |
| 181 | +} |
| 182 | + |
| 183 | +// ApplyChangesetAsync applies changeset asynchronously (delegates to Cosmos store) |
| 184 | +func (s *CompositeStateStore) ApplyChangesetAsync(version int64, changesets []*proto.NamedChangeSet) error { |
| 185 | + // TODO: Add dual-write to EVM_SS in next PR |
| 186 | + return s.cosmosStore.ApplyChangesetAsync(version, changesets) |
| 187 | +} |
| 188 | + |
| 189 | +// Import imports initial state |
| 190 | +func (s *CompositeStateStore) Import(version int64, ch <-chan types.SnapshotNode) error { |
| 191 | + // TODO: Add dual-write to EVM_SS in next PR |
| 192 | + return s.cosmosStore.Import(version, ch) |
| 193 | +} |
| 194 | + |
| 195 | +// RawImport imports raw key-value entries |
| 196 | +func (s *CompositeStateStore) RawImport(ch <-chan types.RawSnapshotNode) error { |
| 197 | + // TODO: Add dual-write to EVM_SS in next PR |
| 198 | + return s.cosmosStore.RawImport(ch) |
| 199 | +} |
| 200 | + |
| 201 | +// Prune removes old versions |
| 202 | +func (s *CompositeStateStore) Prune(version int64) error { |
| 203 | + // Prune both stores |
| 204 | + if s.evmStore != nil { |
| 205 | + if err := s.evmStore.Prune(version); err != nil { |
| 206 | + s.logger.Error("failed to prune EVM store", "error", err) |
| 207 | + // Continue to prune Cosmos store |
| 208 | + } |
| 209 | + } |
| 210 | + return s.cosmosStore.Prune(version) |
| 211 | +} |
0 commit comments