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 }