@ -0,0 +1 @@ |
|||||
|
tmp.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 |
||||
|
} |
@ -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) |
||||
|
} |