Skip to content

feat(sei-db): add flatkv store implementation#2793

Merged
blindchaser merged 14 commits intomainfrom
yiren/flatkv-impl-rebased
Feb 18, 2026
Merged

feat(sei-db): add flatkv store implementation#2793
blindchaser merged 14 commits intomainfrom
yiren/flatkv-impl-rebased

Conversation

@blindchaser
Copy link
Contributor

  • Multi-DB storage: storageDB, accountDB, codeDB, metadataDB
  • LtHash-based state commitment using internal key formats
  • Configurable write toggles per DB type
  • Iterator support for storage keys

Describe your changes and provide context

Testing performed to validate your change

@github-actions
Copy link

github-actions bot commented Feb 3, 2026

The latest Buf updates on your PR. Results from workflow Buf / buf (pull_request).

BuildFormatLintBreakingUpdated (UTC)
✅ passed✅ passed✅ passed✅ passedFeb 17, 2026, 11:31 PM

@codecov
Copy link

codecov bot commented Feb 3, 2026

Codecov Report

❌ Patch coverage is 59.97305% with 297 lines in your changes missing coverage. Please review.
✅ Project coverage is 57.24%. Comparing base (ca2a7c8) to head (1bc1a66).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
sei-db/state_db/sc/flatkv/iterator.go 40.00% 63 Missing and 12 partials ⚠️
sei-db/state_db/sc/flatkv/store_write.go 68.52% 32 Missing and 30 partials ⚠️
sei-db/state_db/sc/flatkv/store_read.go 53.90% 46 Missing and 13 partials ⚠️
sei-db/state_db/sc/flatkv/store.go 64.03% 25 Missing and 16 partials ⚠️
sei-db/state_db/sc/composite/store.go 32.25% 16 Missing and 5 partials ⚠️
sei-db/state_db/sc/flatkv/store_lifecycle.go 54.83% 7 Missing and 7 partials ⚠️
sei-db/state_db/sc/flatkv/store_meta.go 67.56% 6 Missing and 6 partials ⚠️
evmrpc/filter.go 0.00% 4 Missing ⚠️
sei-db/state_db/sc/flatkv/keys.go 84.00% 4 Missing ⚠️
sei-db/common/evm/keys.go 94.73% 0 Missing and 2 partials ⚠️
... and 2 more
Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##             main    #2793       +/-   ##
===========================================
+ Coverage   48.37%   57.24%    +8.87%     
===========================================
  Files         671     2099     +1428     
  Lines       50621   172410   +121789     
===========================================
+ Hits        24486    98701    +74215     
- Misses      23987    64851    +40864     
- Partials     2148     8858     +6710     
Flag Coverage Δ
sei-chain 52.73% <59.97%> (?)
sei-cosmos 48.19% <ø> (+<0.01%) ⬆️
sei-db 68.72% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
app/params/config.go 86.30% <ø> (ø)
cmd/seid/cmd/root.go 0.00% <ø> (ø)
sei-db/ledger_db/receipt/receipt_cache.go 83.92% <100.00%> (ø)
sei-db/state_db/sc/flatkv/config.go 100.00% <100.00%> (ø)
sei-db/ledger_db/receipt/cached_receipt_store.go 89.04% <80.00%> (ø)
sei-db/common/evm/keys.go 91.48% <94.73%> (ø)
sei-db/ledger_db/receipt/receipt_store.go 69.59% <33.33%> (ø)
evmrpc/filter.go 69.26% <0.00%> (ø)
sei-db/state_db/sc/flatkv/keys.go 95.29% <84.00%> (ø)
sei-db/state_db/sc/flatkv/store_meta.go 67.56% <67.56%> (ø)
... and 6 more

... and 1535 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 34a6fed4c3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@blindchaser blindchaser force-pushed the yiren/flatkv-impl-rebased branch from 34a6fed to e3a525f Compare February 3, 2026 19:49
@blindchaser blindchaser force-pushed the yiren/flatkv-impl-rebased branch from 3adc396 to eafddae Compare February 12, 2026 02:14
Comment on lines +158 to +188
for addrStr := range modifiedAccounts {
addr, ok := AddressFromBytes([]byte(addrStr))
if !ok {
return fmt.Errorf("invalid address in modifiedAccounts: %x", addrStr)
}

// Get old AccountValue from DB (committed state)
oldAV, err := s.getAccountValueFromDB(addr)
if err != nil {
return fmt.Errorf("failed to get old account value for addr %x: %w", addr, err)
}
oldValue := oldAV.Encode()

// Get new AccountValue (from pending writes or DB)
var newValue []byte
var isDelete bool
if paw, ok := s.accountWrites[addrStr]; ok {
newValue = paw.value.Encode()
isDelete = paw.isDelete
} else {
// No pending write means no change (shouldn't happen, but be safe)
continue
}

accountPairs = append(accountPairs, lthash.KVPairWithLastValue{
Key: AccountKey(addr),
Value: newValue,
LastValue: oldValue,
Delete: isDelete,
})
}

