From 57332bb03f5ef46e49085df9588cadf2d2e5106e Mon Sep 17 00:00:00 2001 From: oneiric Date: Thu, 14 Jun 2018 22:08:23 +0000 Subject: [PATCH 1/7] NetDb: exclude routers before sorting them It is unnecessary to sort excluded routers. References #643 and #910 --- src/core/router/net_db/impl.cc | 72 +++++++++++++++++----------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/src/core/router/net_db/impl.cc b/src/core/router/net_db/impl.cc index 11fa9d7f..62659c86 100644 --- a/src/core/router/net_db/impl.cc +++ b/src/core/router/net_db/impl.cc @@ -950,15 +950,18 @@ std::shared_ptr NetDb::GetClosestFloodfill( IdentHash dest_key = CreateRoutingKey(destination); min_metric.SetMax(); std::unique_lock l(m_FloodfillsMutex); - for (auto it : m_Floodfills) { - if (!it->IsUnreachable()) { - XORMetric m = dest_key ^ it->GetIdentHash(); - if (m < min_metric && !excluded.count(it->GetIdentHash())) { - min_metric = m; - r = it; - } + for (const auto& it : m_Floodfills) + { + if (!it->IsUnreachable() && !excluded.count(it->GetIdentHash())) + { + XORMetric m = dest_key ^ it->GetIdentHash(); + if (m < min_metric) + { + min_metric = m; + r = it; + } + } } - } return r; } @@ -977,31 +980,24 @@ std::vector NetDb::GetClosestFloodfills( std::set sorted; IdentHash dest_key = CreateRoutingKey(destination); { std::unique_lock l(m_FloodfillsMutex); - for (auto it : m_Floodfills) { - if (!it->IsUnreachable()) { - XORMetric m = dest_key ^ it->GetIdentHash(); - if (sorted.size() < num) { - sorted.insert({it, m}); - } else if (m < sorted.rbegin()->metric) { - sorted.insert({it, m}); - sorted.erase(std::prev(sorted.end())); - } + for (const auto& it : m_Floodfills) + { + if (!it->IsUnreachable() && !excluded.count(it->GetIdentHash())) + { + XORMetric m = dest_key ^ it->GetIdentHash(); + sorted.insert({it, m}); + } } - } } + std::vector res; std::uint8_t i{}; - for (auto it : sorted) { - if (i < num) { - auto& ident = it.r->GetIdentHash(); - if (!excluded.count(ident)) { - res.push_back(ident); - i++; - } - } else { - break; + for (auto it = sorted.begin(); it != sorted.end() && i < num; ++it) + { + const auto& ident = it->r->GetIdentHash(); + res.push_back(ident); + ++i; } - } return res; } @@ -1013,15 +1009,19 @@ std::shared_ptr NetDb::GetClosestNonFloodfill( IdentHash dest_key = CreateRoutingKey(destination); min_metric.SetMax(); // must be called from NetDb thread only - for (auto it : m_RouterInfos) { - if (!it.second->HasCap(RouterInfo::Cap::Floodfill)) { - XORMetric m = dest_key ^ it.first; - if (m < min_metric && !excluded.count(it.first)) { - min_metric = m; - r = it.second; - } + for (const auto& it : m_RouterInfos) + { + if (!it.second->HasCap(RouterInfo::Cap::Floodfill) + && !excluded.count(it.first)) + { + XORMetric m = dest_key ^ it.first; + if (m < min_metric) + { + min_metric = m; + r = it.second; + } + } } - } return r; } From eca1128347a241a12e69340ef83365795baa73f5 Mon Sep 17 00:00:00 2001 From: oneiric Date: Fri, 15 Jun 2018 19:56:51 +0000 Subject: [PATCH 2/7] NetDb: add lock in getting closest non-floodfill References #643 and #910 --- src/core/router/net_db/impl.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/router/net_db/impl.cc b/src/core/router/net_db/impl.cc index 62659c86..bfb3a493 100644 --- a/src/core/router/net_db/impl.cc +++ b/src/core/router/net_db/impl.cc @@ -1008,7 +1008,7 @@ std::shared_ptr NetDb::GetClosestNonFloodfill( XORMetric min_metric; IdentHash dest_key = CreateRoutingKey(destination); min_metric.SetMax(); - // must be called from NetDb thread only + std::unique_lock l(m_RouterInfosMutex); for (const auto& it : m_RouterInfos) { if (!it.second->HasCap(RouterInfo::Cap::Floodfill) From bbdf271c5a18c78e0970bcdb2cc068a9eb5924f6 Mon Sep 17 00:00:00 2001 From: oneiric Date: Sun, 24 Jun 2018 04:42:35 +0000 Subject: [PATCH 3/7] Exception: make Dispatch const-correct References #643 and #910 --- src/core/util/exception.cc | 2 +- src/core/util/exception.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/util/exception.cc b/src/core/util/exception.cc index 936dfa9f..4bc5ad23 100644 --- a/src/core/util/exception.cc +++ b/src/core/util/exception.cc @@ -45,7 +45,7 @@ namespace core { Exception::Exception(const char* message) : m_Message(message) {} // TODO(anonimal): exception error codes to replace strings? -void Exception::Dispatch(const char* message) +void Exception::Dispatch(const char* message) const { // Message to log std::string log; diff --git a/src/core/util/exception.h b/src/core/util/exception.h index dc84d926..e7ae5cc1 100644 --- a/src/core/util/exception.h +++ b/src/core/util/exception.h @@ -49,7 +49,7 @@ class Exception final { /// @brief Exception class dispatcher /// @details Set optional exception message, concats messages, adds trivial formatting /// @param message String message to log for exception - void Dispatch(const char* message = ""); + void Dispatch(const char* message = "") const; private: std::string m_Message; From 06d4f0b5cd8af6eed864e6f58f6e305f238fc029 Mon Sep 17 00:00:00 2001 From: oneiric Date: Sat, 16 Jun 2018 00:15:28 +0000 Subject: [PATCH 4/7] NetDb: catch exceptions getting closest routers References #643 and #910 --- src/core/router/net_db/impl.cc | 113 ++++++++++++++++++++------------- 1 file changed, 70 insertions(+), 43 deletions(-) diff --git a/src/core/router/net_db/impl.cc b/src/core/router/net_db/impl.cc index bfb3a493..686dc769 100644 --- a/src/core/router/net_db/impl.cc +++ b/src/core/router/net_db/impl.cc @@ -946,22 +946,30 @@ std::shared_ptr NetDb::GetClosestFloodfill( const IdentHash& destination, const std::set& excluded) const { std::shared_ptr r; - XORMetric min_metric; - IdentHash dest_key = CreateRoutingKey(destination); - min_metric.SetMax(); - std::unique_lock l(m_FloodfillsMutex); - for (const auto& it : m_Floodfills) - { - if (!it->IsUnreachable() && !excluded.count(it->GetIdentHash())) + try + { + XORMetric min_metric; + IdentHash dest_key = CreateRoutingKey(destination); + min_metric.SetMax(); + std::unique_lock l(m_FloodfillsMutex); + for (const auto& it : m_Floodfills) { - XORMetric m = dest_key ^ it->GetIdentHash(); - if (m < min_metric) + if (!it->IsUnreachable() && !excluded.count(it->GetIdentHash())) { - min_metric = m; - r = it; + XORMetric m = dest_key ^ it->GetIdentHash(); + if (m < min_metric) + { + min_metric = m; + r = it; + } } } } + catch (...) + { + m_Exception.Dispatch(__func__); + throw; + } return r; } @@ -970,33 +978,44 @@ std::vector NetDb::GetClosestFloodfills( std::uint8_t num, std::set& excluded) const { - struct Sorted { - std::shared_ptr r; - XORMetric metric; - bool operator<(const Sorted& other) const { - return metric < other.metric; - } - }; - std::set sorted; - IdentHash dest_key = CreateRoutingKey(destination); { - std::unique_lock l(m_FloodfillsMutex); - for (const auto& it : m_Floodfills) + std::vector res; + try + { + IdentHash dest_key = CreateRoutingKey(destination); + struct Sorted { - if (!it->IsUnreachable() && !excluded.count(it->GetIdentHash())) + std::shared_ptr r; + XORMetric metric; + bool operator<(const Sorted& other) const + { + return metric < other.metric; + } + }; + std::set sorted; + { + std::unique_lock l(m_FloodfillsMutex); + for (const auto& it : m_Floodfills) { - XORMetric m = dest_key ^ it->GetIdentHash(); - sorted.insert({it, m}); + if (!it->IsUnreachable() && !excluded.count(it->GetIdentHash())) + { + XORMetric m = dest_key ^ it->GetIdentHash(); + sorted.insert({it, m}); + } } } - } - std::vector res; - std::uint8_t i{}; - for (auto it = sorted.begin(); it != sorted.end() && i < num; ++it) + std::uint8_t i{}; + for (auto it = sorted.begin(); it != sorted.end() && i < num; ++it) + { + const auto& ident = it->r->GetIdentHash(); + res.push_back(ident); + ++i; + } + } + catch (...) { - const auto& ident = it->r->GetIdentHash(); - res.push_back(ident); - ++i; + m_Exception.Dispatch(__func__); + throw; } return res; } @@ -1005,23 +1024,31 @@ std::shared_ptr NetDb::GetClosestNonFloodfill( const IdentHash& destination, const std::set& excluded) const { std::shared_ptr r; - XORMetric min_metric; - IdentHash dest_key = CreateRoutingKey(destination); - min_metric.SetMax(); - std::unique_lock l(m_RouterInfosMutex); - for (const auto& it : m_RouterInfos) + try { - if (!it.second->HasCap(RouterInfo::Cap::Floodfill) - && !excluded.count(it.first)) + XORMetric min_metric; + IdentHash dest_key = CreateRoutingKey(destination); + min_metric.SetMax(); + std::unique_lock l(m_RouterInfosMutex); + for (const auto& it : m_RouterInfos) { - XORMetric m = dest_key ^ it.first; - if (m < min_metric) + if (!it.second->HasCap(RouterInfo::Cap::Floodfill) + && !excluded.count(it.first)) { - min_metric = m; - r = it.second; + XORMetric m = dest_key ^ it.first; + if (m < min_metric) + { + min_metric = m; + r = it.second; + } } } } + catch (...) + { + m_Exception.Dispatch(__func__); + throw; + } return r; } From 4ea811cbe2b5a7f59483c8f79edb4300c0438376 Mon Sep 17 00:00:00 2001 From: oneiric Date: Fri, 8 Jun 2018 21:30:56 +0000 Subject: [PATCH 5/7] Test: NetDb get closest routers unit-tests References #643 and #910 --- tests/unit_tests/CMakeLists.txt | 1 + tests/unit_tests/core/router/net_db/impl.cc | 196 ++++++++++++++++++++ 2 files changed, 197 insertions(+) create mode 100644 tests/unit_tests/core/router/net_db/impl.cc diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt index d062af32..142afef2 100644 --- a/tests/unit_tests/CMakeLists.txt +++ b/tests/unit_tests/CMakeLists.txt @@ -18,6 +18,7 @@ add_executable(kovri-tests core/crypto/rand.cc core/crypto/util/x509.cc core/router/identity.cc + core/router/net_db/impl.cc core/router/transports/ssu/packet.cc core/util/byte_stream.cc core/util/config.cc diff --git a/tests/unit_tests/core/router/net_db/impl.cc b/tests/unit_tests/core/router/net_db/impl.cc new file mode 100644 index 00000000..7f7bd88b --- /dev/null +++ b/tests/unit_tests/core/router/net_db/impl.cc @@ -0,0 +1,196 @@ +/** // + * Copyright (c) 2013-2018, The Kovri I2P Router Project // + * // + * All rights reserved. // + * // + * Redistribution and use in source and binary forms, with or without modification, are // + * permitted provided that the following conditions are met: // + * // + * 1. Redistributions of source code must retain the above copyright notice, this list of // + * conditions and the following disclaimer. // + * // + * 2. Redistributions in binary form must reproduce the above copyright notice, this list // + * of conditions and the following disclaimer in the documentation and/or other // + * materials provided with the distribution. // + * // + * 3. Neither the name of the copyright holder nor the names of its contributors may be // + * used to endorse or promote products derived from this software without specific // + * prior written permission. // + * // + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY // + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF // + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL // + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, // + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // + * // + * Parts of the project are originally copyright (c) 2013-2015 The PurpleI2P Project // + */ + +#define BOOST_TEST_DYN_LINK + +#include + +#include "core/router/identity.h" +#include "core/router/info.h" +#include "core/router/net_db/impl.h" + +#include "tests/unit_tests/core/router/identity.h" + +namespace core = kovri::core; + +struct NetDbFixture : public IdentityExFixture +{ + /// @alias RouterCap + /// @brief Capabilities alias + /// @details Intended for readability & user-friendliness when writing new tests + using RouterCap = core::RouterInfoTraits::Cap; + + /// @brief Create identity from buffer + /// @details Useful for getting a valid IdentHash + void CreateIdent() + { + // Create valid identity + BOOST_CHECK( + m_Ident.FromBuffer(m_AliceIdentity.data(), m_AliceIdentity.size())); + + // Set ident hash for convenience + m_Hash = m_Ident.GetIdentHash(); + } + + /// @brief Add router to NetDb + /// @param cap Capability to add to router + void AddRouter(const RouterCap cap) + { + // Create new private keys + m_Keys = core::PrivateKeys::CreateRandomKeys( + core::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519); + + // Create new router + m_RI = + std::make_unique(m_Keys, m_Points, m_Transports, cap); + + // Add router to NetDb + BOOST_CHECK_NO_THROW(m_NetDB.AddRouterInfo( + m_RI->GetIdentHash(), m_RI->GetBuffer(), m_RI->GetBufferLen())); + } + + core::NetDb m_NetDB; + core::IdentHash m_Hash; + core::IdentityEx m_Ident; + core::PrivateKeys m_Keys; + std::set m_Ex; + std::unique_ptr m_RI; + std::pair m_Transports{true, false}; + std::vector > m_Points{{"127.0.0.1", 9111}}; +}; + +BOOST_FIXTURE_TEST_SUITE(NetDbTests, NetDbFixture) + +BOOST_AUTO_TEST_CASE(ValidClosestFloodfill) +{ + // Create a valid router identity + CreateIdent(); + + // Add floodfill router to NetDb + AddRouter(RouterCap::Floodfill); + + std::shared_ptr ret_ri; + + // Ensure no exceptions thrown getting valid floodfill + BOOST_CHECK_NO_THROW(ret_ri = m_NetDB.GetClosestFloodfill(m_Hash, m_Ex)); + + // Ensure expected floodfill is returned + BOOST_CHECK(ret_ri && ret_ri->GetIdentHash() == m_RI->GetIdentHash()); +} + +BOOST_AUTO_TEST_CASE(InvalidClosestFloodfill) +{ + // Ensure null destination throws + BOOST_CHECK_THROW( + m_NetDB.GetClosestFloodfill(nullptr, m_Ex), std::invalid_argument); + + // Ensure zero-initialized destination throws + BOOST_CHECK_THROW( + m_NetDB.GetClosestFloodfill(m_Hash, m_Ex), std::invalid_argument); +} + +BOOST_AUTO_TEST_CASE(ValidClosestFloodfills) +{ + // Create a valid router identity + CreateIdent(); + + // Add floodfill router to NetDb + AddRouter(RouterCap::Floodfill); + + // Store the first floodfill locally + std::unique_ptr flood_ri{nullptr}; + flood_ri.swap(m_RI); + + // Add another floodfill router to NetDb + AddRouter(RouterCap::Floodfill); + + std::vector ret_hash; + const std::uint8_t limit = 2; + + // Ensure no exceptions thrown getting valid floodfill(s) + BOOST_CHECK_NO_THROW( + ret_hash = m_NetDB.GetClosestFloodfills(m_Hash, limit, m_Ex)); + + // Ensure number of floodfills added are returned + BOOST_CHECK(ret_hash.size() == limit); + + // Ensure returned ident hashes are unique + BOOST_CHECK(ret_hash.front() != ret_hash.back()); + + // Ensure returned ident hashes match expected floodfills + for (auto const& h : ret_hash) + BOOST_CHECK(h == flood_ri->GetIdentHash() || h == m_RI->GetIdentHash()); + + // Ensure limit is respected + BOOST_CHECK(m_NetDB.GetClosestFloodfills(m_Hash, 0, m_Ex).empty()); +} + +BOOST_AUTO_TEST_CASE(InvalidClosestFloodfills) +{ + // Ensure null destination throws + BOOST_CHECK_THROW( + m_NetDB.GetClosestFloodfills(nullptr, 1, m_Ex), std::invalid_argument); + + // Ensure zero-initialized destination throws + BOOST_CHECK_THROW( + m_NetDB.GetClosestFloodfills(m_Hash, 1, m_Ex), std::invalid_argument); +} + +BOOST_AUTO_TEST_CASE(ValidClosestNonFloodfill) +{ + // Create a valid router identity + CreateIdent(); + + // Add non-floodfill to NetDb + AddRouter(RouterCap::HighBandwidth); + + std::shared_ptr ret_ri; + + // Ensure no exceptions thrown getting valid non-floodfill + BOOST_CHECK_NO_THROW(ret_ri = m_NetDB.GetClosestNonFloodfill(m_Hash, m_Ex)); + + // Ensure expected non-floodfill is returned + BOOST_CHECK(ret_ri && ret_ri->GetIdentHash() == m_RI->GetIdentHash()); +} + +BOOST_AUTO_TEST_CASE(InvalidClosestNonFloodfill) +{ + // Ensure null destination throws + BOOST_CHECK_THROW( + m_NetDB.GetClosestNonFloodfill(nullptr, m_Ex), std::invalid_argument); + + // Ensure zero-initialized destination throws + BOOST_CHECK_THROW( + m_NetDB.GetClosestNonFloodfill(m_Hash, m_Ex), std::invalid_argument); +} + +BOOST_AUTO_TEST_SUITE_END() From 32ebf5dc3c74bd599db2b1d716c0b476c01db9c3 Mon Sep 17 00:00:00 2001 From: oneiric Date: Tue, 26 Jun 2018 16:43:50 +0000 Subject: [PATCH 6/7] NetDb: adds const-correctness + default params Adds const-correctness to getting closest router parameters, and default parameters for excluded routers. Credit to anonimal for the patch. Referencing #643 and #910 --- src/core/router/net_db/impl.cc | 4 ++-- src/core/router/net_db/impl.h | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/core/router/net_db/impl.cc b/src/core/router/net_db/impl.cc index 686dc769..1bef84d3 100644 --- a/src/core/router/net_db/impl.cc +++ b/src/core/router/net_db/impl.cc @@ -975,8 +975,8 @@ std::shared_ptr NetDb::GetClosestFloodfill( std::vector NetDb::GetClosestFloodfills( const IdentHash& destination, - std::uint8_t num, - std::set& excluded) const + const std::uint8_t num, + const std::set& excluded) const { std::vector res; try diff --git a/src/core/router/net_db/impl.h b/src/core/router/net_db/impl.h index c71fdfec..b9d9d19a 100644 --- a/src/core/router/net_db/impl.h +++ b/src/core/router/net_db/impl.h @@ -197,16 +197,16 @@ class NetDb : public NetDbTraits { std::shared_ptr GetClosestFloodfill( const IdentHash& destination, - const std::set& excluded) const; + const std::set& excluded = std::set()) const; std::vector GetClosestFloodfills( const IdentHash& destination, - std::uint8_t num, - std::set& excluded) const; + const std::uint8_t num, + const std::set& excluded = std::set()) const; std::shared_ptr GetClosestNonFloodfill( const IdentHash& destination, - const std::set& excluded) const; + const std::set& excluded = std::set()) const; void SetUnreachable( const IdentHash& ident, From e9d2bf47abf4b01c76159bf44e8a35fa6293d0e7 Mon Sep 17 00:00:00 2001 From: oneiric Date: Tue, 26 Jun 2018 16:47:25 +0000 Subject: [PATCH 7/7] Tests: patches NetDb closest router unit-tests Cleaner, more useable NetDb test fixture, and more concise test cases. Credit to anonimal for the patch. References #643 and #910 --- tests/unit_tests/core/router/net_db/impl.cc | 202 +++++++++----------- 1 file changed, 88 insertions(+), 114 deletions(-) diff --git a/tests/unit_tests/core/router/net_db/impl.cc b/tests/unit_tests/core/router/net_db/impl.cc index 7f7bd88b..232da332 100644 --- a/tests/unit_tests/core/router/net_db/impl.cc +++ b/tests/unit_tests/core/router/net_db/impl.cc @@ -1,5 +1,5 @@ /** // - * Copyright (c) 2013-2018, The Kovri I2P Router Project // + * Copyright (c) 2015-2018, The Kovri I2P Router Project // * // * All rights reserved. // * // @@ -26,14 +26,17 @@ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, // * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // - * // - * Parts of the project are originally copyright (c) 2013-2015 The PurpleI2P Project // */ #define BOOST_TEST_DYN_LINK #include +#include +#include +#include +#include + #include "core/router/identity.h" #include "core/router/info.h" #include "core/router/net_db/impl.h" @@ -44,153 +47,124 @@ namespace core = kovri::core; struct NetDbFixture : public IdentityExFixture { - /// @alias RouterCap - /// @brief Capabilities alias - /// @details Intended for readability & user-friendliness when writing new tests - using RouterCap = core::RouterInfoTraits::Cap; - - /// @brief Create identity from buffer - /// @details Useful for getting a valid IdentHash - void CreateIdent() - { - // Create valid identity - BOOST_CHECK( - m_Ident.FromBuffer(m_AliceIdentity.data(), m_AliceIdentity.size())); + using Cap = core::RouterInfoTraits::Cap; - // Set ident hash for convenience + NetDbFixture() : m_NetDb(std::make_unique()) + { + // Use Alice's data from IdentityEx fixture + core::IdentityEx m_Ident; + m_Ident.FromBuffer(m_AliceIdentity.data(), m_AliceIdentity.size()); m_Hash = m_Ident.GetIdentHash(); } - /// @brief Add router to NetDb - /// @param cap Capability to add to router - void AddRouter(const RouterCap cap) + std::unique_ptr AddRouterInfo(Cap cap) { - // Create new private keys - m_Keys = core::PrivateKeys::CreateRandomKeys( - core::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519); + auto ri = std::make_unique( + core::PrivateKeys::CreateRandomKeys( + core::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519), + std::vector>{{"127.0.0.1", 9111}}, + std::make_pair(true, false), + cap); - // Create new router - m_RI = - std::make_unique(m_Keys, m_Points, m_Transports, cap); + m_NetDb->AddRouterInfo( + ri->GetIdentHash(), ri->GetBuffer(), ri->GetBufferLen()); - // Add router to NetDb - BOOST_CHECK_NO_THROW(m_NetDB.AddRouterInfo( - m_RI->GetIdentHash(), m_RI->GetBuffer(), m_RI->GetBufferLen())); + return ri; + } + + std::shared_ptr GetClosestFloodfill() + { + auto ff = m_NetDb->GetClosestFloodfill(m_Hash); + if (!ff) + throw std::runtime_error("no floodfill available"); + return ff; + } + + std::vector GetClosestFloodfills(const std::uint8_t count) + { + auto ffs = m_NetDb->GetClosestFloodfills(m_Hash, count); + if (ffs.empty()) + throw std::runtime_error("no floodfills available"); + return ffs; + } + + std::shared_ptr GetClosestNonFloodfill() + { + auto ri = m_NetDb->GetClosestNonFloodfill(m_Hash); + if (!ri) + throw std::runtime_error("no routers available"); + return ri; } - core::NetDb m_NetDB; core::IdentHash m_Hash; - core::IdentityEx m_Ident; - core::PrivateKeys m_Keys; - std::set m_Ex; - std::unique_ptr m_RI; - std::pair m_Transports{true, false}; - std::vector > m_Points{{"127.0.0.1", 9111}}; + std::unique_ptr m_NetDb; }; BOOST_FIXTURE_TEST_SUITE(NetDbTests, NetDbFixture) -BOOST_AUTO_TEST_CASE(ValidClosestFloodfill) +// TODO(unassigned): this isn't an accurate testcase (we should rather test kademlia) +BOOST_AUTO_TEST_CASE(ValidClosestFloodfills) { - // Create a valid router identity - CreateIdent(); + constexpr std::uint8_t count(2); // FF count - // Add floodfill router to NetDb - AddRouter(RouterCap::Floodfill); + // Add floodfills to netdb + std::set> infos; + for (auto i(0); i < count; i++) + infos.insert(AddRouterInfo(Cap::Floodfill)); - std::shared_ptr ret_ri; + // Get added floodfill hashes + std::vector hashes; + for (const auto& ri : infos) + hashes.push_back(ri->GetIdentHash()); - // Ensure no exceptions thrown getting valid floodfill - BOOST_CHECK_NO_THROW(ret_ri = m_NetDB.GetClosestFloodfill(m_Hash, m_Ex)); - - // Ensure expected floodfill is returned - BOOST_CHECK(ret_ri && ret_ri->GetIdentHash() == m_RI->GetIdentHash()); -} + // Get closest floodfills + std::vector ffs; + BOOST_REQUIRE_NO_THROW(ffs = GetClosestFloodfills(infos.size())); -BOOST_AUTO_TEST_CASE(InvalidClosestFloodfill) -{ - // Ensure null destination throws - BOOST_CHECK_THROW( - m_NetDB.GetClosestFloodfill(nullptr, m_Ex), std::invalid_argument); + // Floodfill hashes should match added router hashes + // TODO(unassigned): this should change once we include the kademlia test + std::sort(ffs.begin(), ffs.end()); + std::sort(hashes.begin(), hashes.end()); - // Ensure zero-initialized destination throws - BOOST_CHECK_THROW( - m_NetDB.GetClosestFloodfill(m_Hash, m_Ex), std::invalid_argument); + BOOST_CHECK(ffs == hashes); } -BOOST_AUTO_TEST_CASE(ValidClosestFloodfills) +BOOST_AUTO_TEST_CASE(ValidClosestFloodfill) { - // Create a valid router identity - CreateIdent(); - - // Add floodfill router to NetDb - AddRouter(RouterCap::Floodfill); - - // Store the first floodfill locally - std::unique_ptr flood_ri{nullptr}; - flood_ri.swap(m_RI); - - // Add another floodfill router to NetDb - AddRouter(RouterCap::Floodfill); - - std::vector ret_hash; - const std::uint8_t limit = 2; - - // Ensure no exceptions thrown getting valid floodfill(s) - BOOST_CHECK_NO_THROW( - ret_hash = m_NetDB.GetClosestFloodfills(m_Hash, limit, m_Ex)); - - // Ensure number of floodfills added are returned - BOOST_CHECK(ret_hash.size() == limit); + std::unique_ptr ri; + BOOST_REQUIRE_NO_THROW(ri = AddRouterInfo(Cap::Floodfill)); - // Ensure returned ident hashes are unique - BOOST_CHECK(ret_hash.front() != ret_hash.back()); + std::shared_ptr ff; + BOOST_REQUIRE_NO_THROW(ff = GetClosestFloodfill()); - // Ensure returned ident hashes match expected floodfills - for (auto const& h : ret_hash) - BOOST_CHECK(h == flood_ri->GetIdentHash() || h == m_RI->GetIdentHash()); - - // Ensure limit is respected - BOOST_CHECK(m_NetDB.GetClosestFloodfills(m_Hash, 0, m_Ex).empty()); + BOOST_REQUIRE_EQUAL(ff->GetIdentHash(), ri->GetIdentHash()); } -BOOST_AUTO_TEST_CASE(InvalidClosestFloodfills) +BOOST_AUTO_TEST_CASE(ValidClosestNonFloodfill) { - // Ensure null destination throws - BOOST_CHECK_THROW( - m_NetDB.GetClosestFloodfills(nullptr, 1, m_Ex), std::invalid_argument); + std::unique_ptr ri; + BOOST_REQUIRE_NO_THROW(ri = AddRouterInfo(Cap::HighBandwidth)); - // Ensure zero-initialized destination throws - BOOST_CHECK_THROW( - m_NetDB.GetClosestFloodfills(m_Hash, 1, m_Ex), std::invalid_argument); + std::shared_ptr ff; + BOOST_REQUIRE_NO_THROW(ff = GetClosestNonFloodfill()); + + BOOST_CHECK_EQUAL(ff->GetIdentHash(), ri->GetIdentHash()); } -BOOST_AUTO_TEST_CASE(ValidClosestNonFloodfill) +BOOST_AUTO_TEST_CASE(InvalidRouters) { - // Create a valid router identity - CreateIdent(); + core::IdentHash hash; // Empty hash - // Add non-floodfill to NetDb - AddRouter(RouterCap::HighBandwidth); + BOOST_CHECK_THROW(m_NetDb->GetClosestFloodfill(hash), std::exception); + BOOST_CHECK_THROW(m_NetDb->GetClosestFloodfill(nullptr), std::exception); - std::shared_ptr ret_ri; + BOOST_CHECK_THROW(m_NetDb->GetClosestFloodfills(hash, 1), std::exception); + BOOST_CHECK_THROW(m_NetDb->GetClosestFloodfills(nullptr, 1), std::exception); - // Ensure no exceptions thrown getting valid non-floodfill - BOOST_CHECK_NO_THROW(ret_ri = m_NetDB.GetClosestNonFloodfill(m_Hash, m_Ex)); - - // Ensure expected non-floodfill is returned - BOOST_CHECK(ret_ri && ret_ri->GetIdentHash() == m_RI->GetIdentHash()); -} - -BOOST_AUTO_TEST_CASE(InvalidClosestNonFloodfill) -{ - // Ensure null destination throws - BOOST_CHECK_THROW( - m_NetDB.GetClosestNonFloodfill(nullptr, m_Ex), std::invalid_argument); + BOOST_CHECK_THROW(GetClosestFloodfills(0), std::exception); - // Ensure zero-initialized destination throws - BOOST_CHECK_THROW( - m_NetDB.GetClosestNonFloodfill(m_Hash, m_Ex), std::invalid_argument); + BOOST_CHECK_THROW(m_NetDb->GetClosestNonFloodfill(nullptr), std::exception); + BOOST_CHECK_THROW(m_NetDb->GetClosestNonFloodfill(hash), std::exception); } BOOST_AUTO_TEST_SUITE_END()