Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/Unlock objects on deletion lock object #1461

Merged
merged 13 commits into from
Jun 17, 2022
Merged
29 changes: 29 additions & 0 deletions cmd/neofs-cli/internal/client/sdk.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package internal

import (
"context"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"errors"
"fmt"

Expand Down Expand Up @@ -54,3 +57,29 @@ func GetSDKClient(key *ecdsa.PrivateKey, addr network.Address) (*client.Client,

return &c, nil
}

// GetCurrentEpoch returns current epoch.
func GetCurrentEpoch(ctx context.Context, endpoint string) (uint64, error) {
var addr network.Address

if err := addr.FromString(endpoint); err != nil {
return 0, fmt.Errorf("can't parse RPC endpoint: %w", err)
}

key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return 0, fmt.Errorf("can't generate key to sign query: %w", err)
}

c, err := GetSDKClient(key, addr)
if err != nil {
return 0, err
}

ni, err := c.NetworkInfo(ctx, client.PrmNetworkInfo{})
if err != nil {
return 0, err
}

return ni.Info().CurrentEpoch(), nil
}
28 changes: 28 additions & 0 deletions cmd/neofs-cli/internal/common/epoch.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package common

import (
"fmt"
"strconv"

"github.com/spf13/cobra"
)

// ParseEpoch parses epoch argument. Second return value is true if
// the specified epoch is relative, and false otherwise.
func ParseEpoch(cmd *cobra.Command, flag string) (uint64, bool, error) {
s, _ := cmd.Flags().GetString(flag)
if len(s) == 0 {
return 0, false, nil
}

relative := s[0] == '+'
if relative {
s = s[1:]
}

epoch, err := strconv.ParseUint(s, 10, 64)
if err != nil {
return 0, relative, fmt.Errorf("can't parse epoch for %s argument: %w", flag, err)
}
return epoch, relative, nil
}
67 changes: 8 additions & 59 deletions cmd/neofs-cli/modules/bearer/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,15 @@ package bearer

