1#include <test/jtx/Env.h>
2#include <test/jtx/PeerStub.h>
3#include <test/jtx/envconfig.h>
5#include <xrpld/app/main/Application.h>
6#include <xrpld/overlay/Message.h>
7#include <xrpld/overlay/Peer.h>
8#include <xrpld/overlay/ReduceRelayCommon.h>
9#include <xrpld/overlay/Slot.h>
10#include <xrpld/overlay/Squelch.h>
11#include <xrpld/overlay/detail/Handshake.h>
13#include <xrpl/basics/base_uint.h>
14#include <xrpl/basics/random.h>
15#include <xrpl/beast/net/IPAddress.h>
16#include <xrpl/beast/unit_test/suite.h>
17#include <xrpl/beast/utility/Journal.h>
18#include <xrpl/protocol/KeyType.h>
19#include <xrpl/protocol/PublicKey.h>
20#include <xrpl/protocol/SecretKey.h>
22#include <boost/asio/ip/address.hpp>
49using namespace std::chrono;
152 protocol::MessageType type = protocol::mtVALIDATION) = 0;
182 auto sp =
peer_.lock();
185 peer->onMessage(m, f);
200 auto p =
peer_.lock();
207 auto p =
peer_.lock();
229 protocol::TMValidation v;
230 v.set_validation(
"validation");
282 for (
auto id : peers)
284 assert(
links_.contains(
id));
337 auto it =
links_.find(
id);
338 assert(it !=
links_.end());
339 it->second->up(
true);
345 auto it =
links_.find(
id);
346 assert(it !=
links_.end());
347 it->second->up(
false);
439 return res ? *res : 0;
448 protocol::MessageType type = protocol::mtVALIDATION)
override
458 slots_.deletePeer(
id,
true);
492 auto it =
peers_.find(
id);
493 assert(it !=
peers_.end());
517 for (
auto& [
id, _] :
peers_)
544 return selected.contains(peer);
551 assert(!selected.empty());
552 return *selected.begin();
634 auto id =
overlay_.deleteLastPeer();
690 squelch.clear_validatorpubkey();
717 bool resetClock =
true)
728 for (
int m = 0; m < nMessages; ++m)
758 for (
auto& [_, v] : peers)
785 for (
auto& [k, v] : peers)
786 std::cout << k <<
":" << (int)std::get<reduce_relay::PeerState>(v) <<
" ";
800 bool const res =
static_cast<bool>(
duration);
805 auto sp = peerPtr.
lock();
846 bool squelched =
false;
860 str <<
" selected: ";
861 for (
auto s : selected)
865 std::cout << (double)reduce_relay::epoch<milliseconds>(now).count() / 1000.
866 <<
" random, squelched, validator: " <<
validator.id()
871 countingState ==
false &&
872 selected.size() ==
env_.app().config().vpReduceRelaySquelchMaxSelectedPeers);
881 events[event].validator =
validator.id();
883 events[event].peer = link.
peerId();
885 events[event].time = now;
889 events[event].isSelected =
907 bool const allCounting =
network_.allCounting(event.peer);
910 if (event.isSelected)
911 sendSquelch(v, peerPtr, {});
912 event.handled =
true;
919 bool const handled = (!
event.isSelected && !
event.handled) ||
920 (event.isSelected && (event.handled || allCounting));
921 BEAST_EXPECT(handled);
923 event.isSelected =
false;
924 event.handledCnt += handled;
925 event.handled =
false;
926 network_.onDisconnectPeer(event.peer);
941 bool mustHandle =
false;
942 if (event.state ==
State::On && BEAST_EXPECT(event.key))
944 event.isSelected =
network_.overlay().isSelected(*event.key, event.peer);
945 auto peers =
network_.overlay().getPeers(*event.key);
947 std::get<3>(peers[event.peer]);
948 mustHandle =
event.isSelected &&
952 peers.contains(event.peer);
955 event.handled =
true;
956 if (mustHandle && v == event.key)
963 (!event.handled && !mustHandle);
964 BEAST_EXPECT(handled);
971 BEAST_EXPECT(handled);
973 event.isSelected =
false;
974 event.handledCnt += handled;
975 event.handled =
false;
976 network_.enableLink(event.validator, event.peer,
true);
983 BEAST_EXPECT(down.handledCnt >= down.cnt - 1);
985 BEAST_EXPECT(disconnected.cnt == disconnected.handledCnt);
988 std::cout <<
"link down count: " << down.cnt <<
"/" << down.handledCnt
989 <<
" peer disconnect count: " << disconnected.cnt <<
"/"
990 << disconnected.handledCnt;
998 BEAST_EXPECT(countingState == isCountingState);
999 return countingState == isCountingState;
1028 doTest(
"Peer Unsquelched Too Soon",
log, [
this](
bool log) {
1066 kMaxPeers -
env_.app().config().vpReduceRelaySquelchMaxSelectedPeers);
1075 BEAST_EXPECT(selected.size() ==
env_.app().config().vpReduceRelaySquelchMaxSelectedPeers);
1076 BEAST_EXPECT(n == 1);
1079 return n == 1 && res;
1091 bool resetClock =
true)
1093 bool squelched =
false;
1100 BEAST_EXPECT(
false);
1108 return !squelched && res;
1132 doTest(
"Selected Peer Disconnects",
log, [
this](
bool log) {
1141 kMaxPeers -
env_.app().config().vpReduceRelaySquelchMaxSelectedPeers);
1153 doTest(
"Selected Peer Stops Relaying",
log, [
this](
bool log) {
1158 network_.overlay().deleteIdlePeers(
1163 kMaxPeers -
env_.app().config().vpReduceRelaySquelchMaxSelectedPeers);
1174 doTest(
"Squelched Peer Disconnects",
log, [
this](
bool log) {
1179 return std::get<reduce_relay::PeerState>(it.second) ==
1182 assert(it != peers.end());
1186 BEAST_EXPECT(unsquelched == 0);
1194 doTest(
"Test Config - squelch enabled (legacy)",
log, [&](
bool log) {
1206 doTest(
"Test Config - squelch disabled (legacy)",
log, [&](
bool log) {
1219 toLoad = R
"xrpldConfig(
1227 doTest(
"Test Config - squelch enabled",
log, [&](
bool log) {
1232vp_base_squelch_enable=1
1239 doTest(
"Test Config - squelch disabled",
log, [&](
bool log) {
1244vp_base_squelch_enable=0
1251 doTest(
"Test Config - legacy and new",
log, [&](
bool log) {
1256vp_base_squelch_enable=0
1261 auto const expectedError =
1262 "Invalid reduce_relay"
1263 " cannot specify both vp_base_squelch_enable and vp_enable "
1265 "vp_enable was deprecated and replaced by "
1266 "vp_base_squelch_enable";
1277 BEAST_EXPECT(error == expectedError);
1280 doTest(
"Test Config - max selected peers",
log, [&](
bool log) {
1292 toLoad = R"xrpldConfig(
1294vp_base_squelch_max_selected_peers=6
1302 toLoad = R"xrpldConfig(
1304vp_base_squelch_max_selected_peers=2
1308 auto const expectedError =
1309 "Invalid reduce_relay"
1310 " vp_base_squelch_max_selected_peers must be "
1311 "greater than or equal to 3";
1321 BEAST_EXPECT(error == expectedError);
1326 testBaseSquelchReady(
bool log)
1331 env_.app().config().vpReduceRelayBaseSquelchEnable = baseSquelchEnabled;
1336 BEAST_EXPECT(!createSlots(
false).baseSquelchReady());
1340 BEAST_EXPECT(!createSlots(
true).baseSquelchReady());
1345 BEAST_EXPECT(createSlots(
true).baseSquelchReady());
1349 BEAST_EXPECT(!createSlots(
false).baseSquelchReady());
1354 testInternalHashRouter(
bool log)
1356 doTest(
"Duplicate Message",
log, [&](
bool log) {
1360 for (
int i = 0; i < nMessages; i++)
1363 network_.overlay().updateSlotAndSquelch(
1370 BEAST_EXPECT(std::get<1>(peers[0]) == (nMessages - 1));
1372 UInt256 const key(nMessages - 1);
1373 network_.overlay().updateSlotAndSquelch(
1377 BEAST_EXPECT(std::get<1>(peers[0]) == (nMessages - 1));
1380 network_.overlay().updateSlotAndSquelch(
1384 BEAST_EXPECT(std::get<1>(peers[0]) == nMessages);
1390 Handler() =
default;
1397 unsquelch(PublicKey
const&, Peer::ID)
const override
1400 mutable int maxDuration{0};
1404 testRandomSquelch(
bool l)
1406 doTest(
"Random Squelch", l, [&](
bool l) {
1410 auto run = [&](
int npeers) {
1411 handler.maxDuration = 0;
1412 reduce_relay::Slots<ManualClock> slots(env_.app(), handler, env_.app().config());
1419 for (
int peer = 0; peer < npeers; peer++)
1423 std::uint64_t
const mid = (m * 1000) + peer;
1425 slots.updateSlotAndSquelch(
1426 message,
validator, peer, protocol::MessageType::mtVALIDATION);
1433 using namespace reduce_relay;
1438 handler.maxDuration >= kMinUnsquelchExpire.count() &&
1439 handler.maxDuration <= kMaxUnsquelchExpireDefault.count());
1442 handler.maxDuration >= kMinUnsquelchExpire.count() &&
1443 handler.maxDuration <= kMaxUnsquelchExpireDefault.count());
1452 handler.maxDuration >= kMinUnsquelchExpire.count() &&
1453 handler.maxDuration <= kMaxUnsquelchExpirePeers.count());
1455 if (handler.maxDuration <= kMaxUnsquelchExpireDefault.count())
1457 log <<
makeReason(
"warning: squelch duration is low", __FILE__, __LINE__)
1464 handler.maxDuration >= kMinUnsquelchExpire.count() &&
1465 handler.maxDuration <= kMaxUnsquelchExpirePeers.count());
1466 if (handler.maxDuration <= kMaxUnsquelchExpireDefault.count())
1468 log <<
makeReason(
"warning: squelch duration is low", __FILE__, __LINE__)
1476 testHandshake(
bool log)
1478 doTest(
"Handshake", log, [&](
bool log) {
1479 auto setEnv = [&](
bool enable) {
1481 std::stringstream str;
1482 str <<
"[reduce_relay]\n"
1483 <<
"vp_enable=" << enable <<
"\n"
1484 <<
"[compression]\n"
1486 c.loadFromString(str.
str());
1487 env_.app().config().vpReduceRelayBaseSquelchEnable =
1488 c.vpReduceRelayBaseSquelchEnable;
1490 env_.app().config().compression = c.compression;
1492 auto handshake = [&](
int outboundEnable,
int inboundEnable) {
1495 setEnv(outboundEnable);
1498 env_.app().config().compression,
1500 env_.app().config().txReduceRelayEnable,
1501 env_.app().config().vpReduceRelayBaseSquelchEnable);
1503 httpRequest.version(request.version());
1504 httpRequest.base() = request.base();
1507 auto const peerEnabled = inboundEnable && outboundEnable;
1510 auto const inboundEnabled =
1512 BEAST_EXPECT(!(peerEnabled ^ inboundEnabled));
1514 setEnv(inboundEnable);
1516 true, httpRequest, addr, addr,
UInt256{1}, 1, {1, 0}, env_.app());
1519 auto const outboundEnabled =
1521 BEAST_EXPECT(!(peerEnabled ^ outboundEnabled));
1536 cfg->vpReduceRelayBaseSquelchEnable =
true;
1537 cfg->vpReduceRelaySquelchMaxSelectedPeers = 6;
1540 , network_(env_.app())
1547 bool const log =
false;
1549 testInitialRound(log);
1550 testPeerUnsquelchedTooSoon(log);
1551 testPeerUnsquelched(log);
1553 testSquelchedPeerDisconnects(log);
1554 testSelectedPeerDisconnects(log);
1555 testSelectedPeerStopsRelaying(log);
1556 testInternalHashRouter(log);
1557 testRandomSquelch(log);
1559 testBaseSquelchReady(log);
1574 bool const log =
false;
T back_inserter(T... args)
A generic endpoint for log messages.
LogOs< char > log
Logging output stream.
TestcaseT testcase
Memberspace for declaring test cases.
std::size_t vpReduceRelaySquelchMaxSelectedPeers
////////////////// !
bool vpReduceRelayBaseSquelchEnable
void loadFromString(std::string const &fileContents)
Load the config from the contents of the string.
std::uint32_t ID
Uniquely identifies a peer.
std::uint8_t const * data() const noexcept
static std::size_t size() noexcept
Service registry for dependency injection.
An immutable linear range of bytes.
Slot is associated with a specific validator via validator's public key.
Slots is a container for validator's Slot and handles Slot update when a message is received from a v...
Maintains squelching of relaying messages from validators.
Simulate link from a validator to a peer directly connected to the server.
std::pair< milliseconds, milliseconds > Latency
Link(Validator &validator, PeerSPtr peer, Latency latency={milliseconds(5), milliseconds(15)})
void send(MessageSPtr const &m, SquelchCB f)
static void randAdvance(milliseconds min, milliseconds max)
static duration randDuration(milliseconds min, milliseconds max)
static bool const is_steady
static void advance(duration d) noexcept
std::chrono::time_point< ManualClock > time_point
static void reset() noexcept
static time_point now() noexcept
std::chrono::duration< std::uint32_t, period > duration
void onDisconnectPeer(Peer::ID peer)
static void forRand(std::uint32_t min, std::uint32_t max, std::function< void(std::uint32_t)> f)
bool isSelected(Peer::ID id)
Is peer in Selected state in any of the slots.
void propagate(LinkIterCB link, std::uint16_t nValidators=kMaxValidators, std::uint32_t nMessages=kMaxMessages, bool purge=true, bool resetClock=true)
std::vector< Validator > validators_
Network(Application &app)
bool allCounting(Peer::ID peer)
Check if there are peers to unsquelch - peer is in Selected state in any of the slots and there are p...
void enableLink(std::uint16_t validatorId, Peer::ID peer, bool enable)
Validator & validator(std::uint16_t v)
std::set< ID > getSelected(PublicKey const &validator)
std::uint16_t getNumPeers() const
void updateSlotAndSquelch(UInt256 const &key, PublicKey const &validator, Peer::ID id, SquelchCB f, protocol::MessageType type=protocol::mtVALIDATION) override
PeerSPtr addPeer(bool useCache=true)
bool isSelected(PublicKey const &validator, Peer::ID peer)
ID getSelectedPeer(PublicKey const &validator)
ServiceRegistry & registry_
std::unordered_map< ID, std::tuple< reduce_relay::PeerState, std::uint16_t, std::uint32_t, std::uint32_t > > getPeers(PublicKey const &validator)
std::optional< Peer::ID > deleteLastPeer()
std::unordered_map< Peer::ID, PeerSPtr > Peers
void deleteIdlePeers(UnsquelchCB f) override
std::uint16_t inState(PublicKey const &validator, reduce_relay::PeerState state)
OverlaySim(Application &app)
bool isCountingState(PublicKey const &validator)
~OverlaySim() override=default
void deletePeer(ID id, UnsquelchCB f) override
reduce_relay::Slots< ManualClock > slots_
void deletePeer(Peer::ID id, bool useCache=true)
void unsquelch(PublicKey const &validator, Peer::ID id) const override
Unsquelch handler.
void squelch(PublicKey const &validator, Peer::ID id, std::uint32_t squelchDuration) const override
Squelch handler.
Simulate server's OverlayImpl.
virtual void deleteIdlePeers(UnsquelchCB)=0
virtual void updateSlotAndSquelch(UInt256 const &key, PublicKey const &validator, Peer::ID id, SquelchCB f, protocol::MessageType type=protocol::mtVALIDATION)=0
virtual ~Overlay()=default
virtual void deletePeer(Peer::ID, UnsquelchCB)=0
Simulate two entities - peer directly connected to the server (via squelch in PeerSim) and PeerImp (v...
virtual void onMessage(protocol::TMSquelch const &squelch)=0
virtual void onMessage(MessageSPtr const &m, SquelchCB f)=0
void send(protocol::TMSquelch const &squelch)
~PeerSim() override=default
void onMessage(protocol::TMSquelch const &squelch) override
Remote Peer (Directly connected Peer).
reduce_relay::Squelch< ManualClock > squelch_
void onMessage(MessageSPtr const &m, SquelchCB f) override
Local Peer (PeerImp).
PeerSim(Overlay &overlay, beast::Journal journal)
void send(std::shared_ptr< Message > const &) override
void addPeer(PeerSPtr peer)
Validator & operator=(Validator &&)=default
void send(SquelchCB f)
Send to all peers.
void forLinks(LinkIterCB f, bool simulateSlow=false)
Validator(Validator const &)=default
Validator & operator=(Validator const &)=default
void send(std::vector< Peer::ID > peers, SquelchCB f)
Send to specific peers.
void deletePeer(Peer::ID id)
std::unordered_map< Peer::ID, LinkSPtr > Links
Validator(Validator &&)=default
void linkDown(Peer::ID id)
void forLinks(std::vector< Peer::ID > peers, LinkIterCB f)
void testRandom(bool log)
void run() override
Runs the suite.
void printPeers(std::string const &msg, std::uint16_t validator=0)
void doTest(std::string const &msg, bool log, std::function< void(bool)> f)
bool propagateNoSquelch(bool log, std::uint16_t nMessages, bool countingState, bool purge=true, bool resetClock=true)
Send fewer message so that squelch event is not generated.
bool propagateAndSquelch(bool log, bool purge=true, bool resetClock=true)
Propagate enough messages to generate one squelch event.
static Peer::ID sendSquelch(PublicKey const &validator, PeerWPtr const &peerPtr, std::optional< std::uint32_t > duration)
Send squelch (if duration is set) or unsquelch (if duration not set).
void testConfig(bool log)
void random(bool log)
Randomly brings the link between a validator and a peer down.
void testSquelchedPeerDisconnects(bool log)
Squelched peer disconnects.
void testPeerUnsquelchedTooSoon(bool log)
Receiving message from squelched peer too soon should not change the slot's state to Counting.
reduce_relay::Slot< ManualClock > Slot
void testInitialRound(bool log)
Initial counting round: three peers receive message "faster" then others.
void testSelectedPeerStopsRelaying(bool log)
Selected peer stops relaying.
void testSelectedPeerDisconnects(bool log)
Selected peer disconnects.
bool checkCounting(PublicKey const &validator, bool isCountingState)
void testPeerUnsquelched(bool log)
Receiving message from squelched peer should change the slot's state to Counting.
void testNewPeer(bool log)
Receiving a message from new peer should change the slot's state to Counting.
boost::asio::ip::address Address
static std::string makeReason(String const &reason, char const *file, int line)
static constexpr uint16_t kMaxMessageThreshold
static constexpr auto kWaitOnBootup
static constexpr auto kIdled
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
std::unique_ptr< Config > validator(std::unique_ptr< Config >, std::string const &)
adjust configuration with params needed to be a validator
static constexpr std::uint32_t kMaxMessages
std::function< void(Link &, MessageSPtr)> LinkIterCB
BEAST_DEFINE_TESTSUITE(AMMClawback, app, xrpl)
std::shared_ptr< Peer > PeerSPtr
static constexpr std::uint32_t kMaxPeers
BEAST_DEFINE_TESTSUITE_MANUAL(AMMCalc, app, xrpl)
std::shared_ptr< Message > MessageSPtr
static constexpr std::uint32_t kMaxValidators
std::shared_ptr< Link > LinkSPtr
std::weak_ptr< Peer > PeerWPtr
std::function< void(PublicKey const &, PeerWPtr const &)> UnsquelchCB
std::function< void(PublicKey const &, PeerWPtr const &, std::uint32_t)> SquelchCB
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
std::pair< PublicKey, SecretKey > randomKeyPair(KeyType type)
Create a key pair using secure random numbers.
int run(int argc, char **argv)
T get(Section const §ion, std::string const &name, T const &defaultValue=T{})
Retrieve a key/value pair from a section.
HttpResponseType makeResponse(bool crawlPublic, HttpRequestType const &req, beast::ip::Address publicIp, beast::ip::Address remoteIp, UInt256 const &sharedValue, std::optional< std::uint32_t > networkID, ProtocolVersion protocol, Application &app)
Make http response.
auto makeRequest(bool crawlPublic, bool comprEnabled, bool ledgerReplayEnabled, bool txReduceRelayEnabled, bool vpReduceRelayEnabled) -> RequestType
Make outbound http request.
boost::beast::http::request< boost::beast::http::dynamic_body > HttpRequestType
bool peerFeatureEnabled(Headers const &request, std::string const &feature, std::string value, bool config)
Check if a feature should be enabled for a peer.
constexpr Number squelch(Number const &x, Number const &limit) noexcept
static constexpr char kFeatureVprr[]
T dynamic_pointer_cast(T... args)
time_point< ManualClock > time
std::optional< PublicKey > key