github.com/nitinawathare/ethereumassignment3@v0.0.0-20211021213010-f07344c2b868/go-ethereum/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  	"fmt"
    23  
    24  	"github.com/ethereum/go-ethereum/metrics"
    25  	"github.com/ethereum/go-ethereum/p2p/enode"
    26  	"github.com/ethereum/go-ethereum/swarm/log"
    27  	"github.com/ethereum/go-ethereum/swarm/network"
    28  	"github.com/ethereum/go-ethereum/swarm/spancontext"
    29  	"github.com/ethereum/go-ethereum/swarm/storage"
    30  	"github.com/ethereum/go-ethereum/swarm/tracing"
    31  	opentracing "github.com/opentracing/opentracing-go"
    32  	olog "github.com/opentracing/opentracing-go/log"
    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  	retrieveChunkFail             = metrics.NewRegisteredCounter("network.stream.retrieve_chunks_fail.count", nil)
    44  
    45  	requestFromPeersCount     = metrics.NewRegisteredCounter("network.stream.request_from_peers.count", nil)
    46  	requestFromPeersEachCount = metrics.NewRegisteredCounter("network.stream.request_from_peers_each.count", nil)
    47  )
    48  
    49  type Delivery struct {
    50  	chunkStore storage.SyncChunkStore
    51  	kad        *network.Kademlia
    52  	getPeer    func(enode.ID) *Peer
    53  }
    54  
    55  func NewDelivery(kad *network.Kademlia, chunkStore storage.SyncChunkStore) *Delivery {
    56  	return &Delivery{
    57  		chunkStore: chunkStore,
    58  		kad:        kad,
    59  	}
    60  }
    61  
    62  // SwarmChunkServer implements Server
    63  type SwarmChunkServer struct {
    64  	deliveryC  chan []byte
    65  	batchC     chan []byte
    66  	chunkStore storage.ChunkStore
    67  	currentLen uint64
    68  	quit       chan struct{}
    69  }
    70  
    71  // NewSwarmChunkServer is SwarmChunkServer constructor
    72  func NewSwarmChunkServer(chunkStore storage.ChunkStore) *SwarmChunkServer {
    73  	s := &SwarmChunkServer{
    74  		deliveryC:  make(chan []byte, deliveryCap),
    75  		batchC:     make(chan []byte),
    76  		chunkStore: chunkStore,
    77  		quit:       make(chan struct{}),
    78  	}
    79  	go s.processDeliveries()
    80  	return s
    81  }
    82  
    83  // processDeliveries handles delivered chunk hashes
    84  func (s *SwarmChunkServer) processDeliveries() {
    85  	var hashes []byte
    86  	var batchC chan []byte
    87  	for {
    88  		select {
    89  		case <-s.quit:
    90  			return
    91  		case hash := <-s.deliveryC:
    92  			hashes = append(hashes, hash...)
    93  			batchC = s.batchC
    94  		case batchC <- hashes:
    95  			hashes = nil
    96  			batchC = nil
    97  		}
    98  	}
    99  }
   100  
   101  // SessionIndex returns zero in all cases for SwarmChunkServer.
   102  func (s *SwarmChunkServer) SessionIndex() (uint64, error) {
   103  	return 0, nil
   104  }
   105  
   106  // SetNextBatch
   107  func (s *SwarmChunkServer) SetNextBatch(_, _ uint64) (hashes []byte, from uint64, to uint64, proof *HandoverProof, err error) {
   108  	select {
   109  	case hashes = <-s.batchC:
   110  	case <-s.quit:
   111  		return
   112  	}
   113  
   114  	from = s.currentLen
   115  	s.currentLen += uint64(len(hashes))
   116  	to = s.currentLen
   117  	return
   118  }
   119  
   120  // Close needs to be called on a stream server
   121  func (s *SwarmChunkServer) Close() {
   122  	close(s.quit)
   123  }
   124  
   125  // GetData retrives chunk data from db store
   126  func (s *SwarmChunkServer) GetData(ctx context.Context, key []byte) ([]byte, error) {
   127  	chunk, err := s.chunkStore.Get(ctx, storage.Address(key))
   128  	if err != nil {
   129  		return nil, err
   130  	}
   131  	return chunk.Data(), 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  	HopCount  uint8
   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  		"stream.handle.retrieve")
   149  
   150  	osp.LogFields(olog.String("ref", req.Addr.String()))
   151  
   152  	s, err := sp.getServer(NewStream(swarmChunkServerStreamName, "", true))
   153  	if err != nil {
   154  		return err
   155  	}
   156  	streamer := s.Server.(*SwarmChunkServer)
   157  
   158  	var cancel func()
   159  	// TODO: do something with this hardcoded timeout, maybe use TTL in the future
   160  	ctx = context.WithValue(ctx, "peer", sp.ID().String())
   161  	ctx = context.WithValue(ctx, "hopcount", req.HopCount)
   162  	ctx, cancel = context.WithTimeout(ctx, network.RequestTimeout)
   163  
   164  	go func() {
   165  		select {
   166  		case <-ctx.Done():
   167  		case <-streamer.quit:
   168  		}
   169  		cancel()
   170  	}()
   171  
   172  	go func() {
   173  		defer osp.Finish()
   174  		chunk, err := d.chunkStore.Get(ctx, req.Addr)
   175  		if err != nil {
   176  			retrieveChunkFail.Inc(1)
   177  			log.Debug("ChunkStore.Get can not retrieve chunk", "peer", sp.ID().String(), "addr", req.Addr, "hopcount", req.HopCount, "err", err)
   178  			return
   179  		}
   180  		if req.SkipCheck {
   181  			syncing := false
   182  			osp.LogFields(olog.Bool("skipCheck", true))
   183  
   184  			err = sp.Deliver(ctx, chunk, s.priority, syncing)
   185  			if err != nil {
   186  				log.Warn("ERROR in handleRetrieveRequestMsg", "err", err)
   187  			}
   188  			osp.LogFields(olog.Bool("delivered", true))
   189  			return
   190  		}
   191  		osp.LogFields(olog.Bool("skipCheck", false))
   192  		select {
   193  		case streamer.deliveryC <- chunk.Address()[:]:
   194  		case <-streamer.quit:
   195  		}
   196  
   197  	}()
   198  
   199  	return nil
   200  }
   201  
   202  //Chunk delivery always uses the same message type....
   203  type ChunkDeliveryMsg struct {
   204  	Addr  storage.Address
   205  	SData []byte // the stored chunk Data (incl size)
   206  	peer  *Peer  // set in handleChunkDeliveryMsg
   207  }
   208  
   209  //...but swap accounting needs to disambiguate if it is a delivery for syncing or for retrieval
   210  //as it decides based on message type if it needs to account for this message or not
   211  
   212  //defines a chunk delivery for retrieval (with accounting)
   213  type ChunkDeliveryMsgRetrieval ChunkDeliveryMsg
   214  
   215  //defines a chunk delivery for syncing (without accounting)
   216  type ChunkDeliveryMsgSyncing ChunkDeliveryMsg
   217  
   218  // chunk delivery msg is response to retrieverequest msg
   219  func (d *Delivery) handleChunkDeliveryMsg(ctx context.Context, sp *Peer, req *ChunkDeliveryMsg) error {
   220  	var osp opentracing.Span
   221  	ctx, osp = spancontext.StartSpan(
   222  		ctx,
   223  		"handle.chunk.delivery")
   224  
   225  	processReceivedChunksCount.Inc(1)
   226  
   227  	// retrieve the span for the originating retrieverequest
   228  	spanId := fmt.Sprintf("stream.send.request.%v.%v", sp.ID(), req.Addr)
   229  	span := tracing.ShiftSpanByKey(spanId)
   230  
   231  	log.Trace("handle.chunk.delivery", "ref", req.Addr, "from peer", sp.ID())
   232  
   233  	go func() {
   234  		defer osp.Finish()
   235  
   236  		if span != nil {
   237  			span.LogFields(olog.String("finish", "from handleChunkDeliveryMsg"))
   238  			defer span.Finish()
   239  		}
   240  
   241  		req.peer = sp
   242  		log.Trace("handle.chunk.delivery", "put", req.Addr)
   243  		err := d.chunkStore.Put(ctx, storage.NewChunk(req.Addr, req.SData))
   244  		if err != nil {
   245  			if err == storage.ErrChunkInvalid {
   246  				// we removed this log because it spams the logs
   247  				// TODO: Enable this log line
   248  				// log.Warn("invalid chunk delivered", "peer", sp.ID(), "chunk", req.Addr, )
   249  				req.peer.Drop(err)
   250  			}
   251  		}
   252  		log.Trace("handle.chunk.delivery", "done put", req.Addr, "err", err)
   253  	}()
   254  	return nil
   255  }
   256  
   257  // RequestFromPeers sends a chunk retrieve request to a peer
   258  // The most eligible peer that hasn't already been sent to is chosen
   259  // TODO: define "eligible"
   260  func (d *Delivery) RequestFromPeers(ctx context.Context, req *network.Request) (*enode.ID, chan struct{}, error) {
   261  	requestFromPeersCount.Inc(1)
   262  	var sp *Peer
   263  	spID := req.Source
   264  
   265  	if spID != nil {
   266  		sp = d.getPeer(*spID)
   267  		if sp == nil {
   268  			return nil, nil, fmt.Errorf("source peer %v not found", spID.String())
   269  		}
   270  	} else {
   271  		d.kad.EachConn(req.Addr[:], 255, func(p *network.Peer, po int) bool {
   272  			id := p.ID()
   273  			if p.LightNode {
   274  				// skip light nodes
   275  				return true
   276  			}
   277  			if req.SkipPeer(id.String()) {
   278  				log.Trace("Delivery.RequestFromPeers: skip peer", "peer id", id)
   279  				return true
   280  			}
   281  			sp = d.getPeer(id)
   282  			// sp is nil, when we encounter a peer that is not registered for delivery, i.e. doesn't support the `stream` protocol
   283  			if sp == nil {
   284  				return true
   285  			}
   286  			spID = &id
   287  			return false
   288  		})
   289  		if sp == nil {
   290  			return nil, nil, errors.New("no peer found")
   291  		}
   292  	}
   293  
   294  	// setting this value in the context creates a new span that can persist across the sendpriority queue and the network roundtrip
   295  	// this span will finish only when delivery is handled (or times out)
   296  	ctx = context.WithValue(ctx, tracing.StoreLabelId, "stream.send.request")
   297  	ctx = context.WithValue(ctx, tracing.StoreLabelMeta, fmt.Sprintf("%v.%v", sp.ID(), req.Addr))
   298  	log.Trace("request.from.peers", "peer", sp.ID(), "ref", req.Addr)
   299  	err := sp.SendPriority(ctx, &RetrieveRequestMsg{
   300  		Addr:      req.Addr,
   301  		SkipCheck: req.SkipCheck,
   302  		HopCount:  req.HopCount,
   303  	}, Top)
   304  	if err != nil {
   305  		return nil, nil, err
   306  	}
   307  	requestFromPeersEachCount.Inc(1)
   308  
   309  	return spID, sp.quit, nil
   310  }