Skip to content

Commit

Permalink
CM-254 Implement TX Rename
Browse files Browse the repository at this point in the history
  • Loading branch information
rnistuk committed Mar 4, 2020
1 parent 6c4a586 commit 5ad3b23
Show file tree
Hide file tree
Showing 11 changed files with 299 additions and 0 deletions.
21 changes: 21 additions & 0 deletions x/crud/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ func GetTxCmd(_ string, cdc *codec.Codec) *cobra.Command {
GetCmdBLZDelete(cdc),
GetCmdBLZKeys(cdc),
GetCmdBLZHas(cdc),
GetCmdBLZRename(cdc),
)...)

return crudTxCmd
Expand Down Expand Up @@ -164,3 +165,23 @@ func GetCmdBLZHas(cdc *codec.Codec) *cobra.Command {
},
}
}

func GetCmdBLZRename(cdc *codec.Codec) *cobra.Command {
return &cobra.Command{
Use: "rename [UUID] [key] [new key]",
Short: "rename an existing entry in the database",
Args: cobra.ExactArgs(3),
RunE: func(cmd *cobra.Command, args []string) error {
cliCtx := context.NewCLIContext().WithCodec(cdc)
txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc))
msg := types.NewMsgBLZRename(args[0], args[1], args[2], cliCtx.GetFromAddress())

err := msg.ValidateBasic()
if err != nil {
return err
}

return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg})
},
}
}
1 change: 1 addition & 0 deletions x/crud/client/rest/rest.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,5 @@ func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, storeName string)
r.HandleFunc(fmt.Sprintf("/%s/delete", storeName), BlzDeleteHandler(cliCtx)).Methods("DELETE")
r.HandleFunc(fmt.Sprintf("/%s/has", storeName), BlzHasHandler(cliCtx)).Methods("POST")
r.HandleFunc(fmt.Sprintf("/%s/keys", storeName), BlzKeysHandler(cliCtx)).Methods("POST")
r.HandleFunc(fmt.Sprintf("/%s/rename", storeName), BlzRenameHandler(cliCtx)).Methods("POST")
}
41 changes: 41 additions & 0 deletions x/crud/client/rest/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,3 +264,44 @@ func BlzKeysHandler(cliCtx context.CLIContext) http.HandlerFunc {
utils.WriteGenerateStdTxResponse(w, cliCtx, baseReq, []sdk.Msg{msg})
}
}

///////////////////////////////////////////////////////////////////////////////
// Rename
type renameReq struct {
BaseReq rest.BaseReq
UUID string
Key string
NewKey string
Owner string
}

func BlzRenameHandler(cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req renameReq

if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) {
rest.WriteErrorResponse(w, http.StatusBadRequest, "failed to parse request")
return
}

baseReq := req.BaseReq.Sanitize()
if !baseReq.ValidateBasic(w) {
return
}

addr, err := sdk.AccAddressFromBech32(req.Owner)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}

msg := types.NewMsgBLZRename(req.UUID, req.Key, req.NewKey, addr)
err = msg.ValidateBasic()
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}

utils.WriteGenerateStdTxResponse(w, cliCtx, baseReq, []sdk.Msg{msg})
}
}
23 changes: 23 additions & 0 deletions x/crud/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ func NewHandler(keeper keeper.IKeeper) sdk.Handler {
return handleMsgBLZKeys(ctx, keeper, msg)
case types.MsgBLZHas:
return handleMsgBLZHas(ctx, keeper, msg)
case types.MsgBLZRename:
return handleMsgBLZRename(ctx, keeper, msg)
default:
return sdk.ErrUnknownRequest(fmt.Sprintf("Unrecognized crud msg type: %v", msg.Type())).Result()
}
Expand Down Expand Up @@ -142,3 +144,24 @@ func handleMsgBLZHas(ctx sdk.Context, keeper keeper.IKeeper, msg types.MsgBLZHas

return sdk.Result{Data: json_data}
}

