github.com/linapex/ethereum-dpos-chinese@v0.0.0-20190316121959-b78b3a4a1ece/p2p/discv5/ticket.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  //</624342657707020288>
    11  
    12  
    13  package discv5
    14  
    15  import (
    16  	"bytes"
    17  	"encoding/binary"
    18  	"fmt"
    19  	"math"
    20  	"math/rand"
    21  	"sort"
    22  	"time"
    23  
    24  	"github.com/ethereum/go-ethereum/common"
    25  	"github.com/ethereum/go-ethereum/common/mclock"
    26  	"github.com/ethereum/go-ethereum/crypto"
    27  	"github.com/ethereum/go-ethereum/log"
    28  )
    29  
    30  const (
    31  	ticketTimeBucketLen = time.Minute
    32  timeWindow          = 10 //*Tickettimebucketlen
    33  	wantTicketsInWindow = 10
    34  	collectFrequency    = time.Second * 30
    35  	registerFrequency   = time.Second * 60
    36  	maxCollectDebt      = 10
    37  	maxRegisterDebt     = 5
    38  	keepTicketConst     = time.Minute * 10
    39  	keepTicketExp       = time.Minute * 5
    40  	targetWaitTime      = time.Minute * 10
    41  	topicQueryTimeout   = time.Second * 5
    42  	topicQueryResend    = time.Minute
    43  //主题半径检测
    44  	maxRadius           = 0xffffffffffffffff
    45  	radiusTC            = time.Minute * 20
    46  	radiusBucketsPerBit = 8
    47  	minSlope            = 1
    48  	minPeakSize         = 40
    49  	maxNoAdjust         = 20
    50  	lookupWidth         = 8
    51  	minRightSum         = 20
    52  	searchForceQuery    = 4
    53  )
    54  
    55  //TimeBucket表示绝对单调时间,单位为分钟。
    56  //它用作每个主题票据存储桶的索引。
    57  type timeBucket int
    58  
    59  type ticket struct {
    60  	topics  []Topic
    61  regTime []mclock.AbsTime //每个主题可使用票据的本地绝对时间。
    62  
    63  //服务器发出的序列号。
    64  	serial uint32
    65  //由注册器使用,跟踪创建票据的绝对时间。
    66  	issueTime mclock.AbsTime
    67  
    68  //仅由注册者使用的字段
    69  node   *Node  //签署此票证的注册器节点
    70  refCnt int    //跟踪将使用此通知单注册的主题数
    71  pong   []byte //注册人签名的编码pong包
    72  }
    73  
    74  //ticketref指的是票据中的单个主题。
    75  type ticketRef struct {
    76  	t   *ticket
    77  idx int //t.topics和t.regtime中的主题索引
    78  }
    79  
    80  func (ref ticketRef) topic() Topic {
    81  	return ref.t.topics[ref.idx]
    82  }
    83  
    84  func (ref ticketRef) topicRegTime() mclock.AbsTime {
    85  	return ref.t.regTime[ref.idx]
    86  }
    87  
    88  func pongToTicket(localTime mclock.AbsTime, topics []Topic, node *Node, p *ingressPacket) (*ticket, error) {
    89  	wps := p.data.(*pong).WaitPeriods
    90  	if len(topics) != len(wps) {
    91  		return nil, fmt.Errorf("bad wait period list: got %d values, want %d", len(topics), len(wps))
    92  	}
    93  	if rlpHash(topics) != p.data.(*pong).TopicHash {
    94  		return nil, fmt.Errorf("bad topic hash")
    95  	}
    96  	t := &ticket{
    97  		issueTime: localTime,
    98  		node:      node,
    99  		topics:    topics,
   100  		pong:      p.rawData,
   101  		regTime:   make([]mclock.AbsTime, len(wps)),
   102  	}
   103  //将等待时间转换为本地绝对时间。
   104  	for i, wp := range wps {
   105  		t.regTime[i] = localTime + mclock.AbsTime(time.Second*time.Duration(wp))
   106  	}
   107  	return t, nil
   108  }
   109  
   110  func ticketToPong(t *ticket, pong *pong) {
   111  	pong.Expiration = uint64(t.issueTime / mclock.AbsTime(time.Second))
   112  	pong.TopicHash = rlpHash(t.topics)
   113  	pong.TicketSerial = t.serial
   114  	pong.WaitPeriods = make([]uint32, len(t.regTime))
   115  	for i, regTime := range t.regTime {
   116  		pong.WaitPeriods[i] = uint32(time.Duration(regTime-t.issueTime) / time.Second)
   117  	}
   118  }
   119  
   120  type ticketStore struct {
   121  //半径检测器和目标地址发生器
   122  //已搜索和已注册主题都存在
   123  	radius map[Topic]*topicRadius
   124  
   125  //包含门票桶(每绝对分钟)
   126  //可以在那一分钟内使用。
   127  //只有在注册主题时才设置此选项。
   128  	tickets map[Topic]*topicTickets
   129  
   130  regQueue []Topic            //循环尝试的主题注册队列
   131  regSet   map[Topic]struct{} //用于快速填充的主题注册队列内容
   132  
   133  	nodes       map[*Node]*ticket
   134  	nodeLastReq map[*Node]reqInfo
   135  
   136  	lastBucketFetched timeBucket
   137  	nextTicketCached  *ticketRef
   138  	nextTicketReg     mclock.AbsTime
   139  
   140  	searchTopicMap        map[Topic]searchTopic
   141  	nextTopicQueryCleanup mclock.AbsTime
   142  	queriesSent           map[*Node]map[common.Hash]sentQuery
   143  }
   144  
   145  type searchTopic struct {
   146  	foundChn chan<- *Node
   147  }
   148  
   149  type sentQuery struct {
   150  	sent   mclock.AbsTime
   151  	lookup lookupInfo
   152  }
   153  
   154  type topicTickets struct {
   155  	buckets    map[timeBucket][]ticketRef
   156  	nextLookup mclock.AbsTime
   157  	nextReg    mclock.AbsTime
   158  }
   159  
   160  func newTicketStore() *ticketStore {
   161  	return &ticketStore{
   162  		radius:         make(map[Topic]*topicRadius),
   163  		tickets:        make(map[Topic]*topicTickets),
   164  		regSet:         make(map[Topic]struct{}),
   165  		nodes:          make(map[*Node]*ticket),
   166  		nodeLastReq:    make(map[*Node]reqInfo),
   167  		searchTopicMap: make(map[Topic]searchTopic),
   168  		queriesSent:    make(map[*Node]map[common.Hash]sentQuery),
   169  	}
   170  }
   171  
   172  //AddTopic开始跟踪主题。如果寄存器为真,
   173  //本地节点将注册主题并收集票据。
   174  func (s *ticketStore) addTopic(topic Topic, register bool) {
   175  	log.Trace("Adding discovery topic", "topic", topic, "register", register)
   176  	if s.radius[topic] == nil {
   177  		s.radius[topic] = newTopicRadius(topic)
   178  	}
   179  	if register && s.tickets[topic] == nil {
   180  		s.tickets[topic] = &topicTickets{buckets: make(map[timeBucket][]ticketRef)}
   181  	}
   182  }
   183  
   184  func (s *ticketStore) addSearchTopic(t Topic, foundChn chan<- *Node) {
   185  	s.addTopic(t, false)
   186  	if s.searchTopicMap[t].foundChn == nil {
   187  		s.searchTopicMap[t] = searchTopic{foundChn: foundChn}
   188  	}
   189  }
   190  
   191  func (s *ticketStore) removeSearchTopic(t Topic) {
   192  	if st := s.searchTopicMap[t]; st.foundChn != nil {
   193  		delete(s.searchTopicMap, t)
   194  	}
   195  }
   196  
   197  //RemoveRegisterTopic删除给定主题的所有通知单。
   198  func (s *ticketStore) removeRegisterTopic(topic Topic) {
   199  	log.Trace("Removing discovery topic", "topic", topic)
   200  	if s.tickets[topic] == nil {
   201  		log.Warn("Removing non-existent discovery topic", "topic", topic)
   202  		return
   203  	}
   204  	for _, list := range s.tickets[topic].buckets {
   205  		for _, ref := range list {
   206  			ref.t.refCnt--
   207  			if ref.t.refCnt == 0 {
   208  				delete(s.nodes, ref.t.node)
   209  				delete(s.nodeLastReq, ref.t.node)
   210  			}
   211  		}
   212  	}
   213  	delete(s.tickets, topic)
   214  }
   215  
   216  func (s *ticketStore) regTopicSet() []Topic {
   217  	topics := make([]Topic, 0, len(s.tickets))
   218  	for topic := range s.tickets {
   219  		topics = append(topics, topic)
   220  	}
   221  	return topics
   222  }
   223  
   224  //NextRegisterLookup返回下一个票据收集查找的目标。
   225  func (s *ticketStore) nextRegisterLookup() (lookupInfo, time.Duration) {
   226  //将任何新主题(或丢弃的主题)排队,保留迭代顺序
   227  	for topic := range s.tickets {
   228  		if _, ok := s.regSet[topic]; !ok {
   229  			s.regQueue = append(s.regQueue, topic)
   230  			s.regSet[topic] = struct{}{}
   231  		}
   232  	}
   233  //迭代所有主题的集合并查找下一个合适的主题
   234  	for len(s.regQueue) > 0 {
   235  //从队列中提取下一个主题,并确保它仍然存在
   236  		topic := s.regQueue[0]
   237  		s.regQueue = s.regQueue[1:]
   238  		delete(s.regSet, topic)
   239  
   240  		if s.tickets[topic] == nil {
   241  			continue
   242  		}
   243  //如果主题需要更多门票,请将其退回
   244  		if s.tickets[topic].nextLookup < mclock.Now() {
   245  			next, delay := s.radius[topic].nextTarget(false), 100*time.Millisecond
   246  			log.Trace("Found discovery topic to register", "topic", topic, "target", next.target, "delay", delay)
   247  			return next, delay
   248  		}
   249  	}
   250  //找不到注册主题,或者所有主题都已用尽,请睡觉。
   251  	delay := 40 * time.Second
   252  	log.Trace("No topic found to register", "delay", delay)
   253  	return lookupInfo{}, delay
   254  }
   255  
   256  func (s *ticketStore) nextSearchLookup(topic Topic) lookupInfo {
   257  	tr := s.radius[topic]
   258  	target := tr.nextTarget(tr.radiusLookupCnt >= searchForceQuery)
   259  	if target.radiusLookup {
   260  		tr.radiusLookupCnt++
   261  	} else {
   262  		tr.radiusLookupCnt = 0
   263  	}
   264  	return target
   265  }
   266  
   267  //TicketsInWindow返回注册窗口中给定主题的门票。
   268  func (s *ticketStore) ticketsInWindow(topic Topic) []ticketRef {
   269  //在操作之前,请检查主题是否仍然存在
   270  	if s.tickets[topic] == nil {
   271  		log.Warn("Listing non-existing discovery tickets", "topic", topic)
   272  		return nil
   273  	}
   274  //在下一个时间窗口收集所有的票
   275  	var tickets []ticketRef
   276  
   277  	buckets := s.tickets[topic].buckets
   278  	for idx := timeBucket(0); idx < timeWindow; idx++ {
   279  		tickets = append(tickets, buckets[s.lastBucketFetched+idx]...)
   280  	}
   281  	log.Trace("Retrieved discovery registration tickets", "topic", topic, "from", s.lastBucketFetched, "tickets", len(tickets))
   282  	return tickets
   283  }
   284  
   285  func (s *ticketStore) removeExcessTickets(t Topic) {
   286  	tickets := s.ticketsInWindow(t)
   287  	if len(tickets) <= wantTicketsInWindow {
   288  		return
   289  	}
   290  	sort.Sort(ticketRefByWaitTime(tickets))
   291  	for _, r := range tickets[wantTicketsInWindow:] {
   292  		s.removeTicketRef(r)
   293  	}
   294  }
   295  
   296  type ticketRefByWaitTime []ticketRef
   297  
   298  //len是集合中的元素数。
   299  func (s ticketRefByWaitTime) Len() int {
   300  	return len(s)
   301  }
   302  
   303  func (ref ticketRef) waitTime() mclock.AbsTime {
   304  	return ref.t.regTime[ref.idx] - ref.t.issueTime
   305  }
   306  
   307  //少报告元素是否
   308  //索引i应该在索引j的元素之前排序。
   309  func (s ticketRefByWaitTime) Less(i, j int) bool {
   310  	return s[i].waitTime() < s[j].waitTime()
   311  }
   312  
   313  //交换用索引i和j交换元素。
   314  func (s ticketRefByWaitTime) Swap(i, j int) {
   315  	s[i], s[j] = s[j], s[i]
   316  }
   317  
   318  func (s *ticketStore) addTicketRef(r ticketRef) {
   319  	topic := r.t.topics[r.idx]
   320  	tickets := s.tickets[topic]
   321  	if tickets == nil {
   322  		log.Warn("Adding ticket to non-existent topic", "topic", topic)
   323  		return
   324  	}
   325  	bucket := timeBucket(r.t.regTime[r.idx] / mclock.AbsTime(ticketTimeBucketLen))
   326  	tickets.buckets[bucket] = append(tickets.buckets[bucket], r)
   327  	r.t.refCnt++
   328  
   329  	min := mclock.Now() - mclock.AbsTime(collectFrequency)*maxCollectDebt
   330  	if tickets.nextLookup < min {
   331  		tickets.nextLookup = min
   332  	}
   333  	tickets.nextLookup += mclock.AbsTime(collectFrequency)
   334  
   335  //s.removeexcesstickets(主题)
   336  }
   337  
   338  func (s *ticketStore) nextFilteredTicket() (*ticketRef, time.Duration) {
   339  	now := mclock.Now()
   340  	for {
   341  		ticket, wait := s.nextRegisterableTicket()
   342  		if ticket == nil {
   343  			return ticket, wait
   344  		}
   345  		log.Trace("Found discovery ticket to register", "node", ticket.t.node, "serial", ticket.t.serial, "wait", wait)
   346  
   347  		regTime := now + mclock.AbsTime(wait)
   348  		topic := ticket.t.topics[ticket.idx]
   349  		if s.tickets[topic] != nil && regTime >= s.tickets[topic].nextReg {
   350  			return ticket, wait
   351  		}
   352  		s.removeTicketRef(*ticket)
   353  	}
   354  }
   355  
   356  func (s *ticketStore) ticketRegistered(ref ticketRef) {
   357  	now := mclock.Now()
   358  
   359  	topic := ref.t.topics[ref.idx]
   360  	tickets := s.tickets[topic]
   361  	min := now - mclock.AbsTime(registerFrequency)*maxRegisterDebt
   362  	if min > tickets.nextReg {
   363  		tickets.nextReg = min
   364  	}
   365  	tickets.nextReg += mclock.AbsTime(registerFrequency)
   366  	s.tickets[topic] = tickets
   367  
   368  	s.removeTicketRef(ref)
   369  }
   370  
   371  //NextRegisterableTicket返回可使用的下一张票据
   372  //注册。
   373  //
   374  //如果返回的等待时间<=0,则可以使用票据。为正
   375  //等待时间,呼叫方应稍后重新申请下一张票据。
   376  //
   377  //如果等待时间小于等于零,则可以多次返回一张票
   378  //票据包含多个主题。
   379  func (s *ticketStore) nextRegisterableTicket() (*ticketRef, time.Duration) {
   380  	now := mclock.Now()
   381  	if s.nextTicketCached != nil {
   382  		return s.nextTicketCached, time.Duration(s.nextTicketCached.topicRegTime() - now)
   383  	}
   384  
   385  	for bucket := s.lastBucketFetched; ; bucket++ {
   386  		var (
   387  empty      = true    //如果没有票是真的
   388  nextTicket ticketRef //如果此存储桶为空,则未初始化
   389  		)
   390  		for _, tickets := range s.tickets {
   391  //s.removeexcesstickets(主题)
   392  			if len(tickets.buckets) != 0 {
   393  				empty = false
   394  
   395  				list := tickets.buckets[bucket]
   396  				for _, ref := range list {
   397  //debuglog(fmt.sprintf(“nrt bucket=%d node=%x sn=%v wait=%v”,bucket,ref.t.node.id[:8],ref.t.serial,time.duration(ref.topicregtime()-now)))
   398  					if nextTicket.t == nil || ref.topicRegTime() < nextTicket.topicRegTime() {
   399  						nextTicket = ref
   400  					}
   401  				}
   402  			}
   403  		}
   404  		if empty {
   405  			return nil, 0
   406  		}
   407  		if nextTicket.t != nil {
   408  			s.nextTicketCached = &nextTicket
   409  			return &nextTicket, time.Duration(nextTicket.topicRegTime() - now)
   410  		}
   411  		s.lastBucketFetched = bucket
   412  	}
   413  }
   414  
   415  //removeticket从票务商店中删除一张票
   416  func (s *ticketStore) removeTicketRef(ref ticketRef) {
   417  	log.Trace("Removing discovery ticket reference", "node", ref.t.node.ID, "serial", ref.t.serial)
   418  
   419  //使NextRegisterableTicket返回下一个可用的Ticket。
   420  	s.nextTicketCached = nil
   421  
   422  	topic := ref.topic()
   423  	tickets := s.tickets[topic]
   424  
   425  	if tickets == nil {
   426  		log.Trace("Removing tickets from unknown topic", "topic", topic)
   427  		return
   428  	}
   429  	bucket := timeBucket(ref.t.regTime[ref.idx] / mclock.AbsTime(ticketTimeBucketLen))
   430  	list := tickets.buckets[bucket]
   431  	idx := -1
   432  	for i, bt := range list {
   433  		if bt.t == ref.t {
   434  			idx = i
   435  			break
   436  		}
   437  	}
   438  	if idx == -1 {
   439  		panic(nil)
   440  	}
   441  	list = append(list[:idx], list[idx+1:]...)
   442  	if len(list) != 0 {
   443  		tickets.buckets[bucket] = list
   444  	} else {
   445  		delete(tickets.buckets, bucket)
   446  	}
   447  	ref.t.refCnt--
   448  	if ref.t.refCnt == 0 {
   449  		delete(s.nodes, ref.t.node)
   450  		delete(s.nodeLastReq, ref.t.node)
   451  	}
   452  }
   453  
   454  type lookupInfo struct {
   455  	target       common.Hash
   456  	topic        Topic
   457  	radiusLookup bool
   458  }
   459  
   460  type reqInfo struct {
   461  	pingHash []byte
   462  	lookup   lookupInfo
   463  	time     mclock.AbsTime
   464  }
   465  
   466  //如果找不到,返回-1
   467  func (t *ticket) findIdx(topic Topic) int {
   468  	for i, tt := range t.topics {
   469  		if tt == topic {
   470  			return i
   471  		}
   472  	}
   473  	return -1
   474  }
   475  
   476  func (s *ticketStore) registerLookupDone(lookup lookupInfo, nodes []*Node, ping func(n *Node) []byte) {
   477  	now := mclock.Now()
   478  	for i, n := range nodes {
   479  		if i == 0 || (binary.BigEndian.Uint64(n.sha[:8])^binary.BigEndian.Uint64(lookup.target[:8])) < s.radius[lookup.topic].minRadius {
   480  			if lookup.radiusLookup {
   481  				if lastReq, ok := s.nodeLastReq[n]; !ok || time.Duration(now-lastReq.time) > radiusTC {
   482  					s.nodeLastReq[n] = reqInfo{pingHash: ping(n), lookup: lookup, time: now}
   483  				}
   484  			} else {
   485  				if s.nodes[n] == nil {
   486  					s.nodeLastReq[n] = reqInfo{pingHash: ping(n), lookup: lookup, time: now}
   487  				}
   488  			}
   489  		}
   490  	}
   491  }
   492  
   493  func (s *ticketStore) searchLookupDone(lookup lookupInfo, nodes []*Node, query func(n *Node, topic Topic) []byte) {
   494  	now := mclock.Now()
   495  	for i, n := range nodes {
   496  		if i == 0 || (binary.BigEndian.Uint64(n.sha[:8])^binary.BigEndian.Uint64(lookup.target[:8])) < s.radius[lookup.topic].minRadius {
   497  			if lookup.radiusLookup {
   498  				if lastReq, ok := s.nodeLastReq[n]; !ok || time.Duration(now-lastReq.time) > radiusTC {
   499  					s.nodeLastReq[n] = reqInfo{pingHash: nil, lookup: lookup, time: now}
   500  				}
   501  } //否则{
   502  			if s.canQueryTopic(n, lookup.topic) {
   503  				hash := query(n, lookup.topic)
   504  				if hash != nil {
   505  					s.addTopicQuery(common.BytesToHash(hash), n, lookup)
   506  				}
   507  			}
   508  //}
   509  		}
   510  	}
   511  }
   512  
   513  func (s *ticketStore) adjustWithTicket(now mclock.AbsTime, targetHash common.Hash, t *ticket) {
   514  	for i, topic := range t.topics {
   515  		if tt, ok := s.radius[topic]; ok {
   516  			tt.adjustWithTicket(now, targetHash, ticketRef{t, i})
   517  		}
   518  	}
   519  }
   520  
   521  func (s *ticketStore) addTicket(localTime mclock.AbsTime, pingHash []byte, ticket *ticket) {
   522  	log.Trace("Adding discovery ticket", "node", ticket.node.ID, "serial", ticket.serial)
   523  
   524  	lastReq, ok := s.nodeLastReq[ticket.node]
   525  	if !(ok && bytes.Equal(pingHash, lastReq.pingHash)) {
   526  		return
   527  	}
   528  	s.adjustWithTicket(localTime, lastReq.lookup.target, ticket)
   529  
   530  	if lastReq.lookup.radiusLookup || s.nodes[ticket.node] != nil {
   531  		return
   532  	}
   533  
   534  	topic := lastReq.lookup.topic
   535  	topicIdx := ticket.findIdx(topic)
   536  	if topicIdx == -1 {
   537  		return
   538  	}
   539  
   540  	bucket := timeBucket(localTime / mclock.AbsTime(ticketTimeBucketLen))
   541  	if s.lastBucketFetched == 0 || bucket < s.lastBucketFetched {
   542  		s.lastBucketFetched = bucket
   543  	}
   544  
   545  	if _, ok := s.tickets[topic]; ok {
   546  		wait := ticket.regTime[topicIdx] - localTime
   547  		rnd := rand.ExpFloat64()
   548  		if rnd > 10 {
   549  			rnd = 10
   550  		}
   551  		if float64(wait) < float64(keepTicketConst)+float64(keepTicketExp)*rnd {
   552  //使用通知单注册此主题
   553  //fmt.println(“addticket”,ticket.node.id[:8],ticket.node.addr().string(),ticket.serial,ticket.pong)
   554  			s.addTicketRef(ticketRef{ticket, topicIdx})
   555  		}
   556  	}
   557  
   558  	if ticket.refCnt > 0 {
   559  		s.nextTicketCached = nil
   560  		s.nodes[ticket.node] = ticket
   561  	}
   562  }
   563  
   564  func (s *ticketStore) getNodeTicket(node *Node) *ticket {
   565  	if s.nodes[node] == nil {
   566  		log.Trace("Retrieving node ticket", "node", node.ID, "serial", nil)
   567  	} else {
   568  		log.Trace("Retrieving node ticket", "node", node.ID, "serial", s.nodes[node].serial)
   569  	}
   570  	return s.nodes[node]
   571  }
   572  
   573  func (s *ticketStore) canQueryTopic(node *Node, topic Topic) bool {
   574  	qq := s.queriesSent[node]
   575  	if qq != nil {
   576  		now := mclock.Now()
   577  		for _, sq := range qq {
   578  			if sq.lookup.topic == topic && sq.sent > now-mclock.AbsTime(topicQueryResend) {
   579  				return false
   580  			}
   581  		}
   582  	}
   583  	return true
   584  }
   585  
   586  func (s *ticketStore) addTopicQuery(hash common.Hash, node *Node, lookup lookupInfo) {
   587  	now := mclock.Now()
   588  	qq := s.queriesSent[node]
   589  	if qq == nil {
   590  		qq = make(map[common.Hash]sentQuery)
   591  		s.queriesSent[node] = qq
   592  	}
   593  	qq[hash] = sentQuery{sent: now, lookup: lookup}
   594  	s.cleanupTopicQueries(now)
   595  }
   596  
   597  func (s *ticketStore) cleanupTopicQueries(now mclock.AbsTime) {
   598  	if s.nextTopicQueryCleanup > now {
   599  		return
   600  	}
   601  	exp := now - mclock.AbsTime(topicQueryResend)
   602  	for n, qq := range s.queriesSent {
   603  		for h, q := range qq {
   604  			if q.sent < exp {
   605  				delete(qq, h)
   606  			}
   607  		}
   608  		if len(qq) == 0 {
   609  			delete(s.queriesSent, n)
   610  		}
   611  	}
   612  	s.nextTopicQueryCleanup = now + mclock.AbsTime(topicQueryTimeout)
   613  }
   614  
   615  func (s *ticketStore) gotTopicNodes(from *Node, hash common.Hash, nodes []rpcNode) (timeout bool) {
   616  	now := mclock.Now()
   617  //fmt.println(“got”,from.addr().string(),hash,len(nodes))。
   618  	qq := s.queriesSent[from]
   619  	if qq == nil {
   620  		return true
   621  	}
   622  	q, ok := qq[hash]
   623  	if !ok || now > q.sent+mclock.AbsTime(topicQueryTimeout) {
   624  		return true
   625  	}
   626  	inside := float64(0)
   627  	if len(nodes) > 0 {
   628  		inside = 1
   629  	}
   630  	s.radius[q.lookup.topic].adjust(now, q.lookup.target, from.sha, inside)
   631  	chn := s.searchTopicMap[q.lookup.topic].foundChn
   632  	if chn == nil {
   633  //fmt.println(“无通道”)
   634  		return false
   635  	}
   636  	for _, node := range nodes {
   637  		ip := node.IP
   638  		if ip.IsUnspecified() || ip.IsLoopback() {
   639  			ip = from.IP
   640  		}
   641  		n := NewNode(node.ID, ip, node.UDP, node.TCP)
   642  		select {
   643  		case chn <- n:
   644  		default:
   645  			return false
   646  		}
   647  	}
   648  	return false
   649  }
   650  
   651  type topicRadius struct {
   652  	topic             Topic
   653  	topicHashPrefix   uint64
   654  	radius, minRadius uint64
   655  	buckets           []topicRadiusBucket
   656  	converged         bool
   657  	radiusLookupCnt   int
   658  }
   659  
   660  type topicRadiusEvent int
   661  
   662  const (
   663  	trOutside topicRadiusEvent = iota
   664  	trInside
   665  	trNoAdjust
   666  	trCount
   667  )
   668  
   669  type topicRadiusBucket struct {
   670  	weights    [trCount]float64
   671  	lastTime   mclock.AbsTime
   672  	value      float64
   673  	lookupSent map[common.Hash]mclock.AbsTime
   674  }
   675  
   676  func (b *topicRadiusBucket) update(now mclock.AbsTime) {
   677  	if now == b.lastTime {
   678  		return
   679  	}
   680  	exp := math.Exp(-float64(now-b.lastTime) / float64(radiusTC))
   681  	for i, w := range b.weights {
   682  		b.weights[i] = w * exp
   683  	}
   684  	b.lastTime = now
   685  
   686  	for target, tm := range b.lookupSent {
   687  		if now-tm > mclock.AbsTime(respTimeout) {
   688  			b.weights[trNoAdjust] += 1
   689  			delete(b.lookupSent, target)
   690  		}
   691  	}
   692  }
   693  
   694  func (b *topicRadiusBucket) adjust(now mclock.AbsTime, inside float64) {
   695  	b.update(now)
   696  	if inside <= 0 {
   697  		b.weights[trOutside] += 1
   698  	} else {
   699  		if inside >= 1 {
   700  			b.weights[trInside] += 1
   701  		} else {
   702  			b.weights[trInside] += inside
   703  			b.weights[trOutside] += 1 - inside
   704  		}
   705  	}
   706  }
   707  
   708  func newTopicRadius(t Topic) *topicRadius {
   709  	topicHash := crypto.Keccak256Hash([]byte(t))
   710  	topicHashPrefix := binary.BigEndian.Uint64(topicHash[0:8])
   711  
   712  	return &topicRadius{
   713  		topic:           t,
   714  		topicHashPrefix: topicHashPrefix,
   715  		radius:          maxRadius,
   716  		minRadius:       maxRadius,
   717  	}
   718  }
   719  
   720  func (r *topicRadius) getBucketIdx(addrHash common.Hash) int {
   721  	prefix := binary.BigEndian.Uint64(addrHash[0:8])
   722  	var log2 float64
   723  	if prefix != r.topicHashPrefix {
   724  		log2 = math.Log2(float64(prefix ^ r.topicHashPrefix))
   725  	}
   726  	bucket := int((64 - log2) * radiusBucketsPerBit)
   727  	max := 64*radiusBucketsPerBit - 1
   728  	if bucket > max {
   729  		return max
   730  	}
   731  	if bucket < 0 {
   732  		return 0
   733  	}
   734  	return bucket
   735  }
   736  
   737  func (r *topicRadius) targetForBucket(bucket int) common.Hash {
   738  	min := math.Pow(2, 64-float64(bucket+1)/radiusBucketsPerBit)
   739  	max := math.Pow(2, 64-float64(bucket)/radiusBucketsPerBit)
   740  	a := uint64(min)
   741  	b := randUint64n(uint64(max - min))
   742  	xor := a + b
   743  	if xor < a {
   744  		xor = ^uint64(0)
   745  	}
   746  	prefix := r.topicHashPrefix ^ xor
   747  	var target common.Hash
   748  	binary.BigEndian.PutUint64(target[0:8], prefix)
   749  	globalRandRead(target[8:])
   750  	return target
   751  }
   752  
   753  //package rand在go 1.6及更高版本中提供读取功能,但是
   754  //我们还不能使用它,因为我们仍然支持Go 1.5。
   755  func globalRandRead(b []byte) {
   756  	pos := 0
   757  	val := 0
   758  	for n := 0; n < len(b); n++ {
   759  		if pos == 0 {
   760  			val = rand.Int()
   761  			pos = 7
   762  		}
   763  		b[n] = byte(val)
   764  		val >>= 8
   765  		pos--
   766  	}
   767  }
   768  
   769  func (r *topicRadius) isInRadius(addrHash common.Hash) bool {
   770  	nodePrefix := binary.BigEndian.Uint64(addrHash[0:8])
   771  	dist := nodePrefix ^ r.topicHashPrefix
   772  	return dist < r.radius
   773  }
   774  
   775  func (r *topicRadius) chooseLookupBucket(a, b int) int {
   776  	if a < 0 {
   777  		a = 0
   778  	}
   779  	if a > b {
   780  		return -1
   781  	}
   782  	c := 0
   783  	for i := a; i <= b; i++ {
   784  		if i >= len(r.buckets) || r.buckets[i].weights[trNoAdjust] < maxNoAdjust {
   785  			c++
   786  		}
   787  	}
   788  	if c == 0 {
   789  		return -1
   790  	}
   791  	rnd := randUint(uint32(c))
   792  	for i := a; i <= b; i++ {
   793  		if i >= len(r.buckets) || r.buckets[i].weights[trNoAdjust] < maxNoAdjust {
   794  			if rnd == 0 {
   795  				return i
   796  			}
   797  			rnd--
   798  		}
   799  	}
   800  panic(nil) //不应该发生
   801  }
   802  
   803  func (r *topicRadius) needMoreLookups(a, b int, maxValue float64) bool {
   804  	var max float64
   805  	if a < 0 {
   806  		a = 0
   807  	}
   808  	if b >= len(r.buckets) {
   809  		b = len(r.buckets) - 1
   810  		if r.buckets[b].value > max {
   811  			max = r.buckets[b].value
   812  		}
   813  	}
   814  	if b >= a {
   815  		for i := a; i <= b; i++ {
   816  			if r.buckets[i].value > max {
   817  				max = r.buckets[i].value
   818  			}
   819  		}
   820  	}
   821  	return maxValue-max < minPeakSize
   822  }
   823  
   824  func (r *topicRadius) recalcRadius() (radius uint64, radiusLookup int) {
   825  	maxBucket := 0
   826  	maxValue := float64(0)
   827  	now := mclock.Now()
   828  	v := float64(0)
   829  	for i := range r.buckets {
   830  		r.buckets[i].update(now)
   831  		v += r.buckets[i].weights[trOutside] - r.buckets[i].weights[trInside]
   832  		r.buckets[i].value = v
   833  //fmt.printf(“%v%v”,v,r.buckets[i].权重[trnoadjust])
   834  	}
   835  //打印文件()
   836  	slopeCross := -1
   837  	for i, b := range r.buckets {
   838  		v := b.value
   839  		if v < float64(i)*minSlope {
   840  			slopeCross = i
   841  			break
   842  		}
   843  		if v > maxValue {
   844  			maxValue = v
   845  			maxBucket = i + 1
   846  		}
   847  	}
   848  
   849  	minRadBucket := len(r.buckets)
   850  	sum := float64(0)
   851  	for minRadBucket > 0 && sum < minRightSum {
   852  		minRadBucket--
   853  		b := r.buckets[minRadBucket]
   854  		sum += b.weights[trInside] + b.weights[trOutside]
   855  	}
   856  	r.minRadius = uint64(math.Pow(2, 64-float64(minRadBucket)/radiusBucketsPerBit))
   857  
   858  	lookupLeft := -1
   859  	if r.needMoreLookups(0, maxBucket-lookupWidth-1, maxValue) {
   860  		lookupLeft = r.chooseLookupBucket(maxBucket-lookupWidth, maxBucket-1)
   861  	}
   862  	lookupRight := -1
   863  	if slopeCross != maxBucket && (minRadBucket <= maxBucket || r.needMoreLookups(maxBucket+lookupWidth, len(r.buckets)-1, maxValue)) {
   864  		for len(r.buckets) <= maxBucket+lookupWidth {
   865  			r.buckets = append(r.buckets, topicRadiusBucket{lookupSent: make(map[common.Hash]mclock.AbsTime)})
   866  		}
   867  		lookupRight = r.chooseLookupBucket(maxBucket, maxBucket+lookupWidth-1)
   868  	}
   869  	if lookupLeft == -1 {
   870  		radiusLookup = lookupRight
   871  	} else {
   872  		if lookupRight == -1 {
   873  			radiusLookup = lookupLeft
   874  		} else {
   875  			if randUint(2) == 0 {
   876  				radiusLookup = lookupLeft
   877  			} else {
   878  				radiusLookup = lookupRight
   879  			}
   880  		}
   881  	}
   882  
   883  //fmt.println(“mb”,maxbucket,“sc”,slopecross,“mrb”,minradbucket,“ll”,lookupleft,“lr”,look直立,“mv”,maxvalue)
   884  
   885  	if radiusLookup == -1 {
   886  //现在不需要再进行半径查找,返回半径
   887  		r.converged = true
   888  		rad := maxBucket
   889  		if minRadBucket < rad {
   890  			rad = minRadBucket
   891  		}
   892  		radius = ^uint64(0)
   893  		if rad > 0 {
   894  			radius = uint64(math.Pow(2, 64-float64(rad)/radiusBucketsPerBit))
   895  		}
   896  		r.radius = radius
   897  	}
   898  
   899  	return
   900  }
   901  
   902  func (r *topicRadius) nextTarget(forceRegular bool) lookupInfo {
   903  	if !forceRegular {
   904  		_, radiusLookup := r.recalcRadius()
   905  		if radiusLookup != -1 {
   906  			target := r.targetForBucket(radiusLookup)
   907  			r.buckets[radiusLookup].lookupSent[target] = mclock.Now()
   908  			return lookupInfo{target: target, topic: r.topic, radiusLookup: true}
   909  		}
   910  	}
   911  
   912  	radExt := r.radius / 2
   913  	if radExt > maxRadius-r.radius {
   914  		radExt = maxRadius - r.radius
   915  	}
   916  	rnd := randUint64n(r.radius) + randUint64n(2*radExt)
   917  	if rnd > radExt {
   918  		rnd -= radExt
   919  	} else {
   920  		rnd = radExt - rnd
   921  	}
   922  
   923  	prefix := r.topicHashPrefix ^ rnd
   924  	var target common.Hash
   925  	binary.BigEndian.PutUint64(target[0:8], prefix)
   926  	globalRandRead(target[8:])
   927  	return lookupInfo{target: target, topic: r.topic, radiusLookup: false}
   928  }
   929  
   930  func (r *topicRadius) adjustWithTicket(now mclock.AbsTime, targetHash common.Hash, t ticketRef) {
   931  	wait := t.t.regTime[t.idx] - t.t.issueTime
   932  	inside := float64(wait)/float64(targetWaitTime) - 0.5
   933  	if inside > 1 {
   934  		inside = 1
   935  	}
   936  	if inside < 0 {
   937  		inside = 0
   938  	}
   939  	r.adjust(now, targetHash, t.t.node.sha, inside)
   940  }
   941  
   942  func (r *topicRadius) adjust(now mclock.AbsTime, targetHash, addrHash common.Hash, inside float64) {
   943  	bucket := r.getBucketIdx(addrHash)
   944  //fmt.println(“调整”,桶,长度(R.buckets),内部)
   945  	if bucket >= len(r.buckets) {
   946  		return
   947  	}
   948  	r.buckets[bucket].adjust(now, inside)
   949  	delete(r.buckets[bucket].lookupSent, targetHash)
   950  }
   951