github.com/ethersphere/bee/v2@v2.2.0/pkg/api/soc.go (about)

     1  // Copyright 2021 The Swarm Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package api
     6  
     7  import (
     8  	"errors"
     9  	"io"
    10  	"net/http"
    11  
    12  	"github.com/ethersphere/bee/v2/pkg/accesscontrol"
    13  	"github.com/ethersphere/bee/v2/pkg/cac"
    14  	"github.com/ethersphere/bee/v2/pkg/jsonhttp"
    15  	"github.com/ethersphere/bee/v2/pkg/postage"
    16  	"github.com/ethersphere/bee/v2/pkg/soc"
    17  	storage "github.com/ethersphere/bee/v2/pkg/storage"
    18  	storer "github.com/ethersphere/bee/v2/pkg/storer"
    19  	"github.com/ethersphere/bee/v2/pkg/swarm"
    20  	"github.com/gorilla/mux"
    21  )
    22  
    23  type socPostResponse struct {
    24  	Reference swarm.Address `json:"reference"`
    25  }
    26  
    27  func (s *Service) socUploadHandler(w http.ResponseWriter, r *http.Request) {
    28  	logger := s.logger.WithName("post_soc").Build()
    29  
    30  	paths := struct {
    31  		Owner []byte `map:"owner" validate:"required"`
    32  		ID    []byte `map:"id" validate:"required"`
    33  	}{}
    34  	if response := s.mapStructure(mux.Vars(r), &paths); response != nil {
    35  		response("invalid path params", logger, w)
    36  		return
    37  	}
    38  
    39  	queries := struct {
    40  		Sig []byte `map:"sig" validate:"required"`
    41  	}{}
    42  	if response := s.mapStructure(r.URL.Query(), &queries); response != nil {
    43  		response("invalid query params", logger, w)
    44  		return
    45  	}
    46  
    47  	headers := struct {
    48  		BatchID        []byte        `map:"Swarm-Postage-Batch-Id"`
    49  		StampSig       []byte        `map:"Swarm-Postage-Stamp"`
    50  		Pin            bool          `map:"Swarm-Pin"`
    51  		Act            bool          `map:"Swarm-Act"`
    52  		HistoryAddress swarm.Address `map:"Swarm-Act-History-Address"`
    53  	}{}
    54  	if response := s.mapStructure(r.Header, &headers); response != nil {
    55  		response("invalid header params", logger, w)
    56  		return
    57  	}
    58  
    59  	if len(headers.BatchID) == 0 && len(headers.StampSig) == 0 {
    60  		logger.Error(nil, batchIdOrStampSig)
    61  		jsonhttp.BadRequest(w, batchIdOrStampSig)
    62  		return
    63  	}
    64  
    65  	// if pinning header is set we do a deferred upload, else we do a direct upload
    66  	var (
    67  		tag uint64
    68  		err error
    69  	)
    70  	if headers.Pin {
    71  		session, err := s.storer.NewSession()
    72  		if err != nil {
    73  			logger.Debug("get or create tag failed", "error", err)
    74  			logger.Error(nil, "get or create tag failed")
    75  			switch {
    76  			case errors.Is(err, storage.ErrNotFound):
    77  				jsonhttp.NotFound(w, "tag not found")
    78  			default:
    79  				jsonhttp.InternalServerError(w, "cannot get or create tag")
    80  			}
    81  			return
    82  		}
    83  		tag = session.TagID
    84  	}
    85  
    86  	deferred := tag != 0
    87  
    88  	var putter storer.PutterSession
    89  	if len(headers.StampSig) != 0 {
    90  		stamp := postage.Stamp{}
    91  		if err := stamp.UnmarshalBinary(headers.StampSig); err != nil {
    92  			errorMsg := "Stamp deserialization failure"
    93  			logger.Debug(errorMsg, "error", err)
    94  			logger.Error(nil, errorMsg)
    95  			jsonhttp.BadRequest(w, errorMsg)
    96  			return
    97  		}
    98  
    99  		putter, err = s.newStampedPutter(r.Context(), putterOptions{
   100  			BatchID:  stamp.BatchID(),
   101  			TagID:    tag,
   102  			Pin:      headers.Pin,
   103  			Deferred: deferred,
   104  		}, &stamp)
   105  	} else {
   106  		putter, err = s.newStamperPutter(r.Context(), putterOptions{
   107  			BatchID:  headers.BatchID,
   108  			TagID:    tag,
   109  			Pin:      headers.Pin,
   110  			Deferred: deferred,
   111  		})
   112  	}
   113  	if err != nil {
   114  		logger.Debug("get putter failed", "error", err)
   115  		logger.Error(nil, "get putter failed")
   116  		switch {
   117  		case errors.Is(err, errBatchUnusable) || errors.Is(err, postage.ErrNotUsable):
   118  			jsonhttp.UnprocessableEntity(w, "batch not usable yet or does not exist")
   119  		case errors.Is(err, postage.ErrNotFound):
   120  			jsonhttp.NotFound(w, "batch with id not found")
   121  		case errors.Is(err, errInvalidPostageBatch):
   122  			jsonhttp.BadRequest(w, "invalid batch id")
   123  		default:
   124  			jsonhttp.BadRequest(w, nil)
   125  		}
   126  		return
   127  	}
   128  
   129  	ow := &cleanupOnErrWriter{
   130  		ResponseWriter: w,
   131  		onErr:          putter.Cleanup,
   132  		logger:         logger,
   133  	}
   134  
   135  	data, err := io.ReadAll(r.Body)
   136  	if err != nil {
   137  		if jsonhttp.HandleBodyReadError(err, ow) {
   138  			return
   139  		}
   140  		logger.Debug("read body failed", "error", err)
   141  		logger.Error(nil, "read body failed")
   142  		jsonhttp.InternalServerError(ow, "cannot read chunk data")
   143  		return
   144  	}
   145  
   146  	if len(data) < swarm.SpanSize {
   147  		logger.Debug("chunk data too short")
   148  		logger.Error(nil, "chunk data too short")
   149  		jsonhttp.BadRequest(ow, "short chunk data")
   150  		return
   151  	}
   152  
   153  	if len(data) > swarm.ChunkSize+swarm.SpanSize {
   154  		logger.Debug("chunk data exceeds required length", "required_length", swarm.ChunkSize+swarm.SpanSize)
   155  		logger.Error(nil, "chunk data exceeds required length")
   156  		jsonhttp.RequestEntityTooLarge(ow, "payload too large")
   157  		return
   158  	}
   159  
   160  	ch, err := cac.NewWithDataSpan(data)
   161  	if err != nil {
   162  		logger.Debug("create content addressed chunk failed", "error", err)
   163  		logger.Error(nil, "create content addressed chunk failed")
   164  		jsonhttp.BadRequest(ow, "chunk data error")
   165  		return
   166  	}
   167  
   168  	ss, err := soc.NewSigned(paths.ID, ch, paths.Owner, queries.Sig)
   169  	if err != nil {
   170  		logger.Debug("create soc failed", "id", paths.ID, "owner", paths.Owner, "error", err)
   171  		logger.Error(nil, "create soc failed")
   172  		jsonhttp.Unauthorized(ow, "invalid address")
   173  		return
   174  	}
   175  
   176  	sch, err := ss.Chunk()
   177  	if err != nil {
   178  		logger.Debug("read chunk data failed", "error", err)
   179  		logger.Error(nil, "read chunk data failed")
   180  		jsonhttp.InternalServerError(ow, "cannot read chunk data")
   181  		return
   182  	}
   183  
   184  	if !soc.Valid(sch) {
   185  		logger.Debug("invalid chunk", "error", err)
   186  		logger.Error(nil, "invalid chunk")
   187  		jsonhttp.Unauthorized(ow, "invalid chunk")
   188  		return
   189  	}
   190  
   191  	reference := sch.Address()
   192  	if headers.Act {
   193  		reference, err = s.actEncryptionHandler(r.Context(), w, putter, reference, headers.HistoryAddress)
   194  		if err != nil {
   195  			logger.Debug("access control upload failed", "error", err)
   196  			logger.Error(nil, "access control upload failed")
   197  			switch {
   198  			case errors.Is(err, accesscontrol.ErrNotFound):
   199  				jsonhttp.NotFound(w, "act or history entry not found")
   200  			case errors.Is(err, accesscontrol.ErrInvalidPublicKey) || errors.Is(err, accesscontrol.ErrSecretKeyInfinity):
   201  				jsonhttp.BadRequest(w, "invalid public key")
   202  			case errors.Is(err, accesscontrol.ErrUnexpectedType):
   203  				jsonhttp.BadRequest(w, "failed to create history")
   204  			default:
   205  				jsonhttp.InternalServerError(w, errActUpload)
   206  			}
   207  			return
   208  		}
   209  	}
   210  
   211  	err = putter.Put(r.Context(), sch)
   212  	if err != nil {
   213  		logger.Debug("write chunk failed", "chunk_address", sch.Address(), "error", err)
   214  		logger.Error(nil, "write chunk failed")
   215  		jsonhttp.BadRequest(ow, "chunk write error")
   216  		return
   217  	}
   218  
   219  	err = putter.Done(sch.Address())
   220  	if err != nil {
   221  		logger.Debug("done split failed", "error", err)
   222  		logger.Error(nil, "done split failed")
   223  		jsonhttp.InternalServerError(ow, "done split failed")
   224  		return
   225  	}
   226  
   227  	jsonhttp.Created(w, socPostResponse{Reference: reference})
   228  }