func handleMsgBLZRename(ctx sdk.Context, keeper keeper.IKeeper, msg types.MsgBLZRename) sdk.Result {
if len(msg.UUID) == 0 || len(msg.Key) == 0 || len(msg.NewKey) ==0 || msg.Owner.Empty() {
return sdk.ErrInternal("Invalid message").Result()
}

owner := keeper.GetOwner(ctx, keeper.GetKVStore(ctx), msg.UUID, msg.Key)
if owner.Empty() {
return sdk.ErrInternal("Key does not exist").Result()
}

if !msg.Owner.Equals(owner) {
return sdk.ErrUnauthorized("Incorrect Owner").Result()
}

if !keeper.RenameBLZKey(ctx, keeper.GetKVStore(ctx), msg.UUID, msg.Key, msg.NewKey) {
return sdk.ErrInternal("Rename failed").Result()
}

return sdk.Result{}
}
59 changes: 59 additions & 0 deletions x/crud/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -325,3 +325,62 @@ func Test_handleMsgBLZHas(t *testing.T) {
assert.False(t, result.IsOK())
}
}

func Test_handleMsgBLZRename(t *testing.T) {
// check for new key
// check for new key size
// ensure the owners match
//
mockCtrl, mockKeeper, ctx, owner := initTest(t)
defer mockCtrl.Finish()

// Simple Rename test
{
renameMsg := types.MsgBLZRename{
UUID: "uuid",
Key: "key",
NewKey: "newkey",
Owner: owner,
}
assert.Equal(t, renameMsg.Type(), "rename")

// always return nil for a store...
mockKeeper.EXPECT().GetKVStore(ctx).AnyTimes().Return(nil)
mockKeeper.EXPECT().GetOwner(ctx, nil, renameMsg.UUID, renameMsg.Key).Return(owner)
mockKeeper.EXPECT().RenameBLZKey(ctx, gomock.Any(), renameMsg.UUID, renameMsg.Key, renameMsg.NewKey).Return(true)

result := NewHandler(mockKeeper)(ctx, renameMsg)
assert.True(t, result.IsOK())

mockKeeper.EXPECT().GetOwner(ctx, nil, renameMsg.UUID, renameMsg.Key).Return(owner)
renameMsg.Owner = []byte("bluzelle1nnpyp9wr6law2u5jwa23t0ywtmrduldf6h4wqr")
result = handleMsgBLZRename(ctx, mockKeeper, renameMsg)
assert.False(t, result.IsOK())

mockKeeper.EXPECT().GetOwner(ctx, nil, renameMsg.UUID, renameMsg.Key)
result = handleMsgBLZRename(ctx, mockKeeper, renameMsg)
assert.False(t, result.IsOK())

// Rename failed
mockKeeper.EXPECT().GetKVStore(ctx).AnyTimes().Return(nil)
mockKeeper.EXPECT().GetOwner(ctx, nil, renameMsg.UUID, renameMsg.Key).Return(renameMsg.Owner)
mockKeeper.EXPECT().RenameBLZKey(ctx, gomock.Any(), renameMsg.UUID, renameMsg.Key, renameMsg.NewKey).Return(false)

result = NewHandler(mockKeeper)(ctx, renameMsg)
assert.False(t, result.IsOK())
assert.Equal(t, "{\"codespace\":\"sdk\",\"code\":1,\"message\":\"Rename failed\"}", result.Log)
}

// Test for empty message parameters
{

result := handleMsgBLZRename(ctx, mockKeeper, types.MsgBLZRename{})
assert.False(t, result.IsOK())

result = handleMsgBLZRename(ctx, mockKeeper, types.MsgBLZRename{UUID: "uuid", Key: "key"})
assert.False(t, result.IsOK())

result = handleMsgBLZRename(ctx, mockKeeper, types.MsgBLZRename{UUID: "uuid", Key: "key", NewKey: "newkey"})
assert.False(t, result.IsOK())
}
}
19 changes: 19 additions & 0 deletions x/crud/internal/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ type IKeeper interface {
GetKeys(ctx sdk.Context, store sdk.KVStore, UUID string) types.QueryResultKeys
GetOwner(ctx sdk.Context, store sdk.KVStore, UUID string, key string) sdk.AccAddress
GetKVStore(ctx sdk.Context) sdk.KVStore
RenameBLZKey(ctx sdk.Context, store sdk.KVStore, UUID string, key string, newkey string) bool
}

