Skip to content
Draft
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
123 changes: 115 additions & 8 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
stdruntime "runtime"
"slices"
"sort"
"time"

"filippo.io/age"
abci "github.com/cometbft/cometbft/abci/types"
Expand Down Expand Up @@ -43,20 +44,23 @@ import (
ibctm "github.com/cosmos/ibc-go/v10/modules/light-clients/07-tendermint"
memiavlstore "github.com/crypto-org-chain/cronos/store"
"github.com/crypto-org-chain/cronos/v2/client/docs"

// force register the extension json-rpc.
"github.com/crypto-org-chain/cronos/v2/executionbook"
"github.com/crypto-org-chain/cronos/v2/x/cronos"
cronosclient "github.com/crypto-org-chain/cronos/v2/x/cronos/client"
cronoskeeper "github.com/crypto-org-chain/cronos/v2/x/cronos/keeper"
evmhandlers "github.com/crypto-org-chain/cronos/v2/x/cronos/keeper/evmhandlers"
cronosprecompiles "github.com/crypto-org-chain/cronos/v2/x/cronos/keeper/precompiles"
"github.com/crypto-org-chain/cronos/v2/x/cronos/middleware"
// force register the extension json-rpc.
_ "github.com/crypto-org-chain/cronos/v2/x/cronos/rpc"
cronostypes "github.com/crypto-org-chain/cronos/v2/x/cronos/types"
e2ee "github.com/crypto-org-chain/cronos/v2/x/e2ee"
e2eekeeper "github.com/crypto-org-chain/cronos/v2/x/e2ee/keeper"
e2eekeyring "github.com/crypto-org-chain/cronos/v2/x/e2ee/keyring"
e2eetypes "github.com/crypto-org-chain/cronos/v2/x/e2ee/types"
"github.com/ethereum/go-ethereum/common"

// Force-load the tracer engines to trigger registration
"github.com/ethereum/go-ethereum/core/vm"
_ "github.com/ethereum/go-ethereum/eth/tracers/js"
Expand Down Expand Up @@ -314,6 +318,12 @@ type App struct {

CronosKeeper cronoskeeper.Keeper

// preconfer mempool for whitelist management
preconferMempool *executionbook.ExecutionBook

// priority tx service for preconfirmation
priorityTxService *executionbook.PriorityTxService

// the module manager
ModuleManager *module.Manager
BasicModuleManager module.BasicManager
Expand Down Expand Up @@ -376,16 +386,52 @@ func New(

addressCodec := authcodec.NewBech32Codec(sdk.GetConfig().GetBech32AccountAddrPrefix())

// Check if preconfer (priority tx selector) is enabled in app.toml
preconferEnabled := cast.ToBool(appOpts.Get("preconfer.enable"))
preconferWhitelist := cast.ToStringSlice(appOpts.Get("preconfer.whitelist"))

var mpool mempool.Mempool
var preconferMempoolRef *executionbook.ExecutionBook
if maxTxs := cast.ToInt(appOpts.Get(server.FlagMempoolMaxTxs)); maxTxs >= 0 {
// NOTE we use custom transaction decoder that supports the sdk.Tx interface instead of sdk.StdTx
// Setup Mempool and Proposal Handlers
logger.Info("NewPriorityMempool is enabled")
mpool = mempool.NewPriorityMempool(mempool.PriorityNonceMempoolConfig[int64]{
baseMpool := mempool.NewPriorityMempool(mempool.PriorityNonceMempoolConfig[int64]{
TxPriority: mempool.NewDefaultTxPriority(),
SignerExtractor: evmapp.NewEthSignerExtractionAdapter(mempool.NewDefaultSignerExtractionAdapter()),
MaxTx: maxTxs,
})

// Wrap with preconfer ExecutionBook if preconfer is enabled
if preconferEnabled {
logger.Info("Wrapping mempool with executionbook.ExecutionBook for priority transaction support",
"whitelist_enabled", len(preconferWhitelist) > 0,
"whitelist_count", len(preconferWhitelist))
preconferMpool := executionbook.NewExecutionBook(executionbook.ExecutionBookConfig{
BaseMempool: baseMpool,
TxDecoder: txDecoder,
PriorityBoost: executionbook.DefaultPriorityBoost,
Logger: logger,
WhitelistAddresses: preconferWhitelist,
SignerExtractor: evmapp.NewEthSignerExtractionAdapter(mempool.NewDefaultSignerExtractionAdapter()),
})

// Verify and log the mempool configuration
executionbook.LogMempoolConfiguration(preconferMpool, logger)

// Validate that the mempool is correctly configured
if err := executionbook.ValidatePreconferMempool(preconferMpool); err != nil {
logger.Error("Preconfer mempool validation failed", "error", err)
panic(fmt.Sprintf("Invalid preconfer mempool configuration: %v", err))
}
logger.Info("✓ Preconfer mempool validation passed")

mpool = preconferMpool
// Store reference for gRPC service registration later
preconferMempoolRef = preconferMpool
} else {
mpool = baseMpool
}
} else {
logger.Info("NoOpMempool is enabled")
mpool = mempool.NoOpMempool{}
Expand All @@ -394,13 +440,26 @@ func New(
baseAppOptions = append(baseAppOptions, func(app *baseapp.BaseApp) {
app.SetMempool(mpool)

// Re-use the default prepare proposal handler, extend the transaction validation logic
defaultProposalHandler := baseapp.NewDefaultProposalHandlerFast(mpool, app)
defaultProposalHandler.SetTxSelector(NewExtTxSelector(
baseapp.NewDefaultTxSelector(),
txDecoder,
blockProposalHandler.ValidateTransaction,
))

if preconferEnabled {
// Re-use the default prepare proposal handler, extend the transaction validation logic
// with priority transaction support via PriorityTxSelector
// This selector will prioritize transactions marked with PRIORITY: in their memo field
logger.Info("Priority transaction selector (preconfer) enabled")
defaultProposalHandler.SetTxSelector(executionbook.NewPriorityTxSelector(
baseapp.NewDefaultTxSelector(),
txDecoder,
blockProposalHandler.ValidateTransaction,
))
} else {
logger.Info("Priority transaction selector (preconfer) disabled, using default tx selector")
defaultProposalHandler.SetTxSelector(NewExtTxSelector(
baseapp.NewDefaultTxSelector(),
txDecoder,
blockProposalHandler.ValidateTransaction,
))
}

app.SetPrepareProposal(defaultProposalHandler.PrepareProposalHandler())

Expand Down Expand Up @@ -432,6 +491,38 @@ func New(
keys, tkeys, okeys := StoreKeys()

invCheckPeriod := cast.ToUint(appOpts.Get(server.FlagInvCheckPeriod))

// Create priority tx service if preconfer is enabled
var priorityTxServiceRef *executionbook.PriorityTxService
if preconferMempoolRef != nil {
// Get validator address from config (optional)
validatorAddr := cast.ToString(appOpts.Get("preconfer.validator_address"))

// Get preconfirmation timeout from config with default of 30 seconds
preconfirmTimeout := 30 * time.Second
if timeoutStr := cast.ToString(appOpts.Get("preconfer.preconfirm_timeout")); timeoutStr != "" {
if parsedTimeout, err := time.ParseDuration(timeoutStr); err == nil {
preconfirmTimeout = parsedTimeout
logger.Info("Using configured preconfirmation timeout", "timeout", preconfirmTimeout)
} else {
logger.Error("Invalid preconfirm_timeout format, using default",
"configured", timeoutStr,
"default", preconfirmTimeout,
"error", err)
}
}

priorityTxServiceRef = executionbook.NewPriorityTxService(executionbook.PriorityTxServiceConfig{
App: bApp,
Mempool: preconferMempoolRef,
TxDecoder: txDecoder,
Logger: logger.With("module", "priority_tx_service"),
ValidatorAddress: validatorAddr,
PreconfirmTimeout: preconfirmTimeout,
})
logger.Info("Priority transaction service initialized", "preconfirm_timeout", preconfirmTimeout)
}

app := &App{
BaseApp: bApp,
cdc: cdc,
Expand All @@ -445,6 +536,8 @@ func New(
okeys: okeys,
blockProposalHandler: blockProposalHandler,
dummyCheckTx: cast.ToBool(appOpts.Get(FlagUnsafeDummyCheckTx)),
preconferMempool: preconferMempoolRef,
priorityTxService: priorityTxServiceRef,
}

app.SetDisableBlockGasMeter(true)
Expand Down Expand Up @@ -951,6 +1044,20 @@ func New(
}
reflectionv1.RegisterReflectionServiceServer(app.GRPCQueryRouter(), reflectionSvc)

// Register preconfer whitelist gRPC service if preconfer is enabled
if app.preconferMempool != nil {
whitelistServer := executionbook.NewWhitelistGRPCServer(app.preconferMempool)
executionbook.RegisterWhitelistServiceServer(app.GRPCQueryRouter(), whitelistServer)
logger.Info("Preconfer whitelist gRPC service registered")
}

// Register priority tx gRPC service if preconfer is enabled
if app.priorityTxService != nil {
priorityTxServer := executionbook.NewPriorityTxGRPCServer(app.priorityTxService)
executionbook.RegisterPriorityTxServiceServer(app.GRPCQueryRouter(), priorityTxServer)
logger.Info("Priority transaction gRPC service registered")
}

app.sm.RegisterStoreDecoders()

// initialize stores
Expand Down
47 changes: 47 additions & 0 deletions cmd/cronosd/cmd/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,50 @@ var DefaultVersionDBTemplate = `
# Enable defines if the versiondb should be enabled.
enable = {{ .VersionDB.Enable }}
`

type PreconferConfig struct {
// Enable defines if the priority tx selector (preconfirmation) should be enabled.
Enable bool `mapstructure:"enable"`
// ValidatorAddress is the validator address for signing preconfirmations (optional).
ValidatorAddress string `mapstructure:"validator_address"`
// PreconfirmTimeout is the duration before a preconfirmation expires (default: "30s").
// Accepts Go duration format: "10s", "1m", "90s", "1m30s", etc.
PreconfirmTimeout string `mapstructure:"preconfirm_timeout"`
// Whitelist defines the list of Ethereum addresses (0x...) allowed to boost transaction priority.
// If empty, all addresses are allowed to use priority boosting.
// If non-empty, only listed addresses can boost priority.
Whitelist []string `mapstructure:"whitelist"`
}

func DefaultPreconferConfig() PreconferConfig {
return PreconferConfig{
Enable: true, // Enabled by default
ValidatorAddress: "", // Empty by default, optional
PreconfirmTimeout: "30s", // Default 30 seconds
Whitelist: []string{}, // Empty by default, allows all addresses
}
}

var DefaultPreconferTemplate = `
[preconfer]
# Enable defines if the priority transaction selector should be enabled.
# When enabled, transactions with PRIORITY: prefix in memo will be prioritized.
enable = {{ .Preconfer.Enable }}

# Validator address for signing preconfirmations (optional).
# If not set, preconfirmations will still be created but unsigned.
# Format: Bech32 validator address (e.g., cronosvaloper1...)
validator_address = "{{ .Preconfer.ValidatorAddress }}"

# Preconfirmation timeout duration (default: "30s").
# Time before a preconfirmation expires.
# Accepts Go duration format: "10s", "1m", "90s", "1m30s", "2m", etc.
# Valid time units: ns, us (µs), ms, s, m, h
preconfirm_timeout = "{{ .Preconfer.PreconfirmTimeout }}"

# Whitelist defines the list of Ethereum addresses (0x...) allowed to boost transaction priority.
# If empty (default), all addresses are allowed to use priority boosting.
# If non-empty, only listed addresses can boost priority.
# Example: whitelist = ["0x1234567890123456789012345678901234567890", "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd"]
whitelist = [{{ range $i, $addr := .Preconfer.Whitelist }}{{ if $i }}, {{ end }}"{{ $addr }}"{{ end }}]
`
103 changes: 103 additions & 0 deletions cmd/cronosd/cmd/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package cmd

import (
"testing"

"github.com/stretchr/testify/require"
)

func TestDefaultPreconferConfig(t *testing.T) {
config := DefaultPreconferConfig()

// Preconfer should be enabled by default for backward compatibility
require.True(t, config.Enable)
}

func TestPreconferConfigStruct(t *testing.T) {
testCases := []struct {
name string
enable bool
expected bool
}{
{
name: "enabled",
enable: true,
expected: true,
},
{
name: "disabled",
enable: false,
expected: false,
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
config := PreconferConfig{
Enable: tc.enable,
}
require.Equal(t, tc.expected, config.Enable)
})
}
}

func TestPreconferTemplateFormat(t *testing.T) {
// Test that the template is properly formatted
require.Contains(t, DefaultPreconferTemplate, "[preconfer]")
require.Contains(t, DefaultPreconferTemplate, "enable")
require.Contains(t, DefaultPreconferTemplate, "{{ .Preconfer.Enable }}")
}

func TestDefaultVersionDBConfig(t *testing.T) {
config := DefaultVersionDBConfig()

// VersionDB should be disabled by default
require.False(t, config.Enable)
}

func TestVersionDBConfigStruct(t *testing.T) {
testCases := []struct {
name string
enable bool
expected bool
}{
{
name: "enabled",
enable: true,
expected: true,
},
{
name: "disabled",
enable: false,
expected: false,
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
config := VersionDBConfig{
Enable: tc.enable,
}
require.Equal(t, tc.expected, config.Enable)
})
}
}

func TestVersionDBTemplateFormat(t *testing.T) {
// Test that the template is properly formatted
require.Contains(t, DefaultVersionDBTemplate, "[versiondb]")
require.Contains(t, DefaultVersionDBTemplate, "enable")
require.Contains(t, DefaultVersionDBTemplate, "{{ .VersionDB.Enable }}")
}

func TestConfigConsistency(t *testing.T) {
// Verify that default configs are consistent
preconferConfig := DefaultPreconferConfig()
versionDBConfig := DefaultVersionDBConfig()

// Preconfer should be enabled by default (backward compatibility)
require.True(t, preconferConfig.Enable)

// VersionDB should be disabled by default
require.False(t, versionDBConfig.Enable)
}
6 changes: 5 additions & 1 deletion cmd/cronosd/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
memiavlcfg "github.com/crypto-org-chain/cronos/store/config"
"github.com/crypto-org-chain/cronos/v2/app"
"github.com/crypto-org-chain/cronos/v2/cmd/cronosd/opendb"
"github.com/crypto-org-chain/cronos/v2/executionbook"
"github.com/crypto-org-chain/cronos/v2/x/cronos"
e2eecli "github.com/crypto-org-chain/cronos/v2/x/e2ee/client/cli"
ethermintclient "github.com/evmos/ethermint/client"
Expand Down Expand Up @@ -191,6 +192,7 @@ func initRootCmd(
txCommand(),
ethermintclient.KeyCommands(app.DefaultNodeHome),
e2eecli.E2EECommand(),
executionbook.Command(),
)

rootCmd, err := srvflags.AddGlobalFlags(rootCmd)
Expand Down Expand Up @@ -272,6 +274,7 @@ func initAppConfig() (string, interface{}) {

MemIAVL memiavlcfg.MemIAVLConfig `mapstructure:"memiavl"`
VersionDB VersionDBConfig `mapstructure:"versiondb"`
Preconfer PreconferConfig `mapstructure:"preconfer"`
}

tpl, cfg := servercfg.AppConfig("")
Expand All @@ -280,9 +283,10 @@ func initAppConfig() (string, interface{}) {
Config: cfg.(servercfg.Config),
MemIAVL: memiavlcfg.DefaultMemIAVLConfig(),
VersionDB: DefaultVersionDBConfig(),
Preconfer: DefaultPreconferConfig(),
}

return tpl + memiavlcfg.DefaultConfigTemplate + DefaultVersionDBTemplate, customAppConfig
return tpl + memiavlcfg.DefaultConfigTemplate + DefaultVersionDBTemplate + DefaultPreconferTemplate, customAppConfig
}

// newApp creates the application
Expand Down
Loading
Loading