github.com/oskarth/go-ethereum@v1.6.8-0.20191013093314-dac24a9d3494/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/ethereum/go-ethereum/swarm/storage/feed/lookup"
    29  
    30  	"github.com/ethereum/go-ethereum/swarm/log"
    31  	"github.com/ethereum/go-ethereum/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  }