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.

148 lines
3.0 KiB

  1. package kademlia
  2. import (
  3. "math/bits"
  4. "net/rpc"
  5. "strconv"
  6. log "github.com/sirupsen/logrus"
  7. )
  8. const (
  9. alpha = 3 // 'alpha', max parallalel calls
  10. B = 20 // 'B', 160 bits, ID length
  11. KBucketSize = 20 // 'K', bucket size
  12. )
  13. type ListedNode struct {
  14. ID ID
  15. Addr string
  16. Port string
  17. }
  18. type Kademlia struct {
  19. // N is this node data
  20. N ListedNode
  21. KBuckets [B * 8][]ListedNode
  22. }
  23. func NewKademliaTable(id ID, addr, port string) *Kademlia {
  24. return &Kademlia{
  25. N: ListedNode{
  26. ID: id,
  27. Addr: addr,
  28. Port: port,
  29. },
  30. }
  31. }
  32. func (kad Kademlia) String() string {
  33. r := "Node ID: " + kad.N.ID.String() + ", KBuckets:\n"
  34. for i, kb := range kad.KBuckets {
  35. if len(kb) > 0 {
  36. r += " KBucket " + strconv.Itoa(i) + "\n"
  37. for _, e := range kb {
  38. r += " " + e.ID.String() + "\n"
  39. }
  40. }
  41. }
  42. return r
  43. }
  44. func (kad Kademlia) KBucket(o ID) int {
  45. d := kad.N.ID.Distance(o)
  46. return kBucketByDistance(d[:])
  47. }
  48. func kBucketByDistance(b []byte) int {
  49. for i := 0; i < B; i++ {
  50. for a := b[i] ^ 0; a != 0; a &= a - 1 {
  51. return (B-1-i)*8 + (7 - bits.TrailingZeros8(bits.Reverse8(uint8(a))))
  52. }
  53. }
  54. return (B*8 - 1) - (B*8 - 1)
  55. }
  56. func (kad *Kademlia) Update(o ListedNode) {
  57. k := kad.KBucket(o.ID)
  58. kb := kad.KBuckets[k]
  59. if len(kb) >= KBucketSize {
  60. // if n.KBuckets[k] is alrady full, perform ping of the first element
  61. log.Info("node.KBuckets[k] already full, performing ping to node.KBuckets[0]")
  62. kad.PingOldNode(k, o)
  63. return
  64. }
  65. // check that is not already in the list
  66. exist, pos := existsInListedNodes(kad.KBuckets[k], o)
  67. if exist {
  68. // update position of o to the bottom
  69. kad.KBuckets[k] = moveToBottom(kad.KBuckets[k], pos)
  70. log.Info("ListedNode already exists, moved to bottom")
  71. return
  72. }
  73. // not exists, add it to the kBucket
  74. kad.KBuckets[k] = append(kad.KBuckets[k], o)
  75. log.Info("ListedNode not exists, added to the bottom")
  76. return
  77. }
  78. func (kad *Kademlia) PingOldNode(k int, o ListedNode) {
  79. // TODO when rpc layer is done
  80. // ping the n.KBuckets[k][0] (using goroutine)
  81. // if no response (timeout), delete it and add 'o'
  82. // n.KBuckets[k][0] = o
  83. }
  84. func (kad *Kademlia) CallPing(o ListedNode) error {
  85. client, err := rpc.DialHTTP("tcp", o.Addr+":"+o.Port)
  86. if err != nil {
  87. return err
  88. }
  89. ln := ListedNode{
  90. ID: kad.N.ID,
  91. Addr: kad.N.Addr,
  92. Port: kad.N.Port,
  93. }
  94. var reply ListedNode
  95. err = client.Call("Node.Ping", ln, &reply)
  96. if err != nil {
  97. return err
  98. }
  99. return nil
  100. }
  101. func (kad *Kademlia) CallPong(o ListedNode) error {
  102. client, err := rpc.DialHTTP("tcp", o.Addr+":"+o.Port)
  103. if err != nil {
  104. return err
  105. }
  106. ln := ListedNode{
  107. ID: kad.N.ID,
  108. Addr: kad.N.Addr,
  109. Port: kad.N.Port,
  110. }
  111. var reply bool
  112. err = client.Call("Node.Pong", ln, &reply)
  113. if err != nil {
  114. return err
  115. }
  116. return nil
  117. }
  118. func existsInListedNodes(lns []ListedNode, ln ListedNode) (bool, int) {
  119. for i, v := range lns {
  120. if v.ID.Equal(ln.ID) {
  121. return true, i
  122. }
  123. }
  124. return false, 0
  125. }
  126. func moveToBottom(kb []ListedNode, k int) []ListedNode {
  127. e := kb[k]
  128. kb = append(kb[:k], kb[k+1:]...)
  129. kb = append(kb[:], e)
  130. return kb
  131. }