mirror of
https://github.com/XRPLF/rippled.git
synced 2026-10-09 13:18:09 +00:00
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
395 lines
9.3 KiB
C++
395 lines
9.3 KiB
C++
#pragma once
|
|
|
|
#include <xrpl/beast/utility/PropertyStream.h>
|
|
#include <xrpl/beast/utility/instrumentation.h>
|
|
#include <xrpl/peerfinder/Config.h>
|
|
#include <xrpl/peerfinder/Slot.h>
|
|
#include <xrpl/peerfinder/detail/Tuning.h>
|
|
|
|
#include <cstddef>
|
|
#include <sstream>
|
|
#include <string>
|
|
|
|
namespace xrpl::peer_finder {
|
|
|
|
/**
|
|
* Direction of a slot count adjustment.
|
|
*/
|
|
enum class CountAdjustment : int { Decrement = -1, Increment = 1 };
|
|
|
|
/**
|
|
* Manages the count of available connections for the various slots.
|
|
*/
|
|
class Counts
|
|
{
|
|
public:
|
|
/**
|
|
* Adds the slot state and properties to the slot counts.
|
|
*/
|
|
void
|
|
add(Slot const& s)
|
|
{
|
|
adjust(s, CountAdjustment::Increment);
|
|
}
|
|
|
|
/**
|
|
* Removes the slot state and properties from the slot counts.
|
|
*/
|
|
void
|
|
remove(Slot const& s)
|
|
{
|
|
adjust(s, CountAdjustment::Decrement);
|
|
}
|
|
|
|
/**
|
|
* Returns `true` if the slot can become active.
|
|
*/
|
|
[[nodiscard]] bool
|
|
canActivate(Slot const& s) const
|
|
{
|
|
// Must be handshaked and in the right state
|
|
XRPL_ASSERT(
|
|
s.state() == Slot::State::Connected || s.state() == Slot::State::Accept,
|
|
"xrpl::peer_finder::Counts::can_activate : valid input state");
|
|
|
|
if (s.fixed() || s.reserved())
|
|
return true;
|
|
|
|
if (s.inbound())
|
|
return inActive_ < inMax_;
|
|
|
|
return outActive_ < outMax_;
|
|
}
|
|
|
|
/**
|
|
* Returns the number of attempts needed to bring us to the max.
|
|
*/
|
|
[[nodiscard]] std::size_t
|
|
attemptsNeeded() const
|
|
{
|
|
if (attempts_ >= tuning::kMaxConnectAttempts)
|
|
return 0;
|
|
return tuning::kMaxConnectAttempts - attempts_;
|
|
}
|
|
|
|
/**
|
|
* Returns the number of outbound connection attempts.
|
|
*/
|
|
[[nodiscard]] std::size_t
|
|
attempts() const
|
|
{
|
|
return attempts_;
|
|
}
|
|
|
|
/**
|
|
* Returns the total number of outbound slots.
|
|
*/
|
|
[[nodiscard]] int
|
|
outMax() const
|
|
{
|
|
return outMax_;
|
|
}
|
|
|
|
/**
|
|
* Returns the number of outbound peers assigned an open slot.
|
|
* Fixed peers do not count towards outbound slots used.
|
|
*/
|
|
[[nodiscard]] int
|
|
outActive() const
|
|
{
|
|
return outActive_;
|
|
}
|
|
|
|
/**
|
|
* Returns the number of fixed connections.
|
|
*/
|
|
[[nodiscard]] std::size_t
|
|
fixed() const
|
|
{
|
|
return fixed_;
|
|
}
|
|
|
|
/**
|
|
* Returns the number of active fixed connections.
|
|
*/
|
|
[[nodiscard]] std::size_t
|
|
fixedActive() const
|
|
{
|
|
return fixedActive_;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
|
|
/**
|
|
* Called when the config is set or changed.
|
|
*/
|
|
void
|
|
onConfig(Config const& config)
|
|
{
|
|
outMax_ = config.outPeers;
|
|
if (config.wantIncoming)
|
|
inMax_ = config.inPeers;
|
|
}
|
|
|
|
/**
|
|
* Returns the number of accepted connections that haven't handshaked.
|
|
*/
|
|
[[nodiscard]] int
|
|
acceptCount() const
|
|
{
|
|
return acceptCount_;
|
|
}
|
|
|
|
/**
|
|
* Returns the number of connection attempts currently active.
|
|
*/
|
|
[[nodiscard]] int
|
|
connectCount() const
|
|
{
|
|
return attempts_;
|
|
}
|
|
|
|
/**
|
|
* Returns the number of connections that are gracefully closing.
|
|
*/
|
|
[[nodiscard]] int
|
|
closingCount() const
|
|
{
|
|
return closingCount_;
|
|
}
|
|
|
|
/**
|
|
* Returns the total number of inbound slots.
|
|
*/
|
|
[[nodiscard]] int
|
|
inMax() const
|
|
{
|
|
return inMax_;
|
|
}
|
|
|
|
/**
|
|
* Returns the number of inbound peers assigned an open slot.
|
|
*/
|
|
[[nodiscard]] int
|
|
inboundActive() const
|
|
{
|
|
return inActive_;
|
|
}
|
|
|
|
/**
|
|
* Returns the total number of active peers excluding fixed peers.
|
|
*/
|
|
[[nodiscard]] int
|
|
totalActive() const
|
|
{
|
|
return inActive_ + outActive_;
|
|
}
|
|
|
|
/**
|
|
* Returns the number of unused inbound slots.
|
|
* Fixed peers do not deduct from inbound slots or count towards totals.
|
|
*/
|
|
[[nodiscard]] int
|
|
inboundSlotsFree() const
|
|
{
|
|
if (inActive_ < inMax_)
|
|
return inMax_ - inActive_;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Returns the number of unused outbound slots.
|
|
* Fixed peers do not deduct from outbound slots or count towards totals.
|
|
*/
|
|
[[nodiscard]] int
|
|
outboundSlotsFree() const
|
|
{
|
|
if (outActive_ < outMax_)
|
|
return outMax_ - outActive_;
|
|
return 0;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
|
|
/**
|
|
* Returns true if the slot logic considers us "connected" to the network.
|
|
*/
|
|
[[nodiscard]] bool
|
|
isConnectedToNetwork() const
|
|
{
|
|
// We will consider ourselves connected if we have reached
|
|
// the number of outgoing connections desired, or if connect
|
|
// automatically is false.
|
|
//
|
|
// Fixed peers do not count towards the active outgoing total.
|
|
|
|
return outMax_ <= 0;
|
|
}
|
|
|
|
/**
|
|
* Output statistics.
|
|
*/
|
|
void
|
|
onWrite(beast::PropertyStream::Map& map) const
|
|
{
|
|
map["accept"] = acceptCount();
|
|
map["connect"] = connectCount();
|
|
map["close"] = closingCount();
|
|
map["in"] << inActive_ << "/" << inMax_;
|
|
map["out"] << outActive_ << "/" << outMax_;
|
|
map["fixed"] = fixedActive_;
|
|
map["reserved"] = reserved_;
|
|
map["total"] = active_;
|
|
}
|
|
|
|
/**
|
|
* Records the state for diagnostics.
|
|
*/
|
|
[[nodiscard]] std::string
|
|
stateString() const
|
|
{
|
|
std::stringstream ss;
|
|
ss << outActive_ << "/" << outMax_ << " out, " << inActive_ << "/" << inMax_ << " in, "
|
|
<< connectCount() << " connecting, " << closingCount() << " closing";
|
|
return ss.str();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
private:
|
|
/**
|
|
* Increments or decrements a counter based on the adjustment direction.
|
|
*/
|
|
template <typename T>
|
|
static void
|
|
adjustCounter(T& counter, CountAdjustment dir)
|
|
{
|
|
switch (dir)
|
|
{
|
|
case CountAdjustment::Increment:
|
|
++counter;
|
|
break;
|
|
case CountAdjustment::Decrement:
|
|
--counter;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Adjusts counts based on the specified slot, in the direction indicated.
|
|
//
|
|
// IMPORTANT: All std::size_t counters MUST be adjusted via adjustCounter()
|
|
// and NEVER via `+= n` where n = static_cast<int>(dir). When dir is
|
|
// Decrement, n == -1; adding -1 to a std::size_t implicitly converts -1 to
|
|
// SIZE_MAX, which UBSan flags as unsigned-integer-overflow and masks real
|
|
// underflow bugs (decrementing a counter already at zero). Plain int
|
|
// counters (acceptCount_, attempts_, closingCount_) are safe with += n.
|
|
void
|
|
adjust(Slot const& s, CountAdjustment const dir)
|
|
{
|
|
int const n = static_cast<int>(dir);
|
|
if (s.fixed())
|
|
adjustCounter(fixed_, dir);
|
|
|
|
if (s.reserved())
|
|
adjustCounter(reserved_, dir);
|
|
|
|
switch (s.state())
|
|
{
|
|
case Slot::State::Accept:
|
|
XRPL_ASSERT(s.inbound(), "xrpl::peer_finder::Counts::adjust : input is inbound");
|
|
acceptCount_ += n;
|
|
break;
|
|
|
|
case Slot::State::Connect:
|
|
case Slot::State::Connected:
|
|
XRPL_ASSERT(
|
|
!s.inbound(),
|
|
"xrpl::peer_finder::Counts::adjust : input is not "
|
|
"inbound");
|
|
attempts_ += n;
|
|
break;
|
|
|
|
case Slot::State::Active:
|
|
if (s.fixed())
|
|
adjustCounter(fixedActive_, dir);
|
|
if (!s.fixed() && !s.reserved())
|
|
{
|
|
if (s.inbound())
|
|
{
|
|
adjustCounter(inActive_, dir);
|
|
}
|
|
else
|
|
{
|
|
adjustCounter(outActive_, dir);
|
|
}
|
|
}
|
|
adjustCounter(active_, dir);
|
|
break;
|
|
|
|
case Slot::State::Closing:
|
|
closingCount_ += n;
|
|
break;
|
|
|
|
// LCOV_EXCL_START
|
|
default:
|
|
UNREACHABLE("xrpl::peer_finder::Counts::adjust : invalid input state");
|
|
break;
|
|
// LCOV_EXCL_STOP
|
|
};
|
|
}
|
|
|
|
private:
|
|
/**
|
|
* Outbound connection attempts.
|
|
*/
|
|
int attempts_{0};
|
|
|
|
/**
|
|
* Active connections, including fixed and reserved.
|
|
*/
|
|
std::size_t active_{0};
|
|
|
|
/**
|
|
* Total number of inbound slots.
|
|
*/
|
|
std::size_t inMax_{0};
|
|
|
|
/**
|
|
* Number of inbound slots assigned to active peers.
|
|
*/
|
|
std::size_t inActive_{0};
|
|
|
|
/**
|
|
* Maximum desired outbound slots.
|
|
*/
|
|
std::size_t outMax_{0};
|
|
|
|
/**
|
|
* Active outbound slots.
|
|
*/
|
|
std::size_t outActive_{0};
|
|
|
|
/**
|
|
* Fixed connections.
|
|
*/
|
|
std::size_t fixed_{0};
|
|
|
|
/**
|
|
* Active fixed connections.
|
|
*/
|
|
std::size_t fixedActive_{0};
|
|
|
|
/**
|
|
* Reserved connections.
|
|
*/
|
|
std::size_t reserved_{0};
|
|
|
|
// Number of inbound connections that are
|
|
// not active or gracefully closing.
|
|
int acceptCount_{0};
|
|
|
|
// Number of connections that are gracefully closing.
|
|
int closingCount_{0};
|
|
};
|
|
|
|
} // namespace xrpl::peer_finder
|