github.com/linapex/ethereum-dpos-chinese@v0.0.0-20190316121959-b78b3a4a1ece/p2p/discv5/topic.go (about)

     1  
     2  //<developer>
     3  //    <name>linapex 曹一峰</name>
     4  //    <email>linapex@163.com</email>
     5  //    <wx>superexc</wx>
     6  //    <qqgroup>128148617</qqgroup>
     7  //    <url>https://jsq.ink</url>
     8  //    <role>pku engineer</role>
     9  //    <date>2019-03-16 12:09:44</date>
    10  //</624342657799294976>
    11  
    12  
    13  package discv5
    14  
    15  import (
    16  	"container/heap"
    17  	"fmt"
    18  	"math"
    19  	"math/rand"
    20  	"time"
    21  
    22  	"github.com/ethereum/go-ethereum/common/mclock"
    23  	"github.com/ethereum/go-ethereum/log"
    24  )
    25  
    26  const (
    27  	maxEntries         = 10000
    28  	maxEntriesPerTopic = 50
    29  
    30  	fallbackRegistrationExpiry = 1 * time.Hour
    31  )
    32  
    33  type Topic string
    34  
    35  type topicEntry struct {
    36  	topic   Topic
    37  	fifoIdx uint64
    38  	node    *Node
    39  	expire  mclock.AbsTime
    40  }
    41  
    42  type topicInfo struct {
    43  	entries            map[uint64]*topicEntry
    44  	fifoHead, fifoTail uint64
    45  	rqItem             *topicRequestQueueItem
    46  	wcl                waitControlLoop
    47  }
    48  
    49  //从FIFO中删除尾部元素
    50  func (t *topicInfo) getFifoTail() *topicEntry {
    51  	for t.entries[t.fifoTail] == nil {
    52  		t.fifoTail++
    53  	}
    54  	tail := t.entries[t.fifoTail]
    55  	t.fifoTail++
    56  	return tail
    57  }
    58  
    59  type nodeInfo struct {
    60  	entries                          map[Topic]*topicEntry
    61  	lastIssuedTicket, lastUsedTicket uint32
    62  //您不能在noreguntil(绝对时间)之前注册比lastusedticket更新的票据。
    63  	noRegUntil mclock.AbsTime
    64  }
    65  
    66  type topicTable struct {
    67  	db                    *nodeDB
    68  	self                  *Node
    69  	nodes                 map[*Node]*nodeInfo
    70  	topics                map[Topic]*topicInfo
    71  	globalEntries         uint64
    72  	requested             topicRequestQueue
    73  	requestCnt            uint64
    74  	lastGarbageCollection mclock.AbsTime
    75  }
    76  
    77  func newTopicTable(db *nodeDB, self *Node) *topicTable {
    78  	if printTestImgLogs {
    79  		fmt.Printf("*N %016x\n", self.sha[:8])
    80  	}
    81  	return &topicTable{
    82  		db:     db,
    83  		nodes:  make(map[*Node]*nodeInfo),
    84  		topics: make(map[Topic]*topicInfo),
    85  		self:   self,
    86  	}
    87  }
    88  
    89  func (t *topicTable) getOrNewTopic(topic Topic) *topicInfo {
    90  	ti := t.topics[topic]
    91  	if ti == nil {
    92  		rqItem := &topicRequestQueueItem{
    93  			topic:    topic,
    94  			priority: t.requestCnt,
    95  		}
    96  		ti = &topicInfo{
    97  			entries: make(map[uint64]*topicEntry),
    98  			rqItem:  rqItem,
    99  		}
   100  		t.topics[topic] = ti
   101  		heap.Push(&t.requested, rqItem)
   102  	}
   103  	return ti
   104  }
   105  
   106  func (t *topicTable) checkDeleteTopic(topic Topic) {
   107  	ti := t.topics[topic]
   108  	if ti == nil {
   109  		return
   110  	}
   111  	if len(ti.entries) == 0 && ti.wcl.hasMinimumWaitPeriod() {
   112  		delete(t.topics, topic)
   113  		heap.Remove(&t.requested, ti.rqItem.index)
   114  	}
   115  }
   116  
   117  func (t *topicTable) getOrNewNode(node *Node) *nodeInfo {
   118  	n := t.nodes[node]
   119  	if n == nil {
   120  //fmt.printf(“newnode%016x%016x\n”,t.self.sha[:8],node.sha[:8])
   121  		var issued, used uint32
   122  		if t.db != nil {
   123  			issued, used = t.db.fetchTopicRegTickets(node.ID)
   124  		}
   125  		n = &nodeInfo{
   126  			entries:          make(map[Topic]*topicEntry),
   127  			lastIssuedTicket: issued,
   128  			lastUsedTicket:   used,
   129  		}
   130  		t.nodes[node] = n
   131  	}
   132  	return n
   133  }
   134  
   135  func (t *topicTable) checkDeleteNode(node *Node) {
   136  	if n, ok := t.nodes[node]; ok && len(n.entries) == 0 && n.noRegUntil < mclock.Now() {
   137  //fmt.printf(“删除节点%016x%016x\n”,t.self.sha[:8],节点.sha[:8])
   138  		delete(t.nodes, node)
   139  	}
   140  }
   141  
   142  func (t *topicTable) storeTicketCounters(node *Node) {
   143  	n := t.getOrNewNode(node)
   144  	if t.db != nil {
   145  		t.db.updateTopicRegTickets(node.ID, n.lastIssuedTicket, n.lastUsedTicket)
   146  	}
   147  }
   148  
   149  func (t *topicTable) getEntries(topic Topic) []*Node {
   150  	t.collectGarbage()
   151  
   152  	te := t.topics[topic]
   153  	if te == nil {
   154  		return nil
   155  	}
   156  	nodes := make([]*Node, len(te.entries))
   157  	i := 0
   158  	for _, e := range te.entries {
   159  		nodes[i] = e.node
   160  		i++
   161  	}
   162  	t.requestCnt++
   163  	t.requested.update(te.rqItem, t.requestCnt)
   164  	return nodes
   165  }
   166  
   167  func (t *topicTable) addEntry(node *Node, topic Topic) {
   168  	n := t.getOrNewNode(node)
   169  //按同一节点清除以前的条目
   170  	for _, e := range n.entries {
   171  		t.deleteEntry(e)
   172  	}
   173  //***
   174  	n = t.getOrNewNode(node)
   175  
   176  	tm := mclock.Now()
   177  	te := t.getOrNewTopic(topic)
   178  
   179  	if len(te.entries) == maxEntriesPerTopic {
   180  		t.deleteEntry(te.getFifoTail())
   181  	}
   182  
   183  	if t.globalEntries == maxEntries {
   184  t.deleteEntry(t.leastRequested()) //不是空的,不需要检查零
   185  	}
   186  
   187  	fifoIdx := te.fifoHead
   188  	te.fifoHead++
   189  	entry := &topicEntry{
   190  		topic:   topic,
   191  		fifoIdx: fifoIdx,
   192  		node:    node,
   193  		expire:  tm + mclock.AbsTime(fallbackRegistrationExpiry),
   194  	}
   195  	if printTestImgLogs {
   196  		fmt.Printf("*+ %d %v %016x %016x\n", tm/1000000, topic, t.self.sha[:8], node.sha[:8])
   197  	}
   198  	te.entries[fifoIdx] = entry
   199  	n.entries[topic] = entry
   200  	t.globalEntries++
   201  	te.wcl.registered(tm)
   202  }
   203  
   204  //从FIFO中删除请求最少的元素
   205  func (t *topicTable) leastRequested() *topicEntry {
   206  	for t.requested.Len() > 0 && t.topics[t.requested[0].topic] == nil {
   207  		heap.Pop(&t.requested)
   208  	}
   209  	if t.requested.Len() == 0 {
   210  		return nil
   211  	}
   212  	return t.topics[t.requested[0].topic].getFifoTail()
   213  }
   214  
   215  //条目应该存在
   216  func (t *topicTable) deleteEntry(e *topicEntry) {
   217  	if printTestImgLogs {
   218  		fmt.Printf("*- %d %v %016x %016x\n", mclock.Now()/1000000, e.topic, t.self.sha[:8], e.node.sha[:8])
   219  	}
   220  	ne := t.nodes[e.node].entries
   221  	delete(ne, e.topic)
   222  	if len(ne) == 0 {
   223  		t.checkDeleteNode(e.node)
   224  	}
   225  	te := t.topics[e.topic]
   226  	delete(te.entries, e.fifoIdx)
   227  	if len(te.entries) == 0 {
   228  		t.checkDeleteTopic(e.topic)
   229  	}
   230  	t.globalEntries--
   231  }
   232  
   233  //假设主题和等待期的长度相同。
   234  func (t *topicTable) useTicket(node *Node, serialNo uint32, topics []Topic, idx int, issueTime uint64, waitPeriods []uint32) (registered bool) {
   235  	log.Trace("Using discovery ticket", "serial", serialNo, "topics", topics, "waits", waitPeriods)
   236  //fmt.println(“useticket”,serialno,topics,waitperiods)
   237  	t.collectGarbage()
   238  
   239  	n := t.getOrNewNode(node)
   240  	if serialNo < n.lastUsedTicket {
   241  		return false
   242  	}
   243  
   244  	tm := mclock.Now()
   245  	if serialNo > n.lastUsedTicket && tm < n.noRegUntil {
   246  		return false
   247  	}
   248  	if serialNo != n.lastUsedTicket {
   249  		n.lastUsedTicket = serialNo
   250  		n.noRegUntil = tm + mclock.AbsTime(noRegTimeout())
   251  		t.storeTicketCounters(node)
   252  	}
   253  
   254  	currTime := uint64(tm / mclock.AbsTime(time.Second))
   255  	regTime := issueTime + uint64(waitPeriods[idx])
   256  	relTime := int64(currTime - regTime)
   257  if relTime >= -1 && relTime <= regTimeWindow+1 { //在两端给客户一点安全边际
   258  		if e := n.entries[topics[idx]]; e == nil {
   259  			t.addEntry(node, topics[idx])
   260  		} else {
   261  //如果有活动条目,不要移动到FIFO的前面,而是延长过期时间
   262  			e.expire = tm + mclock.AbsTime(fallbackRegistrationExpiry)
   263  		}
   264  		return true
   265  	}
   266  
   267  	return false
   268  }
   269  
   270  func (t *topicTable) getTicket(node *Node, topics []Topic) *ticket {
   271  	t.collectGarbage()
   272  
   273  	now := mclock.Now()
   274  	n := t.getOrNewNode(node)
   275  	n.lastIssuedTicket++
   276  	t.storeTicketCounters(node)
   277  
   278  	tic := &ticket{
   279  		issueTime: now,
   280  		topics:    topics,
   281  		serial:    n.lastIssuedTicket,
   282  		regTime:   make([]mclock.AbsTime, len(topics)),
   283  	}
   284  	for i, topic := range topics {
   285  		var waitPeriod time.Duration
   286  		if topic := t.topics[topic]; topic != nil {
   287  			waitPeriod = topic.wcl.waitPeriod
   288  		} else {
   289  			waitPeriod = minWaitPeriod
   290  		}
   291  
   292  		tic.regTime[i] = now + mclock.AbsTime(waitPeriod)
   293  	}
   294  	return tic
   295  }
   296  
   297  const gcInterval = time.Minute
   298  
   299  func (t *topicTable) collectGarbage() {
   300  	tm := mclock.Now()
   301  	if time.Duration(tm-t.lastGarbageCollection) < gcInterval {
   302  		return
   303  	}
   304  	t.lastGarbageCollection = tm
   305  
   306  	for node, n := range t.nodes {
   307  		for _, e := range n.entries {
   308  			if e.expire <= tm {
   309  				t.deleteEntry(e)
   310  			}
   311  		}
   312  
   313  		t.checkDeleteNode(node)
   314  	}
   315  
   316  	for topic := range t.topics {
   317  		t.checkDeleteTopic(topic)
   318  	}
   319  }
   320  
   321  const (
   322  	minWaitPeriod   = time.Minute
   323  regTimeWindow   = 10 //秒
   324  	avgnoRegTimeout = time.Minute * 10
   325  //两个传入AD请求之间的目标平均间隔
   326  	wcTargetRegInterval = time.Minute * 10 / maxEntriesPerTopic
   327  //
   328  	wcTimeConst = time.Minute * 10
   329  )
   330  
   331  //不需要初始化,将在首次注册时设置为minwaitperiod
   332  type waitControlLoop struct {
   333  	lastIncoming mclock.AbsTime
   334  	waitPeriod   time.Duration
   335  }
   336  
   337  func (w *waitControlLoop) registered(tm mclock.AbsTime) {
   338  	w.waitPeriod = w.nextWaitPeriod(tm)
   339  	w.lastIncoming = tm
   340  }
   341  
   342  func (w *waitControlLoop) nextWaitPeriod(tm mclock.AbsTime) time.Duration {
   343  	period := tm - w.lastIncoming
   344  	wp := time.Duration(float64(w.waitPeriod) * math.Exp((float64(wcTargetRegInterval)-float64(period))/float64(wcTimeConst)))
   345  	if wp < minWaitPeriod {
   346  		wp = minWaitPeriod
   347  	}
   348  	return wp
   349  }
   350  
   351  func (w *waitControlLoop) hasMinimumWaitPeriod() bool {
   352  	return w.nextWaitPeriod(mclock.Now()) == minWaitPeriod
   353  }
   354  
   355  func noRegTimeout() time.Duration {
   356  	e := rand.ExpFloat64()
   357  	if e > 100 {
   358  		e = 100
   359  	}
   360  	return time.Duration(float64(avgnoRegTimeout) * e)
   361  }
   362  
   363  type topicRequestQueueItem struct {
   364  	topic    Topic
   365  	priority uint64
   366  	index    int
   367  }
   368  
   369  //TopicRequestQueue实现heap.interface并保存TopicRequestQueueitems。
   370  type topicRequestQueue []*topicRequestQueueItem
   371  
   372  func (tq topicRequestQueue) Len() int { return len(tq) }
   373  
   374  func (tq topicRequestQueue) Less(i, j int) bool {
   375  	return tq[i].priority < tq[j].priority
   376  }
   377  
   378  func (tq topicRequestQueue) Swap(i, j int) {
   379  	tq[i], tq[j] = tq[j], tq[i]
   380  	tq[i].index = i
   381  	tq[j].index = j
   382  }
   383  
   384  func (tq *topicRequestQueue) Push(x interface{}) {
   385  	n := len(*tq)
   386  	item := x.(*topicRequestQueueItem)
   387  	item.index = n
   388  	*tq = append(*tq, item)
   389  }
   390  
   391  func (tq *topicRequestQueue) Pop() interface{} {
   392  	old := *tq
   393  	n := len(old)
   394  	item := old[n-1]
   395  	item.index = -1
   396  	*tq = old[0 : n-1]
   397  	return item
   398  }
   399  
   400  func (tq *topicRequestQueue) update(item *topicRequestQueueItem, priority uint64) {
   401  	item.priority = priority
   402  	heap.Fix(tq, item.index)
   403  }
   404