forked from TrueCloudLab/frostfs-node
Our initial desire was to prohibit using homomorphic hashing on the network level because of the resource consumption. However, the ability to use it, doesn't mean that we must. So only fail validation if container wants to have homomorphic hashing, but the network prohibits it. Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
217 lines
5.7 KiB
Go
217 lines
5.7 KiB
Go
package container
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event"
|
|
containerEvent "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event/container"
|
|
containerSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
|
|
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session"
|
|
"github.com/nspcc-dev/neo-go/pkg/network/payload"
|
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
// putEvent is a common interface of Put and PutNamed event.
|
|
type putEvent interface {
|
|
event.Event
|
|
Container() []byte
|
|
PublicKey() []byte
|
|
Signature() []byte
|
|
SessionToken() []byte
|
|
NotaryRequest() *payload.P2PNotaryRequest
|
|
}
|
|
|
|
type putContainerContext struct {
|
|
e putEvent
|
|
|
|
d containerSDK.Domain
|
|
}
|
|
|
|
var errContainerAndOwnerNamespaceDontMatch = errors.New("container and owner namespaces do not match")
|
|
|
|
// Process a new container from the user by checking the container sanity
|
|
// and sending approve tx back to the morph.
|
|
func (cp *Processor) processContainerPut(ctx context.Context, put putEvent) bool {
|
|
if !cp.alphabetState.IsAlphabet(ctx) {
|
|
cp.log.Info(ctx, logs.ContainerNonAlphabetModeIgnoreContainerPut)
|
|
return true
|
|
}
|
|
|
|
pctx := &putContainerContext{
|
|
e: put,
|
|
}
|
|
|
|
err := cp.checkPutContainer(ctx, pctx)
|
|
if err != nil {
|
|
cp.log.Error(ctx, logs.ContainerPutContainerCheckFailed,
|
|
zap.Error(err),
|
|
)
|
|
|
|
return false
|
|
}
|
|
|
|
if err := cp.morphClient.NotarySignAndInvokeTX(pctx.e.NotaryRequest().MainTransaction); err != nil {
|
|
cp.log.Error(ctx, logs.ContainerCouldNotApprovePutContainer,
|
|
zap.Error(err),
|
|
)
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func (cp *Processor) checkPutContainer(ctx context.Context, pctx *putContainerContext) error {
|
|
binCnr := pctx.e.Container()
|
|
var cnr containerSDK.Container
|
|
|
|
err := cnr.Unmarshal(binCnr)
|
|
if err != nil {
|
|
return fmt.Errorf("invalid binary container: %w", err)
|
|
}
|
|
|
|
err = cp.verifySignature(ctx, signatureVerificationData{
|
|
ownerContainer: cnr.Owner(),
|
|
verb: session.VerbContainerPut,
|
|
binTokenSession: pctx.e.SessionToken(),
|
|
binPublicKey: pctx.e.PublicKey(),
|
|
signature: pctx.e.Signature(),
|
|
signedData: binCnr,
|
|
})
|
|
if err != nil {
|
|
return fmt.Errorf("auth container creation: %w", err)
|
|
}
|
|
|
|
// check homomorphic hashing setting
|
|
err = checkHomomorphicHashing(ctx, cp.netState, cnr)
|
|
if err != nil {
|
|
return fmt.Errorf("incorrect homomorphic hashing setting: %w", err)
|
|
}
|
|
|
|
// check native name and zone
|
|
err = cp.checkNNS(ctx, pctx, cnr)
|
|
if err != nil {
|
|
return fmt.Errorf("NNS: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Process delete container operation from the user by checking container sanity
|
|
// and sending approve tx back to morph.
|
|
func (cp *Processor) processContainerDelete(ctx context.Context, e containerEvent.Delete) bool {
|
|
if !cp.alphabetState.IsAlphabet(ctx) {
|
|
cp.log.Info(ctx, logs.ContainerNonAlphabetModeIgnoreContainerDelete)
|
|
return true
|
|
}
|
|
|
|
err := cp.checkDeleteContainer(ctx, e)
|
|
if err != nil {
|
|
cp.log.Error(ctx, logs.ContainerDeleteContainerCheckFailed,
|
|
zap.Error(err),
|
|
)
|
|
|
|
return false
|
|
}
|
|
|
|
if err := cp.morphClient.NotarySignAndInvokeTX(e.NotaryRequest().MainTransaction); err != nil {
|
|
cp.log.Error(ctx, logs.ContainerCouldNotApproveDeleteContainer,
|
|
zap.Error(err),
|
|
)
|
|
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func (cp *Processor) checkDeleteContainer(ctx context.Context, e containerEvent.Delete) error {
|
|
binCnr := e.ContainerID()
|
|
|
|
var idCnr cid.ID
|
|
|
|
err := idCnr.Decode(binCnr)
|
|
if err != nil {
|
|
return fmt.Errorf("invalid container ID: %w", err)
|
|
}
|
|
|
|
// receive owner of the related container
|
|
cnr, err := cp.cnrClient.Get(ctx, binCnr)
|
|
if err != nil {
|
|
return fmt.Errorf("could not receive the container: %w", err)
|
|
}
|
|
|
|
err = cp.verifySignature(ctx, signatureVerificationData{
|
|
ownerContainer: cnr.Value.Owner(),
|
|
verb: session.VerbContainerDelete,
|
|
idContainerSet: true,
|
|
idContainer: idCnr,
|
|
binTokenSession: e.SessionToken(),
|
|
signature: e.Signature(),
|
|
signedData: binCnr,
|
|
binPublicKey: e.PublicKeyValue,
|
|
})
|
|
if err != nil {
|
|
return fmt.Errorf("auth container removal: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (cp *Processor) checkNNS(ctx context.Context, pctx *putContainerContext, cnr containerSDK.Container) error {
|
|
// fetch domain info
|
|
pctx.d = containerSDK.ReadDomain(cnr)
|
|
|
|
// if PutNamed event => check if values in container correspond to args
|
|
if named, ok := pctx.e.(interface {
|
|
Name() string
|
|
Zone() string
|
|
}); ok {
|
|
if name := named.Name(); name != pctx.d.Name() {
|
|
return fmt.Errorf("names differ %s/%s", name, pctx.d.Name())
|
|
}
|
|
|
|
if zone := named.Zone(); zone != pctx.d.Zone() {
|
|
return fmt.Errorf("zones differ %s/%s", zone, pctx.d.Zone())
|
|
}
|
|
}
|
|
|
|
addr, err := util.Uint160DecodeBytesBE(cnr.Owner().WalletBytes()[1 : 1+util.Uint160Size])
|
|
if err != nil {
|
|
return fmt.Errorf("could not get container owner address: %w", err)
|
|
}
|
|
|
|
subject, err := cp.frostFSIDClient.GetSubject(ctx, addr)
|
|
if err != nil {
|
|
return fmt.Errorf("could not get subject from FrostfsID contract: %w", err)
|
|
}
|
|
|
|
namespace, hasNamespace := strings.CutSuffix(pctx.d.Zone(), ".ns")
|
|
if !hasNamespace {
|
|
return nil
|
|
}
|
|
|
|
if subject.Namespace != namespace {
|
|
return errContainerAndOwnerNamespaceDontMatch
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func checkHomomorphicHashing(ctx context.Context, ns NetworkState, cnr containerSDK.Container) error {
|
|
netSetting, err := ns.HomomorphicHashDisabled(ctx)
|
|
if err != nil {
|
|
return fmt.Errorf("could not get setting in contract: %w", err)
|
|
}
|
|
|
|
if cnrSetting := containerSDK.IsHomomorphicHashingDisabled(cnr); netSetting && !cnrSetting {
|
|
return fmt.Errorf("network setting: %t, container setting: %t", netSetting, cnrSetting)
|
|
}
|
|
|
|
return nil
|
|
}
|