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

Add Build Address gRPC Query #1753

Merged
merged 11 commits into from
Jan 9, 2024
38 changes: 38 additions & 0 deletions docs/proto/proto-docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@
- [CodeInfoResponse](#cosmwasm.wasm.v1.CodeInfoResponse)
- [QueryAllContractStateRequest](#cosmwasm.wasm.v1.QueryAllContractStateRequest)
- [QueryAllContractStateResponse](#cosmwasm.wasm.v1.QueryAllContractStateResponse)
- [QueryBuildAddressRequest](#cosmwasm.wasm.v1.QueryBuildAddressRequest)
- [QueryBuildAddressResponse](#cosmwasm.wasm.v1.QueryBuildAddressResponse)
- [QueryCodeRequest](#cosmwasm.wasm.v1.QueryCodeRequest)
- [QueryCodeResponse](#cosmwasm.wasm.v1.QueryCodeResponse)
- [QueryCodesRequest](#cosmwasm.wasm.v1.QueryCodesRequest)
Expand Down Expand Up @@ -1018,6 +1020,41 @@ Query/AllContractState RPC method



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

### QueryBuildAddressRequest
QueryBuildAddressRequest is the request type for the Query/BuildAddress RPC
method.


| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| `code_hash` | [string](#string) | | CodeHash is the hash of the code |
| `creator_address` | [string](#string) | | CreatorAddress is the address of the contract instantiator |
| `salt` | [string](#string) | | Salt is a hex encoded salt |
| `init_args` | [bytes](#bytes) | | InitArgs are optional json encoded init args to be used in contract address building if provided |






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

### QueryBuildAddressResponse
QueryBuildAddressResponse is the response type for the Query/BuildAddress RPC
method.


| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| `address` | [string](#string) | | Address is the contract address |






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

### QueryCodeRequest
Expand Down Expand Up @@ -1363,6 +1400,7 @@ Query provides defines the gRPC querier service
| `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}|
| `BuildAddress` | [QueryBuildAddressRequest](#cosmwasm.wasm.v1.QueryBuildAddressRequest) | [QueryBuildAddressResponse](#cosmwasm.wasm.v1.QueryBuildAddressResponse) | BuildAddress builds a contract address | GET|/cosmwasm/wasm/v1/contract/build_address|

<!-- end services -->

Expand Down
27 changes: 27 additions & 0 deletions proto/cosmwasm/wasm/v1/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ service Query {
option (google.api.http).get =
"/cosmwasm/wasm/v1/contracts/creator/{creator_address}";
}

// BuildAddress builds a contract address
rpc BuildAddress(QueryBuildAddressRequest)
returns (QueryBuildAddressResponse) {
option (google.api.http).get = "/cosmwasm/wasm/v1/contract/build_address";
}
}

// QueryContractInfoRequest is the request type for the Query/ContractInfo RPC
Expand Down Expand Up @@ -268,4 +274,25 @@ message QueryContractsByCreatorResponse {
[ (cosmos_proto.scalar) = "cosmos.AddressString" ];
// Pagination defines the pagination in the response.
cosmos.base.query.v1beta1.PageResponse pagination = 2;
}

// QueryBuildAddressRequest is the request type for the Query/BuildAddress RPC
// method.
message QueryBuildAddressRequest {
// CodeHash is the hash of the code
string code_hash = 1;
// CreatorAddress is the address of the contract instantiator
string creator_address = 2 [ (cosmos_proto.scalar) = "cosmos.AddressString" ];
// Salt is a hex encoded salt
string salt = 3;
// InitArgs are optional json encoded init args to be used in contract address
// building if provided
bytes init_args = 4;
}

// QueryBuildAddressResponse is the response type for the Query/BuildAddress RPC
// method.
message QueryBuildAddressResponse {
// Address is the contract address
string address = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ];
}
41 changes: 19 additions & 22 deletions x/wasm/client/cli/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
"github.com/cosmos/cosmos-sdk/client/flags"
sdk "github.com/cosmos/cosmos-sdk/types"

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

Expand Down Expand Up @@ -78,32 +77,30 @@
Aliases: []string{"address"},
Args: cobra.RangeArgs(3, 4),
RunE: func(cmd *cobra.Command, args []string) error {
codeHash, err := hex.DecodeString(args[0])
if err != nil {
return fmt.Errorf("code-hash: %s", err)
}
creator, err := sdk.AccAddressFromBech32(args[1])
clientCtx, err := client.GetClientQueryContext(cmd)

Check warning on line 80 in x/wasm/client/cli/query.go

View check run for this annotation

Codecov / codecov/patch

x/wasm/client/cli/query.go#L80

Added line #L80 was not covered by tests
if err != nil {
return fmt.Errorf("creator: %s", err)
}
salt, err := hex.DecodeString(args[2])
switch {
case err != nil:
return fmt.Errorf("salt: %s", err)
case len(salt) == 0:
return errors.New("empty salt")
return err

Check warning on line 82 in x/wasm/client/cli/query.go

View check run for this annotation

Codecov / codecov/patch

x/wasm/client/cli/query.go#L82

Added line #L82 was not covered by tests
}

if len(args) == 3 {
cmd.Println(keeper.BuildContractAddressPredictable(codeHash, creator, salt, []byte{}).String())
return nil
var initArgs []byte
if len(args) == 4 {
initArgs = types.RawContractMessage(args[3])

Check warning on line 87 in x/wasm/client/cli/query.go

View check run for this annotation

Codecov / codecov/patch

x/wasm/client/cli/query.go#L85-L87

Added lines #L85 - L87 were not covered by tests
}
msg := types.RawContractMessage(args[3])
if err := msg.ValidateBasic(); err != nil {
return fmt.Errorf("init message: %s", err)

queryClient := types.NewQueryClient(clientCtx)
res, err := queryClient.BuildAddress(
context.Background(),
&types.QueryBuildAddressRequest{
CodeHash: args[0],
CreatorAddress: args[1],
Salt: args[2],
InitArgs: initArgs,
},
)
if err != nil {
return err

Check warning on line 101 in x/wasm/client/cli/query.go

View check run for this annotation

Codecov / codecov/patch

x/wasm/client/cli/query.go#L90-L101

Added lines #L90 - L101 were not covered by tests
}
cmd.Println(keeper.BuildContractAddressPredictable(codeHash, creator, salt, msg).String())
return nil
return clientCtx.PrintProto(res)

Check warning on line 103 in x/wasm/client/cli/query.go

View check run for this annotation

Codecov / codecov/patch

x/wasm/client/cli/query.go#L103

Added line #L103 was not covered by tests
},
SilenceUsage: true,
}
Expand Down
35 changes: 35 additions & 0 deletions x/wasm/keeper/querier.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package keeper
import (
"context"
"encoding/binary"
"encoding/hex"
"fmt"
"runtime/debug"

"google.golang.org/grpc/codes"
Expand Down Expand Up @@ -406,3 +408,36 @@ func ensurePaginationParams(req *query.PageRequest) (*query.PageRequest, error)
}
return req, nil
}

func (q GrpcQuerier) BuildAddress(c context.Context, req *types.QueryBuildAddressRequest) (*types.QueryBuildAddressResponse, error) {
if req == nil {
return nil, status.Error(codes.InvalidArgument, "empty request")
}
codeHash, err := hex.DecodeString(req.CodeHash)
if err != nil {
return nil, fmt.Errorf("invalid code hash: %w", err)
}
creator, err := sdk.AccAddressFromBech32(req.CreatorAddress)
if err != nil {
return nil, fmt.Errorf("invalid creator address: %w", err)
}
salt, err := hex.DecodeString(req.Salt)
if err != nil {
return nil, fmt.Errorf("invalid salt: %w", err)
}
if len(salt) == 0 {
return nil, status.Error(codes.InvalidArgument, "empty salt")
}
if req.InitArgs == nil {
return &types.QueryBuildAddressResponse{
Address: BuildContractAddressPredictable(codeHash, creator, salt, []byte{}).String(),
}, nil
}
initMsg := types.RawContractMessage(req.InitArgs)
if err := initMsg.ValidateBasic(); err != nil {
return nil, err
}
return &types.QueryBuildAddressResponse{
Address: BuildContractAddressPredictable(codeHash, creator, salt, initMsg).String(),
}, nil
}
100 changes: 100 additions & 0 deletions x/wasm/keeper/querier_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1047,3 +1047,103 @@ func TestEnsurePaginationParams(t *testing.T) {
})
}
}

func TestQueryBuildAddress(t *testing.T) {
specs := map[string]struct {
src *types.QueryBuildAddressRequest
exp *types.QueryBuildAddressResponse
expErr error
}{
"empty request": {
src: nil,
expErr: status.Error(codes.InvalidArgument, "empty request"),
},
"invalid code hash": {
src: &types.QueryBuildAddressRequest{
CodeHash: "invalid",
CreatorAddress: "cosmos100dejzacpanrldpjjwksjm62shqhyss44jf5xz",
Salt: "61",
InitArgs: nil,
},
expErr: fmt.Errorf("invalid code hash"),
},
"invalid creator address": {
src: &types.QueryBuildAddressRequest{
CodeHash: "13a1fc994cc6d1c81b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2a5",
CreatorAddress: "invalid",
Salt: "61",
InitArgs: nil,
},
expErr: fmt.Errorf("invalid creator address"),
},
"invalid salt": {
src: &types.QueryBuildAddressRequest{
CodeHash: "13a1fc994cc6d1c81b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2a5",
CreatorAddress: "cosmos100dejzacpanrldpjjwksjm62shqhyss44jf5xz",
Salt: "invalid",
InitArgs: nil,
},
expErr: fmt.Errorf("invalid salt"),
},
"empty salt": {
src: &types.QueryBuildAddressRequest{
CodeHash: "13a1fc994cc6d1c81b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2a5",
CreatorAddress: "cosmos100dejzacpanrldpjjwksjm62shqhyss44jf5xz",
Salt: "",
InitArgs: nil,
},
expErr: status.Error(codes.InvalidArgument, "empty salt"),
},
"invalid init args": {
src: &types.QueryBuildAddressRequest{
CodeHash: "13a1fc994cc6d1c81b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2a5",
CreatorAddress: "cosmos100dejzacpanrldpjjwksjm62shqhyss44jf5xz",
Salt: "61",
InitArgs: []byte(`invalid`),
},
expErr: fmt.Errorf("invalid"),
},
"valid - without init args": {
src: &types.QueryBuildAddressRequest{
CodeHash: "13a1fc994cc6d1c81b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2a5",
CreatorAddress: "cosmos100dejzacpanrldpjjwksjm62shqhyss44jf5xz",
Salt: "61",
InitArgs: nil,
},
exp: &types.QueryBuildAddressResponse{
Address: "cosmos165fz7lnnt6e08knjqsz6fnz9drs7gewezyq3pl5uspc3zgt5lldq4ge3pl",
},
expErr: nil,
},
"valid - with init args": {
src: &types.QueryBuildAddressRequest{
CodeHash: "13a1fc994cc6d1c81b746ee0c0ff6f90043875e0bf1d9be6b7d779fc978dc2a5",
CreatorAddress: "cosmos100dejzacpanrldpjjwksjm62shqhyss44jf5xz",
Salt: "61",
InitArgs: []byte(`{"verifier":"cosmos100dejzacpanrldpjjwksjm62shqhyss44jf5xz"}`),
},
exp: &types.QueryBuildAddressResponse{
Address: "cosmos150kq3ggdvc9lftcv6ns75t3v6lcpxdmvuwtqr6e9fc029z6h4maqepgss6",
},
expErr: nil,
},
}

ctx, keepers := CreateTestInput(t, false, AvailableCapabilities)
keeper := keepers.WasmKeeper

q := Querier(keeper)
for msg, spec := range specs {
t.Run(msg, func(t *testing.T) {
got, gotErr := q.BuildAddress(ctx, spec.src)
if spec.expErr != nil {
require.Error(t, gotErr)
assert.ErrorContains(t, gotErr, spec.expErr.Error())
return
}
require.NoError(t, gotErr)
require.NotNil(t, got)
assert.Equal(t, spec.exp.Address, got.Address)
})
}
}
Loading
Loading