1#include <test/jtx/Env.h>
2#include <test/jtx/envconfig.h>
4#include <xrpld/app/main/Application.h>
5#include <xrpld/overlay/Message.h>
6#include <xrpld/overlay/Peer.h>
7#include <xrpld/overlay/ReduceRelayCommon.h>
8#include <xrpld/overlay/Slot.h>
9#include <xrpld/overlay/Squelch.h>
10#include <xrpld/overlay/detail/Handshake.h>
12#include <xrpl/basics/base_uint.h>
13#include <xrpl/basics/random.h>
14#include <xrpl/beast/net/IPAddress.h>
15#include <xrpl/beast/net/IPEndpoint.h>
16#include <xrpl/beast/unit_test/suite.h>
17#include <xrpl/beast/utility/Journal.h>
18#include <xrpl/json/json_value.h>
19#include <xrpl/protocol/KeyType.h>
20#include <xrpl/protocol/PublicKey.h>
21#include <xrpl/protocol/SecretKey.h>
23#include <boost/asio/ip/address.hpp>
51using namespace std::chrono;
259 protocol::MessageType type = protocol::mtVALIDATION) = 0;
289 auto sp =
peer_.lock();
292 peer->onMessage(m, f);
307 auto p =
peer_.lock();
314 auto p =
peer_.lock();
336 protocol::TMValidation v;
337 v.set_validation(
"validation");
389 for (
auto id : peers)
391 assert(
links_.contains(
id));
444 auto it =
links_.find(
id);
445 assert(it !=
links_.end());
446 it->second->up(
true);
452 auto it =
links_.find(
id);
453 assert(it !=
links_.end());
454 it->second->up(
false);
559 return res ? *res : 0;
568 protocol::MessageType type = protocol::mtVALIDATION)
override
578 slots_.deletePeer(
id,
true);
612 auto it =
peers_.find(
id);
613 assert(it !=
peers_.end());
637 for (
auto& [
id, _] :
peers_)
664 return selected.contains(peer);
671 assert(!selected.empty());
672 return *selected.begin();
754 auto id =
overlay_.deleteLastPeer();
810 squelch.clear_validatorpubkey();
837 bool resetClock =
true)
848 for (
int m = 0; m < nMessages; ++m)
878 for (
auto& [_, v] : peers)
905 for (
auto& [k, v] : peers)
906 std::cout << k <<
":" << (int)std::get<reduce_relay::PeerState>(v) <<
" ";
920 bool const res =
static_cast<bool>(
duration);
925 auto sp = peerPtr.
lock();
966 bool squelched =
false;
980 str <<
" selected: ";
981 for (
auto s : selected)
985 std::cout << (double)reduce_relay::epoch<milliseconds>(now).count() / 1000.
986 <<
" random, squelched, validator: " <<
validator.id()
991 countingState ==
false &&
992 selected.size() ==
env_.app().config().vpReduceRelaySquelchMaxSelectedPeers);
1000 events[event].cnt++;
1001 events[event].validator =
validator.id();
1003 events[event].peer = link.
peerId();
1005 events[event].time = now;
1009 events[event].isSelected =
1027 bool const allCounting =
network_.allCounting(event.peer);
1030 if (event.isSelected)
1031 sendSquelch(v, peerPtr, {});
1032 event.handled =
true;
1039 bool const handled = (!
event.isSelected && !
event.handled) ||
1040 (event.isSelected && (event.handled || allCounting));
1041 BEAST_EXPECT(handled);
1043 event.isSelected =
false;
1044 event.handledCnt += handled;
1045 event.handled =
false;
1046 network_.onDisconnectPeer(event.peer);
1061 bool mustHandle =
false;
1062 if (event.state ==
State::On && BEAST_EXPECT(event.key))
1064 event.isSelected =
network_.overlay().isSelected(*event.key, event.peer);
1065 auto peers =
network_.overlay().getPeers(*event.key);
1067 std::get<3>(peers[event.peer]);
1068 mustHandle =
event.isSelected &&
1072 peers.contains(event.peer);
1075 event.handled =
true;
1076 if (mustHandle && v == event.key)
1083 (!event.handled && !mustHandle);
1084 BEAST_EXPECT(handled);
1091 BEAST_EXPECT(handled);
1093 event.isSelected =
false;
1094 event.handledCnt += handled;
1095 event.handled =
false;
1096 network_.enableLink(event.validator, event.peer,
true);
1103 BEAST_EXPECT(down.handledCnt >= down.cnt - 1);
1105 BEAST_EXPECT(disconnected.cnt == disconnected.handledCnt);
1108 std::cout <<
"link down count: " << down.cnt <<
"/" << down.handledCnt
1109 <<
" peer disconnect count: " << disconnected.cnt <<
"/"
1110 << disconnected.handledCnt;
1118 BEAST_EXPECT(countingState == isCountingState);
1119 return countingState == isCountingState;
1148 doTest(
"Peer Unsquelched Too Soon",
log, [
this](
bool log) {
1186 kMaxPeers -
env_.app().config().vpReduceRelaySquelchMaxSelectedPeers);
1195 BEAST_EXPECT(selected.size() ==
env_.app().config().vpReduceRelaySquelchMaxSelectedPeers);
1196 BEAST_EXPECT(n == 1);
1199 return n == 1 && res;
1211 bool resetClock =
true)
1213 bool squelched =
false;
1220 BEAST_EXPECT(
false);
1228 return !squelched && res;
1252 doTest(
"Selected Peer Disconnects",
log, [
this](
bool log) {
1261 kMaxPeers -
env_.app().config().vpReduceRelaySquelchMaxSelectedPeers);
1273 doTest(
"Selected Peer Stops Relaying",
log, [
this](
bool log) {
1278 network_.overlay().deleteIdlePeers(
1283 kMaxPeers -
env_.app().config().vpReduceRelaySquelchMaxSelectedPeers);
1294 doTest(
"Squelched Peer Disconnects",
log, [
this](
bool log) {
1299 return std::get<reduce_relay::PeerState>(it.second) ==
1302 assert(it != peers.end());
1306 BEAST_EXPECT(unsquelched == 0);
1314 doTest(
"Test Config - squelch enabled (legacy)",
log, [&](
bool log) {
1326 doTest(
"Test Config - squelch disabled (legacy)",
log, [&](
bool log) {
1339 toLoad = R
"xrpldConfig(
1347 doTest(
"Test Config - squelch enabled",
log, [&](
bool log) {
1352vp_base_squelch_enable=1
1359 doTest(
"Test Config - squelch disabled",
log, [&](
bool log) {
1364vp_base_squelch_enable=0
1371 doTest(
"Test Config - legacy and new",
log, [&](
bool log) {
1376vp_base_squelch_enable=0
1381 auto const expectedError =
1382 "Invalid reduce_relay"
1383 " cannot specify both vp_base_squelch_enable and vp_enable "
1385 "vp_enable was deprecated and replaced by "
1386 "vp_base_squelch_enable";
1397 BEAST_EXPECT(error == expectedError);
1400 doTest(
"Test Config - max selected peers",
log, [&](
bool log) {
1412 toLoad = R"xrpldConfig(
1414vp_base_squelch_max_selected_peers=6
1422 toLoad = R"xrpldConfig(
1424vp_base_squelch_max_selected_peers=2
1428 auto const expectedError =
1429 "Invalid reduce_relay"
1430 " vp_base_squelch_max_selected_peers must be "
1431 "greater than or equal to 3";
1441 BEAST_EXPECT(error == expectedError);
1446 testBaseSquelchReady(
bool log)
1451 env_.app().config().vpReduceRelayBaseSquelchEnable = baseSquelchEnabled;
1456 BEAST_EXPECT(!createSlots(
false).baseSquelchReady());
1460 BEAST_EXPECT(!createSlots(
true).baseSquelchReady());
1465 BEAST_EXPECT(createSlots(
true).baseSquelchReady());
1469 BEAST_EXPECT(!createSlots(
false).baseSquelchReady());
1474 testInternalHashRouter(
bool log)
1476 doTest(
"Duplicate Message",
log, [&](
bool log) {
1480 for (
int i = 0; i < nMessages; i++)
1483 network_.overlay().updateSlotAndSquelch(
1490 BEAST_EXPECT(std::get<1>(peers[0]) == (nMessages - 1));
1492 uint256 const key(nMessages - 1);
1493 network_.overlay().updateSlotAndSquelch(
1497 BEAST_EXPECT(std::get<1>(peers[0]) == (nMessages - 1));
1500 network_.overlay().updateSlotAndSquelch(
1504 BEAST_EXPECT(std::get<1>(peers[0]) == nMessages);
1510 Handler() =
default;
1517 unsquelch(PublicKey
const&, Peer::id_t)
const override
1520 mutable int maxDuration{0};
1524 testRandomSquelch(
bool l)
1526 doTest(
"Random Squelch", l, [&](
bool l) {
1530 auto run = [&](
int npeers) {
1531 handler.maxDuration = 0;
1532 reduce_relay::Slots<ManualClock> slots(env_.app(), handler, env_.app().config());
1539 for (
int peer = 0; peer < npeers; peer++)
1543 std::uint64_t
const mid = (m * 1000) + peer;
1545 slots.updateSlotAndSquelch(
1546 message,
validator, peer, protocol::MessageType::mtVALIDATION);
1553 using namespace reduce_relay;
1558 handler.maxDuration >= kMinUnsquelchExpire.count() &&
1559 handler.maxDuration <= kMaxUnsquelchExpireDefault.count());
1562 handler.maxDuration >= kMinUnsquelchExpire.count() &&
1563 handler.maxDuration <= kMaxUnsquelchExpireDefault.count());
1572 handler.maxDuration >= kMinUnsquelchExpire.count() &&
1573 handler.maxDuration <= kMaxUnsquelchExpirePeers.count());
1575 if (handler.maxDuration <= kMaxUnsquelchExpireDefault.count())
1577 log <<
makeReason(
"warning: squelch duration is low", __FILE__, __LINE__)
1584 handler.maxDuration >= kMinUnsquelchExpire.count() &&
1585 handler.maxDuration <= kMaxUnsquelchExpirePeers.count());
1586 if (handler.maxDuration <= kMaxUnsquelchExpireDefault.count())
1588 log <<
makeReason(
"warning: squelch duration is low", __FILE__, __LINE__)
1596 testHandshake(
bool log)
1598 doTest(
"Handshake", log, [&](
bool log) {
1599 auto setEnv = [&](
bool enable) {
1601 std::stringstream str;
1602 str <<
"[reduce_relay]\n"
1603 <<
"vp_enable=" << enable <<
"\n"
1604 <<
"[compression]\n"
1606 c.loadFromString(str.
str());
1607 env_.app().config().vpReduceRelayBaseSquelchEnable =
1608 c.vpReduceRelayBaseSquelchEnable;
1610 env_.app().config().compression = c.compression;
1612 auto handshake = [&](
int outboundEnable,
int inboundEnable) {
1615 setEnv(outboundEnable);
1618 env_.app().config().compression,
1620 env_.app().config().txReduceRelayEnable,
1621 env_.app().config().vpReduceRelayBaseSquelchEnable);
1623 httpRequest.version(request.version());
1624 httpRequest.base() = request.base();
1627 auto const peerEnabled = inboundEnable && outboundEnable;
1630 auto const inboundEnabled =
1632 BEAST_EXPECT(!(peerEnabled ^ inboundEnabled));
1634 setEnv(inboundEnable);
1636 true, httpRequest, addr, addr,
uint256{1}, 1, {1, 0}, env_.app());
1639 auto const outboundEnabled =
1641 BEAST_EXPECT(!(peerEnabled ^ outboundEnabled));
1656 cfg->vpReduceRelayBaseSquelchEnable =
true;
1657 cfg->vpReduceRelaySquelchMaxSelectedPeers = 6;
1660 , network_(env_.app())
1667 bool const log =
false;
1669 testInitialRound(log);
1670 testPeerUnsquelchedTooSoon(log);
1671 testPeerUnsquelched(log);
1673 testSquelchedPeerDisconnects(log);
1674 testSelectedPeerDisconnects(log);
1675 testSelectedPeerStopsRelaying(log);
1676 testInternalHashRouter(log);
1677 testRandomSquelch(log);
1679 testBaseSquelchReady(log);
1694 bool const log =
false;
T back_inserter(T... args)
A generic endpoint for log messages.
A version-independent IP address and port combination.
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_t
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
bool allCounting(Peer::id_t peer)
Check if there are peers to unsquelch - peer is in Selected state in any of the slots and there are p...
static void forRand(std::uint32_t min, std::uint32_t max, std::function< void(std::uint32_t)> f)
bool isSelected(Peer::id_t id)
Is peer in Selected state in any of the slots.
void enableLink(std::uint16_t validatorId, Peer::id_t peer, bool enable)
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)
Validator & validator(std::uint16_t v)
void onDisconnectPeer(Peer::id_t peer)
bool isSelected(PublicKey const &validator, Peer::id_t peer)
std::uint16_t getNumPeers() const
std::optional< Peer::id_t > deleteLastPeer()
std::unordered_map< id_t, std::tuple< reduce_relay::PeerState, std::uint16_t, std::uint32_t, std::uint32_t > > getPeers(PublicKey const &validator)
std::set< id_t > getSelected(PublicKey const &validator)
PeerSPtr addPeer(bool useCache=true)
void squelch(PublicKey const &validator, Peer::id_t id, std::uint32_t squelchDuration) const override
Squelch handler.
void deletePeer(Peer::id_t id, bool useCache=true)
ServiceRegistry & registry_
std::unordered_map< Peer::id_t, PeerSPtr > Peers
void unsquelch(PublicKey const &validator, Peer::id_t id) const override
Unsquelch handler.
void deleteIdlePeers(UnsquelchCB f) override
void deletePeer(id_t id, UnsquelchCB f) override
id_t getSelectedPeer(PublicKey const &validator)
std::uint16_t inState(PublicKey const &validator, reduce_relay::PeerState state)
OverlaySim(Application &app)
bool isCountingState(PublicKey const &validator)
~OverlaySim() override=default
void updateSlotAndSquelch(uint256 const &key, PublicKey const &validator, Peer::id_t id, SquelchCB f, protocol::MessageType type=protocol::mtVALIDATION) override
reduce_relay::Slots< ManualClock > slots_
Simulate server's OverlayImpl.
virtual void deleteIdlePeers(UnsquelchCB)=0
virtual void deletePeer(Peer::id_t, UnsquelchCB)=0
virtual ~Overlay()=default
virtual void updateSlotAndSquelch(uint256 const &key, PublicKey const &validator, Peer::id_t id, SquelchCB f, protocol::MessageType type=protocol::mtVALIDATION)=0
uint256 getClosedLedgerHash() const override
void send(std::shared_ptr< Message > const &m) override
bool hasLedger(uint256 const &hash, std::uint32_t seq) const override
void addTxQueue(uint256 const &) override
beast::ip::Endpoint getRemoteAddress() const override
virtual void onMessage(protocol::TMSquelch const &squelch)=0
bool compressionEnabled() const override
virtual void onMessage(MessageSPtr const &m, SquelchCB f)=0
void sendTxQueue() override
void removeTxQueue(uint256 const &) override
json::Value json() override
std::optional< std::size_t > publisherListSequence(PublicKey const &) const override
bool supportsFeature(ProtocolFeature f) const override
bool hasTxSet(uint256 const &hash) const override
void send(protocol::TMSquelch const &squelch)
void ledgerRange(std::uint32_t &minSeq, std::uint32_t &maxSeq) const override
~PeerPartial() override=default
bool cluster() const override
void cycleStatus() override
void charge(resource::Charge const &fee, std::string const &context={}) override
bool hasRange(std::uint32_t uMin, std::uint32_t uMax) override
bool isHighLatency() const override
PublicKey const & getNodePublic() const override
void setPublisherListSequence(PublicKey const &, std::size_t const) override
bool txReduceRelayEnabled() const override
int getScore(bool) const override
~PeerSim() override=default
void onMessage(protocol::TMSquelch const &squelch) override
Remote Peer (Directly connected Peer).
reduce_relay::Squelch< ManualClock > squelch_
std::string const & fingerprint() const override
void onMessage(MessageSPtr const &m, SquelchCB f) override
Local Peer (PeerImp).
PeerSim(Overlay &overlay, beast::Journal journal)
void addPeer(PeerSPtr peer)
Validator & operator=(Validator &&)=default
void send(std::vector< Peer::id_t > peers, SquelchCB f)
Send to specific peers.
void send(SquelchCB f)
Send to all peers.
void forLinks(LinkIterCB f, bool simulateSlow=false)
void linkDown(Peer::id_t id)
Validator(Validator const &)=default
void linkUp(Peer::id_t id)
void deletePeer(Peer::id_t id)
void forLinks(std::vector< Peer::id_t > peers, LinkIterCB f)
Validator & operator=(Validator const &)=default
std::unordered_map< Peer::id_t, LinkSPtr > Links
Validator(Validator &&)=default
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_t 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.
PublicKey derivePublicKey(KeyType type, SecretKey const &sk)
Derive the public key from a secret key.
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.
auto makeRequest(bool crawlPublic, bool comprEnabled, bool ledgerReplayEnabled, bool txReduceRelayEnabled, bool vpReduceRelayEnabled) -> request_type
Make outbound http request.
SecretKey randomSecretKey()
Create a secret key using secure random numbers.
http_response_type makeResponse(bool crawlPublic, http_request_type 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.
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
boost::beast::http::request< boost::beast::http::dynamic_body > http_request_type
static constexpr char kFeatureVprr[]
T dynamic_pointer_cast(T... args)
time_point< ManualClock > time
std::optional< PublicKey > key