From b8590075e6d341e6a9f5882e3eed2201ee08405c Mon Sep 17 00:00:00 2001 From: orignal Date: Thu, 16 Feb 2023 22:14:02 -0500 Subject: [PATCH] initial commit for Kademlia DHT --- libi2pd/KadDHT.cpp | 238 +++++++++++++++++++++++++++++++++++++++++++++ libi2pd/KadDHT.h | 64 ++++++++++++ 2 files changed, 302 insertions(+) create mode 100644 libi2pd/KadDHT.cpp create mode 100644 libi2pd/KadDHT.h diff --git a/libi2pd/KadDHT.cpp b/libi2pd/KadDHT.cpp new file mode 100644 index 00000000..29664f79 --- /dev/null +++ b/libi2pd/KadDHT.cpp @@ -0,0 +1,238 @@ +/* +* Copyright (c) 2023, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +* +*/ + +#include "KadDHT.h" + +namespace i2p +{ +namespace data +{ + DHTNode::DHTNode (): + zero (nullptr), one (nullptr), hash (nullptr) + { + } + + DHTNode::~DHTNode () + { + if (zero) delete zero; + if (one) delete one; + if (hash) delete hash; + } + + void DHTNode::MoveHashUp (bool fromOne) + { + DHTNode *& side = fromOne ? one : zero; + if (side) + { + if (hash) delete hash; // shouldn't happen + hash = side->hash; + side->hash = nullptr; + delete side; + side = nullptr; + } + } + + DHTTable::DHTTable (): + m_Size (0) + { + m_Root = new DHTNode; + } + + DHTTable::~DHTTable () + { + delete m_Root; + } + + DHTNode * DHTTable::Insert (const IdentHash& h) + { + return Insert (new IdentHash (h), m_Root, 0); + } + + DHTNode * DHTTable::Insert (IdentHash * h, DHTNode * root, int level) + { + if (root->hash) + { + if (*(root->hash) == *h) + { + delete h; + return root; + } + auto h2 = root->hash; + root->hash = nullptr; m_Size--; + int bit1, bit2; + do + { + bit1 = h->GetBit (level); + bit2 = h2->GetBit (level); + if (bit1 == bit2) + { + if (bit1) + { + if (root->one) return nullptr; // someting wrong + root->one = new DHTNode; + root = root->one; + } + else + { + if (root->zero) return nullptr; // someting wrong + root->zero = new DHTNode; + root = root->zero; + } + level++; + } + } + while (bit1 == bit2); + + if (!root->zero) + root->zero = new DHTNode; + if (!root->one) + root->one = new DHTNode; + if (bit1) + { + Insert (h2, root->zero, level + 1); + return Insert (h, root->one, level + 1); + } + else + { + Insert (h2, root->one, level + 1); + return Insert (h, root->zero, level + 1); + } + } + else + { + if (!root->zero && !root->one) + { + root->hash = h; m_Size++; + return root; + } + int bit = h->GetBit (level); + if (bit) + { + if (!root->one) + root->one = new DHTNode; + return Insert (h, root->one, level + 1); + } + else + { + if (!root->zero) + root->zero = new DHTNode; + return Insert (h, root->zero, level + 1); + } + } + return nullptr; + } + + bool DHTTable::Remove (const IdentHash& h) + { + return Remove (h, m_Root, 0); + } + + bool DHTTable::Remove (const IdentHash& h, DHTNode * root, int level) + { + if (root) + { + if (root->hash && *(root->hash) == h) + { + delete root->hash; root->hash = nullptr; + m_Size--; + return true; + } + int bit = h.GetBit (level); + if (bit) + { + if (root->one && Remove (h, root->one, level + 1)) + { + if (root->one->IsEmpty ()) + { + delete root->one; + root->one = nullptr; + if (root->zero && root->zero->hash) + root->MoveHashUp (false); + } + else if (root->one->hash && !root->zero) + root->MoveHashUp (true); + return true; + } + } + else + { + if (root->zero && Remove (h, root->zero, level + 1)) + { + if (root->zero->IsEmpty ()) + { + delete root->zero; + root->zero = nullptr; + if (root->one && root->one->hash) + root->MoveHashUp (true); + } + else if (root->zero->hash && !root->one) + root->MoveHashUp (false); + return true; + } + } + } + return false; + } + + IdentHash * DHTTable::FindClosest (const IdentHash& h) + { + return FindClosest (h, m_Root, 0); + } + + IdentHash * DHTTable::FindClosest (const IdentHash& h, DHTNode * root, int level) + { + if (root->hash) return root->hash; + int bit = h.GetBit (level); + if (bit) + { + if (root->one) + return FindClosest (h, root->one, level + 1); + if (root->zero) + return FindClosest (h, root->zero, level + 1); + } + else + { + if (root->zero) + return FindClosest (h, root->zero, level + 1); + if (root->one) + return FindClosest (h, root->one, level + 1); + } + return nullptr; + } + + void DHTTable::Print (std::stringstream& s) + { + Print (s, m_Root, 0); + } + + void DHTTable::Print (std::stringstream& s, DHTNode * root, int level) + { + if (!root) return; + s << std::string (level, '-'); + if (root->hash) + { + if (!root->zero && !root->one) + s << '>' << GetIdentHashAbbreviation (*(root->hash)); + else + s << "error"; + } + s << std::endl; + if (root->zero) + { + s << std::string (level, '-') << "0" << std::endl; + Print (s, root->zero, level + 1); + } + if (root->one) + { + s << std::string (level, '-') << "1" << std::endl; + Print (s, root->one, level + 1); + } + } +} +} diff --git a/libi2pd/KadDHT.h b/libi2pd/KadDHT.h new file mode 100644 index 00000000..4a1b61b6 --- /dev/null +++ b/libi2pd/KadDHT.h @@ -0,0 +1,64 @@ +/* +* Copyright (c) 2023, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +* +*/ + +#ifndef KADDHT_H__ +#define KADDHT_H__ + +#include +#include +#include "Identity.h" + +// Kademlia DHT (XOR distance) + +namespace i2p +{ +namespace data +{ + struct DHTNode + { + DHTNode * zero, * one; + IdentHash * hash; + + DHTNode (); + ~DHTNode (); + + bool IsEmpty () const { return !zero && !one && !hash; }; + void MoveHashUp (bool fromOne); + }; + + class DHTTable + { + public: + + DHTTable (); + ~DHTTable (); + + DHTNode * Insert (const IdentHash& h); + bool Remove (const IdentHash& h); + IdentHash * FindClosest (const IdentHash& h); + + void Print (std::stringstream& s); + size_t GetSize () const { return m_Size; }; + + private: + + DHTNode * Insert (IdentHash * h, DHTNode * root, int level); // recursive + bool Remove (const IdentHash& h, DHTNode * root, int level); + IdentHash * FindClosest (const IdentHash& h, DHTNode * root, int level); + void Print (std::stringstream& s, DHTNode * root, int level); + + private: + + DHTNode * m_Root; + size_t m_Size; + }; +} +} + +#endif