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
37 changes: 22 additions & 15 deletions sei-db/common/evm/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,30 +96,37 @@ func ParseEVMKey(key []byte) (kind EVMKeyKind, keyBytes []byte) {
return EVMKeyLegacy, key
}

// EVMKeyPrefixByte returns the single-byte on-disk prefix for a given key kind.
// Returns (0, false) for kinds that have no fixed prefix (e.g. EVMKeyLegacy).
func EVMKeyPrefixByte(kind EVMKeyKind) (byte, bool) {
switch kind {
case EVMKeyStorage:
return stateKeyPrefix[0], true
case EVMKeyNonce:
return nonceKeyPrefix[0], true
case EVMKeyCodeHash:
return codeHashKeyPrefix[0], true
case EVMKeyCode:
return codeKeyPrefix[0], true
default:
return 0, false
}
}

// BuildMemIAVLEVMKey builds a memiavl key from internal bytes.
// This is the reverse of ParseEVMKey for optimized key types.
//
// NOTE: This is primarily used for tests and temporary compatibility.
// FlatKV stores data in internal format; this function converts back to
// memiavl format for Iterator/Exporter output.
func BuildMemIAVLEVMKey(kind EVMKeyKind, keyBytes []byte) []byte {
var prefix []byte
switch kind {
case EVMKeyStorage:
prefix = stateKeyPrefix
case EVMKeyNonce:
prefix = nonceKeyPrefix
case EVMKeyCodeHash:
prefix = codeHashKeyPrefix
case EVMKeyCode:
prefix = codeKeyPrefix
default:
prefix, ok := EVMKeyPrefixByte(kind)
if !ok {
return nil
}

result := make([]byte, 0, len(prefix)+len(keyBytes))
result = append(result, prefix...)
result = append(result, keyBytes...)
result := make([]byte, 1+len(keyBytes))
result[0] = prefix
copy(result[1:], keyBytes)
return result
}

Expand Down
3 changes: 2 additions & 1 deletion sei-db/state_db/bench/wrappers/flatkv_wrapper.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package wrappers

import (
"github.com/sei-protocol/sei-chain/sei-db/common/evm"
"github.com/sei-protocol/sei-chain/sei-db/common/metrics"
"github.com/sei-protocol/sei-chain/sei-db/proto"
"github.com/sei-protocol/sei-chain/sei-db/state_db/sc/flatkv"
Expand Down Expand Up @@ -61,7 +62,7 @@ func (f *flatKVWrapper) Close() error {
}

func (f *flatKVWrapper) Read(key []byte) (data []byte, found bool, err error) {
val, ok := f.base.Get(key)
val, ok := f.base.Get(evm.EVMStoreKey, key)
return val, ok, nil
}

Expand Down
25 changes: 13 additions & 12 deletions sei-db/state_db/sc/composite/store_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"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"
"github.com/sei-protocol/sei-chain/sei-db/state_db/sc/flatkv/ktype"
"github.com/sei-protocol/sei-chain/sei-db/state_db/sc/types"
)

Expand All @@ -26,11 +27,11 @@ func (f *failingEVMStore) LoadVersion(int64, bool) (flatkv.Store, error) {
}
func (f *failingEVMStore) ApplyChangeSets([]*proto.NamedChangeSet) error { return nil }
func (f *failingEVMStore) Commit() (int64, error) { return 0, nil }
func (f *failingEVMStore) Get([]byte) ([]byte, bool) { return nil, false }
func (f *failingEVMStore) GetBlockHeightModified([]byte) (int64, bool, error) {
func (f *failingEVMStore) Get(string, []byte) ([]byte, bool) { return nil, false }
func (f *failingEVMStore) GetBlockHeightModified(string, []byte) (int64, bool, error) {
return -1, false, nil
}
func (f *failingEVMStore) Has([]byte) bool { return false }
func (f *failingEVMStore) Has(string, []byte) bool { return false }
func (f *failingEVMStore) Iterator(_, _ []byte) flatkv.Iterator { return nil }
func (f *failingEVMStore) IteratorByPrefix([]byte) flatkv.Iterator { return nil }
func (f *failingEVMStore) RootHash() []byte { return nil }
Expand Down Expand Up @@ -514,10 +515,10 @@ func TestExportImportSplitWrite(t *testing.T) {
_, err := src.LoadVersion(0, false)
require.NoError(t, err)

addr := flatkv.Address{0xAA}
slot := flatkv.Slot{0xBB}
addr := ktype.Address{0xAA}
slot := ktype.Slot{0xBB}
storageKey := evm.BuildMemIAVLEVMKey(evm.EVMKeyStorage,
flatkv.StorageKey(addr, slot))
ktype.StorageKey(addr, slot))
storageVal := padLeft32(0x42)

nonceKey := evm.BuildMemIAVLEVMKey(evm.EVMKeyNonce, addr[:])
Expand Down Expand Up @@ -580,11 +581,11 @@ func TestExportImportSplitWrite(t *testing.T) {

// Verify FlatKV data
require.NotNil(t, dst.evmCommitter)
got, found := dst.evmCommitter.Get(storageKey)
got, found := dst.evmCommitter.Get(evm.EVMStoreKey, storageKey)
require.True(t, found, "storage key should exist in FlatKV after import")
require.Equal(t, storageVal, got)

got, found = dst.evmCommitter.Get(nonceKey)
got, found = dst.evmCommitter.Get(evm.EVMStoreKey, nonceKey)
require.True(t, found, "nonce key should exist in FlatKV after import")
require.Equal(t, nonceVal, got)
}
Expand Down Expand Up @@ -684,7 +685,7 @@ func TestReconcileVersionsAfterCrash(t *testing.T) {
addr := [20]byte{0xAA}
slot := [32]byte{0xBB}
storageKey := evm.BuildMemIAVLEVMKey(evm.EVMKeyStorage,
flatkv.StorageKey(addr, slot))
ktype.StorageKey(addr, slot))

cfg := splitWriteConfig()

Expand Down Expand Up @@ -758,7 +759,7 @@ func TestReconcileVersionsThenContinueCommitting(t *testing.T) {
addr := [20]byte{0xEE}
slot := [32]byte{0xFF}
storageKey := evm.BuildMemIAVLEVMKey(evm.EVMKeyStorage,
flatkv.StorageKey(addr, slot))
ktype.StorageKey(addr, slot))

cfg := splitWriteConfig()

Expand Down Expand Up @@ -836,7 +837,7 @@ func TestReconcileVersionsThenContinueCommitting(t *testing.T) {
bankStore := cs3.GetChildStoreByName("bank")
require.Equal(t, []byte{0xA5}, bankStore.Get([]byte("bal")))

got, found := cs3.evmCommitter.Get(storageKey)
got, found := cs3.evmCommitter.Get(evm.EVMStoreKey, storageKey)
require.True(t, found)
require.Equal(t, padLeft32(0xA5), got)
}
Expand All @@ -845,7 +846,7 @@ func TestReconcileVersionsCosmosAheadByMultiple(t *testing.T) {
addr := [20]byte{0xCC}
slot := [32]byte{0xDD}
storageKey := evm.BuildMemIAVLEVMKey(evm.EVMKeyStorage,
flatkv.StorageKey(addr, slot))
ktype.StorageKey(addr, slot))

cfg := splitWriteConfig()

Expand Down
27 changes: 16 additions & 11 deletions sei-db/state_db/sc/flatkv/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,21 +27,27 @@ type Store interface {
LoadVersion(targetVersion int64, readOnly bool) (Store, error)

// ApplyChangeSets buffers EVM changesets (x/evm memiavl keys) and updates LtHash.
// Non-EVM modules are ignored. Call Commit to persist.
// Non-EVM modules are routed into legacy storage under their module prefix.
// Call Commit to persist.
ApplyChangeSets(cs []*proto.NamedChangeSet) error

// Commit persists buffered writes and advances the version.
Commit() (int64, error)

// Get returns the value for the x/evm memiavl key. If not found, returns (nil, false).
Get(key []byte) (value []byte, found bool)
// Get returns the value for a key within the given module.
// For EVM keys (moduleName == "evm"), the key is a memiavl EVM key
// routed to account/storage/code/legacy DBs internally.
// For non-EVM modules, the key is read from legacy storage with the module prefix.
// If not found, returns (nil, false).
Get(moduleName string, key []byte) (value []byte, found bool)

// GetBlockHeightModified returns the block height at which the key was last modified.
// Only supported for EVM keys; non-EVM legacy data does not track block height.
// If not found, returns (-1, false, nil).
GetBlockHeightModified(key []byte) (int64, bool, error)
GetBlockHeightModified(moduleName string, key []byte) (int64, bool, error)

// Has reports whether the x/evm memiavl key exists.
Has(key []byte) bool
// Has reports whether the key exists within the given module.
Has(moduleName string, key []byte) bool

// Iterator returns an iterator over [start, end) in memiavl key order.
// Pass nil for unbounded.
Expand Down Expand Up @@ -91,7 +97,8 @@ type Store interface {
// Iterator provides ordered iteration over EVM keys.
// Follows PebbleDB semantics: not positioned on creation.
//
// Keys are returned in internal format (without memiavl prefix).
// Keys are returned in physical format ("evm/" + type_prefix_byte + stripped_key).
// SeekGE/SeekLT accept both physical keys and memiavl keys (prefix_byte + stripped_key).
//
// EXPERIMENTAL: not used in production. Interface may change when
// Exporter/state-sync is implemented.
Expand All @@ -108,10 +115,8 @@ type Iterator interface {
Next() bool
Prev() bool

// Key returns the current key in internal format (valid until next move).
// Internal formats:
// - Storage: addr(20) || slot(32)
// - Nonce/Code/CodeHash: addr(20)
// Key returns the current key in physical format (valid until next move).
// Physical format: "evm/" + type_prefix_byte + stripped_key.
Key() []byte

// Value returns the current value (valid until next move).
Expand Down
31 changes: 16 additions & 15 deletions sei-db/state_db/sc/flatkv/crash_recovery_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/sei-protocol/sei-chain/sei-db/common/evm"
"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/sc/flatkv/ktype"
"github.com/stretchr/testify/require"
)

Expand Down Expand Up @@ -93,7 +94,7 @@ func TestCrashRecoveryGlobalMetadataAheadOfDataDBs(t *testing.T) {

addr := addrN(0x02)
for i := 1; i <= 5; i++ {
key := evm.BuildMemIAVLEVMKey(evm.EVMKeyStorage, StorageKey(addr, slotN(byte(i))))
key := evm.BuildMemIAVLEVMKey(evm.EVMKeyStorage, ktype.StorageKey(addr, slotN(byte(i))))
cs := makeChangeSet(key, padLeft32(byte(i*11)), false)
require.NoError(t, s.ApplyChangeSets([]*proto.NamedChangeSet{cs}))
_, err := s.Commit()
Expand Down Expand Up @@ -121,8 +122,8 @@ func TestCrashRecoveryGlobalMetadataAheadOfDataDBs(t *testing.T) {
verifyLtHashConsistency(t, s2)

for i := 1; i <= 5; i++ {
key := evm.BuildMemIAVLEVMKey(evm.EVMKeyStorage, StorageKey(addr, slotN(byte(i))))
val, found := s2.Get(key)
key := evm.BuildMemIAVLEVMKey(evm.EVMKeyStorage, ktype.StorageKey(addr, slotN(byte(i))))
val, found := s2.Get(evm.EVMStoreKey, key)
require.True(t, found, "slot %d should exist after recovery", i)
require.Equal(t, padLeft32(byte(i*11)), val)
}
Expand All @@ -141,7 +142,7 @@ func TestCrashRecoveryWALReplayLargeGap(t *testing.T) {

addr := addrN(0x03)
for i := 1; i <= 20; i++ {
key := evm.BuildMemIAVLEVMKey(evm.EVMKeyStorage, StorageKey(addr, slotN(byte(i))))
key := evm.BuildMemIAVLEVMKey(evm.EVMKeyStorage, ktype.StorageKey(addr, slotN(byte(i))))
cs := makeChangeSet(key, padLeft32(byte(i)), false)
require.NoError(t, s.ApplyChangeSets([]*proto.NamedChangeSet{cs}))
_, err := s.Commit()
Expand All @@ -163,8 +164,8 @@ func TestCrashRecoveryWALReplayLargeGap(t *testing.T) {

// All 20 storage slots should be readable.
for i := 1; i <= 20; i++ {
key := evm.BuildMemIAVLEVMKey(evm.EVMKeyStorage, StorageKey(addr, slotN(byte(i))))
val, found := s2.Get(key)
key := evm.BuildMemIAVLEVMKey(evm.EVMKeyStorage, ktype.StorageKey(addr, slotN(byte(i))))
val, found := s2.Get(evm.EVMStoreKey, key)
require.True(t, found, "slot %d should exist", i)
require.Equal(t, padLeft32(byte(i)), val)
}
Expand All @@ -181,7 +182,7 @@ func TestCrashRecoveryEmptyWALAfterSnapshot(t *testing.T) {
require.NoError(t, err)

addr := addrN(0x04)
key := evm.BuildMemIAVLEVMKey(evm.EVMKeyStorage, StorageKey(addr, slotN(0x01)))
key := evm.BuildMemIAVLEVMKey(evm.EVMKeyStorage, ktype.StorageKey(addr, slotN(0x01)))
cs := makeChangeSet(key, padLeft32(0xAA), false)
require.NoError(t, s.ApplyChangeSets([]*proto.NamedChangeSet{cs}))
_, err = s.Commit()
Expand All @@ -205,7 +206,7 @@ func TestCrashRecoveryEmptyWALAfterSnapshot(t *testing.T) {
require.Equal(t, expectedVersion, s2.Version())
require.Equal(t, expectedHash, s2.RootHash())

val, found := s2.Get(key)
val, found := s2.Get(evm.EVMStoreKey, key)
require.True(t, found)
require.Equal(t, padLeft32(0xAA), val)

Expand Down Expand Up @@ -234,7 +235,7 @@ func TestCrashRecoveryCorruptedAccountValueInDB(t *testing.T) {

// Corrupt the account value in the DB with invalid-length data.
batch := s.accountDB.NewBatch()
require.NoError(t, batch.Set(AccountKey(addr), []byte{0xDE, 0xAD}))
require.NoError(t, batch.Set(accountPhysKey(addr), []byte{0xDE, 0xAD}))
require.NoError(t, batch.Commit(types.WriteOptions{Sync: true}))
_ = batch.Close()

Expand Down Expand Up @@ -264,7 +265,7 @@ func TestCrashRecoveryCrashAfterWALBeforeDBCommit(t *testing.T) {

addr := addrN(0x06)
slot := slotN(0x01)
key := evm.BuildMemIAVLEVMKey(evm.EVMKeyStorage, StorageKey(addr, slot))
key := evm.BuildMemIAVLEVMKey(evm.EVMKeyStorage, ktype.StorageKey(addr, slot))
cs := makeChangeSet(key, padLeft32(0x11), false)
require.NoError(t, s.ApplyChangeSets([]*proto.NamedChangeSet{cs}))
_, err = s.Commit()
Expand Down Expand Up @@ -298,7 +299,7 @@ func TestCrashRecoveryCrashAfterWALBeforeDBCommit(t *testing.T) {
require.Equal(t, int64(2), s2.Version())
require.NotEqual(t, hashAfterV1, s2.RootHash(), "hash should differ after v2 replay")

val, found := s2.Get(key)
val, found := s2.Get(evm.EVMStoreKey, key)
require.True(t, found)
require.Equal(t, padLeft32(0x22), val, "v2 value should be present after catchup")
verifyLtHashConsistency(t, s2)
Expand All @@ -320,7 +321,7 @@ func TestCrashRecoveryLtHashConsistencyAfterAllPaths(t *testing.T) {
pairs := []*proto.KVPair{
noncePair(addr, uint64(i)),
{
Key: evm.BuildMemIAVLEVMKey(evm.EVMKeyStorage, StorageKey(addr, slotN(byte(i)))),
Key: evm.BuildMemIAVLEVMKey(evm.EVMKeyStorage, ktype.StorageKey(addr, slotN(byte(i)))),
Value: padLeft32(byte(i)),
},
}
Expand Down Expand Up @@ -380,7 +381,7 @@ func TestCrashRecoveryCorruptLtHashBlobInMetadata(t *testing.T) {
require.NoError(t, err)

cs := makeChangeSet(
evm.BuildMemIAVLEVMKey(evm.EVMKeyStorage, StorageKey(addrN(0x01), slotN(0x01))),
evm.BuildMemIAVLEVMKey(evm.EVMKeyStorage, ktype.StorageKey(addrN(0x01), slotN(0x01))),
padLeft32(0x11), false,
)
require.NoError(t, s.ApplyChangeSets([]*proto.NamedChangeSet{cs}))
Expand Down Expand Up @@ -415,7 +416,7 @@ func TestCrashRecoveryCorruptLtHashBlobInPerDBMeta(t *testing.T) {
require.NoError(t, err)

cs := makeChangeSet(
evm.BuildMemIAVLEVMKey(evm.EVMKeyStorage, StorageKey(addrN(0x02), slotN(0x01))),
evm.BuildMemIAVLEVMKey(evm.EVMKeyStorage, ktype.StorageKey(addrN(0x02), slotN(0x01))),
padLeft32(0x22), false,
)
require.NoError(t, s.ApplyChangeSets([]*proto.NamedChangeSet{cs}))
Expand Down Expand Up @@ -450,7 +451,7 @@ func TestCrashRecoveryGlobalVersionOverflow(t *testing.T) {
require.NoError(t, err)

cs := makeChangeSet(
evm.BuildMemIAVLEVMKey(evm.EVMKeyStorage, StorageKey(addrN(0x03), slotN(0x01))),
evm.BuildMemIAVLEVMKey(evm.EVMKeyStorage, ktype.StorageKey(addrN(0x03), slotN(0x01))),
padLeft32(0x33), false,
)
require.NoError(t, s.ApplyChangeSets([]*proto.NamedChangeSet{cs}))
Expand Down
Loading
Loading