Skip to content

Commit 40cf990

Browse files
committed
Refactor Stargate querier init
1 parent 14aa31c commit 40cf990

File tree

6 files changed

+127
-72
lines changed

6 files changed

+127
-72
lines changed

x/wasm/keeper/keeper.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ func NewKeeper(
119119
capabilityKeeper types.CapabilityKeeper,
120120
portSource types.ICS20TransferPortSource,
121121
router MessageRouter,
122-
queryRouter GRPCQueryRouter,
122+
_ GRPCQueryRouter,
123123
homeDir string,
124124
wasmConfig types.WasmConfig,
125125
availableCapabilities string,
@@ -150,7 +150,7 @@ func NewKeeper(
150150
maxQueryStackSize: types.DefaultMaxQueryStackSize,
151151
acceptedAccountTypes: defaultAcceptedAccountTypes,
152152
}
153-
keeper.wasmVMQueryHandler = DefaultQueryPlugins(bankKeeper, stakingKeeper, distKeeper, channelKeeper, queryRouter, keeper, cdc)
153+
keeper.wasmVMQueryHandler = DefaultQueryPlugins(bankKeeper, stakingKeeper, distKeeper, channelKeeper, keeper)
154154
for _, o := range opts {
155155
o.apply(keeper)
156156
}

x/wasm/keeper/options.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ func WithQueryHandlerDecorator(d func(old WasmVMQueryHandler) WasmVMQueryHandler
5757
}
5858

5959
// WithQueryPlugins is an optional constructor parameter to pass custom query plugins for wasmVM requests.
60-
// This option expects the default `QueryHandler` set an should not be combined with Option `WithQueryHandler` or `WithQueryHandlerDecorator`.
60+
// This option expects the default `QueryHandler` set and should not be combined with Option `WithQueryHandler` or `WithQueryHandlerDecorator`.
6161
func WithQueryPlugins(x *QueryPlugins) Option {
6262
return optsFn(func(k *Keeper) {
6363
q, ok := k.wasmVMQueryHandler.(QueryPlugins)

x/wasm/keeper/query_plugins.go

+30-23
Original file line numberDiff line numberDiff line change
@@ -102,16 +102,14 @@ func DefaultQueryPlugins(
102102
staking types.StakingKeeper,
103103
distKeeper types.DistributionKeeper,
104104
channelKeeper types.ChannelKeeper,
105-
queryRouter GRPCQueryRouter,
106105
wasm wasmQueryKeeper,
107-
codec codec.Codec,
108106
) QueryPlugins {
109107
return QueryPlugins{
110108
Bank: BankQuerier(bank),
111109
Custom: NoCustomQuerier,
112110
IBC: IBCQuerier(wasm, channelKeeper),
113111
Staking: StakingQuerier(staking, distKeeper),
114-
Stargate: StargateQuerier(queryRouter, codec),
112+
Stargate: RejectStargateQuerier(),
115113
Wasm: WasmQuerier(wasm),
116114
}
117115
}
@@ -282,10 +280,30 @@ func IBCQuerier(wasm contractMetaDataSource, channelKeeper types.ChannelKeeper)
282280
}
283281
}
284282

285-
func StargateQuerier(queryRouter GRPCQueryRouter, codec codec.Codec) func(ctx sdk.Context, request *wasmvmtypes.StargateQuery) ([]byte, error) {
283+
// RejectStargateQuerier rejects all stargate queries
284+
func RejectStargateQuerier() func(ctx sdk.Context, request *wasmvmtypes.StargateQuery) ([]byte, error) {
286285
return func(ctx sdk.Context, request *wasmvmtypes.StargateQuery) ([]byte, error) {
287-
protoResponse, whitelisted := AcceptList.Load(request.Path)
288-
if !whitelisted {
286+
return nil, wasmvmtypes.UnsupportedRequest{Kind: "Stargate queries are disabled"}
287+
}
288+
}
289+
290+
// AcceptedStargateQueries define accepted Stargate queries as a map with path as key and response type as value.
291+
// For example:
292+
// acceptList["/cosmos.auth.v1beta1.Query/Account"]= &authtypes.QueryAccountResponse{}
293+
type AcceptedStargateQueries map[string]codec.ProtoMarshaler
294+
295+
// AcceptListStargateQuerier supports a preconfigured set of stargate queries only.
296+
// All arguments must be non nil.
297+
//
298+
// Warning: Chains need to test and maintain their accept list carefully.
299+
// There were critical consensus breaking issues in the past with non-deterministic behaviour in the SDK.
300+
//
301+
// This queries can be set via WithQueryPlugins option in the wasm keeper constructor:
302+
// WithQueryPlugins(&QueryPlugins{Stargate: AcceptListStargateQuerier(acceptList, queryRouter, codec)})
303+
func AcceptListStargateQuerier(acceptList AcceptedStargateQueries, queryRouter GRPCQueryRouter, codec codec.Codec) func(ctx sdk.Context, request *wasmvmtypes.StargateQuery) ([]byte, error) {
304+
return func(ctx sdk.Context, request *wasmvmtypes.StargateQuery) ([]byte, error) {
305+
protoResponse, accepted := acceptList[request.Path]
306+
if !accepted {
289307
return nil, wasmvmtypes.UnsupportedRequest{Kind: fmt.Sprintf("'%s' path is not allowed from the contract", request.Path)}
290308
}
291309

@@ -302,12 +320,7 @@ func StargateQuerier(queryRouter GRPCQueryRouter, codec codec.Codec) func(ctx sd
302320
return nil, err
303321
}
304322

305-
bz, err := ConvertProtoToJSONMarshal(protoResponse, res.Value, codec)
306-
if err != nil {
307-
return nil, err
308-
}
309-
310-
return bz, nil
323+
return ConvertProtoToJSONMarshal(codec, protoResponse, res.Value)
311324
}
312325
}
313326

@@ -557,22 +570,16 @@ func ConvertSdkCoinToWasmCoin(coin sdk.Coin) wasmvmtypes.Coin {
557570
// ConvertProtoToJSONMarshal unmarshals the given bytes into a proto message and then marshals it to json.
558571
// This is done so that clients calling stargate queries do not need to define their own proto unmarshalers,
559572
// being able to use response directly by json marshalling, which is supported in cosmwasm.
560-
func ConvertProtoToJSONMarshal(protoResponse interface{}, bz []byte, cdc codec.Codec) ([]byte, error) {
561-
// all values are proto message
562-
message, ok := protoResponse.(codec.ProtoMarshaler)
563-
if !ok {
564-
return nil, wasmvmtypes.Unknown{}
565-
}
566-
573+
func ConvertProtoToJSONMarshal(cdc codec.Codec, protoResponse codec.ProtoMarshaler, bz []byte) ([]byte, error) {
567574
// unmarshal binary into stargate response data structure
568-
err := cdc.Unmarshal(bz, message)
575+
err := cdc.Unmarshal(bz, protoResponse)
569576
if err != nil {
570-
return nil, wasmvmtypes.Unknown{}
577+
return nil, sdkerrors.Wrap(err, "to proto")
571578
}
572579

573-
bz, err = cdc.MarshalJSON(message)
580+
bz, err = cdc.MarshalJSON(protoResponse)
574581
if err != nil {
575-
return nil, wasmvmtypes.Unknown{}
582+
return nil, sdkerrors.Wrap(err, "to json")
576583
}
577584

578585
return bz, nil

x/wasm/keeper/query_plugins_test.go

+90-25
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,27 @@ import (
55
"encoding/json"
66
"fmt"
77
"testing"
8+
"time"
89

9-
"github.com/CosmWasm/wasmd/app"
10-
"google.golang.org/protobuf/runtime/protoiface"
11-
10+
wasmvmtypes "github.com/CosmWasm/wasmvm/types"
11+
"github.com/cosmos/cosmos-sdk/codec"
1212
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
13+
"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
1314
"github.com/cosmos/cosmos-sdk/store"
14-
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
15-
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
16-
"github.com/golang/protobuf/proto"
17-
dbm "github.com/tendermint/tm-db"
18-
19-
wasmvmtypes "github.com/CosmWasm/wasmvm/types"
2015
sdk "github.com/cosmos/cosmos-sdk/types"
2116
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
2217
"github.com/cosmos/cosmos-sdk/types/query"
18+
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
19+
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
20+
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
2321
channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types"
22+
"github.com/gogo/protobuf/proto"
2423
"github.com/stretchr/testify/assert"
2524
"github.com/stretchr/testify/require"
25+
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
26+
dbm "github.com/tendermint/tm-db"
2627

28+
"github.com/CosmWasm/wasmd/app"
2729
"github.com/CosmWasm/wasmd/x/wasm/keeper"
2830
"github.com/CosmWasm/wasmd/x/wasm/keeper/wasmtesting"
2931
"github.com/CosmWasm/wasmd/x/wasm/types"
@@ -486,6 +488,71 @@ func TestQueryErrors(t *testing.T) {
486488
}
487489
}
488490

491+
func TestAcceptListStargateQuerier(t *testing.T) {
492+
wasmApp := app.SetupWithEmptyStore(t)
493+
ctx := wasmApp.NewUncachedContext(false, tmproto.Header{ChainID: "foo", Height: 1, Time: time.Now()})
494+
wasmApp.StakingKeeper.SetParams(ctx, stakingtypes.DefaultParams())
495+
496+
addrs := app.AddTestAddrs(wasmApp, ctx, 2, sdk.NewInt(1_000_000))
497+
accepted := keeper.AcceptedStargateQueries{
498+
"/cosmos.auth.v1beta1.Query/Account": &authtypes.QueryAccountResponse{},
499+
"/no/route/to/this": &authtypes.QueryAccountResponse{},
500+
}
501+
502+
marshal := func(pb proto.Message) []byte {
503+
b, err := proto.Marshal(pb)
504+
require.NoError(t, err)
505+
return b
506+
}
507+
508+
specs := map[string]struct {
509+
req *wasmvmtypes.StargateQuery
510+
expErr bool
511+
expResp string
512+
}{
513+
"in accept list - success result": {
514+
req: &wasmvmtypes.StargateQuery{
515+
Path: "/cosmos.auth.v1beta1.Query/Account",
516+
Data: marshal(&authtypes.QueryAccountRequest{Address: addrs[0].String()}),
517+
},
518+
expResp: fmt.Sprintf(`{"account":{"@type":"/cosmos.auth.v1beta1.BaseAccount","address":%q,"pub_key":null,"account_number":"1","sequence":"0"}}`, addrs[0].String()),
519+
},
520+
"in accept list - error result": {
521+
req: &wasmvmtypes.StargateQuery{
522+
Path: "/cosmos.auth.v1beta1.Query/Account",
523+
Data: marshal(&authtypes.QueryAccountRequest{Address: sdk.AccAddress(ed25519.GenPrivKey().PubKey().Address()).String()}),
524+
},
525+
expErr: true,
526+
},
527+
"not in accept list": {
528+
req: &wasmvmtypes.StargateQuery{
529+
Path: "/cosmos.bank.v1beta1.Query/AllBalances",
530+
Data: marshal(&banktypes.QueryAllBalancesRequest{Address: addrs[0].String()}),
531+
},
532+
expErr: true,
533+
},
534+
"unknown route": {
535+
req: &wasmvmtypes.StargateQuery{
536+
Path: "/no/route/to/this",
537+
Data: marshal(&banktypes.QueryAllBalancesRequest{Address: addrs[0].String()}),
538+
},
539+
expErr: true,
540+
},
541+
}
542+
for name, spec := range specs {
543+
t.Run(name, func(t *testing.T) {
544+
q := keeper.AcceptListStargateQuerier(accepted, wasmApp.GRPCQueryRouter(), wasmApp.AppCodec())
545+
gotBz, gotErr := q(ctx, spec.req)
546+
if spec.expErr {
547+
require.Error(t, gotErr)
548+
return
549+
}
550+
require.NoError(t, gotErr)
551+
assert.JSONEq(t, spec.expResp, string(gotBz), string(gotBz))
552+
})
553+
}
554+
}
555+
489556
type mockWasmQueryKeeper struct {
490557
GetContractInfoFn func(ctx sdk.Context, contractAddress sdk.AccAddress) *types.ContractInfo
491558
QueryRawFn func(ctx sdk.Context, contractAddress sdk.AccAddress, key []byte) []byte
@@ -548,13 +615,13 @@ func (m bankKeeperMock) GetAllBalances(ctx sdk.Context, addr sdk.AccAddress) sdk
548615
return m.GetAllBalancesFn(ctx, addr)
549616
}
550617

551-
func TestConvertProtoToJSONMarshal(t *testing.T) {
618+
func TestCo3nvertProtoToJSONMarshal(t *testing.T) {
552619
testCases := []struct {
553620
name string
554621
queryPath string
555-
protoResponseStruct proto.Message
622+
protoResponseStruct codec.ProtoMarshaler
556623
originalResponse string
557-
expectedProtoResponse proto.Message
624+
expectedProtoResponse codec.ProtoMarshaler
558625
expectedError bool
559626
}{
560627
{
@@ -573,28 +640,26 @@ func TestConvertProtoToJSONMarshal(t *testing.T) {
573640
name: "invalid proto response struct",
574641
queryPath: "/cosmos.bank.v1beta1.Query/AllBalances",
575642
originalResponse: "0a090a036261721202333012050a03666f6f",
576-
protoResponseStruct: protoiface.MessageV1(nil),
643+
protoResponseStruct: &authtypes.QueryAccountResponse{},
577644
expectedError: true,
578645
},
579646
}
580647

581648
for _, tc := range testCases {
582649
t.Run(fmt.Sprintf("Case %s", tc.name), func(t *testing.T) {
583-
// set up app for testing
584-
wasmApp := app.SetupWithEmptyStore(t)
585-
586650
originalVersionBz, err := hex.DecodeString(tc.originalResponse)
587651
require.NoError(t, err)
652+
appCodec := app.MakeEncodingConfig().Marshaler
588653

589-
jsonMarshalledResponse, err := keeper.ConvertProtoToJSONMarshal(tc.protoResponseStruct, originalVersionBz, wasmApp.AppCodec())
654+
jsonMarshalledResponse, err := keeper.ConvertProtoToJSONMarshal(appCodec, tc.protoResponseStruct, originalVersionBz)
590655
if tc.expectedError {
591656
require.Error(t, err)
592657
return
593658
}
594659
require.NoError(t, err)
595660

596661
// check response by json marshalling proto response into json response manually
597-
jsonMarshalExpectedResponse, err := wasmApp.AppCodec().MarshalJSON(tc.expectedProtoResponse)
662+
jsonMarshalExpectedResponse, err := appCodec.MarshalJSON(tc.expectedProtoResponse)
598663
require.NoError(t, err)
599664
require.JSONEq(t, string(jsonMarshalledResponse), string(jsonMarshalExpectedResponse))
600665
})
@@ -609,8 +674,8 @@ func TestDeterministicJsonMarshal(t *testing.T) {
609674
originalResponse string
610675
updatedResponse string
611676
queryPath string
612-
responseProtoStruct interface{}
613-
expectedProto func() proto.Message
677+
responseProtoStruct codec.ProtoMarshaler
678+
expectedProto func() codec.ProtoMarshaler
614679
}{
615680
/**
616681
*
@@ -638,7 +703,7 @@ func TestDeterministicJsonMarshal(t *testing.T) {
638703
"0a530a202f636f736d6f732e617574682e763162657461312e426173654163636f756e74122f0a2d636f736d6f733166387578756c746e3873717a687a6e72737a3371373778776171756867727367366a79766679122d636f736d6f733166387578756c746e3873717a687a6e72737a3371373778776171756867727367366a79766679",
639704
"/cosmos.auth.v1beta1.Query/Account",
640705
&authtypes.QueryAccountResponse{},
641-
func() proto.Message {
706+
func() codec.ProtoMarshaler {
642707
account := authtypes.BaseAccount{
643708
Address: "cosmos1f8uxultn8sqzhznrsz3q77xwaquhgrsg6jyvfy",
644709
}
@@ -653,23 +718,23 @@ func TestDeterministicJsonMarshal(t *testing.T) {
653718

654719
for _, tc := range testCases {
655720
t.Run(fmt.Sprintf("Case %s", tc.name), func(t *testing.T) {
656-
wasmApp := app.SetupWithEmptyStore(t)
721+
appCodec := app.MakeEncodingConfig().Marshaler
657722

658723
originVersionBz, err := hex.DecodeString(tc.originalResponse)
659724
require.NoError(t, err)
660-
jsonMarshalledOriginalBz, err := keeper.ConvertProtoToJSONMarshal(tc.responseProtoStruct, originVersionBz, wasmApp.AppCodec())
725+
jsonMarshalledOriginalBz, err := keeper.ConvertProtoToJSONMarshal(appCodec, tc.responseProtoStruct, originVersionBz)
661726
require.NoError(t, err)
662727

663728
newVersionBz, err := hex.DecodeString(tc.updatedResponse)
664729
require.NoError(t, err)
665-
jsonMarshalledUpdatedBz, err := keeper.ConvertProtoToJSONMarshal(tc.responseProtoStruct, newVersionBz, wasmApp.AppCodec())
730+
jsonMarshalledUpdatedBz, err := keeper.ConvertProtoToJSONMarshal(appCodec, tc.responseProtoStruct, newVersionBz)
666731
require.NoError(t, err)
667732

668733
// json marshalled bytes should be the same since we use the same proto struct for unmarshalling
669734
require.Equal(t, jsonMarshalledOriginalBz, jsonMarshalledUpdatedBz)
670735

671736
// raw build also make same result
672-
jsonMarshalExpectedResponse, err := wasmApp.AppCodec().MarshalJSON(tc.expectedProto())
737+
jsonMarshalExpectedResponse, err := appCodec.MarshalJSON(tc.expectedProto())
673738
require.NoError(t, err)
674739
require.Equal(t, jsonMarshalledUpdatedBz, jsonMarshalExpectedResponse)
675740
})

x/wasm/keeper/stargate_whitelist.go

-18
This file was deleted.

x/wasm/simulation/proposals.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@ package simulation
33
import (
44
"math/rand"
55

6-
"github.com/CosmWasm/wasmd/app/params"
7-
"github.com/CosmWasm/wasmd/x/wasm/keeper/testdata"
8-
"github.com/CosmWasm/wasmd/x/wasm/types"
96
sdk "github.com/cosmos/cosmos-sdk/types"
107
simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
118
"github.com/cosmos/cosmos-sdk/x/simulation"
9+
10+
"github.com/CosmWasm/wasmd/app/params"
11+
"github.com/CosmWasm/wasmd/x/wasm/keeper/testdata"
12+
"github.com/CosmWasm/wasmd/x/wasm/types"
1213
)
1314

1415
const (

0 commit comments

Comments
 (0)