diff --git a/docs/architecture/adr-064-abci-2.0.md b/docs/architecture/adr-064-abci-2.0.md index 9a47180509..98651a43ab 100644 --- a/docs/architecture/adr-064-abci-2.0.md +++ b/docs/architecture/adr-064-abci-2.0.md @@ -5,6 +5,7 @@ * 2023-01-17: Initial Draft (@alexanderbez) * 2023-04-06: Add upgrading section (@alexanderbez) * 2023-04-10: Simplify vote extension state persistence (@alexanderbez) +* 2023-07-07: Revise vote extension state persistence (@alexanderbez) ## Status @@ -279,32 +280,22 @@ decision based on the vote extensions. #### Vote Extension Persistence In certain contexts, it may be useful or necessary for applications to persist -data derived from vote extensions. In order to facilitate this use case, we -propose to allow application developers to manually retrieve the `finalizeState` -context (see [`FinalizeBlock`](#finalizeblock-1) below). Using this context, -state can be directly written to `finalizeState`, which will be used during -`FinalizeBlock` and eventually committed to the application state. Note, since -`ProcessProposal` can timeout and thus require another round of consensus, we -will reset `finalizeState` in the beginning of `ProcessProposal`. +data derived from vote extensions. In order to facilitate this use case, we propose +to allow app developers to define a pre-FinalizeBlock hook which will be called +at the very beginning of `FinalizeBlock`, i.e. before `BeginBlock` (see below). -A `ProcessProposal` handler could look like the following: +Note, we cannot allow applications to directly write to the application state +during `ProcessProposal` because during replay, CometBFT will NOT call `ProcessProposal`, +which would result in an incomplete state view. ```go -func (h MyHandler) ProcessProposalHandler() sdk.ProcessProposalHandler { - return func(ctx sdk.Context, req abci.RequestProcessProposal) abci.ResponseProcessProposal { - for _, txBytes := range req.Txs { - _, err := h.app.ProcessProposalVerifyTx(txBytes) - if err != nil { - return abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_REJECT} - } - } - - fCtx := h.app.GetFinalizeState() - - // Any state changes that occur on the provided fCtx WILL be written to state! - h.myKeeper.SetVoteExtResult(fCtx, ...) +func (a MyApp) PreFinalizeBlockHook(ctx sdk.Context, req.RequestFinalizeBlock) error { + voteExts := GetVoteExtensions(ctx, req.Txs) - return abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT} + // Process and perform some compute on vote extensions, storing any resulting + // state. + if err a.processVoteExtensions(ctx, voteExts); if err != nil { + return err } } ``` @@ -347,9 +338,14 @@ legacy ABCI types, e.g. `LegacyBeginBlockRequest` and `LegacyEndBlockRequest`. O we can come up with new types and names altogether. ```go -func (app *BaseApp) FinalizeBlock(req abci.RequestFinalizeBlock) abci.ResponseFinalizeBlock { - // merge any state changes from ProcessProposal into the FinalizeBlock state - app.MergeProcessProposalState() +func (app *BaseApp) FinalizeBlock(req abci.RequestFinalizeBlock) (*abci.ResponseFinalizeBlock, error) { + ctx := ... + + if app.preFinalizeBlockHook != nil { + if err := app.preFinalizeBlockHook(ctx, req); err != nil { + return nil, err + } + } beginBlockResp := app.beginBlock(ctx, req) appendBlockEventAttr(beginBlockResp.Events, "begin_block")