Skip to content

Commit

Permalink
List contract address without other data
Browse files Browse the repository at this point in the history
  • Loading branch information
alpe committed Apr 23, 2021
1 parent 8ef2d26 commit 0d732e5
Show file tree
Hide file tree
Showing 15 changed files with 201 additions and 456 deletions.
2 changes: 1 addition & 1 deletion contrib/local/02-contracts.sh
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ wasmd tx wasm instantiate "$CODE_ID" "$INIT" --admin=$(wasmd keys show validator
--from validator --amount="100ustake" --label "local0.1.0" \
--gas 1000000 -y --chain-id=testing -b block | jq

CONTRACT=$(wasmd query wasm list-contract-by-code "$CODE_ID" -o json | jq -r '.contract_infos[-1].address')
CONTRACT=$(wasmd query wasm list-contract-by-code "$CODE_ID" -o json | jq -r '.contracts[-1]')
echo "* Contract address: $CONTRACT"
echo "### Query all"
RESP=$(wasmd query wasm contract-state all "$CONTRACT" -o json)
Expand Down
16 changes: 9 additions & 7 deletions contrib/local/03-grpc-queries.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,28 @@ set -o errexit -o nounset -o pipefail
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"

echo "-----------------------"
COSMOS_SDK_DIR=${COSMOS_SDK_DIR:-$(go list -f "{{ .Dir }}" -m github.com/cosmos/cosmos-sdk)}
PROTO_THRD="$DIR/../../third_party/proto"
PROTO_WASMD="$DIR/../../proto"
PROTO_WASMD_QUERY="$PROTO_WASMD/cosmwasm/wasm/v1beta1/query.proto"

echo "### List all codes"
RESP=$(grpcurl -plaintext -import-path $COSMOS_SDK_DIR/third_party/proto -import-path $COSMOS_SDK_DIR/proto -import-path . -proto ./x/wasm/types/query.proto \
RESP=$(grpcurl -plaintext -import-path $PROTO_THRD -import-path $PROTO_WASMD -proto $PROTO_WASMD_QUERY \
localhost:9090 cosmwasm.wasm.v1beta1.Query/Codes)
echo "$RESP" | jq

CODE_ID=$(echo "$RESP" | jq -r '.codeInfos[-1].codeId')
echo "### List contract by code"
RESP=$(grpcurl -plaintext -import-path $COSMOS_SDK_DIR/third_party/proto -import-path $COSMOS_SDK_DIR/proto -import-path . -proto ./x/wasm/types/query.proto \
echo "### List contracts by code"
RESP=$(grpcurl -plaintext -import-path $PROTO_THRD -import-path $PROTO_WASMD -proto $PROTO_WASMD_QUERY \
-d "{\"codeId\": $CODE_ID}" localhost:9090 cosmwasm.wasm.v1beta1.Query/ContractsByCode )
echo $RESP | jq

echo "### Show history for contract"
CONTRACT=$(echo $RESP | jq -r ".contractInfos[-1].address")
grpcurl -plaintext -import-path $COSMOS_SDK_DIR/third_party/proto -import-path $COSMOS_SDK_DIR/proto -import-path . -proto ./x/wasm/types/query.proto \
CONTRACT=$(echo $RESP | jq -r ".contracts[-1]")
grpcurl -plaintext -import-path $PROTO_THRD -import-path $PROTO_WASMD -proto $PROTO_WASMD_QUERY \
-d "{\"address\": \"$CONTRACT\"}" localhost:9090 cosmwasm.wasm.v1beta1.Query/ContractHistory | jq

echo "### Show contract state"
grpcurl -plaintext -import-path $COSMOS_SDK_DIR/third_party/proto -import-path $COSMOS_SDK_DIR/proto -import-path . -proto ./x/wasm/types/query.proto \
grpcurl -plaintext -import-path $PROTO_THRD -import-path $PROTO_WASMD -proto $PROTO_WASMD_QUERY \
-d "{\"address\": \"$CONTRACT\"}" localhost:9090 cosmwasm.wasm.v1beta1.Query/AllContractState | jq

echo "Empty state due to 'burner' contract cleanup"
20 changes: 1 addition & 19 deletions docs/proto/proto-docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@

- [cosmwasm/wasm/v1beta1/query.proto](#cosmwasm/wasm/v1beta1/query.proto)
- [CodeInfoResponse](#cosmwasm.wasm.v1beta1.CodeInfoResponse)
- [ContractInfoWithAddress](#cosmwasm.wasm.v1beta1.ContractInfoWithAddress)
- [QueryAllContractStateRequest](#cosmwasm.wasm.v1beta1.QueryAllContractStateRequest)
- [QueryAllContractStateResponse](#cosmwasm.wasm.v1beta1.QueryAllContractStateResponse)
- [QueryCodeRequest](#cosmwasm.wasm.v1beta1.QueryCodeRequest)
Expand Down Expand Up @@ -814,23 +813,6 @@ CodeInfoResponse contains code meta data from CodeInfo



<a name="cosmwasm.wasm.v1beta1.ContractInfoWithAddress"></a>

### ContractInfoWithAddress
ContractInfoWithAddress adds the address (key) to the ContractInfo
representation


| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| `address` | [string](#string) | | |
| `contract_info` | [ContractInfo](#cosmwasm.wasm.v1beta1.ContractInfo) | | |






<a name="cosmwasm.wasm.v1beta1.QueryAllContractStateRequest"></a>

### QueryAllContractStateRequest
Expand Down Expand Up @@ -1020,7 +1002,7 @@ Query/ContractsByCode RPC method

| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| `contract_infos` | [ContractInfoWithAddress](#cosmwasm.wasm.v1beta1.ContractInfoWithAddress) | repeated | |
| `contracts` | [string](#string) | repeated | contracts are a set of contract addresses |
| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | pagination defines the pagination in the response. |


Expand Down
17 changes: 3 additions & 14 deletions proto/cosmwasm/wasm/v1beta1/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -100,23 +100,12 @@ message QueryContractsByCodeRequest {
cosmos.base.query.v1beta1.PageRequest pagination = 2;
}

// ContractInfoWithAddress adds the address (key) to the ContractInfo
// representation
message ContractInfoWithAddress {
option (gogoproto.equal) = true;

string address = 1;
ContractInfo contract_info = 2 [
(gogoproto.embed) = true,
(gogoproto.nullable) = false,
(gogoproto.jsontag) = ""
];
}
// QueryContractsByCodeResponse is the response type for the
// Query/ContractsByCode RPC method
message QueryContractsByCodeResponse {
repeated ContractInfoWithAddress contract_infos = 1
[ (gogoproto.nullable) = false ];
// contracts are a set of contract addresses
repeated string contracts = 1;

// pagination defines the pagination in the response.
cosmos.base.query.v1beta1.PageResponse pagination = 2;
}
Expand Down
1 change: 0 additions & 1 deletion x/wasm/alias.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,6 @@ type (
ContractInfo = types.ContractInfo
CreatedAt = types.AbsoluteTxPosition
Config = types.WasmConfig
ContractInfoWithAddress = types.ContractInfoWithAddress
CodeInfoResponse = types.CodeInfoResponse
MessageHandler = keeper.SDKMessageHandler
BankEncoder = keeper.BankEncoder
Expand Down
11 changes: 11 additions & 0 deletions x/wasm/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,17 @@ func (k Keeper) removeFromContractCodeSecondaryIndex(ctx sdk.Context, contractAd
ctx.KVStore(k.storeKey).Delete(types.GetContractByCreatedSecondaryIndexKey(contractAddress, entry))
}

// IterateContractsByCode iterates over all contracts with given codeID ASC on code update time.
func (k Keeper) IterateContractsByCode(ctx sdk.Context, codeID uint64, cb func(address sdk.AccAddress) bool) {
prefixStore := prefix.NewStore(ctx.KVStore(k.storeKey), types.GetContractByCodeIDSecondaryIndexPrefix(codeID))
for iter := prefixStore.Iterator(nil, nil); iter.Valid(); iter.Next() {
key := iter.Key()
if cb(key[types.AbsoluteTxPositionLen:]) {
return
}
}
}

func (k Keeper) setContractAdmin(ctx sdk.Context, contractAddress, caller, newAdmin sdk.AccAddress, authZ AuthorizationPolicy) error {
contractInfo := k.GetContractInfo(ctx, contractAddress)
if contractInfo == nil {
Expand Down
41 changes: 41 additions & 0 deletions x/wasm/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1055,6 +1055,47 @@ func TestMigrateWithDispatchedMessage(t *testing.T) {
assert.Equal(t, deposit, balance)
}

func TestIterateContractsByCode(t *testing.T) {
ctx, keepers := CreateTestInput(t, false, SupportedFeatures)
k, c := keepers.WasmKeeper, keepers.ContractKeeper
example1 := InstantiateHackatomExampleContract(t, ctx, keepers)
ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1)
example2 := InstantiateIBCReflectContract(t, ctx, keepers)
ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1)
initMsg := HackatomExampleInitMsg{
Verifier: RandomAccountAddress(t),
Beneficiary: RandomAccountAddress(t),
}.GetBytes(t)
contractAddr3, _, err := c.Instantiate(ctx, example1.CodeID, example1.CreatorAddr, nil, initMsg, "foo", nil)
require.NoError(t, err)
specs := map[string]struct {
codeID uint64
exp []sdk.AccAddress
}{
"multiple results": {
codeID: example1.CodeID,
exp: []sdk.AccAddress{example1.Contract, contractAddr3},
},
"single results": {
codeID: example2.CodeID,
exp: []sdk.AccAddress{example2.Contract},
},
"empty results": {
codeID: 99999,
},
}
for name, spec := range specs {
t.Run(name, func(t *testing.T) {
var gotAddr []sdk.AccAddress
k.IterateContractsByCode(ctx, spec.codeID, func(address sdk.AccAddress) bool {
gotAddr = append(gotAddr, address)
return false
})
assert.Equal(t, spec.exp, gotAddr)
})
}
}

type sudoMsg struct {
// This is a tongue-in-check demo command. This is not the intended purpose of Sudo.
// Here we show that some priviledged Go module can make a call that should never be exposed
Expand Down
40 changes: 8 additions & 32 deletions x/wasm/keeper/legacy_querier.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,12 @@ package keeper

import (
"encoding/json"
"reflect"
"sort"
"strconv"
"strings"

"github.com/CosmWasm/wasmd/x/wasm/types"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
abci "github.com/tendermint/tendermint/abci/types"
"reflect"
"strconv"
)

const (
Expand Down Expand Up @@ -47,7 +44,7 @@ func NewLegacyQuerier(keeper types.ViewKeeper, gasLimit sdk.Gas) sdk.Querier {
if err != nil {
return nil, sdkerrors.Wrapf(types.ErrInvalid, "code id: %s", err.Error())
}
rsp, err = queryContractListByCode(ctx, codeID, keeper)
rsp = queryContractListByCode(ctx, codeID, keeper)
case QueryGetContractState:
if len(path) < 3 {
return nil, sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "unknown data query endpoint")
Expand Down Expand Up @@ -145,32 +142,11 @@ func queryContractHistory(ctx sdk.Context, contractAddr sdk.AccAddress, keeper t
return history, nil
}

func queryContractListByCode(ctx sdk.Context, codeID uint64, keeper types.ViewKeeper) ([]types.ContractInfoWithAddress, error) {
var contracts []types.ContractInfoWithAddress
keeper.IterateContractInfo(ctx, func(addr sdk.AccAddress, info types.ContractInfo) bool {
if info.CodeID == codeID {
// and add the address
infoWithAddress := types.ContractInfoWithAddress{
Address: addr.String(),
ContractInfo: info,
}
contracts = append(contracts, infoWithAddress)
}
func queryContractListByCode(ctx sdk.Context, codeID uint64, keeper types.ViewKeeper) []string {
var contracts []string
keeper.IterateContractsByCode(ctx, codeID, func(addr sdk.AccAddress) bool {
contracts = append(contracts, addr.String())
return false
})

// now we sort them by AbsoluteTxPosition
sort.Slice(contracts, func(i, j int) bool {
this := contracts[i].ContractInfo.Created
other := contracts[j].ContractInfo.Created
if this.Equal(other) {
return strings.Compare(contracts[i].Address, contracts[j].Address) < 0
}
return this.LessThan(other)
})

for i := range contracts {
contracts[i].Created = nil
}
return contracts, nil
return contracts
}
9 changes: 3 additions & 6 deletions x/wasm/keeper/legacy_querier_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,17 +205,14 @@ func TestLegacyQueryContractListByCodeOrdering(t *testing.T) {
res, err := q(ctx, query, data)
require.NoError(t, err)

var contracts []map[string]interface{}
var contracts []string
err = json.Unmarshal(res, &contracts)
require.NoError(t, err)

require.Equal(t, 10, len(contracts))

for i, contract := range contracts {
assert.Equal(t, fmt.Sprintf("contract %d", i), contract["label"])
assert.NotEmpty(t, contract["address"])
// ensure these are not shown
assert.Nil(t, contract["created"])
for _, contract := range contracts {
assert.NotEmpty(t, contract)
}
}

Expand Down
28 changes: 9 additions & 19 deletions x/wasm/keeper/querier.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,7 @@ func (q grpcQuerier) ContractInfo(c context.Context, req *types.QueryContractInf
case rsp == nil:
return nil, types.ErrNotFound
}
return &types.QueryContractInfoResponse{
Address: rsp.Address,
ContractInfo: rsp.ContractInfo,
}, nil
return rsp, nil
}

func (q grpcQuerier) ContractHistory(c context.Context, req *types.QueryContractHistoryRequest) (*types.QueryContractHistoryResponse, error) {
Expand Down Expand Up @@ -82,6 +79,7 @@ func (q grpcQuerier) ContractHistory(c context.Context, req *types.QueryContract
}, nil
}

// ContractsByCode lists all smart contracts for a code id
func (q grpcQuerier) ContractsByCode(c context.Context, req *types.QueryContractsByCodeRequest) (*types.QueryContractsByCodeResponse, error) {
if req == nil {
return nil, status.Error(codes.InvalidArgument, "empty request")
Expand All @@ -90,30 +88,22 @@ func (q grpcQuerier) ContractsByCode(c context.Context, req *types.QueryContract
return nil, sdkerrors.Wrap(types.ErrInvalid, "code id")
}
ctx := sdk.UnwrapSDKContext(c)
r := make([]types.ContractInfoWithAddress, 0)
r := make([]string, 0)

prefixStore := prefix.NewStore(ctx.KVStore(q.storeKey), types.GetContractByCodeIDSecondaryIndexPrefix(req.CodeId))
pageRes, err := query.FilteredPaginate(prefixStore, req.Pagination, func(key []byte, value []byte, accumulate bool) (bool, error) {
var contractAddr sdk.AccAddress = key[types.AbsoluteTxPositionLen:]
c := q.keeper.GetContractInfo(ctx, contractAddr)
if c == nil {
return false, types.ErrNotFound
}
c.Created = nil // redact
if accumulate {
r = append(r, types.ContractInfoWithAddress{
Address: contractAddr.String(),
ContractInfo: *c,
})
var contractAddr sdk.AccAddress = key[types.AbsoluteTxPositionLen:]
r = append(r, contractAddr.String())
}
return true, nil
})
if err != nil {
return nil, err
}
return &types.QueryContractsByCodeResponse{
ContractInfos: r,
Pagination: pageRes,
Contracts: r,
Pagination: pageRes,
}, nil
}

Expand Down Expand Up @@ -258,14 +248,14 @@ func (q grpcQuerier) Codes(c context.Context, req *types.QueryCodesRequest) (*ty
return &types.QueryCodesResponse{CodeInfos: r, Pagination: pageRes}, nil
}

func queryContractInfo(ctx sdk.Context, addr sdk.AccAddress, keeper types.ViewKeeper) (*types.ContractInfoWithAddress, error) {
func queryContractInfo(ctx sdk.Context, addr sdk.AccAddress, keeper types.ViewKeeper) (*types.QueryContractInfoResponse, error) {
info := keeper.GetContractInfo(ctx, addr)
if info == nil {
return nil, types.ErrNotFound
}
// redact the Created field (just used for sorting, not part of public API)
info.Created = nil
return &types.ContractInfoWithAddress{
return &types.QueryContractInfoResponse{
Address: addr.String(),
ContractInfo: *info,
}, nil
Expand Down
13 changes: 5 additions & 8 deletions x/wasm/keeper/querier_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -283,13 +283,13 @@ func TestQueryContractListByCodeOrdering(t *testing.T) {
}

// create 10 contracts with real block/gas setup
for i := range [10]int{} {
for i := 0; i < 10; i++ {
// 3 tx per block, so we ensure both comparisons work
if i%3 == 0 {
ctx = setBlock(ctx, h)
h++
}
_, _, err = keepers.ContractKeeper.Instantiate(ctx, codeID, creator, nil, initMsgBz, fmt.Sprintf("contract %d", i), topUp)
_, _, err = keepers.ContractKeeper.Instantiate(ctx, codeID, creator, nil, initMsgBz, fmt.Sprintf("contractAddr %d", i), topUp)
require.NoError(t, err)
}

Expand All @@ -298,13 +298,10 @@ func TestQueryContractListByCodeOrdering(t *testing.T) {
res, err := q.ContractsByCode(sdk.WrapSDKContext(ctx), &types.QueryContractsByCodeRequest{CodeId: codeID})
require.NoError(t, err)

require.Equal(t, 10, len(res.ContractInfos))
require.Equal(t, 10, len(res.Contracts))

for i, contract := range res.ContractInfos {
assert.Equal(t, fmt.Sprintf("contract %d", i), contract.Label)
assert.NotEmpty(t, contract.Address)
// ensure these are not shown
assert.Nil(t, contract.Created)
for _, contractAddr := range res.Contracts {
assert.NotEmpty(t, contractAddr)
}
}

Expand Down
10 changes: 8 additions & 2 deletions x/wasm/keeper/wasmtesting/mock_engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -271,10 +271,16 @@ type contractExecutable interface {
) (*wasmvmtypes.Response, uint64, error)
}

//MakeIBCInstantiable adds some noop functions to not fail when contract is used for instantiation
func MakeIBCInstantiable(m *MockWasmer) {
//MakeInstantiable adds some noop functions to not fail when contract is used for instantiation
func MakeInstantiable(m *MockWasmer) {
m.CreateFn = HashOnlyCreateFn
m.InstantiateFn = NoOpInstantiateFn
m.AnalyzeCodeFn = WithoutIBCAnalyzeFn
}

//MakeIBCInstantiable adds some noop functions to not fail when contract is used for instantiation
func MakeIBCInstantiable(m *MockWasmer) {
MakeInstantiable(m)
m.AnalyzeCodeFn = HasIBCAnalyzeFn
}

Expand Down
Loading

0 comments on commit 0d732e5

Please sign in to comment.