Check warning

Code scanning / CodeQL

Iteration over map Warning

Iteration over map may be a possible source of non-determinism
Comment on lines +280 to +293
for _, paw := range s.accountWrites {
key := AccountKey(paw.addr)
if paw.isDelete {
if err := batch.Delete(key); err != nil {
return fmt.Errorf("accountDB delete: %w", err)
}
} else {
// Encode AccountValue and store with addr as key
encoded := EncodeAccountValue(paw.value)
if err := batch.Set(key, encoded); err != nil {
return fmt.Errorf("accountDB set: %w", err)
}
}
}

Check warning

Code scanning / CodeQL

Iteration over map Warning

Iteration over map may be a possible source of non-determinism
Comment on lines +316 to +326
for _, pw := range s.codeWrites {
if pw.isDelete {
if err := batch.Delete(pw.key); err != nil {
return fmt.Errorf("codeDB delete: %w", err)
}
} else {
if err := batch.Set(pw.key, pw.value); err != nil {
return fmt.Errorf("codeDB set: %w", err)
}
}
}

Check warning

Code scanning / CodeQL

Iteration over map Warning

Iteration over map may be a possible source of non-determinism
Comment on lines +349 to +359
for _, pw := range s.storageWrites {
if pw.isDelete {
if err := batch.Delete(pw.key); err != nil {
return fmt.Errorf("storageDB delete: %w", err)
}
} else {
if err := batch.Set(pw.key, pw.value); err != nil {
return fmt.Errorf("storageDB set: %w", err)
}
}
}

Check warning

Code scanning / CodeQL

Iteration over map Warning

Iteration over map may be a possible source of non-determinism
Copy link
Contributor

@yzang2019 yzang2019 left a comment

Choose a reason for hiding this comment

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

Overall LGTM, approved with some more minor comments

yzang2019
yzang2019 approved these changes Feb 13, 2026
- Multi-DB storage: storageDB, accountDB, codeDB, metadataDB
- LtHash-based state commitment using internal key formats
- Configurable write toggles per DB type
- Iterator support for storage keys
@blindchaser blindchaser force-pushed the yiren/flatkv-impl-rebased branch from 878551c to b20956a Compare February 13, 2026 17:00
@blindchaser blindchaser force-pushed the yiren/flatkv-impl-rebased branch from 77a33a2 to d7ba059 Compare February 13, 2026 17:18
Copy link

@cody-littley cody-littley left a comment

Choose a reason for hiding this comment

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

My comments are mostly nitpicks, overall LGTM. Please take my review with a grain of salt, since I'm still building context.

Comment on lines +113 to +118
func (it *dbIterator) Error() error {
if it.err != nil {
return it.err
}
return it.iter.Error()
}

Choose a reason for hiding this comment

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

A potential alternative to logging might be to make it so that any error returned during iterator construction can be surfaced here (e.g. by adding an err field to emptyIterator).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done


// Get returns the value for the given memiavl key.
// Returns (value, true) if found, (nil, false) if not found.
func (s *CommitStore) Get(key []byte) ([]byte, bool) {

Choose a reason for hiding this comment

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

Nit, it might be nice to split out the logic in the switch statement case blocks into small helper functions.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

acknowledged, the cases are ~10-15 lines each so I lean towards keeping them inline for now to avoid function-per-case overhead. happy to revisit if the cases grow in complexity.

Comment on lines +103 to +105
default:
return nil, false
}

Choose a reason for hiding this comment

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

Do we expect to hit the default case during normal operation? If not, would it make sense to return an error in case we accidentally end up in that scenario?

Suggested change
default:
return nil, false
}
default:
return nil, fmt.Errorf("unhandled kind: %d", kind)
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

the Get() signature returns ([]byte, bool) per the Store interface matching the memiavl, the default case is expected to fire for some EVMKeyLegacy, but im not 100% sure during operations though. returning (nil, false) is the correct "not found" semantics here.


