diff --git a/docs/proto/proto-docs.md b/docs/proto/proto-docs.md index e5cd7ec88f..e62564dc73 100644 --- a/docs/proto/proto-docs.md +++ b/docs/proto/proto-docs.md @@ -119,7 +119,8 @@ AccessConfig access control type. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | `permission` | [AccessType](#cosmwasm.wasm.v1.AccessType) | | | -| `address` | [string](#string) | | | +| `address` | [string](#string) | | Address Deprecated: replaced by addresses | +| `addresses` | [string](#string) | repeated | | @@ -240,8 +241,9 @@ AccessType permission types | ---- | ------ | ----------- | | ACCESS_TYPE_UNSPECIFIED | 0 | AccessTypeUnspecified placeholder for empty value | | ACCESS_TYPE_NOBODY | 1 | AccessTypeNobody forbidden | -| ACCESS_TYPE_ONLY_ADDRESS | 2 | AccessTypeOnlyAddress restricted to an address | +| ACCESS_TYPE_ONLY_ADDRESS | 2 | AccessTypeOnlyAddress restricted to a single address Deprecated: use AccessTypeAnyOfAddresses instead | | ACCESS_TYPE_EVERYBODY | 3 | AccessTypeEverybody unrestricted | +| ACCESS_TYPE_ANY_OF_ADDRESSES | 4 | AccessTypeAnyOfAddresses allow any of the addresses | diff --git a/proto/cosmwasm/wasm/v1/types.proto b/proto/cosmwasm/wasm/v1/types.proto index 739aed2af7..8d140248a8 100644 --- a/proto/cosmwasm/wasm/v1/types.proto +++ b/proto/cosmwasm/wasm/v1/types.proto @@ -19,12 +19,16 @@ enum AccessType { // AccessTypeNobody forbidden ACCESS_TYPE_NOBODY = 1 [ (gogoproto.enumvalue_customname) = "AccessTypeNobody" ]; - // AccessTypeOnlyAddress restricted to an address + // AccessTypeOnlyAddress restricted to a single address + // Deprecated: use AccessTypeAnyOfAddresses instead ACCESS_TYPE_ONLY_ADDRESS = 2 [ (gogoproto.enumvalue_customname) = "AccessTypeOnlyAddress" ]; // AccessTypeEverybody unrestricted ACCESS_TYPE_EVERYBODY = 3 [ (gogoproto.enumvalue_customname) = "AccessTypeEverybody" ]; + // AccessTypeAnyOfAddresses allow any of the addresses + ACCESS_TYPE_ANY_OF_ADDRESSES = 4 + [ (gogoproto.enumvalue_customname) = "AccessTypeAnyOfAddresses" ]; } // AccessTypeParam @@ -37,7 +41,11 @@ message AccessTypeParam { message AccessConfig { option (gogoproto.goproto_stringer) = true; AccessType permission = 1 [ (gogoproto.moretags) = "yaml:\"permission\"" ]; + + // Address + // Deprecated: replaced by addresses string address = 2 [ (gogoproto.moretags) = "yaml:\"address\"" ]; + repeated string addresses = 3 [ (gogoproto.moretags) = "yaml:\"addresses\"" ]; } // Params defines the set of wasm parameters. diff --git a/x/wasm/client/cli/genesis_msg.go b/x/wasm/client/cli/genesis_msg.go index f4ba94fbd2..7ed5a7f290 100644 --- a/x/wasm/client/cli/genesis_msg.go +++ b/x/wasm/client/cli/genesis_msg.go @@ -73,6 +73,7 @@ func GenesisStoreCodeCmd(defaultNodeHome string, genesisMutator GenesisMutator) cmd.Flags().String(flagInstantiateByEverybody, "", "Everybody can instantiate a contract from the code, optional") cmd.Flags().String(flagInstantiateNobody, "", "Nobody except the governance process can instantiate a contract from the code, optional") cmd.Flags().String(flagInstantiateByAddress, "", "Only this address can instantiate a contract instance from the code, optional") + cmd.Flags().StringSlice(flagInstantiateByAnyOfAddress, []string{}, "Any of the addresses can instantiate a contract from the code, optional") cmd.Flags().String(flags.FlagHome, defaultNodeHome, "The application home directory") cmd.Flags().String(flags.FlagKeyringBackend, flags.DefaultKeyringBackend, "Select keyring's backend (os|file|kwallet|pass|test)") diff --git a/x/wasm/client/cli/gov_tx.go b/x/wasm/client/cli/gov_tx.go index b619dbb8b1..8daeb82183 100644 --- a/x/wasm/client/cli/gov_tx.go +++ b/x/wasm/client/cli/gov_tx.go @@ -597,27 +597,38 @@ func ProposalUnpinCodesCmd() *cobra.Command { return cmd } -func parseAccessConfig(config string) (types.AccessConfig, error) { - switch config { +func parseAccessConfig(raw string) (c types.AccessConfig, err error) { + switch raw { case "nobody": return types.AllowNobody, nil case "everybody": return types.AllowEverybody, nil default: - address, err := sdk.AccAddressFromBech32(config) - if err != nil { - return types.AccessConfig{}, fmt.Errorf("unable to parse address %s", config) + parts := strings.Split(raw, ",") + addrs := make([]sdk.AccAddress, len(parts)) + for i, v := range parts { + addr, err := sdk.AccAddressFromBech32(v) + if err != nil { + return types.AccessConfig{}, fmt.Errorf("unable to parse address %q: %s", v, err) + } + addrs[i] = addr } - return types.AccessTypeOnlyAddress.With(address), nil + defer func() { // convert panic in ".With" to error for better output + if r := recover(); r != nil { + err = r.(error) + } + }() + cfg := types.AccessTypeAnyOfAddresses.With(addrs...) + return cfg, cfg.ValidateBasic() } } func parseAccessConfigUpdates(args []string) ([]types.AccessConfigUpdate, error) { updates := make([]types.AccessConfigUpdate, len(args)) for i, c := range args { - // format: code_id,access_config - // access_config: nobody|everybody|address - parts := strings.Split(c, ",") + // format: code_id:access_config + // access_config: nobody|everybody|address(es) + parts := strings.Split(c, ":") if len(parts) != 2 { return nil, fmt.Errorf("invalid format") } @@ -642,15 +653,15 @@ func parseAccessConfigUpdates(args []string) ([]types.AccessConfigUpdate, error) func ProposalUpdateInstantiateConfigCmd() *cobra.Command { bech32Prefix := sdk.GetConfig().GetBech32AccountAddrPrefix() cmd := &cobra.Command{ - Use: "update-instantiate-config [code-id,permission]...", + Use: "update-instantiate-config [code-id:permission]...", Short: "Submit an update instantiate config proposal.", Args: cobra.MinimumNArgs(1), Long: strings.TrimSpace( fmt.Sprintf(`Submit an update instantiate config proposal for multiple code ids. Example: -$ %s tx gov submit-proposal update-instantiate-config 1,nobody 2,everybody 3,%s1l2rsakp388kuv9k8qzq6lrm9taddae7fpx59wm -`, version.AppName, bech32Prefix)), +$ %s tx gov submit-proposal update-instantiate-config 1:nobody 2:everybody 3:%s1l2rsakp388kuv9k8qzq6lrm9taddae7fpx59wm,%s1vx8knpllrj7n963p9ttd80w47kpacrhuts497x +`, version.AppName, bech32Prefix, bech32Prefix)), RunE: func(cmd *cobra.Command, args []string) error { clientCtx, err := client.GetClientTxContext(cmd) if err != nil { diff --git a/x/wasm/client/cli/gov_tx_test.go b/x/wasm/client/cli/gov_tx_test.go new file mode 100644 index 0000000000..7c8600e07f --- /dev/null +++ b/x/wasm/client/cli/gov_tx_test.go @@ -0,0 +1,97 @@ +package cli + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/CosmWasm/wasmd/x/wasm/types" +) + +func TestParseAccessConfigUpdates(t *testing.T) { + specs := map[string]struct { + src []string + exp []types.AccessConfigUpdate + expErr bool + }{ + "nobody": { + src: []string{"1:nobody"}, + exp: []types.AccessConfigUpdate{{ + CodeID: 1, + InstantiatePermission: types.AccessConfig{Permission: types.AccessTypeNobody}, + }}, + }, + "everybody": { + src: []string{"1:everybody"}, + exp: []types.AccessConfigUpdate{{ + CodeID: 1, + InstantiatePermission: types.AccessConfig{Permission: types.AccessTypeEverybody}, + }}, + }, + "any of addresses - single": { + src: []string{"1:cosmos1vx8knpllrj7n963p9ttd80w47kpacrhuts497x"}, + exp: []types.AccessConfigUpdate{ + { + CodeID: 1, + InstantiatePermission: types.AccessConfig{ + Permission: types.AccessTypeAnyOfAddresses, + Addresses: []string{"cosmos1vx8knpllrj7n963p9ttd80w47kpacrhuts497x"}, + }, + }, + }, + }, + "any of addresses - multiple": { + src: []string{"1:cosmos1vx8knpllrj7n963p9ttd80w47kpacrhuts497x,cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s4hmalr"}, + exp: []types.AccessConfigUpdate{ + { + CodeID: 1, + InstantiatePermission: types.AccessConfig{ + Permission: types.AccessTypeAnyOfAddresses, + Addresses: []string{"cosmos1vx8knpllrj7n963p9ttd80w47kpacrhuts497x", "cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s4hmalr"}, + }, + }, + }, + }, + "multiple code ids with different permissions": { + src: []string{"1:cosmos1vx8knpllrj7n963p9ttd80w47kpacrhuts497x,cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s4hmalr", "2:nobody"}, + exp: []types.AccessConfigUpdate{ + { + CodeID: 1, + InstantiatePermission: types.AccessConfig{ + Permission: types.AccessTypeAnyOfAddresses, + Addresses: []string{"cosmos1vx8knpllrj7n963p9ttd80w47kpacrhuts497x", "cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s4hmalr"}, + }, + }, { + CodeID: 2, + InstantiatePermission: types.AccessConfig{ + Permission: types.AccessTypeNobody, + }, + }, + }, + }, + "any of addresses - empty list": { + src: []string{"1:"}, + expErr: true, + }, + "any of addresses - invalid address": { + src: []string{"1:foo"}, + expErr: true, + }, + "any of addresses - duplicate address": { + src: []string{"1:cosmos1vx8knpllrj7n963p9ttd80w47kpacrhuts497x,cosmos1vx8knpllrj7n963p9ttd80w47kpacrhuts497x"}, + expErr: true, + }, + } + for name, spec := range specs { + t.Run(name, func(t *testing.T) { + got, gotErr := parseAccessConfigUpdates(spec.src) + if spec.expErr { + require.Error(t, gotErr) + return + } + require.NoError(t, gotErr) + assert.Equal(t, spec.exp, got) + }) + } +} diff --git a/x/wasm/client/cli/tx.go b/x/wasm/client/cli/tx.go index 28f0a1e2a2..c4dfa473f4 100644 --- a/x/wasm/client/cli/tx.go +++ b/x/wasm/client/cli/tx.go @@ -19,15 +19,16 @@ import ( ) const ( - flagAmount = "amount" - flagLabel = "label" - flagAdmin = "admin" - flagNoAdmin = "no-admin" - flagRunAs = "run-as" - flagInstantiateByEverybody = "instantiate-everybody" - flagInstantiateNobody = "instantiate-nobody" - flagInstantiateByAddress = "instantiate-only-address" - flagUnpinCode = "unpin-code" + flagAmount = "amount" + flagLabel = "label" + flagAdmin = "admin" + flagNoAdmin = "no-admin" + flagRunAs = "run-as" + flagInstantiateByEverybody = "instantiate-everybody" + flagInstantiateNobody = "instantiate-nobody" + flagInstantiateByAddress = "instantiate-only-address" + flagInstantiateByAnyOfAddress = "instantiate-anyof-addresses" + flagUnpinCode = "unpin-code" ) // GetTxCmd returns the transaction commands for this module @@ -75,7 +76,8 @@ func StoreCodeCmd() *cobra.Command { cmd.Flags().String(flagInstantiateByEverybody, "", "Everybody can instantiate a contract from the code, optional") cmd.Flags().String(flagInstantiateNobody, "", "Nobody except the governance process can instantiate a contract from the code, optional") - cmd.Flags().String(flagInstantiateByAddress, "", "Only this address can instantiate a contract instance from the code, optional") + cmd.Flags().String(flagInstantiateByAddress, "", "Deprecated: Only this address can instantiate a contract from the code, optional") + cmd.Flags().StringSlice(flagInstantiateByAnyOfAddress, []string{}, "Any of the addresses can instantiate a contract from the code, optional") flags.AddTxFlagsToCmd(cmd) return cmd } @@ -97,55 +99,76 @@ func parseStoreCodeArgs(file string, sender sdk.AccAddress, flags *flag.FlagSet) return types.MsgStoreCode{}, fmt.Errorf("invalid input file. Use wasm binary or gzip") } - var perm *types.AccessConfig + perm, err := parseAccessConfigFlags(flags) + if err != nil { + return types.MsgStoreCode{}, err + } + + msg := types.MsgStoreCode{ + Sender: sender.String(), + WASMByteCode: wasm, + InstantiatePermission: perm, + } + return msg, nil +} + +func parseAccessConfigFlags(flags *flag.FlagSet) (*types.AccessConfig, error) { + addrs, err := flags.GetStringSlice(flagInstantiateByAnyOfAddress) + if err != nil { + return nil, fmt.Errorf("flag any of: %s", err) + } + if len(addrs) != 0 { + acceptedAddrs := make([]sdk.AccAddress, len(addrs)) + for i, v := range addrs { + acceptedAddrs[i], err = sdk.AccAddressFromBech32(v) + if err != nil { + return nil, fmt.Errorf("parse %q: %w", v, err) + } + } + x := types.AccessTypeAnyOfAddresses.With(acceptedAddrs...) + return &x, nil + } + onlyAddrStr, err := flags.GetString(flagInstantiateByAddress) if err != nil { - return types.MsgStoreCode{}, fmt.Errorf("instantiate by address: %s", err) + return nil, fmt.Errorf("instantiate by address: %s", err) } if onlyAddrStr != "" { allowedAddr, err := sdk.AccAddressFromBech32(onlyAddrStr) if err != nil { - return types.MsgStoreCode{}, sdkerrors.Wrap(err, flagInstantiateByAddress) + return nil, sdkerrors.Wrap(err, flagInstantiateByAddress) } x := types.AccessTypeOnlyAddress.With(allowedAddr) - perm = &x - } else { - everybodyStr, err := flags.GetString(flagInstantiateByEverybody) + return &x, nil + } + everybodyStr, err := flags.GetString(flagInstantiateByEverybody) + if err != nil { + return nil, fmt.Errorf("instantiate by everybody: %s", err) + } + if everybodyStr != "" { + ok, err := strconv.ParseBool(everybodyStr) if err != nil { - return types.MsgStoreCode{}, fmt.Errorf("instantiate by everybody: %s", err) + return nil, fmt.Errorf("boolean value expected for instantiate by everybody: %s", err) } - if everybodyStr != "" { - ok, err := strconv.ParseBool(everybodyStr) - if err != nil { - return types.MsgStoreCode{}, fmt.Errorf("boolean value expected for instantiate by everybody: %s", err) - } - if ok { - perm = &types.AllowEverybody - } + if ok { + return &types.AllowEverybody, nil } + } - nobodyStr, err := flags.GetString(flagInstantiateNobody) + nobodyStr, err := flags.GetString(flagInstantiateNobody) + if err != nil { + return nil, fmt.Errorf("instantiate by nobody: %s", err) + } + if nobodyStr != "" { + ok, err := strconv.ParseBool(nobodyStr) if err != nil { - return types.MsgStoreCode{}, fmt.Errorf("instantiate by nobody: %s", err) + return nil, fmt.Errorf("boolean value expected for instantiate by nobody: %s", err) } - if nobodyStr != "" { - ok, err := strconv.ParseBool(nobodyStr) - if err != nil { - return types.MsgStoreCode{}, fmt.Errorf("boolean value expected for instantiate by nobody: %s", err) - } - if ok { - perm = &types.AllowNobody - } + if ok { + return &types.AllowNobody, nil } - } - - msg := types.MsgStoreCode{ - Sender: sender.String(), - WASMByteCode: wasm, - InstantiatePermission: perm, - } - return msg, nil + return nil, nil } // InstantiateContractCmd will instantiate a contract from previously uploaded code. diff --git a/x/wasm/client/cli/tx_test.go b/x/wasm/client/cli/tx_test.go new file mode 100644 index 0000000000..8fc0cc9def --- /dev/null +++ b/x/wasm/client/cli/tx_test.go @@ -0,0 +1,59 @@ +package cli + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/CosmWasm/wasmd/x/wasm/types" +) + +func TestParseAccessConfigFlags(t *testing.T) { + specs := map[string]struct { + args []string + expCfg *types.AccessConfig + expErr bool + }{ + "nobody": { + args: []string{"--instantiate-nobody=true"}, + expCfg: &types.AccessConfig{Permission: types.AccessTypeNobody}, + }, + "everybody": { + args: []string{"--instantiate-everybody=true"}, + expCfg: &types.AccessConfig{Permission: types.AccessTypeEverybody}, + }, + "only address": { + args: []string{"--instantiate-only-address=cosmos1vx8knpllrj7n963p9ttd80w47kpacrhuts497x"}, + expCfg: &types.AccessConfig{Permission: types.AccessTypeOnlyAddress, Address: "cosmos1vx8knpllrj7n963p9ttd80w47kpacrhuts497x"}, + }, + "only address - invalid": { + args: []string{"--instantiate-only-address=foo"}, + expErr: true, + }, + "any of address": { + args: []string{"--instantiate-anyof-addresses=cosmos1vx8knpllrj7n963p9ttd80w47kpacrhuts497x,cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s4hmalr"}, + expCfg: &types.AccessConfig{Permission: types.AccessTypeAnyOfAddresses, Addresses: []string{"cosmos1vx8knpllrj7n963p9ttd80w47kpacrhuts497x", "cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s4hmalr"}}, + }, + "any of address - invalid": { + args: []string{"--instantiate-anyof-addresses=cosmos1vx8knpllrj7n963p9ttd80w47kpacrhuts497x,foo"}, + expErr: true, + }, + "not set": { + args: []string{}, + }, + } + for name, spec := range specs { + t.Run(name, func(t *testing.T) { + flags := StoreCodeCmd().Flags() + require.NoError(t, flags.Parse(spec.args)) + gotCfg, gotErr := parseAccessConfigFlags(flags) + if spec.expErr { + require.Error(t, gotErr) + return + } + require.NoError(t, gotErr) + assert.Equal(t, spec.expCfg, gotCfg) + }) + } +} diff --git a/x/wasm/keeper/authz_policy_test.go b/x/wasm/keeper/authz_policy_test.go new file mode 100644 index 0000000000..acc9cb346c --- /dev/null +++ b/x/wasm/keeper/authz_policy_test.go @@ -0,0 +1,311 @@ +package keeper + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/assert" + + "github.com/CosmWasm/wasmd/x/wasm/types" +) + +func TestDefaultAuthzPolicyCanCreateCode(t *testing.T) { + myActorAddress := RandomAccountAddress(t) + otherAddress := RandomAccountAddress(t) + specs := map[string]struct { + config types.AccessConfig + actor sdk.AccAddress + exp bool + panics bool + }{ + "nobody": { + config: types.AllowNobody, + exp: false, + }, + "everybody": { + config: types.AllowEverybody, + exp: true, + }, + "only address - same": { + config: types.AccessTypeOnlyAddress.With(myActorAddress), + exp: true, + }, + "only address - different": { + config: types.AccessTypeOnlyAddress.With(otherAddress), + exp: false, + }, + "any address - included": { + config: types.AccessTypeAnyOfAddresses.With(otherAddress, myActorAddress), + exp: true, + }, + "any address - not included": { + config: types.AccessTypeAnyOfAddresses.With(otherAddress), + exp: false, + }, + "undefined config - panics": { + config: types.AccessConfig{}, + panics: true, + }, + } + for name, spec := range specs { + t.Run(name, func(t *testing.T) { + policy := DefaultAuthorizationPolicy{} + if !spec.panics { + got := policy.CanCreateCode(spec.config, myActorAddress) + assert.Equal(t, spec.exp, got) + return + } + assert.Panics(t, func() { + policy.CanCreateCode(spec.config, myActorAddress) + }) + }) + } +} + +func TestDefaultAuthzPolicyCanInstantiateContract(t *testing.T) { + myActorAddress := RandomAccountAddress(t) + otherAddress := RandomAccountAddress(t) + specs := map[string]struct { + config types.AccessConfig + actor sdk.AccAddress + exp bool + panics bool + }{ + "nobody": { + config: types.AllowNobody, + exp: false, + }, + "everybody": { + config: types.AllowEverybody, + exp: true, + }, + "only address - same": { + config: types.AccessTypeOnlyAddress.With(myActorAddress), + exp: true, + }, + "only address - different": { + config: types.AccessTypeOnlyAddress.With(otherAddress), + exp: false, + }, + "any address - included": { + config: types.AccessTypeAnyOfAddresses.With(otherAddress, myActorAddress), + exp: true, + }, + "any address - not included": { + config: types.AccessTypeAnyOfAddresses.With(otherAddress), + exp: false, + }, + "undefined config - panics": { + config: types.AccessConfig{}, + panics: true, + }, + } + for name, spec := range specs { + t.Run(name, func(t *testing.T) { + policy := DefaultAuthorizationPolicy{} + if !spec.panics { + got := policy.CanInstantiateContract(spec.config, myActorAddress) + assert.Equal(t, spec.exp, got) + return + } + assert.Panics(t, func() { + policy.CanInstantiateContract(spec.config, myActorAddress) + }) + }) + } +} + +func TestDefaultAuthzPolicyCanModifyContract(t *testing.T) { + myActorAddress := RandomAccountAddress(t) + otherAddress := RandomAccountAddress(t) + + specs := map[string]struct { + admin sdk.AccAddress + exp bool + }{ + "same as actor": { + admin: myActorAddress, + exp: true, + }, + "different admin": { + admin: otherAddress, + exp: false, + }, + "no admin": { + exp: false, + }, + } + for name, spec := range specs { + t.Run(name, func(t *testing.T) { + policy := DefaultAuthorizationPolicy{} + got := policy.CanModifyContract(spec.admin, myActorAddress) + assert.Equal(t, spec.exp, got) + }) + } +} + +func TestDefaultAuthzPolicyCanModifyCodeAccessConfig(t *testing.T) { + myActorAddress := RandomAccountAddress(t) + otherAddress := RandomAccountAddress(t) + + specs := map[string]struct { + admin sdk.AccAddress + subset bool + exp bool + }{ + "same as actor - subset": { + admin: myActorAddress, + subset: true, + exp: true, + }, + "same as actor - not subset": { + admin: myActorAddress, + subset: false, + exp: false, + }, + "different admin": { + admin: otherAddress, + exp: false, + }, + "no admin": { + exp: false, + }, + } + for name, spec := range specs { + t.Run(name, func(t *testing.T) { + policy := DefaultAuthorizationPolicy{} + got := policy.CanModifyCodeAccessConfig(spec.admin, myActorAddress, spec.subset) + assert.Equal(t, spec.exp, got) + }) + } +} + +func TestGovAuthzPolicyCanCreateCode(t *testing.T) { + myActorAddress := RandomAccountAddress(t) + otherAddress := RandomAccountAddress(t) + specs := map[string]struct { + config types.AccessConfig + actor sdk.AccAddress + }{ + "nobody": { + config: types.AllowNobody, + }, + "everybody": { + config: types.AllowEverybody, + }, + "only address - same": { + config: types.AccessTypeOnlyAddress.With(myActorAddress), + }, + "only address - different": { + config: types.AccessTypeOnlyAddress.With(otherAddress), + }, + "any address - included": { + config: types.AccessTypeAnyOfAddresses.With(otherAddress, myActorAddress), + }, + "any address - not included": { + config: types.AccessTypeAnyOfAddresses.With(otherAddress), + }, + "undefined config - panics": { + config: types.AccessConfig{}, + }, + } + for name, spec := range specs { + t.Run(name, func(t *testing.T) { + policy := GovAuthorizationPolicy{} + got := policy.CanCreateCode(spec.config, myActorAddress) + assert.True(t, got) + }) + } +} + +func TestGovAuthzPolicyCanInstantiateContract(t *testing.T) { + myActorAddress := RandomAccountAddress(t) + otherAddress := RandomAccountAddress(t) + specs := map[string]struct { + config types.AccessConfig + actor sdk.AccAddress + }{ + "nobody": { + config: types.AllowNobody, + }, + "everybody": { + config: types.AllowEverybody, + }, + "only address - same": { + config: types.AccessTypeOnlyAddress.With(myActorAddress), + }, + "only address - different": { + config: types.AccessTypeOnlyAddress.With(otherAddress), + }, + "any address - included": { + config: types.AccessTypeAnyOfAddresses.With(otherAddress, myActorAddress), + }, + "any address - not included": { + config: types.AccessTypeAnyOfAddresses.With(otherAddress), + }, + "undefined config - panics": { + config: types.AccessConfig{}, + }, + } + for name, spec := range specs { + t.Run(name, func(t *testing.T) { + policy := GovAuthorizationPolicy{} + got := policy.CanInstantiateContract(spec.config, myActorAddress) + assert.True(t, got) + }) + } +} + +func TestGovAuthzPolicyCanModifyContract(t *testing.T) { + myActorAddress := RandomAccountAddress(t) + otherAddress := RandomAccountAddress(t) + + specs := map[string]struct { + admin sdk.AccAddress + }{ + "same as actor": { + admin: myActorAddress, + }, + "different admin": { + admin: otherAddress, + }, + "no admin": {}, + } + for name, spec := range specs { + t.Run(name, func(t *testing.T) { + policy := GovAuthorizationPolicy{} + got := policy.CanModifyContract(spec.admin, myActorAddress) + assert.True(t, got) + }) + } +} + +func TestGovAuthzPolicyCanModifyCodeAccessConfig(t *testing.T) { + myActorAddress := RandomAccountAddress(t) + otherAddress := RandomAccountAddress(t) + + specs := map[string]struct { + admin sdk.AccAddress + subset bool + }{ + "same as actor - subset": { + admin: myActorAddress, + subset: true, + }, + "same as actor - not subset": { + admin: myActorAddress, + subset: false, + }, + "different admin": { + admin: otherAddress, + }, + "no admin": {}, + } + for name, spec := range specs { + t.Run(name, func(t *testing.T) { + policy := GovAuthorizationPolicy{} + got := policy.CanModifyCodeAccessConfig(spec.admin, myActorAddress, spec.subset) + assert.True(t, got) + }) + } +} diff --git a/x/wasm/keeper/proposal_handler.go b/x/wasm/keeper/proposal_handler.go index f4cd8a4e1a..905b073d5e 100644 --- a/x/wasm/keeper/proposal_handler.go +++ b/x/wasm/keeper/proposal_handler.go @@ -74,7 +74,6 @@ func handleStoreCodeProposal(ctx sdk.Context, k types.ContractOpsKeeper, p types return nil } return k.PinCode(ctx, codeID) - } func handleInstantiateProposal(ctx sdk.Context, k types.ContractOpsKeeper, p types.InstantiateContractProposal) error { diff --git a/x/wasm/types/params.go b/x/wasm/types/params.go index ea8046cf3d..0ee7dcb619 100644 --- a/x/wasm/types/params.go +++ b/x/wasm/types/params.go @@ -20,20 +20,33 @@ var ( var AllAccessTypes = []AccessType{ AccessTypeNobody, AccessTypeOnlyAddress, + AccessTypeAnyOfAddresses, AccessTypeEverybody, } -func (a AccessType) With(addr sdk.AccAddress) AccessConfig { +func (a AccessType) With(addrs ...sdk.AccAddress) AccessConfig { switch a { case AccessTypeNobody: return AllowNobody case AccessTypeOnlyAddress: - if err := sdk.VerifyAddressFormat(addr); err != nil { + if n := len(addrs); n != 1 { + panic(fmt.Sprintf("expected exactly 1 address but got %d", n)) + } + if err := sdk.VerifyAddressFormat(addrs[0]); err != nil { panic(err) } - return AccessConfig{Permission: AccessTypeOnlyAddress, Address: addr.String()} + return AccessConfig{Permission: AccessTypeOnlyAddress, Address: addrs[0].String()} case AccessTypeEverybody: return AllowEverybody + case AccessTypeAnyOfAddresses: + bech32Addrs := make([]string, len(addrs)) + for i, v := range addrs { + bech32Addrs[i] = v.String() + } + if err := assertValidAddresses(bech32Addrs); err != nil { + panic(sdkerrors.Wrap(err, "addresses")) + } + return AccessConfig{Permission: AccessTypeAnyOfAddresses, Addresses: bech32Addrs} } panic("unsupported access type") } @@ -46,6 +59,8 @@ func (a AccessType) String() string { return "OnlyAddress" case AccessTypeEverybody: return "Everybody" + case AccessTypeAnyOfAddresses: + return "AnyOfAddresses" } return "Unspecified" } @@ -147,6 +162,7 @@ func validateAccessType(i interface{}) error { return sdkerrors.Wrapf(ErrInvalid, "unknown type: %q", a) } +// ValidateBasic performs basic validation func (a AccessConfig) ValidateBasic() error { switch a.Permission { case AccessTypeUnspecified: @@ -157,12 +173,39 @@ func (a AccessConfig) ValidateBasic() error { } return nil case AccessTypeOnlyAddress: + if len(a.Addresses) != 0 { + return ErrInvalid.Wrap("addresses field set") + } _, err := sdk.AccAddressFromBech32(a.Address) return err + case AccessTypeAnyOfAddresses: + if a.Address != "" { + return ErrInvalid.Wrap("address field set") + } + return sdkerrors.Wrap(assertValidAddresses(a.Addresses), "addresses") } return sdkerrors.Wrapf(ErrInvalid, "unknown type: %q", a.Permission) } +func assertValidAddresses(addrs []string) error { + if len(addrs) == 0 { + return ErrEmpty + } + idx := make(map[string]struct{}, len(addrs)) + for _, a := range addrs { + if _, err := sdk.AccAddressFromBech32(a); err != nil { + return sdkerrors.Wrapf(err, "address: %s", a) + } + if _, exists := idx[a]; exists { + return ErrDuplicate.Wrapf("address: %s", a) + } + idx[a] = struct{}{} + } + return nil +} + +// Allowed returns if permission includes the actor. +// Actor address must be valid and not nil func (a AccessConfig) Allowed(actor sdk.AccAddress) bool { switch a.Permission { case AccessTypeNobody: @@ -171,6 +214,13 @@ func (a AccessConfig) Allowed(actor sdk.AccAddress) bool { return true case AccessTypeOnlyAddress: return a.Address == actor.String() + case AccessTypeAnyOfAddresses: + for _, v := range a.Addresses { + if v == actor.String() { + return true + } + } + return false default: panic("unknown type") } diff --git a/x/wasm/types/params_test.go b/x/wasm/types/params_test.go index 35799cb48b..77ac8b7424 100644 --- a/x/wasm/types/params_test.go +++ b/x/wasm/types/params_test.go @@ -1,6 +1,7 @@ package types import ( + "bytes" "encoding/json" "testing" @@ -14,6 +15,7 @@ import ( func TestValidateParams(t *testing.T) { var ( anyAddress sdk.AccAddress = make([]byte, ContractAddrLen) + otherAddress sdk.AccAddress = bytes.Repeat([]byte{1}, ContractAddrLen) invalidAddress = "invalid address" ) @@ -42,6 +44,18 @@ func TestValidateParams(t *testing.T) { InstantiateDefaultPermission: AccessTypeOnlyAddress, }, }, + "all good with anyOf address": { + src: Params{ + CodeUploadAccess: AccessTypeAnyOfAddresses.With(anyAddress), + InstantiateDefaultPermission: AccessTypeAnyOfAddresses, + }, + }, + "all good with anyOf addresses": { + src: Params{ + CodeUploadAccess: AccessTypeAnyOfAddresses.With(anyAddress, otherAddress), + InstantiateDefaultPermission: AccessTypeAnyOfAddresses, + }, + }, "reject empty type in instantiate permission": { src: Params{ CodeUploadAccess: AllowNobody, @@ -62,6 +76,13 @@ func TestValidateParams(t *testing.T) { }, expErr: true, }, + "reject wrong field addresses in only address": { + src: Params{ + CodeUploadAccess: AccessConfig{Permission: AccessTypeOnlyAddress, Address: anyAddress.String(), Addresses: []string{anyAddress.String()}}, + InstantiateDefaultPermission: AccessTypeOnlyAddress, + }, + expErr: true, + }, "reject CodeUploadAccess Everybody with obsolete address": { src: Params{ CodeUploadAccess: AccessConfig{Permission: AccessTypeEverybody, Address: anyAddress.String()}, @@ -89,6 +110,41 @@ func TestValidateParams(t *testing.T) { }, expErr: true, }, + "reject empty addresses in any of addresses": { + src: Params{ + CodeUploadAccess: AccessConfig{Permission: AccessTypeAnyOfAddresses, Addresses: []string{}}, + InstantiateDefaultPermission: AccessTypeAnyOfAddresses, + }, + expErr: true, + }, + "reject addresses not set in any of addresses": { + src: Params{ + CodeUploadAccess: AccessConfig{Permission: AccessTypeAnyOfAddresses}, + InstantiateDefaultPermission: AccessTypeAnyOfAddresses, + }, + expErr: true, + }, + "reject invalid address in any of addresses": { + src: Params{ + CodeUploadAccess: AccessConfig{Permission: AccessTypeAnyOfAddresses, Addresses: []string{invalidAddress}}, + InstantiateDefaultPermission: AccessTypeAnyOfAddresses, + }, + expErr: true, + }, + "reject duplicate address in any of addresses": { + src: Params{ + CodeUploadAccess: AccessConfig{Permission: AccessTypeAnyOfAddresses, Addresses: []string{anyAddress.String(), anyAddress.String()}}, + InstantiateDefaultPermission: AccessTypeAnyOfAddresses, + }, + expErr: true, + }, + "reject wrong field address in any of addresses": { + src: Params{ + CodeUploadAccess: AccessConfig{Permission: AccessTypeAnyOfAddresses, Address: anyAddress.String(), Addresses: []string{anyAddress.String()}}, + InstantiateDefaultPermission: AccessTypeAnyOfAddresses, + }, + expErr: true, + }, } for msg, spec := range specs { t.Run(msg, func(t *testing.T) { @@ -107,11 +163,12 @@ func TestAccessTypeMarshalJson(t *testing.T) { src AccessType exp string }{ - "Unspecified": {src: AccessTypeUnspecified, exp: `"Unspecified"`}, - "Nobody": {src: AccessTypeNobody, exp: `"Nobody"`}, - "OnlyAddress": {src: AccessTypeOnlyAddress, exp: `"OnlyAddress"`}, - "Everybody": {src: AccessTypeEverybody, exp: `"Everybody"`}, - "unknown": {src: 999, exp: `"Unspecified"`}, + "Unspecified": {src: AccessTypeUnspecified, exp: `"Unspecified"`}, + "Nobody": {src: AccessTypeNobody, exp: `"Nobody"`}, + "OnlyAddress": {src: AccessTypeOnlyAddress, exp: `"OnlyAddress"`}, + "AccessTypeAnyOfAddresses": {src: AccessTypeAnyOfAddresses, exp: `"AnyOfAddresses"`}, + "Everybody": {src: AccessTypeEverybody, exp: `"Everybody"`}, + "unknown": {src: 999, exp: `"Unspecified"`}, } for msg, spec := range specs { t.Run(msg, func(t *testing.T) { @@ -127,11 +184,12 @@ func TestAccessTypeUnmarshalJson(t *testing.T) { src string exp AccessType }{ - "Unspecified": {src: `"Unspecified"`, exp: AccessTypeUnspecified}, - "Nobody": {src: `"Nobody"`, exp: AccessTypeNobody}, - "OnlyAddress": {src: `"OnlyAddress"`, exp: AccessTypeOnlyAddress}, - "Everybody": {src: `"Everybody"`, exp: AccessTypeEverybody}, - "unknown": {src: `""`, exp: AccessTypeUnspecified}, + "Unspecified": {src: `"Unspecified"`, exp: AccessTypeUnspecified}, + "Nobody": {src: `"Nobody"`, exp: AccessTypeNobody}, + "OnlyAddress": {src: `"OnlyAddress"`, exp: AccessTypeOnlyAddress}, + "AnyOfAddresses": {src: `"AnyOfAddresses"`, exp: AccessTypeAnyOfAddresses}, + "Everybody": {src: `"Everybody"`, exp: AccessTypeEverybody}, + "unknown": {src: `""`, exp: AccessTypeUnspecified}, } for msg, spec := range specs { t.Run(msg, func(t *testing.T) { @@ -166,3 +224,83 @@ func TestParamsUnmarshalJson(t *testing.T) { }) } } + +func TestAccessTypeWith(t *testing.T) { + myAddress := sdk.AccAddress(randBytes(SDKAddrLen)) + myOtherAddress := sdk.AccAddress(randBytes(SDKAddrLen)) + specs := map[string]struct { + src AccessType + addrs []sdk.AccAddress + exp AccessConfig + expPanic bool + }{ + "nobody": { + src: AccessTypeNobody, + exp: AccessConfig{Permission: AccessTypeNobody}, + }, + "nobody with address": { + src: AccessTypeNobody, + addrs: []sdk.AccAddress{myAddress}, + exp: AccessConfig{Permission: AccessTypeNobody}, + }, + "everybody": { + src: AccessTypeEverybody, + exp: AccessConfig{Permission: AccessTypeEverybody}, + }, + "everybody with address": { + src: AccessTypeEverybody, + addrs: []sdk.AccAddress{myAddress}, + exp: AccessConfig{Permission: AccessTypeEverybody}, + }, + "only address without address": { + src: AccessTypeOnlyAddress, + expPanic: true, + }, + "only address with address": { + src: AccessTypeOnlyAddress, + addrs: []sdk.AccAddress{myAddress}, + exp: AccessConfig{Permission: AccessTypeOnlyAddress, Address: myAddress.String()}, + }, + "only address with invalid address": { + src: AccessTypeOnlyAddress, + addrs: []sdk.AccAddress{nil}, + expPanic: true, + }, + "any of address without address": { + src: AccessTypeAnyOfAddresses, + expPanic: true, + }, + "any of address with single address": { + src: AccessTypeAnyOfAddresses, + addrs: []sdk.AccAddress{myAddress}, + exp: AccessConfig{Permission: AccessTypeAnyOfAddresses, Addresses: []string{myAddress.String()}}, + }, + "any of address with multiple addresses": { + src: AccessTypeAnyOfAddresses, + addrs: []sdk.AccAddress{myAddress, myOtherAddress}, + exp: AccessConfig{Permission: AccessTypeAnyOfAddresses, Addresses: []string{myAddress.String(), myOtherAddress.String()}}, + }, + "any of address with duplicate addresses": { + src: AccessTypeAnyOfAddresses, + addrs: []sdk.AccAddress{myAddress, myAddress}, + expPanic: true, + }, + "any of address with invalid address": { + src: AccessTypeAnyOfAddresses, + addrs: []sdk.AccAddress{nil}, + expPanic: true, + }, + } + for name, spec := range specs { + t.Run(name, func(t *testing.T) { + if !spec.expPanic { + got := spec.src.With(spec.addrs...) + assert.Equal(t, spec.exp, got) + return + } + assert.Panics(t, func() { + spec.src.With(spec.addrs...) + }) + }) + } +} diff --git a/x/wasm/types/types.go b/x/wasm/types/types.go index cdf669e5c9..3712af192c 100644 --- a/x/wasm/types/types.go +++ b/x/wasm/types/types.go @@ -342,9 +342,9 @@ func (a AccessType) IsSubset(superSet AccessType) bool { case AccessTypeNobody: // Only an exact match is a subset of this return a == AccessTypeNobody - case AccessTypeOnlyAddress: - // An exact match or nobody - return a == AccessTypeNobody || a == AccessTypeOnlyAddress + case AccessTypeOnlyAddress, AccessTypeAnyOfAddresses: + // Nobody or address(es) + return a == AccessTypeNobody || a == AccessTypeOnlyAddress || a == AccessTypeAnyOfAddresses default: return false } @@ -356,8 +356,32 @@ func (a AccessConfig) IsSubset(superSet AccessConfig) bool { switch superSet.Permission { case AccessTypeOnlyAddress: // An exact match or nobody - return a.Permission == AccessTypeNobody || (a.Permission == AccessTypeOnlyAddress && a.Address == superSet.Address) + return a.Permission == AccessTypeNobody || (a.Permission == AccessTypeOnlyAddress && a.Address == superSet.Address) || + (a.Permission == AccessTypeAnyOfAddresses && isSubset([]string{superSet.Address}, a.Addresses)) + case AccessTypeAnyOfAddresses: + // An exact match or nobody + return a.Permission == AccessTypeNobody || (a.Permission == AccessTypeOnlyAddress && isSubset(superSet.Addresses, []string{a.Address})) || + a.Permission == AccessTypeAnyOfAddresses && isSubset(superSet.Addresses, a.Addresses) + case AccessTypeUnspecified: + return false default: return a.Permission.IsSubset(superSet.Permission) } } + +// return true when all elements in sub are also part of super +func isSubset(super, sub []string) bool { + if len(sub) == 0 { + return true + } + var matches int + for _, o := range sub { + for _, s := range super { + if o == s { + matches++ + break + } + } + } + return matches == len(sub) +} diff --git a/x/wasm/types/types.pb.go b/x/wasm/types/types.pb.go index 05ff057a0d..f7dc08b405 100644 --- a/x/wasm/types/types.pb.go +++ b/x/wasm/types/types.pb.go @@ -39,10 +39,13 @@ const ( AccessTypeUnspecified AccessType = 0 // AccessTypeNobody forbidden AccessTypeNobody AccessType = 1 - // AccessTypeOnlyAddress restricted to an address + // AccessTypeOnlyAddress restricted to a single address + // Deprecated: use AccessTypeAnyOfAddresses instead AccessTypeOnlyAddress AccessType = 2 // AccessTypeEverybody unrestricted AccessTypeEverybody AccessType = 3 + // AccessTypeAnyOfAddresses allow any of the addresses + AccessTypeAnyOfAddresses AccessType = 4 ) var AccessType_name = map[int32]string{ @@ -50,13 +53,15 @@ var AccessType_name = map[int32]string{ 1: "ACCESS_TYPE_NOBODY", 2: "ACCESS_TYPE_ONLY_ADDRESS", 3: "ACCESS_TYPE_EVERYBODY", + 4: "ACCESS_TYPE_ANY_OF_ADDRESSES", } var AccessType_value = map[string]int32{ - "ACCESS_TYPE_UNSPECIFIED": 0, - "ACCESS_TYPE_NOBODY": 1, - "ACCESS_TYPE_ONLY_ADDRESS": 2, - "ACCESS_TYPE_EVERYBODY": 3, + "ACCESS_TYPE_UNSPECIFIED": 0, + "ACCESS_TYPE_NOBODY": 1, + "ACCESS_TYPE_ONLY_ADDRESS": 2, + "ACCESS_TYPE_EVERYBODY": 3, + "ACCESS_TYPE_ANY_OF_ADDRESSES": 4, } func (AccessType) EnumDescriptor() ([]byte, []int) { @@ -145,7 +150,10 @@ var xxx_messageInfo_AccessTypeParam proto.InternalMessageInfo // AccessConfig access control type. type AccessConfig struct { Permission AccessType `protobuf:"varint,1,opt,name=permission,proto3,enum=cosmwasm.wasm.v1.AccessType" json:"permission,omitempty" yaml:"permission"` - Address string `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty" yaml:"address"` + // Address + // Deprecated: replaced by addresses + Address string `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty" yaml:"address"` + Addresses []string `protobuf:"bytes,3,rep,name=addresses,proto3" json:"addresses,omitempty" yaml:"addresses"` } func (m *AccessConfig) Reset() { *m = AccessConfig{} } @@ -493,78 +501,81 @@ func init() { func init() { proto.RegisterFile("cosmwasm/wasm/v1/types.proto", fileDescriptor_e6155d98fa173e02) } var fileDescriptor_e6155d98fa173e02 = []byte{ - // 1123 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x56, 0xcd, 0x8f, 0xdb, 0x44, - 0x14, 0x8f, 0x93, 0xec, 0xd7, 0x34, 0x14, 0x77, 0xd8, 0xa5, 0x49, 0xa8, 0x9c, 0xd4, 0x14, 0xd8, - 0x7e, 0x25, 0x74, 0x41, 0x80, 0x7a, 0xa8, 0x94, 0x0f, 0xd3, 0xb8, 0x62, 0x93, 0x68, 0x92, 0x52, - 0x2d, 0x52, 0x65, 0x39, 0xf6, 0x6c, 0x62, 0xd5, 0xf1, 0x44, 0x9e, 0xc9, 0x36, 0xfe, 0x0f, 0x50, - 0x24, 0x04, 0x37, 0xb8, 0x44, 0x42, 0x80, 0x50, 0xff, 0x00, 0xae, 0xdc, 0x2b, 0x4e, 0x3d, 0x72, - 0x8a, 0x20, 0xbd, 0xc0, 0x75, 0x8f, 0xe5, 0x82, 0x3c, 0x93, 0x10, 0xab, 0xdd, 0x76, 0xc3, 0xc5, - 0xf2, 0xbc, 0xf7, 0x7e, 0xbf, 0xf7, 0xde, 0x6f, 0xde, 0xb3, 0x0c, 0x2e, 0x58, 0x84, 0xf6, 0x1f, - 0x9a, 0xb4, 0x5f, 0xe4, 0x8f, 0xa3, 0x1b, 0x45, 0x16, 0x0c, 0x30, 0x2d, 0x0c, 0x7c, 0xc2, 0x08, - 0x94, 0x17, 0xde, 0x02, 0x7f, 0x1c, 0xdd, 0xc8, 0x66, 0x42, 0x0b, 0xa1, 0x06, 0xf7, 0x17, 0xc5, - 0x41, 0x04, 0x67, 0xb7, 0xbb, 0xa4, 0x4b, 0x84, 0x3d, 0x7c, 0x9b, 0x5b, 0x33, 0x5d, 0x42, 0xba, - 0x2e, 0x2e, 0xf2, 0x53, 0x67, 0x78, 0x58, 0x34, 0xbd, 0x40, 0xb8, 0xd4, 0xfb, 0xe0, 0xf5, 0x92, - 0x65, 0x61, 0x4a, 0xdb, 0xc1, 0x00, 0x37, 0x4d, 0xdf, 0xec, 0xc3, 0x2a, 0x58, 0x3b, 0x32, 0xdd, - 0x21, 0x4e, 0x4b, 0x79, 0x69, 0xf7, 0xec, 0xde, 0x85, 0xc2, 0xf3, 0x05, 0x14, 0x96, 0x88, 0xb2, - 0x7c, 0x3c, 0xcd, 0xa5, 0x02, 0xb3, 0xef, 0xde, 0x54, 0x39, 0x48, 0x45, 0x02, 0x7c, 0x33, 0xf9, - 0xdd, 0xf7, 0x39, 0x49, 0xfd, 0x56, 0x02, 0x29, 0x11, 0x5d, 0x21, 0xde, 0xa1, 0xd3, 0x85, 0x2d, - 0x00, 0x06, 0xd8, 0xef, 0x3b, 0x94, 0x3a, 0xc4, 0x5b, 0x29, 0xc3, 0xce, 0xf1, 0x34, 0x77, 0x4e, - 0x64, 0x58, 0x22, 0x55, 0x14, 0xa1, 0x81, 0xd7, 0xc0, 0x86, 0x69, 0xdb, 0x3e, 0xa6, 0x34, 0x1d, - 0xcf, 0x4b, 0xbb, 0x5b, 0x65, 0x78, 0x3c, 0xcd, 0x9d, 0x15, 0x98, 0xb9, 0x43, 0x45, 0x8b, 0x90, - 0x79, 0x65, 0x5f, 0xc7, 0xc1, 0x3a, 0xef, 0x97, 0x42, 0x02, 0xa0, 0x45, 0x6c, 0x6c, 0x0c, 0x07, - 0x2e, 0x31, 0x6d, 0xc3, 0xe4, 0xb9, 0x79, 0x6d, 0x67, 0xf6, 0x94, 0x97, 0xd5, 0x26, 0xfa, 0x29, - 0x5f, 0x7c, 0x3c, 0xcd, 0xc5, 0x8e, 0xa7, 0xb9, 0x8c, 0xc8, 0xf6, 0x22, 0x8f, 0x8a, 0xe4, 0xd0, - 0x78, 0x97, 0xdb, 0x04, 0x14, 0x7e, 0x25, 0x01, 0xc5, 0xf1, 0x28, 0x33, 0x3d, 0xe6, 0x98, 0x0c, - 0x1b, 0x36, 0x3e, 0x34, 0x87, 0x2e, 0x33, 0x22, 0xca, 0xc4, 0x57, 0x50, 0xe6, 0xf2, 0xf1, 0x34, - 0xf7, 0x8e, 0xc8, 0xfb, 0x6a, 0x36, 0x15, 0x5d, 0x88, 0x04, 0x54, 0x85, 0xbf, 0xf9, 0x9f, 0x9b, - 0x2b, 0x12, 0x53, 0x7f, 0x90, 0xc0, 0x66, 0x85, 0xd8, 0x58, 0xf7, 0x0e, 0x09, 0x7c, 0x0b, 0x6c, - 0xf1, 0x5e, 0x7a, 0x26, 0xed, 0x71, 0x29, 0x52, 0x68, 0x33, 0x34, 0xd4, 0x4c, 0xda, 0x83, 0x69, - 0xb0, 0x61, 0xf9, 0xd8, 0x64, 0xc4, 0x17, 0x7a, 0xa3, 0xc5, 0x11, 0xb6, 0x00, 0x8c, 0x96, 0x62, - 0x71, 0x91, 0xd2, 0x6b, 0x2b, 0x49, 0x99, 0x0c, 0xa5, 0x44, 0xe7, 0x22, 0x78, 0xe1, 0xb8, 0x93, - 0xdc, 0x4c, 0xc8, 0xc9, 0x3b, 0xc9, 0xcd, 0xa4, 0xbc, 0xa6, 0xfe, 0x1a, 0x07, 0xa9, 0x0a, 0xf1, - 0x98, 0x6f, 0x5a, 0x8c, 0x17, 0xfa, 0x36, 0xd8, 0xe0, 0x85, 0x3a, 0x36, 0x2f, 0x33, 0x59, 0x06, - 0xb3, 0x69, 0x6e, 0x9d, 0xf7, 0x51, 0x45, 0xeb, 0xa1, 0x4b, 0xb7, 0x5f, 0x51, 0xf0, 0x36, 0x58, - 0x33, 0xed, 0xbe, 0xe3, 0xa5, 0x13, 0xdc, 0x2e, 0x0e, 0xa1, 0xd5, 0x35, 0x3b, 0xd8, 0x4d, 0x27, - 0x85, 0x95, 0x1f, 0xe0, 0xad, 0x39, 0x0b, 0xb6, 0xe7, 0x1d, 0x5d, 0x3a, 0xa1, 0xa3, 0x0e, 0x25, - 0xee, 0x90, 0xe1, 0xf6, 0xa8, 0x49, 0xa8, 0xc3, 0x1c, 0xe2, 0xa1, 0x05, 0x08, 0x5e, 0x07, 0x67, - 0x9c, 0x8e, 0x65, 0x0c, 0x88, 0xcf, 0xc2, 0x72, 0xd7, 0xf9, 0xa8, 0xbe, 0x36, 0x9b, 0xe6, 0xb6, - 0xf4, 0x72, 0xa5, 0x49, 0x7c, 0xa6, 0x57, 0xd1, 0x96, 0xd3, 0xb1, 0xf8, 0xab, 0x0d, 0xf7, 0xc1, - 0x16, 0x1e, 0x31, 0xec, 0xf1, 0x79, 0xd8, 0xe0, 0x09, 0xb7, 0x0b, 0x62, 0x93, 0x0b, 0x8b, 0x4d, - 0x2e, 0x94, 0xbc, 0xa0, 0x9c, 0xf9, 0xed, 0x97, 0xeb, 0x3b, 0x51, 0x51, 0xb4, 0x05, 0x0c, 0x2d, - 0x19, 0x6e, 0x26, 0xff, 0x0a, 0xc7, 0xfe, 0x1f, 0x09, 0xa4, 0x17, 0xa1, 0xa1, 0x48, 0x35, 0x87, - 0x32, 0xe2, 0x07, 0x9a, 0xc7, 0xfc, 0x00, 0x36, 0xc1, 0x16, 0x19, 0x60, 0xdf, 0x64, 0xcb, 0xdd, - 0xdc, 0x7b, 0xb1, 0xc5, 0x13, 0xe0, 0x8d, 0x05, 0x2a, 0x9c, 0x4b, 0xb4, 0x24, 0x89, 0xde, 0x4e, - 0xfc, 0xa5, 0xb7, 0x73, 0x0b, 0x6c, 0x0c, 0x07, 0x36, 0xd7, 0x35, 0xf1, 0x7f, 0x74, 0x9d, 0x83, - 0xe0, 0x2e, 0x48, 0xf4, 0x69, 0x97, 0xdf, 0x55, 0xaa, 0xfc, 0xe6, 0xb3, 0x69, 0x0e, 0x22, 0xf3, - 0xe1, 0xa2, 0xca, 0x7d, 0x4c, 0xa9, 0xd9, 0xc5, 0x28, 0x0c, 0x51, 0x11, 0x80, 0x2f, 0x12, 0xc1, - 0x8b, 0x20, 0xd5, 0x71, 0x89, 0xf5, 0xc0, 0xe8, 0x61, 0xa7, 0xdb, 0x63, 0x62, 0x8e, 0xd0, 0x19, - 0x6e, 0xab, 0x71, 0x13, 0xcc, 0x80, 0x4d, 0x36, 0x32, 0x1c, 0xcf, 0xc6, 0x23, 0xd1, 0x08, 0xda, - 0x60, 0x23, 0x3d, 0x3c, 0xaa, 0x0e, 0x58, 0xdb, 0x27, 0x36, 0x76, 0xe1, 0x1d, 0x90, 0x78, 0x80, - 0x03, 0xb1, 0x2c, 0xe5, 0x4f, 0x9e, 0x4d, 0x73, 0x1f, 0x76, 0x1d, 0xd6, 0x1b, 0x76, 0x0a, 0x16, - 0xe9, 0x17, 0x19, 0xf6, 0xec, 0x70, 0xe1, 0x3c, 0x16, 0x7d, 0x75, 0x9d, 0x0e, 0x2d, 0x76, 0x02, - 0x86, 0x69, 0xa1, 0x86, 0x47, 0xe5, 0xf0, 0x05, 0x85, 0x24, 0xe1, 0x00, 0x8a, 0x6f, 0x70, 0x9c, - 0xaf, 0x9e, 0x38, 0x5c, 0xf9, 0x5b, 0x02, 0x60, 0xb9, 0xff, 0xf0, 0x23, 0x70, 0xbe, 0x54, 0xa9, - 0x68, 0xad, 0x96, 0xd1, 0x3e, 0x68, 0x6a, 0xc6, 0xdd, 0x7a, 0xab, 0xa9, 0x55, 0xf4, 0x4f, 0x75, - 0xad, 0x2a, 0xc7, 0xb2, 0x99, 0xf1, 0x24, 0xbf, 0xb3, 0x0c, 0xbe, 0xeb, 0xd1, 0x01, 0xb6, 0x9c, - 0x43, 0x07, 0xdb, 0xf0, 0x1a, 0x80, 0x51, 0x5c, 0xbd, 0x51, 0x6e, 0x54, 0x0f, 0x64, 0x29, 0xbb, - 0x3d, 0x9e, 0xe4, 0xe5, 0x25, 0xa4, 0x4e, 0x3a, 0xc4, 0x0e, 0xe0, 0xc7, 0x20, 0x1d, 0x8d, 0x6e, - 0xd4, 0x3f, 0x3b, 0x30, 0x4a, 0xd5, 0x2a, 0xd2, 0x5a, 0x2d, 0x39, 0xfe, 0x7c, 0x9a, 0x86, 0xe7, - 0x06, 0x25, 0xf1, 0x9d, 0x85, 0x7b, 0x60, 0x27, 0x0a, 0xd4, 0x3e, 0xd7, 0xd0, 0x01, 0xcf, 0x94, - 0xc8, 0x9e, 0x1f, 0x4f, 0xf2, 0x6f, 0x2c, 0x51, 0xda, 0x11, 0xf6, 0x83, 0x30, 0x59, 0x76, 0xf3, - 0xcb, 0x1f, 0x95, 0xd8, 0xa3, 0x9f, 0x94, 0xd8, 0x95, 0x9f, 0x13, 0x20, 0x7f, 0xda, 0xa4, 0x41, - 0x0c, 0xde, 0xaf, 0x34, 0xea, 0x6d, 0x54, 0xaa, 0xb4, 0x8d, 0x4a, 0xa3, 0xaa, 0x19, 0x35, 0xbd, - 0xd5, 0x6e, 0xa0, 0x03, 0xa3, 0xd1, 0xd4, 0x50, 0xa9, 0xad, 0x37, 0xea, 0x27, 0x49, 0x53, 0x1c, - 0x4f, 0xf2, 0x57, 0x4f, 0xe3, 0x8e, 0x0a, 0x76, 0x0f, 0x5c, 0x5e, 0x29, 0x8d, 0x5e, 0xd7, 0xdb, - 0xb2, 0x94, 0xdd, 0x1d, 0x4f, 0xf2, 0x97, 0x4e, 0xe3, 0xd7, 0x3d, 0x87, 0xc1, 0xfb, 0xe0, 0xda, - 0x4a, 0xc4, 0xfb, 0xfa, 0x6d, 0x54, 0x6a, 0x6b, 0x72, 0x3c, 0x7b, 0x75, 0x3c, 0xc9, 0xbf, 0x77, - 0x1a, 0xf7, 0xbe, 0xd3, 0xf5, 0x4d, 0x86, 0x57, 0xa6, 0xbf, 0xad, 0xd5, 0xb5, 0x96, 0xde, 0x92, - 0x13, 0xab, 0xd1, 0xdf, 0xc6, 0x1e, 0xa6, 0x0e, 0xcd, 0x26, 0xc3, 0xcb, 0x2a, 0xd7, 0x1e, 0xff, - 0xa9, 0xc4, 0x1e, 0xcd, 0x14, 0xe9, 0xf1, 0x4c, 0x91, 0x9e, 0xcc, 0x14, 0xe9, 0x8f, 0x99, 0x22, - 0x7d, 0xf3, 0x54, 0x89, 0x3d, 0x79, 0xaa, 0xc4, 0x7e, 0x7f, 0xaa, 0xc4, 0xbe, 0x78, 0x37, 0xb2, - 0x07, 0x15, 0x42, 0xfb, 0xf7, 0x16, 0xbf, 0x3a, 0x76, 0x71, 0x24, 0x7e, 0x79, 0xf8, 0xff, 0x4e, - 0x67, 0x9d, 0x7f, 0xd5, 0x3e, 0xf8, 0x37, 0x00, 0x00, 0xff, 0xff, 0x21, 0x8c, 0xa0, 0x70, 0x10, - 0x09, 0x00, 0x00, + // 1178 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x56, 0xcf, 0x8f, 0xdb, 0xc4, + 0x17, 0x8f, 0x93, 0xec, 0x8f, 0x4c, 0xf7, 0xdb, 0xaf, 0x3b, 0xec, 0xd2, 0x6c, 0x58, 0x25, 0xa9, + 0x29, 0xb0, 0xfd, 0x95, 0xd0, 0x05, 0x01, 0xea, 0xa1, 0x52, 0x7e, 0xb8, 0x5d, 0x57, 0x6c, 0x1c, + 0x4d, 0x52, 0xaa, 0x45, 0xaa, 0x2c, 0xc7, 0x9e, 0xcd, 0x5a, 0x75, 0x3c, 0x91, 0x67, 0xb2, 0x8d, + 0xff, 0x03, 0x14, 0x09, 0xc1, 0x91, 0x4b, 0x24, 0x04, 0x08, 0x95, 0x3b, 0x57, 0xee, 0x15, 0x5c, + 0x7a, 0xe4, 0x14, 0xc1, 0xf6, 0xc2, 0x39, 0xc7, 0x72, 0x41, 0x9e, 0x89, 0x1b, 0xd3, 0x6e, 0xbb, + 0xe1, 0x62, 0xcd, 0xbc, 0xf7, 0x3e, 0x9f, 0xf7, 0xde, 0x67, 0xe6, 0x8d, 0x0c, 0xb6, 0x2c, 0x42, + 0x7b, 0x0f, 0x4d, 0xda, 0x2b, 0xf3, 0xcf, 0xd1, 0xf5, 0x32, 0x0b, 0xfa, 0x98, 0x96, 0xfa, 0x3e, + 0x61, 0x04, 0xca, 0x91, 0xb7, 0xc4, 0x3f, 0x47, 0xd7, 0x73, 0x9b, 0xa1, 0x85, 0x50, 0x83, 0xfb, + 0xcb, 0x62, 0x23, 0x82, 0x73, 0xeb, 0x5d, 0xd2, 0x25, 0xc2, 0x1e, 0xae, 0x66, 0xd6, 0xcd, 0x2e, + 0x21, 0x5d, 0x17, 0x97, 0xf9, 0xae, 0x33, 0x38, 0x28, 0x9b, 0x5e, 0x20, 0x5c, 0xca, 0x7d, 0xf0, + 0xff, 0x8a, 0x65, 0x61, 0x4a, 0xdb, 0x41, 0x1f, 0x37, 0x4d, 0xdf, 0xec, 0xc1, 0x3a, 0x58, 0x3a, + 0x32, 0xdd, 0x01, 0xce, 0x4a, 0x45, 0x69, 0xfb, 0xec, 0xce, 0x56, 0xe9, 0xc5, 0x02, 0x4a, 0x73, + 0x44, 0x55, 0x9e, 0x4e, 0x0a, 0x6b, 0x81, 0xd9, 0x73, 0x6f, 0x28, 0x1c, 0xa4, 0x20, 0x01, 0xbe, + 0x91, 0xfe, 0xe6, 0xdb, 0x82, 0xa4, 0xfc, 0x26, 0x81, 0x35, 0x11, 0x5d, 0x23, 0xde, 0x81, 0xd3, + 0x85, 0x2d, 0x00, 0xfa, 0xd8, 0xef, 0x39, 0x94, 0x3a, 0xc4, 0x5b, 0x28, 0xc3, 0xc6, 0x74, 0x52, + 0x38, 0x27, 0x32, 0xcc, 0x91, 0x0a, 0x8a, 0xd1, 0xc0, 0xab, 0x60, 0xc5, 0xb4, 0x6d, 0x1f, 0x53, + 0x9a, 0x4d, 0x16, 0xa5, 0xed, 0x4c, 0x15, 0x4e, 0x27, 0x85, 0xb3, 0x02, 0x33, 0x73, 0x28, 0x28, + 0x0a, 0x81, 0x3b, 0x20, 0x33, 0x5b, 0x62, 0x9a, 0x4d, 0x15, 0x53, 0xdb, 0x99, 0xea, 0xfa, 0x74, + 0x52, 0x90, 0xff, 0x15, 0x8f, 0xa9, 0x82, 0xe6, 0x61, 0xb3, 0x6e, 0xbe, 0x4a, 0x82, 0x65, 0xae, + 0x11, 0x85, 0x04, 0x40, 0x8b, 0xd8, 0xd8, 0x18, 0xf4, 0x5d, 0x62, 0xda, 0x86, 0xc9, 0xeb, 0xe5, + 0xfd, 0x9c, 0xd9, 0xc9, 0xbf, 0xaa, 0x1f, 0xa1, 0x41, 0xf5, 0xc2, 0xe3, 0x49, 0x21, 0x31, 0x9d, + 0x14, 0x36, 0x45, 0xc6, 0x97, 0x79, 0x14, 0x24, 0x87, 0xc6, 0xbb, 0xdc, 0x26, 0xa0, 0xf0, 0x4b, + 0x09, 0xe4, 0x1d, 0x8f, 0x32, 0xd3, 0x63, 0x8e, 0xc9, 0xb0, 0x61, 0xe3, 0x03, 0x73, 0xe0, 0x32, + 0x23, 0xa6, 0x66, 0x72, 0x01, 0x35, 0x2f, 0x4d, 0x27, 0x85, 0x77, 0x44, 0xde, 0xd7, 0xb3, 0x29, + 0x68, 0x2b, 0x16, 0x50, 0x17, 0xfe, 0xe6, 0x73, 0x37, 0x57, 0x24, 0xa1, 0x7c, 0x27, 0x81, 0xd5, + 0x1a, 0xb1, 0xb1, 0xe6, 0x1d, 0x10, 0xf8, 0x16, 0xc8, 0xf0, 0x5e, 0x0e, 0x4d, 0x7a, 0xc8, 0xa5, + 0x58, 0x43, 0xab, 0xa1, 0x61, 0xd7, 0xa4, 0x87, 0x30, 0x0b, 0x56, 0x2c, 0x1f, 0x9b, 0x8c, 0xf8, + 0xe2, 0x8c, 0x50, 0xb4, 0x85, 0x2d, 0x00, 0xe3, 0xa5, 0x58, 0x5c, 0xa4, 0xec, 0xd2, 0x42, 0x52, + 0xa6, 0x43, 0x29, 0xd1, 0xb9, 0x18, 0x5e, 0x38, 0xee, 0xa4, 0x57, 0x53, 0x72, 0xfa, 0x4e, 0x7a, + 0x35, 0x2d, 0x2f, 0x29, 0xbf, 0x24, 0xc1, 0x5a, 0x8d, 0x78, 0xcc, 0x37, 0x2d, 0xc6, 0x0b, 0x7d, + 0x1b, 0xac, 0xf0, 0x42, 0x1d, 0x9b, 0x97, 0x99, 0xae, 0x82, 0xe3, 0x49, 0x61, 0x99, 0xf7, 0x51, + 0x47, 0xcb, 0xa1, 0x4b, 0xb3, 0x5f, 0x53, 0xf0, 0x3a, 0x58, 0x32, 0xed, 0x9e, 0xe3, 0x65, 0x53, + 0xdc, 0x2e, 0x36, 0xa1, 0xd5, 0x35, 0x3b, 0xd8, 0xcd, 0xa6, 0x85, 0x95, 0x6f, 0xe0, 0xcd, 0x19, + 0x0b, 0xb6, 0x67, 0x1d, 0x5d, 0x3c, 0xa1, 0xa3, 0x0e, 0x25, 0xee, 0x80, 0xe1, 0xf6, 0xb0, 0x49, + 0xa8, 0xc3, 0x1c, 0xe2, 0xa1, 0x08, 0x04, 0xaf, 0x81, 0x33, 0x4e, 0xc7, 0x32, 0xfa, 0xc4, 0x67, + 0x61, 0xb9, 0xcb, 0xfc, 0x7a, 0xff, 0xef, 0x78, 0x52, 0xc8, 0x68, 0xd5, 0x5a, 0x93, 0xf8, 0x4c, + 0xab, 0xa3, 0x8c, 0xd3, 0xb1, 0xf8, 0xd2, 0x86, 0x7b, 0x20, 0x83, 0x87, 0x0c, 0x7b, 0xfc, 0x3e, + 0xac, 0xf0, 0x84, 0xeb, 0x25, 0x31, 0xfd, 0xa5, 0x68, 0xfa, 0x4b, 0x15, 0x2f, 0xa8, 0x6e, 0xfe, + 0xfa, 0xf3, 0xb5, 0x8d, 0xb8, 0x28, 0x6a, 0x04, 0x43, 0x73, 0x86, 0x1b, 0xe9, 0xbf, 0xc2, 0x6b, + 0xff, 0xb7, 0x04, 0xb2, 0x51, 0x68, 0x28, 0xd2, 0xae, 0x43, 0x19, 0xf1, 0x03, 0xd5, 0x63, 0x7e, + 0x00, 0x9b, 0x20, 0x43, 0xfa, 0xd8, 0x37, 0xd9, 0x7c, 0x9e, 0x77, 0x5e, 0x6e, 0xf1, 0x04, 0xb8, + 0x1e, 0xa1, 0xc2, 0x7b, 0x89, 0xe6, 0x24, 0xf1, 0xd3, 0x49, 0xbe, 0xf2, 0x74, 0x6e, 0x82, 0x95, + 0x41, 0xdf, 0xe6, 0xba, 0xa6, 0xfe, 0x8b, 0xae, 0x33, 0x10, 0xdc, 0x06, 0xa9, 0x1e, 0xed, 0xf2, + 0xb3, 0x5a, 0xab, 0xbe, 0xf9, 0x6c, 0x52, 0x80, 0xc8, 0x7c, 0x18, 0x55, 0xb9, 0x87, 0x29, 0x35, + 0xbb, 0x18, 0x85, 0x21, 0x0a, 0x02, 0xf0, 0x65, 0x22, 0x78, 0x01, 0xac, 0x75, 0x5c, 0x62, 0x3d, + 0x30, 0x0e, 0xb1, 0xd3, 0x3d, 0x64, 0xe2, 0x1e, 0xa1, 0x33, 0xdc, 0xb6, 0xcb, 0x4d, 0x70, 0x13, + 0xac, 0xb2, 0xa1, 0xe1, 0x78, 0x36, 0x1e, 0x8a, 0x46, 0xd0, 0x0a, 0x1b, 0x6a, 0xe1, 0x56, 0x71, + 0xc0, 0xd2, 0x1e, 0xb1, 0xb1, 0x0b, 0xef, 0x80, 0xd4, 0x03, 0x1c, 0x88, 0x61, 0xa9, 0x7e, 0xf2, + 0x6c, 0x52, 0xf8, 0xb0, 0xeb, 0xb0, 0xc3, 0x41, 0xa7, 0x64, 0x91, 0x5e, 0x99, 0x61, 0xcf, 0x0e, + 0x07, 0xce, 0x63, 0xf1, 0xa5, 0xeb, 0x74, 0x68, 0xb9, 0x13, 0x30, 0x4c, 0x4b, 0xbb, 0x78, 0x58, + 0x0d, 0x17, 0x28, 0x24, 0x09, 0x2f, 0xa0, 0x78, 0xb7, 0x93, 0x7c, 0xf4, 0xc4, 0xe6, 0xf2, 0x4f, + 0x49, 0x00, 0xe6, 0xf3, 0x0f, 0x3f, 0x02, 0xe7, 0x2b, 0xb5, 0x9a, 0xda, 0x6a, 0x19, 0xed, 0xfd, + 0xa6, 0x6a, 0xdc, 0x6d, 0xb4, 0x9a, 0x6a, 0x4d, 0xbb, 0xa5, 0xa9, 0x75, 0x39, 0x91, 0xdb, 0x1c, + 0x8d, 0x8b, 0x1b, 0xf3, 0xe0, 0xbb, 0x1e, 0xed, 0x63, 0xcb, 0x39, 0x70, 0xb0, 0x0d, 0xaf, 0x02, + 0x18, 0xc7, 0x35, 0xf4, 0xaa, 0x5e, 0xdf, 0x97, 0xa5, 0xdc, 0xfa, 0x68, 0x5c, 0x94, 0xe7, 0x90, + 0x06, 0xe9, 0x10, 0x3b, 0x80, 0x1f, 0x83, 0x6c, 0x3c, 0x5a, 0x6f, 0x7c, 0xba, 0x6f, 0x54, 0xea, + 0x75, 0xa4, 0xb6, 0x5a, 0x72, 0xf2, 0xc5, 0x34, 0xba, 0xe7, 0x06, 0x95, 0xe7, 0x6f, 0xf3, 0x46, + 0x1c, 0xa8, 0x7e, 0xa6, 0xa2, 0x7d, 0x9e, 0x29, 0x95, 0x3b, 0x3f, 0x1a, 0x17, 0xdf, 0x98, 0xa3, + 0xd4, 0x23, 0xec, 0x07, 0x3c, 0xd9, 0x4d, 0xb0, 0x15, 0xc7, 0x54, 0x1a, 0xfb, 0x86, 0x7e, 0x2b, + 0x4a, 0xa7, 0xb6, 0xe4, 0x74, 0x6e, 0x6b, 0x34, 0x2e, 0x66, 0xe7, 0xd0, 0x8a, 0x17, 0xe8, 0x07, + 0x95, 0xe8, 0x6d, 0xcf, 0xad, 0x7e, 0xf1, 0x7d, 0x3e, 0xf1, 0xe8, 0x87, 0x7c, 0xe2, 0xf2, 0x8f, + 0x29, 0x50, 0x3c, 0xed, 0xa6, 0x42, 0x0c, 0xde, 0xaf, 0xe9, 0x8d, 0x36, 0xaa, 0xd4, 0xda, 0x46, + 0x4d, 0xaf, 0xab, 0xc6, 0xae, 0xd6, 0x6a, 0xeb, 0x68, 0xdf, 0xd0, 0x9b, 0x2a, 0xaa, 0xb4, 0x35, + 0xbd, 0x71, 0x92, 0xb4, 0xe5, 0xd1, 0xb8, 0x78, 0xe5, 0x34, 0xee, 0xb8, 0xe0, 0xf7, 0xc0, 0xa5, + 0x85, 0xd2, 0x68, 0x0d, 0xad, 0x2d, 0x4b, 0xb9, 0xed, 0xd1, 0xb8, 0x78, 0xf1, 0x34, 0x7e, 0xcd, + 0x73, 0x18, 0xbc, 0x0f, 0xae, 0x2e, 0x44, 0xbc, 0xa7, 0xdd, 0x46, 0x95, 0xb6, 0x2a, 0x27, 0x73, + 0x57, 0x46, 0xe3, 0xe2, 0x7b, 0xa7, 0x71, 0xef, 0x39, 0x5d, 0xdf, 0x64, 0x78, 0x61, 0xfa, 0xdb, + 0x6a, 0x43, 0x6d, 0x69, 0x2d, 0x39, 0xb5, 0x18, 0xfd, 0x6d, 0xec, 0x61, 0xea, 0xd0, 0x5c, 0x3a, + 0x3c, 0xac, 0xea, 0xee, 0xe3, 0x3f, 0xf3, 0x89, 0x47, 0xc7, 0x79, 0xe9, 0xf1, 0x71, 0x5e, 0x7a, + 0x72, 0x9c, 0x97, 0xfe, 0x38, 0xce, 0x4b, 0x5f, 0x3f, 0xcd, 0x27, 0x9e, 0x3c, 0xcd, 0x27, 0x7e, + 0x7f, 0x9a, 0x4f, 0x7c, 0xfe, 0x6e, 0x6c, 0x8e, 0x6a, 0x84, 0xf6, 0xee, 0x45, 0xbf, 0x57, 0x76, + 0x79, 0x28, 0x7e, 0xb3, 0xf8, 0x3f, 0x56, 0x67, 0x99, 0xbf, 0x8a, 0x1f, 0xfc, 0x13, 0x00, 0x00, + 0xff, 0xff, 0x7a, 0x16, 0x8c, 0xd9, 0x84, 0x09, 0x00, 0x00, } func (this *AccessTypeParam) Equal(that interface{}) bool { @@ -617,6 +628,14 @@ func (this *AccessConfig) Equal(that interface{}) bool { if this.Address != that1.Address { return false } + if len(this.Addresses) != len(that1.Addresses) { + return false + } + for i := range this.Addresses { + if this.Addresses[i] != that1.Addresses[i] { + return false + } + } return true } @@ -860,6 +879,15 @@ func (m *AccessConfig) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.Addresses) > 0 { + for iNdEx := len(m.Addresses) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Addresses[iNdEx]) + copy(dAtA[i:], m.Addresses[iNdEx]) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Addresses[iNdEx]))) + i-- + dAtA[i] = 0x1a + } + } if len(m.Address) > 0 { i -= len(m.Address) copy(dAtA[i:], m.Address) @@ -1199,6 +1227,12 @@ func (m *AccessConfig) Size() (n int) { if l > 0 { n += 1 + l + sovTypes(uint64(l)) } + if len(m.Addresses) > 0 { + for _, s := range m.Addresses { + l = len(s) + n += 1 + l + sovTypes(uint64(l)) + } + } return n } @@ -1484,6 +1518,38 @@ func (m *AccessConfig) Unmarshal(dAtA []byte) error { } m.Address = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Addresses", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Addresses = append(m.Addresses, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTypes(dAtA[iNdEx:]) diff --git a/x/wasm/types/types_test.go b/x/wasm/types/types_test.go index 91b39419c5..be270ddeb3 100644 --- a/x/wasm/types/types_test.go +++ b/x/wasm/types/types_test.go @@ -374,78 +374,186 @@ func TestVerifyAddressLen(t *testing.T) { } func TestAccessConfigSubset(t *testing.T) { + // read + // <, <= is subset of + // !< is not subset of specs := map[string]struct { check AccessConfig superSet AccessConfig isSubSet bool }{ + // nobody "nobody <= nobody": { superSet: AccessConfig{Permission: AccessTypeNobody}, check: AccessConfig{Permission: AccessTypeNobody}, isSubSet: true, }, - "only > nobody": { + "only !< nobody": { superSet: AccessConfig{Permission: AccessTypeNobody}, check: AccessConfig{Permission: AccessTypeOnlyAddress, Address: "foobar"}, isSubSet: false, }, - "everybody > nobody": { + "anyOf !< nobody": { + superSet: AccessConfig{Permission: AccessTypeNobody}, + check: AccessConfig{Permission: AccessTypeAnyOfAddresses, Addresses: []string{"foobar"}}, + isSubSet: false, + }, + "everybody !< nobody ": { superSet: AccessConfig{Permission: AccessTypeNobody}, check: AccessConfig{Permission: AccessTypeEverybody}, isSubSet: false, }, - "unspecified > nobody": { + "unspecified !< nobody": { superSet: AccessConfig{Permission: AccessTypeNobody}, check: AccessConfig{Permission: AccessTypeUnspecified}, isSubSet: false, }, - "nobody <= everybody": { + // only + "nobody < only": { + superSet: AccessConfig{Permission: AccessTypeOnlyAddress, Address: "owner"}, + check: AccessConfig{Permission: AccessTypeNobody}, + isSubSet: true, + }, + "only <= only(same)": { + superSet: AccessConfig{Permission: AccessTypeOnlyAddress, Address: "owner"}, + check: AccessConfig{Permission: AccessTypeOnlyAddress, Address: "owner"}, + isSubSet: true, + }, + "only !< only(other)": { + superSet: AccessConfig{Permission: AccessTypeOnlyAddress, Address: "owner"}, + check: AccessConfig{Permission: AccessTypeOnlyAddress, Address: "other"}, + isSubSet: false, + }, + "anyOf(same) <= only": { + superSet: AccessConfig{Permission: AccessTypeOnlyAddress, Address: "owner"}, + check: AccessConfig{Permission: AccessTypeAnyOfAddresses, Addresses: []string{"owner"}}, + isSubSet: true, + }, + "anyOf(other) !< only": { + superSet: AccessConfig{Permission: AccessTypeOnlyAddress, Address: "owner"}, + check: AccessConfig{Permission: AccessTypeAnyOfAddresses, Addresses: []string{"foobar"}}, + isSubSet: false, + }, + "anyOf(same, other) !< only": { + superSet: AccessConfig{Permission: AccessTypeOnlyAddress, Address: "owner"}, + check: AccessConfig{Permission: AccessTypeAnyOfAddresses, Addresses: []string{"owner", "foobar"}}, + isSubSet: false, + }, + "everybody !< only": { + superSet: AccessConfig{Permission: AccessTypeOnlyAddress, Address: "owner"}, + check: AccessConfig{Permission: AccessTypeEverybody}, + isSubSet: false, + }, + "unspecified !<= only": { + superSet: AccessConfig{Permission: AccessTypeOnlyAddress, Address: "owner"}, + check: AccessConfig{Permission: AccessTypeUnspecified}, + isSubSet: false, + }, + + // any of + "nobody < anyOf": { + superSet: AccessConfig{Permission: AccessTypeAnyOfAddresses, Addresses: []string{"owner"}}, + check: AccessConfig{Permission: AccessTypeNobody}, + isSubSet: true, + }, + "only(same) < anyOf(same)": { + superSet: AccessConfig{Permission: AccessTypeAnyOfAddresses, Addresses: []string{"owner"}}, + check: AccessConfig{Permission: AccessTypeOnlyAddress, Address: "owner"}, + isSubSet: true, + }, + "only(same) < anyOf(same, other)": { + superSet: AccessConfig{Permission: AccessTypeAnyOfAddresses, Addresses: []string{"owner", "other"}}, + check: AccessConfig{Permission: AccessTypeOnlyAddress, Address: "owner"}, + isSubSet: true, + }, + "only(other) !< anyOf": { + superSet: AccessConfig{Permission: AccessTypeAnyOfAddresses, Addresses: []string{"owner"}}, + check: AccessConfig{Permission: AccessTypeOnlyAddress, Address: "other"}, + isSubSet: false, + }, + "anyOf < anyOf": { + superSet: AccessConfig{Permission: AccessTypeAnyOfAddresses, Addresses: []string{"owner"}}, + check: AccessConfig{Permission: AccessTypeAnyOfAddresses, Addresses: []string{"owner"}}, + isSubSet: true, + }, + "anyOf(multiple) < anyOf(multiple)": { + superSet: AccessConfig{Permission: AccessTypeAnyOfAddresses, Addresses: []string{"owner", "other"}}, + check: AccessConfig{Permission: AccessTypeAnyOfAddresses, Addresses: []string{"owner", "other"}}, + isSubSet: true, + }, + "anyOf(multiple, other) !< anyOf(multiple)": { + superSet: AccessConfig{Permission: AccessTypeAnyOfAddresses, Addresses: []string{"owner", "other", "foo"}}, + check: AccessConfig{Permission: AccessTypeAnyOfAddresses, Addresses: []string{"owner", "other", "bar"}}, + isSubSet: false, + }, + "anyOf(multiple) !< anyOf": { + superSet: AccessConfig{Permission: AccessTypeAnyOfAddresses, Addresses: []string{"owner"}}, + check: AccessConfig{Permission: AccessTypeAnyOfAddresses, Addresses: []string{"owner", "other"}}, + isSubSet: false, + }, + "everybody !< anyOf": { + superSet: AccessConfig{Permission: AccessTypeAnyOfAddresses, Addresses: []string{"owner"}}, + check: AccessConfig{Permission: AccessTypeEverybody}, + isSubSet: false, + }, + "unspecified !< anyOf ": { + superSet: AccessConfig{Permission: AccessTypeAnyOfAddresses, Addresses: []string{"owner"}}, + check: AccessConfig{Permission: AccessTypeUnspecified}, + isSubSet: false, + }, + // everybody + "nobody < everybody": { superSet: AccessConfig{Permission: AccessTypeEverybody}, check: AccessConfig{Permission: AccessTypeNobody}, isSubSet: true, }, - "only <= everybody": { + "only < everybody": { superSet: AccessConfig{Permission: AccessTypeEverybody}, check: AccessConfig{Permission: AccessTypeOnlyAddress, Address: "foobar"}, isSubSet: true, }, + "anyOf < everybody": { + superSet: AccessConfig{Permission: AccessTypeEverybody}, + check: AccessConfig{Permission: AccessTypeAnyOfAddresses, Addresses: []string{"foobar"}}, + isSubSet: true, + }, "everybody <= everybody": { superSet: AccessConfig{Permission: AccessTypeEverybody}, check: AccessConfig{Permission: AccessTypeEverybody}, isSubSet: true, }, - "unspecified > everybody": { + "unspecified !< everybody ": { superSet: AccessConfig{Permission: AccessTypeEverybody}, check: AccessConfig{Permission: AccessTypeUnspecified}, isSubSet: false, }, - "nobody <= only": { - superSet: AccessConfig{Permission: AccessTypeOnlyAddress, Address: "owner"}, + // unspecified + "nobody !< unspecified": { + superSet: AccessConfig{Permission: AccessTypeUnspecified}, check: AccessConfig{Permission: AccessTypeNobody}, - isSubSet: true, + isSubSet: false, }, - "only <= only(same)": { - superSet: AccessConfig{Permission: AccessTypeOnlyAddress, Address: "owner"}, - check: AccessConfig{Permission: AccessTypeOnlyAddress, Address: "owner"}, - isSubSet: true, + "only !< unspecified": { + superSet: AccessConfig{Permission: AccessTypeUnspecified}, + check: AccessConfig{Permission: AccessTypeOnlyAddress, Address: "foobar"}, + isSubSet: false, }, - "only > only(other)": { - superSet: AccessConfig{Permission: AccessTypeOnlyAddress, Address: "owner"}, - check: AccessConfig{Permission: AccessTypeOnlyAddress, Address: "other"}, + "anyOf !< unspecified": { + superSet: AccessConfig{Permission: AccessTypeUnspecified}, + check: AccessConfig{Permission: AccessTypeAnyOfAddresses, Addresses: []string{"foobar"}}, isSubSet: false, }, - "everybody > only": { - superSet: AccessConfig{Permission: AccessTypeOnlyAddress, Address: "owner"}, + "everybody !< unspecified": { + superSet: AccessConfig{Permission: AccessTypeUnspecified}, check: AccessConfig{Permission: AccessTypeEverybody}, isSubSet: false, }, - "nobody > unspecified": { + "unspecified !< unspecified ": { superSet: AccessConfig{Permission: AccessTypeUnspecified}, - check: AccessConfig{Permission: AccessTypeNobody}, + check: AccessConfig{Permission: AccessTypeUnspecified}, isSubSet: false, }, } - for name, spec := range specs { t.Run(name, func(t *testing.T) { subset := spec.check.IsSubset(spec.superSet) @@ -460,64 +568,134 @@ func TestAccessTypeSubset(t *testing.T) { superSet AccessType isSubSet bool }{ + // nobody "nobody <= nobody": { superSet: AccessTypeNobody, check: AccessTypeNobody, isSubSet: true, }, - "only > nobody": { + "only !< nobody": { superSet: AccessTypeNobody, check: AccessTypeOnlyAddress, isSubSet: false, }, - "everybody > nobody": { + "any !< nobody": { + superSet: AccessTypeNobody, + check: AccessTypeAnyOfAddresses, + isSubSet: false, + }, + "everybody !< nobody": { superSet: AccessTypeNobody, check: AccessTypeEverybody, isSubSet: false, }, - "unspecified > nobody": { + "unspecified !< nobody": { superSet: AccessTypeNobody, check: AccessTypeUnspecified, isSubSet: false, }, - "nobody <= everybody": { + // only + "nobody < only": { + superSet: AccessTypeOnlyAddress, + check: AccessTypeNobody, + isSubSet: true, + }, + "only <= only": { + superSet: AccessTypeOnlyAddress, + check: AccessTypeOnlyAddress, + isSubSet: true, + }, + "anyOf !< only": { + superSet: AccessTypeOnlyAddress, + check: AccessTypeAnyOfAddresses, + isSubSet: true, + }, + "everybody !< only": { + superSet: AccessTypeOnlyAddress, + check: AccessTypeEverybody, + isSubSet: false, + }, + "unspecified !< only": { + superSet: AccessTypeOnlyAddress, + check: AccessTypeUnspecified, + isSubSet: false, + }, + // any of + "nobody < anyOf": { + superSet: AccessTypeAnyOfAddresses, + check: AccessTypeNobody, + isSubSet: true, + }, + "only <= anyOf": { + superSet: AccessTypeAnyOfAddresses, + check: AccessTypeOnlyAddress, + isSubSet: true, + }, + "anyOf <= anyOf": { + superSet: AccessTypeAnyOfAddresses, + check: AccessTypeAnyOfAddresses, + isSubSet: true, + }, + "everybody !< anyOf": { + superSet: AccessTypeAnyOfAddresses, + check: AccessTypeEverybody, + isSubSet: false, + }, + "unspecified !< anyOf": { + superSet: AccessTypeAnyOfAddresses, + check: AccessTypeUnspecified, + isSubSet: false, + }, + // everybody + "nobody < everybody": { superSet: AccessTypeEverybody, check: AccessTypeNobody, isSubSet: true, }, - "only <= everybody": { + "only < everybody": { superSet: AccessTypeEverybody, check: AccessTypeOnlyAddress, isSubSet: true, }, + "anyOf < everybody": { + superSet: AccessTypeEverybody, + check: AccessTypeAnyOfAddresses, + isSubSet: true, + }, "everybody <= everybody": { superSet: AccessTypeEverybody, check: AccessTypeEverybody, isSubSet: true, }, - "unspecified > everybody": { + "unspecified !< everybody": { superSet: AccessTypeEverybody, check: AccessTypeUnspecified, isSubSet: false, }, - "nobody <= only": { - superSet: AccessTypeOnlyAddress, + // unspecified + "nobody !< unspecified": { + superSet: AccessTypeUnspecified, check: AccessTypeNobody, - isSubSet: true, + isSubSet: false, }, - "only <= only(same)": { - superSet: AccessTypeOnlyAddress, + "only !< unspecified": { + superSet: AccessTypeUnspecified, check: AccessTypeOnlyAddress, - isSubSet: true, + isSubSet: false, }, - "everybody > only": { - superSet: AccessTypeOnlyAddress, + "anyOf !< unspecified": { + superSet: AccessTypeUnspecified, + check: AccessTypeAnyOfAddresses, + isSubSet: false, + }, + "everybody !< unspecified": { + superSet: AccessTypeUnspecified, check: AccessTypeEverybody, isSubSet: false, }, - "nobody > unspecified": { + "unspecified !< unspecified": { superSet: AccessTypeUnspecified, - check: AccessTypeNobody, + check: AccessTypeUnspecified, isSubSet: false, }, }