storj/pkg/piecestore/psserver/readerwriter.go
Michal Niewrzal 6186b3f90a
Storage node hash calculation on upload (#1347)
Storage node is calculating hash of uploaded data and send it back to uplink with signature
2019-02-23 11:46:07 +01:00

115 lines
2.9 KiB
Go

// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
package psserver
import (
"crypto/sha256"
"hash"
"github.com/zeebo/errs"
"storj.io/storj/pkg/pb"
"storj.io/storj/pkg/utils"
)
// StreamWriterError is a type of error for failures in StreamWriter
var StreamWriterError = errs.Class("stream writer error")
// StreamWriter -- Struct for writing piece to server upload stream
type StreamWriter struct {
server *Server
stream pb.PieceStoreRoutes_RetrieveServer
}
// NewStreamWriter returns a new StreamWriter
func NewStreamWriter(s *Server, stream pb.PieceStoreRoutes_RetrieveServer) *StreamWriter {
return &StreamWriter{server: s, stream: stream}
}
// Write -- Write method for piece upload to stream for Server.Retrieve
func (s *StreamWriter) Write(b []byte) (int, error) {
// Write the buffer to the stream we opened earlier
if err := s.stream.Send(&pb.PieceRetrievalStream{PieceSize: int64(len(b)), Content: b}); err != nil {
return 0, err
}
return len(b), nil
}
// StreamReader is a struct for Retrieving data from server
type StreamReader struct {
src *utils.ReaderSource
bandwidthAllocation *pb.Order
currentTotal int64
bandwidthRemaining int64
spaceRemaining int64
sofar int64
hash hash.Hash
}
// NewStreamReader returns a new StreamReader for Server.Store
func NewStreamReader(s *Server, stream pb.PieceStoreRoutes_StoreServer, bandwidthRemaining, spaceRemaining int64) *StreamReader {
sr := &StreamReader{
bandwidthRemaining: bandwidthRemaining,
spaceRemaining: spaceRemaining,
hash: sha256.New(),
}
sr.src = utils.NewReaderSource(func() ([]byte, error) {
recv, err := stream.Recv()
if err != nil {
return nil, err
}
pd := recv.GetPieceData()
rba := recv.BandwidthAllocation
if err = s.verifySignature(stream.Context(), rba); err != nil {
return nil, err
}
pba := rba.PayerAllocation
if err = s.verifyPayerAllocation(&pba, "PUT"); err != nil {
return nil, err
}
// if whitelist does not contain PBA satellite ID, reject storage request
if len(s.whitelist) != 0 {
if !s.isWhitelisted(pba.SatelliteId) {
return nil, StoreError.New("Satellite ID not approved")
}
}
// Update bandwidthallocation to be stored
if rba.Total > sr.currentTotal {
sr.bandwidthAllocation = rba
sr.currentTotal = rba.Total
}
return pd.GetContent(), nil
})
return sr
}
// Read -- Read method for piece download from stream
func (s *StreamReader) Read(b []byte) (int, error) {
if s.sofar >= s.bandwidthRemaining {
return 0, StreamWriterError.New("out of bandwidth")
}
if s.sofar >= s.spaceRemaining {
return 0, StreamWriterError.New("out of space")
}
n, err := s.src.Read(b)
s.sofar += int64(n)
_, errHash := s.hash.Write(b[:n])
err = errs.Combine(err, errHash)
if err != nil {
return n, err
}
if s.sofar >= s.spaceRemaining {
return n, StreamWriterError.New("out of space")
}
return n, nil
}