Initial Vexta Core v0.1.0 release
This commit is contained in:
commit
25c454329d
2481 changed files with 689289 additions and 0 deletions
326
src/dandelion.cpp
Normal file
326
src/dandelion.cpp
Normal file
|
|
@ -0,0 +1,326 @@
|
|||
// Copyright (c) 2021 The Digibyte developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#if defined(HAVE_CONFIG_H)
|
||||
#include <config/vexta-config.h>
|
||||
#endif
|
||||
|
||||
#include <net.h>
|
||||
#include <shutdown.h>
|
||||
|
||||
extern bool g_ibd_complete;
|
||||
|
||||
bool CConnman::isDandelionInbound(const CNode* const pnode) const
|
||||
{
|
||||
return (std::find(vDandelionInbound.begin(), vDandelionInbound.end(), pnode) != vDandelionInbound.end());
|
||||
}
|
||||
|
||||
bool CConnman::isLocalDandelionDestinationSet() const
|
||||
{
|
||||
return (localDandelionDestination != nullptr);
|
||||
}
|
||||
|
||||
bool CConnman::setLocalDandelionDestination()
|
||||
{
|
||||
if (!isLocalDandelionDestinationSet()) {
|
||||
localDandelionDestination = SelectFromDandelionDestinations();
|
||||
LogPrint(BCLog::DANDELION, "Set local Dandelion destination:\n%s", GetDandelionRoutingDataDebugString());
|
||||
}
|
||||
return isLocalDandelionDestinationSet();
|
||||
}
|
||||
|
||||
CNode* CConnman::getDandelionDestination(CNode* pfrom)
|
||||
{
|
||||
for (auto const& e : mDandelionRoutes) {
|
||||
if (pfrom == e.first) {
|
||||
return e.second;
|
||||
}
|
||||
}
|
||||
CNode* newPto = SelectFromDandelionDestinations();
|
||||
if (newPto != nullptr) {
|
||||
mDandelionRoutes.insert(std::make_pair(pfrom, newPto));
|
||||
LogPrint(BCLog::DANDELION, "Added Dandelion route:\n%s", GetDandelionRoutingDataDebugString());
|
||||
}
|
||||
return newPto;
|
||||
}
|
||||
|
||||
bool CConnman::localDandelionDestinationPushInventory(const CInv& inv)
|
||||
{
|
||||
if (isLocalDandelionDestinationSet()) {
|
||||
localDandelionDestination->PushOtherInventory(inv);
|
||||
return true;
|
||||
} else if (setLocalDandelionDestination()) {
|
||||
localDandelionDestination->PushOtherInventory(inv);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool CConnman::insertDandelionEmbargo(const uint256& hash, std::chrono::microseconds& embargo) {
|
||||
auto pair = mDandelionEmbargo.insert(std::make_pair(hash, embargo));
|
||||
return pair.second;
|
||||
}
|
||||
|
||||
bool CConnman::isTxDandelionEmbargoed(const uint256& hash) const
|
||||
{
|
||||
auto it = mDandelionEmbargo.find(hash);
|
||||
if (it == mDandelionEmbargo.end()) {
|
||||
return false;
|
||||
}
|
||||
// Compare current time with stored embargo time
|
||||
auto now = GetTime<std::chrono::microseconds>();
|
||||
if (now < it->second) {
|
||||
// Embargo not yet expired
|
||||
return true;
|
||||
}
|
||||
// Otherwise, embargo expired — best practice is to remove it here or in CheckDandelionEmbargoes()
|
||||
// We'll just return false, so that net_processing can proceed and eventually remove it.
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool CConnman::removeDandelionEmbargo(const uint256& hash)
|
||||
{
|
||||
bool removed = false;
|
||||
for (auto iter = mDandelionEmbargo.begin(); iter != mDandelionEmbargo.end(); )
|
||||
{
|
||||
if (iter->first == hash) {
|
||||
iter = mDandelionEmbargo.erase(iter);
|
||||
removed = true;
|
||||
} else {
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
|
||||
|
||||
CNode* CConnman::SelectFromDandelionDestinations() const
|
||||
{
|
||||
std::map<CNode*, uint64_t> mDandelionDestinationCounts;
|
||||
for (size_t i = 0; i < vDandelionDestination.size(); i++) {
|
||||
mDandelionDestinationCounts.insert(std::make_pair(vDandelionDestination.at(i), 0));
|
||||
}
|
||||
for (auto& e : mDandelionDestinationCounts) {
|
||||
for (auto const& f : mDandelionRoutes) {
|
||||
if (e.first == f.second) {
|
||||
e.second += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
unsigned int minNumConnections = vDandelionInbound.size();
|
||||
for (auto const& e : mDandelionDestinationCounts) {
|
||||
if (e.second < minNumConnections) {
|
||||
minNumConnections = e.second;
|
||||
}
|
||||
}
|
||||
std::vector<CNode*> candidateDestinations;
|
||||
for (auto const& e : mDandelionDestinationCounts) {
|
||||
if (e.second == minNumConnections) {
|
||||
candidateDestinations.push_back(e.first);
|
||||
}
|
||||
}
|
||||
FastRandomContext rng;
|
||||
CNode* dandelionDestination = nullptr;
|
||||
if (candidateDestinations.size() > 0) {
|
||||
dandelionDestination = candidateDestinations.at(rng.randrange(candidateDestinations.size()));
|
||||
}
|
||||
return dandelionDestination;
|
||||
}
|
||||
|
||||
void CConnman::CloseDandelionConnections(const CNode* const pnode)
|
||||
{
|
||||
// Remove pnode from vDandelionInbound, if present
|
||||
for (auto iter = vDandelionInbound.begin(); iter != vDandelionInbound.end();) {
|
||||
if (*iter == pnode) {
|
||||
iter = vDandelionInbound.erase(iter);
|
||||
} else {
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
// Remove pnode from vDandelionOutbound, if present
|
||||
for (auto iter = vDandelionOutbound.begin(); iter != vDandelionOutbound.end();) {
|
||||
if (*iter == pnode) {
|
||||
iter = vDandelionOutbound.erase(iter);
|
||||
} else {
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
// Remove pnode from vDandelionDestination, if present
|
||||
bool isDandelionDestination = false;
|
||||
for (auto iter = vDandelionDestination.begin(); iter != vDandelionDestination.end();) {
|
||||
if (*iter == pnode) {
|
||||
isDandelionDestination = true;
|
||||
iter = vDandelionDestination.erase(iter);
|
||||
} else {
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
// Generate a replacement Dandelion destination, if necessary
|
||||
if (isDandelionDestination) {
|
||||
// Gather a vector of candidate replacements (outbound peers that are not already destinations)
|
||||
std::vector<CNode*> candidateReplacements;
|
||||
for (auto iteri = vDandelionOutbound.begin(); iteri != vDandelionOutbound.end();) {
|
||||
bool eligibleCandidate = true;
|
||||
for (auto iterj = vDandelionDestination.begin(); iterj != vDandelionDestination.end();) {
|
||||
if (*iteri == *iterj) {
|
||||
eligibleCandidate = false;
|
||||
iterj = vDandelionDestination.end();
|
||||
} else {
|
||||
iterj++;
|
||||
}
|
||||
}
|
||||
if (eligibleCandidate) {
|
||||
candidateReplacements.push_back(*iteri);
|
||||
}
|
||||
iteri++;
|
||||
}
|
||||
// Select a candidate to be the replacement destination
|
||||
FastRandomContext rng;
|
||||
CNode* replacementDestination = nullptr;
|
||||
if (candidateReplacements.size() > 0) {
|
||||
replacementDestination = candidateReplacements.at(rng.randrange(candidateReplacements.size()));
|
||||
}
|
||||
if (replacementDestination != nullptr) {
|
||||
vDandelionDestination.push_back(replacementDestination);
|
||||
}
|
||||
}
|
||||
// Generate a replacement pnode, to be used if necessary
|
||||
CNode* newPto = SelectFromDandelionDestinations();
|
||||
// Remove from mDandelionRoutes, if present; if destination, try to replace
|
||||
for (auto iter = mDandelionRoutes.begin(); iter != mDandelionRoutes.end();) {
|
||||
if (iter->first == pnode) {
|
||||
iter = mDandelionRoutes.erase(iter);
|
||||
} else if (iter->second == pnode) {
|
||||
if (newPto == nullptr) {
|
||||
iter = mDandelionRoutes.erase(iter);
|
||||
} else {
|
||||
iter->second = newPto;
|
||||
iter++;
|
||||
}
|
||||
} else {
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
// Replace localDandelionDestination if equal to pnode
|
||||
if (localDandelionDestination == pnode) {
|
||||
localDandelionDestination = newPto;
|
||||
}
|
||||
}
|
||||
|
||||
std::string CConnman::GetDandelionRoutingDataDebugString() const
|
||||
{
|
||||
std::string dandelionRoutingDataDebugString = "";
|
||||
dandelionRoutingDataDebugString.append(" vDandelionInbound: ");
|
||||
for (auto const& e : vDandelionInbound) {
|
||||
dandelionRoutingDataDebugString.append(std::to_string(e->GetId()) + " ");
|
||||
}
|
||||
dandelionRoutingDataDebugString.append("\n vDandelionOutbound: ");
|
||||
for (auto const& e : vDandelionOutbound) {
|
||||
dandelionRoutingDataDebugString.append(std::to_string(e->GetId()) + " ");
|
||||
}
|
||||
dandelionRoutingDataDebugString.append("\n vDandelionDestination: ");
|
||||
for (auto const& e : vDandelionDestination) {
|
||||
dandelionRoutingDataDebugString.append(std::to_string(e->GetId()) + " ");
|
||||
}
|
||||
dandelionRoutingDataDebugString.append("\n mDandelionRoutes: ");
|
||||
for (auto const& e : mDandelionRoutes) {
|
||||
dandelionRoutingDataDebugString.append("(" + std::to_string(e.first->GetId()) + "," + std::to_string(e.second->GetId()) + ") ");
|
||||
}
|
||||
dandelionRoutingDataDebugString.append("\n localDandelionDestination: ");
|
||||
if (!localDandelionDestination) {
|
||||
dandelionRoutingDataDebugString.append("nullptr");
|
||||
} else {
|
||||
dandelionRoutingDataDebugString.append(std::to_string(localDandelionDestination->GetId()));
|
||||
}
|
||||
dandelionRoutingDataDebugString.append("\n");
|
||||
return dandelionRoutingDataDebugString;
|
||||
}
|
||||
|
||||
void CConnman::DandelionShuffle()
|
||||
{
|
||||
// Dandelion debug message
|
||||
LogPrint(BCLog::DANDELION, "Before Dandelion shuffle:\n%s", GetDandelionRoutingDataDebugString());
|
||||
{
|
||||
// Lock node pointers
|
||||
LOCK(cs_vNodes);
|
||||
// Iterate through mDandelionRoutes to facilitate bookkeeping
|
||||
for (auto iter = mDandelionRoutes.begin(); iter != mDandelionRoutes.end();) {
|
||||
iter = mDandelionRoutes.erase(iter);
|
||||
}
|
||||
// Set localDandelionDestination to nulltpr and perform bookkeeping
|
||||
if (localDandelionDestination != nullptr) {
|
||||
localDandelionDestination = nullptr;
|
||||
}
|
||||
// Clear vDandelionDestination
|
||||
// (bookkeeping already done while iterating through mDandelionRoutes)
|
||||
vDandelionDestination.clear();
|
||||
// Repopulate vDandelionDestination
|
||||
while (vDandelionDestination.size() < DANDELION_MAX_DESTINATIONS && vDandelionDestination.size() < vDandelionOutbound.size()) {
|
||||
std::vector<CNode*> candidateDestinations;
|
||||
for (auto iteri = vDandelionOutbound.begin(); iteri != vDandelionOutbound.end();) {
|
||||
bool eligibleCandidate = true;
|
||||
for (auto iterj = vDandelionDestination.begin(); iterj != vDandelionDestination.end();) {
|
||||
if (*iteri == *iterj) {
|
||||
eligibleCandidate = false;
|
||||
iterj = vDandelionDestination.end();
|
||||
} else {
|
||||
iterj++;
|
||||
}
|
||||
}
|
||||
if (eligibleCandidate) {
|
||||
candidateDestinations.push_back(*iteri);
|
||||
}
|
||||
iteri++;
|
||||
}
|
||||
FastRandomContext rng;
|
||||
if (candidateDestinations.size() > 0) {
|
||||
vDandelionDestination.push_back(candidateDestinations.at(rng.randrange(candidateDestinations.size())));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Generate new routes
|
||||
for (auto pnode : vDandelionInbound) {
|
||||
CNode* pto = SelectFromDandelionDestinations();
|
||||
if (pto) {
|
||||
mDandelionRoutes.insert(std::make_pair(pnode, pto));
|
||||
}
|
||||
}
|
||||
localDandelionDestination = SelectFromDandelionDestinations();
|
||||
}
|
||||
// Dandelion debug message
|
||||
LogPrint(BCLog::DANDELION, "After Dandelion shuffle:\n%s", GetDandelionRoutingDataDebugString());
|
||||
}
|
||||
|
||||
void CConnman::ThreadDandelionShuffle()
|
||||
{
|
||||
while (!ShutdownRequested()) {
|
||||
UninterruptibleSleep(std::chrono::milliseconds{1000});
|
||||
if (g_ibd_complete) {
|
||||
break;
|
||||
}
|
||||
if(ShutdownRequested()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto now = GetTime<std::chrono::microseconds>();
|
||||
auto nNextDandelionShuffle = PoissonNextSend(now, DANDELION_SHUFFLE_INTERVAL);
|
||||
|
||||
while (!ShutdownRequested()) {
|
||||
now = GetTime<std::chrono::milliseconds>();
|
||||
if (now > nNextDandelionShuffle) {
|
||||
DandelionShuffle();
|
||||
nNextDandelionShuffle = PoissonNextSend(now, DANDELION_SHUFFLE_INTERVAL);
|
||||
}
|
||||
if (ShutdownRequested()) {
|
||||
return;
|
||||
}
|
||||
if (!interruptNet.sleep_for(std::chrono::milliseconds(100))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue