diff --git a/common/fixation_entry.go b/common/fixation_entry.go index d6b1999a45..329dbab526 100644 --- a/common/fixation_entry.go +++ b/common/fixation_entry.go @@ -164,12 +164,15 @@ func (fs *FixationStore) getEntryStore(ctx sdk.Context, index string) *prefix.St return &store } -func (fs *FixationStore) replaceTimer(ctx sdk.Context, prev, next types.Entry, kind byte) { +// transferTimer moves a timer (unexpired) from a previous entry to a new entry, with +// the same expirty block. Useful, for example, when a newer entry takes responsibility +// for a pending deletion from the previous owner. +func (fs *FixationStore) transferTimer(ctx sdk.Context, prev, next types.Entry, block uint64, kind byte) { key := encodeForTimer(prev.Index, prev.Block, kind) - fs.tstore.DelTimerByBlockHeight(ctx, next.DeleteAt, key) + fs.tstore.DelTimerByBlockHeight(ctx, block, key) key = encodeForTimer(next.Index, next.Block, kind) - fs.tstore.AddTimerByBlockHeight(ctx, next.DeleteAt, key, []byte{}) + fs.tstore.AddTimerByBlockHeight(ctx, block, key, []byte{}) } // getEntry returns an existing entry in the store @@ -227,7 +230,7 @@ func (fs *FixationStore) AppendEntry( // temporary: do not allow adding new entries for an index that was deleted // and still not fully cleaned up (e.g. not stale or with references held) - if latestEntry.IsDeletedBy(ctxBlock) { + if latestEntry.IsDeletedBy(block) { return utils.LavaFormatError("AppendEntry", fmt.Errorf("entry already deleted and pending cleanup"), utils.Attribute{Key: "index", Value: index}, @@ -241,17 +244,22 @@ func (fs *FixationStore) AppendEntry( return nil } + // if the previous latest entry is marked with DeleteAt which is set to expire after + // theis future entry's maturity (block), then transfer this DeleteAt to the future + // entry, and then replace the old timer with a new timer (below) + // (note: deletion, if any, cannot be for the current block, since it would have been + // processed at the beginning of the block, and AppendEntry would fail earlier). + + if latestEntry.HasDeleteAt() { // already know !latestEntry.IsDeletedBy(block) + deleteAt = latestEntry.DeleteAt + latestEntry.DeleteAt = math.MaxUint64 + } + // if we are superseding a previous latest entry, then drop the latter's refcount; // otherwise we are a future entry version, so set a timer for when it will become // the new latest entry. - // and if indeed we are, and the latest entry has DeleteAt set, then transfer this - // DeleteAt to the new entry, drop the old timer, and start a new timer. (note: - // deletion, if any, cannot be for the current block, because it would have been - // processed at the beginning of the block (and AppendEntry would fail earlier). if block == ctxBlock { - deleteAt = latestEntry.DeleteAt - latestEntry.DeleteAt = math.MaxUint64 fs.putEntry(ctx, latestEntry) } else { key := encodeForTimer(safeIndex, block, timerFutureEntry) @@ -273,7 +281,7 @@ func (fs *FixationStore) AppendEntry( } if entry.HasDeleteAt() { - fs.replaceTimer(ctx, latestEntry, entry, timerDeleteEntry) + fs.transferTimer(ctx, latestEntry, entry, entry.DeleteAt, timerDeleteEntry) } fs.setEntry(ctx, entry) @@ -302,28 +310,13 @@ func (fs *FixationStore) updateFutureEntry(ctx sdk.Context, safeIndex string, bl latestEntry, found := fs.getUnmarshaledEntryForBlock(ctx, safeIndex, block-1) if found { - // if the latest entry has DeleteAt set, then transfer this DeleteAt to this - // future entry (becoming latest). also, drop the old timer, and start a new - // timer. (note: // deletion, if any, cannot be for the current block, since - // it would have been processed at the beginning of the block. + // previous latest entry should never have its DeleteAt set for this block: + // if our AppendEntry happened before the DelEntry, we would get marked (and + // not the previous latest entry); if the DelEntry happened first, then we + // would inherit the DeleteAt from the previous latest entry. if latestEntry.HasDeleteAt() { - entry := fs.getEntry(ctx, safeIndex, block) - entry.DeleteAt = latestEntry.DeleteAt - latestEntry.DeleteAt = math.MaxUint64 - fs.setEntry(ctx, entry) - - if entry.IsDeletedBy(block) { - // if DeleteAt is exactly now as we transition from future entry to - // be the latest entry, then simply invoke deleteMarkedEntry(). - key := encodeForTimer(latestEntry.Index, latestEntry.Block, timerDeleteEntry) - fs.tstore.DelTimerByBlockHeight(ctx, block, key) - fs.deleteMarkedEntry(ctx, safeIndex, block) - } else { - // otherwise, then replace the old timer (for the previous latest) - // with a new timer (for the new latest). - fs.replaceTimer(ctx, latestEntry, entry, timerDeleteEntry) - } + panic(fmt.Sprintf("Future entry: latest entry has DeleteAt %d", latestEntry.DeleteAt)) } // latest entry had extra refcount for being the latest; so drop that refcount @@ -614,7 +607,7 @@ func (fs *FixationStore) DelEntry(ctx sdk.Context, index string, block uint64) e panic(fmt.Sprintf("DelEntry for block %d < current ctx block %d", block, ctxBlock)) } - entry, found := fs.getUnmarshaledEntryForBlock(ctx, safeIndex, ctxBlock) + entry, found := fs.getUnmarshaledEntryForBlock(ctx, safeIndex, block) if !found || entry.HasDeleteAt() { return sdkerrors.ErrNotFound } @@ -626,7 +619,7 @@ func (fs *FixationStore) DelEntry(ctx sdk.Context, index string, block uint64) e // to do the work. otherwise this is a future entry delete, so set a timer for // when it will become imminent. - if block == uint64(ctx.BlockHeight()) { + if block == ctxBlock { fs.deleteMarkedEntry(ctx, safeIndex, entry.Block) } else { key := encodeForTimer(safeIndex, entry.Block, timerDeleteEntry) diff --git a/cookbook/projects/example_project_keys.yml b/cookbook/projects/example_project_keys.yml index b4c88db267..1cfe18bfb8 100644 --- a/cookbook/projects/example_project_keys.yml +++ b/cookbook/projects/example_project_keys.yml @@ -3,5 +3,4 @@ Project-Keys: - key: lava@1xtfqykth53pkt97v955h3lql8zkj2m4s4rq9cr Kinds: 3 - key: lava@1r3ernqu6rzp95z92580wae7xpuqwmznk3eqd7w - types: Kinds: 1 diff --git a/proto/projects/project.proto b/proto/projects/project.proto index 564054448d..fbba982a52 100644 --- a/proto/projects/project.proto +++ b/proto/projects/project.proto @@ -29,7 +29,7 @@ message ProjectKey { DEVELOPER = 0x2; } - uint32 kinds = 4; + uint32 kinds = 4; } // protobuf expected in YAML format: used "moretags" to simplify parsing diff --git a/proto/projects/tx.proto b/proto/projects/tx.proto index a67b94bd66..8c62bd7403 100644 --- a/proto/projects/tx.proto +++ b/proto/projects/tx.proto @@ -11,6 +11,7 @@ option go_package = "github.com/lavanet/lava/x/projects/types"; // Msg defines the Msg service. service Msg { rpc AddKeys(MsgAddKeys) returns (MsgAddKeysResponse); + rpc DelKeys(MsgDelKeys) returns (MsgDelKeysResponse); rpc SetPolicy(MsgSetPolicy) returns (MsgSetPolicyResponse); rpc SetSubscriptionPolicy(MsgSetSubscriptionPolicy) returns (MsgSetSubscriptionPolicyResponse); // this line is used by starport scaffolding # proto/tx/rpc @@ -25,6 +26,15 @@ message MsgAddKeys { message MsgAddKeysResponse { } +message MsgDelKeys { + string creator = 1; + string project = 2; + repeated ProjectKey project_keys = 3 [(gogoproto.nullable) = false]; +} + +message MsgDelKeysResponse { +} + message MsgSetPolicy { string creator = 1; string project = 2; diff --git a/scripts/init_e2e.sh b/scripts/init_e2e.sh index aa10be435c..8e24ed8b80 100755 --- a/scripts/init_e2e.sh +++ b/scripts/init_e2e.sh @@ -17,6 +17,7 @@ lavad tx gov submit-proposal plans-add ./cookbook/plans/default.json,./cookbook/ lavad tx gov vote 2 yes -y --from alice --gas-adjustment "1.5" --gas "auto" --gas-prices $GASPRICE sleep 4 +# Plan removal (of one) lavad tx gov submit-proposal plans-del ./cookbook/plans/temporary-del.json -y --from alice --gas-adjustment "1.5" --gas "auto" --gas-prices $GASPRICE lavad tx gov vote 3 yes -y --from alice --gas-adjustment "1.5" --gas "auto" --gas-prices $GASPRICE @@ -44,7 +45,21 @@ lavad tx pairing stake-provider "LAV1" $STAKE "127.0.0.1:2265,tendermintrpc,1 12 lavad tx pairing stake-client "ETH1" $STAKE 1 -y --from user1 --gas-adjustment "1.5" --gas "auto" --gas-prices $GASPRICE lavad tx pairing stake-client "LAV1" $STAKE 1 -y --from user2 --gas-adjustment "1.5" --gas "auto" --gas-prices $GASPRICE +# subscriptions and projects lavad tx subscription buy "DefaultPlan" -y --from user3 --gas-adjustment "1.5" --gas "auto" --gas-prices $GASPRICE +user3addr=$(lavad keys show user3 -a) + +lavad tx subscription add-project "myproject" ./cookbook/projects/example_policy.yml -y --from user3 --gas-adjustment "1.5" --gas "auto" --gas-prices $GASPRICE +sleep_until_next_epoch + +lavad q subscription list-projects user3addr + +lavad tx project add-keys -y "$user3addr-myproject" --from user3 cookbook/projects/example_project_keys.yml --gas-prices=$GASPRICE +sleep_until_next_epoch + +lavad tx project del-keys -y "$user3addr-myproject" --from user3 cookbook/projects/example_project_keys.yml --gas-prices=$GASPRICE +sleep_until_next_epoch + # we need to wait for the next epoch for the stake to take action. sleep_until_next_epoch diff --git a/x/projects/client/cli/tx.go b/x/projects/client/cli/tx.go index d9bd893608..895d6dbd18 100644 --- a/x/projects/client/cli/tx.go +++ b/x/projects/client/cli/tx.go @@ -29,6 +29,7 @@ func GetTxCmd() *cobra.Command { } cmd.AddCommand(CmdAddKeys()) + cmd.AddCommand(CmdDelKeys()) cmd.AddCommand(CmdSetPolicy()) cmd.AddCommand(CmdSetSubscriptionPolicy()) // this line is used by starport scaffolding # 1 diff --git a/x/projects/client/cli/tx_add_keys.go b/x/projects/client/cli/tx_add_keys.go index 75b1998c92..f3b1ef3c86 100644 --- a/x/projects/client/cli/tx_add_keys.go +++ b/x/projects/client/cli/tx_add_keys.go @@ -1,8 +1,6 @@ package cli import ( - "strconv" - "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/client/tx" @@ -11,15 +9,12 @@ import ( "github.com/spf13/cobra" ) -var _ = strconv.Itoa(0) - func CmdAddKeys() *cobra.Command { cmd := &cobra.Command{ Use: "add-keys [project-id] [optional: project-keys-file-path]", Short: "Add developer/admin keys to an existing project", Long: `The add-keys command allows the project admin to add new project keys (admin/developer) to the project. To add the keys you can optionally provide a YAML file of the new project keys (see example in cookbook/project/example_project_keys.yml). - Note that in project keys, to define the key type, you should follow the enum described in the top of example_project_keys.yml. Another way to add keys is with the --admin-key and --developer-key flags.`, Example: `required flags: --from (the project's subscription address is also considered admin) diff --git a/x/projects/client/cli/tx_del_keys.go b/x/projects/client/cli/tx_del_keys.go new file mode 100644 index 0000000000..bc8dcc263c --- /dev/null +++ b/x/projects/client/cli/tx_del_keys.go @@ -0,0 +1,74 @@ +package cli + +import ( + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/tx" + commontypes "github.com/lavanet/lava/common/types" + "github.com/lavanet/lava/x/projects/types" + "github.com/spf13/cobra" +) + +func CmdDelKeys() *cobra.Command { + cmd := &cobra.Command{ + Use: "del-keys [project-id] [optional: project-keys-file-path]", + Short: "Delete developer/admin keys to an existing project", + Long: `The del-keys command allows the project admin to delete project keys (admin/developer) from the project. + To delete the keys you can optionally provide a YAML file of the project keys to delete (see example in cookbook/project/example_project_keys.yml). + Another way to delete keys is with the --admin-key and --developer-key flags.`, + Example: `required flags: --from (the project's subscription address is also considered admin) + + lavad tx project del-keys [project-id] [project-keys-file-path] --from + lavad tx project del-keys [project-id] --admin-key --admin-key --developer-key --from `, + Args: cobra.RangeArgs(1, 2), + RunE: func(cmd *cobra.Command, args []string) (err error) { + projectID := args[0] + var projectKeys []types.ProjectKey + + if len(args) > 1 { + projectKeysFilePath := args[1] + err = commontypes.ReadYaml(projectKeysFilePath, "Project-Keys", &projectKeys) + if err != nil { + return err + } + } else { + developerFlagsValue, err := cmd.Flags().GetStringSlice("developer-key") + if err != nil { + return err + } + for _, developerFlagValue := range developerFlagsValue { + projectKeys = append(projectKeys, types.ProjectDeveloperKey(developerFlagValue)) + } + + adminAddresses, err := cmd.Flags().GetStringSlice("admin-key") + if err != nil { + return err + } + for _, adminAddress := range adminAddresses { + projectKeys = append(projectKeys, types.ProjectAdminKey(adminAddress)) + } + } + + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + msg := types.NewMsgDelKeys( + clientCtx.GetFromAddress().String(), + projectID, + projectKeys, + ) + if err := msg.ValidateBasic(); err != nil { + return err + } + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + + cmd.Flags().StringSlice("developer-key", []string{}, "Developer keys to add") + cmd.Flags().StringSlice("admin-key", []string{}, "Admin keys to add") + flags.AddTxFlagsToCmd(cmd) + + return cmd +} diff --git a/x/projects/handler.go b/x/projects/handler.go index 7df3cee316..0f98bc68fe 100644 --- a/x/projects/handler.go +++ b/x/projects/handler.go @@ -20,6 +20,9 @@ func NewHandler(k keeper.Keeper) sdk.Handler { case *types.MsgAddKeys: res, err := msgServer.AddKeys(sdk.WrapSDKContext(ctx), msg) return sdk.WrapServiceResult(ctx, res, err) + case *types.MsgDelKeys: + res, err := msgServer.DelKeys(sdk.WrapSDKContext(ctx), msg) + return sdk.WrapServiceResult(ctx, res, err) case *types.MsgSetPolicy: res, err := msgServer.SetPolicy(sdk.WrapSDKContext(ctx), msg) return sdk.WrapServiceResult(ctx, res, err) diff --git a/x/projects/keeper/creation.go b/x/projects/keeper/creation.go index 2a883cae05..982ae63b92 100644 --- a/x/projects/keeper/creation.go +++ b/x/projects/keeper/creation.go @@ -2,6 +2,7 @@ package keeper import ( "fmt" + "strconv" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" @@ -29,8 +30,8 @@ func (k Keeper) CreateProject(ctx sdk.Context, subAddr string, projectData types } var emptyProject types.Project - blockHeight := uint64(ctx.BlockHeight()) - if found := k.projectsFS.FindEntry(ctx, project.Index, blockHeight, &emptyProject); found { + ctxBlock := uint64(ctx.BlockHeight()) + if found := k.projectsFS.FindEntry(ctx, project.Index, ctxBlock, &emptyProject); found { // the project with the same name already exists if no error has returned return utils.LavaFormatWarning( "project already exist for the current subscription with the same name", @@ -46,13 +47,13 @@ func (k Keeper) CreateProject(ctx sdk.Context, subAddr string, projectData types project.SubscriptionPolicy = project.AdminPolicy for _, projectKey := range projectData.GetProjectKeys() { - err = k.registerKey(ctx, projectKey, &project, blockHeight) + err = k.registerKey(ctx, projectKey, &project, ctxBlock) if err != nil { return err } } - return k.projectsFS.AppendEntry(ctx, project.Index, blockHeight, &project) + return k.projectsFS.AppendEntry(ctx, project.Index, ctxBlock, &project) } func (k Keeper) registerKey(ctx sdk.Context, key types.ProjectKey, project *types.Project, blockHeight uint64) error { @@ -61,52 +62,122 @@ func (k Keeper) registerKey(ctx sdk.Context, key types.ProjectKey, project *type } if key.IsType(types.ProjectKey_ADMIN) { - k.addAdminKey(project, key.GetKey()) + project.AppendKey(types.ProjectAdminKey(key.Key)) } if key.IsType(types.ProjectKey_DEVELOPER) { + ctxBlock := uint64(ctx.BlockHeight()) + + // check that the developer key is valid in the current project state var devkeyData types.ProtoDeveloperData - found := k.developerKeysFS.FindEntry(ctx, key.GetKey(), blockHeight, &devkeyData) + found := k.developerKeysFS.FindEntry(ctx, key.Key, ctxBlock, &devkeyData) - // the developer key may not already belong to a different project + // the developer key may already belong to a different project if found && devkeyData.ProjectID != project.GetIndex() { - return utils.LavaFormatWarning("key already exists", - fmt.Errorf("could not register key to project"), + return utils.LavaFormatWarning("failed to register key", + fmt.Errorf("key already exists"), + utils.Attribute{Key: "key", Value: key.Key}, + utils.Attribute{Key: "keyTypes", Value: key.Kinds}, + ) + } + + // the project may have future (e.g. end of epoch) changes pending; so + // check that the developer key is still valid in that future state + // (for example, it could be removed and added elsewhere by then). + found = k.developerKeysFS.FindEntry(ctx, key.Key, blockHeight, &devkeyData) + + // the developer key may already belong to a different project + devkeyData = types.ProtoDeveloperData{} + if found && devkeyData.ProjectID != project.GetIndex() { + return utils.LavaFormatWarning("failed to register key", + fmt.Errorf("key already exists in next epoch"), utils.Attribute{Key: "key", Value: key.Key}, utils.Attribute{Key: "keyTypes", Value: key.Kinds}, ) } if !found { - err := k.addDeveloperKey(ctx, key.GetKey(), project, blockHeight) + devkeyData := types.ProtoDeveloperData{ + ProjectID: project.GetIndex(), + } + + err := k.developerKeysFS.AppendEntry(ctx, key.Key, blockHeight, &devkeyData) if err != nil { - return utils.LavaFormatError("adding developer key to project failed", err, - utils.Attribute{Key: "developerKey", Value: key.Key}, - utils.Attribute{Key: "projectIndex", Value: project.Index}, - utils.Attribute{Key: "blockHeight", Value: blockHeight}, + return utils.LavaFormatError("failed to register key", err, + utils.Attribute{Key: "key", Value: key.Key}, + utils.Attribute{Key: "keyTypes", Value: key.Kinds}, ) } + + logger := k.Logger(ctx) + details := map[string]string{ + "project": project.GetIndex(), + "key": key.Key, + "keytype": strconv.FormatInt(int64(key.Kinds), 10), + } + utils.LogLavaEvent(ctx, logger, types.AddProjectKeyEventName, details, "key added to project") + + project.AppendKey(types.ProjectDeveloperKey(key.Key)) } } return nil } -func (k Keeper) addAdminKey(project *types.Project, adminKey string) { - project.AppendKey(types.ProjectAdminKey(adminKey)) -} - -func (k Keeper) addDeveloperKey(ctx sdk.Context, devkey string, project *types.Project, blockHeight uint64) error { - devkeyData := types.ProtoDeveloperData{ - ProjectID: project.GetIndex(), +func (k Keeper) unregisterKey(ctx sdk.Context, key types.ProjectKey, project *types.Project, blockHeight uint64) error { + if !key.IsTypeValid() { + return sdkerrors.ErrInvalidType } - err := k.developerKeysFS.AppendEntry(ctx, devkey, blockHeight, &devkeyData) - if err != nil { - return err + if key.IsType(types.ProjectKey_ADMIN) { + found := project.DeleteKey(types.ProjectAdminKey(key.Key)) + if !found { + return sdkerrors.ErrKeyNotFound + } } - project.AppendKey(types.ProjectDeveloperKey(devkey)) + if key.IsType(types.ProjectKey_DEVELOPER) { + ctxBlock := uint64(ctx.BlockHeight()) + + // check that the developer key is valid in the current project state + var devkeyData types.ProtoDeveloperData + found := k.developerKeysFS.FindEntry(ctx, key.Key, ctxBlock, &devkeyData) + if !found { + // if not found now, check if it is valid in the future (e.g. end of + // epoch) state, as it may have been added in this epoch and pending + // to become visible). + found = k.developerKeysFS.FindEntry(ctx, key.Key, blockHeight, &devkeyData) + } + + if !found { + return sdkerrors.ErrNotFound + } + + // the developer key belongs to a different project + if devkeyData.ProjectID != project.GetIndex() { + return utils.LavaFormatWarning("failed to unregister key", sdkerrors.ErrNotFound, + utils.Attribute{Key: "key", Value: key.Key}, + utils.Attribute{Key: "keyTypes", Value: key.Kinds}, + ) + } + + err := k.developerKeysFS.DelEntry(ctx, key.Key, blockHeight) + if err != nil { + return err + } + found = project.DeleteKey(types.ProjectDeveloperKey(key.Key)) + if !found { + panic("unregisterKey: developer key not found") + } + + logger := k.Logger(ctx) + details := map[string]string{ + "project": project.GetIndex(), + "key": key.Key, + "keytype": strconv.FormatInt(int64(key.Kinds), 10), + } + utils.LogLavaEvent(ctx, logger, types.DelProjectKeyEventName, details, "key deleted from project") + } return nil } diff --git a/x/projects/keeper/keeper.go b/x/projects/keeper/keeper.go index 8e2b5448ad..caee69523f 100644 --- a/x/projects/keeper/keeper.go +++ b/x/projects/keeper/keeper.go @@ -19,7 +19,7 @@ type ( memKey sdk.StoreKey paramstore paramtypes.Subspace - epochStorageKeeper types.EpochStorageKeeper + epochstorageKeeper types.EpochStorageKeeper projectsFS common.FixationStore developerKeysFS common.FixationStore @@ -31,7 +31,7 @@ func NewKeeper( storeKey, memKey sdk.StoreKey, ps paramtypes.Subspace, - epochStorageKeeper types.EpochStorageKeeper, + epochstorageKeeper types.EpochStorageKeeper, ) *Keeper { // set KeyTable if it has not already been set if !ps.HasKeyTable() { @@ -48,7 +48,7 @@ func NewKeeper( paramstore: ps, projectsFS: *projectsfs, developerKeysFS: *developerKeysfs, - epochStorageKeeper: epochStorageKeeper, + epochstorageKeeper: epochstorageKeeper, } } diff --git a/x/projects/keeper/msg_server_del_project_keys.go b/x/projects/keeper/msg_server_del_project_keys.go new file mode 100644 index 0000000000..ee88477b1b --- /dev/null +++ b/x/projects/keeper/msg_server_del_project_keys.go @@ -0,0 +1,32 @@ +package keeper + +import ( + "context" + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/lavanet/lava/utils" + "github.com/lavanet/lava/x/projects/types" +) + +func (k msgServer) DelKeys(goCtx context.Context, msg *types.MsgDelKeys) (*types.MsgDelKeysResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + for _, projectKey := range msg.GetProjectKeys() { + if !projectKey.IsTypeValid() { + return nil, utils.LavaFormatWarning( + "invalid project key type (must be ADMIN(=1) or DEVELOPER(=2)", + fmt.Errorf("invalid project key type"), + utils.Attribute{Key: "key", Value: projectKey.Key}, + utils.Attribute{Key: "keyType", Value: projectKey.Kinds}, + ) + } + } + + err := k.DelKeysFromProject(ctx, msg.Project, msg.Creator, msg.ProjectKeys) + if err != nil { + return nil, err + } + + return &types.MsgDelKeysResponse{}, nil +} diff --git a/x/projects/keeper/project.go b/x/projects/keeper/project.go index ea9ff53724..9d28ceed25 100644 --- a/x/projects/keeper/project.go +++ b/x/projects/keeper/project.go @@ -13,7 +13,8 @@ func (k Keeper) GetProjectForBlock(ctx sdk.Context, projectID string, blockHeigh var project types.Project if found := k.projectsFS.FindEntry(ctx, projectID, blockHeight, &project); !found { - return project, utils.LavaFormatWarning("could not get project for block", fmt.Errorf("project not found"), + return project, utils.LavaFormatWarning("failed to get project for block", + fmt.Errorf("project or block not found"), utils.Attribute{Key: "project", Value: projectID}, utils.Attribute{Key: "blockHeight", Value: blockHeight}, ) @@ -23,11 +24,11 @@ func (k Keeper) GetProjectForBlock(ctx sdk.Context, projectID string, blockHeigh } func (k Keeper) GetProjectDeveloperData(ctx sdk.Context, developerKey string, blockHeight uint64) (types.ProtoDeveloperData, error) { - var projectDeveloperData types.ProtoDeveloperData - if found := k.developerKeysFS.FindEntry(ctx, developerKey, blockHeight, &projectDeveloperData); !found { - return types.ProtoDeveloperData{}, fmt.Errorf("GetProjectIDForDeveloper_invalid_key, the requesting key is not registered to a project, developer: %s", developerKey) + var devkeyData types.ProtoDeveloperData + if found := k.developerKeysFS.FindEntry(ctx, developerKey, blockHeight, &devkeyData); !found { + return types.ProtoDeveloperData{}, fmt.Errorf("GetProjectIDForDeveloper_invalid_key: %s", developerKey) } - return projectDeveloperData, nil + return devkeyData, nil } func (k Keeper) GetProjectForDeveloper(ctx sdk.Context, developerKey string, blockHeight uint64) (proj types.Project, errRet error) { @@ -48,32 +49,117 @@ func (k Keeper) GetProjectForDeveloper(ctx sdk.Context, developerKey string, blo } func (k Keeper) AddKeysToProject(ctx sdk.Context, projectID string, adminKey string, projectKeys []types.ProjectKey) error { + ctxBlock := uint64(ctx.BlockHeight()) + + nextEpoch, err := k.epochstorageKeeper.GetNextEpoch(ctx, ctxBlock) + if err != nil { + return utils.LavaFormatError("AddKeysToProject: failed to get NextEpoch", err, + utils.Attribute{Key: "index", Value: projectID}, + ) + } + + // validate the admin key against the current project state; but make the + // changes on the future (= end of epoch) version if already exists. This + // ensures that we do not lose changes if there are e.g. multiple additions + // in the same epoch. + + // validate admin key with current state var project types.Project - if found := k.projectsFS.FindEntry(ctx, projectID, uint64(ctx.BlockHeight()), &project); !found { - return utils.LavaFormatWarning("could not add keys to project", fmt.Errorf("project not found"), + if found := k.projectsFS.FindEntry(ctx, projectID, ctxBlock, &project); !found { + return utils.LavaFormatWarning("failed to add keys", + fmt.Errorf("project not found"), utils.Attribute{Key: "project", Value: projectID}, + utils.Attribute{Key: "block", Value: ctxBlock}, ) } - // check if the admin key is valid if !project.IsAdminKey(adminKey) { - return utils.LavaFormatWarning("could not add keys to project", fmt.Errorf("the requesting key is not admin key"), + return utils.LavaFormatWarning("failed to add keys", + fmt.Errorf("requesting key must be admin key"), utils.Attribute{Key: "project", Value: projectID}, ) } + // make changes on the future state + project = types.Project{} + if found := k.projectsFS.FindEntry(ctx, projectID, nextEpoch, &project); !found { + return utils.LavaFormatError("failed to add keys", + fmt.Errorf("project not found (for next epoch)"), + utils.Attribute{Key: "project", Value: projectID}, + utils.Attribute{Key: "block", Value: ctxBlock}, + ) + } + for _, projectKey := range projectKeys { - err := k.registerKey(ctx, projectKey, &project, uint64(ctx.BlockHeight())) + err := k.registerKey(ctx, projectKey, &project, nextEpoch) if err != nil { return utils.LavaFormatError("failed to register key to project", err, utils.Attribute{Key: "project", Value: projectID}, - utils.Attribute{Key: "key", Value: projectKey.Key}, - utils.Attribute{Key: "keyTypes", Value: projectKey.Kinds}, ) } } - return k.projectsFS.AppendEntry(ctx, projectID, uint64(ctx.BlockHeight()), &project) + return k.projectsFS.AppendEntry(ctx, projectID, nextEpoch, &project) +} + +func (k Keeper) DelKeysFromProject(ctx sdk.Context, projectID string, adminKey string, projectKeys []types.ProjectKey) error { + ctxBlock := uint64(ctx.BlockHeight()) + + nextEpoch, err := k.epochstorageKeeper.GetNextEpoch(ctx, ctxBlock) + if err != nil { + return utils.LavaFormatError("DelKeysFromProject: failed to get NextEpoch", err, + utils.Attribute{Key: "index", Value: projectID}, + ) + } + + // validate the admin key against the current project state; but make the + // changes on the future (= end of epoch) version if already exists. This + // ensures that we do not lose changes if there are e.g. multiple additions + // in the same epoch. + + // validate admin key with current state + var project types.Project + if found := k.projectsFS.FindEntry(ctx, projectID, ctxBlock, &project); !found { + return utils.LavaFormatWarning("failed to delete keys", + fmt.Errorf("project not found"), + utils.Attribute{Key: "project", Value: projectID}, + ) + } + + if !project.IsAdminKey(adminKey) { + return utils.LavaFormatWarning("failed to delete keys", + fmt.Errorf("requesting key must be admin key"), + utils.Attribute{Key: "project", Value: projectID}, + ) + } + + project = types.Project{} + if found := k.projectsFS.FindEntry(ctx, projectID, nextEpoch, &project); !found { + return utils.LavaFormatWarning("failed to delete keys", + fmt.Errorf("project not found (for next epoch)"), + utils.Attribute{Key: "project", Value: projectID}, + ) + } + + // make changes on the future state + for _, projectKey := range projectKeys { + // check if the deleted key is subscription owner + if projectKey.Key == project.Subscription { + return utils.LavaFormatWarning("failed to delete keys", + fmt.Errorf("subscription key may not be deleted"), + utils.Attribute{Key: "project", Value: projectID}, + ) + } + + err := k.unregisterKey(ctx, projectKey, &project, nextEpoch) + if err != nil { + return utils.LavaFormatWarning("failed to unregister key to project", err, + utils.Attribute{Key: "project", Value: projectID}, + ) + } + } + + return k.projectsFS.AppendEntry(ctx, projectID, nextEpoch, &project) } func (k Keeper) ChargeComputeUnitsToProject(ctx sdk.Context, project types.Project, blockHeight uint64, cu uint64) (err error) { @@ -123,7 +209,7 @@ func (k Keeper) SetProjectPolicy(ctx sdk.Context, projectIDs []string, policy *t } } - nextEpoch, err := k.epochStorageKeeper.GetNextEpoch(ctx, uint64(ctx.BlockHeight())) + nextEpoch, err := k.epochstorageKeeper.GetNextEpoch(ctx, uint64(ctx.BlockHeight())) if err != nil { panic("could not set policy. can't get next epoch") } diff --git a/x/projects/keeper/project_test.go b/x/projects/keeper/project_test.go index 8500681dd6..949cae3711 100644 --- a/x/projects/keeper/project_test.go +++ b/x/projects/keeper/project_test.go @@ -3,6 +3,7 @@ package keeper_test import ( "context" "math" + "strconv" "strings" "testing" @@ -16,27 +17,69 @@ import ( const projectName = "mockname" -func prepareProjectsData(ctx context.Context, keepers *testkeeper.Keepers) (projects []types.ProjectData) { - adm1Addr := common.CreateNewAccount(ctx, *keepers, 10000).Addr.String() - adm2Addr := common.CreateNewAccount(ctx, *keepers, 10000).Addr.String() - adm3Addr := common.CreateNewAccount(ctx, *keepers, 10000).Addr.String() - dev3Addr := common.CreateNewAccount(ctx, *keepers, 10000).Addr.String() +type testStruct struct { + t *testing.T + servers *testkeeper.Servers + keepers *testkeeper.Keepers + _ctx context.Context + ctx sdk.Context + accounts map[string]string + projects map[string]types.ProjectData +} + +func newTestStruct(t *testing.T) *testStruct { + servers, keepers, _ctx := testkeeper.InitAllKeepers(t) + ts := &testStruct{ + t: t, + servers: servers, + keepers: keepers, + _ctx: _ctx, + ctx: sdk.UnwrapSDKContext(_ctx), + } + ts.AdvanceEpoch(1) + return ts +} + +func (ts *testStruct) prepareData(numSub, numAdmin, numDevel int) { + ts.accounts = make(map[string]string) + for i := 0; i < numSub; i++ { + k := "sub" + strconv.Itoa(i+1) + v := common.CreateNewAccount(ts._ctx, *ts.keepers, 10000).Addr.String() + ts.accounts[k] = v + } + for i := 0; i < numAdmin; i++ { + k := "adm" + strconv.Itoa(i+1) + v := common.CreateNewAccount(ts._ctx, *ts.keepers, 10000).Addr.String() + ts.accounts[k] = v + } + for i := 0; i < numDevel; i++ { + k := "dev" + strconv.Itoa(i+1) + v := common.CreateNewAccount(ts._ctx, *ts.keepers, 10000).Addr.String() + ts.accounts[k] = v + } + + ts.projects = make(map[string]types.ProjectData) + + ts.accounts["pd1_adm"] = common.CreateNewAccount(ts._ctx, *ts.keepers, 10000).Addr.String() + ts.accounts["pd2_both"] = common.CreateNewAccount(ts._ctx, *ts.keepers, 10000).Addr.String() + ts.accounts["pd3_adm"] = common.CreateNewAccount(ts._ctx, *ts.keepers, 10000).Addr.String() + ts.accounts["pd3_dev"] = common.CreateNewAccount(ts._ctx, *ts.keepers, 10000).Addr.String() // admin key keys_1_admin := []types.ProjectKey{ - types.ProjectAdminKey(adm1Addr), + types.ProjectAdminKey(ts.accounts["pd1_adm"]), } // developer key keys_1_admin_dev := []types.ProjectKey{ - types.NewProjectKey(adm2Addr). + types.NewProjectKey(ts.accounts["pd2_both"]). AddType(types.ProjectKey_ADMIN). AddType(types.ProjectKey_DEVELOPER), } // both (admin+developer) key keys_2_admin_and_dev := []types.ProjectKey{ - types.ProjectAdminKey(adm3Addr), - types.ProjectDeveloperKey(dev3Addr), + types.ProjectAdminKey(ts.accounts["pd3_adm"]), + types.ProjectDeveloperKey(ts.accounts["pd3_dev"]), } policy1 := &types.Policy{ @@ -45,68 +88,110 @@ func prepareProjectsData(ctx context.Context, keepers *testkeeper.Keepers) (proj } templates := []struct { + code string name string enabled bool keys []types.ProjectKey policy *types.Policy }{ // project with admin key, enabled, has policy - {"mock_project_1", true, keys_1_admin, policy1}, + {"pd1", "mock_project_1", true, keys_1_admin, policy1}, // project with "both" key, disabled, with policy - {"mock_project_2", false, keys_1_admin_dev, policy1}, + {"pd2", "mock_project_2", false, keys_1_admin_dev, policy1}, // project with 2 keys (one admin, one developer) disabled, no policy - {"mock_project_3", false, keys_2_admin_and_dev, nil}, + {"pd3", "mock_project_3", false, keys_2_admin_and_dev, nil}, } for _, tt := range templates { - projectData := types.ProjectData{ + ts.projects[tt.code] = types.ProjectData{ Name: tt.name, Enabled: tt.enabled, ProjectKeys: tt.keys, Policy: tt.policy, } - projects = append(projects, projectData) } +} + +func (ts *testStruct) BlockHeight() uint64 { + return uint64(ts.ctx.BlockHeight()) +} + +func (ts *testStruct) AdvanceBlock(count int) { + for i := 0; i < count; i += 1 { + ts._ctx = testkeeper.AdvanceBlock(ts._ctx, ts.keepers) + } + ts.ctx = sdk.UnwrapSDKContext(ts._ctx) +} + +func (ts *testStruct) AdvanceEpoch(count int) { + for i := 0; i < count; i += 1 { + ts._ctx = testkeeper.AdvanceEpoch(ts._ctx, ts.keepers) + } + ts.ctx = sdk.UnwrapSDKContext(ts._ctx) +} + +func (ts *testStruct) isKeyInProject(index, key string, kind types.ProjectKey_Type) bool { + resp, err := ts.keepers.Projects.Info(ts._ctx, &types.QueryInfoRequest{Project: index}) + require.Nil(ts.t, err, "project: "+index+", key: "+key) + pk := resp.Project.GetKey(key) + return pk.IsType(kind) +} - return projects +func (ts *testStruct) addProjectKeys(index, creator string, projectKeys ...types.ProjectKey) error { + msg := types.MsgAddKeys{ + Creator: creator, + Project: index, + ProjectKeys: projectKeys, + } + _, err := ts.servers.ProjectServer.AddKeys(ts._ctx, &msg) + return err +} + +func (ts *testStruct) delProjectKeys(index, creator string, projectKeys ...types.ProjectKey) error { + msg := types.MsgDelKeys{ + Creator: creator, + Project: index, + ProjectKeys: projectKeys, + } + _, err := ts.servers.ProjectServer.DelKeys(ts._ctx, &msg) + return err } func TestCreateDefaultProject(t *testing.T) { - _, keepers, ctx := testkeeper.InitAllKeepers(t) + ts := newTestStruct(t) - subAddr := common.CreateNewAccount(ctx, *keepers, 10000).Addr.String() + subAddr := common.CreateNewAccount(ts._ctx, *ts.keepers, 10000).Addr.String() plan := common.CreateMockPlan() - err := keepers.Projects.CreateAdminProject(sdk.UnwrapSDKContext(ctx), subAddr, plan) + err := ts.keepers.Projects.CreateAdminProject(ts.ctx, subAddr, plan) require.Nil(t, err) // subscription key is a developer in the default project - response1, err := keepers.Projects.Developer(ctx, &types.QueryDeveloperRequest{Developer: subAddr}) + response1, err := ts.keepers.Projects.Developer(ts._ctx, &types.QueryDeveloperRequest{Developer: subAddr}) require.Nil(t, err) - ctx = testkeeper.AdvanceEpoch(ctx, keepers) + ts.AdvanceEpoch(1) - response2, err := keepers.Projects.Info(ctx, &types.QueryInfoRequest{Project: response1.Project.Index}) + response2, err := ts.keepers.Projects.Info(ts._ctx, &types.QueryInfoRequest{Project: response1.Project.Index}) require.Nil(t, err) require.Equal(t, response2.Project, response1.Project) } func TestCreateProject(t *testing.T) { - servers, keepers, _ctx := testkeeper.InitAllKeepers(t) - ctx := sdk.UnwrapSDKContext(_ctx) + ts := newTestStruct(t) + ts.prepareData(1, 0, 0) // 1 sub, 0 adm, 0 dev - projectData := prepareProjectsData(_ctx, keepers)[1] + projectData := ts.projects["pd2"] plan := common.CreateMockPlan() - subAddr := common.CreateNewAccount(_ctx, *keepers, 10000).Addr.String() - admAddr := projectData.ProjectKeys[0].Key + subAddr := ts.accounts["sub1"] + admAddr := ts.accounts["pd2_both"] - err := keepers.Projects.CreateProject(ctx, subAddr, projectData, plan) + err := ts.keepers.Projects.CreateProject(ts.ctx, subAddr, projectData, plan) require.Nil(t, err) - _ctx = testkeeper.AdvanceEpoch(_ctx, keepers) - ctx = sdk.UnwrapSDKContext(_ctx) + ts.AdvanceEpoch(1) // test invalid project name defaultProjectName := types.ADMIN_PROJECT_NAME @@ -129,7 +214,7 @@ func TestCreateProject(t *testing.T) { for _, tt := range nameTests { t.Run(tt.name, func(t *testing.T) { testProjectData.Name = tt.projectName - err = keepers.Projects.CreateProject(ctx, subAddr, testProjectData, plan) + err = ts.keepers.Projects.CreateProject(ts.ctx, subAddr, testProjectData, plan) require.NotNil(t, err) }) } @@ -144,26 +229,26 @@ func TestCreateProject(t *testing.T) { } // should fail because there's an invalid key - _, err = servers.SubscriptionServer.AddProject(_ctx, &subscriptiontypes.MsgAddProject{ + _, err = ts.servers.SubscriptionServer.AddProject(ts._ctx, &subscriptiontypes.MsgAddProject{ Creator: subAddr, ProjectData: invalidKeysProjectData, }) require.NotNil(t, err) - // get project by developer - subscription key is not a developer, should fail (if it succeeds, it means that the valid project key - // from invalidKeysProjectData was registered, which is not desired!) - _, err = keepers.Projects.Developer(_ctx, &types.QueryDeveloperRequest{Developer: subAddr}) + // subscription key is not a developer, so get project by developer should fail (if it succeeds, + // then the valid project key from invalidKeysProjectData was registered, which is undesired!) + _, err = ts.keepers.Projects.Developer(ts._ctx, &types.QueryDeveloperRequest{Developer: subAddr}) require.NotNil(t, err) - response1, err := keepers.Projects.Developer(_ctx, &types.QueryDeveloperRequest{Developer: admAddr}) + response1, err := ts.keepers.Projects.Developer(ts._ctx, &types.QueryDeveloperRequest{Developer: admAddr}) require.Nil(t, err) - response2, err := keepers.Projects.Info(_ctx, &types.QueryInfoRequest{Project: response1.Project.Index}) + response2, err := ts.keepers.Projects.Info(ts._ctx, &types.QueryInfoRequest{Project: response1.Project.Index}) require.Nil(t, err) require.Equal(t, response2.Project, response1.Project) - _, err = keepers.Projects.GetProjectForBlock(ctx, response1.Project.Index, 0) + _, err = ts.keepers.Projects.GetProjectForBlock(ts.ctx, response1.Project.Index, ts.BlockHeight()) require.Nil(t, err) // there should be one project key @@ -177,86 +262,186 @@ func TestCreateProject(t *testing.T) { require.True(t, response2.Project.ProjectKeys[0].IsType(types.ProjectKey_DEVELOPER)) } -func TestAddKeys(t *testing.T) { - servers, keepers, ctx := testkeeper.InitAllKeepers(t) - _ctx := sdk.UnwrapSDKContext(ctx) - projectData := prepareProjectsData(ctx, keepers)[2] +func TestProjectsServerAPI(t *testing.T) { + ts := newTestStruct(t) + ts.prepareData(2, 1, 5) // 2 sub, 1 adm, 5 dev + + sub1Acc := common.CreateNewAccount(ts._ctx, *ts.keepers, 20000) + sub1Addr := sub1Acc.Addr.String() + + dev1Addr := ts.accounts["dev1"] + dev2Addr := ts.accounts["dev2"] + + plan := common.CreateMockPlan() + ts.keepers.Plans.AddPlan(ts.ctx, plan) + + common.BuySubscription(t, ts._ctx, *ts.keepers, *ts.servers, sub1Acc, plan.Index) + + projectData := types.ProjectData{ + Name: "mockname2", + Enabled: true, + ProjectKeys: []types.ProjectKey{types.ProjectDeveloperKey(dev1Addr)}, + Policy: &plan.PlanPolicy, + } + + projectID := types.ProjectIndex(sub1Addr, projectData.Name) + + msgAddProject := &subscriptiontypes.MsgAddProject{ + Creator: sub1Addr, + ProjectData: projectData, + } + _, err := ts.servers.SubscriptionServer.AddProject(ts._ctx, msgAddProject) + require.Nil(t, err) + + ts.AdvanceEpoch(1) + + msgAddKeys := types.MsgAddKeys{ + Creator: sub1Addr, + Project: projectID, + ProjectKeys: []types.ProjectKey{types.ProjectDeveloperKey(dev2Addr)}, + } + _, err = ts.servers.ProjectServer.AddKeys(ts._ctx, &msgAddKeys) + require.Nil(t, err) + + ts.AdvanceBlock(1) + + require.True(t, ts.isKeyInProject(projectID, dev1Addr, types.ProjectKey_DEVELOPER)) + require.False(t, ts.isKeyInProject(projectID, dev2Addr, types.ProjectKey_DEVELOPER)) + ts.AdvanceEpoch(1) + require.True(t, ts.isKeyInProject(projectID, dev2Addr, types.ProjectKey_DEVELOPER)) + + msgQueryDev := &types.QueryDeveloperRequest{Developer: sub1Addr} + res, err := ts.keepers.Projects.Developer(ts._ctx, msgQueryDev) + require.Nil(t, err) + require.Equal(t, 1, len(res.Project.ProjectKeys)) +} + +func TestAddDelKeys(t *testing.T) { + ts := newTestStruct(t) + ts.prepareData(1, 0, 2) // 1 sub, 0 adm, 2 dev + + projectData := ts.projects["pd3"] plan := common.CreateMockPlan() - subAddr := common.CreateNewAccount(ctx, *keepers, 10000).Addr.String() - admAddr := projectData.ProjectKeys[0].Key - dev1Addr := projectData.ProjectKeys[1].Key - dev2Addr := common.CreateNewAccount(ctx, *keepers, 10000).Addr.String() - dev3Addr := common.CreateNewAccount(ctx, *keepers, 10000).Addr.String() + subAddr := ts.accounts["sub1"] + admAddr := ts.accounts["pd3_adm"] + dev1Addr := ts.accounts["pd3_dev"] + dev2Addr := ts.accounts["dev1"] + dev3Addr := ts.accounts["dev2"] - err := keepers.Projects.CreateProject(_ctx, subAddr, projectData, plan) + err := ts.keepers.Projects.CreateProject(ts.ctx, subAddr, projectData, plan) require.Nil(t, err) - ctx = testkeeper.AdvanceEpoch(ctx, keepers) + ts.AdvanceEpoch(1) - projectRes, err := keepers.Projects.Developer(ctx, &types.QueryDeveloperRequest{Developer: dev1Addr}) + projectRes, err := ts.keepers.Projects.Developer(ts._ctx, &types.QueryDeveloperRequest{Developer: dev1Addr}) require.Nil(t, err) project := projectRes.Project pk := types.ProjectAdminKey(dev1Addr) // try adding myself as admin, should fail - _, err = servers.ProjectServer.AddKeys(ctx, &types.MsgAddKeys{Creator: dev1Addr, Project: project.Index, ProjectKeys: []types.ProjectKey{pk}}) + err = ts.addProjectKeys(project.Index, dev1Addr, pk) require.NotNil(t, err) // admin key adding an invalid key pk = types.NewProjectKey(dev2Addr).AddType(0x4) - _, err = servers.ProjectServer.AddKeys(ctx, &types.MsgAddKeys{Creator: admAddr, Project: project.Index, ProjectKeys: []types.ProjectKey{pk}}) + err = ts.addProjectKeys(project.Index, admAddr, pk) require.NotNil(t, err) // admin key adding a developer pk = types.ProjectDeveloperKey(dev2Addr) - _, err = servers.ProjectServer.AddKeys(ctx, &types.MsgAddKeys{Creator: admAddr, Project: project.Index, ProjectKeys: []types.ProjectKey{pk}}) + err = ts.addProjectKeys(project.Index, admAddr, pk) require.Nil(t, err) // developer tries to add the second developer as admin pk = types.ProjectAdminKey(dev2Addr) - _, err = servers.ProjectServer.AddKeys(ctx, &types.MsgAddKeys{Creator: dev1Addr, Project: project.Index, ProjectKeys: []types.ProjectKey{pk}}) + err = ts.addProjectKeys(project.Index, dev1Addr, pk) require.NotNil(t, err) // admin adding admin pk = types.ProjectAdminKey(dev1Addr) - _, err = servers.ProjectServer.AddKeys(ctx, &types.MsgAddKeys{Creator: admAddr, Project: project.Index, ProjectKeys: []types.ProjectKey{pk}}) + err = ts.addProjectKeys(project.Index, admAddr, pk) require.Nil(t, err) + // additions above will only take effect in next epoch + require.False(t, ts.isKeyInProject(project.Index, dev1Addr, types.ProjectKey_ADMIN)) + require.False(t, ts.isKeyInProject(project.Index, dev2Addr, types.ProjectKey_DEVELOPER)) + ts.AdvanceEpoch(1) + require.True(t, ts.isKeyInProject(project.Index, dev1Addr, types.ProjectKey_ADMIN)) + require.True(t, ts.isKeyInProject(project.Index, dev2Addr, types.ProjectKey_DEVELOPER)) + // new admin adding another developer pk = types.ProjectDeveloperKey(dev3Addr) - _, err = servers.ProjectServer.AddKeys(ctx, &types.MsgAddKeys{Creator: dev1Addr, Project: project.Index, ProjectKeys: []types.ProjectKey{pk}}) + err = ts.addProjectKeys(project.Index, dev1Addr, pk) require.Nil(t, err) + // additions above will only take effect in next epoch + require.False(t, ts.isKeyInProject(project.Index, dev3Addr, types.ProjectKey_DEVELOPER)) + ts.AdvanceEpoch(1) + require.True(t, ts.isKeyInProject(project.Index, dev3Addr, types.ProjectKey_DEVELOPER)) + // fetch project with new developer - _, err = keepers.Projects.Developer(ctx, &types.QueryDeveloperRequest{Developer: dev3Addr}) + _, err = ts.keepers.Projects.Developer(ts._ctx, &types.QueryDeveloperRequest{Developer: dev3Addr}) require.Nil(t, err) + + // developer delete admin + pk = types.ProjectAdminKey(admAddr) + err = ts.delProjectKeys(project.Index, dev2Addr, pk) + require.NotNil(t, err) + + // developer delete developer + pk = types.ProjectDeveloperKey(dev3Addr) + err = ts.delProjectKeys(project.Index, dev2Addr, pk) + require.NotNil(t, err) + + // new admin delete subscription owner (admin) + pk = types.ProjectAdminKey(subAddr) + err = ts.delProjectKeys(project.Index, admAddr, pk) + require.NotNil(t, err) + + // subscription owner (admin) delete other admin + pk = types.ProjectAdminKey(admAddr) + err = ts.delProjectKeys(project.Index, subAddr, pk) + require.Nil(t, err) + + // admin delete developer + pk = types.ProjectDeveloperKey(dev3Addr) + err = ts.delProjectKeys(project.Index, admAddr, pk) + require.Nil(t, err) + + // deletion above will only take effect in next epoch + require.True(t, ts.isKeyInProject(project.Index, admAddr, types.ProjectKey_ADMIN)) + require.True(t, ts.isKeyInProject(project.Index, dev3Addr, types.ProjectKey_DEVELOPER)) + ts.AdvanceEpoch(1) + require.False(t, ts.isKeyInProject(project.Index, admAddr, types.ProjectKey_ADMIN)) + require.False(t, ts.isKeyInProject(project.Index, dev3Addr, types.ProjectKey_DEVELOPER)) } func TestAddAdminInTwoProjects(t *testing.T) { - _, keepers, ctx := testkeeper.InitAllKeepers(t) - _ctx := sdk.UnwrapSDKContext(ctx) - projectData := prepareProjectsData(ctx, keepers)[0] + ts := newTestStruct(t) + ts.prepareData(1, 0, 0) // 1 sub, 0 admin, 0 devel + + projectData := ts.projects["pd1"] plan := common.CreateMockPlan() - subAddr := common.CreateNewAccount(ctx, *keepers, 10000).Addr.String() - admAddr := projectData.ProjectKeys[0].Key + subAddr := ts.accounts["sub1"] + admAddr := ts.accounts["pd1_adm"] - err := keepers.Projects.CreateAdminProject(_ctx, subAddr, plan) + err := ts.keepers.Projects.CreateAdminProject(ts.ctx, subAddr, plan) require.Nil(t, err) // this is not supposed to fail because you can use the same admin key for two different projects // creating a regular project (not admin project) so subAccount won't be a developer there - err = keepers.Projects.CreateProject(_ctx, subAddr, projectData, plan) + err = ts.keepers.Projects.CreateProject(ts.ctx, subAddr, projectData, plan) require.Nil(t, err) - ctx = testkeeper.AdvanceEpoch(ctx, keepers) + ts.AdvanceEpoch(1) - _, err = keepers.Projects.Developer(ctx, &types.QueryDeveloperRequest{Developer: admAddr}) + _, err = ts.keepers.Projects.Developer(ts._ctx, &types.QueryDeveloperRequest{Developer: admAddr}) require.NotNil(t, err) - response, err := keepers.Projects.Developer(ctx, &types.QueryDeveloperRequest{Developer: subAddr}) + response, err := ts.keepers.Projects.Developer(ts._ctx, &types.QueryDeveloperRequest{Developer: subAddr}) require.Nil(t, err) require.Equal(t, response.Project.Index, types.ProjectIndex(subAddr, types.ADMIN_PROJECT_NAME)) } @@ -270,27 +455,27 @@ func TestSetSubscriptionPolicy(t *testing.T) { } func SetPolicyTest(t *testing.T, testAdminPolicy bool) { - servers, keepers, _ctx := testkeeper.InitAllKeepers(t) - ctx := sdk.UnwrapSDKContext(_ctx) + ts := newTestStruct(t) + ts.prepareData(1, 0, 1) // 1 sub, 0 admin, 1 data - projectData := prepareProjectsData(_ctx, keepers)[0] + projectData := ts.projects["pd1"] plan := common.CreateMockPlan() - subAddr := common.CreateNewAccount(_ctx, *keepers, 10000).Addr.String() - admAddr := projectData.ProjectKeys[0].Key - devAddr := common.CreateNewAccount(_ctx, *keepers, 10000).Addr.String() + subAddr := ts.accounts["sub1"] + admAddr := ts.accounts["pd1_adm"] + devAddr := ts.accounts["dev1"] projectID := types.ProjectIndex(subAddr, projectData.Name) - err := keepers.Projects.CreateProject(ctx, subAddr, projectData, plan) + err := ts.keepers.Projects.CreateProject(ts.ctx, subAddr, projectData, plan) require.Nil(t, err) pk := types.ProjectDeveloperKey(devAddr) - err = keepers.Projects.AddKeysToProject(ctx, projectID, admAddr, []types.ProjectKey{pk}) + err = ts.keepers.Projects.AddKeysToProject(ts.ctx, projectID, admAddr, []types.ProjectKey{pk}) require.Nil(t, err) spec := common.CreateMockSpec() - keepers.Spec.SetSpec(ctx, spec) + ts.keepers.Spec.SetSpec(ts.ctx, spec) templates := []struct { name string @@ -385,13 +570,12 @@ func SetPolicyTest(t *testing.T, testAdminPolicy bool) { err = SetPolicyMessage.ValidateBasic() require.Nil(t, err) - _, err := servers.ProjectServer.SetPolicy(_ctx, &SetPolicyMessage) + _, err := ts.servers.ProjectServer.SetPolicy(ts._ctx, &SetPolicyMessage) if tt.setAdminPolicySuccess { require.Nil(t, err) - _ctx = testkeeper.AdvanceEpoch(_ctx, keepers) - ctx = sdk.UnwrapSDKContext(_ctx) + ts.AdvanceEpoch(1) - proj, err := keepers.Projects.GetProjectForBlock(ctx, tt.projectID, uint64(ctx.BlockHeight())) + proj, err := ts.keepers.Projects.GetProjectForBlock(ts.ctx, tt.projectID, ts.BlockHeight()) require.Nil(t, err) require.Equal(t, newPolicy, *proj.AdminPolicy) @@ -408,15 +592,13 @@ func SetPolicyTest(t *testing.T, testAdminPolicy bool) { err = setSubscriptionPolicyMessage.ValidateBasic() require.Nil(t, err) - _, err := servers.ProjectServer.SetSubscriptionPolicy(_ctx, &setSubscriptionPolicyMessage) + _, err := ts.servers.ProjectServer.SetSubscriptionPolicy(ts._ctx, &setSubscriptionPolicyMessage) if tt.setSubscriptionPolicySuccess { require.Nil(t, err) - _ctx = testkeeper.AdvanceEpoch(_ctx, keepers) - ctx = sdk.UnwrapSDKContext(_ctx) + ts.AdvanceEpoch(1) - proj, err := keepers.Projects.GetProjectForBlock(ctx, tt.projectID, uint64(ctx.BlockHeight())) + proj, err := ts.keepers.Projects.GetProjectForBlock(ts.ctx, tt.projectID, ts.BlockHeight()) require.Nil(t, err) - require.Equal(t, newPolicy, *proj.SubscriptionPolicy) } else { require.NotNil(t, err) @@ -427,121 +609,184 @@ func SetPolicyTest(t *testing.T, testAdminPolicy bool) { } func TestChargeComputeUnits(t *testing.T) { - servers, keepers, _ctx := testkeeper.InitAllKeepers(t) + ts := newTestStruct(t) + ts.prepareData(0, 0, 1) // 0 sub, 0 adm, 1 dev - projectData := prepareProjectsData(_ctx, keepers)[0] + projectData := ts.projects["pd1"] plan := common.CreateMockPlan() - subAddr := projectData.ProjectKeys[0].Key - devAddr := common.CreateNewAccount(_ctx, *keepers, 10000).Addr.String() - - _ctx = testkeeper.AdvanceEpoch(_ctx, keepers) - ctx := sdk.UnwrapSDKContext(_ctx) - block1 := uint64(ctx.BlockHeight()) + subAddr := ts.accounts["pd1_adm"] + devAddr := ts.accounts["dev1"] - err := keepers.Projects.CreateProject(ctx, subAddr, projectData, plan) + err := ts.keepers.Projects.CreateProject(ts.ctx, subAddr, projectData, plan) require.Nil(t, err) - _ctx = testkeeper.AdvanceEpoch(_ctx, keepers) - ctx = sdk.UnwrapSDKContext(_ctx) - block2 := uint64(ctx.BlockHeight()) + ts.AdvanceEpoch(1) + block1 := ts.BlockHeight() projectID := types.ProjectIndex(subAddr, projectData.Name) - project, err := keepers.Projects.GetProjectForBlock(ctx, projectID, block2) + project, err := ts.keepers.Projects.GetProjectForBlock(ts.ctx, projectID, block1) require.Nil(t, err) // add developer key (created fixation) - pk := types.ProjectDeveloperKey(devAddr) - _, err = servers.ProjectServer.AddKeys(_ctx, &types.MsgAddKeys{ - Creator: subAddr, - Project: project.Index, - ProjectKeys: []types.ProjectKey{pk}, - }) + err = ts.addProjectKeys(project.Index, subAddr, types.ProjectDeveloperKey(devAddr)) require.Nil(t, err) - _ctx = testkeeper.AdvanceEpoch(_ctx, keepers) - ctx = sdk.UnwrapSDKContext(_ctx) - block3 := uint64(ctx.BlockHeight()) + // first epoch for the developer key addition to take place. - keepers.Projects.SnapshotSubscriptionProjects(ctx, subAddr) + ts.AdvanceEpoch(1) + block2 := ts.BlockHeight() + + // second epoch to move further, otherwise snapshot will affect the current new + // dev key instead of creating a separate fixation version. + + ts.AdvanceEpoch(1) + block3 := ts.BlockHeight() + + ts.keepers.Projects.SnapshotSubscriptionProjects(ts.ctx, subAddr) // try to charge CUs: should update oldest and second-oldest entries, but not the latest // (because the latter is in a new snapshot) - err = keepers.Projects.ChargeComputeUnitsToProject(ctx, project, block1, 1000) + err = ts.keepers.Projects.ChargeComputeUnitsToProject(ts.ctx, project, block1, 1000) require.Nil(t, err) - proj, err := keepers.Projects.GetProjectForBlock(ctx, project.Index, block1) + proj, err := ts.keepers.Projects.GetProjectForBlock(ts.ctx, project.Index, block1) require.Nil(t, err) require.Equal(t, uint64(1000), proj.UsedCu) - proj, err = keepers.Projects.GetProjectForBlock(ctx, project.Index, block2) + proj, err = ts.keepers.Projects.GetProjectForBlock(ts.ctx, project.Index, block2) require.Nil(t, err) require.Equal(t, uint64(1000), proj.UsedCu) - proj, err = keepers.Projects.GetProjectForBlock(ctx, project.Index, block3) + proj, err = ts.keepers.Projects.GetProjectForBlock(ts.ctx, project.Index, block3) require.Nil(t, err) require.Equal(t, uint64(0), proj.UsedCu) - keepers.Projects.ChargeComputeUnitsToProject(ctx, project, block2, 1000) + ts.keepers.Projects.ChargeComputeUnitsToProject(ts.ctx, project, block2, 1000) - proj, err = keepers.Projects.GetProjectForBlock(ctx, project.Index, block1) + proj, err = ts.keepers.Projects.GetProjectForBlock(ts.ctx, project.Index, block1) require.Nil(t, err) require.Equal(t, uint64(1000), proj.UsedCu) - proj, err = keepers.Projects.GetProjectForBlock(ctx, project.Index, block2) + proj, err = ts.keepers.Projects.GetProjectForBlock(ts.ctx, project.Index, block2) require.Nil(t, err) require.Equal(t, uint64(2000), proj.UsedCu) - proj, err = keepers.Projects.GetProjectForBlock(ctx, project.Index, block3) + proj, err = ts.keepers.Projects.GetProjectForBlock(ts.ctx, project.Index, block3) require.Nil(t, err) require.Equal(t, uint64(0), proj.UsedCu) } -func TestAddDevKeyToSameProjectDifferentBlocks(t *testing.T) { - _, keepers, ctx := testkeeper.InitAllKeepers(t) - _ctx := sdk.UnwrapSDKContext(ctx) - projectName := "mockname1" - subAddr := common.CreateNewAccount(ctx, *keepers, 10000).Addr.String() - dev1Addr := common.CreateNewAccount(ctx, *keepers, 10000).Addr.String() - dev2Addr := common.CreateNewAccount(ctx, *keepers, 10000).Addr.String() - projectID := types.ProjectIndex(subAddr, projectName) +func TestAddDelKeysSameEpoch(t *testing.T) { + ts := newTestStruct(t) + ts.prepareData(2, 1, 5) // 2 sub, 1 adm, 5 dev + + sub1Addr := ts.accounts["sub1"] + sub2Addr := ts.accounts["sub2"] + adm1Addr := ts.accounts["adm1"] + dev1Addr := ts.accounts["dev1"] + dev2Addr := ts.accounts["dev2"] + dev3Addr := ts.accounts["dev3"] + dev4Addr := ts.accounts["dev4"] + dev5Addr := ts.accounts["dev5"] + plan := common.CreateMockPlan() - projectData := types.ProjectData{ - Name: projectName, + projectData1 := types.ProjectData{ + Name: "mockname1", Enabled: true, - ProjectKeys: []types.ProjectKey{types.ProjectDeveloperKey(subAddr)}, + ProjectKeys: []types.ProjectKey{types.ProjectDeveloperKey(sub1Addr)}, + Policy: &plan.PlanPolicy, + } + err := ts.keepers.Projects.CreateProject(ts.ctx, sub1Addr, projectData1, plan) + require.Nil(t, err) + + projectData2 := types.ProjectData{ + Name: "mockname2", + Enabled: true, + ProjectKeys: []types.ProjectKey{types.ProjectDeveloperKey(sub2Addr)}, Policy: &plan.PlanPolicy, } - err := keepers.Projects.CreateProject(_ctx, subAddr, projectData, plan) + err = ts.keepers.Projects.CreateProject(ts.ctx, sub2Addr, projectData2, plan) require.Nil(t, err) - ctx = testkeeper.AdvanceBlock(ctx, keepers) - _ctx = sdk.UnwrapSDKContext(ctx) + ts.AdvanceEpoch(1) - err = keepers.Projects.AddKeysToProject(_ctx, projectID, subAddr, + projectID1 := types.ProjectIndex(sub1Addr, projectData1.Name) + projectID2 := types.ProjectIndex(sub2Addr, projectData2.Name) + + err = ts.keepers.Projects.AddKeysToProject(ts.ctx, projectID1, sub1Addr, []types.ProjectKey{types.ProjectDeveloperKey(dev1Addr)}) require.Nil(t, err) - ctx = testkeeper.AdvanceBlock(ctx, keepers) - _ctx = sdk.UnwrapSDKContext(ctx) + ts.AdvanceBlock(1) - err = keepers.Projects.AddKeysToProject(_ctx, projectID, subAddr, + err = ts.keepers.Projects.AddKeysToProject(ts.ctx, projectID1, sub1Addr, []types.ProjectKey{types.ProjectDeveloperKey(dev2Addr)}) require.Nil(t, err) - proj, err := keepers.Projects.GetProjectForDeveloper(_ctx, subAddr, - uint64(_ctx.BlockHeight())) - require.Nil(t, err) + require.False(t, ts.isKeyInProject(projectID1, dev1Addr, types.ProjectKey_DEVELOPER)) + require.False(t, ts.isKeyInProject(projectID1, dev2Addr, types.ProjectKey_DEVELOPER)) + ts.AdvanceEpoch(1) + require.True(t, ts.isKeyInProject(projectID1, dev1Addr, types.ProjectKey_DEVELOPER)) + require.True(t, ts.isKeyInProject(projectID1, dev2Addr, types.ProjectKey_DEVELOPER)) + proj, err := ts.keepers.Projects.GetProjectForDeveloper(ts.ctx, sub1Addr, ts.BlockHeight()) + require.Nil(t, err) require.Equal(t, 3, len(proj.ProjectKeys)) + + // add twice - ok + err = ts.addProjectKeys(projectID1, sub1Addr, types.ProjectAdminKey(adm1Addr)) + require.Nil(t, err) + err = ts.addProjectKeys(projectID1, sub1Addr, types.ProjectDeveloperKey(dev3Addr)) + require.Nil(t, err) + + ts.AdvanceEpoch(1) + require.True(t, ts.isKeyInProject(projectID1, adm1Addr, types.ProjectKey_ADMIN)) + require.True(t, ts.isKeyInProject(projectID1, dev3Addr, types.ProjectKey_DEVELOPER)) + + // del twice - fail + err = ts.delProjectKeys(projectID1, sub1Addr, types.ProjectAdminKey(adm1Addr)) + require.Nil(t, err) + err = ts.delProjectKeys(projectID1, sub1Addr, types.ProjectAdminKey(adm1Addr)) + require.NotNil(t, err) + err = ts.delProjectKeys(projectID1, sub1Addr, types.ProjectDeveloperKey(dev3Addr)) + require.Nil(t, err) + err = ts.delProjectKeys(projectID1, sub1Addr, types.ProjectDeveloperKey(dev3Addr)) + require.NotNil(t, err) + + ts.AdvanceEpoch(1) + require.False(t, ts.isKeyInProject(projectID1, adm1Addr, types.ProjectKey_ADMIN)) + require.False(t, ts.isKeyInProject(projectID1, dev3Addr, types.ProjectKey_DEVELOPER)) + + // add, del in same epoch + err = ts.addProjectKeys(projectID2, sub2Addr, types.ProjectAdminKey(adm1Addr)) + require.Nil(t, err) + err = ts.delProjectKeys(projectID2, sub2Addr, types.ProjectAdminKey(adm1Addr)) + require.Nil(t, err) + + err = ts.addProjectKeys(projectID2, sub2Addr, types.ProjectDeveloperKey(dev4Addr)) + require.Nil(t, err) + err = ts.delProjectKeys(projectID2, sub2Addr, types.ProjectDeveloperKey(dev4Addr)) + require.Nil(t, err) + + ts.AdvanceEpoch(1) + require.False(t, ts.isKeyInProject(projectID2, adm1Addr, types.ProjectKey_ADMIN)) + require.False(t, ts.isKeyInProject(projectID2, dev3Addr, types.ProjectKey_DEVELOPER)) + + // add dev to two projects in same epoch - latter fails + err = ts.addProjectKeys(projectID2, sub2Addr, types.ProjectDeveloperKey(dev5Addr)) + require.Nil(t, err) + err = ts.addProjectKeys(projectID2, sub1Addr, types.ProjectDeveloperKey(dev5Addr)) + require.NotNil(t, err) } func TestAddDevKeyToDifferentProjectsInSameBlock(t *testing.T) { - _, keepers, ctx := testkeeper.InitAllKeepers(t) - _ctx := sdk.UnwrapSDKContext(ctx) - plan := common.CreateMockPlan() + ts := newTestStruct(t) + ts.prepareData(2, 0, 1) // 2 sub, 0 adm, 1 dev - sub1Addr := common.CreateNewAccount(ctx, *keepers, 10000).Addr.String() - sub2Addr := common.CreateNewAccount(ctx, *keepers, 10000).Addr.String() - devAddr := common.CreateNewAccount(ctx, *keepers, 10000).Addr.String() + sub1Addr := ts.accounts["sub1"] + sub2Addr := ts.accounts["sub2"] + dev1Addr := ts.accounts["dev1"] + + plan := common.CreateMockPlan() projectName1 := "mockname1" projectName2 := "mockname2" @@ -555,7 +800,7 @@ func TestAddDevKeyToDifferentProjectsInSameBlock(t *testing.T) { ProjectKeys: []types.ProjectKey{types.ProjectDeveloperKey(sub1Addr)}, Policy: &plan.PlanPolicy, } - err := keepers.Projects.CreateProject(_ctx, sub1Addr, projectData1, plan) + err := ts.keepers.Projects.CreateProject(ts.ctx, sub1Addr, projectData1, plan) require.Nil(t, err) projectData2 := types.ProjectData{ @@ -564,26 +809,25 @@ func TestAddDevKeyToDifferentProjectsInSameBlock(t *testing.T) { ProjectKeys: []types.ProjectKey{types.ProjectDeveloperKey(sub2Addr)}, Policy: &plan.PlanPolicy, } - err = keepers.Projects.CreateProject(_ctx, sub2Addr, projectData2, plan) + err = ts.keepers.Projects.CreateProject(ts.ctx, sub2Addr, projectData2, plan) require.Nil(t, err) - ctx = testkeeper.AdvanceBlock(ctx, keepers) - _ctx = sdk.UnwrapSDKContext(ctx) + ts.AdvanceEpoch(1) - err = keepers.Projects.AddKeysToProject(_ctx, projectID1, sub1Addr, - []types.ProjectKey{types.ProjectDeveloperKey(devAddr)}) + err = ts.keepers.Projects.AddKeysToProject(ts.ctx, projectID1, sub1Addr, + []types.ProjectKey{types.ProjectDeveloperKey(dev1Addr)}) require.Nil(t, err) - err = keepers.Projects.AddKeysToProject(_ctx, projectID2, sub2Addr, - []types.ProjectKey{types.ProjectDeveloperKey(devAddr)}) - require.NotNil(t, err) // should fail since this developer was already added to the first project + err = ts.keepers.Projects.AddKeysToProject(ts.ctx, projectID2, sub2Addr, + []types.ProjectKey{types.ProjectDeveloperKey(dev1Addr)}) + require.NotNil(t, err) // developer was already added to the first project + + ts.AdvanceEpoch(1) - proj1, err := keepers.Projects.GetProjectForDeveloper(_ctx, sub1Addr, - uint64(_ctx.BlockHeight())) + proj1, err := ts.keepers.Projects.GetProjectForDeveloper(ts.ctx, sub1Addr, ts.BlockHeight()) require.Nil(t, err) - proj2, err := keepers.Projects.GetProjectForDeveloper(_ctx, sub2Addr, - uint64(_ctx.BlockHeight())) + proj2, err := ts.keepers.Projects.GetProjectForDeveloper(ts.ctx, sub2Addr, ts.BlockHeight()) require.Nil(t, err) require.Equal(t, 2, len(proj1.ProjectKeys)) diff --git a/x/projects/module_simulation.go b/x/projects/module_simulation.go index a19045ded7..e593edc93d 100644 --- a/x/projects/module_simulation.go +++ b/x/projects/module_simulation.go @@ -28,6 +28,10 @@ const ( // TODO: Determine the simulation weight value defaultWeightMsgAddKeys int = 100 + opWeightMsgDelKeys = "op_weight_msg_del_keys" + // TODO: Determine the simulation weight value + defaultWeightMsgDelKeys int = 100 + opWeightMsgSetPolicy = "op_weight_msg_set_admin_policy" // TODO: Determine the simulation weight value defaultWeightMsgSetPolicy int = 100 @@ -80,6 +84,17 @@ func (am AppModule) WeightedOperations(simState module.SimulationState) []simtyp projectssimulation.SimulateMsgAddKeys(am.keeper), )) + var weightMsgDelKeys int + simState.AppParams.GetOrGenerate(simState.Cdc, opWeightMsgDelKeys, &weightMsgDelKeys, nil, + func(_ *rand.Rand) { + weightMsgDelKeys = defaultWeightMsgDelKeys + }, + ) + operations = append(operations, simulation.NewWeightedOperation( + weightMsgDelKeys, + projectssimulation.SimulateMsgDelKeys(am.keeper), + )) + var weightMsgSetPolicy int simState.AppParams.GetOrGenerate(simState.Cdc, opWeightMsgSetPolicy, &weightMsgSetPolicy, nil, func(_ *rand.Rand) { diff --git a/x/projects/simulation/del_project_keys.go b/x/projects/simulation/del_project_keys.go new file mode 100644 index 0000000000..d9a87c18fe --- /dev/null +++ b/x/projects/simulation/del_project_keys.go @@ -0,0 +1,27 @@ +package simulation + +import ( + "math/rand" + + "github.com/cosmos/cosmos-sdk/baseapp" + sdk "github.com/cosmos/cosmos-sdk/types" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + "github.com/lavanet/lava/x/projects/keeper" + "github.com/lavanet/lava/x/projects/types" +) + +func SimulateMsgDelKeys( + k keeper.Keeper, +) simtypes.Operation { + return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, + ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { + simAccount, _ := simtypes.RandomAcc(r, accs) + msg := &types.MsgDelKeys{ + Creator: simAccount.Address.String(), + } + + // TODO: Handling the DelKeys simulation + + return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "DelKeys simulation not implemented"), nil, nil + } +} diff --git a/x/projects/types/codec.go b/x/projects/types/codec.go index 20f09c6b92..c6f25bfddf 100644 --- a/x/projects/types/codec.go +++ b/x/projects/types/codec.go @@ -10,6 +10,7 @@ import ( func RegisterCodec(cdc *codec.LegacyAmino) { cdc.RegisterConcrete(&MsgAddKeys{}, "projects/AddKeys", nil) + cdc.RegisterConcrete(&MsgDelKeys{}, "projects/DelKeys", nil) cdc.RegisterConcrete(&MsgSetPolicy{}, "projects/SetPolicy", nil) cdc.RegisterConcrete(&MsgSetSubscriptionPolicy{}, "projects/SetSubscriptionPolicy", nil) // this line is used by starport scaffolding # 2 @@ -19,6 +20,9 @@ func RegisterInterfaces(registry cdctypes.InterfaceRegistry) { registry.RegisterImplementations((*sdk.Msg)(nil), &MsgAddKeys{}, ) + registry.RegisterImplementations((*sdk.Msg)(nil), + &MsgDelKeys{}, + ) registry.RegisterImplementations((*sdk.Msg)(nil), &MsgSetPolicy{}, ) diff --git a/x/projects/types/message_del_keys.go b/x/projects/types/message_del_keys.go new file mode 100644 index 0000000000..1f9ce022b1 --- /dev/null +++ b/x/projects/types/message_del_keys.go @@ -0,0 +1,48 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +const TypeMsgDelKeys = "del_keys" + +var _ sdk.Msg = &MsgDelKeys{} + +func NewMsgDelKeys(creator string, projectID string, projectKeys []ProjectKey) *MsgDelKeys { + return &MsgDelKeys{ + Creator: creator, + Project: projectID, + ProjectKeys: projectKeys, + } +} + +func (msg *MsgDelKeys) Route() string { + return RouterKey +} + +func (msg *MsgDelKeys) Type() string { + return TypeMsgDelKeys +} + +func (msg *MsgDelKeys) GetSigners() []sdk.AccAddress { + creator, err := sdk.AccAddressFromBech32(msg.Creator) + if err != nil { + panic(err) + } + return []sdk.AccAddress{creator} +} + +func (msg *MsgDelKeys) GetSignBytes() []byte { + bz := ModuleCdc.MustMarshalJSON(msg) + return sdk.MustSortJSON(bz) +} + +func (msg *MsgDelKeys) ValidateBasic() error { + _, err := sdk.AccAddressFromBech32(msg.Creator) + if err != nil { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid creator address (%s)", err) + } + + return nil +} diff --git a/x/projects/types/message_del_keys_test.go b/x/projects/types/message_del_keys_test.go new file mode 100644 index 0000000000..9f92be4cc6 --- /dev/null +++ b/x/projects/types/message_del_keys_test.go @@ -0,0 +1,40 @@ +package types + +import ( + "testing" + + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/lavanet/lava/testutil/sample" + "github.com/stretchr/testify/require" +) + +func TestMsgDelKeys_ValidateBasic(t *testing.T) { + tests := []struct { + name string + msg MsgDelKeys + err error + }{ + { + name: "invalid address", + msg: MsgDelKeys{ + Creator: "invalid_address", + }, + err: sdkerrors.ErrInvalidAddress, + }, { + name: "valid address", + msg: MsgDelKeys{ + Creator: sample.AccAddress(), + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := tt.msg.ValidateBasic() + if tt.err != nil { + require.ErrorIs(t, err, tt.err) + return + } + require.NoError(t, err) + }) + } +} diff --git a/x/projects/types/project.go b/x/projects/types/project.go index 23262fa675..49c8f82132 100644 --- a/x/projects/types/project.go +++ b/x/projects/types/project.go @@ -77,14 +77,32 @@ func (project *Project) GetKey(key string) ProjectKey { return ProjectKey{} } -func (project *Project) AppendKey(key ProjectKey) { +func (project *Project) AppendKey(key ProjectKey) bool { for i, projectKey := range project.ProjectKeys { if projectKey.Key == key.Key { project.ProjectKeys[i].Kinds |= key.Kinds - return + return true } } project.ProjectKeys = append(project.ProjectKeys, key) + return false +} + +func (project *Project) DeleteKey(key ProjectKey) bool { + length := len(project.ProjectKeys) + for i, projectKey := range project.ProjectKeys { + if projectKey.Key == key.Key { + project.ProjectKeys[i].Kinds &= ^key.Kinds + if project.ProjectKeys[i].Kinds == uint32(ProjectKey_NONE) { + if i < length-1 { + project.ProjectKeys[i] = project.ProjectKeys[length-1] + } + project.ProjectKeys = project.ProjectKeys[0 : length-1] + } + return true + } + } + return false } func (project *Project) IsAdminKey(key string) bool { diff --git a/x/projects/types/tx.pb.go b/x/projects/types/tx.pb.go index fca8629b46..abb7be9e39 100644 --- a/x/projects/types/tx.pb.go +++ b/x/projects/types/tx.pb.go @@ -124,6 +124,102 @@ func (m *MsgAddKeysResponse) XXX_DiscardUnknown() { var xxx_messageInfo_MsgAddKeysResponse proto.InternalMessageInfo +type MsgDelKeys struct { + Creator string `protobuf:"bytes,1,opt,name=creator,proto3" json:"creator,omitempty"` + Project string `protobuf:"bytes,2,opt,name=project,proto3" json:"project,omitempty"` + ProjectKeys []ProjectKey `protobuf:"bytes,3,rep,name=project_keys,json=projectKeys,proto3" json:"project_keys"` +} + +func (m *MsgDelKeys) Reset() { *m = MsgDelKeys{} } +func (m *MsgDelKeys) String() string { return proto.CompactTextString(m) } +func (*MsgDelKeys) ProtoMessage() {} +func (*MsgDelKeys) Descriptor() ([]byte, []int) { + return fileDescriptor_b5dcbe7dfba713c0, []int{2} +} +func (m *MsgDelKeys) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgDelKeys) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgDelKeys.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgDelKeys) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgDelKeys.Merge(m, src) +} +func (m *MsgDelKeys) XXX_Size() int { + return m.Size() +} +func (m *MsgDelKeys) XXX_DiscardUnknown() { + xxx_messageInfo_MsgDelKeys.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgDelKeys proto.InternalMessageInfo + +func (m *MsgDelKeys) GetCreator() string { + if m != nil { + return m.Creator + } + return "" +} + +func (m *MsgDelKeys) GetProject() string { + if m != nil { + return m.Project + } + return "" +} + +func (m *MsgDelKeys) GetProjectKeys() []ProjectKey { + if m != nil { + return m.ProjectKeys + } + return nil +} + +type MsgDelKeysResponse struct { +} + +func (m *MsgDelKeysResponse) Reset() { *m = MsgDelKeysResponse{} } +func (m *MsgDelKeysResponse) String() string { return proto.CompactTextString(m) } +func (*MsgDelKeysResponse) ProtoMessage() {} +func (*MsgDelKeysResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_b5dcbe7dfba713c0, []int{3} +} +func (m *MsgDelKeysResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgDelKeysResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgDelKeysResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgDelKeysResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgDelKeysResponse.Merge(m, src) +} +func (m *MsgDelKeysResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgDelKeysResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgDelKeysResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgDelKeysResponse proto.InternalMessageInfo + type MsgSetPolicy struct { Creator string `protobuf:"bytes,1,opt,name=creator,proto3" json:"creator,omitempty"` Project string `protobuf:"bytes,2,opt,name=project,proto3" json:"project,omitempty"` @@ -134,7 +230,7 @@ func (m *MsgSetPolicy) Reset() { *m = MsgSetPolicy{} } func (m *MsgSetPolicy) String() string { return proto.CompactTextString(m) } func (*MsgSetPolicy) ProtoMessage() {} func (*MsgSetPolicy) Descriptor() ([]byte, []int) { - return fileDescriptor_b5dcbe7dfba713c0, []int{2} + return fileDescriptor_b5dcbe7dfba713c0, []int{4} } func (m *MsgSetPolicy) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -191,7 +287,7 @@ func (m *MsgSetPolicyResponse) Reset() { *m = MsgSetPolicyResponse{} } func (m *MsgSetPolicyResponse) String() string { return proto.CompactTextString(m) } func (*MsgSetPolicyResponse) ProtoMessage() {} func (*MsgSetPolicyResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_b5dcbe7dfba713c0, []int{3} + return fileDescriptor_b5dcbe7dfba713c0, []int{5} } func (m *MsgSetPolicyResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -230,7 +326,7 @@ func (m *MsgSetSubscriptionPolicy) Reset() { *m = MsgSetSubscriptionPoli func (m *MsgSetSubscriptionPolicy) String() string { return proto.CompactTextString(m) } func (*MsgSetSubscriptionPolicy) ProtoMessage() {} func (*MsgSetSubscriptionPolicy) Descriptor() ([]byte, []int) { - return fileDescriptor_b5dcbe7dfba713c0, []int{4} + return fileDescriptor_b5dcbe7dfba713c0, []int{6} } func (m *MsgSetSubscriptionPolicy) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -287,7 +383,7 @@ func (m *MsgSetSubscriptionPolicyResponse) Reset() { *m = MsgSetSubscrip func (m *MsgSetSubscriptionPolicyResponse) String() string { return proto.CompactTextString(m) } func (*MsgSetSubscriptionPolicyResponse) ProtoMessage() {} func (*MsgSetSubscriptionPolicyResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_b5dcbe7dfba713c0, []int{5} + return fileDescriptor_b5dcbe7dfba713c0, []int{7} } func (m *MsgSetSubscriptionPolicyResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -319,6 +415,8 @@ var xxx_messageInfo_MsgSetSubscriptionPolicyResponse proto.InternalMessageInfo func init() { proto.RegisterType((*MsgAddKeys)(nil), "lavanet.lava.projects.MsgAddKeys") proto.RegisterType((*MsgAddKeysResponse)(nil), "lavanet.lava.projects.MsgAddKeysResponse") + proto.RegisterType((*MsgDelKeys)(nil), "lavanet.lava.projects.MsgDelKeys") + proto.RegisterType((*MsgDelKeysResponse)(nil), "lavanet.lava.projects.MsgDelKeysResponse") proto.RegisterType((*MsgSetPolicy)(nil), "lavanet.lava.projects.MsgSetPolicy") proto.RegisterType((*MsgSetPolicyResponse)(nil), "lavanet.lava.projects.MsgSetPolicyResponse") proto.RegisterType((*MsgSetSubscriptionPolicy)(nil), "lavanet.lava.projects.MsgSetSubscriptionPolicy") @@ -328,7 +426,7 @@ func init() { func init() { proto.RegisterFile("projects/tx.proto", fileDescriptor_b5dcbe7dfba713c0) } var fileDescriptor_b5dcbe7dfba713c0 = []byte{ - // 394 bytes of a gzipped FileDescriptorProto + // 420 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x2c, 0x28, 0xca, 0xcf, 0x4a, 0x4d, 0x2e, 0x29, 0xd6, 0x2f, 0xa9, 0xd0, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0xcd, 0x49, 0x2c, 0x4b, 0xcc, 0x4b, 0x2d, 0xd1, 0x03, 0xd1, 0x7a, 0x30, 0x79, 0x29, 0x31, 0xb8, 0x4a, @@ -339,21 +437,23 @@ var fileDescriptor_b5dcbe7dfba713c0 = []byte{ 0x81, 0x32, 0xe3, 0xb3, 0x53, 0x2b, 0x8b, 0x25, 0x98, 0x15, 0x98, 0x35, 0xb8, 0x8d, 0x14, 0xf5, 0xb0, 0x3a, 0x4f, 0x2f, 0x00, 0xc2, 0xf0, 0x4e, 0xad, 0x74, 0x62, 0x39, 0x71, 0x4f, 0x9e, 0x21, 0x88, 0xbb, 0x00, 0x2e, 0x52, 0xac, 0x24, 0xc2, 0x25, 0x84, 0x70, 0x4d, 0x50, 0x6a, 0x71, 0x41, - 0x7e, 0x5e, 0x71, 0xaa, 0x52, 0x3d, 0x17, 0x8f, 0x6f, 0x71, 0x7a, 0x70, 0x6a, 0x49, 0x40, 0x7e, - 0x4e, 0x66, 0x72, 0x25, 0x59, 0xae, 0xb4, 0xe6, 0x62, 0x2b, 0x00, 0xeb, 0x96, 0x60, 0x56, 0x60, - 0xd4, 0xe0, 0x36, 0x92, 0xc5, 0xe5, 0x3e, 0xb0, 0x22, 0xa8, 0xdb, 0xa0, 0x5a, 0x94, 0xc4, 0xb8, - 0x44, 0x90, 0x1d, 0x00, 0x77, 0x58, 0x2f, 0x23, 0x97, 0x04, 0x44, 0x22, 0xb8, 0x34, 0xa9, 0x38, - 0xb9, 0x28, 0xb3, 0xa0, 0x24, 0x33, 0x3f, 0x8f, 0xa0, 0x2b, 0xa5, 0xb8, 0x38, 0x60, 0xf6, 0x49, - 0x30, 0x29, 0x30, 0x6b, 0x70, 0x06, 0xc1, 0xf9, 0x94, 0xb9, 0x53, 0x89, 0x4b, 0x01, 0x97, 0x73, - 0x60, 0x6e, 0x36, 0x3a, 0xc8, 0xc4, 0xc5, 0xec, 0x5b, 0x9c, 0x2e, 0x14, 0xce, 0xc5, 0x0e, 0x8b, - 0x75, 0x5c, 0x71, 0x85, 0x88, 0x0a, 0x29, 0x4d, 0x82, 0x4a, 0x60, 0x16, 0x08, 0xc5, 0x72, 0x71, - 0x22, 0xa2, 0x4a, 0x19, 0xb7, 0x3e, 0xb8, 0x22, 0x29, 0x6d, 0x22, 0x14, 0xc1, 0x8d, 0x6f, 0x64, - 0xe4, 0x12, 0xc5, 0x1e, 0xe0, 0xfa, 0x78, 0x8d, 0xc1, 0xd4, 0x20, 0x65, 0x4e, 0xa2, 0x06, 0x98, - 0x1b, 0x9c, 0x9c, 0x4e, 0x3c, 0x92, 0x63, 0xbc, 0xf0, 0x48, 0x8e, 0xf1, 0xc1, 0x23, 0x39, 0xc6, - 0x09, 0x8f, 0xe5, 0x18, 0x2e, 0x3c, 0x96, 0x63, 0xb8, 0xf1, 0x58, 0x8e, 0x21, 0x4a, 0x23, 0x3d, - 0xb3, 0x24, 0xa3, 0x34, 0x49, 0x2f, 0x39, 0x3f, 0x57, 0x1f, 0x6a, 0x38, 0x98, 0xd6, 0xaf, 0xd0, - 0x47, 0xe4, 0xe0, 0xca, 0x82, 0xd4, 0xe2, 0x24, 0x36, 0x70, 0x06, 0x34, 0x06, 0x04, 0x00, 0x00, - 0xff, 0xff, 0x4b, 0x75, 0x92, 0x3b, 0xda, 0x03, 0x00, 0x00, + 0x7e, 0x5e, 0x71, 0x2a, 0xcc, 0x91, 0x2e, 0xa9, 0x39, 0x83, 0xc8, 0x91, 0x50, 0xd7, 0xc0, 0x1d, + 0x59, 0xcf, 0xc5, 0xe3, 0x5b, 0x9c, 0x1e, 0x9c, 0x5a, 0x12, 0x90, 0x9f, 0x93, 0x99, 0x5c, 0x49, + 0x96, 0x2b, 0xad, 0xb9, 0xd8, 0x0a, 0xc0, 0xba, 0x25, 0x98, 0x15, 0x18, 0x35, 0xb8, 0x8d, 0x64, + 0x71, 0xb9, 0x0f, 0xac, 0x08, 0xea, 0x36, 0xa8, 0x16, 0x25, 0x31, 0x2e, 0x11, 0x64, 0x07, 0xc0, + 0x1d, 0xd6, 0xcb, 0xc8, 0x25, 0x01, 0x91, 0x08, 0x2e, 0x4d, 0x2a, 0x4e, 0x2e, 0xca, 0x2c, 0x28, + 0xc9, 0xcc, 0xcf, 0x23, 0xe8, 0x4a, 0x29, 0x2e, 0x0e, 0x98, 0x7d, 0x12, 0x4c, 0x0a, 0xcc, 0x1a, + 0x9c, 0x41, 0x70, 0x3e, 0x65, 0xee, 0x54, 0xe2, 0x52, 0xc0, 0xe5, 0x1c, 0x98, 0x9b, 0x8d, 0x66, + 0x31, 0x73, 0x31, 0xfb, 0x16, 0xa7, 0x0b, 0x85, 0x73, 0xb1, 0xc3, 0x92, 0x26, 0xae, 0xb8, 0x42, + 0xa4, 0x17, 0x29, 0x4d, 0x82, 0x4a, 0x60, 0x16, 0x80, 0x0c, 0x86, 0x25, 0x27, 0x3c, 0x06, 0x43, + 0x95, 0xe0, 0x33, 0x18, 0x2d, 0x19, 0x08, 0xc5, 0x72, 0x71, 0x22, 0xd2, 0x80, 0x32, 0x6e, 0x7d, + 0x70, 0x45, 0x52, 0xda, 0x44, 0x28, 0x82, 0x1b, 0xdf, 0xc8, 0xc8, 0x25, 0x8a, 0x3d, 0x26, 0xf5, + 0xf1, 0x1a, 0x83, 0xa9, 0x41, 0xca, 0x9c, 0x44, 0x0d, 0x30, 0x37, 0x38, 0x39, 0x9d, 0x78, 0x24, + 0xc7, 0x78, 0xe1, 0x91, 0x1c, 0xe3, 0x83, 0x47, 0x72, 0x8c, 0x13, 0x1e, 0xcb, 0x31, 0x5c, 0x78, + 0x2c, 0xc7, 0x70, 0xe3, 0xb1, 0x1c, 0x43, 0x94, 0x46, 0x7a, 0x66, 0x49, 0x46, 0x69, 0x92, 0x5e, + 0x72, 0x7e, 0xae, 0x3e, 0xd4, 0x70, 0x30, 0xad, 0x5f, 0xa1, 0x8f, 0x28, 0xbf, 0x2a, 0x0b, 0x52, + 0x8b, 0x93, 0xd8, 0xc0, 0xc5, 0x8f, 0x31, 0x20, 0x00, 0x00, 0xff, 0xff, 0x26, 0xb0, 0x01, 0xc4, + 0xd8, 0x04, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -369,6 +469,7 @@ const _ = grpc.SupportPackageIsVersion4 // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type MsgClient interface { AddKeys(ctx context.Context, in *MsgAddKeys, opts ...grpc.CallOption) (*MsgAddKeysResponse, error) + DelKeys(ctx context.Context, in *MsgDelKeys, opts ...grpc.CallOption) (*MsgDelKeysResponse, error) SetPolicy(ctx context.Context, in *MsgSetPolicy, opts ...grpc.CallOption) (*MsgSetPolicyResponse, error) SetSubscriptionPolicy(ctx context.Context, in *MsgSetSubscriptionPolicy, opts ...grpc.CallOption) (*MsgSetSubscriptionPolicyResponse, error) } @@ -390,6 +491,15 @@ func (c *msgClient) AddKeys(ctx context.Context, in *MsgAddKeys, opts ...grpc.Ca return out, nil } +func (c *msgClient) DelKeys(ctx context.Context, in *MsgDelKeys, opts ...grpc.CallOption) (*MsgDelKeysResponse, error) { + out := new(MsgDelKeysResponse) + err := c.cc.Invoke(ctx, "/lavanet.lava.projects.Msg/DelKeys", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *msgClient) SetPolicy(ctx context.Context, in *MsgSetPolicy, opts ...grpc.CallOption) (*MsgSetPolicyResponse, error) { out := new(MsgSetPolicyResponse) err := c.cc.Invoke(ctx, "/lavanet.lava.projects.Msg/SetPolicy", in, out, opts...) @@ -411,6 +521,7 @@ func (c *msgClient) SetSubscriptionPolicy(ctx context.Context, in *MsgSetSubscri // MsgServer is the server API for Msg service. type MsgServer interface { AddKeys(context.Context, *MsgAddKeys) (*MsgAddKeysResponse, error) + DelKeys(context.Context, *MsgDelKeys) (*MsgDelKeysResponse, error) SetPolicy(context.Context, *MsgSetPolicy) (*MsgSetPolicyResponse, error) SetSubscriptionPolicy(context.Context, *MsgSetSubscriptionPolicy) (*MsgSetSubscriptionPolicyResponse, error) } @@ -422,6 +533,9 @@ type UnimplementedMsgServer struct { func (*UnimplementedMsgServer) AddKeys(ctx context.Context, req *MsgAddKeys) (*MsgAddKeysResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method AddKeys not implemented") } +func (*UnimplementedMsgServer) DelKeys(ctx context.Context, req *MsgDelKeys) (*MsgDelKeysResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DelKeys not implemented") +} func (*UnimplementedMsgServer) SetPolicy(ctx context.Context, req *MsgSetPolicy) (*MsgSetPolicyResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method SetPolicy not implemented") } @@ -451,6 +565,24 @@ func _Msg_AddKeys_Handler(srv interface{}, ctx context.Context, dec func(interfa return interceptor(ctx, in, info, handler) } +func _Msg_DelKeys_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgDelKeys) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).DelKeys(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/lavanet.lava.projects.Msg/DelKeys", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).DelKeys(ctx, req.(*MsgDelKeys)) + } + return interceptor(ctx, in, info, handler) +} + func _Msg_SetPolicy_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(MsgSetPolicy) if err := dec(in); err != nil { @@ -495,6 +627,10 @@ var _Msg_serviceDesc = grpc.ServiceDesc{ MethodName: "AddKeys", Handler: _Msg_AddKeys_Handler, }, + { + MethodName: "DelKeys", + Handler: _Msg_DelKeys_Handler, + }, { MethodName: "SetPolicy", Handler: _Msg_SetPolicy_Handler, @@ -582,6 +718,80 @@ func (m *MsgAddKeysResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *MsgDelKeys) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgDelKeys) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgDelKeys) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ProjectKeys) > 0 { + for iNdEx := len(m.ProjectKeys) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.ProjectKeys[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if len(m.Project) > 0 { + i -= len(m.Project) + copy(dAtA[i:], m.Project) + i = encodeVarintTx(dAtA, i, uint64(len(m.Project))) + i-- + dAtA[i] = 0x12 + } + if len(m.Creator) > 0 { + i -= len(m.Creator) + copy(dAtA[i:], m.Creator) + i = encodeVarintTx(dAtA, i, uint64(len(m.Creator))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgDelKeysResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgDelKeysResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgDelKeysResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + func (m *MsgSetPolicy) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -767,6 +977,38 @@ func (m *MsgAddKeysResponse) Size() (n int) { return n } +func (m *MsgDelKeys) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Creator) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Project) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if len(m.ProjectKeys) > 0 { + for _, e := range m.ProjectKeys { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } + return n +} + +func (m *MsgDelKeysResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + func (m *MsgSetPolicy) Size() (n int) { if m == nil { return 0 @@ -1029,6 +1271,204 @@ func (m *MsgAddKeysResponse) Unmarshal(dAtA []byte) error { } return nil } +func (m *MsgDelKeys) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgDelKeys: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgDelKeys: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Creator", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + 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 ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Creator = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Project", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + 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 ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Project = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProjectKeys", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ProjectKeys = append(m.ProjectKeys, ProjectKey{}) + if err := m.ProjectKeys[len(m.ProjectKeys)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgDelKeysResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgDelKeysResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgDelKeysResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *MsgSetPolicy) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/x/projects/types/types.go b/x/projects/types/types.go index 78cc2f5065..05c76964d3 100644 --- a/x/projects/types/types.go +++ b/x/projects/types/types.go @@ -11,3 +11,8 @@ const ( SET_ADMIN_POLICY SetPolicyEnum = 1 SET_SUBSCRIPTION_POLICY SetPolicyEnum = 2 ) + +const ( + AddProjectKeyEventName = "add_key_to_project_event" + DelProjectKeyEventName = "del_key_from_project_event" +) diff --git a/x/subscription/client/cli/tx_add_project.go b/x/subscription/client/cli/tx_add_project.go index 165fcac971..9ecf827e5a 100644 --- a/x/subscription/client/cli/tx_add_project.go +++ b/x/subscription/client/cli/tx_add_project.go @@ -30,10 +30,10 @@ func CmdAddProject() *cobra.Command { Note, after the project is added, its name (a.k.a. index) is changed to "-".`, Example: `required flags: --from - + optional flags: --policy-file , --project-keys-file , --disable - - lavad tx subscription add-project [project-file-path] --from `, + + lavad tx subscription add-project --policy-file policy-file-path --from `, Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) (err error) { projectName := args[0]