import (
"context"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"encoding/json"
"fmt"
"io/ioutil"
"strconv"
"time"

internalclient "github.com/nspcc-dev/neofs-node/cmd/neofs-cli/internal/client"
"github.com/nspcc-dev/neofs-node/cmd/neofs-cli/internal/common"
"github.com/nspcc-dev/neofs-node/cmd/neofs-cli/internal/commonflags"
"github.com/nspcc-dev/neofs-node/pkg/network"
"github.com/nspcc-dev/neofs-sdk-go/bearer"
"github.com/nspcc-dev/neofs-sdk-go/client"
eaclSDK "github.com/nspcc-dev/neofs-sdk-go/eacl"
"github.com/nspcc-dev/neofs-sdk-go/user"
"github.com/spf13/cobra"
Expand Down Expand Up @@ -63,21 +58,24 @@ func init() {
}

func createToken(cmd *cobra.Command, _ []string) error {
iat, iatRelative, err := parseEpoch(cmd, issuedAtFlag)
iat, iatRelative, err := common.ParseEpoch(cmd, issuedAtFlag)
if err != nil {
return err
}
exp, expRelative, err := parseEpoch(cmd, expireAtFlag)
exp, expRelative, err := common.ParseEpoch(cmd, expireAtFlag)
if err != nil {
return err
}
nvb, nvbRelative, err := parseEpoch(cmd, notValidBeforeFlag)
nvb, nvbRelative, err := common.ParseEpoch(cmd, notValidBeforeFlag)
if err != nil {
return err
}
if iatRelative || expRelative || nvbRelative {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
defer cancel()

endpoint, _ := cmd.Flags().GetString(commonflags.RPC)
currEpoch, err := getCurrentEpoch(endpoint)
currEpoch, err := internalclient.GetCurrentEpoch(ctx, endpoint)
if err != nil {
return err
}
Expand Down Expand Up @@ -140,52 +138,3 @@ func createToken(cmd *cobra.Command, _ []string) error {

return nil
}

// parseEpoch parses epoch argument. Second return value is true if
// the specified epoch is relative, and false otherwise.
func parseEpoch(cmd *cobra.Command, flag string) (uint64, bool, error) {
s, _ := cmd.Flags().GetString(flag)
if len(s) == 0 {
return 0, false, nil
}

relative := s[0] == '+'
if relative {
s = s[1:]
}

epoch, err := strconv.ParseUint(s, 10, 64)
if err != nil {
return 0, relative, fmt.Errorf("can't parse epoch for %s argument: %w", flag, err)
}
return epoch, relative, nil
}

// getCurrentEpoch returns current epoch.
func getCurrentEpoch(endpoint string) (uint64, error) {
var addr network.Address

if err := addr.FromString(endpoint); err != nil {
return 0, fmt.Errorf("can't parse RPC endpoint: %w", err)
}

key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return 0, fmt.Errorf("can't generate key to sign query: %w", err)
}

c, err := internalclient.GetSDKClient(key, addr)
if err != nil {
return 0, err
}

ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
defer cancel()

ni, err := c.NetworkInfo(ctx, client.PrmNetworkInfo{})
if err != nil {
return 0, err
}

return ni.Info().CurrentEpoch(), nil
}
37 changes: 33 additions & 4 deletions cmd/neofs-cli/modules/object/lock.go
Original file line number Diff line number Diff line change
@@ -1,20 +1,26 @@
package object

import (
"context"
"fmt"
"strconv"
"time"

objectV2 "github.com/nspcc-dev/neofs-api-go/v2/object"
internalclient "github.com/nspcc-dev/neofs-node/cmd/neofs-cli/internal/client"
"github.com/nspcc-dev/neofs-node/cmd/neofs-cli/internal/common"
"github.com/nspcc-dev/neofs-node/cmd/neofs-cli/internal/commonflags"
"github.com/nspcc-dev/neofs-node/cmd/neofs-cli/internal/key"
sessionCli "github.com/nspcc-dev/neofs-node/cmd/neofs-cli/modules/session"
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
"github.com/nspcc-dev/neofs-sdk-go/object"
objectSDK "github.com/nspcc-dev/neofs-sdk-go/object"
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
"github.com/nspcc-dev/neofs-sdk-go/user"
"github.com/spf13/cobra"
)

const lockExpiresOnFlag = "expires-on"

// object lock command.
var objectLockCmd = &cobra.Command{
Use: "lock CONTAINER OBJECT...",
Expand All @@ -41,13 +47,33 @@ var objectLockCmd = &cobra.Command{
var idOwner user.ID
user.IDFromKey(&idOwner, key.PublicKey)

var lock object.Lock
var lock objectSDK.Lock
lock.WriteMembers(lockList)

obj := object.New()
exp, relative, err := common.ParseEpoch(cmd, lockExpiresOnFlag)
common.ExitOnErr(cmd, "Parsing expiration epoch: %w", err)

if relative {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
defer cancel()

endpoint, _ := cmd.Flags().GetString(commonflags.RPC)

currEpoch, err := internalclient.GetCurrentEpoch(ctx, endpoint)
common.ExitOnErr(cmd, "Request current epoch: %w", err)

exp += currEpoch
}

var expirationAttr objectSDK.Attribute
expirationAttr.SetKey(objectV2.SysAttributeExpEpoch)
expirationAttr.SetValue(strconv.FormatUint(exp, 10))

obj := objectSDK.New()
obj.SetContainerID(cnr)
obj.SetOwnerID(&idOwner)
obj.SetType(object.TypeLock)
obj.SetType(objectSDK.TypeLock)
obj.SetAttributes(expirationAttr)
obj.SetPayload(lock.Marshal())

var prm internalclient.PutObjectPrm
Expand All @@ -66,4 +92,7 @@ var objectLockCmd = &cobra.Command{
func initCommandObjectLock() {
commonflags.Init(objectLockCmd)
commonflags.InitSession(objectLockCmd)

objectLockCmd.Flags().StringP(lockExpiresOnFlag, "e", "", "Lock expiration epoch")
_ = objectLockCmd.MarkFlagRequired(lockExpiresOnFlag)
}
1 change: 1 addition & 0 deletions cmd/neofs-node/control.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ func initControlService(c *cfg) {
controlSvc.WithDeletedObjectHandler(func(addrList []oid.Address) error {
var prm engine.DeletePrm
prm.WithAddresses(addrList...)
prm.WithForceRemoval()

_, err := c.cfgObject.cfgLocalStorage.localStorage.Delete(prm)

Expand Down
12 changes: 11 additions & 1 deletion pkg/core/object/fmt.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,9 +242,19 @@ func (v *FormatValidator) ValidateContent(o *object.Object) error {
return errors.New("missing ID")
}

// check that LOCK object has correct expiration epoch
lockExp, err := expirationEpochAttribute(o)
if err != nil {
return fmt.Errorf("lock object expiration epoch: %w", err)
}

if currEpoch := v.netState.CurrentEpoch(); lockExp <= currEpoch {
return fmt.Errorf("lock object expiration: %d; current: %d", lockExp, currEpoch)
}

var lock object.Lock

err := lock.Unmarshal(o.Payload())
err = lock.Unmarshal(o.Payload())
if err != nil {
return fmt.Errorf("decode lock payload: %w", err)
}
Expand Down
14 changes: 14 additions & 0 deletions pkg/local_object_storage/engine/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import (
// DeletePrm groups the parameters of Delete operation.
type DeletePrm struct {
addr []oid.Address

forceRemoval bool
}

// DeleteRes groups the resulting values of Delete operation.
Expand All @@ -25,6 +27,15 @@ func (p *DeletePrm) WithAddresses(addr ...oid.Address) {
}
}

// WithForceRemoval is a Delete option to remove an object despite any
// restrictions imposed on deleting that object. Expected to be used
// only in control service.
func (p *DeletePrm) WithForceRemoval() {
if p != nil {
p.forceRemoval = true
}
}

// Delete marks the objects to be removed.
//
// Returns an error if executions are blocked (see BlockExecution).
Expand Down Expand Up @@ -65,6 +76,9 @@ func (e *StorageEngine) delete(prm DeletePrm) (DeleteRes, error) {

var shPrm shard.InhumePrm
shPrm.MarkAsGarbage(prm.addr[i])
if prm.forceRemoval {
shPrm.ForceRemoval()
}

_, err = sh.Inhume(shPrm)
if err != nil {
Expand Down
Loading