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