mirror of https://github.com/oxen-io/lokinet
Add test suites for dht bucket, kademlia and key
parent
7296ebcbe8
commit
03d56c1591
@ -1,123 +0,0 @@
|
||||
#include <dht/bucket.hpp>
|
||||
#include <dht/key.hpp>
|
||||
#include <dht/node.hpp>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
using Key_t = llarp::dht::Key_t;
|
||||
|
||||
class KademliaDHTTest : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
KademliaDHTTest()
|
||||
{
|
||||
}
|
||||
~KademliaDHTTest()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
SetUp()
|
||||
{
|
||||
us.Fill(16);
|
||||
nodes = new llarp::dht::Bucket< llarp::dht::RCNode >(us);
|
||||
size_t numNodes = 10;
|
||||
byte_t fill = 1;
|
||||
while(numNodes)
|
||||
{
|
||||
llarp::dht::RCNode n;
|
||||
n.ID.Fill(fill);
|
||||
nodes->PutNode(n);
|
||||
--numNodes;
|
||||
++fill;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TearDown()
|
||||
{
|
||||
delete nodes;
|
||||
}
|
||||
|
||||
llarp::dht::Bucket< llarp::dht::RCNode >* nodes = nullptr;
|
||||
llarp::dht::Key_t us;
|
||||
};
|
||||
|
||||
TEST_F(KademliaDHTTest, TestBucketFindClosest)
|
||||
{
|
||||
llarp::dht::Key_t result;
|
||||
llarp::dht::Key_t target;
|
||||
llarp::dht::Key_t oldResult;
|
||||
target.Fill(5);
|
||||
ASSERT_TRUE(nodes->FindClosest(target, result));
|
||||
ASSERT_TRUE(target == result);
|
||||
oldResult = result;
|
||||
target.Fill(0xf5);
|
||||
ASSERT_TRUE(nodes->FindClosest(target, result));
|
||||
ASSERT_TRUE(oldResult == result);
|
||||
};
|
||||
|
||||
TEST_F(KademliaDHTTest, TestBucketOperators)
|
||||
{
|
||||
llarp::dht::Key_t zero;
|
||||
llarp::dht::Key_t one;
|
||||
llarp::dht::Key_t three;
|
||||
|
||||
zero.Zero();
|
||||
one.Fill(1);
|
||||
three.Fill(3);
|
||||
ASSERT_TRUE(zero < one);
|
||||
ASSERT_TRUE(zero < three);
|
||||
ASSERT_FALSE(zero > one);
|
||||
ASSERT_FALSE(zero > three);
|
||||
ASSERT_TRUE(zero != three);
|
||||
ASSERT_FALSE(zero == three);
|
||||
ASSERT_TRUE((zero ^ one) == one);
|
||||
ASSERT_TRUE(one < three);
|
||||
ASSERT_TRUE(three > one);
|
||||
ASSERT_TRUE(one != three);
|
||||
ASSERT_FALSE(one == three);
|
||||
ASSERT_TRUE((one ^ three) == (three ^ one));
|
||||
};
|
||||
|
||||
TEST_F(KademliaDHTTest, TestBucketRandomized_1000)
|
||||
{
|
||||
size_t moreNodes = 100;
|
||||
while(moreNodes--)
|
||||
{
|
||||
llarp::dht::RCNode n;
|
||||
n.ID.Randomize();
|
||||
nodes->PutNode(n);
|
||||
}
|
||||
const size_t count = 1000;
|
||||
size_t left = count;
|
||||
while(left--)
|
||||
{
|
||||
llarp::dht::Key_t result;
|
||||
llarp::dht::Key_t target;
|
||||
llarp::dht::Key_t expect;
|
||||
target.Randomize();
|
||||
expect = target;
|
||||
ASSERT_TRUE(nodes->FindClosest(target, result));
|
||||
if(target == result)
|
||||
{
|
||||
ASSERT_FALSE((result ^ target) < (expect ^ target));
|
||||
ASSERT_FALSE((result ^ target) != (expect ^ target));
|
||||
ASSERT_TRUE((result ^ target) == (expect ^ target));
|
||||
}
|
||||
else
|
||||
{
|
||||
Key_t dist = result ^ target;
|
||||
Key_t oldDist = expect ^ target;
|
||||
ASSERT_TRUE((result ^ target) != (expect ^ target));
|
||||
if((result ^ target) < (expect ^ target))
|
||||
{
|
||||
std::cout << "result=" << result << "expect=" << expect << std::endl;
|
||||
std::cout << dist << ">=" << oldDist << "iteration=" << (count - left)
|
||||
<< std::endl;
|
||||
ASSERT_TRUE(false);
|
||||
}
|
||||
ASSERT_FALSE((result ^ target) == (expect ^ target));
|
||||
}
|
||||
}
|
||||
};
|
@ -0,0 +1,370 @@
|
||||
#include <dht/bucket.hpp>
|
||||
#include <dht/key.hpp>
|
||||
#include <dht/node.hpp>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
using Key_t = llarp::dht::Key_t;
|
||||
using Value_t = llarp::dht::RCNode;
|
||||
using Bucket_t = llarp::dht::Bucket< Value_t >;
|
||||
|
||||
class TestDhtBucket : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
TestDhtBucket() : randInt(0)
|
||||
{
|
||||
us.Fill(16);
|
||||
nodes = std::make_unique< Bucket_t >(us, [&]() { return randInt++; });
|
||||
size_t numNodes = 10;
|
||||
byte_t fill = 1;
|
||||
while(numNodes)
|
||||
{
|
||||
Value_t n;
|
||||
n.ID.Fill(fill);
|
||||
nodes->PutNode(n);
|
||||
--numNodes;
|
||||
++fill;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t randInt;
|
||||
|
||||
llarp::dht::Key_t us;
|
||||
std::unique_ptr< Bucket_t > nodes;
|
||||
};
|
||||
|
||||
TEST_F(TestDhtBucket, simple_cycle)
|
||||
{
|
||||
// Empty the current bucket.
|
||||
nodes->Clear();
|
||||
|
||||
// Create a simple value, and add it to the bucket.
|
||||
Value_t val;
|
||||
val.ID.Fill(1);
|
||||
|
||||
nodes->PutNode(val);
|
||||
|
||||
// Verify the value is in the bucket
|
||||
ASSERT_TRUE(nodes->HasNode(val.ID));
|
||||
ASSERT_EQ(1u, nodes->size());
|
||||
|
||||
// Verify after deletion, the value is no longer in the bucket
|
||||
nodes->DelNode(val.ID);
|
||||
ASSERT_FALSE(nodes->HasNode(val.ID));
|
||||
|
||||
// Verify deleting again succeeds;
|
||||
nodes->DelNode(val.ID);
|
||||
ASSERT_FALSE(nodes->HasNode(val.ID));
|
||||
}
|
||||
|
||||
TEST_F(TestDhtBucket, get_random_node_excluding)
|
||||
{
|
||||
// Empty the current bucket.
|
||||
nodes->Clear();
|
||||
|
||||
// We expect not to find anything
|
||||
Key_t result;
|
||||
std::set< Key_t > excludeSet;
|
||||
ASSERT_FALSE(nodes->GetRandomNodeExcluding(result, excludeSet));
|
||||
|
||||
// Create a simple value.
|
||||
Value_t val;
|
||||
val.ID.Fill(1);
|
||||
|
||||
// Add the simple value to the exclude set
|
||||
excludeSet.insert(val.ID);
|
||||
ASSERT_FALSE(nodes->GetRandomNodeExcluding(result, excludeSet));
|
||||
|
||||
// Add the simple value to the bucket
|
||||
nodes->PutNode(val);
|
||||
ASSERT_FALSE(nodes->GetRandomNodeExcluding(result, excludeSet));
|
||||
|
||||
excludeSet.clear();
|
||||
|
||||
ASSERT_TRUE(nodes->GetRandomNodeExcluding(result, excludeSet));
|
||||
ASSERT_EQ(val.ID, result);
|
||||
|
||||
// Add an element to the exclude set which isn't the bucket.
|
||||
Key_t other;
|
||||
other.Fill(0xff);
|
||||
excludeSet.insert(other);
|
||||
|
||||
ASSERT_TRUE(nodes->GetRandomNodeExcluding(result, excludeSet));
|
||||
ASSERT_EQ(val.ID, result);
|
||||
|
||||
// Add a node which is in both bucket and excludeSet
|
||||
Value_t nextVal;
|
||||
nextVal.ID.Fill(0xAA);
|
||||
excludeSet.insert(nextVal.ID);
|
||||
nodes->PutNode(nextVal);
|
||||
|
||||
ASSERT_TRUE(nodes->GetRandomNodeExcluding(result, excludeSet));
|
||||
ASSERT_EQ(val.ID, result);
|
||||
|
||||
// Clear the excludeSet - we should still have 2 nodes in the bucket
|
||||
excludeSet.clear();
|
||||
|
||||
randInt = 0;
|
||||
ASSERT_TRUE(nodes->GetRandomNodeExcluding(result, excludeSet));
|
||||
ASSERT_EQ(val.ID, result);
|
||||
|
||||
// Set the random value to be 1, we should get the other node.
|
||||
randInt = 1;
|
||||
ASSERT_TRUE(nodes->GetRandomNodeExcluding(result, excludeSet));
|
||||
ASSERT_EQ(nextVal.ID, result);
|
||||
|
||||
// Set the random value to be 100, we should get the first node.
|
||||
randInt = 100;
|
||||
ASSERT_TRUE(nodes->GetRandomNodeExcluding(result, excludeSet));
|
||||
ASSERT_EQ(val.ID, result);
|
||||
}
|
||||
|
||||
TEST_F(TestDhtBucket, find_closest)
|
||||
{
|
||||
// Empty the current bucket.
|
||||
nodes->Clear();
|
||||
|
||||
// We expect not to find anything
|
||||
Key_t target;
|
||||
target.Fill(0xF0);
|
||||
|
||||
Key_t result;
|
||||
ASSERT_FALSE(nodes->FindClosest(target, result));
|
||||
|
||||
// Add a node to the bucket
|
||||
Value_t first;
|
||||
first.ID.Zero();
|
||||
nodes->PutNode(first);
|
||||
|
||||
ASSERT_TRUE(nodes->FindClosest(target, result));
|
||||
ASSERT_EQ(result, first.ID);
|
||||
|
||||
// Add another node to the bucket, closer to the target
|
||||
Value_t second;
|
||||
second.ID.Fill(0x10);
|
||||
nodes->PutNode(second);
|
||||
ASSERT_TRUE(nodes->FindClosest(target, result));
|
||||
ASSERT_EQ(result, second.ID);
|
||||
|
||||
// Add a third node to the bucket, closer to the target
|
||||
Value_t third;
|
||||
third.ID.Fill(0x20);
|
||||
nodes->PutNode(third);
|
||||
ASSERT_TRUE(nodes->FindClosest(target, result));
|
||||
ASSERT_EQ(result, third.ID);
|
||||
|
||||
// Add a fourth node to the bucket, greater than the target
|
||||
Value_t fourth;
|
||||
fourth.ID.Fill(0xF1);
|
||||
nodes->PutNode(fourth);
|
||||
ASSERT_TRUE(nodes->FindClosest(target, result));
|
||||
ASSERT_EQ(result, fourth.ID);
|
||||
|
||||
// Add a fifth node to the bucket, equal to the target
|
||||
Value_t fifth;
|
||||
fifth.ID.Fill(0xF0);
|
||||
nodes->PutNode(fifth);
|
||||
ASSERT_TRUE(nodes->FindClosest(target, result));
|
||||
ASSERT_EQ(result, fifth.ID);
|
||||
}
|
||||
|
||||
TEST_F(TestDhtBucket, get_many_random)
|
||||
{
|
||||
// Empty the current bucket.
|
||||
nodes->Clear();
|
||||
|
||||
// Verify behaviour with empty node set
|
||||
std::set< Key_t > result;
|
||||
ASSERT_FALSE(nodes->GetManyRandom(result, 0));
|
||||
ASSERT_FALSE(nodes->GetManyRandom(result, 1));
|
||||
|
||||
// Add 5 nodes to the bucket
|
||||
std::set< Value_t > curValues;
|
||||
std::set< Key_t > curKeys;
|
||||
for(byte_t i = 0x00; i < 0x05; ++i)
|
||||
{
|
||||
Value_t v;
|
||||
v.ID.Fill(i);
|
||||
ASSERT_TRUE(curKeys.insert(v.ID).second);
|
||||
nodes->PutNode(v);
|
||||
}
|
||||
|
||||
// Fetching more than the current size fails
|
||||
ASSERT_EQ(5u, nodes->size());
|
||||
ASSERT_FALSE(nodes->GetManyRandom(result, nodes->size() + 1));
|
||||
|
||||
// Fetching the current size succeeds
|
||||
ASSERT_TRUE(nodes->GetManyRandom(result, nodes->size()));
|
||||
ASSERT_EQ(curKeys, result);
|
||||
|
||||
// Fetching a subset succeeds.
|
||||
// Note we hack this by "fixing" the random number generator
|
||||
result.clear();
|
||||
|
||||
ASSERT_TRUE(nodes->GetManyRandom(result, 1u));
|
||||
ASSERT_EQ(1u, result.size());
|
||||
ASSERT_EQ(*curKeys.begin(), *result.begin());
|
||||
|
||||
randInt = 0;
|
||||
result.clear();
|
||||
|
||||
ASSERT_TRUE(nodes->GetManyRandom(result, nodes->size() - 1));
|
||||
ASSERT_EQ(nodes->size() - 1, result.size());
|
||||
ASSERT_EQ(std::set< Key_t >(++curKeys.rbegin(), curKeys.rend()), result);
|
||||
}
|
||||
|
||||
TEST_F(TestDhtBucket, find_close_excluding)
|
||||
{
|
||||
// Empty the current bucket.
|
||||
nodes->Clear();
|
||||
|
||||
Key_t target;
|
||||
target.Zero();
|
||||
std::set< Key_t > exclude;
|
||||
Key_t result;
|
||||
|
||||
// Empty node + exclude set fails
|
||||
ASSERT_FALSE(nodes->FindCloseExcluding(target, result, exclude));
|
||||
|
||||
Value_t first;
|
||||
first.ID.Fill(0xF0);
|
||||
exclude.insert(first.ID);
|
||||
|
||||
// Empty nodes fails
|
||||
ASSERT_FALSE(nodes->FindCloseExcluding(target, result, exclude));
|
||||
|
||||
// Nodes and exclude set match
|
||||
nodes->PutNode(first);
|
||||
ASSERT_FALSE(nodes->FindCloseExcluding(target, result, exclude));
|
||||
|
||||
// Exclude set empty
|
||||
exclude.clear();
|
||||
ASSERT_TRUE(nodes->FindCloseExcluding(target, result, exclude));
|
||||
result = first.ID;
|
||||
|
||||
Value_t second;
|
||||
second.ID.Fill(0x01);
|
||||
nodes->PutNode(second);
|
||||
|
||||
ASSERT_TRUE(nodes->FindCloseExcluding(target, result, exclude));
|
||||
result = second.ID;
|
||||
|
||||
exclude.insert(second.ID);
|
||||
ASSERT_TRUE(nodes->FindCloseExcluding(target, result, exclude));
|
||||
result = first.ID;
|
||||
}
|
||||
|
||||
TEST_F(TestDhtBucket, find_many_near_excluding)
|
||||
{
|
||||
// Empty the current bucket.
|
||||
nodes->Clear();
|
||||
|
||||
Key_t target;
|
||||
target.Zero();
|
||||
std::set< Key_t > exclude;
|
||||
std::set< Key_t > result;
|
||||
|
||||
// Empty node + exclude set, with size 0 succeeds
|
||||
ASSERT_TRUE(nodes->GetManyNearExcluding(target, result, 0, exclude));
|
||||
ASSERT_EQ(0u, result.size());
|
||||
// Empty node + exclude set fails
|
||||
ASSERT_FALSE(nodes->GetManyNearExcluding(target, result, 1, exclude));
|
||||
|
||||
Value_t first;
|
||||
first.ID.Fill(0xF0);
|
||||
exclude.insert(first.ID);
|
||||
|
||||
// Empty nodes fails
|
||||
ASSERT_FALSE(nodes->GetManyNearExcluding(target, result, 1, exclude));
|
||||
|
||||
// Nodes and exclude set match
|
||||
nodes->PutNode(first);
|
||||
ASSERT_FALSE(nodes->GetManyNearExcluding(target, result, 1, exclude));
|
||||
|
||||
// Single node succeeds
|
||||
exclude.clear();
|
||||
ASSERT_TRUE(nodes->GetManyNearExcluding(target, result, 1, exclude));
|
||||
ASSERT_EQ(result, std::set< Key_t >({first.ID}));
|
||||
|
||||
// Trying to grab 2 nodes from a 1 node set fails
|
||||
result.clear();
|
||||
ASSERT_FALSE(nodes->GetManyNearExcluding(target, result, 2, exclude));
|
||||
|
||||
// two nodes finds closest
|
||||
Value_t second;
|
||||
second.ID.Fill(0x01);
|
||||
nodes->PutNode(second);
|
||||
result.clear();
|
||||
ASSERT_TRUE(nodes->GetManyNearExcluding(target, result, 1, exclude));
|
||||
ASSERT_EQ(result, std::set< Key_t >({second.ID}));
|
||||
|
||||
// 3 nodes finds 2 closest
|
||||
Value_t third;
|
||||
third.ID.Fill(0x02);
|
||||
nodes->PutNode(third);
|
||||
result.clear();
|
||||
ASSERT_TRUE(nodes->GetManyNearExcluding(target, result, 2, exclude));
|
||||
ASSERT_EQ(result, std::set< Key_t >({second.ID, third.ID}));
|
||||
|
||||
// 4 nodes, one in exclude set finds 2 closest
|
||||
Value_t fourth;
|
||||
fourth.ID.Fill(0x03);
|
||||
nodes->PutNode(fourth);
|
||||
exclude.insert(third.ID);
|
||||
result.clear();
|
||||
ASSERT_TRUE(nodes->GetManyNearExcluding(target, result, 2, exclude));
|
||||
ASSERT_EQ(result, std::set< Key_t >({second.ID, fourth.ID}));
|
||||
}
|
||||
|
||||
TEST_F(TestDhtBucket, TestBucketFindClosest)
|
||||
{
|
||||
llarp::dht::Key_t result;
|
||||
llarp::dht::Key_t target;
|
||||
target.Fill(5);
|
||||
ASSERT_TRUE(nodes->FindClosest(target, result));
|
||||
ASSERT_EQ(target, result);
|
||||
const llarp::dht::Key_t oldResult = result;
|
||||
target.Fill(0xf5);
|
||||
ASSERT_TRUE(nodes->FindClosest(target, result));
|
||||
ASSERT_EQ(oldResult, result);
|
||||
};
|
||||
|
||||
TEST_F(TestDhtBucket, TestBucketRandomized_1000)
|
||||
{
|
||||
size_t moreNodes = 100;
|
||||
while(moreNodes--)
|
||||
{
|
||||
llarp::dht::RCNode n;
|
||||
n.ID.Fill(randInt);
|
||||
randInt++;
|
||||
nodes->PutNode(n);
|
||||
}
|
||||
const size_t count = 1000;
|
||||
size_t left = count;
|
||||
while(left--)
|
||||
{
|
||||
llarp::dht::Key_t result;
|
||||
llarp::dht::Key_t target;
|
||||
target.Randomize();
|
||||
const llarp::dht::Key_t expect = target;
|
||||
ASSERT_TRUE(nodes->FindClosest(target, result));
|
||||
if(target == result)
|
||||
{
|
||||
ASSERT_GE(result ^ target, expect ^ target);
|
||||
ASSERT_EQ(result ^ target, expect ^ target);
|
||||
ASSERT_EQ(result ^ target, expect ^ target);
|
||||
}
|
||||
else
|
||||
{
|
||||
Key_t dist = result ^ target;
|
||||
Key_t oldDist = expect ^ target;
|
||||
ASSERT_NE(result ^ target, expect ^ target);
|
||||
|
||||
ASSERT_GE(result ^ target, expect ^ target)
|
||||
<< "result=" << result << "expect=" << expect << std::endl
|
||||
<< dist << ">=" << oldDist << "iteration=" << (count - left);
|
||||
|
||||
ASSERT_NE(result ^ target, expect ^ target);
|
||||
}
|
||||
}
|
||||
};
|
@ -0,0 +1,88 @@
|
||||
#include <dht/kademlia.hpp>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
using llarp::dht::Key_t;
|
||||
|
||||
using Array = std::array< byte_t, Key_t::SIZE >;
|
||||
|
||||
struct XorMetricData
|
||||
{
|
||||
Array us;
|
||||
Array left;
|
||||
Array right;
|
||||
bool result;
|
||||
|
||||
XorMetricData(const Array& u, const Array& l, const Array& r, bool res)
|
||||
: us(u), left(l), right(r), result(res)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream& stream, const XorMetricData& x)
|
||||
{
|
||||
stream << int(x.us[0]) << " " << int(x.left[0]) << " " << int(x.right[0])
|
||||
<< " " << std::boolalpha << x.result;
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
struct XorMetric : public ::testing::TestWithParam< XorMetricData >
|
||||
{
|
||||
};
|
||||
|
||||
TEST_P(XorMetric, test)
|
||||
{
|
||||
auto d = GetParam();
|
||||
ASSERT_EQ(llarp::dht::XorMetric{Key_t{d.us}}(Key_t{d.left}, Key_t{d.right}),
|
||||
d.result);
|
||||
}
|
||||
|
||||
std::vector< XorMetricData >
|
||||
makeData()
|
||||
{
|
||||
std::vector< XorMetricData > result;
|
||||
|
||||
Array zero;
|
||||
zero.fill(0);
|
||||
Array one;
|
||||
one.fill(1);
|
||||
Array two;
|
||||
two.fill(2);
|
||||
Array three;
|
||||
three.fill(3);
|
||||
|
||||
result.emplace_back(zero, zero, zero, false);
|
||||
result.emplace_back(zero, zero, one, true);
|
||||
result.emplace_back(zero, zero, two, true);
|
||||
result.emplace_back(zero, one, zero, false);
|
||||
result.emplace_back(zero, one, one, false);
|
||||
result.emplace_back(zero, one, two, true);
|
||||
result.emplace_back(zero, two, zero, false);
|
||||
result.emplace_back(zero, two, one, false);
|
||||
result.emplace_back(zero, two, two, false);
|
||||
result.emplace_back(one, zero, zero, false);
|
||||
result.emplace_back(one, zero, one, false);
|
||||
result.emplace_back(one, zero, two, true);
|
||||
result.emplace_back(one, one, zero, true);
|
||||
result.emplace_back(one, one, one, false);
|
||||
result.emplace_back(one, one, two, true);
|
||||
result.emplace_back(one, two, zero, false);
|
||||
result.emplace_back(one, two, one, false);
|
||||
result.emplace_back(one, two, two, false);
|
||||
result.emplace_back(two, zero, zero, false);
|
||||
result.emplace_back(two, zero, one, true);
|
||||
result.emplace_back(two, zero, two, false);
|
||||
result.emplace_back(two, one, zero, false);
|
||||
result.emplace_back(two, one, one, false);
|
||||
result.emplace_back(two, one, two, false);
|
||||
result.emplace_back(two, two, zero, true);
|
||||
result.emplace_back(two, two, one, true);
|
||||
result.emplace_back(two, two, two, false);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(TestDhtXorMetric, XorMetric,
|
||||
::testing::ValuesIn(makeData()));
|
Loading…
Reference in New Issue