Merge pull request #236 from michael-loki/dht_tests

Add test suites for dht bucket, kademlia and key
pull/244/head
Ryan Tharp 5 years ago committed by GitHub
commit 31f196a6eb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -501,24 +501,17 @@ set(LIB_SRC
llarp/crypto/types.cpp
llarp/dht/bucket.cpp
llarp/dht/context.cpp
llarp/dht/decode.cpp
llarp/dht/dht_immediate.cpp
llarp/dht/dht.cpp
llarp/dht/find_intro.cpp
llarp/dht/find_router.cpp
llarp/dht/got_intro.cpp
llarp/dht/got_router.cpp
llarp/dht/kademlia.cpp
llarp/dht/key.cpp
llarp/dht/message.cpp
llarp/dht/messages/all.cpp
llarp/dht/messages/findintro.cpp
llarp/dht/messages/findrouter.cpp
llarp/dht/messages/gotintro.cpp
llarp/dht/messages/gotrouter.cpp
llarp/dht/messages/pubintro.cpp
llarp/dht/node.cpp
llarp/dht/publish_intro.cpp
llarp/dht/txowner.cpp
llarp/dns.cpp
llarp/dnsc.cpp
llarp/dnsd.cpp
@ -615,8 +608,10 @@ set(TEST_SRC
# actual test cases
test/crypto/test_llarp_crypto_types.cpp
test/crypto/test_llarp_crypto.cpp
test/dht/test_llarp_dht_bucket.cpp
test/dht/test_llarp_dht_kademlia.cpp
test/dht/test_llarp_dht_key.cpp
test/dht/test_llarp_dht.cpp
test/dht/test_llarp_dht_txowner.cpp
test/dns/test_llarp_dns_dns.cpp
test/exit/test_llarp_exit_context.cpp
test/link/test_llarp_link.cpp

@ -1,7 +1,6 @@
#ifndef LLARP_DHT_BUCKET_HPP
#define LLARP_DHT_BUCKET_HPP
#include <crypto/crypto.hpp>
#include <dht/kademlia.hpp>
#include <dht/key.hpp>
@ -17,28 +16,47 @@ namespace llarp
struct Bucket
{
using BucketStorage_t = std::map< Key_t, Val_t, XorMetric >;
using Random_t = std::function< uint64_t() >;
Bucket(const Key_t& us) : nodes(XorMetric(us)){};
Bucket(const Key_t& us, Random_t r) : nodes(XorMetric(us)), random(r){};
size_t
Size() const
size() const
{
return nodes.size();
}
struct SetIntersector
{
bool
operator()(const typename BucketStorage_t::value_type& lhs,
const Key_t& rhs)
{
return lhs.first < rhs;
}
bool
operator()(const Key_t& lhs,
const typename BucketStorage_t::value_type& rhs)
{
return lhs < rhs.first;
}
};
bool
GetRandomNodeExcluding(Key_t& result,
const std::set< Key_t >& exclude) const
{
std::vector< Key_t > candidates;
for(const auto& item : nodes)
std::vector< typename BucketStorage_t::value_type > candidates;
std::set_difference(nodes.begin(), nodes.end(), exclude.begin(),
exclude.end(), std::back_inserter(candidates),
SetIntersector());
if(candidates.empty())
{
if(exclude.find(item.first) == exclude.end())
candidates.push_back(item.first);
}
if(candidates.size() == 0)
return false;
result = candidates[llarp::randint() % candidates.size()];
}
result = candidates[random() % candidates.size()].first;
return true;
}
@ -62,7 +80,7 @@ namespace llarp
bool
GetManyRandom(std::set< Key_t >& result, size_t N) const
{
if(nodes.size() < N)
if(nodes.size() < N || nodes.empty())
{
llarp::LogWarn("Not enough dht nodes, have ", nodes.size(), " want ",
N);
@ -70,10 +88,10 @@ namespace llarp
}
if(nodes.size() == N)
{
for(const auto& node : nodes)
{
result.insert(node.first);
}
std::transform(nodes.begin(), nodes.end(),
std::inserter(result, result.end()),
[](const auto& a) { return a.first; });
return true;
}
size_t expecting = N;
@ -81,31 +99,15 @@ namespace llarp
while(N)
{
auto itr = nodes.begin();
std::advance(itr, llarp::randint() % sz);
std::advance(itr, random() % sz);
if(result.insert(itr->first).second)
{
--N;
}
}
return result.size() == expecting;
}
bool
GetManyNearExcluding(const Key_t& target, std::set< Key_t >& result,
size_t N, const std::set< Key_t >& exclude) const
{
std::set< Key_t > s;
for(const auto& k : exclude)
s.insert(k);
Key_t peer;
while(N--)
{
if(!FindCloseExcluding(target, peer, s))
return false;
s.insert(peer);
result.insert(peer);
}
return true;
}
bool
FindCloseExcluding(const Key_t& target, Key_t& result,
const std::set< Key_t >& exclude) const
@ -117,7 +119,9 @@ namespace llarp
for(const auto& item : nodes)
{
if(exclude.count(item.first))
{
continue;
}
auto curDist = item.first ^ target;
if(curDist < mindist)
@ -129,12 +133,33 @@ namespace llarp
return mindist < maxdist;
}
bool
GetManyNearExcluding(const Key_t& target, std::set< Key_t >& result,
size_t N, const std::set< Key_t >& exclude) const
{
std::set< Key_t > s(exclude.begin(), exclude.end());
Key_t peer;
while(N--)
{
if(!FindCloseExcluding(target, peer, s))
{
return false;
}
s.insert(peer);
result.insert(peer);
}
return true;
}
void
PutNode(const Val_t& val)
{
auto itr = nodes.find(val.ID);
if(itr == nodes.end() || itr->second < val)
{
nodes[val.ID] = val;
}
}
void
@ -142,7 +167,9 @@ namespace llarp
{
auto itr = nodes.find(key);
if(itr != nodes.end())
{
nodes.erase(itr);
}
}
bool
@ -151,7 +178,14 @@ namespace llarp
return nodes.find(key) != nodes.end();
}
void
Clear()
{
nodes.clear();
}
BucketStorage_t nodes;
Random_t random;
};
} // namespace dht
} // namespace llarp

@ -1,6 +1,10 @@
#include <dht/context.hpp>
#include <dht/messages/findrouter.hpp>
#include <dht/messages/gotintro.hpp>
#include <dht/messages/gotrouter.hpp>
#include <dht/messages/pubintro.hpp>
#include <dht/node.hpp>
#include <messages/dht.hpp>
#include <messages/dht_immediate.hpp>
#include <router/router.hpp>
@ -268,9 +272,9 @@ namespace llarp
{
router = r;
ourKey = us;
nodes = new Bucket< RCNode >(ourKey);
services = new Bucket< ISNode >(ourKey);
llarp::LogDebug("intialize dht with key ", ourKey);
nodes = new Bucket< RCNode >(ourKey, llarp::randint);
services = new Bucket< ISNode >(ourKey, llarp::randint);
llarp::LogDebug("initialize dht with key ", ourKey);
// start exploring
r->logic->call_later(
@ -288,7 +292,7 @@ namespace llarp
void
Context::DHTSendTo(const RouterID &peer, IMessage *msg, bool keepalive)
{
llarp::DHTImmeidateMessage m;
llarp::DHTImmediateMessage m;
m.msgs.emplace_back(msg);
router->SendToOrQueue(peer, &m);
if(keepalive)
@ -452,7 +456,7 @@ namespace llarp
if(I.A != introset.A)
{
llarp::LogWarn(
"publish introset acknoledgement acked a different service");
"publish introset acknowledgement acked a different service");
return false;
}
return true;
@ -666,14 +670,14 @@ namespace llarp
if(!nodes)
return false;
size_t nodeCount = nodes->Size();
size_t nodeCount = nodes->size();
if(nodeCount == 0)
{
llarp::LogError(
"cannot handle exploritory router lookup, no dht peers");
return false;
}
llarp::LogDebug("We have ", nodes->Size(),
llarp::LogDebug("We have ", nodes->size(),
" connected nodes into the DHT");
// ourKey should never be in the connected list
// requester is likely in the connected list

@ -7,6 +7,7 @@
#include <dht/message.hpp>
#include <dht/messages/findintro.hpp>
#include <dht/node.hpp>
#include <dht/txowner.hpp>
#include <service/IntroSet.hpp>
#include <util/time.hpp>
@ -18,40 +19,6 @@ namespace llarp
namespace dht
{
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 >
@ -224,7 +191,7 @@ namespace llarp
const Key_t& target, bool recursive,
std::vector< std::unique_ptr< IMessage > >& replies);
/// relay a dht messeage from a local path to the main network
/// relay a dht message from a local path to the main network
bool
RelayRequestForPath(const llarp::PathID_t& localPath,
const IMessage* msg);

@ -1,135 +0,0 @@
#include <dht/context.hpp>
#include <dht/messages/all.hpp>
namespace llarp
{
namespace dht
{
struct MessageDecoder
{
const Key_t &From;
std::unique_ptr< IMessage > msg;
bool firstKey = true;
bool relayed = false;
MessageDecoder(const Key_t &from) : From(from)
{
}
static bool
on_key(dict_reader *r, llarp_buffer_t *key)
{
llarp_buffer_t strbuf;
MessageDecoder *dec = static_cast< MessageDecoder * >(r->user);
// check for empty dict
if(!key)
return !dec->firstKey;
// first key
if(dec->firstKey)
{
if(!llarp_buffer_eq(*key, "A"))
return false;
if(!bencode_read_string(r->buffer, &strbuf))
return false;
// bad msg size?
if(strbuf.sz != 1)
return false;
llarp::LogInfo("Handle DHT message ", *strbuf.base,
" relayed=", dec->relayed);
switch(*strbuf.base)
{
case 'F':
dec->msg.reset(new FindIntroMessage(dec->From, dec->relayed));
break;
case 'R':
if(dec->relayed)
dec->msg.reset(new RelayedFindRouterMessage(dec->From));
else
dec->msg.reset(new FindRouterMessage(dec->From));
break;
case 'S':
dec->msg.reset(new GotRouterMessage(dec->From, dec->relayed));
break;
case 'I':
dec->msg.reset(new PublishIntroMessage());
break;
case 'G':
if(dec->relayed)
{
dec->msg.reset(new RelayedGotIntroMessage());
break;
}
else
{
dec->msg.reset(new GotIntroMessage(dec->From));
break;
}
default:
llarp::LogWarn("unknown dht message type: ", (char)*strbuf.base);
// bad msg type
return false;
}
dec->firstKey = false;
return dec->msg != nullptr;
}
else
return dec->msg->DecodeKey(*key, r->buffer);
}
};
std::unique_ptr< IMessage >
DecodeMesssage(const Key_t &from, llarp_buffer_t *buf, bool relayed)
{
MessageDecoder dec(from);
dec.relayed = relayed;
dict_reader r;
r.user = &dec;
r.on_key = &MessageDecoder::on_key;
if(!bencode_read_dict(buf, &r))
return nullptr;
return std::unique_ptr< IMessage >(std::move(dec.msg));
}
struct ListDecoder
{
ListDecoder(const Key_t &from,
std::vector< std::unique_ptr< IMessage > > &list)
: From(from), l(list){};
bool relayed = false;
const Key_t &From;
std::vector< std::unique_ptr< IMessage > > &l;
static bool
on_item(list_reader *r, bool has)
{
ListDecoder *dec = static_cast< ListDecoder * >(r->user);
if(!has)
return true;
auto msg = DecodeMesssage(dec->From, r->buffer, dec->relayed);
if(msg)
{
dec->l.emplace_back(std::move(msg));
return true;
}
else
return false;
}
};
bool
DecodeMesssageList(Key_t from, llarp_buffer_t *buf,
std::vector< std::unique_ptr< IMessage > > &list,
bool relayed)
{
ListDecoder dec(from, list);
dec.relayed = relayed;
list_reader r;
r.user = &dec;
r.on_item = &ListDecoder::on_item;
return bencode_read_list(buf, &r);
}
} // namespace dht
} // namespace llarp

@ -1,86 +0,0 @@
#include <messages/dht_immediate.hpp>
#include <router/router.hpp>
namespace llarp
{
DHTImmeidateMessage::~DHTImmeidateMessage()
{
}
void
DHTImmeidateMessage::Clear()
{
msgs.clear();
}
bool
DHTImmeidateMessage::DecodeKey(llarp_buffer_t key, llarp_buffer_t *buf)
{
if(llarp_buffer_eq(key, "m"))
return llarp::dht::DecodeMesssageList(dht::Key_t(session->GetPubKey()),
buf, msgs);
if(llarp_buffer_eq(key, "v"))
{
if(!bencode_read_integer(buf, &version))
return false;
return version == LLARP_PROTO_VERSION;
}
// bad key
return false;
}
bool
DHTImmeidateMessage::BEncode(llarp_buffer_t *buf) const
{
if(!bencode_start_dict(buf))
return false;
// message type
if(!bencode_write_bytestring(buf, "a", 1))
return false;
if(!bencode_write_bytestring(buf, "m", 1))
return false;
// dht messages
if(!bencode_write_bytestring(buf, "m", 1))
return false;
// begin list
if(!bencode_start_list(buf))
return false;
for(const auto &msg : msgs)
{
if(!msg->BEncode(buf))
return false;
}
// end list
if(!bencode_end(buf))
return false;
// protocol version
if(!bencode_write_version_entry(buf))
return false;
return bencode_end(buf);
}
bool
DHTImmeidateMessage::HandleMessage(llarp::Router *router) const
{
DHTImmeidateMessage reply;
reply.session = session;
bool result = true;
for(auto &msg : msgs)
{
result &= msg->HandleMessage(router->dht, reply.msgs);
}
if(reply.msgs.size())
{
if(result)
{
result = router->SendToOrQueue(session->GetPubKey(), &reply);
}
}
return result;
}
} // namespace llarp

@ -1,217 +0,0 @@
#include <dht/context.hpp>
#include <dht/messages/findintro.hpp>
#include <dht/messages/gotintro.hpp>
#include <routing/message.hpp>
namespace llarp
{
namespace dht
{
/*
struct IntroSetLookupInformer
{
llarp::Router* router;
service::Address target;
void
SendReply(const llarp::routing::IMessage* msg)
{
}
};
*/
FindIntroMessage::~FindIntroMessage()
{
}
bool
FindIntroMessage::DecodeKey(llarp_buffer_t k, llarp_buffer_t* val)
{
bool read = false;
if(!BEncodeMaybeReadDictEntry("N", N, read, k, val))
return false;
if(!BEncodeMaybeReadDictInt("R", R, read, k, val))
return false;
if(!BEncodeMaybeReadDictEntry("S", S, read, k, val))
return false;
if(!BEncodeMaybeReadDictInt("T", T, read, k, val))
return false;
if(!BEncodeMaybeReadVersion("V", version, LLARP_PROTO_VERSION, read, k,
val))
return false;
return read;
}
bool
FindIntroMessage::BEncode(llarp_buffer_t* buf) const
{
if(!bencode_start_dict(buf))
return false;
// message id
if(!BEncodeWriteDictMsgType(buf, "A", "F"))
return false;
if(N.Empty())
{
// recursion
if(!BEncodeWriteDictInt("R", R, buf))
return false;
// service address
if(!BEncodeWriteDictEntry("S", S, buf))
return false;
}
else
{
if(!BEncodeWriteDictEntry("N", N, buf))
return false;
// recursion
if(!BEncodeWriteDictInt("R", R, buf))
return false;
}
// txid
if(!BEncodeWriteDictInt("T", T, buf))
return false;
// protocol version
if(!BEncodeWriteDictInt("V", LLARP_PROTO_VERSION, buf))
return false;
return bencode_end(buf);
}
bool
FindIntroMessage::HandleMessage(
llarp_dht_context* ctx,
std::vector< std::unique_ptr< IMessage > >& replies) const
{
if(R > 5)
{
llarp::LogError("R value too big, ", R, "> 5");
return false;
}
auto& dht = ctx->impl;
if(dht.pendingIntrosetLookups.HasPendingLookupFrom(TXOwner{From, T}))
{
llarp::LogWarn("duplicate FIM from ", From, " txid=", T);
return false;
}
Key_t peer;
std::set< Key_t > exclude = {dht.OurKey(), From};
if(N.Empty())
{
llarp::LogInfo("lookup ", S.ToString());
const auto introset = dht.GetIntroSetByServiceAddress(S);
if(introset)
{
service::IntroSet i = *introset;
replies.emplace_back(new GotIntroMessage({i}, T));
return true;
}
else
{
if(R == 0)
{
// we don't have it
Key_t target = S.ToKey();
Key_t closer;
// find closer peer
if(!dht.nodes->FindClosest(target, closer))
return false;
if(relayed)
dht.LookupIntroSetForPath(S, T, pathID, closer);
else
replies.emplace_back(new GotIntroMessage(From, closer, T));
return true;
}
else
{
Key_t us = dht.OurKey();
Key_t target = S.ToKey();
// we are recursive
if(dht.nodes->FindCloseExcluding(target, peer, exclude))
{
if(relayed)
dht.LookupIntroSetForPath(S, T, pathID, peer);
else
{
if((us ^ target) < (peer ^ target))
{
// we are not closer than our peer to the target so don't
// recurse farther
replies.emplace_back(new GotIntroMessage({}, T));
return true;
}
else if(R > 0)
dht.LookupIntroSetRecursive(S, From, T, peer, R - 1);
else
dht.LookupIntroSetIterative(S, From, T, peer);
}
return true;
}
else
{
// no more closer peers
replies.emplace_back(new GotIntroMessage({}, T));
return true;
}
}
}
}
else
{
if(relayed)
{
// tag lookup
if(dht.nodes->GetRandomNodeExcluding(peer, exclude))
{
dht.LookupTagForPath(N, T, pathID, peer);
}
else
{
// no more closer peers
replies.emplace_back(new GotIntroMessage({}, T));
return true;
}
}
else
{
if(R == 0)
{
// base case
auto introsets = dht.FindRandomIntroSetsWithTagExcluding(N, 2, {});
std::vector< service::IntroSet > reply;
for(const auto& introset : introsets)
{
reply.push_back(introset);
}
replies.emplace_back(new GotIntroMessage(reply, T));
return true;
}
else if(R < 5)
{
// tag lookup
if(dht.nodes->GetRandomNodeExcluding(peer, exclude))
{
dht.LookupTagRecursive(N, From, T, peer, R - 1);
}
else
{
replies.emplace_back(new GotIntroMessage({}, T));
}
}
else
{
// too big R value
replies.emplace_back(new GotIntroMessage({}, T));
}
}
}
return true;
}
} // namespace dht
} // namespace llarp

@ -1,172 +0,0 @@
#include <dht/messages/findrouter.hpp>
#include <dht/context.hpp>
#include <dht/messages/gotrouter.hpp>
#include <messages/dht.hpp>
#include <router/router.hpp>
namespace llarp
{
namespace dht
{
bool
RelayedFindRouterMessage::HandleMessage(
llarp_dht_context *ctx,
std::vector< std::unique_ptr< IMessage > > &replies) const
{
auto &dht = ctx->impl;
/// lookup for us, send an immeidate reply
Key_t us = dht.OurKey();
Key_t k{K};
if(K == us)
{
auto path = dht.router->paths.GetByUpstream(K, pathID);
if(path)
{
replies.emplace_back(
new GotRouterMessage(k, txid, {dht.router->rc()}, false));
return true;
}
return false;
}
Key_t peer;
// check if we know this in our nodedb first
RouterContact found;
if(dht.router->nodedb->Get(K, found))
{
replies.emplace_back(new GotRouterMessage(k, txid, {found}, false));
return true;
}
// lookup if we don't have it in our nodedb
if(dht.nodes->FindClosest(k, peer))
dht.LookupRouterForPath(K, txid, pathID, peer);
return true;
}
FindRouterMessage::~FindRouterMessage()
{
}
bool
FindRouterMessage::BEncode(llarp_buffer_t *buf) const
{
if(!bencode_start_dict(buf))
return false;
// message type
if(!bencode_write_bytestring(buf, "A", 1))
return false;
if(!bencode_write_bytestring(buf, "R", 1))
return false;
// exploritory or not?
if(!bencode_write_bytestring(buf, "E", 1))
return false;
if(!bencode_write_uint64(buf, exploritory ? 1 : 0))
return false;
// iterative or not?
if(!bencode_write_bytestring(buf, "I", 1))
return false;
if(!bencode_write_uint64(buf, iterative ? 1 : 0))
return false;
// key
if(!bencode_write_bytestring(buf, "K", 1))
return false;
if(!bencode_write_bytestring(buf, K.data(), K.size()))
return false;
// txid
if(!bencode_write_bytestring(buf, "T", 1))
return false;
if(!bencode_write_uint64(buf, txid))
return false;
// version
if(!bencode_write_bytestring(buf, "V", 1))
return false;
if(!bencode_write_uint64(buf, version))
return false;
return bencode_end(buf);
}
bool
FindRouterMessage::DecodeKey(llarp_buffer_t key, llarp_buffer_t *val)
{
llarp_buffer_t strbuf;
if(llarp_buffer_eq(key, "E"))
{
uint64_t result;
if(!bencode_read_integer(val, &result))
return false;
exploritory = result != 0;
return true;
}
if(llarp_buffer_eq(key, "I"))
{
uint64_t result;
if(!bencode_read_integer(val, &result))
return false;
iterative = result != 0;
return true;
}
if(llarp_buffer_eq(key, "K"))
{
if(!bencode_read_string(val, &strbuf))
return false;
if(strbuf.sz != K.size())
return false;
std::copy(strbuf.base, strbuf.base + K.SIZE, K.begin());
return true;
}
if(llarp_buffer_eq(key, "T"))
{
return bencode_read_integer(val, &txid);
}
if(llarp_buffer_eq(key, "V"))
{
return bencode_read_integer(val, &version);
}
return false;
}
bool
FindRouterMessage::HandleMessage(
llarp_dht_context *ctx,
std::vector< std::unique_ptr< IMessage > > &replies) const
{
auto &dht = ctx->impl;
if(!dht.allowTransit)
{
llarp::LogWarn("Got DHT lookup from ", From,
" when we are not allowing dht transit");
return false;
}
if(dht.pendingRouterLookups.HasPendingLookupFrom({From, txid}))
{
llarp::LogWarn("Duplicate FRM from ", From, " txid=", txid);
return false;
}
RouterContact found;
Key_t k{K};
if(exploritory)
return dht.HandleExploritoryRouterLookup(From, txid, K, replies);
else if(dht.router->nodedb->Get(K, found))
{
replies.emplace_back(new GotRouterMessage(k, txid, {found}, false));
return true;
}
else
dht.LookupRouterRelayed(From, txid, k, !iterative, replies);
return true;
}
} // namespace dht
} // namespace llarp

@ -1,124 +0,0 @@
#include <dht/context.hpp>
#include <dht/messages/gotintro.hpp>
#include <messages/dht.hpp>
#include <router/router.hpp>
namespace llarp
{
namespace dht
{
GotIntroMessage::GotIntroMessage(
const std::vector< llarp::service::IntroSet > &results, uint64_t tx)
: IMessage({}), I(results), T(tx)
{
}
GotIntroMessage::~GotIntroMessage()
{
}
bool
GotIntroMessage::HandleMessage(
llarp_dht_context *ctx,
__attribute__((unused))
std::vector< std::unique_ptr< IMessage > > &replies) const
{
auto &dht = ctx->impl;
auto crypto = &dht.router->crypto;
for(const auto &introset : I)
{
if(!introset.Verify(crypto, dht.Now()))
{
llarp::LogWarn(
"Invalid introset while handling direct GotIntro "
"from ",
From);
return false;
}
}
TXOwner owner(From, T);
auto tagLookup = dht.pendingTagLookups.GetPendingLookupFrom(owner);
if(tagLookup)
{
dht.pendingTagLookups.Found(owner, tagLookup->target, I);
return true;
}
auto serviceLookup =
dht.pendingIntrosetLookups.GetPendingLookupFrom(owner);
if(serviceLookup)
{
if(I.size())
{
dht.pendingIntrosetLookups.Found(owner, serviceLookup->target, I);
}
else
{
dht.pendingIntrosetLookups.NotFound(owner, K);
}
return true;
}
llarp::LogError("no pending TX for GIM from ", From, " txid=", T);
return false;
}
bool
RelayedGotIntroMessage::HandleMessage(
llarp_dht_context *ctx,
__attribute__((unused))
std::vector< std::unique_ptr< IMessage > > &replies) const
{
// TODO: implement me better?
auto pathset = ctx->impl.router->paths.GetLocalPathSet(pathID);
if(pathset)
{
return pathset->HandleGotIntroMessage(this);
}
llarp::LogWarn("No path for got intro message pathid=", pathID);
return false;
}
bool
GotIntroMessage::DecodeKey(llarp_buffer_t key, llarp_buffer_t *buf)
{
if(llarp_buffer_eq(key, "I"))
{
return BEncodeReadList(I, buf);
}
if(llarp_buffer_eq(key, "K"))
{
if(K) // duplicate key?
return false;
K.reset(new dht::Key_t());
return K->BDecode(buf);
}
bool read = false;
if(!BEncodeMaybeReadDictInt("T", T, read, key, buf))
return false;
if(!BEncodeMaybeReadDictInt("V", version, read, key, buf))
return false;
return read;
}
bool
GotIntroMessage::BEncode(llarp_buffer_t *buf) const
{
if(!bencode_start_dict(buf))
return false;
if(!BEncodeWriteDictMsgType(buf, "A", "G"))
return false;
if(!BEncodeWriteDictList("I", I, buf))
return false;
if(K)
{
if(!BEncodeWriteDictEntry("K", *K.get(), buf))
return false;
}
if(!BEncodeWriteDictInt("T", T, buf))
return false;
if(!BEncodeWriteDictInt("V", version, buf))
return false;
return bencode_end(buf);
}
} // namespace dht
} // namespace llarp

@ -1,123 +0,0 @@
#include <dht/context.hpp>
#include <dht/messages/gotrouter.hpp>
#include <router/router.hpp>
namespace llarp
{
namespace dht
{
GotRouterMessage::~GotRouterMessage()
{
}
bool
GotRouterMessage::BEncode(llarp_buffer_t *buf) const
{
if(!bencode_start_dict(buf))
return false;
// message type
if(!BEncodeWriteDictMsgType(buf, "A", "S"))
return false;
if(K)
{
if(!BEncodeWriteDictEntry("K", *K.get(), buf))
return false;
}
// near
if(N.size())
{
if(!BEncodeWriteDictList("N", N, buf))
return false;
}
if(!BEncodeWriteDictList("R", R, buf))
return false;
// txid
if(!BEncodeWriteDictInt("T", txid, buf))
return false;
// version
if(!BEncodeWriteDictInt("V", version, buf))
return false;
return bencode_end(buf);
}
bool
GotRouterMessage::DecodeKey(llarp_buffer_t key, llarp_buffer_t *val)
{
if(llarp_buffer_eq(key, "K"))
{
if(K) // duplicate key?
return false;
K.reset(new dht::Key_t());
return K->BDecode(val);
}
if(llarp_buffer_eq(key, "N"))
{
return BEncodeReadList(N, val);
}
if(llarp_buffer_eq(key, "R"))
{
return BEncodeReadList(R, val);
}
if(llarp_buffer_eq(key, "T"))
{
return bencode_read_integer(val, &txid);
}
bool read = false;
if(!BEncodeMaybeReadVersion("V", version, LLARP_PROTO_VERSION, read, key,
val))
return false;
return read;
}
bool
GotRouterMessage::HandleMessage(
llarp_dht_context *ctx,
__attribute__((unused))
std::vector< std::unique_ptr< IMessage > > &replies) const
{
auto &dht = ctx->impl;
if(relayed)
{
auto pathset = ctx->impl.router->paths.GetLocalPathSet(pathID);
return pathset && pathset->HandleGotRouterMessage(this);
}
// not relayed
TXOwner owner(From, txid);
if(dht.pendingExploreLookups.HasPendingLookupFrom(owner))
{
if(N.size() == 0)
dht.pendingExploreLookups.NotFound(owner, K);
else
{
dht.pendingExploreLookups.Found(owner, From.as_array(), N);
}
return true;
}
// not explore lookup
if(!dht.pendingRouterLookups.HasPendingLookupFrom(owner))
{
llarp::LogWarn("Unwarrented GRM from ", From, " txid=", txid);
return false;
}
// no pending lookup
llarp::LogInfo("DHT no pending lookup");
if(R.size() == 1)
dht.pendingRouterLookups.Found(owner, R[0].pubkey, {R[0]});
else
dht.pendingRouterLookups.NotFound(owner, K);
return true;
}
} // namespace dht
} // namespace llarp

@ -1 +1,140 @@
#include <dht/message.hpp>
#include <dht/context.hpp>
#include <dht/messages/findintro.hpp>
#include <dht/messages/findrouter.hpp>
#include <dht/messages/gotintro.hpp>
#include <dht/messages/gotrouter.hpp>
#include <dht/messages/pubintro.hpp>
namespace llarp
{
namespace dht
{
struct MessageDecoder
{
const Key_t &From;
std::unique_ptr< IMessage > msg;
bool firstKey = true;
bool relayed = false;
MessageDecoder(const Key_t &from) : From(from)
{
}
static bool
on_key(dict_reader *r, llarp_buffer_t *key)
{
llarp_buffer_t strbuf;
MessageDecoder *dec = static_cast< MessageDecoder * >(r->user);
// check for empty dict
if(!key)
return !dec->firstKey;
// first key
if(dec->firstKey)
{
if(!llarp_buffer_eq(*key, "A"))
return false;
if(!bencode_read_string(r->buffer, &strbuf))
return false;
// bad msg size?
if(strbuf.sz != 1)
return false;
llarp::LogInfo("Handle DHT message ", *strbuf.base,
" relayed=", dec->relayed);
switch(*strbuf.base)
{
case 'F':
dec->msg.reset(new FindIntroMessage(dec->From, dec->relayed));
break;
case 'R':
if(dec->relayed)
dec->msg.reset(new RelayedFindRouterMessage(dec->From));
else
dec->msg.reset(new FindRouterMessage(dec->From));
break;
case 'S':
dec->msg.reset(new GotRouterMessage(dec->From, dec->relayed));
break;
case 'I':
dec->msg.reset(new PublishIntroMessage());
break;
case 'G':
if(dec->relayed)
{
dec->msg.reset(new RelayedGotIntroMessage());
break;
}
else
{
dec->msg.reset(new GotIntroMessage(dec->From));
break;
}
default:
llarp::LogWarn("unknown dht message type: ", (char)*strbuf.base);
// bad msg type
return false;
}
dec->firstKey = false;
return dec->msg != nullptr;
}
else
return dec->msg->DecodeKey(*key, r->buffer);
}
};
std::unique_ptr< IMessage >
DecodeMesssage(const Key_t &from, llarp_buffer_t *buf, bool relayed)
{
MessageDecoder dec(from);
dec.relayed = relayed;
dict_reader r;
r.user = &dec;
r.on_key = &MessageDecoder::on_key;
if(!bencode_read_dict(buf, &r))
return nullptr;
return std::unique_ptr< IMessage >(std::move(dec.msg));
}
struct ListDecoder
{
ListDecoder(const Key_t &from,
std::vector< std::unique_ptr< IMessage > > &list)
: From(from), l(list){};
bool relayed = false;
const Key_t &From;
std::vector< std::unique_ptr< IMessage > > &l;
static bool
on_item(list_reader *r, bool has)
{
ListDecoder *dec = static_cast< ListDecoder * >(r->user);
if(!has)
return true;
auto msg = DecodeMesssage(dec->From, r->buffer, dec->relayed);
if(msg)
{
dec->l.emplace_back(std::move(msg));
return true;
}
else
return false;
}
};
bool
DecodeMesssageList(Key_t from, llarp_buffer_t *buf,
std::vector< std::unique_ptr< IMessage > > &list,
bool relayed)
{
ListDecoder dec(from, list);
dec.relayed = relayed;
list_reader r;
r.user = &dec;
r.on_item = &ListDecoder::on_item;
return bencode_read_list(buf, &r);
}
} // namespace dht
} // namespace llarp

@ -1 +0,0 @@
#include <dht/messages/all.hpp>

@ -1,8 +0,0 @@
#ifndef LLARP_DHT_MESSAGES_ALL_HPP
#define LLARP_DHT_MESSAGES_ALL_HPP
#include <dht/messages/findintro.hpp>
#include <dht/messages/findrouter.hpp>
#include <dht/messages/gotintro.hpp>
#include <dht/messages/gotrouter.hpp>
#include <dht/messages/pubintro.hpp>
#endif

@ -1 +1,204 @@
#include <dht/context.hpp>
#include <dht/messages/findintro.hpp>
#include <dht/messages/gotintro.hpp>
#include <routing/message.hpp>
namespace llarp
{
namespace dht
{
FindIntroMessage::~FindIntroMessage()
{
}
bool
FindIntroMessage::DecodeKey(llarp_buffer_t k, llarp_buffer_t* val)
{
bool read = false;
if(!BEncodeMaybeReadDictEntry("N", N, read, k, val))
return false;
if(!BEncodeMaybeReadDictInt("R", R, read, k, val))
return false;
if(!BEncodeMaybeReadDictEntry("S", S, read, k, val))
return false;
if(!BEncodeMaybeReadDictInt("T", T, read, k, val))
return false;
if(!BEncodeMaybeReadVersion("V", version, LLARP_PROTO_VERSION, read, k,
val))
return false;
return read;
}
bool
FindIntroMessage::BEncode(llarp_buffer_t* buf) const
{
if(!bencode_start_dict(buf))
return false;
// message id
if(!BEncodeWriteDictMsgType(buf, "A", "F"))
return false;
if(N.Empty())
{
// recursion
if(!BEncodeWriteDictInt("R", R, buf))
return false;
// service address
if(!BEncodeWriteDictEntry("S", S, buf))
return false;
}
else
{
if(!BEncodeWriteDictEntry("N", N, buf))
return false;
// recursion
if(!BEncodeWriteDictInt("R", R, buf))
return false;
}
// txid
if(!BEncodeWriteDictInt("T", T, buf))
return false;
// protocol version
if(!BEncodeWriteDictInt("V", LLARP_PROTO_VERSION, buf))
return false;
return bencode_end(buf);
}
bool
FindIntroMessage::HandleMessage(
llarp_dht_context* ctx,
std::vector< std::unique_ptr< IMessage > >& replies) const
{
if(R > 5)
{
llarp::LogError("R value too big, ", R, "> 5");
return false;
}
auto& dht = ctx->impl;
if(dht.pendingIntrosetLookups.HasPendingLookupFrom(TXOwner{From, T}))
{
llarp::LogWarn("duplicate FIM from ", From, " txid=", T);
return false;
}
Key_t peer;
std::set< Key_t > exclude = {dht.OurKey(), From};
if(N.Empty())
{
llarp::LogInfo("lookup ", S.ToString());
const auto introset = dht.GetIntroSetByServiceAddress(S);
if(introset)
{
service::IntroSet i = *introset;
replies.emplace_back(new GotIntroMessage({i}, T));
return true;
}
else
{
if(R == 0)
{
// we don't have it
Key_t target = S.ToKey();
Key_t closer;
// find closer peer
if(!dht.nodes->FindClosest(target, closer))
return false;
if(relayed)
dht.LookupIntroSetForPath(S, T, pathID, closer);
else
replies.emplace_back(new GotIntroMessage(From, closer, T));
return true;
}
else
{
Key_t us = dht.OurKey();
Key_t target = S.ToKey();
// we are recursive
if(dht.nodes->FindCloseExcluding(target, peer, exclude))
{
if(relayed)
dht.LookupIntroSetForPath(S, T, pathID, peer);
else
{
if((us ^ target) < (peer ^ target))
{
// we are not closer than our peer to the target so don't
// recurse farther
replies.emplace_back(new GotIntroMessage({}, T));
return true;
}
else if(R > 0)
dht.LookupIntroSetRecursive(S, From, T, peer, R - 1);
else
dht.LookupIntroSetIterative(S, From, T, peer);
}
return true;
}
else
{
// no more closer peers
replies.emplace_back(new GotIntroMessage({}, T));
return true;
}
}
}
}
else
{
if(relayed)
{
// tag lookup
if(dht.nodes->GetRandomNodeExcluding(peer, exclude))
{
dht.LookupTagForPath(N, T, pathID, peer);
}
else
{
// no more closer peers
replies.emplace_back(new GotIntroMessage({}, T));
return true;
}
}
else
{
if(R == 0)
{
// base case
auto introsets = dht.FindRandomIntroSetsWithTagExcluding(N, 2, {});
std::vector< service::IntroSet > reply;
for(const auto& introset : introsets)
{
reply.push_back(introset);
}
replies.emplace_back(new GotIntroMessage(reply, T));
return true;
}
else if(R < 5)
{
// tag lookup
if(dht.nodes->GetRandomNodeExcluding(peer, exclude))
{
dht.LookupTagRecursive(N, From, T, peer, R - 1);
}
else
{
replies.emplace_back(new GotIntroMessage({}, T));
}
}
else
{
// too big R value
replies.emplace_back(new GotIntroMessage({}, T));
}
}
}
return true;
}
} // namespace dht
} // namespace llarp

@ -1 +1,172 @@
#include <dht/messages/findrouter.hpp>
#include <dht/context.hpp>
#include <dht/messages/gotrouter.hpp>
#include <messages/dht.hpp>
#include <router/router.hpp>
namespace llarp
{
namespace dht
{
bool
RelayedFindRouterMessage::HandleMessage(
llarp_dht_context *ctx,
std::vector< std::unique_ptr< IMessage > > &replies) const
{
auto &dht = ctx->impl;
/// lookup for us, send an immeidate reply
Key_t us = dht.OurKey();
Key_t k{K};
if(K == us)
{
auto path = dht.router->paths.GetByUpstream(K, pathID);
if(path)
{
replies.emplace_back(
new GotRouterMessage(k, txid, {dht.router->rc()}, false));
return true;
}
return false;
}
Key_t peer;
// check if we know this in our nodedb first
RouterContact found;
if(dht.router->nodedb->Get(K, found))
{
replies.emplace_back(new GotRouterMessage(k, txid, {found}, false));
return true;
}
// lookup if we don't have it in our nodedb
if(dht.nodes->FindClosest(k, peer))
dht.LookupRouterForPath(K, txid, pathID, peer);
return true;
}
FindRouterMessage::~FindRouterMessage()
{
}
bool
FindRouterMessage::BEncode(llarp_buffer_t *buf) const
{
if(!bencode_start_dict(buf))
return false;
// message type
if(!bencode_write_bytestring(buf, "A", 1))
return false;
if(!bencode_write_bytestring(buf, "R", 1))
return false;
// exploritory or not?
if(!bencode_write_bytestring(buf, "E", 1))
return false;
if(!bencode_write_uint64(buf, exploritory ? 1 : 0))
return false;
// iterative or not?
if(!bencode_write_bytestring(buf, "I", 1))
return false;
if(!bencode_write_uint64(buf, iterative ? 1 : 0))
return false;
// key
if(!bencode_write_bytestring(buf, "K", 1))
return false;
if(!bencode_write_bytestring(buf, K.data(), K.size()))
return false;
// txid
if(!bencode_write_bytestring(buf, "T", 1))
return false;
if(!bencode_write_uint64(buf, txid))
return false;
// version
if(!bencode_write_bytestring(buf, "V", 1))
return false;
if(!bencode_write_uint64(buf, version))
return false;
return bencode_end(buf);
}
bool
FindRouterMessage::DecodeKey(llarp_buffer_t key, llarp_buffer_t *val)
{
llarp_buffer_t strbuf;
if(llarp_buffer_eq(key, "E"))
{
uint64_t result;
if(!bencode_read_integer(val, &result))
return false;
exploritory = result != 0;
return true;
}
if(llarp_buffer_eq(key, "I"))
{
uint64_t result;
if(!bencode_read_integer(val, &result))
return false;
iterative = result != 0;
return true;
}
if(llarp_buffer_eq(key, "K"))
{
if(!bencode_read_string(val, &strbuf))
return false;
if(strbuf.sz != K.size())
return false;
std::copy(strbuf.base, strbuf.base + K.SIZE, K.begin());
return true;
}
if(llarp_buffer_eq(key, "T"))
{
return bencode_read_integer(val, &txid);
}
if(llarp_buffer_eq(key, "V"))
{
return bencode_read_integer(val, &version);
}
return false;
}
bool
FindRouterMessage::HandleMessage(
llarp_dht_context *ctx,
std::vector< std::unique_ptr< IMessage > > &replies) const
{
auto &dht = ctx->impl;
if(!dht.allowTransit)
{
llarp::LogWarn("Got DHT lookup from ", From,
" when we are not allowing dht transit");
return false;
}
if(dht.pendingRouterLookups.HasPendingLookupFrom({From, txid}))
{
llarp::LogWarn("Duplicate FRM from ", From, " txid=", txid);
return false;
}
RouterContact found;
Key_t k{K};
if(exploritory)
return dht.HandleExploritoryRouterLookup(From, txid, K, replies);
else if(dht.router->nodedb->Get(K, found))
{
replies.emplace_back(new GotRouterMessage(k, txid, {found}, false));
return true;
}
else
dht.LookupRouterRelayed(From, txid, k, !iterative, replies);
return true;
}
} // namespace dht
} // namespace llarp

@ -1 +1,125 @@
#include <dht/messages/gotintro.hpp>
#include <dht/context.hpp>
#include <messages/dht.hpp>
#include <router/router.hpp>
namespace llarp
{
namespace dht
{
GotIntroMessage::GotIntroMessage(
const std::vector< llarp::service::IntroSet > &results, uint64_t tx)
: IMessage({}), I(results), T(tx)
{
}
GotIntroMessage::~GotIntroMessage()
{
}
bool
GotIntroMessage::HandleMessage(
llarp_dht_context *ctx,
__attribute__((unused))
std::vector< std::unique_ptr< IMessage > > &replies) const
{
auto &dht = ctx->impl;
auto crypto = &dht.router->crypto;
for(const auto &introset : I)
{
if(!introset.Verify(crypto, dht.Now()))
{
llarp::LogWarn(
"Invalid introset while handling direct GotIntro "
"from ",
From);
return false;
}
}
TXOwner owner(From, T);
auto tagLookup = dht.pendingTagLookups.GetPendingLookupFrom(owner);
if(tagLookup)
{
dht.pendingTagLookups.Found(owner, tagLookup->target, I);
return true;
}
auto serviceLookup =
dht.pendingIntrosetLookups.GetPendingLookupFrom(owner);
if(serviceLookup)
{
if(I.size())
{
dht.pendingIntrosetLookups.Found(owner, serviceLookup->target, I);
}
else
{
dht.pendingIntrosetLookups.NotFound(owner, K);
}
return true;
}
llarp::LogError("no pending TX for GIM from ", From, " txid=", T);
return false;
}
bool
RelayedGotIntroMessage::HandleMessage(
llarp_dht_context *ctx,
__attribute__((unused))
std::vector< std::unique_ptr< IMessage > > &replies) const
{
// TODO: implement me better?
auto pathset = ctx->impl.router->paths.GetLocalPathSet(pathID);
if(pathset)
{
return pathset->HandleGotIntroMessage(this);
}
llarp::LogWarn("No path for got intro message pathid=", pathID);
return false;
}
bool
GotIntroMessage::DecodeKey(llarp_buffer_t key, llarp_buffer_t *buf)
{
if(llarp_buffer_eq(key, "I"))
{
return BEncodeReadList(I, buf);
}
if(llarp_buffer_eq(key, "K"))
{
if(K) // duplicate key?
return false;
K.reset(new dht::Key_t());
return K->BDecode(buf);
}
bool read = false;
if(!BEncodeMaybeReadDictInt("T", T, read, key, buf))
return false;
if(!BEncodeMaybeReadDictInt("V", version, read, key, buf))
return false;
return read;
}
bool
GotIntroMessage::BEncode(llarp_buffer_t *buf) const
{
if(!bencode_start_dict(buf))
return false;
if(!BEncodeWriteDictMsgType(buf, "A", "G"))
return false;
if(!BEncodeWriteDictList("I", I, buf))
return false;
if(K)
{
if(!BEncodeWriteDictEntry("K", *K.get(), buf))
return false;
}
if(!BEncodeWriteDictInt("T", T, buf))
return false;
if(!BEncodeWriteDictInt("V", version, buf))
return false;
return bencode_end(buf);
}
} // namespace dht
} // namespace llarp

@ -1 +1,123 @@
#include <dht/context.hpp>
#include <dht/messages/gotrouter.hpp>
#include <router/router.hpp>
namespace llarp
{
namespace dht
{
GotRouterMessage::~GotRouterMessage()
{
}
bool
GotRouterMessage::BEncode(llarp_buffer_t *buf) const
{
if(!bencode_start_dict(buf))
return false;
// message type
if(!BEncodeWriteDictMsgType(buf, "A", "S"))
return false;
if(K)
{
if(!BEncodeWriteDictEntry("K", *K.get(), buf))
return false;
}
// near
if(N.size())
{
if(!BEncodeWriteDictList("N", N, buf))
return false;
}
if(!BEncodeWriteDictList("R", R, buf))
return false;
// txid
if(!BEncodeWriteDictInt("T", txid, buf))
return false;
// version
if(!BEncodeWriteDictInt("V", version, buf))
return false;
return bencode_end(buf);
}
bool
GotRouterMessage::DecodeKey(llarp_buffer_t key, llarp_buffer_t *val)
{
if(llarp_buffer_eq(key, "K"))
{
if(K) // duplicate key?
return false;
K.reset(new dht::Key_t());
return K->BDecode(val);
}
if(llarp_buffer_eq(key, "N"))
{
return BEncodeReadList(N, val);
}
if(llarp_buffer_eq(key, "R"))
{
return BEncodeReadList(R, val);
}
if(llarp_buffer_eq(key, "T"))
{
return bencode_read_integer(val, &txid);
}
bool read = false;
if(!BEncodeMaybeReadVersion("V", version, LLARP_PROTO_VERSION, read, key,
val))
return false;
return read;
}
bool
GotRouterMessage::HandleMessage(
llarp_dht_context *ctx,
__attribute__((unused))
std::vector< std::unique_ptr< IMessage > > &replies) const
{
auto &dht = ctx->impl;
if(relayed)
{
auto pathset = ctx->impl.router->paths.GetLocalPathSet(pathID);
return pathset && pathset->HandleGotRouterMessage(this);
}
// not relayed
TXOwner owner(From, txid);
if(dht.pendingExploreLookups.HasPendingLookupFrom(owner))
{
if(N.size() == 0)
dht.pendingExploreLookups.NotFound(owner, K);
else
{
dht.pendingExploreLookups.Found(owner, From.as_array(), N);
}
return true;
}
// not explore lookup
if(!dht.pendingRouterLookups.HasPendingLookupFrom(owner))
{
llarp::LogWarn("Unwarrented GRM from ", From, " txid=", txid);
return false;
}
// no pending lookup
llarp::LogInfo("DHT no pending lookup");
if(R.size() == 1)
dht.pendingRouterLookups.Found(owner, R[0].pubkey, {R[0]});
else
dht.pendingRouterLookups.NotFound(owner, K);
return true;
}
} // namespace dht
} // namespace llarp

@ -1 +1,121 @@
#include <dht/messages/pubintro.hpp>
#include <dht/context.hpp>
#include <dht/messages/gotintro.hpp>
#include <messages/dht.hpp>
#include <messages/dht_immediate.hpp>
#include <router/router.hpp>
namespace llarp
{
namespace dht
{
PublishIntroMessage::~PublishIntroMessage()
{
}
bool
PublishIntroMessage::DecodeKey(llarp_buffer_t key, llarp_buffer_t *val)
{
bool read = false;
if(llarp_buffer_eq(key, "E"))
{
return BEncodeReadList(E, val);
}
if(!BEncodeMaybeReadDictEntry("I", I, read, key, val))
return false;
if(!BEncodeMaybeReadDictInt("R", R, read, key, val))
return false;
if(llarp_buffer_eq(key, "S"))
{
read = true;
hasS = true;
if(!bencode_read_integer(val, &S))
return false;
}
if(!BEncodeMaybeReadDictInt("T", txID, read, key, val))
return false;
if(!BEncodeMaybeReadDictInt("V", version, read, key, val))
return false;
return read;
}
bool
PublishIntroMessage::HandleMessage(
llarp_dht_context *ctx,
std::vector< std::unique_ptr< IMessage > > &replies) const
{
auto now = ctx->impl.Now();
if(S > 5)
{
llarp::LogWarn("invalid S value ", S, " > 5");
return false;
}
auto &dht = ctx->impl;
if(!I.Verify(&dht.router->crypto, now))
{
llarp::LogWarn("invalid introset: ", I);
// don't propogate or store
replies.emplace_back(new GotIntroMessage({}, txID));
return true;
}
if(I.W && !I.W->IsValid(dht.router->crypto.shorthash, now))
{
llarp::LogWarn("proof of work not good enough for IntroSet");
// don't propogate or store
replies.emplace_back(new GotIntroMessage({}, txID));
return true;
}
llarp::dht::Key_t addr;
if(!I.A.CalculateAddress(addr.as_array()))
{
llarp::LogWarn(
"failed to calculate hidden service address for PubIntro message");
return false;
}
now += llarp::service::MAX_INTROSET_TIME_DELTA;
if(I.IsExpired(now))
{
// don't propogate or store
replies.emplace_back(new GotIntroMessage({}, txID));
return true;
}
dht.services->PutNode(I);
replies.emplace_back(new GotIntroMessage({I}, txID));
Key_t peer;
std::set< Key_t > exclude;
for(const auto &e : E)
exclude.insert(e);
exclude.insert(From);
exclude.insert(dht.OurKey());
if(S && dht.nodes->FindCloseExcluding(addr, peer, exclude))
{
dht.PropagateIntroSetTo(From, txID, I, peer, S - 1, exclude);
}
return true;
}
bool
PublishIntroMessage::BEncode(llarp_buffer_t *buf) const
{
if(!bencode_start_dict(buf))
return false;
if(!BEncodeWriteDictMsgType(buf, "A", "I"))
return false;
if(!BEncodeWriteDictList("E", E, buf))
return false;
if(!BEncodeWriteDictEntry("I", I, buf))
return false;
if(!BEncodeWriteDictInt("R", R, buf))
return false;
if(!BEncodeWriteDictInt("S", S, buf))
return false;
if(!BEncodeWriteDictInt("T", txID, buf))
return false;
if(!BEncodeWriteDictInt("V", LLARP_PROTO_VERSION, buf))
return false;
return bencode_end(buf);
}
} // namespace dht
} // namespace llarp

@ -11,8 +11,7 @@ namespace llarp
{
struct RCNode
{
llarp::RouterContact rc;
RouterContact rc;
Key_t ID;
RCNode()
@ -20,7 +19,7 @@ namespace llarp
ID.Zero();
}
RCNode(const llarp::RouterContact& other) : rc(other), ID(other.pubkey)
RCNode(const RouterContact& other) : rc(other), ID(other.pubkey)
{
}
@ -33,7 +32,7 @@ namespace llarp
struct ISNode
{
llarp::service::IntroSet introset;
service::IntroSet introset;
Key_t ID;
@ -42,7 +41,7 @@ namespace llarp
ID.Zero();
}
ISNode(const llarp::service::IntroSet& other)
ISNode(const service::IntroSet& other)
{
introset = other;
introset.A.CalculateAddress(ID.as_array());

@ -1,119 +0,0 @@
#include <dht/context.hpp>
#include <dht/messages/pubintro.hpp>
#include <messages/dht.hpp>
#include <messages/dht_immediate.hpp>
#include <router/router.hpp>
namespace llarp
{
namespace dht
{
PublishIntroMessage::~PublishIntroMessage()
{
}
bool
PublishIntroMessage::DecodeKey(llarp_buffer_t key, llarp_buffer_t *val)
{
bool read = false;
if(llarp_buffer_eq(key, "E"))
{
return BEncodeReadList(E, val);
}
if(!BEncodeMaybeReadDictEntry("I", I, read, key, val))
return false;
if(!BEncodeMaybeReadDictInt("R", R, read, key, val))
return false;
if(llarp_buffer_eq(key, "S"))
{
read = true;
hasS = true;
if(!bencode_read_integer(val, &S))
return false;
}
if(!BEncodeMaybeReadDictInt("T", txID, read, key, val))
return false;
if(!BEncodeMaybeReadDictInt("V", version, read, key, val))
return false;
return read;
}
bool
PublishIntroMessage::HandleMessage(
llarp_dht_context *ctx,
std::vector< std::unique_ptr< IMessage > > &replies) const
{
auto now = ctx->impl.Now();
if(S > 5)
{
llarp::LogWarn("invalid S value ", S, " > 5");
return false;
}
auto &dht = ctx->impl;
if(!I.Verify(&dht.router->crypto, now))
{
llarp::LogWarn("invalid introset: ", I);
// don't propogate or store
replies.emplace_back(new GotIntroMessage({}, txID));
return true;
}
if(I.W && !I.W->IsValid(dht.router->crypto.shorthash, now))
{
llarp::LogWarn("proof of work not good enough for IntroSet");
// don't propogate or store
replies.emplace_back(new GotIntroMessage({}, txID));
return true;
}
llarp::dht::Key_t addr;
if(!I.A.CalculateAddress(addr.as_array()))
{
llarp::LogWarn(
"failed to calculate hidden service address for PubIntro message");
return false;
}
now += llarp::service::MAX_INTROSET_TIME_DELTA;
if(I.IsExpired(now))
{
// don't propogate or store
replies.emplace_back(new GotIntroMessage({}, txID));
return true;
}
dht.services->PutNode(I);
replies.emplace_back(new GotIntroMessage({I}, txID));
Key_t peer;
std::set< Key_t > exclude;
for(const auto &e : E)
exclude.insert(e);
exclude.insert(From);
exclude.insert(dht.OurKey());
if(S && dht.nodes->FindCloseExcluding(addr, peer, exclude))
{
dht.PropagateIntroSetTo(From, txID, I, peer, S - 1, exclude);
}
return true;
}
bool
PublishIntroMessage::BEncode(llarp_buffer_t *buf) const
{
if(!bencode_start_dict(buf))
return false;
if(!BEncodeWriteDictMsgType(buf, "A", "I"))
return false;
if(!BEncodeWriteDictList("E", E, buf))
return false;
if(!BEncodeWriteDictEntry("I", I, buf))
return false;
if(!BEncodeWriteDictInt("R", R, buf))
return false;
if(!BEncodeWriteDictInt("S", S, buf))
return false;
if(!BEncodeWriteDictInt("T", txID, buf))
return false;
if(!BEncodeWriteDictInt("V", LLARP_PROTO_VERSION, buf))
return false;
return bencode_end(buf);
}
} // namespace dht
} // namespace llarp

@ -0,0 +1 @@
#include <dht/txowner.hpp>

@ -0,0 +1,54 @@
#ifndef LLARP_DHT_TXOWNER_HPP
#define LLARP_DHT_TXOWNER_HPP
#include <dht/key.hpp>
#include <cstdint>
namespace llarp
{
namespace dht
{
struct TXOwner
{
Key_t node;
uint64_t txid = 0;
TXOwner() = default;
TXOwner(const TXOwner&) = default;
TXOwner(TXOwner&&) = default;
TXOwner&
operator=(const TXOwner&) = default;
TXOwner(const Key_t& k, uint64_t id) : node(k), txid(id)
{
}
bool
operator==(const TXOwner& other) const
{
return std::tie(txid, node) == std::tie(other.txid, other.node);
}
bool
operator<(const TXOwner& other) const
{
return std::tie(txid, node) < std::tie(other.txid, other.node);
}
struct Hash
{
std::size_t
operator()(const TXOwner& o) const noexcept
{
std::size_t sz2;
memcpy(&sz2, &o.node[0], sizeof(std::size_t));
return o.txid ^ (sz2 << 1);
}
};
};
} // namespace dht
} // namespace llarp
#endif

@ -1,4 +1,5 @@
#include <exit/session.hpp>
#include <path/path.hpp>
#include <router/router.hpp>
@ -85,7 +86,7 @@ namespace llarp
if(p->SendExitRequest(&obtain, router))
llarp::LogInfo("asking ", m_ExitRouter, " for exit");
else
llarp::LogError("faild to send exit request");
llarp::LogError("failed to send exit request");
}
bool

@ -1 +1,86 @@
#include <messages/dht_immediate.hpp>
#include <router/router.hpp>
namespace llarp
{
DHTImmediateMessage::~DHTImmediateMessage()
{
}
void
DHTImmediateMessage::Clear()
{
msgs.clear();
}
bool
DHTImmediateMessage::DecodeKey(llarp_buffer_t key, llarp_buffer_t *buf)
{
if(llarp_buffer_eq(key, "m"))
return llarp::dht::DecodeMesssageList(dht::Key_t(session->GetPubKey()),
buf, msgs);
if(llarp_buffer_eq(key, "v"))
{
if(!bencode_read_integer(buf, &version))
return false;
return version == LLARP_PROTO_VERSION;
}
// bad key
return false;
}
bool
DHTImmediateMessage::BEncode(llarp_buffer_t *buf) const
{
if(!bencode_start_dict(buf))
return false;
// message type
if(!bencode_write_bytestring(buf, "a", 1))
return false;
if(!bencode_write_bytestring(buf, "m", 1))
return false;
// dht messages
if(!bencode_write_bytestring(buf, "m", 1))
return false;
// begin list
if(!bencode_start_list(buf))
return false;
for(const auto &msg : msgs)
{
if(!msg->BEncode(buf))
return false;
}
// end list
if(!bencode_end(buf))
return false;
// protocol version
if(!bencode_write_version_entry(buf))
return false;
return bencode_end(buf);
}
bool
DHTImmediateMessage::HandleMessage(llarp::Router *router) const
{
DHTImmediateMessage reply;
reply.session = session;
bool result = true;
for(auto &msg : msgs)
{
result &= msg->HandleMessage(router->dht, reply.msgs);
}
if(reply.msgs.size())
{
if(result)
{
result = router->SendToOrQueue(session->GetPubKey(), &reply);
}
}
return result;
}
} // namespace llarp

@ -8,13 +8,13 @@
namespace llarp
{
struct DHTImmeidateMessage : public ILinkMessage
struct DHTImmediateMessage : public ILinkMessage
{
DHTImmeidateMessage() : ILinkMessage()
DHTImmediateMessage() : ILinkMessage()
{
}
~DHTImmeidateMessage();
~DHTImmediateMessage();
std::vector< std::unique_ptr< llarp::dht::IMessage > > msgs;

@ -46,7 +46,7 @@ namespace llarp
LinkIntroMessage i;
RelayDownstreamMessage d;
RelayUpstreamMessage u;
DHTImmeidateMessage m;
DHTImmediateMessage m;
LR_CommitMessage c;
DiscardMessage x;
};

@ -4,6 +4,8 @@
#include <path/pathset.hpp>
#include <atomic>
struct llarp_dht_context;
namespace llarp
{
namespace path
@ -25,7 +27,7 @@ namespace llarp
}
llarp::Router* router;
struct llarp_dht_context* dht;
llarp_dht_context* dht;
llarp::SecretKey enckey;
size_t numHops;
llarp_time_t lastBuild = 0;
@ -35,7 +37,7 @@ namespace llarp
std::atomic< uint8_t > keygens;
/// construct
Builder(llarp::Router* p_router, struct llarp_dht_context* p_dht,
Builder(llarp::Router* p_router, llarp_dht_context* p_dht,
size_t numPaths, size_t numHops);
virtual ~Builder();

@ -1,7 +1,6 @@
#ifndef LLARP_PATHSET_HPP
#define LLARP_PATHSET_HPP
#include <dht/messages/all.hpp>
#include <path/path_types.hpp>
#include <router_id.hpp>
#include <routing/message.hpp>
@ -18,10 +17,13 @@ struct llarp_nodedb;
namespace llarp
{
struct RouterContact;
namespace dht
{
struct GotIntroMessage;
}
struct GotRouterMessage;
} // namespace dht
namespace path
{
@ -34,12 +36,12 @@ namespace llarp
ePathExpired
};
/// the role of this path can fuffill
/// the role of this path can fulfill
using PathRole = int;
/// capable of any role
constexpr PathRole ePathRoleAny = 0;
/// outbound hs traffic capabale
/// outbound hs traffic capable
constexpr PathRole ePathRoleOutboundHS = (1 << 0);
/// inbound hs traffic capable
constexpr PathRole ePathRoleInboundHS = (1 << 1);

@ -3,6 +3,7 @@
#include <constants/proto.hpp>
#include <crypto/crypto.hpp>
#include <dht/context.hpp>
#include <dht/node.hpp>
#include <link/iwp.hpp>
#include <link/server.hpp>
#include <link/utp.hpp>
@ -888,7 +889,7 @@ namespace llarp
{
auto buf = llarp::ConstBuffer(itr->second.front());
if(!chosen->SendTo(remote, buf))
llarp::LogWarn("failed to send outboud message to ", remote, " via ",
llarp::LogWarn("failed to send outbound message to ", remote, " via ",
chosen->Name());
itr->second.pop();

@ -1,6 +1,10 @@
#include <service/endpoint.hpp>
#include <dht/messages/findintro.hpp>
#include <dht/messages/findrouter.hpp>
#include <dht/messages/gotintro.hpp>
#include <dht/messages/gotrouter.hpp>
#include <dht/messages/pubintro.hpp>
#include <messages/dht.hpp>
#include <router/router.hpp>
#include <service/protocol.hpp>
@ -1702,7 +1706,7 @@ namespace llarp
if(markedBad)
return false;
bool should = path::Builder::ShouldBuildMore(now);
// determinte newest intro
// determine newest intro
Introduction intro;
if(!GetNewestIntro(intro))
return should;

@ -1,123 +0,0 @@
#include <dht/bucket.hpp>
#include <dht/key.hpp>
#include <dht/node.hpp>
#include <gtest/gtest.h>
using Key_t = llarp::dht::Key_t;
class KademliaDHTTest : public ::testing::Test
{
public:
KademliaDHTTest()
{
}
~KademliaDHTTest()
{
}
void
SetUp()
{
us.Fill(16);
nodes = new llarp::dht::Bucket< llarp::dht::RCNode >(us);
size_t numNodes = 10;
byte_t fill = 1;
while(numNodes)
{
llarp::dht::RCNode n;
n.ID.Fill(fill);
nodes->PutNode(n);
--numNodes;
++fill;
}
}
void
TearDown()
{
delete nodes;
}
llarp::dht::Bucket< llarp::dht::RCNode >* nodes = nullptr;
llarp::dht::Key_t us;
};
TEST_F(KademliaDHTTest, TestBucketFindClosest)
{
llarp::dht::Key_t result;
llarp::dht::Key_t target;
llarp::dht::Key_t oldResult;
target.Fill(5);
ASSERT_TRUE(nodes->FindClosest(target, result));
ASSERT_TRUE(target == result);
oldResult = result;
target.Fill(0xf5);
ASSERT_TRUE(nodes->FindClosest(target, result));
ASSERT_TRUE(oldResult == result);
};
TEST_F(KademliaDHTTest, TestBucketOperators)
{
llarp::dht::Key_t zero;
llarp::dht::Key_t one;
llarp::dht::Key_t three;
zero.Zero();
one.Fill(1);
three.Fill(3);
ASSERT_TRUE(zero < one);
ASSERT_TRUE(zero < three);
ASSERT_FALSE(zero > one);
ASSERT_FALSE(zero > three);
ASSERT_TRUE(zero != three);
ASSERT_FALSE(zero == three);
ASSERT_TRUE((zero ^ one) == one);
ASSERT_TRUE(one < three);
ASSERT_TRUE(three > one);
ASSERT_TRUE(one != three);
ASSERT_FALSE(one == three);
ASSERT_TRUE((one ^ three) == (three ^ one));
};
TEST_F(KademliaDHTTest, TestBucketRandomized_1000)
{
size_t moreNodes = 100;
while(moreNodes--)
{
llarp::dht::RCNode n;
n.ID.Randomize();
nodes->PutNode(n);
}
const size_t count = 1000;
size_t left = count;
while(left--)
{
llarp::dht::Key_t result;
llarp::dht::Key_t target;
llarp::dht::Key_t expect;
target.Randomize();
expect = target;
ASSERT_TRUE(nodes->FindClosest(target, result));
if(target == result)
{
ASSERT_FALSE((result ^ target) < (expect ^ target));
ASSERT_FALSE((result ^ target) != (expect ^ target));
ASSERT_TRUE((result ^ target) == (expect ^ target));
}
else
{
Key_t dist = result ^ target;
Key_t oldDist = expect ^ target;
ASSERT_TRUE((result ^ target) != (expect ^ target));
if((result ^ target) < (expect ^ target))
{
std::cout << "result=" << result << "expect=" << expect << std::endl;
std::cout << dist << ">=" << oldDist << "iteration=" << (count - left)
<< std::endl;
ASSERT_TRUE(false);
}
ASSERT_FALSE((result ^ target) == (expect ^ target));
}
}
};

@ -0,0 +1,370 @@
#include <dht/bucket.hpp>
#include <dht/key.hpp>
#include <dht/node.hpp>
#include <gtest/gtest.h>
using Key_t = llarp::dht::Key_t;
using Value_t = llarp::dht::RCNode;
using Bucket_t = llarp::dht::Bucket< Value_t >;
class TestDhtBucket : public ::testing::Test
{
public:
TestDhtBucket() : randInt(0)
{
us.Fill(16);
nodes = std::make_unique< Bucket_t >(us, [&]() { return randInt++; });
size_t numNodes = 10;
byte_t fill = 1;
while(numNodes)
{
Value_t n;
n.ID.Fill(fill);
nodes->PutNode(n);
--numNodes;
++fill;
}
}
uint64_t randInt;
llarp::dht::Key_t us;
std::unique_ptr< Bucket_t > nodes;
};
TEST_F(TestDhtBucket, simple_cycle)
{
// Empty the current bucket.
nodes->Clear();
// Create a simple value, and add it to the bucket.
Value_t val;
val.ID.Fill(1);
nodes->PutNode(val);
// Verify the value is in the bucket
ASSERT_TRUE(nodes->HasNode(val.ID));
ASSERT_EQ(1u, nodes->size());
// Verify after deletion, the value is no longer in the bucket
nodes->DelNode(val.ID);
ASSERT_FALSE(nodes->HasNode(val.ID));
// Verify deleting again succeeds;
nodes->DelNode(val.ID);
ASSERT_FALSE(nodes->HasNode(val.ID));
}
TEST_F(TestDhtBucket, get_random_node_excluding)
{
// Empty the current bucket.
nodes->Clear();
// We expect not to find anything
Key_t result;
std::set< Key_t > excludeSet;
ASSERT_FALSE(nodes->GetRandomNodeExcluding(result, excludeSet));
// Create a simple value.
Value_t val;
val.ID.Fill(1);
// Add the simple value to the exclude set
excludeSet.insert(val.ID);
ASSERT_FALSE(nodes->GetRandomNodeExcluding(result, excludeSet));
// Add the simple value to the bucket
nodes->PutNode(val);
ASSERT_FALSE(nodes->GetRandomNodeExcluding(result, excludeSet));
excludeSet.clear();
ASSERT_TRUE(nodes->GetRandomNodeExcluding(result, excludeSet));
ASSERT_EQ(val.ID, result);
// Add an element to the exclude set which isn't the bucket.
Key_t other;
other.Fill(0xff);
excludeSet.insert(other);
ASSERT_TRUE(nodes->GetRandomNodeExcluding(result, excludeSet));
ASSERT_EQ(val.ID, result);
// Add a node which is in both bucket and excludeSet
Value_t nextVal;
nextVal.ID.Fill(0xAA);
excludeSet.insert(nextVal.ID);
nodes->PutNode(nextVal);
ASSERT_TRUE(nodes->GetRandomNodeExcluding(result, excludeSet));
ASSERT_EQ(val.ID, result);
// Clear the excludeSet - we should still have 2 nodes in the bucket
excludeSet.clear();
randInt = 0;
ASSERT_TRUE(nodes->GetRandomNodeExcluding(result, excludeSet));
ASSERT_EQ(val.ID, result);
// Set the random value to be 1, we should get the other node.
randInt = 1;
ASSERT_TRUE(nodes->GetRandomNodeExcluding(result, excludeSet));
ASSERT_EQ(nextVal.ID, result);
// Set the random value to be 100, we should get the first node.
randInt = 100;
ASSERT_TRUE(nodes->GetRandomNodeExcluding(result, excludeSet));
ASSERT_EQ(val.ID, result);
}
TEST_F(TestDhtBucket, find_closest)
{
// Empty the current bucket.
nodes->Clear();
// We expect not to find anything
Key_t target;
target.Fill(0xF0);
Key_t result;
ASSERT_FALSE(nodes->FindClosest(target, result));
// Add a node to the bucket
Value_t first;
first.ID.Zero();
nodes->PutNode(first);
ASSERT_TRUE(nodes->FindClosest(target, result));
ASSERT_EQ(result, first.ID);
// Add another node to the bucket, closer to the target
Value_t second;
second.ID.Fill(0x10);
nodes->PutNode(second);
ASSERT_TRUE(nodes->FindClosest(target, result));
ASSERT_EQ(result, second.ID);
// Add a third node to the bucket, closer to the target
Value_t third;
third.ID.Fill(0x20);
nodes->PutNode(third);
ASSERT_TRUE(nodes->FindClosest(target, result));
ASSERT_EQ(result, third.ID);
// Add a fourth node to the bucket, greater than the target
Value_t fourth;
fourth.ID.Fill(0xF1);
nodes->PutNode(fourth);
ASSERT_TRUE(nodes->FindClosest(target, result));
ASSERT_EQ(result, fourth.ID);
// Add a fifth node to the bucket, equal to the target
Value_t fifth;
fifth.ID.Fill(0xF0);
nodes->PutNode(fifth);
ASSERT_TRUE(nodes->FindClosest(target, result));
ASSERT_EQ(result, fifth.ID);
}
TEST_F(TestDhtBucket, get_many_random)
{
// Empty the current bucket.
nodes->Clear();
// Verify behaviour with empty node set
std::set< Key_t > result;
ASSERT_FALSE(nodes->GetManyRandom(result, 0));
ASSERT_FALSE(nodes->GetManyRandom(result, 1));
// Add 5 nodes to the bucket
std::set< Value_t > curValues;
std::set< Key_t > curKeys;
for(byte_t i = 0x00; i < 0x05; ++i)
{
Value_t v;
v.ID.Fill(i);
ASSERT_TRUE(curKeys.insert(v.ID).second);
nodes->PutNode(v);
}
// Fetching more than the current size fails
ASSERT_EQ(5u, nodes->size());
ASSERT_FALSE(nodes->GetManyRandom(result, nodes->size() + 1));
// Fetching the current size succeeds
ASSERT_TRUE(nodes->GetManyRandom(result, nodes->size()));
ASSERT_EQ(curKeys, result);
// Fetching a subset succeeds.
// Note we hack this by "fixing" the random number generator
result.clear();
ASSERT_TRUE(nodes->GetManyRandom(result, 1u));
ASSERT_EQ(1u, result.size());
ASSERT_EQ(*curKeys.begin(), *result.begin());
randInt = 0;
result.clear();
ASSERT_TRUE(nodes->GetManyRandom(result, nodes->size() - 1));
ASSERT_EQ(nodes->size() - 1, result.size());
ASSERT_EQ(std::set< Key_t >(++curKeys.rbegin(), curKeys.rend()), result);
}
TEST_F(TestDhtBucket, find_close_excluding)
{
// Empty the current bucket.
nodes->Clear();
Key_t target;
target.Zero();
std::set< Key_t > exclude;
Key_t result;
// Empty node + exclude set fails
ASSERT_FALSE(nodes->FindCloseExcluding(target, result, exclude));
Value_t first;
first.ID.Fill(0xF0);
exclude.insert(first.ID);
// Empty nodes fails
ASSERT_FALSE(nodes->FindCloseExcluding(target, result, exclude));
// Nodes and exclude set match
nodes->PutNode(first);
ASSERT_FALSE(nodes->FindCloseExcluding(target, result, exclude));
// Exclude set empty
exclude.clear();
ASSERT_TRUE(nodes->FindCloseExcluding(target, result, exclude));
result = first.ID;
Value_t second;
second.ID.Fill(0x01);
nodes->PutNode(second);
ASSERT_TRUE(nodes->FindCloseExcluding(target, result, exclude));
result = second.ID;
exclude.insert(second.ID);
ASSERT_TRUE(nodes->FindCloseExcluding(target, result, exclude));
result = first.ID;
}
TEST_F(TestDhtBucket, find_many_near_excluding)
{
// Empty the current bucket.
nodes->Clear();
Key_t target;
target.Zero();
std::set< Key_t > exclude;
std::set< Key_t > result;
// Empty node + exclude set, with size 0 succeeds
ASSERT_TRUE(nodes->GetManyNearExcluding(target, result, 0, exclude));
ASSERT_EQ(0u, result.size());
// Empty node + exclude set fails
ASSERT_FALSE(nodes->GetManyNearExcluding(target, result, 1, exclude));
Value_t first;
first.ID.Fill(0xF0);
exclude.insert(first.ID);
// Empty nodes fails
ASSERT_FALSE(nodes->GetManyNearExcluding(target, result, 1, exclude));
// Nodes and exclude set match
nodes->PutNode(first);
ASSERT_FALSE(nodes->GetManyNearExcluding(target, result, 1, exclude));
// Single node succeeds
exclude.clear();
ASSERT_TRUE(nodes->GetManyNearExcluding(target, result, 1, exclude));
ASSERT_EQ(result, std::set< Key_t >({first.ID}));
// Trying to grab 2 nodes from a 1 node set fails
result.clear();
ASSERT_FALSE(nodes->GetManyNearExcluding(target, result, 2, exclude));
// two nodes finds closest
Value_t second;
second.ID.Fill(0x01);
nodes->PutNode(second);
result.clear();
ASSERT_TRUE(nodes->GetManyNearExcluding(target, result, 1, exclude));
ASSERT_EQ(result, std::set< Key_t >({second.ID}));
// 3 nodes finds 2 closest
Value_t third;
third.ID.Fill(0x02);
nodes->PutNode(third);
result.clear();
ASSERT_TRUE(nodes->GetManyNearExcluding(target, result, 2, exclude));
ASSERT_EQ(result, std::set< Key_t >({second.ID, third.ID}));
// 4 nodes, one in exclude set finds 2 closest
Value_t fourth;
fourth.ID.Fill(0x03);
nodes->PutNode(fourth);
exclude.insert(third.ID);
result.clear();
ASSERT_TRUE(nodes->GetManyNearExcluding(target, result, 2, exclude));
ASSERT_EQ(result, std::set< Key_t >({second.ID, fourth.ID}));
}
TEST_F(TestDhtBucket, TestBucketFindClosest)
{
llarp::dht::Key_t result;
llarp::dht::Key_t target;
target.Fill(5);
ASSERT_TRUE(nodes->FindClosest(target, result));
ASSERT_EQ(target, result);
const llarp::dht::Key_t oldResult = result;
target.Fill(0xf5);
ASSERT_TRUE(nodes->FindClosest(target, result));
ASSERT_EQ(oldResult, result);
};
TEST_F(TestDhtBucket, TestBucketRandomized_1000)
{
size_t moreNodes = 100;
while(moreNodes--)
{
llarp::dht::RCNode n;
n.ID.Fill(randInt);
randInt++;
nodes->PutNode(n);
}
const size_t count = 1000;
size_t left = count;
while(left--)
{
llarp::dht::Key_t result;
llarp::dht::Key_t target;
target.Randomize();
const llarp::dht::Key_t expect = target;
ASSERT_TRUE(nodes->FindClosest(target, result));
if(target == result)
{
ASSERT_GE(result ^ target, expect ^ target);
ASSERT_EQ(result ^ target, expect ^ target);
ASSERT_EQ(result ^ target, expect ^ target);
}
else
{
Key_t dist = result ^ target;
Key_t oldDist = expect ^ target;
ASSERT_NE(result ^ target, expect ^ target);
ASSERT_GE(result ^ target, expect ^ target)
<< "result=" << result << "expect=" << expect << std::endl
<< dist << ">=" << oldDist << "iteration=" << (count - left);
ASSERT_NE(result ^ target, expect ^ target);
}
}
};

@ -0,0 +1,88 @@
#include <dht/kademlia.hpp>
#include <gtest/gtest.h>
using llarp::dht::Key_t;
using Array = std::array< byte_t, Key_t::SIZE >;
struct XorMetricData
{
Array us;
Array left;
Array right;
bool result;
XorMetricData(const Array& u, const Array& l, const Array& r, bool res)
: us(u), left(l), right(r), result(res)
{
}
};
std::ostream&
operator<<(std::ostream& stream, const XorMetricData& x)
{
stream << int(x.us[0]) << " " << int(x.left[0]) << " " << int(x.right[0])
<< " " << std::boolalpha << x.result;
return stream;
}
struct XorMetric : public ::testing::TestWithParam< XorMetricData >
{
};
TEST_P(XorMetric, test)
{
auto d = GetParam();
ASSERT_EQ(llarp::dht::XorMetric{Key_t{d.us}}(Key_t{d.left}, Key_t{d.right}),
d.result);
}
std::vector< XorMetricData >
makeData()
{
std::vector< XorMetricData > result;
Array zero;
zero.fill(0);
Array one;
one.fill(1);
Array two;
two.fill(2);
Array three;
three.fill(3);
result.emplace_back(zero, zero, zero, false);
result.emplace_back(zero, zero, one, true);
result.emplace_back(zero, zero, two, true);
result.emplace_back(zero, one, zero, false);
result.emplace_back(zero, one, one, false);
result.emplace_back(zero, one, two, true);
result.emplace_back(zero, two, zero, false);
result.emplace_back(zero, two, one, false);
result.emplace_back(zero, two, two, false);
result.emplace_back(one, zero, zero, false);
result.emplace_back(one, zero, one, false);
result.emplace_back(one, zero, two, true);
result.emplace_back(one, one, zero, true);
result.emplace_back(one, one, one, false);
result.emplace_back(one, one, two, true);
result.emplace_back(one, two, zero, false);
result.emplace_back(one, two, one, false);
result.emplace_back(one, two, two, false);
result.emplace_back(two, zero, zero, false);
result.emplace_back(two, zero, one, true);
result.emplace_back(two, zero, two, false);
result.emplace_back(two, one, zero, false);
result.emplace_back(two, one, one, false);
result.emplace_back(two, one, two, false);
result.emplace_back(two, two, zero, true);
result.emplace_back(two, two, one, true);
result.emplace_back(two, two, two, false);
return result;
}
INSTANTIATE_TEST_CASE_P(TestDhtXorMetric, XorMetric,
::testing::ValuesIn(makeData()));

@ -89,3 +89,26 @@ TEST(TestDhtKey, XOR)
ASSERT_EQ(dht::Key_t(xorResult),
dht::Key_t(seqArray) ^ dht::Key_t(fullArray));
}
TEST(TestDhtKey, TestBucketOperators)
{
dht::Key_t zero;
dht::Key_t one;
dht::Key_t three;
zero.Zero();
one.Fill(1);
three.Fill(3);
ASSERT_LT(zero, one);
ASSERT_LT(zero, three);
ASSERT_FALSE(zero > one);
ASSERT_FALSE(zero > three);
ASSERT_NE(zero, three);
ASSERT_FALSE(zero == three);
ASSERT_EQ(zero ^ one, one);
ASSERT_LT(one, three);
ASSERT_GT(three, one);
ASSERT_NE(one, three);
ASSERT_FALSE(one == three);
ASSERT_EQ(one ^ three, three ^ one);
};

@ -0,0 +1,123 @@
#include <dht/txowner.hpp>
#include <gtest/gtest.h>
namespace
{
using llarp::dht::Key_t;
using llarp::dht::TXOwner;
struct TxOwnerData
{
Key_t node;
uint64_t id;
size_t expectedHash;
TxOwnerData(const Key_t& k, uint64_t i, size_t h)
: node(k), id(i), expectedHash(h)
{
}
};
struct TxOwner : public ::testing::TestWithParam< TxOwnerData >
{
};
TEST_F(TxOwner, default_construct)
{
TXOwner dc;
ASSERT_TRUE(dc.node.IsZero());
ASSERT_EQ(0u, dc.txid);
ASSERT_EQ(0u, TXOwner::Hash()(dc));
}
TEST_P(TxOwner, hash)
{
// test single interactions (constructor and hash)
auto d = GetParam();
TXOwner constructor(d.node, d.id);
ASSERT_EQ(d.expectedHash, TXOwner::Hash()(constructor));
}
std::vector< TxOwnerData >
makeData()
{
std::vector< TxOwnerData > result;
Key_t zero;
zero.Zero();
Key_t one;
one.Fill(0x01);
Key_t two;
two.Fill(0x02);
uint64_t max = std::numeric_limits< uint64_t >::max();
result.emplace_back(zero, 0, 0ull);
result.emplace_back(zero, 1, 1ull);
result.emplace_back(one, 0, 144680345676153346ull);
result.emplace_back(one, 1, 144680345676153347ull);
result.emplace_back(two, 0, 289360691352306692ull);
result.emplace_back(two, 2, 289360691352306694ull);
result.emplace_back(zero, max, 18446744073709551615ull);
result.emplace_back(one, max, 18302063728033398269ull);
result.emplace_back(two, max, 18157383382357244923ull);
return result;
}
struct TxOwnerCmpData
{
TXOwner lhs;
TXOwner rhs;
bool equal;
bool less;
TxOwnerCmpData(const TXOwner& l, const TXOwner& r, bool e, bool ls)
: lhs(l), rhs(r), equal(e), less(ls)
{
}
};
struct TxOwnerOps : public ::testing::TestWithParam< TxOwnerCmpData >
{
};
TEST_P(TxOwnerOps, operators)
{
// test single interactions (constructor and hash)
auto d = GetParam();
ASSERT_EQ(d.lhs == d.rhs, d.equal);
ASSERT_EQ(d.lhs < d.rhs, d.less);
}
std::vector< TxOwnerCmpData >
makeCmpData()
{
std::vector< TxOwnerCmpData > result;
Key_t zero;
zero.Fill(0x00);
Key_t one;
one.Fill(0x01);
Key_t two;
two.Fill(0x02);
result.emplace_back(TXOwner(zero, 0), TXOwner(zero, 0), true, false);
result.emplace_back(TXOwner(one, 0), TXOwner(one, 0), true, false);
result.emplace_back(TXOwner(two, 0), TXOwner(two, 0), true, false);
result.emplace_back(TXOwner(zero, 0), TXOwner(one, 0), false, true);
result.emplace_back(TXOwner(two, 0), TXOwner(one, 0), false, false);
return result;
}
} // namespace
INSTANTIATE_TEST_CASE_P(TestDhtTxOwner, TxOwner,
::testing::ValuesIn(makeData()));
INSTANTIATE_TEST_CASE_P(TestDhtTxOwner, TxOwnerOps,
::testing::ValuesIn(makeCmpData()));
Loading…
Cancel
Save