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