mirror of
https://github.com/arnaucube/go-dht.git
synced 2026-02-06 19:06:44 +01:00
start node methods, calculate KBuckets
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
tmp.go
|
||||
5
id.go
5
id.go
@@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
)
|
||||
@@ -32,6 +33,10 @@ func IDFromString(s string) (ID, error) {
|
||||
return id, nil
|
||||
}
|
||||
|
||||
func (idA ID) Equal(idB ID) bool {
|
||||
return bytes.Equal(idA[:], idB[:])
|
||||
}
|
||||
|
||||
// Cmp returns true if idA > idB
|
||||
func (idA ID) Cmp(idB ID) bool {
|
||||
for i := 0; i < len(idA); i++ {
|
||||
|
||||
@@ -7,8 +7,12 @@ import (
|
||||
)
|
||||
|
||||
func TestNewID(t *testing.T) {
|
||||
_, err := NewID()
|
||||
assert.Nil(t, err)
|
||||
// get some IDs
|
||||
// for i := 0; i < 10; i++ {
|
||||
// id, err := NewID()
|
||||
// assert.Nil(t, err)
|
||||
// fmt.Println(id)
|
||||
// }
|
||||
|
||||
idA, err := IDFromString("0fd85ddddf15aeec2d5d8b01b013dbca030a18d7")
|
||||
assert.Nil(t, err)
|
||||
|
||||
99
kademlia.go
Normal file
99
kademlia.go
Normal file
@@ -0,0 +1,99 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"math/bits"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
const (
|
||||
alpha = 3 // 'alpha', max parallalel calls
|
||||
B = 20 // 'B', 160 bits, ID length
|
||||
KBucketSize = 20 // 'K', bucket size
|
||||
)
|
||||
|
||||
type ListedNode struct {
|
||||
ID ID
|
||||
Addr string
|
||||
}
|
||||
|
||||
type Node struct {
|
||||
ID ID
|
||||
KBuckets [B * 8][]ListedNode
|
||||
}
|
||||
|
||||
func (n Node) String() string {
|
||||
r := "Node ID: " + n.ID.String() + ", KBuckets:\n"
|
||||
for i, kb := range n.KBuckets {
|
||||
if len(kb) > 0 {
|
||||
r += " KBucket " + strconv.Itoa(i) + "\n"
|
||||
for _, e := range kb {
|
||||
r += " " + e.ID.String() + "\n"
|
||||
}
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func NewNode() (Node, error) {
|
||||
// TODO if node already has id, import it
|
||||
id, err := NewID()
|
||||
if err != nil {
|
||||
return Node{}, err
|
||||
}
|
||||
|
||||
var n Node
|
||||
n.ID = id
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func LoadNode(idStr string) (Node, error) {
|
||||
id, err := IDFromString("0fd85ddddf15aeec2d5d8b01b013dbca030a18d7")
|
||||
if err != nil {
|
||||
return Node{}, err
|
||||
}
|
||||
var n Node
|
||||
n.ID = id
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (n Node) KBucket(o ID) int {
|
||||
d := n.ID.Distance(o)
|
||||
return kBucketByDistance(d[:])
|
||||
|
||||
}
|
||||
|
||||
func kBucketByDistance(b []byte) int {
|
||||
for i := 0; i < B; i++ {
|
||||
for a := b[i] ^ 0; a != 0; a &= a - 1 {
|
||||
return (B-1-i)*8 + (7 - bits.TrailingZeros8(bits.Reverse8(uint8(a))))
|
||||
}
|
||||
}
|
||||
return (B*8 - 1) - (B*8 - 1)
|
||||
}
|
||||
|
||||
func (n *Node) Update(o ListedNode) {
|
||||
k := n.KBucket(o.ID)
|
||||
kb := n.KBuckets[k]
|
||||
if len(kb) >= KBucketSize {
|
||||
// TODO ping the kb[0], and if no response, delete it, and add the current o (ID)
|
||||
return
|
||||
}
|
||||
// check that is not already in the list
|
||||
exist, _ := existsInListedNodes(n.KBuckets[k], o)
|
||||
if exist {
|
||||
// update position to the bottom
|
||||
return
|
||||
}
|
||||
// not exists, add it to the kBucket
|
||||
n.KBuckets[k] = append(n.KBuckets[k], o)
|
||||
return
|
||||
}
|
||||
|
||||
func existsInListedNodes(lns []ListedNode, ln ListedNode) (bool, int) {
|
||||
for i, v := range lns {
|
||||
if v.ID.Equal(ln.ID) {
|
||||
return true, i
|
||||
}
|
||||
}
|
||||
return false, 0
|
||||
}
|
||||
97
kademlia_test.go
Normal file
97
kademlia_test.go
Normal file
@@ -0,0 +1,97 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var debug = true
|
||||
|
||||
func TestCountZeros(t *testing.T) {
|
||||
zeroes := []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
assert.Equal(t, 0, kBucketByDistance(zeroes))
|
||||
|
||||
b := []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
|
||||
assert.Equal(t, 20, len(b))
|
||||
assert.Equal(t, 159, kBucketByDistance(b))
|
||||
b[19] = 0x00
|
||||
assert.Equal(t, 159, kBucketByDistance(b))
|
||||
|
||||
b[0] = 0x0f
|
||||
assert.Equal(t, 159-4, kBucketByDistance(b))
|
||||
|
||||
b[0] = 0x0c
|
||||
assert.Equal(t, 159-4, kBucketByDistance(b))
|
||||
|
||||
b[0] = 0x00
|
||||
b[1] = 0x00
|
||||
b[2] = 0x0f
|
||||
assert.Equal(t, 159-20, kBucketByDistance(b))
|
||||
|
||||
b[2] = 0x07
|
||||
assert.Equal(t, 159-21, kBucketByDistance(b))
|
||||
|
||||
b[2] = 0x03
|
||||
assert.Equal(t, 159-22, kBucketByDistance(b))
|
||||
}
|
||||
|
||||
func TestNodeKBucket(t *testing.T) {
|
||||
node, err := LoadNode("0fd85ddddf15aeec2d5d8b01b013dbca030a18d7")
|
||||
assert.Nil(t, err)
|
||||
|
||||
d := node.KBucket(node.ID)
|
||||
assert.Equal(t, 0, d) // same node should have distance 0
|
||||
|
||||
idB, err := IDFromString("c48d8b53dbefb609ed4e94d386dd5b22efcb2c5b")
|
||||
assert.Nil(t, err)
|
||||
|
||||
d = node.KBucket(idB)
|
||||
assert.Equal(t, 159, d)
|
||||
}
|
||||
|
||||
func TestUpdate(t *testing.T) {
|
||||
nodeA, err := LoadNode("0fd85ddddf15aeec2d5d8b01b013dbca030a18d7")
|
||||
assert.Nil(t, err)
|
||||
|
||||
lnIDs := []string{
|
||||
"c48d8b53dbefb609ed4e94d386dd5b22efcb2c5b",
|
||||
"420bfebd44fc62615253224328f40f29c9b225fa",
|
||||
"6272bb67b1661abfa07d1d32cd9b810e531d42cd",
|
||||
"07db608db033384895e48098a1bbe25266387463",
|
||||
"c19c3285ab9ada4b420050ae1a204640b2bed508",
|
||||
"f8971d5da24517c8cc5a316fb0658de8906c4155",
|
||||
"04122a5f2dec42284147b1847ec1bc41ecd78626",
|
||||
"407a90870d7b482a271446c85fda940ce78a4c7a",
|
||||
"5ebe4539e7a33721a8623f7ebfab62600aa503e7",
|
||||
"fc44a42879ef3a74d6bd8303bc3e4e205a92acf9",
|
||||
"fc44a42879ef3a74d6bd8303bc3e4e205a92acf9",
|
||||
"10c86f96ebaa1685a46a6417e6faa2ef34a68976",
|
||||
"243c81515196a5b0e2d4675e73f0da3eead12793",
|
||||
"0fd85ddddf15aeec2d5d8b01b013dbca030a18d7",
|
||||
"0fd85ddddf15aeec2d5d8b01b013dbca030a18d5",
|
||||
"0fd85ddddf15aeec2d5d8b01b013dbca030a18d0",
|
||||
"0fd85ddddf15aeec2d5d8b01b013dbca030a1800",
|
||||
"0750931c40a52a2facd220a02851f7d34f95e1fa",
|
||||
"ebfba615ac50bcd0f5c2420741d032e885abf576",
|
||||
}
|
||||
for i := 0; i < len(lnIDs); i++ {
|
||||
idI, err := IDFromString(lnIDs[i])
|
||||
assert.Nil(t, err)
|
||||
lnI := ListedNode{
|
||||
ID: idI,
|
||||
Addr: "",
|
||||
}
|
||||
nodeA.Update(lnI)
|
||||
}
|
||||
|
||||
if debug {
|
||||
fmt.Println(nodeA)
|
||||
}
|
||||
|
||||
assert.Equal(t, len(nodeA.KBuckets[0]), 1)
|
||||
assert.Equal(t, len(nodeA.KBuckets[1]), 1)
|
||||
assert.Equal(t, len(nodeA.KBuckets[158]), 4)
|
||||
assert.Equal(t, len(nodeA.KBuckets[159]), 5)
|
||||
}
|
||||
Reference in New Issue
Block a user