github.com/daragao/go-ethereum@v1.8.14-0.20180809141559-45eaef243198/swarm/network/stream/delivery.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  package stream
    18  
    19  import (
    20  	"context"
    21  	"errors"
    22  	"time"
    23  
    24  	"github.com/ethereum/go-ethereum/common"
    25  	"github.com/ethereum/go-ethereum/metrics"
    26  	"github.com/ethereum/go-ethereum/p2p/discover"
    27  	"github.com/ethereum/go-ethereum/swarm/log"
    28  	"github.com/ethereum/go-ethereum/swarm/network"
    29  	"github.com/ethereum/go-ethereum/swarm/spancontext"
    30  	"github.com/ethereum/go-ethereum/swarm/storage"
    31  	opentracing "github.com/opentracing/opentracing-go"
    32  )
    33  
    34  const (
    35  	swarmChunkServerStreamName = "RETRIEVE_REQUEST"
    36  	deliveryCap                = 32
    37  )
    38  
    39  var (
    40  	processReceivedChunksCount    = metrics.NewRegisteredCounter("network.stream.received_chunks.count", nil)
    41  	handleRetrieveRequestMsgCount = metrics.NewRegisteredCounter("network.stream.handle_retrieve_request_msg.count", nil)
    42  
    43  	requestFromPeersCount     = metrics.NewRegisteredCounter("network.stream.request_from_peers.count", nil)
    44  	requestFromPeersEachCount = metrics.NewRegisteredCounter("network.stream.request_from_peers_each.count", nil)
    45  )
    46  
    47  type Delivery struct {
    48  	db       *storage.DBAPI
    49  	overlay  network.Overlay
    50  	receiveC chan *ChunkDeliveryMsg
    51  	getPeer  func(discover.NodeID) *Peer
    52  }
    53  
    54  func NewDelivery(overlay network.Overlay, db *storage.DBAPI) *Delivery {
    55  	d := &Delivery{
    56  		db:       db,
    57  		overlay:  overlay,
    58  		receiveC: make(chan *ChunkDeliveryMsg, deliveryCap),
    59  	}
    60  
    61  	go d.processReceivedChunks()
    62  	return d
    63  }
    64  
    65  // SwarmChunkServer implements Server
    66  type SwarmChunkServer struct {
    67  	deliveryC  chan []byte
    68  	batchC     chan []byte
    69  	db         *storage.DBAPI
    70  	currentLen uint64
    71  	quit       chan struct{}
    72  }
    73  
    74  // NewSwarmChunkServer is SwarmChunkServer constructor
    75  func NewSwarmChunkServer(db *storage.DBAPI) *SwarmChunkServer {
    76  	s := &SwarmChunkServer{
    77  		deliveryC: make(chan []byte, deliveryCap),
    78  		batchC:    make(chan []byte),
    79  		db:        db,
    80  		quit:      make(chan struct{}),
    81  	}
    82  	go s.processDeliveries()
    83  	return s
    84  }
    85  
    86  // processDeliveries handles delivered chunk hashes
    87  func (s *SwarmChunkServer) processDeliveries() {
    88  	var hashes []byte
    89  	var batchC chan []byte
    90  	for {
    91  		select {
    92  		case <-s.quit:
    93  			return
    94  		case hash := <-s.deliveryC:
    95  			hashes = append(hashes, hash...)
    96  			batchC = s.batchC
    97  		case batchC <- hashes:
    98  			hashes = nil
    99  			batchC = nil
   100  		}
   101  	}
   102  }
   103  
   104  // SetNextBatch
   105  func (s *SwarmChunkServer) SetNextBatch(_, _ uint64) (hashes []byte, from uint64, to uint64, proof *HandoverProof, err error) {
   106  	select {
   107  	case hashes = <-s.batchC:
   108  	case <-s.quit:
   109  		return
   110  	}
   111  
   112  	from = s.currentLen
   113  	s.currentLen += uint64(len(hashes))
   114  	to = s.currentLen
   115  	return
   116  }
   117  
   118  // Close needs to be called on a stream server
   119  func (s *SwarmChunkServer) Close() {
   120  	close(s.quit)
   121  }
   122  
   123  // GetData retrives chunk data from db store
   124  func (s *SwarmChunkServer) GetData(ctx context.Context, key []byte) ([]byte, error) {
   125  	chunk, err := s.db.Get(ctx, storage.Address(key))
   126  	if err == storage.ErrFetching {
   127  		<-chunk.ReqC
   128  	} else if err != nil {
   129  		return nil, err
   130  	}
   131  	return chunk.SData, nil
   132  }
   133  
   134  // RetrieveRequestMsg is the protocol msg for chunk retrieve requests
   135  type RetrieveRequestMsg struct {
   136  	Addr      storage.Address
   137  	SkipCheck bool
   138  }
   139  
   140  func (d *Delivery) handleRetrieveRequestMsg(ctx context.Context, sp *Peer, req *RetrieveRequestMsg) error {
   141  	log.Trace("received request", "peer", sp.ID(), "hash", req.Addr)
   142  	handleRetrieveRequestMsgCount.Inc(1)
   143  
   144  	var osp opentracing.Span
   145  	ctx, osp = spancontext.StartSpan(
   146  		ctx,
   147  		"retrieve.request")
   148  	defer osp.Finish()
   149  
   150  	s, err := sp.getServer(NewStream(swarmChunkServerStreamName, "", false))
   151  	if err != nil {
   152  		return err
   153  	}
   154  	streamer := s.Server.(*SwarmChunkServer)
   155  	chunk, created := d.db.GetOrCreateRequest(ctx, req.Addr)
   156  	if chunk.ReqC != nil {
   157  		if created {
   158  			if err := d.RequestFromPeers(ctx, chunk.Addr[:], true, sp.ID()); err != nil {
   159  				log.Warn("unable to forward chunk request", "peer", sp.ID(), "key", chunk.Addr, "err", err)
   160  				chunk.SetErrored(storage.ErrChunkForward)
   161  				return nil
   162  			}
   163  		}
   164  		go func() {
   165  			var osp opentracing.Span
   166  			ctx, osp = spancontext.StartSpan(
   167  				ctx,
   168  				"waiting.delivery")
   169  			defer osp.Finish()
   170  
   171  			t := time.NewTimer(10 * time.Minute)
   172  			defer t.Stop()
   173  
   174  			log.Debug("waiting delivery", "peer", sp.ID(), "hash", req.Addr, "node", common.Bytes2Hex(d.overlay.BaseAddr()), "created", created)
   175  			start := time.Now()
   176  			select {
   177  			case <-chunk.ReqC:
   178  				log.Debug("retrieve request ReqC closed", "peer", sp.ID(), "hash", req.Addr, "time", time.Since(start))
   179  			case <-t.C:
   180  				log.Debug("retrieve request timeout", "peer", sp.ID(), "hash", req.Addr)
   181  				chunk.SetErrored(storage.ErrChunkTimeout)
   182  				return
   183  			}
   184  			chunk.SetErrored(nil)
   185  
   186  			if req.SkipCheck {
   187  				err := sp.Deliver(ctx, chunk, s.priority)
   188  				if err != nil {
   189  					log.Warn("ERROR in handleRetrieveRequestMsg, DROPPING peer!", "err", err)
   190  					sp.Drop(err)
   191  				}
   192  			}
   193  			streamer.deliveryC <- chunk.Addr[:]
   194  		}()
   195  		return nil
   196  	}
   197  	// TODO: call the retrieve function of the outgoing syncer
   198  	if req.SkipCheck {
   199  		log.Trace("deliver", "peer", sp.ID(), "hash", chunk.Addr)
   200  		if length := len(chunk.SData); length < 9 {
   201  			log.Error("Chunk.SData to deliver is too short", "len(chunk.SData)", length, "address", chunk.Addr)
   202  		}
   203  		return sp.Deliver(ctx, chunk, s.priority)
   204  	}
   205  	streamer.deliveryC <- chunk.Addr[:]
   206  	return nil
   207  }
   208  
   209  type ChunkDeliveryMsg struct {
   210  	Addr  storage.Address
   211  	SData []byte // the stored chunk Data (incl size)
   212  	peer  *Peer  // set in handleChunkDeliveryMsg
   213  }
   214  
   215  func (d *Delivery) handleChunkDeliveryMsg(ctx context.Context, sp *Peer, req *ChunkDeliveryMsg) error {
   216  	var osp opentracing.Span
   217  	ctx, osp = spancontext.StartSpan(
   218  		ctx,
   219  		"chunk.delivery")
   220  	defer osp.Finish()
   221  
   222  	req.peer = sp
   223  	d.receiveC <- req
   224  	return nil
   225  }
   226  
   227  func (d *Delivery) processReceivedChunks() {
   228  R:
   229  	for req := range d.receiveC {
   230  		processReceivedChunksCount.Inc(1)
   231  
   232  		// this should be has locally
   233  		chunk, err := d.db.Get(context.TODO(), req.Addr)
   234  		if err == nil {
   235  			continue R
   236  		}
   237  		if err != storage.ErrFetching {
   238  			log.Error("processReceivedChunks db error", "addr", req.Addr, "err", err, "chunk", chunk)
   239  			continue R
   240  		}
   241  		select {
   242  		case <-chunk.ReqC:
   243  			log.Error("someone else delivered?", "hash", chunk.Addr.Hex())
   244  			continue R
   245  		default:
   246  		}
   247  		chunk.SData = req.SData
   248  		d.db.Put(context.TODO(), chunk)
   249  
   250  		go func(req *ChunkDeliveryMsg) {
   251  			err := chunk.WaitToStore()
   252  			if err == storage.ErrChunkInvalid {
   253  				req.peer.Drop(err)
   254  			}
   255  		}(req)
   256  	}
   257  }
   258  
   259  // RequestFromPeers sends a chunk retrieve request to
   260  func (d *Delivery) RequestFromPeers(ctx context.Context, hash []byte, skipCheck bool, peersToSkip ...discover.NodeID) error {
   261  	var success bool
   262  	var err error
   263  	requestFromPeersCount.Inc(1)
   264  
   265  	d.overlay.EachConn(hash, 255, func(p network.OverlayConn, po int, nn bool) bool {
   266  		spId := p.(network.Peer).ID()
   267  		for _, p := range peersToSkip {
   268  			if p == spId {
   269  				log.Trace("Delivery.RequestFromPeers: skip peer", "peer", spId)
   270  				return true
   271  			}
   272  		}
   273  		sp := d.getPeer(spId)
   274  		if sp == nil {
   275  			log.Warn("Delivery.RequestFromPeers: peer not found", "id", spId)
   276  			return true
   277  		}
   278  		err = sp.SendPriority(ctx, &RetrieveRequestMsg{
   279  			Addr:      hash,
   280  			SkipCheck: skipCheck,
   281  		}, Top)
   282  		if err != nil {
   283  			return true
   284  		}
   285  		requestFromPeersEachCount.Inc(1)
   286  		success = true
   287  		return false
   288  	})
   289  	if success {
   290  		return nil
   291  	}
   292  	return errors.New("no peer found")
   293  }