1
1
Fork 0
mirror of https://github.com/oxen-io/lokinet synced 2023-12-14 06:53:00 +01:00

implement new rc gossip logic

Relays will now re-sign and gossip their RCs every 6 hours (minus a
couple random minutes) using the new gossip_rc message.

Removes the old RCGossiper concept
This commit is contained in:
Thomas Winget 2023-11-17 01:26:59 -05:00
parent 2425652696
commit b044622a21
6 changed files with 31 additions and 279 deletions

View file

@ -24,7 +24,6 @@ lokinet_add_library(lokinet-core-utils
exit/policy.cpp # net/ exit/policy.cpp # net/
handlers/exit.cpp # link/ exit/ handlers/exit.cpp # link/ exit/
handlers/tun.cpp handlers/tun.cpp
router/rc_gossiper.cpp
service/auth.cpp # config/ service/auth.cpp # config/
service/context.cpp service/context.cpp
service/identity.cpp service/identity.cpp

View file

@ -1,151 +0,0 @@
#include "rc_gossiper.hpp"
#include <llarp/router_contact.hpp>
#include <llarp/util/time.hpp>
namespace llarp
{
// 30 minutes
static constexpr auto RCGossipFilterDecayInterval = 30min;
// (30 minutes * 2) - 5 minutes
static constexpr auto GossipOurRCInterval = (RCGossipFilterDecayInterval * 2) - (5min);
RCGossiper::RCGossiper() : filter(std::chrono::duration_cast<Time_t>(RCGossipFilterDecayInterval))
{}
void
RCGossiper::Init(LinkManager* l, const RouterID& ourID, Router* r)
{
rid = ourID;
link_manager = l;
router = r;
}
bool
RCGossiper::ShouldGossipOurRC(Time_t now) const
{
return now >= (last_rc_gossip + GossipOurRCInterval);
}
bool
RCGossiper::IsOurRC(const LocalRC& rc) const
{
return rc.router_id() == rid;
}
void
RCGossiper::Decay(Time_t now)
{
filter.Decay(now);
}
void
RCGossiper::Forget(const RouterID& pk)
{
filter.Remove(pk);
if (rid == pk)
last_rc_gossip = 0s;
}
TimePoint_t
RCGossiper::NextGossipAt() const
{
if (auto maybe = LastGossipAt())
return *maybe + GossipOurRCInterval;
return DateClock_t::now();
}
std::optional<TimePoint_t>
RCGossiper::LastGossipAt() const
{
if (last_rc_gossip == 0s)
return std::nullopt;
return DateClock_t::time_point{last_rc_gossip};
}
bool
RCGossiper::GossipRC(const LocalRC& rc)
{
// only distribute public routers
if (not rc.is_public_router())
return false;
if (link_manager == nullptr)
return false;
const RouterID pubkey(rc.router_id());
// filter check
if (filter.Contains(pubkey))
return false;
filter.Insert(pubkey);
const auto now = time_now_ms();
// is this our rc?
if (IsOurRC(rc))
{
// should we gossip our rc?
if (not ShouldGossipOurRC(now))
{
// nah drop it
return false;
}
// ya pop it
last_rc_gossip = now;
}
// send a GRCM as gossip method
// DHTImmediateMessage gossip;
// gossip.msgs.emplace_back(new dht::GotRouterMessage(dht::Key_t{}, 0, {rc}, false));
// std::vector<RouterID> gossipTo;
/*
* TODO: gossip RC via libquic
*
// select peers to gossip to
m_LinkManager->ForEachPeer(
[&](const AbstractLinkSession* peerSession, bool) {
// ensure connected session
if (not(peerSession && peerSession->IsEstablished()))
return;
// check if public router
const auto other_rc = peerSession->GetRemoteRC();
if (not other_rc.IsPublicRouter())
return;
gossipTo.emplace_back(other_rc.pubkey);
},
true);
std::unordered_set<RouterID> keys;
// grab the keys we want to use
std::sample(
gossipTo.begin(), gossipTo.end(), std::inserter(keys, keys.end()), MaxGossipPeers,
llarp::csrng);
m_LinkManager->ForEachPeer([&](AbstractLinkSession* peerSession) {
if (not(peerSession && peerSession->IsEstablished()))
return;
// exclude from gossip as we have not selected to use it
if (keys.count(peerSession->GetPubKey()) == 0)
return;
// encode message
AbstractLinkSession::Message_t msg{};
msg.resize(MAX_LINK_MSG_SIZE / 2);
llarp_buffer_t buf(msg);
if (not gossip.BEncode(&buf))
return;
msg.resize(buf.cur - buf.base);
m_router->NotifyRouterEvent<tooling::RCGossipSentEvent>(m_router->pubkey(), rc);
// send message
peerSession->SendMessageBuffer(std::move(msg), nullptr, gossip.Priority());
});
*
*
*/
return true;
}
} // namespace llarp

View file

@ -1,57 +0,0 @@
#pragma once
#include <llarp/router_id.hpp>
#include <llarp/util/decaying_hashset.hpp>
#include <optional>
namespace llarp
{
struct Router;
/// The maximum number of peers we will flood a gossiped RC to when propagating an RC
constexpr size_t MaxGossipPeers = 20;
struct LinkManager;
struct LocalRC;
struct RCGossiper
{
using Time_t = Duration_t;
RCGossiper();
~RCGossiper() = default;
bool
GossipRC(const LocalRC& rc);
void
Decay(Time_t now);
bool
ShouldGossipOurRC(Time_t now) const;
bool
IsOurRC(const LocalRC& rc) const;
void
Init(LinkManager*, const RouterID&, Router*);
void
Forget(const RouterID& router);
TimePoint_t
NextGossipAt() const;
std::optional<TimePoint_t>
LastGossipAt() const;
private:
RouterID rid;
Time_t last_rc_gossip = 0s;
LinkManager* link_manager = nullptr;
util::DecayingHashSet<RouterID> filter;
Router* router;
};
} // namespace llarp

View file

@ -227,20 +227,6 @@ namespace llarp
_link_manager.set_conn_persist(remote, until); _link_manager.set_conn_persist(remote, until);
} }
void
Router::GossipRCIfNeeded(const LocalRC rc)
{
/// if we are not a service node forget about gossip
if (not is_service_node())
return;
/// wait for random uptime
if (std::chrono::milliseconds{Uptime()} < _randomStartDelay)
return;
auto view = rc.view();
_link_manager.gossip_rc(
pubkey(), std::string{reinterpret_cast<const char*>(view.data()), view.size()});
}
std::optional<RouterID> std::optional<RouterID>
Router::GetRandomGoodRouter() Router::GetRandomGoodRouter()
{ {
@ -533,16 +519,6 @@ namespace llarp
queue_disk_io([&]() { router_contact.write(our_rc_file); }); queue_disk_io([&]() { router_contact.write(our_rc_file); });
} }
bool
Router::update_rc()
{
router_contact.resign();
if (is_service_node())
save_rc();
return true;
}
bool bool
Router::from_config(const Config& conf) Router::from_config(const Config& conf)
{ {
@ -779,12 +755,12 @@ namespace llarp
" | {} active paths | block {} ", " | {} active paths | block {} ",
path_context().CurrentTransitPaths(), path_context().CurrentTransitPaths(),
(_rpc_client ? _rpc_client->BlockHeight() : 0)); (_rpc_client ? _rpc_client->BlockHeight() : 0));
auto maybe_last = _rcGossiper.LastGossipAt(); bool have_gossiped = last_rc_gossip == std::chrono::system_clock::time_point::min();
fmt::format_to( fmt::format_to(
out, out,
" | gossip: (next/last) {} / {}", " | gossip: (next/last) {} / {}",
short_time_from_now(_rcGossiper.NextGossipAt()), short_time_from_now(next_rc_gossip),
maybe_last ? short_time_from_now(*maybe_last) : "never"); have_gossiped ? short_time_from_now(last_rc_gossip) : "never");
} }
else else
{ {
@ -837,33 +813,28 @@ namespace llarp
report_stats(); report_stats();
} }
_rcGossiper.Decay(now);
const bool is_snode = is_service_node(); const bool is_snode = is_service_node();
const bool is_decommed = appears_decommed(); const bool is_decommed = appears_decommed();
bool should_gossip = appears_funded();
if (is_snode // (relay-only) if we have fetched the relay list from oxend and
and (router_contact.expires_within_delta(now, std::chrono::milliseconds(randint() % 10000)) // we are registered and funded, we want to gossip our RC periodically
or (now - router_contact.timestamp().time_since_epoch()) > rc_regen_interval)) auto now_timepoint = std::chrono::system_clock::time_point(now);
if (is_snode and appears_funded() and (now_timepoint > next_rc_gossip))
{ {
LogInfo("regenerating RC"); log::info(logcat, "regenerating and gossiping RC");
if (update_rc()) router_contact.resign();
{ save_rc();
// our rc changed so we should gossip it auto view = router_contact.view();
should_gossip = true; _link_manager.gossip_rc(
// remove our replay entry so it goes out pubkey(), std::string{reinterpret_cast<const char*>(view.data()), view.size()});
_rcGossiper.Forget(pubkey()); last_rc_gossip = now_timepoint;
}
else // 1min to 5min before "stale time" is next gossip time
LogError("failed to update our RC"); auto random_delta =
} std::chrono::seconds{std::uniform_int_distribution<size_t>{60, 300}(llarp::csrng)};
if (should_gossip) next_rc_gossip = now_timepoint + RouterContact::STALE_AGE - random_delta;
{
// if we have the whitelist enabled, we have fetched the list and we are in either
// the white or grey list, we want to gossip our RC
GossipRCIfNeeded(router_contact);
} }
// remove RCs for nodes that are no longer allowed by network policy // remove RCs for nodes that are no longer allowed by network policy
node_db()->RemoveIf([&](const RemoteRC& rc) -> bool { node_db()->RemoveIf([&](const RemoteRC& rc) -> bool {
// don't purge bootstrap nodes from nodedb // don't purge bootstrap nodes from nodedb
@ -1052,7 +1023,6 @@ namespace llarp
log::info(logcat, "Router initialized as service node!"); log::info(logcat, "Router initialized as service node!");
const RouterID us = pubkey(); const RouterID us = pubkey();
_rcGossiper.Init(&_link_manager, us, this);
// relays do not use profiling // relays do not use profiling
router_profiling().Disable(); router_profiling().Disable();
} }

View file

@ -1,6 +1,5 @@
#pragma once #pragma once
#include "rc_gossiper.hpp"
#include "route_poker.hpp" #include "route_poker.hpp"
#include <llarp/bootstrap.hpp> #include <llarp/bootstrap.hpp>
@ -125,11 +124,10 @@ namespace llarp
Profiling _router_profiling; Profiling _router_profiling;
fs::path _profile_file; fs::path _profile_file;
LinkManager _link_manager{*this}; LinkManager _link_manager{*this};
RCGossiper _rcGossiper; std::chrono::system_clock::time_point last_rc_gossip{
std::chrono::system_clock::time_point::min()};
/// how often do we resign our RC? milliseconds. std::chrono::system_clock::time_point next_rc_gossip{
// TODO: make configurable std::chrono::system_clock::time_point::min()};
llarp_time_t rc_regen_interval = 1h;
// should we be sending padded messages every interval? // should we be sending padded messages every interval?
bool send_padding = false; bool send_padding = false;
@ -147,9 +145,6 @@ namespace llarp
void void
save_rc(); save_rc();
bool
update_rc();
bool bool
from_config(const Config& conf); from_config(const Config& conf);
@ -365,9 +360,6 @@ namespace llarp
std::string std::string
status_line(); status_line();
void
GossipRCIfNeeded(const LocalRC rc);
void void
InitInboundLinks(); InitInboundLinks();

View file

@ -56,19 +56,18 @@ namespace llarp
/// Timespans for RCs: /// Timespans for RCs:
/// How long (relative to its timestamp) before an RC becomes stale. Stale records are used /// How long (from its signing time) before an RC is considered "stale". Relays republish
/// (e.g. for path building) only if there are no non-stale records available, such as might be /// their RCs slightly more frequently than this so that ideally this won't happen.
static constexpr auto STALE_AGE = 6h;
/// How long (from its signing time) before an RC becomes "outdated". Outdated records are used
/// (e.g. for path building) only if there are no newer records available, such as might be
/// the case when a client has been turned off for a while. /// the case when a client has been turned off for a while.
static constexpr auto STALE = 12h; static constexpr auto OUTDATED_AGE = 12h;
/// How long before an RC becomes invalid (and thus deleted). /// How long before an RC becomes invalid (and thus deleted).
static constexpr auto LIFETIME = 30 * 24h; static constexpr auto LIFETIME = 30 * 24h;
/// How long before a relay updates and re-publish its RC to the network. (Relays can
/// re-publish more frequently than this if needed; this is meant to apply only if there are no
/// changes i.e. just to push out a new confirmation of the details).
static constexpr auto REPUBLISH = STALE / 2 - 5min;
ustring_view ustring_view
view() const view() const
{ {