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