diff --git a/x/wasm/keeper/query_plugins.go b/x/wasm/keeper/query_plugins.go index 2ac014d63b..323f76347b 100644 --- a/x/wasm/keeper/query_plugins.go +++ b/x/wasm/keeper/query_plugins.go @@ -81,6 +81,7 @@ type wasmQueryKeeper interface { contractMetaDataSource QueryRaw(ctx sdk.Context, contractAddress sdk.AccAddress, key []byte) []byte QuerySmart(ctx sdk.Context, contractAddr sdk.AccAddress, req []byte) ([]byte, error) + IsPinnedCode(ctx sdk.Context, codeID uint64) bool } func DefaultQueryPlugins( @@ -459,21 +460,39 @@ func getAccumulatedRewards(ctx sdk.Context, distKeeper types.DistributionKeeper, return rewards, nil } -func WasmQuerier(wasm wasmQueryKeeper) func(ctx sdk.Context, request *wasmvmtypes.WasmQuery) ([]byte, error) { +func WasmQuerier(k wasmQueryKeeper) func(ctx sdk.Context, request *wasmvmtypes.WasmQuery) ([]byte, error) { return func(ctx sdk.Context, request *wasmvmtypes.WasmQuery) ([]byte, error) { - if request.Smart != nil { + switch { + case request.Smart != nil: addr, err := sdk.AccAddressFromBech32(request.Smart.ContractAddr) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, request.Smart.ContractAddr) } - return wasm.QuerySmart(ctx, addr, request.Smart.Msg) - } - if request.Raw != nil { + return k.QuerySmart(ctx, addr, request.Smart.Msg) + case request.Raw != nil: addr, err := sdk.AccAddressFromBech32(request.Raw.ContractAddr) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, request.Raw.ContractAddr) } - return wasm.QueryRaw(ctx, addr, request.Raw.Key), nil + return k.QueryRaw(ctx, addr, request.Raw.Key), nil + case request.ContractInfo != nil: + addr, err := sdk.AccAddressFromBech32(request.ContractInfo.ContractAddr) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, request.ContractInfo.ContractAddr) + } + info := k.GetContractInfo(ctx, addr) + if info == nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrUnknownAddress, request.ContractInfo.ContractAddr) + } + + res := wasmvmtypes.ContractInfoResponse{ + CodeID: info.CodeID, + Creator: info.Creator, + Admin: info.Admin, + Pinned: k.IsPinnedCode(ctx, info.CodeID), + IBCPort: info.IBCPortID, + } + return json.Marshal(res) } return nil, wasmvmtypes.UnsupportedRequest{Kind: "unknown WasmQuery variant"} } diff --git a/x/wasm/keeper/query_plugins_test.go b/x/wasm/keeper/query_plugins_test.go index e9068fa3f3..f3e6364179 100644 --- a/x/wasm/keeper/query_plugins_test.go +++ b/x/wasm/keeper/query_plugins_test.go @@ -69,7 +69,7 @@ func TestIBCQuerier(t *testing.T) { } specs := map[string]struct { srcQuery *wasmvmtypes.IBCQuery - wasmKeeper *wasmKeeperMock + wasmKeeper *mockWasmQueryKeeper channelKeeper *wasmtesting.MockChannelKeeper expJsonResult string expErr *sdkerrors.Error @@ -78,11 +78,11 @@ func TestIBCQuerier(t *testing.T) { srcQuery: &wasmvmtypes.IBCQuery{ PortID: &wasmvmtypes.PortIDQuery{}, }, - wasmKeeper: newWasmKeeperMock( - func(ctx sdk.Context, contractAddress sdk.AccAddress) *types.ContractInfo { + wasmKeeper: &mockWasmQueryKeeper{ + GetContractInfoFn: func(ctx sdk.Context, contractAddress sdk.AccAddress) *types.ContractInfo { return &types.ContractInfo{IBCPortID: "myIBCPortID"} }, - ), + }, channelKeeper: &wasmtesting.MockChannelKeeper{}, expJsonResult: `{"port_id":"myIBCPortID"}`, }, @@ -205,9 +205,11 @@ func TestIBCQuerier(t *testing.T) { ChannelID: "myQueryChannelID", }, }, - wasmKeeper: newWasmKeeperMock(func(ctx sdk.Context, contractAddress sdk.AccAddress) *types.ContractInfo { - return &types.ContractInfo{IBCPortID: "myLoadedPortID"} - }), + wasmKeeper: &mockWasmQueryKeeper{ + GetContractInfoFn: func(ctx sdk.Context, contractAddress sdk.AccAddress) *types.ContractInfo { + return &types.ContractInfo{IBCPortID: "myLoadedPortID"} + }, + }, channelKeeper: &wasmtesting.MockChannelKeeper{ GetChannelFn: func(ctx sdk.Context, srcPort, srcChan string) (channel channeltypes.Channel, found bool) { return channeltypes.Channel{ @@ -337,21 +339,142 @@ func TestBankQuerierBalance(t *testing.T) { assert.Equal(t, exp, got) } -type wasmKeeperMock struct { - GetContractInfoFn func(ctx sdk.Context, contractAddress sdk.AccAddress) *types.ContractInfo +func TestContractInfoWasmQuerier(t *testing.T) { + var myValidContractAddr = RandomBech32AccountAddress(t) + var myCreatorAddr = RandomBech32AccountAddress(t) + var myAdminAddr = RandomBech32AccountAddress(t) + var ctx sdk.Context + + specs := map[string]struct { + req *wasmvmtypes.WasmQuery + mock mockWasmQueryKeeper + expRes wasmvmtypes.ContractInfoResponse + expErr bool + }{ + "all good": { + req: &wasmvmtypes.WasmQuery{ + ContractInfo: &wasmvmtypes.ContractInfoQuery{ContractAddr: myValidContractAddr}, + }, + mock: mockWasmQueryKeeper{GetContractInfoFn: func(ctx sdk.Context, contractAddress sdk.AccAddress) *types.ContractInfo { + val := types.ContractInfoFixture(func(i *types.ContractInfo) { + i.Admin, i.Creator, i.IBCPortID = myAdminAddr, myCreatorAddr, "myIBCPort" + }) + return &val + }, + IsPinnedCodeFn: func(ctx sdk.Context, codeID uint64) bool { return true }, + }, + expRes: wasmvmtypes.ContractInfoResponse{ + CodeID: 1, + Creator: myCreatorAddr, + Admin: myAdminAddr, + Pinned: true, + IBCPort: "myIBCPort", + }, + }, + "invalid addr": { + req: &wasmvmtypes.WasmQuery{ + ContractInfo: &wasmvmtypes.ContractInfoQuery{ContractAddr: "not a valid addr"}, + }, + expErr: true, + }, + "unknown addr": { + req: &wasmvmtypes.WasmQuery{ + ContractInfo: &wasmvmtypes.ContractInfoQuery{ContractAddr: myValidContractAddr}, + }, + mock: mockWasmQueryKeeper{GetContractInfoFn: func(ctx sdk.Context, contractAddress sdk.AccAddress) *types.ContractInfo { + return nil + }}, + expErr: true, + }, + "not pinned": { + req: &wasmvmtypes.WasmQuery{ + ContractInfo: &wasmvmtypes.ContractInfoQuery{ContractAddr: myValidContractAddr}, + }, + mock: mockWasmQueryKeeper{GetContractInfoFn: func(ctx sdk.Context, contractAddress sdk.AccAddress) *types.ContractInfo { + val := types.ContractInfoFixture(func(i *types.ContractInfo) { + i.Admin, i.Creator = myAdminAddr, myCreatorAddr + }) + return &val + }, + IsPinnedCodeFn: func(ctx sdk.Context, codeID uint64) bool { return false }, + }, + expRes: wasmvmtypes.ContractInfoResponse{ + CodeID: 1, + Creator: myCreatorAddr, + Admin: myAdminAddr, + Pinned: false, + }, + }, + "without admin": { + req: &wasmvmtypes.WasmQuery{ + ContractInfo: &wasmvmtypes.ContractInfoQuery{ContractAddr: myValidContractAddr}, + }, + mock: mockWasmQueryKeeper{GetContractInfoFn: func(ctx sdk.Context, contractAddress sdk.AccAddress) *types.ContractInfo { + val := types.ContractInfoFixture(func(i *types.ContractInfo) { + i.Creator = myCreatorAddr + }) + return &val + }, + IsPinnedCodeFn: func(ctx sdk.Context, codeID uint64) bool { return true }, + }, + expRes: wasmvmtypes.ContractInfoResponse{ + CodeID: 1, + Creator: myCreatorAddr, + Pinned: true, + }, + }, + } + for name, spec := range specs { + t.Run(name, func(t *testing.T) { + q := WasmQuerier(spec.mock) + gotBz, gotErr := q(ctx, spec.req) + if spec.expErr { + require.Error(t, gotErr) + return + } + require.NoError(t, gotErr) + var gotRes wasmvmtypes.ContractInfoResponse + require.NoError(t, json.Unmarshal(gotBz, &gotRes)) + assert.Equal(t, spec.expRes, gotRes) + }) + } } -func newWasmKeeperMock(f func(ctx sdk.Context, contractAddress sdk.AccAddress) *types.ContractInfo) *wasmKeeperMock { - return &wasmKeeperMock{GetContractInfoFn: f} +type mockWasmQueryKeeper struct { + GetContractInfoFn func(ctx sdk.Context, contractAddress sdk.AccAddress) *types.ContractInfo + QueryRawFn func(ctx sdk.Context, contractAddress sdk.AccAddress, key []byte) []byte + QuerySmartFn func(ctx sdk.Context, contractAddr sdk.AccAddress, req []byte) ([]byte, error) + IsPinnedCodeFn func(ctx sdk.Context, codeID uint64) bool } -func (m wasmKeeperMock) GetContractInfo(ctx sdk.Context, contractAddress sdk.AccAddress) *types.ContractInfo { +func (m mockWasmQueryKeeper) GetContractInfo(ctx sdk.Context, contractAddress sdk.AccAddress) *types.ContractInfo { if m.GetContractInfoFn == nil { panic("not expected to be called") } return m.GetContractInfoFn(ctx, contractAddress) } +func (m mockWasmQueryKeeper) QueryRaw(ctx sdk.Context, contractAddress sdk.AccAddress, key []byte) []byte { + if m.QueryRawFn == nil { + panic("not expected to be called") + } + return m.QueryRawFn(ctx, contractAddress, key) +} + +func (m mockWasmQueryKeeper) QuerySmart(ctx sdk.Context, contractAddr sdk.AccAddress, req []byte) ([]byte, error) { + if m.QuerySmartFn == nil { + panic("not expected to be called") + } + return m.QuerySmartFn(ctx, contractAddr, req) +} + +func (m mockWasmQueryKeeper) IsPinnedCode(ctx sdk.Context, codeID uint64) bool { + if m.IsPinnedCodeFn == nil { + panic("not expected to be called") + } + return m.IsPinnedCodeFn(ctx, codeID) +} + type bankKeeperMock struct { GetBalanceFn func(ctx sdk.Context, addr sdk.AccAddress, denom string) sdk.Coin GetAllBalancesFn func(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins