1
1
Fork 0
mirror of https://github.com/oxen-io/lokinet synced 2023-12-14 06:53:00 +01:00
lokinet/llarp/nodedb.hpp
Thomas Winget 6952e8f705 Add command to fetch RCs from remote node
This command will be called periodically by clients to maintain a list
of RCs of active relay nodes.  It will require another command (future
commit) to fetch the RouterIDs from many nodes and reconcile those so we
have some notion of good-ness of the RCs we're getting; if we get what
seems to be a bad set of RCs (this concept not yet implemented), we will
choose a different relay to fetch RCs from.  These are left as TODOs for
now.
2023-11-27 14:40:09 -05:00

287 lines
7.7 KiB
C++

#pragma once
#include "crypto/crypto.hpp"
#include "dht/key.hpp"
#include "router_contact.hpp"
#include "router_id.hpp"
#include "util/common.hpp"
#include "util/fs.hpp"
#include "util/thread/threading.hpp"
#include <llarp/router/router.hpp>
#include <algorithm>
#include <atomic>
#include <optional>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <utility>
namespace llarp
{
struct Router;
class NodeDB
{
std::unordered_map<RouterID, RemoteRC> known_rcs;
Router& router;
const fs::path m_Root;
const std::function<void(std::function<void()>)> disk;
llarp_time_t m_NextFlushAt;
/// asynchronously remove the files for a set of rcs on disk given their public ident key
void
remove_many_from_disk_async(std::unordered_set<RouterID> idents) const;
/// get filename of an RC file given its public ident key
fs::path
get_path_by_pubkey(RouterID pk) const;
std::unordered_map<RouterID, RemoteRC> bootstraps;
// Router lists for snodes
// whitelist = active routers
std::unordered_set<RouterID> router_whitelist;
// greylist = fully funded, but decommissioned routers
std::unordered_set<RouterID> router_greylist;
// greenlist = registered but not fully-staked routers
std::unordered_set<RouterID> router_greenlist;
// all registered relays (snodes)
std::unordered_set<RouterID> registered_routers;
std::unordered_map<RouterID, rc_time> last_rc_update_times;
// Router list for clients
std::unordered_set<RouterID> client_known_rcs;
// only ever use to specific edges as path first-hops
std::unordered_set<RouterID> pinned_edges;
// rc update info
RouterID rc_fetch_source;
rc_time last_rc_update_relay_timestamp;
std::unordered_set<RouterID> router_id_fetch_sources;
std::unordered_map<RouterID, std::vector<RouterID>> router_id_fetch_responses;
// process responses once all are received (or failed/timed out)
size_t router_id_response_count{0};
bool
want_rc(const RouterID& rid) const;
public:
void
set_bootstrap_routers(const std::set<RemoteRC>& rcs);
const std::unordered_set<RouterID>&
whitelist() const
{
return router_whitelist;
}
const std::unordered_set<RouterID>&
greylist() const
{
return router_greylist;
}
const std::unordered_set<RouterID>&
get_registered_routers() const
{
return registered_routers;
}
const std::unordered_map<RouterID, RemoteRC>&
get_rcs() const
{
return known_rcs;
}
const std::unordered_map<RouterID, rc_time>&
get_last_rc_update_times() const
{
return last_rc_update_times;
}
// If we receive a set of RCs from our current RC source relay, we consider
// that relay to be a bad source of RCs and we randomly choose a new one.
//
// When using a new RC fetch relay, we first re-fetch the full RC list and, if
// that aligns with our RouterID list, we go back to periodic updates from that relay.
//
// This will respect edge-pinning and attempt to use a relay we already have
// a connection with.
void
rotate_rc_source();
void
ingest_rcs(RouterID source, std::vector<RemoteRC> rcs, rc_time timestamp);
void
ingest_router_ids(RouterID source, std::vector<RouterID> ids);
void
update_rcs();
void
set_router_whitelist(
const std::vector<RouterID>& whitelist,
const std::vector<RouterID>& greylist,
const std::vector<RouterID>& greenlist);
std::optional<RouterID>
get_random_whitelist_router() const;
// client:
// if pinned edges were specified, connections are allowed only to those and
// to the configured bootstrap nodes. otherwise, always allow.
//
// relay:
// outgoing connections are allowed only to other registered, funded relays
// (whitelist and greylist, respectively).
bool
is_connection_allowed(const RouterID& remote) const;
// client:
// same as is_connection_allowed
//
// server:
// we only build new paths through registered, not decommissioned relays
// (i.e. whitelist)
bool
is_path_allowed(const RouterID& remote) const
{
return router_whitelist.count(remote);
}
// if pinned edges were specified, the remote must be in that set, else any remote
// is allowed as first hop.
bool
is_first_hop_allowed(const RouterID& remote) const;
const std::unordered_set<RouterID>&
get_pinned_edges() const
{
return pinned_edges;
}
explicit NodeDB(
fs::path rootdir, std::function<void(std::function<void()>)> diskCaller, Router* r);
/// in memory nodedb
NodeDB();
/// load all known_rcs from disk syncrhonously
void
load_from_disk();
/// explicit save all RCs to disk synchronously
void
save_to_disk() const;
/// the number of RCs that are loaded from disk
size_t
num_loaded() const;
/// do periodic tasks like flush to disk and expiration
void
Tick(llarp_time_t now);
/// find the absolute closets router to a dht location
RemoteRC
find_closest_to(dht::Key_t location) const;
/// find many routers closest to dht key
std::vector<RemoteRC>
find_many_closest_to(dht::Key_t location, uint32_t numRouters) const;
/// return true if we have an rc by its ident pubkey
bool
has_rc(RouterID pk) const;
/// maybe get an rc by its ident pubkey
std::optional<RemoteRC>
get_rc(RouterID pk) const;
template <typename Filter>
std::optional<RemoteRC>
GetRandom(Filter visit) const
{
return router.loop()->call_get([visit]() -> std::optional<RemoteRC> {
std::vector<const decltype(known_rcs)::value_type*> known_rcs;
for (const auto& entry : known_rcs)
known_rcs.push_back(entry);
std::shuffle(known_rcs.begin(), known_rcs.end(), llarp::csrng);
for (const auto entry : known_rcs)
{
if (visit(entry->second))
return entry->second;
}
return std::nullopt;
});
}
/// visit all known_rcs
template <typename Visit>
void
VisitAll(Visit visit) const
{
router.loop()->call([this, visit]() {
for (const auto& item : known_rcs)
visit(item.second);
});
}
/// remove an entry via its ident pubkey
void
remove_router(RouterID pk);
/// remove an entry given a filter that inspects the rc
template <typename Filter>
void
RemoveIf(Filter visit)
{
router.loop()->call([this, visit]() {
std::unordered_set<RouterID> removed;
auto itr = known_rcs.begin();
while (itr != known_rcs.end())
{
if (visit(itr->second))
{
removed.insert(itr->second.router_id());
itr = known_rcs.erase(itr);
}
else
++itr;
}
if (not removed.empty())
remove_many_from_disk_async(std::move(removed));
});
}
/// remove rcs that are not in keep and have been inserted before cutoff
void
remove_stale_rcs(std::unordered_set<RouterID> keep, llarp_time_t cutoff);
/// put (or replace) the RC if we consider it valid (want_rc). returns true if put.
bool
put_rc(
RemoteRC rc,
rc_time now =
std::chrono::time_point_cast<std::chrono::seconds>(std::chrono::system_clock::now()));
/// if we consider it valid (want_rc),
/// put this rc into the cache if it is not there or is newer than the one there already
/// returns true if the rc was inserted
bool
put_rc_if_newer(
RemoteRC rc,
rc_time now =
std::chrono::time_point_cast<std::chrono::seconds>(std::chrono::system_clock::now()));
};
} // namespace llarp