Closes: #10769 ## Description Solution: - create a `WithBranchedStore ` to handle state snapshot and revert - extract `ConsumeBlockGasMiddleware ` out from `RecoveryTxMiddleware`. - order the middlewares properly. --- ### Author Checklist *All items are required. Please add a note to the item if the item is not applicable and please add links to any relevant follow up issues.* I have... - [ ] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] added `!` to the type prefix if API or client breaking change - [ ] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting)) - [ ] provided a link to the relevant issue or specification - [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules) - [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing) - [ ] added a changelog entry to `CHANGELOG.md` - [ ] included comments for [documenting Go code](https://blog.golang.org/godoc) - [ ] updated the relevant documentation or specification - [ ] reviewed "Files changed" and left comments if necessary - [ ] confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... - [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] confirmed `!` in the type prefix if API or client breaking change - [ ] confirmed all author checklist items have been addressed - [ ] reviewed state machine logic - [ ] reviewed API design and naming - [ ] reviewed documentation is accurate - [ ] reviewed tests and test coverage - [ ] manually tested (if applicable)
118 lines
4.6 KiB
Go
118 lines
4.6 KiB
Go
package middleware
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
|
|
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
|
"github.com/cosmos/cosmos-sdk/types/tx"
|
|
"github.com/cosmos/cosmos-sdk/x/auth/migrations/legacytx"
|
|
)
|
|
|
|
type runMsgsTxHandler struct {
|
|
legacyRouter sdk.Router // router for redirecting legacy Msgs
|
|
msgServiceRouter *MsgServiceRouter // router for redirecting Msg service messages
|
|
}
|
|
|
|
func NewRunMsgsTxHandler(msr *MsgServiceRouter, legacyRouter sdk.Router) tx.Handler {
|
|
return runMsgsTxHandler{
|
|
legacyRouter: legacyRouter,
|
|
msgServiceRouter: msr,
|
|
}
|
|
}
|
|
|
|
var _ tx.Handler = runMsgsTxHandler{}
|
|
|
|
// CheckTx implements tx.Handler.CheckTx method.
|
|
func (txh runMsgsTxHandler) CheckTx(ctx context.Context, req tx.Request, checkReq tx.RequestCheckTx) (tx.Response, tx.ResponseCheckTx, error) {
|
|
// Don't run Msgs during CheckTx.
|
|
return tx.Response{}, tx.ResponseCheckTx{}, nil
|
|
}
|
|
|
|
// DeliverTx implements tx.Handler.DeliverTx method.
|
|
func (txh runMsgsTxHandler) DeliverTx(ctx context.Context, req tx.Request) (tx.Response, error) {
|
|
return txh.runMsgs(sdk.UnwrapSDKContext(ctx), req.Tx.GetMsgs(), req.TxBytes)
|
|
}
|
|
|
|
// SimulateTx implements tx.Handler.SimulateTx method.
|
|
func (txh runMsgsTxHandler) SimulateTx(ctx context.Context, req tx.Request) (tx.Response, error) {
|
|
return txh.runMsgs(sdk.UnwrapSDKContext(ctx), req.Tx.GetMsgs(), req.TxBytes)
|
|
}
|
|
|
|
// runMsgs iterates through a list of messages and executes them with the provided
|
|
// Context and execution mode. Messages will only be executed during simulation
|
|
// and DeliverTx. An error is returned if any single message fails or if a
|
|
// Handler does not exist for a given message route. Otherwise, a reference to a
|
|
// Result is returned. The caller must not commit state if an error is returned.
|
|
func (txh runMsgsTxHandler) runMsgs(sdkCtx sdk.Context, msgs []sdk.Msg, txBytes []byte) (tx.Response, error) {
|
|
// Attempt to execute all messages and only update state if all messages pass
|
|
// 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.
|
|
msgLogs := make(sdk.ABCIMessageLogs, 0, len(msgs))
|
|
events := sdkCtx.EventManager().Events()
|
|
msgResponses := make([]*codectypes.Any, len(msgs))
|
|
|
|
// NOTE: GasWanted is determined by the Gas TxHandler and GasUsed by the GasMeter.
|
|
for i, msg := range msgs {
|
|
var (
|
|
msgResult *sdk.Result
|
|
eventMsgName string // name to use as value in event `message.action`
|
|
err error
|
|
)
|
|
|
|
if handler := txh.msgServiceRouter.Handler(msg); handler != nil {
|
|
// ADR 031 request type routing
|
|
msgResult, err = handler(sdkCtx, msg)
|
|
eventMsgName = sdk.MsgTypeURL(msg)
|
|
} else if legacyMsg, ok := msg.(legacytx.LegacyMsg); ok {
|
|
// legacy sdk.Msg routing
|
|
// Assuming that the app developer has migrated all their Msgs to
|
|
// proto messages and has registered all `Msg services`, then this
|
|
// path should never be called, because all those Msgs should be
|
|
// registered within the `MsgServiceRouter` already.
|
|
msgRoute := legacyMsg.Route()
|
|
eventMsgName = legacyMsg.Type()
|
|
handler := txh.legacyRouter.Route(sdkCtx, msgRoute)
|
|
if handler == nil {
|
|
return tx.Response{}, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized message route: %s; message index: %d", msgRoute, i)
|
|
}
|
|
|
|
msgResult, err = handler(sdkCtx, msg)
|
|
} else {
|
|
return tx.Response{}, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "can't route message %+v", msg)
|
|
}
|
|
|
|
if err != nil {
|
|
return tx.Response{}, sdkerrors.Wrapf(err, "failed to execute message; message index: %d", i)
|
|
}
|
|
|
|
msgEvents := sdk.Events{
|
|
sdk.NewEvent(sdk.EventTypeMessage, sdk.NewAttribute(sdk.AttributeKeyAction, eventMsgName)),
|
|
}
|
|
msgEvents = msgEvents.AppendEvents(msgResult.GetEvents())
|
|
|
|
// append message events, data and logs
|
|
//
|
|
// Note: Each message result's data must be length-prefixed in order to
|
|
// separate each result.
|
|
events = events.AppendEvents(msgEvents)
|
|
|
|
// Each individual sdk.Result has exactly one Msg response. We aggregate here.
|
|
msgResponse := msgResult.MsgResponses[0]
|
|
if msgResponse == nil {
|
|
return tx.Response{}, sdkerrors.ErrLogic.Wrapf("got nil Msg response at index %d for msg %s", i, sdk.MsgTypeURL(msg))
|
|
}
|
|
msgResponses[i] = msgResponse
|
|
msgLogs = append(msgLogs, sdk.NewABCIMessageLog(uint32(i), msgResult.Log, msgEvents))
|
|
}
|
|
|
|
return tx.Response{
|
|
// GasInfo will be populated by the Gas middleware.
|
|
Log: strings.TrimSpace(msgLogs.String()),
|
|
Events: events.ToABCIEvents(),
|
|
MsgResponses: msgResponses,
|
|
}, nil
|
|
}
|