Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
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
9 changes: 9 additions & 0 deletions baseapp/baseapp.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,10 @@ type BaseApp struct {
// SAFETY: it's safe to do if validators validate the total gas wanted in the `ProcessProposal`, which is the case in the default handler.
disableBlockGasMeter bool

// skipEndBlocker will skip EndBlocker processing when true, useful for query-only modes
// where EndBlocker operations might block or are unnecessary.
skipEndBlocker bool

// nextBlockDelay is the delay to wait until the next block after ABCI has committed.
// This gives the application more time to receive precommits. This is the same as TimeoutCommit,
// but can now be set from the application. This value defaults to 0, and CometBFT will use the
Expand Down Expand Up @@ -735,6 +739,11 @@ func (app *BaseApp) deliverTx(tx []byte) *abci.ExecTxResult {
func (app *BaseApp) endBlock(_ context.Context) (sdk.EndBlock, error) {
var endblock sdk.EndBlock

if app.skipEndBlocker {
// Skip EndBlocker processing when flag is set
return endblock, nil
}

if app.abciHandlers.EndBlocker != nil {
eb, err := app.abciHandlers.EndBlocker(app.stateManager.GetState(execModeFinalize).Context())
if err != nil {
Expand Down
10 changes: 10 additions & 0 deletions baseapp/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,11 @@ func DisableBlockGasMeter() func(*BaseApp) {
return func(app *BaseApp) { app.SetDisableBlockGasMeter(true) }
}

// SkipEndBlocker skips EndBlocker processing for non-blocking query mode.
func SkipEndBlocker() func(*BaseApp) {
return func(app *BaseApp) { app.SetSkipEndBlocker(true) }
}

func (app *BaseApp) SetName(name string) {
if app.sealed {
panic("SetName() on sealed BaseApp")
Expand Down Expand Up @@ -409,6 +414,11 @@ func (app *BaseApp) SetDisableBlockGasMeter(disableBlockGasMeter bool) {
app.disableBlockGasMeter = disableBlockGasMeter
}

// SetSkipEndBlocker sets the skipEndBlocker flag for the BaseApp.
func (app *BaseApp) SetSkipEndBlocker(skipEndBlocker bool) {
app.skipEndBlocker = skipEndBlocker
}
Comment on lines +427 to +430
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add sealed check for consistency and safety.

The method should include a sealed check like other setter methods in this file to prevent modification after BaseApp initialization.

Apply this diff to add the sealed check:

 // SetSkipEndBlocker sets the skipEndBlocker flag for the BaseApp.
 func (app *BaseApp) SetSkipEndBlocker(skipEndBlocker bool) {
+	if app.sealed {
+		panic("SetSkipEndBlocker() on sealed BaseApp")
+	}
 	app.skipEndBlocker = skipEndBlocker
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// SetSkipEndBlocker sets the skipEndBlocker flag for the BaseApp.
func (app *BaseApp) SetSkipEndBlocker(skipEndBlocker bool) {
app.skipEndBlocker = skipEndBlocker
}
// SetSkipEndBlocker sets the skipEndBlocker flag for the BaseApp.
func (app *BaseApp) SetSkipEndBlocker(skipEndBlocker bool) {
if app.sealed {
panic("SetSkipEndBlocker() on sealed BaseApp")
}
app.skipEndBlocker = skipEndBlocker
}
🤖 Prompt for AI Agents
In baseapp/options.go around lines 417 to 420, the SetSkipEndBlocker method
lacks a sealed check to prevent modifications after BaseApp initialization. Add
a check at the start of the method to verify if the BaseApp is sealed, and if
so, panic or return an error to block further changes. This ensures consistency
and safety like other setter methods in the file.


// SetMsgServiceRouter sets the MsgServiceRouter of a BaseApp.
func (app *BaseApp) SetMsgServiceRouter(msgServiceRouter *MsgServiceRouter) {
app.msgServiceRouter = msgServiceRouter
Expand Down
51 changes: 51 additions & 0 deletions server/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"net"
"os"
"path/filepath"
"reflect"
"runtime/pprof"
"strings"
"time"
Expand Down Expand Up @@ -39,6 +40,7 @@ import (

pruningtypes "cosmossdk.io/store/pruning/types"

"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/codec"
Expand Down Expand Up @@ -79,6 +81,7 @@ const (
FlagDisableIAVLFastNode = "iavl-disable-fastnode"
FlagIAVLSyncPruning = "iavl-sync-pruning"
FlagShutdownGrace = "shutdown-grace"
FlagSkipEndBlocker = "skip-endblocker"

// state sync-related flags

Expand Down Expand Up @@ -631,6 +634,42 @@ func getCtx(svrCtx *Context, block bool) (*errgroup.Group, context.Context) {
return g, ctx
}

// getBaseAppFromApp attempts to extract a BaseApp pointer from various app types
func getBaseAppFromApp(app types.Application) *baseapp.BaseApp {
// Direct cast won't work since Application is an interface that BaseApp doesn't fully implement
// BaseApp doesn't implement RegisterAPIRoutes and other methods required by Application interface

// Try interface method
if appWithBaseApp, ok := app.(interface{ GetBaseApp() *baseapp.BaseApp }); ok {
return appWithBaseApp.GetBaseApp()
}

// Use reflection to find embedded BaseApp
appValue := reflect.ValueOf(app)
if appValue.Kind() == reflect.Ptr {
appValue = appValue.Elem()
}

if appValue.Kind() == reflect.Struct {
// Look for embedded BaseApp field
for i := 0; i < appValue.NumField(); i++ {
field := appValue.Field(i)
fieldType := appValue.Type().Field(i)

// Check if it's an embedded BaseApp
if fieldType.Type == reflect.TypeOf((*baseapp.BaseApp)(nil)) && fieldType.Anonymous {
if field.CanInterface() {
if baseApp, ok := field.Interface().(*baseapp.BaseApp); ok {
return baseApp
}
}
}
}
}

return nil
}

func startApp(svrCtx *Context, appCreator types.AppCreator, opts StartCmdOptions) (app types.Application, cleanupFn func(), err error) {
traceWriter, traceCleanupFn, err := setupTraceWriter(svrCtx)
if err != nil {
Expand All @@ -652,6 +691,17 @@ func startApp(svrCtx *Context, appCreator types.AppCreator, opts StartCmdOptions
app = appCreator(svrCtx.Logger, db, traceWriter, svrCtx.Viper)
}

// Check if skip-endblocker flag is set and configure the app accordingly
if skipEndBlocker := svrCtx.Viper.GetBool(FlagSkipEndBlocker); skipEndBlocker {
baseAppPtr := getBaseAppFromApp(app)
if baseAppPtr != nil {
baseAppPtr.SetSkipEndBlocker(true)
svrCtx.Logger.Info("EndBlocker processing disabled via flag")
} else {
svrCtx.Logger.Warn("Skip EndBlocker flag set but unable to access BaseApp")
}
}

cleanupFn = func() {
traceCleanupFn()
if localErr := app.Close(); localErr != nil {
Expand Down Expand Up @@ -1035,6 +1085,7 @@ func addStartNodeFlags(cmd *cobra.Command, opts StartCmdOptions) {
cmd.Flags().Bool(FlagDisableIAVLFastNode, false, "Disable fast node for IAVL tree")
cmd.Flags().Int(FlagMempoolMaxTxs, mempool.DefaultMaxTx, "Sets MaxTx value for the app-side mempool")
cmd.Flags().Duration(FlagShutdownGrace, 0*time.Second, "On Shutdown, duration to wait for resource clean up")
cmd.Flags().Bool(FlagSkipEndBlocker, false, "Skip EndBlocker processing for non-blocking query mode")

// support old flags name for backwards compatibility
cmd.Flags().SetNormalizeFunc(func(f *pflag.FlagSet, name string) pflag.NormalizedName {
Expand Down
Loading