mirror of
https://github.com/oxen-io/lokinet
synced 2023-12-14 06:53:00 +01:00
(WIP) new bootstrap logic
This commit is contained in:
parent
51eab687d3
commit
13e2082862
|
@ -8,7 +8,7 @@ namespace llarp::RCFetchMessage
|
||||||
messages::serialize_response({{messages::STATUS_KEY, "Invalid relay ID requested"}});
|
messages::serialize_response({{messages::STATUS_KEY, "Invalid relay ID requested"}});
|
||||||
|
|
||||||
inline static std::string
|
inline static std::string
|
||||||
serialize(std::chrono::system_clock::time_point since, const std::vector<RouterID>& explicit_ids)
|
serialize(std::chrono::system_clock::time_point since, const std::vector<RouterID>& explicit_ids = {})
|
||||||
{
|
{
|
||||||
oxenc::bt_dict_producer btdp;
|
oxenc::bt_dict_producer btdp;
|
||||||
|
|
||||||
|
|
107
llarp/nodedb.cpp
107
llarp/nodedb.cpp
|
@ -106,6 +106,101 @@ namespace llarp
|
||||||
return registered_routers.count(rid);
|
return registered_routers.count(rid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
NodeDB::check_bootstrap_state()
|
||||||
|
{
|
||||||
|
size_t active_count{0};
|
||||||
|
size_t stale_count{0};
|
||||||
|
|
||||||
|
auto now = time_now_ms();
|
||||||
|
for (const auto& [rid, rc] : known_rcs)
|
||||||
|
{
|
||||||
|
if (not rc.is_outdated(now))
|
||||||
|
active_count++;
|
||||||
|
else
|
||||||
|
stale_count++;
|
||||||
|
|
||||||
|
if (active_count > ROUTER_ID_SOURCE_COUNT)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (active_count > ROUTER_ID_SOURCE_COUNT)
|
||||||
|
{
|
||||||
|
log::info(logcat, "We appear to be bootstrapped.");
|
||||||
|
_bootstrapped = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_bootstrapped = false;
|
||||||
|
if (stale_count > ROUTER_ID_SOURCE_COUNT)
|
||||||
|
{
|
||||||
|
log::info(logcat, "We need to soft-bootstrap from stale RCs.");
|
||||||
|
// TODO: initiate soft-bootstrap
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
log::info(logcat, "We need to bootstrap from scratch.");
|
||||||
|
bootstrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
NodeDB::bootstrap(size_t index)
|
||||||
|
{
|
||||||
|
// if we're here, our RC state is unusable; clear it
|
||||||
|
known_rcs.clear();
|
||||||
|
last_rc_update_times.clear();
|
||||||
|
|
||||||
|
// bootstrapping has failed completely, inform Router to exit.
|
||||||
|
if (index >= bootstrap_order.size())
|
||||||
|
{
|
||||||
|
log::error(logcat, "Bootstrapping has failed from all bootstraps; exiting.");
|
||||||
|
_router.Stop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto& rid = bootstrap_order[index];
|
||||||
|
|
||||||
|
_router.link_manager().send_control_message(
|
||||||
|
rid,
|
||||||
|
"fetch_rcs",
|
||||||
|
RCFetchMessage::serialize(rc_time::min()),
|
||||||
|
[this, src = rid, index](oxen::quic::message m) {
|
||||||
|
// TODO (Tom): DRY this out with the other invocations of fetch_rcs in here
|
||||||
|
try
|
||||||
|
{
|
||||||
|
oxenc::bt_dict_consumer btdc{m.body()};
|
||||||
|
if (not m)
|
||||||
|
{
|
||||||
|
auto reason = btdc.require<std::string_view>(messages::STATUS_KEY);
|
||||||
|
log::info(logcat, "RC fetch to {} returned error: {}", src, reason);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto btlc = btdc.require<oxenc::bt_list_consumer>("rcs"sv);
|
||||||
|
auto timestamp = rc_time{std::chrono::seconds{btdc.require<int64_t>("time"sv)}};
|
||||||
|
|
||||||
|
std::vector<RemoteRC> rcs;
|
||||||
|
|
||||||
|
while (not btlc.is_finished())
|
||||||
|
{
|
||||||
|
rcs.emplace_back(btlc.consume_dict_consumer());
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO (Tom): add flag to mark these as coming from a bootstrap, rather
|
||||||
|
// than an arbitrary relay. A relay will still check that the RCs
|
||||||
|
// match its registered relays lists; a client will just trust them.
|
||||||
|
if (process_fetched_rcs(src, std::move(rcs), timestamp))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const std::exception& e)
|
||||||
|
{
|
||||||
|
log::info(logcat, "Failed to parse RC fetch response from {}: {}", src, e.what());
|
||||||
|
}
|
||||||
|
// failure, try next bootstrap
|
||||||
|
log::warning(logcat, "Failed to bootstrap from {}, trying next.", src);
|
||||||
|
bootstrap(index + 1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
NodeDB::set_bootstrap_routers(const std::set<RemoteRC>& rcs)
|
NodeDB::set_bootstrap_routers(const std::set<RemoteRC>& rcs)
|
||||||
{
|
{
|
||||||
|
@ -113,7 +208,11 @@ namespace llarp
|
||||||
for (const auto& rc : rcs)
|
for (const auto& rc : rcs)
|
||||||
{
|
{
|
||||||
bootstraps.emplace(rc.router_id(), rc);
|
bootstraps.emplace(rc.router_id(), rc);
|
||||||
|
bootstrap_order.push_back(rc.router_id());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// so we use bootstraps in a random order
|
||||||
|
std::shuffle(bootstrap_order.begin(), bootstrap_order.end(), llarp::csrng);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
@ -806,6 +905,8 @@ namespace llarp
|
||||||
}
|
}
|
||||||
itr++;
|
itr++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
check_bootstrap_state();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<RouterID>
|
std::optional<RouterID>
|
||||||
|
@ -900,6 +1001,12 @@ namespace llarp
|
||||||
for (const auto& fpath : purge)
|
for (const auto& fpath : purge)
|
||||||
fs::remove(fpath);
|
fs::remove(fpath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// (client-only) after loading RCs, check if we're in a usable state
|
||||||
|
// relay will do this after set_router_whitelist, as it gets its RouterID list
|
||||||
|
// from oxend and needs that before it can decide.
|
||||||
|
if (not _router.is_service_node())
|
||||||
|
check_bootstrap_state();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
@ -47,6 +47,7 @@ namespace llarp
|
||||||
get_path_by_pubkey(RouterID pk) const;
|
get_path_by_pubkey(RouterID pk) const;
|
||||||
|
|
||||||
std::unordered_map<RouterID, RemoteRC> bootstraps;
|
std::unordered_map<RouterID, RemoteRC> bootstraps;
|
||||||
|
std::vector<RouterID> bootstrap_order;
|
||||||
|
|
||||||
// Router lists for snodes
|
// Router lists for snodes
|
||||||
// whitelist = active routers
|
// whitelist = active routers
|
||||||
|
@ -80,13 +81,40 @@ namespace llarp
|
||||||
std::atomic<bool> is_fetching_rids{false}, is_fetching_rcs{false};
|
std::atomic<bool> is_fetching_rids{false}, is_fetching_rcs{false};
|
||||||
std::atomic<int> fetch_failures{0};
|
std::atomic<int> fetch_failures{0};
|
||||||
|
|
||||||
|
bool _bootstrapped{true};
|
||||||
|
|
||||||
bool
|
bool
|
||||||
want_rc(const RouterID& rid) const;
|
want_rc(const RouterID& rid) const;
|
||||||
|
|
||||||
|
/// Check if we need to bootstrap, and set that in motion if so
|
||||||
|
///
|
||||||
|
/// For clients, this is called after loading the db (on startup)
|
||||||
|
/// after bootstrap success, a client will only call this again if the number
|
||||||
|
/// of non-stale RCs goes below ROUTER_ID_SOURCE_COUNT + 1, as this means we're
|
||||||
|
/// no longer confident we know enough active relays.
|
||||||
|
///
|
||||||
|
/// For relays, this is called when receiving whitelist updates from oxend.
|
||||||
|
void
|
||||||
|
check_bootstrap_state();
|
||||||
|
|
||||||
|
/// Bootstrap from scratch, i.e. from one of `bootstraps`
|
||||||
|
/// If the index given is zero, shuffle the map so we try them in a random order.
|
||||||
|
/// If the index given is nonzero, advance an iterator that many places and try.
|
||||||
|
/// If the iterator advances to ::end(), we've failed to bootstrap from all of
|
||||||
|
/// them and need to inform Router to shut down.
|
||||||
|
void
|
||||||
|
bootstrap(size_t index = 0);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void
|
void
|
||||||
set_bootstrap_routers(const std::set<RemoteRC>& rcs);
|
set_bootstrap_routers(const std::set<RemoteRC>& rcs);
|
||||||
|
|
||||||
|
bool
|
||||||
|
bootstrapped() const
|
||||||
|
{
|
||||||
|
return _bootstrapped;
|
||||||
|
}
|
||||||
|
|
||||||
const std::unordered_set<RouterID>&
|
const std::unordered_set<RouterID>&
|
||||||
whitelist() const
|
whitelist() const
|
||||||
{
|
{
|
||||||
|
|
|
@ -794,6 +794,8 @@ namespace llarp
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO (Tom): rearrange so anything we want to do regardless of network state
|
||||||
|
// always happens, and the rest only happens if we're bootstrapped.
|
||||||
void
|
void
|
||||||
Router::Tick()
|
Router::Tick()
|
||||||
{
|
{
|
||||||
|
@ -1080,9 +1082,6 @@ namespace llarp
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
log::info(logcat, "Loading NodeDB from disk...");
|
|
||||||
_node_db->load_from_disk();
|
|
||||||
|
|
||||||
_contacts = std::make_shared<Contacts>(llarp::dht::Key_t(pubkey()), *this);
|
_contacts = std::make_shared<Contacts>(llarp::dht::Key_t(pubkey()), *this);
|
||||||
|
|
||||||
for (const auto& rc : bootstrap_rc_list)
|
for (const auto& rc : bootstrap_rc_list)
|
||||||
|
@ -1092,6 +1091,9 @@ namespace llarp
|
||||||
log::info(logcat, "Added bootstrap node (rid: {})", rc.router_id());
|
log::info(logcat, "Added bootstrap node (rid: {})", rc.router_id());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log::info(logcat, "Loading NodeDB from disk...");
|
||||||
|
_node_db->load_from_disk();
|
||||||
|
|
||||||
log::info(logcat, "Router populated NodeDB with {} routers", _node_db->num_loaded());
|
log::info(logcat, "Router populated NodeDB with {} routers", _node_db->num_loaded());
|
||||||
|
|
||||||
_loop->call_every(ROUTER_TICK_INTERVAL, weak_from_this(), [this] { Tick(); });
|
_loop->call_every(ROUTER_TICK_INTERVAL, weak_from_this(), [this] { Tick(); });
|
||||||
|
|
Loading…
Reference in a new issue