Merge PR #5421: Refactor Error Handling
This commit is contained in:
+99
-77
@@ -11,6 +11,7 @@ import (
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
)
|
||||
|
||||
// InitChain implements the ABCI interface. It runs the initialization logic
|
||||
@@ -153,54 +154,66 @@ func (app *BaseApp) EndBlock(req abci.RequestEndBlock) (res abci.ResponseEndBloc
|
||||
return
|
||||
}
|
||||
|
||||
// CheckTx implements the ABCI interface. It runs the "basic checks" to see
|
||||
// whether or not a transaction can possibly be executed, first decoding and then
|
||||
// the ante handler (which checks signatures/fees/ValidateBasic).
|
||||
//
|
||||
// NOTE:CheckTx does not run the actual Msg handler function(s).
|
||||
func (app *BaseApp) CheckTx(req abci.RequestCheckTx) (res abci.ResponseCheckTx) {
|
||||
var result sdk.Result
|
||||
|
||||
// CheckTx implements the ABCI interface and executes a tx in CheckTx mode. In
|
||||
// CheckTx mode, messages are not executed. This means messages are only validated
|
||||
// and only the AnteHandler is executed. State is persisted to the BaseApp's
|
||||
// internal CheckTx state if the AnteHandler passes. Otherwise, the ResponseCheckTx
|
||||
// will contain releveant error information. Regardless of tx execution outcome,
|
||||
// the ResponseCheckTx will contain relevant gas execution context.
|
||||
func (app *BaseApp) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx {
|
||||
tx, err := app.txDecoder(req.Tx)
|
||||
if err != nil {
|
||||
return sdkerrors.ResponseCheckTx(err, 0, 0)
|
||||
}
|
||||
|
||||
var mode runTxMode
|
||||
|
||||
switch {
|
||||
case err != nil:
|
||||
result = err.Result()
|
||||
case req.Type == abci.CheckTxType_New:
|
||||
result = app.runTx(runTxModeCheck, req.Tx, tx)
|
||||
mode = runTxModeCheck
|
||||
|
||||
case req.Type == abci.CheckTxType_Recheck:
|
||||
result = app.runTx(runTxModeReCheck, req.Tx, tx)
|
||||
mode = runTxModeReCheck
|
||||
|
||||
default:
|
||||
panic(fmt.Sprintf("Unknown RequestCheckTx Type: %v", req.Type))
|
||||
panic(fmt.Sprintf("unknown RequestCheckTx type: %s", req.Type))
|
||||
}
|
||||
|
||||
gInfo, result, err := app.runTx(mode, req.Tx, tx)
|
||||
if err != nil {
|
||||
return sdkerrors.ResponseCheckTx(err, gInfo.GasWanted, gInfo.GasUsed)
|
||||
}
|
||||
|
||||
return abci.ResponseCheckTx{
|
||||
Code: uint32(result.Code),
|
||||
Data: result.Data,
|
||||
GasWanted: int64(gInfo.GasWanted), // TODO: Should type accept unsigned ints?
|
||||
GasUsed: int64(gInfo.GasUsed), // TODO: Should type accept unsigned ints?
|
||||
Log: result.Log,
|
||||
GasWanted: int64(result.GasWanted), // TODO: Should type accept unsigned ints?
|
||||
GasUsed: int64(result.GasUsed), // TODO: Should type accept unsigned ints?
|
||||
Data: result.Data,
|
||||
Events: result.Events.ToABCIEvents(),
|
||||
}
|
||||
}
|
||||
|
||||
// DeliverTx implements the ABCI interface.
|
||||
func (app *BaseApp) DeliverTx(req abci.RequestDeliverTx) (res abci.ResponseDeliverTx) {
|
||||
var result sdk.Result
|
||||
|
||||
// DeliverTx implements the ABCI interface and executes a tx in DeliverTx mode.
|
||||
// State only gets persisted if all messages are valid and get executed successfully.
|
||||
// Otherwise, the ResponseDeliverTx will contain releveant error information.
|
||||
// Regardless of tx execution outcome, the ResponseDeliverTx will contain relevant
|
||||
// gas execution context.
|
||||
func (app *BaseApp) DeliverTx(req abci.RequestDeliverTx) abci.ResponseDeliverTx {
|
||||
tx, err := app.txDecoder(req.Tx)
|
||||
if err != nil {
|
||||
result = err.Result()
|
||||
} else {
|
||||
result = app.runTx(runTxModeDeliver, req.Tx, tx)
|
||||
return sdkerrors.ResponseDeliverTx(err, 0, 0)
|
||||
}
|
||||
|
||||
gInfo, result, err := app.runTx(runTxModeDeliver, req.Tx, tx)
|
||||
if err != nil {
|
||||
return sdkerrors.ResponseDeliverTx(err, gInfo.GasWanted, gInfo.GasUsed)
|
||||
}
|
||||
|
||||
return abci.ResponseDeliverTx{
|
||||
Code: uint32(result.Code),
|
||||
Codespace: string(result.Codespace),
|
||||
Data: result.Data,
|
||||
GasWanted: int64(gInfo.GasWanted), // TODO: Should type accept unsigned ints?
|
||||
GasUsed: int64(gInfo.GasUsed), // TODO: Should type accept unsigned ints?
|
||||
Log: result.Log,
|
||||
GasWanted: int64(result.GasWanted), // TODO: Should type accept unsigned ints?
|
||||
GasUsed: int64(result.GasUsed), // TODO: Should type accept unsigned ints?
|
||||
Data: result.Data,
|
||||
Events: result.Events.ToABCIEvents(),
|
||||
}
|
||||
}
|
||||
@@ -278,11 +291,10 @@ func (app *BaseApp) halt() {
|
||||
|
||||
// Query implements the ABCI interface. It delegates to CommitMultiStore if it
|
||||
// implements Queryable.
|
||||
func (app *BaseApp) Query(req abci.RequestQuery) (res abci.ResponseQuery) {
|
||||
func (app *BaseApp) Query(req abci.RequestQuery) abci.ResponseQuery {
|
||||
path := splitPath(req.Path)
|
||||
if len(path) == 0 {
|
||||
msg := "no query path provided"
|
||||
return sdk.ErrUnknownRequest(msg).QueryResult()
|
||||
sdkerrors.QueryResult(sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "no query path provided"))
|
||||
}
|
||||
|
||||
switch path[0] {
|
||||
@@ -294,61 +306,59 @@ func (app *BaseApp) Query(req abci.RequestQuery) (res abci.ResponseQuery) {
|
||||
return handleQueryStore(app, path, req)
|
||||
|
||||
case "p2p":
|
||||
return handleQueryP2P(app, path, req)
|
||||
return handleQueryP2P(app, path)
|
||||
|
||||
case "custom":
|
||||
return handleQueryCustom(app, path, req)
|
||||
}
|
||||
|
||||
msg := "unknown query path"
|
||||
return sdk.ErrUnknownRequest(msg).QueryResult()
|
||||
return sdkerrors.QueryResult(sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "unknown query path"))
|
||||
}
|
||||
|
||||
func handleQueryApp(app *BaseApp, path []string, req abci.RequestQuery) (res abci.ResponseQuery) {
|
||||
func handleQueryApp(app *BaseApp, path []string, req abci.RequestQuery) abci.ResponseQuery {
|
||||
if len(path) >= 2 {
|
||||
var result sdk.Result
|
||||
|
||||
switch path[1] {
|
||||
case "simulate":
|
||||
txBytes := req.Data
|
||||
|
||||
tx, err := app.txDecoder(txBytes)
|
||||
if err != nil {
|
||||
result = err.Result()
|
||||
} else {
|
||||
result = app.Simulate(txBytes, tx)
|
||||
return sdkerrors.QueryResult(sdkerrors.Wrap(err, "failed to decode tx"))
|
||||
}
|
||||
|
||||
gInfo, _, _ := app.Simulate(txBytes, tx)
|
||||
|
||||
return abci.ResponseQuery{
|
||||
Codespace: sdkerrors.RootCodespace,
|
||||
Height: req.Height,
|
||||
Value: codec.Cdc.MustMarshalBinaryLengthPrefixed(gInfo.GasUsed),
|
||||
}
|
||||
|
||||
case "version":
|
||||
return abci.ResponseQuery{
|
||||
Code: uint32(sdk.CodeOK),
|
||||
Codespace: string(sdk.CodespaceRoot),
|
||||
Codespace: sdkerrors.RootCodespace,
|
||||
Height: req.Height,
|
||||
Value: []byte(app.appVersion),
|
||||
}
|
||||
|
||||
default:
|
||||
result = sdk.ErrUnknownRequest(fmt.Sprintf("unknown query: %s", path)).Result()
|
||||
}
|
||||
|
||||
value := codec.Cdc.MustMarshalBinaryLengthPrefixed(result)
|
||||
return abci.ResponseQuery{
|
||||
Code: uint32(sdk.CodeOK),
|
||||
Codespace: string(sdk.CodespaceRoot),
|
||||
Height: req.Height,
|
||||
Value: value,
|
||||
return sdkerrors.QueryResult(sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unknown query: %s", path))
|
||||
}
|
||||
}
|
||||
|
||||
msg := "expected second parameter to be either 'simulate' or 'version', neither was present"
|
||||
return sdk.ErrUnknownRequest(msg).QueryResult()
|
||||
return sdkerrors.QueryResult(
|
||||
sdkerrors.Wrap(
|
||||
sdkerrors.ErrUnknownRequest,
|
||||
"expected second parameter to be either 'simulate' or 'version', neither was present",
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
func handleQueryStore(app *BaseApp, path []string, req abci.RequestQuery) abci.ResponseQuery {
|
||||
// "/store" prefix for store queries
|
||||
queryable, ok := app.cms.(sdk.Queryable)
|
||||
if !ok {
|
||||
msg := "multistore doesn't support queries"
|
||||
return sdk.ErrUnknownRequest(msg).QueryResult()
|
||||
return sdkerrors.QueryResult(sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "multistore doesn't support queries"))
|
||||
}
|
||||
|
||||
req.Path = "/" + strings.Join(path[1:], "/")
|
||||
@@ -359,7 +369,12 @@ func handleQueryStore(app *BaseApp, path []string, req abci.RequestQuery) abci.R
|
||||
}
|
||||
|
||||
if req.Height <= 1 && req.Prove {
|
||||
return sdk.ErrInternal("cannot query with proof when height <= 1; please provide a valid height").QueryResult()
|
||||
return sdkerrors.QueryResult(
|
||||
sdkerrors.Wrap(
|
||||
sdkerrors.ErrInvalidRequest,
|
||||
"cannot query with proof when height <= 1; please provide a valid height",
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
resp := queryable.Query(req)
|
||||
@@ -368,7 +383,7 @@ func handleQueryStore(app *BaseApp, path []string, req abci.RequestQuery) abci.R
|
||||
return resp
|
||||
}
|
||||
|
||||
func handleQueryP2P(app *BaseApp, path []string, _ abci.RequestQuery) (res abci.ResponseQuery) {
|
||||
func handleQueryP2P(app *BaseApp, path []string) abci.ResponseQuery {
|
||||
// "/p2p" prefix for p2p queries
|
||||
if len(path) >= 4 {
|
||||
cmd, typ, arg := path[1], path[2], path[3]
|
||||
@@ -383,28 +398,30 @@ func handleQueryP2P(app *BaseApp, path []string, _ abci.RequestQuery) (res abci.
|
||||
}
|
||||
|
||||
default:
|
||||
msg := "expected second parameter to be 'filter'"
|
||||
return sdk.ErrUnknownRequest(msg).QueryResult()
|
||||
return sdkerrors.QueryResult(sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "expected second parameter to be 'filter'"))
|
||||
}
|
||||
}
|
||||
|
||||
msg := "Expected path is p2p filter <addr|id> <parameter>"
|
||||
return sdk.ErrUnknownRequest(msg).QueryResult()
|
||||
return sdkerrors.QueryResult(
|
||||
sdkerrors.Wrap(
|
||||
sdkerrors.ErrUnknownRequest, "expected path is p2p filter <addr|id> <parameter>",
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
func handleQueryCustom(app *BaseApp, path []string, req abci.RequestQuery) (res abci.ResponseQuery) {
|
||||
func handleQueryCustom(app *BaseApp, path []string, req abci.RequestQuery) abci.ResponseQuery {
|
||||
// path[0] should be "custom" because "/custom" prefix is required for keeper
|
||||
// queries.
|
||||
//
|
||||
// The QueryRouter routes using path[1]. For example, in the path
|
||||
// "custom/gov/proposal", QueryRouter routes using "gov".
|
||||
if len(path) < 2 || path[1] == "" {
|
||||
return sdk.ErrUnknownRequest("No route for custom query specified").QueryResult()
|
||||
return sdkerrors.QueryResult(sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "no route for custom query specified"))
|
||||
}
|
||||
|
||||
querier := app.queryRouter.Route(path[1])
|
||||
if querier == nil {
|
||||
return sdk.ErrUnknownRequest(fmt.Sprintf("no custom querier found for route %s", path[1])).QueryResult()
|
||||
return sdkerrors.QueryResult(sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "no custom querier found for route %s", path[1]))
|
||||
}
|
||||
|
||||
// when a client did not provide a query height, manually inject the latest
|
||||
@@ -413,17 +430,22 @@ func handleQueryCustom(app *BaseApp, path []string, req abci.RequestQuery) (res
|
||||
}
|
||||
|
||||
if req.Height <= 1 && req.Prove {
|
||||
return sdk.ErrInternal("cannot query with proof when height <= 1; please provide a valid height").QueryResult()
|
||||
return sdkerrors.QueryResult(
|
||||
sdkerrors.Wrap(
|
||||
sdkerrors.ErrInvalidRequest,
|
||||
"cannot query with proof when height <= 1; please provide a valid height",
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
cacheMS, err := app.cms.CacheMultiStoreWithVersion(req.Height)
|
||||
if err != nil {
|
||||
return sdk.ErrInternal(
|
||||
fmt.Sprintf(
|
||||
"failed to load state at height %d; %s (latest height: %d)",
|
||||
req.Height, err, app.LastBlockHeight(),
|
||||
return sdkerrors.QueryResult(
|
||||
sdkerrors.Wrapf(
|
||||
sdkerrors.ErrInvalidRequest,
|
||||
"failed to load state at height %d; %s (latest height: %d)", req.Height, err, app.LastBlockHeight(),
|
||||
),
|
||||
).QueryResult()
|
||||
)
|
||||
}
|
||||
|
||||
// cache wrap the commit-multistore for safety
|
||||
@@ -435,18 +457,18 @@ func handleQueryCustom(app *BaseApp, path []string, req abci.RequestQuery) (res
|
||||
//
|
||||
// For example, in the path "custom/gov/proposal/test", the gov querier gets
|
||||
// []string{"proposal", "test"} as the path.
|
||||
resBytes, queryErr := querier(ctx, path[2:], req)
|
||||
if queryErr != nil {
|
||||
resBytes, err := querier(ctx, path[2:], req)
|
||||
if err != nil {
|
||||
space, code, log := sdkerrors.ABCIInfo(err, false)
|
||||
return abci.ResponseQuery{
|
||||
Code: uint32(queryErr.Code()),
|
||||
Codespace: string(queryErr.Codespace()),
|
||||
Code: code,
|
||||
Codespace: space,
|
||||
Log: log,
|
||||
Height: req.Height,
|
||||
Log: queryErr.ABCILog(),
|
||||
}
|
||||
}
|
||||
|
||||
return abci.ResponseQuery{
|
||||
Code: uint32(sdk.CodeOK),
|
||||
Height: req.Height,
|
||||
Value: resBytes,
|
||||
}
|
||||
|
||||
+82
-93
@@ -19,6 +19,7 @@ import (
|
||||
"github.com/cosmos/cosmos-sdk/store"
|
||||
storetypes "github.com/cosmos/cosmos-sdk/store/types"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -446,13 +447,12 @@ func (app *BaseApp) validateHeight(req abci.RequestBeginBlock) error {
|
||||
}
|
||||
|
||||
// validateBasicTxMsgs executes basic validator calls for messages.
|
||||
func validateBasicTxMsgs(msgs []sdk.Msg) sdk.Error {
|
||||
func validateBasicTxMsgs(msgs []sdk.Msg) error {
|
||||
if len(msgs) == 0 {
|
||||
return sdk.ErrUnknownRequest("Tx.GetMsgs() must return at least one message in list")
|
||||
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "must contain at least one message")
|
||||
}
|
||||
|
||||
for _, msg := range msgs {
|
||||
// Validate the Msg.
|
||||
err := msg.ValidateBasic()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -508,11 +508,14 @@ func (app *BaseApp) cacheTxContext(ctx sdk.Context, txBytes []byte) (sdk.Context
|
||||
return ctx.WithMultiStore(msCache), msCache
|
||||
}
|
||||
|
||||
// runTx processes a transaction. The transactions is processed via an
|
||||
// anteHandler. The provided txBytes may be nil in some cases, eg. in tests. For
|
||||
// further details on transaction execution, reference the BaseApp SDK
|
||||
// documentation.
|
||||
func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk.Result) {
|
||||
// runTx processes a transaction within a given execution mode, encoded transaction
|
||||
// bytes, and the decoded transaction itself. All state transitions occur through
|
||||
// a cached Context depending on the mode provided. State only gets persisted
|
||||
// if all messages get executed successfully and the execution mode is DeliverTx.
|
||||
// Note, gas execution info is always returned. A reference to a Result is
|
||||
// returned if the tx does not run out of gas and if all the messages are valid
|
||||
// and execute successfully. An error is returned otherwise.
|
||||
func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (gInfo sdk.GasInfo, result *sdk.Result, err error) {
|
||||
// NOTE: GasWanted should be returned by the AnteHandler. GasUsed is
|
||||
// determined by the GasMeter. We need access to the context to get the gas
|
||||
// meter so we initialize upfront.
|
||||
@@ -523,7 +526,8 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk
|
||||
|
||||
// only run the tx if there is block gas remaining
|
||||
if mode == runTxModeDeliver && ctx.BlockGasMeter().IsOutOfGas() {
|
||||
return sdk.ErrOutOfGas("no block gas left to run tx").Result()
|
||||
gInfo = sdk.GasInfo{GasUsed: ctx.BlockGasMeter().GasConsumed()}
|
||||
return gInfo, nil, sdkerrors.Wrap(sdkerrors.ErrOutOfGas, "no block gas left to run tx")
|
||||
}
|
||||
|
||||
var startingGas uint64
|
||||
@@ -534,20 +538,28 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
switch rType := r.(type) {
|
||||
// TODO: Use ErrOutOfGas instead of ErrorOutOfGas which would allow us
|
||||
// to keep the stracktrace.
|
||||
case sdk.ErrorOutOfGas:
|
||||
log := fmt.Sprintf(
|
||||
"out of gas in location: %v; gasWanted: %d, gasUsed: %d",
|
||||
rType.Descriptor, gasWanted, ctx.GasMeter().GasConsumed(),
|
||||
err = sdkerrors.Wrap(
|
||||
sdkerrors.ErrOutOfGas, fmt.Sprintf(
|
||||
"out of gas in location: %v; gasWanted: %d, gasUsed: %d",
|
||||
rType.Descriptor, gasWanted, ctx.GasMeter().GasConsumed(),
|
||||
),
|
||||
)
|
||||
result = sdk.ErrOutOfGas(log).Result()
|
||||
|
||||
default:
|
||||
log := fmt.Sprintf("recovered: %v\nstack:\n%v", r, string(debug.Stack()))
|
||||
result = sdk.ErrInternal(log).Result()
|
||||
err = sdkerrors.Wrap(
|
||||
sdkerrors.ErrPanic, fmt.Sprintf(
|
||||
"recovered: %v\nstack:\n%v", r, string(debug.Stack()),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
result = nil
|
||||
}
|
||||
|
||||
result.GasWanted = gasWanted
|
||||
result.GasUsed = ctx.GasMeter().GasConsumed()
|
||||
gInfo = sdk.GasInfo{GasWanted: gasWanted, GasUsed: ctx.GasMeter().GasConsumed()}
|
||||
}()
|
||||
|
||||
// If BlockGasMeter() panics it will be caught by the above recover and will
|
||||
@@ -558,8 +570,7 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk
|
||||
defer func() {
|
||||
if mode == runTxModeDeliver {
|
||||
ctx.BlockGasMeter().ConsumeGas(
|
||||
ctx.GasMeter().GasConsumedToLimit(),
|
||||
"block gas meter",
|
||||
ctx.GasMeter().GasConsumedToLimit(), "block gas meter",
|
||||
)
|
||||
|
||||
if ctx.BlockGasMeter().GasConsumed() < startingGas {
|
||||
@@ -568,20 +579,21 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk
|
||||
}
|
||||
}()
|
||||
|
||||
var msgs = tx.GetMsgs()
|
||||
msgs := tx.GetMsgs()
|
||||
if err := validateBasicTxMsgs(msgs); err != nil {
|
||||
return err.Result()
|
||||
gInfo = sdk.GasInfo{GasUsed: ctx.BlockGasMeter().GasConsumed()}
|
||||
return gInfo, nil, err
|
||||
}
|
||||
|
||||
if app.anteHandler != nil {
|
||||
var anteCtx sdk.Context
|
||||
var msCache sdk.CacheMultiStore
|
||||
|
||||
// Cache wrap context before anteHandler call in case it aborts.
|
||||
// Cache wrap context before AnteHandler call in case it aborts.
|
||||
// This is required for both CheckTx and DeliverTx.
|
||||
// Ref: https://github.com/cosmos/cosmos-sdk/issues/2772
|
||||
//
|
||||
// NOTE: Alternatively, we could require that anteHandler ensures that
|
||||
// NOTE: Alternatively, we could require that AnteHandler ensures that
|
||||
// writes do not happen if aborted/failed. This may have some
|
||||
// performance benefits, but it'll be more difficult to get right.
|
||||
anteCtx, msCache = app.cacheTxContext(ctx, txBytes)
|
||||
@@ -589,11 +601,11 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk
|
||||
newCtx, err := app.anteHandler(anteCtx, tx, mode == runTxModeSimulate)
|
||||
if !newCtx.IsZero() {
|
||||
// At this point, newCtx.MultiStore() is cache-wrapped, or something else
|
||||
// replaced by the ante handler. We want the original multistore, not one
|
||||
// which was cache-wrapped for the ante handler.
|
||||
// replaced by the AnteHandler. We want the original multistore, not one
|
||||
// which was cache-wrapped for the AnteHandler.
|
||||
//
|
||||
// Also, in the case of the tx aborting, we need to track gas consumed via
|
||||
// the instantiated gas meter in the ante handler, so we update the context
|
||||
// the instantiated gas meter in the AnteHandler, so we update the context
|
||||
// prior to returning.
|
||||
ctx = newCtx.WithMultiStore(ms)
|
||||
}
|
||||
@@ -602,10 +614,7 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk
|
||||
gasWanted = ctx.GasMeter().Limit()
|
||||
|
||||
if err != nil {
|
||||
res := sdk.ResultFromError(err)
|
||||
res.GasWanted = gasWanted
|
||||
res.GasUsed = ctx.GasMeter().GasConsumed()
|
||||
return res
|
||||
return gInfo, nil, err
|
||||
}
|
||||
|
||||
msCache.Write()
|
||||
@@ -615,83 +624,63 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk
|
||||
// MultiStore in case message processing fails. At this point, the MultiStore
|
||||
// is doubly cached-wrapped.
|
||||
runMsgCtx, msCache := app.cacheTxContext(ctx, txBytes)
|
||||
result = app.runMsgs(runMsgCtx, msgs, mode)
|
||||
result.GasWanted = gasWanted
|
||||
|
||||
// Safety check: don't write the cache state unless we're in DeliverTx.
|
||||
if mode != runTxModeDeliver {
|
||||
return result
|
||||
}
|
||||
|
||||
// only update state if all messages pass
|
||||
if result.IsOK() {
|
||||
// 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.
|
||||
result, err = app.runMsgs(runMsgCtx, msgs, mode)
|
||||
if err == nil && mode == runTxModeDeliver {
|
||||
msCache.Write()
|
||||
}
|
||||
|
||||
return result
|
||||
return gInfo, result, err
|
||||
}
|
||||
|
||||
// runMsgs iterates through all the messages and executes them.
|
||||
func (app *BaseApp) runMsgs(ctx sdk.Context, msgs []sdk.Msg, mode runTxMode) (result sdk.Result) {
|
||||
// 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 (app *BaseApp) runMsgs(ctx sdk.Context, msgs []sdk.Msg, mode runTxMode) (*sdk.Result, error) {
|
||||
msgLogs := make(sdk.ABCIMessageLogs, 0, len(msgs))
|
||||
|
||||
data := make([]byte, 0, len(msgs))
|
||||
var (
|
||||
code sdk.CodeType
|
||||
codespace sdk.CodespaceType
|
||||
)
|
||||
|
||||
events := sdk.EmptyEvents()
|
||||
|
||||
// NOTE: GasWanted is determined by ante handler and GasUsed by the GasMeter.
|
||||
// NOTE: GasWanted is determined by the AnteHandler and GasUsed by the GasMeter.
|
||||
for i, msg := range msgs {
|
||||
// match message route
|
||||
msgRoute := msg.Route()
|
||||
handler := app.router.Route(msgRoute)
|
||||
if handler == nil {
|
||||
return sdk.ErrUnknownRequest("unrecognized message type: " + msgRoute).Result()
|
||||
}
|
||||
|
||||
var msgResult sdk.Result
|
||||
|
||||
// skip actual execution for CheckTx and ReCheckTx mode
|
||||
if mode != runTxModeCheck && mode != runTxModeReCheck {
|
||||
msgResult = handler(ctx, msg)
|
||||
}
|
||||
|
||||
// Each message result's Data must be length prefixed in order to separate
|
||||
// each result.
|
||||
data = append(data, msgResult.Data...)
|
||||
|
||||
msgEvents := msgResult.Events
|
||||
|
||||
// append events from the message's execution and a message action event
|
||||
msgEvents = msgEvents.AppendEvent(
|
||||
sdk.NewEvent(sdk.EventTypeMessage, sdk.NewAttribute(sdk.AttributeKeyAction, msg.Type())),
|
||||
)
|
||||
|
||||
events = events.AppendEvents(msgEvents)
|
||||
|
||||
// stop execution and return on first failed message
|
||||
if !msgResult.IsOK() {
|
||||
msgLogs = append(msgLogs, sdk.NewABCIMessageLog(uint16(i), false, msgResult.Log, msgEvents))
|
||||
|
||||
code = msgResult.Code
|
||||
codespace = msgResult.Codespace
|
||||
// skip actual execution for (Re)CheckTx mode
|
||||
if mode == runTxModeCheck || mode == runTxModeReCheck {
|
||||
break
|
||||
}
|
||||
|
||||
msgLogs = append(msgLogs, sdk.NewABCIMessageLog(uint16(i), true, msgResult.Log, msgEvents))
|
||||
msgRoute := msg.Route()
|
||||
handler := app.router.Route(msgRoute)
|
||||
if handler == nil {
|
||||
return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized message route: %s; message index: %d", msgRoute, i)
|
||||
}
|
||||
|
||||
msgResult, err := handler(ctx, msg)
|
||||
if err != nil {
|
||||
return nil, sdkerrors.Wrapf(err, "failed to execute message; message index: %d", i)
|
||||
}
|
||||
|
||||
msgEvents := sdk.Events{
|
||||
sdk.NewEvent(sdk.EventTypeMessage, sdk.NewAttribute(sdk.AttributeKeyAction, msg.Type())),
|
||||
}
|
||||
msgEvents = msgEvents.AppendEvents(msgResult.Events)
|
||||
|
||||
// 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)
|
||||
data = append(data, msgResult.Data...)
|
||||
msgLogs = append(msgLogs, sdk.NewABCIMessageLog(uint16(i), msgResult.Log, msgEvents))
|
||||
}
|
||||
|
||||
result = sdk.Result{
|
||||
Code: code,
|
||||
Codespace: codespace,
|
||||
Data: data,
|
||||
Log: strings.TrimSpace(msgLogs.String()),
|
||||
GasUsed: ctx.GasMeter().GasConsumed(),
|
||||
Events: events,
|
||||
}
|
||||
|
||||
return result
|
||||
return &sdk.Result{
|
||||
Data: data,
|
||||
Log: strings.TrimSpace(msgLogs.String()),
|
||||
Events: events,
|
||||
}, nil
|
||||
}
|
||||
|
||||
+121
-82
@@ -514,8 +514,8 @@ func (tx *txTest) setFailOnHandler(fail bool) {
|
||||
}
|
||||
|
||||
// Implements Tx
|
||||
func (tx txTest) GetMsgs() []sdk.Msg { return tx.Msgs }
|
||||
func (tx txTest) ValidateBasic() sdk.Error { return nil }
|
||||
func (tx txTest) GetMsgs() []sdk.Msg { return tx.Msgs }
|
||||
func (tx txTest) ValidateBasic() error { return nil }
|
||||
|
||||
const (
|
||||
routeMsgCounter = "msgCounter"
|
||||
@@ -534,19 +534,20 @@ func (msg msgCounter) Route() string { return routeMsgCounter }
|
||||
func (msg msgCounter) Type() string { return "counter1" }
|
||||
func (msg msgCounter) GetSignBytes() []byte { return nil }
|
||||
func (msg msgCounter) GetSigners() []sdk.AccAddress { return nil }
|
||||
func (msg msgCounter) ValidateBasic() sdk.Error {
|
||||
func (msg msgCounter) ValidateBasic() error {
|
||||
if msg.Counter >= 0 {
|
||||
return nil
|
||||
}
|
||||
return sdk.ErrInvalidSequence("counter should be a non-negative integer.")
|
||||
return sdkerrors.Wrap(sdkerrors.ErrInvalidSequence, "counter should be a non-negative integer")
|
||||
}
|
||||
|
||||
func newTxCounter(txInt int64, msgInts ...int64) *txTest {
|
||||
msgs := make([]sdk.Msg, 0, len(msgInts))
|
||||
for _, msgInt := range msgInts {
|
||||
msgs = append(msgs, msgCounter{msgInt, false})
|
||||
func newTxCounter(counter int64, msgCounters ...int64) *txTest {
|
||||
msgs := make([]sdk.Msg, 0, len(msgCounters))
|
||||
for _, c := range msgCounters {
|
||||
msgs = append(msgs, msgCounter{c, false})
|
||||
}
|
||||
return &txTest{msgs, txInt, false}
|
||||
|
||||
return &txTest{msgs, counter, false}
|
||||
}
|
||||
|
||||
// a msg we dont know how to route
|
||||
@@ -573,24 +574,26 @@ func (msg msgCounter2) Route() string { return routeMsgCounter2 }
|
||||
func (msg msgCounter2) Type() string { return "counter2" }
|
||||
func (msg msgCounter2) GetSignBytes() []byte { return nil }
|
||||
func (msg msgCounter2) GetSigners() []sdk.AccAddress { return nil }
|
||||
func (msg msgCounter2) ValidateBasic() sdk.Error {
|
||||
func (msg msgCounter2) ValidateBasic() error {
|
||||
if msg.Counter >= 0 {
|
||||
return nil
|
||||
}
|
||||
return sdk.ErrInvalidSequence("counter should be a non-negative integer.")
|
||||
return sdkerrors.Wrap(sdkerrors.ErrInvalidSequence, "counter should be a non-negative integer")
|
||||
}
|
||||
|
||||
// amino decode
|
||||
func testTxDecoder(cdc *codec.Codec) sdk.TxDecoder {
|
||||
return func(txBytes []byte) (sdk.Tx, sdk.Error) {
|
||||
return func(txBytes []byte) (sdk.Tx, error) {
|
||||
var tx txTest
|
||||
if len(txBytes) == 0 {
|
||||
return nil, sdk.ErrTxDecode("txBytes are empty")
|
||||
return nil, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "tx bytes are empty")
|
||||
}
|
||||
|
||||
err := cdc.UnmarshalBinaryLengthPrefixed(txBytes, &tx)
|
||||
if err != nil {
|
||||
return nil, sdk.ErrTxDecode("").TraceSDK(err.Error())
|
||||
return nil, sdkerrors.ErrTxDecode
|
||||
}
|
||||
|
||||
return tx, nil
|
||||
}
|
||||
}
|
||||
@@ -604,25 +607,28 @@ func anteHandlerTxTest(t *testing.T, capKey sdk.StoreKey, storeKey []byte) sdk.A
|
||||
return newCtx, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "ante handler failure")
|
||||
}
|
||||
|
||||
res := incrementingCounter(t, store, storeKey, txTest.Counter)
|
||||
if !res.IsOK() {
|
||||
err = sdkerrors.ABCIError(string(res.Codespace), uint32(res.Code), res.Log)
|
||||
_, err = incrementingCounter(t, store, storeKey, txTest.Counter)
|
||||
if err != nil {
|
||||
return newCtx, err
|
||||
}
|
||||
return
|
||||
|
||||
return newCtx, nil
|
||||
}
|
||||
}
|
||||
|
||||
func handlerMsgCounter(t *testing.T, capKey sdk.StoreKey, deliverKey []byte) sdk.Handler {
|
||||
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
|
||||
return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) {
|
||||
store := ctx.KVStore(capKey)
|
||||
var msgCount int64
|
||||
|
||||
switch m := msg.(type) {
|
||||
case *msgCounter:
|
||||
if m.FailOnHandler {
|
||||
return sdk.ErrInternal("message handler failure").Result()
|
||||
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "message handler failure")
|
||||
}
|
||||
|
||||
msgCount = m.Counter
|
||||
|
||||
case *msgCounter2:
|
||||
msgCount = m.Counter
|
||||
}
|
||||
@@ -651,11 +657,11 @@ func setIntOnStore(store sdk.KVStore, key []byte, i int64) {
|
||||
|
||||
// check counter matches what's in store.
|
||||
// increment and store
|
||||
func incrementingCounter(t *testing.T, store sdk.KVStore, counterKey []byte, counter int64) (res sdk.Result) {
|
||||
func incrementingCounter(t *testing.T, store sdk.KVStore, counterKey []byte, counter int64) (*sdk.Result, error) {
|
||||
storedCounter := getIntFromStore(store, counterKey)
|
||||
require.Equal(t, storedCounter, counter)
|
||||
setIntOnStore(store, counterKey, counter+1)
|
||||
return
|
||||
return &sdk.Result{}, nil
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
@@ -675,7 +681,9 @@ func TestCheckTx(t *testing.T) {
|
||||
anteOpt := func(bapp *BaseApp) { bapp.SetAnteHandler(anteHandlerTxTest(t, capKey1, counterKey)) }
|
||||
routerOpt := func(bapp *BaseApp) {
|
||||
// TODO: can remove this once CheckTx doesnt process msgs.
|
||||
bapp.Router().AddRoute(routeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) sdk.Result { return sdk.Result{} })
|
||||
bapp.Router().AddRoute(routeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) {
|
||||
return &sdk.Result{}, nil
|
||||
})
|
||||
}
|
||||
|
||||
app := setupBaseApp(t, anteOpt, routerOpt)
|
||||
@@ -847,9 +855,9 @@ func TestSimulateTx(t *testing.T) {
|
||||
}
|
||||
|
||||
routerOpt := func(bapp *BaseApp) {
|
||||
bapp.Router().AddRoute(routeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
|
||||
bapp.Router().AddRoute(routeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) {
|
||||
ctx.GasMeter().ConsumeGas(gasConsumed, "test")
|
||||
return sdk.Result{GasUsed: ctx.GasMeter().GasConsumed()}
|
||||
return &sdk.Result{}, nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -872,14 +880,16 @@ func TestSimulateTx(t *testing.T) {
|
||||
require.Nil(t, err)
|
||||
|
||||
// simulate a message, check gas reported
|
||||
result := app.Simulate(txBytes, tx)
|
||||
require.True(t, result.IsOK(), result.Log)
|
||||
require.Equal(t, gasConsumed, result.GasUsed)
|
||||
gInfo, result, err := app.Simulate(txBytes, tx)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, result)
|
||||
require.Equal(t, gasConsumed, gInfo.GasUsed)
|
||||
|
||||
// simulate again, same result
|
||||
result = app.Simulate(txBytes, tx)
|
||||
require.True(t, result.IsOK(), result.Log)
|
||||
require.Equal(t, gasConsumed, result.GasUsed)
|
||||
gInfo, result, err = app.Simulate(txBytes, tx)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, result)
|
||||
require.Equal(t, gasConsumed, gInfo.GasUsed)
|
||||
|
||||
// simulate by calling Query with encoded tx
|
||||
query := abci.RequestQuery{
|
||||
@@ -889,11 +899,10 @@ func TestSimulateTx(t *testing.T) {
|
||||
queryResult := app.Query(query)
|
||||
require.True(t, queryResult.IsOK(), queryResult.Log)
|
||||
|
||||
var res sdk.Result
|
||||
codec.Cdc.MustUnmarshalBinaryLengthPrefixed(queryResult.Value, &res)
|
||||
require.Nil(t, err, "Result unmarshalling failed")
|
||||
require.True(t, res.IsOK(), res.Log)
|
||||
require.Equal(t, gasConsumed, res.GasUsed, res.Log)
|
||||
var res uint64
|
||||
err = codec.Cdc.UnmarshalBinaryLengthPrefixed(queryResult.Value, &res)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, gasConsumed, res)
|
||||
app.EndBlock(abci.RequestEndBlock{})
|
||||
app.Commit()
|
||||
}
|
||||
@@ -906,7 +915,9 @@ func TestRunInvalidTransaction(t *testing.T) {
|
||||
})
|
||||
}
|
||||
routerOpt := func(bapp *BaseApp) {
|
||||
bapp.Router().AddRoute(routeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) (res sdk.Result) { return })
|
||||
bapp.Router().AddRoute(routeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) {
|
||||
return &sdk.Result{}, nil
|
||||
})
|
||||
}
|
||||
|
||||
app := setupBaseApp(t, anteOpt, routerOpt)
|
||||
@@ -914,15 +925,19 @@ func TestRunInvalidTransaction(t *testing.T) {
|
||||
header := abci.Header{Height: 1}
|
||||
app.BeginBlock(abci.RequestBeginBlock{Header: header})
|
||||
|
||||
// Transaction with no messages
|
||||
// transaction with no messages
|
||||
{
|
||||
emptyTx := &txTest{}
|
||||
err := app.Deliver(emptyTx)
|
||||
require.EqualValues(t, sdk.CodeUnknownRequest, err.Code)
|
||||
require.EqualValues(t, sdk.CodespaceRoot, err.Codespace)
|
||||
_, result, err := app.Deliver(emptyTx)
|
||||
require.Error(t, err)
|
||||
require.Nil(t, result)
|
||||
|
||||
space, code, _ := sdkerrors.ABCIInfo(err, false)
|
||||
require.EqualValues(t, sdkerrors.ErrInvalidRequest.Codespace(), space, err)
|
||||
require.EqualValues(t, sdkerrors.ErrInvalidRequest.ABCICode(), code, err)
|
||||
}
|
||||
|
||||
// Transaction where ValidateBasic fails
|
||||
// transaction where ValidateBasic fails
|
||||
{
|
||||
testCases := []struct {
|
||||
tx *txTest
|
||||
@@ -940,27 +955,39 @@ func TestRunInvalidTransaction(t *testing.T) {
|
||||
|
||||
for _, testCase := range testCases {
|
||||
tx := testCase.tx
|
||||
res := app.Deliver(tx)
|
||||
_, result, err := app.Deliver(tx)
|
||||
|
||||
if testCase.fail {
|
||||
require.EqualValues(t, sdk.CodeInvalidSequence, res.Code)
|
||||
require.EqualValues(t, sdk.CodespaceRoot, res.Codespace)
|
||||
require.Error(t, err)
|
||||
|
||||
space, code, _ := sdkerrors.ABCIInfo(err, false)
|
||||
require.EqualValues(t, sdkerrors.ErrInvalidSequence.Codespace(), space, err)
|
||||
require.EqualValues(t, sdkerrors.ErrInvalidSequence.ABCICode(), code, err)
|
||||
} else {
|
||||
require.True(t, res.IsOK(), fmt.Sprintf("%v", res))
|
||||
require.NotNil(t, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Transaction with no known route
|
||||
// transaction with no known route
|
||||
{
|
||||
unknownRouteTx := txTest{[]sdk.Msg{msgNoRoute{}}, 0, false}
|
||||
err := app.Deliver(unknownRouteTx)
|
||||
require.EqualValues(t, sdk.CodeUnknownRequest, err.Code)
|
||||
require.EqualValues(t, sdk.CodespaceRoot, err.Codespace)
|
||||
_, result, err := app.Deliver(unknownRouteTx)
|
||||
require.Error(t, err)
|
||||
require.Nil(t, result)
|
||||
|
||||
space, code, _ := sdkerrors.ABCIInfo(err, false)
|
||||
require.EqualValues(t, sdkerrors.ErrUnknownRequest.Codespace(), space, err)
|
||||
require.EqualValues(t, sdkerrors.ErrUnknownRequest.ABCICode(), code, err)
|
||||
|
||||
unknownRouteTx = txTest{[]sdk.Msg{msgCounter{}, msgNoRoute{}}, 0, false}
|
||||
err = app.Deliver(unknownRouteTx)
|
||||
require.EqualValues(t, sdk.CodeUnknownRequest, err.Code)
|
||||
require.EqualValues(t, sdk.CodespaceRoot, err.Codespace)
|
||||
_, result, err = app.Deliver(unknownRouteTx)
|
||||
require.Error(t, err)
|
||||
require.Nil(t, result)
|
||||
|
||||
space, code, _ = sdkerrors.ABCIInfo(err, false)
|
||||
require.EqualValues(t, sdkerrors.ErrUnknownRequest.Codespace(), space, err)
|
||||
require.EqualValues(t, sdkerrors.ErrUnknownRequest.ABCICode(), code, err)
|
||||
}
|
||||
|
||||
// Transaction with an unregistered message
|
||||
@@ -975,9 +1002,10 @@ func TestRunInvalidTransaction(t *testing.T) {
|
||||
|
||||
txBytes, err := newCdc.MarshalBinaryLengthPrefixed(tx)
|
||||
require.NoError(t, err)
|
||||
|
||||
res := app.DeliverTx(abci.RequestDeliverTx{Tx: txBytes})
|
||||
require.EqualValues(t, sdk.CodeTxDecode, res.Code)
|
||||
require.EqualValues(t, sdk.CodespaceRoot, res.Codespace)
|
||||
require.EqualValues(t, sdkerrors.ErrTxDecode.ABCICode(), res.Code)
|
||||
require.EqualValues(t, sdkerrors.ErrTxDecode.Codespace(), res.Codespace)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -996,8 +1024,7 @@ func TestTxGasLimits(t *testing.T) {
|
||||
if r := recover(); r != nil {
|
||||
switch rType := r.(type) {
|
||||
case sdk.ErrorOutOfGas:
|
||||
log := fmt.Sprintf("out of gas in location: %v", rType.Descriptor)
|
||||
err = sdkerrors.Wrap(sdkerrors.ErrOutOfGas, log)
|
||||
err = sdkerrors.Wrapf(sdkerrors.ErrOutOfGas, "out of gas in location: %v", rType.Descriptor)
|
||||
default:
|
||||
panic(r)
|
||||
}
|
||||
@@ -1007,16 +1034,16 @@ func TestTxGasLimits(t *testing.T) {
|
||||
count := tx.(*txTest).Counter
|
||||
newCtx.GasMeter().ConsumeGas(uint64(count), "counter-ante")
|
||||
|
||||
return
|
||||
return newCtx, nil
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
routerOpt := func(bapp *BaseApp) {
|
||||
bapp.Router().AddRoute(routeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
|
||||
bapp.Router().AddRoute(routeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) {
|
||||
count := msg.(msgCounter).Counter
|
||||
ctx.GasMeter().ConsumeGas(uint64(count), "counter-handler")
|
||||
return sdk.Result{}
|
||||
return &sdk.Result{}, nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1051,17 +1078,21 @@ func TestTxGasLimits(t *testing.T) {
|
||||
|
||||
for i, tc := range testCases {
|
||||
tx := tc.tx
|
||||
res := app.Deliver(tx)
|
||||
gInfo, result, err := app.Deliver(tx)
|
||||
|
||||
// check gas used and wanted
|
||||
require.Equal(t, tc.gasUsed, res.GasUsed, fmt.Sprintf("%d: %v, %v", i, tc, res))
|
||||
require.Equal(t, tc.gasUsed, gInfo.GasUsed, fmt.Sprintf("tc #%d; gas: %v, result: %v, err: %s", i, gInfo, result, err))
|
||||
|
||||
// check for out of gas
|
||||
if !tc.fail {
|
||||
require.True(t, res.IsOK(), fmt.Sprintf("%d: %v, %v", i, tc, res))
|
||||
require.NotNil(t, result, fmt.Sprintf("%d: %v, %v", i, tc, err))
|
||||
} else {
|
||||
require.Equal(t, sdk.CodeOutOfGas, res.Code, fmt.Sprintf("%d: %v, %v", i, tc, res))
|
||||
require.Equal(t, sdk.CodespaceRoot, res.Codespace)
|
||||
require.Error(t, err)
|
||||
require.Nil(t, result)
|
||||
|
||||
space, code, _ := sdkerrors.ABCIInfo(err, false)
|
||||
require.EqualValues(t, sdkerrors.ErrOutOfGas.Codespace(), space, err)
|
||||
require.EqualValues(t, sdkerrors.ErrOutOfGas.ABCICode(), code, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1093,10 +1124,10 @@ func TestMaxBlockGasLimits(t *testing.T) {
|
||||
}
|
||||
|
||||
routerOpt := func(bapp *BaseApp) {
|
||||
bapp.Router().AddRoute(routeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
|
||||
bapp.Router().AddRoute(routeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) {
|
||||
count := msg.(msgCounter).Counter
|
||||
ctx.GasMeter().ConsumeGas(uint64(count), "counter-handler")
|
||||
return sdk.Result{}
|
||||
return &sdk.Result{}, nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1137,23 +1168,29 @@ func TestMaxBlockGasLimits(t *testing.T) {
|
||||
|
||||
// execute the transaction multiple times
|
||||
for j := 0; j < tc.numDelivers; j++ {
|
||||
res := app.Deliver(tx)
|
||||
_, result, err := app.Deliver(tx)
|
||||
|
||||
ctx := app.getState(runTxModeDeliver).ctx
|
||||
blockGasUsed := ctx.BlockGasMeter().GasConsumed()
|
||||
|
||||
// check for failed transactions
|
||||
if tc.fail && (j+1) > tc.failAfterDeliver {
|
||||
require.Equal(t, res.Code, sdk.CodeOutOfGas, fmt.Sprintf("%d: %v, %v", i, tc, res))
|
||||
require.Equal(t, res.Codespace, sdk.CodespaceRoot, fmt.Sprintf("%d: %v, %v", i, tc, res))
|
||||
require.Error(t, err, fmt.Sprintf("tc #%d; result: %v, err: %s", i, result, err))
|
||||
require.Nil(t, result, fmt.Sprintf("tc #%d; result: %v, err: %s", i, result, err))
|
||||
|
||||
space, code, _ := sdkerrors.ABCIInfo(err, false)
|
||||
require.EqualValues(t, sdkerrors.ErrOutOfGas.Codespace(), space, err)
|
||||
require.EqualValues(t, sdkerrors.ErrOutOfGas.ABCICode(), code, err)
|
||||
require.True(t, ctx.BlockGasMeter().IsOutOfGas())
|
||||
} else {
|
||||
// check gas used and wanted
|
||||
blockGasUsed := ctx.BlockGasMeter().GasConsumed()
|
||||
expBlockGasUsed := tc.gasUsedPerDeliver * uint64(j+1)
|
||||
require.Equal(t, expBlockGasUsed, blockGasUsed,
|
||||
fmt.Sprintf("%d,%d: %v, %v, %v, %v", i, j, tc, expBlockGasUsed, blockGasUsed, res))
|
||||
require.Equal(
|
||||
t, expBlockGasUsed, blockGasUsed,
|
||||
fmt.Sprintf("%d,%d: %v, %v, %v, %v", i, j, tc, expBlockGasUsed, blockGasUsed, result),
|
||||
)
|
||||
|
||||
require.True(t, res.IsOK(), fmt.Sprintf("%d,%d: %v, %v", i, j, tc, res))
|
||||
require.NotNil(t, result, fmt.Sprintf("tc #%d; currDeliver: %d, result: %v, err: %s", i, j, result, err))
|
||||
require.False(t, ctx.BlockGasMeter().IsPastLimit())
|
||||
}
|
||||
}
|
||||
@@ -1260,10 +1297,10 @@ func TestGasConsumptionBadTx(t *testing.T) {
|
||||
}
|
||||
|
||||
routerOpt := func(bapp *BaseApp) {
|
||||
bapp.Router().AddRoute(routeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
|
||||
bapp.Router().AddRoute(routeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) {
|
||||
count := msg.(msgCounter).Counter
|
||||
ctx.GasMeter().ConsumeGas(uint64(count), "counter-handler")
|
||||
return sdk.Result{}
|
||||
return &sdk.Result{}, nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1313,10 +1350,10 @@ func TestQuery(t *testing.T) {
|
||||
}
|
||||
|
||||
routerOpt := func(bapp *BaseApp) {
|
||||
bapp.Router().AddRoute(routeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
|
||||
bapp.Router().AddRoute(routeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) {
|
||||
store := ctx.KVStore(capKey1)
|
||||
store.Set(key, value)
|
||||
return sdk.Result{}
|
||||
return &sdk.Result{}, nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1338,8 +1375,9 @@ func TestQuery(t *testing.T) {
|
||||
require.Equal(t, 0, len(res.Value))
|
||||
|
||||
// query is still empty after a CheckTx
|
||||
resTx := app.Check(tx)
|
||||
require.True(t, resTx.IsOK(), fmt.Sprintf("%v", resTx))
|
||||
_, resTx, err := app.Check(tx)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, resTx)
|
||||
res = app.Query(query)
|
||||
require.Equal(t, 0, len(res.Value))
|
||||
|
||||
@@ -1347,8 +1385,9 @@ func TestQuery(t *testing.T) {
|
||||
header := abci.Header{Height: app.LastBlockHeight() + 1}
|
||||
app.BeginBlock(abci.RequestBeginBlock{Header: header})
|
||||
|
||||
resTx = app.Deliver(tx)
|
||||
require.True(t, resTx.IsOK(), fmt.Sprintf("%v", resTx))
|
||||
_, resTx, err = app.Deliver(tx)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, resTx)
|
||||
res = app.Query(query)
|
||||
require.Equal(t, 0, len(res.Value))
|
||||
|
||||
|
||||
+3
-3
@@ -10,15 +10,15 @@ import (
|
||||
|
||||
var isAlphaNumeric = regexp.MustCompile(`^[a-zA-Z0-9]+$`).MatchString
|
||||
|
||||
func (app *BaseApp) Check(tx sdk.Tx) (result sdk.Result) {
|
||||
func (app *BaseApp) Check(tx sdk.Tx) (sdk.GasInfo, *sdk.Result, error) {
|
||||
return app.runTx(runTxModeCheck, nil, tx)
|
||||
}
|
||||
|
||||
func (app *BaseApp) Simulate(txBytes []byte, tx sdk.Tx) (result sdk.Result) {
|
||||
func (app *BaseApp) Simulate(txBytes []byte, tx sdk.Tx) (sdk.GasInfo, *sdk.Result, error) {
|
||||
return app.runTx(runTxModeSimulate, txBytes, tx)
|
||||
}
|
||||
|
||||
func (app *BaseApp) Deliver(tx sdk.Tx) (result sdk.Result) {
|
||||
func (app *BaseApp) Deliver(tx sdk.Tx) (sdk.GasInfo, *sdk.Result, error) {
|
||||
return app.runTx(runTxModeDeliver, nil, tx)
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
var testQuerier = func(_ sdk.Context, _ []string, _ abci.RequestQuery) (res []byte, err sdk.Error) {
|
||||
var testQuerier = func(_ sdk.Context, _ []string, _ abci.RequestQuery) ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@ import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
var testHandler = func(_ sdk.Context, _ sdk.Msg) sdk.Result {
|
||||
return sdk.Result{}
|
||||
var testHandler = func(_ sdk.Context, _ sdk.Msg) (*sdk.Result, error) {
|
||||
return &sdk.Result{}, nil
|
||||
}
|
||||
|
||||
func TestRouter(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user