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