frostfs-node/pkg/services/object_manager/transformer/fmt.go
Leonard Lyubich ec21040542 [#264] transformer: Finalize parent header once
In previous implementation parent object header finalized twice in size
limiter + formatter. On the one hand, this added redundant action, on the
other hand, it could provoke a difference in the headers of the linking and
the last part. Change formatter to finalize parent header if it does not
container the signature. Change size limiter to reuse parent header after
last child finalization in linking child.

Signed-off-by: Leonard Lyubich <leonard@nspcc.ru>
2020-12-18 16:42:17 +03:00

109 lines
2.5 KiB
Go

package transformer
import (
"crypto/ecdsa"
"github.com/nspcc-dev/neofs-api-go/pkg"
objectSDK "github.com/nspcc-dev/neofs-api-go/pkg/object"
"github.com/nspcc-dev/neofs-api-go/pkg/token"
"github.com/nspcc-dev/neofs-node/pkg/core/netmap"
"github.com/nspcc-dev/neofs-node/pkg/core/object"
"github.com/pkg/errors"
)
type formatter struct {
prm *FormatterParams
obj *object.RawObject
sz uint64
}
// FormatterParams groups NewFormatTarget parameters.
type FormatterParams struct {
Key *ecdsa.PrivateKey
NextTarget ObjectTarget
SessionToken *token.SessionToken
NetworkState netmap.State
}
// NewFormatTarget returns ObjectTarget instance that finalizes object structure
// and writes it to the next target.
//
// Chunks must be written before the WriteHeader call.
//
// Object changes:
// - sets version to current SDK version;
// - sets payload size to the total length of all written chunks;
// - sets session token;
// - sets number of creation epoch;
// - calculates and sets verification fields (ID, Signature).
func NewFormatTarget(p *FormatterParams) ObjectTarget {
return &formatter{
prm: p,
}
}
func (f *formatter) WriteHeader(obj *object.RawObject) error {
f.obj = obj
return nil
}
func (f *formatter) Write(p []byte) (n int, err error) {
n, err = f.prm.NextTarget.Write(p)
f.sz += uint64(n)
return
}
func (f *formatter) Close() (*AccessIdentifiers, error) {
curEpoch := f.prm.NetworkState.CurrentEpoch()
f.obj.SetVersion(pkg.SDKVersion())
f.obj.SetPayloadSize(f.sz)
f.obj.SetSessionToken(f.prm.SessionToken)
f.obj.SetCreationEpoch(curEpoch)
var (
parID *objectSDK.ID
parHdr *objectSDK.Object
)
if par := f.obj.Parent(); par != nil && par.Signature() == nil {
rawPar := objectSDK.NewRawFromV2(par.ToV2())
rawPar.SetSessionToken(f.prm.SessionToken)
rawPar.SetCreationEpoch(curEpoch)
if err := objectSDK.SetIDWithSignature(f.prm.Key, rawPar); err != nil {
return nil, errors.Wrap(err, "could not finalize parent object")
}
parID = rawPar.ID()
parHdr = rawPar.Object()
f.obj.SetParent(parHdr)
}
if err := objectSDK.SetIDWithSignature(f.prm.Key, f.obj.SDK()); err != nil {
return nil, errors.Wrap(err, "could not finalize object")
}
if err := f.prm.NextTarget.WriteHeader(f.obj); err != nil {
return nil, errors.Wrap(err, "could not write header to next target")
}
if _, err := f.prm.NextTarget.Close(); err != nil {
return nil, errors.Wrap(err, "could not close next target")
}
return new(AccessIdentifiers).
WithSelfID(f.obj.ID()).
WithParentID(parID).
WithParent(parHdr), nil
}