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