mirror of
https://github.com/XRPLF/rippled.git
synced 2026-07-23 23:20:33 +00:00
test: Modularize Peerfinder component and migrate Peerfinder tests from Beast to GTest and GMock (#7054)
Co-authored-by: Alex Kremer <akremer@ripple.com>
This commit is contained in:
@@ -1,9 +1,6 @@
|
||||
Loop: xrpld.app xrpld.overlay
|
||||
xrpld.app > xrpld.overlay
|
||||
|
||||
Loop: xrpld.app xrpld.peerfinder
|
||||
xrpld.peerfinder ~= xrpld.app
|
||||
|
||||
Loop: xrpld.app xrpld.rpc
|
||||
xrpld.rpc > xrpld.app
|
||||
|
||||
|
||||
@@ -25,6 +25,9 @@ libxrpl.nodestore > xrpl.config
|
||||
libxrpl.nodestore > xrpl.json
|
||||
libxrpl.nodestore > xrpl.nodestore
|
||||
libxrpl.nodestore > xrpl.protocol
|
||||
libxrpl.peerfinder > xrpl.basics
|
||||
libxrpl.peerfinder > xrpl.peerfinder
|
||||
libxrpl.peerfinder > xrpl.protocol
|
||||
libxrpl.protocol > xrpl.basics
|
||||
libxrpl.protocol > xrpl.json
|
||||
libxrpl.protocol > xrpl.protocol
|
||||
@@ -146,19 +149,13 @@ test.overlay > xrpl.config
|
||||
test.overlay > xrpld.app
|
||||
test.overlay > xrpld.core
|
||||
test.overlay > xrpld.overlay
|
||||
test.overlay > xrpld.peerfinder
|
||||
test.overlay > xrpl.json
|
||||
test.overlay > xrpl.nodestore
|
||||
test.overlay > xrpl.peerfinder
|
||||
test.overlay > xrpl.protocol
|
||||
test.overlay > xrpl.resource
|
||||
test.overlay > xrpl.server
|
||||
test.overlay > xrpl.shamap
|
||||
test.peerfinder > test.beast
|
||||
test.peerfinder > test.unit_test
|
||||
test.peerfinder > xrpl.basics
|
||||
test.peerfinder > xrpld.core
|
||||
test.peerfinder > xrpld.peerfinder
|
||||
test.peerfinder > xrpl.protocol
|
||||
test.protocol > test.jtx
|
||||
test.protocol > test.unit_test
|
||||
test.protocol > xrpl.basics
|
||||
@@ -197,6 +194,7 @@ tests.libxrpl > xrpl.json
|
||||
tests.libxrpl > xrpl.ledger
|
||||
tests.libxrpl > xrpl.net
|
||||
tests.libxrpl > xrpl.nodestore
|
||||
tests.libxrpl > xrpl.peerfinder
|
||||
tests.libxrpl > xrpl.protocol
|
||||
tests.libxrpl > xrpl.protocol_autogen
|
||||
tests.libxrpl > xrpl.resource
|
||||
@@ -220,6 +218,8 @@ xrpl.nodestore > xrpl.basics
|
||||
xrpl.nodestore > xrpl.config
|
||||
xrpl.nodestore > xrpl.json
|
||||
xrpl.nodestore > xrpl.protocol
|
||||
xrpl.peerfinder > xrpl.basics
|
||||
xrpl.peerfinder > xrpl.protocol
|
||||
xrpl.protocol > xrpl.basics
|
||||
xrpl.protocol > xrpl.json
|
||||
xrpl.protocol_autogen > xrpl.json
|
||||
@@ -253,6 +253,7 @@ xrpld.app > xrpl.json
|
||||
xrpld.app > xrpl.ledger
|
||||
xrpld.app > xrpl.net
|
||||
xrpld.app > xrpl.nodestore
|
||||
xrpld.app > xrpl.peerfinder
|
||||
xrpld.app > xrpl.protocol
|
||||
xrpld.app > xrpl.rdb
|
||||
xrpld.app > xrpl.resource
|
||||
@@ -277,15 +278,16 @@ xrpld.overlay > xrpld.core
|
||||
xrpld.overlay > xrpld.peerfinder
|
||||
xrpld.overlay > xrpl.json
|
||||
xrpld.overlay > xrpl.ledger
|
||||
xrpld.overlay > xrpl.peerfinder
|
||||
xrpld.overlay > xrpl.protocol
|
||||
xrpld.overlay > xrpl.resource
|
||||
xrpld.overlay > xrpl.server
|
||||
xrpld.overlay > xrpl.shamap
|
||||
xrpld.overlay > xrpl.tx
|
||||
xrpld.peerfinder > xrpl.basics
|
||||
xrpld.peerfinder > xrpl.config
|
||||
xrpld.peerfinder > xrpld.app
|
||||
xrpld.peerfinder > xrpld.core
|
||||
xrpld.peerfinder > xrpl.protocol
|
||||
xrpld.peerfinder > xrpl.peerfinder
|
||||
xrpld.peerfinder > xrpl.rdb
|
||||
xrpld.perflog > xrpl.basics
|
||||
xrpld.perflog > xrpl.config
|
||||
|
||||
@@ -133,6 +133,12 @@ target_link_libraries(
|
||||
add_module(xrpl resource)
|
||||
target_link_libraries(xrpl.libxrpl.resource PUBLIC xrpl.libxrpl.protocol)
|
||||
|
||||
add_module(xrpl peerfinder)
|
||||
target_link_libraries(
|
||||
xrpl.libxrpl.peerfinder
|
||||
PUBLIC xrpl.libxrpl.basics xrpl.libxrpl.protocol
|
||||
)
|
||||
|
||||
# Level 08
|
||||
add_module(xrpl net)
|
||||
target_link_libraries(
|
||||
@@ -227,6 +233,7 @@ target_link_modules(
|
||||
ledger
|
||||
net
|
||||
nodestore
|
||||
peerfinder
|
||||
protocol
|
||||
protocol_autogen
|
||||
rdb
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <xrpl/beast/container/detail/aged_associative_container.h>
|
||||
#include <xrpl/beast/container/detail/aged_container_iterator.h>
|
||||
#include <xrpl/beast/container/detail/empty_base_optimization.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
|
||||
#include <boost/intrusive/list.hpp>
|
||||
#include <boost/intrusive/unordered_set.hpp>
|
||||
|
||||
163
include/xrpl/peerfinder/Config.h
Normal file
163
include/xrpl/peerfinder/Config.h
Normal file
@@ -0,0 +1,163 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/utility/PropertyStream.h>
|
||||
#include <xrpl/peerfinder/detail/Tuning.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace xrpl::PeerFinder {
|
||||
|
||||
struct PeerLimitConfig
|
||||
{
|
||||
std::optional<std::size_t> maxPeers;
|
||||
std::optional<std::size_t> inPeers;
|
||||
std::optional<std::size_t> outPeers;
|
||||
};
|
||||
|
||||
/**
|
||||
* PeerFinder configuration settings.
|
||||
*/
|
||||
struct Config
|
||||
{
|
||||
/**
|
||||
* The largest number of public peer slots to allow.
|
||||
* This includes both inbound and outbound, but does not include
|
||||
* fixed peers.
|
||||
*/
|
||||
std::size_t maxPeers{Tuning::kDefaultMaxPeers};
|
||||
|
||||
/**
|
||||
* The number of automatic outbound connections to maintain.
|
||||
* Outbound connections are only maintained if autoConnect
|
||||
* is `true`.
|
||||
*/
|
||||
std::size_t outPeers = calcOutPeers(); // Note: relies on `maxPeers` being initialized
|
||||
|
||||
/**
|
||||
* The number of automatic inbound connections to maintain.
|
||||
* Inbound connections are only maintained if wantIncoming
|
||||
* is `true`.
|
||||
*/
|
||||
std::size_t inPeers{0};
|
||||
|
||||
/**
|
||||
* `true` if we want our IP address kept private.
|
||||
*/
|
||||
bool peerPrivate = true;
|
||||
|
||||
/**
|
||||
* `true` if we want to accept incoming connections.
|
||||
*/
|
||||
bool wantIncoming{true};
|
||||
|
||||
/**
|
||||
* `true` if we want to establish connections automatically
|
||||
*/
|
||||
bool autoConnect{true};
|
||||
|
||||
/**
|
||||
* The listening port number.
|
||||
*/
|
||||
std::uint16_t listeningPort{0};
|
||||
|
||||
/**
|
||||
* The set of features we advertise.
|
||||
*/
|
||||
std::string features;
|
||||
|
||||
/**
|
||||
* Limit how many incoming connections we allow per IP
|
||||
*/
|
||||
int ipLimit{0};
|
||||
|
||||
/**
|
||||
* `true` if we want to verify endpoints in TMEndpoints messages
|
||||
*/
|
||||
bool verifyEndpoints = true;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns a suitable value for outPeers according to the rules.
|
||||
*/
|
||||
[[nodiscard]] std::size_t
|
||||
calcOutPeers() const;
|
||||
|
||||
/**
|
||||
* Adjusts the values so they follow the business rules.
|
||||
*/
|
||||
void
|
||||
applyTuning();
|
||||
|
||||
/**
|
||||
* Write the configuration into a property stream
|
||||
*/
|
||||
void
|
||||
onWrite(beast::PropertyStream::Map& map) const;
|
||||
|
||||
/**
|
||||
* Make PeerFinder::Config from peer limit and server mode parameters.
|
||||
*/
|
||||
static Config
|
||||
makeConfig(
|
||||
bool peerPrivate,
|
||||
bool standalone,
|
||||
PeerLimitConfig const& limits,
|
||||
std::uint16_t port,
|
||||
bool validationPublicKey,
|
||||
int ipLimit,
|
||||
bool verifyEndpoints);
|
||||
|
||||
/**
|
||||
* Compares two configurations for equality field by field.
|
||||
*/
|
||||
friend bool
|
||||
operator==(Config const& lhs, Config const& rhs) = default;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Possible results from activating a slot.
|
||||
*/
|
||||
enum class Result { InboundDisabled, DuplicatePeer, IpLimitExceeded, Full, Success };
|
||||
|
||||
/**
|
||||
* @brief Converts a `Result` enum value to its string representation.
|
||||
*
|
||||
* This function provides a human-readable string for a given `Result` enum,
|
||||
* which is useful for logging, debugging, or displaying status messages.
|
||||
*
|
||||
* @param result The `Result` enum value to convert.
|
||||
* @return A `std::string_view` representing the enum value. Returns "unknown"
|
||||
* if the enum value is not explicitly handled.
|
||||
*
|
||||
* @note This function returns a `std::string_view` for performance.
|
||||
* A `std::string` would need to allocate memory on the heap and copy the
|
||||
* string literal into it every time the function is called.
|
||||
*/
|
||||
inline std::string_view
|
||||
to_string(Result result) noexcept
|
||||
{
|
||||
switch (result)
|
||||
{
|
||||
case Result::InboundDisabled:
|
||||
return "inbound disabled";
|
||||
case Result::DuplicatePeer:
|
||||
return "peer already connected";
|
||||
case Result::IpLimitExceeded:
|
||||
return "ip limit exceeded";
|
||||
case Result::Full:
|
||||
return "slots full";
|
||||
case Result::Success:
|
||||
return "success";
|
||||
}
|
||||
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
} // namespace xrpl::PeerFinder
|
||||
179
include/xrpl/peerfinder/PeerfinderManager.h
Normal file
179
include/xrpl/peerfinder/PeerfinderManager.h
Normal file
@@ -0,0 +1,179 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/net/IPEndpoint.h>
|
||||
#include <xrpl/beast/utility/PropertyStream.h>
|
||||
#include <xrpl/peerfinder/Config.h>
|
||||
#include <xrpl/peerfinder/Slot.h>
|
||||
#include <xrpl/peerfinder/Types.h>
|
||||
#include <xrpl/protocol/PublicKey.h>
|
||||
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace xrpl::PeerFinder {
|
||||
|
||||
/**
|
||||
* Maintains a set of IP addresses used for getting into the network.
|
||||
*/
|
||||
class Manager : public beast::PropertyStream::Source
|
||||
{
|
||||
protected:
|
||||
Manager() noexcept;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Destroy the object.
|
||||
* Any pending source fetch operations are aborted.
|
||||
* There may be some listener calls made before the
|
||||
* destructor returns.
|
||||
*/
|
||||
~Manager() override = default;
|
||||
|
||||
/**
|
||||
* Set the configuration for the manager.
|
||||
* The new settings will be applied asynchronously.
|
||||
* Thread safety:
|
||||
* Can be called from any threads at any time.
|
||||
*/
|
||||
virtual void
|
||||
setConfig(Config const& config) = 0;
|
||||
|
||||
/**
|
||||
* Transition to the started state, synchronously.
|
||||
*/
|
||||
virtual void
|
||||
start() = 0;
|
||||
|
||||
/**
|
||||
* Transition to the stopped state, synchronously.
|
||||
*/
|
||||
virtual void
|
||||
stop() = 0;
|
||||
|
||||
/**
|
||||
* Returns the configuration for the manager.
|
||||
*/
|
||||
virtual Config
|
||||
config() = 0;
|
||||
|
||||
/**
|
||||
* Add a peer that should always be connected.
|
||||
* This is useful for maintaining a private cluster of peers.
|
||||
* The string is the name as specified in the configuration
|
||||
* file, along with the set of corresponding IP addresses.
|
||||
*/
|
||||
virtual void
|
||||
addFixedPeer(std::string_view name, std::vector<beast::IP::Endpoint> const& addresses) = 0;
|
||||
|
||||
/**
|
||||
* Add a set of strings as fallback IP::Endpoint sources.
|
||||
* @param name A label used for diagnostics.
|
||||
*/
|
||||
virtual void
|
||||
addFallbackStrings(std::string const& name, std::vector<std::string> const& strings) = 0;
|
||||
|
||||
/**
|
||||
* Add a URL as a fallback location to obtain IP::Endpoint sources.
|
||||
* @param name A label used for diagnostics.
|
||||
*/
|
||||
/* VFALCO NOTE Unimplemented
|
||||
virtual void addFallbackURL (std::string const& name,
|
||||
std::string const& url) = 0;
|
||||
*/
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Create a new inbound slot with the specified remote endpoint.
|
||||
* If nullptr is returned, then the slot could not be assigned.
|
||||
* Usually this is because of a detected self-connection.
|
||||
*/
|
||||
virtual std::pair<std::shared_ptr<Slot>, Result>
|
||||
newInboundSlot(
|
||||
beast::IP::Endpoint const& localEndpoint,
|
||||
beast::IP::Endpoint const& remoteEndpoint) = 0;
|
||||
|
||||
/**
|
||||
* Create a new outbound slot with the specified remote endpoint.
|
||||
* If nullptr is returned, then the slot could not be assigned.
|
||||
* Usually this is because of a duplicate connection.
|
||||
*/
|
||||
virtual std::pair<std::shared_ptr<Slot>, Result>
|
||||
newOutboundSlot(beast::IP::Endpoint const& remoteEndpoint) = 0;
|
||||
|
||||
/**
|
||||
* Called when mtENDPOINTS is received.
|
||||
*/
|
||||
virtual void
|
||||
onEndpoints(std::shared_ptr<Slot> const& slot, Endpoints const& endpoints) = 0;
|
||||
|
||||
/**
|
||||
* Called when the slot is closed.
|
||||
* This always happens when the socket is closed, unless the socket
|
||||
* was canceled.
|
||||
*/
|
||||
virtual void
|
||||
onClosed(std::shared_ptr<Slot> const& slot) = 0;
|
||||
|
||||
/**
|
||||
* Called when an outbound connection is deemed to have failed
|
||||
*/
|
||||
virtual void
|
||||
onFailure(std::shared_ptr<Slot> const& slot) = 0;
|
||||
|
||||
/**
|
||||
* Called when we received redirect IPs from a busy peer.
|
||||
*/
|
||||
virtual void
|
||||
onRedirects(
|
||||
boost::asio::ip::tcp::endpoint const& remoteAddress,
|
||||
std::vector<boost::asio::ip::tcp::endpoint> const& eps) = 0;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Called when an outbound connection attempt succeeds.
|
||||
* The local endpoint must be valid. If the caller receives an error
|
||||
* when retrieving the local endpoint from the socket, it should
|
||||
* proceed as if the connection attempt failed by calling on_closed
|
||||
* instead of on_connected.
|
||||
* @return `true` if the connection should be kept
|
||||
*/
|
||||
virtual bool
|
||||
onConnected(std::shared_ptr<Slot> const& slot, beast::IP::Endpoint const& localEndpoint) = 0;
|
||||
|
||||
/**
|
||||
* Request an active slot type.
|
||||
*/
|
||||
virtual Result
|
||||
activate(std::shared_ptr<Slot> const& slot, PublicKey const& key, bool reserved) = 0;
|
||||
|
||||
/**
|
||||
* Returns a set of endpoints suitable for redirection.
|
||||
*/
|
||||
virtual std::vector<Endpoint>
|
||||
redirect(std::shared_ptr<Slot> const& slot) = 0;
|
||||
|
||||
/**
|
||||
* Return a set of addresses we should connect to.
|
||||
*/
|
||||
virtual std::vector<beast::IP::Endpoint>
|
||||
autoconnect() = 0;
|
||||
|
||||
virtual std::vector<std::pair<std::shared_ptr<Slot>, std::vector<Endpoint>>>
|
||||
buildEndpointsForPeers() = 0;
|
||||
|
||||
/**
|
||||
* Perform periodic activity.
|
||||
* This should be called once per second.
|
||||
*/
|
||||
virtual void
|
||||
oncePerSecond() = 0;
|
||||
};
|
||||
|
||||
} // namespace xrpl::PeerFinder
|
||||
46
include/xrpl/peerfinder/Types.h
Normal file
46
include/xrpl/peerfinder/Types.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/clock/abstract_clock.h>
|
||||
#include <xrpl/beast/net/IPEndpoint.h>
|
||||
#include <xrpl/peerfinder/detail/Tuning.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
namespace xrpl::PeerFinder {
|
||||
|
||||
using clock_type = beast::AbstractClock<std::chrono::steady_clock>;
|
||||
|
||||
/**
|
||||
* Represents a set of addresses.
|
||||
*/
|
||||
using IPAddresses = std::vector<beast::IP::Endpoint>;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Describes a connectable peer address along with some metadata.
|
||||
*/
|
||||
struct Endpoint
|
||||
{
|
||||
Endpoint() = default;
|
||||
|
||||
Endpoint(beast::IP::Endpoint ep, std::uint32_t hops);
|
||||
|
||||
std::uint32_t hops = 0;
|
||||
beast::IP::Endpoint address;
|
||||
};
|
||||
|
||||
inline bool
|
||||
operator<(Endpoint const& lhs, Endpoint const& rhs)
|
||||
{
|
||||
return lhs.address < rhs.address;
|
||||
}
|
||||
|
||||
/**
|
||||
* A set of Endpoint used for connecting.
|
||||
*/
|
||||
using Endpoints = std::vector<Endpoint>;
|
||||
|
||||
} // namespace xrpl::PeerFinder
|
||||
@@ -1,11 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpld/peerfinder/PeerfinderManager.h>
|
||||
#include <xrpld/peerfinder/detail/Store.h>
|
||||
|
||||
#include <xrpl/beast/net/IPEndpoint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/beast/utility/PropertyStream.h>
|
||||
#include <xrpl/peerfinder/Types.h>
|
||||
#include <xrpl/peerfinder/detail/Store.h>
|
||||
#include <xrpl/peerfinder/detail/Tuning.h>
|
||||
|
||||
#include <boost/bimap.hpp>
|
||||
#include <boost/bimap/multiset_of.hpp>
|
||||
@@ -1,11 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpld/peerfinder/PeerfinderManager.h>
|
||||
#include <xrpld/peerfinder/Slot.h>
|
||||
#include <xrpld/peerfinder/detail/Tuning.h>
|
||||
|
||||
#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>
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpld/peerfinder/PeerfinderManager.h>
|
||||
#include <xrpld/peerfinder/detail/Tuning.h>
|
||||
#include <xrpl/peerfinder/Types.h>
|
||||
#include <xrpl/peerfinder/detail/Tuning.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
@@ -1,12 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpld/peerfinder/PeerfinderManager.h>
|
||||
#include <xrpld/peerfinder/detail/SlotImp.h>
|
||||
#include <xrpld/peerfinder/detail/Tuning.h>
|
||||
|
||||
#include <xrpl/beast/container/aged_set.h>
|
||||
#include <xrpl/beast/net/IPAddress.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/peerfinder/Types.h>
|
||||
#include <xrpl/peerfinder/detail/SlotImp.h>
|
||||
#include <xrpl/peerfinder/detail/Tuning.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
@@ -1,9 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpld/peerfinder/PeerfinderManager.h>
|
||||
#include <xrpld/peerfinder/detail/Tuning.h>
|
||||
#include <xrpld/peerfinder/detail/iosformat.h>
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/basics/random.h>
|
||||
#include <xrpl/beast/container/aged_map.h>
|
||||
@@ -12,6 +8,8 @@
|
||||
#include <xrpl/beast/utility/PropertyStream.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/beast/utility/maybe_const.h>
|
||||
#include <xrpl/peerfinder/Types.h>
|
||||
#include <xrpl/peerfinder/detail/Tuning.h>
|
||||
|
||||
#include <boost/intrusive/list.hpp>
|
||||
#include <boost/iterator/transform_iterator.hpp>
|
||||
@@ -22,6 +20,8 @@
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <iomanip>
|
||||
#include <ios>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
@@ -411,7 +411,7 @@ Livecache<Allocator>::expire()
|
||||
}
|
||||
if (n > 0)
|
||||
{
|
||||
JLOG(journal_.debug()) << beast::Leftw(18) << "Livecache expired " << n
|
||||
JLOG(journal_.debug()) << std::left << std::setw(18) << "Livecache expired " << n
|
||||
<< ((n > 1) ? " entries" : " entry");
|
||||
}
|
||||
}
|
||||
@@ -434,7 +434,7 @@ Livecache<Allocator>::insert(Endpoint const& ep)
|
||||
if (result.second)
|
||||
{
|
||||
hops.insert(e);
|
||||
JLOG(journal_.debug()) << beast::Leftw(18) << "Livecache insert " << ep.address
|
||||
JLOG(journal_.debug()) << std::left << std::setw(18) << "Livecache insert " << ep.address
|
||||
<< " at hops " << ep.hops;
|
||||
return;
|
||||
}
|
||||
@@ -442,7 +442,7 @@ Livecache<Allocator>::insert(Endpoint const& ep)
|
||||
{
|
||||
// Drop duplicates at higher hops
|
||||
std::size_t const excess(ep.hops - e.endpoint.hops);
|
||||
JLOG(journal_.trace()) << beast::Leftw(18) << "Livecache drop " << ep.address
|
||||
JLOG(journal_.trace()) << std::left << std::setw(18) << "Livecache drop " << ep.address
|
||||
<< " at hops +" << excess;
|
||||
return;
|
||||
}
|
||||
@@ -453,12 +453,12 @@ Livecache<Allocator>::insert(Endpoint const& ep)
|
||||
if (ep.hops < e.endpoint.hops)
|
||||
{
|
||||
hops.reinsert(e, ep.hops);
|
||||
JLOG(journal_.debug()) << beast::Leftw(18) << "Livecache update " << ep.address
|
||||
JLOG(journal_.debug()) << std::left << std::setw(18) << "Livecache update " << ep.address
|
||||
<< " at hops " << ep.hops;
|
||||
}
|
||||
else
|
||||
{
|
||||
JLOG(journal_.trace()) << beast::Leftw(18) << "Livecache refresh " << ep.address
|
||||
JLOG(journal_.trace()) << std::left << std::setw(18) << "Livecache refresh " << ep.address
|
||||
<< " at hops " << ep.hops;
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpld/peerfinder/PeerfinderManager.h>
|
||||
#include <xrpld/peerfinder/Slot.h>
|
||||
#include <xrpld/peerfinder/detail/Bootcache.h>
|
||||
#include <xrpld/peerfinder/detail/Counts.h>
|
||||
#include <xrpld/peerfinder/detail/Fixed.h>
|
||||
#include <xrpld/peerfinder/detail/Handouts.h>
|
||||
#include <xrpld/peerfinder/detail/Livecache.h>
|
||||
#include <xrpld/peerfinder/detail/SlotImp.h>
|
||||
#include <xrpld/peerfinder/detail/Source.h>
|
||||
#include <xrpld/peerfinder/detail/Store.h>
|
||||
#include <xrpld/peerfinder/detail/iosformat.h>
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/basics/contract.h>
|
||||
#include <xrpl/basics/random.h>
|
||||
@@ -22,18 +10,34 @@
|
||||
#include <xrpl/beast/utility/PropertyStream.h>
|
||||
#include <xrpl/beast/utility/WrappedSink.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/peerfinder/Config.h>
|
||||
#include <xrpl/peerfinder/Slot.h>
|
||||
#include <xrpl/peerfinder/Types.h>
|
||||
#include <xrpl/peerfinder/detail/Bootcache.h>
|
||||
#include <xrpl/peerfinder/detail/Counts.h>
|
||||
#include <xrpl/peerfinder/detail/Fixed.h>
|
||||
#include <xrpl/peerfinder/detail/Handouts.h>
|
||||
#include <xrpl/peerfinder/detail/Livecache.h>
|
||||
#include <xrpl/peerfinder/detail/SlotImp.h>
|
||||
#include <xrpl/peerfinder/detail/Source.h>
|
||||
#include <xrpl/peerfinder/detail/Store.h>
|
||||
#include <xrpl/protocol/PublicKey.h>
|
||||
|
||||
#include <boost/asio/error.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <iomanip>
|
||||
#include <ios>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
@@ -197,8 +201,8 @@ public:
|
||||
|
||||
if (result.second)
|
||||
{
|
||||
JLOG(journal.debug())
|
||||
<< beast::Leftw(18) << "Logic add fixed '" << name << "' at " << remoteAddress;
|
||||
JLOG(journal.debug()) << std::left << std::setw(18) << "Logic add fixed '" << name
|
||||
<< "' at " << remoteAddress;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -221,7 +225,7 @@ public:
|
||||
if (iter == slots.end())
|
||||
{
|
||||
// The slot disconnected before we finished the check
|
||||
JLOG(journal.debug()) << beast::Leftw(18) << "Logic tested " << checkedAddress
|
||||
JLOG(journal.debug()) << std::left << std::setw(18) << "Logic tested " << checkedAddress
|
||||
<< " but the connection was closed";
|
||||
return;
|
||||
}
|
||||
@@ -255,7 +259,7 @@ public:
|
||||
beast::IP::Endpoint const& localEndpoint,
|
||||
beast::IP::Endpoint const& remoteEndpoint)
|
||||
{
|
||||
JLOG(journal.debug()) << beast::Leftw(18) << "Logic accept" << remoteEndpoint
|
||||
JLOG(journal.debug()) << std::left << std::setw(18) << "Logic accept" << remoteEndpoint
|
||||
<< " on local " << localEndpoint;
|
||||
|
||||
std::scoped_lock const _(lock);
|
||||
@@ -266,7 +270,7 @@ public:
|
||||
auto const count = connectedAddresses.count(remoteEndpoint.address());
|
||||
if (count + 1 > config_.ipLimit)
|
||||
{
|
||||
JLOG(journal.debug()) << beast::Leftw(18) << "Logic dropping inbound "
|
||||
JLOG(journal.debug()) << std::left << std::setw(18) << "Logic dropping inbound "
|
||||
<< remoteEndpoint << " because of ip limits.";
|
||||
return {SlotImp::ptr(), Result::IpLimitExceeded};
|
||||
}
|
||||
@@ -275,8 +279,8 @@ public:
|
||||
// Check for duplicate connection
|
||||
if (slots.contains(remoteEndpoint))
|
||||
{
|
||||
JLOG(journal.debug()) << beast::Leftw(18) << "Logic dropping " << remoteEndpoint
|
||||
<< " as duplicate incoming";
|
||||
JLOG(journal.debug()) << std::left << std::setw(18) << "Logic dropping "
|
||||
<< remoteEndpoint << " as duplicate incoming";
|
||||
return {SlotImp::ptr(), Result::DuplicatePeer};
|
||||
}
|
||||
|
||||
@@ -304,15 +308,15 @@ public:
|
||||
std::pair<SlotImp::ptr, Result>
|
||||
newOutboundSlot(beast::IP::Endpoint const& remoteEndpoint)
|
||||
{
|
||||
JLOG(journal.debug()) << beast::Leftw(18) << "Logic connect " << remoteEndpoint;
|
||||
JLOG(journal.debug()) << std::left << std::setw(18) << "Logic connect " << remoteEndpoint;
|
||||
|
||||
std::scoped_lock const _(lock);
|
||||
|
||||
// Check for duplicate connection
|
||||
if (slots.contains(remoteEndpoint))
|
||||
{
|
||||
JLOG(journal.debug()) << beast::Leftw(18) << "Logic dropping " << remoteEndpoint
|
||||
<< " as duplicate connect";
|
||||
JLOG(journal.debug()) << std::left << std::setw(18) << "Logic dropping "
|
||||
<< remoteEndpoint << " as duplicate connect";
|
||||
return {SlotImp::ptr(), Result::DuplicatePeer};
|
||||
}
|
||||
|
||||
@@ -506,15 +510,15 @@ public:
|
||||
|
||||
if (!h.list().empty())
|
||||
{
|
||||
JLOG(journal.debug())
|
||||
<< beast::Leftw(18) << "Logic connect " << h.list().size() << " fixed";
|
||||
JLOG(journal.debug()) << std::left << std::setw(18) << "Logic connect "
|
||||
<< h.list().size() << " fixed";
|
||||
return h.list();
|
||||
}
|
||||
|
||||
if (counts_.attempts() > 0)
|
||||
{
|
||||
JLOG(journal.debug())
|
||||
<< beast::Leftw(18) << "Logic waiting on " << counts_.attempts() << " attempts";
|
||||
JLOG(journal.debug()) << std::left << std::setw(18) << "Logic waiting on "
|
||||
<< counts_.attempts() << " attempts";
|
||||
return none;
|
||||
}
|
||||
}
|
||||
@@ -535,14 +539,14 @@ public:
|
||||
if (!h.list().empty())
|
||||
{
|
||||
JLOG(journal.debug())
|
||||
<< beast::Leftw(18) << "Logic connect " << h.list().size() << " live "
|
||||
<< std::left << std::setw(18) << "Logic connect " << h.list().size() << " live "
|
||||
<< ((h.list().size() > 1) ? "endpoints" : "endpoint");
|
||||
return h.list();
|
||||
}
|
||||
if (counts_.attempts() > 0)
|
||||
{
|
||||
JLOG(journal.debug())
|
||||
<< beast::Leftw(18) << "Logic waiting on " << counts_.attempts() << " attempts";
|
||||
JLOG(journal.debug()) << std::left << std::setw(18) << "Logic waiting on "
|
||||
<< counts_.attempts() << " attempts";
|
||||
return none;
|
||||
}
|
||||
}
|
||||
@@ -568,8 +572,9 @@ public:
|
||||
|
||||
if (!h.list().empty())
|
||||
{
|
||||
JLOG(journal.debug()) << beast::Leftw(18) << "Logic connect " << h.list().size()
|
||||
<< " boot " << ((h.list().size() > 1) ? "addresses" : "address");
|
||||
JLOG(journal.debug()) << std::left << std::setw(18) << "Logic connect "
|
||||
<< h.list().size() << " boot "
|
||||
<< ((h.list().size() > 1) ? "addresses" : "address");
|
||||
return h.list();
|
||||
}
|
||||
|
||||
@@ -689,8 +694,8 @@ public:
|
||||
// Enforce hop limit
|
||||
if (ep.hops > Tuning::kMaxHops)
|
||||
{
|
||||
JLOG(journal.debug()) << beast::Leftw(18) << "Endpoints drop " << ep.address
|
||||
<< " for excess hops " << ep.hops;
|
||||
JLOG(journal.debug()) << std::left << std::setw(18) << "Endpoints drop "
|
||||
<< ep.address << " for excess hops " << ep.hops;
|
||||
iter = list.erase(iter);
|
||||
continue;
|
||||
}
|
||||
@@ -706,18 +711,18 @@ public:
|
||||
}
|
||||
else
|
||||
{
|
||||
JLOG(journal.debug())
|
||||
<< beast::Leftw(18) << "Endpoints drop " << ep.address << " for extra self";
|
||||
JLOG(journal.debug()) << std::left << std::setw(18) << "Endpoints drop "
|
||||
<< ep.address << " for extra self";
|
||||
iter = list.erase(iter);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Discard invalid addresses
|
||||
if (config_.verifyEndpoints && !isValidAddress(ep.address))
|
||||
if (!isValidAddress(ep.address))
|
||||
{
|
||||
JLOG(journal.debug())
|
||||
<< beast::Leftw(18) << "Endpoints drop " << ep.address << " as invalid";
|
||||
JLOG(journal.debug()) << std::left << std::setw(18) << "Endpoints drop "
|
||||
<< ep.address << " as invalid";
|
||||
iter = list.erase(iter);
|
||||
continue;
|
||||
}
|
||||
@@ -727,8 +732,8 @@ public:
|
||||
return ep.address == other.address;
|
||||
}))
|
||||
{
|
||||
JLOG(journal.debug())
|
||||
<< beast::Leftw(18) << "Endpoints drop " << ep.address << " as duplicate";
|
||||
JLOG(journal.debug()) << std::left << std::setw(18) << "Endpoints drop "
|
||||
<< ep.address << " as duplicate";
|
||||
iter = list.erase(iter);
|
||||
continue;
|
||||
}
|
||||
@@ -1074,13 +1079,13 @@ public:
|
||||
if (!results.error)
|
||||
{
|
||||
int const count(addBootcacheAddresses(results.addresses));
|
||||
JLOG(journal.info()) << beast::Leftw(18) << "Logic added " << count << " new "
|
||||
JLOG(journal.info()) << std::left << std::setw(18) << "Logic added " << count << " new "
|
||||
<< ((count == 1) ? "address" : "addresses") << " from "
|
||||
<< source->name();
|
||||
}
|
||||
else
|
||||
{
|
||||
JLOG(journal.error()) << beast::Leftw(18) << "Logic failed "
|
||||
JLOG(journal.error()) << std::left << std::setw(18) << "Logic failed "
|
||||
<< "'" << source->name() << "' fetch, "
|
||||
<< results.error.message();
|
||||
}
|
||||
@@ -1098,8 +1103,6 @@ public:
|
||||
{
|
||||
if (isUnspecified(address))
|
||||
return false;
|
||||
if (isLoopback(address))
|
||||
return false;
|
||||
if (!isPublic(address))
|
||||
return false;
|
||||
if (address.port() == 0)
|
||||
@@ -1221,8 +1224,8 @@ Logic<Checker>::onRedirects(
|
||||
bootcache.insert(beast::IPAddressConversion::fromAsio(*first));
|
||||
if (n > 0)
|
||||
{
|
||||
JLOG(journal.trace()) << beast::Leftw(18) << "Logic add " << n << " redirect IPs from "
|
||||
<< remoteAddress;
|
||||
JLOG(journal.trace()) << std::left << std::setw(18) << "Logic add " << n
|
||||
<< " redirect IPs from " << remoteAddress;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpld/peerfinder/PeerfinderManager.h>
|
||||
#include <xrpld/peerfinder/Slot.h>
|
||||
|
||||
#include <xrpl/beast/container/aged_unordered_map.h>
|
||||
#include <xrpl/beast/net/IPEndpoint.h>
|
||||
#include <xrpl/peerfinder/Slot.h>
|
||||
#include <xrpl/peerfinder/Types.h>
|
||||
#include <xrpl/protocol/PublicKey.h>
|
||||
|
||||
#include <atomic>
|
||||
@@ -172,7 +171,7 @@ private:
|
||||
std::optional<beast::IP::Endpoint> localEndpoint_;
|
||||
std::optional<PublicKey> publicKey_;
|
||||
|
||||
static constexpr std::int32_t kUnknownPort = -1;
|
||||
static std::int32_t constexpr kUnknownPort = -1;
|
||||
std::atomic<std::int32_t> listeningPort_;
|
||||
|
||||
public:
|
||||
@@ -1,8 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpld/peerfinder/PeerfinderManager.h>
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/peerfinder/Types.h>
|
||||
|
||||
#include <boost/system/error_code.hpp>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpld/peerfinder/detail/Source.h>
|
||||
#include <xrpl/peerfinder/detail/Source.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
36
include/xrpl/peerfinder/make_Manager.h
Normal file
36
include/xrpl/peerfinder/make_Manager.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/insight/Collector.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/peerfinder/PeerfinderManager.h>
|
||||
#include <xrpl/peerfinder/Types.h>
|
||||
#include <xrpl/peerfinder/detail/Store.h>
|
||||
|
||||
#include <boost/asio/io_context.hpp>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace xrpl::PeerFinder {
|
||||
|
||||
/**
|
||||
* @brief Create a new Manager.
|
||||
*
|
||||
* @param ioContext The io_context used to schedule asynchronous work.
|
||||
* @param clock The clock used for timekeeping.
|
||||
* @param journal The journal used for logging.
|
||||
* @param store The persistence backend for the bootstrap cache. The caller
|
||||
* retains ownership and must keep it alive (and opened) for the lifetime of
|
||||
* the returned Manager. This lets consumers supply their own Store
|
||||
* implementation (e.g. the SQLite-backed StoreSqdb in xrpld).
|
||||
* @param collector The collector used to report metrics.
|
||||
* @return The newly created Manager.
|
||||
*/
|
||||
std::unique_ptr<Manager>
|
||||
makeManager(
|
||||
boost::asio::io_context& ioContext,
|
||||
clock_type& clock,
|
||||
beast::Journal journal,
|
||||
Store& store,
|
||||
beast::insight::Collector::ptr const& collector);
|
||||
|
||||
} // namespace xrpl::PeerFinder
|
||||
@@ -1,19 +1,19 @@
|
||||
#include <xrpld/peerfinder/detail/Bootcache.h>
|
||||
|
||||
#include <xrpld/peerfinder/PeerfinderManager.h>
|
||||
#include <xrpld/peerfinder/detail/Store.h>
|
||||
#include <xrpld/peerfinder/detail/Tuning.h>
|
||||
#include <xrpld/peerfinder/detail/iosformat.h>
|
||||
#include <xrpl/peerfinder/detail/Bootcache.h>
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/beast/net/IPEndpoint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/beast/utility/PropertyStream.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/peerfinder/Types.h>
|
||||
#include <xrpl/peerfinder/detail/Store.h>
|
||||
#include <xrpl/peerfinder/detail/Tuning.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <iomanip>
|
||||
#include <ios>
|
||||
#include <vector>
|
||||
|
||||
namespace xrpl::PeerFinder {
|
||||
@@ -82,13 +82,14 @@ Bootcache::load()
|
||||
auto const result(this->map_.insert(value_type(endpoint, valence)));
|
||||
if (!result.second)
|
||||
{
|
||||
JLOG(this->journal_.error()) << beast::Leftw(18) << "Bootcache discard " << endpoint;
|
||||
JLOG(this->journal_.error())
|
||||
<< std::left << std::setw(18) << "Bootcache discard " << endpoint;
|
||||
}
|
||||
}));
|
||||
|
||||
if (n > 0)
|
||||
{
|
||||
JLOG(journal_.info()) << beast::Leftw(18) << "Bootcache loaded " << n
|
||||
JLOG(journal_.info()) << std::left << std::setw(18) << "Bootcache loaded " << n
|
||||
<< ((n > 1) ? " addresses" : " address");
|
||||
prune();
|
||||
}
|
||||
@@ -100,7 +101,7 @@ Bootcache::insert(beast::IP::Endpoint const& endpoint)
|
||||
auto const result(map_.insert(value_type(endpoint, 0)));
|
||||
if (result.second)
|
||||
{
|
||||
JLOG(journal_.trace()) << beast::Leftw(18) << "Bootcache insert " << endpoint;
|
||||
JLOG(journal_.trace()) << std::left << std::setw(18) << "Bootcache insert " << endpoint;
|
||||
prune();
|
||||
flagForUpdate();
|
||||
}
|
||||
@@ -121,7 +122,7 @@ Bootcache::insertStatic(beast::IP::Endpoint const& endpoint)
|
||||
|
||||
if (result.second)
|
||||
{
|
||||
JLOG(journal_.trace()) << beast::Leftw(18) << "Bootcache insert " << endpoint;
|
||||
JLOG(journal_.trace()) << std::left << std::setw(18) << "Bootcache insert " << endpoint;
|
||||
prune();
|
||||
flagForUpdate();
|
||||
}
|
||||
@@ -146,8 +147,9 @@ Bootcache::onSuccess(beast::IP::Endpoint const& endpoint)
|
||||
XRPL_ASSERT(result.second, "xrpl::PeerFinder::Bootcache::onSuccess : endpoint inserted");
|
||||
}
|
||||
Entry const& entry(result.first->right);
|
||||
JLOG(journal_.info()) << beast::Leftw(18) << "Bootcache connect " << endpoint << " with "
|
||||
<< entry.valence() << ((entry.valence() > 1) ? " successes" : " success");
|
||||
JLOG(journal_.info()) << std::left << std::setw(18) << "Bootcache connect " << endpoint
|
||||
<< " with " << entry.valence()
|
||||
<< ((entry.valence() > 1) ? " successes" : " success");
|
||||
flagForUpdate();
|
||||
}
|
||||
|
||||
@@ -170,8 +172,8 @@ Bootcache::onFailure(beast::IP::Endpoint const& endpoint)
|
||||
}
|
||||
Entry const& entry(result.first->right);
|
||||
auto const n(std::abs(entry.valence()));
|
||||
JLOG(journal_.debug()) << beast::Leftw(18) << "Bootcache failed " << endpoint << " with " << n
|
||||
<< ((n > 1) ? " attempts" : " attempt");
|
||||
JLOG(journal_.debug()) << std::left << std::setw(18) << "Bootcache failed " << endpoint
|
||||
<< " with " << n << ((n > 1) ? " attempts" : " attempt");
|
||||
flagForUpdate();
|
||||
}
|
||||
|
||||
@@ -209,17 +211,19 @@ Bootcache::prune()
|
||||
// Work backwards because bimap doesn't handle
|
||||
// erasing using a reverse iterator very well.
|
||||
//
|
||||
for (auto iter(map_.right.end()); count-- > 0 && iter != map_.right.begin(); ++pruned)
|
||||
for (auto iter(map_.right.end()); count > 0 && iter != map_.right.begin(); ++pruned)
|
||||
{
|
||||
--count;
|
||||
--iter;
|
||||
beast::IP::Endpoint const& endpoint(iter->get_left());
|
||||
Entry const& entry(iter->get_right());
|
||||
JLOG(journal_.trace()) << beast::Leftw(18) << "Bootcache pruned" << endpoint
|
||||
JLOG(journal_.trace()) << std::left << std::setw(18) << "Bootcache pruned" << endpoint
|
||||
<< " at valence " << entry.valence();
|
||||
iter = map_.right.erase(iter);
|
||||
}
|
||||
|
||||
JLOG(journal_.debug()) << beast::Leftw(18) << "Bootcache pruned " << pruned << " entries total";
|
||||
JLOG(journal_.debug()) << std::left << std::setw(18) << "Bootcache pruned " << pruned
|
||||
<< " entries total";
|
||||
}
|
||||
|
||||
// Updates the Store with the current set of entries if needed.
|
||||
135
src/libxrpl/peerfinder/Config.cpp
Normal file
135
src/libxrpl/peerfinder/Config.cpp
Normal file
@@ -0,0 +1,135 @@
|
||||
#include <xrpl/peerfinder/Config.h>
|
||||
|
||||
#include <xrpl/beast/utility/PropertyStream.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace xrpl::PeerFinder {
|
||||
|
||||
std::size_t
|
||||
Config::calcOutPeers() const
|
||||
{
|
||||
return std::max(
|
||||
((maxPeers * Tuning::kOutPercent) + 50) / 100, std::size_t(Tuning::kMinOutCount));
|
||||
}
|
||||
|
||||
void
|
||||
Config::applyTuning()
|
||||
{
|
||||
if (ipLimit == 0)
|
||||
{
|
||||
// Unless a limit is explicitly set, we allow between
|
||||
// 2 and 5 connections from non RFC-1918 "private"
|
||||
// IP addresses.
|
||||
ipLimit = 2;
|
||||
|
||||
if (inPeers > Tuning::kDefaultMaxPeers)
|
||||
ipLimit += std::min(5, static_cast<int>(inPeers / Tuning::kDefaultMaxPeers));
|
||||
}
|
||||
|
||||
// We don't allow a single IP to consume all incoming slots,
|
||||
// unless we only have one incoming slot available.
|
||||
ipLimit = std::max(1, std::min(ipLimit, static_cast<int>(inPeers / 2)));
|
||||
}
|
||||
|
||||
void
|
||||
Config::onWrite(beast::PropertyStream::Map& map) const
|
||||
{
|
||||
map["max_peers"] = maxPeers;
|
||||
map["out_peers"] = outPeers;
|
||||
map["want_incoming"] = wantIncoming;
|
||||
map["auto_connect"] = autoConnect;
|
||||
map["port"] = listeningPort;
|
||||
map["features"] = features;
|
||||
map["ip_limit"] = ipLimit;
|
||||
map["verify_endpoints"] = verifyEndpoints;
|
||||
}
|
||||
|
||||
Config
|
||||
Config::makeConfig(
|
||||
bool peerPrivate,
|
||||
bool standalone,
|
||||
PeerLimitConfig const& limits,
|
||||
std::uint16_t port,
|
||||
bool validationPublicKey,
|
||||
int ipLimit,
|
||||
bool verifyEndpoints)
|
||||
{
|
||||
PeerFinder::Config config;
|
||||
|
||||
if (!limits.maxPeers)
|
||||
{
|
||||
if (limits.inPeers && !limits.outPeers)
|
||||
throw std::runtime_error("Both inbound and outbound peer limits must be configured");
|
||||
|
||||
if (limits.outPeers && !limits.inPeers)
|
||||
throw std::runtime_error("Both inbound and outbound peer limits must be configured");
|
||||
|
||||
if (limits.inPeers && *limits.inPeers > 1000)
|
||||
throw std::runtime_error("Inbound peer limit must be less than or equal to 1000");
|
||||
|
||||
if (limits.outPeers && (*limits.outPeers < 10 || *limits.outPeers > 1000))
|
||||
throw std::runtime_error("Outbound peer limit must be in the range 10-1000");
|
||||
}
|
||||
|
||||
config.peerPrivate = peerPrivate;
|
||||
|
||||
// Servers with peer privacy don't want to allow incoming connections
|
||||
config.wantIncoming = (!config.peerPrivate) && (port != 0);
|
||||
|
||||
if (limits.maxPeers || (!limits.inPeers && !limits.outPeers))
|
||||
{
|
||||
if (limits.maxPeers && *limits.maxPeers != 0)
|
||||
config.maxPeers = *limits.maxPeers;
|
||||
|
||||
config.maxPeers = std::max<std::size_t>(config.maxPeers, Tuning::kMinOutCount);
|
||||
config.outPeers = config.calcOutPeers();
|
||||
|
||||
// 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)
|
||||
config.outPeers = config.maxPeers;
|
||||
|
||||
// Calculate the largest number of inbound connections we could
|
||||
// take.
|
||||
if (config.maxPeers >= config.outPeers)
|
||||
{
|
||||
config.inPeers = config.maxPeers - config.outPeers;
|
||||
}
|
||||
else
|
||||
{
|
||||
config.inPeers = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
config.outPeers = *limits.outPeers;
|
||||
config.inPeers = *limits.inPeers;
|
||||
config.maxPeers = 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 = !standalone && !peerPrivate;
|
||||
config.listeningPort = port;
|
||||
config.features = "";
|
||||
config.ipLimit = ipLimit;
|
||||
config.verifyEndpoints = verifyEndpoints;
|
||||
|
||||
// Enforce business rules
|
||||
config.applyTuning();
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
} // namespace xrpl::PeerFinder
|
||||
@@ -1,7 +1,5 @@
|
||||
#include <xrpld/peerfinder/PeerfinderManager.h>
|
||||
#include <xrpld/peerfinder/detail/Tuning.h>
|
||||
|
||||
#include <xrpl/beast/net/IPEndpoint.h>
|
||||
#include <xrpl/peerfinder/Types.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
@@ -1,11 +1,4 @@
|
||||
#include <xrpld/peerfinder/PeerfinderManager.h>
|
||||
|
||||
#include <xrpld/peerfinder/Slot.h>
|
||||
#include <xrpld/peerfinder/detail/Checker.h>
|
||||
#include <xrpld/peerfinder/detail/Logic.h>
|
||||
#include <xrpld/peerfinder/detail/SlotImp.h>
|
||||
#include <xrpld/peerfinder/detail/SourceStrings.h>
|
||||
#include <xrpld/peerfinder/detail/StoreSqdb.h>
|
||||
#include <xrpl/peerfinder/PeerfinderManager.h>
|
||||
|
||||
#include <xrpl/beast/insight/Collector.h>
|
||||
#include <xrpl/beast/insight/Gauge.h>
|
||||
@@ -13,7 +6,15 @@
|
||||
#include <xrpl/beast/net/IPEndpoint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/beast/utility/PropertyStream.h>
|
||||
#include <xrpl/config/BasicConfig.h>
|
||||
#include <xrpl/peerfinder/Config.h>
|
||||
#include <xrpl/peerfinder/Slot.h>
|
||||
#include <xrpl/peerfinder/Types.h>
|
||||
#include <xrpl/peerfinder/detail/Checker.h>
|
||||
#include <xrpl/peerfinder/detail/Logic.h>
|
||||
#include <xrpl/peerfinder/detail/SlotImp.h>
|
||||
#include <xrpl/peerfinder/detail/SourceStrings.h>
|
||||
#include <xrpl/peerfinder/detail/Store.h>
|
||||
#include <xrpl/peerfinder/make_Manager.h>
|
||||
#include <xrpl/protocol/PublicKey.h>
|
||||
|
||||
#include <boost/asio/executor_work_guard.hpp>
|
||||
@@ -38,10 +39,9 @@ public:
|
||||
std::optional<boost::asio::executor_work_guard<boost::asio::io_context::executor_type>> work_;
|
||||
clock_type& clock_;
|
||||
beast::Journal journal_;
|
||||
StoreSqdb store_;
|
||||
Store& store_;
|
||||
Checker<boost::asio::ip::tcp> checker_;
|
||||
Logic<decltype(checker_)> logic_;
|
||||
BasicConfig const& config_;
|
||||
// NOLINTEND(readability-identifier-naming)
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
@@ -50,16 +50,15 @@ public:
|
||||
boost::asio::io_context& ioContext,
|
||||
clock_type& clock,
|
||||
beast::Journal journal,
|
||||
BasicConfig const& config,
|
||||
Store& store,
|
||||
beast::insight::Collector::ptr const& collector)
|
||||
: io_context_(ioContext)
|
||||
, work_(std::in_place, boost::asio::make_work_guard(io_context_))
|
||||
, clock_(clock)
|
||||
, journal_(journal)
|
||||
, store_(journal)
|
||||
, store_(store)
|
||||
, checker_(io_context_)
|
||||
, logic_(clock, store_, checker_, journal)
|
||||
, config_(config)
|
||||
, stats_([this] { collectMetrics(); }, collector)
|
||||
{
|
||||
}
|
||||
@@ -206,7 +205,6 @@ public:
|
||||
void
|
||||
start() override
|
||||
{
|
||||
store_.open(config_);
|
||||
logic_.load();
|
||||
}
|
||||
|
||||
@@ -261,10 +259,10 @@ makeManager(
|
||||
boost::asio::io_context& ioContext,
|
||||
clock_type& clock,
|
||||
beast::Journal journal,
|
||||
BasicConfig const& config,
|
||||
Store& store,
|
||||
beast::insight::Collector::ptr const& collector)
|
||||
{
|
||||
return std::make_unique<ManagerImp>(ioContext, clock, journal, config, collector);
|
||||
return std::make_unique<ManagerImp>(ioContext, clock, journal, store, collector);
|
||||
}
|
||||
|
||||
} // namespace xrpl::PeerFinder
|
||||
@@ -1,12 +1,10 @@
|
||||
#include <xrpld/peerfinder/detail/SlotImp.h>
|
||||
#include <xrpl/peerfinder/detail/SlotImp.h>
|
||||
|
||||
#include <xrpld/peerfinder/PeerfinderManager.h>
|
||||
#include <xrpld/peerfinder/Slot.h>
|
||||
#include <xrpld/peerfinder/detail/Tuning.h>
|
||||
|
||||
#include <xrpl/beast/container/detail/aged_unordered_container.h>
|
||||
#include <xrpl/beast/net/IPEndpoint.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/peerfinder/Slot.h>
|
||||
#include <xrpl/peerfinder/Types.h>
|
||||
#include <xrpl/peerfinder/detail/Tuning.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
@@ -1,9 +1,8 @@
|
||||
#include <xrpld/peerfinder/detail/SourceStrings.h>
|
||||
|
||||
#include <xrpld/peerfinder/detail/Source.h>
|
||||
#include <xrpl/peerfinder/detail/SourceStrings.h>
|
||||
|
||||
#include <xrpl/beast/net/IPEndpoint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/peerfinder/detail/Source.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
@@ -8,7 +8,6 @@
|
||||
#include <xrpld/overlay/detail/PeerImp.h>
|
||||
#include <xrpld/overlay/detail/ProtocolVersion.h>
|
||||
#include <xrpld/overlay/detail/Tuning.h>
|
||||
#include <xrpld/peerfinder/Slot.h>
|
||||
|
||||
#include <xrpl/basics/Blob.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
@@ -16,6 +15,7 @@
|
||||
#include <xrpl/beast/net/IPEndpoint.h>
|
||||
#include <xrpl/beast/unit_test/suite.h>
|
||||
#include <xrpl/nodestore/NodeObject.h>
|
||||
#include <xrpl/peerfinder/Slot.h>
|
||||
#include <xrpl/protocol/KeyType.h>
|
||||
#include <xrpl/protocol/PublicKey.h>
|
||||
#include <xrpl/protocol/SecretKey.h>
|
||||
|
||||
@@ -9,12 +9,12 @@
|
||||
#include <xrpld/overlay/detail/OverlayImpl.h>
|
||||
#include <xrpld/overlay/detail/PeerImp.h>
|
||||
#include <xrpld/overlay/detail/ProtocolVersion.h>
|
||||
#include <xrpld/peerfinder/Slot.h>
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/basics/make_SSLContext.h>
|
||||
#include <xrpl/beast/net/IPEndpoint.h>
|
||||
#include <xrpl/beast/unit_test/suite.h>
|
||||
#include <xrpl/peerfinder/Slot.h>
|
||||
#include <xrpl/protocol/KeyType.h>
|
||||
#include <xrpl/protocol/PublicKey.h>
|
||||
#include <xrpl/protocol/SecretKey.h>
|
||||
|
||||
@@ -1,212 +0,0 @@
|
||||
#include <test/beast/IPEndpointCommon.h>
|
||||
#include <test/unit_test/SuiteJournal.h>
|
||||
|
||||
#include <xrpld/peerfinder/PeerfinderManager.h>
|
||||
#include <xrpld/peerfinder/detail/Livecache.h>
|
||||
#include <xrpld/peerfinder/detail/Tuning.h>
|
||||
|
||||
#include <xrpl/basics/chrono.h>
|
||||
#include <xrpl/basics/random.h>
|
||||
#include <xrpl/beast/net/IPEndpoint.h>
|
||||
#include <xrpl/beast/unit_test/suite.h>
|
||||
|
||||
#include <boost/algorithm/string/classification.hpp>
|
||||
#include <boost/algorithm/string/split.hpp>
|
||||
#include <boost/algorithm/string/trim.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace xrpl::PeerFinder {
|
||||
|
||||
bool
|
||||
operator==(Endpoint const& a, Endpoint const& b)
|
||||
{
|
||||
return (a.hops == b.hops && a.address == b.address);
|
||||
}
|
||||
|
||||
class Livecache_test : public beast::unit_test::Suite
|
||||
{
|
||||
TestStopwatch clock_;
|
||||
test::SuiteJournal journal_;
|
||||
|
||||
public:
|
||||
Livecache_test() : journal_("Livecache_test", *this)
|
||||
{
|
||||
}
|
||||
|
||||
// Add the address as an endpoint
|
||||
template <class C>
|
||||
void
|
||||
add(beast::IP::Endpoint ep, C& c, std::uint32_t hops = 0)
|
||||
{
|
||||
Endpoint const cep{ep, hops};
|
||||
c.insert(cep);
|
||||
}
|
||||
|
||||
void
|
||||
testBasicInsert()
|
||||
{
|
||||
testcase("Basic Insert");
|
||||
Livecache<> c(clock_, journal_);
|
||||
BEAST_EXPECT(c.empty());
|
||||
|
||||
for (auto i = 0; i < 10; ++i)
|
||||
add(beast::IP::randomEP(true), c);
|
||||
|
||||
BEAST_EXPECT(!c.empty());
|
||||
BEAST_EXPECT(c.size() == 10);
|
||||
|
||||
for (auto i = 0; i < 10; ++i)
|
||||
add(beast::IP::randomEP(false), c);
|
||||
|
||||
BEAST_EXPECT(!c.empty());
|
||||
BEAST_EXPECT(c.size() == 20);
|
||||
}
|
||||
|
||||
void
|
||||
testInsertUpdate()
|
||||
{
|
||||
testcase("Insert/Update");
|
||||
Livecache<> c(clock_, journal_);
|
||||
|
||||
auto ep1 = Endpoint{beast::IP::randomEP(), 2};
|
||||
c.insert(ep1);
|
||||
BEAST_EXPECT(c.size() == 1);
|
||||
// third position list will contain the entry
|
||||
BEAST_EXPECT((c.hops.begin() + 2)->begin()->hops == 2);
|
||||
|
||||
auto ep2 = Endpoint{ep1.address, 4};
|
||||
// this will not change the entry has higher hops
|
||||
c.insert(ep2);
|
||||
BEAST_EXPECT(c.size() == 1);
|
||||
// still in third position list
|
||||
BEAST_EXPECT((c.hops.begin() + 2)->begin()->hops == 2);
|
||||
|
||||
auto ep3 = Endpoint{ep1.address, 2};
|
||||
// this will not change the entry has the same hops as existing
|
||||
c.insert(ep3);
|
||||
BEAST_EXPECT(c.size() == 1);
|
||||
// still in third position list
|
||||
BEAST_EXPECT((c.hops.begin() + 2)->begin()->hops == 2);
|
||||
|
||||
auto ep4 = Endpoint{ep1.address, 1};
|
||||
c.insert(ep4);
|
||||
BEAST_EXPECT(c.size() == 1);
|
||||
// now at second position list
|
||||
BEAST_EXPECT((c.hops.begin() + 1)->begin()->hops == 1);
|
||||
}
|
||||
|
||||
void
|
||||
testExpire()
|
||||
{
|
||||
testcase("Expire");
|
||||
using namespace std::chrono_literals;
|
||||
Livecache<> c(clock_, journal_);
|
||||
|
||||
auto ep1 = Endpoint{beast::IP::randomEP(), 1};
|
||||
c.insert(ep1);
|
||||
BEAST_EXPECT(c.size() == 1);
|
||||
c.expire();
|
||||
BEAST_EXPECT(c.size() == 1);
|
||||
// verify that advancing to 1 sec before expiration
|
||||
// leaves our entry intact
|
||||
clock_.advance(Tuning::kLiveCacheSecondsToLive - 1s);
|
||||
c.expire();
|
||||
BEAST_EXPECT(c.size() == 1);
|
||||
// now advance to the point of expiration
|
||||
clock_.advance(1s);
|
||||
c.expire();
|
||||
BEAST_EXPECT(c.empty());
|
||||
}
|
||||
|
||||
void
|
||||
testHistogram()
|
||||
{
|
||||
testcase("Histogram");
|
||||
static constexpr auto kNumEps = 40;
|
||||
Livecache<> c(clock_, journal_);
|
||||
for (auto i = 0; i < kNumEps; ++i)
|
||||
add(beast::IP::randomEP(true), c, xrpl::randInt<std::uint32_t>());
|
||||
auto h = c.hops.histogram();
|
||||
if (!BEAST_EXPECT(!h.empty()))
|
||||
return;
|
||||
std::vector<std::string> v;
|
||||
boost::split(v, h, boost::algorithm::is_any_of(","));
|
||||
auto sum = 0;
|
||||
for (auto const& n : v)
|
||||
{
|
||||
auto val = boost::lexical_cast<int>(boost::trim_copy(n));
|
||||
sum += val;
|
||||
BEAST_EXPECT(val >= 0);
|
||||
}
|
||||
BEAST_EXPECT(sum == kNumEps);
|
||||
}
|
||||
|
||||
void
|
||||
testShuffle()
|
||||
{
|
||||
testcase("Shuffle");
|
||||
Livecache<> c(clock_, journal_);
|
||||
for (auto i = 0; i < 100; ++i)
|
||||
add(beast::IP::randomEP(true), c, xrpl::randInt(Tuning::kMaxHops + 1));
|
||||
|
||||
using at_hop = std::vector<xrpl::PeerFinder::Endpoint>;
|
||||
using all_hops = std::array<at_hop, 1 + Tuning::kMaxHops + 1>;
|
||||
|
||||
auto cmpEp = [](Endpoint const& a, Endpoint const& b) {
|
||||
return (b.hops < a.hops || (b.hops == a.hops && b.address < a.address));
|
||||
};
|
||||
all_hops before;
|
||||
all_hops beforeSorted;
|
||||
for (auto i = std::make_pair(0, c.hops.begin()); i.second != c.hops.end();
|
||||
++i.first, ++i.second)
|
||||
{
|
||||
std::ranges::copy(*i.second, std::back_inserter(before[i.first]));
|
||||
std::ranges::copy(*i.second, std::back_inserter(beforeSorted[i.first]));
|
||||
std::ranges::sort(beforeSorted[i.first], cmpEp);
|
||||
}
|
||||
|
||||
c.hops.shuffle();
|
||||
|
||||
all_hops after;
|
||||
all_hops afterSorted;
|
||||
for (auto i = std::make_pair(0, c.hops.begin()); i.second != c.hops.end();
|
||||
++i.first, ++i.second)
|
||||
{
|
||||
std::ranges::copy(*i.second, std::back_inserter(after[i.first]));
|
||||
std::ranges::copy(*i.second, std::back_inserter(afterSorted[i.first]));
|
||||
std::ranges::sort(afterSorted[i.first], cmpEp);
|
||||
}
|
||||
|
||||
// each hop bucket should contain the same items
|
||||
// before and after sort, albeit in different order
|
||||
bool allMatch = true;
|
||||
for (auto i = 0; i < before.size(); ++i)
|
||||
{
|
||||
BEAST_EXPECT(before[i].size() == after[i].size());
|
||||
allMatch = allMatch && (before[i] == after[i]);
|
||||
BEAST_EXPECT(beforeSorted[i] == afterSorted[i]);
|
||||
}
|
||||
BEAST_EXPECT(!allMatch);
|
||||
}
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
testBasicInsert();
|
||||
testInsertUpdate();
|
||||
testExpire();
|
||||
testHistogram();
|
||||
testShuffle();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(Livecache, peerfinder, xrpl);
|
||||
|
||||
} // namespace xrpl::PeerFinder
|
||||
@@ -1,789 +0,0 @@
|
||||
#include <test/unit_test/SuiteJournal.h>
|
||||
|
||||
#include <xrpld/core/Config.h>
|
||||
#include <xrpld/peerfinder/PeerfinderManager.h>
|
||||
#include <xrpld/peerfinder/detail/Counts.h>
|
||||
#include <xrpld/peerfinder/detail/Logic.h>
|
||||
#include <xrpld/peerfinder/detail/Store.h>
|
||||
|
||||
#include <xrpl/basics/chrono.h>
|
||||
#include <xrpl/beast/net/IPEndpoint.h>
|
||||
#include <xrpl/beast/unit_test/suite.h>
|
||||
#include <xrpl/protocol/KeyType.h>
|
||||
#include <xrpl/protocol/PublicKey.h>
|
||||
#include <xrpl/protocol/SecretKey.h>
|
||||
|
||||
#include <boost/system/detail/error_code.hpp>
|
||||
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace xrpl::PeerFinder {
|
||||
|
||||
class PeerFinder_test : public beast::unit_test::Suite
|
||||
{
|
||||
test::SuiteJournal journal_;
|
||||
|
||||
public:
|
||||
PeerFinder_test() : journal_("PeerFinder_test", *this)
|
||||
{
|
||||
}
|
||||
|
||||
struct TestStore : Store
|
||||
{
|
||||
std::size_t
|
||||
load(load_callback const& cb) override
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
save(std::vector<Entry> const&) override
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct TestChecker
|
||||
{
|
||||
void
|
||||
stop()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
wait()
|
||||
{
|
||||
}
|
||||
|
||||
template <class Handler>
|
||||
void
|
||||
asyncConnect(beast::IP::Endpoint const& ep, Handler&& handler)
|
||||
{
|
||||
// NOLINTNEXTLINE(misc-const-correctness)
|
||||
boost::system::error_code ec;
|
||||
handler(ec);
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
testBackoff1()
|
||||
{
|
||||
auto const seconds = 10000;
|
||||
testcase("backoff 1");
|
||||
TestStore store;
|
||||
TestChecker checker;
|
||||
TestStopwatch clock;
|
||||
Logic<TestChecker> logic(clock, store, checker, journal_);
|
||||
logic.addFixedPeer("test", beast::IP::Endpoint::fromString("65.0.0.1:5"));
|
||||
{
|
||||
Config c;
|
||||
c.autoConnect = false;
|
||||
c.listeningPort = 1024;
|
||||
logic.config(c);
|
||||
}
|
||||
std::size_t n = 0;
|
||||
for (std::size_t i = 0; i < seconds; ++i)
|
||||
{
|
||||
auto const list = logic.autoconnect();
|
||||
if (!list.empty())
|
||||
{
|
||||
BEAST_EXPECT(list.size() == 1);
|
||||
auto const [slot, _] = logic.newOutboundSlot(list.front());
|
||||
BEAST_EXPECT(
|
||||
logic.onConnected(slot, beast::IP::Endpoint::fromString("65.0.0.2:5")));
|
||||
logic.onClosed(slot);
|
||||
++n;
|
||||
}
|
||||
clock.advance(std::chrono::seconds(1));
|
||||
logic.oncePerSecond();
|
||||
}
|
||||
// Less than 20 attempts
|
||||
BEAST_EXPECT(n < 20);
|
||||
}
|
||||
|
||||
// with activate
|
||||
void
|
||||
testBackoff2()
|
||||
{
|
||||
auto const seconds = 10000;
|
||||
testcase("backoff 2");
|
||||
TestStore store;
|
||||
TestChecker checker;
|
||||
TestStopwatch clock;
|
||||
Logic<TestChecker> logic(clock, store, checker, journal_);
|
||||
logic.addFixedPeer("test", beast::IP::Endpoint::fromString("65.0.0.1:5"));
|
||||
{
|
||||
Config c;
|
||||
c.autoConnect = false;
|
||||
c.listeningPort = 1024;
|
||||
logic.config(c);
|
||||
}
|
||||
|
||||
PublicKey const pk(randomKeyPair(KeyType::Secp256k1).first);
|
||||
std::size_t n = 0;
|
||||
|
||||
for (std::size_t i = 0; i < seconds; ++i)
|
||||
{
|
||||
auto const list = logic.autoconnect();
|
||||
if (!list.empty())
|
||||
{
|
||||
BEAST_EXPECT(list.size() == 1);
|
||||
auto const [slot, _] = logic.newOutboundSlot(list.front());
|
||||
if (!BEAST_EXPECT(
|
||||
logic.onConnected(slot, beast::IP::Endpoint::fromString("65.0.0.2:5"))))
|
||||
return;
|
||||
if (!BEAST_EXPECT(logic.activate(slot, pk, false) == PeerFinder::Result::Success))
|
||||
return;
|
||||
logic.onClosed(slot);
|
||||
++n;
|
||||
}
|
||||
clock.advance(std::chrono::seconds(1));
|
||||
logic.oncePerSecond();
|
||||
}
|
||||
// No more often than once per minute
|
||||
BEAST_EXPECT(n <= (seconds + 59) / 60);
|
||||
}
|
||||
|
||||
// test accepting an incoming slot for an already existing outgoing slot
|
||||
void
|
||||
testDuplicateOutIn()
|
||||
{
|
||||
testcase("duplicate out/in");
|
||||
TestStore store;
|
||||
TestChecker checker;
|
||||
TestStopwatch clock;
|
||||
Logic<TestChecker> logic(clock, store, checker, journal_);
|
||||
{
|
||||
Config c;
|
||||
c.autoConnect = false;
|
||||
c.listeningPort = 1024;
|
||||
c.ipLimit = 2;
|
||||
logic.config(c);
|
||||
}
|
||||
|
||||
auto const remote = beast::IP::Endpoint::fromString("65.0.0.1:5");
|
||||
auto const [slot1, r] = logic.newOutboundSlot(remote);
|
||||
BEAST_EXPECT(slot1 != nullptr);
|
||||
BEAST_EXPECT(r == Result::Success);
|
||||
BEAST_EXPECT(logic.connectedAddresses.count(remote.address()) == 1);
|
||||
|
||||
auto const local = beast::IP::Endpoint::fromString("65.0.0.2:1024");
|
||||
auto const [slot2, r2] = logic.newInboundSlot(local, remote);
|
||||
BEAST_EXPECT(logic.connectedAddresses.count(remote.address()) == 1);
|
||||
BEAST_EXPECT(r2 == Result::DuplicatePeer);
|
||||
|
||||
if (!BEAST_EXPECT(slot2 == nullptr))
|
||||
logic.onClosed(slot2);
|
||||
|
||||
logic.onClosed(slot1);
|
||||
}
|
||||
|
||||
// test establishing outgoing slot for an already existing incoming slot
|
||||
void
|
||||
testDuplicateInOut()
|
||||
{
|
||||
testcase("duplicate in/out");
|
||||
TestStore store;
|
||||
TestChecker checker;
|
||||
TestStopwatch clock;
|
||||
Logic<TestChecker> logic(clock, store, checker, journal_);
|
||||
{
|
||||
Config c;
|
||||
c.autoConnect = false;
|
||||
c.listeningPort = 1024;
|
||||
c.ipLimit = 2;
|
||||
logic.config(c);
|
||||
}
|
||||
|
||||
auto const remote = beast::IP::Endpoint::fromString("65.0.0.1:5");
|
||||
auto const local = beast::IP::Endpoint::fromString("65.0.0.2:1024");
|
||||
|
||||
auto const [slot1, r] = logic.newInboundSlot(local, remote);
|
||||
BEAST_EXPECT(slot1 != nullptr);
|
||||
BEAST_EXPECT(r == Result::Success);
|
||||
BEAST_EXPECT(logic.connectedAddresses.count(remote.address()) == 1);
|
||||
|
||||
auto const [slot2, r2] = logic.newOutboundSlot(remote);
|
||||
BEAST_EXPECT(r2 == Result::DuplicatePeer);
|
||||
BEAST_EXPECT(logic.connectedAddresses.count(remote.address()) == 1);
|
||||
if (!BEAST_EXPECT(slot2 == nullptr))
|
||||
logic.onClosed(slot2);
|
||||
logic.onClosed(slot1);
|
||||
}
|
||||
|
||||
void
|
||||
testPeerLimitExceeded()
|
||||
{
|
||||
testcase("peer limit exceeded");
|
||||
TestStore store;
|
||||
TestChecker checker;
|
||||
TestStopwatch clock;
|
||||
Logic<TestChecker> logic(clock, store, checker, journal_);
|
||||
{
|
||||
Config c;
|
||||
c.autoConnect = false;
|
||||
c.listeningPort = 1024;
|
||||
c.ipLimit = 2;
|
||||
logic.config(c);
|
||||
}
|
||||
|
||||
auto const local = beast::IP::Endpoint::fromString("65.0.0.2:1024");
|
||||
auto const [slot, r] =
|
||||
logic.newInboundSlot(local, beast::IP::Endpoint::fromString("55.104.0.2:1025"));
|
||||
BEAST_EXPECT(slot != nullptr);
|
||||
BEAST_EXPECT(r == Result::Success);
|
||||
|
||||
auto const [slot1, r1] =
|
||||
logic.newInboundSlot(local, beast::IP::Endpoint::fromString("55.104.0.2:1026"));
|
||||
BEAST_EXPECT(slot1 != nullptr);
|
||||
BEAST_EXPECT(r1 == Result::Success);
|
||||
|
||||
auto const [slot2, r2] =
|
||||
logic.newInboundSlot(local, beast::IP::Endpoint::fromString("55.104.0.2:1027"));
|
||||
BEAST_EXPECT(r2 == Result::IpLimitExceeded);
|
||||
|
||||
if (!BEAST_EXPECT(slot2 == nullptr))
|
||||
logic.onClosed(slot2);
|
||||
logic.onClosed(slot1);
|
||||
logic.onClosed(slot);
|
||||
}
|
||||
|
||||
void
|
||||
testActivateDuplicatePeer()
|
||||
{
|
||||
testcase("test activate duplicate peer");
|
||||
TestStore store;
|
||||
TestChecker checker;
|
||||
TestStopwatch clock;
|
||||
Logic<TestChecker> logic(clock, store, checker, journal_);
|
||||
{
|
||||
Config c;
|
||||
c.autoConnect = false;
|
||||
c.listeningPort = 1024;
|
||||
c.ipLimit = 2;
|
||||
logic.config(c);
|
||||
}
|
||||
|
||||
auto const local = beast::IP::Endpoint::fromString("65.0.0.2:1024");
|
||||
|
||||
PublicKey const pk1(randomKeyPair(KeyType::Secp256k1).first);
|
||||
|
||||
auto const [slot, rSlot] =
|
||||
logic.newOutboundSlot(beast::IP::Endpoint::fromString("55.104.0.2:1025"));
|
||||
BEAST_EXPECT(slot != nullptr);
|
||||
BEAST_EXPECT(rSlot == Result::Success);
|
||||
|
||||
auto const [slot2, r2Slot] =
|
||||
logic.newOutboundSlot(beast::IP::Endpoint::fromString("55.104.0.2:1026"));
|
||||
BEAST_EXPECT(slot2 != nullptr);
|
||||
BEAST_EXPECT(r2Slot == Result::Success);
|
||||
|
||||
BEAST_EXPECT(logic.onConnected(slot, local));
|
||||
BEAST_EXPECT(logic.onConnected(slot2, local));
|
||||
|
||||
BEAST_EXPECT(logic.activate(slot, pk1, false) == Result::Success);
|
||||
|
||||
// activating a different slot with the same node ID (pk) must fail
|
||||
BEAST_EXPECT(logic.activate(slot2, pk1, false) == Result::DuplicatePeer);
|
||||
|
||||
logic.onClosed(slot);
|
||||
|
||||
// accept the same key for a new slot after removing the old slot
|
||||
BEAST_EXPECT(logic.activate(slot2, pk1, false) == Result::Success);
|
||||
logic.onClosed(slot2);
|
||||
}
|
||||
|
||||
void
|
||||
testActivateInboundDisabled()
|
||||
{
|
||||
testcase("test activate inbound disabled");
|
||||
TestStore store;
|
||||
TestChecker checker;
|
||||
TestStopwatch clock;
|
||||
Logic<TestChecker> logic(clock, store, checker, journal_);
|
||||
{
|
||||
Config c;
|
||||
c.autoConnect = false;
|
||||
c.listeningPort = 1024;
|
||||
c.ipLimit = 2;
|
||||
logic.config(c);
|
||||
}
|
||||
|
||||
PublicKey const pk1(randomKeyPair(KeyType::Secp256k1).first);
|
||||
auto const local = beast::IP::Endpoint::fromString("65.0.0.2:1024");
|
||||
|
||||
auto const [slot, rSlot] =
|
||||
logic.newInboundSlot(local, beast::IP::Endpoint::fromString("55.104.0.2:1025"));
|
||||
BEAST_EXPECT(slot != nullptr);
|
||||
BEAST_EXPECT(rSlot == Result::Success);
|
||||
|
||||
BEAST_EXPECT(logic.activate(slot, pk1, false) == Result::InboundDisabled);
|
||||
|
||||
{
|
||||
Config c;
|
||||
c.autoConnect = false;
|
||||
c.listeningPort = 1024;
|
||||
c.ipLimit = 2;
|
||||
c.inPeers = 1;
|
||||
logic.config(c);
|
||||
}
|
||||
// new inbound slot must succeed when inbound connections are enabled
|
||||
BEAST_EXPECT(logic.activate(slot, pk1, false) == Result::Success);
|
||||
|
||||
// creating a new inbound slot must succeed as IP Limit is not exceeded
|
||||
auto const [slot2, r2Slot] =
|
||||
logic.newInboundSlot(local, beast::IP::Endpoint::fromString("55.104.0.2:1026"));
|
||||
BEAST_EXPECT(slot2 != nullptr);
|
||||
BEAST_EXPECT(r2Slot == Result::Success);
|
||||
|
||||
PublicKey const pk2(randomKeyPair(KeyType::Secp256k1).first);
|
||||
|
||||
// an inbound slot exceeding inPeers limit must fail
|
||||
BEAST_EXPECT(logic.activate(slot2, pk2, false) == Result::Full);
|
||||
|
||||
logic.onClosed(slot2);
|
||||
logic.onClosed(slot);
|
||||
}
|
||||
|
||||
void
|
||||
testAddFixedPeerNoPort()
|
||||
{
|
||||
testcase("test addFixedPeer no port");
|
||||
TestStore store;
|
||||
TestChecker checker;
|
||||
TestStopwatch clock;
|
||||
Logic<TestChecker> logic(clock, store, checker, journal_);
|
||||
try
|
||||
{
|
||||
logic.addFixedPeer("test", beast::IP::Endpoint::fromString("65.0.0.2"));
|
||||
fail("invalid endpoint successfully added");
|
||||
}
|
||||
catch (std::runtime_error const& e)
|
||||
{
|
||||
pass();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testIsValidAddress()
|
||||
{
|
||||
testcase("is_valid_address");
|
||||
TestStore store;
|
||||
TestChecker checker;
|
||||
TestStopwatch clock;
|
||||
Logic<TestChecker> logic(clock, store, checker, journal_);
|
||||
|
||||
auto const pass = [&](std::string const& s) {
|
||||
BEAST_EXPECT(logic.isValidAddress(beast::IP::Endpoint::fromString(s)));
|
||||
};
|
||||
auto const fail = [&](std::string const& s) {
|
||||
BEAST_EXPECT(!logic.isValidAddress(beast::IP::Endpoint::fromString(s)));
|
||||
};
|
||||
|
||||
// Invalid: port 0
|
||||
fail("65.0.0.1:0");
|
||||
|
||||
// --- IPv4 ranges ---
|
||||
// For each range: 1 before (pass), first (fail), last (fail),
|
||||
// 1 after (pass)
|
||||
|
||||
// 0.0.0.0/8 - "This network"
|
||||
// No "before" - nothing before 0.0.0.0
|
||||
fail("0.0.0.0:8080");
|
||||
fail("0.255.255.255:8080");
|
||||
pass("1.0.0.0:8080");
|
||||
|
||||
// 10.0.0.0/8 - Private (RFC 1918)
|
||||
pass("9.255.255.255:8080");
|
||||
fail("10.0.0.0:8080");
|
||||
fail("10.255.255.255:8080");
|
||||
pass("11.0.0.0:8080");
|
||||
|
||||
// 100.64.0.0/10 - Shared Address Space / CGNAT (RFC 6598)
|
||||
pass("100.63.255.255:8080");
|
||||
fail("100.64.0.0:8080");
|
||||
fail("100.127.255.255:8080");
|
||||
pass("100.128.0.0:8080");
|
||||
|
||||
// 127.0.0.0/8 - Loopback
|
||||
pass("126.255.255.255:8080");
|
||||
fail("127.0.0.0:8080");
|
||||
fail("127.255.255.255:8080");
|
||||
pass("128.0.0.0:8080");
|
||||
|
||||
// 169.254.0.0/16 - Link-local
|
||||
pass("169.253.255.255:8080");
|
||||
fail("169.254.0.0:8080");
|
||||
fail("169.254.255.255:8080");
|
||||
pass("169.255.0.0:8080");
|
||||
|
||||
// 172.16.0.0/12 - Private (RFC 1918)
|
||||
pass("172.15.255.255:8080");
|
||||
fail("172.16.0.0:8080");
|
||||
fail("172.31.255.255:8080");
|
||||
pass("172.32.0.0:8080");
|
||||
|
||||
// 192.0.0.0/24 - IETF Protocol Assignments (RFC 6890)
|
||||
pass("191.255.255.255:8080");
|
||||
fail("192.0.0.0:8080");
|
||||
fail("192.0.0.255:8080");
|
||||
pass("192.0.1.0:8080");
|
||||
|
||||
// 192.0.2.0/24 - TEST-NET-1 (RFC 5737)
|
||||
pass("192.0.1.255:8080");
|
||||
fail("192.0.2.0:8080");
|
||||
fail("192.0.2.255:8080");
|
||||
pass("192.0.3.0:8080");
|
||||
|
||||
// 192.88.99.0/24 - 6to4 Relay Anycast (RFC 7526)
|
||||
pass("192.88.98.255:8080");
|
||||
fail("192.88.99.0:8080");
|
||||
fail("192.88.99.255:8080");
|
||||
pass("192.88.100.0:8080");
|
||||
|
||||
// 192.168.0.0/16 - Private (RFC 1918)
|
||||
pass("192.167.255.255:8080");
|
||||
fail("192.168.0.0:8080");
|
||||
fail("192.168.255.255:8080");
|
||||
pass("192.169.0.0:8080");
|
||||
|
||||
// 198.18.0.0/15 - Benchmarking (RFC 2544)
|
||||
pass("198.17.255.255:8080");
|
||||
fail("198.18.0.0:8080");
|
||||
fail("198.19.255.255:8080");
|
||||
pass("198.20.0.0:8080");
|
||||
|
||||
// 198.51.100.0/24 - TEST-NET-2 (RFC 5737)
|
||||
pass("198.51.99.255:8080");
|
||||
fail("198.51.100.0:8080");
|
||||
fail("198.51.100.255:8080");
|
||||
pass("198.51.101.0:8080");
|
||||
|
||||
// 203.0.113.0/24 - TEST-NET-3 (RFC 5737)
|
||||
pass("203.0.112.255:8080");
|
||||
fail("203.0.113.0:8080");
|
||||
fail("203.0.113.255:8080");
|
||||
pass("203.0.114.0:8080");
|
||||
|
||||
// 224.0.0.0/4 - Multicast
|
||||
pass("223.255.255.255:8080");
|
||||
fail("224.0.0.0:8080");
|
||||
fail("239.255.255.255:8080");
|
||||
// 240.0.0.0 (after multicast) is also blocked (reserved)
|
||||
|
||||
// 240.0.0.0/4 - Reserved (RFC 1112)
|
||||
// 239.255.255.255 (before reserved) is also blocked (multicast)
|
||||
fail("240.0.0.0:8080");
|
||||
fail("255.255.255.255:8080");
|
||||
|
||||
// --- IPv6 ranges ---
|
||||
|
||||
// ::1 - Loopback (single address)
|
||||
fail("[::1]:8080");
|
||||
|
||||
// :: - Unspecified (single address)
|
||||
fail("[::]:8080");
|
||||
|
||||
// fc00::/7 - Unique Local Address (ULA)
|
||||
pass("[fb00::1]:8080");
|
||||
fail("[fc00::1]:8080");
|
||||
fail("[fdff::1]:8080");
|
||||
pass("[fe00::1]:8080");
|
||||
|
||||
// fe80::/10 - Link-local
|
||||
pass("[fe7f::1]:8080");
|
||||
fail("[fe80::1]:8080");
|
||||
fail("[febf::1]:8080");
|
||||
pass("[fec0::1]:8080");
|
||||
|
||||
// ff00::/8 - Multicast
|
||||
pass("[feff::1]:8080");
|
||||
fail("[ff00::1]:8080");
|
||||
fail("[ffff::1]:8080");
|
||||
// No "after" - ffff:... is the highest IPv6 range
|
||||
|
||||
// 100::/64 - Discard prefix (RFC 6666)
|
||||
pass("[ff::1]:8080");
|
||||
fail("[100::]:8080");
|
||||
fail("[100::ffff:ffff:ffff:ffff]:8080");
|
||||
pass("[100:0:0:1::1]:8080");
|
||||
|
||||
// 2001::/32 - IETF Protocol Assignments / Teredo (RFC 4380)
|
||||
pass("[2000:ffff::1]:8080");
|
||||
fail("[2001::]:8080");
|
||||
fail("[2001:0:ffff::1]:8080");
|
||||
pass("[2001:1::1]:8080");
|
||||
|
||||
// 2001:20::/28 - ORCHIDv2 (RFC 7343)
|
||||
pass("[2001:1f::1]:8080");
|
||||
fail("[2001:20::1]:8080");
|
||||
fail("[2001:2f::1]:8080");
|
||||
pass("[2001:30::1]:8080");
|
||||
|
||||
// 2001:db8::/32 - Documentation (RFC 3849)
|
||||
pass("[2001:db7::1]:8080");
|
||||
fail("[2001:db8::1]:8080");
|
||||
fail("[2001:db8:ffff::1]:8080");
|
||||
pass("[2001:db9::1]:8080");
|
||||
|
||||
// 2002::/16 - 6to4 (RFC 3056, deprecated)
|
||||
pass("[2001:ffff::1]:8080");
|
||||
fail("[2002::1]:8080");
|
||||
fail("[2002:ffff::1]:8080");
|
||||
pass("[2003::1]:8080");
|
||||
|
||||
// --- IPv6 v4-mapped (delegates to IPv4 checks) ---
|
||||
fail("[::ffff:10.0.0.1]:8080");
|
||||
fail("[::ffff:100.64.0.1]:8080");
|
||||
fail("[::ffff:169.254.1.1]:8080");
|
||||
fail("[::ffff:192.0.2.1]:8080");
|
||||
fail("[::ffff:198.18.0.1]:8080");
|
||||
fail("[::ffff:224.0.0.1]:8080");
|
||||
fail("[::ffff:240.0.0.1]:8080");
|
||||
|
||||
// --- Valid public addresses ---
|
||||
pass("8.8.8.8:443");
|
||||
pass("65.0.0.1:8080");
|
||||
pass("[2001:4860:4860::8888]:8080");
|
||||
pass("[2606:4700:4700::1111]:8080");
|
||||
}
|
||||
|
||||
void
|
||||
testVerifyEndpoints()
|
||||
{
|
||||
// Helper that sets up a Logic instance, creates and activates a slot,
|
||||
// then calls on_endpoints with the given list and returns the
|
||||
// livecache size afterwards.
|
||||
auto run = [&](bool verifyEndpoints, Endpoints eps) -> std::size_t {
|
||||
TestStore store;
|
||||
TestChecker checker;
|
||||
TestStopwatch clock;
|
||||
Logic<TestChecker> logic(clock, store, checker, journal_);
|
||||
{
|
||||
Config c;
|
||||
c.autoConnect = false;
|
||||
c.listeningPort = 1024;
|
||||
c.ipLimit = 2;
|
||||
c.verifyEndpoints = verifyEndpoints;
|
||||
logic.config(c);
|
||||
}
|
||||
|
||||
auto const remote = beast::IP::Endpoint::fromString("65.0.0.1:5");
|
||||
auto const local = beast::IP::Endpoint::fromString("65.0.0.2:1024");
|
||||
|
||||
auto const [slot, r] = logic.newOutboundSlot(remote);
|
||||
BEAST_EXPECT(slot != nullptr);
|
||||
BEAST_EXPECT(r == Result::Success);
|
||||
BEAST_EXPECT(logic.onConnected(slot, local));
|
||||
|
||||
PublicKey const pk(randomKeyPair(KeyType::Secp256k1).first);
|
||||
BEAST_EXPECT(logic.activate(slot, pk, false) == Result::Success);
|
||||
|
||||
logic.onEndpoints(slot, std::move(eps));
|
||||
|
||||
auto const size = logic.livecache.size();
|
||||
logic.onClosed(slot);
|
||||
return size;
|
||||
};
|
||||
|
||||
{
|
||||
testcase("verify_endpoints enabled");
|
||||
|
||||
// Valid public addresses
|
||||
Endpoints eps;
|
||||
eps.emplace_back(beast::IP::Endpoint::fromString("44.0.0.1:5"), 1);
|
||||
eps.emplace_back(beast::IP::Endpoint::fromString("44.0.0.2:6"), 1);
|
||||
// Invalid: private address
|
||||
eps.emplace_back(beast::IP::Endpoint::fromString("10.0.0.1:5"), 1);
|
||||
// Invalid: port 0
|
||||
eps.emplace_back(beast::IP::Endpoint::fromString("44.0.0.3:0"), 1);
|
||||
|
||||
// With verification enabled, only the 2 valid endpoints survive
|
||||
BEAST_EXPECT(run(true, eps) == 2);
|
||||
}
|
||||
{
|
||||
testcase("verify_endpoints disabled");
|
||||
|
||||
Endpoints eps;
|
||||
eps.emplace_back(beast::IP::Endpoint::fromString("44.0.0.1:5"), 1);
|
||||
eps.emplace_back(beast::IP::Endpoint::fromString("44.0.0.2:6"), 1);
|
||||
// Private address — kept when verification is off
|
||||
eps.emplace_back(beast::IP::Endpoint::fromString("10.0.0.1:5"), 1);
|
||||
// Port 0 — kept when verification is off
|
||||
eps.emplace_back(beast::IP::Endpoint::fromString("44.0.0.3:0"), 1);
|
||||
|
||||
// Without verification, all 4 endpoints survive
|
||||
BEAST_EXPECT(run(false, eps) == 4);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testOnConnectedSelfConnection()
|
||||
{
|
||||
testcase("test onConnected self connection");
|
||||
TestStore store;
|
||||
TestChecker checker;
|
||||
TestStopwatch clock;
|
||||
Logic<TestChecker> logic(clock, store, checker, journal_);
|
||||
|
||||
auto const local = beast::IP::Endpoint::fromString("65.0.0.2:1234");
|
||||
auto const [slot, r] = logic.newOutboundSlot(local);
|
||||
BEAST_EXPECT(slot != nullptr);
|
||||
BEAST_EXPECT(r == Result::Success);
|
||||
|
||||
// Must fail when a slot is to our own IP address
|
||||
BEAST_EXPECT(!logic.onConnected(slot, local));
|
||||
logic.onClosed(slot);
|
||||
}
|
||||
|
||||
void
|
||||
testConfig()
|
||||
{
|
||||
// if peers_max is configured then peers_in_max and peers_out_max
|
||||
// are ignored
|
||||
auto run = [&](std::string const& test,
|
||||
std::optional<std::uint16_t> maxPeers,
|
||||
std::optional<std::uint16_t> maxIn,
|
||||
std::optional<std::uint16_t> maxOut,
|
||||
std::uint16_t port,
|
||||
std::uint16_t expectOut,
|
||||
std::uint16_t expectIn,
|
||||
std::uint16_t expectIpLimit) {
|
||||
xrpl::Config c;
|
||||
|
||||
testcase(test);
|
||||
|
||||
std::string toLoad;
|
||||
int max = 0;
|
||||
if (maxPeers)
|
||||
{
|
||||
max = maxPeers.value();
|
||||
toLoad += "[peers_max]\n" + std::to_string(max) + "\n" + "[peers_in_max]\n" +
|
||||
std::to_string(maxIn.value_or(0)) + "\n" + "[peers_out_max]\n" +
|
||||
std::to_string(maxOut.value_or(0)) + "\n";
|
||||
}
|
||||
else if (maxIn && maxOut)
|
||||
{
|
||||
toLoad += "[peers_in_max]\n" + std::to_string(*maxIn) + "\n" + "[peers_out_max]\n" +
|
||||
std::to_string(*maxOut) + "\n";
|
||||
}
|
||||
|
||||
c.loadFromString(toLoad);
|
||||
BEAST_EXPECT(
|
||||
(c.peersMax == max && c.peersInMax == 0 && c.peersOutMax == 0) ||
|
||||
(c.peersInMax == *maxIn && c.peersOutMax == *maxOut));
|
||||
|
||||
Config const config = Config::makeConfig(c, port, false, 0, true);
|
||||
|
||||
Counts counts;
|
||||
counts.onConfig(config);
|
||||
BEAST_EXPECT(
|
||||
counts.outMax() == expectOut && counts.inMax() == expectIn &&
|
||||
config.ipLimit == expectIpLimit);
|
||||
|
||||
TestStore store;
|
||||
TestChecker checker;
|
||||
TestStopwatch clock;
|
||||
Logic<TestChecker> logic(clock, store, checker, journal_);
|
||||
logic.config(config);
|
||||
|
||||
BEAST_EXPECT(logic.config() == config);
|
||||
};
|
||||
|
||||
// if max_peers == 0 => maxPeers = 21,
|
||||
// else if max_peers < 10 => maxPeers = 10 else maxPeers =
|
||||
// max_peers
|
||||
// expectOut => if legacy => max(0.15 * maxPeers, 10),
|
||||
// if legacy && !wantIncoming => maxPeers else max_out_peers
|
||||
// expectIn => if legacy && wantIncoming => maxPeers - outPeers
|
||||
// else if !wantIncoming => 0 else max_in_peers
|
||||
// ipLimit => if expectIn <= 21 => 2 else 2 + min(5, expectIn/21)
|
||||
// ipLimit = max(1, min(ipLimit, expectIn/2))
|
||||
|
||||
// legacy test with max_peers
|
||||
run("legacy no config", {}, {}, {}, 4000, 10, 11, 2);
|
||||
run("legacy max_peers 0", 0, 100, 10, 4000, 10, 11, 2);
|
||||
run("legacy max_peers 5", 5, 100, 10, 4000, 10, 0, 1);
|
||||
run("legacy max_peers 20", 20, 100, 10, 4000, 10, 10, 2);
|
||||
run("legacy max_peers 100", 100, 100, 10, 4000, 15, 85, 6);
|
||||
run("legacy max_peers 20, private", 20, 100, 10, 0, 20, 0, 1);
|
||||
|
||||
// test with max_in_peers and max_out_peers
|
||||
run("new in 100/out 10", {}, 100, 10, 4000, 10, 100, 6);
|
||||
run("new in 0/out 10", {}, 0, 10, 4000, 10, 0, 1);
|
||||
run("new in 100/out 10, private", {}, 100, 10, 0, 10, 0, 6);
|
||||
}
|
||||
|
||||
void
|
||||
testInvalidConfig()
|
||||
{
|
||||
testcase("invalid config");
|
||||
|
||||
auto run = [&](std::string const& toLoad) {
|
||||
xrpl::Config c;
|
||||
try
|
||||
{
|
||||
c.loadFromString(toLoad);
|
||||
fail();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
pass();
|
||||
}
|
||||
};
|
||||
run(R"xrpldConfig(
|
||||
[peers_in_max]
|
||||
100
|
||||
)xrpldConfig");
|
||||
run(R"xrpldConfig(
|
||||
[peers_out_max]
|
||||
100
|
||||
)xrpldConfig");
|
||||
run(R"xrpldConfig(
|
||||
[peers_in_max]
|
||||
100
|
||||
[peers_out_max]
|
||||
5
|
||||
)xrpldConfig");
|
||||
run(R"xrpldConfig(
|
||||
[peers_in_max]
|
||||
1001
|
||||
[peers_out_max]
|
||||
10
|
||||
)xrpldConfig");
|
||||
run(R"xrpldConfig(
|
||||
[peers_in_max]
|
||||
10
|
||||
[peers_out_max]
|
||||
1001
|
||||
)xrpldConfig");
|
||||
}
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
testBackoff1();
|
||||
testBackoff2();
|
||||
testDuplicateOutIn();
|
||||
testDuplicateInOut();
|
||||
testConfig();
|
||||
testInvalidConfig();
|
||||
testPeerLimitExceeded();
|
||||
testActivateDuplicatePeer();
|
||||
testActivateInboundDisabled();
|
||||
testAddFixedPeerNoPort();
|
||||
testOnConnectedSelfConnection();
|
||||
testIsValidAddress();
|
||||
testVerifyEndpoints();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(PeerFinder, peerfinder, xrpl);
|
||||
|
||||
} // namespace xrpl::PeerFinder
|
||||
@@ -21,7 +21,7 @@ set_target_properties(
|
||||
)
|
||||
# Lets test sources include the shared helpers as <helpers/...>.
|
||||
target_include_directories(xrpl_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_link_libraries(xrpl_tests PRIVATE GTest::gtest xrpl.libxrpl)
|
||||
target_link_libraries(xrpl_tests PRIVATE GTest::gtest GTest::gmock xrpl.libxrpl)
|
||||
|
||||
# One source subdirectory per module. Network unit tests are currently not
|
||||
# supported on Windows.
|
||||
@@ -29,6 +29,7 @@ set(test_modules
|
||||
basics
|
||||
crypto
|
||||
json
|
||||
peerfinder
|
||||
resource
|
||||
shamap
|
||||
tx
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
int
|
||||
main(int argc, char** argv)
|
||||
{
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
::testing::InitGoogleMock(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
294
src/tests/libxrpl/peerfinder/Livecache.cpp
Normal file
294
src/tests/libxrpl/peerfinder/Livecache.cpp
Normal file
@@ -0,0 +1,294 @@
|
||||
#include <xrpl/peerfinder/detail/Livecache.h>
|
||||
|
||||
#include <xrpl/basics/chrono.h>
|
||||
#include <xrpl/basics/random.h>
|
||||
#include <xrpl/beast/net/IPAddressV4.h>
|
||||
#include <xrpl/beast/net/IPAddressV6.h>
|
||||
#include <xrpl/beast/net/IPEndpoint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/beast/utility/PropertyStream.h>
|
||||
#include <xrpl/json/JsonPropertyStream.h>
|
||||
#include <xrpl/json/json_forwards.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/peerfinder/Types.h>
|
||||
#include <xrpl/peerfinder/detail/Tuning.h>
|
||||
|
||||
#include <boost/algorithm/string/classification.hpp>
|
||||
#include <boost/algorithm/string/split.hpp>
|
||||
#include <boost/algorithm/string/trim.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <helpers/TestSink.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace xrpl::PeerFinder {
|
||||
namespace {
|
||||
|
||||
class LivecacheTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
static beast::Journal
|
||||
journal()
|
||||
{
|
||||
return beast::Journal{TestSink::instance()};
|
||||
}
|
||||
|
||||
static beast::IP::Endpoint
|
||||
endpoint(std::uint16_t index, bool v4 = true)
|
||||
{
|
||||
auto const port = static_cast<std::uint16_t>(10000 + index);
|
||||
|
||||
if (v4)
|
||||
{
|
||||
auto bytes = beast::IP::AddressV4::bytes_type{
|
||||
{54,
|
||||
static_cast<std::uint8_t>((index / 256) % 256),
|
||||
static_cast<std::uint8_t>(index % 256),
|
||||
1}};
|
||||
return beast::IP::Endpoint{beast::IP::Address{beast::IP::AddressV4{bytes}}, port};
|
||||
}
|
||||
|
||||
auto bytes = beast::IP::AddressV6::bytes_type{
|
||||
{0x20,
|
||||
0x01,
|
||||
0x0d,
|
||||
0xb8,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
static_cast<std::uint8_t>((index / 256) % 256),
|
||||
static_cast<std::uint8_t>(index % 256),
|
||||
1}};
|
||||
return beast::IP::Endpoint{beast::IP::Address{beast::IP::AddressV6{bytes}}, port};
|
||||
}
|
||||
|
||||
void
|
||||
addEndpoint(beast::IP::Endpoint const& ep, std::uint32_t hops = 0)
|
||||
{
|
||||
cache_.insert(Endpoint{ep, hops});
|
||||
}
|
||||
|
||||
TestStopwatch clock_;
|
||||
Livecache<> cache_{clock_, journal()};
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_F(LivecacheTest, basic_insert)
|
||||
{
|
||||
EXPECT_TRUE(cache_.empty());
|
||||
|
||||
for (auto i = 0; i < 10; ++i)
|
||||
addEndpoint(endpoint(i, true));
|
||||
|
||||
EXPECT_FALSE(cache_.empty());
|
||||
EXPECT_EQ(cache_.size(), 10u);
|
||||
|
||||
for (auto i = 10; i < 20; ++i)
|
||||
addEndpoint(endpoint(i, false));
|
||||
|
||||
EXPECT_FALSE(cache_.empty());
|
||||
EXPECT_EQ(cache_.size(), 20u);
|
||||
}
|
||||
|
||||
TEST_F(LivecacheTest, insert_update_keeps_lowest_hop_count)
|
||||
{
|
||||
auto const ep1 = Endpoint{endpoint(1), 2};
|
||||
cache_.insert(ep1);
|
||||
ASSERT_EQ(cache_.size(), 1u);
|
||||
EXPECT_EQ((cache_.hops.begin() + 2)->begin()->hops, 2u);
|
||||
|
||||
auto const ep2 = Endpoint{ep1.address, 4};
|
||||
cache_.insert(ep2);
|
||||
EXPECT_EQ(cache_.size(), 1u);
|
||||
EXPECT_EQ((cache_.hops.begin() + 2)->begin()->hops, 2u);
|
||||
|
||||
auto const ep3 = Endpoint{ep1.address, 2};
|
||||
cache_.insert(ep3);
|
||||
EXPECT_EQ(cache_.size(), 1u);
|
||||
EXPECT_EQ((cache_.hops.begin() + 2)->begin()->hops, 2u);
|
||||
|
||||
auto const ep4 = Endpoint{ep1.address, 1};
|
||||
cache_.insert(ep4);
|
||||
EXPECT_EQ(cache_.size(), 1u);
|
||||
EXPECT_EQ((cache_.hops.begin() + 1)->begin()->hops, 1u);
|
||||
}
|
||||
|
||||
TEST_F(LivecacheTest, hop_iterators_support_const_reverse_and_move_back)
|
||||
{
|
||||
auto const ep1 = Endpoint{endpoint(1), 1};
|
||||
auto const ep2 = Endpoint{endpoint(2), 1};
|
||||
cache_.insert(ep1);
|
||||
cache_.insert(ep2);
|
||||
|
||||
auto hop = *(cache_.hops.begin() + 1);
|
||||
ASSERT_NE(hop.begin(), hop.end());
|
||||
ASSERT_NE(hop.cbegin(), hop.cend());
|
||||
ASSERT_NE(hop.rbegin(), hop.rend());
|
||||
ASSERT_NE(hop.crbegin(), hop.crend());
|
||||
|
||||
auto const firstAddress = hop.begin()->address;
|
||||
hop.moveBack(hop.begin());
|
||||
EXPECT_EQ(hop.rbegin()->address, firstAddress);
|
||||
|
||||
auto const& constHops = cache_.hops;
|
||||
EXPECT_NE(constHops.begin(), constHops.end());
|
||||
EXPECT_NE(constHops.cbegin(), constHops.cend());
|
||||
EXPECT_NE(constHops.rbegin(), constHops.rend());
|
||||
EXPECT_NE(constHops.crbegin(), constHops.crend());
|
||||
|
||||
auto const constHop = *(constHops.cbegin() + 1);
|
||||
EXPECT_EQ(std::distance(constHop.begin(), constHop.end()), 2);
|
||||
EXPECT_EQ(std::distance(constHop.cbegin(), constHop.cend()), 2);
|
||||
EXPECT_EQ(std::distance(constHop.rbegin(), constHop.rend()), 2);
|
||||
EXPECT_EQ(std::distance(constHop.crbegin(), constHop.crend()), 2);
|
||||
}
|
||||
|
||||
TEST_F(LivecacheTest, on_write_reports_entries_and_expiration)
|
||||
{
|
||||
cache_.insert(Endpoint{endpoint(1), 1});
|
||||
cache_.insert(Endpoint{endpoint(2), Tuning::kMaxHops + 1});
|
||||
|
||||
JsonPropertyStream stream;
|
||||
{
|
||||
beast::PropertyStream::Map map(stream);
|
||||
cache_.onWrite(map);
|
||||
}
|
||||
|
||||
auto const& top = stream.top();
|
||||
EXPECT_EQ(top["size"].asUInt(), 2u);
|
||||
EXPECT_FALSE(top["hist"].asString().empty());
|
||||
ASSERT_TRUE(top.isMember("entries"));
|
||||
ASSERT_EQ(top["entries"].size(), 2u);
|
||||
auto const& entry = top["entries"][json::UInt{0}];
|
||||
EXPECT_TRUE(entry.isMember("hops"));
|
||||
EXPECT_TRUE(entry.isMember("address"));
|
||||
EXPECT_TRUE(entry.isMember("expires"));
|
||||
}
|
||||
|
||||
TEST_F(LivecacheTest, expire_removes_entries_after_ttl)
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
cache_.insert(Endpoint{endpoint(1), 1});
|
||||
ASSERT_EQ(cache_.size(), 1u);
|
||||
|
||||
cache_.expire();
|
||||
EXPECT_EQ(cache_.size(), 1u);
|
||||
|
||||
clock_.advance(Tuning::kLiveCacheSecondsToLive - 1s);
|
||||
cache_.expire();
|
||||
EXPECT_EQ(cache_.size(), 1u);
|
||||
|
||||
clock_.advance(1s);
|
||||
cache_.expire();
|
||||
EXPECT_TRUE(cache_.empty());
|
||||
}
|
||||
|
||||
TEST_F(LivecacheTest, expire_removes_multiple_entries_after_ttl)
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
cache_.insert(Endpoint{endpoint(1), 1});
|
||||
cache_.insert(Endpoint{endpoint(2), 2});
|
||||
|
||||
clock_.advance(Tuning::kLiveCacheSecondsToLive);
|
||||
cache_.expire();
|
||||
EXPECT_TRUE(cache_.empty());
|
||||
}
|
||||
|
||||
TEST_F(LivecacheTest, histogram_counts_all_entries)
|
||||
{
|
||||
constexpr auto kNumEndpoints = 40;
|
||||
|
||||
for (auto i = 0; i < kNumEndpoints; ++i)
|
||||
{
|
||||
addEndpoint(endpoint(static_cast<std::uint16_t>(i)), xrpl::randInt<std::uint32_t>());
|
||||
}
|
||||
|
||||
auto const histogram = cache_.hops.histogram();
|
||||
ASSERT_FALSE(histogram.empty());
|
||||
|
||||
std::vector<std::string> values;
|
||||
boost::split(values, histogram, boost::algorithm::is_any_of(","));
|
||||
|
||||
auto sum = 0;
|
||||
for (auto const& value : values)
|
||||
{
|
||||
auto const count = boost::lexical_cast<int>(boost::trim_copy(value));
|
||||
sum += count;
|
||||
EXPECT_GE(count, 0);
|
||||
}
|
||||
EXPECT_EQ(sum, kNumEndpoints);
|
||||
}
|
||||
|
||||
TEST_F(LivecacheTest, shuffle_preserves_bucket_contents)
|
||||
{
|
||||
for (auto i = 0; i < 100; ++i)
|
||||
{
|
||||
addEndpoint(endpoint(static_cast<std::uint16_t>(i)), xrpl::randInt(Tuning::kMaxHops + 1));
|
||||
}
|
||||
|
||||
using AtHop = std::vector<Endpoint>;
|
||||
using AllHops = std::array<AtHop, 1 + Tuning::kMaxHops + 1>;
|
||||
|
||||
auto const compareEndpoint = [](Endpoint const& lhs, Endpoint const& rhs) {
|
||||
return rhs.hops < lhs.hops || (rhs.hops == lhs.hops && rhs.address < lhs.address);
|
||||
};
|
||||
auto const sameEndpoint = [](Endpoint const& lhs, Endpoint const& rhs) {
|
||||
return lhs.hops == rhs.hops && lhs.address == rhs.address;
|
||||
};
|
||||
auto const sameEndpoints =
|
||||
[&sameEndpoint](std::vector<Endpoint> const& lhs, std::vector<Endpoint> const& rhs) {
|
||||
return lhs.size() == rhs.size() &&
|
||||
std::equal(lhs.begin(), lhs.end(), rhs.begin(), sameEndpoint);
|
||||
};
|
||||
|
||||
AllHops before;
|
||||
AllHops beforeSorted;
|
||||
for (auto i = std::make_pair(0, cache_.hops.begin()); i.second != cache_.hops.end();
|
||||
++i.first, ++i.second)
|
||||
{
|
||||
std::ranges::copy(*i.second, std::back_inserter(before[i.first]));
|
||||
std::ranges::copy(*i.second, std::back_inserter(beforeSorted[i.first]));
|
||||
std::ranges::sort(beforeSorted[i.first], compareEndpoint);
|
||||
}
|
||||
|
||||
cache_.hops.shuffle();
|
||||
|
||||
AllHops after;
|
||||
AllHops afterSorted;
|
||||
for (auto i = std::make_pair(0, cache_.hops.begin()); i.second != cache_.hops.end();
|
||||
++i.first, ++i.second)
|
||||
{
|
||||
std::ranges::copy(*i.second, std::back_inserter(after[i.first]));
|
||||
std::ranges::copy(*i.second, std::back_inserter(afterSorted[i.first]));
|
||||
std::ranges::sort(afterSorted[i.first], compareEndpoint);
|
||||
}
|
||||
|
||||
auto allBucketsKeptOriginalOrder = true;
|
||||
for (auto i = 0u; i < before.size(); ++i)
|
||||
{
|
||||
EXPECT_EQ(before[i].size(), after[i].size());
|
||||
allBucketsKeptOriginalOrder =
|
||||
allBucketsKeptOriginalOrder && sameEndpoints(before[i], after[i]);
|
||||
EXPECT_TRUE(sameEndpoints(beforeSorted[i], afterSorted[i]));
|
||||
}
|
||||
EXPECT_FALSE(allBucketsKeptOriginalOrder);
|
||||
}
|
||||
|
||||
} // namespace xrpl::PeerFinder
|
||||
1270
src/tests/libxrpl/peerfinder/PeerFinder.cpp
Normal file
1270
src/tests/libxrpl/peerfinder/PeerFinder.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpld/peerfinder/detail/Store.h>
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/config/BasicConfig.h>
|
||||
#include <xrpl/peerfinder/detail/Store.h>
|
||||
#include <xrpl/rdb/DatabaseCon.h>
|
||||
|
||||
#include <functional>
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
#include <xrpld/app/rdb/PeerFinder.h>
|
||||
|
||||
#include <xrpld/peerfinder/detail/Store.h>
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/basics/contract.h>
|
||||
#include <xrpl/beast/net/IPEndpoint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/config/BasicConfig.h>
|
||||
#include <xrpl/peerfinder/detail/Store.h>
|
||||
#include <xrpl/rdb/SociDB.h>
|
||||
|
||||
#include <boost/optional/optional.hpp> // IWYU pragma: keep
|
||||
|
||||
@@ -7,8 +7,6 @@
|
||||
#include <xrpld/overlay/detail/OverlayImpl.h>
|
||||
#include <xrpld/overlay/detail/PeerImp.h>
|
||||
#include <xrpld/overlay/detail/ProtocolVersion.h>
|
||||
#include <xrpld/peerfinder/PeerfinderManager.h>
|
||||
#include <xrpld/peerfinder/Slot.h>
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/beast/net/IPAddressConversion.h>
|
||||
@@ -16,6 +14,8 @@
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/json/json_reader.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/peerfinder/Config.h>
|
||||
#include <xrpl/peerfinder/Slot.h>
|
||||
#include <xrpl/protocol/PublicKey.h>
|
||||
#include <xrpl/protocol/tokens.h>
|
||||
#include <xrpl/resource/Consumer.h>
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
#include <xrpld/app/main/Application.h>
|
||||
#include <xrpld/overlay/Peer.h>
|
||||
#include <xrpld/overlay/detail/OverlayImpl.h>
|
||||
#include <xrpld/peerfinder/Slot.h>
|
||||
|
||||
#include <xrpl/beast/net/IPAddressConversion.h>
|
||||
#include <xrpl/beast/net/IPEndpoint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/beast/utility/WrappedSink.h>
|
||||
#include <xrpl/peerfinder/Slot.h>
|
||||
#include <xrpl/resource/Consumer.h>
|
||||
|
||||
#include <chrono>
|
||||
|
||||
@@ -10,8 +10,6 @@
|
||||
#include <xrpld/overlay/detail/TrafficCount.h>
|
||||
#include <xrpld/overlay/detail/Tuning.h>
|
||||
#include <xrpld/peerfinder/PeerfinderManager.h>
|
||||
#include <xrpld/peerfinder/Slot.h>
|
||||
#include <xrpld/peerfinder/make_Manager.h>
|
||||
#include <xrpld/rpc/ServerHandler.h>
|
||||
#include <xrpld/rpc/handlers/admin/status/GetCounts.h>
|
||||
#include <xrpld/rpc/json_body.h>
|
||||
@@ -39,6 +37,9 @@
|
||||
#include <xrpl/config/Constants.h>
|
||||
#include <xrpl/core/HashRouter.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/peerfinder/Config.h>
|
||||
#include <xrpl/peerfinder/Slot.h>
|
||||
#include <xrpl/peerfinder/make_Manager.h>
|
||||
#include <xrpl/protocol/BuildInfo.h>
|
||||
#include <xrpl/protocol/STTx.h>
|
||||
#include <xrpl/protocol/Serializer.h>
|
||||
@@ -179,12 +180,13 @@ OverlayImpl::OverlayImpl(
|
||||
, journal_(app_.getJournal("Overlay"))
|
||||
, serverHandler_(serverHandler)
|
||||
, resourceManager_(resourceManager)
|
||||
, store_(app_.getJournal("PeerFinder"))
|
||||
, peerFinder_(
|
||||
PeerFinder::makeManager(
|
||||
ioContext,
|
||||
stopwatch(),
|
||||
app_.getJournal("PeerFinder"),
|
||||
config,
|
||||
store_,
|
||||
collector))
|
||||
, resolver_(resolver)
|
||||
, nextId_(1)
|
||||
@@ -201,6 +203,7 @@ OverlayImpl::OverlayImpl(
|
||||
return ret;
|
||||
}())
|
||||
{
|
||||
store_.open(config);
|
||||
beast::PropertyStream::Source::add(peerFinder_.get());
|
||||
}
|
||||
|
||||
@@ -505,7 +508,7 @@ OverlayImpl::remove(std::shared_ptr<PeerFinder::Slot> const& slot)
|
||||
void
|
||||
OverlayImpl::start()
|
||||
{
|
||||
PeerFinder::Config const config = PeerFinder::Config::makeConfig(
|
||||
PeerFinder::Config const config = PeerFinder::makeConfig(
|
||||
app_.config(),
|
||||
serverHandler_.setup().overlay.port(),
|
||||
app_.getValidationPublicKey().has_value(),
|
||||
|
||||
@@ -8,8 +8,7 @@
|
||||
#include <xrpld/overlay/detail/Handshake.h>
|
||||
#include <xrpld/overlay/detail/TrafficCount.h>
|
||||
#include <xrpld/overlay/detail/TxMetrics.h>
|
||||
#include <xrpld/peerfinder/PeerfinderManager.h>
|
||||
#include <xrpld/peerfinder/Slot.h>
|
||||
#include <xrpld/peerfinder/detail/StoreSqdb.h>
|
||||
#include <xrpld/rpc/ServerHandler.h>
|
||||
|
||||
#include <xrpl/basics/Resolver.h>
|
||||
@@ -24,6 +23,8 @@
|
||||
#include <xrpl/beast/utility/PropertyStream.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/peerfinder/PeerfinderManager.h>
|
||||
#include <xrpl/peerfinder/Slot.h>
|
||||
#include <xrpl/resource/ResourceManager.h>
|
||||
#include <xrpl/server/Handoff.h>
|
||||
#include <xrpl/server/Writer.h>
|
||||
@@ -109,6 +110,7 @@ private:
|
||||
beast::Journal const journal_;
|
||||
ServerHandler& serverHandler_;
|
||||
Resource::Manager& resourceManager_;
|
||||
PeerFinder::StoreSqdb store_;
|
||||
std::unique_ptr<PeerFinder::Manager> peerFinder_;
|
||||
TrafficCount traffic_;
|
||||
hash_map<std::shared_ptr<PeerFinder::Slot>, std::weak_ptr<PeerImp>> peers_;
|
||||
|
||||
@@ -19,8 +19,6 @@
|
||||
#include <xrpld/overlay/detail/ProtocolVersion.h>
|
||||
#include <xrpld/overlay/detail/TrafficCount.h>
|
||||
#include <xrpld/overlay/detail/Tuning.h>
|
||||
#include <xrpld/peerfinder/PeerfinderManager.h>
|
||||
#include <xrpld/peerfinder/Slot.h>
|
||||
|
||||
#include <xrpl/basics/Blob.h>
|
||||
#include <xrpl/basics/Log.h>
|
||||
@@ -43,6 +41,8 @@
|
||||
#include <xrpl/json/json_forwards.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/ledger/Ledger.h>
|
||||
#include <xrpl/peerfinder/Slot.h>
|
||||
#include <xrpl/peerfinder/Types.h>
|
||||
#include <xrpl/protocol/KeyType.h>
|
||||
#include <xrpl/protocol/LedgerHeader.h>
|
||||
#include <xrpl/protocol/Protocol.h>
|
||||
|
||||
@@ -9,8 +9,6 @@
|
||||
#include <xrpld/overlay/Squelch.h>
|
||||
#include <xrpld/overlay/detail/OverlayImpl.h>
|
||||
#include <xrpld/overlay/detail/ProtocolVersion.h>
|
||||
#include <xrpld/peerfinder/PeerfinderManager.h>
|
||||
#include <xrpld/peerfinder/Slot.h>
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/basics/Number.h>
|
||||
@@ -24,6 +22,8 @@
|
||||
#include <xrpl/core/HashRouter.h>
|
||||
#include <xrpl/core/LoadEvent.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/peerfinder/Slot.h>
|
||||
#include <xrpl/peerfinder/Types.h>
|
||||
#include <xrpl/protocol/Protocol.h>
|
||||
#include <xrpl/protocol/PublicKey.h>
|
||||
#include <xrpl/protocol/STTx.h>
|
||||
|
||||
@@ -1,368 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpld/core/Config.h>
|
||||
#include <xrpld/peerfinder/Slot.h>
|
||||
#include <xrpld/peerfinder/detail/Tuning.h>
|
||||
|
||||
#include <xrpl/beast/clock/abstract_clock.h>
|
||||
#include <xrpl/beast/net/IPEndpoint.h>
|
||||
#include <xrpl/beast/utility/PropertyStream.h>
|
||||
#include <xrpl/protocol/PublicKey.h>
|
||||
#include <xrpl/peerfinder/Config.h>
|
||||
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace xrpl::PeerFinder {
|
||||
|
||||
using clock_type = beast::AbstractClock<std::chrono::steady_clock>;
|
||||
|
||||
/**
|
||||
* Represents a set of addresses.
|
||||
*/
|
||||
using IPAddresses = std::vector<beast::IP::Endpoint>;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* PeerFinder configuration settings.
|
||||
*/
|
||||
struct Config
|
||||
{
|
||||
/**
|
||||
* The largest number of public peer slots to allow.
|
||||
* This includes both inbound and outbound, but does not include
|
||||
* fixed peers.
|
||||
*/
|
||||
std::size_t maxPeers{Tuning::kDefaultMaxPeers};
|
||||
|
||||
/**
|
||||
* The number of automatic outbound connections to maintain.
|
||||
* Outbound connections are only maintained if autoConnect
|
||||
* is `true`.
|
||||
*/
|
||||
std::size_t outPeers;
|
||||
|
||||
/**
|
||||
* The number of automatic inbound connections to maintain.
|
||||
* Inbound connections are only maintained if wantIncoming
|
||||
* is `true`.
|
||||
*/
|
||||
std::size_t inPeers{0};
|
||||
|
||||
/**
|
||||
* `true` if we want our IP address kept private.
|
||||
*/
|
||||
bool peerPrivate = true;
|
||||
|
||||
/**
|
||||
* `true` if we want to accept incoming connections.
|
||||
*/
|
||||
bool wantIncoming{true};
|
||||
|
||||
/**
|
||||
* `true` if we want to establish connections automatically
|
||||
*/
|
||||
bool autoConnect{true};
|
||||
|
||||
/**
|
||||
* The listening port number.
|
||||
*/
|
||||
std::uint16_t listeningPort{0};
|
||||
|
||||
/**
|
||||
* The set of features we advertise.
|
||||
*/
|
||||
std::string features;
|
||||
|
||||
/**
|
||||
* Limit how many incoming connections we allow per IP
|
||||
*/
|
||||
int ipLimit{0};
|
||||
|
||||
/**
|
||||
* `true` if we want to verify endpoints in TMEndpoints messages
|
||||
*/
|
||||
bool verifyEndpoints = true;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Create a configuration with default values.
|
||||
*/
|
||||
Config();
|
||||
|
||||
/**
|
||||
* Returns a suitable value for outPeers according to the rules.
|
||||
*/
|
||||
[[nodiscard]] std::size_t
|
||||
calcOutPeers() const;
|
||||
|
||||
/**
|
||||
* Adjusts the values so they follow the business rules.
|
||||
*/
|
||||
void
|
||||
applyTuning();
|
||||
|
||||
/**
|
||||
* Write the configuration into a property stream
|
||||
*/
|
||||
void
|
||||
onWrite(beast::PropertyStream::Map& map) const;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @param verifyEndpoints `true` if we want to verify endpoints in
|
||||
* TMEndpoints messages
|
||||
* @return PeerFinder::Config
|
||||
*/
|
||||
static Config
|
||||
makeConfig(
|
||||
xrpl::Config const& config,
|
||||
std::uint16_t port,
|
||||
bool validationPublicKey,
|
||||
int ipLimit,
|
||||
bool verifyEndpoints);
|
||||
|
||||
friend bool
|
||||
operator==(Config const& lhs, Config const& rhs) = default;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Describes a connectable peer address along with some metadata.
|
||||
*/
|
||||
struct Endpoint
|
||||
{
|
||||
Endpoint() = default;
|
||||
|
||||
Endpoint(beast::IP::Endpoint ep, std::uint32_t hops);
|
||||
|
||||
std::uint32_t hops = 0;
|
||||
beast::IP::Endpoint address;
|
||||
};
|
||||
|
||||
inline bool
|
||||
operator<(Endpoint const& lhs, Endpoint const& rhs)
|
||||
{
|
||||
return lhs.address < rhs.address;
|
||||
}
|
||||
|
||||
/**
|
||||
* A set of Endpoint used for connecting.
|
||||
*/
|
||||
using Endpoints = std::vector<Endpoint>;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Possible results from activating a slot.
|
||||
*/
|
||||
enum class Result { InboundDisabled, DuplicatePeer, IpLimitExceeded, Full, Success };
|
||||
|
||||
/**
|
||||
* @brief Converts a `Result` enum value to its string representation.
|
||||
*
|
||||
* This function provides a human-readable string for a given `Result` enum,
|
||||
* which is useful for logging, debugging, or displaying status messages.
|
||||
*
|
||||
* @param result The `Result` enum value to convert.
|
||||
* @return A `std::string_view` representing the enum value. Returns "unknown"
|
||||
* if the enum value is not explicitly handled.
|
||||
*
|
||||
* @note This function returns a `std::string_view` for performance.
|
||||
* A `std::string` would need to allocate memory on the heap and copy the
|
||||
* string literal into it every time the function is called.
|
||||
*/
|
||||
inline std::string_view
|
||||
to_string(Result result) noexcept
|
||||
{
|
||||
switch (result)
|
||||
{
|
||||
case Result::InboundDisabled:
|
||||
return "inbound disabled";
|
||||
case Result::DuplicatePeer:
|
||||
return "peer already connected";
|
||||
case Result::IpLimitExceeded:
|
||||
return "ip limit exceeded";
|
||||
case Result::Full:
|
||||
return "slots full";
|
||||
case Result::Success:
|
||||
return "success";
|
||||
}
|
||||
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
/**
|
||||
* Maintains a set of IP addresses used for getting into the network.
|
||||
*/
|
||||
class Manager : public beast::PropertyStream::Source
|
||||
{
|
||||
protected:
|
||||
Manager() noexcept;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Destroy the object.
|
||||
* Any pending source fetch operations are aborted.
|
||||
* There may be some listener calls made before the
|
||||
* destructor returns.
|
||||
*/
|
||||
~Manager() override = default;
|
||||
|
||||
/**
|
||||
* Set the configuration for the manager.
|
||||
* The new settings will be applied asynchronously.
|
||||
* Thread safety:
|
||||
* Can be called from any threads at any time.
|
||||
*/
|
||||
virtual void
|
||||
setConfig(Config const& config) = 0;
|
||||
|
||||
/**
|
||||
* Transition to the started state, synchronously.
|
||||
*/
|
||||
virtual void
|
||||
start() = 0;
|
||||
|
||||
/**
|
||||
* Transition to the stopped state, synchronously.
|
||||
*/
|
||||
virtual void
|
||||
stop() = 0;
|
||||
|
||||
/**
|
||||
* Returns the configuration for the manager.
|
||||
*/
|
||||
virtual Config
|
||||
config() = 0;
|
||||
|
||||
/**
|
||||
* Add a peer that should always be connected.
|
||||
* This is useful for maintaining a private cluster of peers.
|
||||
* The string is the name as specified in the configuration
|
||||
* file, along with the set of corresponding IP addresses.
|
||||
*/
|
||||
virtual void
|
||||
addFixedPeer(std::string_view name, std::vector<beast::IP::Endpoint> const& addresses) = 0;
|
||||
|
||||
/**
|
||||
* Add a set of strings as fallback IP::Endpoint sources.
|
||||
* @param name A label used for diagnostics.
|
||||
*/
|
||||
virtual void
|
||||
addFallbackStrings(std::string const& name, std::vector<std::string> const& strings) = 0;
|
||||
|
||||
/**
|
||||
* Add a URL as a fallback location to obtain IP::Endpoint sources.
|
||||
* @param name A label used for diagnostics.
|
||||
*/
|
||||
/* VFALCO NOTE Unimplemented
|
||||
virtual void addFallbackURL (std::string const& name,
|
||||
std::string const& url) = 0;
|
||||
*/
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Create a new inbound slot with the specified remote endpoint.
|
||||
* If nullptr is returned, then the slot could not be assigned.
|
||||
* Usually this is because of a detected self-connection.
|
||||
*/
|
||||
virtual std::pair<std::shared_ptr<Slot>, Result>
|
||||
newInboundSlot(
|
||||
beast::IP::Endpoint const& localEndpoint,
|
||||
beast::IP::Endpoint const& remoteEndpoint) = 0;
|
||||
|
||||
/**
|
||||
* Create a new outbound slot with the specified remote endpoint.
|
||||
* If nullptr is returned, then the slot could not be assigned.
|
||||
* Usually this is because of a duplicate connection.
|
||||
*/
|
||||
virtual std::pair<std::shared_ptr<Slot>, Result>
|
||||
newOutboundSlot(beast::IP::Endpoint const& remoteEndpoint) = 0;
|
||||
|
||||
/**
|
||||
* Called when mtENDPOINTS is received.
|
||||
*/
|
||||
virtual void
|
||||
onEndpoints(std::shared_ptr<Slot> const& slot, Endpoints const& endpoints) = 0;
|
||||
|
||||
/**
|
||||
* Called when the slot is closed.
|
||||
* This always happens when the socket is closed, unless the socket
|
||||
* was canceled.
|
||||
*/
|
||||
virtual void
|
||||
onClosed(std::shared_ptr<Slot> const& slot) = 0;
|
||||
|
||||
/**
|
||||
* Called when an outbound connection is deemed to have failed
|
||||
*/
|
||||
virtual void
|
||||
onFailure(std::shared_ptr<Slot> const& slot) = 0;
|
||||
|
||||
/**
|
||||
* Called when we received redirect IPs from a busy peer.
|
||||
*/
|
||||
virtual void
|
||||
onRedirects(
|
||||
boost::asio::ip::tcp::endpoint const& remoteAddress,
|
||||
std::vector<boost::asio::ip::tcp::endpoint> const& eps) = 0;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Called when an outbound connection attempt succeeds.
|
||||
* The local endpoint must be valid. If the caller receives an error
|
||||
* when retrieving the local endpoint from the socket, it should
|
||||
* proceed as if the connection attempt failed by calling on_closed
|
||||
* instead of on_connected.
|
||||
* @return `true` if the connection should be kept
|
||||
*/
|
||||
virtual bool
|
||||
onConnected(std::shared_ptr<Slot> const& slot, beast::IP::Endpoint const& localEndpoint) = 0;
|
||||
|
||||
/**
|
||||
* Request an active slot type.
|
||||
*/
|
||||
virtual Result
|
||||
activate(std::shared_ptr<Slot> const& slot, PublicKey const& key, bool reserved) = 0;
|
||||
|
||||
/**
|
||||
* Returns a set of endpoints suitable for redirection.
|
||||
*/
|
||||
virtual std::vector<Endpoint>
|
||||
redirect(std::shared_ptr<Slot> const& slot) = 0;
|
||||
|
||||
/**
|
||||
* Return a set of addresses we should connect to.
|
||||
*/
|
||||
virtual std::vector<beast::IP::Endpoint>
|
||||
autoconnect() = 0;
|
||||
|
||||
virtual std::vector<std::pair<std::shared_ptr<Slot>, std::vector<Endpoint>>>
|
||||
buildEndpointsForPeers() = 0;
|
||||
|
||||
/**
|
||||
* Perform periodic activity.
|
||||
* This should be called once per second.
|
||||
*/
|
||||
virtual void
|
||||
oncePerSecond() = 0;
|
||||
};
|
||||
Config
|
||||
makeConfig(
|
||||
xrpl::Config const& config,
|
||||
std::uint16_t port,
|
||||
bool validationPublicKey,
|
||||
int ipLimit,
|
||||
bool verifyEndpoints);
|
||||
|
||||
} // namespace xrpl::PeerFinder
|
||||
|
||||
@@ -1,124 +1,39 @@
|
||||
#include <xrpld/core/Config.h>
|
||||
#include <xrpld/peerfinder/PeerfinderManager.h>
|
||||
#include <xrpld/peerfinder/detail/Tuning.h>
|
||||
|
||||
#include <xrpl/beast/utility/PropertyStream.h>
|
||||
#include <xrpl/peerfinder/Config.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
namespace xrpl::PeerFinder {
|
||||
|
||||
Config::Config() : outPeers(calcOutPeers())
|
||||
|
||||
{
|
||||
}
|
||||
|
||||
std::size_t
|
||||
Config::calcOutPeers() const
|
||||
{
|
||||
return std::max(
|
||||
((maxPeers * Tuning::kOutPercent) + 50) / 100, std::size_t(Tuning::kMinOutCount));
|
||||
}
|
||||
|
||||
void
|
||||
Config::applyTuning()
|
||||
{
|
||||
if (ipLimit == 0)
|
||||
{
|
||||
// Unless a limit is explicitly set, we allow between
|
||||
// 2 and 5 connections from non RFC-1918 "private"
|
||||
// IP addresses.
|
||||
ipLimit = 2;
|
||||
|
||||
if (inPeers > Tuning::kDefaultMaxPeers)
|
||||
ipLimit += std::min(5, static_cast<int>(inPeers / Tuning::kDefaultMaxPeers));
|
||||
}
|
||||
|
||||
// We don't allow a single IP to consume all incoming slots,
|
||||
// unless we only have one incoming slot available.
|
||||
ipLimit = std::max(1, std::min(ipLimit, static_cast<int>(inPeers / 2)));
|
||||
}
|
||||
|
||||
void
|
||||
Config::onWrite(beast::PropertyStream::Map& map) const
|
||||
{
|
||||
map["max_peers"] = maxPeers;
|
||||
map["out_peers"] = outPeers;
|
||||
map["want_incoming"] = wantIncoming;
|
||||
map["auto_connect"] = autoConnect;
|
||||
map["port"] = listeningPort;
|
||||
map["features"] = features;
|
||||
map["ip_limit"] = ipLimit;
|
||||
map["verify_endpoints"] = verifyEndpoints;
|
||||
}
|
||||
|
||||
Config
|
||||
Config::makeConfig(
|
||||
makeConfig(
|
||||
xrpl::Config const& cfg,
|
||||
std::uint16_t port,
|
||||
bool validationPublicKey,
|
||||
int ipLimit,
|
||||
bool verifyEndpoints)
|
||||
{
|
||||
PeerFinder::Config config;
|
||||
|
||||
config.peerPrivate = cfg.peerPrivate;
|
||||
|
||||
// Servers with peer privacy don't want to allow incoming connections
|
||||
config.wantIncoming = (!config.peerPrivate) && (port != 0);
|
||||
|
||||
PeerLimitConfig limits;
|
||||
if ((cfg.peersOutMax == 0u) && (cfg.peersInMax == 0u))
|
||||
{
|
||||
if (cfg.peersMax != 0)
|
||||
config.maxPeers = cfg.peersMax;
|
||||
|
||||
config.maxPeers = std::max<std::size_t>(config.maxPeers, Tuning::kMinOutCount);
|
||||
config.outPeers = config.calcOutPeers();
|
||||
|
||||
// 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)
|
||||
config.outPeers = config.maxPeers;
|
||||
|
||||
// Calculate the largest number of inbound connections we could
|
||||
// take.
|
||||
if (config.maxPeers >= config.outPeers)
|
||||
{
|
||||
config.inPeers = config.maxPeers - config.outPeers;
|
||||
}
|
||||
else
|
||||
{
|
||||
config.inPeers = 0;
|
||||
}
|
||||
limits.maxPeers = cfg.peersMax;
|
||||
}
|
||||
else
|
||||
{
|
||||
config.outPeers = cfg.peersOutMax;
|
||||
config.inPeers = cfg.peersInMax;
|
||||
config.maxPeers = 0;
|
||||
limits.inPeers = cfg.peersInMax;
|
||||
limits.outPeers = cfg.peersOutMax;
|
||||
}
|
||||
|
||||
// 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.peerPrivate;
|
||||
config.listeningPort = port;
|
||||
config.features = "";
|
||||
config.ipLimit = ipLimit;
|
||||
config.verifyEndpoints = verifyEndpoints;
|
||||
|
||||
// Enforce business rules
|
||||
config.applyTuning();
|
||||
|
||||
return config;
|
||||
return Config::makeConfig(
|
||||
cfg.peerPrivate,
|
||||
cfg.standalone(),
|
||||
limits,
|
||||
port,
|
||||
validationPublicKey,
|
||||
ipLimit,
|
||||
verifyEndpoints);
|
||||
}
|
||||
|
||||
} // namespace xrpl::PeerFinder
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpld/app/rdb/PeerFinder.h>
|
||||
#include <xrpld/peerfinder/detail/Store.h>
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/beast/net/IPEndpoint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/peerfinder/detail/Store.h>
|
||||
#include <xrpl/rdb/SociDB.h>
|
||||
|
||||
#include <soci/session.h>
|
||||
|
||||
@@ -1,201 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <ios>
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
namespace beast {
|
||||
|
||||
// A collection of handy stream manipulators and
|
||||
// functions to produce nice looking log output.
|
||||
|
||||
/**
|
||||
* Left justifies a field at the specified width.
|
||||
*/
|
||||
struct Leftw
|
||||
{
|
||||
explicit Leftw(int width) : width(width)
|
||||
{
|
||||
}
|
||||
int const width;
|
||||
template <class CharT, class Traits>
|
||||
friend std::basic_ios<CharT, Traits>&
|
||||
operator<<(std::basic_ios<CharT, Traits>& ios, Leftw const& p)
|
||||
{
|
||||
ios.setf(std::ios_base::left, std::ios_base::adjustfield);
|
||||
ios.width(p.width);
|
||||
return ios;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Produce a section heading and fill the rest of the line with dashes.
|
||||
*/
|
||||
template <class CharT, class Traits, class Allocator>
|
||||
std::basic_string<CharT, Traits, Allocator>
|
||||
heading(std::basic_string<CharT, Traits, Allocator> title, int width = 80, CharT fill = CharT('-'))
|
||||
{
|
||||
title.reserve(width);
|
||||
title.push_back(CharT(' '));
|
||||
title.resize(width, fill);
|
||||
return title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce a dashed line separator, with a specified or default size.
|
||||
*/
|
||||
struct Divider
|
||||
{
|
||||
using CharT = char;
|
||||
explicit Divider(int width = 80, CharT fill = CharT('-')) : width(width), fill(fill)
|
||||
{
|
||||
}
|
||||
int const width;
|
||||
CharT const fill;
|
||||
template <class CharT, class Traits>
|
||||
friend std::basic_ostream<CharT, Traits>&
|
||||
operator<<(std::basic_ostream<CharT, Traits>& os, Divider const& d)
|
||||
{
|
||||
os << std::basic_string<CharT, Traits>(d.width, d.fill);
|
||||
return os;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a padded field with an optional fill character.
|
||||
*/
|
||||
struct Fpad
|
||||
{
|
||||
explicit Fpad(int width, int pad = 0, char fill = ' ') : width(width + pad), fill(fill)
|
||||
{
|
||||
}
|
||||
int const width;
|
||||
char const fill;
|
||||
template <class CharT, class Traits>
|
||||
friend std::basic_ostream<CharT, Traits>&
|
||||
operator<<(std::basic_ostream<CharT, Traits>& os, Fpad const& f)
|
||||
{
|
||||
os << std::basic_string<CharT, Traits>(f.width, f.fill);
|
||||
return os;
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename T>
|
||||
std::string
|
||||
to_string(T const& t)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << t;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* Justifies a field at the specified width.
|
||||
*/
|
||||
/** @{ */
|
||||
template <
|
||||
class CharT,
|
||||
class Traits = std::char_traits<CharT>,
|
||||
class Allocator = std::allocator<CharT>>
|
||||
class FieldT
|
||||
{
|
||||
public:
|
||||
using string_t = std::basic_string<CharT, Traits, Allocator>;
|
||||
FieldT(string_t const& text, int width, int pad, bool right)
|
||||
: text(text), width(width), pad(pad), right(right)
|
||||
{
|
||||
}
|
||||
string_t const text;
|
||||
int const width;
|
||||
int const pad;
|
||||
bool const right;
|
||||
template <class CharT2, class Traits2>
|
||||
friend std::basic_ostream<CharT2, Traits2>&
|
||||
operator<<(std::basic_ostream<CharT2, Traits2>& os, FieldT<CharT, Traits, Allocator> const& f)
|
||||
{
|
||||
std::size_t const length(f.text.length());
|
||||
if (f.right)
|
||||
{
|
||||
if (length < f.width)
|
||||
os << std::basic_string<CharT2, Traits2>(f.width - length, CharT2(' '));
|
||||
os << f.text;
|
||||
}
|
||||
else
|
||||
{
|
||||
os << f.text;
|
||||
if (length < f.width)
|
||||
os << std::basic_string<CharT2, Traits2>(f.width - length, CharT2(' '));
|
||||
}
|
||||
if (f.pad != 0)
|
||||
os << string_t(f.pad, CharT(' '));
|
||||
return os;
|
||||
}
|
||||
};
|
||||
|
||||
template <class CharT, class Traits, class Allocator>
|
||||
FieldT<CharT, Traits, Allocator>
|
||||
field(
|
||||
std::basic_string<CharT, Traits, Allocator> const& text,
|
||||
int width = 8,
|
||||
int pad = 0,
|
||||
bool right = false)
|
||||
{
|
||||
return FieldT<CharT, Traits, Allocator>(text, width, pad, right);
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
FieldT<CharT>
|
||||
field(CharT const* text, int width = 8, int pad = 0, bool right = false)
|
||||
{
|
||||
return FieldT<CharT, std::char_traits<CharT>, std::allocator<CharT>>(
|
||||
std::basic_string<CharT, std::char_traits<CharT>, std::allocator<CharT>>(text),
|
||||
width,
|
||||
pad,
|
||||
right);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
FieldT<char>
|
||||
field(T const& t, int width = 8, int pad = 0, bool right = false)
|
||||
{
|
||||
std::string const text(detail::to_string(t));
|
||||
return field(text, width, pad, right);
|
||||
}
|
||||
|
||||
template <class CharT, class Traits, class Allocator>
|
||||
FieldT<CharT, Traits, Allocator>
|
||||
rField(std::basic_string<CharT, Traits, Allocator> const& text, int width = 8, int pad = 0)
|
||||
{
|
||||
return FieldT<CharT, Traits, Allocator>(text, width, pad, true);
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
FieldT<CharT>
|
||||
rField(CharT const* text, int width = 8, int pad = 0)
|
||||
{
|
||||
return FieldT<CharT, std::char_traits<CharT>, std::allocator<CharT>>(
|
||||
std::basic_string<CharT, std::char_traits<CharT>, std::allocator<CharT>>(text),
|
||||
width,
|
||||
pad,
|
||||
true);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
FieldT<char>
|
||||
rField(T const& t, int width = 8, int pad = 0)
|
||||
{
|
||||
std::string const text(detail::to_string(t));
|
||||
return field(text, width, pad, true);
|
||||
}
|
||||
/** @} */
|
||||
|
||||
} // namespace beast
|
||||
@@ -1,26 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpld/peerfinder/PeerfinderManager.h>
|
||||
|
||||
#include <xrpl/beast/insight/Collector.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/config/BasicConfig.h>
|
||||
|
||||
#include <boost/asio/io_context.hpp>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace xrpl::PeerFinder {
|
||||
|
||||
/**
|
||||
* Create a new Manager.
|
||||
*/
|
||||
std::unique_ptr<Manager>
|
||||
makeManager(
|
||||
boost::asio::io_context& ioContext,
|
||||
clock_type& clock,
|
||||
beast::Journal journal,
|
||||
BasicConfig const& config,
|
||||
beast::insight::Collector::ptr const& collector);
|
||||
|
||||
} // namespace xrpl::PeerFinder
|
||||
Reference in New Issue
Block a user