mirror of
https://github.com/arnaucube/go-dht.git
synced 2026-02-06 19:06:44 +01:00
add FindClosestKBucket, add rpc FindNode & FindValue & Store (wip)
This commit is contained in:
@@ -3,8 +3,8 @@ package kademlia
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type ID [B]byte
|
||||
@@ -29,8 +29,6 @@ func (id ID) MarshalText() ([]byte, error) {
|
||||
}
|
||||
|
||||
func (id *ID) UnmarshalText(data []byte) error {
|
||||
fmt.Println("UNMARSHAL")
|
||||
fmt.Println("d", string(data))
|
||||
var err error
|
||||
var idFromStr ID
|
||||
idFromStr, err = IDFromString(string(data))
|
||||
@@ -72,3 +70,10 @@ func (idA ID) Distance(idB ID) ID {
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
func HashData(d []byte) ID {
|
||||
h := sha256.Sum256(d)
|
||||
var r ID
|
||||
copy(r[:], h[:20])
|
||||
return r
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package kademlia
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -28,7 +28,6 @@ func TestIDMarshalers(t *testing.T) {
|
||||
idStr, err := json.Marshal(id)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "\"0fd85ddddf15aeec2d5d8b01b013dbca030a18d7\"", string(idStr))
|
||||
fmt.Println("idStr", string(idStr))
|
||||
|
||||
var idParsed ID
|
||||
err = json.Unmarshal(idStr, &idParsed)
|
||||
@@ -56,3 +55,8 @@ func TestIDDistance(t *testing.T) {
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "cb55d68e04fa18e5c0131fd236ce80e8ecc1348c", idA.Distance(idB).String())
|
||||
}
|
||||
|
||||
func TestHashData(t *testing.T) {
|
||||
h := HashData([]byte("test data"))
|
||||
assert.Equal(t, "916f0027a575074ce72a331777c3478d6513f786", hex.EncodeToString(h[:]))
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package kademlia
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math/bits"
|
||||
"net/rpc"
|
||||
"strconv"
|
||||
@@ -23,7 +24,7 @@ type ListedNode struct {
|
||||
type Kademlia struct {
|
||||
// N is this node data
|
||||
N ListedNode
|
||||
KBuckets [B * 8][]ListedNode
|
||||
KBuckets [B][]ListedNode
|
||||
}
|
||||
|
||||
func NewKademliaTable(id ID, addr, port string) *Kademlia {
|
||||
@@ -49,6 +50,22 @@ func (kad Kademlia) String() string {
|
||||
return r
|
||||
}
|
||||
|
||||
func (kad Kademlia) FindClosestKBucket(id ID) (int, error) {
|
||||
kb := kad.KBucket(id)
|
||||
if len(kad.KBuckets[kb]) != 0 {
|
||||
return kb, nil
|
||||
}
|
||||
for i := 0; kb-i > 0 || kb+i < 8; i++ {
|
||||
if len(kad.KBuckets[kb+i]) != 0 {
|
||||
return kb + i, nil
|
||||
}
|
||||
if len(kad.KBuckets[kb-i]) != 0 {
|
||||
return kb - i, nil
|
||||
}
|
||||
}
|
||||
return 0, errors.New("not found")
|
||||
}
|
||||
|
||||
func (kad Kademlia) KBucket(o ID) int {
|
||||
d := kad.N.ID.Distance(o)
|
||||
return kBucketByDistance(d[:])
|
||||
@@ -56,9 +73,22 @@ func (kad Kademlia) KBucket(o ID) int {
|
||||
}
|
||||
|
||||
func kBucketByDistance(b []byte) int {
|
||||
z := countZeroes(b)
|
||||
if z == 0 {
|
||||
return 0
|
||||
}
|
||||
for i := 0; i < 8; i++ {
|
||||
if z>>uint8(i) == 1 { // check until 1 instead of 0 to avoid one operation
|
||||
return i
|
||||
}
|
||||
}
|
||||
return 7
|
||||
}
|
||||
|
||||
func countZeroes(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) - (i * 8) - (8 - bits.TrailingZeros8(bits.Reverse8(uint8(a))))
|
||||
}
|
||||
}
|
||||
return (B*8 - 1) - (B*8 - 1)
|
||||
@@ -69,7 +99,7 @@ func (kad *Kademlia) Update(o ListedNode) {
|
||||
kb := kad.KBuckets[k]
|
||||
if len(kb) >= KBucketSize {
|
||||
// if n.KBuckets[k] is alrady full, perform ping of the first element
|
||||
log.Info("node.KBuckets[k] already full, performing ping to node.KBuckets[0]")
|
||||
log.Debug("node.KBuckets[k] already full, performing ping to node.KBuckets[0]")
|
||||
kad.PingOldNode(k, o)
|
||||
return
|
||||
}
|
||||
@@ -78,12 +108,12 @@ func (kad *Kademlia) Update(o ListedNode) {
|
||||
if exist {
|
||||
// update position of o to the bottom
|
||||
kad.KBuckets[k] = moveToBottom(kad.KBuckets[k], pos)
|
||||
log.Info("ListedNode already exists, moved to bottom")
|
||||
log.Debug("ListedNode (" + o.ID.String() + ") already exists, moved to bottom")
|
||||
return
|
||||
}
|
||||
// not exists, add it to the kBucket
|
||||
kad.KBuckets[k] = append(kad.KBuckets[k], o)
|
||||
log.Info("ListedNode not exists, added to the bottom")
|
||||
log.Debug("ListedNode (" + o.ID.String() + ") not exists, added to the bottom")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -109,6 +139,7 @@ func (kad *Kademlia) CallPing(o ListedNode) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// TODO use reply as PONG
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -4,37 +4,54 @@ import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var debug = false
|
||||
|
||||
func init() {
|
||||
log.SetLevel(log.DebugLevel)
|
||||
}
|
||||
|
||||
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))
|
||||
assert.Equal(t, 7, kBucketByDistance(b))
|
||||
b[19] = 0x00
|
||||
assert.Equal(t, 159, kBucketByDistance(b))
|
||||
assert.Equal(t, 7, kBucketByDistance(b))
|
||||
|
||||
b[0] = 0x0f
|
||||
assert.Equal(t, 159-4, kBucketByDistance(b))
|
||||
assert.Equal(t, 7, kBucketByDistance(b))
|
||||
|
||||
b[0] = 0x0c
|
||||
assert.Equal(t, 159-4, kBucketByDistance(b))
|
||||
assert.Equal(t, 7, kBucketByDistance(b))
|
||||
|
||||
b[0] = 0x00
|
||||
b[1] = 0x00
|
||||
b[2] = 0x0f
|
||||
assert.Equal(t, 159-20, kBucketByDistance(b))
|
||||
assert.Equal(t, 7, kBucketByDistance(b))
|
||||
|
||||
b[2] = 0x07
|
||||
assert.Equal(t, 159-21, kBucketByDistance(b))
|
||||
assert.Equal(t, 7, kBucketByDistance(b))
|
||||
|
||||
b[2] = 0x03
|
||||
assert.Equal(t, 159-22, kBucketByDistance(b))
|
||||
assert.Equal(t, 7, kBucketByDistance(b))
|
||||
|
||||
b = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
b[19] = 0x01
|
||||
assert.Equal(t, 2, kBucketByDistance(b))
|
||||
b[19] = 0x05
|
||||
assert.Equal(t, 2, kBucketByDistance(b))
|
||||
|
||||
b[19] = 0x10
|
||||
assert.Equal(t, 1, kBucketByDistance(b))
|
||||
|
||||
b[18] = 0x10
|
||||
assert.Equal(t, 3, kBucketByDistance(b))
|
||||
}
|
||||
|
||||
func TestKBucket(t *testing.T) {
|
||||
@@ -49,7 +66,7 @@ func TestKBucket(t *testing.T) {
|
||||
assert.Nil(t, err)
|
||||
|
||||
d = kademlia.KBucket(idB)
|
||||
assert.Equal(t, 159, d)
|
||||
assert.Equal(t, 7, d)
|
||||
}
|
||||
|
||||
func prepareTestListedNodes() []ListedNode {
|
||||
@@ -111,8 +128,55 @@ func TestUpdate(t *testing.T) {
|
||||
fmt.Println(kademlia)
|
||||
}
|
||||
|
||||
assert.Equal(t, len(kademlia.KBuckets[0]), 1)
|
||||
assert.Equal(t, len(kademlia.KBuckets[1]), 1)
|
||||
assert.Equal(t, len(kademlia.KBuckets[158]), 4)
|
||||
assert.Equal(t, len(kademlia.KBuckets[159]), 5)
|
||||
assert.Equal(t, 2, len(kademlia.KBuckets[0]))
|
||||
assert.Equal(t, 0, len(kademlia.KBuckets[1]))
|
||||
assert.Equal(t, 2, len(kademlia.KBuckets[2]))
|
||||
assert.Equal(t, 0, len(kademlia.KBuckets[3]))
|
||||
assert.Equal(t, 14, len(kademlia.KBuckets[7]))
|
||||
}
|
||||
|
||||
func TestFindClosestKBucket(t *testing.T) {
|
||||
idA, err := IDFromString("0fd85ddddf15aeec2d5d8b01b013dbca030a18d7")
|
||||
assert.Nil(t, err)
|
||||
kademlia := NewKademliaTable(idA, "127.0.0.1", "5000")
|
||||
|
||||
lns := prepareTestListedNodes()
|
||||
for _, lnI := range lns {
|
||||
kademlia.Update(lnI)
|
||||
}
|
||||
|
||||
if debug {
|
||||
fmt.Println(kademlia)
|
||||
}
|
||||
|
||||
idB, err := IDFromString("0fd85ddddf15aeec2d5d8b01b013dbca030a18d5")
|
||||
assert.Nil(t, err)
|
||||
|
||||
k, err := kademlia.FindClosestKBucket(idB)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 2, k)
|
||||
|
||||
idB, err = IDFromString("0fd85ddddf15aeec2d5d8b01b013dbca030a1000")
|
||||
assert.Nil(t, err)
|
||||
|
||||
// the theorical KBucket should be 3
|
||||
k = kademlia.KBucket(idB)
|
||||
assert.Equal(t, 3, k)
|
||||
|
||||
// while the real KBucket (as the 3 is empty), should be 2
|
||||
k, err = kademlia.FindClosestKBucket(idB)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 2, k)
|
||||
|
||||
idB, err = IDFromString("0fd85ddddf15aeec2d5d8b01b013dbc000000000")
|
||||
assert.Nil(t, err)
|
||||
|
||||
// the theorical KBucket should be 3
|
||||
k = kademlia.KBucket(idB)
|
||||
assert.Equal(t, 5, k)
|
||||
|
||||
// while the real KBucket (as the 3 is empty), should be 2
|
||||
k, err = kademlia.FindClosestKBucket(idB)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 7, k)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user