Skip to content

Commit

Permalink
Add peers_in_max and peers_out_max configuration.
Browse files Browse the repository at this point in the history
Replaces peers_max with individual inbound and
outbound configuration. If peers_max is configured
then outbound and inbound connections are derived
as prior to this change. Otherwise the new
configuration is used. Config::outPeers is no longer
adjusted up or down.
  • Loading branch information
gregtatcam committed Sep 24, 2020
1 parent e1a2939 commit 70c4ecc
Show file tree
Hide file tree
Showing 8 changed files with 287 additions and 73 deletions.
9 changes: 9 additions & 0 deletions src/ripple/core/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,16 @@ class Config : public BasicConfig

// True to ask peers not to relay current IP.
bool PEER_PRIVATE = false;
// peers_max is a legacy configuration, which is going to be replaced
// with individual inbound peers peers_in_max and outbound peers
// peers_out_max configuration. for now we support both the legacy and
// the new configuration. if peers_max is configured then peers_in_max and
// peers_out_max are ignored.
std::size_t PEERS_MAX = 0;
// is true if peers_max is configured
bool legacyPeersMax_ = false;
std::size_t PEERS_OUT_MAX = 0;
std::size_t PEERS_IN_MAX = 0;

std::chrono::seconds WEBSOCKET_PING_FREQ = std::chrono::minutes{5};

Expand Down
2 changes: 2 additions & 0 deletions src/ripple/core/ConfigSections.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ struct ConfigSection
#define SECTION_PATH_SEARCH_MAX "path_search_max"
#define SECTION_PEER_PRIVATE "peer_private"
#define SECTION_PEERS_MAX "peers_max"
#define SECTION_PEERS_IN_MAX "peers_in_max"
#define SECTION_PEERS_OUT_MAX "peers_out_max"
#define SECTION_REDUCE_RELAY "reduce_relay"
#define SECTION_RELAY_PROPOSALS "relay_proposals"
#define SECTION_RELAY_VALIDATIONS "relay_validations"
Expand Down
43 changes: 43 additions & 0 deletions src/ripple/core/impl/Config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,50 @@ Config::loadFromString(std::string const& fileContents)
PEER_PRIVATE = beast::lexicalCastThrow<bool>(strTemp);

if (getSingleSection(secConfig, SECTION_PEERS_MAX, strTemp, j_))
{
PEERS_MAX = beast::lexicalCastThrow<std::size_t>(strTemp);
legacyPeersMax_ = true;
}
else
{
std::optional<int> peers_in_max{};
if (getSingleSection(secConfig, SECTION_PEERS_IN_MAX, strTemp, j_))
{
peers_in_max = beast::lexicalCastThrow<std::size_t>(strTemp);
if (*peers_in_max > 1000)
Throw<std::runtime_error>(
"Invalid value specified in [" SECTION_PEERS_IN_MAX
"] section; the value must be less or equal than 1000");
}

std::optional<int> peers_out_max{};
if (getSingleSection(secConfig, SECTION_PEERS_OUT_MAX, strTemp, j_))
{
peers_out_max = beast::lexicalCastThrow<std::size_t>(strTemp);
if (*peers_out_max < 10 || *peers_out_max > 1000)
Throw<std::runtime_error>(
"Invalid value specified in [" SECTION_PEERS_OUT_MAX
"] section; the value must be in range 10-1000");
}

// if one section is configured then the other must be configured too
if ((peers_in_max && !peers_out_max) ||
(peers_out_max && !peers_in_max))
Throw<std::runtime_error>("Both sections [" SECTION_PEERS_IN_MAX
"]"
"and [" SECTION_PEERS_OUT_MAX
"] must be configured");
// if none is configured then we assume the legacy path
if (!peers_in_max && !peers_out_max)
{
legacyPeersMax_ = true;
}
else
{
PEERS_IN_MAX = *peers_in_max;
PEERS_OUT_MAX = *peers_out_max;
}
}

