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		// TODO ping the kb[0], and if no response, delete it, and add the current o (ID)
79		return
80	}
81	// check that is not already in the list
82	exist, _ := existsInListedNodes(n.KBuckets[k], o)
83	if exist {
84		// update position to the bottom
85		return
86	}
87	// not exists, add it to the kBucket
88	n.KBuckets[k] = append(n.KBuckets[k], o)
89	return
90}
91
92func existsInListedNodes(lns []ListedNode, ln ListedNode) (bool, int) {
93	for i, v := range lns {
94		if v.ID.Equal(ln.ID) {
95			return true, i
96		}
97	}
98	return false, 0
99}