Skip to content

Commit

Permalink
Merge pull request #535 from lavanet/CNS-420-project-keys-allow-delete
Browse files Browse the repository at this point in the history
CNS-420: support project keys delete
  • Loading branch information
Yaroms authored Jun 12, 2023
2 parents 680786a + 7516d4f commit 634da41
Show file tree
Hide file tree
Showing 23 changed files with 1,382 additions and 262 deletions.
59 changes: 26 additions & 33 deletions common/fixation_entry.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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},
Expand All @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
}
Expand All @@ -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)
Expand Down
1 change: 0 additions & 1 deletion cookbook/projects/example_project_keys.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,4 @@ Project-Keys:
- key: lava@1xtfqykth53pkt97v955h3lql8zkj2m4s4rq9cr
Kinds: 3
- key: lava@1r3ernqu6rzp95z92580wae7xpuqwmznk3eqd7w
types:
Kinds: 1
2 changes: 1 addition & 1 deletion proto/projects/project.proto
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ message ProjectKey {
DEVELOPER = 0x2;
}

uint32 kinds = 4;
uint32 kinds = 4;
}

// protobuf expected in YAML format: used "moretags" to simplify parsing
Expand Down
10 changes: 10 additions & 0 deletions proto/projects/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;
Expand Down
15 changes: 15 additions & 0 deletions scripts/init_e2e.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
1 change: 1 addition & 0 deletions x/projects/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 0 additions & 5 deletions x/projects/client/cli/tx_add_keys.go
Original file line number Diff line number Diff line change
@@ -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"
Expand All @@ -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 <admin-key> (the project's subscription address is also considered admin)
Expand Down
74 changes: 74 additions & 0 deletions x/projects/client/cli/tx_del_keys.go
Original file line number Diff line number Diff line change
@@ -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 <admin-key> (the project's subscription address is also considered admin)
lavad tx project del-keys [project-id] [project-keys-file-path] --from <admin-key>
lavad tx project del-keys [project-id] --admin-key <other-admin-key> --admin-key <another-admin-key> --developer-key <developer-key> --from <admin-key>`,
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
}
3 changes: 3 additions & 0 deletions x/projects/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading

0 comments on commit 634da41

Please sign in to comment.