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 }