github.com/SmartMeshFoundation/Spectrum@v0.0.0-20220621030607-452a266fee1e/swarm/network/forwarding.go (about)

     1  // Copyright 2016 The Spectrum Authors
     2  // This file is part of the Spectrum library.
     3  //
     4  // The Spectrum 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 Spectrum 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 Spectrum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package network
    18  
    19  import (
    20  	"fmt"
    21  	"math/rand"
    22  	"time"
    23  
    24  	"github.com/SmartMeshFoundation/Spectrum/log"
    25  	"github.com/SmartMeshFoundation/Spectrum/swarm/storage"
    26  )
    27  
    28  const requesterCount = 3
    29  
    30  /*
    31  forwarder implements the CloudStore interface (use by storage.NetStore)
    32  and serves as the cloud store backend orchestrating storage/retrieval/delivery
    33  via the native bzz protocol
    34  which uses an MSB logarithmic distance-based semi-permanent Kademlia table for
    35  * recursive forwarding style routing for retrieval
    36  * smart syncronisation
    37  */
    38  
    39  type forwarder struct {
    40  	hive *Hive
    41  }
    42  
    43  func NewForwarder(hive *Hive) *forwarder {
    44  	return &forwarder{hive: hive}
    45  }
    46  
    47  // generate a unique id uint64
    48  func generateId() uint64 {
    49  	r := rand.New(rand.NewSource(time.Now().UnixNano()))
    50  	return uint64(r.Int63())
    51  }
    52  
    53  var searchTimeout = 3 * time.Second
    54  
    55  // forwarding logic
    56  // logic propagating retrieve requests to peers given by the kademlia hive
    57  func (self *forwarder) Retrieve(chunk *storage.Chunk) {
    58  	peers := self.hive.getPeers(chunk.Key, 0)
    59  	log.Trace(fmt.Sprintf("forwarder.Retrieve: %v - received %d peers from KΛÐΞMLIΛ...", chunk.Key.Log(), len(peers)))
    60  OUT:
    61  	for _, p := range peers {
    62  		log.Trace(fmt.Sprintf("forwarder.Retrieve: sending retrieveRequest %v to peer [%v]", chunk.Key.Log(), p))
    63  		for _, recipients := range chunk.Req.Requesters {
    64  			for _, recipient := range recipients {
    65  				req := recipient.(*retrieveRequestMsgData)
    66  				if req.from.Addr() == p.Addr() {
    67  					continue OUT
    68  				}
    69  			}
    70  		}
    71  		req := &retrieveRequestMsgData{
    72  			Key: chunk.Key,
    73  			Id:  generateId(),
    74  		}
    75  		var err error
    76  		if p.swap != nil {
    77  			err = p.swap.Add(-1)
    78  		}
    79  		if err == nil {
    80  			p.retrieve(req)
    81  			break OUT
    82  		}
    83  		log.Warn(fmt.Sprintf("forwarder.Retrieve: unable to send retrieveRequest to peer [%v]: %v", chunk.Key.Log(), err))
    84  	}
    85  }
    86  
    87  // requests to specific peers given by the kademlia hive
    88  // except for peers that the store request came from (if any)
    89  // delivery queueing taken care of by syncer
    90  func (self *forwarder) Store(chunk *storage.Chunk) {
    91  	var n int
    92  	msg := &storeRequestMsgData{
    93  		Key:   chunk.Key,
    94  		SData: chunk.SData,
    95  	}
    96  	var source *peer
    97  	if chunk.Source != nil {
    98  		source = chunk.Source.(*peer)
    99  	}
   100  	for _, p := range self.hive.getPeers(chunk.Key, 0) {
   101  		log.Trace(fmt.Sprintf("forwarder.Store: %v %v", p, chunk))
   102  
   103  		if p.syncer != nil && (source == nil || p.Addr() != source.Addr()) {
   104  			n++
   105  			Deliver(p, msg, PropagateReq)
   106  		}
   107  	}
   108  	log.Trace(fmt.Sprintf("forwarder.Store: sent to %v peers (chunk = %v)", n, chunk))
   109  }
   110  
   111  // once a chunk is found deliver it to its requesters unless timed out
   112  func (self *forwarder) Deliver(chunk *storage.Chunk) {
   113  	// iterate over request entries
   114  	for id, requesters := range chunk.Req.Requesters {
   115  		counter := requesterCount
   116  		msg := &storeRequestMsgData{
   117  			Key:   chunk.Key,
   118  			SData: chunk.SData,
   119  		}
   120  		var n int
   121  		var req *retrieveRequestMsgData
   122  		// iterate over requesters with the same id
   123  		for id, r := range requesters {
   124  			req = r.(*retrieveRequestMsgData)
   125  			if req.timeout == nil || req.timeout.After(time.Now()) {
   126  				log.Trace(fmt.Sprintf("forwarder.Deliver: %v -> %v", req.Id, req.from))
   127  				msg.Id = uint64(id)
   128  				Deliver(req.from, msg, DeliverReq)
   129  				n++
   130  				counter--
   131  				if counter <= 0 {
   132  					break
   133  				}
   134  			}
   135  		}
   136  		log.Trace(fmt.Sprintf("forwarder.Deliver: submit chunk %v (request id %v) for delivery to %v peers", chunk.Key.Log(), id, n))
   137  	}
   138  }
   139  
   140  // initiate delivery of a chunk to a particular peer via syncer#addRequest
   141  // depending on syncer mode and priority settings and sync request type
   142  // this either goes via confirmation roundtrip or queued or pushed directly
   143  func Deliver(p *peer, req interface{}, ty int) {
   144  	p.syncer.addRequest(req, ty)
   145  }
   146  
   147  // push chunk over to peer
   148  func Push(p *peer, key storage.Key, priority uint) {
   149  	p.syncer.doDelivery(key, priority, p.syncer.quit)
   150  }