github.com/murrekatt/go-ethereum@v1.5.8-0.20170123175102-fc52f2c007fb/swarm/network/forwarding.go (about)

     1  // Copyright 2016 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  package network
    18  
    19  import (
    20  	"math/rand"
    21  	"time"
    22  
    23  	"github.com/ethereum/go-ethereum/logger"
    24  	"github.com/ethereum/go-ethereum/logger/glog"
    25  	"github.com/ethereum/go-ethereum/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  	glog.V(logger.Detail).Infof("forwarder.Retrieve: %v - received %d peers from KΛÐΞMLIΛ...", chunk.Key.Log(), len(peers))
    60  OUT:
    61  	for _, p := range peers {
    62  		glog.V(logger.Detail).Infof("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  		glog.V(logger.Warn).Infof("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  		glog.V(logger.Detail).Infof("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  	glog.V(logger.Detail).Infof("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  				glog.V(logger.Detail).Infof("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  		glog.V(logger.Detail).Infof("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  }