github.com/Ethersocial/go-esn@v0.3.7/swarm/storage/feed/handler.go (about) 1 // Copyright 2018 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 // Handler is the API for feeds 18 // It enables creating, updating, syncing and retrieving feed updates and their data 19 package feed 20 21 import ( 22 "bytes" 23 "context" 24 "fmt" 25 "sync" 26 "time" 27 28 "github.com/ethersocial/go-esn/swarm/storage/feed/lookup" 29 30 "github.com/ethersocial/go-esn/swarm/log" 31 "github.com/ethersocial/go-esn/swarm/storage" 32 ) 33 34 type Handler struct { 35 chunkStore *storage.NetStore 36 HashSize int 37 cache map[uint64]*cacheEntry 38 cacheLock sync.RWMutex 39 storeTimeout time.Duration 40 queryMaxPeriods uint32 41 } 42 43 // HandlerParams pass parameters to the Handler constructor NewHandler 44 // Signer and TimestampProvider are mandatory parameters 45 type HandlerParams struct { 46 } 47 48 // hashPool contains a pool of ready hashers 49 var hashPool sync.Pool 50 51 // init initializes the package and hashPool 52 func init() { 53 hashPool = sync.Pool{ 54 New: func() interface{} { 55 return storage.MakeHashFunc(feedsHashAlgorithm)() 56 }, 57 } 58 } 59 60 // NewHandler creates a new Swarm feeds API 61 func NewHandler(params *HandlerParams) *Handler { 62 fh := &Handler{ 63 cache: make(map[uint64]*cacheEntry), 64 } 65 66 for i := 0; i < hasherCount; i++ { 67 hashfunc := storage.MakeHashFunc(feedsHashAlgorithm)() 68 if fh.HashSize == 0 { 69 fh.HashSize = hashfunc.Size() 70 } 71 hashPool.Put(hashfunc) 72 } 73 74 return fh 75 } 76 77 // SetStore sets the store backend for the Swarm feeds API 78 func (h *Handler) SetStore(store *storage.NetStore) { 79 h.chunkStore = store 80 } 81 82 // Validate is a chunk validation method 83 // If it looks like a feed update, the chunk address is checked against the userAddr of the update's signature 84 // It implements the storage.ChunkValidator interface 85 func (h *Handler) Validate(chunkAddr storage.Address, data []byte) bool { 86 dataLength := len(data) 87 if dataLength < minimumSignedUpdateLength { 88 return false 89 } 90 91 // check if it is a properly formatted update chunk with 92 // valid signature and proof of ownership of the feed it is trying 93 // to update 94 95 // First, deserialize the chunk 96 var r Request 97 if err := r.fromChunk(chunkAddr, data); err != nil { 98 log.Debug("Invalid feed update chunk", "addr", chunkAddr.Hex(), "err", err.Error()) 99 return false 100 } 101 102 // Verify signatures and that the signer actually owns the feed 103 // If it fails, it means either the signature is not valid, data is corrupted 104 // or someone is trying to update someone else's feed. 105 if err := r.Verify(); err != nil { 106 log.Debug("Invalid feed update signature", "err", err) 107 return false 108 } 109 110 return true 111 } 112 113 // GetContent retrieves the data payload of the last synced update of the feed 114 func (h *Handler) GetContent(feed *Feed) (storage.Address, []byte, error) { 115 if feed == nil { 116 return nil, nil, NewError(ErrInvalidValue, "feed is nil") 117 } 118 feedUpdate := h.get(feed) 119 if feedUpdate == nil { 120 return nil, nil, NewError(ErrNotFound, "feed update not cached") 121 } 122 return feedUpdate.lastKey, feedUpdate.data, nil 123 } 124 125 // NewRequest prepares a Request structure with all the necessary information to 126 // just add the desired data and sign it. 127 // The resulting structure can then be signed and passed to Handler.Update to be verified and sent 128 func (h *Handler) NewRequest(ctx context.Context, feed *Feed) (request *Request, err error) { 129 if feed == nil { 130 return nil, NewError(ErrInvalidValue, "feed cannot be nil") 131 } 132 133 now := TimestampProvider.Now().Time 134 request = new(Request) 135 request.Header.Version = ProtocolVersion 136 137 query := NewQueryLatest(feed, lookup.NoClue) 138 139 feedUpdate, err := h.Lookup(ctx, query) 140 if err != nil { 141 if err.(*Error).code != ErrNotFound { 142 return nil, err 143 } 144 // not finding updates means that there is a network error 145 // or that the feed really does not have updates 146 } 147 148 request.Feed = *feed 149 150 // if we already have an update, then find next epoch 151 if feedUpdate != nil { 152 request.Epoch = lookup.GetNextEpoch(feedUpdate.Epoch, now) 153 } else { 154 request.Epoch = lookup.GetFirstEpoch(now) 155 } 156 157 return request, nil 158 } 159 160 // Lookup retrieves a specific or latest feed update 161 // Lookup works differently depending on the configuration of `query` 162 // See the `query` documentation and helper functions: 163 // `NewQueryLatest` and `NewQuery` 164 func (h *Handler) Lookup(ctx context.Context, query *Query) (*cacheEntry, error) { 165 166 timeLimit := query.TimeLimit 167 if timeLimit == 0 { // if time limit is set to zero, the user wants to get the latest update 168 timeLimit = TimestampProvider.Now().Time 169 } 170 171 if query.Hint == lookup.NoClue { // try to use our cache 172 entry := h.get(&query.Feed) 173 if entry != nil && entry.Epoch.Time <= timeLimit { // avoid bad hints 174 query.Hint = entry.Epoch 175 } 176 } 177 178 // we can't look for anything without a store 179 if h.chunkStore == nil { 180 return nil, NewError(ErrInit, "Call Handler.SetStore() before performing lookups") 181 } 182 183 var id ID 184 id.Feed = query.Feed 185 var readCount int 186 187 // Invoke the lookup engine. 188 // The callback will be called every time the lookup algorithm needs to guess 189 requestPtr, err := lookup.Lookup(timeLimit, query.Hint, func(epoch lookup.Epoch, now uint64) (interface{}, error) { 190 readCount++ 191 id.Epoch = epoch 192 ctx, cancel := context.WithTimeout(ctx, defaultRetrieveTimeout) 193 defer cancel() 194 195 chunk, err := h.chunkStore.Get(ctx, id.Addr()) 196 if err != nil { // TODO: check for catastrophic errors other than chunk not found 197 return nil, nil 198 } 199 200 var request Request 201 if err := request.fromChunk(chunk.Address(), chunk.Data()); err != nil { 202 return nil, nil 203 } 204 if request.Time <= timeLimit { 205 return &request, nil 206 } 207 return nil, nil 208 }) 209 if err != nil { 210 return nil, err 211 } 212 213 log.Info(fmt.Sprintf("Feed lookup finished in %d lookups", readCount)) 214 215 request, _ := requestPtr.(*Request) 216 if request == nil { 217 return nil, NewError(ErrNotFound, "no feed updates found") 218 } 219 return h.updateCache(request) 220 221 } 222 223 // update feed updates cache with specified content 224 func (h *Handler) updateCache(request *Request) (*cacheEntry, error) { 225 226 updateAddr := request.Addr() 227 log.Trace("feed cache update", "topic", request.Topic.Hex(), "updateaddr", updateAddr, "epoch time", request.Epoch.Time, "epoch level", request.Epoch.Level) 228 229 feedUpdate := h.get(&request.Feed) 230 if feedUpdate == nil { 231 feedUpdate = &cacheEntry{} 232 h.set(&request.Feed, feedUpdate) 233 } 234 235 // update our rsrcs entry map 236 feedUpdate.lastKey = updateAddr 237 feedUpdate.Update = request.Update 238 feedUpdate.Reader = bytes.NewReader(feedUpdate.data) 239 return feedUpdate, nil 240 } 241 242 // Update publishes a feed update 243 // Note that a feed update cannot span chunks, and thus has a MAX NET LENGTH 4096, INCLUDING update header data and signature. 244 // This results in a max payload of `maxUpdateDataLength` (check update.go for more details) 245 // An error will be returned if the total length of the chunk payload will exceed this limit. 246 // Update can only check if the caller is trying to overwrite the very last known version, otherwise it just puts the update 247 // on the network. 248 func (h *Handler) Update(ctx context.Context, r *Request) (updateAddr storage.Address, err error) { 249 250 // we can't update anything without a store 251 if h.chunkStore == nil { 252 return nil, NewError(ErrInit, "Call Handler.SetStore() before updating") 253 } 254 255 feedUpdate := h.get(&r.Feed) 256 if feedUpdate != nil && feedUpdate.Epoch.Equals(r.Epoch) { // This is the only cheap check we can do for sure 257 return nil, NewError(ErrInvalidValue, "A former update in this epoch is already known to exist") 258 } 259 260 chunk, err := r.toChunk() // Serialize the update into a chunk. Fails if data is too big 261 if err != nil { 262 return nil, err 263 } 264 265 // send the chunk 266 h.chunkStore.Put(ctx, chunk) 267 log.Trace("feed update", "updateAddr", r.idAddr, "epoch time", r.Epoch.Time, "epoch level", r.Epoch.Level, "data", chunk.Data()) 268 // update our feed updates map cache entry if the new update is older than the one we have, if we have it. 269 if feedUpdate != nil && r.Epoch.After(feedUpdate.Epoch) { 270 feedUpdate.Epoch = r.Epoch 271 feedUpdate.data = make([]byte, len(r.data)) 272 feedUpdate.lastKey = r.idAddr 273 copy(feedUpdate.data, r.data) 274 feedUpdate.Reader = bytes.NewReader(feedUpdate.data) 275 } 276 277 return r.idAddr, nil 278 } 279 280 // Retrieves the feed update cache value for the given nameHash 281 func (h *Handler) get(feed *Feed) *cacheEntry { 282 mapKey := feed.mapKey() 283 h.cacheLock.RLock() 284 defer h.cacheLock.RUnlock() 285 feedUpdate := h.cache[mapKey] 286 return feedUpdate 287 } 288 289 // Sets the feed update cache value for the given feed 290 func (h *Handler) set(feed *Feed, feedUpdate *cacheEntry) { 291 mapKey := feed.mapKey() 292 h.cacheLock.Lock() 293 defer h.cacheLock.Unlock() 294 h.cache[mapKey] = feedUpdate 295 }