Overhauled errors to sdk/errors

This commit is contained in:
Ethan Frey
2020-01-16 14:35:25 +01:00
parent ab5e56351e
commit 275c70bcff
5 changed files with 109 additions and 104 deletions

View File

@@ -10,6 +10,7 @@ import (
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/store/prefix"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkErrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/auth/exported"
"github.com/cosmos/cosmos-sdk/x/bank"
@@ -62,18 +63,19 @@ func NewKeeper(cdc *codec.Codec, storeKey sdk.StoreKey, accountKeeper auth.Accou
}
// Create uploads and compiles a WASM contract, returning a short identifier for the contract
func (k Keeper) Create(ctx sdk.Context, creator sdk.AccAddress, wasmCode []byte) (codeID uint64, sdkErr sdk.Error) {
func (k Keeper) Create(ctx sdk.Context, creator sdk.AccAddress, wasmCode []byte) (uint64, error) {
wasmCode, err := uncompress(wasmCode)
if err != nil {
return 0, types.ErrCreateFailed(err)
return 0, sdkErrors.Wrap(types.ErrCreateFailed, err.Error())
}
codeHash, err := k.wasmer.Create(wasmCode)
if err != nil {
return 0, types.ErrCreateFailed(err)
// return 0, sdkErrors.Wrap(err, "cosmwasm create")
return 0, sdkErrors.Wrap(types.ErrCreateFailed, err.Error())
}
store := ctx.KVStore(k.storeKey)
codeID = k.autoIncrementID(ctx, types.KeyLastCodeID)
codeID := k.autoIncrementID(ctx, types.KeyLastCodeID)
contractInfo := types.NewCodeInfo(codeHash, creator)
// 0x01 | codeID (uint64) -> ContractInfo
store.Set(types.GetCodeKey(codeID), k.cdc.MustMarshalBinaryBare(contractInfo))
@@ -82,12 +84,12 @@ func (k Keeper) Create(ctx sdk.Context, creator sdk.AccAddress, wasmCode []byte)
}
// Instantiate creates an instance of a WASM contract
func (k Keeper) Instantiate(ctx sdk.Context, creator sdk.AccAddress, codeID uint64, initMsg []byte, deposit sdk.Coins) (sdk.AccAddress, sdk.Error) {
func (k Keeper) Instantiate(ctx sdk.Context, creator sdk.AccAddress, codeID uint64, initMsg []byte, deposit sdk.Coins) (sdk.AccAddress, error) {
// create contract address
contractAddress := k.generateContractAddress(ctx, codeID)
existingAccnt := k.accountKeeper.GetAccount(ctx, contractAddress)
if existingAccnt != nil {
return nil, types.ErrAccountExists(existingAccnt.GetAddress())
existingAcct := k.accountKeeper.GetAccount(ctx, contractAddress)
if existingAcct != nil {
return nil, sdkErrors.Wrap(types.ErrAccountExists, existingAcct.GetAddress().String())
}
// deposit initial contract funds
@@ -101,7 +103,7 @@ func (k Keeper) Instantiate(ctx sdk.Context, creator sdk.AccAddress, codeID uint
store := ctx.KVStore(k.storeKey)
bz := store.Get(types.GetCodeKey(codeID))
if bz == nil {
return nil, types.ErrNotFound("contract")
return nil, sdkErrors.Wrap(types.ErrNotFound, "contract")
}
var codeInfo types.CodeInfo
k.cdc.MustUnmarshalBinaryBare(bz, &codeInfo)
@@ -118,13 +120,14 @@ func (k Keeper) Instantiate(ctx sdk.Context, creator sdk.AccAddress, codeID uint
gas := gasForContract(ctx)
res, err := k.wasmer.Instantiate(codeInfo.CodeHash, params, initMsg, prefixStore, cosmwasmAPI, gas)
if err != nil {
return contractAddress, types.ErrInstantiateFailed(err)
return contractAddress, sdkErrors.Wrap(types.ErrInstantiateFailed, err.Error())
// return contractAddress, sdkErrors.Wrap(err, "cosmwasm instantiate")
}
consumeGas(ctx, res.GasUsed)
sdkerr = k.dispatchMessages(ctx, contractAccount, res.Messages)
if sdkerr != nil {
return nil, sdkerr
err = k.dispatchMessages(ctx, contractAccount, res.Messages)
if err != nil {
return nil, err
}
// persist instance
@@ -136,7 +139,7 @@ func (k Keeper) Instantiate(ctx sdk.Context, creator sdk.AccAddress, codeID uint
}
// Execute executes the contract instance
func (k Keeper) Execute(ctx sdk.Context, contractAddress sdk.AccAddress, caller sdk.AccAddress, coins sdk.Coins, msg []byte) (sdk.Result, sdk.Error) {
func (k Keeper) Execute(ctx sdk.Context, contractAddress sdk.AccAddress, caller sdk.AccAddress, coins sdk.Coins, msg []byte) (sdk.Result, error) {
codeInfo, prefixStore, err := k.contractInstance(ctx, contractAddress)
if err != nil {
return sdk.Result{}, err
@@ -152,20 +155,21 @@ func (k Keeper) Execute(ctx sdk.Context, contractAddress sdk.AccAddress, caller
gas := gasForContract(ctx)
res, execErr := k.wasmer.Execute(codeInfo.CodeHash, params, msg, prefixStore, cosmwasmAPI, gas)
if execErr != nil {
return sdk.Result{}, types.ErrExecuteFailed(execErr)
// return sdk.Result{}, sdkErrors.Wrap(types.ErrExecuteFailed, err.Error())
return sdk.Result{}, sdkErrors.Wrap(execErr, "cosmwasm execute")
}
consumeGas(ctx, res.GasUsed)
sdkerr = k.dispatchMessages(ctx, contractAccount, res.Messages)
if sdkerr != nil {
return sdk.Result{}, sdkerr
err = k.dispatchMessages(ctx, contractAccount, res.Messages)
if err != nil {
return sdk.Result{}, err
}
return types.CosmosResult(*res), nil
}
// QuerySmart queries the smart contract itself.
func (k Keeper) QuerySmart(ctx sdk.Context, contractAddr sdk.AccAddress, req []byte) ([]byte, sdk.Error) {
func (k Keeper) QuerySmart(ctx sdk.Context, contractAddr sdk.AccAddress, req []byte) ([]byte, error) {
ctx = ctx.WithGasMeter(sdk.NewGasMeter(k.queryGasLimit))
codeInfo, prefixStore, err := k.contractInstance(ctx, contractAddr)
@@ -174,7 +178,9 @@ func (k Keeper) QuerySmart(ctx sdk.Context, contractAddr sdk.AccAddress, req []b
}
queryResult, gasUsed, qErr := k.wasmer.Query(codeInfo.CodeHash, req, prefixStore, cosmwasmAPI, gasForContract(ctx))
if qErr != nil {
return nil, types.ErrExecuteFailed(qErr)
return nil, sdkErrors.Wrap(types.ErrQueryFailed, "unknown")
// return nil, sdkErrors.Wrap(types.ErrQueryFailed, err.Error())
// return nil, sdkErrors.Wrap(qErr, "cosmwasm query")
}
consumeGas(ctx, gasUsed)
return queryResult, nil
@@ -198,19 +204,19 @@ func (k Keeper) QueryRaw(ctx sdk.Context, contractAddress sdk.AccAddress, key []
return result
}
func (k Keeper) contractInstance(ctx sdk.Context, contractAddress sdk.AccAddress) (types.CodeInfo, prefix.Store, sdk.Error) {
func (k Keeper) contractInstance(ctx sdk.Context, contractAddress sdk.AccAddress) (types.CodeInfo, prefix.Store, error) {
store := ctx.KVStore(k.storeKey)
contractBz := store.Get(types.GetContractAddressKey(contractAddress))
if contractBz == nil {
return types.CodeInfo{}, prefix.Store{}, types.ErrNotFound("contract")
return types.CodeInfo{}, prefix.Store{}, sdkErrors.Wrap(types.ErrNotFound, "contract")
}
var contract types.ContractInfo
k.cdc.MustUnmarshalBinaryBare(contractBz, &contract)
contractInfoBz := store.Get(types.GetCodeKey(contract.CodeID))
if contractInfoBz == nil {
return types.CodeInfo{}, prefix.Store{}, types.ErrNotFound("contract info")
return types.CodeInfo{}, prefix.Store{}, sdkErrors.Wrap(types.ErrNotFound, "contract info")
}
var codeInfo types.CodeInfo
k.cdc.MustUnmarshalBinaryBare(contractInfoBz, &codeInfo)
@@ -284,7 +290,7 @@ func (k Keeper) GetByteCode(ctx sdk.Context, codeID uint64) ([]byte, error) {
return k.wasmer.GetCode(codeInfo.CodeHash)
}
func (k Keeper) dispatchMessages(ctx sdk.Context, contract exported.Account, msgs []wasmTypes.CosmosMsg) sdk.Error {
func (k Keeper) dispatchMessages(ctx sdk.Context, contract exported.Account, msgs []wasmTypes.CosmosMsg) error {
for _, msg := range msgs {
if err := k.dispatchMessage(ctx, contract, msg); err != nil {
return err
@@ -293,7 +299,7 @@ func (k Keeper) dispatchMessages(ctx sdk.Context, contract exported.Account, msg
return nil
}
func (k Keeper) dispatchMessage(ctx sdk.Context, contract exported.Account, msg wasmTypes.CosmosMsg) sdk.Error {
func (k Keeper) dispatchMessage(ctx sdk.Context, contract exported.Account, msg wasmTypes.CosmosMsg) error {
// we check each type (pointers would make it easier to test if set)
if msg.Send.FromAddress != "" {
sendMsg, err := convertCosmosSendMsg(msg.Send)
@@ -349,23 +355,23 @@ func convertCosmosSendMsg(msg wasmTypes.SendMsg) (bank.MsgSend, sdk.Error) {
return sendMsg, nil
}
func (k Keeper) handleSdkMessage(ctx sdk.Context, contract exported.Account, msg sdk.Msg) sdk.Error {
func (k Keeper) handleSdkMessage(ctx sdk.Context, contract exported.Account, msg sdk.Msg) error {
// make sure this account can send it
contractAddr := contract.GetAddress()
for _, acct := range msg.GetSigners() {
if !acct.Equals(contractAddr) {
return sdk.ErrUnauthorized("contract doesn't have permission")
return sdkErrors.Wrap(sdkErrors.ErrUnauthorized, "contract doesn't have permission")
}
}
// find the handler and execute it
h := k.router.Route(msg.Route())
if h == nil {
return sdk.ErrUnknownRequest(msg.Route())
return sdkErrors.Wrap(sdkErrors.ErrUnknownRequest, msg.Route())
}
res := h(ctx, msg)
if !res.IsOK() {
return sdk.NewError(res.Codespace, res.Code, res.Log)
return sdkErrors.ABCIError(string(res.Codespace), uint32(res.Code), res.Log)
}
return nil
}

View File

@@ -123,7 +123,7 @@ func TestInstantiateWithNonExistingCodeID(t *testing.T) {
const nonExistingCodeID = 9999
addr, err := keeper.Instantiate(ctx, creator, nonExistingCodeID, initMsgBz, nil)
require.Error(t, err, types.ErrNotFound("contract"))
require.True(t, types.ErrNotFound.Is(err), err)
require.Nil(t, addr)
}
@@ -217,7 +217,7 @@ func TestExecuteWithNonExistingAddress(t *testing.T) {
// unauthorized - trialCtx so we don't change state
nonExistingAddress := addrFromUint64(9999)
_, err = keeper.Execute(ctx, nonExistingAddress, creator, nil, []byte(`{}`))
require.Error(t, err, types.ErrNotFound("contract info"))
require.True(t, types.ErrNotFound.Is(err), err)
}
type InitMsg struct {

View File

@@ -5,6 +5,7 @@ import (
"strconv"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkErrors "github.com/cosmos/cosmos-sdk/types/errors"
abci "github.com/tendermint/tendermint/abci/types"
cmn "github.com/tendermint/tendermint/libs/common"
@@ -26,26 +27,40 @@ const (
QueryMethodContractStateRaw = "raw"
)
// controls error output on querier
const debug = true
// NewQuerier creates a new querier
func NewQuerier(keeper Keeper) sdk.Querier {
return func(ctx sdk.Context, path []string, req abci.RequestQuery) ([]byte, sdk.Error) {
var res []byte
var err error
switch path[0] {
case QueryGetContract:
return queryContractInfo(ctx, path[1], req, keeper)
res, err = queryContractInfo(ctx, path[1], req, keeper)
case QueryListContracts:
return queryContractList(ctx, req, keeper)
res, err = queryContractList(ctx, req, keeper)
case QueryGetContractState:
if len(path) < 3 {
return nil, sdk.ErrUnknownRequest("unknown data query endpoint")
res, err = nil, sdkErrors.Wrap(sdkErrors.ErrUnknownRequest, "unknown data query endpoint")
}
return queryContractState(ctx, path[1], path[2], req, keeper)
res, err = queryContractState(ctx, path[1], path[2], req, keeper)
case QueryGetCode:
return queryCode(ctx, path[1], req, keeper)
res, err = queryCode(ctx, path[1], req, keeper)
case QueryListCode:
return queryCodeList(ctx, req, keeper)
res, err = queryCodeList(ctx, req, keeper)
default:
return nil, sdk.ErrUnknownRequest("unknown data query endpoint")
res, err = nil, sdkErrors.Wrap(sdkErrors.ErrUnknownRequest, "unknown data query endpoint")
}
// convert returned errors
if err != nil {
space, code, log := sdkErrors.ABCIInfo(err, debug)
sdkErr := sdk.NewError(sdk.CodespaceType(space), sdk.CodeType(code), log)
return nil, sdkErr
}
return res, nil
}
}
@@ -63,7 +78,7 @@ func queryContractInfo(ctx sdk.Context, bech string, req abci.RequestQuery, keep
return bz, nil
}
func queryContractList(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) {
func queryContractList(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, error) {
var addrs []string
keeper.ListContractInfo(ctx, func(addr sdk.AccAddress, _ types.ContractInfo) bool {
addrs = append(addrs, addr.String())
@@ -71,15 +86,15 @@ func queryContractList(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([
})
bz, err := json.MarshalIndent(addrs, "", " ")
if err != nil {
return nil, sdk.ErrInvalidAddress(err.Error())
return nil, sdkErrors.Wrap(sdkErrors.ErrJSONMarshal, err.Error())
}
return bz, nil
}
func queryContractState(ctx sdk.Context, bech, queryMethod string, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) {
func queryContractState(ctx sdk.Context, bech, queryMethod string, req abci.RequestQuery, keeper Keeper) ([]byte, error) {
contractAddr, err := sdk.AccAddressFromBech32(bech)
if err != nil {
return nil, sdk.ErrUnknownRequest(err.Error())
return nil, sdkErrors.Wrap(sdkErrors.ErrInvalidAddress, bech)
}
var resultData []types.Model
@@ -99,11 +114,11 @@ func queryContractState(ctx sdk.Context, bech, queryMethod string, req abci.Requ
case QueryMethodContractStateSmart:
return keeper.QuerySmart(ctx, contractAddr, req.Data)
default:
return nil, sdk.ErrUnknownRequest("unsupported data query method for contract-state")
return nil, sdkErrors.Wrap(sdkErrors.ErrUnknownRequest, queryMethod)
}
bz, err := json.MarshalIndent(resultData, "", " ")
if err != nil {
return nil, sdk.ErrUnknownRequest(err.Error())
return nil, sdkErrors.Wrap(sdkErrors.ErrJSONMarshal, err.Error())
}
return bz, nil
}
@@ -112,20 +127,20 @@ type GetCodeResponse struct {
Code []byte `json:"code" yaml:"code"`
}
func queryCode(ctx sdk.Context, codeIDstr string, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) {
func queryCode(ctx sdk.Context, codeIDstr string, req abci.RequestQuery, keeper Keeper) ([]byte, error) {
codeID, err := strconv.ParseUint(codeIDstr, 10, 64)
if err != nil {
return nil, sdk.ErrUnknownRequest("invalid codeID: " + err.Error())
return nil, sdkErrors.Wrap(sdkErrors.ErrUnknownRequest, "invalid codeID: "+err.Error())
}
code, err := keeper.GetByteCode(ctx, codeID)
if err != nil {
return nil, sdk.ErrUnknownRequest("loading wasm code: " + err.Error())
return nil, sdkErrors.Wrap(err, "loading wasm code")
}
bz, err := json.MarshalIndent(GetCodeResponse{code}, "", " ")
if err != nil {
return nil, sdk.ErrUnknownRequest(err.Error())
return nil, sdkErrors.Wrap(sdkErrors.ErrJSONMarshal, err.Error())
}
return bz, nil
}
@@ -136,7 +151,7 @@ type ListCodeResponse struct {
CodeHash cmn.HexBytes `json:"code_hash"`
}
func queryCodeList(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) {
func queryCodeList(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, error) {
var info []ListCodeResponse
var i uint64
@@ -155,7 +170,7 @@ func queryCodeList(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byt
bz, err := json.MarshalIndent(info, "", " ")
if err != nil {
return nil, sdk.ErrUnknownRequest(err.Error())
return nil, sdkErrors.Wrap(sdkErrors.ErrJSONMarshal, err.Error())
}
return bz, nil
}

View File

@@ -7,6 +7,7 @@ import (
"testing"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkErrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmwasm/wasmd/x/wasm/internal/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -63,7 +64,7 @@ func TestQueryContractState(t *testing.T) {
// if success and expSmartRes is not set, we parse into []model and compare
expModelLen int
expModelContains []model
expErr sdk.Error
expErr *sdkErrors.Error
}{
"query all": {
srcPath: []string{QueryGetContractState, addr.String(), QueryMethodContractStateAll},
@@ -86,11 +87,15 @@ func TestQueryContractState(t *testing.T) {
expModelContains: []model{{Key: string([]byte{0x0, 0x1}), Value: string([]byte{0x2, 0x3})}},
},
"query smart": {
srcPath: []string{QueryGetContractState, addr.String(), QueryMethodContractStateSmart},
srcReq: abci.RequestQuery{Data: []byte(`{"verifier":{}}`)},
// srcReq: abci.RequestQuery{Data: []byte(`{"raw":{"key":"config"}}`)},
srcPath: []string{QueryGetContractState, addr.String(), QueryMethodContractStateSmart},
srcReq: abci.RequestQuery{Data: []byte(`{"verifier":{}}`)},
expSmartRes: "cosmos1uf348t8j0h06ghr5udaw0hvlj9fhemhlsvve0f",
},
// "query smart invalid request": {
// srcPath: []string{QueryGetContractState, addr.String(), QueryMethodContractStateSmart},
// srcReq: abci.RequestQuery{Data: []byte(`{"raw":{"key":"config"}}`)},
// expErr: nil, // TODO
// },
"query unknown raw key": {
srcPath: []string{QueryGetContractState, addr.String(), QueryMethodContractStateRaw},
srcReq: abci.RequestQuery{Data: []byte("unknown")},
@@ -111,15 +116,15 @@ func TestQueryContractState(t *testing.T) {
"query smart with unknown address": {
srcPath: []string{QueryGetContractState, anyAddr.String(), QueryMethodContractStateSmart},
expModelLen: 0,
expErr: types.ErrNotFound("contract"),
expErr: types.ErrNotFound,
},
}
for msg, spec := range specs {
t.Run(msg, func(t *testing.T) {
binResult, err := q(ctx, spec.srcPath, spec.srcReq)
require.Equal(t, spec.expErr, err, "unexpected error")
// require.Equal(t, spec.expErr, err, err.Error())
// require.True(t, spec.expErr.Is(err), "unexpected error")
require.True(t, spec.expErr.Is(err), err)
// if smart query, check custom response
if spec.expSmartRes != "" {

View File

@@ -1,55 +1,34 @@
package types
import (
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkErrors "github.com/cosmos/cosmos-sdk/types/errors"
)
// Codes for wasm contract errors
const (
DefaultCodespace sdk.CodespaceType = ModuleName
var (
DefaultCodespace = ModuleName
CodeCreatedFailed sdk.CodeType = 1
CodeAccountExists sdk.CodeType = 2
CodeInstantiateFailed sdk.CodeType = 3
CodeExecuteFailed sdk.CodeType = 4
CodeGasLimit sdk.CodeType = 5
CodeInvalidGenesis sdk.CodeType = 6
CodeNotFound sdk.CodeType = 7
// ErrCreateFailed error for wasm code that has already been uploaded or failed
ErrCreateFailed = sdkErrors.Register(DefaultCodespace, 1, "create wasm contract failed")
// ErrAccountExists error for a contract account that already exists
ErrAccountExists = sdkErrors.Register(DefaultCodespace, 2, "contract account already exists")
// ErrInstantiateFailed error for rust instantiate contract failure
ErrInstantiateFailed = sdkErrors.Register(DefaultCodespace, 3, "instantiate wasm contract failed")
// ErrExecuteFailed error for rust execution contract failure
ErrExecuteFailed = sdkErrors.Register(DefaultCodespace, 4, "execute wasm contract failed")
// ErrGasLimit error for out of gas
ErrGasLimit = sdkErrors.Register(DefaultCodespace, 5, "insufficient gas")
// ErrInvalidGenesis error for invalid genesis file syntax
ErrInvalidGenesis = sdkErrors.Register(DefaultCodespace, 6, "invalid genesis")
// ErrNotFound error for an entry not found in the store
ErrNotFound = sdkErrors.Register(DefaultCodespace, 7, "not found")
// ErrQueryFailed error for rust smart query contract failure
ErrQueryFailed = sdkErrors.Register(DefaultCodespace, 8, "query wasm contract failed")
)
// ErrCreateFailed error for wasm code that has already been uploaded or failed
func ErrCreateFailed(err error) sdk.Error {
return sdk.NewError(DefaultCodespace, CodeCreatedFailed, fmt.Sprintf("create wasm contract failed: %s", err.Error()))
}
// ErrAccountExists error for a contract account that already exists
func ErrAccountExists(addr sdk.AccAddress) sdk.Error {
return sdk.NewError(DefaultCodespace, CodeAccountExists, fmt.Sprintf("contract account %s already exists", addr.String()))
}
// ErrInstantiateFailed error for rust instantiate contract failure
func ErrInstantiateFailed(err error) sdk.Error {
return sdk.NewError(DefaultCodespace, CodeInstantiateFailed, fmt.Sprintf("instantiate wasm contract failed: %s", err.Error()))
}
// ErrExecuteFailed error for rust execution contract failure
func ErrExecuteFailed(err error) sdk.Error {
return sdk.NewError(DefaultCodespace, CodeExecuteFailed, fmt.Sprintf("execute wasm contract failed: %s", err.Error()))
}
// ErrGasLimit error for out of gas
func ErrGasLimit(msg string) sdk.Error {
return sdk.NewError(DefaultCodespace, CodeGasLimit, fmt.Sprintf("insufficient gas: %s", msg))
}
// ErrInvalidGenesis error for out of gas
func ErrInvalidGenesis(msg string) sdk.Error {
return sdk.NewError(DefaultCodespace, CodeInvalidGenesis, fmt.Sprintf("invalid genesis: %s", msg))
}
// ErrNotFound error for an entry not found in the stoe
func ErrNotFound(msg string) sdk.Error {
return sdk.NewError(DefaultCodespace, CodeNotFound, fmt.Sprintf("not found: %s", msg))
}