// Route to appropriate DB based on key type
switch kind {
case evm.EVMKeyStorage:

Choose a reason for hiding this comment

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

Also potentially a good candidate to split switch case blocks into helper functions.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

agreed it would improve readability. I'll keep this as-is for now since the cases are tightly coupled with the local variables (modifiedAccounts, storagePairs, codePairs, s.storageWrites, etc.), happy to revisit if the function grows further.

Comment on lines +39 to +42
if kind == evm.EVMKeyUnknown {
// Skip non-EVM keys silently
continue
}

Choose a reason for hiding this comment

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

Do we expect to find non-EVM keys during regular operations? If not, would it make sense to log a warning?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

will add a log.Warn as good defensive measure.

Comment on lines +247 to +258
func (s *CommitStore) flushAllDBs() error {
if err := s.accountDB.Flush(); err != nil {
return fmt.Errorf("accountDB flush: %w", err)
}
if err := s.codeDB.Flush(); err != nil {
return fmt.Errorf("codeDB flush: %w", err)
}
if err := s.storageDB.Flush(); err != nil {
return fmt.Errorf("storageDB flush: %w", err)
}
return nil
}

Choose a reason for hiding this comment

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

Since commit/flush is probably doing nontrivial IO work, I wonder if we could get some meaningful performance gain by committing/flushing each DB on a parallel goroutine.

Suggested change
func (s *CommitStore) flushAllDBs() error {
if err := s.accountDB.Flush(); err != nil {
return fmt.Errorf("accountDB flush: %w", err)
}
if err := s.codeDB.Flush(); err != nil {
return fmt.Errorf("codeDB flush: %w", err)
}
if err := s.storageDB.Flush(); err != nil {
return fmt.Errorf("storageDB flush: %w", err)
}
return nil
}
func (s *CommitStore) flushAllDBs() error {
errChan := make(chan error, 3)
go func() {
if err := s.accountDB.Flush(); err != nil {
errChan <- fmt.Errorf("accountDB flush: %w", err)
} else {
errChan <- nil
}
}()
go func() {
if err := s.codeDB.Flush(); err != nil {
errChan <- fmt.Errorf("codeDB flush: %w", err)
} else {
errChan <- nil
}
}()
go func() {
if err := s.storageDB.Flush(); err != nil {
errChan <- fmt.Errorf("storageDB flush: %w", err)
} else {
errChan <- nil
}
}()
for i := 0; i < 3; i++ {
err := <-errChan
if err != nil {
return fmt.Errorf("failed to flush DB: %v", err)
}
}
return nil
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

good thought! i think memtable → L0 is probably fast enough, If profiling shows flush is a bottleneck we can revisit. will keep this in mind

@blindchaser blindchaser merged commit 07025b9 into main Feb 18, 2026
41 of 42 checks passed
@blindchaser blindchaser deleted the yiren/flatkv-impl-rebased branch February 18, 2026 00:02
yzang2019 added a commit that referenced this pull request Feb 18, 2026
* main:
  feat(sei-db): add flatkv store implementation (#2793)
// setFullnodeTypeTendermintConfig sets common Tendermint config for fullnode-like nodes
func setFullnodeTypeTendermintConfig(config *tmcfg.Config) {
config.TxIndex.Indexer = []string{"kv"} // Full nodes need tx indexing for queries
config.RPC.ListenAddress = "tcp://0.0.0.0:26657"
Copy link
Contributor

Choose a reason for hiding this comment

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

Hey @blindchaser
These lines were added by #2903 Can you please explain why they are removed? or was it an unresolved conflict issue?

Copy link
Contributor

@mojtaba-esk mojtaba-esk Feb 18, 2026

Choose a reason for hiding this comment

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

This PR is a large one and such accident could happen. A new PR is opened to put them back #2913 would you please have a look and see if it is alright? thanks

blindchaser added a commit that referenced this pull request Feb 18, 2026
blindchaser added a commit that referenced this pull request Feb 18, 2026
This reverts commit 07025b9.

- 07025b9 contains wrongly rebased
code, will revert and re-commit.

## Describe your changes and provide context

## Testing performed to validate your change
mojtaba-esk added a commit that referenced this pull request Feb 18, 2026
## Describe your changes and provide context

A change was done by #2903 was accidentally removed by #2793 
This PR attempts to add them back + a doc change.

## Testing performed to validate your change

Co-authored-by: Mojtaba <mojtaba@celestia.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

Comments