zero cost exception handling my ass

- rework bootstrap loading to move all logic into BootstrapList object
- ability to parse lists and dicts of bootstraps implemented
- netid parsing refers to the correct fallback
- cross your fingers boys here we go
pull/2228/head
dr7ana 5 months ago
parent 5be09563fa
commit 30f62d2689

Binary file not shown.

@ -9,10 +9,25 @@ namespace llarp
bool bool
BootstrapList::bt_decode(std::string_view buf) BootstrapList::bt_decode(std::string_view buf)
{ {
try const auto& f = buf.front();
switch (f)
{ {
oxenc::bt_list_consumer btlc{buf}; case 'l':
return bt_decode(oxenc::bt_list_consumer{buf});
case 'd':
return bt_decode(oxenc::bt_dict_consumer{buf});
default:
log::critical(logcat, "Unable to parse bootstrap as bt list or dict!");
return false;
}
}
bool
BootstrapList::bt_decode(oxenc::bt_list_consumer btlc)
{
try
{
while (not btlc.is_finished()) while (not btlc.is_finished())
emplace(btlc.consume_dict_data()); emplace(btlc.consume_dict_data());
} }
@ -22,6 +37,24 @@ namespace llarp
return false; return false;
} }
_curr = begin();
return true;
}
bool
BootstrapList::bt_decode(oxenc::bt_dict_consumer btdc)
{
try
{
emplace(btdc);
}
catch (const std::exception& e)
{
log::warning(logcat, "Unable to decode bootstrap RemoteRC: {}", e.what());
return false;
}
_curr = begin();
return true; return true;
} }
@ -55,37 +88,82 @@ namespace llarp
} }
void void
BootstrapList::read_from_file(const fs::path& fpath) BootstrapList::populate_bootstraps(
std::vector<fs::path> paths, const fs::path& def, bool load_fallbacks)
{ {
bool isListFile = false; for (const auto& f : paths)
if (std::ifstream inf(fpath.c_str(), std::ios::binary); inf.is_open())
{ {
const char ch = inf.get(); // TESTNET: TODO: revise fucked config
isListFile = ch == 'l'; log::debug(logcat, "Loading BootstrapRC from file at path:{}", f);
if (not read_from_file(f))
throw std::invalid_argument{"User-provided BootstrapRC is invalid!"};
} }
if (isListFile) if (empty())
{ {
auto content = util::file_to_string(fpath); log::debug(
logcat,
"BootstrapRC list empty; looking for default BootstrapRC from file at path:{}",
def);
read_from_file(def);
}
if (not bt_decode(content)) for (auto itr = begin(); itr != end(); ++itr)
{
if (RouterContact::is_obsolete(*itr)) // can move this into ::read_from_file
{ {
throw std::runtime_error{fmt::format("failed to read bootstrap list file '{}'", fpath)}; log::critical(logcat, "Deleting obsolete BootstrapRC (rid:{})", itr->router_id());
itr = erase(itr);
continue;
} }
} }
else
if (empty() and load_fallbacks)
{ {
RemoteRC rc; log::critical(logcat, "BootstrapRC list empty; loading fallbacks...");
auto fallbacks = llarp::load_bootstrap_fallbacks();
if (not rc.read(fpath)) if (auto itr = fallbacks.find(RouterContact::ACTIVE_NETID); itr != fallbacks.end())
{ {
throw std::runtime_error{ log::critical(
fmt::format("failed to decode bootstrap RC, file='{}', rc={}", fpath, rc.to_string())}; logcat, "Loading {} default fallback bootstrap router(s)!", itr->second.size());
merge(itr->second);
}
if (empty())
{
log::error(
logcat,
"No Bootstrap routers were loaded. The default Bootstrap file {} does not exist, and "
"loading fallback Bootstrap RCs failed.",
def);
throw std::runtime_error("No Bootstrap nodes available.");
} }
insert(rc);
} }
log::critical(logcat, "We have {} Bootstrap router(s)!", size());
_curr = begin();
}
bool
BootstrapList::read_from_file(const fs::path& fpath)
{
bool result = false;
if (not fs::exists(fpath))
{
log::critical(logcat, "Bootstrap RC file non-existant at path:{}", fpath);
return result;
}
auto content = util::file_to_string(fpath);
result = bt_decode(content);
log::critical(
logcat, "{}uccessfully loaded BootstrapRC file at path:{}", result ? "S" : "Un", fpath);
_curr = begin(); _curr = begin();
return result;
} }
} // namespace llarp } // namespace llarp

