You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

257 lines
5.5 KiB

  1. package kademlia
  2. import (
  3. "errors"
  4. "fmt"
  5. "math/bits"
  6. "net/rpc"
  7. "strconv"
  8. log "github.com/sirupsen/logrus"
  9. )
  10. const (
  11. alpha = 3 // 'alpha', max parallalel calls
  12. B = 20 // 'B', 160 bits, ID length
  13. KBucketSize = 20 // 'K', bucket size
  14. )
  15. type ListedNode struct {
  16. ID ID
  17. Addr string
  18. Port string
  19. }
  20. type Kademlia struct {
  21. // N is this node data
  22. N ListedNode
  23. KBuckets [B][]ListedNode
  24. }
  25. func NewKademliaTable(id ID, addr, port string) *Kademlia {
  26. return &Kademlia{
  27. N: ListedNode{
  28. ID: id,
  29. Addr: addr,
  30. Port: port,
  31. },
  32. }
  33. }
  34. func (kad Kademlia) String() string {
  35. r := "Node ID: " + kad.N.ID.String() + ", KBuckets:\n"
  36. for i, kb := range kad.KBuckets {
  37. if len(kb) > 0 {
  38. r += " KBucket " + strconv.Itoa(i) + "\n"
  39. for _, e := range kb {
  40. r += " " + e.ID.String() + "\n"
  41. }
  42. }
  43. }
  44. return r
  45. }
  46. func (kad Kademlia) GetClosestKBucket(id ID) (int, error) {
  47. kb := kad.KBucket(id)
  48. if kb == 0 {
  49. // is this node
  50. return kb, nil
  51. }
  52. if len(kad.KBuckets[kb]) != 0 {
  53. return kb, nil
  54. }
  55. for i := 0; kb-i > 0 || kb+i < 8; i++ {
  56. if len(kad.KBuckets[kb+i]) != 0 {
  57. return kb + i, nil
  58. }
  59. if len(kad.KBuckets[kb-i]) != 0 {
  60. return kb - i, nil
  61. }
  62. }
  63. return 0, errors.New("not found")
  64. }
  65. func (kad Kademlia) KBucket(o ID) int {
  66. d := kad.N.ID.Distance(o)
  67. return kBucketByDistance(d[:])
  68. }
  69. func kBucketByDistance(b []byte) int {
  70. z := countZeroes(b)
  71. if z == 0 {
  72. return 0
  73. }
  74. for i := 0; i < 8; i++ {
  75. if z>>uint8(i) == 1 { // check until 1 instead of 0 to avoid one operation
  76. return i
  77. }
  78. }
  79. return 7
  80. }
  81. func countZeroes(b []byte) int {
  82. for i := 0; i < B; i++ {
  83. for a := b[i] ^ 0; a != 0; a &= a - 1 {
  84. return (B * 8) - (i * 8) - (8 - bits.TrailingZeros8(bits.Reverse8(uint8(a))))
  85. }
  86. }
  87. return (B*8 - 1) - (B*8 - 1)
  88. }
  89. func (kad *Kademlia) Update(o ListedNode) {
  90. k := kad.KBucket(o.ID)
  91. kb := kad.KBuckets[k]
  92. if len(kb) >= KBucketSize {
  93. // if n.KBuckets[k] is alrady full, perform ping of the first element
  94. log.Debug("node.KBuckets[k] already full, performing ping to node.KBuckets[0]")
  95. kad.PingOldNode(k, o)
  96. return
  97. }
  98. // check that is not already in the list
  99. exist, pos := existsInListedNodes(kad.KBuckets[k], o)
  100. if exist {
  101. // update position of o to the bottom
  102. kad.KBuckets[k] = moveToBottom(kad.KBuckets[k], pos)
  103. log.Debug("ListedNode (" + o.ID.String() + ") already exists, moved to bottom")
  104. return
  105. }
  106. // not exists, add it to the kBucket
  107. kad.KBuckets[k] = append(kad.KBuckets[k], o)
  108. log.Debug("ListedNode (" + o.ID.String() + ") not exists, added to the bottom")
  109. return
  110. }
  111. func (kad *Kademlia) PingOldNode(k int, o ListedNode) {
  112. // TODO
  113. // ping the n.KBuckets[k][0] (using goroutine)
  114. // if no response (timeout), delete it and add 'o'
  115. // n.KBuckets[k][0] = o
  116. }
  117. func (kad *Kademlia) CallPing(o ListedNode) error {
  118. client, err := rpc.DialHTTP("tcp", o.Addr+":"+o.Port)
  119. if err != nil {
  120. return err
  121. }
  122. ln := ListedNode{
  123. ID: kad.N.ID,
  124. Addr: kad.N.Addr,
  125. Port: kad.N.Port,
  126. }
  127. var reply ListedNode
  128. err = client.Call("Node.Ping", ln, &reply)
  129. if err != nil {
  130. return err
  131. }
  132. // TODO use reply as PONG
  133. return nil
  134. }
  135. func (kad *Kademlia) CallPong(o ListedNode) error {
  136. client, err := rpc.DialHTTP("tcp", o.Addr+":"+o.Port)
  137. if err != nil {
  138. return err
  139. }
  140. ln := ListedNode{
  141. ID: kad.N.ID,
  142. Addr: kad.N.Addr,
  143. Port: kad.N.Port,
  144. }
  145. var reply bool
  146. err = client.Call("Node.Pong", ln, &reply)
  147. if err != nil {
  148. return err
  149. }
  150. return nil
  151. }
  152. func existsInListedNodes(lns []ListedNode, ln ListedNode) (bool, int) {
  153. for i, v := range lns {
  154. if v.ID.Equal(ln.ID) {
  155. return true, i
  156. }
  157. }
  158. return false, 0
  159. }
  160. func moveToBottom(kb []ListedNode, k int) []ListedNode {
  161. e := kb[k]
  162. kb = append(kb[:k], kb[k+1:]...)
  163. kb = append(kb[:], e)
  164. return kb
  165. }
  166. func removeFromListedNodes(lns []ListedNode, k int) []ListedNode {
  167. lns = append(lns[:k], lns[k+1:]...)
  168. return lns
  169. }
  170. func (kad Kademlia) CallFindNode(id ID, o ListedNode) ([]ListedNode, error) {
  171. client, err := rpc.DialHTTP("tcp", o.Addr+":"+o.Port)
  172. if err != nil {
  173. return nil, err
  174. }
  175. var lns []ListedNode
  176. err = client.Call("Node.FindNode", id, &lns)
  177. if err != nil {
  178. return nil, err
  179. }
  180. return lns, nil
  181. }
  182. func (kad Kademlia) NodeLookup(id ID) ([]ListedNode, error) {
  183. log.Debug("[NodeLookup] get closest KBucket for ", id)
  184. // find closest kbucket for this current node
  185. k, err := kad.GetClosestKBucket(id)
  186. if err != nil {
  187. return []ListedNode{}, fmt.Errorf("No KBuckets")
  188. }
  189. log.Debug("[NodeLookup] closest KBucket", k)
  190. var closest ListedNode
  191. var newClosest ListedNode
  192. closest = kad.N
  193. var lns []ListedNode
  194. lns = kad.KBuckets[k]
  195. for !closest.ID.Equal(newClosest.ID) { // TODO
  196. // call each ListedNode from the kbucket, asking for their list of closest kbucket
  197. var newLns []ListedNode
  198. closest = newClosest
  199. for k, ln := range lns {
  200. // TODO TMP for the moment is synchronous
  201. fmt.Println("calling node", ln)
  202. flns, err := kad.CallFindNode(id, ln)
  203. if err != nil {
  204. log.Debug(err)
  205. }
  206. removeFromListedNodes(lns, k)
  207. // newLns = append(newLns, flns...)
  208. for _, fln := range flns {
  209. exist, _ := existsInListedNodes(newLns, fln)
  210. if !exist {
  211. fmt.Println("ln", fln.ID, " not exists")
  212. newLns = append(newLns, fln)
  213. }
  214. }
  215. }
  216. lns = newLns
  217. newClosest = updateClosest(id, lns, closest)
  218. }
  219. return lns, nil
  220. }
  221. func updateClosest(id ID, lns []ListedNode, closest ListedNode) ListedNode {
  222. d := id.Distance(closest.ID)
  223. closestZ := countZeroes(d[:])
  224. for _, ln := range lns {
  225. d := id.Distance(ln.ID)
  226. z := countZeroes(d[:])
  227. if z < closestZ {
  228. closest = ln
  229. closestZ = z
  230. }
  231. }
  232. return closest
  233. }