Browse Source

start node methods, calculate KBuckets

master
arnaucube 4 years ago
parent
commit
63f1bb818b
5 changed files with 208 additions and 2 deletions
  1. +1
    -0
      .gitignore
  2. +5
    -0
      id.go
  3. +6
    -2
      id_test.go
  4. +99
    -0
      kademlia.go
  5. +97
    -0
      kademlia_test.go

+ 1
- 0
.gitignore

@ -0,0 +1 @@
tmp.go

+ 5
- 0
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++ {

+ 6
- 2
id_test.go

@ -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
- 0
kademlia.go

@ -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
- 0
kademlia_test.go

@ -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)
}

Loading…
Cancel
Save