package client import ( "context" "github.com/nspcc-dev/neofs-api-go/pkg/container" "github.com/nspcc-dev/neofs-api-go/pkg/refs" "github.com/nspcc-dev/neofs-api-go/util/signature" "github.com/nspcc-dev/neofs-api-go/v2/client" v2container "github.com/nspcc-dev/neofs-api-go/v2/container" v2refs "github.com/nspcc-dev/neofs-api-go/v2/refs" v2signature "github.com/nspcc-dev/neofs-api-go/v2/signature" "github.com/pkg/errors" ) func (c Client) PutContainer(ctx context.Context, cnr *container.Container, opts ...CallOption) (refs.ContainerID, error) { switch c.remoteNode.Version.Major { case 2: return c.putContainerV2(ctx, cnr, opts...) default: return refs.ContainerID{}, unsupportedProtocolErr } } func (c Client) GetContainer(ctx context.Context, id refs.ContainerID, opts ...CallOption) (*container.Container, error) { switch c.remoteNode.Version.Major { case 2: return c.getContainerV2(ctx, id, opts...) default: return nil, unsupportedProtocolErr } } func (c Client) ListContainers(ctx context.Context, owner refs.NEO3Wallet, opts ...CallOption) ([]refs.ContainerID, error) { switch c.remoteNode.Version.Major { case 2: return c.listContainerV2(ctx, owner, opts...) default: return nil, unsupportedProtocolErr } } func (c Client) ListSelfContainers(ctx context.Context, opts ...CallOption) ([]refs.ContainerID, error) { owner, err := refs.NEO3WalletFromPublicKey(&c.key.PublicKey) if err != nil { return nil, err } return c.ListContainers(ctx, owner, opts...) } func (c Client) DeleteContainer(ctx context.Context, id refs.ContainerID, opts ...CallOption) error { switch c.remoteNode.Version.Major { case 2: panic("not implemented") default: return unsupportedProtocolErr } } // todo: func (c Client) GetExtendedACL // todo: func (c Client) SetExtendedACL func (c Client) putContainerV2(ctx context.Context, cnr *container.Container, opts ...CallOption) (refs.ContainerID, error) { // apply all available options callOptions := defaultCallOptions() for i := range opts { opts[i].apply(&callOptions) } cid := refs.ContainerID{} // set transport version cnr.SetVersion(c.remoteNode.Version.ToV2Version()) // if container owner is not set, then use client key as owner if cnr.GetOwnerID() == nil { owner, err := refs.NEO3WalletFromPublicKey(&c.key.PublicKey) if err != nil { return cid, err } v2Owner := new(v2refs.OwnerID) v2Owner.SetValue(owner[:]) cnr.SetOwnerID(v2Owner) } reqBody := new(v2container.PutRequestBody) reqBody.SetContainer(&cnr.Container) // sign container signWrapper := v2signature.StableMarshalerWrapper{SM: reqBody.GetContainer()} err := signature.SignDataWithHandler(c.key, signWrapper, func(key []byte, sig []byte) { containerSignature := new(v2refs.Signature) containerSignature.SetKey(key) containerSignature.SetSign(sig) reqBody.SetSignature(containerSignature) }, signature.SignWithRFC6979()) if err != nil { return cid, err } req := new(v2container.PutRequest) req.SetBody(reqBody) req.SetMetaHeader(v2MetaHeaderFromOpts(callOptions)) err = v2signature.SignServiceMessage(c.key, req) if err != nil { return cid, err } switch c.remoteNode.Protocol { case GRPC: cli, err := v2ContainerClientFromOptions(c.opts) if err != nil { return cid, errors.Wrap(err, "can't create grpc client") } resp, err := cli.Put(ctx, req) if err != nil { return cid, errors.Wrap(err, "transport error") } err = v2signature.VerifyServiceMessage(resp) if err != nil { return cid, errors.Wrap(err, "can't verify response message") } copy(cid[:], resp.GetBody().GetContainerID().GetValue()) return cid, nil default: return cid, unsupportedProtocolErr } } func (c Client) getContainerV2(ctx context.Context, id refs.ContainerID, opts ...CallOption) (*container.Container, error) { // apply all available options callOptions := defaultCallOptions() for i := range opts { opts[i].apply(&callOptions) } cid := new(v2refs.ContainerID) cid.SetValue(id[:]) reqBody := new(v2container.GetRequestBody) reqBody.SetContainerID(cid) req := new(v2container.GetRequest) req.SetBody(reqBody) req.SetMetaHeader(v2MetaHeaderFromOpts(callOptions)) err := v2signature.SignServiceMessage(c.key, req) if err != nil { return nil, err } switch c.remoteNode.Protocol { case GRPC: cli, err := v2ContainerClientFromOptions(c.opts) if err != nil { return nil, errors.Wrap(err, "can't create grpc client") } resp, err := cli.Get(ctx, req) if err != nil { return nil, errors.Wrap(err, "transport error") } err = v2signature.VerifyServiceMessage(resp) if err != nil { return nil, errors.Wrap(err, "can't verify response message") } return &container.Container{ Container: *resp.GetBody().GetContainer(), }, nil default: return nil, unsupportedProtocolErr } } func (c Client) listContainerV2(ctx context.Context, owner refs.NEO3Wallet, opts ...CallOption) ([]refs.ContainerID, error) { // apply all available options callOptions := defaultCallOptions() for i := range opts { opts[i].apply(&callOptions) } v2owner := new(v2refs.OwnerID) v2owner.SetValue(owner[:]) reqBody := new(v2container.ListRequestBody) reqBody.SetOwnerID(v2owner) req := new(v2container.ListRequest) req.SetBody(reqBody) req.SetMetaHeader(v2MetaHeaderFromOpts(callOptions)) err := v2signature.SignServiceMessage(c.key, req) if err != nil { return nil, err } switch c.remoteNode.Protocol { case GRPC: cli, err := v2ContainerClientFromOptions(c.opts) if err != nil { return nil, errors.Wrap(err, "can't create grpc client") } resp, err := cli.List(ctx, req) if err != nil { return nil, errors.Wrap(err, "transport error") } err = v2signature.VerifyServiceMessage(resp) if err != nil { return nil, errors.Wrap(err, "can't verify response message") } result := make([]refs.ContainerID, len(resp.GetBody().GetContainerIDs())) for i, cid := range resp.GetBody().GetContainerIDs() { copy(result[i][:], cid.GetValue()) } return result, nil default: return nil, unsupportedProtocolErr } } func (c Client) delContainerV2(ctx context.Context, id refs.ContainerID, opts ...CallOption) error { // apply all available options callOptions := defaultCallOptions() for i := range opts { opts[i].apply(&callOptions) } cid := new(v2refs.ContainerID) cid.SetValue(id[:]) reqBody := new(v2container.DeleteRequestBody) reqBody.SetContainerID(cid) // sign container signWrapper := v2signature.StableMarshalerWrapper{SM: reqBody.GetContainerID()} err := signature.SignDataWithHandler(c.key, signWrapper, func(key []byte, sig []byte) { containerSignature := new(v2refs.Signature) containerSignature.SetKey(key) containerSignature.SetSign(sig) reqBody.SetSignature(containerSignature) }, signature.SignWithRFC6979()) if err != nil { return err } req := new(v2container.DeleteRequest) req.SetBody(reqBody) req.SetMetaHeader(v2MetaHeaderFromOpts(callOptions)) err = v2signature.SignServiceMessage(c.key, req) if err != nil { return err } switch c.remoteNode.Protocol { case GRPC: cli, err := v2ContainerClientFromOptions(c.opts) if err != nil { return errors.Wrap(err, "can't create grpc client") } resp, err := cli.Delete(ctx, req) if err != nil { return errors.Wrap(err, "transport error") } err = v2signature.VerifyServiceMessage(resp) if err != nil { return errors.Wrap(err, "can't verify response message") } return nil default: return unsupportedProtocolErr } } func v2ContainerClientFromOptions(opts *clientOptions) (cli *v2container.Client, err error) { switch { case opts.grpcOpts.v2ContainerClient != nil: // return value from client cache return opts.grpcOpts.v2ContainerClient, nil case opts.grpcOpts.conn != nil: cli, err = v2container.NewClient(v2container.WithGlobalOpts( client.WithGRPCConn(opts.grpcOpts.conn)), ) case opts.addr != "": cli, err = v2container.NewClient(v2container.WithGlobalOpts( client.WithNetworkAddress(opts.addr)), ) default: return nil, errors.New("lack of sdk client options to create accounting client") } // check if client correct and save in cache if err != nil { return nil, err } opts.grpcOpts.v2ContainerClient = cli return cli, nil }