github.com/letterj/go-ethereum@v1.8.22-0.20190204142846-520024dfd689/les/serverpool.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 implements the Light Ethereum Subprotocol.
    18  package les
    19  
    20  import (
    21  	"crypto/ecdsa"
    22  	"fmt"
    23  	"io"
    24  	"math"
    25  	"math/rand"
    26  	"net"
    27  	"strconv"
    28  	"sync"
    29  	"time"
    30  
    31  	"github.com/ethereum/go-ethereum/common/mclock"
    32  	"github.com/ethereum/go-ethereum/crypto"
    33  	"github.com/ethereum/go-ethereum/ethdb"
    34  	"github.com/ethereum/go-ethereum/log"
    35  	"github.com/ethereum/go-ethereum/p2p"
    36  	"github.com/ethereum/go-ethereum/p2p/discv5"
    37  	"github.com/ethereum/go-ethereum/p2p/enode"
    38  	"github.com/ethereum/go-ethereum/rlp"
    39  )
    40  
    41  const (
    42  	// After a connection has been ended or timed out, there is a waiting period
    43  	// before it can be selected for connection again.
    44  	// waiting period = base delay * (1 + random(1))
    45  	// base delay = shortRetryDelay for the first shortRetryCnt times after a
    46  	// successful connection, after that longRetryDelay is applied
    47  	shortRetryCnt   = 5
    48  	shortRetryDelay = time.Second * 5
    49  	longRetryDelay  = time.Minute * 10
    50  	// maxNewEntries is the maximum number of newly discovered (never connected) nodes.
    51  	// If the limit is reached, the least recently discovered one is thrown out.
    52  	maxNewEntries = 1000
    53  	// maxKnownEntries is the maximum number of known (already connected) nodes.
    54  	// If the limit is reached, the least recently connected one is thrown out.
    55  	// (not that unlike new entries, known entries are persistent)
    56  	maxKnownEntries = 1000
    57  	// target for simultaneously connected servers
    58  	targetServerCount = 5
    59  	// target for servers selected from the known table
    60  	// (we leave room for trying new ones if there is any)
    61  	targetKnownSelect = 3
    62  	// after dialTimeout, consider the server unavailable and adjust statistics
    63  	dialTimeout = time.Second * 30
    64  	// targetConnTime is the minimum expected connection duration before a server
    65  	// drops a client without any specific reason
    66  	targetConnTime = time.Minute * 10
    67  	// new entry selection weight calculation based on most recent discovery time:
    68  	// unity until discoverExpireStart, then exponential decay with discoverExpireConst
    69  	discoverExpireStart = time.Minute * 20
    70  	discoverExpireConst = time.Minute * 20
    71  	// known entry selection weight is dropped by a factor of exp(-failDropLn) after
    72  	// each unsuccessful connection (restored after a successful one)
    73  	failDropLn = 0.1
    74  	// known node connection success and quality statistics have a long term average
    75  	// and a short term value which is adjusted exponentially with a factor of
    76  	// pstatRecentAdjust with each dial/connection and also returned exponentially
    77  	// to the average with the time constant pstatReturnToMeanTC
    78  	pstatReturnToMeanTC = time.Hour
    79  	// node address selection weight is dropped by a factor of exp(-addrFailDropLn) after
    80  	// each unsuccessful connection (restored after a successful one)
    81  	addrFailDropLn = math.Ln2
    82  	// responseScoreTC and delayScoreTC are exponential decay time constants for
    83  	// calculating selection chances from response times and block delay times
    84  	responseScoreTC = time.Millisecond * 100
    85  	delayScoreTC    = time.Second * 5
    86  	timeoutPow      = 10
    87  	// initStatsWeight is used to initialize previously unknown peers with good
    88  	// statistics to give a chance to prove themselves
    89  	initStatsWeight = 1
    90  )
    91  
    92  // connReq represents a request for peer connection.
    93  type connReq struct {
    94  	p      *peer
    95  	node   *enode.Node
    96  	result chan *poolEntry
    97  }
    98  
    99  // disconnReq represents a request for peer disconnection.
   100  type disconnReq struct {
   101  	entry   *poolEntry
   102  	stopped bool
   103  	done    chan struct{}
   104  }
   105  
   106  // registerReq represents a request for peer registration.
   107  type registerReq struct {
   108  	entry *poolEntry
   109  	done  chan struct{}
   110  }
   111  
   112  // serverPool implements a pool for storing and selecting newly discovered and already
   113  // known light server nodes. It received discovered nodes, stores statistics about
   114  // known nodes and takes care of always having enough good quality servers connected.
   115  type serverPool struct {
   116  	db     ethdb.Database
   117  	dbKey  []byte
   118  	server *p2p.Server
   119  	quit   chan struct{}
   120  	wg     *sync.WaitGroup
   121  	connWg sync.WaitGroup
   122  
   123  	topic discv5.Topic
   124  
   125  	discSetPeriod chan time.Duration
   126  	discNodes     chan *enode.Node
   127  	discLookups   chan bool
   128  
   129  	trustedNodes         map[enode.ID]*enode.Node
   130  	entries              map[enode.ID]*poolEntry
   131  	timeout, enableRetry chan *poolEntry
   132  	adjustStats          chan poolStatAdjust
   133  
   134  	knownQueue, newQueue       poolEntryQueue
   135  	knownSelect, newSelect     *weightedRandomSelect
   136  	knownSelected, newSelected int
   137  	fastDiscover               bool
   138  	connCh                     chan *connReq
   139  	disconnCh                  chan *disconnReq
   140  	registerCh                 chan *registerReq
   141  }
   142  
   143  // newServerPool creates a new serverPool instance
   144  func newServerPool(db ethdb.Database, quit chan struct{}, wg *sync.WaitGroup, trustedNodes []string) *serverPool {
   145  	pool := &serverPool{
   146  		db:           db,
   147  		quit:         quit,
   148  		wg:           wg,
   149  		entries:      make(map[enode.ID]*poolEntry),
   150  		timeout:      make(chan *poolEntry, 1),
   151  		adjustStats:  make(chan poolStatAdjust, 100),
   152  		enableRetry:  make(chan *poolEntry, 1),
   153  		connCh:       make(chan *connReq),
   154  		disconnCh:    make(chan *disconnReq),
   155  		registerCh:   make(chan *registerReq),
   156  		knownSelect:  newWeightedRandomSelect(),
   157  		newSelect:    newWeightedRandomSelect(),
   158  		fastDiscover: true,
   159  		trustedNodes: parseTrustedNodes(trustedNodes),
   160  	}
   161  
   162  	pool.knownQueue = newPoolEntryQueue(maxKnownEntries, pool.removeEntry)
   163  	pool.newQueue = newPoolEntryQueue(maxNewEntries, pool.removeEntry)
   164  	return pool
   165  }
   166  
   167  func (pool *serverPool) start(server *p2p.Server, topic discv5.Topic) {
   168  	pool.server = server
   169  	pool.topic = topic
   170  	pool.dbKey = append([]byte("serverPool/"), []byte(topic)...)
   171  	pool.wg.Add(1)
   172  	pool.loadNodes()
   173  	pool.connectToTrustedNodes()
   174  
   175  	if pool.server.DiscV5 != nil {
   176  		pool.discSetPeriod = make(chan time.Duration, 1)
   177  		pool.discNodes = make(chan *enode.Node, 100)
   178  		pool.discLookups = make(chan bool, 100)
   179  		go pool.discoverNodes()
   180  	}
   181  	pool.checkDial()
   182  	go pool.eventLoop()
   183  }
   184  
   185  // discoverNodes wraps SearchTopic, converting result nodes to enode.Node.
   186  func (pool *serverPool) discoverNodes() {
   187  	ch := make(chan *discv5.Node)
   188  	go func() {
   189  		pool.server.DiscV5.SearchTopic(pool.topic, pool.discSetPeriod, ch, pool.discLookups)
   190  		close(ch)
   191  	}()
   192  	for n := range ch {
   193  		pubkey, err := decodePubkey64(n.ID[:])
   194  		if err != nil {
   195  			continue
   196  		}
   197  		pool.discNodes <- enode.NewV4(pubkey, n.IP, int(n.TCP), int(n.UDP))
   198  	}
   199  }
   200  
   201  // connect should be called upon any incoming connection. If the connection has been
   202  // dialed by the server pool recently, the appropriate pool entry is returned.
   203  // Otherwise, the connection should be rejected.
   204  // Note that whenever a connection has been accepted and a pool entry has been returned,
   205  // disconnect should also always be called.
   206  func (pool *serverPool) connect(p *peer, node *enode.Node) *poolEntry {
   207  	log.Debug("Connect new entry", "enode", p.id)
   208  	req := &connReq{p: p, node: node, result: make(chan *poolEntry, 1)}
   209  	select {
   210  	case pool.connCh <- req:
   211  	case <-pool.quit:
   212  		return nil
   213  	}
   214  	return <-req.result
   215  }
   216  
   217  // registered should be called after a successful handshake
   218  func (pool *serverPool) registered(entry *poolEntry) {
   219  	log.Debug("Registered new entry", "enode", entry.node.ID())
   220  	req := &registerReq{entry: entry, done: make(chan struct{})}
   221  	select {
   222  	case pool.registerCh <- req:
   223  	case <-pool.quit:
   224  		return
   225  	}
   226  	<-req.done
   227  }
   228  
   229  // disconnect should be called when ending a connection. Service quality statistics
   230  // can be updated optionally (not updated if no registration happened, in this case
   231  // only connection statistics are updated, just like in case of timeout)
   232  func (pool *serverPool) disconnect(entry *poolEntry) {
   233  	stopped := false
   234  	select {
   235  	case <-pool.quit:
   236  		stopped = true
   237  	default:
   238  	}
   239  	log.Debug("Disconnected old entry", "enode", entry.node.ID())
   240  	req := &disconnReq{entry: entry, stopped: stopped, done: make(chan struct{})}
   241  
   242  	// Block until disconnection request is served.
   243  	pool.disconnCh <- req
   244  	<-req.done
   245  }
   246  
   247  const (
   248  	pseBlockDelay = iota
   249  	pseResponseTime
   250  	pseResponseTimeout
   251  )
   252  
   253  // poolStatAdjust records are sent to adjust peer block delay/response time statistics
   254  type poolStatAdjust struct {
   255  	adjustType int
   256  	entry      *poolEntry
   257  	time       time.Duration
   258  }
   259  
   260  // adjustBlockDelay adjusts the block announce delay statistics of a node
   261  func (pool *serverPool) adjustBlockDelay(entry *poolEntry, time time.Duration) {
   262  	if entry == nil {
   263  		return
   264  	}
   265  	pool.adjustStats <- poolStatAdjust{pseBlockDelay, entry, time}
   266  }
   267  
   268  // adjustResponseTime adjusts the request response time statistics of a node
   269  func (pool *serverPool) adjustResponseTime(entry *poolEntry, time time.Duration, timeout bool) {
   270  	if entry == nil {
   271  		return
   272  	}
   273  	if timeout {
   274  		pool.adjustStats <- poolStatAdjust{pseResponseTimeout, entry, time}
   275  	} else {
   276  		pool.adjustStats <- poolStatAdjust{pseResponseTime, entry, time}
   277  	}
   278  }
   279  
   280  // eventLoop handles pool events and mutex locking for all internal functions
   281  func (pool *serverPool) eventLoop() {
   282  	lookupCnt := 0
   283  	var convTime mclock.AbsTime
   284  	if pool.discSetPeriod != nil {
   285  		pool.discSetPeriod <- time.Millisecond * 100
   286  	}
   287  
   288  	// disconnect updates service quality statistics depending on the connection time
   289  	// and disconnection initiator.
   290  	disconnect := func(req *disconnReq, stopped bool) {
   291  		// Handle peer disconnection requests.
   292  		entry := req.entry
   293  		if entry.state == psRegistered {
   294  			connAdjust := float64(mclock.Now()-entry.regTime) / float64(targetConnTime)
   295  			if connAdjust > 1 {
   296  				connAdjust = 1
   297  			}
   298  			if stopped {
   299  				// disconnect requested by ourselves.
   300  				entry.connectStats.add(1, connAdjust)
   301  			} else {
   302  				// disconnect requested by server side.
   303  				entry.connectStats.add(connAdjust, 1)
   304  			}
   305  		}
   306  		entry.state = psNotConnected
   307  
   308  		if entry.knownSelected {
   309  			pool.knownSelected--
   310  		} else {
   311  			pool.newSelected--
   312  		}
   313  		pool.setRetryDial(entry)
   314  		pool.connWg.Done()
   315  		close(req.done)
   316  	}
   317  
   318  	for {
   319  		select {
   320  		case entry := <-pool.timeout:
   321  			if !entry.removed {
   322  				pool.checkDialTimeout(entry)
   323  			}
   324  
   325  		case entry := <-pool.enableRetry:
   326  			if !entry.removed {
   327  				entry.delayedRetry = false
   328  				pool.updateCheckDial(entry)
   329  			}
   330  
   331  		case adj := <-pool.adjustStats:
   332  			switch adj.adjustType {
   333  			case pseBlockDelay:
   334  				adj.entry.delayStats.add(float64(adj.time), 1)
   335  			case pseResponseTime:
   336  				adj.entry.responseStats.add(float64(adj.time), 1)
   337  				adj.entry.timeoutStats.add(0, 1)
   338  			case pseResponseTimeout:
   339  				adj.entry.timeoutStats.add(1, 1)
   340  			}
   341  
   342  		case node := <-pool.discNodes:
   343  			if pool.trustedNodes[node.ID()] == nil {
   344  				entry := pool.findOrNewNode(node)
   345  				pool.updateCheckDial(entry)
   346  			}
   347  
   348  		case conv := <-pool.discLookups:
   349  			if conv {
   350  				if lookupCnt == 0 {
   351  					convTime = mclock.Now()
   352  				}
   353  				lookupCnt++
   354  				if pool.fastDiscover && (lookupCnt == 50 || time.Duration(mclock.Now()-convTime) > time.Minute) {
   355  					pool.fastDiscover = false
   356  					if pool.discSetPeriod != nil {
   357  						pool.discSetPeriod <- time.Minute
   358  					}
   359  				}
   360  			}
   361  
   362  		case req := <-pool.connCh:
   363  			if pool.trustedNodes[req.p.ID()] != nil {
   364  				// ignore trusted nodes
   365  				req.result <- nil
   366  			} else {
   367  				// Handle peer connection requests.
   368  				entry := pool.entries[req.p.ID()]
   369  				if entry == nil {
   370  					entry = pool.findOrNewNode(req.node)
   371  				}
   372  				if entry.state == psConnected || entry.state == psRegistered {
   373  					req.result <- nil
   374  					continue
   375  				}
   376  				pool.connWg.Add(1)
   377  				entry.peer = req.p
   378  				entry.state = psConnected
   379  				addr := &poolEntryAddress{
   380  					ip:       req.node.IP(),
   381  					port:     uint16(req.node.TCP()),
   382  					lastSeen: mclock.Now(),
   383  				}
   384  				entry.lastConnected = addr
   385  				entry.addr = make(map[string]*poolEntryAddress)
   386  				entry.addr[addr.strKey()] = addr
   387  				entry.addrSelect = *newWeightedRandomSelect()
   388  				entry.addrSelect.update(addr)
   389  				req.result <- entry
   390  			}
   391  
   392  		case req := <-pool.registerCh:
   393  			// Handle peer registration requests.
   394  			entry := req.entry
   395  			entry.state = psRegistered
   396  			entry.regTime = mclock.Now()
   397  			if !entry.known {
   398  				pool.newQueue.remove(entry)
   399  				entry.known = true
   400  			}
   401  			pool.knownQueue.setLatest(entry)
   402  			entry.shortRetry = shortRetryCnt
   403  			close(req.done)
   404  
   405  		case req := <-pool.disconnCh:
   406  			// Handle peer disconnection requests.
   407  			disconnect(req, req.stopped)
   408  
   409  		case <-pool.quit:
   410  			if pool.discSetPeriod != nil {
   411  				close(pool.discSetPeriod)
   412  			}
   413  
   414  			// Spawn a goroutine to close the disconnCh after all connections are disconnected.
   415  			go func() {
   416  				pool.connWg.Wait()
   417  				close(pool.disconnCh)
   418  			}()
   419  
   420  			// Handle all remaining disconnection requests before exit.
   421  			for req := range pool.disconnCh {
   422  				disconnect(req, true)
   423  			}
   424  			pool.saveNodes()
   425  			pool.wg.Done()
   426  			return
   427  		}
   428  	}
   429  }
   430  
   431  func (pool *serverPool) findOrNewNode(node *enode.Node) *poolEntry {
   432  	now := mclock.Now()
   433  	entry := pool.entries[node.ID()]
   434  	if entry == nil {
   435  		log.Debug("Discovered new entry", "id", node.ID())
   436  		entry = &poolEntry{
   437  			node:       node,
   438  			addr:       make(map[string]*poolEntryAddress),
   439  			addrSelect: *newWeightedRandomSelect(),
   440  			shortRetry: shortRetryCnt,
   441  		}
   442  		pool.entries[node.ID()] = entry
   443  		// initialize previously unknown peers with good statistics to give a chance to prove themselves
   444  		entry.connectStats.add(1, initStatsWeight)
   445  		entry.delayStats.add(0, initStatsWeight)
   446  		entry.responseStats.add(0, initStatsWeight)
   447  		entry.timeoutStats.add(0, initStatsWeight)
   448  	}
   449  	entry.lastDiscovered = now
   450  	addr := &poolEntryAddress{ip: node.IP(), port: uint16(node.TCP())}
   451  	if a, ok := entry.addr[addr.strKey()]; ok {
   452  		addr = a
   453  	} else {
   454  		entry.addr[addr.strKey()] = addr
   455  	}
   456  	addr.lastSeen = now
   457  	entry.addrSelect.update(addr)
   458  	if !entry.known {
   459  		pool.newQueue.setLatest(entry)
   460  	}
   461  	return entry
   462  }
   463  
   464  // loadNodes loads known nodes and their statistics from the database
   465  func (pool *serverPool) loadNodes() {
   466  	enc, err := pool.db.Get(pool.dbKey)
   467  	if err != nil {
   468  		return
   469  	}
   470  	var list []*poolEntry
   471  	err = rlp.DecodeBytes(enc, &list)
   472  	if err != nil {
   473  		log.Debug("Failed to decode node list", "err", err)
   474  		return
   475  	}
   476  	for _, e := range list {
   477  		log.Debug("Loaded server stats", "id", e.node.ID(), "fails", e.lastConnected.fails,
   478  			"conn", fmt.Sprintf("%v/%v", e.connectStats.avg, e.connectStats.weight),
   479  			"delay", fmt.Sprintf("%v/%v", time.Duration(e.delayStats.avg), e.delayStats.weight),
   480  			"response", fmt.Sprintf("%v/%v", time.Duration(e.responseStats.avg), e.responseStats.weight),
   481  			"timeout", fmt.Sprintf("%v/%v", e.timeoutStats.avg, e.timeoutStats.weight))
   482  		pool.entries[e.node.ID()] = e
   483  		if pool.trustedNodes[e.node.ID()] == nil {
   484  			pool.knownQueue.setLatest(e)
   485  			pool.knownSelect.update((*knownEntry)(e))
   486  		}
   487  	}
   488  }
   489  
   490  // connectToTrustedNodes adds trusted server nodes as static trusted peers.
   491  //
   492  // Note: trusted nodes are not handled by the server pool logic, they are not
   493  // added to either the known or new selection pools. They are connected/reconnected
   494  // by p2p.Server whenever possible.
   495  func (pool *serverPool) connectToTrustedNodes() {
   496  	//connect to trusted nodes
   497  	for _, node := range pool.trustedNodes {
   498  		pool.server.AddTrustedPeer(node)
   499  		pool.server.AddPeer(node)
   500  		log.Debug("Added trusted node", "id", node.ID().String())
   501  	}
   502  }
   503  
   504  // parseTrustedNodes returns valid and parsed enodes
   505  func parseTrustedNodes(trustedNodes []string) map[enode.ID]*enode.Node {
   506  	nodes := make(map[enode.ID]*enode.Node)
   507  
   508  	for _, node := range trustedNodes {
   509  		node, err := enode.ParseV4(node)
   510  		if err != nil {
   511  			log.Warn("Trusted node URL invalid", "enode", node, "err", err)
   512  			continue
   513  		}
   514  		nodes[node.ID()] = node
   515  	}
   516  	return nodes
   517  }
   518  
   519  // saveNodes saves known nodes and their statistics into the database. Nodes are
   520  // ordered from least to most recently connected.
   521  func (pool *serverPool) saveNodes() {
   522  	list := make([]*poolEntry, len(pool.knownQueue.queue))
   523  	for i := range list {
   524  		list[i] = pool.knownQueue.fetchOldest()
   525  	}
   526  	enc, err := rlp.EncodeToBytes(list)
   527  	if err == nil {
   528  		pool.db.Put(pool.dbKey, enc)
   529  	}
   530  }
   531  
   532  // removeEntry removes a pool entry when the entry count limit is reached.
   533  // Note that it is called by the new/known queues from which the entry has already
   534  // been removed so removing it from the queues is not necessary.
   535  func (pool *serverPool) removeEntry(entry *poolEntry) {
   536  	pool.newSelect.remove((*discoveredEntry)(entry))
   537  	pool.knownSelect.remove((*knownEntry)(entry))
   538  	entry.removed = true
   539  	delete(pool.entries, entry.node.ID())
   540  }
   541  
   542  // setRetryDial starts the timer which will enable dialing a certain node again
   543  func (pool *serverPool) setRetryDial(entry *poolEntry) {
   544  	delay := longRetryDelay
   545  	if entry.shortRetry > 0 {
   546  		entry.shortRetry--
   547  		delay = shortRetryDelay
   548  	}
   549  	delay += time.Duration(rand.Int63n(int64(delay) + 1))
   550  	entry.delayedRetry = true
   551  	go func() {
   552  		select {
   553  		case <-pool.quit:
   554  		case <-time.After(delay):
   555  			select {
   556  			case <-pool.quit:
   557  			case pool.enableRetry <- entry:
   558  			}
   559  		}
   560  	}()
   561  }
   562  
   563  // updateCheckDial is called when an entry can potentially be dialed again. It updates
   564  // its selection weights and checks if new dials can/should be made.
   565  func (pool *serverPool) updateCheckDial(entry *poolEntry) {
   566  	pool.newSelect.update((*discoveredEntry)(entry))
   567  	pool.knownSelect.update((*knownEntry)(entry))
   568  	pool.checkDial()
   569  }
   570  
   571  // checkDial checks if new dials can/should be made. It tries to select servers both
   572  // based on good statistics and recent discovery.
   573  func (pool *serverPool) checkDial() {
   574  	fillWithKnownSelects := !pool.fastDiscover
   575  	for pool.knownSelected < targetKnownSelect {
   576  		entry := pool.knownSelect.choose()
   577  		if entry == nil {
   578  			fillWithKnownSelects = false
   579  			break
   580  		}
   581  		pool.dial((*poolEntry)(entry.(*knownEntry)), true)
   582  	}
   583  	for pool.knownSelected+pool.newSelected < targetServerCount {
   584  		entry := pool.newSelect.choose()
   585  		if entry == nil {
   586  			break
   587  		}
   588  		pool.dial((*poolEntry)(entry.(*discoveredEntry)), false)
   589  	}
   590  	if fillWithKnownSelects {
   591  		// no more newly discovered nodes to select and since fast discover period
   592  		// is over, we probably won't find more in the near future so select more
   593  		// known entries if possible
   594  		for pool.knownSelected < targetServerCount {
   595  			entry := pool.knownSelect.choose()
   596  			if entry == nil {
   597  				break
   598  			}
   599  			pool.dial((*poolEntry)(entry.(*knownEntry)), true)
   600  		}
   601  	}
   602  }
   603  
   604  // dial initiates a new connection
   605  func (pool *serverPool) dial(entry *poolEntry, knownSelected bool) {
   606  	if pool.server == nil || entry.state != psNotConnected {
   607  		return
   608  	}
   609  	entry.state = psDialed
   610  	entry.knownSelected = knownSelected
   611  	if knownSelected {
   612  		pool.knownSelected++
   613  	} else {
   614  		pool.newSelected++
   615  	}
   616  	addr := entry.addrSelect.choose().(*poolEntryAddress)
   617  	log.Debug("Dialing new peer", "lesaddr", entry.node.ID().String()+"@"+addr.strKey(), "set", len(entry.addr), "known", knownSelected)
   618  	entry.dialed = addr
   619  	go func() {
   620  		pool.server.AddPeer(entry.node)
   621  		select {
   622  		case <-pool.quit:
   623  		case <-time.After(dialTimeout):
   624  			select {
   625  			case <-pool.quit:
   626  			case pool.timeout <- entry:
   627  			}
   628  		}
   629  	}()
   630  }
   631  
   632  // checkDialTimeout checks if the node is still in dialed state and if so, resets it
   633  // and adjusts connection statistics accordingly.
   634  func (pool *serverPool) checkDialTimeout(entry *poolEntry) {
   635  	if entry.state != psDialed {
   636  		return
   637  	}
   638  	log.Debug("Dial timeout", "lesaddr", entry.node.ID().String()+"@"+entry.dialed.strKey())
   639  	entry.state = psNotConnected
   640  	if entry.knownSelected {
   641  		pool.knownSelected--
   642  	} else {
   643  		pool.newSelected--
   644  	}
   645  	entry.connectStats.add(0, 1)
   646  	entry.dialed.fails++
   647  	pool.setRetryDial(entry)
   648  }
   649  
   650  const (
   651  	psNotConnected = iota
   652  	psDialed
   653  	psConnected
   654  	psRegistered
   655  )
   656  
   657  // poolEntry represents a server node and stores its current state and statistics.
   658  type poolEntry struct {
   659  	peer                  *peer
   660  	pubkey                [64]byte // secp256k1 key of the node
   661  	addr                  map[string]*poolEntryAddress
   662  	node                  *enode.Node
   663  	lastConnected, dialed *poolEntryAddress
   664  	addrSelect            weightedRandomSelect
   665  
   666  	lastDiscovered              mclock.AbsTime
   667  	known, knownSelected        bool
   668  	connectStats, delayStats    poolStats
   669  	responseStats, timeoutStats poolStats
   670  	state                       int
   671  	regTime                     mclock.AbsTime
   672  	queueIdx                    int
   673  	removed                     bool
   674  
   675  	delayedRetry bool
   676  	shortRetry   int
   677  }
   678  
   679  // poolEntryEnc is the RLP encoding of poolEntry.
   680  type poolEntryEnc struct {
   681  	Pubkey                     []byte
   682  	IP                         net.IP
   683  	Port                       uint16
   684  	Fails                      uint
   685  	CStat, DStat, RStat, TStat poolStats
   686  }
   687  
   688  func (e *poolEntry) EncodeRLP(w io.Writer) error {
   689  	return rlp.Encode(w, &poolEntryEnc{
   690  		Pubkey: encodePubkey64(e.node.Pubkey()),
   691  		IP:     e.lastConnected.ip,
   692  		Port:   e.lastConnected.port,
   693  		Fails:  e.lastConnected.fails,
   694  		CStat:  e.connectStats,
   695  		DStat:  e.delayStats,
   696  		RStat:  e.responseStats,
   697  		TStat:  e.timeoutStats,
   698  	})
   699  }
   700  
   701  func (e *poolEntry) DecodeRLP(s *rlp.Stream) error {
   702  	var entry poolEntryEnc
   703  	if err := s.Decode(&entry); err != nil {
   704  		return err
   705  	}
   706  	pubkey, err := decodePubkey64(entry.Pubkey)
   707  	if err != nil {
   708  		return err
   709  	}
   710  	addr := &poolEntryAddress{ip: entry.IP, port: entry.Port, fails: entry.Fails, lastSeen: mclock.Now()}
   711  	e.node = enode.NewV4(pubkey, entry.IP, int(entry.Port), int(entry.Port))
   712  	e.addr = make(map[string]*poolEntryAddress)
   713  	e.addr[addr.strKey()] = addr
   714  	e.addrSelect = *newWeightedRandomSelect()
   715  	e.addrSelect.update(addr)
   716  	e.lastConnected = addr
   717  	e.connectStats = entry.CStat
   718  	e.delayStats = entry.DStat
   719  	e.responseStats = entry.RStat
   720  	e.timeoutStats = entry.TStat
   721  	e.shortRetry = shortRetryCnt
   722  	e.known = true
   723  	return nil
   724  }
   725  
   726  func encodePubkey64(pub *ecdsa.PublicKey) []byte {
   727  	return crypto.FromECDSAPub(pub)[1:]
   728  }
   729  
   730  func decodePubkey64(b []byte) (*ecdsa.PublicKey, error) {
   731  	return crypto.UnmarshalPubkey(append([]byte{0x04}, b...))
   732  }
   733  
   734  // discoveredEntry implements wrsItem
   735  type discoveredEntry poolEntry
   736  
   737  // Weight calculates random selection weight for newly discovered entries
   738  func (e *discoveredEntry) Weight() int64 {
   739  	if e.state != psNotConnected || e.delayedRetry {
   740  		return 0
   741  	}
   742  	t := time.Duration(mclock.Now() - e.lastDiscovered)
   743  	if t <= discoverExpireStart {
   744  		return 1000000000
   745  	}
   746  	return int64(1000000000 * math.Exp(-float64(t-discoverExpireStart)/float64(discoverExpireConst)))
   747  }
   748  
   749  // knownEntry implements wrsItem
   750  type knownEntry poolEntry
   751  
   752  // Weight calculates random selection weight for known entries
   753  func (e *knownEntry) Weight() int64 {
   754  	if e.state != psNotConnected || !e.known || e.delayedRetry {
   755  		return 0
   756  	}
   757  	return int64(1000000000 * e.connectStats.recentAvg() * math.Exp(-float64(e.lastConnected.fails)*failDropLn-e.responseStats.recentAvg()/float64(responseScoreTC)-e.delayStats.recentAvg()/float64(delayScoreTC)) * math.Pow(1-e.timeoutStats.recentAvg(), timeoutPow))
   758  }
   759  
   760  // poolEntryAddress is a separate object because currently it is necessary to remember
   761  // multiple potential network addresses for a pool entry. This will be removed after
   762  // the final implementation of v5 discovery which will retrieve signed and serial
   763  // numbered advertisements, making it clear which IP/port is the latest one.
   764  type poolEntryAddress struct {
   765  	ip       net.IP
   766  	port     uint16
   767  	lastSeen mclock.AbsTime // last time it was discovered, connected or loaded from db
   768  	fails    uint           // connection failures since last successful connection (persistent)
   769  }
   770  
   771  func (a *poolEntryAddress) Weight() int64 {
   772  	t := time.Duration(mclock.Now() - a.lastSeen)
   773  	return int64(1000000*math.Exp(-float64(t)/float64(discoverExpireConst)-float64(a.fails)*addrFailDropLn)) + 1
   774  }
   775  
   776  func (a *poolEntryAddress) strKey() string {
   777  	return a.ip.String() + ":" + strconv.Itoa(int(a.port))
   778  }
   779  
   780  // poolStats implement statistics for a certain quantity with a long term average
   781  // and a short term value which is adjusted exponentially with a factor of
   782  // pstatRecentAdjust with each update and also returned exponentially to the
   783  // average with the time constant pstatReturnToMeanTC
   784  type poolStats struct {
   785  	sum, weight, avg, recent float64
   786  	lastRecalc               mclock.AbsTime
   787  }
   788  
   789  // init initializes stats with a long term sum/update count pair retrieved from the database
   790  func (s *poolStats) init(sum, weight float64) {
   791  	s.sum = sum
   792  	s.weight = weight
   793  	var avg float64
   794  	if weight > 0 {
   795  		avg = s.sum / weight
   796  	}
   797  	s.avg = avg
   798  	s.recent = avg
   799  	s.lastRecalc = mclock.Now()
   800  }
   801  
   802  // recalc recalculates recent value return-to-mean and long term average
   803  func (s *poolStats) recalc() {
   804  	now := mclock.Now()
   805  	s.recent = s.avg + (s.recent-s.avg)*math.Exp(-float64(now-s.lastRecalc)/float64(pstatReturnToMeanTC))
   806  	if s.sum == 0 {
   807  		s.avg = 0
   808  	} else {
   809  		if s.sum > s.weight*1e30 {
   810  			s.avg = 1e30
   811  		} else {
   812  			s.avg = s.sum / s.weight
   813  		}
   814  	}
   815  	s.lastRecalc = now
   816  }
   817  
   818  // add updates the stats with a new value
   819  func (s *poolStats) add(value, weight float64) {
   820  	s.weight += weight
   821  	s.sum += value * weight
   822  	s.recalc()
   823  }
   824  
   825  // recentAvg returns the short-term adjusted average
   826  func (s *poolStats) recentAvg() float64 {
   827  	s.recalc()
   828  	return s.recent
   829  }
   830  
   831  func (s *poolStats) EncodeRLP(w io.Writer) error {
   832  	return rlp.Encode(w, []interface{}{math.Float64bits(s.sum), math.Float64bits(s.weight)})
   833  }
   834  
   835  func (s *poolStats) DecodeRLP(st *rlp.Stream) error {
   836  	var stats struct {
   837  		SumUint, WeightUint uint64
   838  	}
   839  	if err := st.Decode(&stats); err != nil {
   840  		return err
   841  	}
   842  	s.init(math.Float64frombits(stats.SumUint), math.Float64frombits(stats.WeightUint))
   843  	return nil
   844  }
   845  
   846  // poolEntryQueue keeps track of its least recently accessed entries and removes
   847  // them when the number of entries reaches the limit
   848  type poolEntryQueue struct {
   849  	queue                  map[int]*poolEntry // known nodes indexed by their latest lastConnCnt value
   850  	newPtr, oldPtr, maxCnt int
   851  	removeFromPool         func(*poolEntry)
   852  }
   853  
   854  // newPoolEntryQueue returns a new poolEntryQueue
   855  func newPoolEntryQueue(maxCnt int, removeFromPool func(*poolEntry)) poolEntryQueue {
   856  	return poolEntryQueue{queue: make(map[int]*poolEntry), maxCnt: maxCnt, removeFromPool: removeFromPool}
   857  }
   858  
   859  // fetchOldest returns and removes the least recently accessed entry
   860  func (q *poolEntryQueue) fetchOldest() *poolEntry {
   861  	if len(q.queue) == 0 {
   862  		return nil
   863  	}
   864  	for {
   865  		if e := q.queue[q.oldPtr]; e != nil {
   866  			delete(q.queue, q.oldPtr)
   867  			q.oldPtr++
   868  			return e
   869  		}
   870  		q.oldPtr++
   871  	}
   872  }
   873  
   874  // remove removes an entry from the queue
   875  func (q *poolEntryQueue) remove(entry *poolEntry) {
   876  	if q.queue[entry.queueIdx] == entry {
   877  		delete(q.queue, entry.queueIdx)
   878  	}
   879  }
   880  
   881  // setLatest adds or updates a recently accessed entry. It also checks if an old entry
   882  // needs to be removed and removes it from the parent pool too with a callback function.
   883  func (q *poolEntryQueue) setLatest(entry *poolEntry) {
   884  	if q.queue[entry.queueIdx] == entry {
   885  		delete(q.queue, entry.queueIdx)
   886  	} else {
   887  		if len(q.queue) == q.maxCnt {
   888  			e := q.fetchOldest()
   889  			q.remove(e)
   890  			q.removeFromPool(e)
   891  		}
   892  	}
   893  	entry.queueIdx = q.newPtr
   894  	q.queue[entry.queueIdx] = entry
   895  	q.newPtr++
   896  }