if (getSingleSection(secConfig, SECTION_NODE_SIZE, strTemp, j_))
{
Expand Down
36 changes: 5 additions & 31 deletions src/ripple/overlay/impl/OverlayImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -499,37 +499,11 @@ OverlayImpl::checkStopped()
void
OverlayImpl::onPrepare()
{
PeerFinder::Config config;

if (app_.config().PEERS_MAX != 0)
config.maxPeers = app_.config().PEERS_MAX;

config.outPeers = config.calcOutPeers();

auto const port = serverHandler_.setup().overlay.port;

config.peerPrivate = app_.config().PEER_PRIVATE;

// Servers with peer privacy don't want to allow incoming connections
config.wantIncoming = (!config.peerPrivate) && (port != 0);

// This will cause servers configured as validators to request that
// peers they connect to never report their IP address. We set this
// after we set the 'wantIncoming' because we want a "soft" version
// of peer privacy unless the operator explicitly asks for it.
if (!app_.getValidationPublicKey().empty())
config.peerPrivate = true;

// if it's a private peer or we are running as standalone
// automatic connections would defeat the purpose.
config.autoConnect =
!app_.config().standalone() && !app_.config().PEER_PRIVATE;
config.listeningPort = port;
config.features = "";
config.ipLimit = setup_.ipLimit;

// Enforce business rules
config.applyTuning();
PeerFinder::Config config = PeerFinder::Config::makeConfig(
app_.config(),
serverHandler_.setup().overlay.port,
!app_.getValidationPublicKey().empty(),
setup_.ipLimit);

m_peerFinder->setConfig(config);

Expand Down
36 changes: 27 additions & 9 deletions src/ripple/peerfinder/PeerfinderManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

#include <ripple/beast/clock/abstract_clock.h>
#include <ripple/beast/utility/PropertyStream.h>
#include <ripple/core/Config.h>
#include <ripple/core/Stoppable.h>
#include <ripple/peerfinder/Slot.h>
#include <boost/asio/ip/tcp.hpp>
Expand All @@ -45,17 +46,20 @@ struct Config
*/
int maxPeers;

/** Legacy config if peers_max is set. */
bool legacyConfig;

/** The number of automatic outbound connections to maintain.
Outbound connections are only maintained if autoConnect
is `true`. The value can be fractional; The decision to round up
or down will be made using a per-process pseudorandom number and
a probability proportional to the fractional part.
Example:
If outPeers is 9.3, then 30% of nodes will maintain 9 outbound
connections, while 70% of nodes will maintain 10 outbound
connections.
is `true`.
*/
int outPeers;

/** The number of automatic inbound connections to maintain.
Inbound connections are only maintained if wantIncoming
is `true`.
*/
double outPeers;
int inPeers;

/** `true` if we want our IP address kept private. */
bool peerPrivate = true;
Expand All @@ -81,7 +85,7 @@ struct Config
Config();

/** Returns a suitable value for outPeers according to the rules. */
double
std::size_t
calcOutPeers() const;

/** Adjusts the values so they follow the business rules. */
Expand All @@ -91,6 +95,20 @@ struct Config
/** Write the configuration into a property stream */
void
onWrite(beast::PropertyStream::Map& map);

/** Make PeerFinder::Config from configuration parameters
* @param config server's configuration
* @param port server's listening port
* @param validationPublicKey true if validation public key is not empty
* @param ipLimit limit of incoming connections per IP
* @return PeerFinder::Config
*/
static Config
makeConfig(
ripple::Config const& config,
std::uint16_t port,
bool validationPublicKey,
int ipLimit);
};

//------------------------------------------------------------------------------
Expand Down
43 changes: 17 additions & 26 deletions src/ripple/peerfinder/impl/Counts.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,6 @@ class Counts
, m_acceptCount(0)
, m_closingCount(0)
{
m_roundingThreshold =
std::generate_canonical<double, 10>(default_prng());
}

//--------------------------------------------------------------------------
Expand Down Expand Up @@ -136,28 +134,28 @@ class Counts
void
onConfig(Config const& config)
{
// Calculate the number of outbound peers we want. If we dont want or
// can't accept incoming, this will simply be equal to maxPeers.
// Otherwise we calculate a fractional amount based on percentages and
// pseudo-randomly round up or down.
//
if (config.wantIncoming)
if (config.legacyConfig)
{
// Round outPeers upwards using a Bernoulli distribution
m_out_max = std::floor(config.outPeers);
if (m_roundingThreshold < (config.outPeers - m_out_max))
++m_out_max;
// Calculate the number of outbound peers we want. If we dont want
// or can't accept incoming, this will simply be equal to maxPeers.
if (config.wantIncoming)
m_out_max = config.outPeers;
else
m_out_max = config.maxPeers;

// Calculate the largest number of inbound connections we could
// take.
if (config.maxPeers >= m_out_max)
m_in_max = config.maxPeers - m_out_max;
else
m_in_max = 0;
}
else
{
m_out_max = config.maxPeers;
m_out_max = config.outPeers;
if (config.wantIncoming)
m_in_max = config.inPeers;
}

// Calculate the largest number of inbound connections we could take.
if (config.maxPeers >= m_out_max)
m_in_max = config.maxPeers - m_out_max;
else
m_in_max = 0;
}

/** Returns the number of accepted connections that haven't handshaked. */
Expand Down Expand Up @@ -350,13 +348,6 @@ class Counts

// Number of connections that are gracefully closing.
int m_closingCount;

/** Fractional threshold below which we round down.
This is used to round the value of Config::outPeers up or down in
such a way that the network-wide average number of outgoing
connections approximates the recommended, fractional value.
*/
double m_roundingThreshold;
};

} // namespace PeerFinder
Expand Down
71 changes: 64 additions & 7 deletions src/ripple/peerfinder/impl/PeerfinderConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,29 +25,38 @@ namespace PeerFinder {

Config::Config()
: maxPeers(Tuning::defaultMaxPeers)
, legacyConfig(false)
, outPeers(calcOutPeers())
, inPeers(0)
, wantIncoming(true)
, autoConnect(true)
, listeningPort(0)
, ipLimit(0)
{
}

double
std::size_t
Config::calcOutPeers() const
{
return std::max(
maxPeers * Tuning::outPercent * 0.01, double(Tuning::minOutCount));
return std::round(std::max(
maxPeers * Tuning::outPercent * 0.01, double(Tuning::minOutCount)));
}

void
Config::applyTuning()
{
if (maxPeers < Tuning::minOutCount)
maxPeers = Tuning::minOutCount;
outPeers = calcOutPeers();
if (legacyConfig)
{
if (maxPeers < Tuning::minOutCount)
maxPeers = Tuning::minOutCount;
outPeers = calcOutPeers();

auto const inPeers = maxPeers - outPeers;
inPeers = maxPeers - outPeers;
}
else
{
maxPeers = 0;
}

if (ipLimit == 0)
{
Expand Down Expand Up @@ -78,5 +87,53 @@ Config::onWrite(beast::PropertyStream::Map& map)
map["ip_limit"] = ipLimit;
}

Config
Config::makeConfig(
ripple::Config const& cfg,
std::uint16_t port,
bool validationPublicKey,
int ipLimit)
{
PeerFinder::Config config;

config.legacyConfig = cfg.legacyPeersMax_;
if (config.legacyConfig)
{
if (cfg.PEERS_MAX != 0)
config.maxPeers = cfg.PEERS_MAX;

config.outPeers = config.calcOutPeers();
}
else
{
config.outPeers = cfg.PEERS_OUT_MAX;
config.inPeers = cfg.PEERS_IN_MAX;
}

config.peerPrivate = cfg.PEER_PRIVATE;

// Servers with peer privacy don't want to allow incoming connections
config.wantIncoming = (!config.peerPrivate) && (port != 0);

// This will cause servers configured as validators to request that
// peers they connect to never report their IP address. We set this
// after we set the 'wantIncoming' because we want a "soft" version
// of peer privacy unless the operator explicitly asks for it.
if (validationPublicKey)
config.peerPrivate = true;

// if it's a private peer or we are running as standalone
// automatic connections would defeat the purpose.
config.autoConnect = !cfg.standalone() && !cfg.PEER_PRIVATE;
config.listeningPort = port;
config.features = "";
config.ipLimit = ipLimit;

// Enforce business rules
config.applyTuning();

return config;
}

} // namespace PeerFinder
} // namespace ripple
Loading

0 comments on commit 70c4ecc

Please sign in to comment.