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