Merge PR #5421: Refactor Error Handling

This commit is contained in:
Alexander Bezobchuk
2019-12-27 12:57:54 -05:00
committed by GitHub
parent 3c82b66347
commit 9a183ffbcc
170 changed files with 2317 additions and 2934 deletions
+99 -77
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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)
}
+1 -1
View File
@@ -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
}
+2 -2
View File
@@ -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) {