github.com/aaa256/atlantis@v0.0.0-20210707112435-42ee889287a2/swarm/network/stream/delivery.go (about)

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