github.com/avence12/go-ethereum@v1.5.10-0.20170320123548-1dfd65f6d047/les/odr.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 les
    18  
    19  import (
    20  	"crypto/rand"
    21  	"encoding/binary"
    22  	"sync"
    23  	"time"
    24  
    25  	"github.com/ethereum/go-ethereum/common/mclock"
    26  	"github.com/ethereum/go-ethereum/ethdb"
    27  	"github.com/ethereum/go-ethereum/light"
    28  	"github.com/ethereum/go-ethereum/log"
    29  	"golang.org/x/net/context"
    30  )
    31  
    32  var (
    33  	softRequestTimeout = time.Millisecond * 500
    34  	hardRequestTimeout = time.Second * 10
    35  	retryPeers         = time.Second * 1
    36  )
    37  
    38  // peerDropFn is a callback type for dropping a peer detected as malicious.
    39  type peerDropFn func(id string)
    40  
    41  type odrPeerSelector interface {
    42  	selectPeerWait(uint64, func(*peer) (bool, time.Duration), <-chan struct{}) *peer
    43  	adjustResponseTime(*poolEntry, time.Duration, bool)
    44  }
    45  
    46  type LesOdr struct {
    47  	light.OdrBackend
    48  	db           ethdb.Database
    49  	stop         chan struct{}
    50  	removePeer   peerDropFn
    51  	mlock, clock sync.Mutex
    52  	sentReqs     map[uint64]*sentReq
    53  	serverPool   odrPeerSelector
    54  }
    55  
    56  func NewLesOdr(db ethdb.Database) *LesOdr {
    57  	return &LesOdr{
    58  		db:       db,
    59  		stop:     make(chan struct{}),
    60  		sentReqs: make(map[uint64]*sentReq),
    61  	}
    62  }
    63  
    64  func (odr *LesOdr) Stop() {
    65  	close(odr.stop)
    66  }
    67  
    68  func (odr *LesOdr) Database() ethdb.Database {
    69  	return odr.db
    70  }
    71  
    72  // validatorFunc is a function that processes a message.
    73  type validatorFunc func(ethdb.Database, *Msg) error
    74  
    75  // sentReq is a request waiting for an answer that satisfies its valFunc
    76  type sentReq struct {
    77  	valFunc  validatorFunc
    78  	sentTo   map[*peer]chan struct{}
    79  	lock     sync.RWMutex  // protects acces to sentTo
    80  	answered chan struct{} // closed and set to nil when any peer answers it
    81  }
    82  
    83  const (
    84  	MsgBlockBodies = iota
    85  	MsgCode
    86  	MsgReceipts
    87  	MsgProofs
    88  	MsgHeaderProofs
    89  )
    90  
    91  // Msg encodes a LES message that delivers reply data for a request
    92  type Msg struct {
    93  	MsgType int
    94  	ReqID   uint64
    95  	Obj     interface{}
    96  }
    97  
    98  // Deliver is called by the LES protocol manager to deliver ODR reply messages to waiting requests
    99  func (self *LesOdr) Deliver(peer *peer, msg *Msg) error {
   100  	var delivered chan struct{}
   101  	self.mlock.Lock()
   102  	req, ok := self.sentReqs[msg.ReqID]
   103  	self.mlock.Unlock()
   104  	if ok {
   105  		req.lock.Lock()
   106  		delivered, ok = req.sentTo[peer]
   107  		req.lock.Unlock()
   108  	}
   109  
   110  	if !ok {
   111  		return errResp(ErrUnexpectedResponse, "reqID = %v", msg.ReqID)
   112  	}
   113  
   114  	if err := req.valFunc(self.db, msg); err != nil {
   115  		peer.Log().Warn("Invalid odr response", "err", err)
   116  		return errResp(ErrInvalidResponse, "reqID = %v", msg.ReqID)
   117  	}
   118  	close(delivered)
   119  	req.lock.Lock()
   120  	delete(req.sentTo, peer)
   121  	if req.answered != nil {
   122  		close(req.answered)
   123  		req.answered = nil
   124  	}
   125  	req.lock.Unlock()
   126  	return nil
   127  }
   128  
   129  func (self *LesOdr) requestPeer(req *sentReq, peer *peer, delivered, timeout chan struct{}, reqWg *sync.WaitGroup) {
   130  	stime := mclock.Now()
   131  	defer func() {
   132  		req.lock.Lock()
   133  		delete(req.sentTo, peer)
   134  		req.lock.Unlock()
   135  		reqWg.Done()
   136  	}()
   137  
   138  	select {
   139  	case <-delivered:
   140  		if self.serverPool != nil {
   141  			self.serverPool.adjustResponseTime(peer.poolEntry, time.Duration(mclock.Now()-stime), false)
   142  		}
   143  		return
   144  	case <-time.After(softRequestTimeout):
   145  		close(timeout)
   146  	case <-self.stop:
   147  		return
   148  	}
   149  
   150  	select {
   151  	case <-delivered:
   152  	case <-time.After(hardRequestTimeout):
   153  		peer.Log().Debug("Request timed out hard")
   154  		go self.removePeer(peer.id)
   155  	case <-self.stop:
   156  		return
   157  	}
   158  	if self.serverPool != nil {
   159  		self.serverPool.adjustResponseTime(peer.poolEntry, time.Duration(mclock.Now()-stime), true)
   160  	}
   161  }
   162  
   163  // networkRequest sends a request to known peers until an answer is received
   164  // or the context is cancelled
   165  func (self *LesOdr) networkRequest(ctx context.Context, lreq LesOdrRequest) error {
   166  	answered := make(chan struct{})
   167  	req := &sentReq{
   168  		valFunc:  lreq.Valid,
   169  		sentTo:   make(map[*peer]chan struct{}),
   170  		answered: answered, // reply delivered by any peer
   171  	}
   172  	reqID := getNextReqID()
   173  	self.mlock.Lock()
   174  	self.sentReqs[reqID] = req
   175  	self.mlock.Unlock()
   176  
   177  	reqWg := new(sync.WaitGroup)
   178  	reqWg.Add(1)
   179  	defer reqWg.Done()
   180  	go func() {
   181  		reqWg.Wait()
   182  		self.mlock.Lock()
   183  		delete(self.sentReqs, reqID)
   184  		self.mlock.Unlock()
   185  	}()
   186  
   187  	exclude := make(map[*peer]struct{})
   188  	for {
   189  		var p *peer
   190  		if self.serverPool != nil {
   191  			p = self.serverPool.selectPeerWait(reqID, func(p *peer) (bool, time.Duration) {
   192  				if _, ok := exclude[p]; ok || !lreq.CanSend(p) {
   193  					return false, 0
   194  				}
   195  				return true, p.fcServer.CanSend(lreq.GetCost(p))
   196  			}, ctx.Done())
   197  		}
   198  		if p == nil {
   199  			select {
   200  			case <-ctx.Done():
   201  				return ctx.Err()
   202  			case <-req.answered:
   203  				return nil
   204  			case <-time.After(retryPeers):
   205  			}
   206  		} else {
   207  			exclude[p] = struct{}{}
   208  			delivered := make(chan struct{})
   209  			timeout := make(chan struct{})
   210  			req.lock.Lock()
   211  			req.sentTo[p] = delivered
   212  			req.lock.Unlock()
   213  			reqWg.Add(1)
   214  			cost := lreq.GetCost(p)
   215  			p.fcServer.SendRequest(reqID, cost)
   216  			go self.requestPeer(req, p, delivered, timeout, reqWg)
   217  			lreq.Request(reqID, p)
   218  
   219  			select {
   220  			case <-ctx.Done():
   221  				return ctx.Err()
   222  			case <-answered:
   223  				return nil
   224  			case <-timeout:
   225  			}
   226  		}
   227  	}
   228  }
   229  
   230  // Retrieve tries to fetch an object from the local db, then from the LES network.
   231  // If the network retrieval was successful, it stores the object in local db.
   232  func (self *LesOdr) Retrieve(ctx context.Context, req light.OdrRequest) (err error) {
   233  	lreq := LesRequest(req)
   234  	err = self.networkRequest(ctx, lreq)
   235  	if err == nil {
   236  		// retrieved from network, store in db
   237  		req.StoreResult(self.db)
   238  	} else {
   239  		log.Debug("Failed to retrieve data from network", "err", err)
   240  	}
   241  	return
   242  }
   243  
   244  func getNextReqID() uint64 {
   245  	var rnd [8]byte
   246  	rand.Read(rnd[:])
   247  	return binary.BigEndian.Uint64(rnd[:])
   248  }