Merge pull request #435 from CosmWasm/stargate-msg-and-query

Stargate msg and query
This commit is contained in:
Ethan Frey
2021-03-04 19:05:51 +01:00
committed by GitHub
9 changed files with 219 additions and 38 deletions

View File

@@ -346,14 +346,12 @@ func NewWasmApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest b
)
app.evidenceKeeper = *evidenceKeeper
// just re-use the full router - do we want to limit this more?
var wasmRouter = bApp.Router()
wasmDir := filepath.Join(homePath, "wasm")
wasmConfig, err := wasm.ReadWasmConfig(appOpts)
if err != nil {
panic("error while reading wasm config: " + err.Error())
}
// The last arguments can contain custom message handlers, and custom query handlers,
// if we want to allow any custom callbacks
supportedFeatures := "staking,stargate"
@@ -368,7 +366,8 @@ func NewWasmApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest b
app.ibcKeeper.ChannelKeeper,
&app.ibcKeeper.PortKeeper,
scopedWasmKeeper,
wasmRouter,
app.Router(),
app.GRPCQueryRouter(),
wasmDir,
wasmConfig,
supportedFeatures,

View File

@@ -616,7 +616,7 @@ func setupKeeper(t *testing.T) (*Keeper, sdk.Context, []sdk.StoreKey) {
wasmConfig := wasmTypes.DefaultWasmConfig()
pk := paramskeeper.NewKeeper(encodingConfig.Marshaler, encodingConfig.Amino, keyParams, tkeyParams)
srcKeeper := NewKeeper(encodingConfig.Marshaler, keyWasm, pk.Subspace(wasmTypes.DefaultParamspace), authkeeper.AccountKeeper{}, nil, stakingkeeper.Keeper{}, distributionkeeper.Keeper{}, nil, nil, nil, nil, tempDir, wasmConfig, SupportedFeatures, nil, nil)
srcKeeper := NewKeeper(encodingConfig.Marshaler, keyWasm, pk.Subspace(wasmTypes.DefaultParamspace), authkeeper.AccountKeeper{}, nil, stakingkeeper.Keeper{}, distributionkeeper.Keeper{}, nil, nil, nil, nil, nil, tempDir, wasmConfig, SupportedFeatures, nil, nil)
return &srcKeeper, ctx, []sdk.StoreKey{keyWasm, keyParams}
}

View File