@ -23,10 +23,19 @@ namespace llarp
bool bool
bt_decode(std::string_view buf); bt_decode(std::string_view buf);
bool
bt_decode(oxenc::bt_list_consumer btlc);
bool
bt_decode(oxenc::bt_dict_consumer btdc);
std::string_view std::string_view
bt_encode() const; bt_encode() const;
void void
populate_bootstraps(std::vector<fs::path> paths, const fs::path& def, bool load_fallbacks);
bool
read_from_file(const fs::path& fpath); read_from_file(const fs::path& fpath);
bool bool

@ -1068,26 +1068,6 @@ namespace llarp
}); });
} }
void
ConnectConfig::define_config_options(ConfigDefinition& conf, const ConfigGenParameters& params)
{
(void)params;
conf.add_undeclared_handler(
"connect", [this](std::string_view section, std::string_view name, std::string_view value) {
fs::path file{value.begin(), value.end()};
if (not fs::exists(file))
throw std::runtime_error{fmt::format(
"Specified bootstrap file {} specified in [{}]:{} does not exist",
value,
section,
name)};
routers.emplace_back(std::move(file));
return true;
});
}
void void
ApiConfig::define_config_options(ConfigDefinition& conf, const ConfigGenParameters& params) ApiConfig::define_config_options(ConfigDefinition& conf, const ConfigGenParameters& params)
{ {
@ -1204,14 +1184,12 @@ namespace llarp
}, },
[this](std::string arg) { [this](std::string arg) {
if (arg.empty()) if (arg.empty())
{
throw std::invalid_argument("cannot use empty filename as bootstrap"); throw std::invalid_argument("cannot use empty filename as bootstrap");
}
files.emplace_back(std::move(arg)); files.emplace_back(std::move(arg));
if (not fs::exists(files.back())) if (not fs::exists(files.back()))
{
throw std::invalid_argument("file does not exist: " + arg); throw std::invalid_argument("file does not exist: " + arg);
}
}); });
} }
@ -1460,7 +1438,6 @@ namespace llarp
router.define_config_options(conf, params); router.define_config_options(conf, params);
network.define_config_options(conf, params); network.define_config_options(conf, params);
paths.define_config_options(conf, params); paths.define_config_options(conf, params);
connect.define_config_options(conf, params);
dns.define_config_options(conf, params); dns.define_config_options(conf, params);
links.define_config_options(conf, params); links.define_config_options(conf, params);
api.define_config_options(conf, params); api.define_config_options(conf, params);

