From 73c9ddff522ce3cd5dbe326b760ceeac3220ed74 Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Wed, 20 May 2020 11:26:45 -0600 Subject: [PATCH 01/70] Begin peer stats infrastructure --- .gitmodules | 3 ++ CMakeLists.txt | 3 ++ external/sqlite_orm | 1 + llarp/CMakeLists.txt | 3 +- llarp/peerstats/peer_db.cpp | 67 +++++++++++++++++++++++++++++++++++ llarp/peerstats/peer_db.hpp | 69 +++++++++++++++++++++++++++++++++++++ test/CMakeLists.txt | 1 + test/peerstats/peer_db.cpp | 46 +++++++++++++++++++++++++ 8 files changed, 192 insertions(+), 1 deletion(-) create mode 160000 external/sqlite_orm create mode 100644 llarp/peerstats/peer_db.cpp create mode 100644 llarp/peerstats/peer_db.hpp create mode 100644 test/peerstats/peer_db.cpp diff --git a/.gitmodules b/.gitmodules index 18e3dc04b..f8462c270 100644 --- a/.gitmodules +++ b/.gitmodules @@ -31,3 +31,6 @@ path = external/loki-mq url = https://github.com/loki-project/loki-mq branch = dev +[submodule "external/sqlite_orm"] + path = external/sqlite_orm + url = https://github.com/fnc12/sqlite_orm diff --git a/CMakeLists.txt b/CMakeLists.txt index a0a9bc955..04ef7ba82 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -305,6 +305,7 @@ if(SUBMODULE_CHECK) check_submodule(external/ghc-filesystem) check_submodule(external/date) check_submodule(external/pybind11) + check_submodule(external/sqlite_orm) if (NOT WIN32) # we grab libuv for windows separately in win32-setup/libuv. see note in cmake/win32.cmake. check_submodule(external/libuv) endif() @@ -324,6 +325,8 @@ add_subdirectory(external/nlohmann EXCLUDE_FROM_ALL) add_subdirectory(external/cxxopts EXCLUDE_FROM_ALL) add_subdirectory(external/date EXCLUDE_FROM_ALL) +include_directories(SYSTEM external/sqlite_orm/include) + add_subdirectory(vendor) if(ANDROID) diff --git a/external/sqlite_orm b/external/sqlite_orm new file mode 160000 index 000000000..f7ef17a6b --- /dev/null +++ b/external/sqlite_orm @@ -0,0 +1 @@ +Subproject commit f7ef17a6bde6162e8b487deb36519bace412920a diff --git a/llarp/CMakeLists.txt b/llarp/CMakeLists.txt index e87cb6673..8192b3303 100644 --- a/llarp/CMakeLists.txt +++ b/llarp/CMakeLists.txt @@ -34,13 +34,13 @@ target_link_libraries(lokinet-util PUBLIC filesystem date::date lokimq + sqlite3 ) if(ANDROID) target_link_libraries(lokinet-util PUBLIC log) endif() - add_library(lokinet-platform # for networking ev/ev.cpp @@ -156,6 +156,7 @@ add_library(liblokinet path/pathbuilder.cpp path/pathset.cpp path/transit_hop.cpp + peerstats/peer_db.cpp pow.cpp profiling.cpp router/outbound_message_handler.cpp diff --git a/llarp/peerstats/peer_db.cpp b/llarp/peerstats/peer_db.cpp new file mode 100644 index 000000000..a15356d2e --- /dev/null +++ b/llarp/peerstats/peer_db.cpp @@ -0,0 +1,67 @@ +#include + +namespace llarp +{ + PeerStats& + PeerStats::operator+=(const PeerStats& other) + { + numConnectionAttempts += other.numConnectionAttempts; + numConnectionSuccesses += other.numConnectionSuccesses; + numConnectionRejections += other.numConnectionRejections; + numConnectionTimeouts += other.numConnectionTimeouts; + + numPathBuilds += other.numPathBuilds; + numPacketsAttempted += other.numPacketsAttempted; + numPacketsSent += other.numPacketsSent; + numPacketsDropped += other.numPacketsDropped; + numPacketsResent += other.numPacketsResent; + + numDistinctRCsReceived += other.numDistinctRCsReceived; + numLateRCs += other.numLateRCs; + + peakBandwidthBytesPerSec = std::max(peakBandwidthBytesPerSec, other.peakBandwidthBytesPerSec); + longestRCReceiveInterval = std::max(longestRCReceiveInterval, other.longestRCReceiveInterval); + mostExpiredRC = std::max(mostExpiredRC, other.mostExpiredRC); + + return *this; + } + + bool + PeerStats::operator==(const PeerStats& other) + { + return numConnectionAttempts == other.numConnectionAttempts + and numConnectionSuccesses == other.numConnectionSuccesses + and numConnectionRejections == other.numConnectionRejections + and numConnectionTimeouts == other.numConnectionTimeouts + + and numPathBuilds == other.numPathBuilds + and numPacketsAttempted == other.numPacketsAttempted + and numPacketsSent == other.numPacketsSent and numPacketsDropped == other.numPacketsDropped + and numPacketsResent == other.numPacketsResent + + and numDistinctRCsReceived == other.numDistinctRCsReceived + and numLateRCs == other.numLateRCs + + and peakBandwidthBytesPerSec == peakBandwidthBytesPerSec + and longestRCReceiveInterval == longestRCReceiveInterval and mostExpiredRC == mostExpiredRC; + } + + void + PeerDb::accumulatePeerStats(const RouterID& routerId, const PeerStats& delta) + { + std::lock_guard gaurd(m_statsLock); + m_peerStats[routerId] += delta; + } + + PeerStats + PeerDb::getCurrentPeerStats(const RouterID& routerId) const + { + std::lock_guard gaurd(m_statsLock); + auto itr = m_peerStats.find(routerId); + if (itr == m_peerStats.end()) + return {}; + else + return itr->second; + } + +}; // namespace llarp diff --git a/llarp/peerstats/peer_db.hpp b/llarp/peerstats/peer_db.hpp new file mode 100644 index 000000000..cd22dc8df --- /dev/null +++ b/llarp/peerstats/peer_db.hpp @@ -0,0 +1,69 @@ +#pragma once + +#include +#include + +#include + +#include +#include + +namespace llarp +{ + // Struct containing stats we know about a peer + struct PeerStats + { + int32_t numConnectionAttempts = 0; + int32_t numConnectionSuccesses = 0; + int32_t numConnectionRejections = 0; + int32_t numConnectionTimeouts = 0; + + int32_t numPathBuilds = 0; + int64_t numPacketsAttempted = 0; + int64_t numPacketsSent = 0; + int64_t numPacketsDropped = 0; + int64_t numPacketsResent = 0; + + int64_t numDistinctRCsReceived = 0; + int64_t numLateRCs = 0; + + double peakBandwidthBytesPerSec = 0; + std::chrono::milliseconds longestRCReceiveInterval = 0ms; + std::chrono::milliseconds mostExpiredRC = 0ms; + + PeerStats& + operator+=(const PeerStats& other); + bool + operator==(const PeerStats& other); + }; + + /// Maintains a database of stats collected about the connections with our Service Node peers + struct PeerDb + { + /// Add the given stats to the cummulative stats for the given peer. For cummulative stats, the + /// stats are added together; for watermark stats, the max is kept. + /// + /// This is intended to be used in the following pattern: + /// + /// 1) Initialize an empty PeerStats + /// 2) Collect relevant stats + /// 3) Call accumulatePeerStats() with the stats + /// 4) Reset the stats to 0 + /// 5) + void + accumulatePeerStats(const RouterID& routerId, const PeerStats& delta); + + /// Provides a snapshot of the most recent PeerStats we have for the given peer. If we don't + /// have any stats for the peer, an empty PeerStats is returned. + /// + /// @param routerId is the RouterID of the requested peer + /// @return a copy of the most recent peer stats or an empty one if no such peer is known + PeerStats + getCurrentPeerStats(const RouterID& routerId) const; + + private: + std::unordered_map m_peerStats; + std::mutex m_statsLock; + }; + +} // namespace llarp diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index bd3714233..a6239d941 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -68,6 +68,7 @@ add_executable(catchAll util/test_llarp_util_printer.cpp util/test_llarp_util_str.cpp util/test_llarp_util_decaying_hashset.cpp + peerstats/peer_db.cpp config/test_llarp_config_definition.cpp config/test_llarp_config_output.cpp net/test_ip_address.cpp diff --git a/test/peerstats/peer_db.cpp b/test/peerstats/peer_db.cpp new file mode 100644 index 000000000..66e5837ea --- /dev/null +++ b/test/peerstats/peer_db.cpp @@ -0,0 +1,46 @@ +#include +#include + +#include + +TEST_CASE("Test PeerStats operator+=", "[PeerStats]") +{ + // TODO: test all members + llarp::PeerStats stats; + stats.numConnectionAttempts = 1; + stats.peakBandwidthBytesPerSec = 12; + + llarp::PeerStats delta; + delta.numConnectionAttempts = 2; + delta.peakBandwidthBytesPerSec = 4; + + stats += delta; + + CHECK(stats.numConnectionAttempts == 3); + CHECK(stats.peakBandwidthBytesPerSec == 12); // should take max(), not add +} + +TEST_CASE("Test PeerDb PeerStats memory storage", "[PeerDb]") +{ + const llarp::PeerStats empty = {}; + const llarp::RouterID id = {}; + + llarp::PeerDb db; + CHECK(db.getCurrentPeerStats(id) == empty); + + llarp::PeerStats delta; + delta.numConnectionAttempts = 4; + delta.peakBandwidthBytesPerSec = 5; + db.accumulatePeerStats(id, delta); + CHECK(db.getCurrentPeerStats(id) == delta); + + delta = {}; + delta.numConnectionAttempts = 5; + delta.peakBandwidthBytesPerSec = 6; + db.accumulatePeerStats(id, delta); + + llarp::PeerStats expected; + expected.numConnectionAttempts = 9; + expected.peakBandwidthBytesPerSec = 6; + CHECK(db.getCurrentPeerStats(id) == expected); +} From 8adb6295fcc8958df4f2924aa902e20604222c23 Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Wed, 20 May 2020 15:00:10 -0600 Subject: [PATCH 02/70] Initialize sqlite_orm and start interacting with it --- llarp/CMakeLists.txt | 1 + llarp/peerstats/orm.hpp | 38 +++++++++++++++ llarp/peerstats/peer_db.cpp | 77 +++++++++++++++++------------- llarp/peerstats/peer_db.hpp | 61 ++++++++++++----------- llarp/peerstats/types.cpp | 56 ++++++++++++++++++++++ llarp/peerstats/types.hpp | 44 +++++++++++++++++ test/CMakeLists.txt | 3 +- test/peerstats/peer_db.cpp | 46 ------------------ test/peerstats/test_peer_db.cpp | 75 +++++++++++++++++++++++++++++ test/peerstats/test_peer_types.cpp | 23 +++++++++ 10 files changed, 314 insertions(+), 110 deletions(-) create mode 100644 llarp/peerstats/orm.hpp create mode 100644 llarp/peerstats/types.cpp create mode 100644 llarp/peerstats/types.hpp delete mode 100644 test/peerstats/peer_db.cpp create mode 100644 test/peerstats/test_peer_db.cpp create mode 100644 test/peerstats/test_peer_types.cpp diff --git a/llarp/CMakeLists.txt b/llarp/CMakeLists.txt index 8192b3303..4f89f6fad 100644 --- a/llarp/CMakeLists.txt +++ b/llarp/CMakeLists.txt @@ -157,6 +157,7 @@ add_library(liblokinet path/pathset.cpp path/transit_hop.cpp peerstats/peer_db.cpp + peerstats/types.cpp pow.cpp profiling.cpp router/outbound_message_handler.cpp diff --git a/llarp/peerstats/orm.hpp b/llarp/peerstats/orm.hpp new file mode 100644 index 000000000..17fa39ae6 --- /dev/null +++ b/llarp/peerstats/orm.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include + +#include + +/// Contains some code to help deal with sqlite_orm in hopes of keeping other headers clean + +namespace llarp +{ + inline auto + initStorage(const std::string& file) + { + using namespace sqlite_orm; + return make_storage( + file, + make_table( + "peerstats", + make_column("routerId", &PeerStats::routerIdHex, primary_key(), unique()), + make_column("numConnectionAttempts", &PeerStats::numConnectionAttempts), + make_column("numConnectionSuccesses", &PeerStats::numConnectionSuccesses), + make_column("numConnectionRejections", &PeerStats::numConnectionRejections), + make_column("numConnectionTimeouts", &PeerStats::numConnectionTimeouts), + make_column("numPathBuilds", &PeerStats::numPathBuilds), + make_column("numPacketsAttempted", &PeerStats::numPacketsAttempted), + make_column("numPacketsSent", &PeerStats::numPacketsSent), + make_column("numPacketsDropped", &PeerStats::numPacketsDropped), + make_column("numPacketsResent", &PeerStats::numPacketsResent), + make_column("numDistinctRCsReceived", &PeerStats::numDistinctRCsReceived), + make_column("numLateRCs", &PeerStats::numLateRCs), + make_column("peakBandwidthBytesPerSec", &PeerStats::peakBandwidthBytesPerSec), + make_column("longestRCReceiveInterval", &PeerStats::longestRCReceiveIntervalMs), + make_column("mostExpiredRC", &PeerStats::mostExpiredRCMs))); + } + + using PeerDbStorage = decltype(initStorage("")); + +} // namespace llarp diff --git a/llarp/peerstats/peer_db.cpp b/llarp/peerstats/peer_db.cpp index a15356d2e..4ba18223a 100644 --- a/llarp/peerstats/peer_db.cpp +++ b/llarp/peerstats/peer_db.cpp @@ -1,65 +1,74 @@ #include +#include +#include + namespace llarp { - PeerStats& - PeerStats::operator+=(const PeerStats& other) + void + PeerDb::loadDatabase(std::optional file) { - numConnectionAttempts += other.numConnectionAttempts; - numConnectionSuccesses += other.numConnectionSuccesses; - numConnectionRejections += other.numConnectionRejections; - numConnectionTimeouts += other.numConnectionTimeouts; + std::lock_guard gaurd(m_statsLock); - numPathBuilds += other.numPathBuilds; - numPacketsAttempted += other.numPacketsAttempted; - numPacketsSent += other.numPacketsSent; - numPacketsDropped += other.numPacketsDropped; - numPacketsResent += other.numPacketsResent; + m_peerStats.clear(); - numDistinctRCsReceived += other.numDistinctRCsReceived; - numLateRCs += other.numLateRCs; + if (m_storage) + throw std::runtime_error("Reloading database not supported"); // TODO - peakBandwidthBytesPerSec = std::max(peakBandwidthBytesPerSec, other.peakBandwidthBytesPerSec); - longestRCReceiveInterval = std::max(longestRCReceiveInterval, other.longestRCReceiveInterval); - mostExpiredRC = std::max(mostExpiredRC, other.mostExpiredRC); + // sqlite_orm treats empty-string as an indicator to load a memory-backed database, which we'll + // use if file is an empty-optional + std::string fileString; + if (file.has_value()) + fileString = file.value().native(); - return *this; + m_storage = std::make_unique(initStorage(fileString)); } - bool - PeerStats::operator==(const PeerStats& other) + void + PeerDb::flushDatabase() { - return numConnectionAttempts == other.numConnectionAttempts - and numConnectionSuccesses == other.numConnectionSuccesses - and numConnectionRejections == other.numConnectionRejections - and numConnectionTimeouts == other.numConnectionTimeouts + if (not m_storage) + throw std::runtime_error("Cannot flush database before it has been loaded"); + + decltype(m_peerStats) copy; - and numPathBuilds == other.numPathBuilds - and numPacketsAttempted == other.numPacketsAttempted - and numPacketsSent == other.numPacketsSent and numPacketsDropped == other.numPacketsDropped - and numPacketsResent == other.numPacketsResent + { + std::lock_guard gaurd(m_statsLock); + copy = m_peerStats; // expensive deep copy + } - and numDistinctRCsReceived == other.numDistinctRCsReceived - and numLateRCs == other.numLateRCs + for (const auto& entry : m_peerStats) + { + // call me paranoid... + assert(not entry.second.routerIdHex.empty()); + assert(entry.first.ToHex() == entry.second.routerIdHex); - and peakBandwidthBytesPerSec == peakBandwidthBytesPerSec - and longestRCReceiveInterval == longestRCReceiveInterval and mostExpiredRC == mostExpiredRC; + m_storage->insert(entry.second); + } } void PeerDb::accumulatePeerStats(const RouterID& routerId, const PeerStats& delta) { + if (routerId.ToHex() != delta.routerIdHex) + throw std::invalid_argument( + stringify("routerId ", routerId, " doesn't match ", delta.routerIdHex)); + std::lock_guard gaurd(m_statsLock); - m_peerStats[routerId] += delta; + auto itr = m_peerStats.find(routerId); + if (itr == m_peerStats.end()) + itr = m_peerStats.insert({routerId, delta}).first; + else + itr->second += delta; } - PeerStats + std::optional PeerDb::getCurrentPeerStats(const RouterID& routerId) const { std::lock_guard gaurd(m_statsLock); auto itr = m_peerStats.find(routerId); if (itr == m_peerStats.end()) - return {}; + return std::nullopt; else return itr->second; } diff --git a/llarp/peerstats/peer_db.hpp b/llarp/peerstats/peer_db.hpp index cd22dc8df..ea4ebd317 100644 --- a/llarp/peerstats/peer_db.hpp +++ b/llarp/peerstats/peer_db.hpp @@ -1,45 +1,46 @@ #pragma once #include +#include #include #include #include #include +#include +#include namespace llarp { - // Struct containing stats we know about a peer - struct PeerStats + /// Maintains a database of stats collected about the connections with our Service Node peers. + /// This uses a sqlite3 database behind the scenes as persistance, but this database is + /// periodically flushed to, meaning that it will become stale as PeerDb accumulates stats without + /// a flush. + struct PeerDb { - int32_t numConnectionAttempts = 0; - int32_t numConnectionSuccesses = 0; - int32_t numConnectionRejections = 0; - int32_t numConnectionTimeouts = 0; - - int32_t numPathBuilds = 0; - int64_t numPacketsAttempted = 0; - int64_t numPacketsSent = 0; - int64_t numPacketsDropped = 0; - int64_t numPacketsResent = 0; - - int64_t numDistinctRCsReceived = 0; - int64_t numLateRCs = 0; - - double peakBandwidthBytesPerSec = 0; - std::chrono::milliseconds longestRCReceiveInterval = 0ms; - std::chrono::milliseconds mostExpiredRC = 0ms; + /// Loads the database from disk using the provided filepath. If the file is equal to + /// `std::nullopt`, the database will be loaded into memory (useful for testing). + /// + /// This must be called prior to calling flushDatabase(), and will truncate any existing data. + /// + /// This is a blocking call, both in the sense that it blocks on disk/database I/O and that it + /// will sit on a mutex while the database is loaded. + /// + /// @param file is an optional file which doesn't have to exist but must be writable, if a value + /// is provided. If no value is provided, the database will be memory-backed. + /// @throws if sqlite_orm/sqlite3 is unable to open or create a database at the given file + void + loadDatabase(std::optional file); - PeerStats& - operator+=(const PeerStats& other); - bool - operator==(const PeerStats& other); - }; + /// Flushes the database. Must be called after loadDatabase(). This call will block during I/O + /// and should be called in an appropriate threading context. However, it will make a temporary + /// copy of the peer stats so as to avoid sitting on a mutex lock during disk I/O. + /// + /// @throws if the database could not be written to (esp. if loadDatabase() has not been called) + void + flushDatabase(); - /// Maintains a database of stats collected about the connections with our Service Node peers - struct PeerDb - { /// Add the given stats to the cummulative stats for the given peer. For cummulative stats, the /// stats are added together; for watermark stats, the max is kept. /// @@ -54,16 +55,18 @@ namespace llarp accumulatePeerStats(const RouterID& routerId, const PeerStats& delta); /// Provides a snapshot of the most recent PeerStats we have for the given peer. If we don't - /// have any stats for the peer, an empty PeerStats is returned. + /// have any stats for the peer, std::nullopt /// /// @param routerId is the RouterID of the requested peer /// @return a copy of the most recent peer stats or an empty one if no such peer is known - PeerStats + std::optional getCurrentPeerStats(const RouterID& routerId) const; private: std::unordered_map m_peerStats; std::mutex m_statsLock; + + std::unique_ptr m_storage; }; } // namespace llarp diff --git a/llarp/peerstats/types.cpp b/llarp/peerstats/types.cpp new file mode 100644 index 000000000..74f2d5668 --- /dev/null +++ b/llarp/peerstats/types.cpp @@ -0,0 +1,56 @@ +#include + +namespace llarp +{ + PeerStats::PeerStats(const RouterID& routerId) + { + routerIdHex = routerId.ToHex(); + } + + PeerStats& + PeerStats::operator+=(const PeerStats& other) + { + numConnectionAttempts += other.numConnectionAttempts; + numConnectionSuccesses += other.numConnectionSuccesses; + numConnectionRejections += other.numConnectionRejections; + numConnectionTimeouts += other.numConnectionTimeouts; + + numPathBuilds += other.numPathBuilds; + numPacketsAttempted += other.numPacketsAttempted; + numPacketsSent += other.numPacketsSent; + numPacketsDropped += other.numPacketsDropped; + numPacketsResent += other.numPacketsResent; + + numDistinctRCsReceived += other.numDistinctRCsReceived; + numLateRCs += other.numLateRCs; + + peakBandwidthBytesPerSec = std::max(peakBandwidthBytesPerSec, other.peakBandwidthBytesPerSec); + longestRCReceiveIntervalMs = + std::max(longestRCReceiveIntervalMs, other.longestRCReceiveIntervalMs); + mostExpiredRCMs = std::max(mostExpiredRCMs, other.mostExpiredRCMs); + + return *this; + } + + bool + PeerStats::operator==(const PeerStats& other) + { + return routerIdHex == other.routerIdHex and numConnectionAttempts == other.numConnectionAttempts + and numConnectionSuccesses == other.numConnectionSuccesses + and numConnectionRejections == other.numConnectionRejections + and numConnectionTimeouts == other.numConnectionTimeouts + + and numPathBuilds == other.numPathBuilds + and numPacketsAttempted == other.numPacketsAttempted + and numPacketsSent == other.numPacketsSent and numPacketsDropped == other.numPacketsDropped + and numPacketsResent == other.numPacketsResent + + and numDistinctRCsReceived == other.numDistinctRCsReceived + and numLateRCs == other.numLateRCs + + and peakBandwidthBytesPerSec == other.peakBandwidthBytesPerSec + and longestRCReceiveIntervalMs == other.longestRCReceiveIntervalMs + and mostExpiredRCMs == other.mostExpiredRCMs; + } + +}; // namespace llarp diff --git a/llarp/peerstats/types.hpp b/llarp/peerstats/types.hpp new file mode 100644 index 000000000..d17bedf37 --- /dev/null +++ b/llarp/peerstats/types.hpp @@ -0,0 +1,44 @@ +#pragma once + +#include +#include + +#include +#include + +/// Types stored in our peerstats database are declared here + +namespace llarp +{ + // Struct containing stats we know about a peer + struct PeerStats + { + std::string routerIdHex; + + int32_t numConnectionAttempts = 0; + int32_t numConnectionSuccesses = 0; + int32_t numConnectionRejections = 0; + int32_t numConnectionTimeouts = 0; + + int32_t numPathBuilds = 0; + int64_t numPacketsAttempted = 0; + int64_t numPacketsSent = 0; + int64_t numPacketsDropped = 0; + int64_t numPacketsResent = 0; + + int32_t numDistinctRCsReceived = 0; + int32_t numLateRCs = 0; + + double peakBandwidthBytesPerSec = 0; + int64_t longestRCReceiveIntervalMs = 0; + int64_t mostExpiredRCMs = 0; + + PeerStats(const RouterID& routerId); + + PeerStats& + operator+=(const PeerStats& other); + bool + operator==(const PeerStats& other); + }; + +} // namespace llarp diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a6239d941..b921350a5 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -68,7 +68,8 @@ add_executable(catchAll util/test_llarp_util_printer.cpp util/test_llarp_util_str.cpp util/test_llarp_util_decaying_hashset.cpp - peerstats/peer_db.cpp + peerstats/test_peer_db.cpp + peerstats/test_peer_types.cpp config/test_llarp_config_definition.cpp config/test_llarp_config_output.cpp net/test_ip_address.cpp diff --git a/test/peerstats/peer_db.cpp b/test/peerstats/peer_db.cpp deleted file mode 100644 index 66e5837ea..000000000 --- a/test/peerstats/peer_db.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include -#include - -#include - -TEST_CASE("Test PeerStats operator+=", "[PeerStats]") -{ - // TODO: test all members - llarp::PeerStats stats; - stats.numConnectionAttempts = 1; - stats.peakBandwidthBytesPerSec = 12; - - llarp::PeerStats delta; - delta.numConnectionAttempts = 2; - delta.peakBandwidthBytesPerSec = 4; - - stats += delta; - - CHECK(stats.numConnectionAttempts == 3); - CHECK(stats.peakBandwidthBytesPerSec == 12); // should take max(), not add -} - -TEST_CASE("Test PeerDb PeerStats memory storage", "[PeerDb]") -{ - const llarp::PeerStats empty = {}; - const llarp::RouterID id = {}; - - llarp::PeerDb db; - CHECK(db.getCurrentPeerStats(id) == empty); - - llarp::PeerStats delta; - delta.numConnectionAttempts = 4; - delta.peakBandwidthBytesPerSec = 5; - db.accumulatePeerStats(id, delta); - CHECK(db.getCurrentPeerStats(id) == delta); - - delta = {}; - delta.numConnectionAttempts = 5; - delta.peakBandwidthBytesPerSec = 6; - db.accumulatePeerStats(id, delta); - - llarp::PeerStats expected; - expected.numConnectionAttempts = 9; - expected.peakBandwidthBytesPerSec = 6; - CHECK(db.getCurrentPeerStats(id) == expected); -} diff --git a/test/peerstats/test_peer_db.cpp b/test/peerstats/test_peer_db.cpp new file mode 100644 index 000000000..cc3ba2d17 --- /dev/null +++ b/test/peerstats/test_peer_db.cpp @@ -0,0 +1,75 @@ +#include +#include + +#include + +TEST_CASE("Test PeerDb PeerStats memory storage", "[PeerDb]") +{ + const llarp::RouterID id = {}; + const llarp::PeerStats empty(id); + + llarp::PeerDb db; + CHECK(db.getCurrentPeerStats(id).has_value() == false); + + llarp::PeerStats delta(id); + delta.numConnectionAttempts = 4; + delta.peakBandwidthBytesPerSec = 5; + db.accumulatePeerStats(id, delta); + CHECK(db.getCurrentPeerStats(id).value() == delta); + + delta = llarp::PeerStats(id); + delta.numConnectionAttempts = 5; + delta.peakBandwidthBytesPerSec = 6; + db.accumulatePeerStats(id, delta); + + llarp::PeerStats expected(id); + expected.numConnectionAttempts = 9; + expected.peakBandwidthBytesPerSec = 6; + CHECK(db.getCurrentPeerStats(id).value() == expected); +} + +TEST_CASE("Test PeerDb flush before load", "[PeerDb]") +{ + llarp::PeerDb db; + CHECK_THROWS_WITH(db.flushDatabase(), "Cannot flush database before it has been loaded"); +} + +TEST_CASE("Test PeerDb load twice", "[PeerDb]") +{ + llarp::PeerDb db; + CHECK_NOTHROW(db.loadDatabase(std::nullopt)); + CHECK_THROWS_WITH(db.loadDatabase(std::nullopt), "Reloading database not supported"); +} + +TEST_CASE("Test PeerDb nukes stats on load", "[PeerDb]") +{ + const llarp::RouterID id = {}; + + llarp::PeerDb db; + + llarp::PeerStats stats(id); + stats.numConnectionAttempts = 1; + + db.accumulatePeerStats(id, stats); + CHECK(db.getCurrentPeerStats(id).value() == stats); + + db.loadDatabase(std::nullopt); + + CHECK(db.getCurrentPeerStats(id).has_value() == false); +} + +/* +TEST_CASE("Test file-backed database", "[PeerDb]") +{ + llarp::PeerDb db; + db.loadDatabase(std::nullopt); + + const llarp::RouterID id = {}; + llarp::PeerStats stats(id); + stats.numConnectionAttempts = 42; + + db.accumulatePeerStats(id, stats); + + db.flushDatabase(); +} +*/ diff --git a/test/peerstats/test_peer_types.cpp b/test/peerstats/test_peer_types.cpp new file mode 100644 index 000000000..d260f6d5a --- /dev/null +++ b/test/peerstats/test_peer_types.cpp @@ -0,0 +1,23 @@ +#include +#include + +#include + +TEST_CASE("Test PeerStats operator+=", "[PeerStats]") +{ + llarp::RouterID id = {}; + + // TODO: test all members + llarp::PeerStats stats(id); + stats.numConnectionAttempts = 1; + stats.peakBandwidthBytesPerSec = 12; + + llarp::PeerStats delta(id); + delta.numConnectionAttempts = 2; + delta.peakBandwidthBytesPerSec = 4; + + stats += delta; + + CHECK(stats.numConnectionAttempts == 3); + CHECK(stats.peakBandwidthBytesPerSec == 12); // should take max(), not add +} From 98171aad368e9c3e1c27ee5a0a53707a7a40da7b Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Wed, 20 May 2020 17:21:21 -0600 Subject: [PATCH 03/70] Add libsqlite3-dev to drone --- .drone.jsonnet | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.drone.jsonnet b/.drone.jsonnet index 07c8e44e5..8ec0bf8a2 100644 --- a/.drone.jsonnet +++ b/.drone.jsonnet @@ -1,4 +1,4 @@ -local default_deps_base='libsystemd-dev python3-dev libcurl4-openssl-dev libuv1-dev libunbound-dev nettle-dev libssl-dev libevent-dev'; +local default_deps_base='libsystemd-dev python3-dev libcurl4-openssl-dev libuv1-dev libunbound-dev nettle-dev libssl-dev libevent-dev' libsqlite3-dev; local default_deps_nocxx='libsodium-dev ' + default_deps_base; // libsodium-dev needs to be >= 1.0.18 local default_deps='g++ ' + default_deps_nocxx; // g++ sometimes needs replacement local default_windows_deps='mingw-w64-binutils mingw-w64-gcc mingw-w64-crt mingw-w64-headers mingw-w64-winpthreads perl openssh zip bash'; // deps for windows cross compile From a30806b3750ec1920a696889756463376c7dcba8 Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Thu, 21 May 2020 09:31:31 -0600 Subject: [PATCH 04/70] Some sqlite_orm related fixes --- llarp/peerstats/orm.hpp | 4 ++-- llarp/peerstats/peer_db.cpp | 5 +++-- test/peerstats/test_peer_db.cpp | 11 ++++++----- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/llarp/peerstats/orm.hpp b/llarp/peerstats/orm.hpp index 17fa39ae6..eadc18615 100644 --- a/llarp/peerstats/orm.hpp +++ b/llarp/peerstats/orm.hpp @@ -29,8 +29,8 @@ namespace llarp make_column("numDistinctRCsReceived", &PeerStats::numDistinctRCsReceived), make_column("numLateRCs", &PeerStats::numLateRCs), make_column("peakBandwidthBytesPerSec", &PeerStats::peakBandwidthBytesPerSec), - make_column("longestRCReceiveInterval", &PeerStats::longestRCReceiveIntervalMs), - make_column("mostExpiredRC", &PeerStats::mostExpiredRCMs))); + make_column("longestRCReceiveIntervalMs", &PeerStats::longestRCReceiveIntervalMs), + make_column("mostExpiredRCMs", &PeerStats::mostExpiredRCMs))); } using PeerDbStorage = decltype(initStorage("")); diff --git a/llarp/peerstats/peer_db.cpp b/llarp/peerstats/peer_db.cpp index 4ba18223a..7f008b331 100644 --- a/llarp/peerstats/peer_db.cpp +++ b/llarp/peerstats/peer_db.cpp @@ -22,6 +22,7 @@ namespace llarp fileString = file.value().native(); m_storage = std::make_unique(initStorage(fileString)); + m_storage->sync_schema(true); // true for "preserve" as in "don't nuke" (how cute!) } void @@ -37,13 +38,13 @@ namespace llarp copy = m_peerStats; // expensive deep copy } - for (const auto& entry : m_peerStats) + for (const auto& entry : copy) { // call me paranoid... assert(not entry.second.routerIdHex.empty()); assert(entry.first.ToHex() == entry.second.routerIdHex); - m_storage->insert(entry.second); + m_storage->replace(entry.second); } } diff --git a/test/peerstats/test_peer_db.cpp b/test/peerstats/test_peer_db.cpp index cc3ba2d17..42422c707 100644 --- a/test/peerstats/test_peer_db.cpp +++ b/test/peerstats/test_peer_db.cpp @@ -1,11 +1,12 @@ -#include #include +#include +#include #include TEST_CASE("Test PeerDb PeerStats memory storage", "[PeerDb]") { - const llarp::RouterID id = {}; + const llarp::RouterID id = llarp::test::makeBuf(0x01); const llarp::PeerStats empty(id); llarp::PeerDb db; @@ -43,7 +44,7 @@ TEST_CASE("Test PeerDb load twice", "[PeerDb]") TEST_CASE("Test PeerDb nukes stats on load", "[PeerDb]") { - const llarp::RouterID id = {}; + const llarp::RouterID id = llarp::test::makeBuf(0x01); llarp::PeerDb db; @@ -62,9 +63,9 @@ TEST_CASE("Test PeerDb nukes stats on load", "[PeerDb]") TEST_CASE("Test file-backed database", "[PeerDb]") { llarp::PeerDb db; - db.loadDatabase(std::nullopt); + db.loadDatabase("/tmp/peerdb_test_tmp.db.sqlite"); - const llarp::RouterID id = {}; + const llarp::RouterID id = llarp::test::makeBuf(0x01); llarp::PeerStats stats(id); stats.numConnectionAttempts = 42; From cc6e9c882a191b750319323f64ffa4062ac0e5b4 Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Thu, 21 May 2020 10:35:34 -0600 Subject: [PATCH 05/70] Use foo.snode as peerstats unique id, test file-backed db --- llarp/peerstats/orm.hpp | 2 +- llarp/peerstats/peer_db.cpp | 27 +++++++++++++++++++++++---- llarp/peerstats/types.cpp | 8 +++++--- llarp/peerstats/types.hpp | 3 ++- test/peerstats/test_peer_db.cpp | 33 +++++++++++++++++++++++---------- 5 files changed, 54 insertions(+), 19 deletions(-) diff --git a/llarp/peerstats/orm.hpp b/llarp/peerstats/orm.hpp index eadc18615..3cf3a3143 100644 --- a/llarp/peerstats/orm.hpp +++ b/llarp/peerstats/orm.hpp @@ -16,7 +16,7 @@ namespace llarp file, make_table( "peerstats", - make_column("routerId", &PeerStats::routerIdHex, primary_key(), unique()), + make_column("routerId", &PeerStats::routerId, primary_key(), unique()), make_column("numConnectionAttempts", &PeerStats::numConnectionAttempts), make_column("numConnectionSuccesses", &PeerStats::numConnectionSuccesses), make_column("numConnectionRejections", &PeerStats::numConnectionRejections), diff --git a/llarp/peerstats/peer_db.cpp b/llarp/peerstats/peer_db.cpp index 7f008b331..a7751e22c 100644 --- a/llarp/peerstats/peer_db.cpp +++ b/llarp/peerstats/peer_db.cpp @@ -19,10 +19,29 @@ namespace llarp // use if file is an empty-optional std::string fileString; if (file.has_value()) + { fileString = file.value().native(); + LogInfo("Loading PeerDb from file ", fileString); + } + else + { + LogInfo("Loading memory-backed PeerDb"); + } m_storage = std::make_unique(initStorage(fileString)); m_storage->sync_schema(true); // true for "preserve" as in "don't nuke" (how cute!) + + auto allStats = m_storage->get_all(); + LogInfo("Loading ", allStats.size(), " PeerStats from table peerstats..."); + for (const PeerStats& stats : allStats) + { + RouterID id; + if (not id.FromString(stats.routerId)) + throw std::runtime_error( + stringify("Database contains invalid PeerStats with id ", stats.routerId)); + + m_peerStats[id] = stats; + } } void @@ -41,8 +60,8 @@ namespace llarp for (const auto& entry : copy) { // call me paranoid... - assert(not entry.second.routerIdHex.empty()); - assert(entry.first.ToHex() == entry.second.routerIdHex); + assert(not entry.second.routerId.empty()); + assert(entry.first.ToString() == entry.second.routerId); m_storage->replace(entry.second); } @@ -51,9 +70,9 @@ namespace llarp void PeerDb::accumulatePeerStats(const RouterID& routerId, const PeerStats& delta) { - if (routerId.ToHex() != delta.routerIdHex) + if (routerId.ToString() != delta.routerId) throw std::invalid_argument( - stringify("routerId ", routerId, " doesn't match ", delta.routerIdHex)); + stringify("routerId ", routerId, " doesn't match ", delta.routerId)); std::lock_guard gaurd(m_statsLock); auto itr = m_peerStats.find(routerId); diff --git a/llarp/peerstats/types.cpp b/llarp/peerstats/types.cpp index 74f2d5668..bcb36573e 100644 --- a/llarp/peerstats/types.cpp +++ b/llarp/peerstats/types.cpp @@ -2,9 +2,11 @@ namespace llarp { - PeerStats::PeerStats(const RouterID& routerId) + PeerStats::PeerStats() = default; + + PeerStats::PeerStats(const RouterID& routerId_) { - routerIdHex = routerId.ToHex(); + routerId = routerId_.ToString(); } PeerStats& @@ -35,7 +37,7 @@ namespace llarp bool PeerStats::operator==(const PeerStats& other) { - return routerIdHex == other.routerIdHex and numConnectionAttempts == other.numConnectionAttempts + return routerId == other.routerId and numConnectionAttempts == other.numConnectionAttempts and numConnectionSuccesses == other.numConnectionSuccesses and numConnectionRejections == other.numConnectionRejections and numConnectionTimeouts == other.numConnectionTimeouts diff --git a/llarp/peerstats/types.hpp b/llarp/peerstats/types.hpp index d17bedf37..3b4c3783f 100644 --- a/llarp/peerstats/types.hpp +++ b/llarp/peerstats/types.hpp @@ -13,7 +13,7 @@ namespace llarp // Struct containing stats we know about a peer struct PeerStats { - std::string routerIdHex; + std::string routerId; int32_t numConnectionAttempts = 0; int32_t numConnectionSuccesses = 0; @@ -33,6 +33,7 @@ namespace llarp int64_t longestRCReceiveIntervalMs = 0; int64_t mostExpiredRCMs = 0; + PeerStats(); PeerStats(const RouterID& routerId); PeerStats& diff --git a/test/peerstats/test_peer_db.cpp b/test/peerstats/test_peer_db.cpp index 42422c707..1a07f6b3c 100644 --- a/test/peerstats/test_peer_db.cpp +++ b/test/peerstats/test_peer_db.cpp @@ -59,18 +59,31 @@ TEST_CASE("Test PeerDb nukes stats on load", "[PeerDb]") CHECK(db.getCurrentPeerStats(id).has_value() == false); } -/* -TEST_CASE("Test file-backed database", "[PeerDb]") +TEST_CASE("Test PeerDb file-backed database reloads properly", "[PeerDb]") { - llarp::PeerDb db; - db.loadDatabase("/tmp/peerdb_test_tmp.db.sqlite"); + const std::string filename = "/tmp/peerdb_test_tmp2.db.sqlite"; + const llarp::RouterID id = llarp::test::makeBuf(0x02); - const llarp::RouterID id = llarp::test::makeBuf(0x01); - llarp::PeerStats stats(id); - stats.numConnectionAttempts = 42; + { + llarp::PeerDb db; + db.loadDatabase(filename); - db.accumulatePeerStats(id, stats); + llarp::PeerStats stats(id); + stats.numConnectionAttempts = 43; + + db.accumulatePeerStats(id, stats); + + db.flushDatabase(); + } + + { + llarp::PeerDb db; + db.loadDatabase(filename); + + auto stats = db.getCurrentPeerStats(id); + CHECK(stats.has_value() == true); + CHECK(stats.value().numConnectionAttempts == 43); + } - db.flushDatabase(); + fs::remove(filename); } -*/ From 2a30e7dac2c53adc9cb81f61a0782f994277985a Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Tue, 26 May 2020 11:03:21 -0600 Subject: [PATCH 06/70] Add PeerDb to Router --- llarp/config/config.cpp | 42 +++++++++++++++++++++++++++++++++++++ llarp/config/config.hpp | 2 ++ llarp/peerstats/peer_db.cpp | 35 +++++++++++++++++++++++++++++++ llarp/peerstats/peer_db.hpp | 21 ++++++++++++++++++- llarp/router/router.cpp | 12 +++++++++++ llarp/router/router.hpp | 2 ++ 6 files changed, 113 insertions(+), 1 deletion(-) diff --git a/llarp/config/config.cpp b/llarp/config/config.cpp index b718de338..12bcee7ec 100644 --- a/llarp/config/config.cpp +++ b/llarp/config/config.cpp @@ -36,6 +36,7 @@ namespace llarp constexpr int DefaultWorkerThreads = 1; constexpr int DefaultNetThreads = 1; constexpr bool DefaultBlockBogons = true; + constexpr bool DefaultEnablePeerStats = false; conf.defineOption("router", "job-queue-size", false, DefaultJobQueueSize, [this](int arg) { if (arg < 1024) @@ -128,6 +129,13 @@ namespace llarp conf.defineOption( "router", "transport-privkey", false, "", AssignmentAcceptor(m_transportKeyFile)); + + conf.defineOption( + "router", + "enable-peer-stats", + false, + DefaultEnablePeerStats, + AssignmentAcceptor(m_enablePeerStats)); } void @@ -987,6 +995,40 @@ namespace llarp "File containing service node's seed.", }); + // extra [network] options + // TODO: probably better to create an [exit] section and only allow it for routers + def.addOptionComments( + "network", + "exit", + { + "Whether or not we should act as an exit node. Beware that this increases demand", + "on the server and may pose liability concerns. Enable at your own risk.", + }); + + // TODO: define the order of precedence (e.g. is whitelist applied before blacklist?) + // additionally, what's default? What if I don't whitelist anything? + def.addOptionComments( + "network", + "exit-whitelist", + { + "List of destination protocol:port pairs to whitelist, example: udp:*", + "or tcp:80. Multiple values supported.", + }); + + def.addOptionComments( + "network", + "exit-blacklist", + { + "Blacklist of destinations (same format as whitelist).", + }); + + def.addOptionComments( + "router", + "enable-peer-stats", + { + "Enable collection of SNode peer stats", + }); + return def.generateINIConfig(true); } diff --git a/llarp/config/config.hpp b/llarp/config/config.hpp index 1a2733a95..77d3e5de0 100644 --- a/llarp/config/config.hpp +++ b/llarp/config/config.hpp @@ -64,6 +64,8 @@ namespace llarp std::string m_identityKeyFile; std::string m_transportKeyFile; + bool m_enablePeerStats = false; + void defineConfigOptions(ConfigDefinition& conf, const ConfigGenParameters& params); }; diff --git a/llarp/peerstats/peer_db.cpp b/llarp/peerstats/peer_db.cpp index a7751e22c..a814e65e6 100644 --- a/llarp/peerstats/peer_db.cpp +++ b/llarp/peerstats/peer_db.cpp @@ -5,6 +5,11 @@ namespace llarp { + PeerDb::PeerDb() + { + m_lastFlush.store({}); + } + void PeerDb::loadDatabase(std::optional file) { @@ -47,6 +52,12 @@ namespace llarp void PeerDb::flushDatabase() { + LogDebug("flushing PeerDb..."); + + auto start = time_now_ms(); + if (not shouldFlush(start)) + LogWarn("Double PeerDb flush?"); + if (not m_storage) throw std::runtime_error("Cannot flush database before it has been loaded"); @@ -65,6 +76,13 @@ namespace llarp m_storage->replace(entry.second); } + + auto end = time_now_ms(); + + auto elapsed = end - start; + LogDebug("PeerDb flush took about ", elapsed, " millis"); + + m_lastFlush.store(end); } void @@ -93,4 +111,21 @@ namespace llarp return itr->second; } + void + PeerDb::configure(const RouterConfig& routerConfig) + { + if (not routerConfig.m_enablePeerStats) + throw std::runtime_error("[router]:enable-peer-stats is not enabled"); + + fs::path dbPath = routerConfig.m_dataDir / "peerstats.sqlite"; + + loadDatabase(dbPath); + } + + bool + PeerDb::shouldFlush(llarp_time_t now) + { + return (now - m_lastFlush.load() >= m_targetFlushInterval); + } + }; // namespace llarp diff --git a/llarp/peerstats/peer_db.hpp b/llarp/peerstats/peer_db.hpp index ea4ebd317..b8ba75efa 100644 --- a/llarp/peerstats/peer_db.hpp +++ b/llarp/peerstats/peer_db.hpp @@ -1,11 +1,11 @@ #pragma once -#include #include #include #include +#include #include #include #include @@ -19,6 +19,9 @@ namespace llarp /// a flush. struct PeerDb { + /// Constructor + PeerDb(); + /// Loads the database from disk using the provided filepath. If the file is equal to /// `std::nullopt`, the database will be loaded into memory (useful for testing). /// @@ -62,11 +65,27 @@ namespace llarp std::optional getCurrentPeerStats(const RouterID& routerId) const; + /// Configures the PeerDb based on RouterConfig + /// + /// @param routerConfig + void + configure(const RouterConfig& routerConfig); + + /// Returns whether or not we should flush, as determined by the last time we flushed and the + /// configured flush interval. + /// + /// @param now is the current[-ish] time + bool + shouldFlush(llarp_time_t now); + private: std::unordered_map m_peerStats; std::mutex m_statsLock; std::unique_ptr m_storage; + + llarp_time_t m_targetFlushInterval = 30s; + std::atomic m_lastFlush; }; } // namespace llarp diff --git a/llarp/router/router.cpp b/llarp/router/router.cpp index 7b8f0d336..1d77721d4 100644 --- a/llarp/router/router.cpp +++ b/llarp/router/router.cpp @@ -578,6 +578,14 @@ namespace llarp hiddenServiceContext().AddEndpoint(*conf); } + // peer stats + if (conf->router.m_enablePeerStats) + { + LogInfo("Initializing peerdb..."); + m_peerDb = std::make_unique(); + m_peerDb->configure(conf->router); + } + // Logging config LogContext::Instance().Initialize( conf->logging.m_logLevel, @@ -754,6 +762,10 @@ namespace llarp { nodedb()->AsyncFlushToDisk(); } + + if (m_peerDb and m_peerDb->shouldFlush(now)) + diskworker()->addJob([this]() { m_peerDb->flushDatabase(); }); + // get connected peers std::set peersWeHave; _linkManager.ForEachPeer([&peersWeHave](ILinkSession* s) { diff --git a/llarp/router/router.hpp b/llarp/router/router.hpp index 22dcb3514..3a3f16445 100644 --- a/llarp/router/router.hpp +++ b/llarp/router/router.hpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -495,6 +496,7 @@ namespace llarp llarp_time_t m_LastStatsReport = 0s; std::shared_ptr m_keyManager; + std::unique_ptr m_peerDb; uint32_t path_build_count = 0; From 4f4192e272c12b2808337aba702f137a1bff1d34 Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Tue, 26 May 2020 11:29:22 -0600 Subject: [PATCH 07/70] constexpr --- llarp/peerstats/peer_db.cpp | 3 ++- llarp/peerstats/peer_db.hpp | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/llarp/peerstats/peer_db.cpp b/llarp/peerstats/peer_db.cpp index a814e65e6..e81ea361e 100644 --- a/llarp/peerstats/peer_db.cpp +++ b/llarp/peerstats/peer_db.cpp @@ -125,7 +125,8 @@ namespace llarp bool PeerDb::shouldFlush(llarp_time_t now) { - return (now - m_lastFlush.load() >= m_targetFlushInterval); + static constexpr llarp_time_t TargetFlushInterval = 30s; + return (now - m_lastFlush.load() >= TargetFlushInterval); } }; // namespace llarp diff --git a/llarp/peerstats/peer_db.hpp b/llarp/peerstats/peer_db.hpp index b8ba75efa..fb3a8e5f1 100644 --- a/llarp/peerstats/peer_db.hpp +++ b/llarp/peerstats/peer_db.hpp @@ -84,7 +84,6 @@ namespace llarp std::unique_ptr m_storage; - llarp_time_t m_targetFlushInterval = 30s; std::atomic m_lastFlush; }; From 7109ddc951680c4175842637c09156dca070bd64 Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Tue, 26 May 2020 13:17:51 -0600 Subject: [PATCH 08/70] Add PeerDb::modifyPeerStats() --- llarp/peerstats/peer_db.cpp | 12 +++++++++++- llarp/peerstats/peer_db.hpp | 16 ++++++++++++++++ test/peerstats/test_peer_db.cpp | 24 ++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 1 deletion(-) diff --git a/llarp/peerstats/peer_db.cpp b/llarp/peerstats/peer_db.cpp index e81ea361e..77cb817a6 100644 --- a/llarp/peerstats/peer_db.cpp +++ b/llarp/peerstats/peer_db.cpp @@ -100,6 +100,16 @@ namespace llarp itr->second += delta; } + void + PeerDb::modifyPeerStats(const RouterID& routerId, std::function callback) + { + std::lock_guard gaurd(m_statsLock); + + PeerStats& stats = m_peerStats[routerId]; + stats.routerId = routerId.ToString(); + callback(stats); + } + std::optional PeerDb::getCurrentPeerStats(const RouterID& routerId) const { @@ -125,7 +135,7 @@ namespace llarp bool PeerDb::shouldFlush(llarp_time_t now) { - static constexpr llarp_time_t TargetFlushInterval = 30s; + constexpr llarp_time_t TargetFlushInterval = 30s; return (now - m_lastFlush.load() >= TargetFlushInterval); } diff --git a/llarp/peerstats/peer_db.hpp b/llarp/peerstats/peer_db.hpp index fb3a8e5f1..9b668d65b 100644 --- a/llarp/peerstats/peer_db.hpp +++ b/llarp/peerstats/peer_db.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -54,9 +55,24 @@ namespace llarp /// 3) Call accumulatePeerStats() with the stats /// 4) Reset the stats to 0 /// 5) + /// + /// @param routerId is the id of the router whose stats should be modified. + /// @param delta is the stats to add to the existing stats void accumulatePeerStats(const RouterID& routerId, const PeerStats& delta); + /// Allows write-access to the stats for a given peer while appropriate mutex lock is held. This + /// is an alternative means of incrementing peer stats that is suitable for one-off + /// modifications. + /// + /// Note that this holds m_statsLock during the callback invocation, so the callback should + /// return as quickly as possible. + /// + /// @param routerId is the id of the router whose stats should be modified. + /// @param callback is a function which will be called immediately with mutex held + void + modifyPeerStats(const RouterID& routerId, std::function callback); + /// Provides a snapshot of the most recent PeerStats we have for the given peer. If we don't /// have any stats for the peer, std::nullopt /// diff --git a/test/peerstats/test_peer_db.cpp b/test/peerstats/test_peer_db.cpp index 1a07f6b3c..18bc9ecd5 100644 --- a/test/peerstats/test_peer_db.cpp +++ b/test/peerstats/test_peer_db.cpp @@ -87,3 +87,27 @@ TEST_CASE("Test PeerDb file-backed database reloads properly", "[PeerDb]") fs::remove(filename); } + +TEST_CASE("Test PeerDb modifyPeerStats", "[PeerDb]") +{ + const llarp::RouterID id = llarp::test::makeBuf(0xF2); + + int numTimesCalled = 0; + + llarp::PeerDb db; + db.loadDatabase(std::nullopt); + + db.modifyPeerStats(id, [&](llarp::PeerStats& stats) { + numTimesCalled++; + + stats.numPathBuilds += 42; + }); + + db.flushDatabase(); + + CHECK(numTimesCalled == 1); + + auto stats = db.getCurrentPeerStats(id); + CHECK(stats.has_value()); + CHECK(stats.value().numPathBuilds == 42); +} From 595288e04659ddafa56f7ca18eb9c99e9d7b224e Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Tue, 26 May 2020 19:57:27 -0600 Subject: [PATCH 09/70] Add PeerDb::handleGossipedRC --- llarp/dht/messages/gotrouter.cpp | 4 ++++ llarp/peerstats/peer_db.cpp | 25 +++++++++++++++++++ llarp/peerstats/peer_db.hpp | 10 ++++++++ llarp/peerstats/types.hpp | 1 + llarp/router/abstractrouter.hpp | 4 ++++ llarp/router/router.cpp | 2 +- llarp/router/router.hpp | 8 ++++++- test/peerstats/test_peer_db.cpp | 41 ++++++++++++++++++++++++++++++++ 8 files changed, 93 insertions(+), 2 deletions(-) diff --git a/llarp/dht/messages/gotrouter.cpp b/llarp/dht/messages/gotrouter.cpp index fef4666ca..17ea95bfb 100644 --- a/llarp/dht/messages/gotrouter.cpp +++ b/llarp/dht/messages/gotrouter.cpp @@ -127,6 +127,10 @@ namespace llarp auto* router = dht.GetRouter(); router->NotifyRouterEvent(router->pubkey(), rc); router->GossipRCIfNeeded(rc); + + auto peerDb = router->peerDb(); + if (peerDb) + peerDb->handleGossipedRC(rc); } } return true; diff --git a/llarp/peerstats/peer_db.cpp b/llarp/peerstats/peer_db.cpp index 77cb817a6..9f1966ce8 100644 --- a/llarp/peerstats/peer_db.cpp +++ b/llarp/peerstats/peer_db.cpp @@ -121,6 +121,31 @@ namespace llarp return itr->second; } + void + PeerDb::handleGossipedRC(const RouterContact& rc, llarp_time_t now) + { + std::lock_guard gaurd(m_statsLock); + + RouterID id(rc.pubkey); + auto& stats = m_peerStats[id]; + + if (stats.lastRCUpdated < rc.last_updated.count()) + { + // we track max expiry as the delta between (time received - last expiration time), + // and this value will often be negative for a healthy router + // TODO: handle case where new RC is also expired? just ignore? + int64_t expiry = (now.count() - (stats.lastRCUpdated + RouterContact::Lifetime.count())); + + if (stats.numDistinctRCsReceived == 0) + stats.mostExpiredRCMs = expiry; + else + stats.mostExpiredRCMs = std::max(stats.mostExpiredRCMs, expiry); + + stats.numDistinctRCsReceived++; + stats.lastRCUpdated = rc.last_updated.count(); + } + } + void PeerDb::configure(const RouterConfig& routerConfig) { diff --git a/llarp/peerstats/peer_db.hpp b/llarp/peerstats/peer_db.hpp index 9b668d65b..3280447ce 100644 --- a/llarp/peerstats/peer_db.hpp +++ b/llarp/peerstats/peer_db.hpp @@ -81,6 +81,16 @@ namespace llarp std::optional getCurrentPeerStats(const RouterID& routerId) const; + /// Handles a new gossiped RC, updating stats as needed. The database tracks the last + /// advertised update time, so it knows whether this is a new RC or not. + /// + /// The given RC is assumed to be valid. + /// + /// @param rc is the RouterContact to handle + /// @param now is an optional time representing the current time + void + handleGossipedRC(const RouterContact& rc, llarp_time_t now = time_now_ms()); + /// Configures the PeerDb based on RouterConfig /// /// @param routerConfig diff --git a/llarp/peerstats/types.hpp b/llarp/peerstats/types.hpp index 3b4c3783f..ffab7afdc 100644 --- a/llarp/peerstats/types.hpp +++ b/llarp/peerstats/types.hpp @@ -32,6 +32,7 @@ namespace llarp double peakBandwidthBytesPerSec = 0; int64_t longestRCReceiveIntervalMs = 0; int64_t mostExpiredRCMs = 0; + int64_t lastRCUpdated = 0; PeerStats(); PeerStats(const RouterID& routerId); diff --git a/llarp/router/abstractrouter.hpp b/llarp/router/abstractrouter.hpp index 90ae1e952..f53c2917a 100644 --- a/llarp/router/abstractrouter.hpp +++ b/llarp/router/abstractrouter.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #ifdef LOKINET_HIVE #include "tooling/router_hive.hpp" @@ -151,6 +152,9 @@ namespace llarp virtual I_RCLookupHandler& rcLookupHandler() = 0; + virtual std::shared_ptr + peerDb() = 0; + virtual bool Sign(Signature& sig, const llarp_buffer_t& buf) const = 0; diff --git a/llarp/router/router.cpp b/llarp/router/router.cpp index 1d77721d4..1b040d7c1 100644 --- a/llarp/router/router.cpp +++ b/llarp/router/router.cpp @@ -582,7 +582,7 @@ namespace llarp if (conf->router.m_enablePeerStats) { LogInfo("Initializing peerdb..."); - m_peerDb = std::make_unique(); + m_peerDb = std::make_shared(); m_peerDb->configure(conf->router); } diff --git a/llarp/router/router.hpp b/llarp/router/router.hpp index 3a3f16445..275679ca0 100644 --- a/llarp/router/router.hpp +++ b/llarp/router/router.hpp @@ -307,6 +307,12 @@ namespace llarp return _rcLookupHandler; } + std::shared_ptr + peerDb() override + { + return m_peerDb; + } + void GossipRCIfNeeded(const RouterContact rc) override; @@ -496,7 +502,7 @@ namespace llarp llarp_time_t m_LastStatsReport = 0s; std::shared_ptr m_keyManager; - std::unique_ptr m_peerDb; + std::shared_ptr m_peerDb; uint32_t path_build_count = 0; diff --git a/test/peerstats/test_peer_db.cpp b/test/peerstats/test_peer_db.cpp index 18bc9ecd5..bab8c5cbb 100644 --- a/test/peerstats/test_peer_db.cpp +++ b/test/peerstats/test_peer_db.cpp @@ -3,6 +3,8 @@ #include #include +#include "router_contact.hpp" +#include "util/time.hpp" TEST_CASE("Test PeerDb PeerStats memory storage", "[PeerDb]") { @@ -111,3 +113,42 @@ TEST_CASE("Test PeerDb modifyPeerStats", "[PeerDb]") CHECK(stats.has_value()); CHECK(stats.value().numPathBuilds == 42); } + +TEST_CASE("Test PeerDb handleGossipedRC", "[PeerDb]") +{ + const llarp::RouterID id = llarp::test::makeBuf(0xCA); + + auto rcLifetime = llarp::RouterContact::Lifetime; + llarp_time_t now = 0s; + + llarp::RouterContact rc; + rc.pubkey = llarp::PubKey(id); + rc.last_updated = 10s; + + llarp::PeerDb db; + db.handleGossipedRC(rc, now); + + auto stats = db.getCurrentPeerStats(id); + CHECK(stats.has_value()); + CHECK(stats.value().mostExpiredRCMs == (0s - rcLifetime).count()); + CHECK(stats.value().numDistinctRCsReceived == 1); + CHECK(stats.value().lastRCUpdated == 10000); + + now = 9s; + db.handleGossipedRC(rc, now); + stats = db.getCurrentPeerStats(id); + CHECK(stats.has_value()); + // these values should remain unchanged, this is not a new RC + CHECK(stats.value().mostExpiredRCMs == (0s - rcLifetime).count()); + CHECK(stats.value().numDistinctRCsReceived == 1); + CHECK(stats.value().lastRCUpdated == 10000); + + rc.last_updated = 11s; + + db.handleGossipedRC(rc, now); + stats = db.getCurrentPeerStats(id); + // new RC received at 9sec, making it (expiration time - 9 sec) expired (a negative number) + CHECK(stats.value().mostExpiredRCMs == (9s - (now + rcLifetime)).count()); + CHECK(stats.value().numDistinctRCsReceived == 2); + CHECK(stats.value().lastRCUpdated == 11000); +} From 5e05defc7648ef229e3b93c123f2b528800717b7 Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Wed, 27 May 2020 15:00:09 -0600 Subject: [PATCH 10/70] Add API query for peer stats, other related fixes --- llarp/peerstats/peer_db.cpp | 52 +++++++++++++++++++++++++++------ llarp/peerstats/peer_db.hpp | 6 ++++ llarp/peerstats/types.cpp | 26 ++++++++++++++++- llarp/peerstats/types.hpp | 4 +++ llarp/router/router.cpp | 21 ++++++++----- test/peerstats/test_peer_db.cpp | 4 +-- 6 files changed, 94 insertions(+), 19 deletions(-) diff --git a/llarp/peerstats/peer_db.cpp b/llarp/peerstats/peer_db.cpp index 9f1966ce8..220953123 100644 --- a/llarp/peerstats/peer_db.cpp +++ b/llarp/peerstats/peer_db.cpp @@ -1,6 +1,7 @@ #include #include +#include #include namespace llarp @@ -80,7 +81,7 @@ namespace llarp auto end = time_now_ms(); auto elapsed = end - start; - LogDebug("PeerDb flush took about ", elapsed, " millis"); + LogInfo("PeerDb flush took about ", elapsed, " millis"); m_lastFlush.store(end); } @@ -128,19 +129,24 @@ namespace llarp RouterID id(rc.pubkey); auto& stats = m_peerStats[id]; + stats.routerId = id.ToString(); if (stats.lastRCUpdated < rc.last_updated.count()) { - // we track max expiry as the delta between (time received - last expiration time), - // and this value will often be negative for a healthy router - // TODO: handle case where new RC is also expired? just ignore? - int64_t expiry = (now.count() - (stats.lastRCUpdated + RouterContact::Lifetime.count())); - - if (stats.numDistinctRCsReceived == 0) - stats.mostExpiredRCMs = expiry; - else + if (stats.numDistinctRCsReceived > 0) + { + // we track max expiry as the delta between (time received - last expiration time), + // and this value will often be negative for a healthy router + // TODO: handle case where new RC is also expired? just ignore? + int64_t expiry = (now.count() - (stats.lastRCUpdated + RouterContact::Lifetime.count())); stats.mostExpiredRCMs = std::max(stats.mostExpiredRCMs, expiry); + if (stats.numDistinctRCsReceived == 1) + stats.mostExpiredRCMs = expiry; + else + stats.mostExpiredRCMs = std::max(stats.mostExpiredRCMs, expiry); + } + stats.numDistinctRCsReceived++; stats.lastRCUpdated = rc.last_updated.count(); } @@ -164,4 +170,32 @@ namespace llarp return (now - m_lastFlush.load() >= TargetFlushInterval); } + util::StatusObject + PeerDb::ExtractStatus() const + { + std::lock_guard gaurd(m_statsLock); + + bool loaded = (m_storage.get() != nullptr); + util::StatusObject dbFile = nullptr; + if (loaded) + dbFile = m_storage->filename(); + + std::vector statsObjs; + statsObjs.reserve(m_peerStats.size()); + LogInfo("Building peer stats..."); + for (const auto& pair : m_peerStats) + { + LogInfo("Stat here"); + statsObjs.push_back(pair.second.toJson()); + } + + util::StatusObject obj{ + {"dbLoaded", loaded}, + {"dbFile", dbFile}, + {"lastFlushMs", m_lastFlush.load().count()}, + {"stats", statsObjs}, + }; + return obj; + } + }; // namespace llarp diff --git a/llarp/peerstats/peer_db.hpp b/llarp/peerstats/peer_db.hpp index 3280447ce..6f0c2e98f 100644 --- a/llarp/peerstats/peer_db.hpp +++ b/llarp/peerstats/peer_db.hpp @@ -104,6 +104,12 @@ namespace llarp bool shouldFlush(llarp_time_t now); + /// Get JSON status for API + /// + /// @return JSON object representing our current status + util::StatusObject + ExtractStatus() const; + private: std::unordered_map m_peerStats; std::mutex m_statsLock; diff --git a/llarp/peerstats/types.cpp b/llarp/peerstats/types.cpp index bcb36573e..f3cd5368c 100644 --- a/llarp/peerstats/types.cpp +++ b/llarp/peerstats/types.cpp @@ -30,6 +30,7 @@ namespace llarp longestRCReceiveIntervalMs = std::max(longestRCReceiveIntervalMs, other.longestRCReceiveIntervalMs); mostExpiredRCMs = std::max(mostExpiredRCMs, other.mostExpiredRCMs); + lastRCUpdated = std::max(lastRCUpdated, other.lastRCUpdated); return *this; } @@ -52,7 +53,30 @@ namespace llarp and peakBandwidthBytesPerSec == other.peakBandwidthBytesPerSec and longestRCReceiveIntervalMs == other.longestRCReceiveIntervalMs - and mostExpiredRCMs == other.mostExpiredRCMs; + and mostExpiredRCMs == other.mostExpiredRCMs and lastRCUpdated == other.lastRCUpdated; + } + + util::StatusObject + PeerStats::toJson() const + { + return { + {"routerId", routerId}, + // {"numConnectionAttempts", numConnectionAttempts}, + // {"numConnectionSuccesses", numConnectionSuccesses}, + // {"numConnectionRejections", numConnectionRejections}, + // {"numConnectionTimeouts", numConnectionTimeouts}, + // {"numPathBuilds", numPathBuilds}, + // {"numPacketsAttempted", numPacketsAttempted}, + // {"numPacketsSent", numPacketsSent}, + // {"numPacketsDropped", numPacketsDropped}, + // {"numPacketsResent", numPacketsResent}, + {"numDistinctRCsReceived", numDistinctRCsReceived}, + {"numLateRCs", numLateRCs}, + // {"peakBandwidthBytesPerSec", peakBandwidthBytesPerSec}, + {"longestRCReceiveIntervalMs", longestRCReceiveIntervalMs}, + {"mostExpiredRCMs", mostExpiredRCMs}, + {"lastRCUpdated", lastRCUpdated}, + }; } }; // namespace llarp diff --git a/llarp/peerstats/types.hpp b/llarp/peerstats/types.hpp index ffab7afdc..7db98a40d 100644 --- a/llarp/peerstats/types.hpp +++ b/llarp/peerstats/types.hpp @@ -4,6 +4,7 @@ #include #include +#include #include /// Types stored in our peerstats database are declared here @@ -41,6 +42,9 @@ namespace llarp operator+=(const PeerStats& other); bool operator==(const PeerStats& other); + + util::StatusObject + toJson() const; }; } // namespace llarp diff --git a/llarp/router/router.cpp b/llarp/router/router.cpp index 1b040d7c1..0dea08ae7 100644 --- a/llarp/router/router.cpp +++ b/llarp/router/router.cpp @@ -23,6 +23,7 @@ #include #include "tooling/router_event.hpp" +#include "util/status.hpp" #include #include @@ -80,13 +81,19 @@ namespace llarp { if (_running) { - return util::StatusObject{{"running", true}, - {"numNodesKnown", _nodedb->num_loaded()}, - {"dht", _dht->impl->ExtractStatus()}, - {"services", _hiddenServiceContext.ExtractStatus()}, - {"exit", _exitContext.ExtractStatus()}, - {"links", _linkManager.ExtractStatus()}, - {"outboundMessages", _outboundMessageHandler.ExtractStatus()}}; + util::StatusObject peerStatsObj = nullptr; + if (m_peerDb) + peerStatsObj = m_peerDb->ExtractStatus(); + + return util::StatusObject{ + {"running", true}, + {"numNodesKnown", _nodedb->num_loaded()}, + {"dht", _dht->impl->ExtractStatus()}, + {"services", _hiddenServiceContext.ExtractStatus()}, + {"exit", _exitContext.ExtractStatus()}, + {"links", _linkManager.ExtractStatus()}, + {"outboundMessages", _outboundMessageHandler.ExtractStatus()}, + {"peerStats", peerStatsObj}}; } else { diff --git a/test/peerstats/test_peer_db.cpp b/test/peerstats/test_peer_db.cpp index bab8c5cbb..d6d2f5918 100644 --- a/test/peerstats/test_peer_db.cpp +++ b/test/peerstats/test_peer_db.cpp @@ -130,7 +130,7 @@ TEST_CASE("Test PeerDb handleGossipedRC", "[PeerDb]") auto stats = db.getCurrentPeerStats(id); CHECK(stats.has_value()); - CHECK(stats.value().mostExpiredRCMs == (0s - rcLifetime).count()); + CHECK(stats.value().mostExpiredRCMs == 0); // not calculated on first received RC CHECK(stats.value().numDistinctRCsReceived == 1); CHECK(stats.value().lastRCUpdated == 10000); @@ -139,7 +139,7 @@ TEST_CASE("Test PeerDb handleGossipedRC", "[PeerDb]") stats = db.getCurrentPeerStats(id); CHECK(stats.has_value()); // these values should remain unchanged, this is not a new RC - CHECK(stats.value().mostExpiredRCMs == (0s - rcLifetime).count()); + CHECK(stats.value().mostExpiredRCMs == 0); CHECK(stats.value().numDistinctRCsReceived == 1); CHECK(stats.value().lastRCUpdated == 10000); From 4b4284ccf43f4af2bbf783506f17c21b48f20865 Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Thu, 28 May 2020 10:03:07 -0600 Subject: [PATCH 11/70] PeerDb sqlite optimizations --- llarp/peerstats/peer_db.cpp | 44 ++++++++++++++++++++++++++++--------- llarp/peerstats/types.hpp | 3 +++ 2 files changed, 37 insertions(+), 10 deletions(-) diff --git a/llarp/peerstats/peer_db.cpp b/llarp/peerstats/peer_db.cpp index 220953123..f1f62ee9c 100644 --- a/llarp/peerstats/peer_db.cpp +++ b/llarp/peerstats/peer_db.cpp @@ -39,13 +39,14 @@ namespace llarp auto allStats = m_storage->get_all(); LogInfo("Loading ", allStats.size(), " PeerStats from table peerstats..."); - for (const PeerStats& stats : allStats) + for (PeerStats& stats : allStats) { RouterID id; if (not id.FromString(stats.routerId)) throw std::runtime_error( stringify("Database contains invalid PeerStats with id ", stats.routerId)); + stats.stale = false; m_peerStats[id] = stats; } } @@ -57,31 +58,50 @@ namespace llarp auto start = time_now_ms(); if (not shouldFlush(start)) - LogWarn("Double PeerDb flush?"); + { + LogWarn("Call to flushDatabase() while already in progress, ignoring"); + return; + } if (not m_storage) throw std::runtime_error("Cannot flush database before it has been loaded"); - decltype(m_peerStats) copy; + std::vector staleStats; { std::lock_guard gaurd(m_statsLock); - copy = m_peerStats; // expensive deep copy + + // copy all stale entries + for (auto& entry : m_peerStats) + { + if (entry.second.stale) + { + staleStats.push_back(entry.second); + entry.second.stale = false; + } + } } - for (const auto& entry : copy) + LogInfo("Updating ", staleStats.size(), " stats"); + { - // call me paranoid... - assert(not entry.second.routerId.empty()); - assert(entry.first.ToString() == entry.second.routerId); + auto guard = m_storage->transaction_guard(); - m_storage->replace(entry.second); + for (const auto& stats : staleStats) + { + // call me paranoid... + assert(not stats.routerId.empty()); + + m_storage->replace(stats); + } + + guard.commit(); } auto end = time_now_ms(); auto elapsed = end - start; - LogInfo("PeerDb flush took about ", elapsed, " millis"); + LogInfo("PeerDb flush took about ", elapsed, " seconds"); m_lastFlush.store(end); } @@ -99,6 +119,8 @@ namespace llarp itr = m_peerStats.insert({routerId, delta}).first; else itr->second += delta; + + itr->second.stale = true; } void @@ -108,6 +130,7 @@ namespace llarp PeerStats& stats = m_peerStats[routerId]; stats.routerId = routerId.ToString(); + stats.stale = true; callback(stats); } @@ -149,6 +172,7 @@ namespace llarp stats.numDistinctRCsReceived++; stats.lastRCUpdated = rc.last_updated.count(); + stats.stale = true; } } diff --git a/llarp/peerstats/types.hpp b/llarp/peerstats/types.hpp index 7db98a40d..c7987c6e8 100644 --- a/llarp/peerstats/types.hpp +++ b/llarp/peerstats/types.hpp @@ -35,6 +35,9 @@ namespace llarp int64_t mostExpiredRCMs = 0; int64_t lastRCUpdated = 0; + // not serialized + bool stale = true; + PeerStats(); PeerStats(const RouterID& routerId); From aa1c8f257f5bc5e045413d7213adf47f1d4fec9b Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Fri, 29 May 2020 12:03:57 -0600 Subject: [PATCH 12/70] Sort out peerstats receive <-> expiry windows --- llarp/peerstats/peer_db.cpp | 73 ++++++++++++++++++++++++++++----- test/peerstats/test_peer_db.cpp | 67 +++++++++++++++++++++++++++++- 2 files changed, 127 insertions(+), 13 deletions(-) diff --git a/llarp/peerstats/peer_db.cpp b/llarp/peerstats/peer_db.cpp index f1f62ee9c..8b3cb26ab 100644 --- a/llarp/peerstats/peer_db.cpp +++ b/llarp/peerstats/peer_db.cpp @@ -145,32 +145,85 @@ namespace llarp return itr->second; } + /// Assume we receive an RC at some point `R` in time which was signed at some point `S` in time + /// and expires at some point `E` in time, as depicted below: + /// + /// +-----------------------------+ + /// | signed rc | <- useful lifetime of RC + /// +-----------------------------+ + /// ^ [ . . . . . . . . ] <----------- window in which we receive this RC gossiped to us + /// | ^ ^ + /// | | | + /// S R E + /// + /// One useful metric from this is the difference between (E - R), the useful contact time of this + /// RC. As we track this metric over time, the high and low watermarks serve to tell us how + /// quickly we receive signed RCs from a given router and how close to expiration they are when + /// we receive them. The latter is particularly useful, and should always be a positive number for + /// a healthy router. A negative number indicates that we are receiving an expired RC. + /// + /// TODO: we actually discard expired RCs, so we currently would not detect a negative value for + /// (E - R) + /// + /// Another related metric is the distance between a newly received RC and the previous RC's + /// expiration, which represents how close we came to having no useful RC to work with. This + /// should be a high (positive) number for a healthy router, and if negative indicates that we + /// had no way to contact this router for a period of time. + /// + /// E1 E2 E3 + /// | | | + /// v | | + /// +-----------------------------+ | | + /// | signed rc 1 | | | + /// +-----------------------------+ | | + /// [ . . . . . ] v | + /// ^ +-----------------------------+ | + /// | | signed rc 2 | | + /// | +-----------------------------+ | + /// | [ . . . . . . . . . . ] v + /// | ^ +-----------------------------+ + /// | | | signed rc 3 | + /// | | +-----------------------------+ + /// | | [ . . ] + /// | | ^ + /// | | | + /// R1 R2 R3 + /// + /// Example: the delta between (E1 - R2) is healthy, but the delta between (E2 - R3) is indicates + /// that we had a brief period of time where we had no valid (non-expired) RC for this router + /// (because it is negative). void PeerDb::handleGossipedRC(const RouterContact& rc, llarp_time_t now) { std::lock_guard gaurd(m_statsLock); + LogWarn("Handling gossiped RC", rc); + RouterID id(rc.pubkey); auto& stats = m_peerStats[id]; stats.routerId = id.ToString(); - if (stats.lastRCUpdated < rc.last_updated.count()) + const bool isNewRC = (stats.lastRCUpdated < rc.last_updated.count()); + + if (isNewRC) { - if (stats.numDistinctRCsReceived > 0) + stats.numDistinctRCsReceived++; + + if (stats.numDistinctRCsReceived > 1) { - // we track max expiry as the delta between (time received - last expiration time), - // and this value will often be negative for a healthy router + const int64_t prevRCExpiration = (stats.lastRCUpdated + RouterContact::Lifetime.count()); + + // we track max expiry as the delta between (last expiration time - time received), + // and this value will be negative for an unhealthy router // TODO: handle case where new RC is also expired? just ignore? - int64_t expiry = (now.count() - (stats.lastRCUpdated + RouterContact::Lifetime.count())); - stats.mostExpiredRCMs = std::max(stats.mostExpiredRCMs, expiry); + int64_t expiry = (prevRCExpiration - now.count()); - if (stats.numDistinctRCsReceived == 1) + if (stats.numDistinctRCsReceived == 2) stats.mostExpiredRCMs = expiry; else - stats.mostExpiredRCMs = std::max(stats.mostExpiredRCMs, expiry); + stats.mostExpiredRCMs = std::min(stats.mostExpiredRCMs, expiry); } - stats.numDistinctRCsReceived++; stats.lastRCUpdated = rc.last_updated.count(); stats.stale = true; } @@ -206,10 +259,8 @@ namespace llarp std::vector statsObjs; statsObjs.reserve(m_peerStats.size()); - LogInfo("Building peer stats..."); for (const auto& pair : m_peerStats) { - LogInfo("Stat here"); statsObjs.push_back(pair.second.toJson()); } diff --git a/test/peerstats/test_peer_db.cpp b/test/peerstats/test_peer_db.cpp index d6d2f5918..414b49fda 100644 --- a/test/peerstats/test_peer_db.cpp +++ b/test/peerstats/test_peer_db.cpp @@ -3,6 +3,7 @@ #include #include +#include "peerstats/types.hpp" #include "router_contact.hpp" #include "util/time.hpp" @@ -147,8 +148,70 @@ TEST_CASE("Test PeerDb handleGossipedRC", "[PeerDb]") db.handleGossipedRC(rc, now); stats = db.getCurrentPeerStats(id); - // new RC received at 9sec, making it (expiration time - 9 sec) expired (a negative number) - CHECK(stats.value().mostExpiredRCMs == (9s - (now + rcLifetime)).count()); + // should be (previous expiration time - new received time) + CHECK(stats.value().mostExpiredRCMs == ((10s + rcLifetime) - now).count()); CHECK(stats.value().numDistinctRCsReceived == 2); CHECK(stats.value().lastRCUpdated == 11000); } + +TEST_CASE("Test PeerDb handleGossipedRC expiry calcs", "[PeerDb]") +{ + const llarp::RouterID id = llarp::test::makeBuf(0xF9); + + // see comments in peer_db.cpp above PeerDb::handleGossipedRC() for some context around these + // tests and esp. these numbers + const llarp_time_t ref = 48h; + const llarp_time_t rcLifetime = llarp::RouterContact::Lifetime; + + // rc1, first rc received + const llarp_time_t s1 = ref; + const llarp_time_t r1 = s1 + 30s; + const llarp_time_t e1 = s1 + rcLifetime; + llarp::RouterContact rc1; + rc1.pubkey = llarp::PubKey(id); + rc1.last_updated = s1; + + // rc2, second rc received + // received "healthily", with lots of room to spare before rc1 expires + const llarp_time_t s2 = s1 + 8h; + const llarp_time_t r2 = s2 + 30s; // healthy recv time + const llarp_time_t e2 = s2 + rcLifetime; + llarp::RouterContact rc2; + rc2.pubkey = llarp::PubKey(id); + rc2.last_updated = s2; + + // rc3, third rc received + // received "unhealthily" (after rc2 expires) + const llarp_time_t s3 = s2 + 8h; + const llarp_time_t r3 = e2 + 1h; // received after e2 + const llarp_time_t e3 = s3 + rcLifetime; + llarp::RouterContact rc3; + rc3.pubkey = llarp::PubKey(id); + rc3.last_updated = s3; + + llarp::PeerDb db; + + db.handleGossipedRC(rc1, r1); + auto stats1 = db.getCurrentPeerStats(id); + CHECK(stats1.has_value()); + CHECK(stats1.value().mostExpiredRCMs == 0); + CHECK(stats1.value().numDistinctRCsReceived == 1); + CHECK(stats1.value().lastRCUpdated == s1.count()); + + db.handleGossipedRC(rc2, r2); + auto stats2 = db.getCurrentPeerStats(id); + CHECK(stats2.has_value()); + CHECK(stats2.value().mostExpiredRCMs == (e1 - r2).count()); + CHECK(stats2.value().mostExpiredRCMs > 0); // ensure positive indicates healthy + CHECK(stats2.value().numDistinctRCsReceived == 2); + CHECK(stats2.value().lastRCUpdated == s2.count()); + + db.handleGossipedRC(rc3, r3); + auto stats3 = db.getCurrentPeerStats(id); + CHECK(stats3.has_value()); + CHECK(stats3.value().mostExpiredRCMs == (e2 - r3).count()); + CHECK( + stats3.value().mostExpiredRCMs < 0); // ensure negative indicates unhealthy and we use min() + CHECK(stats3.value().numDistinctRCsReceived == 3); + CHECK(stats3.value().lastRCUpdated == s3.count()); +} From a9ce319e7697422ed17ae042789ede03cb764b8d Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Mon, 1 Jun 2020 09:26:31 -0600 Subject: [PATCH 13/70] Make llarp_time_t serializable in sqlite_orm --- llarp/peerstats/orm.hpp | 55 +++++++++++++++++++++++++++++++-- llarp/peerstats/peer_db.cpp | 12 +++---- llarp/peerstats/types.cpp | 16 +++++----- llarp/peerstats/types.hpp | 6 ++-- test/peerstats/test_peer_db.cpp | 29 ++++++++--------- 5 files changed, 85 insertions(+), 33 deletions(-) diff --git a/llarp/peerstats/orm.hpp b/llarp/peerstats/orm.hpp index 3cf3a3143..ed2d6b07a 100644 --- a/llarp/peerstats/orm.hpp +++ b/llarp/peerstats/orm.hpp @@ -29,10 +29,61 @@ namespace llarp make_column("numDistinctRCsReceived", &PeerStats::numDistinctRCsReceived), make_column("numLateRCs", &PeerStats::numLateRCs), make_column("peakBandwidthBytesPerSec", &PeerStats::peakBandwidthBytesPerSec), - make_column("longestRCReceiveIntervalMs", &PeerStats::longestRCReceiveIntervalMs), - make_column("mostExpiredRCMs", &PeerStats::mostExpiredRCMs))); + make_column("longestRCReceiveInterval", &PeerStats::longestRCReceiveInterval), + make_column("leastRCRemainingLifetime", &PeerStats::leastRCRemainingLifetime))); } using PeerDbStorage = decltype(initStorage("")); } // namespace llarp + +/// "custom" types for sqlite_orm +/// reference: https://github.com/fnc12/sqlite_orm/blob/master/examples/enum_binding.cpp +namespace sqlite_orm +{ + template <> + struct type_printer : public integer_printer + { + }; + + template <> + struct statement_binder + { + int + bind(sqlite3_stmt* stmt, int index, const llarp_time_t& value) + { + return statement_binder().bind(stmt, index, value.count()); + } + }; + + template <> + struct field_printer + { + std::string + operator()(const llarp_time_t& value) const + { + std::stringstream stream; + stream << value.count(); + return stream.str(); + } + }; + + template <> + struct row_extractor + { + llarp_time_t + extract(const char* row_value) + { + int64_t raw = static_cast(atoi(row_value)); + return llarp_time_t(raw); + } + + llarp_time_t + extract(sqlite3_stmt* stmt, int columnIndex) + { + auto str = sqlite3_column_text(stmt, columnIndex); + return this->extract((const char*)str); + } + }; + +} // namespace sqlite_orm diff --git a/llarp/peerstats/peer_db.cpp b/llarp/peerstats/peer_db.cpp index 8b3cb26ab..77fe40871 100644 --- a/llarp/peerstats/peer_db.cpp +++ b/llarp/peerstats/peer_db.cpp @@ -203,7 +203,7 @@ namespace llarp auto& stats = m_peerStats[id]; stats.routerId = id.ToString(); - const bool isNewRC = (stats.lastRCUpdated < rc.last_updated.count()); + const bool isNewRC = (stats.lastRCUpdated < rc.last_updated); if (isNewRC) { @@ -211,20 +211,20 @@ namespace llarp if (stats.numDistinctRCsReceived > 1) { - const int64_t prevRCExpiration = (stats.lastRCUpdated + RouterContact::Lifetime.count()); + auto prevRCExpiration = (stats.lastRCUpdated + RouterContact::Lifetime); // we track max expiry as the delta between (last expiration time - time received), // and this value will be negative for an unhealthy router // TODO: handle case where new RC is also expired? just ignore? - int64_t expiry = (prevRCExpiration - now.count()); + auto expiry = prevRCExpiration - now; if (stats.numDistinctRCsReceived == 2) - stats.mostExpiredRCMs = expiry; + stats.leastRCRemainingLifetime = expiry; else - stats.mostExpiredRCMs = std::min(stats.mostExpiredRCMs, expiry); + stats.leastRCRemainingLifetime = std::min(stats.leastRCRemainingLifetime, expiry); } - stats.lastRCUpdated = rc.last_updated.count(); + stats.lastRCUpdated = rc.last_updated; stats.stale = true; } } diff --git a/llarp/peerstats/types.cpp b/llarp/peerstats/types.cpp index f3cd5368c..21aefa4e3 100644 --- a/llarp/peerstats/types.cpp +++ b/llarp/peerstats/types.cpp @@ -27,9 +27,8 @@ namespace llarp numLateRCs += other.numLateRCs; peakBandwidthBytesPerSec = std::max(peakBandwidthBytesPerSec, other.peakBandwidthBytesPerSec); - longestRCReceiveIntervalMs = - std::max(longestRCReceiveIntervalMs, other.longestRCReceiveIntervalMs); - mostExpiredRCMs = std::max(mostExpiredRCMs, other.mostExpiredRCMs); + longestRCReceiveInterval = std::max(longestRCReceiveInterval, other.longestRCReceiveInterval); + leastRCRemainingLifetime = std::max(leastRCRemainingLifetime, other.leastRCRemainingLifetime); lastRCUpdated = std::max(lastRCUpdated, other.lastRCUpdated); return *this; @@ -52,8 +51,9 @@ namespace llarp and numLateRCs == other.numLateRCs and peakBandwidthBytesPerSec == other.peakBandwidthBytesPerSec - and longestRCReceiveIntervalMs == other.longestRCReceiveIntervalMs - and mostExpiredRCMs == other.mostExpiredRCMs and lastRCUpdated == other.lastRCUpdated; + and longestRCReceiveInterval == other.longestRCReceiveInterval + and leastRCRemainingLifetime == other.leastRCRemainingLifetime + and lastRCUpdated == other.lastRCUpdated; } util::StatusObject @@ -73,9 +73,9 @@ namespace llarp {"numDistinctRCsReceived", numDistinctRCsReceived}, {"numLateRCs", numLateRCs}, // {"peakBandwidthBytesPerSec", peakBandwidthBytesPerSec}, - {"longestRCReceiveIntervalMs", longestRCReceiveIntervalMs}, - {"mostExpiredRCMs", mostExpiredRCMs}, - {"lastRCUpdated", lastRCUpdated}, + {"longestRCReceiveInterval", longestRCReceiveInterval.count()}, + {"leastRCRemainingLifetime", leastRCRemainingLifetime.count()}, + {"lastRCUpdated", lastRCUpdated.count()}, }; } diff --git a/llarp/peerstats/types.hpp b/llarp/peerstats/types.hpp index c7987c6e8..81be0a9bc 100644 --- a/llarp/peerstats/types.hpp +++ b/llarp/peerstats/types.hpp @@ -31,9 +31,9 @@ namespace llarp int32_t numLateRCs = 0; double peakBandwidthBytesPerSec = 0; - int64_t longestRCReceiveIntervalMs = 0; - int64_t mostExpiredRCMs = 0; - int64_t lastRCUpdated = 0; + llarp_time_t longestRCReceiveInterval = 0ms; + llarp_time_t leastRCRemainingLifetime = 0ms; + llarp_time_t lastRCUpdated = 0ms; // not serialized bool stale = true; diff --git a/test/peerstats/test_peer_db.cpp b/test/peerstats/test_peer_db.cpp index 414b49fda..2e2401a06 100644 --- a/test/peerstats/test_peer_db.cpp +++ b/test/peerstats/test_peer_db.cpp @@ -131,27 +131,27 @@ TEST_CASE("Test PeerDb handleGossipedRC", "[PeerDb]") auto stats = db.getCurrentPeerStats(id); CHECK(stats.has_value()); - CHECK(stats.value().mostExpiredRCMs == 0); // not calculated on first received RC + CHECK(stats.value().leastRCRemainingLifetime == 0ms); // not calculated on first received RC CHECK(stats.value().numDistinctRCsReceived == 1); - CHECK(stats.value().lastRCUpdated == 10000); + CHECK(stats.value().lastRCUpdated == 10000ms); now = 9s; db.handleGossipedRC(rc, now); stats = db.getCurrentPeerStats(id); CHECK(stats.has_value()); // these values should remain unchanged, this is not a new RC - CHECK(stats.value().mostExpiredRCMs == 0); + CHECK(stats.value().leastRCRemainingLifetime == 0ms); CHECK(stats.value().numDistinctRCsReceived == 1); - CHECK(stats.value().lastRCUpdated == 10000); + CHECK(stats.value().lastRCUpdated == 10000ms); rc.last_updated = 11s; db.handleGossipedRC(rc, now); stats = db.getCurrentPeerStats(id); // should be (previous expiration time - new received time) - CHECK(stats.value().mostExpiredRCMs == ((10s + rcLifetime) - now).count()); + CHECK(stats.value().leastRCRemainingLifetime == ((10s + rcLifetime) - now)); CHECK(stats.value().numDistinctRCsReceived == 2); - CHECK(stats.value().lastRCUpdated == 11000); + CHECK(stats.value().lastRCUpdated == 11000ms); } TEST_CASE("Test PeerDb handleGossipedRC expiry calcs", "[PeerDb]") @@ -194,24 +194,25 @@ TEST_CASE("Test PeerDb handleGossipedRC expiry calcs", "[PeerDb]") db.handleGossipedRC(rc1, r1); auto stats1 = db.getCurrentPeerStats(id); CHECK(stats1.has_value()); - CHECK(stats1.value().mostExpiredRCMs == 0); + CHECK(stats1.value().leastRCRemainingLifetime == 0ms); CHECK(stats1.value().numDistinctRCsReceived == 1); - CHECK(stats1.value().lastRCUpdated == s1.count()); + CHECK(stats1.value().lastRCUpdated == s1); db.handleGossipedRC(rc2, r2); auto stats2 = db.getCurrentPeerStats(id); CHECK(stats2.has_value()); - CHECK(stats2.value().mostExpiredRCMs == (e1 - r2).count()); - CHECK(stats2.value().mostExpiredRCMs > 0); // ensure positive indicates healthy + CHECK(stats2.value().leastRCRemainingLifetime == (e1 - r2)); + CHECK(stats2.value().leastRCRemainingLifetime > 0ms); // ensure positive indicates healthy CHECK(stats2.value().numDistinctRCsReceived == 2); - CHECK(stats2.value().lastRCUpdated == s2.count()); + CHECK(stats2.value().lastRCUpdated == s2); db.handleGossipedRC(rc3, r3); auto stats3 = db.getCurrentPeerStats(id); CHECK(stats3.has_value()); - CHECK(stats3.value().mostExpiredRCMs == (e2 - r3).count()); + CHECK(stats3.value().leastRCRemainingLifetime == (e2 - r3)); CHECK( - stats3.value().mostExpiredRCMs < 0); // ensure negative indicates unhealthy and we use min() + stats3.value().leastRCRemainingLifetime + < 0ms); // ensure negative indicates unhealthy and we use min() CHECK(stats3.value().numDistinctRCsReceived == 3); - CHECK(stats3.value().lastRCUpdated == s3.count()); + CHECK(stats3.value().lastRCUpdated == s3); } From 77b98459dd19355775fb2ab989ff10e552b17ccd Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Mon, 1 Jun 2020 10:29:24 -0600 Subject: [PATCH 14/70] Implement RouterID serialization in sqlite_orm --- llarp/peerstats/orm.hpp | 48 +++++++++++++++++++++++++++++++++++++ llarp/peerstats/peer_db.cpp | 21 +++++++--------- llarp/peerstats/types.cpp | 5 ++-- llarp/peerstats/types.hpp | 2 +- 4 files changed, 59 insertions(+), 17 deletions(-) diff --git a/llarp/peerstats/orm.hpp b/llarp/peerstats/orm.hpp index ed2d6b07a..53670cd27 100644 --- a/llarp/peerstats/orm.hpp +++ b/llarp/peerstats/orm.hpp @@ -41,6 +41,7 @@ namespace llarp /// reference: https://github.com/fnc12/sqlite_orm/blob/master/examples/enum_binding.cpp namespace sqlite_orm { + /// llarp_time_t serialization template <> struct type_printer : public integer_printer { @@ -86,4 +87,51 @@ namespace sqlite_orm } }; + /// RouterID serialization + template <> + struct type_printer : public text_printer + { + }; + + template <> + struct statement_binder + { + int + bind(sqlite3_stmt* stmt, int index, const llarp::RouterID& value) + { + return statement_binder().bind(stmt, index, value.ToString()); + } + }; + + template <> + struct field_printer + { + std::string + operator()(const llarp::RouterID& value) const + { + return value.ToString(); + } + }; + + template <> + struct row_extractor + { + llarp::RouterID + extract(const char* row_value) + { + llarp::RouterID id; + if (not id.FromString(row_value)) + throw std::runtime_error("Invalid RouterID in sqlite3 database"); + + return id; + } + + llarp::RouterID + extract(sqlite3_stmt* stmt, int columnIndex) + { + auto str = sqlite3_column_text(stmt, columnIndex); + return this->extract((const char*)str); + } + }; + } // namespace sqlite_orm diff --git a/llarp/peerstats/peer_db.cpp b/llarp/peerstats/peer_db.cpp index 77fe40871..a05e647f1 100644 --- a/llarp/peerstats/peer_db.cpp +++ b/llarp/peerstats/peer_db.cpp @@ -16,11 +16,11 @@ namespace llarp { std::lock_guard gaurd(m_statsLock); - m_peerStats.clear(); - if (m_storage) throw std::runtime_error("Reloading database not supported"); // TODO + m_peerStats.clear(); + // sqlite_orm treats empty-string as an indicator to load a memory-backed database, which we'll // use if file is an empty-optional std::string fileString; @@ -41,13 +41,11 @@ namespace llarp LogInfo("Loading ", allStats.size(), " PeerStats from table peerstats..."); for (PeerStats& stats : allStats) { - RouterID id; - if (not id.FromString(stats.routerId)) - throw std::runtime_error( - stringify("Database contains invalid PeerStats with id ", stats.routerId)); + // we cleared m_peerStats, and the database should enforce that routerId is unique... + assert(m_peerStats.find(stats.routerId) == m_peerStats.end()); stats.stale = false; - m_peerStats[id] = stats; + m_peerStats[stats.routerId] = stats; } } @@ -89,9 +87,6 @@ namespace llarp for (const auto& stats : staleStats) { - // call me paranoid... - assert(not stats.routerId.empty()); - m_storage->replace(stats); } @@ -109,7 +104,7 @@ namespace llarp void PeerDb::accumulatePeerStats(const RouterID& routerId, const PeerStats& delta) { - if (routerId.ToString() != delta.routerId) + if (routerId != delta.routerId) throw std::invalid_argument( stringify("routerId ", routerId, " doesn't match ", delta.routerId)); @@ -129,7 +124,7 @@ namespace llarp std::lock_guard gaurd(m_statsLock); PeerStats& stats = m_peerStats[routerId]; - stats.routerId = routerId.ToString(); + stats.routerId = routerId; stats.stale = true; callback(stats); } @@ -201,7 +196,7 @@ namespace llarp RouterID id(rc.pubkey); auto& stats = m_peerStats[id]; - stats.routerId = id.ToString(); + stats.routerId = id; const bool isNewRC = (stats.lastRCUpdated < rc.last_updated); diff --git a/llarp/peerstats/types.cpp b/llarp/peerstats/types.cpp index 21aefa4e3..ede210a96 100644 --- a/llarp/peerstats/types.cpp +++ b/llarp/peerstats/types.cpp @@ -4,9 +4,8 @@ namespace llarp { PeerStats::PeerStats() = default; - PeerStats::PeerStats(const RouterID& routerId_) + PeerStats::PeerStats(const RouterID& routerId_) : routerId(routerId_) { - routerId = routerId_.ToString(); } PeerStats& @@ -60,7 +59,7 @@ namespace llarp PeerStats::toJson() const { return { - {"routerId", routerId}, + {"routerId", routerId.ToString()}, // {"numConnectionAttempts", numConnectionAttempts}, // {"numConnectionSuccesses", numConnectionSuccesses}, // {"numConnectionRejections", numConnectionRejections}, diff --git a/llarp/peerstats/types.hpp b/llarp/peerstats/types.hpp index 81be0a9bc..0a365c21b 100644 --- a/llarp/peerstats/types.hpp +++ b/llarp/peerstats/types.hpp @@ -14,7 +14,7 @@ namespace llarp // Struct containing stats we know about a peer struct PeerStats { - std::string routerId; + RouterID routerId; int32_t numConnectionAttempts = 0; int32_t numConnectionSuccesses = 0; From d897099e1de39ea5f8ef216ffa4dad6c7fff9f00 Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Thu, 4 Jun 2020 10:00:30 -0600 Subject: [PATCH 15/70] Track traffic peerstats --- llarp/iwp/session.cpp | 7 +++++ llarp/iwp/session.hpp | 18 +++--------- llarp/link/i_link_manager.hpp | 4 +++ llarp/link/link_manager.cpp | 52 +++++++++++++++++++++++++++++++++++ llarp/link/link_manager.hpp | 5 ++++ llarp/link/session.hpp | 17 ++++++++++++ llarp/router/router.cpp | 14 ++++++++-- 7 files changed, 101 insertions(+), 16 deletions(-) diff --git a/llarp/iwp/session.cpp b/llarp/iwp/session.cpp index dd395f82b..82c659354 100644 --- a/llarp/iwp/session.cpp +++ b/llarp/iwp/session.cpp @@ -281,6 +281,13 @@ namespace llarp return false; } + SessionStats + Session::GetSessionStats() const + { + // TODO: thread safety + return m_Stats; + } + util::StatusObject Session::ExtractStatus() const { diff --git a/llarp/iwp/session.hpp b/llarp/iwp/session.hpp index 56044ffd8..00ae14510 100644 --- a/llarp/iwp/session.hpp +++ b/llarp/iwp/session.hpp @@ -115,6 +115,9 @@ namespace llarp bool ShouldPing() const override; + SessionStats + GetSessionStats() const override; + util::StatusObject ExtractStatus() const override; @@ -141,20 +144,7 @@ namespace llarp static std::string StateToString(State state); State m_State; - - struct Stats - { - // rate - uint64_t currentRateRX = 0; - uint64_t currentRateTX = 0; - - uint64_t totalPacketsRX = 0; - - uint64_t totalAckedTX = 0; - uint64_t totalDroppedTX = 0; - uint64_t totalInFlightTX = 0; - }; - Stats m_Stats; + SessionStats m_Stats; /// are we inbound session ? const bool m_Inbound; diff --git a/llarp/link/i_link_manager.hpp b/llarp/link/i_link_manager.hpp index 002a59f52..52b16c33a 100644 --- a/llarp/link/i_link_manager.hpp +++ b/llarp/link/i_link_manager.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include @@ -77,6 +78,9 @@ namespace llarp virtual void CheckPersistingSessions(llarp_time_t now) = 0; + virtual void + updatePeerDb(std::shared_ptr peerDb) = 0; + virtual util::StatusObject ExtractStatus() const = 0; }; diff --git a/llarp/link/link_manager.cpp b/llarp/link/link_manager.cpp index 9caf940c1..705652df5 100644 --- a/llarp/link/link_manager.cpp +++ b/llarp/link/link_manager.cpp @@ -327,6 +327,58 @@ namespace llarp } } + void + LinkManager::updatePeerDb(std::shared_ptr peerDb) + { + LogWarn("LinkManager::updatePeerDb()"); + + std::vector> statsToUpdate; + + int64_t diffTotalTX = 0; + + ForEachPeer([&](ILinkSession* session) { + // derive RouterID + RouterID id = RouterID(session->GetRemoteRC().pubkey); + LogWarn(" Session : ", id); + + SessionStats sessionStats = session->GetSessionStats(); + SessionStats diff; + SessionStats& lastStats = m_lastRouterStats[id]; + + // TODO: operator overloads / member func for diff + diff.currentRateRX = std::max(sessionStats.currentRateRX, lastStats.currentRateRX); + diff.currentRateTX = std::max(sessionStats.currentRateTX, lastStats.currentRateTX); + diff.totalPacketsRX = sessionStats.totalPacketsRX - lastStats.totalPacketsRX; + diff.totalAckedTX = sessionStats.totalAckedTX - lastStats.totalAckedTX; + diff.totalDroppedTX = sessionStats.totalDroppedTX - lastStats.totalDroppedTX; + + diffTotalTX = diff.totalAckedTX + diff.totalDroppedTX + diff.totalInFlightTX; + + lastStats = sessionStats; + + // TODO: if we have both inbound and outbound session, this will overwrite + statsToUpdate.push_back({id, diff}); + }); + + for (auto& routerStats : statsToUpdate) + { + peerDb->modifyPeerStats(routerStats.first, [&](PeerStats& stats) { + // TODO: store separate stats for up vs down + const auto& diff = routerStats.second; + + // note that 'currentRateRX' and 'currentRateTX' are per-second + stats.peakBandwidthBytesPerSec = std::max( + stats.peakBandwidthBytesPerSec, + (double)std::max(diff.currentRateRX, diff.currentRateTX)); + stats.numPacketsDropped += diff.totalDroppedTX; + stats.numPacketsSent = diff.totalAckedTX; + stats.numPacketsAttempted = diffTotalTX; + + // TODO: others -- we have slight mismatch on what we store + }); + } + } + util::StatusObject LinkManager::ExtractStatus() const { diff --git a/llarp/link/link_manager.hpp b/llarp/link/link_manager.hpp index ae9bfabf7..ddd546854 100644 --- a/llarp/link/link_manager.hpp +++ b/llarp/link/link_manager.hpp @@ -74,6 +74,9 @@ namespace llarp void CheckPersistingSessions(llarp_time_t now) override; + void + updatePeerDb(std::shared_ptr peerDb) override; + util::StatusObject ExtractStatus() const override; @@ -96,6 +99,8 @@ namespace llarp std::unordered_map m_PersistingSessions GUARDED_BY(_mutex); + std::unordered_map m_lastRouterStats; + IOutboundSessionMaker* _sessionMaker; }; diff --git a/llarp/link/session.hpp b/llarp/link/session.hpp index 199b14996..efb6f1dd5 100644 --- a/llarp/link/session.hpp +++ b/llarp/link/session.hpp @@ -15,6 +15,19 @@ namespace llarp struct ILinkMessage; struct ILinkLayer; + struct SessionStats + { + // rate + uint64_t currentRateRX = 0; + uint64_t currentRateTX = 0; + + uint64_t totalPacketsRX = 0; + + uint64_t totalAckedTX = 0; + uint64_t totalDroppedTX = 0; + uint64_t totalInFlightTX = 0; + }; + struct ILinkSession { virtual ~ILinkSession() = default; @@ -108,6 +121,10 @@ namespace llarp virtual bool ShouldPing() const = 0; + /// return the current stats for this session + virtual SessionStats + GetSessionStats() const = 0; + virtual util::StatusObject ExtractStatus() const = 0; }; diff --git a/llarp/router/router.cpp b/llarp/router/router.cpp index 0dea08ae7..dbcf98de7 100644 --- a/llarp/router/router.cpp +++ b/llarp/router/router.cpp @@ -770,8 +770,18 @@ namespace llarp nodedb()->AsyncFlushToDisk(); } - if (m_peerDb and m_peerDb->shouldFlush(now)) - diskworker()->addJob([this]() { m_peerDb->flushDatabase(); }); + if (m_peerDb) + { + // TODO: throttle this? + // TODO: need to capture session stats when session terminates / is removed from link manager + _linkManager.updatePeerDb(m_peerDb); + + if (m_peerDb->shouldFlush(now)) + { + LogWarn("Queing database flush..."); + diskworker()->addJob([this]() { m_peerDb->flushDatabase(); }); + } + } // get connected peers std::set peersWeHave; From 54017652d6204383dacb12a50d0d93ca8064c775 Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Fri, 5 Jun 2020 10:03:00 -0600 Subject: [PATCH 16/70] log-- --- llarp/link/link_manager.cpp | 3 --- llarp/peerstats/peer_db.cpp | 2 -- 2 files changed, 5 deletions(-) diff --git a/llarp/link/link_manager.cpp b/llarp/link/link_manager.cpp index 705652df5..447b7d9d7 100644 --- a/llarp/link/link_manager.cpp +++ b/llarp/link/link_manager.cpp @@ -330,8 +330,6 @@ namespace llarp void LinkManager::updatePeerDb(std::shared_ptr peerDb) { - LogWarn("LinkManager::updatePeerDb()"); - std::vector> statsToUpdate; int64_t diffTotalTX = 0; @@ -339,7 +337,6 @@ namespace llarp ForEachPeer([&](ILinkSession* session) { // derive RouterID RouterID id = RouterID(session->GetRemoteRC().pubkey); - LogWarn(" Session : ", id); SessionStats sessionStats = session->GetSessionStats(); SessionStats diff; diff --git a/llarp/peerstats/peer_db.cpp b/llarp/peerstats/peer_db.cpp index a05e647f1..fb14e2508 100644 --- a/llarp/peerstats/peer_db.cpp +++ b/llarp/peerstats/peer_db.cpp @@ -192,8 +192,6 @@ namespace llarp { std::lock_guard gaurd(m_statsLock); - LogWarn("Handling gossiped RC", rc); - RouterID id(rc.pubkey); auto& stats = m_peerStats[id]; stats.routerId = id; From 023e061146730aa7b3e9ad15a9f83ec202fef2ba Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Fri, 5 Jun 2020 10:16:00 -0600 Subject: [PATCH 17/70] Make [router]:enable-peer-stats only valid for client --- llarp/config/config.cpp | 20 ++++++++++++++------ llarp/router/router.cpp | 4 ++++ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/llarp/config/config.cpp b/llarp/config/config.cpp index 12bcee7ec..a5b65caeb 100644 --- a/llarp/config/config.cpp +++ b/llarp/config/config.cpp @@ -130,12 +130,20 @@ namespace llarp conf.defineOption( "router", "transport-privkey", false, "", AssignmentAcceptor(m_transportKeyFile)); - conf.defineOption( - "router", - "enable-peer-stats", - false, - DefaultEnablePeerStats, - AssignmentAcceptor(m_enablePeerStats)); + if (not params.isRelay) + { + // TODO: remove this -- all service nodes should run peer db + conf.defineOption( + "router", + "enable-peer-stats", + false, + DefaultEnablePeerStats, + AssignmentAcceptor(m_enablePeerStats)); + } + else + { + m_enablePeerStats = true; + } } void diff --git a/llarp/router/router.cpp b/llarp/router/router.cpp index dbcf98de7..7915dc255 100644 --- a/llarp/router/router.cpp +++ b/llarp/router/router.cpp @@ -592,6 +592,10 @@ namespace llarp m_peerDb = std::make_shared(); m_peerDb->configure(conf->router); } + else + { + assert(not IsServiceNode()); // enable peer stats must be enabled for service nodes + } // Logging config LogContext::Instance().Initialize( From 5a8f390b3b847e51744420a012cea6bd325e2542 Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Fri, 5 Jun 2020 10:50:32 -0600 Subject: [PATCH 18/70] Make mutex mutable, fix typo --- llarp/peerstats/peer_db.cpp | 14 +++++++------- llarp/peerstats/peer_db.hpp | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/llarp/peerstats/peer_db.cpp b/llarp/peerstats/peer_db.cpp index fb14e2508..6d3e56575 100644 --- a/llarp/peerstats/peer_db.cpp +++ b/llarp/peerstats/peer_db.cpp @@ -14,7 +14,7 @@ namespace llarp void PeerDb::loadDatabase(std::optional file) { - std::lock_guard gaurd(m_statsLock); + std::lock_guard guard(m_statsLock); if (m_storage) throw std::runtime_error("Reloading database not supported"); // TODO @@ -67,7 +67,7 @@ namespace llarp std::vector staleStats; { - std::lock_guard gaurd(m_statsLock); + std::lock_guard guard(m_statsLock); // copy all stale entries for (auto& entry : m_peerStats) @@ -108,7 +108,7 @@ namespace llarp throw std::invalid_argument( stringify("routerId ", routerId, " doesn't match ", delta.routerId)); - std::lock_guard gaurd(m_statsLock); + std::lock_guard guard(m_statsLock); auto itr = m_peerStats.find(routerId); if (itr == m_peerStats.end()) itr = m_peerStats.insert({routerId, delta}).first; @@ -121,7 +121,7 @@ namespace llarp void PeerDb::modifyPeerStats(const RouterID& routerId, std::function callback) { - std::lock_guard gaurd(m_statsLock); + std::lock_guard guard(m_statsLock); PeerStats& stats = m_peerStats[routerId]; stats.routerId = routerId; @@ -132,7 +132,7 @@ namespace llarp std::optional PeerDb::getCurrentPeerStats(const RouterID& routerId) const { - std::lock_guard gaurd(m_statsLock); + std::lock_guard guard(m_statsLock); auto itr = m_peerStats.find(routerId); if (itr == m_peerStats.end()) return std::nullopt; @@ -190,7 +190,7 @@ namespace llarp void PeerDb::handleGossipedRC(const RouterContact& rc, llarp_time_t now) { - std::lock_guard gaurd(m_statsLock); + std::lock_guard guard(m_statsLock); RouterID id(rc.pubkey); auto& stats = m_peerStats[id]; @@ -243,7 +243,7 @@ namespace llarp util::StatusObject PeerDb::ExtractStatus() const { - std::lock_guard gaurd(m_statsLock); + std::lock_guard guard(m_statsLock); bool loaded = (m_storage.get() != nullptr); util::StatusObject dbFile = nullptr; diff --git a/llarp/peerstats/peer_db.hpp b/llarp/peerstats/peer_db.hpp index 6f0c2e98f..11a603013 100644 --- a/llarp/peerstats/peer_db.hpp +++ b/llarp/peerstats/peer_db.hpp @@ -112,7 +112,7 @@ namespace llarp private: std::unordered_map m_peerStats; - std::mutex m_statsLock; + mutable std::mutex m_statsLock; std::unique_ptr m_storage; From c9faddc8e420edf6b5dfd4fb57c74a65000a2989 Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Fri, 5 Jun 2020 10:56:11 -0600 Subject: [PATCH 19/70] Use fs::path since mac doesn't support std::filesystem --- llarp/peerstats/peer_db.cpp | 2 +- llarp/peerstats/peer_db.hpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/llarp/peerstats/peer_db.cpp b/llarp/peerstats/peer_db.cpp index 6d3e56575..0c01951c7 100644 --- a/llarp/peerstats/peer_db.cpp +++ b/llarp/peerstats/peer_db.cpp @@ -12,7 +12,7 @@ namespace llarp } void - PeerDb::loadDatabase(std::optional file) + PeerDb::loadDatabase(std::optional file) { std::lock_guard guard(m_statsLock); diff --git a/llarp/peerstats/peer_db.hpp b/llarp/peerstats/peer_db.hpp index 11a603013..c61fb3716 100644 --- a/llarp/peerstats/peer_db.hpp +++ b/llarp/peerstats/peer_db.hpp @@ -6,6 +6,7 @@ #include +#include #include #include #include @@ -35,7 +36,7 @@ namespace llarp /// is provided. If no value is provided, the database will be memory-backed. /// @throws if sqlite_orm/sqlite3 is unable to open or create a database at the given file void - loadDatabase(std::optional file); + loadDatabase(std::optional file); /// Flushes the database. Must be called after loadDatabase(). This call will block during I/O /// and should be called in an appropriate threading context. However, it will make a temporary From cb8e5354f57c2aa2ca85f2494a6ab32754d040b7 Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Fri, 5 Jun 2020 14:16:51 -0600 Subject: [PATCH 20/70] Add some breadcrumbs about how ILinkLayer's callbacks are used --- llarp/link/server.hpp | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/llarp/link/server.hpp b/llarp/link/server.hpp index 8c9005e5c..ea8e30f78 100644 --- a/llarp/link/server.hpp +++ b/llarp/link/server.hpp @@ -17,34 +17,53 @@ namespace llarp { - /// handle a link layer message + /// handle a link layer message. this allows for the message to be handled by "upper layers" + /// + /// currently called from iwp::Session when messages are sent or received. using LinkMessageHandler = std::function; - /// sign a buffer with identity key + /// sign a buffer with identity key. this function should take the given `llarp_buffer_t` and + /// sign it, prividing the signature in the out variable `Signature&`. + /// + /// currently called from iwp::Session for signing LIMs (link introduction messages) using SignBufferFunc = std::function; /// handle connection timeout + /// + /// currently called from ILinkLayer::Pump() when an unestablished session times out using TimeoutHandler = std::function; /// get our RC + /// + /// currently called by iwp::Session to include as part of a LIM (link introduction message) using GetRCFunc = std::function; /// handler of session established /// return false to reject /// return true to accept + /// + /// currently called in iwp::Session when a valid LIM is received. using SessionEstablishedHandler = std::function; /// f(new, old) /// handler of session renegotiation /// returns true if the new rc is valid /// returns false otherwise and the session is terminated + /// + /// currently called from iwp::Session when we receive a renegotiation LIM using SessionRenegotiateHandler = std::function; /// handles close of all sessions with pubkey + /// + /// Note that this handler is called while m_AuthedLinksMutex is held + /// + /// currently called from iwp::ILinkSession when a previously established session times out using SessionClosedHandler = std::function; /// notifies router that a link session has ended its pump and we should flush /// messages to upper layers + /// + /// currently called at the end of every iwp::Session::Pump() call using PumpDoneHandler = std::function; using Work_t = std::function; From 2453fff10b2b5beba19ec722e6f4fe8440453c1c Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Mon, 8 Jun 2020 14:03:03 -0600 Subject: [PATCH 21/70] Piggyback on link callbacks to add peer stats --- llarp/router/router.cpp | 32 ++++++++++++++++++++++++++++---- llarp/router/router.hpp | 8 ++++++++ 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/llarp/router/router.cpp b/llarp/router/router.cpp index 7915dc255..4fc8599a0 100644 --- a/llarp/router/router.cpp +++ b/llarp/router/router.cpp @@ -548,9 +548,9 @@ namespace llarp util::memFn(&AbstractRouter::rc, this), util::memFn(&AbstractRouter::HandleRecvLinkMessageBuffer, this), util::memFn(&AbstractRouter::Sign, this), - util::memFn(&IOutboundSessionMaker::OnSessionEstablished, &_outboundSessionMaker), + util::memFn(&Router::ConnectionEstablished, this), util::memFn(&AbstractRouter::CheckRenegotiateValid, this), - util::memFn(&IOutboundSessionMaker::OnConnectTimeout, &_outboundSessionMaker), + util::memFn(&Router::ConnectionTimedOut, this), util::memFn(&AbstractRouter::SessionClosed, this), util::memFn(&AbstractRouter::PumpLL, this), util::memFn(&AbstractRouter::QueueWork, this)); @@ -824,6 +824,30 @@ namespace llarp LogInfo("Session to ", remote, " fully closed"); } + void + Router::ConnectionTimedOut(ILinkSession* session) + { + if (m_peerDb) + { + RouterID id{session->GetPubKey()}; + // TODO: make sure this is a public router (on whitelist)? + m_peerDb->modifyPeerStats(id, [&](PeerStats& stats) { stats.numConnectionTimeouts++; }); + } + _outboundSessionMaker.OnConnectTimeout(session); + } + + bool + Router::ConnectionEstablished(ILinkSession* session) + { + if (m_peerDb) + { + RouterID id{session->GetPubKey()}; + // TODO: make sure this is a public router (on whitelist)? + m_peerDb->modifyPeerStats(id, [&](PeerStats& stats) { stats.numConnectionSuccesses++; }); + } + return _outboundSessionMaker.OnSessionEstablished(session); + } + bool Router::GetRandomConnectedRouter(RouterContact& result) const { @@ -1175,9 +1199,9 @@ namespace llarp util::memFn(&AbstractRouter::rc, this), util::memFn(&AbstractRouter::HandleRecvLinkMessageBuffer, this), util::memFn(&AbstractRouter::Sign, this), - util::memFn(&IOutboundSessionMaker::OnSessionEstablished, &_outboundSessionMaker), + util::memFn(&Router::ConnectionEstablished, this), util::memFn(&AbstractRouter::CheckRenegotiateValid, this), - util::memFn(&IOutboundSessionMaker::OnConnectTimeout, &_outboundSessionMaker), + util::memFn(&Router::ConnectionTimedOut, this), util::memFn(&AbstractRouter::SessionClosed, this), util::memFn(&AbstractRouter::PumpLL, this), util::memFn(&AbstractRouter::QueueWork, this)); diff --git a/llarp/router/router.hpp b/llarp/router/router.hpp index 275679ca0..5f99540bb 100644 --- a/llarp/router/router.hpp +++ b/llarp/router/router.hpp @@ -434,6 +434,14 @@ namespace llarp void SessionClosed(RouterID remote) override; + /// called by link when an unestablished connection times out + void + ConnectionTimedOut(ILinkSession* session); + + /// called by link when session is fully established + bool + ConnectionEstablished(ILinkSession* session); + /// call internal router ticker void Tick(); From b2a72dd46a9fc026bb642693eec7eb858ba1180b Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Thu, 11 Jun 2020 13:02:34 -0600 Subject: [PATCH 22/70] Initial test_peer_stats hive test --- llarp/iwp/session.cpp | 4 ++-- llarp/link/server.hpp | 2 +- llarp/router/router.cpp | 6 ++++-- llarp/router/router.hpp | 2 +- pybind/llarp/tooling/router_event.cpp | 5 +++++ test/hive/conftest.py | 12 ++++++++++++ 6 files changed, 25 insertions(+), 6 deletions(-) diff --git a/llarp/iwp/session.cpp b/llarp/iwp/session.cpp index 82c659354..06ba7dc41 100644 --- a/llarp/iwp/session.cpp +++ b/llarp/iwp/session.cpp @@ -76,7 +76,7 @@ namespace llarp GotLIM = util::memFn(&Session::GotRenegLIM, this); m_RemoteRC = msg->rc; m_Parent->MapAddr(m_RemoteRC.pubkey, this); - return m_Parent->SessionEstablished(this); + return m_Parent->SessionEstablished(this, true); } bool @@ -96,7 +96,7 @@ namespace llarp { self->m_State = State::Ready; self->m_Parent->MapAddr(self->m_RemoteRC.pubkey, self.get()); - self->m_Parent->SessionEstablished(self.get()); + self->m_Parent->SessionEstablished(self.get(), false); } }); return true; diff --git a/llarp/link/server.hpp b/llarp/link/server.hpp index ea8e30f78..55df4fa6f 100644 --- a/llarp/link/server.hpp +++ b/llarp/link/server.hpp @@ -43,7 +43,7 @@ namespace llarp /// return true to accept /// /// currently called in iwp::Session when a valid LIM is received. - using SessionEstablishedHandler = std::function; + using SessionEstablishedHandler = std::function; /// f(new, old) /// handler of session renegotiation diff --git a/llarp/router/router.cpp b/llarp/router/router.cpp index 4fc8599a0..0a1b5c667 100644 --- a/llarp/router/router.cpp +++ b/llarp/router/router.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include "tooling/router_event.hpp" #include "util/status.hpp" @@ -837,14 +838,15 @@ namespace llarp } bool - Router::ConnectionEstablished(ILinkSession* session) + Router::ConnectionEstablished(ILinkSession* session, bool inbound) { + RouterID id{session->GetPubKey()}; if (m_peerDb) { - RouterID id{session->GetPubKey()}; // TODO: make sure this is a public router (on whitelist)? m_peerDb->modifyPeerStats(id, [&](PeerStats& stats) { stats.numConnectionSuccesses++; }); } + NotifyRouterEvent(pubkey(), id, inbound); return _outboundSessionMaker.OnSessionEstablished(session); } diff --git a/llarp/router/router.hpp b/llarp/router/router.hpp index 5f99540bb..1be5a99c7 100644 --- a/llarp/router/router.hpp +++ b/llarp/router/router.hpp @@ -440,7 +440,7 @@ namespace llarp /// called by link when session is fully established bool - ConnectionEstablished(ILinkSession* session); + ConnectionEstablished(ILinkSession* session, bool inbound); /// call internal router ticker void diff --git a/pybind/llarp/tooling/router_event.cpp b/pybind/llarp/tooling/router_event.cpp index 8098d4283..906469044 100644 --- a/pybind/llarp/tooling/router_event.cpp +++ b/pybind/llarp/tooling/router_event.cpp @@ -5,6 +5,7 @@ #include "tooling/dht_event.hpp" #include "tooling/path_event.hpp" #include "tooling/rc_event.hpp" +#include "tooling/peer_stats_event.hpp" #include #include @@ -73,6 +74,10 @@ namespace tooling mod, "FindRouterReceivedEvent"); py::class_(mod, "FindRouterSentEvent"); + + py::class_(mod, "LinkSessionEstablishedEvent") + .def_readonly("remoteId", &LinkSessionEstablishedEvent::remoteId) + .def_readonly("inbound", &LinkSessionEstablishedEvent::inbound); } } // namespace tooling diff --git a/test/hive/conftest.py b/test/hive/conftest.py index e44427ee8..371f62740 100644 --- a/test/hive/conftest.py +++ b/test/hive/conftest.py @@ -33,3 +33,15 @@ def HiveArbitrary(): router_hive.Stop() +@pytest.fixture() +def HiveForPeerStats(): + router_hive = None + def _make(n_relays, n_clients, netid): + nonlocal router_hive + router_hive = hive.RouterHive(n_relays, n_clients, netid) + router_hive.Start() + return router_hive + + yield _make + + router_hive.Stop() From d69d538f1a36bbdf518c9c7ce79de3c09e66704b Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Mon, 15 Jun 2020 11:34:40 -0600 Subject: [PATCH 23/70] Add missing files --- llarp/tooling/peer_stats_event.hpp | 44 +++++++++++++++++++++++ pybind/llarp/tooling/peer_stats_event.hpp | 28 +++++++++++++++ test/hive/test_peer_stats.py | 44 +++++++++++++++++++++++ 3 files changed, 116 insertions(+) create mode 100644 llarp/tooling/peer_stats_event.hpp create mode 100644 pybind/llarp/tooling/peer_stats_event.hpp create mode 100644 test/hive/test_peer_stats.py diff --git a/llarp/tooling/peer_stats_event.hpp b/llarp/tooling/peer_stats_event.hpp new file mode 100644 index 000000000..34d1f6519 --- /dev/null +++ b/llarp/tooling/peer_stats_event.hpp @@ -0,0 +1,44 @@ +#pragma once + +#include "router_event.hpp" + +namespace tooling +{ + struct LinkSessionEstablishedEvent : public RouterEvent + { + llarp::RouterID remoteId; + bool inbound = false; + + LinkSessionEstablishedEvent( + const llarp::RouterID& ourRouterId, const llarp::RouterID& remoteId_, bool inbound_) + : RouterEvent("Link: LinkSessionEstablishedEvent", ourRouterId, false) + , remoteId(remoteId_) + , inbound(inbound_) + { + } + + std::string + ToString() const + { + return RouterEvent::ToString() + (inbound ? "inbound" : "outbound") + + " : LinkSessionEstablished with " + remoteId.ToString(); + } + }; + + struct ConnectionAttemptEvent : public RouterEvent + { + llarp::RouterID remoteId; + + ConnectionAttemptEvent(const llarp::RouterID& ourRouterId, const llarp::RouterID& remoteId_) + : RouterEvent("Link: ConnectionAttemptEvent", ourRouterId, false), remoteId(remoteId_) + { + } + + std::string + ToString() const + { + return RouterEvent::ToString() + " : LinkSessionEstablished with " + remoteId.ToString(); + } + }; + +} // namespace tooling diff --git a/pybind/llarp/tooling/peer_stats_event.hpp b/pybind/llarp/tooling/peer_stats_event.hpp new file mode 100644 index 000000000..92e7c7381 --- /dev/null +++ b/pybind/llarp/tooling/peer_stats_event.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include "router_event.hpp" + +namespace tooling +{ + struct LinkSessionEstablishedEvent : public RouterEvent + { + llarp::RouterID remoteId; + bool inbound = false; + + LinkSessionEstablishedEvent( + const llarp::RouterID& ourRouterId, const llarp::RouterID& remoteId_, bool inbound_) + : RouterEvent("Link: LinkSessionEstablishedEvent", ourRouterId, false) + , remoteId(remoteId_) + , inbound(inbound_) + { + } + + std::string + ToString() const + { + return RouterEvent::ToString() + (inbound ? "inbound" : "outbound") + + " : LinkSessionEstablished with " + remoteId.ToString(); + } + }; + +} // namespace tooling diff --git a/test/hive/test_peer_stats.py b/test/hive/test_peer_stats.py new file mode 100644 index 000000000..844e76377 --- /dev/null +++ b/test/hive/test_peer_stats.py @@ -0,0 +1,44 @@ +import pyllarp +from time import time + +def test_peer_stats(HiveForPeerStats): + hive = HiveForPeerStats(n_relays=5, n_clients=0, netid="hive") + + start_time = time() + cur_time = start_time + test_duration = 30 #seconds + + paths = [] + + print("looking for events...") + + numInbound = 0 + numOutbound = 0 + numAttempts = 0 + + while cur_time < start_time + test_duration: + + hive.CollectAllEvents() + + for event in hive.events: + event_name = event.__class__.__name__ + + if event_name == "LinkSessionEstablishedEvent": + if event.inbound: + numInbound += 1 + else: + numOutbound += 1 + + if event_name == "ConnectionAttemptEvent": + numAttempts += 1 + + hive.events = [] + cur_time = time() + + print("test duration exceeded") + print("in: {} out: {} attempts: {}", numInbound, numOutbound, numAttempts); + assert(numInbound == numOutbound) + assert(numOutbound == numAttempts) + +if __name__ == "__main__": + main() From d1b629f494a1c49084f7b064739298462b08ae52 Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Mon, 15 Jun 2020 11:37:57 -0600 Subject: [PATCH 24/70] RouterHive ConnectionAttemptEvent --- llarp/router/outbound_session_maker.cpp | 18 ++++++++++++++++-- llarp/router/outbound_session_maker.hpp | 2 ++ llarp/router/router.cpp | 1 + pybind/llarp/tooling/router_event.cpp | 3 +++ test/hive/test_peer_stats.py | 2 +- 5 files changed, 23 insertions(+), 3 deletions(-) diff --git a/llarp/router/outbound_session_maker.cpp b/llarp/router/outbound_session_maker.cpp index f7e50185d..c03dc34f7 100644 --- a/llarp/router/outbound_session_maker.cpp +++ b/llarp/router/outbound_session_maker.cpp @@ -1,5 +1,7 @@ #include +#include +#include #include #include #include @@ -152,6 +154,7 @@ namespace llarp void OutboundSessionMaker::Init( + AbstractRouter* router, ILinkManager* linkManager, I_RCLookupHandler* rcLookup, Profiling* profiler, @@ -159,6 +162,7 @@ namespace llarp llarp_nodedb* nodedb, WorkerFunc_t dowork) { + _router = router; _linkManager = linkManager; _rcLookup = rcLookup; _logic = logic; @@ -298,8 +302,18 @@ namespace llarp void OutboundSessionMaker::CreatePendingSession(const RouterID& router) { - util::Lock l(_mutex); - pendingSessions.emplace(router, nullptr); + { + util::Lock l(_mutex); + pendingSessions.emplace(router, nullptr); + } + + auto peerDb = _router->peerDb(); + if (peerDb) + { + peerDb->modifyPeerStats(router, [](PeerStats& stats) { stats.numConnectionAttempts++; }); + } + + _router->NotifyRouterEvent(_router->pubkey(), router); } void diff --git a/llarp/router/outbound_session_maker.hpp b/llarp/router/outbound_session_maker.hpp index 63069f65e..063594cbd 100644 --- a/llarp/router/outbound_session_maker.hpp +++ b/llarp/router/outbound_session_maker.hpp @@ -58,6 +58,7 @@ namespace llarp void Init( + AbstractRouter* router, ILinkManager* linkManager, I_RCLookupHandler* rcLookup, Profiling* profiler, @@ -110,6 +111,7 @@ namespace llarp std::unordered_map pendingCallbacks GUARDED_BY(_mutex); + AbstractRouter* _router = nullptr; ILinkManager* _linkManager = nullptr; I_RCLookupHandler* _rcLookup = nullptr; Profiling* _profiler = nullptr; diff --git a/llarp/router/router.cpp b/llarp/router/router.cpp index 0a1b5c667..e92be092e 100644 --- a/llarp/router/router.cpp +++ b/llarp/router/router.cpp @@ -523,6 +523,7 @@ namespace llarp // Init components after relevant config settings loaded _outboundMessageHandler.Init(&_linkManager, _logic); _outboundSessionMaker.Init( + this, &_linkManager, &_rcLookupHandler, &_routerProfiling, diff --git a/pybind/llarp/tooling/router_event.cpp b/pybind/llarp/tooling/router_event.cpp index 906469044..3fe62db18 100644 --- a/pybind/llarp/tooling/router_event.cpp +++ b/pybind/llarp/tooling/router_event.cpp @@ -78,6 +78,9 @@ namespace tooling py::class_(mod, "LinkSessionEstablishedEvent") .def_readonly("remoteId", &LinkSessionEstablishedEvent::remoteId) .def_readonly("inbound", &LinkSessionEstablishedEvent::inbound); + + py::class_(mod, "ConnectionAttemptEvent") + .def_readonly("remoteId", &ConnectionAttemptEvent::remoteId); } } // namespace tooling diff --git a/test/hive/test_peer_stats.py b/test/hive/test_peer_stats.py index 844e76377..d618279c0 100644 --- a/test/hive/test_peer_stats.py +++ b/test/hive/test_peer_stats.py @@ -2,7 +2,7 @@ import pyllarp from time import time def test_peer_stats(HiveForPeerStats): - hive = HiveForPeerStats(n_relays=5, n_clients=0, netid="hive") + hive = HiveForPeerStats(n_relays=12, n_clients=0, netid="hive") start_time = time() cur_time = start_time From c4cbbd673180779a8a9564ef3595a9b70754f0a2 Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Mon, 15 Jun 2020 12:53:17 -0600 Subject: [PATCH 25/70] RouterHive: store router contexts by routerId instead of index --- llarp/tooling/router_hive.cpp | 70 ++++++++++++++--------------------- llarp/tooling/router_hive.hpp | 23 ++++-------- 2 files changed, 34 insertions(+), 59 deletions(-) diff --git a/llarp/tooling/router_hive.cpp b/llarp/tooling/router_hive.cpp index 613db5a9a..769f0a77b 100644 --- a/llarp/tooling/router_hive.cpp +++ b/llarp/tooling/router_hive.cpp @@ -14,15 +14,19 @@ using namespace std::chrono_literals; namespace tooling { void - RouterHive::AddRouter( - const std::shared_ptr& config, std::vector* routers, bool isRelay) + RouterHive::AddRouter(const std::shared_ptr& config, bool isRelay) { + auto& container = (isRelay ? relays : clients); + llarp_main* ctx = llarp_main_init_from_config(config->Copy(), isRelay); auto result = llarp_main_setup(ctx, isRelay); if (result == 0) { - llarp::Context::Get(ctx)->InjectHive(this); - routers->push_back(ctx); + auto context = llarp::Context::Get(ctx); + auto routerId = llarp::RouterID(context->router->pubkey()); + context->InjectHive(this); + container[routerId] = ctx; + std::cout << "Generated router with ID " << routerId << std::endl; } else { @@ -37,19 +41,21 @@ namespace tooling void RouterHive::AddRelay(const std::shared_ptr& config) { - AddRouter(config, &relays, true); + AddRouter(config, true); } void RouterHive::AddClient(const std::shared_ptr& config) { - AddRouter(config, &clients, false); + AddRouter(config, false); } void - RouterHive::StartRouters(std::vector* routers, bool isRelay) + RouterHive::StartRouters(bool isRelay) { - for (llarp_main* ctx : *routers) + auto& container = (isRelay ? relays : clients); + + for (auto [routerId, ctx] : container) { routerMainThreads.emplace_back([=]() { llarp_main_run(ctx, llarp_main_runtime_opts{false, false, false, isRelay}); @@ -61,37 +67,37 @@ namespace tooling void RouterHive::StartRelays() { - StartRouters(&relays, true); + StartRouters(true); } void RouterHive::StartClients() { - StartRouters(&clients, false); + StartRouters(false); } void RouterHive::StopRouters() { llarp::LogInfo("Signalling all routers to stop"); - for (llarp_main* ctx : relays) + for (auto [routerId, ctx] : relays) { llarp_main_signal(ctx, 2 /* SIGINT */); } - for (llarp_main* ctx : clients) + for (auto [routerId, ctx] : clients) { llarp_main_signal(ctx, 2 /* SIGINT */); } llarp::LogInfo("Waiting on routers to be stopped"); - for (llarp_main* ctx : relays) + for (auto [routerId, ctx] : relays) { while (llarp_main_is_running(ctx)) { std::this_thread::sleep_for(10ms); } } - for (llarp_main* ctx : clients) + for (auto [routerId, ctx] : clients) { while (llarp_main_is_running(ctx)) { @@ -154,28 +160,6 @@ namespace tooling LogicCall(ctx->logic, [visit, ctx]() { visit(ctx); }); } - void - RouterHive::VisitRelay(size_t index, std::function visit) - { - if (index >= relays.size()) - { - visit(nullptr); - return; - } - VisitRouter(relays[index], visit); - } - - void - RouterHive::VisitClient(size_t index, std::function visit) - { - if (index >= clients.size()) - { - visit(nullptr); - return; - } - VisitRouter(clients[index], visit); - } - std::vector RouterHive::RelayConnectedRelays() { @@ -185,11 +169,11 @@ namespace tooling size_t i = 0; size_t done_count = 0; - for (auto relay : relays) + for (auto [routerId, ctx] : relays) { - auto ctx = llarp::Context::Get(relay); - LogicCall(ctx->logic, [&, i, ctx]() { - size_t count = ctx->router->NumberOfConnectedRouters(); + auto context = llarp::Context::Get(ctx); + LogicCall(context->logic, [&, i, context]() { + size_t count = context->router->NumberOfConnectedRouters(); std::lock_guard guard{results_lock}; results[i] = count; done_count++; @@ -220,10 +204,10 @@ namespace tooling results.resize(relays.size()); size_t i = 0; - for (auto relay : relays) + for (auto [routerId, ctx] : relays) { - auto ctx = llarp::Context::Get(relay); - results[i] = ctx->router->rc(); + auto context = llarp::Context::Get(ctx); + results[i] = context->router->rc(); i++; } return results; diff --git a/llarp/tooling/router_hive.hpp b/llarp/tooling/router_hive.hpp index 9e9982e35..7c547eeea 100644 --- a/llarp/tooling/router_hive.hpp +++ b/llarp/tooling/router_hive.hpp @@ -26,26 +26,17 @@ namespace tooling private: void - StartRouters(std::vector* routers, bool isRelay); + StartRouters(bool isRelay); void AddRouter( const std::shared_ptr& config, - std::vector* routers, bool isRelay); /// safely visit router void VisitRouter(llarp_main* router, std::function visit); - /// safely visit relay at index N - void - VisitRelay(size_t index, std::function visit); - - /// safely visit client at index N - void - VisitClient(size_t index, std::function visit); - public: RouterHive() = default; @@ -76,18 +67,18 @@ namespace tooling void ForEachRelay(std::function visit) { - for (size_t idx = 0; idx < relays.size(); ++idx) + for (auto [routerId, ctx] : relays) { - VisitRelay(idx, visit); + VisitRouter(ctx, visit); } } void ForEachClient(std::function visit) { - for (size_t idx = 0; idx < clients.size(); ++idx) + for (auto [routerId, ctx] : clients) { - VisitClient(idx, visit); + VisitRouter(ctx, visit); } } @@ -105,8 +96,8 @@ namespace tooling std::vector GetRelayRCs(); - std::vector relays; - std::vector clients; + std::unordered_map relays; + std::unordered_map clients; std::vector routerMainThreads; From 3b6f84c68ce450b114e3fa1607da607147002f0d Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Wed, 17 Jun 2020 15:42:22 -0600 Subject: [PATCH 26/70] Peek at peer stats db in test_peer_stats --- llarp/tooling/router_hive.cpp | 62 ++++++++++++++++++++++++++++ llarp/tooling/router_hive.hpp | 35 +++++++--------- pybind/CMakeLists.txt | 2 + pybind/common.hpp | 9 ++++ pybind/llarp/peerstats.cpp | 40 ++++++++++++++++++ pybind/llarp/router.cpp | 15 +++++++ pybind/llarp/router_id.cpp | 4 +- pybind/llarp/tooling/router_hive.cpp | 14 +++++-- pybind/module.cpp | 3 ++ test/hive/test_peer_stats.py | 44 ++++++++++++++++---- 10 files changed, 193 insertions(+), 35 deletions(-) create mode 100644 pybind/llarp/peerstats.cpp create mode 100644 pybind/llarp/router.cpp diff --git a/llarp/tooling/router_hive.cpp b/llarp/tooling/router_hive.cpp index 769f0a77b..502e2c903 100644 --- a/llarp/tooling/router_hive.cpp +++ b/llarp/tooling/router_hive.cpp @@ -160,6 +160,17 @@ namespace tooling LogicCall(ctx->logic, [visit, ctx]() { visit(ctx); }); } + llarp::AbstractRouter* + RouterHive::GetRelay(const llarp::RouterID& id) + { + auto itr = relays.find(id); + if (itr == relays.end()) + return nullptr; + + auto ctx = llarp::Context::Get(itr->second); + return ctx->router.get(); + } + std::vector RouterHive::RelayConnectedRelays() { @@ -213,4 +224,55 @@ namespace tooling return results; } + void + RouterHive::ForEachRelayRouter(std::function visit) + { + for (auto [routerId, ctx] : relays) + { + visit(GetRelay(routerId)); + } + } + + void + RouterHive::ForEachClientRouter(std::function visit) + { + for (auto [routerId, ctx] : clients) + { + visit(GetRelay(routerId)); + } + } + + void + RouterHive::ForEachRouterRouter(std::function visit) + { + ForEachRelayRouter(visit); + ForEachClientRouter(visit); + } + + void + RouterHive::ForEachRelayContext(std::function visit) + { + for (auto [routerId, ctx] : relays) + { + VisitRouter(ctx, visit); + } + } + + void + RouterHive::ForEachClientContext(std::function visit) + { + for (auto [routerId, ctx] : clients) + { + VisitRouter(ctx, visit); + } + } + + /// safely visit every router context + void + RouterHive::ForEachRouterContext(std::function visit) + { + ForEachRelayContext(visit); + ForEachClientContext(visit); + } + } // namespace tooling diff --git a/llarp/tooling/router_hive.hpp b/llarp/tooling/router_hive.hpp index 7c547eeea..d88186da3 100644 --- a/llarp/tooling/router_hive.hpp +++ b/llarp/tooling/router_hive.hpp @@ -16,6 +16,7 @@ struct llarp_main; namespace llarp { struct Context; + struct AbstractRouter; } namespace tooling @@ -64,31 +65,23 @@ namespace tooling std::deque GetAllEvents(); + // functions to safely visit each relay and/or client's AbstractRouter or Context void - ForEachRelay(std::function visit) - { - for (auto [routerId, ctx] : relays) - { - VisitRouter(ctx, visit); - } - } + ForEachRelayRouter(std::function visit); + void + ForEachClientRouter(std::function visit); + void + ForEachRouterRouter(std::function visit); void - ForEachClient(std::function visit) - { - for (auto [routerId, ctx] : clients) - { - VisitRouter(ctx, visit); - } - } - - /// safely visit every router context + ForEachRelayContext(std::function visit); void - ForEachRouter(std::function visit) - { - ForEachRelay(visit); - ForEachClient(visit); - } + ForEachClientContext(std::function visit); + void + ForEachRouterContext(std::function visit); + + llarp::AbstractRouter* + GetRelay(const llarp::RouterID& id); std::vector RelayConnectedRelays(); diff --git a/pybind/CMakeLists.txt b/pybind/CMakeLists.txt index e307f67cd..a8706ff50 100644 --- a/pybind/CMakeLists.txt +++ b/pybind/CMakeLists.txt @@ -1,11 +1,13 @@ pybind11_add_module(pyllarp MODULE module.cpp llarp/context.cpp + llarp/router.cpp llarp/router_id.cpp llarp/router_contact.cpp llarp/crypto/types.cpp llarp/config.cpp llarp/logger.cpp + llarp/peerstats.cpp llarp/dht/dht_types.cpp llarp/path/path_types.cpp llarp/path/path_hop_config.cpp diff --git a/pybind/common.hpp b/pybind/common.hpp index 93faecd6e..125944016 100644 --- a/pybind/common.hpp +++ b/pybind/common.hpp @@ -20,6 +20,9 @@ namespace llarp void CryptoTypes_Init(py::module& mod); + void + AbstractRouter_Init(py::module& mod); + void RouterID_Init(py::module& mod); @@ -32,6 +35,12 @@ namespace llarp void PathTypes_Init(py::module& mod); + void + PeerDb_Init(py::module& mod); + + void + PeerStats_Init(py::module& mod); + namespace dht { void diff --git a/pybind/llarp/peerstats.cpp b/pybind/llarp/peerstats.cpp new file mode 100644 index 000000000..eebca2059 --- /dev/null +++ b/pybind/llarp/peerstats.cpp @@ -0,0 +1,40 @@ +#include "common.hpp" +#include "config/config.hpp" +#include "peerstats/peer_db.hpp" +#include "peerstats/types.hpp" + +#include + +namespace llarp +{ + void + PeerDb_Init(py::module& mod) + { + using PeerDb_ptr = std::shared_ptr; + py::class_(mod, "PeerDb") + .def("getCurrentPeerStats", &PeerDb::getCurrentPeerStats); + } + + void + PeerStats_Init(py::module& mod) + { + py::class_(mod, "PeerStats") + .def_readwrite("routerId", &PeerStats::routerId) + .def_readwrite("numConnectionAttempts", &PeerStats::numConnectionAttempts) + .def_readwrite("numConnectionSuccesses", &PeerStats::numConnectionSuccesses) + .def_readwrite("numConnectionRejections", &PeerStats::numConnectionRejections) + .def_readwrite("numConnectionTimeouts", &PeerStats::numConnectionTimeouts) + .def_readwrite("numPathBuilds", &PeerStats::numPathBuilds) + .def_readwrite("numPacketsAttempted", &PeerStats::numPacketsAttempted) + .def_readwrite("numPacketsSent", &PeerStats::numPacketsSent) + .def_readwrite("numPacketsDropped", &PeerStats::numPacketsDropped) + .def_readwrite("numPacketsResent", &PeerStats::numPacketsResent) + .def_readwrite("numDistinctRCsReceived", &PeerStats::numDistinctRCsReceived) + .def_readwrite("numLateRCs", &PeerStats::numLateRCs) + .def_readwrite("peakBandwidthBytesPerSec", &PeerStats::peakBandwidthBytesPerSec) + .def_readwrite("longestRCReceiveInterval", &PeerStats::longestRCReceiveInterval) + .def_readwrite("leastRCRemainingLifetime", &PeerStats::leastRCRemainingLifetime) + .def_readwrite("lastRCUpdated", &PeerStats::lastRCUpdated) + .def_readwrite("stale", &PeerStats::stale); + } +} // namespace llarp diff --git a/pybind/llarp/router.cpp b/pybind/llarp/router.cpp new file mode 100644 index 000000000..7531c27c5 --- /dev/null +++ b/pybind/llarp/router.cpp @@ -0,0 +1,15 @@ +#include "common.hpp" + +#include "router/abstractrouter.hpp" + +namespace llarp +{ + void + AbstractRouter_Init(py::module& mod) + { + py::class_(mod, "AbstractRouter") + .def("rc", &AbstractRouter::rc) + .def("Stop", &AbstractRouter::Stop) + .def("peerDb", &AbstractRouter::peerDb); + } +} // namespace llarp diff --git a/pybind/llarp/router_id.cpp b/pybind/llarp/router_id.cpp index 6c15f8814..a04784d52 100644 --- a/pybind/llarp/router_id.cpp +++ b/pybind/llarp/router_id.cpp @@ -18,8 +18,6 @@ namespace llarp .def("__repr__", &RouterID::ToString) .def("__str__", &RouterID::ToString) .def("ShortString", &RouterID::ShortString) - .def("__eq__", [](const RouterID* const lhs, const RouterID* const rhs) { - return *lhs == *rhs; - }); + .def("__eq__", [](const RouterID& lhs, const RouterID& rhs) { return lhs == rhs; }); } } // namespace llarp diff --git a/pybind/llarp/tooling/router_hive.cpp b/pybind/llarp/tooling/router_hive.cpp index 63f7e6c44..098d46e9b 100644 --- a/pybind/llarp/tooling/router_hive.cpp +++ b/pybind/llarp/tooling/router_hive.cpp @@ -3,7 +3,9 @@ #include "pybind11/iostream.h" #include +#include "router/abstractrouter.hpp" #include "llarp.hpp" + namespace tooling { void @@ -17,12 +19,16 @@ namespace tooling .def("StartRelays", &RouterHive::StartRelays) .def("StartClients", &RouterHive::StartClients) .def("StopAll", &RouterHive::StopRouters) - .def("ForEachRelay", &RouterHive::ForEachRelay) - .def("ForEachClient", &RouterHive::ForEachClient) - .def("ForEachRouter", &RouterHive::ForEachRouter) + .def("ForEachRelayContext", &RouterHive::ForEachRelayContext) + .def("ForEachClientContext", &RouterHive::ForEachClientContext) + .def("ForEachRouterContext", &RouterHive::ForEachRouterContext) + .def("ForEachRelayRouter", &RouterHive::ForEachRelayRouter) + .def("ForEachClientRouter", &RouterHive::ForEachClientRouter) + .def("ForEachRouterRouter", &RouterHive::ForEachRouterRouter) .def("GetNextEvent", &RouterHive::GetNextEvent) .def("GetAllEvents", &RouterHive::GetAllEvents) .def("RelayConnectedRelays", &RouterHive::RelayConnectedRelays) - .def("GetRelayRCs", &RouterHive::GetRelayRCs); + .def("GetRelayRCs", &RouterHive::GetRelayRCs) + .def("GetRelay", &RouterHive::GetRelay); } } // namespace tooling diff --git a/pybind/module.cpp b/pybind/module.cpp index e53894179..aa57fa25a 100644 --- a/pybind/module.cpp +++ b/pybind/module.cpp @@ -5,6 +5,9 @@ PYBIND11_MODULE(pyllarp, m) { tooling::RouterHive_Init(m); tooling::RouterEvent_Init(m); + llarp::AbstractRouter_Init(m); + llarp::PeerDb_Init(m); + llarp::PeerStats_Init(m); llarp::RouterID_Init(m); llarp::RouterContact_Init(m); llarp::CryptoTypes_Init(m); diff --git a/test/hive/test_peer_stats.py b/test/hive/test_peer_stats.py index d618279c0..fd47b656f 100644 --- a/test/hive/test_peer_stats.py +++ b/test/hive/test_peer_stats.py @@ -2,20 +2,23 @@ import pyllarp from time import time def test_peer_stats(HiveForPeerStats): - hive = HiveForPeerStats(n_relays=12, n_clients=0, netid="hive") + + numRelays = 12 + + hive = HiveForPeerStats(n_relays=numRelays, n_clients=0, netid="hive") start_time = time() cur_time = start_time test_duration = 30 #seconds - paths = [] - - print("looking for events...") - + # we track the number of attempted sessions and inbound/outbound established sessions numInbound = 0 numOutbound = 0 numAttempts = 0 + # we pick an arbitrary router out of our routers + someRouterId = None + while cur_time < start_time + test_duration: hive.CollectAllEvents() @@ -32,13 +35,40 @@ def test_peer_stats(HiveForPeerStats): if event_name == "ConnectionAttemptEvent": numAttempts += 1 + if someRouterId is None: + someRouterId = event.remoteId; + hive.events = [] cur_time = time() + # these should be strictly equal, although there is variation because of + # the time we sample print("test duration exceeded") print("in: {} out: {} attempts: {}", numInbound, numOutbound, numAttempts); - assert(numInbound == numOutbound) - assert(numOutbound == numAttempts) + totalReceived = tally_rc_received_for_peer(hive.hive, someRouterId) + + # every router should have received this relay's RC exactly once + print("total times RC received: {} numRelays: {}", totalReceived, numRelays) + assert(totalReceived == numRelays) + +def tally_rc_received_for_peer(hive, routerId): + + numFound = 0 + + def visit(relay): + nonlocal numFound + + peerDb = relay.peerDb() + stats = peerDb.getCurrentPeerStats(routerId); + + assert(stats.routerId == routerId) + + numFound += stats.numDistinctRCsReceived + + hive.ForEachRelayRouter(visit) + + return numFound; + if __name__ == "__main__": main() From 63f41d6a98213f5fd5f016ebf9d6bd9c8ae99cf4 Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Wed, 24 Jun 2020 14:07:15 -0600 Subject: [PATCH 27/70] Introduce mutex for hive's routers --- llarp/tooling/router_hive.cpp | 14 +++++++++++--- llarp/tooling/router_hive.hpp | 3 ++- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/llarp/tooling/router_hive.cpp b/llarp/tooling/router_hive.cpp index 502e2c903..2e6e38418 100644 --- a/llarp/tooling/router_hive.cpp +++ b/llarp/tooling/router_hive.cpp @@ -161,8 +161,12 @@ namespace tooling } llarp::AbstractRouter* - RouterHive::GetRelay(const llarp::RouterID& id) + RouterHive::GetRelay(const llarp::RouterID& id, bool needMutexLock) { + auto guard = needMutexLock + ? std::make_optional>(routerMutex) + : std::nullopt; + auto itr = relays.find(id); if (itr == relays.end()) return nullptr; @@ -174,6 +178,7 @@ namespace tooling std::vector RouterHive::RelayConnectedRelays() { + std::lock_guard guard{routerMutex}; std::vector results; results.resize(relays.size()); std::mutex results_lock; @@ -211,6 +216,7 @@ namespace tooling std::vector RouterHive::GetRelayRCs() { + std::lock_guard guard{routerMutex}; std::vector results; results.resize(relays.size()); @@ -227,18 +233,20 @@ namespace tooling void RouterHive::ForEachRelayRouter(std::function visit) { + std::lock_guard guard{routerMutex}; for (auto [routerId, ctx] : relays) { - visit(GetRelay(routerId)); + visit(GetRelay(routerId, false)); } } void RouterHive::ForEachClientRouter(std::function visit) { + std::lock_guard guard{routerMutex}; for (auto [routerId, ctx] : clients) { - visit(GetRelay(routerId)); + visit(GetRelay(routerId, false)); } } diff --git a/llarp/tooling/router_hive.hpp b/llarp/tooling/router_hive.hpp index d88186da3..3260db0ba 100644 --- a/llarp/tooling/router_hive.hpp +++ b/llarp/tooling/router_hive.hpp @@ -81,7 +81,7 @@ namespace tooling ForEachRouterContext(std::function visit); llarp::AbstractRouter* - GetRelay(const llarp::RouterID& id); + GetRelay(const llarp::RouterID& id, bool needMutexLock = true); std::vector RelayConnectedRelays(); @@ -89,6 +89,7 @@ namespace tooling std::vector GetRelayRCs(); + std::mutex routerMutex; std::unordered_map relays; std::unordered_map clients; From bdac43e19f8288222f273d42dfc6368e26f6d2cd Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Wed, 24 Jun 2020 14:49:35 -0600 Subject: [PATCH 28/70] Peer stats test which artificially stops a router from gossiping its RC --- llarp/router/abstractrouter.hpp | 4 ++ llarp/router/router.cpp | 3 ++ llarp/router/router.hpp | 8 +++ pybind/llarp/router.cpp | 3 +- test/hive/test_peer_stats.py | 86 +++++++++++++++++++++------------ 5 files changed, 71 insertions(+), 33 deletions(-) diff --git a/llarp/router/abstractrouter.hpp b/llarp/router/abstractrouter.hpp index f53c2917a..211c18634 100644 --- a/llarp/router/abstractrouter.hpp +++ b/llarp/router/abstractrouter.hpp @@ -81,6 +81,10 @@ namespace llarp tooling::RouterHive* hive = nullptr; #endif + // XXX / TODO: this code shouldn't ever make it into a release binary + virtual void + stopGossipingRC() = 0; + virtual ~AbstractRouter() = default; virtual bool diff --git a/llarp/router/router.cpp b/llarp/router/router.cpp index e92be092e..9a8034a9e 100644 --- a/llarp/router/router.cpp +++ b/llarp/router/router.cpp @@ -125,6 +125,9 @@ namespace llarp void Router::GossipRCIfNeeded(const RouterContact rc) { + if (not m_shouldGossipRC) + return; + /// if we are not a service node forget about gossip if (not IsServiceNode()) return; diff --git a/llarp/router/router.hpp b/llarp/router/router.hpp index 1be5a99c7..db393c073 100644 --- a/llarp/router/router.hpp +++ b/llarp/router/router.hpp @@ -51,6 +51,14 @@ namespace llarp { struct Router final : public AbstractRouter { + // XXX / TODO: this code shouldn't ever make it into a release binary + virtual void + stopGossipingRC() + { + m_shouldGossipRC = false; + } + bool m_shouldGossipRC = true; + llarp_time_t _lastPump = 0s; bool ready; // transient iwp encryption key diff --git a/pybind/llarp/router.cpp b/pybind/llarp/router.cpp index 7531c27c5..8dcedc6a4 100644 --- a/pybind/llarp/router.cpp +++ b/pybind/llarp/router.cpp @@ -10,6 +10,7 @@ namespace llarp py::class_(mod, "AbstractRouter") .def("rc", &AbstractRouter::rc) .def("Stop", &AbstractRouter::Stop) - .def("peerDb", &AbstractRouter::peerDb); + .def("peerDb", &AbstractRouter::peerDb) + .def("stopGossipingRC", &AbstractRouter::stopGossipingRC); } } // namespace llarp diff --git a/test/hive/test_peer_stats.py b/test/hive/test_peer_stats.py index fd47b656f..4d0530ed0 100644 --- a/test/hive/test_peer_stats.py +++ b/test/hive/test_peer_stats.py @@ -6,50 +6,72 @@ def test_peer_stats(HiveForPeerStats): numRelays = 12 hive = HiveForPeerStats(n_relays=numRelays, n_clients=0, netid="hive") + someRouterId = None - start_time = time() - cur_time = start_time - test_duration = 30 #seconds + def collectStatsForAWhile(duration): + print("collecting router hive stats for {} seconds...", duration) - # we track the number of attempted sessions and inbound/outbound established sessions - numInbound = 0 - numOutbound = 0 - numAttempts = 0 + start_time = time() + cur_time = start_time - # we pick an arbitrary router out of our routers - someRouterId = None + # we track the number of attempted sessions and inbound/outbound established sessions + numInbound = 0 + numOutbound = 0 + numAttempts = 0 + + nonlocal someRouterId + + while cur_time < start_time + duration: + hive.CollectAllEvents() + + for event in hive.events: + event_name = event.__class__.__name__ + + if event_name == "LinkSessionEstablishedEvent": + if event.inbound: + numInbound += 1 + else: + numOutbound += 1 + + if event_name == "ConnectionAttemptEvent": + numAttempts += 1 - while cur_time < start_time + test_duration: + # we pick an arbitrary router out of our routers + if someRouterId is None: + someRouterId = event.remoteId; - hive.CollectAllEvents() + hive.events = [] + cur_time = time() - for event in hive.events: - event_name = event.__class__.__name__ + # these should be strictly equal, although there is variation because of + # the time we sample + print("test duration exceeded") + print("in: {} out: {} attempts: {}", numInbound, numOutbound, numAttempts); + totalReceived = tally_rc_received_for_peer(hive.hive, someRouterId) - if event_name == "LinkSessionEstablishedEvent": - if event.inbound: - numInbound += 1 - else: - numOutbound += 1 + # every router should have received this relay's RC exactly once + print("total times RC received: {} numRelays: {}", totalReceived, numRelays) - if event_name == "ConnectionAttemptEvent": - numAttempts += 1 + return { + "numInbound": numInbound, + "numOutbound": numOutbound, + "numAttempts": numAttempts, + "totalTargetRCsReceived": totalReceived, + }; - if someRouterId is None: - someRouterId = event.remoteId; + results1 = collectStatsForAWhile(30); + assert(results1["totalTargetRCsReceived"] == numRelays) - hive.events = [] - cur_time = time() + # stop our router from gossiping + router = hive.hive.GetRelay(someRouterId, True) + router.stopGossipingRC(); - # these should be strictly equal, although there is variation because of - # the time we sample - print("test duration exceeded") - print("in: {} out: {} attempts: {}", numInbound, numOutbound, numAttempts); - totalReceived = tally_rc_received_for_peer(hive.hive, someRouterId) + ignore = collectStatsForAWhile(30); - # every router should have received this relay's RC exactly once - print("total times RC received: {} numRelays: {}", totalReceived, numRelays) - assert(totalReceived == numRelays) + # ensure that no one hears a fresh RC from this router again + print("Starting second (longer) stats collection...") + results2 = collectStatsForAWhile(3600); + assert(results2["totalTargetRCsReceived"] == numRelays) # should not have increased def tally_rc_received_for_peer(hive, routerId): From aeb0c2be3a3ee1bc4d10aaca78c8064ed52a4f18 Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Thu, 25 Jun 2020 11:46:31 -0600 Subject: [PATCH 29/70] Remove ability to stop routers from gossiping for now --- llarp/router/abstractrouter.hpp | 4 ---- llarp/router/router.cpp | 2 +- llarp/router/router.hpp | 14 +++++++------- pybind/llarp/router.cpp | 3 +-- 4 files changed, 9 insertions(+), 14 deletions(-) diff --git a/llarp/router/abstractrouter.hpp b/llarp/router/abstractrouter.hpp index 211c18634..f53c2917a 100644 --- a/llarp/router/abstractrouter.hpp +++ b/llarp/router/abstractrouter.hpp @@ -81,10 +81,6 @@ namespace llarp tooling::RouterHive* hive = nullptr; #endif - // XXX / TODO: this code shouldn't ever make it into a release binary - virtual void - stopGossipingRC() = 0; - virtual ~AbstractRouter() = default; virtual bool diff --git a/llarp/router/router.cpp b/llarp/router/router.cpp index 9a8034a9e..6589660a5 100644 --- a/llarp/router/router.cpp +++ b/llarp/router/router.cpp @@ -125,7 +125,7 @@ namespace llarp void Router::GossipRCIfNeeded(const RouterContact rc) { - if (not m_shouldGossipRC) + if (disableGossipingRC_TestingOnly()) return; /// if we are not a service node forget about gossip diff --git a/llarp/router/router.hpp b/llarp/router/router.hpp index db393c073..5af8c6b38 100644 --- a/llarp/router/router.hpp +++ b/llarp/router/router.hpp @@ -51,13 +51,6 @@ namespace llarp { struct Router final : public AbstractRouter { - // XXX / TODO: this code shouldn't ever make it into a release binary - virtual void - stopGossipingRC() - { - m_shouldGossipRC = false; - } - bool m_shouldGossipRC = true; llarp_time_t _lastPump = 0s; bool ready; @@ -536,6 +529,13 @@ namespace llarp void MessageSent(const RouterID& remote, SendStatus status); + + protected: + virtual bool + disableGossipingRC_TestingOnly() + { + return false; + }; }; } // namespace llarp diff --git a/pybind/llarp/router.cpp b/pybind/llarp/router.cpp index 8dcedc6a4..7531c27c5 100644 --- a/pybind/llarp/router.cpp +++ b/pybind/llarp/router.cpp @@ -10,7 +10,6 @@ namespace llarp py::class_(mod, "AbstractRouter") .def("rc", &AbstractRouter::rc) .def("Stop", &AbstractRouter::Stop) - .def("peerDb", &AbstractRouter::peerDb) - .def("stopGossipingRC", &AbstractRouter::stopGossipingRC); + .def("peerDb", &AbstractRouter::peerDb); } } // namespace llarp From 4aa6f8e2df3b2c2e4fb666e01fb72f5b0e69af23 Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Thu, 25 Jun 2020 15:36:13 -0600 Subject: [PATCH 30/70] Use find_package() for sqlite3 --- cmake/FindPackageHandleStandardArgs.cmake | 466 ++++++++++++++++++++++ cmake/FindPackageMessage.cmake | 48 +++ cmake/FindSQLite3.cmake | 66 +++ llarp/CMakeLists.txt | 2 + 4 files changed, 582 insertions(+) create mode 100644 cmake/FindPackageHandleStandardArgs.cmake create mode 100644 cmake/FindPackageMessage.cmake create mode 100644 cmake/FindSQLite3.cmake diff --git a/cmake/FindPackageHandleStandardArgs.cmake b/cmake/FindPackageHandleStandardArgs.cmake new file mode 100644 index 000000000..4fb08259a --- /dev/null +++ b/cmake/FindPackageHandleStandardArgs.cmake @@ -0,0 +1,466 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +#[=======================================================================[.rst: +FindPackageHandleStandardArgs +----------------------------- + +This module provides a function intended to be used in :ref:`Find Modules` +implementing :command:`find_package()` calls. It handles the +``REQUIRED``, ``QUIET`` and version-related arguments of ``find_package``. +It also sets the ``_FOUND`` variable. The package is +considered found if all variables listed contain valid results, e.g. +valid filepaths. + +.. command:: find_package_handle_standard_args + + There are two signatures:: + + find_package_handle_standard_args( + (DEFAULT_MSG|) + ... + ) + + find_package_handle_standard_args( + [FOUND_VAR ] + [REQUIRED_VARS ...] + [VERSION_VAR ] + [HANDLE_COMPONENTS] + [CONFIG_MODE] + [NAME_MISMATCHED] + [REASON_FAILURE_MESSAGE ] + [FAIL_MESSAGE ] + ) + + The ``_FOUND`` variable will be set to ``TRUE`` if all + the variables ``...`` are valid and any optional + constraints are satisfied, and ``FALSE`` otherwise. A success or + failure message may be displayed based on the results and on + whether the ``REQUIRED`` and/or ``QUIET`` option was given to + the :command:`find_package` call. + + The options are: + + ``(DEFAULT_MSG|)`` + In the simple signature this specifies the failure message. + Use ``DEFAULT_MSG`` to ask for a default message to be computed + (recommended). Not valid in the full signature. + + ``FOUND_VAR `` + Obsolete. Specifies either ``_FOUND`` or + ``_FOUND`` as the result variable. This exists only + for compatibility with older versions of CMake and is now ignored. + Result variables of both names are always set for compatibility. + + ``REQUIRED_VARS ...`` + Specify the variables which are required for this package. + These may be named in the generated failure message asking the + user to set the missing variable values. Therefore these should + typically be cache entries such as ``FOO_LIBRARY`` and not output + variables like ``FOO_LIBRARIES``. This option is mandatory if + ``HANDLE_COMPONENTS`` is not specified. + + ``VERSION_VAR `` + Specify the name of a variable that holds the version of the package + that has been found. This version will be checked against the + (potentially) specified required version given to the + :command:`find_package` call, including its ``EXACT`` option. + The default messages include information about the required + version and the version which has been actually found, both + if the version is ok or not. + + ``HANDLE_COMPONENTS`` + Enable handling of package components. In this case, the command + will report which components have been found and which are missing, + and the ``_FOUND`` variable will be set to ``FALSE`` + if any of the required components (i.e. not the ones listed after + the ``OPTIONAL_COMPONENTS`` option of :command:`find_package`) are + missing. + + ``CONFIG_MODE`` + Specify that the calling find module is a wrapper around a + call to ``find_package( NO_MODULE)``. This implies + a ``VERSION_VAR`` value of ``_VERSION``. The command + will automatically check whether the package configuration file + was found. + + ``REASON_FAILURE_MESSAGE `` + Specify a custom message of the reason for the failure which will be + appended to the default generated message. + + ``FAIL_MESSAGE `` + Specify a custom failure message instead of using the default + generated message. Not recommended. + + ``NAME_MISMATCHED`` + Indicate that the ```` does not match + ``${CMAKE_FIND_PACKAGE_NAME}``. This is usually a mistake and raises a + warning, but it may be intentional for usage of the command for components + of a larger package. + +Example for the simple signature: + +.. code-block:: cmake + + find_package_handle_standard_args(LibXml2 DEFAULT_MSG + LIBXML2_LIBRARY LIBXML2_INCLUDE_DIR) + +The ``LibXml2`` package is considered to be found if both +``LIBXML2_LIBRARY`` and ``LIBXML2_INCLUDE_DIR`` are valid. +Then also ``LibXml2_FOUND`` is set to ``TRUE``. If it is not found +and ``REQUIRED`` was used, it fails with a +:command:`message(FATAL_ERROR)`, independent whether ``QUIET`` was +used or not. If it is found, success will be reported, including +the content of the first ````. On repeated CMake runs, +the same message will not be printed again. + +.. note:: + + If ```` does not match ``CMAKE_FIND_PACKAGE_NAME`` for the + calling module, a warning that there is a mismatch is given. The + ``FPHSA_NAME_MISMATCHED`` variable may be set to bypass the warning if using + the old signature and the ``NAME_MISMATCHED`` argument using the new + signature. To avoid forcing the caller to require newer versions of CMake for + usage, the variable's value will be used if defined when the + ``NAME_MISMATCHED`` argument is not passed for the new signature (but using + both is an error).. + +Example for the full signature: + +.. code-block:: cmake + + find_package_handle_standard_args(LibArchive + REQUIRED_VARS LibArchive_LIBRARY LibArchive_INCLUDE_DIR + VERSION_VAR LibArchive_VERSION) + +In this case, the ``LibArchive`` package is considered to be found if +both ``LibArchive_LIBRARY`` and ``LibArchive_INCLUDE_DIR`` are valid. +Also the version of ``LibArchive`` will be checked by using the version +contained in ``LibArchive_VERSION``. Since no ``FAIL_MESSAGE`` is given, +the default messages will be printed. + +Another example for the full signature: + +.. code-block:: cmake + + find_package(Automoc4 QUIET NO_MODULE HINTS /opt/automoc4) + find_package_handle_standard_args(Automoc4 CONFIG_MODE) + +In this case, a ``FindAutmoc4.cmake`` module wraps a call to +``find_package(Automoc4 NO_MODULE)`` and adds an additional search +directory for ``automoc4``. Then the call to +``find_package_handle_standard_args`` produces a proper success/failure +message. +#]=======================================================================] + +include(${CMAKE_CURRENT_LIST_DIR}/FindPackageMessage.cmake) + +# internal helper macro +macro(_FPHSA_FAILURE_MESSAGE _msg) + set (__msg "${_msg}") + if (FPHSA_REASON_FAILURE_MESSAGE) + string(APPEND __msg "\n Reason given by package: ${FPHSA_REASON_FAILURE_MESSAGE}\n") + endif() + if (${_NAME}_FIND_REQUIRED) + message(FATAL_ERROR "${__msg}") + else () + if (NOT ${_NAME}_FIND_QUIETLY) + message(STATUS "${__msg}") + endif () + endif () +endmacro() + + +# internal helper macro to generate the failure message when used in CONFIG_MODE: +macro(_FPHSA_HANDLE_FAILURE_CONFIG_MODE) + # _CONFIG is set, but FOUND is false, this means that some other of the REQUIRED_VARS was not found: + if(${_NAME}_CONFIG) + _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: missing:${MISSING_VARS} (found ${${_NAME}_CONFIG} ${VERSION_MSG})") + else() + # If _CONSIDERED_CONFIGS is set, the config-file has been found, but no suitable version. + # List them all in the error message: + if(${_NAME}_CONSIDERED_CONFIGS) + set(configsText "") + list(LENGTH ${_NAME}_CONSIDERED_CONFIGS configsCount) + math(EXPR configsCount "${configsCount} - 1") + foreach(currentConfigIndex RANGE ${configsCount}) + list(GET ${_NAME}_CONSIDERED_CONFIGS ${currentConfigIndex} filename) + list(GET ${_NAME}_CONSIDERED_VERSIONS ${currentConfigIndex} version) + string(APPEND configsText "\n ${filename} (version ${version})") + endforeach() + if (${_NAME}_NOT_FOUND_MESSAGE) + if (FPHSA_REASON_FAILURE_MESSAGE) + string(PREPEND FPHSA_REASON_FAILURE_MESSAGE "${${_NAME}_NOT_FOUND_MESSAGE}\n ") + else() + set(FPHSA_REASON_FAILURE_MESSAGE "${${_NAME}_NOT_FOUND_MESSAGE}") + endif() + else() + string(APPEND configsText "\n") + endif() + _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE} ${VERSION_MSG}, checked the following files:${configsText}") + + else() + # Simple case: No Config-file was found at all: + _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: found neither ${_NAME}Config.cmake nor ${_NAME_LOWER}-config.cmake ${VERSION_MSG}") + endif() + endif() +endmacro() + + +function(FIND_PACKAGE_HANDLE_STANDARD_ARGS _NAME _FIRST_ARG) + + # Set up the arguments for `cmake_parse_arguments`. + set(options CONFIG_MODE HANDLE_COMPONENTS NAME_MISMATCHED) + set(oneValueArgs FAIL_MESSAGE REASON_FAILURE_MESSAGE VERSION_VAR FOUND_VAR) + set(multiValueArgs REQUIRED_VARS) + + # Check whether we are in 'simple' or 'extended' mode: + set(_KEYWORDS_FOR_EXTENDED_MODE ${options} ${oneValueArgs} ${multiValueArgs} ) + list(FIND _KEYWORDS_FOR_EXTENDED_MODE "${_FIRST_ARG}" INDEX) + + unset(FPHSA_NAME_MISMATCHED_override) + if (DEFINED FPHSA_NAME_MISMATCHED) + # If the variable NAME_MISMATCHED variable is set, error if it is passed as + # an argument. The former is for old signatures, the latter is for new + # signatures. + list(FIND ARGN "NAME_MISMATCHED" name_mismatched_idx) + if (NOT name_mismatched_idx EQUAL "-1") + message(FATAL_ERROR + "The `NAME_MISMATCHED` argument may only be specified by the argument or " + "the variable, not both.") + endif () + + # But use the variable if it is not an argument to avoid forcing minimum + # CMake version bumps for calling modules. + set(FPHSA_NAME_MISMATCHED_override "${FPHSA_NAME_MISMATCHED}") + endif () + + if(${INDEX} EQUAL -1) + set(FPHSA_FAIL_MESSAGE ${_FIRST_ARG}) + set(FPHSA_REQUIRED_VARS ${ARGN}) + set(FPHSA_VERSION_VAR) + else() + cmake_parse_arguments(FPHSA "${options}" "${oneValueArgs}" "${multiValueArgs}" ${_FIRST_ARG} ${ARGN}) + + if(FPHSA_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "Unknown keywords given to FIND_PACKAGE_HANDLE_STANDARD_ARGS(): \"${FPHSA_UNPARSED_ARGUMENTS}\"") + endif() + + if(NOT FPHSA_FAIL_MESSAGE) + set(FPHSA_FAIL_MESSAGE "DEFAULT_MSG") + endif() + + # In config-mode, we rely on the variable _CONFIG, which is set by find_package() + # when it successfully found the config-file, including version checking: + if(FPHSA_CONFIG_MODE) + list(INSERT FPHSA_REQUIRED_VARS 0 ${_NAME}_CONFIG) + list(REMOVE_DUPLICATES FPHSA_REQUIRED_VARS) + set(FPHSA_VERSION_VAR ${_NAME}_VERSION) + endif() + + if(NOT FPHSA_REQUIRED_VARS AND NOT FPHSA_HANDLE_COMPONENTS) + message(FATAL_ERROR "No REQUIRED_VARS specified for FIND_PACKAGE_HANDLE_STANDARD_ARGS()") + endif() + endif() + + if (DEFINED FPHSA_NAME_MISMATCHED_override) + set(FPHSA_NAME_MISMATCHED "${FPHSA_NAME_MISMATCHED_override}") + endif () + + if (DEFINED CMAKE_FIND_PACKAGE_NAME + AND NOT FPHSA_NAME_MISMATCHED + AND NOT _NAME STREQUAL CMAKE_FIND_PACKAGE_NAME) + message(AUTHOR_WARNING + "The package name passed to `find_package_handle_standard_args` " + "(${_NAME}) does not match the name of the calling package " + "(${CMAKE_FIND_PACKAGE_NAME}). This can lead to problems in calling " + "code that expects `find_package` result variables (e.g., `_FOUND`) " + "to follow a certain pattern.") + endif () + +# now that we collected all arguments, process them + + if("x${FPHSA_FAIL_MESSAGE}" STREQUAL "xDEFAULT_MSG") + set(FPHSA_FAIL_MESSAGE "Could NOT find ${_NAME}") + endif() + + if (FPHSA_REQUIRED_VARS) + list(GET FPHSA_REQUIRED_VARS 0 _FIRST_REQUIRED_VAR) + endif() + + string(TOUPPER ${_NAME} _NAME_UPPER) + string(TOLOWER ${_NAME} _NAME_LOWER) + + if(FPHSA_FOUND_VAR) + set(_FOUND_VAR_UPPER ${_NAME_UPPER}_FOUND) + set(_FOUND_VAR_MIXED ${_NAME}_FOUND) + if(FPHSA_FOUND_VAR STREQUAL _FOUND_VAR_MIXED OR FPHSA_FOUND_VAR STREQUAL _FOUND_VAR_UPPER) + set(_FOUND_VAR ${FPHSA_FOUND_VAR}) + else() + message(FATAL_ERROR "The argument for FOUND_VAR is \"${FPHSA_FOUND_VAR}\", but only \"${_FOUND_VAR_MIXED}\" and \"${_FOUND_VAR_UPPER}\" are valid names.") + endif() + else() + set(_FOUND_VAR ${_NAME_UPPER}_FOUND) + endif() + + # collect all variables which were not found, so they can be printed, so the + # user knows better what went wrong (#6375) + set(MISSING_VARS "") + set(DETAILS "") + # check if all passed variables are valid + set(FPHSA_FOUND_${_NAME} TRUE) + foreach(_CURRENT_VAR ${FPHSA_REQUIRED_VARS}) + if(NOT ${_CURRENT_VAR}) + set(FPHSA_FOUND_${_NAME} FALSE) + string(APPEND MISSING_VARS " ${_CURRENT_VAR}") + else() + string(APPEND DETAILS "[${${_CURRENT_VAR}}]") + endif() + endforeach() + if(FPHSA_FOUND_${_NAME}) + set(${_NAME}_FOUND TRUE) + set(${_NAME_UPPER}_FOUND TRUE) + else() + set(${_NAME}_FOUND FALSE) + set(${_NAME_UPPER}_FOUND FALSE) + endif() + + # component handling + unset(FOUND_COMPONENTS_MSG) + unset(MISSING_COMPONENTS_MSG) + + if(FPHSA_HANDLE_COMPONENTS) + foreach(comp ${${_NAME}_FIND_COMPONENTS}) + if(${_NAME}_${comp}_FOUND) + + if(NOT DEFINED FOUND_COMPONENTS_MSG) + set(FOUND_COMPONENTS_MSG "found components:") + endif() + string(APPEND FOUND_COMPONENTS_MSG " ${comp}") + + else() + + if(NOT DEFINED MISSING_COMPONENTS_MSG) + set(MISSING_COMPONENTS_MSG "missing components:") + endif() + string(APPEND MISSING_COMPONENTS_MSG " ${comp}") + + if(${_NAME}_FIND_REQUIRED_${comp}) + set(${_NAME}_FOUND FALSE) + string(APPEND MISSING_VARS " ${comp}") + endif() + + endif() + endforeach() + set(COMPONENT_MSG "${FOUND_COMPONENTS_MSG} ${MISSING_COMPONENTS_MSG}") + string(APPEND DETAILS "[c${COMPONENT_MSG}]") + endif() + + # version handling: + set(VERSION_MSG "") + set(VERSION_OK TRUE) + + # check with DEFINED here as the requested or found version may be "0" + if (DEFINED ${_NAME}_FIND_VERSION) + if(DEFINED ${FPHSA_VERSION_VAR}) + set(_FOUND_VERSION ${${FPHSA_VERSION_VAR}}) + + if(${_NAME}_FIND_VERSION_EXACT) # exact version required + # count the dots in the version string + string(REGEX REPLACE "[^.]" "" _VERSION_DOTS "${_FOUND_VERSION}") + # add one dot because there is one dot more than there are components + string(LENGTH "${_VERSION_DOTS}." _VERSION_DOTS) + if (_VERSION_DOTS GREATER ${_NAME}_FIND_VERSION_COUNT) + # Because of the C++ implementation of find_package() ${_NAME}_FIND_VERSION_COUNT + # is at most 4 here. Therefore a simple lookup table is used. + if (${_NAME}_FIND_VERSION_COUNT EQUAL 1) + set(_VERSION_REGEX "[^.]*") + elseif (${_NAME}_FIND_VERSION_COUNT EQUAL 2) + set(_VERSION_REGEX "[^.]*\\.[^.]*") + elseif (${_NAME}_FIND_VERSION_COUNT EQUAL 3) + set(_VERSION_REGEX "[^.]*\\.[^.]*\\.[^.]*") + else () + set(_VERSION_REGEX "[^.]*\\.[^.]*\\.[^.]*\\.[^.]*") + endif () + string(REGEX REPLACE "^(${_VERSION_REGEX})\\..*" "\\1" _VERSION_HEAD "${_FOUND_VERSION}") + unset(_VERSION_REGEX) + if (NOT ${_NAME}_FIND_VERSION VERSION_EQUAL _VERSION_HEAD) + set(VERSION_MSG "Found unsuitable version \"${_FOUND_VERSION}\", but required is exact version \"${${_NAME}_FIND_VERSION}\"") + set(VERSION_OK FALSE) + else () + set(VERSION_MSG "(found suitable exact version \"${_FOUND_VERSION}\")") + endif () + unset(_VERSION_HEAD) + else () + if (NOT ${_NAME}_FIND_VERSION VERSION_EQUAL _FOUND_VERSION) + set(VERSION_MSG "Found unsuitable version \"${_FOUND_VERSION}\", but required is exact version \"${${_NAME}_FIND_VERSION}\"") + set(VERSION_OK FALSE) + else () + set(VERSION_MSG "(found suitable exact version \"${_FOUND_VERSION}\")") + endif () + endif () + unset(_VERSION_DOTS) + + else() # minimum version specified: + if (${_NAME}_FIND_VERSION VERSION_GREATER _FOUND_VERSION) + set(VERSION_MSG "Found unsuitable version \"${_FOUND_VERSION}\", but required is at least \"${${_NAME}_FIND_VERSION}\"") + set(VERSION_OK FALSE) + else () + set(VERSION_MSG "(found suitable version \"${_FOUND_VERSION}\", minimum required is \"${${_NAME}_FIND_VERSION}\")") + endif () + endif() + + else() + + # if the package was not found, but a version was given, add that to the output: + if(${_NAME}_FIND_VERSION_EXACT) + set(VERSION_MSG "(Required is exact version \"${${_NAME}_FIND_VERSION}\")") + else() + set(VERSION_MSG "(Required is at least version \"${${_NAME}_FIND_VERSION}\")") + endif() + + endif() + else () + # Check with DEFINED as the found version may be 0. + if(DEFINED ${FPHSA_VERSION_VAR}) + set(VERSION_MSG "(found version \"${${FPHSA_VERSION_VAR}}\")") + endif() + endif () + + if(VERSION_OK) + string(APPEND DETAILS "[v${${FPHSA_VERSION_VAR}}(${${_NAME}_FIND_VERSION})]") + else() + set(${_NAME}_FOUND FALSE) + endif() + + + # print the result: + if (${_NAME}_FOUND) + FIND_PACKAGE_MESSAGE(${_NAME} "Found ${_NAME}: ${${_FIRST_REQUIRED_VAR}} ${VERSION_MSG} ${COMPONENT_MSG}" "${DETAILS}") + else () + + if(FPHSA_CONFIG_MODE) + _FPHSA_HANDLE_FAILURE_CONFIG_MODE() + else() + if(NOT VERSION_OK) + set(RESULT_MSG) + if (_FIRST_REQUIRED_VAR) + string (APPEND RESULT_MSG "found ${${_FIRST_REQUIRED_VAR}}") + endif() + if (COMPONENT_MSG) + if (RESULT_MSG) + string (APPEND RESULT_MSG ", ") + endif() + string (APPEND RESULT_MSG "${FOUND_COMPONENTS_MSG}") + endif() + _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: ${VERSION_MSG} (${RESULT_MSG})") + else() + _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE} (missing:${MISSING_VARS}) ${VERSION_MSG}") + endif() + endif() + + endif () + + set(${_NAME}_FOUND ${${_NAME}_FOUND} PARENT_SCOPE) + set(${_NAME_UPPER}_FOUND ${${_NAME}_FOUND} PARENT_SCOPE) +endfunction() diff --git a/cmake/FindPackageMessage.cmake b/cmake/FindPackageMessage.cmake new file mode 100644 index 000000000..0628b9816 --- /dev/null +++ b/cmake/FindPackageMessage.cmake @@ -0,0 +1,48 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +#[=======================================================================[.rst: +FindPackageMessage +------------------ + +.. code-block:: cmake + + find_package_message( "message for user" "find result details") + +This function is intended to be used in FindXXX.cmake modules files. +It will print a message once for each unique find result. This is +useful for telling the user where a package was found. The first +argument specifies the name (XXX) of the package. The second argument +specifies the message to display. The third argument lists details +about the find result so that if they change the message will be +displayed again. The macro also obeys the QUIET argument to the +find_package command. + +Example: + +.. code-block:: cmake + + if(X11_FOUND) + find_package_message(X11 "Found X11: ${X11_X11_LIB}" + "[${X11_X11_LIB}][${X11_INCLUDE_DIR}]") + else() + ... + endif() +#]=======================================================================] + +function(find_package_message pkg msg details) + # Avoid printing a message repeatedly for the same find result. + if(NOT ${pkg}_FIND_QUIETLY) + string(REPLACE "\n" "" details "${details}") + set(DETAILS_VAR FIND_PACKAGE_MESSAGE_DETAILS_${pkg}) + if(NOT "${details}" STREQUAL "${${DETAILS_VAR}}") + # The message has not yet been printed. + message(STATUS "${msg}") + + # Save the find details in the cache to avoid printing the same + # message again. + set("${DETAILS_VAR}" "${details}" + CACHE INTERNAL "Details about finding ${pkg}") + endif() + endif() +endfunction() diff --git a/cmake/FindSQLite3.cmake b/cmake/FindSQLite3.cmake new file mode 100644 index 000000000..374d7af6c --- /dev/null +++ b/cmake/FindSQLite3.cmake @@ -0,0 +1,66 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +#[=======================================================================[.rst: +FindSQLite3 +----------- + +Find the SQLite libraries, v3 + +IMPORTED targets +^^^^^^^^^^^^^^^^ + +This module defines the following :prop_tgt:`IMPORTED` target: + +``SQLite::SQLite3`` + +Result variables +^^^^^^^^^^^^^^^^ + +This module will set the following variables if found: + +``SQLite3_INCLUDE_DIRS`` + where to find sqlite3.h, etc. +``SQLite3_LIBRARIES`` + the libraries to link against to use SQLite3. +``SQLite3_VERSION`` + version of the SQLite3 library found +``SQLite3_FOUND`` + TRUE if found + +#]=======================================================================] + +# Look for the necessary header +find_path(SQLite3_INCLUDE_DIR NAMES sqlite3.h) +mark_as_advanced(SQLite3_INCLUDE_DIR) + +# Look for the necessary library +find_library(SQLite3_LIBRARY NAMES sqlite3 sqlite) +mark_as_advanced(SQLite3_LIBRARY) + +# Extract version information from the header file +if(SQLite3_INCLUDE_DIR) + file(STRINGS ${SQLite3_INCLUDE_DIR}/sqlite3.h _ver_line + REGEX "^#define SQLITE_VERSION *\"[0-9]+\\.[0-9]+\\.[0-9]+\"" + LIMIT_COUNT 1) + string(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+" + SQLite3_VERSION "${_ver_line}") + unset(_ver_line) +endif() + +include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake) +find_package_handle_standard_args(SQLite3 + REQUIRED_VARS SQLite3_INCLUDE_DIR SQLite3_LIBRARY + VERSION_VAR SQLite3_VERSION) + +# Create the imported target +if(SQLite3_FOUND) + set(SQLite3_INCLUDE_DIRS ${SQLite3_INCLUDE_DIR}) + set(SQLite3_LIBRARIES ${SQLite3_LIBRARY}) + if(NOT TARGET SQLite::SQLite3) + add_library(SQLite::SQLite3 UNKNOWN IMPORTED) + set_target_properties(SQLite::SQLite3 PROPERTIES + IMPORTED_LOCATION "${SQLite3_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${SQLite3_INCLUDE_DIR}") + endif() +endif() diff --git a/llarp/CMakeLists.txt b/llarp/CMakeLists.txt index 4f89f6fad..1b766aae9 100644 --- a/llarp/CMakeLists.txt +++ b/llarp/CMakeLists.txt @@ -28,6 +28,8 @@ add_dependencies(lokinet-util genversion) target_include_directories(lokinet-util PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${PROJECT_SOURCE_DIR}/include) +find_package(SQLite3 REQUIRED) + target_link_libraries(lokinet-util PUBLIC lokinet-cryptography nlohmann_json::nlohmann_json From 8628212269723cbd3acc8aa58f374af50c470287 Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Thu, 25 Jun 2020 15:53:00 -0600 Subject: [PATCH 31/70] <3 jsonnet --- .drone.jsonnet | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.drone.jsonnet b/.drone.jsonnet index 8ec0bf8a2..b5bdb3f67 100644 --- a/.drone.jsonnet +++ b/.drone.jsonnet @@ -1,4 +1,4 @@ -local default_deps_base='libsystemd-dev python3-dev libcurl4-openssl-dev libuv1-dev libunbound-dev nettle-dev libssl-dev libevent-dev' libsqlite3-dev; +local default_deps_base='libsystemd-dev python3-dev libcurl4-openssl-dev libuv1-dev libunbound-dev nettle-dev libssl-dev libevent-dev libsqlite3-dev'; local default_deps_nocxx='libsodium-dev ' + default_deps_base; // libsodium-dev needs to be >= 1.0.18 local default_deps='g++ ' + default_deps_nocxx; // g++ sometimes needs replacement local default_windows_deps='mingw-w64-binutils mingw-w64-gcc mingw-w64-crt mingw-w64-headers mingw-w64-winpthreads perl openssh zip bash'; // deps for windows cross compile From 0ecdda7a891961d450d700a73852627d77cf4f6f Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Thu, 25 Jun 2020 15:59:42 -0600 Subject: [PATCH 32/70] make format --- llarp/router/router.cpp | 17 ++++++++--------- llarp/router/router.hpp | 1 - llarp/tooling/router_hive.cpp | 5 ++--- llarp/tooling/router_hive.hpp | 6 ++---- 4 files changed, 12 insertions(+), 17 deletions(-) diff --git a/llarp/router/router.cpp b/llarp/router/router.cpp index 6589660a5..7da06ccbc 100644 --- a/llarp/router/router.cpp +++ b/llarp/router/router.cpp @@ -86,15 +86,14 @@ namespace llarp if (m_peerDb) peerStatsObj = m_peerDb->ExtractStatus(); - return util::StatusObject{ - {"running", true}, - {"numNodesKnown", _nodedb->num_loaded()}, - {"dht", _dht->impl->ExtractStatus()}, - {"services", _hiddenServiceContext.ExtractStatus()}, - {"exit", _exitContext.ExtractStatus()}, - {"links", _linkManager.ExtractStatus()}, - {"outboundMessages", _outboundMessageHandler.ExtractStatus()}, - {"peerStats", peerStatsObj}}; + return util::StatusObject{{"running", true}, + {"numNodesKnown", _nodedb->num_loaded()}, + {"dht", _dht->impl->ExtractStatus()}, + {"services", _hiddenServiceContext.ExtractStatus()}, + {"exit", _exitContext.ExtractStatus()}, + {"links", _linkManager.ExtractStatus()}, + {"outboundMessages", _outboundMessageHandler.ExtractStatus()}, + {"peerStats", peerStatsObj}}; } else { diff --git a/llarp/router/router.hpp b/llarp/router/router.hpp index 5af8c6b38..e49f0329c 100644 --- a/llarp/router/router.hpp +++ b/llarp/router/router.hpp @@ -51,7 +51,6 @@ namespace llarp { struct Router final : public AbstractRouter { - llarp_time_t _lastPump = 0s; bool ready; // transient iwp encryption key diff --git a/llarp/tooling/router_hive.cpp b/llarp/tooling/router_hive.cpp index 2e6e38418..7001c70ea 100644 --- a/llarp/tooling/router_hive.cpp +++ b/llarp/tooling/router_hive.cpp @@ -163,9 +163,8 @@ namespace tooling llarp::AbstractRouter* RouterHive::GetRelay(const llarp::RouterID& id, bool needMutexLock) { - auto guard = needMutexLock - ? std::make_optional>(routerMutex) - : std::nullopt; + auto guard = + needMutexLock ? std::make_optional>(routerMutex) : std::nullopt; auto itr = relays.find(id); if (itr == relays.end()) diff --git a/llarp/tooling/router_hive.hpp b/llarp/tooling/router_hive.hpp index 3260db0ba..fb9a35384 100644 --- a/llarp/tooling/router_hive.hpp +++ b/llarp/tooling/router_hive.hpp @@ -17,7 +17,7 @@ namespace llarp { struct Context; struct AbstractRouter; -} +} // namespace llarp namespace tooling { @@ -30,9 +30,7 @@ namespace tooling StartRouters(bool isRelay); void - AddRouter( - const std::shared_ptr& config, - bool isRelay); + AddRouter(const std::shared_ptr& config, bool isRelay); /// safely visit router void From 6e1a23cdc7d7335a3ac188fc1107376e5c9b86ba Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Thu, 25 Jun 2020 17:11:07 -0600 Subject: [PATCH 33/70] Use pkg_check_modules instead of find_package for sqlite3 --- cmake/FindPackageHandleStandardArgs.cmake | 466 ---------------------- cmake/FindPackageMessage.cmake | 48 --- cmake/FindSQLite3.cmake | 66 --- llarp/CMakeLists.txt | 8 +- 4 files changed, 7 insertions(+), 581 deletions(-) delete mode 100644 cmake/FindPackageHandleStandardArgs.cmake delete mode 100644 cmake/FindPackageMessage.cmake delete mode 100644 cmake/FindSQLite3.cmake diff --git a/cmake/FindPackageHandleStandardArgs.cmake b/cmake/FindPackageHandleStandardArgs.cmake deleted file mode 100644 index 4fb08259a..000000000 --- a/cmake/FindPackageHandleStandardArgs.cmake +++ /dev/null @@ -1,466 +0,0 @@ -# Distributed under the OSI-approved BSD 3-Clause License. See accompanying -# file Copyright.txt or https://cmake.org/licensing for details. - -#[=======================================================================[.rst: -FindPackageHandleStandardArgs ------------------------------ - -This module provides a function intended to be used in :ref:`Find Modules` -implementing :command:`find_package()` calls. It handles the -``REQUIRED``, ``QUIET`` and version-related arguments of ``find_package``. -It also sets the ``_FOUND`` variable. The package is -considered found if all variables listed contain valid results, e.g. -valid filepaths. - -.. command:: find_package_handle_standard_args - - There are two signatures:: - - find_package_handle_standard_args( - (DEFAULT_MSG|) - ... - ) - - find_package_handle_standard_args( - [FOUND_VAR ] - [REQUIRED_VARS ...] - [VERSION_VAR ] - [HANDLE_COMPONENTS] - [CONFIG_MODE] - [NAME_MISMATCHED] - [REASON_FAILURE_MESSAGE ] - [FAIL_MESSAGE ] - ) - - The ``_FOUND`` variable will be set to ``TRUE`` if all - the variables ``...`` are valid and any optional - constraints are satisfied, and ``FALSE`` otherwise. A success or - failure message may be displayed based on the results and on - whether the ``REQUIRED`` and/or ``QUIET`` option was given to - the :command:`find_package` call. - - The options are: - - ``(DEFAULT_MSG|)`` - In the simple signature this specifies the failure message. - Use ``DEFAULT_MSG`` to ask for a default message to be computed - (recommended). Not valid in the full signature. - - ``FOUND_VAR `` - Obsolete. Specifies either ``_FOUND`` or - ``_FOUND`` as the result variable. This exists only - for compatibility with older versions of CMake and is now ignored. - Result variables of both names are always set for compatibility. - - ``REQUIRED_VARS ...`` - Specify the variables which are required for this package. - These may be named in the generated failure message asking the - user to set the missing variable values. Therefore these should - typically be cache entries such as ``FOO_LIBRARY`` and not output - variables like ``FOO_LIBRARIES``. This option is mandatory if - ``HANDLE_COMPONENTS`` is not specified. - - ``VERSION_VAR `` - Specify the name of a variable that holds the version of the package - that has been found. This version will be checked against the - (potentially) specified required version given to the - :command:`find_package` call, including its ``EXACT`` option. - The default messages include information about the required - version and the version which has been actually found, both - if the version is ok or not. - - ``HANDLE_COMPONENTS`` - Enable handling of package components. In this case, the command - will report which components have been found and which are missing, - and the ``_FOUND`` variable will be set to ``FALSE`` - if any of the required components (i.e. not the ones listed after - the ``OPTIONAL_COMPONENTS`` option of :command:`find_package`) are - missing. - - ``CONFIG_MODE`` - Specify that the calling find module is a wrapper around a - call to ``find_package( NO_MODULE)``. This implies - a ``VERSION_VAR`` value of ``_VERSION``. The command - will automatically check whether the package configuration file - was found. - - ``REASON_FAILURE_MESSAGE `` - Specify a custom message of the reason for the failure which will be - appended to the default generated message. - - ``FAIL_MESSAGE `` - Specify a custom failure message instead of using the default - generated message. Not recommended. - - ``NAME_MISMATCHED`` - Indicate that the ```` does not match - ``${CMAKE_FIND_PACKAGE_NAME}``. This is usually a mistake and raises a - warning, but it may be intentional for usage of the command for components - of a larger package. - -Example for the simple signature: - -.. code-block:: cmake - - find_package_handle_standard_args(LibXml2 DEFAULT_MSG - LIBXML2_LIBRARY LIBXML2_INCLUDE_DIR) - -The ``LibXml2`` package is considered to be found if both -``LIBXML2_LIBRARY`` and ``LIBXML2_INCLUDE_DIR`` are valid. -Then also ``LibXml2_FOUND`` is set to ``TRUE``. If it is not found -and ``REQUIRED`` was used, it fails with a -:command:`message(FATAL_ERROR)`, independent whether ``QUIET`` was -used or not. If it is found, success will be reported, including -the content of the first ````. On repeated CMake runs, -the same message will not be printed again. - -.. note:: - - If ```` does not match ``CMAKE_FIND_PACKAGE_NAME`` for the - calling module, a warning that there is a mismatch is given. The - ``FPHSA_NAME_MISMATCHED`` variable may be set to bypass the warning if using - the old signature and the ``NAME_MISMATCHED`` argument using the new - signature. To avoid forcing the caller to require newer versions of CMake for - usage, the variable's value will be used if defined when the - ``NAME_MISMATCHED`` argument is not passed for the new signature (but using - both is an error).. - -Example for the full signature: - -.. code-block:: cmake - - find_package_handle_standard_args(LibArchive - REQUIRED_VARS LibArchive_LIBRARY LibArchive_INCLUDE_DIR - VERSION_VAR LibArchive_VERSION) - -In this case, the ``LibArchive`` package is considered to be found if -both ``LibArchive_LIBRARY`` and ``LibArchive_INCLUDE_DIR`` are valid. -Also the version of ``LibArchive`` will be checked by using the version -contained in ``LibArchive_VERSION``. Since no ``FAIL_MESSAGE`` is given, -the default messages will be printed. - -Another example for the full signature: - -.. code-block:: cmake - - find_package(Automoc4 QUIET NO_MODULE HINTS /opt/automoc4) - find_package_handle_standard_args(Automoc4 CONFIG_MODE) - -In this case, a ``FindAutmoc4.cmake`` module wraps a call to -``find_package(Automoc4 NO_MODULE)`` and adds an additional search -directory for ``automoc4``. Then the call to -``find_package_handle_standard_args`` produces a proper success/failure -message. -#]=======================================================================] - -include(${CMAKE_CURRENT_LIST_DIR}/FindPackageMessage.cmake) - -# internal helper macro -macro(_FPHSA_FAILURE_MESSAGE _msg) - set (__msg "${_msg}") - if (FPHSA_REASON_FAILURE_MESSAGE) - string(APPEND __msg "\n Reason given by package: ${FPHSA_REASON_FAILURE_MESSAGE}\n") - endif() - if (${_NAME}_FIND_REQUIRED) - message(FATAL_ERROR "${__msg}") - else () - if (NOT ${_NAME}_FIND_QUIETLY) - message(STATUS "${__msg}") - endif () - endif () -endmacro() - - -# internal helper macro to generate the failure message when used in CONFIG_MODE: -macro(_FPHSA_HANDLE_FAILURE_CONFIG_MODE) - # _CONFIG is set, but FOUND is false, this means that some other of the REQUIRED_VARS was not found: - if(${_NAME}_CONFIG) - _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: missing:${MISSING_VARS} (found ${${_NAME}_CONFIG} ${VERSION_MSG})") - else() - # If _CONSIDERED_CONFIGS is set, the config-file has been found, but no suitable version. - # List them all in the error message: - if(${_NAME}_CONSIDERED_CONFIGS) - set(configsText "") - list(LENGTH ${_NAME}_CONSIDERED_CONFIGS configsCount) - math(EXPR configsCount "${configsCount} - 1") - foreach(currentConfigIndex RANGE ${configsCount}) - list(GET ${_NAME}_CONSIDERED_CONFIGS ${currentConfigIndex} filename) - list(GET ${_NAME}_CONSIDERED_VERSIONS ${currentConfigIndex} version) - string(APPEND configsText "\n ${filename} (version ${version})") - endforeach() - if (${_NAME}_NOT_FOUND_MESSAGE) - if (FPHSA_REASON_FAILURE_MESSAGE) - string(PREPEND FPHSA_REASON_FAILURE_MESSAGE "${${_NAME}_NOT_FOUND_MESSAGE}\n ") - else() - set(FPHSA_REASON_FAILURE_MESSAGE "${${_NAME}_NOT_FOUND_MESSAGE}") - endif() - else() - string(APPEND configsText "\n") - endif() - _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE} ${VERSION_MSG}, checked the following files:${configsText}") - - else() - # Simple case: No Config-file was found at all: - _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: found neither ${_NAME}Config.cmake nor ${_NAME_LOWER}-config.cmake ${VERSION_MSG}") - endif() - endif() -endmacro() - - -function(FIND_PACKAGE_HANDLE_STANDARD_ARGS _NAME _FIRST_ARG) - - # Set up the arguments for `cmake_parse_arguments`. - set(options CONFIG_MODE HANDLE_COMPONENTS NAME_MISMATCHED) - set(oneValueArgs FAIL_MESSAGE REASON_FAILURE_MESSAGE VERSION_VAR FOUND_VAR) - set(multiValueArgs REQUIRED_VARS) - - # Check whether we are in 'simple' or 'extended' mode: - set(_KEYWORDS_FOR_EXTENDED_MODE ${options} ${oneValueArgs} ${multiValueArgs} ) - list(FIND _KEYWORDS_FOR_EXTENDED_MODE "${_FIRST_ARG}" INDEX) - - unset(FPHSA_NAME_MISMATCHED_override) - if (DEFINED FPHSA_NAME_MISMATCHED) - # If the variable NAME_MISMATCHED variable is set, error if it is passed as - # an argument. The former is for old signatures, the latter is for new - # signatures. - list(FIND ARGN "NAME_MISMATCHED" name_mismatched_idx) - if (NOT name_mismatched_idx EQUAL "-1") - message(FATAL_ERROR - "The `NAME_MISMATCHED` argument may only be specified by the argument or " - "the variable, not both.") - endif () - - # But use the variable if it is not an argument to avoid forcing minimum - # CMake version bumps for calling modules. - set(FPHSA_NAME_MISMATCHED_override "${FPHSA_NAME_MISMATCHED}") - endif () - - if(${INDEX} EQUAL -1) - set(FPHSA_FAIL_MESSAGE ${_FIRST_ARG}) - set(FPHSA_REQUIRED_VARS ${ARGN}) - set(FPHSA_VERSION_VAR) - else() - cmake_parse_arguments(FPHSA "${options}" "${oneValueArgs}" "${multiValueArgs}" ${_FIRST_ARG} ${ARGN}) - - if(FPHSA_UNPARSED_ARGUMENTS) - message(FATAL_ERROR "Unknown keywords given to FIND_PACKAGE_HANDLE_STANDARD_ARGS(): \"${FPHSA_UNPARSED_ARGUMENTS}\"") - endif() - - if(NOT FPHSA_FAIL_MESSAGE) - set(FPHSA_FAIL_MESSAGE "DEFAULT_MSG") - endif() - - # In config-mode, we rely on the variable _CONFIG, which is set by find_package() - # when it successfully found the config-file, including version checking: - if(FPHSA_CONFIG_MODE) - list(INSERT FPHSA_REQUIRED_VARS 0 ${_NAME}_CONFIG) - list(REMOVE_DUPLICATES FPHSA_REQUIRED_VARS) - set(FPHSA_VERSION_VAR ${_NAME}_VERSION) - endif() - - if(NOT FPHSA_REQUIRED_VARS AND NOT FPHSA_HANDLE_COMPONENTS) - message(FATAL_ERROR "No REQUIRED_VARS specified for FIND_PACKAGE_HANDLE_STANDARD_ARGS()") - endif() - endif() - - if (DEFINED FPHSA_NAME_MISMATCHED_override) - set(FPHSA_NAME_MISMATCHED "${FPHSA_NAME_MISMATCHED_override}") - endif () - - if (DEFINED CMAKE_FIND_PACKAGE_NAME - AND NOT FPHSA_NAME_MISMATCHED - AND NOT _NAME STREQUAL CMAKE_FIND_PACKAGE_NAME) - message(AUTHOR_WARNING - "The package name passed to `find_package_handle_standard_args` " - "(${_NAME}) does not match the name of the calling package " - "(${CMAKE_FIND_PACKAGE_NAME}). This can lead to problems in calling " - "code that expects `find_package` result variables (e.g., `_FOUND`) " - "to follow a certain pattern.") - endif () - -# now that we collected all arguments, process them - - if("x${FPHSA_FAIL_MESSAGE}" STREQUAL "xDEFAULT_MSG") - set(FPHSA_FAIL_MESSAGE "Could NOT find ${_NAME}") - endif() - - if (FPHSA_REQUIRED_VARS) - list(GET FPHSA_REQUIRED_VARS 0 _FIRST_REQUIRED_VAR) - endif() - - string(TOUPPER ${_NAME} _NAME_UPPER) - string(TOLOWER ${_NAME} _NAME_LOWER) - - if(FPHSA_FOUND_VAR) - set(_FOUND_VAR_UPPER ${_NAME_UPPER}_FOUND) - set(_FOUND_VAR_MIXED ${_NAME}_FOUND) - if(FPHSA_FOUND_VAR STREQUAL _FOUND_VAR_MIXED OR FPHSA_FOUND_VAR STREQUAL _FOUND_VAR_UPPER) - set(_FOUND_VAR ${FPHSA_FOUND_VAR}) - else() - message(FATAL_ERROR "The argument for FOUND_VAR is \"${FPHSA_FOUND_VAR}\", but only \"${_FOUND_VAR_MIXED}\" and \"${_FOUND_VAR_UPPER}\" are valid names.") - endif() - else() - set(_FOUND_VAR ${_NAME_UPPER}_FOUND) - endif() - - # collect all variables which were not found, so they can be printed, so the - # user knows better what went wrong (#6375) - set(MISSING_VARS "") - set(DETAILS "") - # check if all passed variables are valid - set(FPHSA_FOUND_${_NAME} TRUE) - foreach(_CURRENT_VAR ${FPHSA_REQUIRED_VARS}) - if(NOT ${_CURRENT_VAR}) - set(FPHSA_FOUND_${_NAME} FALSE) - string(APPEND MISSING_VARS " ${_CURRENT_VAR}") - else() - string(APPEND DETAILS "[${${_CURRENT_VAR}}]") - endif() - endforeach() - if(FPHSA_FOUND_${_NAME}) - set(${_NAME}_FOUND TRUE) - set(${_NAME_UPPER}_FOUND TRUE) - else() - set(${_NAME}_FOUND FALSE) - set(${_NAME_UPPER}_FOUND FALSE) - endif() - - # component handling - unset(FOUND_COMPONENTS_MSG) - unset(MISSING_COMPONENTS_MSG) - - if(FPHSA_HANDLE_COMPONENTS) - foreach(comp ${${_NAME}_FIND_COMPONENTS}) - if(${_NAME}_${comp}_FOUND) - - if(NOT DEFINED FOUND_COMPONENTS_MSG) - set(FOUND_COMPONENTS_MSG "found components:") - endif() - string(APPEND FOUND_COMPONENTS_MSG " ${comp}") - - else() - - if(NOT DEFINED MISSING_COMPONENTS_MSG) - set(MISSING_COMPONENTS_MSG "missing components:") - endif() - string(APPEND MISSING_COMPONENTS_MSG " ${comp}") - - if(${_NAME}_FIND_REQUIRED_${comp}) - set(${_NAME}_FOUND FALSE) - string(APPEND MISSING_VARS " ${comp}") - endif() - - endif() - endforeach() - set(COMPONENT_MSG "${FOUND_COMPONENTS_MSG} ${MISSING_COMPONENTS_MSG}") - string(APPEND DETAILS "[c${COMPONENT_MSG}]") - endif() - - # version handling: - set(VERSION_MSG "") - set(VERSION_OK TRUE) - - # check with DEFINED here as the requested or found version may be "0" - if (DEFINED ${_NAME}_FIND_VERSION) - if(DEFINED ${FPHSA_VERSION_VAR}) - set(_FOUND_VERSION ${${FPHSA_VERSION_VAR}}) - - if(${_NAME}_FIND_VERSION_EXACT) # exact version required - # count the dots in the version string - string(REGEX REPLACE "[^.]" "" _VERSION_DOTS "${_FOUND_VERSION}") - # add one dot because there is one dot more than there are components - string(LENGTH "${_VERSION_DOTS}." _VERSION_DOTS) - if (_VERSION_DOTS GREATER ${_NAME}_FIND_VERSION_COUNT) - # Because of the C++ implementation of find_package() ${_NAME}_FIND_VERSION_COUNT - # is at most 4 here. Therefore a simple lookup table is used. - if (${_NAME}_FIND_VERSION_COUNT EQUAL 1) - set(_VERSION_REGEX "[^.]*") - elseif (${_NAME}_FIND_VERSION_COUNT EQUAL 2) - set(_VERSION_REGEX "[^.]*\\.[^.]*") - elseif (${_NAME}_FIND_VERSION_COUNT EQUAL 3) - set(_VERSION_REGEX "[^.]*\\.[^.]*\\.[^.]*") - else () - set(_VERSION_REGEX "[^.]*\\.[^.]*\\.[^.]*\\.[^.]*") - endif () - string(REGEX REPLACE "^(${_VERSION_REGEX})\\..*" "\\1" _VERSION_HEAD "${_FOUND_VERSION}") - unset(_VERSION_REGEX) - if (NOT ${_NAME}_FIND_VERSION VERSION_EQUAL _VERSION_HEAD) - set(VERSION_MSG "Found unsuitable version \"${_FOUND_VERSION}\", but required is exact version \"${${_NAME}_FIND_VERSION}\"") - set(VERSION_OK FALSE) - else () - set(VERSION_MSG "(found suitable exact version \"${_FOUND_VERSION}\")") - endif () - unset(_VERSION_HEAD) - else () - if (NOT ${_NAME}_FIND_VERSION VERSION_EQUAL _FOUND_VERSION) - set(VERSION_MSG "Found unsuitable version \"${_FOUND_VERSION}\", but required is exact version \"${${_NAME}_FIND_VERSION}\"") - set(VERSION_OK FALSE) - else () - set(VERSION_MSG "(found suitable exact version \"${_FOUND_VERSION}\")") - endif () - endif () - unset(_VERSION_DOTS) - - else() # minimum version specified: - if (${_NAME}_FIND_VERSION VERSION_GREATER _FOUND_VERSION) - set(VERSION_MSG "Found unsuitable version \"${_FOUND_VERSION}\", but required is at least \"${${_NAME}_FIND_VERSION}\"") - set(VERSION_OK FALSE) - else () - set(VERSION_MSG "(found suitable version \"${_FOUND_VERSION}\", minimum required is \"${${_NAME}_FIND_VERSION}\")") - endif () - endif() - - else() - - # if the package was not found, but a version was given, add that to the output: - if(${_NAME}_FIND_VERSION_EXACT) - set(VERSION_MSG "(Required is exact version \"${${_NAME}_FIND_VERSION}\")") - else() - set(VERSION_MSG "(Required is at least version \"${${_NAME}_FIND_VERSION}\")") - endif() - - endif() - else () - # Check with DEFINED as the found version may be 0. - if(DEFINED ${FPHSA_VERSION_VAR}) - set(VERSION_MSG "(found version \"${${FPHSA_VERSION_VAR}}\")") - endif() - endif () - - if(VERSION_OK) - string(APPEND DETAILS "[v${${FPHSA_VERSION_VAR}}(${${_NAME}_FIND_VERSION})]") - else() - set(${_NAME}_FOUND FALSE) - endif() - - - # print the result: - if (${_NAME}_FOUND) - FIND_PACKAGE_MESSAGE(${_NAME} "Found ${_NAME}: ${${_FIRST_REQUIRED_VAR}} ${VERSION_MSG} ${COMPONENT_MSG}" "${DETAILS}") - else () - - if(FPHSA_CONFIG_MODE) - _FPHSA_HANDLE_FAILURE_CONFIG_MODE() - else() - if(NOT VERSION_OK) - set(RESULT_MSG) - if (_FIRST_REQUIRED_VAR) - string (APPEND RESULT_MSG "found ${${_FIRST_REQUIRED_VAR}}") - endif() - if (COMPONENT_MSG) - if (RESULT_MSG) - string (APPEND RESULT_MSG ", ") - endif() - string (APPEND RESULT_MSG "${FOUND_COMPONENTS_MSG}") - endif() - _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: ${VERSION_MSG} (${RESULT_MSG})") - else() - _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE} (missing:${MISSING_VARS}) ${VERSION_MSG}") - endif() - endif() - - endif () - - set(${_NAME}_FOUND ${${_NAME}_FOUND} PARENT_SCOPE) - set(${_NAME_UPPER}_FOUND ${${_NAME}_FOUND} PARENT_SCOPE) -endfunction() diff --git a/cmake/FindPackageMessage.cmake b/cmake/FindPackageMessage.cmake deleted file mode 100644 index 0628b9816..000000000 --- a/cmake/FindPackageMessage.cmake +++ /dev/null @@ -1,48 +0,0 @@ -# Distributed under the OSI-approved BSD 3-Clause License. See accompanying -# file Copyright.txt or https://cmake.org/licensing for details. - -#[=======================================================================[.rst: -FindPackageMessage ------------------- - -.. code-block:: cmake - - find_package_message( "message for user" "find result details") - -This function is intended to be used in FindXXX.cmake modules files. -It will print a message once for each unique find result. This is -useful for telling the user where a package was found. The first -argument specifies the name (XXX) of the package. The second argument -specifies the message to display. The third argument lists details -about the find result so that if they change the message will be -displayed again. The macro also obeys the QUIET argument to the -find_package command. - -Example: - -.. code-block:: cmake - - if(X11_FOUND) - find_package_message(X11 "Found X11: ${X11_X11_LIB}" - "[${X11_X11_LIB}][${X11_INCLUDE_DIR}]") - else() - ... - endif() -#]=======================================================================] - -function(find_package_message pkg msg details) - # Avoid printing a message repeatedly for the same find result. - if(NOT ${pkg}_FIND_QUIETLY) - string(REPLACE "\n" "" details "${details}") - set(DETAILS_VAR FIND_PACKAGE_MESSAGE_DETAILS_${pkg}) - if(NOT "${details}" STREQUAL "${${DETAILS_VAR}}") - # The message has not yet been printed. - message(STATUS "${msg}") - - # Save the find details in the cache to avoid printing the same - # message again. - set("${DETAILS_VAR}" "${details}" - CACHE INTERNAL "Details about finding ${pkg}") - endif() - endif() -endfunction() diff --git a/cmake/FindSQLite3.cmake b/cmake/FindSQLite3.cmake deleted file mode 100644 index 374d7af6c..000000000 --- a/cmake/FindSQLite3.cmake +++ /dev/null @@ -1,66 +0,0 @@ -# Distributed under the OSI-approved BSD 3-Clause License. See accompanying -# file Copyright.txt or https://cmake.org/licensing for details. - -#[=======================================================================[.rst: -FindSQLite3 ------------ - -Find the SQLite libraries, v3 - -IMPORTED targets -^^^^^^^^^^^^^^^^ - -This module defines the following :prop_tgt:`IMPORTED` target: - -``SQLite::SQLite3`` - -Result variables -^^^^^^^^^^^^^^^^ - -This module will set the following variables if found: - -``SQLite3_INCLUDE_DIRS`` - where to find sqlite3.h, etc. -``SQLite3_LIBRARIES`` - the libraries to link against to use SQLite3. -``SQLite3_VERSION`` - version of the SQLite3 library found -``SQLite3_FOUND`` - TRUE if found - -#]=======================================================================] - -# Look for the necessary header -find_path(SQLite3_INCLUDE_DIR NAMES sqlite3.h) -mark_as_advanced(SQLite3_INCLUDE_DIR) - -# Look for the necessary library -find_library(SQLite3_LIBRARY NAMES sqlite3 sqlite) -mark_as_advanced(SQLite3_LIBRARY) - -# Extract version information from the header file -if(SQLite3_INCLUDE_DIR) - file(STRINGS ${SQLite3_INCLUDE_DIR}/sqlite3.h _ver_line - REGEX "^#define SQLITE_VERSION *\"[0-9]+\\.[0-9]+\\.[0-9]+\"" - LIMIT_COUNT 1) - string(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+" - SQLite3_VERSION "${_ver_line}") - unset(_ver_line) -endif() - -include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake) -find_package_handle_standard_args(SQLite3 - REQUIRED_VARS SQLite3_INCLUDE_DIR SQLite3_LIBRARY - VERSION_VAR SQLite3_VERSION) - -# Create the imported target -if(SQLite3_FOUND) - set(SQLite3_INCLUDE_DIRS ${SQLite3_INCLUDE_DIR}) - set(SQLite3_LIBRARIES ${SQLite3_LIBRARY}) - if(NOT TARGET SQLite::SQLite3) - add_library(SQLite::SQLite3 UNKNOWN IMPORTED) - set_target_properties(SQLite::SQLite3 PROPERTIES - IMPORTED_LOCATION "${SQLite3_LIBRARY}" - INTERFACE_INCLUDE_DIRECTORIES "${SQLite3_INCLUDE_DIR}") - endif() -endif() diff --git a/llarp/CMakeLists.txt b/llarp/CMakeLists.txt index 1b766aae9..57d57ca44 100644 --- a/llarp/CMakeLists.txt +++ b/llarp/CMakeLists.txt @@ -28,7 +28,13 @@ add_dependencies(lokinet-util genversion) target_include_directories(lokinet-util PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${PROJECT_SOURCE_DIR}/include) -find_package(SQLite3 REQUIRED) +if(STATIC_LINK) + # sqlite3 target already set up +else() + add_library(sqlite3 INTERFACE) + pkg_check_modules(SQLITE3 REQUIRED IMPORTED_TARGET sqlite3) + target_link_libraries(sqlite3 INTERFACE PkgConfig::SQLITE3) +endif() target_link_libraries(lokinet-util PUBLIC lokinet-cryptography From fd230dd93b64160936857dba59e0d38d884d9572 Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Thu, 25 Jun 2020 17:17:04 -0600 Subject: [PATCH 34/70] <3 apple --- llarp/peerstats/peer_db.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llarp/peerstats/peer_db.cpp b/llarp/peerstats/peer_db.cpp index 0c01951c7..ccb5d70c4 100644 --- a/llarp/peerstats/peer_db.cpp +++ b/llarp/peerstats/peer_db.cpp @@ -26,7 +26,7 @@ namespace llarp std::string fileString; if (file.has_value()) { - fileString = file.value().native(); + fileString = file->native(); LogInfo("Loading PeerDb from file ", fileString); } else From 186a35c0e214c42cf1669c854828de4d9c5891e2 Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Thu, 25 Jun 2020 17:23:37 -0600 Subject: [PATCH 35/70] Appease clang (fixes for minor compilation errors) --- llarp/peerstats/types.cpp | 2 +- llarp/peerstats/types.hpp | 2 +- test/peerstats/test_peer_db.cpp | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/llarp/peerstats/types.cpp b/llarp/peerstats/types.cpp index ede210a96..c861c3527 100644 --- a/llarp/peerstats/types.cpp +++ b/llarp/peerstats/types.cpp @@ -34,7 +34,7 @@ namespace llarp } bool - PeerStats::operator==(const PeerStats& other) + PeerStats::operator==(const PeerStats& other) const { return routerId == other.routerId and numConnectionAttempts == other.numConnectionAttempts and numConnectionSuccesses == other.numConnectionSuccesses diff --git a/llarp/peerstats/types.hpp b/llarp/peerstats/types.hpp index 0a365c21b..31e425273 100644 --- a/llarp/peerstats/types.hpp +++ b/llarp/peerstats/types.hpp @@ -44,7 +44,7 @@ namespace llarp PeerStats& operator+=(const PeerStats& other); bool - operator==(const PeerStats& other); + operator==(const PeerStats& other) const; util::StatusObject toJson() const; diff --git a/test/peerstats/test_peer_db.cpp b/test/peerstats/test_peer_db.cpp index 2e2401a06..6f74a45d5 100644 --- a/test/peerstats/test_peer_db.cpp +++ b/test/peerstats/test_peer_db.cpp @@ -184,7 +184,6 @@ TEST_CASE("Test PeerDb handleGossipedRC expiry calcs", "[PeerDb]") // received "unhealthily" (after rc2 expires) const llarp_time_t s3 = s2 + 8h; const llarp_time_t r3 = e2 + 1h; // received after e2 - const llarp_time_t e3 = s3 + rcLifetime; llarp::RouterContact rc3; rc3.pubkey = llarp::PubKey(id); rc3.last_updated = s3; From eb9a862f5109f46ac666278974d8dc48353763d6 Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Thu, 25 Jun 2020 17:38:25 -0600 Subject: [PATCH 36/70] Sqlite3 deps for Windows / static targets --- cmake/StaticBuild.cmake | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/cmake/StaticBuild.cmake b/cmake/StaticBuild.cmake index 3bb312231..a0c27c71f 100644 --- a/cmake/StaticBuild.cmake +++ b/cmake/StaticBuild.cmake @@ -25,6 +25,13 @@ set(UNBOUND_SOURCE unbound-${UNBOUND_VERSION}.tar.gz) set(UNBOUND_HASH SHA256=b73677c21a71cf92f15cc8cfe76a3d875e40f65b6150081c39620b286582d536 CACHE STRING "unbound source hash") +set(SQLITE3_VERSION 3320200 CACHE STRING "sqlite3 version") +set(SQLITE3_MIRROR ${LOCAL_MIRROR} https://www.sqlite.org/2020 + CACHE STRING "sqlite3 download mirror(s)") +set(SQLITE3_SOURCE sqlite-autoconf-${SQLITE3_VERSION}.tar.gz) +set(SQLITE3_HASH SHA512=5b551a1366ce4fd5dfaa687e5021194d34315935b26dd7d71f8abc9935d03c3caea323263a8330fb42038c487cd399e95de68e451cc26d573f852f219c00a02f + CACHE STRING "sqlite3 source hash") + set(SODIUM_VERSION 1.0.18 CACHE STRING "libsodium version") set(SODIUM_MIRROR ${LOCAL_MIRROR} https://download.libsodium.org/libsodium/releases @@ -193,6 +200,9 @@ endif() build_external(sodium) add_static_target(sodium sodium_external libsodium.a) +build_external(sqlite3) +add_static_target(sqlite3 sqlite_external libsqlite3.a) + if(ZMQ_VERSION VERSION_LESS 4.3.3 AND CMAKE_CROSSCOMPILING AND ARCH_TRIPLET MATCHES mingw) set(zmq_patch PATCH_COMMAND patch -p1 -i ${PROJECT_SOURCE_DIR}/contrib/cross/patches/libzmq-pr3601-mingw-build-fix.patch From 93bafcf142aade5b534e3fa75688b811c52ec83d Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Thu, 25 Jun 2020 18:07:36 -0600 Subject: [PATCH 37/70] Set up sqlite3 deps 'if NOT TAGRET sqlite3' --- llarp/CMakeLists.txt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/llarp/CMakeLists.txt b/llarp/CMakeLists.txt index 57d57ca44..c072080b1 100644 --- a/llarp/CMakeLists.txt +++ b/llarp/CMakeLists.txt @@ -28,9 +28,7 @@ add_dependencies(lokinet-util genversion) target_include_directories(lokinet-util PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${PROJECT_SOURCE_DIR}/include) -if(STATIC_LINK) - # sqlite3 target already set up -else() +if(NOT TARGET sqlite3) add_library(sqlite3 INTERFACE) pkg_check_modules(SQLITE3 REQUIRED IMPORTED_TARGET sqlite3) target_link_libraries(sqlite3 INTERFACE PkgConfig::SQLITE3) From 1e95625f2766bbc77a7784df1058aea90224cde8 Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Thu, 25 Jun 2020 18:13:24 -0600 Subject: [PATCH 38/70] <3 apple -- avoid std::optional::value() --- test/peerstats/test_peer_db.cpp | 50 ++++++++++++++++----------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/test/peerstats/test_peer_db.cpp b/test/peerstats/test_peer_db.cpp index 6f74a45d5..b3baa3a5b 100644 --- a/test/peerstats/test_peer_db.cpp +++ b/test/peerstats/test_peer_db.cpp @@ -19,7 +19,7 @@ TEST_CASE("Test PeerDb PeerStats memory storage", "[PeerDb]") delta.numConnectionAttempts = 4; delta.peakBandwidthBytesPerSec = 5; db.accumulatePeerStats(id, delta); - CHECK(db.getCurrentPeerStats(id).value() == delta); + CHECK(* db.getCurrentPeerStats(id) == delta); delta = llarp::PeerStats(id); delta.numConnectionAttempts = 5; @@ -29,7 +29,7 @@ TEST_CASE("Test PeerDb PeerStats memory storage", "[PeerDb]") llarp::PeerStats expected(id); expected.numConnectionAttempts = 9; expected.peakBandwidthBytesPerSec = 6; - CHECK(db.getCurrentPeerStats(id).value() == expected); + CHECK(* db.getCurrentPeerStats(id) == expected); } TEST_CASE("Test PeerDb flush before load", "[PeerDb]") @@ -55,7 +55,7 @@ TEST_CASE("Test PeerDb nukes stats on load", "[PeerDb]") stats.numConnectionAttempts = 1; db.accumulatePeerStats(id, stats); - CHECK(db.getCurrentPeerStats(id).value() == stats); + CHECK(* db.getCurrentPeerStats(id) == stats); db.loadDatabase(std::nullopt); @@ -85,7 +85,7 @@ TEST_CASE("Test PeerDb file-backed database reloads properly", "[PeerDb]") auto stats = db.getCurrentPeerStats(id); CHECK(stats.has_value() == true); - CHECK(stats.value().numConnectionAttempts == 43); + CHECK(stats->numConnectionAttempts == 43); } fs::remove(filename); @@ -112,7 +112,7 @@ TEST_CASE("Test PeerDb modifyPeerStats", "[PeerDb]") auto stats = db.getCurrentPeerStats(id); CHECK(stats.has_value()); - CHECK(stats.value().numPathBuilds == 42); + CHECK(stats->numPathBuilds == 42); } TEST_CASE("Test PeerDb handleGossipedRC", "[PeerDb]") @@ -131,27 +131,27 @@ TEST_CASE("Test PeerDb handleGossipedRC", "[PeerDb]") auto stats = db.getCurrentPeerStats(id); CHECK(stats.has_value()); - CHECK(stats.value().leastRCRemainingLifetime == 0ms); // not calculated on first received RC - CHECK(stats.value().numDistinctRCsReceived == 1); - CHECK(stats.value().lastRCUpdated == 10000ms); + CHECK(stats->leastRCRemainingLifetime == 0ms); // not calculated on first received RC + CHECK(stats->numDistinctRCsReceived == 1); + CHECK(stats->lastRCUpdated == 10000ms); now = 9s; db.handleGossipedRC(rc, now); stats = db.getCurrentPeerStats(id); CHECK(stats.has_value()); // these values should remain unchanged, this is not a new RC - CHECK(stats.value().leastRCRemainingLifetime == 0ms); - CHECK(stats.value().numDistinctRCsReceived == 1); - CHECK(stats.value().lastRCUpdated == 10000ms); + CHECK(stats->leastRCRemainingLifetime == 0ms); + CHECK(stats->numDistinctRCsReceived == 1); + CHECK(stats->lastRCUpdated == 10000ms); rc.last_updated = 11s; db.handleGossipedRC(rc, now); stats = db.getCurrentPeerStats(id); // should be (previous expiration time - new received time) - CHECK(stats.value().leastRCRemainingLifetime == ((10s + rcLifetime) - now)); - CHECK(stats.value().numDistinctRCsReceived == 2); - CHECK(stats.value().lastRCUpdated == 11000ms); + CHECK(stats->leastRCRemainingLifetime == ((10s + rcLifetime) - now)); + CHECK(stats->numDistinctRCsReceived == 2); + CHECK(stats->lastRCUpdated == 11000ms); } TEST_CASE("Test PeerDb handleGossipedRC expiry calcs", "[PeerDb]") @@ -193,25 +193,25 @@ TEST_CASE("Test PeerDb handleGossipedRC expiry calcs", "[PeerDb]") db.handleGossipedRC(rc1, r1); auto stats1 = db.getCurrentPeerStats(id); CHECK(stats1.has_value()); - CHECK(stats1.value().leastRCRemainingLifetime == 0ms); - CHECK(stats1.value().numDistinctRCsReceived == 1); - CHECK(stats1.value().lastRCUpdated == s1); + CHECK(stats1->leastRCRemainingLifetime == 0ms); + CHECK(stats1->numDistinctRCsReceived == 1); + CHECK(stats1->lastRCUpdated == s1); db.handleGossipedRC(rc2, r2); auto stats2 = db.getCurrentPeerStats(id); CHECK(stats2.has_value()); - CHECK(stats2.value().leastRCRemainingLifetime == (e1 - r2)); - CHECK(stats2.value().leastRCRemainingLifetime > 0ms); // ensure positive indicates healthy - CHECK(stats2.value().numDistinctRCsReceived == 2); - CHECK(stats2.value().lastRCUpdated == s2); + CHECK(stats2->leastRCRemainingLifetime == (e1 - r2)); + CHECK(stats2->leastRCRemainingLifetime > 0ms); // ensure positive indicates healthy + CHECK(stats2->numDistinctRCsReceived == 2); + CHECK(stats2->lastRCUpdated == s2); db.handleGossipedRC(rc3, r3); auto stats3 = db.getCurrentPeerStats(id); CHECK(stats3.has_value()); - CHECK(stats3.value().leastRCRemainingLifetime == (e2 - r3)); + CHECK(stats3->leastRCRemainingLifetime == (e2 - r3)); CHECK( - stats3.value().leastRCRemainingLifetime + stats3->leastRCRemainingLifetime < 0ms); // ensure negative indicates unhealthy and we use min() - CHECK(stats3.value().numDistinctRCsReceived == 3); - CHECK(stats3.value().lastRCUpdated == s3); + CHECK(stats3->numDistinctRCsReceived == 3); + CHECK(stats3->lastRCUpdated == s3); } From e38a5075519f507f3b22d6c04c2b49d85f981886 Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Thu, 25 Jun 2020 18:27:59 -0600 Subject: [PATCH 39/70] <3 Windows (prefer fs::path::string() over native()) --- llarp/peerstats/peer_db.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llarp/peerstats/peer_db.cpp b/llarp/peerstats/peer_db.cpp index ccb5d70c4..045839d43 100644 --- a/llarp/peerstats/peer_db.cpp +++ b/llarp/peerstats/peer_db.cpp @@ -26,7 +26,7 @@ namespace llarp std::string fileString; if (file.has_value()) { - fileString = file->native(); + fileString = file->string(); LogInfo("Loading PeerDb from file ", fileString); } else From 2b2c41fdf6e0807cf19ebf7fd8b0e30c0ab8fd19 Mon Sep 17 00:00:00 2001 From: Jason Rhinelander Date: Fri, 26 Jun 2020 13:27:02 -0300 Subject: [PATCH 40/70] Fix sqlite3 external target name --- cmake/StaticBuild.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/StaticBuild.cmake b/cmake/StaticBuild.cmake index a0c27c71f..ae1748cb8 100644 --- a/cmake/StaticBuild.cmake +++ b/cmake/StaticBuild.cmake @@ -201,7 +201,7 @@ build_external(sodium) add_static_target(sodium sodium_external libsodium.a) build_external(sqlite3) -add_static_target(sqlite3 sqlite_external libsqlite3.a) +add_static_target(sqlite3 sqlite3_external libsqlite3.a) if(ZMQ_VERSION VERSION_LESS 4.3.3 AND CMAKE_CROSSCOMPILING AND ARCH_TRIPLET MATCHES mingw) From 84c83a2400bcc8cdbc38a543e30443c414faabce Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Mon, 29 Jun 2020 09:44:13 -0600 Subject: [PATCH 41/70] Add specialized subclass of Router for Hive --- include/llarp.hpp | 8 ++++++++ llarp/CMakeLists.txt | 6 +++++- llarp/context.cpp | 11 ++++++++++- llarp/router/router.hpp | 4 ++-- llarp/tooling/hive_context.cpp | 16 ++++++++++++++++ llarp/tooling/hive_context.hpp | 18 ++++++++++++++++++ llarp/tooling/hive_router.cpp | 31 +++++++++++++++++++++++++++++++ llarp/tooling/hive_router.hpp | 34 ++++++++++++++++++++++++++++++++++ llarp/tooling/router_hive.hpp | 2 ++ pybind/llarp/router.cpp | 13 +++++++++++++ 10 files changed, 139 insertions(+), 4 deletions(-) create mode 100644 llarp/tooling/hive_context.cpp create mode 100644 llarp/tooling/hive_context.hpp create mode 100644 llarp/tooling/hive_router.cpp create mode 100644 llarp/tooling/hive_router.hpp diff --git a/include/llarp.hpp b/include/llarp.hpp index 762bf38b1..4c07e764e 100644 --- a/include/llarp.hpp +++ b/include/llarp.hpp @@ -90,6 +90,14 @@ namespace llarp bool CallSafe(std::function f); + /// Creates a router. Can be overridden to allow a different class of router + /// to be created instead. Defaults to llarp::Router. + virtual std::unique_ptr + makeRouter( + std::shared_ptr worker, + llarp_ev_loop_ptr __netloop, + std::shared_ptr logic); + #ifdef LOKINET_HIVE void InjectHive(tooling::RouterHive* hive); diff --git a/llarp/CMakeLists.txt b/llarp/CMakeLists.txt index c072080b1..5cdd64b84 100644 --- a/llarp/CMakeLists.txt +++ b/llarp/CMakeLists.txt @@ -227,7 +227,11 @@ if(TESTNET) endif() if(WITH_HIVE) - target_sources(liblokinet PRIVATE tooling/router_hive.cpp) + target_sources(liblokinet PRIVATE + tooling/router_hive.cpp + tooling/hive_router.cpp + tooling/hive_context.cpp + ) endif() target_link_libraries(liblokinet PUBLIC cxxopts lokinet-platform lokinet-util lokinet-cryptography) diff --git a/llarp/context.cpp b/llarp/context.cpp index 7daa00818..7925e7836 100644 --- a/llarp/context.cpp +++ b/llarp/context.cpp @@ -90,7 +90,7 @@ namespace llarp crypto = std::make_unique(); cryptoManager = std::make_unique(crypto.get()); - router = std::make_unique(mainloop, logic); + router = makeRouter(worker, mainloop, logic); nodedb = std::make_unique( nodedb_dir, [r = router.get()](auto call) { r->QueueDiskIO(std::move(call)); }); @@ -105,6 +105,15 @@ namespace llarp throw std::runtime_error("Config::Setup() failed to load database"); } + std::unique_ptr + Context::makeRouter( + std::shared_ptr worker, + llarp_ev_loop_ptr netloop, + std::shared_ptr logic) + { + return std::make_unique(worker, netloop, logic); + } + int Context::Run(const RuntimeOptions& opts) { diff --git a/llarp/router/router.hpp b/llarp/router/router.hpp index e49f0329c..8d49d8ddf 100644 --- a/llarp/router/router.hpp +++ b/llarp/router/router.hpp @@ -49,7 +49,7 @@ namespace llarp { - struct Router final : public AbstractRouter + struct Router : public AbstractRouter { llarp_time_t _lastPump = 0s; bool ready; @@ -318,7 +318,7 @@ namespace llarp explicit Router(llarp_ev_loop_ptr __netloop, std::shared_ptr logic); - ~Router() override; + virtual ~Router() override; bool HandleRecvLinkMessageBuffer(ILinkSession* from, const llarp_buffer_t& msg) override; diff --git a/llarp/tooling/hive_context.cpp b/llarp/tooling/hive_context.cpp new file mode 100644 index 000000000..94b9ef770 --- /dev/null +++ b/llarp/tooling/hive_context.cpp @@ -0,0 +1,16 @@ +#include + +#include + +namespace tooling +{ + std::unique_ptr + HiveContext::makeRouter( + std::shared_ptr worker, + llarp_ev_loop_ptr netloop, + std::shared_ptr logic) + { + return std::make_unique(worker, netloop, logic); + } + +} // namespace tooling diff --git a/llarp/tooling/hive_context.hpp b/llarp/tooling/hive_context.hpp new file mode 100644 index 000000000..8851b3835 --- /dev/null +++ b/llarp/tooling/hive_context.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include + +namespace tooling +{ + /// HiveContext is a subclass of llarp::Context which allows RouterHive to + /// perform custom behavior which might be undesirable in production code. + struct HiveContext : public llarp::Context + { + std::unique_ptr + makeRouter( + std::shared_ptr worker, + llarp_ev_loop_ptr netloop, + std::shared_ptr logic) override; + }; + +} // namespace tooling diff --git a/llarp/tooling/hive_router.cpp b/llarp/tooling/hive_router.cpp new file mode 100644 index 000000000..55bae5cf8 --- /dev/null +++ b/llarp/tooling/hive_router.cpp @@ -0,0 +1,31 @@ +#include + +namespace tooling +{ + HiveRouter::HiveRouter( + std::shared_ptr worker, + llarp_ev_loop_ptr netloop, + std::shared_ptr logic) + : Router(worker, netloop, logic) + { + } + + bool + HiveRouter::disableGossipingRC_TestingOnly() + { + return m_disableGossiping; + } + + void + HiveRouter::disableGossiping() + { + m_disableGossiping = false; + } + + void + HiveRouter::enableGossiping() + { + m_disableGossiping = true; + } + +} // namespace tooling diff --git a/llarp/tooling/hive_router.hpp b/llarp/tooling/hive_router.hpp new file mode 100644 index 000000000..743588550 --- /dev/null +++ b/llarp/tooling/hive_router.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include + +namespace tooling +{ + /// HiveRouter is a subclass of Router which overrides specific behavior in + /// order to perform testing-related functions. It exists largely to prevent + /// this behavior (which may often be "dangerous") from leaking into release + /// code. + struct HiveRouter : public llarp::Router + { + HiveRouter( + std::shared_ptr worker, + llarp_ev_loop_ptr netloop, + std::shared_ptr logic); + + virtual ~HiveRouter() = default; + + /// Override logic to prevent base Router class from gossiping its RC. + virtual bool + disableGossipingRC_TestingOnly() override; + + void + disableGossiping(); + + void + enableGossiping(); + + protected: + bool m_disableGossiping = false; + }; + +} // namespace tooling diff --git a/llarp/tooling/router_hive.hpp b/llarp/tooling/router_hive.hpp index fb9a35384..924a0554e 100644 --- a/llarp/tooling/router_hive.hpp +++ b/llarp/tooling/router_hive.hpp @@ -21,6 +21,8 @@ namespace llarp namespace tooling { + struct HiveRouter; // Hive's version of Router + struct RouterHive { using Context_ptr = std::shared_ptr; diff --git a/pybind/llarp/router.cpp b/pybind/llarp/router.cpp index 7531c27c5..d6d74e40d 100644 --- a/pybind/llarp/router.cpp +++ b/pybind/llarp/router.cpp @@ -1,6 +1,7 @@ #include "common.hpp" #include "router/abstractrouter.hpp" +#include "tooling/hive_router.hpp" namespace llarp { @@ -12,4 +13,16 @@ namespace llarp .def("Stop", &AbstractRouter::Stop) .def("peerDb", &AbstractRouter::peerDb); } + } // namespace llarp + +namespace tooling +{ + void + HiveRouter_Init(py::module& mod) + { + py::class_(mod, "HiveRouter") + .def("disableGossiping", &HiveRouter::disableGossiping) + .def("enableGossiping", &HiveRouter::enableGossiping); + } +} // namespace tooling From b0d8568452b5e518d74053aa12dfa9879f2f84d6 Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Tue, 30 Jun 2020 09:45:26 -0600 Subject: [PATCH 42/70] Remove llarp C API usage from RouterHive --- include/llarp.hpp | 3 +-- llarp/config/config.hpp | 5 ---- llarp/context.cpp | 6 ++--- llarp/tooling/router_hive.cpp | 50 +++++++++++++---------------------- llarp/tooling/router_hive.hpp | 8 +++--- pybind/llarp/context.cpp | 6 ++--- 6 files changed, 28 insertions(+), 50 deletions(-) diff --git a/include/llarp.hpp b/include/llarp.hpp index 4c07e764e..e6a22c520 100644 --- a/include/llarp.hpp +++ b/include/llarp.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -26,8 +27,6 @@ namespace llarp class Logic; struct AbstractRouter; struct Config; - struct Crypto; - struct CryptoManager; struct RouterContact; namespace thread { diff --git a/llarp/config/config.hpp b/llarp/config/config.hpp index 77d3e5de0..5f92571e9 100644 --- a/llarp/config/config.hpp +++ b/llarp/config/config.hpp @@ -24,8 +24,6 @@ #include -struct llarp_config; - namespace llarp { using SectionValues_t = llarp::ConfigParser::SectionValues_t; @@ -224,9 +222,6 @@ namespace llarp std::string generateBaseRouterConfig(fs::path defaultDataDir); - - llarp_config* - Copy() const; }; void diff --git a/llarp/context.cpp b/llarp/context.cpp index 7925e7836..9457235c2 100644 --- a/llarp/context.cpp +++ b/llarp/context.cpp @@ -30,10 +30,8 @@ namespace llarp bool Context::Configure(const RuntimeOptions& opts, std::optional dataDir) { - if (config) - throw std::runtime_error("Re-configure not supported"); - - config = std::make_unique(); + if (not config) + config = std::make_unique(); fs::path defaultDataDir = dataDir ? *dataDir : GetDefaultDataDir(); diff --git a/llarp/tooling/router_hive.cpp b/llarp/tooling/router_hive.cpp index 7001c70ea..3fca59d1f 100644 --- a/llarp/tooling/router_hive.cpp +++ b/llarp/tooling/router_hive.cpp @@ -18,24 +18,15 @@ namespace tooling { auto& container = (isRelay ? relays : clients); - llarp_main* ctx = llarp_main_init_from_config(config->Copy(), isRelay); - auto result = llarp_main_setup(ctx, isRelay); - if (result == 0) - { - auto context = llarp::Context::Get(ctx); - auto routerId = llarp::RouterID(context->router->pubkey()); - context->InjectHive(this); - container[routerId] = ctx; - std::cout << "Generated router with ID " << routerId << std::endl; - } - else - { - throw std::runtime_error(llarp::stringify( - "Failed to add RouterHive ", - (isRelay ? "relay" : "client"), - ", llarp_main_setup() returned ", - result)); - } + Context_ptr context = std::make_shared(); + context->config = std::make_unique(*config.get()); + context->Configure(isRelay, {}); + context->Setup(isRelay); + + auto routerId = llarp::RouterID(context->router->pubkey()); + context->InjectHive(this); + container[routerId] = context; + std::cout << "Generated router with ID " << routerId << std::endl; } void @@ -58,7 +49,7 @@ namespace tooling for (auto [routerId, ctx] : container) { routerMainThreads.emplace_back([=]() { - llarp_main_run(ctx, llarp_main_runtime_opts{false, false, false, isRelay}); + ctx->Run(llarp::RuntimeOptions{false, false, isRelay}); }); std::this_thread::sleep_for(2ms); } @@ -82,24 +73,24 @@ namespace tooling llarp::LogInfo("Signalling all routers to stop"); for (auto [routerId, ctx] : relays) { - llarp_main_signal(ctx, 2 /* SIGINT */); + LogicCall(ctx->logic, [ctx]() { ctx->HandleSignal(SIGINT); }); } for (auto [routerId, ctx] : clients) { - llarp_main_signal(ctx, 2 /* SIGINT */); + LogicCall(ctx->logic, [ctx]() { ctx->HandleSignal(SIGINT); }); } llarp::LogInfo("Waiting on routers to be stopped"); for (auto [routerId, ctx] : relays) { - while (llarp_main_is_running(ctx)) + while (ctx->IsUp()) { std::this_thread::sleep_for(10ms); } } for (auto [routerId, ctx] : clients) { - while (llarp_main_is_running(ctx)) + while (ctx->IsUp()) { std::this_thread::sleep_for(10ms); } @@ -154,9 +145,8 @@ namespace tooling } void - RouterHive::VisitRouter(llarp_main* router, std::function visit) + RouterHive::VisitRouter(Context_ptr ctx, std::function visit) { - auto ctx = llarp::Context::Get(router); LogicCall(ctx->logic, [visit, ctx]() { visit(ctx); }); } @@ -170,7 +160,7 @@ namespace tooling if (itr == relays.end()) return nullptr; - auto ctx = llarp::Context::Get(itr->second); + auto ctx = itr->second; return ctx->router.get(); } @@ -186,9 +176,8 @@ namespace tooling size_t done_count = 0; for (auto [routerId, ctx] : relays) { - auto context = llarp::Context::Get(ctx); - LogicCall(context->logic, [&, i, context]() { - size_t count = context->router->NumberOfConnectedRouters(); + LogicCall(ctx->logic, [&, i, ctx]() { + size_t count = ctx->router->NumberOfConnectedRouters(); std::lock_guard guard{results_lock}; results[i] = count; done_count++; @@ -222,8 +211,7 @@ namespace tooling size_t i = 0; for (auto [routerId, ctx] : relays) { - auto context = llarp::Context::Get(ctx); - results[i] = context->router->rc(); + results[i] = ctx->router->rc(); i++; } return results; diff --git a/llarp/tooling/router_hive.hpp b/llarp/tooling/router_hive.hpp index 924a0554e..05590cb36 100644 --- a/llarp/tooling/router_hive.hpp +++ b/llarp/tooling/router_hive.hpp @@ -34,9 +34,9 @@ namespace tooling void AddRouter(const std::shared_ptr& config, bool isRelay); - /// safely visit router + /// safely visit router (asynchronously) void - VisitRouter(llarp_main* router, std::function visit); + VisitRouter(Context_ptr ctx, std::function visit); public: RouterHive() = default; @@ -90,8 +90,8 @@ namespace tooling GetRelayRCs(); std::mutex routerMutex; - std::unordered_map relays; - std::unordered_map clients; + std::unordered_map relays; + std::unordered_map clients; std::vector routerMainThreads; diff --git a/pybind/llarp/context.cpp b/pybind/llarp/context.cpp index e2e8084be..2c60098fe 100644 --- a/pybind/llarp/context.cpp +++ b/pybind/llarp/context.cpp @@ -9,10 +9,8 @@ namespace llarp { using Context_ptr = std::shared_ptr; py::class_(mod, "Context") - .def( - "Setup", - [](Context_ptr self, bool isRelay) -> bool { return self->Setup(isRelay) == 0; }) - .def("Run", [](Context_ptr self) -> int { return self->Run(llarp_main_runtime_opts{}); }) + .def("Setup", [](Context_ptr self, bool isRelay) { self->Setup(isRelay); }) + .def("Run", [](Context_ptr self) -> int { return self->Run(RuntimeOptions{}); }) .def("Stop", [](Context_ptr self) { self->CloseAsync(); }) .def("IsUp", &Context::IsUp) .def("IsRelay", [](Context_ptr self) -> bool { return self->router->IsServiceNode(); }) From 552dcce5fd0b75a9a2640a2ca8c8f0aac6803cec Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Wed, 1 Jul 2020 13:46:52 -0600 Subject: [PATCH 43/70] Use inheritance to handle Hive injection --- include/llarp.hpp | 12 ------ llarp/context.cpp | 8 ---- llarp/router/abstractrouter.hpp | 17 +++++++-- llarp/router/router.cpp | 7 ++++ llarp/router/router.hpp | 3 ++ llarp/tooling/hive_context.cpp | 20 +++++++++- llarp/tooling/hive_context.hpp | 13 +++++++ llarp/tooling/hive_router.cpp | 13 ++++++- llarp/tooling/hive_router.hpp | 7 +++- llarp/tooling/router_hive.cpp | 55 ++++++++-------------------- llarp/tooling/router_hive.hpp | 21 ++++------- pybind/llarp/context.cpp | 7 +++- pybind/llarp/tooling/router_hive.cpp | 9 ++--- test/hive/test_peer_stats.py | 2 +- 14 files changed, 104 insertions(+), 90 deletions(-) diff --git a/include/llarp.hpp b/include/llarp.hpp index e6a22c520..50561db9d 100644 --- a/include/llarp.hpp +++ b/include/llarp.hpp @@ -15,13 +15,6 @@ struct llarp_ev_loop; -#ifdef LOKINET_HIVE -namespace tooling -{ - struct RouterHive; -} // namespace tooling -#endif - namespace llarp { class Logic; @@ -97,11 +90,6 @@ namespace llarp llarp_ev_loop_ptr __netloop, std::shared_ptr logic); -#ifdef LOKINET_HIVE - void - InjectHive(tooling::RouterHive* hive); -#endif - private: void SigINT(); diff --git a/llarp/context.cpp b/llarp/context.cpp index 9457235c2..15d0dc38b 100644 --- a/llarp/context.cpp +++ b/llarp/context.cpp @@ -204,14 +204,6 @@ namespace llarp llarp::LogDebug("free logic"); logic.reset(); } - -#ifdef LOKINET_HIVE - void - Context::InjectHive(tooling::RouterHive* hive) - { - router->hive = hive; - } -#endif } // namespace llarp extern "C" diff --git a/llarp/router/abstractrouter.hpp b/llarp/router/abstractrouter.hpp index f53c2917a..0ee28aa75 100644 --- a/llarp/router/abstractrouter.hpp +++ b/llarp/router/abstractrouter.hpp @@ -14,7 +14,7 @@ #include #ifdef LOKINET_HIVE -#include "tooling/router_hive.hpp" +#include "tooling/router_event.hpp" #endif struct llarp_buffer_t; @@ -292,14 +292,23 @@ namespace llarp virtual void GossipRCIfNeeded(const RouterContact rc) = 0; + /// Templated convenience function to generate a RouterHive event and + /// delegate to non-templated (and overridable) function for handling. template void NotifyRouterEvent([[maybe_unused]] Params&&... args) const { -#ifdef LOKINET_HIVE - hive->NotifyEvent(std::make_unique(std::forward(args)...)); -#endif + // TODO: no-op when appropriate + auto event = std::make_unique(args...); + HandleRouterEvent(std::move(event)); } + + protected: + /// Virtual function to handle RouterEvent. HiveRouter overrides this in + /// order to inject the event. The default implementation in Router simply + /// logs it. + virtual void + HandleRouterEvent(tooling::RouterEventPtr event) const = 0; }; } // namespace llarp diff --git a/llarp/router/router.cpp b/llarp/router/router.cpp index 7da06ccbc..a09813082 100644 --- a/llarp/router/router.cpp +++ b/llarp/router/router.cpp @@ -1240,4 +1240,11 @@ namespace llarp LogDebug("Message failed sending to ", remote); } } + + void + Router::HandleRouterEvent(tooling::RouterEventPtr event) const + { + LogDebug(event->ToString()); + } + } // namespace llarp diff --git a/llarp/router/router.hpp b/llarp/router/router.hpp index 8d49d8ddf..e701711bb 100644 --- a/llarp/router/router.hpp +++ b/llarp/router/router.hpp @@ -530,6 +530,9 @@ namespace llarp MessageSent(const RouterID& remote, SendStatus status); protected: + virtual void + HandleRouterEvent(tooling::RouterEventPtr event) const override; + virtual bool disableGossipingRC_TestingOnly() { diff --git a/llarp/tooling/hive_context.cpp b/llarp/tooling/hive_context.cpp index 94b9ef770..4f1d169e2 100644 --- a/llarp/tooling/hive_context.cpp +++ b/llarp/tooling/hive_context.cpp @@ -4,13 +4,31 @@ namespace tooling { + HiveContext::HiveContext(RouterHive* hive) : m_hive(hive) + { + } + std::unique_ptr HiveContext::makeRouter( std::shared_ptr worker, llarp_ev_loop_ptr netloop, std::shared_ptr logic) { - return std::make_unique(worker, netloop, logic); + return std::make_unique(worker, netloop, logic, m_hive); + } + + HiveRouter* + HiveContext::getRouterAsHiveRouter() + { + if (not router) + return nullptr; + + HiveRouter* hiveRouter = dynamic_cast(router.get()); + + if (hiveRouter == nullptr) + throw std::runtime_error("HiveContext has a router not of type HiveRouter"); + + return hiveRouter; } } // namespace tooling diff --git a/llarp/tooling/hive_context.hpp b/llarp/tooling/hive_context.hpp index 8851b3835..61ecd32bb 100644 --- a/llarp/tooling/hive_context.hpp +++ b/llarp/tooling/hive_context.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include namespace tooling { @@ -8,11 +9,23 @@ namespace tooling /// perform custom behavior which might be undesirable in production code. struct HiveContext : public llarp::Context { + HiveContext(RouterHive* hive); + std::unique_ptr makeRouter( std::shared_ptr worker, llarp_ev_loop_ptr netloop, std::shared_ptr logic) override; + + /// Get this context's router as a HiveRouter. + /// + /// Returns nullptr if there is no router or throws an exception if the + /// router is somehow not an instance of HiveRouter. + HiveRouter* + getRouterAsHiveRouter(); + + protected: + RouterHive* m_hive = nullptr; }; } // namespace tooling diff --git a/llarp/tooling/hive_router.cpp b/llarp/tooling/hive_router.cpp index 55bae5cf8..744f76914 100644 --- a/llarp/tooling/hive_router.cpp +++ b/llarp/tooling/hive_router.cpp @@ -1,12 +1,15 @@ #include +#include + namespace tooling { HiveRouter::HiveRouter( std::shared_ptr worker, llarp_ev_loop_ptr netloop, - std::shared_ptr logic) - : Router(worker, netloop, logic) + std::shared_ptr logic, + RouterHive* hive) + : Router(worker, netloop, logic), m_hive(hive) { } @@ -28,4 +31,10 @@ namespace tooling m_disableGossiping = true; } + void + HiveRouter::HandleRouterEvent(RouterEventPtr event) const + { + m_hive->NotifyEvent(std::move(event)); + } + } // namespace tooling diff --git a/llarp/tooling/hive_router.hpp b/llarp/tooling/hive_router.hpp index 743588550..4c3ed5076 100644 --- a/llarp/tooling/hive_router.hpp +++ b/llarp/tooling/hive_router.hpp @@ -13,7 +13,8 @@ namespace tooling HiveRouter( std::shared_ptr worker, llarp_ev_loop_ptr netloop, - std::shared_ptr logic); + std::shared_ptr logic, + RouterHive* hive); virtual ~HiveRouter() = default; @@ -29,6 +30,10 @@ namespace tooling protected: bool m_disableGossiping = false; + RouterHive* m_hive = nullptr; + + virtual void + HandleRouterEvent(RouterEventPtr event) const override; }; } // namespace tooling diff --git a/llarp/tooling/router_hive.cpp b/llarp/tooling/router_hive.cpp index 3fca59d1f..f8f9d0a28 100644 --- a/llarp/tooling/router_hive.cpp +++ b/llarp/tooling/router_hive.cpp @@ -14,17 +14,19 @@ using namespace std::chrono_literals; namespace tooling { void - RouterHive::AddRouter(const std::shared_ptr& config, bool isRelay) + RouterHive::AddRouter(const std::shared_ptr& config, bool isRouter) { - auto& container = (isRelay ? relays : clients); + auto& container = (isRouter ? relays : clients); + + llarp::RuntimeOptions opts; + opts.isRouter = isRouter; - Context_ptr context = std::make_shared(); + Context_ptr context = std::make_shared(this); context->config = std::make_unique(*config.get()); - context->Configure(isRelay, {}); - context->Setup(isRelay); + context->Configure(opts, {}); + context->Setup(opts); auto routerId = llarp::RouterID(context->router->pubkey()); - context->InjectHive(this); container[routerId] = context; std::cout << "Generated router with ID " << routerId << std::endl; } @@ -150,7 +152,7 @@ namespace tooling LogicCall(ctx->logic, [visit, ctx]() { visit(ctx); }); } - llarp::AbstractRouter* + HiveRouter* RouterHive::GetRelay(const llarp::RouterID& id, bool needMutexLock) { auto guard = @@ -161,7 +163,7 @@ namespace tooling return nullptr; auto ctx = itr->second; - return ctx->router.get(); + return ctx->getRouterAsHiveRouter(); } std::vector @@ -218,34 +220,7 @@ namespace tooling } void - RouterHive::ForEachRelayRouter(std::function visit) - { - std::lock_guard guard{routerMutex}; - for (auto [routerId, ctx] : relays) - { - visit(GetRelay(routerId, false)); - } - } - - void - RouterHive::ForEachClientRouter(std::function visit) - { - std::lock_guard guard{routerMutex}; - for (auto [routerId, ctx] : clients) - { - visit(GetRelay(routerId, false)); - } - } - - void - RouterHive::ForEachRouterRouter(std::function visit) - { - ForEachRelayRouter(visit); - ForEachClientRouter(visit); - } - - void - RouterHive::ForEachRelayContext(std::function visit) + RouterHive::ForEachRelay(std::function visit) { for (auto [routerId, ctx] : relays) { @@ -254,7 +229,7 @@ namespace tooling } void - RouterHive::ForEachClientContext(std::function visit) + RouterHive::ForEachClient(std::function visit) { for (auto [routerId, ctx] : clients) { @@ -264,10 +239,10 @@ namespace tooling /// safely visit every router context void - RouterHive::ForEachRouterContext(std::function visit) + RouterHive::ForEachRouter(std::function visit) { - ForEachRelayContext(visit); - ForEachClientContext(visit); + ForEachRelay(visit); + ForEachClient(visit); } } // namespace tooling diff --git a/llarp/tooling/router_hive.hpp b/llarp/tooling/router_hive.hpp index 05590cb36..8cb8fa37b 100644 --- a/llarp/tooling/router_hive.hpp +++ b/llarp/tooling/router_hive.hpp @@ -4,6 +4,7 @@ #include #include +#include #include #include @@ -16,7 +17,6 @@ struct llarp_main; namespace llarp { struct Context; - struct AbstractRouter; } // namespace llarp namespace tooling @@ -25,7 +25,7 @@ namespace tooling struct RouterHive { - using Context_ptr = std::shared_ptr; + using Context_ptr = std::shared_ptr; private: void @@ -65,22 +65,15 @@ namespace tooling std::deque GetAllEvents(); - // functions to safely visit each relay and/or client's AbstractRouter or Context + // functions to safely visit each relay and/or client's HiveContext void - ForEachRelayRouter(std::function visit); + ForEachRelay(std::function visit); void - ForEachClientRouter(std::function visit); + ForEachClient(std::function visit); void - ForEachRouterRouter(std::function visit); + ForEachRouter(std::function visit); - void - ForEachRelayContext(std::function visit); - void - ForEachClientContext(std::function visit); - void - ForEachRouterContext(std::function visit); - - llarp::AbstractRouter* + HiveRouter* GetRelay(const llarp::RouterID& id, bool needMutexLock = true); std::vector diff --git a/pybind/llarp/context.cpp b/pybind/llarp/context.cpp index 2c60098fe..1ed3aa2de 100644 --- a/pybind/llarp/context.cpp +++ b/pybind/llarp/context.cpp @@ -1,5 +1,6 @@ #include "common.hpp" #include +#include #include #include "llarp/handlers/pyhandler.hpp" namespace llarp @@ -9,7 +10,11 @@ namespace llarp { using Context_ptr = std::shared_ptr; py::class_(mod, "Context") - .def("Setup", [](Context_ptr self, bool isRelay) { self->Setup(isRelay); }) + .def( + "Setup", + [](Context_ptr self, bool isRouter) { + self->Setup({false, false, isRouter}); + }) .def("Run", [](Context_ptr self) -> int { return self->Run(RuntimeOptions{}); }) .def("Stop", [](Context_ptr self) { self->CloseAsync(); }) .def("IsUp", &Context::IsUp) diff --git a/pybind/llarp/tooling/router_hive.cpp b/pybind/llarp/tooling/router_hive.cpp index 098d46e9b..86156f5dc 100644 --- a/pybind/llarp/tooling/router_hive.cpp +++ b/pybind/llarp/tooling/router_hive.cpp @@ -19,12 +19,9 @@ namespace tooling .def("StartRelays", &RouterHive::StartRelays) .def("StartClients", &RouterHive::StartClients) .def("StopAll", &RouterHive::StopRouters) - .def("ForEachRelayContext", &RouterHive::ForEachRelayContext) - .def("ForEachClientContext", &RouterHive::ForEachClientContext) - .def("ForEachRouterContext", &RouterHive::ForEachRouterContext) - .def("ForEachRelayRouter", &RouterHive::ForEachRelayRouter) - .def("ForEachClientRouter", &RouterHive::ForEachClientRouter) - .def("ForEachRouterRouter", &RouterHive::ForEachRouterRouter) + .def("ForEachRelay", &RouterHive::ForEachRelay) + .def("ForEachClient", &RouterHive::ForEachClient) + .def("ForEachRouter", &RouterHive::ForEachRouter) .def("GetNextEvent", &RouterHive::GetNextEvent) .def("GetAllEvents", &RouterHive::GetAllEvents) .def("RelayConnectedRelays", &RouterHive::RelayConnectedRelays) diff --git a/test/hive/test_peer_stats.py b/test/hive/test_peer_stats.py index 4d0530ed0..48cbebd52 100644 --- a/test/hive/test_peer_stats.py +++ b/test/hive/test_peer_stats.py @@ -87,7 +87,7 @@ def tally_rc_received_for_peer(hive, routerId): numFound += stats.numDistinctRCsReceived - hive.ForEachRelayRouter(visit) + hive.ForEachRelay(visit) return numFound; From ed47ba998f5b9c454e8c42510ee04f5d1f560c74 Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Thu, 2 Jul 2020 09:40:08 -0600 Subject: [PATCH 44/70] Minor fixes around Context --- daemon/main.cpp | 20 ++++++++++++++++++-- include/llarp.hpp | 14 +++++++------- llarp/context.cpp | 2 +- 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/daemon/main.cpp b/daemon/main.cpp index 0b43f49c2..00b28275f 100644 --- a/daemon/main.cpp +++ b/daemon/main.cpp @@ -98,7 +98,7 @@ run_main_context(const fs::path confFile, const llarp::RuntimeOptions opts) llarp::Config conf; conf.Load(confFile, opts.isRouter, confFile.parent_path()); - ctx = std::shared_ptr(); + ctx = std::make_shared(); ctx->Configure(opts, {}); signal(SIGINT, handle_signal); @@ -297,7 +297,23 @@ main(int argc, char* argv[]) } while (ftr.wait_for(std::chrono::seconds(1)) != std::future_status::ready); main_thread.join(); - const auto code = ftr.get(); + + int code = 0; + + try + { + code = ftr.get(); + } + catch (const std::exception& e) + { + std::cerr << "main thread threw exception: " << e.what() << std::endl; + code = 1; + } + catch (...) + { + std::cerr << "main thread threw non-standard exception" << std::endl; + code = 2; + } llarp::LogContext::Instance().ImmediateFlush(); #ifdef _WIN32 diff --git a/include/llarp.hpp b/include/llarp.hpp index 50561db9d..8c65470c4 100644 --- a/include/llarp.hpp +++ b/include/llarp.hpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -18,7 +19,6 @@ struct llarp_ev_loop; namespace llarp { class Logic; - struct AbstractRouter; struct Config; struct RouterContact; namespace thread @@ -35,12 +35,12 @@ namespace llarp struct Context { - std::unique_ptr crypto; - std::unique_ptr cryptoManager; - std::unique_ptr router; - std::shared_ptr logic; - std::unique_ptr config; - std::unique_ptr nodedb; + std::unique_ptr crypto = nullptr; + std::unique_ptr cryptoManager = nullptr; + std::unique_ptr router = nullptr; + std::shared_ptr logic = nullptr; + std::unique_ptr config = nullptr; + std::unique_ptr nodedb = nullptr; llarp_ev_loop_ptr mainloop; std::string nodedb_dir; diff --git a/llarp/context.cpp b/llarp/context.cpp index 15d0dc38b..20d7a4741 100644 --- a/llarp/context.cpp +++ b/llarp/context.cpp @@ -30,7 +30,7 @@ namespace llarp bool Context::Configure(const RuntimeOptions& opts, std::optional dataDir) { - if (not config) + if (nullptr == config.get()) config = std::make_unique(); fs::path defaultDataDir = dataDir ? *dataDir : GetDefaultDataDir(); From 343252c48dbd33c4a82dbe50d0c84d685b603a01 Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Thu, 2 Jul 2020 09:40:38 -0600 Subject: [PATCH 45/70] Use HiveContext and HiveRouter properly --- pybind/common.hpp | 6 ++++++ pybind/llarp/context.cpp | 11 +++++++++++ pybind/llarp/router.cpp | 2 +- pybind/module.cpp | 2 ++ test/hive/test_peer_stats.py | 4 ++-- 5 files changed, 22 insertions(+), 3 deletions(-) diff --git a/pybind/common.hpp b/pybind/common.hpp index 125944016..6d1508882 100644 --- a/pybind/common.hpp +++ b/pybind/common.hpp @@ -74,4 +74,10 @@ namespace tooling void RouterEvent_Init(py::module& mod); + + void + HiveContext_Init(py::module& mod); + + void + HiveRouter_Init(py::module& mod); } // namespace tooling diff --git a/pybind/llarp/context.cpp b/pybind/llarp/context.cpp index 1ed3aa2de..3298f14da 100644 --- a/pybind/llarp/context.cpp +++ b/pybind/llarp/context.cpp @@ -37,4 +37,15 @@ namespace llarp }) .def("CallSafe", &Context::CallSafe); } + } // namespace llarp + +namespace tooling +{ + void + HiveContext_Init(py::module& mod) + { + py::class_(mod, "HiveContext") + .def("getRouterAsHiveRouter", &tooling::HiveContext::getRouterAsHiveRouter); + } +} // namespace tooling diff --git a/pybind/llarp/router.cpp b/pybind/llarp/router.cpp index d6d74e40d..ad668f4b8 100644 --- a/pybind/llarp/router.cpp +++ b/pybind/llarp/router.cpp @@ -21,7 +21,7 @@ namespace tooling void HiveRouter_Init(py::module& mod) { - py::class_(mod, "HiveRouter") + py::class_(mod, "HiveRouter") .def("disableGossiping", &HiveRouter::disableGossiping) .def("enableGossiping", &HiveRouter::enableGossiping); } diff --git a/pybind/module.cpp b/pybind/module.cpp index aa57fa25a..605d83844 100644 --- a/pybind/module.cpp +++ b/pybind/module.cpp @@ -6,12 +6,14 @@ PYBIND11_MODULE(pyllarp, m) tooling::RouterHive_Init(m); tooling::RouterEvent_Init(m); llarp::AbstractRouter_Init(m); + tooling::HiveRouter_Init(m); llarp::PeerDb_Init(m); llarp::PeerStats_Init(m); llarp::RouterID_Init(m); llarp::RouterContact_Init(m); llarp::CryptoTypes_Init(m); llarp::Context_Init(m); + tooling::HiveContext_Init(m); llarp::Config_Init(m); llarp::dht::DHTTypes_Init(m); llarp::PathTypes_Init(m); diff --git a/test/hive/test_peer_stats.py b/test/hive/test_peer_stats.py index 48cbebd52..0905822e8 100644 --- a/test/hive/test_peer_stats.py +++ b/test/hive/test_peer_stats.py @@ -77,10 +77,10 @@ def tally_rc_received_for_peer(hive, routerId): numFound = 0 - def visit(relay): + def visit(context): nonlocal numFound - peerDb = relay.peerDb() + peerDb = context.getRouterAsHiveRouter().peerDb() stats = peerDb.getCurrentPeerStats(routerId); assert(stats.routerId == routerId) From ec20d94c6ba8fa1c8eb03b433a5a8c80e93ce4bd Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Wed, 1 Jul 2020 16:46:32 -0600 Subject: [PATCH 46/70] Fix Context::Configure() --- daemon/main.cpp | 2 +- include/llarp.hpp | 4 ++-- llarp/context.cpp | 7 +++++-- llarp/tooling/router_hive.cpp | 2 +- test/regress/2020-06-08-key-backup-bug.cpp | 2 +- 5 files changed, 10 insertions(+), 7 deletions(-) diff --git a/daemon/main.cpp b/daemon/main.cpp index 00b28275f..9d2f7cd9f 100644 --- a/daemon/main.cpp +++ b/daemon/main.cpp @@ -99,7 +99,7 @@ run_main_context(const fs::path confFile, const llarp::RuntimeOptions opts) conf.Load(confFile, opts.isRouter, confFile.parent_path()); ctx = std::make_shared(); - ctx->Configure(opts, {}); + ctx->Configure(opts, {}, confFile); signal(SIGINT, handle_signal); signal(SIGTERM, handle_signal); diff --git a/include/llarp.hpp b/include/llarp.hpp index 8c65470c4..12d0b2c72 100644 --- a/include/llarp.hpp +++ b/include/llarp.hpp @@ -60,7 +60,8 @@ namespace llarp HandleSignal(int sig); bool - Configure(const RuntimeOptions& opts, std::optional dataDir); + Configure( + const RuntimeOptions& opts, std::optional dataDir, const fs::path& configfile); bool IsUp() const; @@ -94,7 +95,6 @@ namespace llarp void SigINT(); - std::string configfile; std::unique_ptr> closeWaiter; }; diff --git a/llarp/context.cpp b/llarp/context.cpp index 20d7a4741..b668499fb 100644 --- a/llarp/context.cpp +++ b/llarp/context.cpp @@ -28,14 +28,17 @@ namespace llarp } bool - Context::Configure(const RuntimeOptions& opts, std::optional dataDir) + Context::Configure( + const RuntimeOptions& opts, std::optional dataDir, const fs::path& configfile) { + LogWarn("Context::Configure()"); + if (nullptr == config.get()) config = std::make_unique(); fs::path defaultDataDir = dataDir ? *dataDir : GetDefaultDataDir(); - if (configfile.size()) + if (not configfile.empty()) { if (!config->Load(configfile.c_str(), opts.isRouter, defaultDataDir)) { diff --git a/llarp/tooling/router_hive.cpp b/llarp/tooling/router_hive.cpp index f8f9d0a28..38953bd44 100644 --- a/llarp/tooling/router_hive.cpp +++ b/llarp/tooling/router_hive.cpp @@ -23,7 +23,7 @@ namespace tooling Context_ptr context = std::make_shared(this); context->config = std::make_unique(*config.get()); - context->Configure(opts, {}); + context->Configure(opts, {}, {}); context->Setup(opts); auto routerId = llarp::RouterID(context->router->pubkey()); diff --git a/test/regress/2020-06-08-key-backup-bug.cpp b/test/regress/2020-06-08-key-backup-bug.cpp index d10f47da0..462a7ec42 100644 --- a/test/regress/2020-06-08-key-backup-bug.cpp +++ b/test/regress/2020-06-08-key-backup-bug.cpp @@ -12,7 +12,7 @@ static std::shared_ptr make_context(std::optional keyfile) { auto context = std::make_shared(); - context->Configure(opts, {}); + context->Configure(opts, {}, {}); context->config->network.m_endpointType = "null"; context->config->network.m_keyfile = keyfile; From 88c3e9ce0017a836a4aec8e4bb162c4df7355ec9 Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Thu, 2 Jul 2020 10:35:44 -0600 Subject: [PATCH 47/70] Remove worker thread parameter from Router constructors --- include/llarp.hpp | 1 - llarp/context.cpp | 5 ++--- llarp/tooling/hive_context.cpp | 3 +-- llarp/tooling/hive_context.hpp | 1 - llarp/tooling/hive_router.cpp | 1 - llarp/tooling/hive_router.hpp | 1 - 6 files changed, 3 insertions(+), 9 deletions(-) diff --git a/include/llarp.hpp b/include/llarp.hpp index 12d0b2c72..eaff0aeca 100644 --- a/include/llarp.hpp +++ b/include/llarp.hpp @@ -87,7 +87,6 @@ namespace llarp /// to be created instead. Defaults to llarp::Router. virtual std::unique_ptr makeRouter( - std::shared_ptr worker, llarp_ev_loop_ptr __netloop, std::shared_ptr logic); diff --git a/llarp/context.cpp b/llarp/context.cpp index b668499fb..ce788de61 100644 --- a/llarp/context.cpp +++ b/llarp/context.cpp @@ -91,7 +91,7 @@ namespace llarp crypto = std::make_unique(); cryptoManager = std::make_unique(crypto.get()); - router = makeRouter(worker, mainloop, logic); + router = makeRouter(mainloop, logic); nodedb = std::make_unique( nodedb_dir, [r = router.get()](auto call) { r->QueueDiskIO(std::move(call)); }); @@ -108,11 +108,10 @@ namespace llarp std::unique_ptr Context::makeRouter( - std::shared_ptr worker, llarp_ev_loop_ptr netloop, std::shared_ptr logic) { - return std::make_unique(worker, netloop, logic); + return std::make_unique(netloop, logic); } int diff --git a/llarp/tooling/hive_context.cpp b/llarp/tooling/hive_context.cpp index 4f1d169e2..76f436c13 100644 --- a/llarp/tooling/hive_context.cpp +++ b/llarp/tooling/hive_context.cpp @@ -10,11 +10,10 @@ namespace tooling std::unique_ptr HiveContext::makeRouter( - std::shared_ptr worker, llarp_ev_loop_ptr netloop, std::shared_ptr logic) { - return std::make_unique(worker, netloop, logic, m_hive); + return std::make_unique(netloop, logic, m_hive); } HiveRouter* diff --git a/llarp/tooling/hive_context.hpp b/llarp/tooling/hive_context.hpp index 61ecd32bb..6489705f1 100644 --- a/llarp/tooling/hive_context.hpp +++ b/llarp/tooling/hive_context.hpp @@ -13,7 +13,6 @@ namespace tooling std::unique_ptr makeRouter( - std::shared_ptr worker, llarp_ev_loop_ptr netloop, std::shared_ptr logic) override; diff --git a/llarp/tooling/hive_router.cpp b/llarp/tooling/hive_router.cpp index 744f76914..3223cbb5c 100644 --- a/llarp/tooling/hive_router.cpp +++ b/llarp/tooling/hive_router.cpp @@ -5,7 +5,6 @@ namespace tooling { HiveRouter::HiveRouter( - std::shared_ptr worker, llarp_ev_loop_ptr netloop, std::shared_ptr logic, RouterHive* hive) diff --git a/llarp/tooling/hive_router.hpp b/llarp/tooling/hive_router.hpp index 4c3ed5076..f8af35a0f 100644 --- a/llarp/tooling/hive_router.hpp +++ b/llarp/tooling/hive_router.hpp @@ -11,7 +11,6 @@ namespace tooling struct HiveRouter : public llarp::Router { HiveRouter( - std::shared_ptr worker, llarp_ev_loop_ptr netloop, std::shared_ptr logic, RouterHive* hive); From cdaa28bfd3898877dd25cba03887a1112c4c8fcd Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Thu, 2 Jul 2020 10:36:12 -0600 Subject: [PATCH 48/70] Use QueueDiskIO instead of diskworker for db flushing --- llarp/router/router.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llarp/router/router.cpp b/llarp/router/router.cpp index a09813082..22f9308e6 100644 --- a/llarp/router/router.cpp +++ b/llarp/router/router.cpp @@ -787,7 +787,7 @@ namespace llarp if (m_peerDb->shouldFlush(now)) { LogWarn("Queing database flush..."); - diskworker()->addJob([this]() { m_peerDb->flushDatabase(); }); + QueueDiskIO([this]() { m_peerDb->flushDatabase(); }); } } From 97c14d81fdc858c5469b80e0a09159b60059430d Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Thu, 2 Jul 2020 11:12:04 -0600 Subject: [PATCH 49/70] Refactor test_llarp_exit_context.cpp to remove C API usage --- test/exit/test_llarp_exit_context.cpp | 44 +++++++++++++-------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/test/exit/test_llarp_exit_context.cpp b/test/exit/test_llarp_exit_context.cpp index 2e2e30c26..5edddc731 100644 --- a/test/exit/test_llarp_exit_context.cpp +++ b/test/exit/test_llarp_exit_context.cpp @@ -6,39 +6,38 @@ #include #include -llarp_main* +static const llarp::RuntimeOptions opts = {.background = false, .debug = false, .isRouter = true}; + +std::shared_ptr make_context() { - // make config - auto config = new llarp_config(); - REQUIRE(config != nullptr); - REQUIRE(config->impl.LoadDefault(true, fs::current_path())); + auto context = std::make_shared(); + context->Configure(opts, {}, {}); + REQUIRE(context->config != nullptr); + REQUIRE(context->config->LoadDefault(true, fs::current_path())); + // set testing defaults - config->impl.network.m_endpointType = "null"; - config->impl.bootstrap.skipBootstrap = true; - config->impl.api.m_enableRPCServer = false; + context->config->network.m_endpointType = "null"; + context->config->bootstrap.skipBootstrap = true; + context->config->api.m_enableRPCServer = false; // make a fake inbound link - config->impl.links.m_InboundLinks.emplace_back(); - auto& link = config->impl.links.m_InboundLinks.back(); + context->config->links.m_InboundLinks.emplace_back(); + auto& link = context->config->links.m_InboundLinks.back(); link.interface = llarp::net::LoopbackInterfaceName(); link.addressFamily = AF_INET; link.port = 0; // configure - auto ptr = llarp_main_init_from_config(config, true); - REQUIRE(ptr != nullptr); - llarp_config_free(config); - return ptr; + return context; } TEST_CASE("ensure snode address allocation", "[snode]") { llarp::LogSilencer shutup; auto ctx = make_context(); - REQUIRE(llarp_main_setup(ctx, true) == 0); - auto ctx_pp = llarp::Context::Get(ctx); - ctx_pp->CallSafe([ctx_pp]() { - REQUIRE(ctx_pp->router->IsServiceNode()); - auto& context = ctx_pp->router->exitContext(); + REQUIRE_NOTHROW(ctx->Setup(opts)); + ctx->CallSafe([ctx]() { + REQUIRE(ctx->router->IsServiceNode()); + auto& context = ctx->router->exitContext(); llarp::PubKey pk; pk.Randomize(); @@ -51,8 +50,9 @@ TEST_CASE("ensure snode address allocation", "[snode]") REQUIRE( context.FindEndpointForPath(firstPath)->LocalIP() == context.FindEndpointForPath(secondPath)->LocalIP()); - ctx_pp->CloseAsync(); + ctx->CloseAsync(); }); - REQUIRE(llarp_main_run(ctx, llarp_main_runtime_opts{.isRelay = true}) == 0); - llarp_main_free(ctx); + REQUIRE(ctx->Run(opts) == 0); + + ctx.reset(); } From fb75329b88128830f1ff5e9067e17c2271158eea Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Thu, 2 Jul 2020 11:12:35 -0600 Subject: [PATCH 50/70] Update SessionEstablished function sig in test_iwp_session.cpp --- test/iwp/test_iwp_session.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/iwp/test_iwp_session.cpp b/test/iwp/test_iwp_session.cpp index 0510e73d3..3d2f26394 100644 --- a/test/iwp/test_iwp_session.cpp +++ b/test/iwp/test_iwp_session.cpp @@ -86,8 +86,9 @@ struct IWPLinkContext return true; }, // established handler - [established](llarp::ILinkSession* s) { + [established](llarp::ILinkSession* s, bool linkIsInbound) { REQUIRE(s != nullptr); + REQUIRE(inbound == linkIsInbound); established(s); return true; }, From ced2ac64fbbe4aa99242e312016d741b56188449 Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Thu, 2 Jul 2020 11:25:53 -0600 Subject: [PATCH 51/70] Remove last remnants of 'worker' --- llarp/tooling/hive_router.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/llarp/tooling/hive_router.cpp b/llarp/tooling/hive_router.cpp index 3223cbb5c..0fc74ae39 100644 --- a/llarp/tooling/hive_router.cpp +++ b/llarp/tooling/hive_router.cpp @@ -5,10 +5,8 @@ namespace tooling { HiveRouter::HiveRouter( - llarp_ev_loop_ptr netloop, - std::shared_ptr logic, - RouterHive* hive) - : Router(worker, netloop, logic), m_hive(hive) + llarp_ev_loop_ptr netloop, std::shared_ptr logic, RouterHive* hive) + : Router(netloop, logic), m_hive(hive) { } From f607b99dbeb98adfe5b3a6a8b8288bd4dedbd53e Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Thu, 2 Jul 2020 12:25:16 -0600 Subject: [PATCH 52/70] Fixes to Context::Configure with default config --- llarp/context.cpp | 12 +++++++++++- llarp/router/router.cpp | 6 ++++-- test/exit/test_llarp_exit_context.cpp | 2 +- test/regress/2020-06-08-key-backup-bug.cpp | 3 ++- 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/llarp/context.cpp b/llarp/context.cpp index ce788de61..b1ba8906a 100644 --- a/llarp/context.cpp +++ b/llarp/context.cpp @@ -38,7 +38,17 @@ namespace llarp fs::path defaultDataDir = dataDir ? *dataDir : GetDefaultDataDir(); - if (not configfile.empty()) + // TODO: DRY / refactor to use exceptions + if (configfile.empty()) + { + if (not config->LoadDefault(opts.isRouter, defaultDataDir)) + { + config.release(); + llarp::LogError("failed to load default config"); + return false; + } + } + else { if (!config->Load(configfile.c_str(), opts.isRouter, defaultDataDir)) { diff --git a/llarp/router/router.cpp b/llarp/router/router.cpp index 22f9308e6..acae87ae3 100644 --- a/llarp/router/router.cpp +++ b/llarp/router/router.cpp @@ -243,10 +243,12 @@ namespace llarp // we need this first so we can start lmq to fetch keys if (conf) { + whitelistRouters = conf->lokid.whitelistRouters; + if (whitelistRouters) + lokidRPCAddr = lokimq::address(conf->lokid.lokidRPCAddr); + enableRPCServer = conf->api.m_enableRPCServer; rpcBindAddr = lokimq::address(conf->api.m_rpcBindAddr); - whitelistRouters = conf->lokid.whitelistRouters; - lokidRPCAddr = lokimq::address(conf->lokid.lokidRPCAddr); } if (not StartRpcServer()) throw std::runtime_error("Failed to start rpc server"); diff --git a/test/exit/test_llarp_exit_context.cpp b/test/exit/test_llarp_exit_context.cpp index 5edddc731..c89b01c66 100644 --- a/test/exit/test_llarp_exit_context.cpp +++ b/test/exit/test_llarp_exit_context.cpp @@ -12,7 +12,7 @@ std::shared_ptr make_context() { auto context = std::make_shared(); - context->Configure(opts, {}, {}); + REQUIRE(context->Configure(opts, {}, {}) == true); REQUIRE(context->config != nullptr); REQUIRE(context->config->LoadDefault(true, fs::current_path())); diff --git a/test/regress/2020-06-08-key-backup-bug.cpp b/test/regress/2020-06-08-key-backup-bug.cpp index 462a7ec42..75a433d14 100644 --- a/test/regress/2020-06-08-key-backup-bug.cpp +++ b/test/regress/2020-06-08-key-backup-bug.cpp @@ -12,7 +12,8 @@ static std::shared_ptr make_context(std::optional keyfile) { auto context = std::make_shared(); - context->Configure(opts, {}, {}); + REQUIRE(context->Configure(opts, {}, {}) == true); + REQUIRE(context->config != nullptr); context->config->network.m_endpointType = "null"; context->config->network.m_keyfile = keyfile; From 0f074cff8c30398b39b1179adf156b03fa8a205e Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Mon, 6 Jul 2020 13:13:01 -0600 Subject: [PATCH 53/70] Remove ambguity WRT loading and passing of Config --- daemon/main.cpp | 2 +- include/llarp.hpp | 12 ++- llarp/context.cpp | 41 ++------ llarp/router/abstractrouter.hpp | 11 +-- llarp/router/router.cpp | 103 +++++++++------------ llarp/router/router.hpp | 14 +-- llarp/tooling/router_hive.cpp | 3 +- test/exit/test_llarp_exit_context.cpp | 20 ++-- test/regress/2020-06-08-key-backup-bug.cpp | 15 +-- 9 files changed, 85 insertions(+), 136 deletions(-) diff --git a/daemon/main.cpp b/daemon/main.cpp index 9d2f7cd9f..5bc328d2f 100644 --- a/daemon/main.cpp +++ b/daemon/main.cpp @@ -99,7 +99,7 @@ run_main_context(const fs::path confFile, const llarp::RuntimeOptions opts) conf.Load(confFile, opts.isRouter, confFile.parent_path()); ctx = std::make_shared(); - ctx->Configure(opts, {}, confFile); + ctx->Configure(conf); signal(SIGINT, handle_signal); signal(SIGTERM, handle_signal); diff --git a/include/llarp.hpp b/include/llarp.hpp index eaff0aeca..5485b4730 100644 --- a/include/llarp.hpp +++ b/include/llarp.hpp @@ -39,7 +39,6 @@ namespace llarp std::unique_ptr cryptoManager = nullptr; std::unique_ptr router = nullptr; std::shared_ptr logic = nullptr; - std::unique_ptr config = nullptr; std::unique_ptr nodedb = nullptr; llarp_ev_loop_ptr mainloop; std::string nodedb_dir; @@ -59,9 +58,11 @@ namespace llarp void HandleSignal(int sig); - bool - Configure( - const RuntimeOptions& opts, std::optional dataDir, const fs::path& configfile); + /// Configure given the specified config. + /// + /// note: consider using std::move() when passing conf in. + void + Configure(Config conf); bool IsUp() const; @@ -90,6 +91,9 @@ namespace llarp llarp_ev_loop_ptr __netloop, std::shared_ptr logic); + protected: + std::unique_ptr config = nullptr; + private: void SigINT(); diff --git a/llarp/context.cpp b/llarp/context.cpp index b1ba8906a..f37eee983 100644 --- a/llarp/context.cpp +++ b/llarp/context.cpp @@ -27,42 +27,17 @@ namespace llarp return logic && LogicCall(logic, f); } - bool - Context::Configure( - const RuntimeOptions& opts, std::optional dataDir, const fs::path& configfile) + void + Context::Configure(Config conf) { - LogWarn("Context::Configure()"); - - if (nullptr == config.get()) - config = std::make_unique(); + if (nullptr != config.get()) + throw std::runtime_error("Config already exists"); - fs::path defaultDataDir = dataDir ? *dataDir : GetDefaultDataDir(); - - // TODO: DRY / refactor to use exceptions - if (configfile.empty()) - { - if (not config->LoadDefault(opts.isRouter, defaultDataDir)) - { - config.release(); - llarp::LogError("failed to load default config"); - return false; - } - } - else - { - if (!config->Load(configfile.c_str(), opts.isRouter, defaultDataDir)) - { - config.release(); - llarp::LogError("failed to load config file ", configfile); - return false; - } - } + config = std::make_unique(std::move(conf)); logic = std::make_shared(); nodedb_dir = fs::path(config->router.m_dataDir / nodedb_dirname).string(); - - return true; } bool @@ -87,6 +62,10 @@ namespace llarp void Context::Setup(const RuntimeOptions& opts) { + /// Call one of the Configure() methods before calling Setup() + if (not config) + throw std::runtime_error("Cannot call Setup() on context without a Config"); + llarp::LogInfo(llarp::VERSION_FULL, " ", llarp::RELEASE_MOTTO); llarp::LogInfo("starting up"); if (mainloop == nullptr) @@ -106,7 +85,7 @@ namespace llarp nodedb = std::make_unique( nodedb_dir, [r = router.get()](auto call) { r->QueueDiskIO(std::move(call)); }); - if (!router->Configure(config.get(), opts.isRouter, nodedb.get())) + if (!router->Configure(*config.get(), opts.isRouter, nodedb.get())) throw std::runtime_error("Failed to configure router"); // must be done after router is made so we can use its disk io worker diff --git a/llarp/router/abstractrouter.hpp b/llarp/router/abstractrouter.hpp index 0ee28aa75..c7a581c31 100644 --- a/llarp/router/abstractrouter.hpp +++ b/llarp/router/abstractrouter.hpp @@ -159,7 +159,7 @@ namespace llarp Sign(Signature& sig, const llarp_buffer_t& buf) const = 0; virtual bool - Configure(Config* conf, bool isRouter, llarp_nodedb* nodedb) = 0; + Configure(const Config& conf, bool isRouter, llarp_nodedb* nodedb) = 0; virtual bool IsServiceNode() const = 0; @@ -197,19 +197,10 @@ namespace llarp /// connect to N random routers virtual void ConnectToRandomRouters(int N) = 0; - /// inject configuration and reconfigure router - virtual bool - Reconfigure(Config* conf) = 0; virtual bool TryConnectAsync(RouterContact rc, uint16_t tries) = 0; - /// validate new configuration against old one - /// return true on 100% valid - /// return false if not 100% valid - virtual bool - ValidateConfig(Config* conf) const = 0; - /// called by link when a remote session has no more sessions open virtual void SessionClosed(RouterID remote) = 0; diff --git a/llarp/router/router.cpp b/llarp/router/router.cpp index acae87ae3..02db1131c 100644 --- a/llarp/router/router.cpp +++ b/llarp/router/router.cpp @@ -238,18 +238,16 @@ namespace llarp } bool - Router::Configure(Config* conf, bool isRouter, llarp_nodedb* nodedb) + Router::Configure(const Config& conf, bool isRouter, llarp_nodedb* nodedb) { - // we need this first so we can start lmq to fetch keys - if (conf) - { - whitelistRouters = conf->lokid.whitelistRouters; - if (whitelistRouters) - lokidRPCAddr = lokimq::address(conf->lokid.lokidRPCAddr); + whitelistRouters = conf.lokid.whitelistRouters; + if (whitelistRouters) + lokidRPCAddr = lokimq::address(conf.lokid.lokidRPCAddr); + + enableRPCServer = conf.api.m_enableRPCServer; + if (enableRPCServer) + rpcBindAddr = lokimq::address(conf.api.m_rpcBindAddr); - enableRPCServer = conf->api.m_enableRPCServer; - rpcBindAddr = lokimq::address(conf->api.m_rpcBindAddr); - } if (not StartRpcServer()) throw std::runtime_error("Failed to start rpc server"); @@ -263,13 +261,11 @@ namespace llarp } // fetch keys - if (conf) - { - if (not m_keyManager->initialize(*conf, true, isRouter)) - throw std::runtime_error("KeyManager failed to initialize"); - if (!FromConfig(conf)) - throw std::runtime_error("FromConfig() failed"); - } + if (not m_keyManager->initialize(conf, true, isRouter)) + throw std::runtime_error("KeyManager failed to initialize"); + if (!FromConfig(conf)) + throw std::runtime_error("FromConfig() failed"); + if (!InitOutboundLinks()) throw std::runtime_error("InitOutboundLinks() failed"); @@ -386,12 +382,12 @@ namespace llarp } bool - Router::FromConfig(Config* conf) + Router::FromConfig(const Config& conf) { // Set netid before anything else - if (!conf->router.m_netId.empty() && strcmp(conf->router.m_netId.c_str(), llarp::DEFAULT_NETID)) + if (!conf.router.m_netId.empty() && strcmp(conf.router.m_netId.c_str(), llarp::DEFAULT_NETID)) { - const auto& netid = conf->router.m_netId; + const auto& netid = conf.router.m_netId; llarp::LogWarn( "!!!! you have manually set netid to be '", netid, @@ -406,36 +402,36 @@ namespace llarp } // IWP config - m_OutboundPort = conf->links.m_OutboundLink.port; + m_OutboundPort = conf.links.m_OutboundLink.port; // Router config - _rc.SetNick(conf->router.m_nickname); - _outboundSessionMaker.maxConnectedRouters = conf->router.m_maxConnectedRouters; - _outboundSessionMaker.minConnectedRouters = conf->router.m_minConnectedRouters; + _rc.SetNick(conf.router.m_nickname); + _outboundSessionMaker.maxConnectedRouters = conf.router.m_maxConnectedRouters; + _outboundSessionMaker.minConnectedRouters = conf.router.m_minConnectedRouters; encryption_keyfile = m_keyManager->m_encKeyPath; our_rc_file = m_keyManager->m_rcPath; transport_keyfile = m_keyManager->m_transportKeyPath; ident_keyfile = m_keyManager->m_idKeyPath; - _ourAddress = conf->router.m_publicAddress; + _ourAddress = conf.router.m_publicAddress; - RouterContact::BlockBogons = conf->router.m_blockBogons; + RouterContact::BlockBogons = conf.router.m_blockBogons; // Lokid Config - usingSNSeed = conf->lokid.usingSNSeed; - whitelistRouters = conf->lokid.whitelistRouters; - lokidRPCAddr = lokimq::address(conf->lokid.lokidRPCAddr); + usingSNSeed = conf.lokid.usingSNSeed; + whitelistRouters = conf.lokid.whitelistRouters; + lokidRPCAddr = lokimq::address(conf.lokid.lokidRPCAddr); if (usingSNSeed) - ident_keyfile = conf->lokid.ident_keyfile; + ident_keyfile = conf.lokid.ident_keyfile; // TODO: add config flag for "is service node" - if (conf->links.m_InboundLinks.size()) + if (conf.links.m_InboundLinks.size()) { m_isServiceNode = true; } - networkConfig = conf->network; + networkConfig = conf.network; /// build a set of strictConnectPubkeys ( /// TODO: make this consistent with config -- do we support multiple strict connections @@ -458,21 +454,21 @@ namespace llarp throw std::invalid_argument(stringify("invalid key for strict-connect: ", val)); } - std::vector configRouters = conf->connect.routers; + std::vector configRouters = conf.connect.routers; configRouters.insert( - configRouters.end(), conf->bootstrap.routers.begin(), conf->bootstrap.routers.end()); + configRouters.end(), conf.bootstrap.routers.begin(), conf.bootstrap.routers.end()); // if our conf had no bootstrap files specified, try the default location of // /bootstrap.signed. If this isn't present, leave a useful error message if (configRouters.size() == 0 and not m_isServiceNode) { // TODO: use constant - fs::path defaultBootstrapFile = conf->router.m_dataDir / "bootstrap.signed"; + fs::path defaultBootstrapFile = conf.router.m_dataDir / "bootstrap.signed"; if (fs::exists(defaultBootstrapFile)) { configRouters.push_back(defaultBootstrapFile); } - else if (not conf->bootstrap.skipBootstrap) + else if (not conf.bootstrap.skipBootstrap) { LogError("No bootstrap files specified in config file, and the default"); LogError("bootstrap file ", defaultBootstrapFile, " does not exist."); @@ -547,7 +543,7 @@ namespace llarp m_isServiceNode); // create inbound links, if we are a service node - for (const LinksConfig::LinkInfo& serverConfig : conf->links.m_InboundLinks) + for (const LinksConfig::LinkInfo& serverConfig : conf.links.m_InboundLinks) { auto server = iwp::NewInboundLink( m_keyManager, @@ -572,15 +568,15 @@ namespace llarp } // Network config - if (conf->network.m_enableProfiling.has_value() and not*conf->network.m_enableProfiling) + if (conf.network.m_enableProfiling.has_value() and not*conf.network.m_enableProfiling) { routerProfiling().Disable(); LogWarn("router profiling explicitly disabled"); } - if (!conf->network.m_routerProfilesFile.empty()) + if (!conf.network.m_routerProfilesFile.empty()) { - routerProfilesFile = conf->network.m_routerProfilesFile; + routerProfilesFile = conf.network.m_routerProfilesFile; routerProfiling().Load(routerProfilesFile.c_str()); llarp::LogInfo("setting profiles to ", routerProfilesFile); } @@ -588,15 +584,15 @@ namespace llarp // API config if (not IsServiceNode()) { - hiddenServiceContext().AddEndpoint(*conf); + hiddenServiceContext().AddEndpoint(conf); } // peer stats - if (conf->router.m_enablePeerStats) + if (conf.router.m_enablePeerStats) { LogInfo("Initializing peerdb..."); m_peerDb = std::make_shared(); - m_peerDb->configure(conf->router); + m_peerDb->configure(conf.router); } else { @@ -605,10 +601,10 @@ namespace llarp // Logging config LogContext::Instance().Initialize( - conf->logging.m_logLevel, - conf->logging.m_logType, - conf->logging.m_logFile, - conf->router.m_nickname, + conf.logging.m_logLevel, + conf.logging.m_logType, + conf.logging.m_logFile, + conf.router.m_nickname, util::memFn(&AbstractRouter::QueueDiskIO, this)); return true; @@ -1165,19 +1161,6 @@ namespace llarp return true; } - bool - Router::ValidateConfig(Config* /*conf*/) const - { - return true; - } - - bool - Router::Reconfigure(Config*) - { - // TODO: implement me - return true; - } - bool Router::TryConnectAsync(RouterContact rc, uint16_t tries) { diff --git a/llarp/router/router.hpp b/llarp/router/router.hpp index e701711bb..ccf4d1f33 100644 --- a/llarp/router/router.hpp +++ b/llarp/router/router.hpp @@ -345,7 +345,7 @@ namespace llarp Close(); bool - Configure(Config* conf, bool isRouter, llarp_nodedb* nodedb = nullptr) override; + Configure(const Config& conf, bool isRouter, llarp_nodedb* nodedb = nullptr) override; bool StartRpcServer() override; @@ -392,19 +392,9 @@ namespace llarp void try_connect(fs::path rcfile); - /// inject configuration and reconfigure router - bool - Reconfigure(Config* conf) override; - bool TryConnectAsync(RouterContact rc, uint16_t tries) override; - /// validate new configuration against old one - /// return true on 100% valid - /// return false if not 100% valid - bool - ValidateConfig(Config* conf) const override; - /// send to remote router or queue for sending /// returns false on overflow /// returns true on successful queue @@ -524,7 +514,7 @@ namespace llarp UpdateOurRC(bool rotateKeys = false); bool - FromConfig(Config* conf); + FromConfig(const Config& conf); void MessageSent(const RouterID& remote, SendStatus status); diff --git a/llarp/tooling/router_hive.cpp b/llarp/tooling/router_hive.cpp index 38953bd44..8777b083e 100644 --- a/llarp/tooling/router_hive.cpp +++ b/llarp/tooling/router_hive.cpp @@ -22,8 +22,7 @@ namespace tooling opts.isRouter = isRouter; Context_ptr context = std::make_shared(this); - context->config = std::make_unique(*config.get()); - context->Configure(opts, {}, {}); + context->Configure(*config); context->Setup(opts); auto routerId = llarp::RouterID(context->router->pubkey()); diff --git a/test/exit/test_llarp_exit_context.cpp b/test/exit/test_llarp_exit_context.cpp index c89b01c66..cf93e60ac 100644 --- a/test/exit/test_llarp_exit_context.cpp +++ b/test/exit/test_llarp_exit_context.cpp @@ -11,22 +11,24 @@ static const llarp::RuntimeOptions opts = {.background = false, .debug = false, std::shared_ptr make_context() { - auto context = std::make_shared(); - REQUIRE(context->Configure(opts, {}, {}) == true); - REQUIRE(context->config != nullptr); - REQUIRE(context->config->LoadDefault(true, fs::current_path())); + llarp::Config conf; + conf.LoadDefault(true, fs::current_path()); // set testing defaults - context->config->network.m_endpointType = "null"; - context->config->bootstrap.skipBootstrap = true; - context->config->api.m_enableRPCServer = false; + conf.network.m_endpointType = "null"; + conf.bootstrap.skipBootstrap = true; + conf.api.m_enableRPCServer = false; // make a fake inbound link - context->config->links.m_InboundLinks.emplace_back(); - auto& link = context->config->links.m_InboundLinks.back(); + conf.links.m_InboundLinks.emplace_back(); + auto& link = conf.links.m_InboundLinks.back(); link.interface = llarp::net::LoopbackInterfaceName(); link.addressFamily = AF_INET; link.port = 0; // configure + + auto context = std::make_shared(); + REQUIRE_NOTHROW(context->Configure(std::move(conf))); + return context; } diff --git a/test/regress/2020-06-08-key-backup-bug.cpp b/test/regress/2020-06-08-key-backup-bug.cpp index 75a433d14..bc7e30f7f 100644 --- a/test/regress/2020-06-08-key-backup-bug.cpp +++ b/test/regress/2020-06-08-key-backup-bug.cpp @@ -11,14 +11,15 @@ llarp::RuntimeOptions opts = {false, false, false}; static std::shared_ptr make_context(std::optional keyfile) { - auto context = std::make_shared(); - REQUIRE(context->Configure(opts, {}, {}) == true); - REQUIRE(context->config != nullptr); + llarp::Config conf; + conf.LoadDefault(opts.isRouter, {}); + conf.network.m_endpointType = "null"; + conf.network.m_keyfile = keyfile; + conf.bootstrap.skipBootstrap = true; + conf.api.m_enableRPCServer = false; - context->config->network.m_endpointType = "null"; - context->config->network.m_keyfile = keyfile; - context->config->bootstrap.skipBootstrap = true; - context->config->api.m_enableRPCServer = false; + auto context = std::make_shared(); + REQUIRE_NOTHROW(context->Configure(std::move(conf))); return context; } From 90ec789fe9fa6f00fade6f33a201cd6d703ad329 Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Mon, 6 Jul 2020 13:41:21 -0600 Subject: [PATCH 54/70] Avoid lokid in router hive --- test/hive/hive.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/hive/hive.py b/test/hive/hive.py index 3adda4eef..d78c16c3f 100644 --- a/test/hive/hive.py +++ b/test/hive/hive.py @@ -80,6 +80,8 @@ class RouterHive(object): config.api.enableRPCServer = False + config.lokid.whitelistRouters = False + print("adding relay at index %d" % port); self.hive.AddRelay(config) @@ -110,6 +112,8 @@ class RouterHive(object): config.api.enableRPCServer = False + config.lokid.whitelistRouters = False + self.hive.AddClient(config) def InitFirstRC(self): From 0e99f5bc8668e89b3863f074ba193bcffa1079be Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Mon, 6 Jul 2020 16:10:42 -0600 Subject: [PATCH 55/70] Configure HiveContext it pybind correctly --- pybind/llarp/context.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pybind/llarp/context.cpp b/pybind/llarp/context.cpp index 3298f14da..a179a0a32 100644 --- a/pybind/llarp/context.cpp +++ b/pybind/llarp/context.cpp @@ -45,7 +45,8 @@ namespace tooling void HiveContext_Init(py::module& mod) { - py::class_(mod, "HiveContext") + using HiveContext_ptr = std::shared_ptr; + py::class_(mod, "HiveContext") .def("getRouterAsHiveRouter", &tooling::HiveContext::getRouterAsHiveRouter); } } // namespace tooling From 305795315b9150b803f6a8544f748e47b988e742 Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Mon, 6 Jul 2020 17:38:41 -0600 Subject: [PATCH 56/70] Specify disabling RC gossiping (testing only) properly --- llarp/tooling/hive_router.cpp | 4 ++-- pybind/llarp/config.cpp | 3 ++- test/hive/hive.py | 1 + 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/llarp/tooling/hive_router.cpp b/llarp/tooling/hive_router.cpp index 0fc74ae39..3a40e2e05 100644 --- a/llarp/tooling/hive_router.cpp +++ b/llarp/tooling/hive_router.cpp @@ -19,13 +19,13 @@ namespace tooling void HiveRouter::disableGossiping() { - m_disableGossiping = false; + m_disableGossiping = true; } void HiveRouter::enableGossiping() { - m_disableGossiping = true; + m_disableGossiping = false; } void diff --git a/pybind/llarp/config.cpp b/pybind/llarp/config.cpp index 2ca9934ee..e38626645 100644 --- a/pybind/llarp/config.cpp +++ b/pybind/llarp/config.cpp @@ -48,7 +48,8 @@ namespace llarp }) .def_readwrite("workerThreads", &RouterConfig::m_workerThreads) .def_readwrite("numNetThreads", &RouterConfig::m_numNetThreads) - .def_readwrite("JobQueueSize", &RouterConfig::m_JobQueueSize); + .def_readwrite("JobQueueSize", &RouterConfig::m_JobQueueSize) + .def_readwrite("enablePeerStats", &RouterConfig::m_enablePeerStats); py::class_(mod, "NetworkConfig") .def(py::init<>()) diff --git a/test/hive/hive.py b/test/hive/hive.py index d78c16c3f..fa6fb88e0 100644 --- a/test/hive/hive.py +++ b/test/hive/hive.py @@ -65,6 +65,7 @@ class RouterHive(object): config.router.nickname = "Router%d" % index config.router.overrideAddress('127.0.0.1:{}'.format(port)) config.router.blockBogons = False + config.router.enablePeerStats = True config.network.enableProfiling = False config.network.routerProfilesFile = "%s/profiles.dat" % dirname From a88dc9f026f6067c057fcf800dac182d157e08f3 Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Mon, 6 Jul 2020 17:39:22 -0600 Subject: [PATCH 57/70] Prevent pybind from deleting HiveRouter pointers --- pybind/llarp/context.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pybind/llarp/context.cpp b/pybind/llarp/context.cpp index a179a0a32..b15b67bae 100644 --- a/pybind/llarp/context.cpp +++ b/pybind/llarp/context.cpp @@ -47,6 +47,9 @@ namespace tooling { using HiveContext_ptr = std::shared_ptr; py::class_(mod, "HiveContext") - .def("getRouterAsHiveRouter", &tooling::HiveContext::getRouterAsHiveRouter); + .def( + "getRouterAsHiveRouter", + &tooling::HiveContext::getRouterAsHiveRouter, + py::return_value_policy::reference); } } // namespace tooling From 56e2bc2c4774ec6e6f3213285db80d7afa49cd26 Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Mon, 6 Jul 2020 17:40:42 -0600 Subject: [PATCH 58/70] Acquire python's Global Interpreter Lock in callbacks --- pybind/llarp/tooling/router_hive.cpp | 30 +++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/pybind/llarp/tooling/router_hive.cpp b/pybind/llarp/tooling/router_hive.cpp index 86156f5dc..b5c23d825 100644 --- a/pybind/llarp/tooling/router_hive.cpp +++ b/pybind/llarp/tooling/router_hive.cpp @@ -12,6 +12,9 @@ namespace tooling RouterHive_Init(py::module& mod) { using RouterHive_ptr = std::shared_ptr; + using Context_ptr = RouterHive::Context_ptr; + using ContextVisitor = std::function; + py::class_(mod, "RouterHive") .def(py::init<>()) .def("AddRelay", &RouterHive::AddRelay) @@ -19,9 +22,30 @@ namespace tooling .def("StartRelays", &RouterHive::StartRelays) .def("StartClients", &RouterHive::StartClients) .def("StopAll", &RouterHive::StopRouters) - .def("ForEachRelay", &RouterHive::ForEachRelay) - .def("ForEachClient", &RouterHive::ForEachClient) - .def("ForEachRouter", &RouterHive::ForEachRouter) + .def( + "ForEachRelay", + [](RouterHive& hive, ContextVisitor visit) { + hive.ForEachRelay([visit](Context_ptr ctx) { + py::gil_scoped_acquire acquire; + visit(std::move(ctx)); + }); + }) + .def( + "ForEachClient", + [](RouterHive& hive, ContextVisitor visit) { + hive.ForEachClient([visit](Context_ptr ctx) { + py::gil_scoped_acquire acquire; + visit(std::move(ctx)); + }); + }) + .def( + "ForEachRouter", + [](RouterHive& hive, ContextVisitor visit) { + hive.ForEachRouter([visit](Context_ptr ctx) { + py::gil_scoped_acquire acquire; + visit(std::move(ctx)); + }); + }) .def("GetNextEvent", &RouterHive::GetNextEvent) .def("GetAllEvents", &RouterHive::GetAllEvents) .def("RelayConnectedRelays", &RouterHive::RelayConnectedRelays) From 159447b984da31de6c151b34f6a3dd415f7a655b Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Mon, 6 Jul 2020 18:11:15 -0600 Subject: [PATCH 59/70] Don't "safely" visit relays in RouterHive --- llarp/tooling/router_hive.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/llarp/tooling/router_hive.cpp b/llarp/tooling/router_hive.cpp index 8777b083e..647153772 100644 --- a/llarp/tooling/router_hive.cpp +++ b/llarp/tooling/router_hive.cpp @@ -148,7 +148,10 @@ namespace tooling void RouterHive::VisitRouter(Context_ptr ctx, std::function visit) { - LogicCall(ctx->logic, [visit, ctx]() { visit(ctx); }); + // TODO: this should be called from each router's appropriate Logic thread, e.g.: + // LogicCall(ctx->logic, [visit, ctx]() { visit(ctx); }); + // however, this causes visit calls to be deferred + visit(ctx); } HiveRouter* From baac6bf3bd3f7806ec4cc3df334570a45ed899dd Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Mon, 6 Jul 2020 18:11:53 -0600 Subject: [PATCH 60/70] Update python test code to reflect HiveRouter API --- test/hive/test_peer_stats.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/hive/test_peer_stats.py b/test/hive/test_peer_stats.py index 0905822e8..c8f934053 100644 --- a/test/hive/test_peer_stats.py +++ b/test/hive/test_peer_stats.py @@ -64,7 +64,7 @@ def test_peer_stats(HiveForPeerStats): # stop our router from gossiping router = hive.hive.GetRelay(someRouterId, True) - router.stopGossipingRC(); + router.disableGossiping(); ignore = collectStatsForAWhile(30); From 9deee9e542a2979d7ec89b69ca55ec5386735d72 Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Thu, 9 Jul 2020 13:06:31 -0600 Subject: [PATCH 61/70] Add bencoding serialization to PeerStats --- llarp/peerstats/types.cpp | 92 ++++++++++++++++++++++++------ llarp/peerstats/types.hpp | 3 + test/peerstats/test_peer_types.cpp | 54 ++++++++++++++++++ 3 files changed, 133 insertions(+), 16 deletions(-) diff --git a/llarp/peerstats/types.cpp b/llarp/peerstats/types.cpp index c861c3527..4a0900760 100644 --- a/llarp/peerstats/types.cpp +++ b/llarp/peerstats/types.cpp @@ -1,7 +1,29 @@ #include +#include + +#include + namespace llarp { + + constexpr auto RouterIdKey = "routerId"; + constexpr auto NumConnectionAttemptsKey = "numConnectionAttempts"; + constexpr auto NumConnectionSuccessesKey = "numConnectionSuccesses"; + constexpr auto NumConnectionRejectionsKey = "numConnectionRejections"; + constexpr auto NumConnectionTimeoutsKey = "numConnectionTimeouts"; + constexpr auto NumPathBuildsKey = "numPathBuilds"; + constexpr auto NumPacketsAttemptedKey = "numPacketsAttempted"; + constexpr auto NumPacketsSentKey = "numPacketsSent"; + constexpr auto NumPacketsDroppedKey = "numPacketsDropped"; + constexpr auto NumPacketsResentKey = "numPacketsResent"; + constexpr auto NumDistinctRCsReceivedKey = "numDistinctRCsReceived"; + constexpr auto NumLateRCsKey = "numLateRCs"; + constexpr auto PeakBandwidthBytesPerSecKey = "peakBandwidthBytesPerSec"; + constexpr auto LongestRCReceiveIntervalKey = "longestRCReceiveInterval"; + constexpr auto LeastRCRemainingLifetimeKey = "leastRCRemainingLifetime"; + constexpr auto LastRCUpdatedKey = "lastRCUpdated"; + PeerStats::PeerStats() = default; PeerStats::PeerStats(const RouterID& routerId_) : routerId(routerId_) @@ -59,23 +81,61 @@ namespace llarp PeerStats::toJson() const { return { - {"routerId", routerId.ToString()}, - // {"numConnectionAttempts", numConnectionAttempts}, - // {"numConnectionSuccesses", numConnectionSuccesses}, - // {"numConnectionRejections", numConnectionRejections}, - // {"numConnectionTimeouts", numConnectionTimeouts}, - // {"numPathBuilds", numPathBuilds}, - // {"numPacketsAttempted", numPacketsAttempted}, - // {"numPacketsSent", numPacketsSent}, - // {"numPacketsDropped", numPacketsDropped}, - // {"numPacketsResent", numPacketsResent}, - {"numDistinctRCsReceived", numDistinctRCsReceived}, - {"numLateRCs", numLateRCs}, - // {"peakBandwidthBytesPerSec", peakBandwidthBytesPerSec}, - {"longestRCReceiveInterval", longestRCReceiveInterval.count()}, - {"leastRCRemainingLifetime", leastRCRemainingLifetime.count()}, - {"lastRCUpdated", lastRCUpdated.count()}, + {RouterIdKey, routerId.ToString()}, + {NumConnectionAttemptsKey, numConnectionAttempts}, + {NumConnectionSuccessesKey, numConnectionSuccesses}, + {NumConnectionRejectionsKey, numConnectionRejections}, + {NumConnectionTimeoutsKey, numConnectionTimeouts}, + {NumPathBuildsKey, numPathBuilds}, + {NumPacketsAttemptedKey, numPacketsAttempted}, + {NumPacketsSentKey, numPacketsSent}, + {NumPacketsDroppedKey, numPacketsDropped}, + {NumPacketsResentKey, numPacketsResent}, + {NumDistinctRCsReceivedKey, numDistinctRCsReceived}, + {NumLateRCsKey, numLateRCs}, + {PeakBandwidthBytesPerSecKey, peakBandwidthBytesPerSec}, + {LongestRCReceiveIntervalKey, longestRCReceiveInterval.count()}, + {LeastRCRemainingLifetimeKey, leastRCRemainingLifetime.count()}, + {LastRCUpdatedKey, lastRCUpdated.count()}, + }; + } + + void + PeerStats::BEncode(llarp_buffer_t* buf) + { + if (not buf) + throw std::runtime_error("PeerStats: Can't use null buf"); + + auto encodeUint64Entry = [&](std::string_view key, uint64_t value) { + if (not bencode_write_uint64_entry(buf, key.data(), key.size(), value)) + throw std::runtime_error(stringify("PeerStats: Could not encode ", key)); }; + + if (not bencode_start_dict(buf)) + throw std::runtime_error("PeerStats: Could not create bencode dict"); + + // TODO: we don't have bencode support for dict entries other than uint64...? + + // encodeUint64Entry(RouterIdKey, routerId); + encodeUint64Entry(NumConnectionAttemptsKey, numConnectionAttempts); + encodeUint64Entry(NumConnectionSuccessesKey, numConnectionSuccesses); + encodeUint64Entry(NumConnectionRejectionsKey, numConnectionRejections); + encodeUint64Entry(NumConnectionTimeoutsKey, numConnectionTimeouts); + encodeUint64Entry(NumPathBuildsKey, numPathBuilds); + encodeUint64Entry(NumPacketsAttemptedKey, numPacketsAttempted); + encodeUint64Entry(NumPacketsSentKey, numPacketsSent); + encodeUint64Entry(NumPacketsDroppedKey, numPacketsDropped); + encodeUint64Entry(NumPacketsResentKey, numPacketsResent); + encodeUint64Entry(NumDistinctRCsReceivedKey, numDistinctRCsReceived); + encodeUint64Entry(NumLateRCsKey, numLateRCs); + encodeUint64Entry(PeakBandwidthBytesPerSecKey, (uint64_t)peakBandwidthBytesPerSec); + encodeUint64Entry(LongestRCReceiveIntervalKey, longestRCReceiveInterval.count()); + encodeUint64Entry(LeastRCRemainingLifetimeKey, leastRCRemainingLifetime.count()); + encodeUint64Entry(LastRCUpdatedKey, lastRCUpdated.count()); + + if (not bencode_end(buf)) + throw std::runtime_error("PeerStats: Could not end bencode dict"); + } }; // namespace llarp diff --git a/llarp/peerstats/types.hpp b/llarp/peerstats/types.hpp index 31e425273..d705d99db 100644 --- a/llarp/peerstats/types.hpp +++ b/llarp/peerstats/types.hpp @@ -48,6 +48,9 @@ namespace llarp util::StatusObject toJson() const; + + void + BEncode(llarp_buffer_t* buf); }; } // namespace llarp diff --git a/test/peerstats/test_peer_types.cpp b/test/peerstats/test_peer_types.cpp index d260f6d5a..e957d9dbf 100644 --- a/test/peerstats/test_peer_types.cpp +++ b/test/peerstats/test_peer_types.cpp @@ -1,5 +1,6 @@ #include #include +#include #include @@ -21,3 +22,56 @@ TEST_CASE("Test PeerStats operator+=", "[PeerStats]") CHECK(stats.numConnectionAttempts == 3); CHECK(stats.peakBandwidthBytesPerSec == 12); // should take max(), not add } + +TEST_CASE("Test PeerStats BEncode", "[PeerStats]") +{ + llarp::RouterID id = llarp::test::makeBuf(0x01); + + llarp::PeerStats stats(id); + + stats.numConnectionAttempts = 1; + stats.numConnectionSuccesses = 2; + stats.numConnectionRejections = 3; + stats.numConnectionTimeouts = 4; + stats.numPathBuilds = 5; + stats.numPacketsAttempted = 6; + stats.numPacketsSent = 7; + stats.numPacketsDropped = 8; + stats.numPacketsResent = 9; + stats.numDistinctRCsReceived = 10; + stats.numLateRCs = 11; + stats.peakBandwidthBytesPerSec = 12.1; // should truncate to 12 + stats.longestRCReceiveInterval = 13ms; + stats.leastRCRemainingLifetime = 14ms; + stats.lastRCUpdated = 15ms; + + constexpr int bufSize = 4096; + byte_t* raw = new byte_t[bufSize]; + llarp_buffer_t buf(raw, bufSize); + + CHECK_NOTHROW(stats.BEncode(&buf)); + + std::string asString = (const char*)raw; + constexpr std::string_view expected = + "d" + "21:numConnectionAttempts" "i1e" + "22:numConnectionSuccesses" "i2e" + "23:numConnectionRejections" "i3e" + "21:numConnectionTimeouts" "i4e" + "13:numPathBuilds" "i5e" + "19:numPacketsAttempted" "i6e" + "14:numPacketsSent" "i7e" + "17:numPacketsDropped" "i8e" + "16:numPacketsResent" "i9e" + "22:numDistinctRCsReceived" "i10e" + "10:numLateRCs" "i11e" + "24:peakBandwidthBytesPerSec" "i12e" + "24:longestRCReceiveInterval" "i13e" + "24:leastRCRemainingLifetime" "i14e" + "13:lastRCUpdated" "i15e" + "e"; + + CHECK(asString == expected); + + delete [] raw; +} From acb0248f94a82c47f11875e74d373e1aa3fb9380 Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Thu, 16 Jul 2020 16:46:59 -0600 Subject: [PATCH 62/70] Use LMQ request instead of command for lokid ping --- llarp/rpc/lokid_rpc_client.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/llarp/rpc/lokid_rpc_client.cpp b/llarp/rpc/lokid_rpc_client.cpp index 304331125..9b1de7618 100644 --- a/llarp/rpc/lokid_rpc_client.cpp +++ b/llarp/rpc/lokid_rpc_client.cpp @@ -100,10 +100,18 @@ namespace llarp constexpr auto PingInterval = 1min; constexpr auto NodeListUpdateInterval = 30s; - LogInfo("we connected to lokid [", *m_Connection, "]"); - Command("admin.lokinet_ping"); - m_lokiMQ->add_timer( - [self = shared_from_this()]() { self->Command("admin.lokinet_ping"); }, PingInterval); + auto makePingRequest = [self = shared_from_this()]() { + nlohmann::json payload = {{"version", {VERSION[0], VERSION[1], VERSION[2]}}}; + self->Request( + "admin.lokinet_ping", + [](bool success, std::vector data) { + (void)data; + LogDebug("Received response for ping. Successful: ", success); + }, + payload.dump()); + }; + makePingRequest(); + m_lokiMQ->add_timer(makePingRequest, PingInterval); m_lokiMQ->add_timer( [self = shared_from_this()]() { self->UpdateServiceNodeList(); }, NodeListUpdateInterval); UpdateServiceNodeList(); From c07dcaa2efd4cd1bd0f1b6461a14c57ad6f07ef8 Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Thu, 16 Jul 2020 16:48:04 -0600 Subject: [PATCH 63/70] Handle service node privkeys response correctly --- llarp/rpc/lokid_rpc_client.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/llarp/rpc/lokid_rpc_client.cpp b/llarp/rpc/lokid_rpc_client.cpp index 9b1de7618..1dba52f02 100644 --- a/llarp/rpc/lokid_rpc_client.cpp +++ b/llarp/rpc/lokid_rpc_client.cpp @@ -183,13 +183,13 @@ namespace llarp "failed to get private key request " "failed"); } - if (data.empty()) + if (data.empty() or data.size() < 2) { throw std::runtime_error( "failed to get private key request " "data empty"); } - const auto j = nlohmann::json::parse(data[0]); + const auto j = nlohmann::json::parse(data[1]); SecretKey k; if (not k.FromHex(j.at("service_node_ed25519_privkey").get())) { @@ -197,8 +197,14 @@ namespace llarp } promise.set_value(k); } + catch (const std::exception& e) + { + LogWarn("Caught exception while trying to request admin keys: ", e.what()); + promise.set_exception(std::current_exception()); + } catch (...) { + LogWarn("Caught non-standard exception while trying to request admin keys"); promise.set_exception(std::current_exception()); } }); From bbc1cd5a3122014d482692af77b35dc1f22e1ffa Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Thu, 16 Jul 2020 16:48:26 -0600 Subject: [PATCH 64/70] Stub out get_peer_stats LMQ API request --- llarp/rpc/lokid_rpc_client.cpp | 16 ++++++++++++++++ llarp/rpc/lokid_rpc_client.hpp | 4 ++++ 2 files changed, 20 insertions(+) diff --git a/llarp/rpc/lokid_rpc_client.cpp b/llarp/rpc/lokid_rpc_client.cpp index 1dba52f02..640131167 100644 --- a/llarp/rpc/lokid_rpc_client.cpp +++ b/llarp/rpc/lokid_rpc_client.cpp @@ -36,6 +36,11 @@ namespace llarp : m_lokiMQ(std::move(lmq)), m_Router(r) { // m_lokiMQ->log_level(toLokiMQLogLevel(LogLevel::Instance().curLevel)); + + // TODO: proper auth here + auto lokidCategory = m_lokiMQ->add_category("lokid", lokimq::Access{lokimq::AuthLevel::none}); + lokidCategory.add_request_command( + "get_peer_stats", [this](lokimq::Message& m) { HandleGetPeerStats(m); }); } void @@ -212,5 +217,16 @@ namespace llarp return ftr.get(); } + void + LokidRpcClient::HandleGetPeerStats(lokimq::Message& msg) + { + // TODO: construct response + LogInfo("Got request for peer stats (size: ", msg.data.size(), ")"); + for (auto str : msg.data) + { + LogInfo(" :", str); + } + } + } // namespace rpc } // namespace llarp diff --git a/llarp/rpc/lokid_rpc_client.hpp b/llarp/rpc/lokid_rpc_client.hpp index 682c143ed..b8cef923e 100644 --- a/llarp/rpc/lokid_rpc_client.hpp +++ b/llarp/rpc/lokid_rpc_client.hpp @@ -59,6 +59,10 @@ namespace llarp void HandleGotServiceNodeList(std::string json); + // Handles request from lokid for peer stats on a specific peer + void + HandleGetPeerStats(lokimq::Message& msg); + std::optional m_Connection; LMQ_ptr m_lokiMQ; std::string m_CurrentBlockHash; From eedf7ca599b799b11f4997f327f6e081430e16ec Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Mon, 20 Jul 2020 13:48:57 -0600 Subject: [PATCH 65/70] Add implementation of get_peer_stats API --- llarp/peerstats/peer_db.cpp | 16 ++++++++++++++++ llarp/peerstats/peer_db.hpp | 8 ++++++++ llarp/peerstats/types.cpp | 20 +++++++++++++++++++- llarp/peerstats/types.hpp | 5 ++++- llarp/rpc/lokid_rpc_client.cpp | 30 +++++++++++++++++++++++++++++- 5 files changed, 76 insertions(+), 3 deletions(-) diff --git a/llarp/peerstats/peer_db.cpp b/llarp/peerstats/peer_db.cpp index 045839d43..9d4fe50d2 100644 --- a/llarp/peerstats/peer_db.cpp +++ b/llarp/peerstats/peer_db.cpp @@ -140,6 +140,22 @@ namespace llarp return itr->second; } + std::vector + PeerDb::listAllPeerStats() const + { + std::lock_guard guard(m_statsLock); + + std::vector statsList; + statsList.reserve(m_peerStats.size()); + + for (const auto& [routerId, stats] : m_peerStats) + { + statsList.push_back(stats); + } + + return statsList; + } + /// Assume we receive an RC at some point `R` in time which was signed at some point `S` in time /// and expires at some point `E` in time, as depicted below: /// diff --git a/llarp/peerstats/peer_db.hpp b/llarp/peerstats/peer_db.hpp index c61fb3716..d7985e1bd 100644 --- a/llarp/peerstats/peer_db.hpp +++ b/llarp/peerstats/peer_db.hpp @@ -82,6 +82,14 @@ namespace llarp std::optional getCurrentPeerStats(const RouterID& routerId) const; + /// Lists all peer stats. This essentially dumps the database into a list of PeerStats objects. + /// + /// Note that this avoids disk I/O by copying from our cached map of peers. + /// + /// @return a list of all PeerStats we have maintained + std::vector + listAllPeerStats() const; + /// Handles a new gossiped RC, updating stats as needed. The database tracks the last /// advertised update time, so it knows whether this is a new RC or not. /// diff --git a/llarp/peerstats/types.cpp b/llarp/peerstats/types.cpp index 4a0900760..af78d70c0 100644 --- a/llarp/peerstats/types.cpp +++ b/llarp/peerstats/types.cpp @@ -101,7 +101,7 @@ namespace llarp } void - PeerStats::BEncode(llarp_buffer_t* buf) + PeerStats::BEncode(llarp_buffer_t* buf) const { if (not buf) throw std::runtime_error("PeerStats: Can't use null buf"); @@ -138,4 +138,22 @@ namespace llarp } + void + PeerStats::BEncodeList(const std::vector& statsList, llarp_buffer_t* buf) + { + if (not buf) + throw std::runtime_error("PeerStats: Can't use null buf"); + + if (not bencode_start_list(buf)) + throw std::runtime_error("PeerStats: Could not create bencode dict"); + + for (const auto& stats : statsList) + { + stats.BEncode(buf); + } + + if (not bencode_end(buf)) + throw std::runtime_error("PeerStats: Could not end bencode dict"); + } + }; // namespace llarp diff --git a/llarp/peerstats/types.hpp b/llarp/peerstats/types.hpp index d705d99db..78684a61a 100644 --- a/llarp/peerstats/types.hpp +++ b/llarp/peerstats/types.hpp @@ -50,7 +50,10 @@ namespace llarp toJson() const; void - BEncode(llarp_buffer_t* buf); + BEncode(llarp_buffer_t* buf) const; + + static void + BEncodeList(const std::vector& statsList, llarp_buffer_t* buf); }; } // namespace llarp diff --git a/llarp/rpc/lokid_rpc_client.cpp b/llarp/rpc/lokid_rpc_client.cpp index 640131167..aef954844 100644 --- a/llarp/rpc/lokid_rpc_client.cpp +++ b/llarp/rpc/lokid_rpc_client.cpp @@ -220,12 +220,40 @@ namespace llarp void LokidRpcClient::HandleGetPeerStats(lokimq::Message& msg) { - // TODO: construct response LogInfo("Got request for peer stats (size: ", msg.data.size(), ")"); for (auto str : msg.data) { LogInfo(" :", str); } + + assert(m_Router != nullptr); + + if (not m_Router->peerDb()) + { + LogWarn("HandleGetPeerStats called when router has no peerDb set up."); + throw std::runtime_error("Cannot handle get_peer_stats request when no peer db available"); + } + + try + { + // TODO: parse input, expect list of peers to query for + + auto statsList = m_Router->peerDb()->listAllPeerStats(); + + int32_t bufSize = + 256 + (statsList.size() * 1024); // TODO: tune this or allow to grow dynamically + auto buf = std::unique_ptr(new uint8_t[bufSize]); + llarp_buffer_t llarpBuf(buf.get(), bufSize); + + PeerStats::BEncodeList(statsList, &llarpBuf); + + msg.send_reply(std::string_view((const char*)llarpBuf.base, llarpBuf.cur - llarpBuf.base)); + } + catch (const std::exception& e) + { + LogError("Failed to handle get_peer_stats request: ", e.what()); + // TODO: reply with explicit rejection to make lokid's life easier + } } } // namespace rpc From 607d04a3140a23367dad88e0bf3d23c06dfef800 Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Thu, 23 Jul 2020 10:52:53 -0600 Subject: [PATCH 66/70] Use str instead of lokimq::ConnectionID in pybind --- pybind/llarp/config.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pybind/llarp/config.cpp b/pybind/llarp/config.cpp index e38626645..b14575013 100644 --- a/pybind/llarp/config.cpp +++ b/pybind/llarp/config.cpp @@ -102,7 +102,10 @@ namespace llarp .def_readwrite("usingSNSeed", &LokidConfig::usingSNSeed) .def_readwrite("whitelistRouters", &LokidConfig::whitelistRouters) .def_readwrite("ident_keyfile", &LokidConfig::ident_keyfile) - .def_readwrite("lokidRPCAddr", &LokidConfig::lokidRPCAddr); + .def_property( + "lokidRPCAddr", + [](LokidConfig& self) { return self.lokidRPCAddr.full_address().c_str(); }, + [](LokidConfig& self, std::string arg) { self.lokidRPCAddr = lokimq::address(arg); }); py::class_(mod, "BootstrapConfig") .def(py::init<>()) From 1d9c337021346ad0df43425caa862f1cc7913b90 Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Thu, 23 Jul 2020 10:53:34 -0600 Subject: [PATCH 67/70] Grab lokimq::ConnectionID on connection attempt --- llarp/rpc/lokid_rpc_client.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/llarp/rpc/lokid_rpc_client.cpp b/llarp/rpc/lokid_rpc_client.cpp index aef954844..68c3498cc 100644 --- a/llarp/rpc/lokid_rpc_client.cpp +++ b/llarp/rpc/lokid_rpc_client.cpp @@ -47,12 +47,9 @@ namespace llarp LokidRpcClient::ConnectAsync(lokimq::address url) { LogInfo("connecting to lokid via LMQ at ", url); - m_lokiMQ->connect_remote( + m_Connection = m_lokiMQ->connect_remote( url, - [self = shared_from_this()](lokimq::ConnectionID c) { - self->m_Connection = std::move(c); - self->Connected(); - }, + [self = shared_from_this()](lokimq::ConnectionID) { self->Connected(); }, [self = shared_from_this(), url](lokimq::ConnectionID, std::string_view f) { llarp::LogWarn("Failed to connect to lokid: ", f); LogicCall(self->m_Router->logic(), [self, url]() { self->ConnectAsync(url); }); From 4699280d97cee4f75565ef439e2aa3c3695b9627 Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Thu, 23 Jul 2020 10:54:39 -0600 Subject: [PATCH 68/70] Crude attempt at retrying lokid connection --- llarp/router/router.cpp | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/llarp/router/router.cpp b/llarp/router/router.cpp index 02db1131c..7bbc82ecf 100644 --- a/llarp/router/router.cpp +++ b/llarp/router/router.cpp @@ -222,7 +222,31 @@ namespace llarp return false; #endif #endif - _identity = RpcClient()->ObtainIdentityKey(); + constexpr int maxTries = 5; + int numTries = 0; + while (numTries < maxTries) + { + numTries++; + try + { + _identity = RpcClient()->ObtainIdentityKey(); + LogWarn("Obtained lokid identity keys"); + break; + } + catch (const std::exception& e) + { + LogWarn( + "Failed attempt ", + numTries, + " of ", + maxTries, + " to get lokid identity keys because: ", + e.what()); + + if (numTries == maxTries) + throw; + } + } } else { From b037cf0ae4545e8ddf52e5a5ed9cece50b543bad Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Fri, 24 Jul 2020 11:49:14 -0600 Subject: [PATCH 69/70] Handle get_peer_stats request's list of router ids --- llarp/peerstats/peer_db.cpp | 18 ++++++++++++++++++ llarp/peerstats/peer_db.hpp | 7 +++++++ llarp/rpc/lokid_rpc_client.cpp | 33 ++++++++++++++++++++++++++++++--- 3 files changed, 55 insertions(+), 3 deletions(-) diff --git a/llarp/peerstats/peer_db.cpp b/llarp/peerstats/peer_db.cpp index 9d4fe50d2..3ca8d4328 100644 --- a/llarp/peerstats/peer_db.cpp +++ b/llarp/peerstats/peer_db.cpp @@ -156,6 +156,24 @@ namespace llarp return statsList; } + std::vector + PeerDb::listPeerStats(const std::vector& ids) const + { + std::lock_guard guard(m_statsLock); + + std::vector statsList; + statsList.reserve(ids.size()); + + for (const auto& id : ids) + { + const auto itr = m_peerStats.find(id); + if (itr != m_peerStats.end()) + statsList.push_back(itr->second); + } + + return statsList; + } + /// Assume we receive an RC at some point `R` in time which was signed at some point `S` in time /// and expires at some point `E` in time, as depicted below: /// diff --git a/llarp/peerstats/peer_db.hpp b/llarp/peerstats/peer_db.hpp index d7985e1bd..d5d4a1a05 100644 --- a/llarp/peerstats/peer_db.hpp +++ b/llarp/peerstats/peer_db.hpp @@ -90,6 +90,13 @@ namespace llarp std::vector listAllPeerStats() const; + /// Lists specific peer stats. + /// + /// @param peers is list of RouterIDs which are desired + /// @return a list of the requested peers. Peers not found will be omitted. + std::vector + listPeerStats(const std::vector& ids) const; + /// Handles a new gossiped RC, updating stats as needed. The database tracks the last /// advertised update time, so it knows whether this is a new RC or not. /// diff --git a/llarp/rpc/lokid_rpc_client.cpp b/llarp/rpc/lokid_rpc_client.cpp index 68c3498cc..e5cd8c6c7 100644 --- a/llarp/rpc/lokid_rpc_client.cpp +++ b/llarp/rpc/lokid_rpc_client.cpp @@ -1,5 +1,6 @@ #include +#include #include #include @@ -228,14 +229,40 @@ namespace llarp if (not m_Router->peerDb()) { LogWarn("HandleGetPeerStats called when router has no peerDb set up."); - throw std::runtime_error("Cannot handle get_peer_stats request when no peer db available"); + + // TODO: this can sometimes occur if lokid hits our API before we're done configuring + // (mostly an issue in a loopback testnet) + msg.send_reply("EAGAIN"); + return; } try { - // TODO: parse input, expect list of peers to query for + // msg.data[0] is expected to contain a bt list of router ids (in our preferred string + // format) + if (msg.data.empty()) + { + LogWarn("lokid requested peer stats with no request body"); + msg.send_reply("peer stats request requires list of router IDs"); + return; + } + + std::vector routerIdStrings; + lokimq::bt_deserialize(msg.data[0], routerIdStrings); + + std::vector routerIds; + routerIds.reserve(routerIdStrings.size()); + + for (const auto& routerIdString : routerIdStrings) + { + RouterID id; + if (not id.FromString(routerIdString)) + throw std::invalid_argument(stringify("Invalid router id: ", routerIdString)); + + routerIds.push_back(std::move(id)); + } - auto statsList = m_Router->peerDb()->listAllPeerStats(); + auto statsList = m_Router->peerDb()->listPeerStats(routerIds); int32_t bufSize = 256 + (statsList.size() * 1024); // TODO: tune this or allow to grow dynamically From 83d337ddfd0da8da1a3781eddd08b9c545e54d1f Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Fri, 24 Jul 2020 11:55:15 -0600 Subject: [PATCH 70/70] Send response on error conditions in API request --- llarp/rpc/lokid_rpc_client.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/llarp/rpc/lokid_rpc_client.cpp b/llarp/rpc/lokid_rpc_client.cpp index e5cd8c6c7..54f74b6e6 100644 --- a/llarp/rpc/lokid_rpc_client.cpp +++ b/llarp/rpc/lokid_rpc_client.cpp @@ -257,7 +257,11 @@ namespace llarp { RouterID id; if (not id.FromString(routerIdString)) - throw std::invalid_argument(stringify("Invalid router id: ", routerIdString)); + { + LogWarn("lokid sent us an invalid router id: ", routerIdString); + msg.send_reply("Invalid router id"); + return; + } routerIds.push_back(std::move(id)); } @@ -276,7 +280,7 @@ namespace llarp catch (const std::exception& e) { LogError("Failed to handle get_peer_stats request: ", e.what()); - // TODO: reply with explicit rejection to make lokid's life easier + msg.send_reply("server error"); } }