Skip to content
10 changes: 4 additions & 6 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ import (
"github.com/sei-protocol/sei-chain/sei-cosmos/codec"
"github.com/sei-protocol/sei-chain/sei-cosmos/codec/types"
cryptotypes "github.com/sei-protocol/sei-chain/sei-cosmos/crypto/types"
"github.com/sei-protocol/sei-chain/sei-cosmos/server"
"github.com/sei-protocol/sei-chain/sei-cosmos/server/api"
"github.com/sei-protocol/sei-chain/sei-cosmos/server/config"
servertypes "github.com/sei-protocol/sei-chain/sei-cosmos/server/types"
Expand Down Expand Up @@ -118,7 +117,6 @@ import (
gigautils "github.com/sei-protocol/sei-chain/giga/executor/utils"
"github.com/sei-protocol/sei-chain/precompiles"
putils "github.com/sei-protocol/sei-chain/precompiles/utils"
ssconfig "github.com/sei-protocol/sei-chain/sei-db/config"
"github.com/sei-protocol/sei-chain/sei-ibc-go/modules/apps/transfer"
ibctransferkeeper "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/apps/transfer/keeper"
ibctransfertypes "github.com/sei-protocol/sei-chain/sei-ibc-go/modules/apps/transfer/types"
Expand Down Expand Up @@ -678,10 +676,10 @@ func New(
wasmOpts...,
)

receiptStorePath := filepath.Join(homePath, "data", "receipt.db")
receiptConfig := ssconfig.DefaultReceiptStoreConfig()
receiptConfig.DBDirectory = receiptStorePath
receiptConfig.KeepRecent = cast.ToInt(appOpts.Get(server.FlagMinRetainBlocks))
receiptConfig, err := readReceiptStoreConfig(homePath, appOpts)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

on main, the receipt store inherited its pruning window from min-retain-blocks. This branch removes that fallback and defaults receipts to DefaultReceiptStoreConfig().KeepRecent instead. BaseApp still uses min-retain-blocks, so after upgrade the node can retain far more receipt data than before unless receipt-store.keep-recent is manually set. The updated test also locks in this new behavior.

is this expected?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I think the default matches up with DefaultSSKeepRecent (100k) so I don't think it's a big deal if not set.

if err != nil {
panic(fmt.Sprintf("error reading receipt store config: %s", err))
}
if app.receiptStore == nil {
receiptStore, err := receipt.NewReceiptStore(logger, receiptConfig, keys[evmtypes.StoreKey])
if err != nil {
Expand Down
26 changes: 26 additions & 0 deletions app/receipt_store_config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package app

import (
"path/filepath"

seidbconfig "github.com/sei-protocol/sei-chain/sei-db/config"
)

const (
receiptStoreBackendKey = "receipt-store.rs-backend"
receiptStoreDBDirectoryKey = "receipt-store.db-directory"
receiptStoreAsyncWriteBufferKey = "receipt-store.async-write-buffer"
receiptStoreKeepRecentKey = "receipt-store.keep-recent"
receiptStorePruneIntervalSecondsKey = "receipt-store.prune-interval-seconds"
)

func readReceiptStoreConfig(homePath string, appOpts seidbconfig.AppOptions) (seidbconfig.ReceiptStoreConfig, error) {
receiptConfig, err := seidbconfig.ReadReceiptConfig(appOpts)
if err != nil {
return receiptConfig, err
}
if receiptConfig.DBDirectory == "" {
receiptConfig.DBDirectory = filepath.Join(homePath, "data", "receipt.db")
}
return receiptConfig, nil
}
81 changes: 81 additions & 0 deletions app/seidb_test.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
package app

import (
"path/filepath"
"testing"

"github.com/sei-protocol/sei-chain/sei-cosmos/server"
"github.com/sei-protocol/sei-chain/sei-db/config"
"github.com/sei-protocol/sei-chain/sei-db/ledger_db/receipt"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

type TestSeiDBAppOpts struct {
Expand All @@ -13,6 +17,7 @@ type TestSeiDBAppOpts struct {
func (t TestSeiDBAppOpts) Get(s string) interface{} {
defaultSCConfig := config.DefaultStateCommitConfig()
defaultSSConfig := config.DefaultStateStoreConfig()
defaultReceiptConfig := config.DefaultReceiptStoreConfig()
switch s {
case FlagSCEnable:
return defaultSCConfig.Enable
Expand Down Expand Up @@ -46,6 +51,8 @@ func (t TestSeiDBAppOpts) Get(s string) interface{} {
return defaultSSConfig.PruneIntervalSeconds
case FlagSSImportNumWorkers:
return defaultSSConfig.ImportNumWorkers
case receiptStoreBackendKey:
return defaultReceiptConfig.Backend
case FlagEVMSSDirectory:
return defaultSSConfig.EVMDBDirectory
case FlagEVMSSWriteMode:
Expand All @@ -61,8 +68,11 @@ func TestNewDefaultConfig(t *testing.T) {
appOpts := TestSeiDBAppOpts{}
scConfig := parseSCConfigs(appOpts)
ssConfig := parseSSConfigs(appOpts)
receiptConfig, err := config.ReadReceiptConfig(appOpts)
assert.NoError(t, err)
assert.Equal(t, scConfig, config.DefaultStateCommitConfig())
assert.Equal(t, ssConfig, config.DefaultStateStoreConfig())
assert.Equal(t, receiptConfig, config.DefaultReceiptStoreConfig())
}

type mapAppOpts map[string]interface{}
Expand All @@ -85,3 +95,74 @@ func TestParseSCConfigs_HistoricalProofFlags(t *testing.T) {
assert.Equal(t, 12.5, scConfig.HistoricalProofRateLimit)
assert.Equal(t, 3, scConfig.HistoricalProofBurst)
}

func TestParseReceiptConfigs_DefaultsToPebbleWhenUnset(t *testing.T) {
receiptConfig, err := config.ReadReceiptConfig(mapAppOpts{})
assert.NoError(t, err)
assert.Equal(t, config.DefaultReceiptStoreConfig(), receiptConfig)
}

func TestParseReceiptConfigs_UsesConfiguredBackend(t *testing.T) {
receiptConfig, err := config.ReadReceiptConfig(mapAppOpts{
receiptStoreBackendKey: "parquet",
})
assert.NoError(t, err)
assert.Equal(t, "parquet", receiptConfig.Backend)
assert.Equal(t, config.DefaultReceiptStoreConfig().AsyncWriteBuffer, receiptConfig.AsyncWriteBuffer)
assert.Equal(t, config.DefaultReceiptStoreConfig().KeepRecent, receiptConfig.KeepRecent)
}

func TestParseReceiptConfigs_UsesConfiguredValues(t *testing.T) {
receiptConfig, err := config.ReadReceiptConfig(mapAppOpts{
receiptStoreDBDirectoryKey: "/tmp/custom-receipt-db",
receiptStoreBackendKey: "parquet",
receiptStoreAsyncWriteBufferKey: 7,
receiptStoreKeepRecentKey: 42,
receiptStorePruneIntervalSecondsKey: 9,
})
assert.NoError(t, err)
assert.Equal(t, "/tmp/custom-receipt-db", receiptConfig.DBDirectory)
assert.Equal(t, "parquet", receiptConfig.Backend)
assert.Equal(t, 7, receiptConfig.AsyncWriteBuffer)
assert.Equal(t, 42, receiptConfig.KeepRecent)
assert.Equal(t, 9, receiptConfig.PruneIntervalSeconds)
}

func TestParseReceiptConfigs_RejectsInvalidBackend(t *testing.T) {
_, err := config.ReadReceiptConfig(mapAppOpts{
receiptStoreBackendKey: "rocksdb",
})
assert.Error(t, err)
assert.Contains(t, err.Error(), "unsupported receipt-store backend")
assert.Contains(t, err.Error(), "rocksdb")
}

func TestReadReceiptStoreConfigUsesConfiguredValues(t *testing.T) {
homePath := t.TempDir()
receiptConfig, err := readReceiptStoreConfig(homePath, mapAppOpts{
receiptStoreDBDirectoryKey: "/tmp/custom-receipt-db",
receiptStoreKeepRecentKey: 5,
server.FlagMinRetainBlocks: 100,
})
require.NoError(t, err)
assert.Equal(t, "/tmp/custom-receipt-db", receiptConfig.DBDirectory)
assert.Equal(t, 5, receiptConfig.KeepRecent)
}

func TestReadReceiptStoreConfigUsesDefaultDirectoryWhenUnset(t *testing.T) {
homePath := t.TempDir()
receiptConfig, err := readReceiptStoreConfig(homePath, mapAppOpts{})
require.NoError(t, err)
assert.Equal(t, filepath.Join(homePath, "data", "receipt.db"), receiptConfig.DBDirectory)
}

// TestFullAppPathWithParquetReceiptStore exercises the full app.New path with rs-backend = "parquet"
// and asserts the parquet receipt store is actually instantiated (not pebble).
func TestFullAppPathWithParquetReceiptStore(t *testing.T) {
app := SetupWithScReceiptFromOpts(t, false, false, TestAppOpts{
UseSc: true,
ReceiptBackend: "parquet",
})
require.NotNil(t, app.receiptStore, "receipt store should be created")
assert.Equal(t, "parquet", receipt.BackendTypeName(app.receiptStore), "receipt store backend should be parquet")
}
59 changes: 56 additions & 3 deletions app/test_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,10 @@ func (t TestTx) GetGasEstimate() uint64 {
}

type TestAppOpts struct {
UseSc bool
EnableGiga bool
EnableGigaOCC bool
UseSc bool
EnableGiga bool
EnableGigaOCC bool
ReceiptBackend string // e.g. "parquet" to use parquet receipt store; empty = default (pebble)
}

func (t TestAppOpts) Get(s string) interface{} {
Expand All @@ -99,6 +100,9 @@ func (t TestAppOpts) Get(s string) interface{} {
if s == gigaconfig.FlagOCCEnabled {
return t.EnableGigaOCC
}
if s == receiptStoreBackendKey && t.ReceiptBackend != "" {
return t.ReceiptBackend
}
// Disable EVM HTTP and WebSocket servers in tests to avoid port conflicts
// when multiple tests run in parallel (all would try to bind to port 8545)
if s == "evm.http_enabled" || s == "evm.ws_enabled" {
Expand Down Expand Up @@ -472,6 +476,55 @@ func SetupWithDB(tb testing.TB, db dbm.DB, isCheckTx bool, enableEVMCustomPrecom
return res
}

// SetupWithScReceiptFromOpts is like SetupWithSc but does not inject a receipt store via AppOption.
// The receipt store is created inside New() from testAppOpts (e.g. testAppOpts.ReceiptBackend = "parquet").
// Use this to test the full app path with rs-backend from config.
func SetupWithScReceiptFromOpts(t *testing.T, isCheckTx bool, enableEVMCustomPrecompiles bool, testAppOpts TestAppOpts, baseAppOptions ...func(*bam.BaseApp)) (res *App) {
db := dbm.NewMemDB()
encodingConfig := MakeEncodingConfig()
cdc := encodingConfig.Marshaler

res = New(
log.NewNopLogger(),
db,
nil,
true,
map[int64]bool{},
t.TempDir(),
1,
enableEVMCustomPrecompiles,
config.TestConfig(),
encodingConfig,
wasm.EnableAllProposals,
testAppOpts,
EmptyWasmOpts,
nil, // no options: receipt store is created from testAppOpts inside New()
baseAppOptions...,
)
if !isCheckTx {
genesisState := NewDefaultGenesisState(cdc)
stateBytes, err := json.MarshalIndent(genesisState, "", " ")
if err != nil {
panic(err)
}

defer func() { _ = recover() }()

_, err = res.InitChain(
context.Background(), &abci.RequestInitChain{
Validators: []abci.ValidatorUpdate{},
ConsensusParams: DefaultConsensusParams,
AppStateBytes: stateBytes,
},
)
if err != nil {
panic(err)
}
}

return res
}

func SetupWithSc(t *testing.T, isCheckTx bool, enableEVMCustomPrecompiles bool, testAppOpts TestAppOpts, baseAppOptions ...func(*bam.BaseApp)) (res *App) {
db := dbm.NewMemDB()
encodingConfig := MakeEncodingConfig()
Expand Down
26 changes: 14 additions & 12 deletions cmd/seid/cmd/app_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,23 +22,25 @@ type WASMConfig struct {
type CustomAppConfig struct {
srvconfig.Config

StateCommit seidbconfig.StateCommitConfig `mapstructure:"state-commit"`
StateStore seidbconfig.StateStoreConfig `mapstructure:"state-store"`
WASM WASMConfig `mapstructure:"wasm"`
EVM evmrpcconfig.Config `mapstructure:"evm"`
GigaExecutor gigaconfig.Config `mapstructure:"giga_executor"`
ETHReplay replay.Config `mapstructure:"eth_replay"`
ETHBlockTest blocktest.Config `mapstructure:"eth_block_test"`
EvmQuery querier.Config `mapstructure:"evm_query"`
LightInvariance seiapp.LightInvarianceConfig `mapstructure:"light_invariance"`
StateCommit seidbconfig.StateCommitConfig `mapstructure:"state-commit"`
StateStore seidbconfig.StateStoreConfig `mapstructure:"state-store"`
ReceiptStore seidbconfig.ReceiptStoreConfig `mapstructure:"receipt-store"`
WASM WASMConfig `mapstructure:"wasm"`
EVM evmrpcconfig.Config `mapstructure:"evm"`
GigaExecutor gigaconfig.Config `mapstructure:"giga_executor"`
ETHReplay replay.Config `mapstructure:"eth_replay"`
ETHBlockTest blocktest.Config `mapstructure:"eth_block_test"`
EvmQuery querier.Config `mapstructure:"evm_query"`
LightInvariance seiapp.LightInvarianceConfig `mapstructure:"light_invariance"`
}

// NewCustomAppConfig creates a CustomAppConfig with the given base config and EVM config
func NewCustomAppConfig(baseConfig *srvconfig.Config, evmConfig evmrpcconfig.Config) CustomAppConfig {
return CustomAppConfig{
Config: *baseConfig,
StateCommit: seidbconfig.DefaultStateCommitConfig(),
StateStore: seidbconfig.DefaultStateStoreConfig(),
Config: *baseConfig,
StateCommit: seidbconfig.DefaultStateCommitConfig(),
StateStore: seidbconfig.DefaultStateStoreConfig(),
ReceiptStore: seidbconfig.DefaultReceiptStoreConfig(),
WASM: WASMConfig{
QueryGasLimit: 300000,
LruSize: 1,
Expand Down
33 changes: 33 additions & 0 deletions cmd/seid/cmd/init_test.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package cmd

import (
"bytes"
"encoding/json"
"fmt"
"os"
"path/filepath"
"testing"
"text/template"

"github.com/sei-protocol/sei-chain/app"
"github.com/sei-protocol/sei-chain/app/genesis"
Expand Down Expand Up @@ -149,6 +151,7 @@ func TestInitModeConfiguration(t *testing.T) {

// Build custom template with all sections
customAppTemplate := srvconfig.ManualConfigTemplate + seidbconfig.StateCommitConfigTemplate + seidbconfig.StateStoreConfigTemplate +
seidbconfig.ReceiptStoreConfigTemplate +
srvconfig.AutoManagedConfigTemplate // Simplified - just need the pruning config

srvconfig.SetConfigTemplate(customAppTemplate)
Expand All @@ -167,10 +170,40 @@ func TestInitModeConfiguration(t *testing.T) {
if tt.validateApp != nil {
tt.validateApp(t, configDir)
}

v := viper.New()
v.SetConfigFile(appTomlPath)
err = v.ReadInConfig()
require.NoError(t, err)
require.Equal(t, "pebbledb", v.GetString("receipt-store.rs-backend"))
require.Equal(t, "", v.GetString("receipt-store.db-directory"))
require.Equal(t, seidbconfig.DefaultReceiptStoreConfig().AsyncWriteBuffer, v.GetInt("receipt-store.async-write-buffer"))
require.Equal(t, seidbconfig.DefaultReceiptStoreConfig().KeepRecent, v.GetInt("receipt-store.keep-recent"))
require.Equal(t, seidbconfig.DefaultReceiptStoreConfig().PruneIntervalSeconds, v.GetInt("receipt-store.prune-interval-seconds"))
})
}
}

func TestInitAppConfigIncludesReceiptStoreDefaults(t *testing.T) {
customAppTemplate, customAppConfig := initAppConfig()

tmpl, err := template.New("app").Parse(customAppTemplate)
require.NoError(t, err)

var buf bytes.Buffer
err = tmpl.Execute(&buf, customAppConfig)
require.NoError(t, err)

output := buf.String()
require.Contains(t, output, "[receipt-store]")
require.Contains(t, output, `rs-backend = "pebbledb"`)
require.Contains(t, output, `db-directory = ""`)
require.Contains(t, output, "async-write-buffer =")
require.Contains(t, output, "keep-recent =")
require.Contains(t, output, "prune-interval-seconds =")
require.NotContains(t, output, "use-default-comparer")
}

// TestInitModeFlag verifies the mode flag validation
func TestInitModeFlag(t *testing.T) {
validModes := []string{"validator", "full", "seed", "archive"}
Expand Down
1 change: 1 addition & 0 deletions cmd/seid/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,7 @@ func initAppConfig() (string, interface{}) {
customAppTemplate := serverconfig.ManualConfigTemplate +
seidbconfig.StateCommitConfigTemplate +
seidbconfig.StateStoreConfigTemplate +
seidbconfig.ReceiptStoreConfigTemplate +

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

ReceiptStoreConfigTemplate only has one config for rs-backend, what about other configs? Do we want to allow people to configure them as well?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

fixed

evmrpcconfig.ConfigTemplate +
gigaconfig.ConfigTemplate +
serverconfig.AutoManagedConfigTemplate + `
Expand Down
Loading
Loading