Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Get Contracts by Creator Address #1021

Merged
merged 37 commits into from
Oct 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
0473927
add query to query.proto
vuong177 Sep 23, 2022
0c628d2
add ContractsByCreatorPrefix in keys.go
vuong177 Sep 23, 2022
8e40535
add ContractCreatorThirdIndex to keeper.go
vuong177 Sep 23, 2022
6fd112a
add querier
vuong177 Sep 23, 2022
f324e42
cli
vuong177 Sep 23, 2022
21e5ca4
fix test
vuong177 Sep 24, 2022
fa51c0d
linting
vuong177 Sep 25, 2022
f856dc3
add key test
vuong177 Sep 25, 2022
be53b48
no need to change creator when migrate
vuong177 Sep 25, 2022
94c0dbf
add query test
vuong177 Sep 25, 2022
e0a7384
minor
vuong177 Sep 25, 2022
46b58b7
add migrate logic
vuong177 Sep 25, 2022
f4cfbe8
add more test
vuong177 Sep 25, 2022
c871915
register migration
vuong177 Sep 25, 2022
6bfeb16
minor
vuong177 Sep 25, 2022
86692b5
Update x/wasm/client/cli/query.go
vuong177 Sep 27, 2022
ffbbdf1
nits
vuong177 Sep 27, 2022
fd757f2
remove IterateAllContract
vuong177 Sep 27, 2022
a8c9e34
Update x/wasm/keeper/genesis_test.go
vuong177 Sep 27, 2022
54f157d
nit
vuong177 Sep 27, 2022
df06994
nit: func name
vuong177 Sep 27, 2022
93e79f5
change key
vuong177 Sep 27, 2022
2b732cf
improve TestIteratorContractByCreator
vuong177 Sep 27, 2022
79d6ff6
fix test
vuong177 Sep 27, 2022
0a800f6
use IterateContractInfo in migrate2to3
vuong177 Sep 28, 2022
86e2f54
minor
vuong177 Sep 28, 2022
247487f
move key
vuong177 Sep 28, 2022
f9a53f2
improve test case
vuong177 Sep 28, 2022
c4136b5
add pagReq in ContractsByCreator query
vuong177 Sep 28, 2022
b6241b3
ordering query
vuong177 Sep 29, 2022
c060045
add migrate test
vuong177 Sep 30, 2022
dfbdfc2
Make ContractsByCreator plural; formatting and minor updates
alpe Oct 6, 2022
fd39267
Comment why AbsoluteTxPositionLen makes sense
alpe Oct 6, 2022
5aa7d1c
Migrate 1 to 2
alpe Oct 6, 2022
a05e447
Merge branch 'main' into 1021_polished
alpe Oct 6, 2022
52ee675
Set module version
alpe Oct 6, 2022
6828aa2
Merge branch 'main' into contractsbycrator
catShaark Oct 20, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions docs/proto/proto-docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@
- [QueryContractInfoResponse](#cosmwasm.wasm.v1.QueryContractInfoResponse)
- [QueryContractsByCodeRequest](#cosmwasm.wasm.v1.QueryContractsByCodeRequest)
- [QueryContractsByCodeResponse](#cosmwasm.wasm.v1.QueryContractsByCodeResponse)
- [QueryContractsByCreatorRequest](#cosmwasm.wasm.v1.QueryContractsByCreatorRequest)
- [QueryContractsByCreatorResponse](#cosmwasm.wasm.v1.QueryContractsByCreatorResponse)
- [QueryParamsRequest](#cosmwasm.wasm.v1.QueryParamsRequest)
- [QueryParamsResponse](#cosmwasm.wasm.v1.QueryParamsResponse)
- [QueryPinnedCodesRequest](#cosmwasm.wasm.v1.QueryPinnedCodesRequest)
Expand Down Expand Up @@ -1131,6 +1133,40 @@ Query/ContractsByCode RPC method



<a name="cosmwasm.wasm.v1.QueryContractsByCreatorRequest"></a>

### QueryContractsByCreatorRequest
QueryContractsByCreatorRequest is the request type for the
Query/ContractsByCreator RPC method.


| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| `creator_address` | [string](#string) | | CreatorAddress is the address of contract creator |
| `pagination` | [cosmos.base.query.v1beta1.PageRequest](#cosmos.base.query.v1beta1.PageRequest) | | Pagination defines an optional pagination for the request. |






<a name="cosmwasm.wasm.v1.QueryContractsByCreatorResponse"></a>

### QueryContractsByCreatorResponse
QueryContractsByCreatorResponse is the response type for the
Query/ContractsByCreator RPC method.


| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| `contract_addresses` | [string](#string) | repeated | ContractAddresses result set |
| `pagination` | [cosmos.base.query.v1beta1.PageResponse](#cosmos.base.query.v1beta1.PageResponse) | | Pagination defines the pagination in the response. |






<a name="cosmwasm.wasm.v1.QueryParamsRequest"></a>

### QueryParamsRequest
Expand Down Expand Up @@ -1278,6 +1314,7 @@ Query provides defines the gRPC querier service
| `Codes` | [QueryCodesRequest](#cosmwasm.wasm.v1.QueryCodesRequest) | [QueryCodesResponse](#cosmwasm.wasm.v1.QueryCodesResponse) | Codes gets the metadata for all stored wasm codes | GET|/cosmwasm/wasm/v1/code|
| `PinnedCodes` | [QueryPinnedCodesRequest](#cosmwasm.wasm.v1.QueryPinnedCodesRequest) | [QueryPinnedCodesResponse](#cosmwasm.wasm.v1.QueryPinnedCodesResponse) | PinnedCodes gets the pinned code ids | GET|/cosmwasm/wasm/v1/codes/pinned|
| `Params` | [QueryParamsRequest](#cosmwasm.wasm.v1.QueryParamsRequest) | [QueryParamsResponse](#cosmwasm.wasm.v1.QueryParamsResponse) | Params gets the module params | GET|/cosmwasm/wasm/v1/codes/params|
| `ContractsByCreator` | [QueryContractsByCreatorRequest](#cosmwasm.wasm.v1.QueryContractsByCreatorRequest) | [QueryContractsByCreatorResponse](#cosmwasm.wasm.v1.QueryContractsByCreatorResponse) | ContractsByCreator gets the contracts by creator | GET|/cosmwasm/wasm/v1/contracts/creator/{creator_address}|

<!-- end services -->

Expand Down
25 changes: 25 additions & 0 deletions proto/cosmwasm/wasm/v1/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,13 @@ service Query {
rpc Params(QueryParamsRequest) returns (QueryParamsResponse) {
option (google.api.http).get = "/cosmwasm/wasm/v1/codes/params";
}

// ContractsByCreator gets the contracts by creator
rpc ContractsByCreator(QueryContractsByCreatorRequest)
returns (QueryContractsByCreatorResponse) {
option (google.api.http).get =
"/cosmwasm/wasm/v1/contracts/creator/{creator_address}";
}
}

// QueryContractInfoRequest is the request type for the Query/ContractInfo RPC
Expand Down Expand Up @@ -236,3 +243,21 @@ message QueryParamsResponse {
// params defines the parameters of the module.
Params params = 1 [ (gogoproto.nullable) = false ];
}

// QueryContractsByCreatorRequest is the request type for the
// Query/ContractsByCreator RPC method.
message QueryContractsByCreatorRequest {
// CreatorAddress is the address of contract creator
string creator_address = 1;
// Pagination defines an optional pagination for the request.
cosmos.base.query.v1beta1.PageRequest pagination = 2;
}

// QueryContractsByCreatorResponse is the response type for the
// Query/ContractsByCreator RPC method.
message QueryContractsByCreatorResponse {
// ContractAddresses result set
repeated string contract_addresses = 1;
// Pagination defines the pagination in the response.
cosmos.base.query.v1beta1.PageResponse pagination = 2;
}
40 changes: 40 additions & 0 deletions x/wasm/client/cli/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ func GetQueryCmd() *cobra.Command {
GetCmdLibVersion(),
GetCmdQueryParams(),
GetCmdBuildAddress(),
GetCmdListContractsByCreator(),
)
return queryCmd
}
Expand Down Expand Up @@ -528,6 +529,45 @@ func GetCmdListPinnedCode() *cobra.Command {
return cmd
}

// GetCmdListContractsByCreator lists all contracts by creator
func GetCmdListContractsByCreator() *cobra.Command {
cmd := &cobra.Command{
Use: "list-contracts-by-creator [creator]",
Short: "List all contracts by creator",
Long: "\t\tLong: List all contracts by creator,\n",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}
_, err = sdk.AccAddressFromBech32(args[0])
if err != nil {
return err
}
pageReq, err := client.ReadPageRequest(withPageKeyDecoded(cmd.Flags()))
if err != nil {
return err
}

queryClient := types.NewQueryClient(clientCtx)
res, err := queryClient.ContractsByCreator(
context.Background(),
&types.QueryContractsByCreatorRequest{
CreatorAddress: args[0],
vuong177 marked this conversation as resolved.
Show resolved Hide resolved
Pagination: pageReq,
},
)
if err != nil {
return err
}
return clientCtx.PrintProto(res)
},
}
flags.AddQueryFlagsToCmd(cmd)
return cmd
}

type argumentDecoder struct {
// dec is the default decoder
dec func(string) ([]byte, error)
Expand Down
3 changes: 3 additions & 0 deletions x/wasm/keeper/genesis_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,9 @@ func TestGenesisExportImport(t *testing.T) {

// reset contract code index in source DB for comparison with dest DB
wasmKeeper.IterateContractInfo(srcCtx, func(address sdk.AccAddress, info wasmTypes.ContractInfo) bool {
creatorAddress := sdk.MustAccAddressFromBech32(info.Creator)
wasmKeeper.removeFromContractCodeSecondaryIndex(srcCtx, address, wasmKeeper.getLastContractHistoryEntry(srcCtx, address))

prefixStore := prefix.NewStore(srcCtx.KVStore(wasmKeeper.storeKey), types.GetContractCodeHistoryElementPrefix(address))
iter := prefixStore.Iterator(nil, nil)

Expand All @@ -121,6 +123,7 @@ func TestGenesisExportImport(t *testing.T) {
newHistory := x.ResetFromGenesis(dstCtx)
wasmKeeper.storeContractInfo(srcCtx, address, x)
wasmKeeper.addToContractCodeSecondaryIndex(srcCtx, address, newHistory)
wasmKeeper.addToContractCreatorSecondaryIndex(srcCtx, creatorAddress, newHistory.Updated, address)
wasmKeeper.appendToContractHistory(srcCtx, address, newHistory)
iter.Close()
return false
Expand Down
24 changes: 23 additions & 1 deletion x/wasm/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,7 @@ func (k Keeper) instantiate(
// store contract before dispatch so that contract could be called back
historyEntry := contractInfo.InitialHistory(initMsg)
k.addToContractCodeSecondaryIndex(ctx, contractAddress, historyEntry)
k.addToContractCreatorSecondaryIndex(ctx, creator, historyEntry.Updated, contractAddress)
k.appendToContractHistory(ctx, contractAddress, historyEntry)
k.storeContractInfo(ctx, contractAddress, &contractInfo)

Expand Down Expand Up @@ -489,7 +490,6 @@ func (k Keeper) migrate(ctx sdk.Context, contractAddress sdk.AccAddress, caller
if err != nil {
return nil, sdkerrors.Wrap(types.ErrMigrationFailed, err.Error())
}

// delete old secondary index entry
k.removeFromContractCodeSecondaryIndex(ctx, contractAddress, k.getLastContractHistoryEntry(ctx, contractAddress))
// persist migration updates
Expand Down Expand Up @@ -596,6 +596,23 @@ func (k Keeper) removeFromContractCodeSecondaryIndex(ctx sdk.Context, contractAd
ctx.KVStore(k.storeKey).Delete(types.GetContractByCreatedSecondaryIndexKey(contractAddress, entry))
}

// addToContractCreatorSecondaryIndex adds element to the index for contracts-by-creator queries
func (k Keeper) addToContractCreatorSecondaryIndex(ctx sdk.Context, creatorAddress sdk.AccAddress, position *types.AbsoluteTxPosition, contractAddress sdk.AccAddress) {
store := ctx.KVStore(k.storeKey)
store.Set(types.GetContractByCreatorSecondaryIndexKey(creatorAddress, position.Bytes(), contractAddress), []byte{})
}

// IterateContractsByCreator iterates over all contracts with given creator address in order of creation time asc.
func (k Keeper) IterateContractsByCreator(ctx sdk.Context, creator sdk.AccAddress, cb func(address sdk.AccAddress) bool) {
prefixStore := prefix.NewStore(ctx.KVStore(k.storeKey), types.GetContractsByCreatorPrefix(creator))
for iter := prefixStore.Iterator(nil, nil); iter.Valid(); iter.Next() {
key := iter.Key()
if cb(key[types.AbsoluteTxPositionLen:]) {
return
}
}
}

// 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))
Expand Down Expand Up @@ -1051,10 +1068,15 @@ func (k Keeper) importContract(ctx sdk.Context, contractAddr sdk.AccAddress, c *
return sdkerrors.Wrapf(types.ErrDuplicate, "contract: %s", contractAddr)
}

creatorAddress, err := sdk.AccAddressFromBech32(c.Creator)
if err != nil {
return err
}
historyEntry := c.ResetFromGenesis(ctx)
k.appendToContractHistory(ctx, contractAddr, historyEntry)
k.storeContractInfo(ctx, contractAddr, c)
k.addToContractCodeSecondaryIndex(ctx, contractAddr, historyEntry)
k.addToContractCreatorSecondaryIndex(ctx, creatorAddress, historyEntry.Updated, contractAddr)
return k.importContractState(ctx, contractAddr, state)
}

Expand Down
92 changes: 91 additions & 1 deletion x/wasm/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,7 @@ func TestInstantiate(t *testing.T) {

gasAfter := ctx.GasMeter().GasConsumed()
if types.EnableGasVerification {
require.Equal(t, uint64(0x1964f), gasAfter-gasBefore)
require.Equal(t, uint64(0x1a7bb), gasAfter-gasBefore)
}

// ensure it is stored properly
Expand Down Expand Up @@ -2188,3 +2188,93 @@ func TestCoinBurnerPruneBalances(t *testing.T) {
})
}
}

func TestIteratorAllContract(t *testing.T) {
ctx, keepers := CreateTestInput(t, false, AvailableCapabilities)
example1 := InstantiateHackatomExampleContract(t, ctx, keepers)
example2 := InstantiateHackatomExampleContract(t, ctx, keepers)
example3 := InstantiateHackatomExampleContract(t, ctx, keepers)
example4 := InstantiateHackatomExampleContract(t, ctx, keepers)

var allContract []string
keepers.WasmKeeper.IterateContractInfo(ctx, func(addr sdk.AccAddress, _ types.ContractInfo) bool {
allContract = append(allContract, addr.String())
return false
})

// IterateContractInfo not ordering
expContracts := []string{example4.Contract.String(), example2.Contract.String(), example1.Contract.String(), example3.Contract.String()}
require.Equal(t, allContract, expContracts)
}
vuong177 marked this conversation as resolved.
Show resolved Hide resolved

func TestIteratorContractByCreator(t *testing.T) {
// setup test
parentCtx, keepers := CreateTestInput(t, false, AvailableCapabilities)
keeper := keepers.ContractKeeper

depositFund := sdk.NewCoins(sdk.NewInt64Coin("denom", 1000000))
topUp := sdk.NewCoins(sdk.NewInt64Coin("denom", 5000))
creator := DeterministicAccountAddress(t, 1)
keepers.Faucet.Fund(parentCtx, creator, depositFund.Add(depositFund...)...)
mockAddress1 := keepers.Faucet.NewFundedRandomAccount(parentCtx, topUp...)
mockAddress2 := keepers.Faucet.NewFundedRandomAccount(parentCtx, topUp...)
mockAddress3 := keepers.Faucet.NewFundedRandomAccount(parentCtx, topUp...)

contract1ID, _, err := keeper.Create(parentCtx, creator, hackatomWasm, nil)
contract2ID, _, err := keeper.Create(parentCtx, creator, hackatomWasm, nil)

require.NoError(t, err)

initMsgBz := HackatomExampleInitMsg{
Verifier: mockAddress1,
Beneficiary: mockAddress1,
}.GetBytes(t)

depositContract := sdk.NewCoins(sdk.NewCoin("denom", sdk.NewInt(1_000)))

gotAddr1, _, _ := keepers.ContractKeeper.Instantiate(parentCtx, contract1ID, mockAddress1, nil, initMsgBz, "label", depositContract)
ctx := parentCtx.WithBlockHeight(parentCtx.BlockHeight() + 1)
gotAddr2, _, _ := keepers.ContractKeeper.Instantiate(ctx, contract1ID, mockAddress2, nil, initMsgBz, "label", depositContract)
ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1)
gotAddr3, _, _ := keepers.ContractKeeper.Instantiate(ctx, contract1ID, gotAddr1, nil, initMsgBz, "label", depositContract)
ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1)
gotAddr4, _, _ := keepers.ContractKeeper.Instantiate(ctx, contract2ID, mockAddress2, nil, initMsgBz, "label", depositContract)
ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1)
gotAddr5, _, _ := keepers.ContractKeeper.Instantiate(ctx, contract2ID, mockAddress2, nil, initMsgBz, "label", depositContract)

specs := map[string]struct {
creatorAddr sdk.AccAddress
contractsAddr []string
}{
"single contract": {
creatorAddr: mockAddress1,
contractsAddr: []string{gotAddr1.String()},
},
"multiple contracts": {
creatorAddr: mockAddress2,
contractsAddr: []string{gotAddr2.String(), gotAddr4.String(), gotAddr5.String()},
},
"contractAdress": {
creatorAddr: gotAddr1,
contractsAddr: []string{gotAddr3.String()},
},
"no contracts- unknown": {
creatorAddr: mockAddress3,
contractsAddr: nil,
},
vuong177 marked this conversation as resolved.
Show resolved Hide resolved
}

for name, spec := range specs {
t.Run(name, func(t *testing.T) {
var allContract []string
keepers.WasmKeeper.IterateContractsByCreator(parentCtx, spec.creatorAddr, func(addr sdk.AccAddress) bool {
allContract = append(allContract, addr.String())
return false
})
require.Equal(t,
allContract,
spec.contractsAddr,
)
})
}
}
61 changes: 61 additions & 0 deletions x/wasm/keeper/migrate_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package keeper

import (
"bytes"
"encoding/json"
"testing"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/address"
"github.com/stretchr/testify/require"

"github.com/CosmWasm/wasmd/x/wasm/types"
)

func TestMigrate1To2(t *testing.T) {
ctx, keepers := CreateTestInput(t, false, AvailableCapabilities)
wasmKeeper := keepers.WasmKeeper

deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000))
creator := sdk.AccAddress(bytes.Repeat([]byte{1}, address.Len))
keepers.Faucet.Fund(ctx, creator, deposit...)
example := StoreHackatomExampleContract(t, ctx, keepers)

initMsg := HackatomExampleInitMsg{
Verifier: RandomAccountAddress(t),
Beneficiary: RandomAccountAddress(t),
}
initMsgBz, err := json.Marshal(initMsg)
require.NoError(t, err)

em := sdk.NewEventManager()

// create with no balance is also legal
gotContractAddr1, _, err := keepers.ContractKeeper.Instantiate(ctx.WithEventManager(em), example.CodeID, creator, nil, initMsgBz, "demo contract 1", nil)
ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1)
gotContractAddr2, _, err := keepers.ContractKeeper.Instantiate(ctx.WithEventManager(em), example.CodeID, creator, nil, initMsgBz, "demo contract 1", nil)
ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1)
gotContractAddr3, _, err := keepers.ContractKeeper.Instantiate(ctx.WithEventManager(em), example.CodeID, creator, nil, initMsgBz, "demo contract 1", nil)

info1 := wasmKeeper.GetContractInfo(ctx, gotContractAddr1)
info2 := wasmKeeper.GetContractInfo(ctx, gotContractAddr2)
info3 := wasmKeeper.GetContractInfo(ctx, gotContractAddr3)

// remove key
ctx.KVStore(wasmKeeper.storeKey).Delete(types.GetContractByCreatorSecondaryIndexKey(creator, info1.Created.Bytes(), gotContractAddr1))
ctx.KVStore(wasmKeeper.storeKey).Delete(types.GetContractByCreatorSecondaryIndexKey(creator, info2.Created.Bytes(), gotContractAddr2))
ctx.KVStore(wasmKeeper.storeKey).Delete(types.GetContractByCreatorSecondaryIndexKey(creator, info3.Created.Bytes(), gotContractAddr3))

// migrator
migrator := NewMigrator(*wasmKeeper)
migrator.Migrate1to2(ctx)

// check new store
var allContract []string
wasmKeeper.IterateContractsByCreator(ctx, creator, func(addr sdk.AccAddress) bool {
allContract = append(allContract, addr.String())
return false
})

require.Equal(t, []string{gotContractAddr1.String(), gotContractAddr2.String(), gotContractAddr3.String()}, allContract)
}
Loading