From 77d1ea4c2203071eb66e27f1254f8060908ef72a Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 6 May 2020 13:58:18 +0200 Subject: [PATCH] Add bank querier --- x/wasm/internal/keeper/keeper.go | 33 +++++++-- x/wasm/internal/keeper/keeper_test.go | 4 +- x/wasm/internal/keeper/query_plugins.go | 91 +++++++++++++++++++++---- 3 files changed, 104 insertions(+), 24 deletions(-) diff --git a/x/wasm/internal/keeper/keeper.go b/x/wasm/internal/keeper/keeper.go index 109c2b2..9d1b523 100644 --- a/x/wasm/internal/keeper/keeper.go +++ b/x/wasm/internal/keeper/keeper.go @@ -37,7 +37,7 @@ type Keeper struct { router sdk.Router wasmer wasm.Wasmer - querier QueryHandler + queryMods QueryModules messenger MessageHandler // queryGasLimit is the max wasm gas that can be spent on executing a query with a contract queryGasLimit uint64 @@ -52,7 +52,11 @@ func NewKeeper(cdc *codec.Codec, storeKey sdk.StoreKey, accountKeeper auth.Accou } messenger := MessageHandler{ - router: router, + router: router, + encoders: DefaultEncoders(), + } + queryMods := QueryModules{ + Bank: bankKeeper, } return Keeper{ @@ -62,7 +66,7 @@ func NewKeeper(cdc *codec.Codec, storeKey sdk.StoreKey, accountKeeper auth.Accou accountKeeper: accountKeeper, bankKeeper: bankKeeper, messenger: messenger, - querier: QueryHandler{}, // TODO: not empty + queryMods: queryMods, queryGasLimit: wasmConfig.SmartQueryGasLimit, } } @@ -131,9 +135,15 @@ func (k Keeper) Instantiate(ctx sdk.Context, codeID uint64, creator sdk.AccAddre prefixStoreKey := types.GetContractStorePrefixKey(contractAddress) prefixStore := prefix.NewStore(ctx.KVStore(k.storeKey), prefixStoreKey) + // prepare querier + querier := QueryHandler{ + Ctx: ctx, + Modules: k.queryMods, + } + // instantiate wasm contract gas := gasForContract(ctx) - res, err := k.wasmer.Instantiate(codeInfo.CodeHash, params, initMsg, prefixStore, cosmwasmAPI, k.querier, gas) + res, err := k.wasmer.Instantiate(codeInfo.CodeHash, params, initMsg, prefixStore, cosmwasmAPI, querier, gas) if err != nil { return contractAddress, sdkerrors.Wrap(types.ErrInstantiateFailed, err.Error()) // return contractAddress, sdkerrors.Wrap(err, "cosmwasm instantiate") @@ -175,8 +185,14 @@ func (k Keeper) Execute(ctx sdk.Context, contractAddress sdk.AccAddress, caller params := types.NewParams(ctx, caller, coins, contractAccount) + // prepare querier + querier := QueryHandler{ + Ctx: ctx, + Modules: k.queryMods, + } + gas := gasForContract(ctx) - res, execErr := k.wasmer.Execute(codeInfo.CodeHash, params, msg, prefixStore, cosmwasmAPI, k.querier, gas) + res, execErr := k.wasmer.Execute(codeInfo.CodeHash, params, msg, prefixStore, cosmwasmAPI, querier, gas) if execErr != nil { // TODO: wasmer doesn't return gas used on error. we should consume it (for error on metering failure) return sdk.Result{}, sdkerrors.Wrap(types.ErrExecuteFailed, execErr.Error()) @@ -205,7 +221,12 @@ func (k Keeper) QuerySmart(ctx sdk.Context, contractAddr sdk.AccAddress, req []b if err != nil { return nil, err } - queryResult, gasUsed, qErr := k.wasmer.Query(codeInfo.CodeHash, req, prefixStore, cosmwasmAPI, k.querier, gasForContract(ctx)) + // prepare querier + querier := QueryHandler{ + Ctx: ctx, + Modules: k.queryMods, + } + queryResult, gasUsed, qErr := k.wasmer.Query(codeInfo.CodeHash, req, prefixStore, cosmwasmAPI, querier, gasForContract(ctx)) if qErr != nil { return nil, sdkerrors.Wrap(types.ErrQueryFailed, qErr.Error()) } diff --git a/x/wasm/internal/keeper/keeper_test.go b/x/wasm/internal/keeper/keeper_test.go index ff72941..1159254 100644 --- a/x/wasm/internal/keeper/keeper_test.go +++ b/x/wasm/internal/keeper/keeper_test.go @@ -224,8 +224,6 @@ func TestInstantiateWithNonExistingCodeID(t *testing.T) { } func TestExecute(t *testing.T) { - // TODO - t.Skip("this causes a panic in the rust code!") tempDir, err := ioutil.TempDir("", "wasm") require.NoError(t, err) defer os.RemoveAll(tempDir) @@ -286,7 +284,7 @@ func TestExecute(t *testing.T) { // make sure gas is properly deducted from ctx gasAfter := ctx.GasMeter().GasConsumed() - require.Equal(t, uint64(31162), gasAfter-gasBefore) + require.Equal(t, uint64(32196), gasAfter-gasBefore) // ensure bob now exists and got both payments released bobAcct = accKeeper.GetAccount(ctx, bob) diff --git a/x/wasm/internal/keeper/query_plugins.go b/x/wasm/internal/keeper/query_plugins.go index 16f9486..3af6919 100644 --- a/x/wasm/internal/keeper/query_plugins.go +++ b/x/wasm/internal/keeper/query_plugins.go @@ -1,27 +1,88 @@ package keeper import ( + "encoding/json" wasmTypes "github.com/CosmWasm/go-cosmwasm/types" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/x/bank" ) -// TODO: make this something besides failure -// Give it sub-queriers -type QueryHandler struct{} +type QueryHandler struct { + Ctx sdk.Context + Modules QueryModules +} var _ wasmTypes.Querier = QueryHandler{} +// Fill out more modules +// Rethink interfaces +type QueryModules struct { + Bank bank.ViewKeeper +} + +//type QueryPlugins struct { +// Bank func(msg *wasmTypes.BankQuery) ([]byte, error) +// Custom func(msg json.RawMessage) ([]byte, error) +// Staking func(msg *wasmTypes.StakingQuery) ([]byte, error) +// Wasm func(msg *wasmTypes.WasmQuery) ([]byte, error) +//} + func (q QueryHandler) Query(request wasmTypes.QueryRequest) ([]byte, error) { - //if request.Bank != nil { - // return q.Bank.Query(request.Bank) - //} - //if request.Custom != nil { - // return q.Custom.Query(request.Custom) - //} - //if request.Staking != nil { - // return nil, wasmTypes.UnsupportedRequest{"staking"} - //} - //if request.Wasm != nil { - // return nil, wasmTypes.UnsupportedRequest{"wasm"} - //} + if request.Bank != nil { + return q.QueryBank(request.Bank) + } + // TODO: below + if request.Custom != nil { + return nil, wasmTypes.UnsupportedRequest{"custom"} + } + if request.Staking != nil { + return nil, wasmTypes.UnsupportedRequest{"staking"} + } + if request.Wasm != nil { + return nil, wasmTypes.UnsupportedRequest{"wasm"} + } return nil, wasmTypes.Unknown{} } + +func (q QueryHandler) QueryBank(request *wasmTypes.BankQuery) ([]byte, error) { + if request.AllBalances != nil { + addr, err := sdk.AccAddressFromBech32(request.AllBalances.Address) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, request.AllBalances.Address) + } + coins := q.Modules.Bank.GetCoins(q.Ctx, addr) + res := wasmTypes.AllBalancesResponse{ + Amount: convertSdkCoinToWasmCoin(coins), + } + return json.Marshal(res) + } + if request.Balance != nil { + addr, err := sdk.AccAddressFromBech32(request.Balance.Address) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, request.Balance.Address) + } + coins := q.Modules.Bank.GetCoins(q.Ctx, addr) + amount := coins.AmountOf(request.Balance.Denom) + res := wasmTypes.BalanceResponse{ + Amount: wasmTypes.Coin{ + Denom: request.Balance.Denom, + Amount: amount.String(), + }, + } + return json.Marshal(res) + } + return nil, wasmTypes.UnsupportedRequest{"unknown BankQuery variant"} +} + +func convertSdkCoinToWasmCoin(coins []sdk.Coin) wasmTypes.Coins { + var converted wasmTypes.Coins + for _, coin := range coins { + c := wasmTypes.Coin{ + Denom: coin.Denom, + Amount: coin.Amount.String(), + } + converted = append(converted, c) + } + return converted +}