clang-tidy

This commit is contained in:
Vito
2026-07-22 14:08:01 +02:00
parent 2193c99f6c
commit 78cc3ae1c5
11 changed files with 367 additions and 380 deletions

View File

@@ -341,7 +341,6 @@ words:
- unsquelched
- unsquelching
- unvalidated
- unviable
- unveto
- unvetoed
- upvotes

View File

@@ -1,24 +1,8 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright 2020 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <test/jtx/Env.h>
#include <test/jtx/envconfig.h>
#include <xrpld/app/main/Application.h>
#include <xrpld/core/Config.h>
#include <xrpld/overlay/Message.h>
#include <xrpld/overlay/Peer.h>
#include <xrpld/overlay/ReduceRelayCommon.h>
@@ -26,22 +10,48 @@
#include <xrpld/overlay/SquelchStore.h>
#include <xrpld/overlay/detail/Handshake.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/basics/random.h>
#include <xrpl/beast/unit_test.h>
#include <xrpl/beast/clock/manual_clock.h>
#include <xrpl/beast/net/IPAddress.h>
#include <xrpl/beast/net/IPEndpoint.h>
#include <xrpl/beast/unit_test/suite.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/json/json_value.h>
#include <xrpl/protocol/KeyType.h>
#include <xrpl/protocol/PublicKey.h>
#include <xrpl/protocol/SecretKey.h>
#include <xrpl.pb.h>
#include <algorithm>
#include <cassert>
#include <chrono>
#include <cstddef>
#include <cstdint>
#include <functional>
#include <iostream>
#include <iterator>
#include <memory>
#include <numeric>
#include <optional>
#include <ostream>
#include <random>
#include <set>
#include <sstream>
#include <stdexcept>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
namespace xrpl::test {
using namespace std::chrono;
template <class Clock>
class extended_manual_clock : public beast::ManualClock<Clock>
class ExtendedManualClock : public beast::ManualClock<Clock>
{
public:
using typename beast::ManualClock<Clock>::duration;
@@ -64,11 +74,11 @@ using PeerWPtr = std::weak_ptr<Peer>;
using SquelchCB = std::function<void(PublicKey const&, PeerWPtr const&, std::uint32_t)>;
using UnsquelchCB = std::function<void(PublicKey const&, PeerWPtr const&)>;
using LinkIterCB = std::function<void(Link&, MessageSPtr)>;
using TestStopwatch = extended_manual_clock<std::chrono::steady_clock>;
using TestStopwatch = ExtendedManualClock<std::chrono::steady_clock>;
static constexpr std::uint32_t MAX_PEERS = 10;
static constexpr std::uint32_t MAX_VALIDATORS = 10;
static constexpr std::uint32_t MAX_MESSAGES = 200000;
static constexpr std::uint32_t maxPeers = 10;
static constexpr std::uint32_t maxValidators = 10;
static constexpr std::uint32_t maxMessages = 200000;
/**
* Simulate two entities - peer directly connected to the server
@@ -77,14 +87,12 @@ static constexpr std::uint32_t MAX_MESSAGES = 200000;
class PeerPartial : public Peer
{
public:
PeerPartial() : nodePublicKey_(derivePublicKey(KeyType::Ed25519, randomSecretKey()))
PeerPartial() : nodePublicKey(derivePublicKey(KeyType::Ed25519, randomSecretKey()))
{
}
PublicKey nodePublicKey_;
virtual ~PeerPartial()
{
}
PublicKey nodePublicKey;
~PeerPartial() override = default;
virtual void
onMessage(MessageSPtr const& m, SquelchCB f) = 0;
virtual void
@@ -100,7 +108,7 @@ public:
send(std::shared_ptr<Message> const& m) override
{
}
beast::IP::Endpoint
[[nodiscard]] beast::IP::Endpoint
getRemoteAddress() const override
{
return {};
@@ -109,43 +117,43 @@ public:
charge(Resource::Charge const& fee, std::string const& context = {}) override
{
}
bool
[[nodiscard]] bool
cluster() const override
{
return false;
}
bool
[[nodiscard]] bool
isHighLatency() const override
{
return false;
}
int
[[nodiscard]] int
getScore(bool) const override
{
return 0;
}
PublicKey const&
[[nodiscard]] PublicKey const&
getNodePublic() const override
{
return nodePublicKey_;
return nodePublicKey;
}
json::Value
json() override
{
return {};
}
std::string const&
[[nodiscard]] std::string const&
fingerprint() const override
{
static std::string const kFingerprint{};
return kFingerprint;
}
bool
[[nodiscard]] bool
supportsFeature(ProtocolFeature f) const override
{
return false;
}
std::optional<std::size_t>
[[nodiscard]] std::optional<std::size_t>
publisherListSequence(PublicKey const&) const override
{
return {};
@@ -154,13 +162,13 @@ public:
setPublisherListSequence(PublicKey const&, std::size_t const) override
{
}
uint256 const&
[[nodiscard]] uint256 const&
getClosedLedgerHash() const override
{
static uint256 hash{};
static uint256 const hash{};
return hash;
}
bool
[[nodiscard]] bool
hasLedger(uint256 const& hash, std::uint32_t seq) const override
{
return false;
@@ -169,7 +177,7 @@ public:
ledgerRange(std::uint32_t& minSeq, std::uint32_t& maxSeq) const override
{
}
bool
[[nodiscard]] bool
hasTxSet(uint256 const& hash) const override
{
return false;
@@ -183,12 +191,12 @@ public:
{
return false;
}
bool
[[nodiscard]] bool
compressionEnabled() const override
{
return false;
}
bool
[[nodiscard]] bool
txReduceRelayEnabled() const override
{
return false;
@@ -248,11 +256,8 @@ class Link
using Latency = std::pair<milliseconds, milliseconds>;
public:
Link(
Validator& validator,
PeerSPtr peer,
Latency const& latency = {milliseconds(5), milliseconds(15)})
: validator_(validator), peer_(peer), latency_(latency), up_(true)
Link(Validator& validator, PeerSPtr peer, Latency latency = {milliseconds(5), milliseconds(15)})
: validator_(validator), peer_(peer), latency_(std::move(latency))
{
auto sp = peer_.lock();
assert(sp);
@@ -297,7 +302,7 @@ private:
Validator& validator_;
PeerWPtr peer_;
Latency latency_;
bool up_;
bool up_{true};
};
/**
@@ -308,12 +313,11 @@ class Validator
using Links = std::unordered_map<Peer::id_t, LinkSPtr>;
public:
Validator() : pkey_(std::get<0>(randomKeyPair(KeyType::Ed25519)))
Validator() : pkey_(std::get<0>(randomKeyPair(KeyType::Ed25519))), id_(sid_++)
{
protocol::TMValidation v;
v.set_validation("validation");
message_ = std::make_shared<Message>(v, protocol::mtVALIDATION, pkey_);
id_ = sid_++;
}
Validator(Validator const&) = default;
Validator(Validator&&) = default;
@@ -362,7 +366,7 @@ public:
}
void
for_links(std::vector<Peer::id_t> peers, LinkIterCB f)
forLinks(std::vector<Peer::id_t> peers, LinkIterCB f)
{
for (auto id : peers)
{
@@ -372,12 +376,10 @@ public:
}
void
for_links(LinkIterCB f, bool simulateSlow = false)
forLinks(LinkIterCB f, bool simulateSlow = false)
{
std::vector<LinkSPtr> v;
std::transform(links_.begin(), links_.end(), std::back_inserter(v), [](auto& kv) {
return kv.second;
});
std::ranges::transform(links_, std::back_inserter(v), [](auto& kv) { return kv.second; });
std::random_device d;
std::mt19937 g(d());
std::shuffle(v.begin(), v.end(), g);
@@ -394,7 +396,7 @@ public:
void
send(std::vector<Peer::id_t> peers, SquelchCB f)
{
for_links(peers, [&](Link& link, MessageSPtr m) { link.send(m, f); });
forLinks(peers, [&](Link& link, MessageSPtr m) { link.send(m, f); });
}
/**
@@ -403,7 +405,7 @@ public:
void
send(SquelchCB f)
{
for_links([&](Link& link, MessageSPtr m) { link.send(m, f); });
forLinks([&](Link& link, MessageSPtr m) { link.send(m, f); });
}
MessageSPtr
@@ -413,7 +415,7 @@ public:
}
std::uint16_t
id()
id() const
{
return id_;
}
@@ -467,12 +469,11 @@ class PeerSim : public PeerPartial, public std::enable_shared_from_this<PeerSim>
{
public:
PeerSim(Overlay& overlay, beast::Journal journal)
: overlay_(overlay), squelchStore_(journal, overlay_.clock())
: id_(sid_++), overlay_(overlay), squelchStore_(journal, overlay_.clock())
{
id_ = sid_++;
}
~PeerSim() = default;
~PeerSim() override = default;
Peer::id_t
id() const override
@@ -503,11 +504,11 @@ public:
/**
* Remote Peer (Directly connected Peer)
*/
virtual void
void
onMessage(protocol::TMSquelch const& squelch) override
{
auto validator = squelch.validatorpubkey();
PublicKey key(Slice(validator.data(), validator.size()));
PublicKey const key(Slice(validator.data(), validator.size()));
squelchStore_.handleSquelch(
key, squelch.squelch(), std::chrono::seconds{squelch.squelchduration()});
}
@@ -529,7 +530,7 @@ public:
{
}
~OverlaySim() = default;
~OverlaySim() override = default;
void
clear()
@@ -544,10 +545,12 @@ public:
{
auto const& it = slots_.getSlots().find(validator);
if (it != slots_.getSlots().end())
{
return std::count_if(
it->second.getPeers().begin(), it->second.getPeers().end(), [&](auto const& it) {
return (it.second.state == state);
});
}
return 0;
}
@@ -578,7 +581,7 @@ public:
addPeer(bool useCache = true)
{
PeerSPtr peer{};
Peer::id_t id;
Peer::id_t id = 0;
if (peersCache_.empty() || !useCache)
{
peer = std::make_shared<PeerSim>(*this, logs_.journal("Squelch"));
@@ -626,8 +629,7 @@ public:
for (auto& [id, _] : peers_)
{
(void)_;
if (id > maxId)
maxId = id;
maxId = std::max<unsigned int>(id, maxId);
}
deletePeer(maxId, false);
@@ -654,8 +656,10 @@ public:
std::set<Peer::id_t> r;
for (auto const& [id, info] : it->second.getPeers())
{
if (info.state == reduce_relay::PeerState::Selected)
r.insert(id);
}
return r;
}
@@ -664,14 +668,14 @@ public:
isSelected(PublicKey const& validator, Peer::id_t peer)
{
auto selected = getSelected(validator);
return selected.find(peer) != selected.end();
return selected.contains(peer);
}
Peer::id_t
getSelectedPeer(PublicKey const& validator)
{
auto selected = getSelected(validator);
assert(selected.size());
assert(!selected.empty());
return *selected.begin();
}
@@ -684,7 +688,7 @@ public:
auto r = std::unordered_map<Peer::id_t, reduce_relay::Slot::PeerInfo>();
for (auto const& [id, info] : it->second.getPeers())
r.emplace(std::make_pair(id, info));
r.emplace(id, info);
return r;
}
@@ -741,8 +745,8 @@ public:
void
init()
{
validators_.resize(MAX_VALIDATORS);
for (int p = 0; p < MAX_PEERS; p++)
validators_.resize(maxValidators);
for (int p = 0; p < maxPeers; p++)
{
auto peer = overlay_.addPeer();
for (auto& v : validators_)
@@ -786,7 +790,7 @@ public:
void
purgePeers()
{
while (overlay_.getNumPeers() > MAX_PEERS)
while (overlay_.getNumPeers() > maxPeers)
deleteLastPeer();
}
@@ -806,13 +810,16 @@ public:
void
enableLink(std::uint16_t validatorId, Peer::id_t peer, bool enable)
{
auto it = std::find_if(
validators_.begin(), validators_.end(), [&](auto& v) { return v.id() == validatorId; });
auto it = std::ranges::find_if(validators_, [&](auto& v) { return v.id() == validatorId; });
assert(it != validators_.end());
if (enable)
{
it->linkUp(peer);
}
else
{
it->linkDown(peer);
}
}
void
@@ -826,21 +833,21 @@ public:
squelch.set_squelch(false);
for (auto& v : validators_)
{
PublicKey key = v;
PublicKey const key = v;
squelch.clear_validatorpubkey();
squelch.set_validatorpubkey(key.data(), key.size());
v.for_links({peer}, [&](Link& l, MessageSPtr) {
v.forLinks({peer}, [&](Link& l, MessageSPtr) {
std::dynamic_pointer_cast<PeerSim>(l.getPeer())->send(squelch);
});
}
}
void
for_rand(std::uint32_t min, std::uint32_t max, std::function<void(std::uint32_t)> f)
static void
forRand(std::uint32_t min, std::uint32_t max, std::function<void(std::uint32_t)> f)
{
auto size = max - min;
std::vector<std::uint32_t> s(size);
std::iota(s.begin(), s.end(), min);
std::ranges::iota(s, min);
std::random_device d;
std::mt19937 g(d());
std::shuffle(s.begin(), s.end(), g);
@@ -851,8 +858,8 @@ public:
void
propagate(
LinkIterCB link,
std::uint16_t nValidators = MAX_VALIDATORS,
std::uint32_t nMessages = MAX_MESSAGES,
std::uint16_t nValidators = maxValidators,
std::uint32_t nMessages = maxMessages,
bool purge = true)
{
if (purge)
@@ -864,7 +871,7 @@ public:
for (int m = 0; m < nMessages; ++m)
{
overlay_.clock().randAdvance(milliseconds(1800), milliseconds(2200));
for_rand(0, nValidators, [&](std::uint32_t v) { validators_[v].for_links(link); });
forRand(0, nValidators, [&](std::uint32_t v) { validators_[v].forLinks(link); });
}
}
@@ -929,14 +936,14 @@ protected:
/**
* Send squelch (if duration is set) or unsquelch (if duration not set)
*/
Peer::id_t
static Peer::id_t
sendSquelch(
PublicKey const& validator,
PeerWPtr const& peerPtr,
std::optional<std::uint32_t> duration)
{
protocol::TMSquelch squelch;
bool res = duration ? true : false;
bool const res = static_cast<bool>(duration);
squelch.set_squelch(res);
squelch.set_validatorpubkey(validator.data(), validator.size());
if (res)
@@ -955,15 +962,15 @@ protected:
// time in any quantity
struct Event
{
State state_ = State::Off;
std::uint32_t cnt_ = 0;
std::uint32_t handledCnt_ = 0;
State state = State::Off;
std::uint32_t cnt = 0;
std::uint32_t handledCnt = 0;
bool isSelected_ = false;
Peer::id_t peer_;
std::uint16_t validator_;
std::optional<PublicKey> key_;
TestStopwatch::time_point time_;
bool handled_ = false;
Peer::id_t peer_{};
std::uint16_t validator{};
std::optional<PublicKey> key;
TestStopwatch::time_point time;
bool handled = false;
};
/**
@@ -999,12 +1006,14 @@ protected:
for (auto s : selected)
str << s << " ";
if (log)
{
std::cout << (double)std::chrono::duration_cast<milliseconds>(
now.time_since_epoch())
.count() /
1000.
<< " random, squelched, validator: " << validator.id()
<< " peers: " << str.str() << std::endl;
}
auto countingState = network_.overlay().isCountingState(validator);
BEAST_EXPECT(
countingState == false &&
@@ -1013,15 +1022,15 @@ protected:
// Trigger Link Down or Peer Disconnect event
// Only one Link Down at a time
if (events[EventType::LinkDown].state_ == State::Off)
if (events[EventType::LinkDown].state == State::Off)
{
auto update = [&](EventType event) {
events[event].cnt_++;
events[event].validator_ = validator.id();
events[event].key_ = validator;
events[event].cnt++;
events[event].validator = validator.id();
events[event].key = validator;
events[event].peer_ = link.peerId();
events[event].state_ = State::On;
events[event].time_ = now;
events[event].state = State::On;
events[event].time = now;
if (event == EventType::LinkDown)
{
network_.enableLink(validator.id(), link.peerId(), false);
@@ -1029,7 +1038,11 @@ protected:
network_.overlay().isSelected(validator, link.peerId());
}
else
events[event].isSelected_ = network_.isSelected(link.peerId());
{
{
events[event].isSelected_ = network_.isSelected(link.peerId());
}
}
};
auto r = randInt(0, 1000);
if (r == (int)EventType::LinkDown || r == (int)EventType::PeerDisconnected)
@@ -1038,28 +1051,28 @@ protected:
}
}
if (events[EventType::PeerDisconnected].state_ == State::On)
if (events[EventType::PeerDisconnected].state == State::On)
{
auto& event = events[EventType::PeerDisconnected];
bool allCounting = network_.allCounting(event.peer_);
bool const allCounting = network_.allCounting(event.peer_);
network_.overlay().deletePeer(
event.peer_, [&](PublicKey const& v, PeerWPtr const& peerPtr) {
if (event.isSelected_)
sendSquelch(v, peerPtr, {});
event.handled_ = true;
event.handled = true;
});
// Should only be unsquelched if the peer is in Selected state
// If in Selected state it's possible unsquelching didn't
// take place because there is no peers in Squelched state in
// any of the slots where the peer is in Selected state
// (allCounting is true)
bool handled = (event.isSelected_ == false && !event.handled_) ||
(event.isSelected_ == true && (event.handled_ || allCounting));
bool const handled = (!event.isSelected_ && !event.handled) ||
(event.isSelected_ && (event.handled || allCounting));
BEAST_EXPECT(handled);
event.state_ = State::Off;
event.state = State::Off;
event.isSelected_ = false;
event.handledCnt_ += handled;
event.handled_ = false;
event.handledCnt += handled;
event.handled = false;
network_.onDisconnectPeer(event.peer_);
}
@@ -1076,10 +1089,10 @@ protected:
// 4) peer is in Slot's peers_ (if not then it is deleted
// by Slots::deleteIdlePeers())
bool mustHandle = false;
if (event.state_ == State::On && BEAST_EXPECT(event.key_))
if (event.state == State::On && BEAST_EXPECT(event.key))
{
event.isSelected_ = network_.overlay().isSelected(*event.key_, event.peer_);
auto peers = network_.overlay().getPeers(*event.key_);
event.isSelected_ = network_.overlay().isSelected(*event.key, event.peer_);
auto peers = network_.overlay().getPeers(*event.key);
auto d = std::chrono::duration_cast<std::chrono::milliseconds>(
now.time_since_epoch())
@@ -1090,46 +1103,48 @@ protected:
mustHandle = event.isSelected_ &&
d > milliseconds(reduce_relay::kIdled).count() &&
network_.overlay().inState(
*event.key_, reduce_relay::PeerState::Squelched) > 0 &&
peers.find(event.peer_) != peers.end();
network_.overlay().inState(*event.key, reduce_relay::PeerState::Squelched) >
0 &&
peers.contains(event.peer_);
}
network_.overlay().deleteIdlePeers([&](PublicKey const& v, PeerWPtr const& ptr) {
event.handled_ = true;
if (mustHandle && v == event.key_)
event.handled = true;
if (mustHandle && v == event.key)
{
event.state_ = State::WaitReset;
event.state = State::WaitReset;
sendSquelch(validator, ptr, {});
}
});
bool handled = (event.handled_ && event.state_ == State::WaitReset) ||
(!event.handled_ && !mustHandle);
bool const handled = (event.handled && event.state == State::WaitReset) ||
(!event.handled && !mustHandle);
BEAST_EXPECT(handled);
}
if (event.state_ == State::WaitReset ||
(event.state_ == State::On &&
(now - event.time_ > (reduce_relay::kIdled + seconds(2)))))
if (event.state == State::WaitReset ||
(event.state == State::On &&
(now - event.time > (reduce_relay::kIdled + seconds(2)))))
{
bool handled = event.state_ == State::WaitReset || !event.handled_;
bool const handled = event.state == State::WaitReset || !event.handled;
BEAST_EXPECT(handled);
event.state_ = State::Off;
event.state = State::Off;
event.isSelected_ = false;
event.handledCnt_ += handled;
event.handled_ = false;
network_.enableLink(event.validator_, event.peer_, true);
event.handledCnt += handled;
event.handled = false;
network_.enableLink(event.validator, event.peer_, true);
}
});
auto& down = events[EventType::LinkDown];
auto& disconnected = events[EventType::PeerDisconnected];
// It's possible the last Down Link event is not handled
BEAST_EXPECT(down.handledCnt_ >= down.cnt_ - 1);
BEAST_EXPECT(down.handledCnt >= down.cnt - 1);
// All Peer Disconnect events must be handled
BEAST_EXPECT(disconnected.cnt_ == disconnected.handledCnt_);
BEAST_EXPECT(disconnected.cnt == disconnected.handledCnt);
if (log)
std::cout << "link down count: " << down.cnt_ << "/" << down.handledCnt_
<< " peer disconnect count: " << disconnected.cnt_ << "/"
<< disconnected.handledCnt_;
{
std::cout << "link down count: " << down.cnt << "/" << down.handledCnt
<< " peer disconnect count: " << disconnected.cnt << "/"
<< disconnected.handledCnt;
}
}
bool
@@ -1203,7 +1218,7 @@ protected:
{
BEAST_EXPECT(
squelched ==
MAX_PEERS - env_.app().config().vpReduceRelaySquelchMaxSelectedPeers);
maxPeers - env_.app().config().vpReduceRelaySquelchMaxSelectedPeers);
squelchEvents++;
}
},
@@ -1272,8 +1287,7 @@ protected:
network_.overlay().deletePeer(
id, [&](PublicKey const& key, PeerWPtr const& peer) { unsquelched++; });
BEAST_EXPECT(
unsquelched ==
MAX_PEERS - env_.app().config().vpReduceRelaySquelchMaxSelectedPeers);
unsquelched == maxPeers - env_.app().config().vpReduceRelaySquelchMaxSelectedPeers);
BEAST_EXPECT(checkCounting(network_.validator(0), true));
});
}
@@ -1294,8 +1308,7 @@ protected:
[&](PublicKey const& key, PeerWPtr const& peer) { unsquelched++; });
BEAST_EXPECT(
unsquelched ==
MAX_PEERS - env_.app().config().vpReduceRelaySquelchMaxSelectedPeers);
unsquelched == maxPeers - env_.app().config().vpReduceRelaySquelchMaxSelectedPeers);
BEAST_EXPECT(checkCounting(network_.validator(0), true));
});
}
@@ -1310,7 +1323,7 @@ protected:
network_.overlay().clock().advance(seconds(601));
BEAST_EXPECT(propagateAndSquelch(log, true));
auto peers = network_.overlay().getPeers(network_.validator(0));
auto it = std::find_if(peers.begin(), peers.end(), [&](auto it) {
auto it = std::ranges::find_if(peers, [&](auto it) {
return it.second.state == reduce_relay::PeerState::Squelched;
});
assert(it != peers.end());
@@ -1328,7 +1341,7 @@ protected:
doTest("Test Config - squelch enabled (legacy)", log, [&](bool log) {
Config c;
std::string toLoad(R"rippleConfig(
std::string const toLoad(R"rippleConfig(
[reduce_relay]
vp_enable=1
)rippleConfig");
@@ -1361,7 +1374,7 @@ vp_enable=0
doTest("Test Config - squelch enabled", log, [&](bool log) {
Config c;
std::string toLoad(R"rippleConfig(
std::string const toLoad(R"rippleConfig(
[reduce_relay]
vp_base_squelch_enable=1
)rippleConfig");
@@ -1373,7 +1386,7 @@ vp_base_squelch_enable=1
doTest("Test Config - squelch disabled", log, [&](bool log) {
Config c;
std::string toLoad(R"rippleConfig(
std::string const toLoad(R"rippleConfig(
[reduce_relay]
vp_base_squelch_enable=0
)rippleConfig");
@@ -1385,7 +1398,7 @@ vp_base_squelch_enable=0
doTest("Test Config - legacy and new", log, [&](bool log) {
Config c;
std::string toLoad(R"rippleConfig(
std::string const toLoad(R"rippleConfig(
[reduce_relay]
vp_base_squelch_enable=0
vp_enable=0
@@ -1495,7 +1508,7 @@ vp_base_squelch_max_selected_peers=2
std::int16_t nMessages = 5;
for (int i = 0; i < nMessages; i++)
{
uint256 key(i);
uint256 const key(i);
network_.overlay().updateSlotAndSquelch(
key, network_.validator(0), 0, [&](PublicKey const&, PeerWPtr, std::uint32_t) {
});
@@ -1505,7 +1518,7 @@ vp_base_squelch_max_selected_peers=2
// hence '-1'.
BEAST_EXPECT(peers[0].count == (nMessages - 1));
// add duplicate
uint256 key(nMessages - 1);
uint256 const key(nMessages - 1);
network_.overlay().updateSlotAndSquelch(
key, network_.validator(0), 0, [&](PublicKey const&, PeerWPtr, std::uint32_t) {});
// confirm the same number of messages
@@ -1523,14 +1536,13 @@ vp_base_squelch_max_selected_peers=2
struct Handler : public reduce_relay::SquelchHandler
{
Handler() : maxDuration_(0)
Handler()
{
}
void
squelch(PublicKey const&, Peer::id_t, std::uint32_t duration) const override
{
if (duration > maxDuration_)
maxDuration_ = duration;
maxDuration_ = std::max<std::uint32_t>(duration, maxDuration_);
}
void
@@ -1542,7 +1554,7 @@ vp_base_squelch_max_selected_peers=2
unsquelch(PublicKey const&, Peer::id_t) const override
{
}
mutable int maxDuration_;
mutable int maxDuration_{0};
};
void
@@ -1566,7 +1578,7 @@ vp_base_squelch_max_selected_peers=2
{
// make unique message hash to make the
// slot's internal hash router accept the message
std::uint64_t mid = m * 1000 + peer;
std::uint64_t mid = (m * 1000) + peer;
uint256 const message{mid};
slots.updateSlotAndSquelch(message, validator, peer, true);
}
@@ -1598,18 +1610,22 @@ vp_base_squelch_max_selected_peers=2
handler.maxDuration_ <= kMaxUnsquelchExpirePeers.count());
using namespace beast::unit_test::detail;
if (handler.maxDuration_ <= kMaxUnsquelchExpireDefault.count())
{
log << makeReason("warning: squelch duration is low", __FILE__, __LINE__)
<< std::endl
<< std::flush;
}
// more than 400 is still less than kMaxUnsquelchExpirePeers
run(400);
BEAST_EXPECT(
handler.maxDuration_ >= kMinUnsquelchExpire.count() &&
handler.maxDuration_ <= kMaxUnsquelchExpirePeers.count());
if (handler.maxDuration_ <= kMaxUnsquelchExpireDefault.count())
{
log << makeReason("warning: squelch duration is low", __FILE__, __LINE__)
<< std::endl
<< std::flush;
}
});
}
@@ -1631,7 +1647,7 @@ vp_base_squelch_max_selected_peers=2
env_.app().config().compression = c.compression;
};
auto handshake = [&](int outboundEnable, int inboundEnable) {
beast::IP::Address addr = boost::asio::ip::make_address("172.1.1.100");
beast::IP::Address const addr = boost::asio::ip::make_address("172.1.1.100");
setEnv(outboundEnable);
auto request = makeRequest(
@@ -1640,25 +1656,25 @@ vp_base_squelch_max_selected_peers=2
false,
env_.app().config().txReduceRelayEnable,
env_.app().config().vpReduceRelayBaseSquelchEnable);
http_request_type http_request;
http_request.version(request.version());
http_request.base() = request.base();
http_request_type httpRequest;
httpRequest.version(request.version());
httpRequest.base() = request.base();
// feature enabled on the peer's connection only if both sides
// are enabled
auto const peerEnabled = inboundEnable && outboundEnable;
// inbound is enabled if the request's header has the feature
// enabled and the peer's configuration is enabled
auto const inboundEnabled =
peerFeatureEnabled(http_request, kFeatureVprr, inboundEnable);
peerFeatureEnabled(httpRequest, kFeatureVprr, inboundEnable);
BEAST_EXPECT(!(peerEnabled ^ inboundEnabled));
setEnv(inboundEnable);
auto http_resp =
makeResponse(true, http_request, addr, addr, uint256{1}, 1, {1, 0}, env_.app());
auto httpResp =
makeResponse(true, httpRequest, addr, addr, uint256{1}, 1, {1, 0}, env_.app());
// outbound is enabled if the response's header has the feature
// enabled and the peer's configuration is enabled
auto const outboundEnabled =
peerFeatureEnabled(http_resp, kFeatureVprr, outboundEnable);
peerFeatureEnabled(httpResp, kFeatureVprr, outboundEnable);
BEAST_EXPECT(!(peerEnabled ^ outboundEnabled));
};
handshake(1, 1);
@@ -1685,7 +1701,7 @@ public:
void
run() override
{
bool log = false;
bool const log = false;
testConfig(log);
testInitialRound(log);
testPeerUnsquelchedTooSoon(log);
@@ -1712,7 +1728,7 @@ class base_squelch_simulate_test : public base_squelch_test
void
run() override
{
bool log = false;
bool const log = false;
testRandom(log);
}
};

View File

@@ -1,29 +1,17 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2025 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <test/jtx/Env.h>
#include <xrpld/core/Config.h>
#include <xrpld/overlay/Peer.h>
#include <xrpld/overlay/ReduceRelayCommon.h>
#include <xrpld/overlay/Slot.h>
#include <xrpl/beast/unit_test.h>
#include <xrpl/basics/UnorderedContainers.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/basics/chrono.h>
#include <xrpl/beast/unit_test/suite.h>
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/protocol/KeyType.h>
#include <xrpl/protocol/PublicKey.h>
#include <xrpl/protocol/SecretKey.h>
#include <xrpl/protocol/digest.h>
@@ -31,6 +19,8 @@
#include <cstdint>
#include <functional>
#include <optional>
#include <unordered_map>
#include <utility>
#include <vector>
namespace xrpl::test {
@@ -47,19 +37,18 @@ public:
squelchAll_method squelchAll_f_;
unsquelch_method unsquelch_f_;
TestHandler(
squelch_method const& squelch_f,
squelchAll_method const& squelchAll_f,
unsquelch_method const& unsquelch_f)
: squelch_f_(squelch_f), squelchAll_f_(squelchAll_f), unsquelch_f_(unsquelch_f)
TestHandler(squelch_method squelchF, squelchAll_method squelchAllF, unsquelch_method unsquelchF)
: squelch_f_(std::move(squelchF))
, squelchAll_f_(std::move(squelchAllF))
, unsquelch_f_(std::move(unsquelchF))
{
}
TestHandler(TestHandler& copy)
: squelch_f_(copy.squelch_f_)
, squelchAll_f_(copy.squelchAll_f_)
, unsquelch_f_(copy.unsquelch_f_)
{
squelch_f_ = copy.squelch_f_;
squelchAll_f_ = copy.squelchAll_f_;
unsquelch_f_ = copy.unsquelch_f_;
}
void
@@ -141,31 +130,31 @@ public:
class enhanced_squelch_test : public beast::unit_test::Suite
{
public:
TestHandler::squelch_method noop_squelch = [&](PublicKey const&, Peer::id_t, std::uint32_t) {
TestHandler::squelch_method noopSquelch = [&](PublicKey const&, Peer::id_t, std::uint32_t) {
BEAST_EXPECTS(false, "unexpected call to squelch handler");
};
TestHandler::squelchAll_method noop_squelchAll =
TestHandler::squelchAll_method noopSquelchAll =
[&](PublicKey const&, std::uint32_t, std::function<void(Peer::id_t)>) {
BEAST_EXPECTS(false, "unexpected call to squelchAll handler");
};
TestHandler::unsquelch_method noop_unsquelch = [&](PublicKey const&, Peer::id_t) {
TestHandler::unsquelch_method noopUnsquelch = [&](PublicKey const&, Peer::id_t) {
BEAST_EXPECTS(false, "unexpected call to unsquelch handler");
};
// noop_handler is passed as a place holder Handler to slots
TestHandler noop_handler = {
noop_squelch,
noop_squelchAll,
noop_unsquelch,
TestHandler noopHandler = {
noopSquelch,
noopSquelchAll,
noopUnsquelch,
};
jtx::Env env_;
jtx::Env env;
enhanced_squelch_test() : env_(*this)
enhanced_squelch_test() : env(*this)
{
env_.app().config().vpReduceRelayEnhancedSquelchEnable = true;
env.app().config().vpReduceRelayEnhancedSquelchEnable = true;
}
void
@@ -208,7 +197,7 @@ vp_enhanced_squelch_enable=0
Peer::id_t const squelchedPeerID = 0;
Peer::id_t const newPeerID = 1;
TestStopwatch stopwatch;
EnhancedSquelchingTestSlots slots(env_.app(), noop_handler, env_.app().config(), stopwatch);
EnhancedSquelchingTestSlots slots(env.app(), noopHandler, env.app().config(), stopwatch);
auto const publicKey = randomKeyPair(KeyType::Ed25519).first;
// a new key should not be squelched
@@ -237,11 +226,11 @@ vp_enhanced_squelch_enable=0
}
void
testUpdateValidatorSlot_newValidator()
testUpdateValidatorSlotNewValidator()
{
testcase("updateValidatorSlot_newValidator");
TestStopwatch stopwatch;
EnhancedSquelchingTestSlots slots(env_.app(), noop_handler, env_.app().config(), stopwatch);
EnhancedSquelchingTestSlots slots(env.app(), noopHandler, env.app().config(), stopwatch);
Peer::id_t const peerID = 1;
auto const validator = randomKeyPair(KeyType::Ed25519).first;
@@ -250,10 +239,10 @@ vp_enhanced_squelch_enable=0
slots.updateUntrustedValidatorSlot(message, validator, peerID);
// adding untrusted slot does not effect trusted slots
BEAST_EXPECTS(slots.getSlots(true).size() == 0, "trusted slots changed");
BEAST_EXPECTS(slots.getSlots(true).empty(), "trusted slots changed");
// we expect that the validator was not added to untrusted slots
BEAST_EXPECTS(slots.getSlots(false).size() == 0, "untrusted slot changed");
BEAST_EXPECTS(slots.getSlots(false).empty(), "untrusted slot changed");
// we expect that the validator was added to th consideration list
BEAST_EXPECTS(
@@ -262,7 +251,7 @@ vp_enhanced_squelch_enable=0
}
void
testUpdateValidatorSlot_squelchedValidator()
testUpdateValidatorSlotSquelchedValidator()
{
testcase("testUpdateValidatorSlot_squelchedValidator");
@@ -270,17 +259,17 @@ vp_enhanced_squelch_enable=0
Peer::id_t const newPeerID = 1;
auto const validator = randomKeyPair(KeyType::Ed25519).first;
TestHandler::squelch_method const squelch_f =
TestHandler::squelch_method const squelchF =
[&](PublicKey const& key, Peer::id_t id, std::uint32_t duration) {
BEAST_EXPECTS(key == validator, "squelch called for unknown validator key");
BEAST_EXPECTS(id == newPeerID, "squelch called for the wrong peer");
};
TestHandler handler{squelch_f, noop_squelchAll, noop_unsquelch};
TestHandler handler{squelchF, noopSquelchAll, noopUnsquelch};
TestStopwatch stopwatch;
EnhancedSquelchingTestSlots slots(env_.app(), handler, env_.app().config(), stopwatch);
EnhancedSquelchingTestSlots slots(env.app(), handler, env.app().config(), stopwatch);
slots.squelchValidator(validator, squelchedPeerID);
@@ -302,29 +291,33 @@ vp_enhanced_squelch_enable=0
}
void
testUpdateValidatorSlot_slotsFull()
testUpdateValidatorSlotSlotsFull()
{
testcase("updateValidatorSlot_slotsFull");
Peer::id_t const peerID = 1;
// while there are open untrusted slots, no calls should be made to
// squelch any validators
TestHandler handler{noop_handler};
TestHandler handler{noopHandler};
TestStopwatch stopwatch;
EnhancedSquelchingTestSlots slots(env_.app(), handler, env_.app().config(), stopwatch);
EnhancedSquelchingTestSlots slots(env.app(), handler, env.app().config(), stopwatch);
// saturate validator slots
auto const validators = fillUntrustedSlots(slots);
// adding untrusted slot does not effect trusted slots
BEAST_EXPECTS(slots.getSlots(true).size() == 0, "trusted slots changed");
BEAST_EXPECTS(slots.getSlots(true).empty(), "trusted slots changed");
// simulate additional messages from already selected validators
for (auto const& validator : validators)
{
for (int i = 0; i < reduce_relay::kMaxMessageThreshold; ++i)
{
slots.updateUntrustedValidatorSlot(
sha512Half(validator) + static_cast<uint256>(i), validator, peerID);
}
}
// an untrusted slot was added for each validator
BEAST_EXPECT(slots.getSlots(false).size() == reduce_relay::kMaxUntrustedSlots);
@@ -352,13 +345,13 @@ vp_enhanced_squelch_enable=0
}
void
testDeleteIdlePeers_deleteIdleSlots()
testDeleteIdlePeersDeleteIdleSlots()
{
testcase("deleteIdlePeers");
TestHandler handler{noop_handler};
TestHandler handler{noopHandler};
TestStopwatch stopwatch;
EnhancedSquelchingTestSlots slots(env_.app(), handler, env_.app().config(), stopwatch);
EnhancedSquelchingTestSlots slots(env.app(), handler, env.app().config(), stopwatch);
auto keys = fillUntrustedSlots(slots);
// verify that squelchAll is called for each idled slot validator
@@ -385,20 +378,20 @@ vp_enhanced_squelch_enable=0
slots.deleteIdlePeers();
BEAST_EXPECTS(slots.getSlots(false).size() == 0, "unexpected number of untrusted slots");
BEAST_EXPECTS(slots.getSlots(false).empty(), "unexpected number of untrusted slots");
BEAST_EXPECTS(keys.empty(), "not all validators were squelched");
}
void
testDeleteIdlePeers_deleteIdleUntrustedPeer()
testDeleteIdlePeersDeleteIdleUntrustedPeer()
{
testcase("deleteIdleUntrustedPeer");
Peer::id_t const peerID = 1;
Peer::id_t const peerID2 = 2;
TestStopwatch stopwatch;
EnhancedSquelchingTestSlots slots(env_.app(), noop_handler, env_.app().config(), stopwatch);
EnhancedSquelchingTestSlots slots(env.app(), noopHandler, env.app().config(), stopwatch);
// fill one untrusted validator slot
auto const validator = fillUntrustedSlots(slots, 1)[0];
@@ -426,21 +419,22 @@ vp_enhanced_squelch_enable=0
* updateSlotAndSquelch
*/
void
testUpdateSlotAndSquelch_untrustedValidator()
testUpdateSlotAndSquelchUntrustedValidator()
{
testcase("updateUntrsutedValidatorSlot");
TestHandler handler{noop_handler};
TestHandler handler{noopHandler};
handler.squelch_f_ = [](PublicKey const&, Peer::id_t, std::uint32_t) {};
TestStopwatch stopwatch;
EnhancedSquelchingTestSlots slots(env_.app(), handler, env_.app().config(), stopwatch);
EnhancedSquelchingTestSlots slots(env.app(), handler, env.app().config(), stopwatch);
// peers that will be source of validator messages
std::vector<Peer::id_t> peers = {};
// prepare n+1 peers, we expect the n+1st peer will be squelched
for (int i = 0; i < env_.app().config().vpReduceRelaySquelchMaxSelectedPeers + 1; ++i)
peers.reserve(env_.app().config().vpReduceRelaySquelchMaxSelectedPeers + 1);
for (int i = 0; i < env.app().config().vpReduceRelaySquelchMaxSelectedPeers + 1; ++i)
peers.push_back(i);
auto const validator = fillUntrustedSlots(slots, 1)[0];
@@ -462,6 +456,7 @@ vp_enhanced_squelch_enable=0
// simulate new, unique validator messages sent by peers
for (auto const& peer : peers)
{
for (int i = 0; i < reduce_relay::kMaxMessageThreshold + 1; ++i)
{
auto const now = stopwatch.now();
@@ -473,10 +468,11 @@ vp_enhanced_squelch_enable=0
stopwatch.advance(std::chrono::milliseconds{10});
}
}
auto const slotPeers = getUntrustedSlotPeers(validator, slots);
BEAST_EXPECTS(
slotPeers.size() == env_.app().config().vpReduceRelaySquelchMaxSelectedPeers + 1,
slotPeers.size() == env.app().config().vpReduceRelaySquelchMaxSelectedPeers + 1,
"untrusted validator slot is missing");
int selected = 0;
@@ -498,16 +494,16 @@ vp_enhanced_squelch_enable=0
BEAST_EXPECTS(squelched == 1, "expected one squelched peer");
BEAST_EXPECTS(
selected == env_.app().config().vpReduceRelaySquelchMaxSelectedPeers,
selected == env.app().config().vpReduceRelaySquelchMaxSelectedPeers,
"wrong number of peers selected");
}
void
testUpdateConsideredValidator_new()
testUpdateConsideredValidatorNew()
{
testcase("testUpdateConsideredValidator_new");
TestStopwatch stopwatch;
EnhancedSquelchingTestSlots slots(env_.app(), noop_handler, env_.app().config(), stopwatch);
EnhancedSquelchingTestSlots slots(env.app(), noopHandler, env.app().config(), stopwatch);
// insert some random validator key
auto const validator = randomKeyPair(KeyType::Ed25519).first;
@@ -542,15 +538,15 @@ vp_enhanced_squelch_enable=0
}
void
testUpdateConsideredValidator_idle()
testUpdateConsideredValidatorIdle()
{
testcase("testUpdateConsideredValidator_idle");
TestStopwatch stopwatch;
EnhancedSquelchingTestSlots slots(env_.app(), noop_handler, env_.app().config(), stopwatch);
EnhancedSquelchingTestSlots slots(env.app(), noopHandler, env.app().config(), stopwatch);
// insert some random validator key
auto const validator = randomKeyPair(KeyType::Ed25519).first;
Peer::id_t peerID = 0;
Peer::id_t const peerID = 0;
BEAST_EXPECTS(
!slots.updateConsideredValidator(validator, peerID),
@@ -581,16 +577,16 @@ vp_enhanced_squelch_enable=0
}
void
testUpdateConsideredValidator_selectQualifying()
testUpdateConsideredValidatorSelectQualifying()
{
testcase("testUpdateConsideredValidator_selectQualifying");
TestStopwatch stopwatch;
EnhancedSquelchingTestSlots slots(env_.app(), noop_handler, env_.app().config(), stopwatch);
EnhancedSquelchingTestSlots slots(env.app(), noopHandler, env.app().config(), stopwatch);
// insert some random validator key
auto const validator = randomKeyPair(KeyType::Ed25519).first;
Peer::id_t peerID = 0;
Peer::id_t const peerID = 0;
for (int i = 0; i < reduce_relay::kMaxMessageThreshold - 1; ++i)
{
@@ -613,27 +609,31 @@ vp_enhanced_squelch_enable=0
}
void
testCleanConsideredValidators_resetIdle()
testCleanConsideredValidatorsResetIdle()
{
testcase("testCleanConsideredValidators_resetIdle");
auto const validator = randomKeyPair(KeyType::Ed25519).first;
TestStopwatch stopwatch;
EnhancedSquelchingTestSlots slots(env_.app(), noop_handler, env_.app().config(), stopwatch);
EnhancedSquelchingTestSlots slots(env.app(), noopHandler, env.app().config(), stopwatch);
// send enough messages for a slot to meet peer requirements
for (int i = 0; i < env_.app().config().vpReduceRelaySquelchMaxSelectedPeers; ++i)
for (int i = 0; i < env.app().config().vpReduceRelaySquelchMaxSelectedPeers; ++i)
{
slots.updateUntrustedValidatorSlot(
sha512Half(validator) + static_cast<uint256>(i), validator, i);
}
// send enough messages from some peer to be one message away from
// meeting the selection criteria
for (int i = 0; i < reduce_relay::kMaxMessageThreshold -
(env_.app().config().vpReduceRelaySquelchMaxSelectedPeers + 1);
(env.app().config().vpReduceRelaySquelchMaxSelectedPeers + 1);
++i)
{
slots.updateUntrustedValidatorSlot(
sha512Half(validator) + static_cast<uint256>(i), validator, 0);
}
BEAST_EXPECTS(
slots.getConsideredValidators().at(validator).count ==
@@ -642,7 +642,7 @@ vp_enhanced_squelch_enable=0
BEAST_EXPECTS(
slots.getConsideredValidators().at(validator).peers.size() ==
env_.app().config().vpReduceRelaySquelchMaxSelectedPeers,
env.app().config().vpReduceRelaySquelchMaxSelectedPeers,
"considered validator information is in an invalid state");
stopwatch.advance(reduce_relay::kIdled + std::chrono::seconds{1});
@@ -654,7 +654,7 @@ vp_enhanced_squelch_enable=0
sha512Half(validator) + static_cast<uint256>(1), validator, 0);
// we expect that the validator was not selected
BEAST_EXPECTS(slots.getSlots(false).size() == 0, "untrusted slot was created");
BEAST_EXPECTS(slots.getSlots(false).empty(), "untrusted slot was created");
BEAST_EXPECTS(
slots.getConsideredValidators().at(validator).count == 1,
@@ -666,12 +666,12 @@ vp_enhanced_squelch_enable=0
}
void
testCleanConsideredValidators_deletePoorlyConnected()
testCleanConsideredValidatorsDeletePoorlyConnected()
{
testcase("cleanConsideredValidators_deletePoorlyConnected");
auto const validator = randomKeyPair(KeyType::Ed25519).first;
Peer::id_t const peerID = 0;
TestHandler handler{noop_handler};
TestHandler handler{noopHandler};
// verify that squelchAll is called for poorly connected validator
handler.squelchAll_f_ = [&](PublicKey const& actualKey,
@@ -683,12 +683,14 @@ vp_enhanced_squelch_enable=0
TestStopwatch stopwatch;
EnhancedSquelchingTestSlots slots(env_.app(), handler, env_.app().config(), stopwatch);
EnhancedSquelchingTestSlots slots(env.app(), handler, env.app().config(), stopwatch);
// send enough messages from a single peer
for (int i = 0; i < 2 * reduce_relay::kMaxMessageThreshold + 1; ++i)
for (int i = 0; i < (2 * reduce_relay::kMaxMessageThreshold) + 1; ++i)
{
slots.updateUntrustedValidatorSlot(
sha512Half(validator) + static_cast<uint256>(i), validator, peerID);
}
stopwatch.advance(reduce_relay::kIdled + std::chrono::seconds{1});
@@ -697,12 +699,11 @@ vp_enhanced_squelch_enable=0
slots.deleteIdlePeers();
BEAST_EXPECTS(
slots.getConsideredValidators().size() == 0,
"poorly connected validator was not deleted");
slots.getConsideredValidators().empty(), "poorly connected validator was not deleted");
}
void
testCleanConsideredValidators_deleteSilent()
testCleanConsideredValidatorsDeleteSilent()
{
testcase("cleanConsideredValidators_deleteSilent");
// insert some random validator key
@@ -710,7 +711,7 @@ vp_enhanced_squelch_enable=0
auto const validator = randomKeyPair(KeyType::Ed25519).first;
Peer::id_t const peerID = 0;
TestHandler handler{noop_handler};
TestHandler handler{noopHandler};
// verify that squelchAll is called for idle validator
handler.squelchAll_f_ = [&](PublicKey const& actualKey,
@@ -722,7 +723,7 @@ vp_enhanced_squelch_enable=0
TestStopwatch stopwatch;
EnhancedSquelchingTestSlots slots(env_.app(), handler, env_.app().config(), stopwatch);
EnhancedSquelchingTestSlots slots(env.app(), handler, env.app().config(), stopwatch);
BEAST_EXPECTS(
!slots.updateConsideredValidator(idleValidator, peerID),
@@ -748,14 +749,14 @@ vp_enhanced_squelch_enable=0
}
void
testSquelchUntrustedValidator_consideredListCleared()
testSquelchUntrustedValidatorConsideredListCleared()
{
testcase("testSquelchUntrustedValidator");
auto const validator = randomKeyPair(KeyType::Ed25519).first;
Peer::id_t const peerID = 0;
TestHandler handler{noop_handler};
TestHandler handler{noopHandler};
// verify that squelchAll is called for idle validator
handler.squelchAll_f_ = [&](PublicKey const& actualKey,
std::uint32_t duration,
@@ -764,7 +765,7 @@ vp_enhanced_squelch_enable=0
};
TestStopwatch stopwatch;
EnhancedSquelchingTestSlots slots(env_.app(), handler, env_.app().config(), stopwatch);
EnhancedSquelchingTestSlots slots(env.app(), handler, env.app().config(), stopwatch);
// add the validator to the considered list
slots.updateUntrustedValidatorSlot(sha512Half(validator), validator, peerID);
@@ -781,13 +782,13 @@ vp_enhanced_squelch_enable=0
}
void
testSquelchUntrustedValidator_slotEvicted()
testSquelchUntrustedValidatorSlotEvicted()
{
testcase("testSquelchUntrustedValidator_slotEvicted");
TestHandler handler{noop_handler};
TestHandler handler{noopHandler};
TestStopwatch stopwatch;
EnhancedSquelchingTestSlots slots(env_.app(), handler, env_.app().config(), stopwatch);
EnhancedSquelchingTestSlots slots(env.app(), handler, env.app().config(), stopwatch);
// assign a slot to the untrusted validator
auto const validators = fillUntrustedSlots(slots, 1);
@@ -823,17 +824,21 @@ private:
{
auto const validator = randomKeyPair(KeyType::Ed25519).first;
keys.push_back(validator);
for (int j = 0; j < env_.app().config().vpReduceRelaySquelchMaxSelectedPeers; ++j)
for (int j = 0; j < env.app().config().vpReduceRelaySquelchMaxSelectedPeers; ++j)
{
// send enough messages so that a validator slot is selected
for (int k = 0; k < reduce_relay::kMaxMessageThreshold; ++k)
{
slots.updateUntrustedValidatorSlot(
sha512Half(validator) + static_cast<uint256>(k), validator, j);
}
}
}
return keys;
}
std::unordered_map<Peer::id_t, reduce_relay::Slot::PeerInfo>
static std::unordered_map<Peer::id_t, reduce_relay::Slot::PeerInfo>
getUntrustedSlotPeers(PublicKey const& validator, EnhancedSquelchingTestSlots const& slots)
{
auto const& it = slots.getSlots(false).find(validator);
@@ -843,7 +848,7 @@ private:
auto r = std::unordered_map<Peer::id_t, reduce_relay::Slot::PeerInfo>();
for (auto const& [id, info] : it->second.getPeers())
r.emplace(std::make_pair(id, info));
r.emplace(id, info);
return r;
}
@@ -853,20 +858,20 @@ private:
{
testConfig();
testSquelchTracking();
testUpdateValidatorSlot_newValidator();
testUpdateValidatorSlot_slotsFull();
testUpdateValidatorSlot_squelchedValidator();
testDeleteIdlePeers_deleteIdleSlots();
testDeleteIdlePeers_deleteIdleUntrustedPeer();
testUpdateSlotAndSquelch_untrustedValidator();
testUpdateConsideredValidator_new();
testUpdateConsideredValidator_idle();
testUpdateConsideredValidator_selectQualifying();
testCleanConsideredValidators_deleteSilent();
testCleanConsideredValidators_resetIdle();
testCleanConsideredValidators_deletePoorlyConnected();
testSquelchUntrustedValidator_consideredListCleared();
testSquelchUntrustedValidator_slotEvicted();
testUpdateValidatorSlotNewValidator();
testUpdateValidatorSlotSlotsFull();
testUpdateValidatorSlotSquelchedValidator();
testDeleteIdlePeersDeleteIdleSlots();
testDeleteIdlePeersDeleteIdleUntrustedPeer();
testUpdateSlotAndSquelchUntrustedValidator();
testUpdateConsideredValidatorNew();
testUpdateConsideredValidatorIdle();
testUpdateConsideredValidatorSelectQualifying();
testCleanConsideredValidatorsDeleteSilent();
testCleanConsideredValidatorsResetIdle();
testCleanConsideredValidatorsDeletePoorlyConnected();
testSquelchUntrustedValidatorConsideredListCleared();
testSquelchUntrustedValidatorSlotEvicted();
}
};

View File

@@ -1,29 +1,15 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2025 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <test/jtx/Env.h>
#include <xrpld/overlay/ReduceRelayCommon.h>
#include <xrpld/overlay/SquelchStore.h>
#include <xrpl/beast/unit_test.h>
#include <xrpl/basics/UnorderedContainers.h>
#include <xrpl/basics/chrono.h>
#include <xrpl/beast/unit_test/suite.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/protocol/KeyType.h>
#include <xrpl/protocol/PublicKey.h>
#include <xrpl/protocol/SecretKey.h>
#include <chrono>
@@ -49,9 +35,9 @@ class squelch_store_test : public beast::unit_test::Suite
using seconds = std::chrono::seconds;
public:
jtx::Env env_;
jtx::Env env;
squelch_store_test() : env_(*this)
squelch_store_test() : env(*this)
{
}
@@ -61,7 +47,7 @@ public:
testcase("SquelchStore handleSquelch");
TestStopwatch clock;
auto store = TestSquelchStore(env_.journal, clock);
auto store = TestSquelchStore(env.journal, clock);
auto const validator = randomKeyPair(KeyType::Ed25519).first;
@@ -94,7 +80,7 @@ public:
{
testcase("SquelchStore IsSquelched");
TestStopwatch clock;
auto store = TestSquelchStore(env_.journal, clock);
auto store = TestSquelchStore(env.journal, clock);
auto const validator = randomKeyPair(KeyType::Ed25519).first;
auto const duration = reduce_relay::kMinUnsquelchExpire + seconds{1};
@@ -113,7 +99,7 @@ public:
{
testcase("SquelchStore testClearExpiredSquelches");
TestStopwatch clock;
auto store = TestSquelchStore(env_.journal, clock);
auto store = TestSquelchStore(env.journal, clock);
auto const validator = randomKeyPair(KeyType::Ed25519).first;
auto const duration = reduce_relay::kMinUnsquelchExpire + seconds{1};

View File

@@ -2,11 +2,11 @@
#include <xrpld/core/Config.h>
#include <xrpld/overlay/Peer.h>
#include <xrpld/overlay/ReduceRelayCommon.h>
#include <xrpl/basics/Log.h>
#include <xrpl/basics/UnorderedContainers.h>
#include <xrpl/basics/chrono.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/basics/hardened_hash.h>
#include <xrpl/beast/clock/abstract_clock.h>
#include <xrpl/beast/container/aged_unordered_map.h>
#include <xrpl/beast/utility/Journal.h>
@@ -14,9 +14,16 @@
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/protocol/PublicKey.h>
#include <atomic>
#include <chrono>
#include <cstddef>
#include <cstdint>
#include <functional>
#include <optional>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
namespace xrpl::reduce_relay {
@@ -75,9 +82,7 @@ to_string(SlotState state)
class SquelchHandler
{
public:
virtual ~SquelchHandler()
{
}
virtual ~SquelchHandler() = default;
/**
* Squelch handler for a single peer
* @param validator Public key of the source validator
@@ -142,11 +147,11 @@ public:
*/
struct PeerInfo
{
PeerState state; // peer's state
std::size_t count; // message count
time_point expire; // squelch expiration time
time_point lastMessage; // time last message received
std::size_t timesSelected; // number of times the peer was selected
PeerState state; // peer's state
std::size_t count{}; // message count
time_point expire; // squelch expiration time
time_point lastMessage; // time last message received
std::size_t timesSelected{}; // number of times the peer was selected
};
/**
@@ -183,9 +188,7 @@ private:
uint16_t maxSelectedPeers,
bool isTrusted,
clock_type& clock)
: reachedThreshold_(0)
, lastSelected_(clock.now())
, state_(SlotState::Counting)
: lastSelected_(clock.now())
, handler_(handler)
, journal_(journal)
, maxSelectedPeers_(maxSelectedPeers)
@@ -333,14 +336,14 @@ private:
std::unordered_set<Peer::id_t> considered_;
// A counter of peers that have reached the max message threshold.
std::uint16_t reachedThreshold_;
std::uint16_t reachedThreshold_{0};
// The timestamp of the last peer selection, used to determine when the
// current selection has become stale.
time_point lastSelected_;
// The current state of the slot.
SlotState state_;
SlotState state_{SlotState::Counting};
// A reference to an external handler that executes squelch/unsquelch
// operations on peers.
@@ -532,7 +535,7 @@ public:
uint256 const& key,
PublicKey const& validator,
Peer::id_t id,
typename Slot::ignored_squelch_callback report);
Slot::ignored_squelch_callback report);
/**
* Calls Slot::update of Slot associated with the validator, with a
@@ -586,7 +589,7 @@ public:
uint256 const& key,
PublicKey const& validator,
Peer::id_t id,
typename Slot::ignored_squelch_callback report,
Slot::ignored_squelch_callback report,
bool isTrusted);
/**
@@ -611,8 +614,7 @@ public:
squelchUntrustedValidator(PublicKey const& validatorKey);
/**
* @brief Performs cleanup of idle peers, stale slots, and unviable
* validator candidates.
* @brief Performs cleanup of idle peers, stale slots, and inviable validator candidates.
*
* @details This function is responsible for the health of the entire slot
* system. It executes three distinct cleanup phases:

View File

@@ -1,6 +1,5 @@
#pragma once
#include <xrpl/basics/Log.h>
#include <xrpl/basics/UnorderedContainers.h>
#include <xrpl/beast/clock/abstract_clock.h>
#include <xrpl/beast/utility/Journal.h>
@@ -23,7 +22,7 @@ namespace xrpl::reduce_relay {
class SquelchStore
{
using clock_type = beast::AbstractClock<std::chrono::steady_clock>;
using time_point = typename clock_type::time_point;
using time_point = clock_type::time_point;
public:
explicit SquelchStore(beast::Journal journal, clock_type& clock)

View File

@@ -1543,7 +1543,7 @@ OverlayImpl::handleUntrustedSquelch(PublicKey const& validator)
{
if (!strand_.running_in_this_thread())
{
post(strand_, std::bind(&OverlayImpl::handleUntrustedSquelch, this, validator));
post(strand_, [this, validator] { handleUntrustedSquelch(validator); });
return;
}

View File

@@ -14,7 +14,6 @@
#include <xrpl/basics/Resolver.h>
#include <xrpl/basics/UnorderedContainers.h>
#include <xrpl/basics/UptimeClock.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/beast/insight/Collector.h>
#include <xrpl/beast/insight/Gauge.h>

View File

@@ -15,8 +15,8 @@
#include <xrpl/basics/Log.h>
#include <xrpl/basics/Number.h>
#include <xrpl/basics/UnorderedContainers.h>
#include <xrpl/basics/UptimeClock.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/basics/chrono.h>
#include <xrpl/beast/net/IPEndpoint.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/beast/utility/WrappedSink.h>

View File

@@ -1,22 +1,3 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2025 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <xrpld/overlay/Slot.h>
#include <xrpld/overlay/Peer.h>
@@ -24,18 +5,23 @@
#include <xrpl/basics/Log.h>
#include <xrpl/basics/UnorderedContainers.h>
#include <xrpl/basics/chrono.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/basics/random.h>
#include <xrpl/beast/container/aged_unordered_map.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/beast/utility/PropertyStream.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/protocol/PublicKey.h>
#include <xrpl/protocol/tokens.h>
#include <algorithm>
#include <chrono>
#include <cstddef>
#include <iterator>
#include <optional>
#include <sstream>
#include <string>
#include <unordered_set>
#include <utility>
#include <vector>
namespace xrpl::reduce_relay {
@@ -73,14 +59,13 @@ Slot::update(PublicKey const& validator, Peer::id_t id, ignored_squelch_callback
{
JLOG(journal_.trace()) << "update: adding new slot" << formatLogMessage(validator, id);
peers_.emplace(
std::make_pair(
id,
PeerInfo{
.state = PeerState::Counting,
.count = 0,
.expire = now,
.lastMessage = now,
.timesSelected = 0}));
id,
PeerInfo{
.state = PeerState::Counting,
.count = 0,
.expire = now,
.lastMessage = now,
.timesSelected = 0});
initCounting();
return;
}
@@ -129,7 +114,7 @@ Slot::update(PublicKey const& validator, Peer::id_t id, ignored_squelch_callback
// idled peers.
std::unordered_set<Peer::id_t> selected;
std::stringstream str;
while (selected.size() != maxSelectedPeers_ && considered_.size() != 0)
while (selected.size() != maxSelectedPeers_ && !considered_.empty())
{
auto const i = considered_.size() == 1 ? 0 : randInt(considered_.size() - 1);
auto const it = std::next(considered_.begin(), i);
@@ -176,7 +161,7 @@ Slot::update(PublicKey const& validator, Peer::id_t id, ignored_squelch_callback
{
v.count = 0;
if (selected.find(k) != selected.end())
if (selected.contains(k))
{
v.state = PeerState::Selected;
++v.timesSelected;
@@ -187,7 +172,7 @@ Slot::update(PublicKey const& validator, Peer::id_t id, ignored_squelch_callback
if (journal_.trace())
str << k << " ";
v.state = PeerState::Squelched;
std::chrono::seconds duration =
std::chrono::seconds const duration =
getSquelchDuration(peers_.size() - maxSelectedPeers_);
v.expire = now + duration;
handler_.squelch(validator, k, duration.count());
@@ -230,7 +215,7 @@ Slot::deletePeer(PublicKey const& validator, Peer::id_t id, bool erase)
JLOG(journal_.debug()) << "deletePeer: unsquelching selected peer "
<< formatLogMessage(validator, id)
<< " peer_state: " << to_string(it->second.state)
<< " considered: " << (considered_.find(id) != considered_.end())
<< " considered: " << (considered_.contains(id))
<< " erase: " << erase;
for (auto& [k, v] : peers_)
@@ -320,8 +305,10 @@ bool
Slots::reduceRelayReady()
{
if (!reduceRelayReady_)
{
reduceRelayReady_ = std::chrono::duration_cast<std::chrono::minutes>(
clock_.now().time_since_epoch()) > reduce_relay::kWaitOnBootup;
}
return reduceRelayReady_;
}
@@ -352,7 +339,7 @@ Slots::expireAndIsPeerSquelched(PublicKey const& validatorKey, Peer::id_t peerID
return false;
// if a peer is found the squelch for it has not expired
return it->second.find(peerID) != it->second.end();
return it->second.contains(peerID);
}
bool
@@ -372,7 +359,7 @@ Slots::updateSlotAndSquelch(
uint256 const& key,
PublicKey const& validator,
Peer::id_t id,
typename Slot::ignored_squelch_callback report,
Slot::ignored_squelch_callback report,
bool isTrusted)
{
if (expireAndIsPeerMessageCached(key, id))
@@ -414,11 +401,11 @@ Slots::updateUntrustedValidatorSlot(
uint256 const& key,
PublicKey const& validator,
Peer::id_t id,
typename Slot::ignored_squelch_callback report)
Slot::ignored_squelch_callback report)
{
// We received a message from an already selected validator
// we can ignore this message
if (untrustedSlots_.find(validator) != untrustedSlots_.end())
if (untrustedSlots_.contains(validator))
return;
// Did we receive a message from an already squelched validator?
@@ -567,15 +554,21 @@ Slots::deleteIdlePeers()
// if an untrusted validator slot idled - peers stopped
// sending messages for this validator squelch it
if (!it->second.isTrusted_)
{
handler_.squelchAll(
it->first,
reduce_relay::kMaxUnsquelchExpireDefault.count(),
[&](Peer::id_t id) { registerSquelchedValidator(it->first, id); });
}
it = slots.erase(it);
}
else
++it;
{
{
++it;
}
}
}
};
@@ -586,10 +579,12 @@ Slots::deleteIdlePeers()
// there might be some good validators in this set that "lapsed".
// However, since these are untrusted validators we're not concerned
for (auto const& validator : cleanConsideredValidators())
{
handler_.squelchAll(
validator, reduce_relay::kMaxUnsquelchExpireDefault.count(), [&](Peer::id_t id) {
registerSquelchedValidator(validator, id);
});
}
}
std::vector<PublicKey>
@@ -614,10 +609,14 @@ Slots::cleanConsideredValidators()
++it;
}
else
++it;
{
{
++it;
}
}
}
if (keys.size() > 0)
if (!keys.empty())
{
JLOG(journal_.info()) << "cleanConsideredValidators: removed considered validators "
<< ss.str();

View File

@@ -1,27 +1,9 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2025 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <xrpld/overlay/SquelchStore.h>
#include <xrpld/overlay/ReduceRelayCommon.h>
#include <xrpl/basics/Log.h>
#include <xrpl/basics/Slice.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/protocol/PublicKey.h>