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 29 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
20 changes: 20 additions & 0 deletions proto/cosmwasm/wasm/v1/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ 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 +241,18 @@ message QueryParamsResponse {
// params defines the parameters of the module.
Params params = 1 [ (gogoproto.nullable) = false ];
}

message QueryContractsByCreatorRequest {
// address is the address of creator
string creator_address = 1;
// pagination defines an optional pagination for the request.
cosmos.base.query.v1beta1.PageRequest pagination = 2;
}

message QueryContractsByCreatorResponse {
repeated string contract_address = 1
[ (gogoproto.customname) = "ContractAddress" ];
// 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],
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, 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, 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, contractAddress sdk.AccAddress) {
store := ctx.KVStore(k.storeKey)
store.Set(types.GetContractByCreatorSecondaryIndexKey(creatorAddress, contractAddress), []byte{})
}

// IterateContractsByCreator iterates over all contracts with given creator address.
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) {
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, contractAddr)
return k.importContractState(ctx, contractAddr, state)
}

Expand Down
88 changes: 87 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(0x1a5db), gasAfter-gasBefore)
}

// ensure it is stored properly
Expand Down Expand Up @@ -2188,3 +2188,89 @@ 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)
}

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)
gotAddr2, _, _ := keepers.ContractKeeper.Instantiate(parentCtx, contract1ID, mockAddress2, nil, initMsgBz, "label", depositContract)
gotAddr3, _, _ := keepers.ContractKeeper.Instantiate(parentCtx, contract1ID, gotAddr1, nil, initMsgBz, "label", depositContract)
gotAddr4, _, _ := keepers.ContractKeeper.Instantiate(parentCtx, contract2ID, mockAddress2, nil, initMsgBz, "label", depositContract)
gotAddr5, _, _ := keepers.ContractKeeper.Instantiate(parentCtx, contract2ID, mockAddress2, nil, initMsgBz, "label", depositContract)

specs := map[string]struct {
creatorAddr sdk.AccAddress
contractsAddr []string
}{
"single contract": {
creatorAddr: mockAddress1,
contractsAddr: []string{gotAddr1.String()},
},
"muliple contracts": {
creatorAddr: mockAddress2,
contractsAddr: []string{gotAddr2.String(), gotAddr5.String(), gotAddr4.String()},
},
"contractAdress": {
creatorAddr: gotAddr1,
contractsAddr: []string{gotAddr3.String()},
},
"no contracts- unknown": {
creatorAddr: mockAddress3,
contractsAddr: nil,
},
}

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,
)
})
}
}
26 changes: 26 additions & 0 deletions x/wasm/keeper/migrations.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package keeper

import (
"github.com/CosmWasm/wasmd/x/wasm/types"
sdk "github.com/cosmos/cosmos-sdk/types"
)

// Migrator is a struct for handling in-place store migrations.
type Migrator struct {
keeper Keeper
}

// NewMigrator returns a new Migrator.
func NewMigrator(keeper Keeper) Migrator {
return Migrator{keeper: keeper}
}

// Migrate2to3 migrates from version 2 to 3.
func (m Migrator) Migrate2to3(ctx sdk.Context) error {
m.keeper.IterateContractInfo(ctx, func(contractAddr sdk.AccAddress, contractInfo types.ContractInfo) bool {
creator := sdk.MustAccAddressFromBech32(contractInfo.Creator)
m.keeper.addToContractCreatorSecondaryIndex(ctx, creator, contractAddr)
return false
})
return nil
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A test for the migration is required.

26 changes: 26 additions & 0 deletions x/wasm/keeper/querier.go
Original file line number Diff line number Diff line change
Expand Up @@ -318,3 +318,29 @@ func (q grpcQuerier) Params(c context.Context, req *types.QueryParamsRequest) (*
params := q.keeper.GetParams(ctx)
return &types.QueryParamsResponse{Params: params}, nil
}

func (q grpcQuerier) ContractsByCreator(c context.Context, req *types.QueryContractsByCreatorRequest) (*types.QueryContractsByCreatorResponse, error) {
if req == nil {
return nil, status.Error(codes.InvalidArgument, "empty request")
}
ctx := sdk.UnwrapSDKContext(c)
contracts := make([]string, 0)

creatorAddress, err := sdk.AccAddressFromBech32(req.CreatorAddress)
if err != nil {
return nil, err
}
prefixStore := prefix.NewStore(ctx.KVStore(q.storeKey), types.GetContractsByCreatorPrefix(creatorAddress))
pageRes, err := query.FilteredPaginate(prefixStore, req.Pagination, func(key []byte, _ []byte, accumulate bool) (bool, error) {
if accumulate {
accAddres := sdk.AccAddress(key)
contracts = append(contracts, accAddres.String())
}
return true, nil
})

return &types.QueryContractsByCreatorResponse{
ContractAddress: contracts,
Pagination: pageRes,
}, nil
}
64 changes: 64 additions & 0 deletions x/wasm/keeper/querier_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"errors"
"fmt"
"os"
"sort"
"testing"
"time"

Expand Down Expand Up @@ -802,6 +803,69 @@ func TestQueryCodeInfoList(t *testing.T) {
require.EqualValues(t, allCodesResponse, got.CodeInfos)
}

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

deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 1000000))
topUp := sdk.NewCoins(sdk.NewInt64Coin("denom", 500))
creator := keepers.Faucet.NewFundedRandomAccount(ctx, deposit...)
anyAddr := keepers.Faucet.NewFundedRandomAccount(ctx, topUp...)

wasmCode, err := os.ReadFile("./testdata/hackatom.wasm")
require.NoError(t, err)

codeID, _, err := keepers.ContractKeeper.Create(ctx, creator, wasmCode, nil)
require.NoError(t, err)

_, _, bob := keyPubAddr()
initMsg := HackatomExampleInitMsg{
Verifier: anyAddr,
Beneficiary: bob,
}
initMsgBz, err := json.Marshal(initMsg)
require.NoError(t, err)

// manage some realistic block settings
var h int64 = 10
setBlock := func(ctx sdk.Context, height int64) sdk.Context {
ctx = ctx.WithBlockHeight(height)
meter := sdk.NewGasMeter(1000000)
ctx = ctx.WithGasMeter(meter)
ctx = ctx.WithBlockGasMeter(meter)
return ctx
}

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

// query and check the results are properly sorted
q := Querier(keeper)
res, err := q.ContractsByCreator(sdk.WrapSDKContext(ctx), &types.QueryContractsByCreatorRequest{
CreatorAddress: creator.String(),
Pagination: &query.PageRequest{
Limit: 50,
},
})
require.NoError(t, err)

// need sort because `ContractsByCreator` not ordering
sort.Strings(res.ContractAddress)
sort.Strings(expectedContractsAddr)

require.Equal(t, res.ContractAddress, expectedContractsAddr)
}

func fromBase64(s string) []byte {
r, err := base64.StdEncoding.DecodeString(s)
if err != nil {
Expand Down
Loading