diff --git a/.cirrus.yml b/.cirrus.yml index c2a274ea..17dbb186 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -1,7 +1,7 @@ --- cirrus-ci_task: container: - image: toxchat/toktok-stack:0.0.13 + image: toxchat/toktok-stack:0.0.18 cpu: 2 memory: 2G configure_script: @@ -22,7 +22,7 @@ cirrus-ci_task: cimple_task: container: - image: toxchat/toktok-stack:0.0.13 + image: toxchat/toktok-stack:0.0.18 cpu: 2 memory: 4G configure_script: diff --git a/CMakeLists.txt b/CMakeLists.txt index 98a4eabe..979c4963 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -363,6 +363,7 @@ include(CompileGTest) # unit_test(toxav ring_buffer) unit_test(toxav rtp) +unit_test(toxcore DHT) unit_test(toxcore crypto_core) unit_test(toxcore mono_time) unit_test(toxcore ping_array) diff --git a/toxcore/BUILD.bazel b/toxcore/BUILD.bazel index 0fdfe0ae..0bdd9c72 100644 --- a/toxcore/BUILD.bazel +++ b/toxcore/BUILD.bazel @@ -153,6 +153,16 @@ cc_library( ], ) +cc_test( + name = "DHT_test", + size = "small", + srcs = ["DHT_test.cc"], + deps = [ + ":DHT", + "@com_google_googletest//:gtest_main", + ], +) + cc_library( name = "DHT_srcs", hdrs = [ diff --git a/toxcore/DHT.h b/toxcore/DHT.h index 40ce73f1..4f7b012b 100644 --- a/toxcore/DHT.h +++ b/toxcore/DHT.h @@ -17,6 +17,10 @@ #include +#ifdef __cplusplus +extern "C" { +#endif + /* Maximum number of clients stored per friend. */ #define MAX_FRIEND_CLIENTS 8 @@ -405,4 +409,8 @@ bool dht_non_lan_connected(const DHT *dht); uint32_t addto_lists(DHT *dht, IP_Port ip_port, const uint8_t *public_key); +#ifdef __cplusplus +} // extern "C" +#endif + #endif diff --git a/toxcore/DHT_test.cc b/toxcore/DHT_test.cc new file mode 100644 index 00000000..9ecd3cb9 --- /dev/null +++ b/toxcore/DHT_test.cc @@ -0,0 +1,112 @@ +#include "DHT.h" + +#include + +#include +#include + +#include "crypto_core.h" + +namespace { + +using PublicKey = std::array; + +template +std::array to_array(T const (&arr)[N]) { + std::array stdarr; + memcpy(stdarr.data(), arr, N); + return stdarr; +} + +TEST(IdClosest, IdenticalKeysAreSameDistance) { + PublicKey pk0; + random_bytes(pk0.data(), CRYPTO_PUBLIC_KEY_SIZE); + + PublicKey pk1; + random_bytes(pk1.data(), CRYPTO_PUBLIC_KEY_SIZE); + + PublicKey pk2 = pk1; + + EXPECT_EQ(id_closest(pk0.data(), pk1.data(), pk2.data()), 0); +} + +TEST(IdClosest, DistanceIsCommutative) { + for (uint32_t i = 0; i < 100; ++i) { + PublicKey pk0; + random_bytes(pk0.data(), CRYPTO_PUBLIC_KEY_SIZE); + + PublicKey pk1; + random_bytes(pk1.data(), CRYPTO_PUBLIC_KEY_SIZE); + + PublicKey pk2; + random_bytes(pk2.data(), CRYPTO_PUBLIC_KEY_SIZE); + + ASSERT_NE(pk1, pk2); // RNG can't produce the same random key twice + + // Two non-equal keys can't have the same distance from any given key. + EXPECT_NE(id_closest(pk0.data(), pk1.data(), pk2.data()), 0); + + if (id_closest(pk0.data(), pk1.data(), pk2.data()) == 1) { + EXPECT_EQ(id_closest(pk0.data(), pk2.data(), pk1.data()), 2); + } + + if (id_closest(pk0.data(), pk1.data(), pk2.data()) == 2) { + EXPECT_EQ(id_closest(pk0.data(), pk2.data(), pk1.data()), 1); + } + } +} + +TEST(IdClosest, SmallXorDistanceIsCloser) { + PublicKey const pk0 = {{0xaa}}; + PublicKey const pk1 = {{0xa0}}; + PublicKey const pk2 = {{0x0a}}; + + EXPECT_EQ(id_closest(pk0.data(), pk1.data(), pk2.data()), 1); +} + +TEST(IdClosest, DistinctKeysCannotHaveTheSameDistance) { + PublicKey const pk0 = {{0x06}}; + PublicKey const pk1 = {{0x00}}; + PublicKey pk2 = {{0x00}}; + + for (uint8_t i = 1; i < 0xff; ++i) { + pk2[0] = i; + EXPECT_NE(id_closest(pk0.data(), pk1.data(), pk2.data()), 0); + } +} + +TEST(AddToList, OverridesKeysWithCloserKeys) { + PublicKey const self_pk = {{0xaa}}; + PublicKey const keys[] = { + {{0xa0}}, // closest + {{0x0a}}, // + {{0x0b}}, // + {{0x0c}}, // + {{0x0d}}, // + {{0xa1}}, // closer than the 4 keys above + }; + + std::array nodes{}; + + EXPECT_TRUE(add_to_list(nodes.data(), nodes.size(), keys[0].data(), IP_Port(), self_pk.data())); + EXPECT_TRUE(add_to_list(nodes.data(), nodes.size(), keys[1].data(), IP_Port(), self_pk.data())); + EXPECT_TRUE(add_to_list(nodes.data(), nodes.size(), keys[2].data(), IP_Port(), self_pk.data())); + EXPECT_TRUE(add_to_list(nodes.data(), nodes.size(), keys[3].data(), IP_Port(), self_pk.data())); + + EXPECT_EQ(to_array(nodes[0].public_key), keys[0]); + EXPECT_EQ(to_array(nodes[1].public_key), keys[1]); + EXPECT_EQ(to_array(nodes[2].public_key), keys[2]); + EXPECT_EQ(to_array(nodes[3].public_key), keys[3]); + + // key 4 is less close than keys 0-3 + EXPECT_FALSE(add_to_list(nodes.data(), nodes.size(), keys[4].data(), IP_Port(), self_pk.data())); + // 5 is closer than all except key 0 + EXPECT_TRUE(add_to_list(nodes.data(), nodes.size(), keys[5].data(), IP_Port(), self_pk.data())); + + EXPECT_EQ(to_array(nodes[0].public_key), keys[0]); + EXPECT_EQ(to_array(nodes[1].public_key), keys[5]); + EXPECT_EQ(to_array(nodes[2].public_key), keys[1]); + EXPECT_EQ(to_array(nodes[3].public_key), keys[2]); +} + +} // namespace