Skip to content

Commit

Permalink
cherry-pick update-posthandler commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Vvaradinov committed Jan 10, 2023
1 parent 5791219 commit 45cf4ed
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 16 deletions.
20 changes: 9 additions & 11 deletions baseapp/baseapp.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,17 @@ import (
"sort"
"strings"

dbm "github.com/cosmos/cosmos-db"
"github.com/cosmos/gogoproto/proto"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto/tmhash"
"github.com/tendermint/tendermint/libs/log"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
dbm "github.com/tendermint/tm-db"
"golang.org/x/exp/maps"

codectypes "github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/store"
storemetrics "github.com/cosmos/cosmos-sdk/store/metrics"
"github.com/cosmos/cosmos-sdk/store/snapshots"
storetypes "github.com/cosmos/cosmos-sdk/store/types"
sdk "github.com/cosmos/cosmos-sdk/types"
Expand Down Expand Up @@ -62,7 +63,7 @@ type BaseApp struct { //nolint: maligned

mempool mempool.Mempool // application side mempool
anteHandler sdk.AnteHandler // ante handler for fee and auth
postHandler sdk.AnteHandler // post handler, optional, e.g. for tips
postHandler sdk.PostHandler // post handler, optional, e.g. for tips
initChainer sdk.InitChainer // initialize state with validators and state blob
beginBlocker sdk.BeginBlocker // logic to run before any txs
processProposal sdk.ProcessProposalHandler // the handler which runs on ABCI ProcessProposal
Expand Down Expand Up @@ -141,7 +142,7 @@ type BaseApp struct { //nolint: maligned

// abciListeners for hooking into the ABCI message processing of the BaseApp
// and exposing the requests and responses to external consumers
abciListeners []ABCIListener
abciListeners []storetypes.ABCIListener
}

// NewBaseApp returns a reference to an initialized BaseApp. It accepts a
Expand All @@ -156,7 +157,7 @@ func NewBaseApp(
logger: logger,
name: name,
db: db,
cms: store.NewCommitMultiStore(db),
cms: store.NewCommitMultiStore(db, logger, storemetrics.NewNoOpMetrics()), // by default we use a no-op metric gather in store
storeLoader: DefaultStoreLoader,
grpcQueryRouter: NewGRPCQueryRouter(),
msgServiceRouter: NewMsgServiceRouter(),
Expand Down Expand Up @@ -716,20 +717,17 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte) (gInfo sdk.GasInfo, re
// and we're in DeliverTx. Note, runMsgs will never return a reference to a
// Result if any single message fails or does not have a registered Handler.
result, err = app.runMsgs(runMsgCtx, msgs, mode)

if err == nil {

// Run optional postHandlers.
//
// Note: If the postHandler fails, we also revert the runMsgs state.
if app.postHandler != nil {
// The runMsgCtx context currently contains events emitted by the ante handler.
// We clear this to correctly order events without duplicates.
// Note that the state is still preserved.
postCtx := runMsgCtx.WithEventManager(sdk.NewEventManager())

newCtx, err := app.postHandler(postCtx, tx, mode == runTxModeSimulate)
// Follow-up Ref: https://github.com/cosmos/cosmos-sdk/pull/13941
newCtx, err := app.postHandler(runMsgCtx, tx, mode == runTxModeSimulate, err == nil)
if err != nil {
return gInfo, nil, nil, priority, err
return gInfo, nil, anteEvents, priority, err
}

result.Events = append(result.Events, newCtx.EventManager().ABCIEvents()...)
Expand Down
42 changes: 40 additions & 2 deletions testutil/mock/types_handler.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

43 changes: 40 additions & 3 deletions types/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,21 @@ type Handler func(ctx Context, msg Msg) (*Result, error)
// If newCtx.IsZero(), ctx is used instead.
type AnteHandler func(ctx Context, tx Tx, simulate bool) (newCtx Context, err error)

// AnteDecorator wraps the next AnteHandler to perform custom pre- and post-processing.
// PostHandler like AnteHandler but it executes after RunMsgs. Runs on success
// or failure and enables use cases like gas refunding.
type PostHandler func(ctx Context, tx Tx, simulate, success bool) (newCtx Context, err error)

// AnteDecorator wraps the next AnteHandler to perform custom pre-processing.
type AnteDecorator interface {
AnteHandle(ctx Context, tx Tx, simulate bool, next AnteHandler) (newCtx Context, err error)
}

// ChainDecorator chains AnteDecorators together with each AnteDecorator
// PostDecorator wraps the next PostHandler to perform custom post-processing.
type PostDecorator interface {
PostHandle(ctx Context, tx Tx, simulate, success bool, next PostHandler) (newCtx Context, err error)
}

// ChainAnteDecorators ChainDecorator chains AnteDecorators together with each AnteDecorator
// wrapping over the decorators further along chain and returns a single AnteHandler.
//
// NOTE: The first element is outermost decorator, while the last element is innermost
Expand Down Expand Up @@ -41,6 +50,29 @@ func ChainAnteDecorators(chain ...AnteDecorator) AnteHandler {
}
}

// ChainPostDecorators chains PostDecorators together with each PostDecorator
// wrapping over the decorators further along chain and returns a single PostHandler.
//
// NOTE: The first element is outermost decorator, while the last element is innermost
// decorator. Decorator ordering is critical since some decorators will expect
// certain checks and updates to be performed (e.g. the Context) before the decorator
// is run. These expectations should be documented clearly in a CONTRACT docline
// in the decorator's godoc.
func ChainPostDecorators(chain ...PostDecorator) PostHandler {
if len(chain) == 0 {
return nil
}

// handle non-terminated decorators chain
if (chain[len(chain)-1] != Terminator{}) {
chain = append(chain, Terminator{})
}

return func(ctx Context, tx Tx, simulate, success bool) (Context, error) {
return chain[0].PostHandle(ctx, tx, simulate, success, ChainPostDecorators(chain[1:]...))
}
}

// Terminator AnteDecorator will get added to the chain to simplify decorator code
// Don't need to check if next == nil further up the chain
//
Expand All @@ -61,7 +93,12 @@ func ChainAnteDecorators(chain ...AnteDecorator) AnteHandler {
// snd \ \ \ /
type Terminator struct{}

// Simply return provided Context and nil error
// AnteHandle returns the provided Context and nil error
func (t Terminator) AnteHandle(ctx Context, _ Tx, _ bool, _ AnteHandler) (Context, error) {
return ctx, nil
}

// PostHandle returns the provided Context and nil error
func (t Terminator) PostHandle(ctx Context, _ Tx, _, _ bool, _ PostHandler) (Context, error) {
return ctx, nil
}
31 changes: 31 additions & 0 deletions types/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,34 @@ func TestChainAnteDecorators(t *testing.T) {
mockAnteDecorator2)(ctx, tx, true)
require.NoError(t, err)
}

func TestChainPostDecorators(t *testing.T) {
// test panic when passing an empty sclice of PostDecorators
require.Nil(t, sdk.ChainPostDecorators([]sdk.PostDecorator{}...))

// Create empty context as well as transaction
ctx := sdk.Context{}
tx := sdk.Tx(nil)

// Create mocks
mockCtrl := gomock.NewController(t)
mockPostDecorator1 := mock.NewMockPostDecorator(mockCtrl)
mockPostDecorator2 := mock.NewMockPostDecorator(mockCtrl)

// Test chaining only one post decorator
mockPostDecorator1.EXPECT().PostHandle(gomock.Eq(ctx), gomock.Eq(tx), true, gomock.Eq(true), gomock.Any()).Times(1)
_, err := sdk.ChainPostDecorators(mockPostDecorator1)(ctx, tx, true, true)
require.NoError(t, err)

// Tests chaining multiple post decorators
mockPostDecorator1.EXPECT().PostHandle(gomock.Eq(ctx), gomock.Eq(tx), true, gomock.Eq(true), gomock.Any()).Times(1)
mockPostDecorator2.EXPECT().PostHandle(gomock.Eq(ctx), gomock.Eq(tx), true, gomock.Eq(true), gomock.Any()).Times(1)
// NOTE: we can't check that mockAnteDecorator2 is passed as the last argument because
// ChainAnteDecorators wraps the decorators into closures, so each decorator is
// receiving a closure.
_, err = sdk.ChainPostDecorators(
mockPostDecorator1,
mockPostDecorator2,
)(ctx, tx, true, true)
require.NoError(t, err)
}

0 comments on commit 45cf4ed

Please sign in to comment.