@ -184,14 +184,6 @@ namespace llarp
define_config_options(ConfigDefinition& conf, const ConfigGenParameters& params); define_config_options(ConfigDefinition& conf, const ConfigGenParameters& params);
}; };
struct ConnectConfig
{
std::vector<fs::path> routers;
void
define_config_options(ConfigDefinition& conf, const ConfigGenParameters& params);
};
// TODO: remove oxenmq from this header // TODO: remove oxenmq from this header
struct ApiConfig struct ApiConfig
{ {
@ -215,7 +207,6 @@ namespace llarp
struct BootstrapConfig struct BootstrapConfig
{ {
std::vector<fs::path> files; std::vector<fs::path> files;
BootstrapList routers;
bool seednode; bool seednode;
void void
@ -245,7 +236,6 @@ namespace llarp
RouterConfig router; RouterConfig router;
NetworkConfig network; NetworkConfig network;
PeerSelectionConfig paths; PeerSelectionConfig paths;
ConnectConfig connect;
DnsConfig dns; DnsConfig dns;
LinksConfig links; LinksConfig links;
ApiConfig api; ApiConfig api;

@ -423,13 +423,10 @@ namespace llarp
void handle_update_exit_response(oxen::quic::message); void handle_update_exit_response(oxen::quic::message);
void handle_close_exit_response(oxen::quic::message); void handle_close_exit_response(oxen::quic::message);
std::unordered_map<std::string, void (LinkManager::*)(oxen::quic::message)> rpc_responses = { /**
{"find_name", &LinkManager::handle_find_name_response}, Clients register 0 endpoints
{"publish_intro", &LinkManager::handle_publish_intro_response}, - nobody is making requests to clients
{"find_intro", &LinkManager::handle_find_intro_response}, */
{"update_exit", &LinkManager::handle_update_exit_response},
{"obtain_exit", &LinkManager::handle_obtain_exit_response},
{"close_exit", &LinkManager::handle_close_exit_response}};
}; };
namespace link namespace link

@ -498,21 +498,13 @@ namespace llarp
{ {
if (error) if (error)
{ {
auto& fail_count = (_using_bootstrap_fallback) ? bootstrap_attempts : fetch_failures; if (++fetch_failures >= MAX_FETCH_ATTEMPTS)
auto& THRESHOLD =
(_using_bootstrap_fallback) ? MAX_BOOTSTRAP_FETCH_ATTEMPTS : MAX_FETCH_ATTEMPTS;
// This catches three different failure cases;
// 1) bootstrap fetching and over failure threshold
// 2) bootstrap fetching and more failures to go
// 3) standard fetching and over threshold
if (++fail_count >= THRESHOLD || _using_bootstrap_fallback)
{ {
log::info( log::info(
logcat, logcat,
"RC fetching from {} reached failure threshold ({}); falling back to bootstrap...", "RC fetching from {} reached failure threshold ({}); falling back to bootstrap...",
fetch_source, fetch_source,
THRESHOLD); MAX_FETCH_ATTEMPTS);
fallback_to_bootstrap(); fallback_to_bootstrap();
return; return;
@ -536,7 +528,7 @@ namespace llarp
void void
NodeDB::fetch_rids_result(bool initial) NodeDB::fetch_rids_result(bool initial)
{ {
if (fetch_failures > MAX_FETCH_ATTEMPTS) if (fetch_failures >= MAX_FETCH_ATTEMPTS)
{ {
log::info( log::info(
logcat, logcat,
@ -661,15 +653,18 @@ namespace llarp
log::critical(logcat, "Dispatching BootstrapRC fetch request to {}", fetch_source); log::critical(logcat, "Dispatching BootstrapRC fetch request to {}", fetch_source);
auto num_needed = _router.is_service_node() ? SERVICE_NODE_BOOTSTRAP_SOURCE_COUNT
: CLIENT_BOOTSTRAP_SOURCE_COUNT;
_router.link_manager().fetch_bootstrap_rcs( _router.link_manager().fetch_bootstrap_rcs(
rc, rc,
BootstrapFetchMessage::serialize(_router.router_contact, CLIENT_BOOTSTRAP_SOURCE_COUNT), BootstrapFetchMessage::serialize(_router.router_contact, num_needed),
[this, is_snode = _router.is_service_node()](oxen::quic::message m) mutable { [this, is_snode = _router.is_service_node(), num_needed = num_needed](
oxen::quic::message m) mutable {
log::critical(logcat, "Received response to BootstrapRC fetch request..."); log::critical(logcat, "Received response to BootstrapRC fetch request...");
if (not m) if (not m)
{ {
// ++bootstrap_attempts;
log::warning( log::warning(
logcat, logcat,
"BootstrapRC fetch request to {} failed (error {}/{})", "BootstrapRC fetch request to {} failed (error {}/{})",
@ -680,7 +675,6 @@ namespace llarp
return; return;
} }
// std::set<RouterID> rids;
size_t num = 0; size_t num = 0;
try try
@ -700,7 +694,6 @@ namespace llarp
} }
catch (const std::exception& e) catch (const std::exception& e)
{ {
// ++bootstrap_attempts;
log::warning( log::warning(
logcat, logcat,
"Failed to parse BootstrapRC fetch response from {} (error {}/{}): {}", "Failed to parse BootstrapRC fetch response from {} (error {}/{}): {}",
@ -713,19 +706,12 @@ namespace llarp
return; return;
} }
// We set this to the max allowable value because if this result is bad, we won't
// try this bootstrap again. If this result is undersized, we roll right into the
// next call to fallback_to_bootstrap() and hit the base case, rotating sources
// bootstrap_attempts = MAX_BOOTSTRAP_FETCH_ATTEMPTS;
// const auto& num = rids.size();
log::critical( log::critical(
logcat, logcat,
"BootstrapRC fetch response from {} returned {}/{} needed RCs", "BootstrapRC fetch response from {} returned {}/{} needed RCs",
fetch_source, fetch_source,
num, num,
CLIENT_BOOTSTRAP_SOURCE_COUNT); num_needed);
if (not is_snode) if (not is_snode)
{ {
@ -739,25 +725,6 @@ namespace llarp
log::critical(logcat, "Service node completed processing BootstrapRC fetch!"); log::critical(logcat, "Service node completed processing BootstrapRC fetch!");
post_snode_bootstrap(); post_snode_bootstrap();
} }
// FIXME: when moving to testnet, uncomment this
// if (rids.size() == BOOTSTRAP_SOURCE_COUNT)
// {
// known_rids.merge(rids);
// fetch_initial();
// }
// else
// {
// // ++bootstrap_attempts;
// log::warning(
// logcat,
// "BootstrapRC fetch response from {} returned insufficient number of RC's (error "
// "{}/{})",
// fetch_source,
// bootstrap_attempts,
// MAX_BOOTSTRAP_FETCH_ATTEMPTS);
// fallback_to_bootstrap();
// }
}); });
} }
@ -783,7 +750,6 @@ namespace llarp
replace_subset(rid_sources, specific, known_rids, RID_SOURCE_COUNT, csrng); replace_subset(rid_sources, specific, known_rids, RID_SOURCE_COUNT, csrng);
} }
// TODO: nuke all this shit
void void
NodeDB::set_router_whitelist( NodeDB::set_router_whitelist(
const std::vector<RouterID>& whitelist, const std::vector<RouterID>& whitelist,

@ -25,6 +25,7 @@ namespace llarp
// TESTNET: the following constants have been shortened for testing purposes // TESTNET: the following constants have been shortened for testing purposes
/* RC Fetch Constants */ /* RC Fetch Constants */
// fallback to bootstrap if we have less than this many RCs
inline constexpr size_t MIN_ACTIVE_RCS{6}; inline constexpr size_t MIN_ACTIVE_RCS{6};
// max number of attempts we make in non-bootstrap fetch requests // max number of attempts we make in non-bootstrap fetch requests
inline constexpr int MAX_FETCH_ATTEMPTS{10}; inline constexpr int MAX_FETCH_ATTEMPTS{10};
@ -34,11 +35,10 @@ namespace llarp
inline constexpr double MIN_GOOD_RC_FETCH_THRESHOLD{}; inline constexpr double MIN_GOOD_RC_FETCH_THRESHOLD{};
/* RID Fetch Constants */ /* RID Fetch Constants */
inline constexpr size_t MIN_ACTIVE_RIDS{24};
// the number of rid sources that we make rid fetch requests to // the number of rid sources that we make rid fetch requests to
inline constexpr size_t RID_SOURCE_COUNT{12}; inline constexpr size_t RID_SOURCE_COUNT{8};
// upper limit on how many rid fetch requests to rid sources can fail // upper limit on how many rid fetch requests to rid sources can fail
inline constexpr size_t MAX_RID_ERRORS{4}; inline constexpr size_t MAX_RID_ERRORS{2};
// each returned rid must appear this number of times across all responses // each returned rid must appear this number of times across all responses
inline constexpr int MIN_RID_FETCH_FREQ{RID_SOURCE_COUNT - MAX_RID_ERRORS - 1}; inline constexpr int MIN_RID_FETCH_FREQ{RID_SOURCE_COUNT - MAX_RID_ERRORS - 1};
// the total number of accepted returned rids should be above this number // the total number of accepted returned rids should be above this number

@ -353,6 +353,25 @@ namespace llarp
_config = std::move(c); _config = std::move(c);
auto& conf = *_config; auto& conf = *_config;
const auto& netid = conf.router.net_id;
if (not netid.empty() and netid != llarp::LOKINET_DEFAULT_NETID)
{
_testnet = netid == llarp::LOKINET_TESTNET_NETID;
_testing_disabled = conf.lokid.disable_testing;
RouterContact::ACTIVE_NETID = netid;
if (_testing_disabled and not _testnet)
throw std::runtime_error{"Error: reachability testing can only be disabled on testnet!"};
auto err = "Lokinet network ID set to {}, NOT mainnet! {}"_format(
netid,
_testnet ? "Please ensure your local instance is configured to operate on testnet"
: "Local lokinet instance will attempt to run on the specified network");
log::critical(logcat, err);
}
// Do logging config as early as possible to get the configured log level applied // Do logging config as early as possible to get the configured log level applied
// Backwards compat: before 0.9.10 we used `type=file` with `file=|-|stdout` for print mode // Backwards compat: before 0.9.10 we used `type=file` with `file=|-|stdout` for print mode
@ -528,24 +547,6 @@ namespace llarp
// Set netid before anything else // Set netid before anything else
log::debug(logcat, "Network ID set to {}", conf.router.net_id); log::debug(logcat, "Network ID set to {}", conf.router.net_id);
const auto& netid = conf.router.net_id;
if (not netid.empty() and netid != llarp::LOKINET_DEFAULT_NETID)
{
log::critical(
logcat,
"Network ID set to {}, which is not {}! Lokinet will attempt to run on the specified "
"network",
netid,
llarp::LOKINET_DEFAULT_NETID);
_testnet = netid == llarp::LOKINET_TESTNET_NETID;
_testing_disabled = conf.lokid.disable_testing;
if (_testing_disabled and not _testnet)
throw std::runtime_error{"Error: reachability testing can only be disabled on testnet!"};
}
// Router config // Router config
client_router_connections = conf.router.client_router_connections; client_router_connections = conf.router.client_router_connections;
@ -603,101 +604,15 @@ namespace llarp
log::debug(logcat, "{} strict-connect routers configured", val.size()); log::debug(logcat, "{} strict-connect routers configured", val.size());
} }
std::vector<fs::path> configRouters = conf.connect.routers;
configRouters.insert(
configRouters.end(), conf.bootstrap.files.begin(), conf.bootstrap.files.end());
// if our conf had no bootstrap files specified, try the default location of
// <DATA_DIR>/bootstrap.signed. If this isn't present, leave a useful error message
// TODO: use constant
fs::path defaultBootstrapFile = conf.router.data_dir / "bootstrap.signed";
if (configRouters.empty() and conf.bootstrap.routers.empty())
{
if (fs::exists(defaultBootstrapFile))
configRouters.push_back(defaultBootstrapFile);
}
// BootstrapList _bootstrap_rc_list;
auto& node_bstrap = _node_db->bootstrap_list();
auto clear_bad_rcs = [&]() mutable {
log::critical(logcat, "Clearing bad RCs...");
// in case someone has an old bootstrap file and is trying to use a bootstrap
// that no longer exists
for (auto it = node_bstrap.begin(); it != node_bstrap.end();)
{
if (it->is_obsolete_bootstrap())
log::critical(logcat, "ignoring obsolete bootstrap RC: {}", it->router_id());
else if (not it->verify())
log::critical(logcat, "ignoring invalid bootstrap RC: {}", it->router_id());
else
{
++it;
continue;
}
// we are in one of the above error cases that we warned about:
it = node_bstrap.erase(it);
}
};
for (const auto& router : configRouters)
{
log::debug(logcat, "Loading bootstrap router list from {}", defaultBootstrapFile);
node_bstrap.read_from_file(router);
// _bootstrap_rc_list.read_from_file(router);
}
for (const auto& rc : conf.bootstrap.routers)
{
// _bootstrap_rc_list.emplace(rc);
node_bstrap.emplace(rc);
}
_bootstrap_seed = conf.bootstrap.seednode; _bootstrap_seed = conf.bootstrap.seednode;
if (_bootstrap_seed) std::vector<fs::path> bootstrap_paths{std::move(conf.bootstrap.files)};
log::critical(logcat, "We are a bootstrap seed node!");
if (node_bstrap.empty() and not _bootstrap_seed)
{
log::warning(logcat, "Warning: bootstrap list is empty and we are not a seed node");
auto fallbacks = llarp::load_bootstrap_fallbacks();
if (auto itr = fallbacks.find(RouterContact::ACTIVE_NETID); itr != fallbacks.end())
{
// _bootstrap_rc_list.merge(itr->second);
node_bstrap.merge(itr->second);
}
if (node_bstrap.empty())
{
// empty after trying fallback, if set
log::error(
logcat,
"No bootstrap routers were loaded. The default bootstrap file {} does not exist, and "
"loading fallback bootstrap RCs failed.",
defaultBootstrapFile);
throw std::runtime_error("No bootstrap nodes available.");
}
log::critical(logcat, "Loaded {} default fallback bootstrap routers!", node_bstrap.size());
}
clear_bad_rcs(); fs::path default_bootstrap = conf.router.data_dir / "bootstrap.signed";
log::critical(logcat, "We have {} bootstrap routers!", node_bstrap.size());
// node_db()->set_bootstrap_routers(_bootstrap_rc_list); auto& bootstrap = _node_db->bootstrap_list();
// TODO: RC refactor here bootstrap.populate_bootstraps(bootstrap_paths, default_bootstrap, not _bootstrap_seed);
// if (_is_service_node)
// init_inbounds();
// else
// init_outbounds();
// profiling // profiling
_profile_file = conf.router.data_dir / "profiles.dat"; _profile_file = conf.router.data_dir / "profiles.dat";

@ -36,7 +36,7 @@ namespace llarp
}); });
if (not btdc.is_finished()) if (not btdc.is_finished())
throw std::runtime_error{"RouterContact has some fucked up shit at the end"}; throw std::runtime_error{"RemoteRC has some fucked up shit at the end"};
btdc.finish(); btdc.finish();
} }
@ -185,7 +185,7 @@ namespace llarp
return time_to_expiry(now) <= dlt; return time_to_expiry(now) <= dlt;
} }
static constexpr std::array obsolete_bootstraps = { static const std::set<std::string_view> obsolete_bootstraps{
"7a16ac0b85290bcf69b2f3b52456d7e989ac8913b4afbb980614e249a3723218"sv, "7a16ac0b85290bcf69b2f3b52456d7e989ac8913b4afbb980614e249a3723218"sv,
"e6b3a6fe5e32c379b64212c72232d65b0b88ddf9bbaed4997409d329f8519e0b"sv, "e6b3a6fe5e32c379b64212c72232d65b0b88ddf9bbaed4997409d329f8519e0b"sv,
}; };
@ -200,4 +200,12 @@ namespace llarp
} }
return false; return false;
} }
bool
RouterContact::is_obsolete(const RouterContact& rc)
{
const auto& hex = rc._router_id.ToHex();
return obsolete_bootstraps.count(hex);
}
} // namespace llarp } // namespace llarp

