Skip to content
Merged
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
23 changes: 13 additions & 10 deletions baseapp/abci.go
Original file line number Diff line number Diff line change
Expand Up @@ -871,18 +871,28 @@ func (app *BaseApp) internalFinalizeBlock(ctx context.Context, req *abci.Request
// skipped. This is to support compatibility with proposers injecting vote
// extensions into the proposal, which should not themselves be executed in cases
// where they adhere to the sdk.Tx interface.
func (app *BaseApp) FinalizeBlock(req *abci.RequestFinalizeBlock) (*abci.ResponseFinalizeBlock, error) {
func (app *BaseApp) FinalizeBlock(req *abci.RequestFinalizeBlock) (res *abci.ResponseFinalizeBlock, err error) {
defer func() {
// call the streaming service hooks with the FinalizeBlock messages
for _, streamingListener := range app.streamingManager.ABCIListeners {
if err := streamingListener.ListenFinalizeBlock(app.finalizeBlockState.Context(), *req, *res); err != nil {
app.logger.Error("ListenFinalizeBlock listening hook failed", "height", req.Height, "err", err)
}
}
}()

if app.optimisticExec.Initialized() {
// check if the hash we got is the same as the one we are executing
aborted := app.optimisticExec.AbortIfNeeded(req.Hash)
// Wait for the OE to finish, regardless of whether it was aborted or not
res, err := app.optimisticExec.WaitResult()
res, err = app.optimisticExec.WaitResult()

// only return if we are not aborting
if !aborted {
if res != nil {
res.AppHash = app.workingHash()
}

Comment on lines +883 to +895
Copy link
Contributor

Choose a reason for hiding this comment

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

The logic to handle optimistic execution (OE) seems to be correctly checking if the OE was initialized and aborting if necessary. However, the WaitResult method is called without checking if err is not nil after the OE is aborted. This could potentially lead to a situation where an error from the OE is ignored, and the block execution continues with an invalid state.

if app.optimisticExec.Initialized() {
	aborted := app.optimisticExec.AbortIfNeeded(req.Hash)
	// Wait for the OE to finish, regardless of whether it was aborted or not
	res, err = app.optimisticExec.WaitResult()

	// only return if we are not aborting
	if !aborted {
		if res != nil {
			res.AppHash = app.workingHash()
		}

		return res, err
	}

	// if it was aborted, we need to reset the state
	app.finalizeBlockState = nil
	app.optimisticExec.Reset()
}

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.

Suggested change
if app.optimisticExec.Initialized() {
// check if the hash we got is the same as the one we are executing
aborted := app.optimisticExec.AbortIfNeeded(req.Hash)
// Wait for the OE to finish, regardless of whether it was aborted or not
res, err := app.optimisticExec.WaitResult()
res, err = app.optimisticExec.WaitResult()
// only return if we are not aborting
if !aborted {
if res != nil {
res.AppHash = app.workingHash()
}
if app.optimisticExec.Initialized() {
aborted := app.optimisticExec.AbortIfNeeded(req.Hash)
// Wait for the OE to finish, regardless of whether it was aborted or not
res, err = app.optimisticExec.WaitResult()
// only return if we are not aborting
if !aborted {
if res != nil {
res.AppHash = app.workingHash()
}
return res, err
}
// if it was aborted, we need to reset the state
app.finalizeBlockState = nil
app.optimisticExec.Reset()
}

return res, err
}

Comment on lines 871 to 898
Copy link
Contributor

Choose a reason for hiding this comment

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

📝 NOTE
This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [1-1043]

Overall, the changes made to the FinalizeBlock method and related logic in the BaseApp struct are consistent with the PR objectives. The use of defer to ensure ABCI listeners are executed is a good practice. However, there is an issue with the handling of errors after aborting optimistic execution. This should be addressed to ensure that errors are not ignored and that the application state remains valid.

Expand All @@ -892,18 +902,11 @@ func (app *BaseApp) FinalizeBlock(req *abci.RequestFinalizeBlock) (*abci.Respons
}

// if no OE is running, just run the block (this is either a block replay or a OE that got aborted)
res, err := app.internalFinalizeBlock(context.Background(), req)
res, err = app.internalFinalizeBlock(context.Background(), req)
if res != nil {
res.AppHash = app.workingHash()
}

// call the streaming service hooks with the FinalizeBlock messages
for _, streamingListener := range app.streamingManager.ABCIListeners {
if err := streamingListener.ListenFinalizeBlock(app.finalizeBlockState.Context(), *req, *res); err != nil {
app.logger.Error("ListenFinalizeBlock listening hook failed", "height", req.Height, "err", err)
}
}

return res, err
}

Expand Down