2018-07-11 13:20:14 +00:00
|
|
|
#ifndef LLARP_DHT_CONTEXT_HPP
|
|
|
|
#define LLARP_DHT_CONTEXT_HPP
|
|
|
|
|
|
|
|
#include <llarp/dht.h>
|
|
|
|
#include <llarp/router.h>
|
|
|
|
#include <llarp/dht/bucket.hpp>
|
|
|
|
#include <llarp/dht/key.hpp>
|
|
|
|
#include <llarp/dht/message.hpp>
|
2018-08-10 21:34:11 +00:00
|
|
|
#include <llarp/dht/messages/findintro.hpp>
|
2018-07-11 13:20:14 +00:00
|
|
|
#include <llarp/dht/node.hpp>
|
2018-07-18 20:58:16 +00:00
|
|
|
#include <llarp/service/IntroSet.hpp>
|
2018-07-11 13:20:14 +00:00
|
|
|
|
|
|
|
#include <set>
|
|
|
|
|
|
|
|
namespace llarp
|
|
|
|
{
|
|
|
|
namespace dht
|
|
|
|
{
|
2018-08-29 20:40:26 +00:00
|
|
|
struct TXOwner
|
|
|
|
{
|
|
|
|
Key_t node;
|
|
|
|
uint64_t txid = 0;
|
|
|
|
|
|
|
|
TXOwner() = default;
|
|
|
|
|
|
|
|
TXOwner(const Key_t& k, uint64_t id) : node(k), txid(id)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
operator==(const TXOwner& other) const
|
|
|
|
{
|
|
|
|
return txid == other.txid && node == other.node;
|
|
|
|
}
|
|
|
|
bool
|
|
|
|
operator<(const TXOwner& other) const
|
|
|
|
{
|
|
|
|
return txid < other.txid || node < other.node;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct Hash
|
|
|
|
{
|
|
|
|
std::size_t
|
|
|
|
operator()(TXOwner const& o) const noexcept
|
|
|
|
{
|
|
|
|
std::size_t sz2;
|
|
|
|
memcpy(&sz2, &o.node[0], sizeof(std::size_t));
|
|
|
|
return o.txid ^ (sz2 << 1);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Context;
|
|
|
|
|
|
|
|
template < typename K, typename V >
|
|
|
|
struct TX
|
|
|
|
{
|
|
|
|
TX(const TXOwner& asker, const K& k, Context* p)
|
|
|
|
: target(k), whoasked(asker)
|
|
|
|
{
|
|
|
|
parent = p;
|
|
|
|
}
|
|
|
|
|
|
|
|
K target;
|
|
|
|
Context* parent;
|
|
|
|
std::set< Key_t > peersAsked;
|
|
|
|
std::vector< V > valuesFound;
|
|
|
|
TXOwner whoasked;
|
|
|
|
|
|
|
|
void
|
|
|
|
OnFound(const Key_t& askedPeer, const V& value)
|
|
|
|
{
|
|
|
|
peersAsked.insert(askedPeer);
|
|
|
|
valuesFound.push_back(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void
|
|
|
|
Start(const TXOwner& peer) = 0;
|
|
|
|
|
|
|
|
virtual bool
|
|
|
|
GetNextPeer(Key_t& next, const std::set< Key_t >& exclude) = 0;
|
|
|
|
|
|
|
|
virtual void
|
|
|
|
DoNextRequest(const Key_t& peer) = 0;
|
|
|
|
|
|
|
|
/// return true if we want to persist this tx
|
|
|
|
bool
|
|
|
|
AskNextPeer(const Key_t& prevPeer)
|
|
|
|
{
|
|
|
|
peersAsked.insert(prevPeer);
|
|
|
|
Key_t peer;
|
|
|
|
if(!GetNextPeer(peer, peersAsked))
|
|
|
|
{
|
|
|
|
// no more peers
|
|
|
|
SendReply();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
DoNextRequest(peer);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void
|
|
|
|
SendReply() = 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef std::function< void(const std::vector< service::IntroSet >&) >
|
|
|
|
IntroSetLookupHandler;
|
|
|
|
|
2018-08-30 18:48:43 +00:00
|
|
|
typedef std::function< void(const std::vector< RouterContact >&) >
|
|
|
|
RouterLookupHandler;
|
|
|
|
|
2018-07-11 13:20:14 +00:00
|
|
|
struct Context
|
|
|
|
{
|
|
|
|
Context();
|
|
|
|
~Context();
|
|
|
|
|
2018-08-29 20:40:26 +00:00
|
|
|
/// on behalf of whoasked request introset for target from dht router with
|
|
|
|
/// key askpeer
|
2018-07-11 13:20:14 +00:00
|
|
|
void
|
2018-08-29 20:40:26 +00:00
|
|
|
LookupIntroSetRecursive(const service::Address& target,
|
|
|
|
const Key_t& whoasked, uint64_t whoaskedTX,
|
|
|
|
const Key_t& askpeer, uint64_t R,
|
|
|
|
IntroSetLookupHandler result = nullptr);
|
2018-07-11 13:20:14 +00:00
|
|
|
|
2018-07-12 18:21:44 +00:00
|
|
|
void
|
2018-08-29 20:40:26 +00:00
|
|
|
LookupIntroSetIterative(const service::Address& target,
|
|
|
|
const Key_t& whoasked, uint64_t whoaskedTX,
|
|
|
|
const Key_t& askpeer,
|
|
|
|
IntroSetLookupHandler result = nullptr);
|
2018-07-12 18:21:44 +00:00
|
|
|
|
2018-08-29 20:40:26 +00:00
|
|
|
/// on behalf of whoasked request router with public key target from dht
|
|
|
|
/// router with key askpeer
|
2018-07-11 13:20:14 +00:00
|
|
|
void
|
2018-08-29 20:40:26 +00:00
|
|
|
LookupRouterRecursive(const RouterID& target, const Key_t& whoasked,
|
|
|
|
uint64_t whoaskedTX, const Key_t& askpeer,
|
2018-08-30 18:48:43 +00:00
|
|
|
RouterLookupHandler result = nullptr);
|
|
|
|
|
|
|
|
bool
|
|
|
|
LookupRouter(const RouterID& target, RouterLookupHandler result)
|
|
|
|
{
|
|
|
|
Key_t askpeer;
|
|
|
|
if(!nodes->FindClosest(target.data(), askpeer))
|
|
|
|
return false;
|
|
|
|
LookupRouterRecursive(target, OurKey(), 0, askpeer, result);
|
|
|
|
return true;
|
|
|
|
}
|
2018-07-11 13:20:14 +00:00
|
|
|
|
2018-08-29 20:40:26 +00:00
|
|
|
/// on behalf of whoasked request introsets with tag from dht router with
|
|
|
|
/// key askpeer with Recursion depth R
|
2018-07-17 04:37:50 +00:00
|
|
|
void
|
2018-08-29 20:40:26 +00:00
|
|
|
LookupTagRecursive(const service::Tag& tag, const Key_t& whoasked,
|
|
|
|
uint64_t whoaskedTX, const Key_t& askpeer, uint64_t R);
|
2018-07-17 04:37:50 +00:00
|
|
|
|
2018-08-29 20:40:26 +00:00
|
|
|
/// issue dht lookup for tag via askpeer and send reply to local path
|
2018-07-18 20:58:16 +00:00
|
|
|
void
|
|
|
|
LookupTagForPath(const service::Tag& tag, uint64_t txid,
|
|
|
|
const llarp::PathID_t& path, const Key_t& askpeer);
|
|
|
|
|
2018-08-30 18:48:43 +00:00
|
|
|
/// issue dht lookup for router via askpeer and send reply to local path
|
|
|
|
void
|
|
|
|
LookupRouterForPath(const RouterID& target, uint64_t txid,
|
|
|
|
const llarp::PathID_t& path, const Key_t& askpeer);
|
|
|
|
|
2018-08-29 20:40:26 +00:00
|
|
|
/// issue dht lookup for introset for addr via askpeer and send reply to
|
|
|
|
/// local path
|
2018-08-02 01:41:40 +00:00
|
|
|
void
|
|
|
|
LookupIntroSetForPath(const service::Address& addr, uint64_t txid,
|
2018-08-29 20:40:26 +00:00
|
|
|
const llarp::PathID_t& path, const Key_t& askpeer);
|
2018-08-10 21:34:11 +00:00
|
|
|
|
2018-08-29 20:40:26 +00:00
|
|
|
/// send a dht message to peer, if keepalive is true then keep the session
|
|
|
|
/// with that peer alive for 10 seconds
|
2018-08-10 21:34:11 +00:00
|
|
|
void
|
2018-08-29 20:40:26 +00:00
|
|
|
DHTSendTo(const Key_t& peer, IMessage* msg, bool keepalive = true);
|
2018-08-02 01:41:40 +00:00
|
|
|
|
2018-08-29 20:40:26 +00:00
|
|
|
/// get routers closest to target excluding requester
|
2018-08-27 13:44:16 +00:00
|
|
|
bool
|
2018-09-02 18:25:42 +00:00
|
|
|
HandleExploritoryRouterLookup(
|
|
|
|
const Key_t& requester, uint64_t txid, const RouterID& target,
|
|
|
|
std::vector< std::unique_ptr< IMessage > >& reply);
|
2018-08-02 04:34:46 +00:00
|
|
|
|
2018-07-18 20:58:16 +00:00
|
|
|
std::set< service::IntroSet >
|
2018-08-29 20:40:26 +00:00
|
|
|
FindRandomIntroSetsWithTagExcluding(
|
|
|
|
const service::Tag& tag, size_t max = 2,
|
|
|
|
const std::set< service::IntroSet >& excludes = {});
|
2018-07-18 20:58:16 +00:00
|
|
|
|
2018-08-29 20:40:26 +00:00
|
|
|
/// handle rc lookup from requester for target
|
2018-07-11 13:20:14 +00:00
|
|
|
void
|
|
|
|
LookupRouterRelayed(const Key_t& requester, uint64_t txid,
|
|
|
|
const Key_t& target, bool recursive,
|
2018-09-02 18:25:42 +00:00
|
|
|
std::vector< std::unique_ptr< IMessage > >& replies);
|
2018-07-11 13:20:14 +00:00
|
|
|
|
2018-08-29 20:40:26 +00:00
|
|
|
/// relay a dht messeage from a local path to the main network
|
2018-07-11 13:20:14 +00:00
|
|
|
bool
|
|
|
|
RelayRequestForPath(const llarp::PathID_t& localPath,
|
|
|
|
const IMessage* msg);
|
2018-07-18 22:50:16 +00:00
|
|
|
|
2018-08-29 20:40:26 +00:00
|
|
|
/// send introset to peer from source with S counter and excluding peers
|
2018-07-18 22:50:16 +00:00
|
|
|
void
|
2018-08-29 20:40:26 +00:00
|
|
|
PropagateIntroSetTo(const Key_t& source, uint64_t sourceTX,
|
2018-07-19 04:58:39 +00:00
|
|
|
const service::IntroSet& introset, const Key_t& peer,
|
2018-08-01 22:10:38 +00:00
|
|
|
uint64_t S, const std::set< Key_t >& exclude);
|
2018-07-11 13:20:14 +00:00
|
|
|
|
2018-08-29 20:40:26 +00:00
|
|
|
/// initialize dht context and explore every exploreInterval milliseconds
|
2018-07-11 13:20:14 +00:00
|
|
|
void
|
2018-08-27 13:44:16 +00:00
|
|
|
Init(const Key_t& us, llarp_router* router, llarp_time_t exploreInterval);
|
2018-07-11 13:20:14 +00:00
|
|
|
|
2018-08-29 20:40:26 +00:00
|
|
|
/// get localally stored introset by service address
|
2018-07-16 21:22:25 +00:00
|
|
|
const llarp::service::IntroSet*
|
|
|
|
GetIntroSetByServiceAddress(const llarp::service::Address& addr) const;
|
|
|
|
|
2018-07-11 13:20:14 +00:00
|
|
|
static void
|
|
|
|
handle_cleaner_timer(void* user, uint64_t orig, uint64_t left);
|
|
|
|
|
2018-08-27 13:44:16 +00:00
|
|
|
static void
|
|
|
|
handle_explore_timer(void* user, uint64_t orig, uint64_t left);
|
|
|
|
|
|
|
|
/// explore dht for new routers
|
|
|
|
void
|
|
|
|
Explore();
|
|
|
|
|
2018-07-11 13:20:14 +00:00
|
|
|
llarp_router* router = nullptr;
|
|
|
|
// for router contacts
|
|
|
|
Bucket< RCNode >* nodes = nullptr;
|
|
|
|
|
|
|
|
// for introduction sets
|
|
|
|
Bucket< ISNode >* services = nullptr;
|
|
|
|
bool allowTransit = false;
|
|
|
|
|
|
|
|
const Key_t&
|
|
|
|
OurKey() const
|
|
|
|
{
|
|
|
|
return ourKey;
|
|
|
|
}
|
|
|
|
|
2018-08-29 20:40:26 +00:00
|
|
|
template < typename K, typename V, typename K_Hash,
|
|
|
|
llarp_time_t requestTimeoutMS = 5000UL >
|
|
|
|
struct TXHolder
|
|
|
|
{
|
|
|
|
// tx who are waiting for a reply for each key
|
|
|
|
std::unordered_multimap< K, TXOwner, K_Hash > waiting;
|
|
|
|
// tx timesouts by key
|
|
|
|
std::unordered_map< K, llarp_time_t, K_Hash > timeouts;
|
|
|
|
// maps remote peer with tx to handle reply from them
|
|
|
|
std::unordered_map< TXOwner, std::unique_ptr< TX< K, V > >,
|
|
|
|
TXOwner::Hash >
|
|
|
|
tx;
|
|
|
|
|
|
|
|
const TX< K, V >*
|
|
|
|
GetPendingLookupFrom(const TXOwner& owner) const
|
|
|
|
{
|
|
|
|
auto itr = tx.find(owner);
|
|
|
|
if(itr == tx.end())
|
|
|
|
return nullptr;
|
|
|
|
else
|
|
|
|
return itr->second.get();
|
|
|
|
}
|
2018-07-11 13:20:14 +00:00
|
|
|
|
2018-08-29 20:40:26 +00:00
|
|
|
bool
|
|
|
|
HasPendingLookupFrom(const TXOwner& owner) const
|
|
|
|
{
|
|
|
|
return GetPendingLookupFrom(owner) != nullptr;
|
|
|
|
}
|
2018-08-27 13:44:16 +00:00
|
|
|
|
2018-08-29 20:40:26 +00:00
|
|
|
TX< K, V >*
|
|
|
|
NewTX(const TXOwner& owner, const K& k, TX< K, V >* t)
|
|
|
|
{
|
|
|
|
tx.emplace(owner, std::unique_ptr< TX< K, V > >(t));
|
|
|
|
waiting.insert(std::make_pair(k, owner));
|
|
|
|
auto itr = timeouts.find(k);
|
|
|
|
if(itr == timeouts.end())
|
|
|
|
timeouts.insert(
|
|
|
|
std::make_pair(k, llarp_time_now_ms() + requestTimeoutMS));
|
|
|
|
return t;
|
|
|
|
}
|
2018-07-11 13:20:14 +00:00
|
|
|
|
2018-08-29 20:40:26 +00:00
|
|
|
/// mark tx as not fond
|
|
|
|
void
|
|
|
|
NotFound(const TXOwner& from)
|
|
|
|
{
|
|
|
|
bool sendReply = true;
|
|
|
|
auto txitr = tx.find(from);
|
|
|
|
if(txitr == tx.end())
|
|
|
|
return;
|
|
|
|
|
|
|
|
// ask for next peer
|
|
|
|
if(txitr->second->AskNextPeer(from.node))
|
|
|
|
sendReply = false;
|
|
|
|
Inform(from, txitr->second->target, {}, sendReply, sendReply);
|
|
|
|
}
|
2018-07-11 13:20:14 +00:00
|
|
|
|
2018-08-29 20:40:26 +00:00
|
|
|
void
|
|
|
|
Found(const TXOwner& from, const K& k, const std::vector< V >& values)
|
|
|
|
{
|
|
|
|
Inform(from, k, values, true);
|
|
|
|
}
|
2018-07-11 13:20:14 +00:00
|
|
|
|
2018-08-29 20:40:26 +00:00
|
|
|
/// inform all watches for key of values found
|
|
|
|
void
|
|
|
|
Inform(const TXOwner& from, const K& key,
|
|
|
|
const std::vector< V >& values, bool sendreply = false,
|
|
|
|
bool removeTimeouts = true)
|
2018-07-11 13:20:14 +00:00
|
|
|
{
|
2018-08-29 20:40:26 +00:00
|
|
|
auto range = waiting.equal_range(key);
|
|
|
|
auto itr = range.first;
|
|
|
|
while(itr != range.second)
|
|
|
|
{
|
|
|
|
auto txitr = tx.find(itr->second);
|
|
|
|
if(txitr != tx.end())
|
|
|
|
{
|
|
|
|
for(const auto& value : values)
|
|
|
|
txitr->second->OnFound(from.node, value);
|
|
|
|
if(sendreply)
|
|
|
|
{
|
|
|
|
txitr->second->SendReply();
|
|
|
|
tx.erase(txitr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
++itr;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(sendreply)
|
|
|
|
waiting.erase(key);
|
|
|
|
|
|
|
|
if(removeTimeouts)
|
|
|
|
timeouts.erase(key);
|
2018-07-11 13:20:14 +00:00
|
|
|
}
|
2018-08-29 20:40:26 +00:00
|
|
|
|
|
|
|
void
|
|
|
|
Expire(llarp_time_t now)
|
2018-07-11 13:20:14 +00:00
|
|
|
{
|
2018-08-29 20:40:26 +00:00
|
|
|
auto itr = timeouts.begin();
|
|
|
|
while(itr != timeouts.end())
|
|
|
|
{
|
|
|
|
if(now > itr->second && now - itr->second >= requestTimeoutMS)
|
|
|
|
{
|
|
|
|
Inform(TXOwner{}, itr->first, {}, true, false);
|
|
|
|
itr = timeouts.erase(itr);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
++itr;
|
|
|
|
}
|
2018-07-11 13:20:14 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-08-29 20:40:26 +00:00
|
|
|
TXHolder< service::Address, service::IntroSet, service::Address::Hash >
|
|
|
|
pendingIntrosetLookups;
|
|
|
|
|
|
|
|
TXHolder< service::Tag, service::IntroSet, service::Tag::Hash >
|
|
|
|
pendingTagLookups;
|
|
|
|
|
2018-08-30 18:48:43 +00:00
|
|
|
TXHolder< RouterID, RouterContact, RouterID::Hash > pendingRouterLookups;
|
2018-08-29 20:40:26 +00:00
|
|
|
|
|
|
|
TXHolder< RouterID, RouterID, RouterID::Hash > pendingExploreLookups;
|
|
|
|
|
|
|
|
uint64_t
|
|
|
|
NextID()
|
2018-07-11 13:20:14 +00:00
|
|
|
{
|
2018-08-29 20:40:26 +00:00
|
|
|
return ++ids;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
void
|
|
|
|
ExploreNetworkVia(const Key_t& peer);
|
|
|
|
|
|
|
|
void
|
|
|
|
ScheduleCleanupTimer();
|
|
|
|
|
|
|
|
void
|
|
|
|
CleanupTX();
|
|
|
|
|
|
|
|
uint64_t ids;
|
2018-07-11 13:20:14 +00:00
|
|
|
|
|
|
|
Key_t ourKey;
|
2018-07-17 04:37:50 +00:00
|
|
|
}; // namespace llarp
|
|
|
|
} // namespace dht
|
2018-07-12 13:43:37 +00:00
|
|
|
} // namespace llarp
|
2018-07-11 13:20:14 +00:00
|
|
|
|
|
|
|
struct llarp_dht_context
|
|
|
|
{
|
|
|
|
llarp::dht::Context impl;
|
|
|
|
llarp_router* parent;
|
|
|
|
llarp_dht_context(llarp_router* router);
|
|
|
|
};
|
|
|
|
|
2018-08-27 13:44:16 +00:00
|
|
|
#endif
|