2020-05-11 20:57:46 +02:00
|
|
|
// Copyright (C) 2019 Storj Labs, Inc.
|
|
|
|
// See LICENSE for copying information.
|
|
|
|
|
|
|
|
package storj
|
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/hmac"
|
|
|
|
"crypto/rand"
|
|
|
|
"crypto/sha512"
|
|
|
|
"database/sql/driver"
|
|
|
|
"encoding/base32"
|
|
|
|
"encoding/binary"
|
|
|
|
"encoding/json"
|
|
|
|
|
|
|
|
"github.com/zeebo/errs"
|
|
|
|
)
|
|
|
|
|
2020-05-29 15:08:11 +02:00
|
|
|
// ErrPieceID is used when something goes wrong with a piece ID.
|
2020-05-11 20:57:46 +02:00
|
|
|
var ErrPieceID = errs.Class("piece ID error")
|
|
|
|
|
2020-05-29 15:08:11 +02:00
|
|
|
// pieceIDEncoding is base32 without padding.
|
2020-05-11 20:57:46 +02:00
|
|
|
var pieceIDEncoding = base32.StdEncoding.WithPadding(base32.NoPadding)
|
|
|
|
|
2020-05-29 15:08:11 +02:00
|
|
|
// PieceID is the unique identifier for pieces.
|
2020-05-11 20:57:46 +02:00
|
|
|
type PieceID [32]byte
|
|
|
|
|
2020-05-29 15:08:11 +02:00
|
|
|
// NewPieceID creates a piece ID.
|
2020-05-11 20:57:46 +02:00
|
|
|
func NewPieceID() PieceID {
|
|
|
|
var id PieceID
|
|
|
|
|
|
|
|
_, err := rand.Read(id[:])
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return id
|
|
|
|
}
|
|
|
|
|
2020-05-29 15:08:11 +02:00
|
|
|
// PieceIDFromString decodes a hex encoded piece ID string.
|
2020-05-11 20:57:46 +02:00
|
|
|
func PieceIDFromString(s string) (PieceID, error) {
|
|
|
|
idBytes, err := pieceIDEncoding.DecodeString(s)
|
|
|
|
if err != nil {
|
|
|
|
return PieceID{}, ErrPieceID.Wrap(err)
|
|
|
|
}
|
|
|
|
return PieceIDFromBytes(idBytes)
|
|
|
|
}
|
|
|
|
|
2020-05-29 15:08:11 +02:00
|
|
|
// PieceIDFromBytes converts a byte slice into a piece ID.
|
2020-05-11 20:57:46 +02:00
|
|
|
func PieceIDFromBytes(b []byte) (PieceID, error) {
|
|
|
|
if len(b) != len(PieceID{}) {
|
|
|
|
return PieceID{}, ErrPieceID.New("not enough bytes to make a piece ID; have %d, need %d", len(b), len(PieceID{}))
|
|
|
|
}
|
|
|
|
|
|
|
|
var id PieceID
|
|
|
|
copy(id[:], b)
|
|
|
|
return id, nil
|
|
|
|
}
|
|
|
|
|
2020-05-29 15:08:11 +02:00
|
|
|
// IsZero returns whether piece ID is unassigned.
|
2020-05-11 20:57:46 +02:00
|
|
|
func (id PieceID) IsZero() bool {
|
|
|
|
return id == PieceID{}
|
|
|
|
}
|
|
|
|
|
2020-05-29 15:08:11 +02:00
|
|
|
// String representation of the piece ID.
|
2020-05-11 20:57:46 +02:00
|
|
|
func (id PieceID) String() string { return pieceIDEncoding.EncodeToString(id.Bytes()) }
|
|
|
|
|
2020-05-29 15:08:11 +02:00
|
|
|
// Bytes returns bytes of the piece ID.
|
2020-05-11 20:57:46 +02:00
|
|
|
func (id PieceID) Bytes() []byte { return id[:] }
|
|
|
|
|
2020-05-29 15:08:11 +02:00
|
|
|
// Derive a new PieceID from the current piece ID, the given storage node ID and piece number.
|
2020-05-11 20:57:46 +02:00
|
|
|
func (id PieceID) Derive(storagenodeID NodeID, pieceNum int32) PieceID {
|
|
|
|
// TODO: should the secret / content be swapped?
|
|
|
|
mac := hmac.New(sha512.New, id.Bytes())
|
|
|
|
_, _ = mac.Write(storagenodeID.Bytes()) // on hash.Hash write never returns an error
|
|
|
|
num := make([]byte, 4)
|
|
|
|
binary.BigEndian.PutUint32(num, uint32(pieceNum))
|
|
|
|
_, _ = mac.Write(num) // on hash.Hash write never returns an error
|
|
|
|
var derived PieceID
|
|
|
|
copy(derived[:], mac.Sum(nil))
|
|
|
|
return derived
|
|
|
|
}
|
|
|
|
|
2020-05-29 15:08:11 +02:00
|
|
|
// Marshal serializes a piece ID.
|
2020-05-11 20:57:46 +02:00
|
|
|
func (id PieceID) Marshal() ([]byte, error) {
|
|
|
|
return id.Bytes(), nil
|
|
|
|
}
|
|
|
|
|
2020-05-29 15:08:11 +02:00
|
|
|
// MarshalTo serializes a piece ID into the passed byte slice.
|
2020-05-11 20:57:46 +02:00
|
|
|
func (id *PieceID) MarshalTo(data []byte) (n int, err error) {
|
|
|
|
n = copy(data, id.Bytes())
|
|
|
|
return n, nil
|
|
|
|
}
|
|
|
|
|
2020-05-29 15:08:11 +02:00
|
|
|
// Unmarshal deserializes a piece ID.
|
2020-05-11 20:57:46 +02:00
|
|
|
func (id *PieceID) Unmarshal(data []byte) error {
|
|
|
|
var err error
|
|
|
|
*id, err = PieceIDFromBytes(data)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-05-29 15:08:11 +02:00
|
|
|
// Size returns the length of a piece ID (implements gogo's custom type interface).
|
2020-05-11 20:57:46 +02:00
|
|
|
func (id *PieceID) Size() int {
|
|
|
|
return len(id)
|
|
|
|
}
|
|
|
|
|
2020-05-29 15:08:11 +02:00
|
|
|
// MarshalJSON serializes a piece ID to a json string as bytes.
|
2020-05-11 20:57:46 +02:00
|
|
|
func (id PieceID) MarshalJSON() ([]byte, error) {
|
|
|
|
return []byte(`"` + id.String() + `"`), nil
|
|
|
|
}
|
|
|
|
|
2020-05-29 15:08:11 +02:00
|
|
|
// UnmarshalJSON deserializes a json string (as bytes) to a piece ID.
|
2020-05-11 20:57:46 +02:00
|
|
|
func (id *PieceID) UnmarshalJSON(data []byte) error {
|
|
|
|
var unquoted string
|
|
|
|
err := json.Unmarshal(data, &unquoted)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
*id, err = PieceIDFromString(unquoted)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-05-29 15:08:11 +02:00
|
|
|
// Value set a PieceID to a database field.
|
2020-05-11 20:57:46 +02:00
|
|
|
func (id PieceID) Value() (driver.Value, error) {
|
|
|
|
return id.Bytes(), nil
|
|
|
|
}
|
|
|
|
|
2020-05-29 15:08:11 +02:00
|
|
|
// Scan extracts a PieceID from a database field.
|
2020-05-11 20:57:46 +02:00
|
|
|
func (id *PieceID) Scan(src interface{}) (err error) {
|
|
|
|
b, ok := src.([]byte)
|
|
|
|
if !ok {
|
|
|
|
return ErrPieceID.New("PieceID Scan expects []byte")
|
|
|
|
}
|
|
|
|
n, err := PieceIDFromBytes(b)
|
|
|
|
*id = n
|
|
|
|
return err
|
|
|
|
}
|