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