github.com/yinchengtsinghua/golang-Eos-dpos-Ethereum@v0.0.0-20190121132951-92cc4225ed8e/p2p/discv5/ticket.go (about)

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