3#include <xrpl/basics/Log.h>
4#include <xrpl/basics/contract.h>
5#include <xrpl/basics/random.h>
6#include <xrpl/beast/net/IPAddress.h>
7#include <xrpl/beast/net/IPAddressConversion.h>
8#include <xrpl/beast/net/IPEndpoint.h>
9#include <xrpl/beast/utility/Journal.h>
10#include <xrpl/beast/utility/PropertyStream.h>
11#include <xrpl/beast/utility/WrappedSink.h>
12#include <xrpl/beast/utility/instrumentation.h>
13#include <xrpl/peerfinder/Config.h>
14#include <xrpl/peerfinder/Slot.h>
15#include <xrpl/peerfinder/Types.h>
16#include <xrpl/peerfinder/detail/Bootcache.h>
17#include <xrpl/peerfinder/detail/Counts.h>
18#include <xrpl/peerfinder/detail/Fixed.h>
19#include <xrpl/peerfinder/detail/Handouts.h>
20#include <xrpl/peerfinder/detail/Livecache.h>
21#include <xrpl/peerfinder/detail/SlotImp.h>
22#include <xrpl/peerfinder/detail/Source.h>
23#include <xrpl/peerfinder/detail/Store.h>
24#include <xrpl/protocol/PublicKey.h>
26#include <boost/asio/error.hpp>
53template <
class Checker>
183 if (addresses.
empty())
185 JLOG(
journal.info()) <<
"Could not resolve fixed slot '" << name <<
"'";
189 for (
auto const& remoteAddress : addresses)
191 if (remoteAddress.port() == 0)
194 "Port not specified for address:" + remoteAddress.toString());
197 auto result(
fixed_.emplace(
205 <<
"' at " << remoteAddress;
218 boost::system::error_code ec)
220 if (ec == boost::asio::error::operation_aborted)
224 auto const iter(
slots.find(remoteAddress));
225 if (iter ==
slots.end())
229 <<
" but the connection was closed";
244 JLOG(
journal.error()) <<
"Logic testing " << iter->first <<
" with error, "
252 JLOG(
journal.debug()) <<
"Logic testing " << checkedAddress <<
" succeeded";
263 <<
" on local " << localEndpoint;
268 if (isPublic(remoteEndpoint))
271 if (count + 1 >
config_.ipLimit)
274 << remoteEndpoint <<
" because of ip limits.";
280 if (
slots.contains(remoteEndpoint))
283 << remoteEndpoint <<
" as duplicate incoming";
292 auto const result(
slots.emplace(slot->remoteEndpoint(), slot));
296 "xrpl::peer_finder::Logic::new_inbound_slot : remote endpoint "
304 return {result.first->second, Result::Success};
316 if (
slots.contains(remoteEndpoint))
319 << remoteEndpoint <<
" as duplicate connect";
328 auto const result =
slots.emplace(slot->remoteEndpoint(), slot);
332 "xrpl::peer_finder::Logic::new_outbound_slot : remote endpoint "
341 return {result.first->second, Result::Success};
350 JLOG(
journal.trace()) <<
"Logic connected on local " << localEndpoint;
356 slots.contains(slot->remoteEndpoint()),
357 "xrpl::peer_finder::Logic::onConnected : valid slot input");
359 slot->localEndpoint(localEndpoint);
363 auto const iter(
slots.find(localEndpoint));
364 if (iter !=
slots.end())
367 iter->second->localEndpoint() == slot->remoteEndpoint(),
368 "xrpl::peer_finder::Logic::onConnected : local and remote "
369 "endpoints do match");
370 JLOG(
journal.warn()) <<
"Logic dropping as self connect";
388 JLOG(
journal.debug()) <<
"Logic handshake " << slot->remoteEndpoint() <<
" with "
389 << (reserved ?
"reserved " :
"") <<
"key " << key;
395 slots.contains(slot->remoteEndpoint()),
396 "xrpl::peer_finder::Logic::activate : valid slot input");
400 "xrpl::peer_finder::Logic::activate : valid slot state");
403 if (
keys.contains(key))
404 return Result::DuplicatePeer;
409 slot->reserved(reserved);
413 if (!
counts_.canActivate(*slot))
415 if (!slot->inbound())
416 bootcache.onSuccess(slot->remoteEndpoint());
417 if (slot->inbound() &&
counts_.inMax() == 0)
418 return Result::InboundDisabled;
424 slot->publicKey(key);
426 [[maybe_unused]]
bool const inserted =
keys.insert(key).second;
428 XRPL_ASSERT(inserted,
"xrpl::peer_finder::Logic::activate : public key inserted");
433 slot->activate(
clock.now());
436 if (!slot->inbound())
437 bootcache.onSuccess(slot->remoteEndpoint());
440 if (slot->fixed() && !slot->inbound())
442 auto iter(
fixed_.find(slot->remoteEndpoint()));
446 "peer_finder::Logic::activate(): remote_endpoint "
447 "missing from fixed_");
450 iter->second.success(
clock.now());
451 JLOG(
journal.trace()) <<
"Logic fixed success";
454 return Result::Success;
469 return std::move(h.
list());
488 auto needed(
counts_.attemptsNeeded());
495 for (
auto const& s :
slots)
497 auto const result(
squelches.insert(s.second->remoteEndpoint().address()));
521 <<
counts_.attempts() <<
" attempts";
543 << ((h.
list().size() > 1) ?
"endpoints" :
"endpoint");
549 <<
counts_.attempts() <<
" attempts";
577 << ((h.
list().size() > 1) ?
"addresses" :
"address");
639 for (
auto& t : targets)
648 for (
auto const& t : targets)
651 auto const& list = t.list();
654 JLOG(
journal.trace()) <<
"Logic sending " << list.size()
655 << ((list.size() == 1) ?
" endpoint" :
" endpoints");
674 for (
auto const& entry :
slots)
675 entry.second->expire();
689 bool neighbor(
false);
690 for (
auto iter = list.
begin(); iter != list.
end();)
699 iter = list.
erase(iter);
715 << ep.
address <<
" for extra self";
716 iter = list.
erase(iter);
725 << ep.
address <<
" as invalid";
726 iter = list.
erase(iter);
731 if (
std::any_of(list.
begin(), iter, [ep](Endpoints::value_type
const& other) {
732 return ep.address == other.address;
736 << ep.
address <<
" as duplicate";
737 iter = list.
erase(iter);
763 JLOG(
journal.trace()) <<
"Endpoints contained " << list.
size()
764 << ((list.
size() > 1) ?
" entries" :
" entry");
770 slots.contains(slot->remoteEndpoint()),
771 "xrpl::peer_finder::Logic::onEndpoints : valid slot input");
776 "xrpl::peer_finder::Logic::onEndpoints : valid slot state");
781 if (slot->whenAcceptEndpoints > now)
786 for (
auto const& ep : list)
788 XRPL_ASSERT(ep.hops,
"xrpl::peer_finder::Logic::onEndpoints : nonzero hops");
790 slot->recent.insert(ep.address, ep.hops);
797 if (slot->connectivityCheckInProgress)
800 <<
"Logic testing " << ep.address <<
" already in progress";
807 slot->connectivityCheckInProgress =
true;
814 [
this, remoteAddress = slot->remoteEndpoint(), checkedAddress = ep.address](
815 boost::system::error_code
const& ec) {
816 checkComplete(remoteAddress, checkedAddress, ec);
828 if (!slot->canAccept)
849 auto const iter =
slots.find(slot->remoteEndpoint());
851 if (iter ==
slots.end())
854 "peer_finder::Logic::remove(): remote_endpoint "
855 "missing from slots_");
862 if (slot->publicKey() != std::nullopt)
864 auto const iter =
keys.find(*slot->publicKey());
866 if (iter ==
keys.end())
869 "peer_finder::Logic::remove(): public_key missing "
882 "peer_finder::Logic::remove(): remote_endpoint "
883 "address missing from connectedAddresses_");
906 auto iter(
fixed_.find(slot->remoteEndpoint()));
910 "peer_finder::Logic::on_closed(): remote_endpoint "
911 "missing from fixed_");
914 iter->second.failure(
clock.now());
915 JLOG(
journal.debug()) <<
"Logic fixed failed";
919 switch (slot->state())
922 JLOG(
journal.trace()) <<
"Logic accept failed";
927 bootcache.onFailure(slot->remoteEndpoint());
936 JLOG(
journal.trace()) <<
"Logic close";
940 JLOG(
journal.trace()) <<
"Logic finished";
946 "xrpl::peer_finder::Logic::on_closed : invalid slot "
958 bootcache.onFailure(slot->remoteEndpoint());
962 template <
class FwdIter>
964 onRedirects(FwdIter first, FwdIter last, boost::asio::ip::tcp::endpoint
const& remoteAddress);
974 fixed_, [&endpoint](
auto const& entry) {
return entry.first == endpoint; });
984 fixed_, [&address](
auto const& entry) {
return entry.first.address() == address; });
996 template <
class Container>
1000 auto const now(
clock.now());
1001 for (
auto iter =
fixed_.begin(); needed && iter !=
fixed_.end(); ++iter)
1003 auto const& address(iter->first.address());
1006 return address == v.first.address();
1009 squelches.insert(iter->first.address());
1010 c.push_back(iter->first);
1044 for (
auto const& addr : list)
1069 source->fetch(results,
journal);
1083 << ((count == 1) ?
"address" :
"addresses") <<
" from "
1089 <<
"'" << source->name() <<
"' fetch, "
1090 << results.
error.message();
1104 if (isUnspecified(address))
1106 if (!isPublic(address))
1108 if (address.
port() == 0)
1122 for (
auto const& entry :
slots)
1125 SlotImp const& slot(*entry.second);
1130 item[
"inbound"] =
"yes";
1132 item[
"fixed"] =
"yes";
1134 item[
"reserved"] =
"yes";
1213template <
class Checker>
1214template <
class FwdIter>
1219 boost::asio::ip::tcp::endpoint
const& remoteAddress)
1228 <<
" redirect IPs from " << remoteAddress;
std::chrono::steady_clock::time_point time_point
A generic endpoint for log messages.
Wraps a Journal::Sink to prefix its output with a string.
A version-independent IP address and port combination.
Endpoint atPort(Port port) const
Returns a new Endpoint with a different port.
Port port() const
Returns the port number on the endpoint.
Address const & address() const
Returns the address portion of this endpoint.
Stores IP addresses useful for gaining initial connections.
Tests remote listening sockets to make sure they are connectable.
Receives handouts for making automatic connections.
beast::aged_set< beast::ip::Address > Squelches
bool tryInsert(beast::ip::Endpoint const &endpoint)
Manages the count of available connections for the various slots.
The Livecache holds the short-lived relayed Endpoint messages.
bool fixed(beast::ip::Endpoint const &endpoint) const
void onFailure(SlotImp::ptr const &slot)
std::vector< Endpoint > redirect(SlotImp::ptr const &slot)
Return a list of addresses suitable for redirection.
int addBootcacheAddresses(IPAddresses const &list)
std::recursive_mutex lock
std::vector< std::pair< std::shared_ptr< Slot >, std::vector< Endpoint > > > buildEndpointsForPeers()
Result activate(SlotImp::ptr const &slot, PublicKey const &key, bool reserved)
void preprocess(SlotImp::ptr const &slot, Endpoints &list)
void getFixed(std::size_t needed, Container &c, ConnectHandouts::Squelches &squelches)
Adds eligible Fixed addresses for outbound attempts.
std::vector< beast::ip::Endpoint > autoconnect()
Create new outbound connection attempts as needed.
void addFixedPeer(std::string_view name, std::vector< beast::ip::Endpoint > const &addresses)
void fetch(std::shared_ptr< Source > const &source)
std::multiset< beast::ip::Address > connectedAddresses
std::set< PublicKey > keys
void config(Config const &c)
void onClosed(SlotImp::ptr const &slot)
static std::string stateString(Slot::State state)
bool fixed(beast::ip::Address const &address) const
void addSource(std::shared_ptr< Source > const &source)
std::map< beast::ip::Endpoint, Fixed > fixed_
void writeSlots(beast::PropertyStream::Set &set, Slots const &slots)
void checkComplete(beast::ip::Endpoint const &remoteAddress, beast::ip::Endpoint const &checkedAddress, boost::system::error_code ec)
std::pair< SlotImp::ptr, Result > newOutboundSlot(beast::ip::Endpoint const &remoteEndpoint)
std::pair< SlotImp::ptr, Result > newInboundSlot(beast::ip::Endpoint const &localEndpoint, beast::ip::Endpoint const &remoteEndpoint)
bool onConnected(SlotImp::ptr const &slot, beast::ip::Endpoint const &localEndpoint)
void onRedirects(FwdIter first, FwdIter last, boost::asio::ip::tcp::endpoint const &remoteAddress)
void stop()
Stop the logic.
std::shared_ptr< Source > fetchSource
bool isValidAddress(beast::ip::Endpoint const &address)
void addStaticSource(std::shared_ptr< Source > const &source)
Counts const & counts() const
void remove(SlotImp::ptr const &slot)
void onWrite(beast::PropertyStream::Map &map)
void onEndpoints(SlotImp::ptr const &slot, Endpoints list)
void addFixedPeer(std::string_view name, beast::ip::Endpoint const &ep)
Logic(clock_type &clock, Store &store, Checker &checker, beast::Journal journal)
clock_type::time_point whenBroadcast
std::map< beast::ip::Endpoint, std::shared_ptr< SlotImp > > Slots
std::vector< std::shared_ptr< Source > > sources
ConnectHandouts::Squelches squelches
Receives handouts for redirecting a connection.
std::vector< Endpoint > & list()
bool connectivityCheckInProgress
std::shared_ptr< SlotImp > ptr
std::optional< beast::ip::Endpoint > const & localEndpoint() const override
The local endpoint of the socket, when known.
State state() const override
Returns the state of the connection.
beast::ip::Endpoint const & remoteEndpoint() const override
The remote endpoint of socket.
void setListeningPort(std::uint16_t port)
std::string prefix() const
bool reserved() const override
Returns true if this is a reserved connection.
bool fixed() const override
Returns true if this is a fixed connection.
bool inbound() const override
Returns true if this is an inbound connection.
Abstract persistence for PeerFinder data.
T emplace_back(T... args)
T forward_as_tuple(T... args)
boost::asio::ip::address Address
boost::asio::ip::address_v6 AddressV6
std::size_t expire(AgedContainer &c, std::chrono::duration< Rep, Period > const &age)
Expire aged container items past the specified age.
constexpr std::uint32_t kMaxHops
constexpr std::chrono::seconds kRecentAttemptDuration(60)
constexpr std::chrono::seconds kSecondsPerMessage(151)
static constexpr auto kMaxRedirects
Max redirects we will accept from one connection.
constexpr std::uint32_t kNumberOfEndpointsMax
std::vector< beast::ip::Endpoint > IPAddresses
Represents a set of addresses.
Result
Possible results from activating a slot.
std::vector< Endpoint > Endpoints
A set of Endpoint used for connecting.
beast::AbstractClock< std::chrono::steady_clock > clock_type
void handout(TargetFwdIter first, TargetFwdIter last, SeqFwdIter seqFirst, SeqFwdIter seqLast)
Distributes objects to targets according to business rules.
std::string_view to_string(Result result) noexcept
Converts a Result enum value to its string representation.
bool set(T &target, std::string const &name, Section const §ion)
Set a value from a configuration Section If the named value is not found or doesn't parse as a T,...
void logicError(std::string const &how) noexcept
Called when faulty logic causes a broken invariant.
beast::xor_shift_engine & defaultPrng()
Return the default random engine.
XRPL_NO_SANITIZE_ADDRESS void Throw(Args &&... args)
static ip::Endpoint fromAsio(boost::asio::ip::address const &address)
PeerFinder configuration settings.
Describes a connectable peer address along with some metadata.
beast::ip::Endpoint address
boost::system::error_code error