rippled
Loading...
Searching...
No Matches
reduce_relay_test.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright 2020 Ripple Labs Inc.
5
6 Permission to use, copy, modify, and/or distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18//==============================================================================
19
20#include <test/jtx/Env.h>
21
22#include <xrpld/overlay/Message.h>
23#include <xrpld/overlay/Peer.h>
24#include <xrpld/overlay/Slot.h>
25#include <xrpld/overlay/Squelch.h>
26#include <xrpld/overlay/detail/Handshake.h>
27
28#include <xrpl/basics/random.h>
29#include <xrpl/beast/unit_test.h>
30#include <xrpl/protocol/SecretKey.h>
31#include <xrpl/protocol/messages.h>
32
33#include <boost/thread.hpp>
34
35#include <numeric>
36#include <optional>
37
38namespace ripple {
39
40namespace test {
41
42using namespace std::chrono;
43
44class Link;
45
50using SquelchCB =
51 std::function<void(PublicKey const&, PeerWPtr const&, std::uint32_t)>;
52using UnsquelchCB = std::function<void(PublicKey const&, PeerWPtr const&)>;
54
55static constexpr std::uint32_t MAX_PEERS = 10;
56static constexpr std::uint32_t MAX_VALIDATORS = 10;
57static constexpr std::uint32_t MAX_MESSAGES = 200000;
58
62class PeerPartial : public Peer
63{
64public:
67 {
68 }
69
71 virtual ~PeerPartial()
72 {
73 }
74 virtual void
76 virtual void
77 onMessage(protocol::TMSquelch const& squelch) = 0;
78 void
79 send(protocol::TMSquelch const& squelch)
80 {
82 }
83
84 // dummy implementation
85 void
86 send(std::shared_ptr<Message> const& m) override
87 {
88 }
90 getRemoteAddress() const override
91 {
92 return {};
93 }
94 void
95 charge(Resource::Charge const& fee, std::string const& context = {})
96 override
97 {
98 }
99 bool
100 cluster() const override
101 {
102 return false;
103 }
104 bool
105 isHighLatency() const override
106 {
107 return false;
108 }
109 int
110 getScore(bool) const override
111 {
112 return 0;
113 }
114 PublicKey const&
115 getNodePublic() const override
116 {
117 return nodePublicKey_;
118 }
120 json() override
121 {
122 return {};
123 }
124 bool
126 {
127 return false;
128 }
130 publisherListSequence(PublicKey const&) const override
131 {
132 return {};
133 }
134 void
136 {
137 }
138 uint256 const&
139 getClosedLedgerHash() const override
140 {
141 static uint256 hash{};
142 return hash;
143 }
144 bool
145 hasLedger(uint256 const& hash, std::uint32_t seq) const override
146 {
147 return false;
148 }
149 void
150 ledgerRange(std::uint32_t& minSeq, std::uint32_t& maxSeq) const override
151 {
152 }
153 bool
154 hasTxSet(uint256 const& hash) const override
155 {
156 return false;
157 }
158 void
159 cycleStatus() override
160 {
161 }
162 bool
164 {
165 return false;
166 }
167 bool
168 compressionEnabled() const override
169 {
170 return false;
171 }
172 bool
173 txReduceRelayEnabled() const override
174 {
175 return false;
176 }
177 void
178 sendTxQueue() override
179 {
180 }
181 void
182 addTxQueue(const uint256&) override
183 {
184 }
185 void
186 removeTxQueue(const uint256&) override
187 {
188 }
189};
190
193{
194public:
195 typedef uint64_t rep;
199 inline static const bool is_steady = false;
200
201 static void
202 advance(duration d) noexcept
203 {
204 now_ += d;
205 }
206
207 static void
209 {
210 now_ += randDuration(min, max);
211 }
212
213 static void
214 reset() noexcept
215 {
216 now_ = time_point(seconds(0));
217 }
218
219 static time_point
220 now() noexcept
221 {
222 return now_;
223 }
224
225 static duration
227 {
228 return duration(milliseconds(rand_int(min.count(), max.count())));
229 }
230
231 explicit ManualClock() = default;
232
233private:
234 inline static time_point now_ = time_point(seconds(0));
235};
236
239{
240public:
241 Overlay() = default;
242 virtual ~Overlay() = default;
243
244 virtual void
246 uint256 const& key,
247 PublicKey const& validator,
248 Peer::id_t id,
249 SquelchCB f,
250 protocol::MessageType type = protocol::mtVALIDATION) = 0;
251
252 virtual void deleteIdlePeers(UnsquelchCB) = 0;
253
255};
256
257class Validator;
258
262class Link
263{
265
266public:
269 PeerSPtr peer,
270 Latency const& latency = {milliseconds(5), milliseconds(15)})
271 : validator_(validator), peer_(peer), latency_(latency), up_(true)
272 {
273 auto sp = peer_.lock();
274 assert(sp);
275 }
276 ~Link() = default;
277 void
279 {
280 if (!up_)
281 return;
282 auto sp = peer_.lock();
283 assert(sp);
284 auto peer = std::dynamic_pointer_cast<PeerPartial>(sp);
285 peer->onMessage(m, f);
286 }
287 Validator&
289 {
290 return validator_;
291 }
292 void
293 up(bool linkUp)
294 {
295 up_ = linkUp;
296 }
299 {
300 auto p = peer_.lock();
301 assert(p);
302 return p->id();
303 }
306 {
307 auto p = peer_.lock();
308 assert(p);
309 return p;
310 }
311
312private:
316 bool up_;
317};
318
321{
323
324public:
326 {
327 protocol::TMValidation v;
328 v.set_validation("validation");
329 message_ = std::make_shared<Message>(v, protocol::mtVALIDATION, pkey_);
330 id_ = sid_++;
331 }
332 Validator(Validator const&) = default;
333 Validator(Validator&&) = default;
334 Validator&
335 operator=(Validator const&) = default;
336 Validator&
337 operator=(Validator&&) = default;
339 {
340 clear();
341 }
342
343 void
345 {
346 links_.clear();
347 }
348
349 static void
351 {
352 sid_ = 0;
353 }
354
355 PublicKey const&
357 {
358 return pkey_;
359 }
360
361 operator PublicKey() const
362 {
363 return pkey_;
364 }
365
366 void
368 {
370 std::make_pair(peer->id(), std::make_shared<Link>(*this, peer)));
371 }
372
373 void
375 {
376 links_.erase(id);
377 }
378
379 void
381 {
382 for (auto id : peers)
383 {
384 assert(links_.find(id) != links_.end());
385 f(*links_[id], message_);
386 }
387 }
388
389 void
390 for_links(LinkIterCB f, bool simulateSlow = false)
391 {
394 links_.begin(), links_.end(), std::back_inserter(v), [](auto& kv) {
395 return kv.second;
396 });
398 std::mt19937 g(d());
399 std::shuffle(v.begin(), v.end(), g);
400
401 for (auto& link : v)
402 {
403 f(*link, message_);
404 }
405 }
406
408 void
410 {
411 for_links(peers, [&](Link& link, MessageSPtr m) { link.send(m, f); });
412 }
413
415 void
417 {
418 for_links([&](Link& link, MessageSPtr m) { link.send(m, f); });
419 }
420
423 {
424 return message_;
425 }
426
429 {
430 return id_;
431 }
432
433 void
435 {
436 auto it = links_.find(id);
437 assert(it != links_.end());
438 it->second->up(true);
439 }
440
441 void
443 {
444 auto it = links_.find(id);
445 assert(it != links_.end());
446 it->second->up(false);
447 }
448
449private:
453 inline static std::uint16_t sid_ = 0;
455};
456
457class PeerSim : public PeerPartial, public std::enable_shared_from_this<PeerSim>
458{
459public:
461 PeerSim(Overlay& overlay, beast::Journal journal)
462 : overlay_(overlay), squelch_(journal)
463 {
464 id_ = sid_++;
465 }
466
467 ~PeerSim() = default;
468
469 id_t
470 id() const override
471 {
472 return id_;
473 }
474
475 static void
477 {
478 sid_ = 0;
479 }
480
482 void
483 onMessage(MessageSPtr const& m, SquelchCB f) override
484 {
485 auto validator = m->getValidatorKey();
486 assert(validator);
487 if (!squelch_.expireSquelch(*validator))
488 return;
489
491 }
492
494 virtual void
495 onMessage(protocol::TMSquelch const& squelch) override
496 {
497 auto validator = squelch.validatorpubkey();
498 PublicKey key(Slice(validator.data(), validator.size()));
499 if (squelch.squelch())
500 squelch_.addSquelch(
501 key, std::chrono::seconds{squelch.squelchduration()});
502 else
503 squelch_.removeSquelch(key);
504 }
505
506private:
507 inline static id_t sid_ = 0;
511};
512
514{
516
517public:
520 OverlaySim(Application& app) : slots_(app.logs(), *this), logs_(app.logs())
521 {
522 }
523
524 ~OverlaySim() = default;
525
526 void
528 {
529 peers_.clear();
531 slots_.deleteIdlePeers();
532 }
533
536 {
537 auto res = slots_.inState(validator, state);
538 return res ? *res : 0;
539 }
540
541 void
543 uint256 const& key,
544 PublicKey const& validator,
545 Peer::id_t id,
546 SquelchCB f,
547 protocol::MessageType type = protocol::mtVALIDATION) override
548 {
549 squelch_ = f;
550 slots_.updateSlotAndSquelch(key, validator, id, type);
551 }
552
553 void
555 {
556 unsquelch_ = f;
557 slots_.deletePeer(id, true);
558 }
559
560 void
562 {
563 unsquelch_ = f;
564 slots_.deleteIdlePeers();
565 }
566
568 addPeer(bool useCache = true)
569 {
570 PeerSPtr peer{};
571 Peer::id_t id;
572 if (peersCache_.empty() || !useCache)
573 {
574 peer = std::make_shared<PeerSim>(*this, logs_.journal("Squelch"));
575 id = peer->id();
576 }
577 else
578 {
579 auto it = peersCache_.begin();
580 peer = it->second;
581 id = it->first;
582 peersCache_.erase(it);
583 }
584 peers_.emplace(std::make_pair(id, peer));
585 return peer;
586 }
587
588 void
589 deletePeer(Peer::id_t id, bool useCache = true)
590 {
591 auto it = peers_.find(id);
592 assert(it != peers_.end());
593 deletePeer(id, [&](PublicKey const&, PeerWPtr) {});
594 if (useCache)
595 peersCache_.emplace(std::make_pair(id, it->second));
596 peers_.erase(it);
597 }
598
599 void
601 {
602 while (!peers_.empty())
603 deletePeer(peers_.begin()->first);
604 while (!peersCache_.empty())
605 addPeer();
606 }
607
610 {
611 if (peers_.empty())
612 return {};
613
614 std::uint8_t maxId = 0;
615
616 for (auto& [id, _] : peers_)
617 {
618 (void)_;
619 if (id > maxId)
620 maxId = id;
621 }
622
623 deletePeer(maxId, false);
624
625 return maxId;
626 }
627
628 bool
630 {
632 }
633
636 {
637 return slots_.getSelected(validator);
638 }
639
640 bool
642 {
643 auto selected = slots_.getSelected(validator);
644 return selected.find(peer) != selected.end();
645 }
646
647 id_t
649 {
650 auto selected = slots_.getSelected(validator);
651 assert(selected.size());
652 return *selected.begin();
653 }
654
656 id_t,
663 {
664 return slots_.getPeers(validator);
665 }
666
669 {
670 return peers_.size();
671 }
672
673private:
674 void
676 PublicKey const& validator,
677 Peer::id_t id,
678 std::uint32_t squelchDuration) const override
679 {
680 if (auto it = peers_.find(id); it != peers_.end())
681 squelch_(validator, it->second, squelchDuration);
682 }
683 void
684 unsquelch(PublicKey const& validator, Peer::id_t id) const override
685 {
686 if (auto it = peers_.find(id); it != peers_.end())
687 unsquelch_(validator, it->second);
688 }
695};
696
698{
699public:
701 {
702 init();
703 }
704
705 void
707 {
709 for (int p = 0; p < MAX_PEERS; p++)
710 {
711 auto peer = overlay_.addPeer();
712 for (auto& v : validators_)
713 v.addPeer(peer);
714 }
715 }
716
717 ~Network() = default;
718
719 void
721 {
722 validators_.clear();
723 overlay_.clear();
726 init();
727 }
728
731 {
732 auto peer = overlay_.addPeer();
733 for (auto& v : validators_)
734 v.addPeer(peer);
735 return peer->id();
736 }
737
738 void
740 {
741 auto id = overlay_.deleteLastPeer();
742
743 if (!id)
744 return;
745
746 for (auto& validator : validators_)
748 }
749
750 void
752 {
753 while (overlay_.getNumPeers() > MAX_PEERS)
755 }
756
757 Validator&
759 {
760 assert(v < validators_.size());
761 return validators_[v];
762 }
763
766 {
767 return overlay_;
768 }
769
770 void
771 enableLink(std::uint16_t validatorId, Peer::id_t peer, bool enable)
772 {
773 auto it =
774 std::find_if(validators_.begin(), validators_.end(), [&](auto& v) {
775 return v.id() == validatorId;
776 });
777 assert(it != validators_.end());
778 if (enable)
779 it->linkUp(peer);
780 else
781 it->linkDown(peer);
782 }
783
784 void
786 {
787 // Send unsquelch to the Peer on all links. This way when
788 // the Peer "reconnects" it starts sending messages on the link.
789 // We expect that if a Peer disconnects and then reconnects, it's
790 // unsquelched.
791 protocol::TMSquelch squelch;
792 squelch.set_squelch(false);
793 for (auto& v : validators_)
794 {
795 PublicKey key = v;
796 squelch.clear_validatorpubkey();
797 squelch.set_validatorpubkey(key.data(), key.size());
798 v.for_links({peer}, [&](Link& l, MessageSPtr) {
799 std::dynamic_pointer_cast<PeerSim>(l.getPeer())->send(squelch);
800 });
801 }
802 }
803
804 void
806 std::uint32_t min,
807 std::uint32_t max,
809 {
810 auto size = max - min;
812 std::iota(s.begin(), s.end(), min);
814 std::mt19937 g(d());
815 std::shuffle(s.begin(), s.end(), g);
816 for (auto v : s)
817 f(v);
818 }
819
820 void
822 LinkIterCB link,
823 std::uint16_t nValidators = MAX_VALIDATORS,
824 std::uint32_t nMessages = MAX_MESSAGES,
825 bool purge = true,
826 bool resetClock = true)
827 {
828 if (resetClock)
830
831 if (purge)
832 {
833 purgePeers();
835 }
836
837 for (int m = 0; m < nMessages; ++m)
838 {
840 for_rand(0, nValidators, [&](std::uint32_t v) {
841 validators_[v].for_links(link);
842 });
843 }
844 }
845
847 bool
849 {
850 for (auto& v : validators_)
851 {
852 if (overlay_.isSelected(v, id))
853 return true;
854 }
855 return false;
856 }
857
862 bool
864 {
865 for (auto& v : validators_)
866 {
867 if (!overlay_.isSelected(v, peer))
868 continue;
869 auto peers = overlay_.getPeers(v);
870 for (auto& [_, v] : peers)
871 {
872 (void)_;
873 if (std::get<reduce_relay::PeerState>(v) ==
875 return false;
876 }
877 }
878 return true;
879 }
880
881private:
884};
885
887{
890
891protected:
892 void
894 {
896 std::cout << msg << " " << "num peers "
897 << (int)network_.overlay().getNumPeers() << std::endl;
898 for (auto& [k, v] : peers)
899 std::cout << k << ":" << (int)std::get<reduce_relay::PeerState>(v)
900 << " ";
902 }
903
907 PublicKey const& validator,
908 PeerWPtr const& peerPtr,
910 {
911 protocol::TMSquelch squelch;
912 bool res = duration ? true : false;
913 squelch.set_squelch(res);
914 squelch.set_validatorpubkey(validator.data(), validator.size());
915 if (res)
916 squelch.set_squelchduration(*duration);
917 auto sp = peerPtr.lock();
918 assert(sp);
919 std::dynamic_pointer_cast<PeerSim>(sp)->send(squelch);
920 return sp->id();
921 }
922
923 enum State { On, Off, WaitReset };
925 // Link down or Peer disconnect event
926 // TBD - add new peer event
927 // TBD - add overlapping type of events at any
928 // time in any quantity
929 struct Event
930 {
934 bool isSelected_ = false;
939 bool handled_ = false;
940 };
941
945 void
947 {
949 {LinkDown, {}}, {PeerDisconnected, {}}};
951
952 network_.reset();
953 network_.propagate([&](Link& link, MessageSPtr m) {
954 auto& validator = link.validator();
955 auto now = ManualClock::now();
956
957 bool squelched = false;
959
960 link.send(
961 m,
962 [&](PublicKey const& key,
963 PeerWPtr const& peerPtr,
965 assert(key == validator);
966 auto p = sendSquelch(key, peerPtr, duration);
967 squelched = true;
968 str << p << " ";
969 });
970
971 if (squelched)
972 {
973 auto selected = network_.overlay().getSelected(validator);
974 str << " selected: ";
975 for (auto s : selected)
976 str << s << " ";
977 if (log)
979 << (double)reduce_relay::epoch<milliseconds>(now)
980 .count() /
981 1000.
982 << " random, squelched, validator: " << validator.id()
983 << " peers: " << str.str() << std::endl;
984 auto countingState =
986 BEAST_EXPECT(
987 countingState == false &&
988 selected.size() == reduce_relay::MAX_SELECTED_PEERS);
989 }
990
991 // Trigger Link Down or Peer Disconnect event
992 // Only one Link Down at a time
993 if (events[EventType::LinkDown].state_ == State::Off)
994 {
995 auto update = [&](EventType event) {
996 events[event].cnt_++;
997 events[event].validator_ = validator.id();
998 events[event].key_ = validator;
999 events[event].peer_ = link.peerId();
1000 events[event].state_ = State::On;
1001 events[event].time_ = now;
1002 if (event == EventType::LinkDown)
1003 {
1005 validator.id(), link.peerId(), false);
1006 events[event].isSelected_ =
1008 validator, link.peerId());
1009 }
1010 else
1011 events[event].isSelected_ =
1012 network_.isSelected(link.peerId());
1013 };
1014 auto r = rand_int(0, 1000);
1015 if (r == (int)EventType::LinkDown ||
1017 {
1018 update(static_cast<EventType>(r));
1019 }
1020 }
1021
1022 if (events[EventType::PeerDisconnected].state_ == State::On)
1023 {
1024 auto& event = events[EventType::PeerDisconnected];
1025 bool allCounting = network_.allCounting(event.peer_);
1027 event.peer_,
1028 [&](PublicKey const& v, PeerWPtr const& peerPtr) {
1029 if (event.isSelected_)
1030 sendSquelch(v, peerPtr, {});
1031 event.handled_ = true;
1032 });
1033 // Should only be unsquelched if the peer is in Selected state
1034 // If in Selected state it's possible unsquelching didn't
1035 // take place because there is no peers in Squelched state in
1036 // any of the slots where the peer is in Selected state
1037 // (allCounting is true)
1038 bool handled =
1039 (event.isSelected_ == false && !event.handled_) ||
1040 (event.isSelected_ == true &&
1041 (event.handled_ || allCounting));
1042 BEAST_EXPECT(handled);
1043 event.state_ = State::Off;
1044 event.isSelected_ = false;
1045 event.handledCnt_ += handled;
1046 event.handled_ = false;
1047 network_.onDisconnectPeer(event.peer_);
1048 }
1049
1050 auto& event = events[EventType::LinkDown];
1051 // Check every sec for idled peers. Idled peers are
1052 // created by Link Down event.
1053 if (now - lastCheck > milliseconds(1000))
1054 {
1055 lastCheck = now;
1056 // Check if Link Down event must be handled by
1057 // deleteIdlePeer(): 1) the peer is in Selected state;
1058 // 2) the peer has not received any messages for IDLED time;
1059 // 3) there are peers in Squelched state in the slot.
1060 // 4) peer is in Slot's peers_ (if not then it is deleted
1061 // by Slots::deleteIdlePeers())
1062 bool mustHandle = false;
1063 if (event.state_ == State::On && BEAST_EXPECT(event.key_))
1064 {
1065 event.isSelected_ =
1066 network_.overlay().isSelected(*event.key_, event.peer_);
1067 auto peers = network_.overlay().getPeers(*event.key_);
1068 auto d = reduce_relay::epoch<milliseconds>(now).count() -
1069 std::get<3>(peers[event.peer_]);
1070 mustHandle = event.isSelected_ &&
1074 0 &&
1075 peers.find(event.peer_) != peers.end();
1076 }
1078 [&](PublicKey const& v, PeerWPtr const& ptr) {
1079 event.handled_ = true;
1080 if (mustHandle && v == event.key_)
1081 {
1082 event.state_ = State::WaitReset;
1083 sendSquelch(validator, ptr, {});
1084 }
1085 });
1086 bool handled =
1087 (event.handled_ && event.state_ == State::WaitReset) ||
1088 (!event.handled_ && !mustHandle);
1089 BEAST_EXPECT(handled);
1090 }
1091 if (event.state_ == State::WaitReset ||
1092 (event.state_ == State::On &&
1093 (now - event.time_ > (reduce_relay::IDLED + seconds(2)))))
1094 {
1095 bool handled =
1096 event.state_ == State::WaitReset || !event.handled_;
1097 BEAST_EXPECT(handled);
1098 event.state_ = State::Off;
1099 event.isSelected_ = false;
1100 event.handledCnt_ += handled;
1101 event.handled_ = false;
1102 network_.enableLink(event.validator_, event.peer_, true);
1103 }
1104 });
1105
1106 auto& down = events[EventType::LinkDown];
1107 auto& disconnected = events[EventType::PeerDisconnected];
1108 // It's possible the last Down Link event is not handled
1109 BEAST_EXPECT(down.handledCnt_ >= down.cnt_ - 1);
1110 // All Peer Disconnect events must be handled
1111 BEAST_EXPECT(disconnected.cnt_ == disconnected.handledCnt_);
1112 if (log)
1113 std::cout << "link down count: " << down.cnt_ << "/"
1114 << down.handledCnt_
1115 << " peer disconnect count: " << disconnected.cnt_ << "/"
1116 << disconnected.handledCnt_;
1117 }
1118
1119 bool
1120 checkCounting(PublicKey const& validator, bool isCountingState)
1121 {
1122 auto countingState = network_.overlay().isCountingState(validator);
1123 BEAST_EXPECT(countingState == isCountingState);
1124 return countingState == isCountingState;
1125 }
1126
1127 void
1128 doTest(const std::string& msg, bool log, std::function<void(bool)> f)
1129 {
1130 testcase(msg);
1131 f(log);
1132 }
1133
1139 void
1141 {
1142 doTest("Initial Round", log, [this](bool log) {
1143 BEAST_EXPECT(propagateAndSquelch(log));
1144 });
1145 }
1146
1150 void
1152 {
1153 doTest("Peer Unsquelched Too Soon", log, [this](bool log) {
1154 BEAST_EXPECT(propagateNoSquelch(log, 1, false, false, false));
1155 });
1156 }
1157
1161 void
1163 {
1164 ManualClock::advance(seconds(601));
1165 doTest("Peer Unsquelched", log, [this](bool log) {
1166 BEAST_EXPECT(propagateNoSquelch(log, 2, true, true, false));
1167 });
1168 }
1169
1171 bool
1172 propagateAndSquelch(bool log, bool purge = true, bool resetClock = true)
1173 {
1174 int n = 0;
1175 network_.propagate(
1176 [&](Link& link, MessageSPtr message) {
1177 std::uint16_t squelched = 0;
1178 link.send(
1179 message,
1180 [&](PublicKey const& key,
1181 PeerWPtr const& peerPtr,
1183 squelched++;
1184 sendSquelch(key, peerPtr, duration);
1185 });
1186 if (squelched)
1187 {
1188 BEAST_EXPECT(
1189 squelched ==
1191 n++;
1192 }
1193 },
1194 1,
1196 purge,
1197 resetClock);
1198 auto selected = network_.overlay().getSelected(network_.validator(0));
1199 BEAST_EXPECT(selected.size() == reduce_relay::MAX_SELECTED_PEERS);
1200 BEAST_EXPECT(n == 1); // only one selection round
1201 auto res = checkCounting(network_.validator(0), false);
1202 BEAST_EXPECT(res);
1203 return n == 1 && res;
1204 }
1205
1207 bool
1209 bool log,
1210 std::uint16_t nMessages,
1211 bool countingState,
1212 bool purge = true,
1213 bool resetClock = true)
1214 {
1215 bool squelched = false;
1216 network_.propagate(
1217 [&](Link& link, MessageSPtr message) {
1218 link.send(
1219 message,
1220 [&](PublicKey const& key,
1221 PeerWPtr const& peerPtr,
1223 squelched = true;
1224 BEAST_EXPECT(false);
1225 });
1226 },
1227 1,
1228 nMessages,
1229 purge,
1230 resetClock);
1231 auto res = checkCounting(network_.validator(0), countingState);
1232 return !squelched && res;
1233 }
1234
1238 void
1239 testNewPeer(bool log)
1240 {
1241 doTest("New Peer", log, [this](bool log) {
1242 BEAST_EXPECT(propagateAndSquelch(log, true, false));
1243 network_.addPeer();
1244 BEAST_EXPECT(propagateNoSquelch(log, 1, true, false, false));
1245 });
1246 }
1247
1250 void
1252 {
1253 doTest("Selected Peer Disconnects", log, [this](bool log) {
1254 ManualClock::advance(seconds(601));
1255 BEAST_EXPECT(propagateAndSquelch(log, true, false));
1256 auto id = network_.overlay().getSelectedPeer(network_.validator(0));
1257 std::uint16_t unsquelched = 0;
1258 network_.overlay().deletePeer(
1259 id, [&](PublicKey const& key, PeerWPtr const& peer) {
1260 unsquelched++;
1261 });
1262 BEAST_EXPECT(
1264 BEAST_EXPECT(checkCounting(network_.validator(0), true));
1265 });
1266 }
1267
1270 void
1272 {
1273 doTest("Selected Peer Stops Relaying", log, [this](bool log) {
1274 ManualClock::advance(seconds(601));
1275 BEAST_EXPECT(propagateAndSquelch(log, true, false));
1276 ManualClock::advance(reduce_relay::IDLED + seconds(1));
1277 std::uint16_t unsquelched = 0;
1278 network_.overlay().deleteIdlePeers(
1279 [&](PublicKey const& key, PeerWPtr const& peer) {
1280 unsquelched++;
1281 });
1282 auto peers = network_.overlay().getPeers(network_.validator(0));
1283 BEAST_EXPECT(
1285 BEAST_EXPECT(checkCounting(network_.validator(0), true));
1286 });
1287 }
1288
1291 void
1293 {
1294 doTest("Squelched Peer Disconnects", log, [this](bool log) {
1295 ManualClock::advance(seconds(601));
1296 BEAST_EXPECT(propagateAndSquelch(log, true, false));
1297 auto peers = network_.overlay().getPeers(network_.validator(0));
1298 auto it = std::find_if(peers.begin(), peers.end(), [&](auto it) {
1299 return std::get<reduce_relay::PeerState>(it.second) ==
1300 reduce_relay::PeerState::Squelched;
1301 });
1302 assert(it != peers.end());
1303 std::uint16_t unsquelched = 0;
1304 network_.overlay().deletePeer(
1305 it->first, [&](PublicKey const& key, PeerWPtr const& peer) {
1306 unsquelched++;
1307 });
1308 BEAST_EXPECT(unsquelched == 0);
1309 BEAST_EXPECT(checkCounting(network_.validator(0), false));
1310 });
1311 }
1312
1313 void
1314 testConfig(bool log)
1315 {
1316 doTest("Config Test", log, [&](bool log) {
1317 Config c;
1318
1319 std::string toLoad(R"rippleConfig(
1320[reduce_relay]
1321vp_enable=1
1322vp_squelch=1
1323)rippleConfig");
1324
1325 c.loadFromString(toLoad);
1326 BEAST_EXPECT(c.VP_REDUCE_RELAY_ENABLE == true);
1327 BEAST_EXPECT(c.VP_REDUCE_RELAY_SQUELCH == true);
1328
1329 Config c1;
1330
1331 toLoad = (R"rippleConfig(
1332[reduce_relay]
1333vp_enable=0
1334vp_squelch=0
1335)rippleConfig");
1336
1337 c1.loadFromString(toLoad);
1338 BEAST_EXPECT(c1.VP_REDUCE_RELAY_ENABLE == false);
1339 BEAST_EXPECT(c1.VP_REDUCE_RELAY_SQUELCH == false);
1340
1341 Config c2;
1342
1343 toLoad = R"rippleConfig(
1344[reduce_relay]
1345vp_enabled=1
1346vp_squelched=1
1347)rippleConfig";
1348
1349 c2.loadFromString(toLoad);
1350 BEAST_EXPECT(c2.VP_REDUCE_RELAY_ENABLE == false);
1351 BEAST_EXPECT(c2.VP_REDUCE_RELAY_SQUELCH == false);
1352 });
1353 }
1354
1355 void
1357 {
1358 doTest("Duplicate Message", log, [&](bool log) {
1359 network_.reset();
1360 // update message count for the same peer/validator
1361 std::int16_t nMessages = 5;
1362 for (int i = 0; i < nMessages; i++)
1363 {
1364 uint256 key(i);
1365 network_.overlay().updateSlotAndSquelch(
1366 key,
1367 network_.validator(0),
1368 0,
1369 [&](PublicKey const&, PeerWPtr, std::uint32_t) {});
1370 }
1371 auto peers = network_.overlay().getPeers(network_.validator(0));
1372 // first message changes Slot state to Counting and is not counted,
1373 // hence '-1'.
1374 BEAST_EXPECT(std::get<1>(peers[0]) == (nMessages - 1));
1375 // add duplicate
1376 uint256 key(nMessages - 1);
1377 network_.overlay().updateSlotAndSquelch(
1378 key,
1379 network_.validator(0),
1380 0,
1381 [&](PublicKey const&, PeerWPtr, std::uint32_t) {});
1382 // confirm the same number of messages
1383 peers = network_.overlay().getPeers(network_.validator(0));
1384 BEAST_EXPECT(std::get<1>(peers[0]) == (nMessages - 1));
1385 // advance the clock
1386 ManualClock::advance(reduce_relay::IDLED + seconds(1));
1387 network_.overlay().updateSlotAndSquelch(
1388 key,
1389 network_.validator(0),
1390 0,
1391 [&](PublicKey const&, PeerWPtr, std::uint32_t) {});
1392 peers = network_.overlay().getPeers(network_.validator(0));
1393 // confirm message number increased
1394 BEAST_EXPECT(std::get<1>(peers[0]) == nMessages);
1395 });
1396 }
1397
1399 {
1400 Handler() : maxDuration_(0)
1401 {
1402 }
1403 void
1405 const override
1406 {
1407 if (duration > maxDuration_)
1408 maxDuration_ = duration;
1409 }
1410 void
1411 unsquelch(PublicKey const&, Peer::id_t) const override
1412 {
1413 }
1414 mutable int maxDuration_;
1415 };
1416
1417 void
1419 {
1420 doTest("Random Squelch", l, [&](bool l) {
1422 Handler handler;
1423
1424 auto run = [&](int npeers) {
1425 handler.maxDuration_ = 0;
1427 env_.app().logs(), handler);
1428 // 1st message from a new peer switches the slot
1429 // to counting state and resets the counts of all peers +
1430 // MAX_MESSAGE_THRESHOLD + 1 messages to reach the threshold
1431 // and switch the slot's state to peer selection.
1432 for (int m = 1; m <= reduce_relay::MAX_MESSAGE_THRESHOLD + 2;
1433 m++)
1434 {
1435 for (int peer = 0; peer < npeers; peer++)
1436 {
1437 // make unique message hash to make the
1438 // slot's internal hash router accept the message
1439 std::uint64_t mid = m * 1000 + peer;
1440 uint256 const message{mid};
1442 message,
1443 validator,
1444 peer,
1445 protocol::MessageType::mtVALIDATION);
1446 }
1447 }
1448 // make Slot's internal hash router expire all messages
1449 ManualClock::advance(hours(1));
1450 };
1451
1452 using namespace reduce_relay;
1453 // expect max duration less than MAX_UNSQUELCH_EXPIRE_DEFAULT with
1454 // less than or equal to 60 peers
1455 run(20);
1456 BEAST_EXPECT(
1457 handler.maxDuration_ >= MIN_UNSQUELCH_EXPIRE.count() &&
1458 handler.maxDuration_ <= MAX_UNSQUELCH_EXPIRE_DEFAULT.count());
1459 run(60);
1460 BEAST_EXPECT(
1461 handler.maxDuration_ >= MIN_UNSQUELCH_EXPIRE.count() &&
1462 handler.maxDuration_ <= MAX_UNSQUELCH_EXPIRE_DEFAULT.count());
1463 // expect max duration greater than MIN_UNSQUELCH_EXPIRE and less
1464 // than MAX_UNSQUELCH_EXPIRE_PEERS with peers greater than 60
1465 // and less than 360
1466 run(350);
1467 // can't make this condition stronger. squelch
1468 // duration is probabilistic and max condition may still fail.
1469 // log when the value is low
1470 BEAST_EXPECT(
1471 handler.maxDuration_ >= MIN_UNSQUELCH_EXPIRE.count() &&
1472 handler.maxDuration_ <= MAX_UNSQUELCH_EXPIRE_PEERS.count());
1473 using namespace beast::unit_test::detail;
1474 if (handler.maxDuration_ <= MAX_UNSQUELCH_EXPIRE_DEFAULT.count())
1475 log << make_reason(
1476 "warning: squelch duration is low",
1477 __FILE__,
1478 __LINE__)
1479 << std::endl
1480 << std::flush;
1481 // more than 400 is still less than MAX_UNSQUELCH_EXPIRE_PEERS
1482 run(400);
1483 BEAST_EXPECT(
1484 handler.maxDuration_ >= MIN_UNSQUELCH_EXPIRE.count() &&
1485 handler.maxDuration_ <= MAX_UNSQUELCH_EXPIRE_PEERS.count());
1486 if (handler.maxDuration_ <= MAX_UNSQUELCH_EXPIRE_DEFAULT.count())
1487 log << make_reason(
1488 "warning: squelch duration is low",
1489 __FILE__,
1490 __LINE__)
1491 << std::endl
1492 << std::flush;
1493 });
1494 }
1495
1496 void
1498 {
1499 doTest("Handshake", log, [&](bool log) {
1500 auto setEnv = [&](bool enable) {
1501 Config c;
1503 str << "[reduce_relay]\n"
1504 << "vp_enable=" << enable << "\n"
1505 << "vp_squelch=" << enable << "\n"
1506 << "[compression]\n"
1507 << "1\n";
1508 c.loadFromString(str.str());
1509 env_.app().config().VP_REDUCE_RELAY_ENABLE =
1511 env_.app().config().VP_REDUCE_RELAY_SQUELCH =
1513 env_.app().config().COMPRESSION = c.COMPRESSION;
1514 };
1515 auto handshake = [&](int outboundEnable, int inboundEnable) {
1516 beast::IP::Address addr =
1517 boost::asio::ip::address::from_string("172.1.1.100");
1518
1519 setEnv(outboundEnable);
1520 auto request = ripple::makeRequest(
1521 true,
1522 env_.app().config().COMPRESSION,
1523 false,
1524 env_.app().config().TX_REDUCE_RELAY_ENABLE,
1525 env_.app().config().VP_REDUCE_RELAY_ENABLE);
1526 http_request_type http_request;
1527 http_request.version(request.version());
1528 http_request.base() = request.base();
1529 // feature enabled on the peer's connection only if both sides
1530 // are enabled
1531 auto const peerEnabled = inboundEnable && outboundEnable;
1532 // inbound is enabled if the request's header has the feature
1533 // enabled and the peer's configuration is enabled
1534 auto const inboundEnabled = peerFeatureEnabled(
1535 http_request, FEATURE_VPRR, inboundEnable);
1536 BEAST_EXPECT(!(peerEnabled ^ inboundEnabled));
1537
1538 setEnv(inboundEnable);
1539 auto http_resp = ripple::makeResponse(
1540 true,
1541 http_request,
1542 addr,
1543 addr,
1544 uint256{1},
1545 1,
1546 {1, 0},
1547 env_.app());
1548 // outbound is enabled if the response's header has the feature
1549 // enabled and the peer's configuration is enabled
1550 auto const outboundEnabled =
1551 peerFeatureEnabled(http_resp, FEATURE_VPRR, outboundEnable);
1552 BEAST_EXPECT(!(peerEnabled ^ outboundEnabled));
1553 };
1554 handshake(1, 1);
1555 handshake(1, 0);
1556 handshake(0, 1);
1557 handshake(0, 0);
1558 });
1559 }
1560
1563
1564public:
1565 reduce_relay_test() : env_(*this), network_(env_.app())
1566 {
1567 }
1568
1569 void
1570 run() override
1571 {
1572 bool log = false;
1573 testConfig(log);
1574 testInitialRound(log);
1575 testPeerUnsquelchedTooSoon(log);
1576 testPeerUnsquelched(log);
1577 testNewPeer(log);
1578 testSquelchedPeerDisconnects(log);
1579 testSelectedPeerDisconnects(log);
1580 testSelectedPeerStopsRelaying(log);
1581 testInternalHashRouter(log);
1582 testRandomSquelch(log);
1583 testHandshake(log);
1584 }
1585};
1586
1588{
1589 void
1590 testRandom(bool log)
1591 {
1592 doTest("Random Test", log, [&](bool log) { random(log); });
1593 }
1594
1595 void
1596 run() override
1597 {
1598 bool log = false;
1599 testRandom(log);
1600 }
1601};
1602
1603BEAST_DEFINE_TESTSUITE(reduce_relay, ripple_data, ripple);
1604BEAST_DEFINE_TESTSUITE_MANUAL(reduce_relay_simulate, ripple_data, ripple);
1605
1606} // namespace test
1607
1608} // namespace ripple
T back_inserter(T... args)
T begin(T... args)
Represents a JSON value.
Definition: json_value.h:148
A version-independent IP address and port combination.
Definition: IPEndpoint.h:39
A generic endpoint for log messages.
Definition: Journal.h:60
A testsuite class.
Definition: suite.h:55
log_os< char > log
Logging output stream.
Definition: suite.h:152
bool VP_REDUCE_RELAY_ENABLE
Definition: Config.h:248
void loadFromString(std::string const &fileContents)
Load the config from the contents of the string.
Definition: Config.cpp:478
bool COMPRESSION
Definition: Config.h:220
bool VP_REDUCE_RELAY_SQUELCH
Definition: Config.h:257
Manages partitions for logging.
Definition: Log.h:51
beast::Journal journal(std::string const &name)
Definition: Log.cpp:160
Represents a peer connection in the overlay.
std::uint32_t id_t
Uniquely identifies a peer.
A public key.
Definition: PublicKey.h:62
std::uint8_t const * data() const noexcept
Definition: PublicKey.h:87
std::size_t size() const noexcept
Definition: PublicKey.h:93
A consumption charge.
Definition: Charge.h:31
An immutable linear range of bytes.
Definition: Slice.h:46
Slot is associated with a specific validator via validator's public key.
Definition: overlay/Slot.h:106
Slots is a container for validator's Slot and handles Slot update when a message is received from a v...
Definition: overlay/Slot.h:536
void updateSlotAndSquelch(uint256 const &key, PublicKey const &validator, id_t id, protocol::MessageType type)
Calls Slot::update of Slot associated with the validator.
Definition: overlay/Slot.h:701
Maintains squelching of relaying messages from validators.
Definition: Squelch.h:39
Manually advanced clock.
static void reset() noexcept
std::chrono::duration< std::uint32_t, period > duration
static void advance(duration d) noexcept
static duration randDuration(milliseconds min, milliseconds max)
std::chrono::time_point< ManualClock > time_point
static void randAdvance(milliseconds min, milliseconds max)
static time_point now() noexcept
Validator & validator(std::uint16_t v)
std::vector< Validator > validators_
bool isSelected(Peer::id_t id)
Is peer in Selected state in any of the slots.
Network(Application &app)
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...
void propagate(LinkIterCB link, std::uint16_t nValidators=MAX_VALIDATORS, std::uint32_t nMessages=MAX_MESSAGES, bool purge=true, bool resetClock=true)
void for_rand(std::uint32_t min, std::uint32_t max, std::function< void(std::uint32_t)> f)
void onDisconnectPeer(Peer::id_t peer)
void enableLink(std::uint16_t validatorId, Peer::id_t peer, bool enable)
reduce_relay::Slots< ManualClock > slots_
std::uint16_t getNumPeers() const
std::uint16_t inState(PublicKey const &validator, reduce_relay::PeerState state)
void unsquelch(PublicKey const &validator, Peer::id_t id) const override
Unsquelch handler.
PeerSPtr addPeer(bool useCache=true)
void deletePeer(id_t id, UnsquelchCB f) override
std::optional< Peer::id_t > deleteLastPeer()
void updateSlotAndSquelch(uint256 const &key, PublicKey const &validator, Peer::id_t id, SquelchCB f, protocol::MessageType type=protocol::mtVALIDATION) override
void deleteIdlePeers(UnsquelchCB f) override
void squelch(PublicKey const &validator, Peer::id_t id, std::uint32_t squelchDuration) const override
Squelch handler.
bool isSelected(PublicKey const &validator, Peer::id_t peer)
void deletePeer(Peer::id_t id, bool useCache=true)
bool isCountingState(PublicKey const &validator)
std::set< id_t > getSelected(PublicKey const &validator)
std::unordered_map< id_t, std::tuple< reduce_relay::PeerState, std::uint16_t, std::uint32_t, std::uint32_t > > getPeers(PublicKey const &validator)
id_t getSelectedPeer(PublicKey const &validator)
Simulate server's OverlayImpl.
virtual void updateSlotAndSquelch(uint256 const &key, PublicKey const &validator, Peer::id_t id, SquelchCB f, protocol::MessageType type=protocol::mtVALIDATION)=0
virtual ~Overlay()=default
virtual void deletePeer(Peer::id_t, UnsquelchCB)=0
virtual void deleteIdlePeers(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
std::optional< std::size_t > publisherListSequence(PublicKey const &) const override
void send(protocol::TMSquelch const &squelch)
bool txReduceRelayEnabled() const override
void addTxQueue(const uint256 &) override
Aggregate transaction's hash.
uint256 const & getClosedLedgerHash() const override
bool hasRange(std::uint32_t uMin, std::uint32_t uMax) override
virtual void onMessage(MessageSPtr const &m, SquelchCB f)=0
Json::Value json() override
void send(std::shared_ptr< Message > const &m) override
bool compressionEnabled() const override
beast::IP::Endpoint getRemoteAddress() const override
bool cluster() const override
Returns true if this connection is a member of the cluster.
void setPublisherListSequence(PublicKey const &, std::size_t const) override
int getScore(bool) const override
void charge(Resource::Charge const &fee, std::string const &context={}) override
Adjust this peer's load balance based on the type of load imposed.
bool supportsFeature(ProtocolFeature f) const override
PublicKey const & getNodePublic() const override
void removeTxQueue(const uint256 &) override
Remove hash from the transactions' hashes queue.
bool isHighLatency() const override
bool hasTxSet(uint256 const &hash) const override
bool hasLedger(uint256 const &hash, std::uint32_t seq) const override
void sendTxQueue() override
Send aggregated transactions' hashes.
void ledgerRange(std::uint32_t &minSeq, std::uint32_t &maxSeq) const override
PeerSim(Overlay &overlay, beast::Journal journal)
virtual void onMessage(protocol::TMSquelch const &squelch) override
Remote Peer (Directly connected Peer)
void onMessage(MessageSPtr const &m, SquelchCB f) override
Local Peer (PeerImp)
reduce_relay::Squelch< ManualClock > squelch_
id_t id() const override
static std::uint16_t sid_
void addPeer(PeerSPtr peer)
Validator(Validator &&)=default
void send(SquelchCB f)
Send to all peers.
Validator & operator=(Validator &&)=default
void linkDown(Peer::id_t id)
void for_links(std::vector< Peer::id_t > peers, LinkIterCB f)
void deletePeer(Peer::id_t id)
void for_links(LinkIterCB f, bool simulateSlow=false)
Validator(Validator const &)=default
void send(std::vector< Peer::id_t > peers, SquelchCB f)
Send to specific peers.
void linkUp(Peer::id_t id)
Validator & operator=(Validator const &)=default
A transaction testing environment.
Definition: Env.h:120
Set the fee on a JTx.
Definition: fee.h:37
void testSquelchedPeerDisconnects(bool log)
Squelched peer disconnects.
void testNewPeer(bool log)
Receiving a message from new peer should change the slot's state to Counting.
void random(bool log)
Randomly brings the link between a validator and a peer down.
bool checkCounting(PublicKey const &validator, bool isCountingState)
bool propagateAndSquelch(bool log, bool purge=true, bool resetClock=true)
Propagate enough messages to generate one squelch event.
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.
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 testPeerUnsquelched(bool log)
Receiving message from squelched peer should change the slot's state to Counting.
void printPeers(const std::string &msg, std::uint16_t validator=0)
void testInitialRound(bool log)
Initial counting round: three peers receive message "faster" then others.
void doTest(const std::string &msg, bool log, std::function< void(bool)> f)
void testSelectedPeerStopsRelaying(bool log)
Selected peer stops relaying.
void run() override
Runs the suite.
void testPeerUnsquelchedTooSoon(bool log)
Receiving message from squelched peer too soon should not change the slot's state to Counting.
void testSelectedPeerDisconnects(bool log)
Selected peer disconnects.
T clear(T... args)
T emplace(T... args)
T empty(T... args)
T end(T... args)
T endl(T... args)
T erase(T... args)
T find(T... args)
T flush(T... args)
T iota(T... args)
T lock(T... args)
T make_pair(T... args)
boost::asio::ip::address Address
Definition: IPAddress.h:43
static constexpr uint16_t MAX_SELECTED_PEERS
static constexpr auto IDLED
PeerState
Peer's State.
Definition: overlay/Slot.h:50
static constexpr uint16_t MAX_MESSAGE_THRESHOLD
std::unique_ptr< Config > validator(std::unique_ptr< Config >, std::string const &)
adjust configuration with params needed to be a validator
Definition: envconfig.cpp:113
std::shared_ptr< Message > MessageSPtr
static constexpr std::uint32_t MAX_PEERS
static constexpr std::uint32_t MAX_VALIDATORS
static constexpr std::uint32_t MAX_MESSAGES
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
std::enable_if_t< std::is_integral< Integral >::value, Integral > rand_int()
http_response_type makeResponse(bool crawlPublic, http_request_type const &req, beast::IP::Address public_ip, beast::IP::Address remote_ip, uint256 const &sharedValue, std::optional< std::uint32_t > networkID, ProtocolVersion protocol, Application &app)
Make http response.
Definition: Handshake.cpp:392
PublicKey derivePublicKey(KeyType type, SecretKey const &sk)
Derive the public key from a secret key.
Definition: SecretKey.cpp:331
int run(int argc, char **argv)
Definition: Main.cpp:343
SecretKey randomSecretKey()
Create a secret key using secure random numbers.
Definition: SecretKey.cpp:299
KeyType
Definition: KeyType.h:28
boost::beast::http::request< boost::beast::http::dynamic_body > http_request_type
Definition: Handoff.h:33
bool peerFeatureEnabled(headers const &request, std::string const &feature, std::string value, bool config)
Check if a feature should be enabled for a peer.
Definition: Handshake.h:198
T get(Section const &section, std::string const &name, T const &defaultValue=T{})
Retrieve a key/value pair from a section.
Definition: BasicConfig.h:355
auto makeRequest(bool crawlPublic, bool comprEnabled, bool ledgerReplayEnabled, bool txReduceRelayEnabled, bool vpReduceRelayEnabled) -> request_type
Make outbound http request.
Definition: Handshake.cpp:365
std::pair< PublicKey, SecretKey > randomKeyPair(KeyType type)
Create a key pair using secure random numbers.
Definition: SecretKey.cpp:386
constexpr Number squelch(Number const &x, Number const &limit) noexcept
Definition: Number.h:363
static constexpr char FEATURE_VPRR[]
Definition: Handshake.h:143
STL namespace.
T shuffle(T... args)
T size(T... args)
T str(T... args)
Set the sequence number on a JTx.
Definition: seq.h:34
void squelch(PublicKey const &, Peer::id_t, std::uint32_t duration) const override
Squelch handler.
void unsquelch(PublicKey const &, Peer::id_t) const override
Unsquelch handler.
T transform(T... args)