@ -195,6 +195,9 @@ namespace llarp
bool bool
is_obsolete_bootstrap() const; is_obsolete_bootstrap() const;
static bool
is_obsolete(const RouterContact& rc);
void void
bt_verify(oxenc::bt_dict_consumer& data, bool reject_expired = false) const; bt_verify(oxenc::bt_dict_consumer& data, bool reject_expired = false) const;
@ -304,9 +307,6 @@ namespace llarp
/// the data in the constructor, eliminating the need for a ::verify method/ /// the data in the constructor, eliminating the need for a ::verify method/
struct RemoteRC final : public RouterContact struct RemoteRC final : public RouterContact
{ {
private:
explicit RemoteRC(oxenc::bt_dict_consumer btdc);
public: public:
RemoteRC() = default; RemoteRC() = default;
explicit RemoteRC(std::string_view data) : RemoteRC{oxenc::bt_dict_consumer{data}} explicit RemoteRC(std::string_view data) : RemoteRC{oxenc::bt_dict_consumer{data}}
@ -317,6 +317,7 @@ namespace llarp
{ {
_payload = data; _payload = data;
} }
explicit RemoteRC(oxenc::bt_dict_consumer btdc);
~RemoteRC() = default; ~RemoteRC() = default;
std::string_view std::string_view

Loading…
Cancel
Save