type Keeper struct {
Expand Down Expand Up @@ -106,3 +107,21 @@ func (k Keeper) GetKeys(ctx sdk.Context, store sdk.KVStore, UUID string) types.Q
func (k Keeper) GetOwner(ctx sdk.Context, store sdk.KVStore, UUID string, key string) sdk.AccAddress {
return k.GetBLZValue(ctx, store, UUID, key).Owner
}

func (k Keeper) RenameBLZKey(ctx sdk.Context, store sdk.KVStore, UUID string, key string, newKey string) bool {
BLZNewKey := MakeMetaKey(UUID, newKey)
if k.isUUIDKeyPresent(ctx, store, BLZNewKey) {
return false
}

value := k.GetBLZValue(ctx, store, UUID, key)

if value.Owner.Empty() {
return false
}

k.SetBLZValue(ctx, store, UUID, newKey, value)
k.DeleteBLZValue(ctx, store, UUID, key)

return true
}
20 changes: 20 additions & 0 deletions x/crud/internal/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,3 +175,23 @@ func TestKeeper_GetOwner(t *testing.T) {
assert.True(t, reflect.DeepEqual([]byte(actual), owner))
assert.Empty(t, keeper.GetOwner(ctx, testStore, "notauuid", "notakey"))
}

func TestKeeper_RenameBLZKey(t *testing.T) {
ctx, testStore, owner, cdc := initKeeperTest(t)

keeper := NewKeeper(nil, nil, cdc)

keeper.SetBLZValue(ctx, testStore, "uuid", "key", types.BLZValue{
Value: "a value",
Owner: owner,
})

assert.False(t, keeper.RenameBLZKey(ctx, testStore, "uuid", "badkey", "newkey"))

assert.True(t, keeper.RenameBLZKey(ctx, testStore, "uuid", "key", "newkey"))

assert.True(t, reflect.DeepEqual(keeper.GetBLZValue(ctx,testStore, "uuid", "newkey"), types.BLZValue{
Value: "a value",
Owner: owner,
}))
}
1 change: 1 addition & 0 deletions x/crud/internal/types/codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,5 @@ func RegisterCodec(cdc *codec.Codec) {
cdc.RegisterConcrete(MsgBLZDelete{}, "crud/delete", nil)
cdc.RegisterConcrete(MsgBLZKeys{}, "crud/keys", nil)
cdc.RegisterConcrete(MsgBLZHas{}, "crud/has", nil)
cdc.RegisterConcrete(MsgBLZRename{}, "crud/rename", nil)
}
47 changes: 47 additions & 0 deletions x/crud/internal/types/msgs.go
Original file line number Diff line number Diff line change
Expand Up @@ -263,3 +263,50 @@ func (msg MsgBLZHas) GetSignBytes() []byte {
func (msg MsgBLZHas) GetSigners() []sdk.AccAddress {
return []sdk.AccAddress{msg.Owner}
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Rename
type MsgBLZRename struct {
UUID string
Key string
NewKey string
Owner sdk.AccAddress
}

func NewMsgBLZRename(uuid string, key string, newKey string, owner sdk.AccAddress) MsgBLZRename {
return MsgBLZRename{UUID: uuid, Key: key, NewKey: newKey, Owner: owner}
}

func (msg MsgBLZRename) Route() string { return "crud" }

func (msg MsgBLZRename) Type() string { return "rename" }

func (msg MsgBLZRename) ValidateBasic() sdk.Error {
if msg.Owner.Empty() {
return sdk.ErrInvalidAddress(msg.Owner.String())
}

if len(msg.UUID) == 0 {
return sdk.ErrInvalidPubKey("UUID empty")
}

if len(msg.Key) == 0 {
return sdk.ErrInvalidPubKey("key empty")
}

if len(msg.NewKey) == 0 {
return sdk.ErrInvalidPubKey("new key empty")
}

if len(msg.UUID)+len(msg.NewKey) > MaxKeySize {
return sdk.ErrInternal("UUID+NewKey too large")
}

return nil
}

func (msg MsgBLZRename) GetSignBytes() []byte {
return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(msg))
}

func (msg MsgBLZRename) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{msg.Owner} }
53 changes: 53 additions & 0 deletions x/crud/internal/types/msgs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -313,3 +313,56 @@ func TestMsgBLZHas_GetSigners(t *testing.T) {
msg := NewMsgBLZHas("uuid", "key", []byte("bluzelle1t0ywtmrduldf6h4wqrnnpyp9wr6law2u5jwa23"))
assert.Equal(t, msg.GetSigners(), []sdk.AccAddress{msg.Owner})
}

/////////////////////////////////////////////////////////////////////////////////
func TestNewMsgBLZRename(t *testing.T) {
owner := []byte("bluzelle1t0ywtmrduldf6h4wqrnnpyp9wr6law2u5jwa23")
accepted := MsgBLZRename{UUID: "uuid", Key: "key", NewKey: "newkey", Owner: owner}

sut := NewMsgBLZRename(accepted.UUID, accepted.Key, accepted.NewKey, owner)
assert.IsType(t, MsgBLZRename{}, sut)

assert.True(t, reflect.DeepEqual(accepted, sut))
}

func TestMsgBLZRename_Route(t *testing.T) {
assert.Equal(t, "crud", MsgBLZRename{}.Route())
}

func TestMsgBLZRename_Type(t *testing.T) {
assert.Equal(t, "rename", MsgBLZRename{}.Type())
}

func TestMsgBLZRename_ValidateBasic(t *testing.T) {
sut := NewMsgBLZRename("uuid", "key", "newkey", []byte(""))
assert.Equal(t, sdk.ErrInvalidAddress(sut.Owner.String()), sut.ValidateBasic())

sut.Owner = []byte("bluzelle1t0ywtmrduldf6h4wqrnnpyp9wr6law2u5jwa23")
assert.Nil(t, sut.ValidateBasic())

sut.UUID = ""
assert.Equal(t, sdk.ErrInvalidPubKey("UUID empty"), sut.ValidateBasic())

sut.UUID = "uuid"
sut.Key = ""
assert.Equal(t, sdk.ErrInvalidPubKey("key empty"), sut.ValidateBasic())

sut.Key = "key"
sut.NewKey = ""
assert.Equal(t, sdk.ErrInvalidPubKey("new key empty"), sut.ValidateBasic())

sut.Key = "Key"
sut.NewKey = string(make([]byte, MaxKeySize+1))
assert.Equal(t, sdk.ErrInternal("UUID+NewKey too large"), sut.ValidateBasic())
}

func TestMsgBLZRename_GetSignBytes(t *testing.T) {
sut := NewMsgBLZRename("uuid", "key", "newkey", []byte("bluzelle1t0ywtmrduldf6h4wqrnnpyp9wr6law2u5jwa23"))
exp := "{\"Key\":\"key\",\"NewKey\":\"newkey\",\"Owner\":\"cosmos1vfk827n9d3kx2vt5xpuhwardwfj82mryvcmxsdrhw9exumns09crjamjxekxzaejw56k5ampxgeslhg4h3\",\"UUID\":\"uuid\"}"
assert.Equal(t, exp, string(sut.GetSignBytes()))
}

func TestMsgBLZRename_GetSigners(t *testing.T) {
msg := NewMsgBLZRename("uuid", "key", "newkey", []byte("bluzelle1t0ywtmrduldf6h4wqrnnpyp9wr6law2u5jwa23"))
assert.Equal(t, []sdk.AccAddress{msg.Owner}, msg.GetSigners())
}
14 changes: 14 additions & 0 deletions x/crud/mocks/mock_keeper.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 5ad3b23

Please sign in to comment.