@@ -2,8 +2,10 @@ package keeper
import (
"encoding/json"
"fmt"
"github.com/CosmWasm/wasmd/x/wasm/internal/types"
wasmvmtypes "github.com/CosmWasm/wasmvm/types"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
@@ -20,8 +22,8 @@ type DefaultMessageHandler struct {
encoders MessageEncoders
}
func NewDefaultMessageHandler(router sdk.Router, channelKeeper types.ChannelKeeper, capabilityKeeper types.CapabilityKeeper, customEncoders *MessageEncoders) DefaultMessageHandler {
encoders := DefaultEncoders(channelKeeper, capabilityKeeper).Merge(customEncoders)
func NewDefaultMessageHandler(router sdk.Router, channelKeeper types.ChannelKeeper, capabilityKeeper types.CapabilityKeeper, unpacker codectypes.AnyUnpacker, customEncoders *MessageEncoders) DefaultMessageHandler {
encoders := DefaultEncoders(channelKeeper, capabilityKeeper, unpacker).Merge(customEncoders)
return DefaultMessageHandler{
router: router,
encoders: encoders,
@@ -31,24 +33,27 @@ func NewDefaultMessageHandler(router sdk.Router, channelKeeper types.ChannelKeep
type BankEncoder func(sender sdk.AccAddress, msg *wasmvmtypes.BankMsg) ([]sdk.Msg, error)
type CustomEncoder func(sender sdk.AccAddress, msg json.RawMessage) ([]sdk.Msg, error)
type StakingEncoder func(sender sdk.AccAddress, msg *wasmvmtypes.StakingMsg) ([]sdk.Msg, error)
type StargateEncoder func(sender sdk.AccAddress, msg *wasmvmtypes.StargateMsg) ([]sdk.Msg, error)
type WasmEncoder func(sender sdk.AccAddress, msg *wasmvmtypes.WasmMsg) ([]sdk.Msg, error)
type IBCEncoder func(ctx sdk.Context, sender sdk.AccAddress, contractIBCPortID string, msg *wasmvmtypes.IBCMsg) ([]sdk.Msg, error)
type MessageEncoders struct {
Bank BankEncoder
Custom CustomEncoder
Staking StakingEncoder
Wasm WasmEncoder
IBC IBCEncoder
Bank BankEncoder
Custom CustomEncoder
IBC IBCEncoder
Staking StakingEncoder
Stargate StargateEncoder
Wasm WasmEncoder
}
func DefaultEncoders(channelKeeper types.ChannelKeeper, capabilityKeeper types.CapabilityKeeper) MessageEncoders {
func DefaultEncoders(channelKeeper types.ChannelKeeper, capabilityKeeper types.CapabilityKeeper, unpacker codectypes.AnyUnpacker) MessageEncoders {
return MessageEncoders{
Bank: EncodeBankMsg,
Custom: NoCustomMsg,
Staking: EncodeStakingMsg,
Wasm: EncodeWasmMsg,
IBC: EncodeIBCMsg(channelKeeper, capabilityKeeper),
Bank: EncodeBankMsg,
Custom: NoCustomMsg,
IBC: EncodeIBCMsg(channelKeeper, capabilityKeeper),
Staking: EncodeStakingMsg,
Stargate: EncodeStargateMsg(unpacker),
Wasm: EncodeWasmMsg,
}
}
@@ -62,15 +67,18 @@ func (e MessageEncoders) Merge(o *MessageEncoders) MessageEncoders {
if o.Custom != nil {
e.Custom = o.Custom
}
if o.IBC != nil {
e.IBC = o.IBC
}
if o.Staking != nil {
e.Staking = o.Staking
}
if o.Stargate != nil {
e.Stargate = o.Stargate
}
if o.Wasm != nil {
e.Wasm = o.Wasm
}
if o.IBC != nil {
e.IBC = o.IBC
}
return e
}
@@ -80,12 +88,14 @@ func (e MessageEncoders) Encode(ctx sdk.Context, contractAddr sdk.AccAddress, co
return e.Bank(contractAddr, msg.Bank)
case msg.Custom != nil:
return e.Custom(contractAddr, msg.Custom)
case msg.Staking != nil:
return e.Staking(contractAddr, msg.Staking)
case msg.Wasm != nil:
return e.Wasm(contractAddr, msg.Wasm)
case msg.IBC != nil:
return e.IBC(ctx, contractAddr, contractIBCPortID, msg.IBC)
case msg.Staking != nil:
return e.Staking(contractAddr, msg.Staking)
case msg.Stargate != nil:
return e.Stargate(contractAddr, msg.Stargate)
case msg.Wasm != nil:
return e.Wasm(contractAddr, msg.Wasm)
}
return nil, sdkerrors.Wrap(types.ErrInvalidMsg, "Unknown variant of Wasm")
}
@@ -170,6 +180,23 @@ func EncodeStakingMsg(sender sdk.AccAddress, msg *wasmvmtypes.StakingMsg) ([]sdk
}
}
func EncodeStargateMsg(unpacker codectypes.AnyUnpacker) StargateEncoder {
return func(sender sdk.AccAddress, msg *wasmvmtypes.StargateMsg) ([]sdk.Msg, error) {
any := codectypes.Any{
TypeUrl: msg.TypeURL,
Value: msg.Value,
}
var sdkMsg sdk.Msg
if err := unpacker.UnpackAny(&any, &sdkMsg); err != nil {
return nil, sdkerrors.Wrap(types.ErrInvalidMsg, fmt.Sprintf("Cannot unpack proto message with type URL: %s", msg.TypeURL))
}
if err := codectypes.UnpackInterfaces(sdkMsg, unpacker); err != nil {
return nil, sdkerrors.Wrap(types.ErrInvalidMsg, fmt.Sprintf("UnpackInterfaces inside msg: %s", err))
}
return []sdk.Msg{sdkMsg}, nil
}
}
func EncodeWasmMsg(sender sdk.AccAddress, msg *wasmvmtypes.WasmMsg) ([]sdk.Msg, error) {
switch {
case msg.Execute != nil:

View File

@@ -8,6 +8,7 @@ import (
clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/core/02-client/types"
channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/core/04-channel/types"
ibcexported "github.com/cosmos/cosmos-sdk/x/ibc/core/exported"
"github.com/golang/protobuf/proto"
"testing"
"github.com/CosmWasm/wasmd/x/wasm/internal/types"
@@ -32,6 +33,17 @@ func TestEncoding(t *testing.T) {
jsonMsg := json.RawMessage(`{"foo": 123}`)
bankMsg := &banktypes.MsgSend{
FromAddress: addr2.String(),
ToAddress: addr1.String(),
Amount: sdk.Coins{
sdk.NewInt64Coin("uatom", 12345),
sdk.NewInt64Coin("utgd", 54321),
},
}
bankMsgBin, err := proto.Marshal(bankMsg)
require.NoError(t, err)
cases := map[string]struct {
sender sdk.AccAddress
srcMsg wasmvmtypes.CosmosMsg
@@ -276,6 +288,27 @@ func TestEncoding(t *testing.T) {
},
},
},
// TODO: alpe? can you add an example with sub-interfaces (where the UnpackInterfaces call would be needed)
"stargate encoded bank msg": {
sender: addr2,
srcMsg: wasmvmtypes.CosmosMsg{
Stargate: &wasmvmtypes.StargateMsg{
TypeURL: "/cosmos.bank.v1beta1.MsgSend",
Value: bankMsgBin,
},
},
output: []sdk.Msg{bankMsg},
},
"stargate encoded invalid typeUrl": {
sender: addr2,
srcMsg: wasmvmtypes.CosmosMsg{
Stargate: &wasmvmtypes.StargateMsg{
TypeURL: "/cosmos.bank.v2.MsgSend",
Value: bankMsgBin,
},
},
isError: true,
},
"IBC transfer with block timeout": {
sender: addr1,
srcIBCPort: "myIBCPort",
@@ -355,7 +388,8 @@ func TestEncoding(t *testing.T) {
},
},
}
encoder := DefaultEncoders(nil, nil)
encodingConfig := MakeEncodingConfig(t)
encoder := DefaultEncoders(nil, nil, encodingConfig.Marshaler)
for name, tc := range cases {
tc := tc
t.Run(name, func(t *testing.T) {

View File

@@ -88,6 +88,7 @@ func NewKeeper(
portKeeper types.PortKeeper,
capabilityKeeper types.CapabilityKeeper,
router sdk.Router,
queryRouter GRPCQueryRouter,
homeDir string,
wasmConfig types.WasmConfig,
supportedFeatures string,
@@ -105,7 +106,6 @@ func NewKeeper(
paramSpace = paramSpace.WithKeyTable(types.ParamKeyTable())
}
messageEncoders := DefaultEncoders(channelKeeper, capabilityKeeper).Merge(customEncoders)
keeper := Keeper{
storeKey: storeKey,
cdc: cdc,
@@ -115,12 +115,12 @@ func NewKeeper(
ChannelKeeper: channelKeeper,
portKeeper: portKeeper,
capabilityKeeper: capabilityKeeper,
messenger: NewDefaultMessageHandler(router, channelKeeper, capabilityKeeper, &messageEncoders),
messenger: NewDefaultMessageHandler(router, channelKeeper, capabilityKeeper, cdc, customEncoders),
queryGasLimit: wasmConfig.SmartQueryGasLimit,
authZPolicy: DefaultAuthorizationPolicy{},
paramSpace: paramSpace,
}
keeper.queryPlugins = DefaultQueryPlugins(bankKeeper, stakingKeeper, distKeeper, &keeper).Merge(customPlugins)
keeper.queryPlugins = DefaultQueryPlugins(bankKeeper, stakingKeeper, distKeeper, queryRouter, &keeper).Merge(customPlugins)
for _, o := range opts {
o.apply(&keeper)
}

View File

@@ -43,6 +43,7 @@ func TestConstructorOptions(t *testing.T) {
nil,
nil,
nil,
nil,
"tempDir",
types.DefaultWasmConfig(),
SupportedFeatures,

View File

@@ -2,6 +2,7 @@ package keeper
import (
"encoding/json"
"fmt"
wasmvmtypes "github.com/CosmWasm/wasmvm/types"
sdk "github.com/cosmos/cosmos-sdk/types"
@@ -11,6 +12,7 @@ import (
distributiontypes "github.com/cosmos/cosmos-sdk/x/distribution/types"
stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
abci "github.com/tendermint/tendermint/abci/types"
)
type QueryHandler struct {
@@ -18,6 +20,18 @@ type QueryHandler struct {
Plugins QueryPlugins
}
// -- interfaces from baseapp - so we can use the GPRQueryRouter --
// GRPCQueryHandler defines a function type which handles ABCI Query requests
// using gRPC
type GRPCQueryHandler = func(ctx sdk.Context, req abci.RequestQuery) (abci.ResponseQuery, error)
type GRPCQueryRouter interface {
Route(path string) GRPCQueryHandler
}
// -- end baseapp interfaces --
var _ wasmvmtypes.Querier = QueryHandler{}
func (q QueryHandler) Query(request wasmvmtypes.QueryRequest, gasLimit uint64) ([]byte, error) {
@@ -40,6 +54,9 @@ func (q QueryHandler) Query(request wasmvmtypes.QueryRequest, gasLimit uint64) (
if request.Staking != nil {
return q.Plugins.Staking(subctx, request.Staking)
}
if request.Stargate != nil {
return q.Plugins.Stargate(subctx, request.Stargate)
}
if request.Wasm != nil {
return q.Plugins.Wasm(subctx, request.Wasm)
}
@@ -53,18 +70,20 @@ func (q QueryHandler) GasConsumed() uint64 {
type CustomQuerier func(ctx sdk.Context, request json.RawMessage) ([]byte, error)
type QueryPlugins struct {
Bank func(ctx sdk.Context, request *wasmvmtypes.BankQuery) ([]byte, error)
Custom CustomQuerier
Staking func(ctx sdk.Context, request *wasmvmtypes.StakingQuery) ([]byte, error)
Wasm func(ctx sdk.Context, request *wasmvmtypes.WasmQuery) ([]byte, error)
Bank func(ctx sdk.Context, request *wasmvmtypes.BankQuery) ([]byte, error)
Custom CustomQuerier
Staking func(ctx sdk.Context, request *wasmvmtypes.StakingQuery) ([]byte, error)
Stargate func(ctx sdk.Context, request *wasmvmtypes.StargateQuery) ([]byte, error)
Wasm func(ctx sdk.Context, request *wasmvmtypes.WasmQuery) ([]byte, error)
}
func DefaultQueryPlugins(bank bankkeeper.ViewKeeper, staking stakingkeeper.Keeper, distKeeper distributionkeeper.Keeper, wasm *Keeper) QueryPlugins {
func DefaultQueryPlugins(bank bankkeeper.ViewKeeper, staking stakingkeeper.Keeper, distKeeper distributionkeeper.Keeper, queryRouter GRPCQueryRouter, wasm *Keeper) QueryPlugins {
return QueryPlugins{
Bank: BankQuerier(bank),
Custom: NoCustomQuerier,
Staking: StakingQuerier(staking, distKeeper),
Wasm: WasmQuerier(wasm),
Bank: BankQuerier(bank),
Custom: NoCustomQuerier,
Staking: StakingQuerier(staking, distKeeper),
Stargate: StargateQuerier(queryRouter),
Wasm: WasmQuerier(wasm),
}
}
@@ -82,6 +101,9 @@ func (e QueryPlugins) Merge(o *QueryPlugins) QueryPlugins {
if o.Staking != nil {
e.Staking = o.Staking
}
if o.Stargate != nil {
e.Stargate = o.Stargate
}
if o.Wasm != nil {
e.Wasm = o.Wasm
}
@@ -124,6 +146,24 @@ func NoCustomQuerier(sdk.Context, json.RawMessage) ([]byte, error) {
return nil, wasmvmtypes.UnsupportedRequest{Kind: "custom"}
}
func StargateQuerier(queryRouter GRPCQueryRouter) func(ctx sdk.Context, request *wasmvmtypes.StargateQuery) ([]byte, error) {
return func(ctx sdk.Context, msg *wasmvmtypes.StargateQuery) ([]byte, error) {
route := queryRouter.Route(msg.Path)
if route == nil {
return nil, wasmvmtypes.UnsupportedRequest{Kind: fmt.Sprintf("No route to query '%s'", msg.Path)}
}
req := abci.RequestQuery{
Data: msg.Data,
Path: msg.Path,
}
res, err := route(ctx, req)
if err != nil {
return nil, err
}
return res.Value, nil
}
}
func StakingQuerier(keeper stakingkeeper.Keeper, distKeeper distributionkeeper.Keeper) func(ctx sdk.Context, request *wasmvmtypes.StakingQuery) ([]byte, error) {
return func(ctx sdk.Context, request *wasmvmtypes.StakingQuery) ([]byte, error) {
if request.BondedDenom != nil {

View File

@@ -2,6 +2,7 @@ package keeper
import (
"encoding/json"
"github.com/golang/protobuf/proto"
"io/ioutil"
"strings"
"testing"
@@ -296,6 +297,79 @@ func TestMaskReflectCustomQuery(t *testing.T) {
assert.Equal(t, resp.Text, "ALL CAPS NOW")
}
func TestReflectStargateQuery(t *testing.T) {
cdc := MakeTestCodec(t)
ctx, keepers := CreateTestInput(t, false, MaskFeatures, maskEncoders(cdc), maskPlugins())
accKeeper, keeper, bankKeeper := keepers.AccountKeeper, keepers.WasmKeeper, keepers.BankKeeper
funds := sdk.NewCoins(sdk.NewInt64Coin("denom", 320000))
contractStart := sdk.NewCoins(sdk.NewInt64Coin("denom", 40000))
expectedBalance := funds.Sub(contractStart)
creator := createFakeFundedAccount(t, ctx, accKeeper, bankKeeper, funds)
// upload code
maskCode, err := ioutil.ReadFile("./testdata/reflect.wasm")
require.NoError(t, err)
codeID, err := keeper.Create(ctx, creator, maskCode, "", "", nil)
require.NoError(t, err)
require.Equal(t, uint64(1), codeID)
// creator instantiates a contract and gives it tokens
contractAddr, _, err := keeper.Instantiate(ctx, codeID, creator, nil, []byte("{}"), "mask contract 1", contractStart)
require.NoError(t, err)
require.NotEmpty(t, contractAddr)
// first, normal query for the bank balance (to make sure our query is proper)
bankQuery := wasmvmtypes.QueryRequest{
Bank: &wasmvmtypes.BankQuery{
AllBalances: &wasmvmtypes.AllBalancesQuery{
Address: creator.String(),
},
},
}
simpleQueryBz, err := json.Marshal(MaskQueryMsg{
Chain: &ChainQuery{Request: &bankQuery},
})
require.NoError(t, err)
simpleRes, err := keeper.QuerySmart(ctx, contractAddr, simpleQueryBz)
require.NoError(t, err)
var simpleChain ChainResponse
mustParse(t, simpleRes, &simpleChain)
var simpleBalance wasmvmtypes.AllBalancesResponse
mustParse(t, simpleChain.Data, &simpleBalance)
require.Equal(t, len(expectedBalance), len(simpleBalance.Amount))
assert.Equal(t, simpleBalance.Amount[0].Amount, expectedBalance[0].Amount.String())
assert.Equal(t, simpleBalance.Amount[0].Denom, expectedBalance[0].Denom)
// now, try to build a protobuf query
protoQuery := banktypes.QueryAllBalancesRequest{
Address: creator.String(),
}
protoQueryBin, err := proto.Marshal(&protoQuery)
protoRequest := wasmvmtypes.QueryRequest{
Stargate: &wasmvmtypes.StargateQuery{
Path: "/cosmos.bank.v1beta1.Query/AllBalances",
Data: protoQueryBin,
},
}
protoQueryBz, err := json.Marshal(MaskQueryMsg{
Chain: &ChainQuery{Request: &protoRequest},
})
require.NoError(t, err)
// make a query on the chain
protoRes, err := keeper.QuerySmart(ctx, contractAddr, protoQueryBz)
require.NoError(t, err)
var protoChain ChainResponse
mustParse(t, protoRes, &protoChain)
// unmarshal raw protobuf response
var protoResult banktypes.QueryAllBalancesResponse
err = proto.Unmarshal(protoChain.Data, &protoResult)
require.NoError(t, err)
assert.Equal(t, expectedBalance, protoResult.Balances)
}
type maskState struct {
Owner []byte `json:"owner"`
}

View File

@@ -255,6 +255,11 @@ func CreateTestInput(t *testing.T, isCheckTx bool, supportedFeatures string, enc
dh := distribution.NewHandler(distKeeper)
router.AddRoute(sdk.NewRoute(distributiontypes.RouterKey, dh))
querier := baseapp.NewGRPCQueryRouter()
banktypes.RegisterQueryServer(querier, bankKeeper)
stakingtypes.RegisterQueryServer(querier, stakingkeeper.Querier{Keeper: stakingKeeper})
distributiontypes.RegisterQueryServer(querier, distKeeper)
// Load default wasm config
wasmConfig := types.DefaultWasmConfig()
@@ -270,6 +275,7 @@ func CreateTestInput(t *testing.T, isCheckTx bool, supportedFeatures string, enc
&ibcKeeper.PortKeeper,
scopedWasmKeeper,
router,
querier,
tempDir,
wasmConfig,
supportedFeatures,