table.go 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  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. // Package discv5 implements the RLPx v5 Topic Discovery Protocol.
  17. //
  18. // The Topic Discovery protocol provides a way to find RLPx nodes that
  19. // can be connected to. It uses a Kademlia-like protocol to maintain a
  20. // distributed database of the IDs and endpoints of all listening
  21. // nodes.
  22. package discv5
  23. import (
  24. "crypto/rand"
  25. "encoding/binary"
  26. "fmt"
  27. "net"
  28. "sort"
  29. "github.com/ethereum/go-ethereum/common"
  30. )
  31. const (
  32. alpha = 3 // Kademlia concurrency factor
  33. bucketSize = 16 // Kademlia bucket size
  34. hashBits = len(common.Hash{}) * 8
  35. nBuckets = hashBits + 1 // Number of buckets
  36. maxBondingPingPongs = 16
  37. maxFindnodeFailures = 5
  38. )
  39. type Table struct {
  40. count int // number of nodes
  41. buckets [nBuckets]*bucket // index of known nodes by distance
  42. nodeAddedHook func(*Node) // for testing
  43. self *Node // metadata of the local node
  44. }
  45. // bucket contains nodes, ordered by their last activity. the entry
  46. // that was most recently active is the first element in entries.
  47. type bucket struct {
  48. entries []*Node
  49. replacements []*Node
  50. }
  51. func newTable(ourID NodeID, ourAddr *net.UDPAddr) *Table {
  52. self := NewNode(ourID, ourAddr.IP, uint16(ourAddr.Port), uint16(ourAddr.Port))
  53. tab := &Table{self: self}
  54. for i := range tab.buckets {
  55. tab.buckets[i] = new(bucket)
  56. }
  57. return tab
  58. }
  59. const printTable = false
  60. // chooseBucketRefreshTarget selects random refresh targets to keep all Kademlia
  61. // buckets filled with live connections and keep the network topology healthy.
  62. // This requires selecting addresses closer to our own with a higher probability
  63. // in order to refresh closer buckets too.
  64. //
  65. // This algorithm approximates the distance distribution of existing nodes in the
  66. // table by selecting a random node from the table and selecting a target address
  67. // with a distance less than twice of that of the selected node.
  68. // This algorithm will be improved later to specifically target the least recently
  69. // used buckets.
  70. func (tab *Table) chooseBucketRefreshTarget() common.Hash {
  71. entries := 0
  72. if printTable {
  73. fmt.Println()
  74. }
  75. for i, b := range tab.buckets {
  76. entries += len(b.entries)
  77. if printTable {
  78. for _, e := range b.entries {
  79. fmt.Println(i, e.state, e.addr().String(), e.ID.String(), e.sha.Hex())
  80. }
  81. }
  82. }
  83. prefix := binary.BigEndian.Uint64(tab.self.sha[0:8])
  84. dist := ^uint64(0)
  85. entry := int(randUint(uint32(entries + 1)))
  86. for _, b := range tab.buckets {
  87. if entry < len(b.entries) {
  88. n := b.entries[entry]
  89. dist = binary.BigEndian.Uint64(n.sha[0:8]) ^ prefix
  90. break
  91. }
  92. entry -= len(b.entries)
  93. }
  94. ddist := ^uint64(0)
  95. if dist+dist > dist {
  96. ddist = dist
  97. }
  98. targetPrefix := prefix ^ randUint64n(ddist)
  99. var target common.Hash
  100. binary.BigEndian.PutUint64(target[0:8], targetPrefix)
  101. rand.Read(target[8:])
  102. return target
  103. }
  104. // readRandomNodes fills the given slice with random nodes from the
  105. // table. It will not write the same node more than once. The nodes in
  106. // the slice are copies and can be modified by the caller.
  107. func (tab *Table) readRandomNodes(buf []*Node) (n int) {
  108. // TODO: tree-based buckets would help here
  109. // Find all non-empty buckets and get a fresh slice of their entries.
  110. var buckets [][]*Node
  111. for _, b := range tab.buckets {
  112. if len(b.entries) > 0 {
  113. buckets = append(buckets, b.entries[:])
  114. }
  115. }
  116. if len(buckets) == 0 {
  117. return 0
  118. }
  119. // Shuffle the buckets.
  120. for i := uint32(len(buckets)) - 1; i > 0; i-- {
  121. j := randUint(i)
  122. buckets[i], buckets[j] = buckets[j], buckets[i]
  123. }
  124. // Move head of each bucket into buf, removing buckets that become empty.
  125. var i, j int
  126. for ; i < len(buf); i, j = i+1, (j+1)%len(buckets) {
  127. b := buckets[j]
  128. buf[i] = &(*b[0])
  129. buckets[j] = b[1:]
  130. if len(b) == 1 {
  131. buckets = append(buckets[:j], buckets[j+1:]...)
  132. }
  133. if len(buckets) == 0 {
  134. break
  135. }
  136. }
  137. return i + 1
  138. }
  139. func randUint(max uint32) uint32 {
  140. if max < 2 {
  141. return 0
  142. }
  143. var b [4]byte
  144. rand.Read(b[:])
  145. return binary.BigEndian.Uint32(b[:]) % max
  146. }
  147. func randUint64n(max uint64) uint64 {
  148. if max < 2 {
  149. return 0
  150. }
  151. var b [8]byte
  152. rand.Read(b[:])
  153. return binary.BigEndian.Uint64(b[:]) % max
  154. }
  155. // closest returns the n nodes in the table that are closest to the
  156. // given id. The caller must hold tab.mutex.
  157. func (tab *Table) closest(target common.Hash, nresults int) *nodesByDistance {
  158. // This is a very wasteful way to find the closest nodes but
  159. // obviously correct. I believe that tree-based buckets would make
  160. // this easier to implement efficiently.
  161. close := &nodesByDistance{target: target}
  162. for _, b := range tab.buckets {
  163. for _, n := range b.entries {
  164. close.push(n, nresults)
  165. }
  166. }
  167. return close
  168. }
  169. // add attempts to add the given node its corresponding bucket. If the
  170. // bucket has space available, adding the node succeeds immediately.
  171. // Otherwise, the node is added to the replacement cache for the bucket.
  172. func (tab *Table) add(n *Node) (contested *Node) {
  173. //fmt.Println("add", n.addr().String(), n.ID.String(), n.sha.Hex())
  174. if n.ID == tab.self.ID {
  175. return
  176. }
  177. b := tab.buckets[logdist(tab.self.sha, n.sha)]
  178. switch {
  179. case b.bump(n):
  180. // n exists in b.
  181. return nil
  182. case len(b.entries) < bucketSize:
  183. // b has space available.
  184. b.addFront(n)
  185. tab.count++
  186. if tab.nodeAddedHook != nil {
  187. tab.nodeAddedHook(n)
  188. }
  189. return nil
  190. default:
  191. // b has no space left, add to replacement cache
  192. // and revalidate the last entry.
  193. // TODO: drop previous node
  194. b.replacements = append(b.replacements, n)
  195. if len(b.replacements) > bucketSize {
  196. copy(b.replacements, b.replacements[1:])
  197. b.replacements = b.replacements[:len(b.replacements)-1]
  198. }
  199. return b.entries[len(b.entries)-1]
  200. }
  201. }
  202. // stuff adds nodes the table to the end of their corresponding bucket
  203. // if the bucket is not full.
  204. func (tab *Table) stuff(nodes []*Node) {
  205. outer:
  206. for _, n := range nodes {
  207. if n.ID == tab.self.ID {
  208. continue // don't add self
  209. }
  210. bucket := tab.buckets[logdist(tab.self.sha, n.sha)]
  211. for i := range bucket.entries {
  212. if bucket.entries[i].ID == n.ID {
  213. continue outer // already in bucket
  214. }
  215. }
  216. if len(bucket.entries) < bucketSize {
  217. bucket.entries = append(bucket.entries, n)
  218. tab.count++
  219. if tab.nodeAddedHook != nil {
  220. tab.nodeAddedHook(n)
  221. }
  222. }
  223. }
  224. }
  225. // delete removes an entry from the node table (used to evacuate
  226. // failed/non-bonded discovery peers).
  227. func (tab *Table) delete(node *Node) {
  228. //fmt.Println("delete", node.addr().String(), node.ID.String(), node.sha.Hex())
  229. bucket := tab.buckets[logdist(tab.self.sha, node.sha)]
  230. for i := range bucket.entries {
  231. if bucket.entries[i].ID == node.ID {
  232. bucket.entries = append(bucket.entries[:i], bucket.entries[i+1:]...)
  233. tab.count--
  234. return
  235. }
  236. }
  237. }
  238. func (tab *Table) deleteReplace(node *Node) {
  239. b := tab.buckets[logdist(tab.self.sha, node.sha)]
  240. i := 0
  241. for i < len(b.entries) {
  242. if b.entries[i].ID == node.ID {
  243. b.entries = append(b.entries[:i], b.entries[i+1:]...)
  244. tab.count--
  245. } else {
  246. i++
  247. }
  248. }
  249. // refill from replacement cache
  250. // TODO: maybe use random index
  251. if len(b.entries) < bucketSize && len(b.replacements) > 0 {
  252. ri := len(b.replacements) - 1
  253. b.addFront(b.replacements[ri])
  254. tab.count++
  255. b.replacements[ri] = nil
  256. b.replacements = b.replacements[:ri]
  257. }
  258. }
  259. func (b *bucket) addFront(n *Node) {
  260. b.entries = append(b.entries, nil)
  261. copy(b.entries[1:], b.entries)
  262. b.entries[0] = n
  263. }
  264. func (b *bucket) bump(n *Node) bool {
  265. for i := range b.entries {
  266. if b.entries[i].ID == n.ID {
  267. // move it to the front
  268. copy(b.entries[1:], b.entries[:i])
  269. b.entries[0] = n
  270. return true
  271. }
  272. }
  273. return false
  274. }
  275. // nodesByDistance is a list of nodes, ordered by
  276. // distance to target.
  277. type nodesByDistance struct {
  278. entries []*Node
  279. target common.Hash
  280. }
  281. // push adds the given node to the list, keeping the total size below maxElems.
  282. func (h *nodesByDistance) push(n *Node, maxElems int) {
  283. ix := sort.Search(len(h.entries), func(i int) bool {
  284. return distcmp(h.target, h.entries[i].sha, n.sha) > 0
  285. })
  286. if len(h.entries) < maxElems {
  287. h.entries = append(h.entries, n)
  288. }
  289. if ix == len(h.entries) {
  290. // farther away than all nodes we already have.
  291. // if there was room for it, the node is now the last element.
  292. } else {
  293. // slide existing entries down to make room
  294. // this will overwrite the entry we just appended.
  295. copy(h.entries[ix+1:], h.entries[ix:])
  296. h.entries[ix] = n
  297. }
  298. }