267506bb20
metabase has become a central concept and it's more suitable for it to be directly nested under satellite rather than being part of metainfo. metainfo is going to be the "endpoint" logic for handling requests. Change-Id: I53770d6761ac1e9a1283b5aa68f471b21e784198
197 lines
6.8 KiB
Go
197 lines
6.8 KiB
Go
// Copyright (C) 2020 Storj Labs, Inc.
|
|
// See LICENSE for copying information.
|
|
|
|
package orders
|
|
|
|
import (
|
|
"context"
|
|
"crypto/rand"
|
|
"encoding/binary"
|
|
"time"
|
|
|
|
"github.com/zeebo/errs"
|
|
|
|
"storj.io/common/pb"
|
|
"storj.io/common/signing"
|
|
"storj.io/common/storj"
|
|
"storj.io/storj/satellite/internalpb"
|
|
"storj.io/storj/satellite/metabase"
|
|
)
|
|
|
|
// ErrSigner is default error class for Signer.
|
|
var ErrSigner = errs.Class("signer")
|
|
|
|
// Signer implements signing of order limits.
|
|
type Signer struct {
|
|
// TODO: should this be a ref to the necessary pieces instead of the service?
|
|
Service *Service
|
|
|
|
Bucket metabase.BucketLocation
|
|
|
|
// TODO: use a Template pb.OrderLimit here?
|
|
RootPieceID storj.PieceID
|
|
|
|
PieceExpiration time.Time
|
|
OrderCreation time.Time
|
|
OrderExpiration time.Time
|
|
|
|
PublicKey storj.PiecePublicKey
|
|
PrivateKey storj.PiecePrivateKey
|
|
|
|
Serial storj.SerialNumber
|
|
Action pb.PieceAction
|
|
Limit int64
|
|
|
|
EncryptedMetadataKeyID []byte
|
|
EncryptedMetadata []byte
|
|
|
|
AddressedLimits []*pb.AddressedOrderLimit
|
|
}
|
|
|
|
// createSerial creates a timestamped serial number.
|
|
func createSerial(orderExpiration time.Time) (_ storj.SerialNumber, err error) {
|
|
var serial storj.SerialNumber
|
|
|
|
binary.BigEndian.PutUint64(serial[0:8], uint64(orderExpiration.Unix()))
|
|
_, err = rand.Read(serial[8:])
|
|
if err != nil {
|
|
return storj.SerialNumber{}, ErrSigner.Wrap(err)
|
|
}
|
|
|
|
return serial, nil
|
|
}
|
|
|
|
// NewSigner creates an order limit signer.
|
|
func NewSigner(service *Service, rootPieceID storj.PieceID, pieceExpiration time.Time, orderCreation time.Time, limit int64, action pb.PieceAction, bucket metabase.BucketLocation) (*Signer, error) {
|
|
signer := &Signer{}
|
|
signer.Service = service
|
|
|
|
signer.Bucket = bucket
|
|
|
|
signer.RootPieceID = rootPieceID
|
|
|
|
signer.PieceExpiration = pieceExpiration
|
|
signer.OrderCreation = orderCreation
|
|
signer.OrderExpiration = orderCreation.Add(service.orderExpiration)
|
|
|
|
var err error
|
|
signer.PublicKey, signer.PrivateKey, err = storj.NewPieceKey()
|
|
if err != nil {
|
|
return nil, ErrSigner.Wrap(err)
|
|
}
|
|
|
|
signer.Serial, err = createSerial(signer.OrderExpiration)
|
|
if err != nil {
|
|
return nil, ErrSigner.Wrap(err)
|
|
}
|
|
|
|
signer.Action = action
|
|
signer.Limit = limit
|
|
|
|
return signer, nil
|
|
}
|
|
|
|
// NewSignerGet creates a new signer for get orders.
|
|
func NewSignerGet(service *Service, rootPieceID storj.PieceID, orderCreation time.Time, limit int64, bucket metabase.BucketLocation) (*Signer, error) {
|
|
return NewSigner(service, rootPieceID, time.Time{}, orderCreation, limit, pb.PieceAction_GET, bucket)
|
|
}
|
|
|
|
// NewSignerPut creates a new signer for put orders.
|
|
func NewSignerPut(service *Service, pieceExpiration time.Time, orderCreation time.Time, limit int64, bucket metabase.BucketLocation) (*Signer, error) {
|
|
rootPieceID := storj.NewPieceID()
|
|
return NewSigner(service, rootPieceID, pieceExpiration, orderCreation, limit, pb.PieceAction_PUT, bucket)
|
|
}
|
|
|
|
// NewSignerDelete creates a new signer for delete orders.
|
|
func NewSignerDelete(service *Service, rootPieceID storj.PieceID, orderCreation time.Time, bucket metabase.BucketLocation) (*Signer, error) {
|
|
return NewSigner(service, rootPieceID, time.Time{}, orderCreation, 0, pb.PieceAction_DELETE, bucket)
|
|
}
|
|
|
|
// NewSignerRepairGet creates a new signer for get repair orders.
|
|
func NewSignerRepairGet(service *Service, rootPieceID storj.PieceID, orderCreation time.Time, pieceSize int64, bucket metabase.BucketLocation) (*Signer, error) {
|
|
return NewSigner(service, rootPieceID, time.Time{}, orderCreation, pieceSize, pb.PieceAction_GET_REPAIR, bucket)
|
|
}
|
|
|
|
// NewSignerRepairPut creates a new signer for put repair orders.
|
|
func NewSignerRepairPut(service *Service, rootPieceID storj.PieceID, pieceExpiration time.Time, orderCreation time.Time, pieceSize int64, bucket metabase.BucketLocation) (*Signer, error) {
|
|
return NewSigner(service, rootPieceID, pieceExpiration, orderCreation, pieceSize, pb.PieceAction_PUT_REPAIR, bucket)
|
|
}
|
|
|
|
// NewSignerAudit creates a new signer for audit orders.
|
|
func NewSignerAudit(service *Service, rootPieceID storj.PieceID, orderCreation time.Time, pieceSize int64, bucket metabase.BucketLocation) (*Signer, error) {
|
|
return NewSigner(service, rootPieceID, time.Time{}, orderCreation, pieceSize, pb.PieceAction_GET_AUDIT, bucket)
|
|
}
|
|
|
|
// NewSignerGracefulExit creates a new signer for graceful exit orders.
|
|
func NewSignerGracefulExit(service *Service, rootPieceID storj.PieceID, orderCreation time.Time, shareSize int32, bucket metabase.BucketLocation) (*Signer, error) {
|
|
// TODO: we're using zero time.Time for piece expiration for some reason.
|
|
|
|
// TODO: we're using `PUT_REPAIR` here even though we should be using `PUT`, such
|
|
// that the storage node cannot distinguish between requests. We can't use `PUT`
|
|
// because we don't want to charge bucket owners for graceful exit bandwidth, and
|
|
// we can't use `PUT_GRACEFUL_EXIT` because storagenode will only accept upload
|
|
// orders with `PUT` or `PUT_REPAIR` as the action. we also don't have a bunch of
|
|
// supporting code/tables to aggregate `PUT_GRACEFUL_EXIT` bandwidth into our rollups
|
|
// and stuff. so, for now, we just use `PUT_REPAIR` because it's the least bad of
|
|
// our options. this should be fixed.
|
|
return NewSigner(service, rootPieceID, time.Time{}, orderCreation, int64(shareSize), pb.PieceAction_PUT_REPAIR, bucket)
|
|
}
|
|
|
|
// Sign signs an order limit for the specified node.
|
|
func (signer *Signer) Sign(ctx context.Context, node storj.NodeURL, pieceNum int32) (_ *pb.AddressedOrderLimit, err error) {
|
|
defer mon.Task()(&ctx)(&err)
|
|
|
|
if len(signer.EncryptedMetadata) == 0 {
|
|
encryptionKey := signer.Service.encryptionKeys.Default
|
|
if encryptionKey.IsZero() {
|
|
return nil, ErrSigner.New("default encryption key is missing")
|
|
}
|
|
|
|
encrypted, err := encryptionKey.EncryptMetadata(
|
|
signer.Serial,
|
|
&internalpb.OrderLimitMetadata{
|
|
CompactProjectBucketPrefix: signer.Bucket.CompactPrefix(),
|
|
},
|
|
)
|
|
if err != nil {
|
|
return nil, ErrSigner.Wrap(err)
|
|
}
|
|
signer.EncryptedMetadataKeyID = encryptionKey.ID[:]
|
|
signer.EncryptedMetadata = encrypted
|
|
}
|
|
|
|
limit := &pb.OrderLimit{
|
|
SerialNumber: signer.Serial,
|
|
SatelliteId: signer.Service.satellite.ID(),
|
|
UplinkPublicKey: signer.PublicKey,
|
|
StorageNodeId: node.ID,
|
|
|
|
PieceId: signer.RootPieceID.Derive(node.ID, pieceNum),
|
|
Limit: signer.Limit,
|
|
Action: signer.Action,
|
|
|
|
PieceExpiration: signer.PieceExpiration,
|
|
OrderCreation: signer.OrderCreation,
|
|
OrderExpiration: signer.OrderExpiration,
|
|
|
|
EncryptedMetadataKeyId: signer.EncryptedMetadataKeyID,
|
|
EncryptedMetadata: signer.EncryptedMetadata,
|
|
}
|
|
|
|
signedLimit, err := signing.SignOrderLimit(ctx, signer.Service.satellite, limit)
|
|
if err != nil {
|
|
return nil, ErrSigner.Wrap(err)
|
|
}
|
|
|
|
addressedLimit := &pb.AddressedOrderLimit{
|
|
Limit: signedLimit,
|
|
StorageNodeAddress: &pb.NodeAddress{
|
|
Address: node.Address,
|
|
},
|
|
}
|
|
|
|
signer.AddressedLimits = append(signer.AddressedLimits, addressedLimit)
|
|
|
|
return addressedLimit, nil
|
|
}
|