github.com/annchain/OG@v0.0.9/p2p/discv5/topic.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 discv5
    18  
    19  //go:generate  msgp
    20  import (
    21  	"container/heap"
    22  	"fmt"
    23  	"math"
    24  	"math/rand"
    25  	"time"
    26  
    27  	"github.com/annchain/OG/common/mclock"
    28  )
    29  
    30  const (
    31  	maxEntries         = 10000
    32  	maxEntriesPerTopic = 50
    33  
    34  	fallbackRegistrationExpiry = 1 * time.Hour
    35  )
    36  
    37  type Topic string
    38  
    39  type Topics []Topic
    40  
    41  type topicEntry struct {
    42  	topic   Topic
    43  	fifoIdx uint64
    44  	node    *Node
    45  	expire  mclock.AbsTime
    46  }
    47  
    48  type topicInfo struct {
    49  	entries            map[uint64]*topicEntry
    50  	fifoHead, fifoTail uint64
    51  	rqItem             *topicRequestQueueItem
    52  	wcl                waitControlLoop
    53  }
    54  
    55  // removes tail element from the fifo
    56  func (t *topicInfo) getFifoTail() *topicEntry {
    57  	for t.entries[t.fifoTail] == nil {
    58  		t.fifoTail++
    59  	}
    60  	tail := t.entries[t.fifoTail]
    61  	t.fifoTail++
    62  	return tail
    63  }
    64  
    65  type nodeInfo struct {
    66  	entries                          map[Topic]*topicEntry
    67  	lastIssuedTicket, lastUsedTicket uint32
    68  	// you can't register a ticket newer than lastUsedTicket before noRegUntil (absolute time)
    69  	noRegUntil mclock.AbsTime
    70  }
    71  
    72  type topicTable struct {
    73  	db                    *nodeDB
    74  	self                  *Node
    75  	nodes                 map[*Node]*nodeInfo
    76  	topics                map[Topic]*topicInfo
    77  	globalEntries         uint64
    78  	requested             topicRequestQueue
    79  	requestCnt            uint64
    80  	lastGarbageCollection mclock.AbsTime
    81  }
    82  
    83  func newTopicTable(db *nodeDB, self *Node) *topicTable {
    84  	if printTestImgLogs {
    85  		fmt.Printf("*N %016x\n", self.sha.Bytes[:8])
    86  	}
    87  	return &topicTable{
    88  		db:     db,
    89  		nodes:  make(map[*Node]*nodeInfo),
    90  		topics: make(map[Topic]*topicInfo),
    91  		self:   self,
    92  	}
    93  }
    94  
    95  func (t *topicTable) getOrNewTopic(topic Topic) *topicInfo {
    96  	ti := t.topics[topic]
    97  	if ti == nil {
    98  		rqItem := &topicRequestQueueItem{
    99  			topic:    topic,
   100  			priority: t.requestCnt,
   101  		}
   102  		ti = &topicInfo{
   103  			entries: make(map[uint64]*topicEntry),
   104  			rqItem:  rqItem,
   105  		}
   106  		t.topics[topic] = ti
   107  		heap.Push(&t.requested, rqItem)
   108  	}
   109  	return ti
   110  }
   111  
   112  func (t *topicTable) checkDeleteTopic(topic Topic) {
   113  	ti := t.topics[topic]
   114  	if ti == nil {
   115  		return
   116  	}
   117  	if len(ti.entries) == 0 && ti.wcl.hasMinimumWaitPeriod() {
   118  		delete(t.topics, topic)
   119  		heap.Remove(&t.requested, ti.rqItem.index)
   120  	}
   121  }
   122  
   123  func (t *topicTable) getOrNewNode(node *Node) *nodeInfo {
   124  	n := t.nodes[node]
   125  	if n == nil {
   126  		//fmt.Printf("newNode %016x %016x\n", t.self.sha[:8], node.sha[:8])
   127  		var issued, used uint32
   128  		if t.db != nil {
   129  			issued, used = t.db.fetchTopicRegTickets(node.ID)
   130  		}
   131  		n = &nodeInfo{
   132  			entries:          make(map[Topic]*topicEntry),
   133  			lastIssuedTicket: issued,
   134  			lastUsedTicket:   used,
   135  		}
   136  		t.nodes[node] = n
   137  	}
   138  	return n
   139  }
   140  
   141  func (t *topicTable) checkDeleteNode(node *Node) {
   142  	if n, ok := t.nodes[node]; ok && len(n.entries) == 0 && n.noRegUntil < mclock.Now() {
   143  		//fmt.Printf("deleteNode %016x %016x\n", t.self.sha[:8], node.sha[:8])
   144  		delete(t.nodes, node)
   145  	}
   146  }
   147  
   148  func (t *topicTable) storeTicketCounters(node *Node) {
   149  	n := t.getOrNewNode(node)
   150  	if t.db != nil {
   151  		t.db.updateTopicRegTickets(node.ID, n.lastIssuedTicket, n.lastUsedTicket)
   152  	}
   153  }
   154  
   155  func (t *topicTable) getEntries(topic Topic) []*Node {
   156  	t.collectGarbage()
   157  
   158  	te := t.topics[topic]
   159  	if te == nil {
   160  		return nil
   161  	}
   162  	nodes := make([]*Node, len(te.entries))
   163  	i := 0
   164  	for _, e := range te.entries {
   165  		nodes[i] = e.node
   166  		i++
   167  	}
   168  	t.requestCnt++
   169  	t.requested.update(te.rqItem, t.requestCnt)
   170  	return nodes
   171  }
   172  
   173  func (t *topicTable) addEntry(node *Node, topic Topic) {
   174  	n := t.getOrNewNode(node)
   175  	// clear previous entries by the same node
   176  	for _, e := range n.entries {
   177  		t.deleteEntry(e)
   178  	}
   179  	// ***
   180  	n = t.getOrNewNode(node)
   181  
   182  	tm := mclock.Now()
   183  	te := t.getOrNewTopic(topic)
   184  
   185  	if len(te.entries) == maxEntriesPerTopic {
   186  		t.deleteEntry(te.getFifoTail())
   187  	}
   188  
   189  	if t.globalEntries == maxEntries {
   190  		t.deleteEntry(t.leastRequested()) // not empty, no need to check for nil
   191  	}
   192  
   193  	fifoIdx := te.fifoHead
   194  	te.fifoHead++
   195  	entry := &topicEntry{
   196  		topic:   topic,
   197  		fifoIdx: fifoIdx,
   198  		node:    node,
   199  		expire:  tm + mclock.AbsTime(fallbackRegistrationExpiry),
   200  	}
   201  	if printTestImgLogs {
   202  		fmt.Printf("*+ %d %v %016x %016x\n", tm/1000000, topic, t.self.sha.Bytes[:8], node.sha.Bytes[:8])
   203  	}
   204  	te.entries[fifoIdx] = entry
   205  	n.entries[topic] = entry
   206  	t.globalEntries++
   207  	te.wcl.registered(tm)
   208  }
   209  
   210  // removes least requested element from the fifo
   211  func (t *topicTable) leastRequested() *topicEntry {
   212  	for t.requested.Len() > 0 && t.topics[t.requested[0].topic] == nil {
   213  		heap.Pop(&t.requested)
   214  	}
   215  	if t.requested.Len() == 0 {
   216  		return nil
   217  	}
   218  	return t.topics[t.requested[0].topic].getFifoTail()
   219  }
   220  
   221  // entry should exist
   222  func (t *topicTable) deleteEntry(e *topicEntry) {
   223  	if printTestImgLogs {
   224  		fmt.Printf("*- %d %v %016x %016x\n", mclock.Now()/1000000, e.topic, t.self.sha.Bytes[:8], e.node.sha.Bytes[:8])
   225  	}
   226  	ne := t.nodes[e.node].entries
   227  	delete(ne, e.topic)
   228  	if len(ne) == 0 {
   229  		t.checkDeleteNode(e.node)
   230  	}
   231  	te := t.topics[e.topic]
   232  	delete(te.entries, e.fifoIdx)
   233  	if len(te.entries) == 0 {
   234  		t.checkDeleteTopic(e.topic)
   235  	}
   236  	t.globalEntries--
   237  }
   238  
   239  // It is assumed that topics and waitPeriods have the same length.
   240  func (t *topicTable) useTicket(node *Node, serialNo uint32, topics []Topic, idx int, issueTime uint64, waitPeriods []uint32) (registered bool) {
   241  	log.Trace("Using discovery ticket", "serial", serialNo, "topics", topics, "waits", waitPeriods)
   242  	//fmt.Println("useTicket", serialNo, topics, waitPeriods)
   243  	t.collectGarbage()
   244  
   245  	n := t.getOrNewNode(node)
   246  	if serialNo < n.lastUsedTicket {
   247  		return false
   248  	}
   249  
   250  	tm := mclock.Now()
   251  	if serialNo > n.lastUsedTicket && tm < n.noRegUntil {
   252  		return false
   253  	}
   254  	if serialNo != n.lastUsedTicket {
   255  		n.lastUsedTicket = serialNo
   256  		n.noRegUntil = tm + mclock.AbsTime(noRegTimeout())
   257  		t.storeTicketCounters(node)
   258  	}
   259  
   260  	currTime := uint64(tm / mclock.AbsTime(time.Second))
   261  	regTime := issueTime + uint64(waitPeriods[idx])
   262  	relTime := int64(currTime - regTime)
   263  	if relTime >= -1 && relTime <= regTimeWindow+1 { // give clients a little security margin on both ends
   264  		if e := n.entries[topics[idx]]; e == nil {
   265  			t.addEntry(node, topics[idx])
   266  		} else {
   267  			// if there is an active entry, don't move to the front of the FIFO but prolong expire time
   268  			e.expire = tm + mclock.AbsTime(fallbackRegistrationExpiry)
   269  		}
   270  		return true
   271  	}
   272  
   273  	return false
   274  }
   275  
   276  func (t *topicTable) getTicket(node *Node, topics []Topic) *ticket {
   277  	t.collectGarbage()
   278  
   279  	now := mclock.Now()
   280  	n := t.getOrNewNode(node)
   281  	n.lastIssuedTicket++
   282  	t.storeTicketCounters(node)
   283  
   284  	tic := &ticket{
   285  		issueTime: now,
   286  		topics:    topics,
   287  		serial:    n.lastIssuedTicket,
   288  		regTime:   make([]mclock.AbsTime, len(topics)),
   289  	}
   290  	for i, topic := range topics {
   291  		var waitPeriod time.Duration
   292  		if topic := t.topics[topic]; topic != nil {
   293  			waitPeriod = topic.wcl.waitPeriod
   294  		} else {
   295  			waitPeriod = minWaitPeriod
   296  		}
   297  
   298  		tic.regTime[i] = now + mclock.AbsTime(waitPeriod)
   299  	}
   300  	return tic
   301  }
   302  
   303  const gcInterval = time.Minute
   304  
   305  func (t *topicTable) collectGarbage() {
   306  	tm := mclock.Now()
   307  	if time.Duration(tm-t.lastGarbageCollection) < gcInterval {
   308  		return
   309  	}
   310  	t.lastGarbageCollection = tm
   311  
   312  	for node, n := range t.nodes {
   313  		for _, e := range n.entries {
   314  			if e.expire <= tm {
   315  				t.deleteEntry(e)
   316  			}
   317  		}
   318  
   319  		t.checkDeleteNode(node)
   320  	}
   321  
   322  	for topic := range t.topics {
   323  		t.checkDeleteTopic(topic)
   324  	}
   325  }
   326  
   327  const (
   328  	minWaitPeriod   = time.Minute
   329  	regTimeWindow   = 10 // seconds
   330  	avgnoRegTimeout = time.Minute * 10
   331  	// target average interval between two incoming ad requests
   332  	wcTargetRegInterval = time.Minute * 10 / maxEntriesPerTopic
   333  	//
   334  	wcTimeConst = time.Minute * 10
   335  )
   336  
   337  // initialization is not required, will set to minWaitPeriod at first registration
   338  type waitControlLoop struct {
   339  	lastIncoming mclock.AbsTime
   340  	waitPeriod   time.Duration
   341  }
   342  
   343  func (w *waitControlLoop) registered(tm mclock.AbsTime) {
   344  	w.waitPeriod = w.nextWaitPeriod(tm)
   345  	w.lastIncoming = tm
   346  }
   347  
   348  func (w *waitControlLoop) nextWaitPeriod(tm mclock.AbsTime) time.Duration {
   349  	period := tm - w.lastIncoming
   350  	wp := time.Duration(float64(w.waitPeriod) * math.Exp((float64(wcTargetRegInterval)-float64(period))/float64(wcTimeConst)))
   351  	if wp < minWaitPeriod {
   352  		wp = minWaitPeriod
   353  	}
   354  	return wp
   355  }
   356  
   357  func (w *waitControlLoop) hasMinimumWaitPeriod() bool {
   358  	return w.nextWaitPeriod(mclock.Now()) == minWaitPeriod
   359  }
   360  
   361  func noRegTimeout() time.Duration {
   362  	e := rand.ExpFloat64()
   363  	if e > 100 {
   364  		e = 100
   365  	}
   366  	return time.Duration(float64(avgnoRegTimeout) * e)
   367  }
   368  
   369  type topicRequestQueueItem struct {
   370  	topic    Topic
   371  	priority uint64
   372  	index    int
   373  }
   374  
   375  // A topicRequestQueue implements heap.Interface and holds topicRequestQueueItems.
   376  type topicRequestQueue []*topicRequestQueueItem
   377  
   378  func (tq topicRequestQueue) Len() int { return len(tq) }
   379  
   380  func (tq topicRequestQueue) Less(i, j int) bool {
   381  	return tq[i].priority < tq[j].priority
   382  }
   383  
   384  func (tq topicRequestQueue) Swap(i, j int) {
   385  	tq[i], tq[j] = tq[j], tq[i]
   386  	tq[i].index = i
   387  	tq[j].index = j
   388  }
   389  
   390  func (tq *topicRequestQueue) Push(x interface{}) {
   391  	n := len(*tq)
   392  	item := x.(*topicRequestQueueItem)
   393  	item.index = n
   394  	*tq = append(*tq, item)
   395  }
   396  
   397  func (tq *topicRequestQueue) Pop() interface{} {
   398  	old := *tq
   399  	n := len(old)
   400  	item := old[n-1]
   401  	item.index = -1
   402  	*tq = old[0 : n-1]
   403  	return item
   404  }
   405  
   406  func (tq *topicRequestQueue) update(item *topicRequestQueueItem, priority uint64) {
   407  	item.priority = priority
   408  	heap.Fix(tq, item.index)
   409  }