github.com/FUSIONFoundation/efsn@v3.6.2-0.20200916075423-dbb5dd5d2cc7+incompatible/swarm/storage/mru/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 Mutable Resources
    18  // It enables creating, updating, syncing and retrieving resources and their update data
    19  package mru
    20  
    21  import (
    22  	"bytes"
    23  	"context"
    24  	"sync"
    25  	"time"
    26  	"unsafe"
    27  
    28  	"github.com/FusionFoundation/efsn/swarm/chunk"
    29  	"github.com/FusionFoundation/efsn/swarm/log"
    30  	"github.com/FusionFoundation/efsn/swarm/storage"
    31  )
    32  
    33  type Handler struct {
    34  	chunkStore      *storage.NetStore
    35  	HashSize        int
    36  	resources       map[uint64]*resource
    37  	resourceLock    sync.RWMutex
    38  	storeTimeout    time.Duration
    39  	queryMaxPeriods uint32
    40  }
    41  
    42  // HandlerParams pass parameters to the Handler constructor NewHandler
    43  // Signer and TimestampProvider are mandatory parameters
    44  type HandlerParams struct {
    45  	QueryMaxPeriods uint32
    46  }
    47  
    48  // hashPool contains a pool of ready hashers
    49  var hashPool sync.Pool
    50  var minimumChunkLength int
    51  
    52  // init initializes the package and hashPool
    53  func init() {
    54  	hashPool = sync.Pool{
    55  		New: func() interface{} {
    56  			return storage.MakeHashFunc(resourceHashAlgorithm)()
    57  		},
    58  	}
    59  	if minimumMetadataLength < minimumUpdateDataLength {
    60  		minimumChunkLength = minimumMetadataLength
    61  	} else {
    62  		minimumChunkLength = minimumUpdateDataLength
    63  	}
    64  }
    65  
    66  // NewHandler creates a new Mutable Resource API
    67  func NewHandler(params *HandlerParams) *Handler {
    68  	rh := &Handler{
    69  		resources:       make(map[uint64]*resource),
    70  		storeTimeout:    defaultStoreTimeout,
    71  		queryMaxPeriods: params.QueryMaxPeriods,
    72  	}
    73  
    74  	for i := 0; i < hasherCount; i++ {
    75  		hashfunc := storage.MakeHashFunc(resourceHashAlgorithm)()
    76  		if rh.HashSize == 0 {
    77  			rh.HashSize = hashfunc.Size()
    78  		}
    79  		hashPool.Put(hashfunc)
    80  	}
    81  
    82  	return rh
    83  }
    84  
    85  // SetStore sets the store backend for the Mutable Resource API
    86  func (h *Handler) SetStore(store *storage.NetStore) {
    87  	h.chunkStore = store
    88  }
    89  
    90  // Validate is a chunk validation method
    91  // If it looks like a resource update, the chunk address is checked against the ownerAddr of the update's signature
    92  // It implements the storage.ChunkValidator interface
    93  func (h *Handler) Validate(chunkAddr storage.Address, data []byte) bool {
    94  	dataLength := len(data)
    95  	if dataLength < minimumChunkLength || dataLength > chunk.DefaultSize+8 {
    96  		return false
    97  	}
    98  
    99  	//metadata chunks have the first two bytes set to zero
   100  	if data[0] == 0 && data[1] == 0 && dataLength >= minimumMetadataLength {
   101  		//metadata chunk
   102  		rootAddr, _ := metadataHash(data)
   103  		valid := bytes.Equal(chunkAddr, rootAddr)
   104  		if !valid {
   105  			log.Debug("Invalid root metadata chunk with address", "addr", chunkAddr.Hex())
   106  		}
   107  		return valid
   108  	}
   109  
   110  	// if it is not a metadata chunk, check if it is a properly formatted update chunk with
   111  	// valid signature and proof of ownership of the resource it is trying
   112  	// to update
   113  
   114  	// First, deserialize the chunk
   115  	var r SignedResourceUpdate
   116  	if err := r.fromChunk(chunkAddr, data); err != nil {
   117  		log.Debug("Invalid resource chunk", "addr", chunkAddr.Hex(), "err", err.Error())
   118  		return false
   119  	}
   120  
   121  	// check that the lookup information contained in the chunk matches the updateAddr (chunk search key)
   122  	// that was used to retrieve this chunk
   123  	// if this validation fails, someone forged a chunk.
   124  	if !bytes.Equal(chunkAddr, r.updateHeader.UpdateAddr()) {
   125  		log.Debug("period,version,rootAddr contained in update chunk do not match updateAddr", "addr", chunkAddr.Hex())
   126  		return false
   127  	}
   128  
   129  	// Verify signatures and that the signer actually owns the resource
   130  	// If it fails, it means either the signature is not valid, data is corrupted
   131  	// or someone is trying to update someone else's resource.
   132  	if err := r.Verify(); err != nil {
   133  		log.Debug("Invalid signature", "err", err)
   134  		return false
   135  	}
   136  
   137  	return true
   138  }
   139  
   140  // GetContent retrieves the data payload of the last synced update of the Mutable Resource
   141  func (h *Handler) GetContent(rootAddr storage.Address) (storage.Address, []byte, error) {
   142  	rsrc := h.get(rootAddr)
   143  	if rsrc == nil || !rsrc.isSynced() {
   144  		return nil, nil, NewError(ErrNotFound, " does not exist or is not synced")
   145  	}
   146  	return rsrc.lastKey, rsrc.data, nil
   147  }
   148  
   149  // GetLastPeriod retrieves the period of the last synced update of the Mutable Resource
   150  func (h *Handler) GetLastPeriod(rootAddr storage.Address) (uint32, error) {
   151  	rsrc := h.get(rootAddr)
   152  	if rsrc == nil {
   153  		return 0, NewError(ErrNotFound, " does not exist")
   154  	} else if !rsrc.isSynced() {
   155  		return 0, NewError(ErrNotSynced, " is not synced")
   156  	}
   157  	return rsrc.period, nil
   158  }
   159  
   160  // GetVersion retrieves the period of the last synced update of the Mutable Resource
   161  func (h *Handler) GetVersion(rootAddr storage.Address) (uint32, error) {
   162  	rsrc := h.get(rootAddr)
   163  	if rsrc == nil {
   164  		return 0, NewError(ErrNotFound, " does not exist")
   165  	} else if !rsrc.isSynced() {
   166  		return 0, NewError(ErrNotSynced, " is not synced")
   167  	}
   168  	return rsrc.version, nil
   169  }
   170  
   171  // New creates a new metadata chunk out of the request passed in.
   172  func (h *Handler) New(ctx context.Context, request *Request) error {
   173  
   174  	// frequency 0 is invalid
   175  	if request.metadata.Frequency == 0 {
   176  		return NewError(ErrInvalidValue, "frequency cannot be 0 when creating a resource")
   177  	}
   178  
   179  	// make sure owner is set to something
   180  	if request.metadata.Owner == zeroAddr {
   181  		return NewError(ErrInvalidValue, "ownerAddr must be set to create a new metadata chunk")
   182  	}
   183  
   184  	// create the meta chunk and store it in swarm
   185  	chunk, metaHash, err := request.metadata.newChunk()
   186  	if err != nil {
   187  		return err
   188  	}
   189  	if request.metaHash != nil && !bytes.Equal(request.metaHash, metaHash) ||
   190  		request.rootAddr != nil && !bytes.Equal(request.rootAddr, chunk.Address()) {
   191  		return NewError(ErrInvalidValue, "metaHash in UpdateRequest does not match actual metadata")
   192  	}
   193  
   194  	request.metaHash = metaHash
   195  	request.rootAddr = chunk.Address()
   196  
   197  	h.chunkStore.Put(ctx, chunk)
   198  	log.Debug("new resource", "name", request.metadata.Name, "startTime", request.metadata.StartTime, "frequency", request.metadata.Frequency, "owner", request.metadata.Owner)
   199  
   200  	// create the internal index for the resource and populate it with its metadata
   201  	rsrc := &resource{
   202  		resourceUpdate: resourceUpdate{
   203  			updateHeader: updateHeader{
   204  				UpdateLookup: UpdateLookup{
   205  					rootAddr: chunk.Address(),
   206  				},
   207  			},
   208  		},
   209  		ResourceMetadata: request.metadata,
   210  		updated:          time.Now(),
   211  	}
   212  	h.set(chunk.Address(), rsrc)
   213  
   214  	return nil
   215  }
   216  
   217  // NewUpdateRequest prepares an UpdateRequest structure with all the necessary information to
   218  // just add the desired data and sign it.
   219  // The resulting structure can then be signed and passed to Handler.Update to be verified and sent
   220  func (h *Handler) NewUpdateRequest(ctx context.Context, rootAddr storage.Address) (updateRequest *Request, err error) {
   221  
   222  	if rootAddr == nil {
   223  		return nil, NewError(ErrInvalidValue, "rootAddr cannot be nil")
   224  	}
   225  
   226  	// Make sure we have a cache of the metadata chunk
   227  	rsrc, err := h.Load(ctx, rootAddr)
   228  	if err != nil {
   229  		return nil, err
   230  	}
   231  
   232  	now := TimestampProvider.Now()
   233  
   234  	updateRequest = new(Request)
   235  	updateRequest.period, err = getNextPeriod(rsrc.StartTime.Time, now.Time, rsrc.Frequency)
   236  	if err != nil {
   237  		return nil, err
   238  	}
   239  
   240  	if _, err = h.lookup(rsrc, LookupLatestVersionInPeriod(rsrc.rootAddr, updateRequest.period)); err != nil {
   241  		if err.(*Error).code != ErrNotFound {
   242  			return nil, err
   243  		}
   244  		// not finding updates means that there is a network error
   245  		// or that the resource really does not have updates in this period.
   246  	}
   247  
   248  	updateRequest.multihash = rsrc.multihash
   249  	updateRequest.rootAddr = rsrc.rootAddr
   250  	updateRequest.metaHash = rsrc.metaHash
   251  	updateRequest.metadata = rsrc.ResourceMetadata
   252  
   253  	// if we already have an update for this period then increment version
   254  	// resource object MUST be in sync for version to be correct, but we checked this earlier in the method already
   255  	if h.hasUpdate(rootAddr, updateRequest.period) {
   256  		updateRequest.version = rsrc.version + 1
   257  	} else {
   258  		updateRequest.version = 1
   259  	}
   260  
   261  	return updateRequest, nil
   262  }
   263  
   264  // Lookup retrieves a specific or latest version of the resource update with metadata chunk at params.Root
   265  // Lookup works differently depending on the configuration of `LookupParams`
   266  // See the `LookupParams` documentation and helper functions:
   267  // `LookupLatest`, `LookupLatestVersionInPeriod` and `LookupVersion`
   268  // When looking for the latest update, it starts at the next period after the current time.
   269  // upon failure tries the corresponding keys of each previous period until one is found
   270  // (or startTime is reached, in which case there are no updates).
   271  func (h *Handler) Lookup(ctx context.Context, params *LookupParams) (*resource, error) {
   272  
   273  	rsrc := h.get(params.rootAddr)
   274  	if rsrc == nil {
   275  		return nil, NewError(ErrNothingToReturn, "resource not loaded")
   276  	}
   277  	return h.lookup(rsrc, params)
   278  }
   279  
   280  // LookupPrevious returns the resource before the one currently loaded in the resource cache
   281  // This is useful where resource updates are used incrementally in contrast to
   282  // merely replacing content.
   283  // Requires a cached resource object to determine the current state of the resource.
   284  func (h *Handler) LookupPrevious(ctx context.Context, params *LookupParams) (*resource, error) {
   285  	rsrc := h.get(params.rootAddr)
   286  	if rsrc == nil {
   287  		return nil, NewError(ErrNothingToReturn, "resource not loaded")
   288  	}
   289  	if !rsrc.isSynced() {
   290  		return nil, NewError(ErrNotSynced, "LookupPrevious requires synced resource.")
   291  	} else if rsrc.period == 0 {
   292  		return nil, NewError(ErrNothingToReturn, " not found")
   293  	}
   294  	var version, period uint32
   295  	if rsrc.version > 1 {
   296  		version = rsrc.version - 1
   297  		period = rsrc.period
   298  	} else if rsrc.period == 1 {
   299  		return nil, NewError(ErrNothingToReturn, "Current update is the oldest")
   300  	} else {
   301  		version = 0
   302  		period = rsrc.period - 1
   303  	}
   304  	return h.lookup(rsrc, NewLookupParams(rsrc.rootAddr, period, version, params.Limit))
   305  }
   306  
   307  // base code for public lookup methods
   308  func (h *Handler) lookup(rsrc *resource, params *LookupParams) (*resource, error) {
   309  
   310  	lp := *params
   311  	// we can't look for anything without a store
   312  	if h.chunkStore == nil {
   313  		return nil, NewError(ErrInit, "Call Handler.SetStore() before performing lookups")
   314  	}
   315  
   316  	var specificperiod bool
   317  	if lp.period > 0 {
   318  		specificperiod = true
   319  	} else {
   320  		// get the current time and the next period
   321  		now := TimestampProvider.Now()
   322  
   323  		var period uint32
   324  		period, err := getNextPeriod(rsrc.StartTime.Time, now.Time, rsrc.Frequency)
   325  		if err != nil {
   326  			return nil, err
   327  		}
   328  		lp.period = period
   329  	}
   330  
   331  	// start from the last possible period, and iterate previous ones
   332  	// (unless we want a specific period only) until we find a match.
   333  	// If we hit startTime we're out of options
   334  	var specificversion bool
   335  	if lp.version > 0 {
   336  		specificversion = true
   337  	} else {
   338  		lp.version = 1
   339  	}
   340  
   341  	var hops uint32
   342  	if lp.Limit == 0 {
   343  		lp.Limit = h.queryMaxPeriods
   344  	}
   345  	log.Trace("resource lookup", "period", lp.period, "version", lp.version, "limit", lp.Limit)
   346  	for lp.period > 0 {
   347  		if lp.Limit != 0 && hops > lp.Limit {
   348  			return nil, NewErrorf(ErrPeriodDepth, "Lookup exceeded max period hops (%d)", lp.Limit)
   349  		}
   350  		updateAddr := lp.UpdateAddr()
   351  
   352  		ctx, cancel := context.WithTimeout(context.Background(), defaultRetrieveTimeout)
   353  		defer cancel()
   354  
   355  		chunk, err := h.chunkStore.Get(ctx, updateAddr)
   356  		if err == nil {
   357  			if specificversion {
   358  				return h.updateIndex(rsrc, chunk)
   359  			}
   360  			// check if we have versions > 1. If a version fails, the previous version is used and returned.
   361  			log.Trace("rsrc update version 1 found, checking for version updates", "period", lp.period, "updateAddr", updateAddr)
   362  			for {
   363  				newversion := lp.version + 1
   364  				updateAddr := lp.UpdateAddr()
   365  
   366  				ctx, cancel := context.WithTimeout(context.Background(), defaultRetrieveTimeout)
   367  				defer cancel()
   368  
   369  				newchunk, err := h.chunkStore.Get(ctx, updateAddr)
   370  				if err != nil {
   371  					return h.updateIndex(rsrc, chunk)
   372  				}
   373  				chunk = newchunk
   374  				lp.version = newversion
   375  				log.Trace("version update found, checking next", "version", lp.version, "period", lp.period, "updateAddr", updateAddr)
   376  			}
   377  		}
   378  		if specificperiod {
   379  			break
   380  		}
   381  		log.Trace("rsrc update not found, checking previous period", "period", lp.period, "updateAddr", updateAddr)
   382  		lp.period--
   383  		hops++
   384  	}
   385  	return nil, NewError(ErrNotFound, "no updates found")
   386  }
   387  
   388  // Load retrieves the Mutable Resource metadata chunk stored at rootAddr
   389  // Upon retrieval it creates/updates the index entry for it with metadata corresponding to the chunk contents
   390  func (h *Handler) Load(ctx context.Context, rootAddr storage.Address) (*resource, error) {
   391  	//TODO: Maybe add timeout to context, defaultRetrieveTimeout?
   392  	ctx, cancel := context.WithTimeout(ctx, defaultRetrieveTimeout)
   393  	defer cancel()
   394  	chunk, err := h.chunkStore.Get(ctx, rootAddr)
   395  	if err != nil {
   396  		return nil, NewError(ErrNotFound, err.Error())
   397  	}
   398  
   399  	// create the index entry
   400  	rsrc := &resource{}
   401  
   402  	if err := rsrc.ResourceMetadata.binaryGet(chunk.Data()); err != nil { // Will fail if this is not really a metadata chunk
   403  		return nil, err
   404  	}
   405  
   406  	rsrc.rootAddr, rsrc.metaHash = metadataHash(chunk.Data())
   407  	if !bytes.Equal(rsrc.rootAddr, rootAddr) {
   408  		return nil, NewError(ErrCorruptData, "Corrupt metadata chunk")
   409  	}
   410  	h.set(rootAddr, rsrc)
   411  	log.Trace("resource index load", "rootkey", rootAddr, "name", rsrc.ResourceMetadata.Name, "starttime", rsrc.ResourceMetadata.StartTime, "frequency", rsrc.ResourceMetadata.Frequency)
   412  	return rsrc, nil
   413  }
   414  
   415  // update mutable resource index map with specified content
   416  func (h *Handler) updateIndex(rsrc *resource, chunk storage.Chunk) (*resource, error) {
   417  
   418  	// retrieve metadata from chunk data and check that it matches this mutable resource
   419  	var r SignedResourceUpdate
   420  	if err := r.fromChunk(chunk.Address(), chunk.Data()); err != nil {
   421  		return nil, err
   422  	}
   423  	log.Trace("resource index update", "name", rsrc.ResourceMetadata.Name, "updatekey", chunk.Address(), "period", r.period, "version", r.version)
   424  
   425  	// update our rsrcs entry map
   426  	rsrc.lastKey = chunk.Address()
   427  	rsrc.period = r.period
   428  	rsrc.version = r.version
   429  	rsrc.updated = time.Now()
   430  	rsrc.data = make([]byte, len(r.data))
   431  	rsrc.multihash = r.multihash
   432  	copy(rsrc.data, r.data)
   433  	rsrc.Reader = bytes.NewReader(rsrc.data)
   434  	log.Debug("resource synced", "name", rsrc.ResourceMetadata.Name, "updateAddr", chunk.Address(), "period", rsrc.period, "version", rsrc.version)
   435  	h.set(chunk.Address(), rsrc)
   436  	return rsrc, nil
   437  }
   438  
   439  // Update adds an actual data update
   440  // Uses the Mutable Resource metadata currently loaded in the resources map entry.
   441  // It is the caller's responsibility to make sure that this data is not stale.
   442  // Note that a Mutable Resource update cannot span chunks, and thus has a MAX NET LENGTH 4096, INCLUDING update header data and signature. An error will be returned if the total length of the chunk payload will exceed this limit.
   443  // Update can only check if the caller is trying to overwrite the very last known version, otherwise it just puts the update
   444  // on the network.
   445  func (h *Handler) Update(ctx context.Context, r *SignedResourceUpdate) (storage.Address, error) {
   446  	return h.update(ctx, r)
   447  }
   448  
   449  // create and commit an update
   450  func (h *Handler) update(ctx context.Context, r *SignedResourceUpdate) (updateAddr storage.Address, err error) {
   451  
   452  	// we can't update anything without a store
   453  	if h.chunkStore == nil {
   454  		return nil, NewError(ErrInit, "Call Handler.SetStore() before updating")
   455  	}
   456  
   457  	rsrc := h.get(r.rootAddr)
   458  	if rsrc != nil && rsrc.period != 0 && rsrc.version != 0 && // This is the only cheap check we can do for sure
   459  		rsrc.period == r.period && rsrc.version >= r.version { // without having to lookup update chunks
   460  
   461  		return nil, NewError(ErrInvalidValue, "A former update in this period is already known to exist")
   462  	}
   463  
   464  	chunk, err := r.toChunk() // Serialize the update into a chunk. Fails if data is too big
   465  	if err != nil {
   466  		return nil, err
   467  	}
   468  
   469  	// send the chunk
   470  	h.chunkStore.Put(ctx, chunk)
   471  	log.Trace("resource update", "updateAddr", r.updateAddr, "lastperiod", r.period, "version", r.version, "data", chunk.Data(), "multihash", r.multihash)
   472  
   473  	// update our resources map entry if the new update is older than the one we have, if we have it.
   474  	if rsrc != nil && (r.period > rsrc.period || (rsrc.period == r.period && r.version > rsrc.version)) {
   475  		rsrc.period = r.period
   476  		rsrc.version = r.version
   477  		rsrc.data = make([]byte, len(r.data))
   478  		rsrc.updated = time.Now()
   479  		rsrc.lastKey = r.updateAddr
   480  		rsrc.multihash = r.multihash
   481  		copy(rsrc.data, r.data)
   482  		rsrc.Reader = bytes.NewReader(rsrc.data)
   483  	}
   484  	return r.updateAddr, nil
   485  }
   486  
   487  // Retrieves the resource index value for the given nameHash
   488  func (h *Handler) get(rootAddr storage.Address) *resource {
   489  	if len(rootAddr) < storage.AddressLength {
   490  		log.Warn("Handler.get with invalid rootAddr")
   491  		return nil
   492  	}
   493  	hashKey := *(*uint64)(unsafe.Pointer(&rootAddr[0]))
   494  	h.resourceLock.RLock()
   495  	defer h.resourceLock.RUnlock()
   496  	rsrc := h.resources[hashKey]
   497  	return rsrc
   498  }
   499  
   500  // Sets the resource index value for the given nameHash
   501  func (h *Handler) set(rootAddr storage.Address, rsrc *resource) {
   502  	if len(rootAddr) < storage.AddressLength {
   503  		log.Warn("Handler.set with invalid rootAddr")
   504  		return
   505  	}
   506  	hashKey := *(*uint64)(unsafe.Pointer(&rootAddr[0]))
   507  	h.resourceLock.Lock()
   508  	defer h.resourceLock.Unlock()
   509  	h.resources[hashKey] = rsrc
   510  }
   511  
   512  // Checks if we already have an update on this resource, according to the value in the current state of the resource index
   513  func (h *Handler) hasUpdate(rootAddr storage.Address, period uint32) bool {
   514  	rsrc := h.get(rootAddr)
   515  	return rsrc != nil && rsrc.period == period
   516  }