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