storj/pkg/kademlia/routing_table_test.go

590 lines
16 KiB
Go
Raw Normal View History

// Copyright (C) 2018 Storj Labs, Inc.
// See LICENSE for copying information
package kademlia
import (
"io/ioutil"
"os"
"testing"
"time"
"github.com/stretchr/testify/assert"
proto "storj.io/storj/protos/overlay"
"storj.io/storj/storage"
)
func tempfile(fileName string) string {
f, err := ioutil.TempFile("", fileName)
if err != nil {
panic(err)
}
f.Close()
err = os.Remove(f.Name())
if err != nil {
panic(err)
}
return f.Name()
}
func createRT() *RoutingTable {
localNodeID, _ := newID()
localNode := proto.Node{Id: string(localNodeID)}
rt, _ := NewRoutingTable(&localNode, tempfile("Kadbucket"), tempfile("Nodebucket"), 16, 6)
return rt
}
func mockNodes(id string) *proto.Node {
var node proto.Node
node.Id = id
return &node
}
func TestAddNode(t *testing.T) {
rt := createRT()
//add local node
rt.self = mockNodes("OO") //[79, 79] or [01001111, 01001111]
localNode := rt.self
err := rt.addNode(localNode)
assert.NoError(t, err)
bucket, err := rt.kadBucketDB.Get(storage.Key([]byte{255, 255}))
assert.NoError(t, err)
assert.NotNil(t, bucket)
//add node to unfilled kbucket
node1 := mockNodes("PO") //[80, 79] or [01010000, 01001111]
err = rt.addNode(node1)
assert.NoError(t, err)
kadKeys, err := rt.kadBucketDB.List(nil, 0)
assert.NoError(t, err)
nodeKeys, err := rt.nodeBucketDB.List(nil, 0)
assert.NoError(t, err)
assert.Equal(t, 1, len(kadKeys))
assert.Equal(t, 2, len(nodeKeys))
//add node to full kbucket and split
node2 := mockNodes("NO") //[78, 79] or [01001110, 01001111]
err = rt.addNode(node2)
assert.NoError(t, err)
node3 := mockNodes("MO") //[77, 79] or [01001101, 01001111]
err = rt.addNode(node3)
assert.NoError(t, err)
node4 := mockNodes("LO") //[76, 79] or [01001100, 01001111]
err = rt.addNode(node4)
assert.NoError(t, err)
node5 := mockNodes("QO") //[81, 79] or [01010001, 01001111]
err = rt.addNode(node5)
assert.NoError(t, err)
kadKeys, err = rt.kadBucketDB.List(nil, 0)
assert.NoError(t, err)
nodeKeys, err = rt.nodeBucketDB.List(nil, 0)
assert.NoError(t, err)
assert.Equal(t, 1, len(kadKeys))
assert.Equal(t, 6, len(nodeKeys))
//splitting here
node6 := mockNodes("SO")
err = rt.addNode(node6)
assert.NoError(t, err)
kadKeys, err = rt.kadBucketDB.List(nil, 0)
assert.NoError(t, err)
nodeKeys, err = rt.nodeBucketDB.List(nil, 0)
assert.NoError(t, err)
assert.Equal(t, 5, len(kadKeys))
assert.Equal(t, 7, len(nodeKeys))
// //check how many keys in each bucket
a, err := rt.getNodeIDsWithinKBucket(kadKeys[0])
assert.NoError(t, err)
assert.Equal(t, 0, len(a))
b, err := rt.getNodeIDsWithinKBucket(kadKeys[1])
assert.NoError(t, err)
assert.Equal(t, 4, len(b))
c, err := rt.getNodeIDsWithinKBucket(kadKeys[2])
assert.NoError(t, err)
assert.Equal(t, 3, len(c))
d, err := rt.getNodeIDsWithinKBucket(kadKeys[3])
assert.NoError(t, err)
assert.Equal(t, 0, len(d))
e, err := rt.getNodeIDsWithinKBucket(kadKeys[4])
assert.NoError(t, err)
assert.Equal(t, 0, len(e))
//add node to full kbucket and drop
node7 := mockNodes("?O")
err = rt.addNode(node7)
assert.NoError(t, err)
node8 := mockNodes(">O")
err = rt.addNode(node8)
assert.NoError(t, err)
node9 := mockNodes("=O")
err = rt.addNode(node9)
assert.NoError(t, err)
node10 := mockNodes(";O")
err = rt.addNode(node10)
assert.NoError(t, err)
node11 := mockNodes(":O")
err = rt.addNode(node11)
assert.NoError(t, err)
node12 := mockNodes("9O")
err = rt.addNode(node12)
assert.NoError(t, err)
kadKeys, err = rt.kadBucketDB.List(nil, 0)
assert.NoError(t, err)
nodeKeys, err = rt.nodeBucketDB.List(nil, 0)
assert.NoError(t, err)
assert.Equal(t, 5, len(kadKeys))
assert.Equal(t, 13, len(nodeKeys))
a, err = rt.getNodeIDsWithinKBucket(kadKeys[0])
assert.NoError(t, err)
assert.Equal(t, 6, len(a))
//should drop
node13 := mockNodes("8O")
err = rt.addNode(node13)
assert.NoError(t, err)
kadKeys, err = rt.kadBucketDB.List(nil, 0)
assert.NoError(t, err)
nodeKeys, err = rt.nodeBucketDB.List(nil, 0)
assert.NoError(t, err)
assert.Equal(t, 5, len(kadKeys))
assert.Equal(t, 13, len(nodeKeys))
a, err = rt.getNodeIDsWithinKBucket(kadKeys[0])
assert.NoError(t, err)
assert.Equal(t, 6, len(a))
//add node to highly unbalanced tree
//adding to bucket 1
node14 := mockNodes("KO") //75
err = rt.addNode(node14)
assert.NoError(t, err)
node15 := mockNodes("JO") //74
err = rt.addNode(node15)
assert.NoError(t, err)
//adding to bucket 2
node16 := mockNodes("]O") //93
err = rt.addNode(node16)
assert.NoError(t, err)
node17 := mockNodes("^O") //94
err = rt.addNode(node17)
assert.NoError(t, err)
node18 := mockNodes("_O") //95
err = rt.addNode(node18)
assert.NoError(t, err)
b, err = rt.getNodeIDsWithinKBucket(kadKeys[1])
assert.NoError(t, err)
assert.Equal(t, 6, len(b))
c, err = rt.getNodeIDsWithinKBucket(kadKeys[2])
assert.NoError(t, err)
assert.Equal(t, 6, len(c))
kadKeys, err = rt.kadBucketDB.List(nil, 0)
assert.NoError(t, err)
nodeKeys, err = rt.nodeBucketDB.List(nil, 0)
assert.NoError(t, err)
assert.Equal(t, 5, len(kadKeys))
assert.Equal(t, 18, len(nodeKeys))
//split bucket 2
node19 := mockNodes("@O")
err = rt.addNode(node19)
assert.NoError(t, err)
kadKeys, err = rt.kadBucketDB.List(nil, 0)
assert.NoError(t, err)
nodeKeys, err = rt.nodeBucketDB.List(nil, 0)
assert.NoError(t, err)
assert.Equal(t, 6, len(kadKeys))
assert.Equal(t, 19, len(nodeKeys))
}
func TestCreateOrUpdateKBucket(t *testing.T) {
rt := createRT()
id := []byte{255, 255}
err := rt.createOrUpdateKBucket(storage.Key(id), time.Now())
assert.NoError(t, err)
val, e := rt.kadBucketDB.Get(storage.Key(id))
assert.NotNil(t, val)
assert.NoError(t, e)
}
func TestGetKBucketID(t *testing.T) {
rt := createRT()
kadIDA := storage.Key([]byte{255, 255})
kadIDB := storage.Key([]byte{127, 255})
kadIDC := storage.Key([]byte{63, 255})
now := time.Now()
rt.createOrUpdateKBucket(kadIDA, now)
rt.createOrUpdateKBucket(kadIDB, now)
rt.createOrUpdateKBucket(kadIDC, now)
nodeIDA := []byte{183, 255} //[10110111, 1111111]
nodeIDB := []byte{111, 255} //[01101111, 1111111]
nodeIDC := []byte{47, 255} //[00101111, 1111111]
keyA, _ := rt.getKBucketID(nodeIDA)
assert.Equal(t, kadIDA, keyA)
keyB, _ := rt.getKBucketID(nodeIDB)
assert.Equal(t, kadIDB, keyB)
keyC, _ := rt.getKBucketID(nodeIDC)
assert.Equal(t, kadIDC, keyC)
}
func TestXorTwoIds(t *testing.T) {
x := xorTwoIds([]byte{191}, []byte{159})
assert.Equal(t, []byte{32}, x) //00100000
}
func TestSortByXOR(t *testing.T) {
rt := createRT()
node1 := []byte{127, 255} //xor 0
rt.self.Id = string(node1)
rt.nodeBucketDB.Put(node1, []byte(""))
node2 := []byte{143, 255} //xor 240
rt.nodeBucketDB.Put(node2, []byte(""))
node3 := []byte{255, 255} //xor 128
rt.nodeBucketDB.Put(node3, []byte(""))
node4 := []byte{191, 255} //xor 192
rt.nodeBucketDB.Put(node4, []byte(""))
node5 := []byte{133, 255} //xor 250
rt.nodeBucketDB.Put(node5, []byte(""))
nodes, err := rt.nodeBucketDB.List(nil, 0)
assert.NoError(t, err)
expectedNodes := storage.Keys{node1, node5, node2, node4, node3}
assert.Equal(t, expectedNodes, nodes)
sortedNodes := rt.sortByXOR(nodes)
expectedSorted := storage.Keys{node1, node3, node4, node2, node5}
assert.Equal(t, expectedSorted, sortedNodes)
nodes, err = rt.nodeBucketDB.List(nil, 0)
assert.NoError(t, err)
assert.Equal(t, expectedNodes, nodes)
}
func TestDetermineFurthestIDWithinK(t *testing.T) {
rt := createRT()
node1 := []byte{127, 255} //xor 0
rt.self.Id = string(node1)
rt.nodeBucketDB.Put(node1, []byte(""))
expectedFurthest := node1
nodes, err := rt.nodeBucketDB.List(nil, 0)
assert.NoError(t, err)
furthest, err := rt.determineFurthestIDWithinK(nodes)
assert.Equal(t, expectedFurthest, furthest)
node2 := []byte{143, 255} //xor 240
rt.nodeBucketDB.Put(node2, []byte(""))
expectedFurthest = node2
nodes, err = rt.nodeBucketDB.List(nil, 0)
assert.NoError(t, err)
furthest, err = rt.determineFurthestIDWithinK(nodes)
assert.Equal(t, expectedFurthest, furthest)
node3 := []byte{255, 255} //xor 128
rt.nodeBucketDB.Put(node3, []byte(""))
expectedFurthest = node2
nodes, err = rt.nodeBucketDB.List(nil, 0)
assert.NoError(t, err)
furthest, err = rt.determineFurthestIDWithinK(nodes)
assert.Equal(t, expectedFurthest, furthest)
node4 := []byte{191, 255} //xor 192
rt.nodeBucketDB.Put(node4, []byte(""))
expectedFurthest = node2
nodes, err = rt.nodeBucketDB.List(nil, 0)
assert.NoError(t, err)
furthest, err = rt.determineFurthestIDWithinK(nodes)
assert.Equal(t, expectedFurthest, furthest)
node5 := []byte{133, 255} //xor 250
rt.nodeBucketDB.Put(node5, []byte(""))
expectedFurthest = node5
nodes, err = rt.nodeBucketDB.List(nil, 0)
assert.NoError(t, err)
furthest, err = rt.determineFurthestIDWithinK(nodes)
assert.Equal(t, expectedFurthest, furthest)
}
func TestNodeIsWithinNearestK(t *testing.T) {
rt := createRT()
rt.bucketSize = 2
selfNode := []byte{127, 255}
rt.self.Id = string(selfNode)
rt.nodeBucketDB.Put(selfNode, []byte(""))
expectTrue, err := rt.nodeIsWithinNearestK(selfNode)
assert.NoError(t, err)
assert.True(t, expectTrue)
furthestNode := []byte{143, 255}
expectTrue, err = rt.nodeIsWithinNearestK(furthestNode)
assert.NoError(t, err)
assert.True(t, expectTrue)
rt.nodeBucketDB.Put(furthestNode, []byte(""))
node1 := []byte{255, 255}
expectTrue, err = rt.nodeIsWithinNearestK(node1)
assert.NoError(t, err)
assert.True(t, expectTrue)
rt.nodeBucketDB.Put(node1, []byte(""))
node2 := []byte{191, 255}
expectTrue, err = rt.nodeIsWithinNearestK(node2)
assert.NoError(t, err)
assert.True(t, expectTrue)
rt.nodeBucketDB.Put(node1, []byte(""))
node3 := []byte{133, 255}
expectFalse, err := rt.nodeIsWithinNearestK(node3)
assert.NoError(t, err)
assert.False(t, expectFalse)
}
func TestKadBucketContainsLocalNode(t *testing.T) {
rt := createRT()
kadIDA := storage.Key([]byte{255, 255})
kadIDB := storage.Key([]byte{127, 255})
now := time.Now()
err := rt.createOrUpdateKBucket(kadIDA, now)
assert.NoError(t, err)
err = rt.createOrUpdateKBucket(kadIDB, now)
assert.NoError(t, err)
nodeIDA := []byte{183, 255} //[10110111, 1111111]
err = rt.nodeBucketDB.Put(nodeIDA, []byte(""))
assert.NoError(t, err)
rt.self.Id = string(nodeIDA)
resultTrue, err := rt.kadBucketContainsLocalNode(kadIDA)
assert.NoError(t, err)
resultFalse, err := rt.kadBucketContainsLocalNode(kadIDB)
assert.NoError(t, err)
assert.True(t, resultTrue)
assert.False(t, resultFalse)
}
func TestKadBucketHasRoom(t *testing.T) {
//valid when kad bucket has >0 nodes in it due to ../storage/boltdb/client.go:99
rt := createRT()
kadIDA := storage.Key([]byte{255, 255})
now := time.Now()
rt.createOrUpdateKBucket(kadIDA, now)
node1 := []byte{255, 255}
node2 := []byte{191, 255}
node3 := []byte{127, 255}
node4 := []byte{63, 255}
node5 := []byte{159, 255}
node6 := []byte{0, 127}
rt.nodeBucketDB.Put(node1, []byte(""))
resultA, err := rt.kadBucketHasRoom(kadIDA)
assert.NoError(t, err)
assert.True(t, resultA)
rt.nodeBucketDB.Put(node2, []byte(""))
rt.nodeBucketDB.Put(node3, []byte(""))
rt.nodeBucketDB.Put(node4, []byte(""))
rt.nodeBucketDB.Put(node5, []byte(""))
rt.nodeBucketDB.Put(node6, []byte(""))
resultB, err := rt.kadBucketHasRoom(kadIDA)
assert.NoError(t, err)
assert.False(t, resultB)
}
func TestGetNodeIDsWithinKBucket(t *testing.T) {
rt := createRT()
kadIDA := storage.Key([]byte{255, 255})
kadIDB := storage.Key([]byte{127, 255})
now := time.Now()
rt.createOrUpdateKBucket(kadIDA, now)
rt.createOrUpdateKBucket(kadIDB, now)
nodeIDA := []byte{183, 255} //[10110111, 1111111]
nodeIDB := []byte{111, 255} //[01101111, 1111111]
nodeIDC := []byte{47, 255} //[00101111, 1111111]
rt.nodeBucketDB.Put(nodeIDA, []byte(""))
rt.nodeBucketDB.Put(nodeIDB, []byte(""))
rt.nodeBucketDB.Put(nodeIDC, []byte(""))
expectedA := storage.Keys{nodeIDA}
expectedB := storage.Keys{nodeIDC, nodeIDB}
A, err := rt.getNodeIDsWithinKBucket(kadIDA)
assert.NoError(t, err)
B, err := rt.getNodeIDsWithinKBucket(kadIDB)
assert.NoError(t, err)
assert.Equal(t, expectedA, A)
assert.Equal(t, expectedB, B)
}
func TestGetKBucketRange(t *testing.T) {
rt := createRT()
idA := []byte{255, 255}
idB := []byte{127, 255}
idC := []byte{63, 255}
rt.kadBucketDB.Put(idA, []byte(""))
rt.kadBucketDB.Put(idB, []byte(""))
rt.kadBucketDB.Put(idC, []byte(""))
expectedA := storage.Keys{idB, idA}
expectedB := storage.Keys{idC, idB}
expectedC := storage.Keys{rt.createZeroAsStorageKey(), idC}
endpointsA, err := rt.getKBucketRange(idA)
assert.NoError(t, err)
endpointsB, err := rt.getKBucketRange(idB)
assert.NoError(t, err)
endpointsC, err := rt.getKBucketRange(idC)
assert.NoError(t, err)
assert.Equal(t, expectedA, endpointsA)
assert.Equal(t, expectedB, endpointsB)
assert.Equal(t, expectedC, endpointsC)
}
func TestCreateFirstBucketID(t *testing.T) {
rt := createRT()
x := rt.createFirstBucketID()
expected := []byte{255, 255}
assert.Equal(t, x, expected)
}
func TestCreateZeroAsStorageKey(t *testing.T) {
rt := createRT()
zero := rt.createZeroAsStorageKey()
expected := []byte{0, 0}
assert.Equal(t, zero, storage.Key(expected))
}
func TestDetermineLeafDepth(t *testing.T) {
rt := createRT()
idA := []byte{255, 255}
idB := []byte{127, 255}
idC := []byte{63, 255}
err := rt.kadBucketDB.Put(idA, []byte(""))
assert.NoError(t, err)
first, err := rt.determineLeafDepth(idA)
assert.NoError(t, err)
assert.Equal(t, 0, first)
err = rt.kadBucketDB.Put(idB, []byte(""))
assert.NoError(t, err)
second, err := rt.determineLeafDepth(idB)
assert.NoError(t, err)
assert.Equal(t, 1, second)
err = rt.kadBucketDB.Put(idC, []byte(""))
assert.NoError(t, err)
one, err := rt.determineLeafDepth(idA)
assert.NoError(t, err)
assert.Equal(t, 1, one)
two, err := rt.determineLeafDepth(idB)
assert.NoError(t, err)
assert.Equal(t, 2, two)
alsoTwo, err := rt.determineLeafDepth(idC)
assert.NoError(t, err)
assert.Equal(t, 2, alsoTwo)
}
func TestDetermineDifferingBitIndex(t *testing.T) {
rt := createRT()
diff, err := rt.determineDifferingBitIndex([]byte{191, 255}, []byte{255, 255})
assert.NoError(t, err)
assert.Equal(t, 1, diff)
diff, err = rt.determineDifferingBitIndex([]byte{255, 255}, []byte{191, 255})
assert.NoError(t, err)
assert.Equal(t, 1, diff)
diff, err = rt.determineDifferingBitIndex([]byte{95, 255}, []byte{127, 255})
assert.NoError(t, err)
assert.Equal(t, 2, diff)
diff, err = rt.determineDifferingBitIndex([]byte{95, 255}, []byte{79, 255})
assert.NoError(t, err)
assert.Equal(t, 3, diff)
diff, err = rt.determineDifferingBitIndex([]byte{95, 255}, []byte{63, 255})
assert.NoError(t, err)
assert.Equal(t, 2, diff)
diff, err = rt.determineDifferingBitIndex([]byte{95, 255}, []byte{79, 255})
assert.NoError(t, err)
assert.Equal(t, 3, diff)
diff, err = rt.determineDifferingBitIndex([]byte{255, 255}, []byte{255, 255})
assert.Error(t, err)
assert.Equal(t, -2, diff)
diff, err = rt.determineDifferingBitIndex([]byte{255, 255}, []byte{0, 0})
assert.NoError(t, err)
assert.Equal(t, -1, diff)
diff, err = rt.determineDifferingBitIndex([]byte{127, 255}, []byte{0, 0})
assert.NoError(t, err)
assert.Equal(t, 0, diff)
diff, err = rt.determineDifferingBitIndex([]byte{63, 255}, []byte{0, 0})
assert.NoError(t, err)
assert.Equal(t, 1, diff)
diff, err = rt.determineDifferingBitIndex([]byte{31, 255}, []byte{0, 0})
assert.NoError(t, err)
assert.Equal(t, 2, diff)
diff, err = rt.determineDifferingBitIndex([]byte{95, 255}, []byte{63, 255})
assert.NoError(t, err)
assert.Equal(t, 2, diff)
}
func TestSplitBucket(t *testing.T) {
rt := createRT()
id1 := []byte{255, 255}
id2 := []byte{191, 255}
id3 := []byte{127, 255}
id4 := []byte{63, 255}
id5 := []byte{159, 255}
id6 := []byte{0, 127}
id7 := []byte{0, 255}
id8 := []byte{95, 255}
id9 := []byte{87, 255}
newID1 := rt.splitBucket(id1, 1) //[11111111, 11111111] -> [10111111, 11111111]
assert.Equal(t, id2, newID1)
newID2 := rt.splitBucket(id2, 2) //[10111111, 11111111] -> [10011111, 11111111]
assert.Equal(t, id5, newID2)
newID3 := rt.splitBucket(id3, 1) //[01111111, 11111111] -> [00111111, 11111111]
assert.Equal(t, id4, newID3)
newID4 := rt.splitBucket(id7, 8) //[00000000, 11111111] -> [00000000, 01111111]
assert.Equal(t, id6, newID4)
newID5 := rt.splitBucket(id8, 4) //[01011111, 11111111] -> [01010111, 11111111]
assert.Equal(t, id9, newID5)
newID6 := rt.splitBucket(id8, 3)
assert.Equal(t, []byte{79, 255}, newID6)
}