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:
parent
2425652696
commit
b044622a21
|
@ -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
|
||||||
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue