mirror of https://github.com/oxen-io/lokinet
Merge branch 'staging' of ssh://github.com/loki-project/loki-network into staging
commit
cbfbdc506d
@ -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/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/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/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 <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/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
|
||||||
|
@ -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 +1,86 @@
|
|||||||
#include <messages/dht_immediate.hpp>
|
#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
|
||||||
|
@ -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()));
|
@ -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…
Reference in New Issue