frostfs-node/pkg/innerring/processors/container/process_container.go
Evgenii Stratonikov 003d568ae2
[#1628] innerring: Relax container homomorphic hashing check
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>
2025-02-27 10:20:08 +03:00

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
}