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