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