add FindClosestKBucket, add rpc FindNode & FindValue & Store (wip)

This commit is contained in:
arnaucube
2019-12-09 19:47:20 +01:00
parent 229a9003bb
commit d971d1503a
11 changed files with 319 additions and 45 deletions

View File

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

View File

@@ -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[:]))
}

View File

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

View File

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