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(uint256 const&) override
183 {
184 }
185 void
186 removeTxQueue(uint256 const&) override
187 {
188 }
189};
190
193{
194public:
195 typedef uint64_t rep;
199 inline static bool const 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 << " "
897 << "num peers " << (int)network_.overlay().getNumPeers()
898 << std::endl;
899 for (auto& [k, v] : peers)
900 std::cout << k << ":" << (int)std::get<reduce_relay::PeerState>(v)
901 << " ";
903 }
904
908 PublicKey const& validator,
909 PeerWPtr const& peerPtr,
911 {
912 protocol::TMSquelch squelch;
913 bool res = duration ? true : false;
914 squelch.set_squelch(res);
915 squelch.set_validatorpubkey(validator.data(), validator.size());
916 if (res)
917 squelch.set_squelchduration(*duration);
918 auto sp = peerPtr.lock();
919 assert(sp);
920 std::dynamic_pointer_cast<PeerSim>(sp)->send(squelch);
921 return sp->id();
922 }
923
924 enum State { On, Off, WaitReset };
926 // Link down or Peer disconnect event
927 // TBD - add new peer event
928 // TBD - add overlapping type of events at any
929 // time in any quantity
930 struct Event
931 {
935 bool isSelected_ = false;
940 bool handled_ = false;
941 };
942
946 void
948 {
950 {LinkDown, {}}, {PeerDisconnected, {}}};
952
953 network_.reset();
954 network_.propagate([&](Link& link, MessageSPtr m) {
955 auto& validator = link.validator();
956 auto now = ManualClock::now();
957
958 bool squelched = false;
960
961 link.send(
962 m,
963 [&](PublicKey const& key,
964 PeerWPtr const& peerPtr,
966 assert(key == validator);
967 auto p = sendSquelch(key, peerPtr, duration);
968 squelched = true;
969 str << p << " ";
970 });
971
972 if (squelched)
973 {
974 auto selected = network_.overlay().getSelected(validator);
975 str << " selected: ";
976 for (auto s : selected)
977 str << s << " ";
978 if (log)
980 << (double)reduce_relay::epoch<milliseconds>(now)
981 .count() /
982 1000.
983 << " random, squelched, validator: " << validator.id()
984 << " peers: " << str.str() << std::endl;
985 auto countingState =
987 BEAST_EXPECT(
988 countingState == false &&
989 selected.size() == reduce_relay::MAX_SELECTED_PEERS);
990 }
991
992 // Trigger Link Down or Peer Disconnect event
993 // Only one Link Down at a time
994 if (events[EventType::LinkDown].state_ == State::Off)
995 {
996 auto update = [&](EventType event) {
997 events[event].cnt_++;
998 events[event].validator_ = validator.id();
999 events[event].key_ = validator;
1000 events[event].peer_ = link.peerId();
1001 events[event].state_ = State::On;
1002 events[event].time_ = now;
1003 if (event == EventType::LinkDown)
1004 {
1006 validator.id(), link.peerId(), false);
1007 events[event].isSelected_ =
1009 validator, link.peerId());
1010 }
1011 else
1012 events[event].isSelected_ =
1013 network_.isSelected(link.peerId());
1014 };
1015 auto r = rand_int(0, 1000);
1016 if (r == (int)EventType::LinkDown ||
1018 {
1019 update(static_cast<EventType>(r));
1020 }
1021 }
1022
1023 if (events[EventType::PeerDisconnected].state_ == State::On)
1024 {
1025 auto& event = events[EventType::PeerDisconnected];
1026 bool allCounting = network_.allCounting(event.peer_);
1028 event.peer_,
1029 [&](PublicKey const& v, PeerWPtr const& peerPtr) {
1030 if (event.isSelected_)
1031 sendSquelch(v, peerPtr, {});
1032 event.handled_ = true;
1033 });
1034 // Should only be unsquelched if the peer is in Selected state
1035 // If in Selected state it's possible unsquelching didn't
1036 // take place because there is no peers in Squelched state in
1037 // any of the slots where the peer is in Selected state
1038 // (allCounting is true)
1039 bool handled =
1040 (event.isSelected_ == false && !event.handled_) ||
1041 (event.isSelected_ == true &&
1042 (event.handled_ || allCounting));
1043 BEAST_EXPECT(handled);
1044 event.state_ = State::Off;
1045 event.isSelected_ = false;
1046 event.handledCnt_ += handled;
1047 event.handled_ = false;
1048 network_.onDisconnectPeer(event.peer_);
1049 }
1050
1051 auto& event = events[EventType::LinkDown];
1052 // Check every sec for idled peers. Idled peers are
1053 // created by Link Down event.
1054 if (now - lastCheck > milliseconds(1000))
1055 {
1056 lastCheck = now;
1057 // Check if Link Down event must be handled by
1058 // deleteIdlePeer(): 1) the peer is in Selected state;
1059 // 2) the peer has not received any messages for IDLED time;
1060 // 3) there are peers in Squelched state in the slot.
1061 // 4) peer is in Slot's peers_ (if not then it is deleted
1062 // by Slots::deleteIdlePeers())
1063 bool mustHandle = false;
1064 if (event.state_ == State::On && BEAST_EXPECT(event.key_))
1065 {
1066 event.isSelected_ =
1067 network_.overlay().isSelected(*event.key_, event.peer_);
1068 auto peers = network_.overlay().getPeers(*event.key_);
1069 auto d = reduce_relay::epoch<milliseconds>(now).count() -
1070 std::get<3>(peers[event.peer_]);
1071 mustHandle = event.isSelected_ &&
1075 0 &&
1076 peers.find(event.peer_) != peers.end();
1077 }
1079 [&](PublicKey const& v, PeerWPtr const& ptr) {
1080 event.handled_ = true;
1081 if (mustHandle && v == event.key_)
1082 {
1083 event.state_ = State::WaitReset;
1084 sendSquelch(validator, ptr, {});
1085 }
1086 });
1087 bool handled =
1088 (event.handled_ && event.state_ == State::WaitReset) ||
1089 (!event.handled_ && !mustHandle);
1090 BEAST_EXPECT(handled);
1091 }
1092 if (event.state_ == State::WaitReset ||
1093 (event.state_ == State::On &&
1094 (now - event.time_ > (reduce_relay::IDLED + seconds(2)))))
1095 {
1096 bool handled =
1097 event.state_ == State::WaitReset || !event.handled_;
1098 BEAST_EXPECT(handled);
1099 event.state_ = State::Off;
1100 event.isSelected_ = false;
1101 event.handledCnt_ += handled;
1102 event.handled_ = false;
1103 network_.enableLink(event.validator_, event.peer_, true);
1104 }
1105 });
1106
1107 auto& down = events[EventType::LinkDown];
1108 auto& disconnected = events[EventType::PeerDisconnected];
1109 // It's possible the last Down Link event is not handled
1110 BEAST_EXPECT(down.handledCnt_ >= down.cnt_ - 1);
1111 // All Peer Disconnect events must be handled
1112 BEAST_EXPECT(disconnected.cnt_ == disconnected.handledCnt_);
1113 if (log)
1114 std::cout << "link down count: " << down.cnt_ << "/"
1115 << down.handledCnt_
1116 << " peer disconnect count: " << disconnected.cnt_ << "/"
1117 << disconnected.handledCnt_;
1118 }
1119
1120 bool
1121 checkCounting(PublicKey const& validator, bool isCountingState)
1122 {
1123 auto countingState = network_.overlay().isCountingState(validator);
1124 BEAST_EXPECT(countingState == isCountingState);
1125 return countingState == isCountingState;
1126 }
1127
1128 void
1129 doTest(std::string const& msg, bool log, std::function<void(bool)> f)
1130 {
1131 testcase(msg);
1132 f(log);
1133 }
1134
1140 void
1142 {
1143 doTest("Initial Round", log, [this](bool log) {
1144 BEAST_EXPECT(propagateAndSquelch(log));
1145 });
1146 }
1147
1151 void
1153 {
1154 doTest("Peer Unsquelched Too Soon", log, [this](bool log) {
1155 BEAST_EXPECT(propagateNoSquelch(log, 1, false, false, false));
1156 });
1157 }
1158
1162 void
1164 {
1165 ManualClock::advance(seconds(601));
1166 doTest("Peer Unsquelched", log, [this](bool log) {
1167 BEAST_EXPECT(propagateNoSquelch(log, 2, true, true, false));
1168 });
1169 }
1170
1172 bool
1173 propagateAndSquelch(bool log, bool purge = true, bool resetClock = true)
1174 {
1175 int n = 0;
1176 network_.propagate(
1177 [&](Link& link, MessageSPtr message) {
1178 std::uint16_t squelched = 0;
1179 link.send(
1180 message,
1181 [&](PublicKey const& key,
1182 PeerWPtr const& peerPtr,
1184 squelched++;
1185 sendSquelch(key, peerPtr, duration);
1186 });
1187 if (squelched)
1188 {
1189 BEAST_EXPECT(
1190 squelched ==
1192 n++;
1193 }
1194 },
1195 1,
1197 purge,
1198 resetClock);
1199 auto selected = network_.overlay().getSelected(network_.validator(0));
1200 BEAST_EXPECT(selected.size() == reduce_relay::MAX_SELECTED_PEERS);
1201 BEAST_EXPECT(n == 1); // only one selection round
1202 auto res = checkCounting(network_.validator(0), false);
1203 BEAST_EXPECT(res);
1204 return n == 1 && res;
1205 }
1206
1208 bool
1210 bool log,
1211 std::uint16_t nMessages,
1212 bool countingState,
1213 bool purge = true,
1214 bool resetClock = true)
1215 {
1216 bool squelched = false;
1217 network_.propagate(
1218 [&](Link& link, MessageSPtr message) {
1219 link.send(
1220 message,
1221 [&](PublicKey const& key,
1222 PeerWPtr const& peerPtr,
1224 squelched = true;
1225 BEAST_EXPECT(false);
1226 });
1227 },
1228 1,
1229 nMessages,
1230 purge,
1231 resetClock);
1232 auto res = checkCounting(network_.validator(0), countingState);
1233 return !squelched && res;
1234 }
1235
1239 void
1240 testNewPeer(bool log)
1241 {
1242 doTest("New Peer", log, [this](bool log) {
1243 BEAST_EXPECT(propagateAndSquelch(log, true, false));
1244 network_.addPeer();
1245 BEAST_EXPECT(propagateNoSquelch(log, 1, true, false, false));
1246 });
1247 }
1248
1251 void
1253 {
1254 doTest("Selected Peer Disconnects", log, [this](bool log) {
1255 ManualClock::advance(seconds(601));
1256 BEAST_EXPECT(propagateAndSquelch(log, true, false));
1257 auto id = network_.overlay().getSelectedPeer(network_.validator(0));
1258 std::uint16_t unsquelched = 0;
1259 network_.overlay().deletePeer(
1260 id, [&](PublicKey const& key, PeerWPtr const& peer) {
1261 unsquelched++;
1262 });
1263 BEAST_EXPECT(
1265 BEAST_EXPECT(checkCounting(network_.validator(0), true));
1266 });
1267 }
1268
1271 void
1273 {
1274 doTest("Selected Peer Stops Relaying", log, [this](bool log) {
1275 ManualClock::advance(seconds(601));
1276 BEAST_EXPECT(propagateAndSquelch(log, true, false));
1277 ManualClock::advance(reduce_relay::IDLED + seconds(1));
1278 std::uint16_t unsquelched = 0;
1279 network_.overlay().deleteIdlePeers(
1280 [&](PublicKey const& key, PeerWPtr const& peer) {
1281 unsquelched++;
1282 });
1283 auto peers = network_.overlay().getPeers(network_.validator(0));
1284 BEAST_EXPECT(
1286 BEAST_EXPECT(checkCounting(network_.validator(0), true));
1287 });
1288 }
1289
1292 void
1294 {
1295 doTest("Squelched Peer Disconnects", log, [this](bool log) {
1296 ManualClock::advance(seconds(601));
1297 BEAST_EXPECT(propagateAndSquelch(log, true, false));
1298 auto peers = network_.overlay().getPeers(network_.validator(0));
1299 auto it = std::find_if(peers.begin(), peers.end(), [&](auto it) {
1300 return std::get<reduce_relay::PeerState>(it.second) ==
1301 reduce_relay::PeerState::Squelched;
1302 });
1303 assert(it != peers.end());
1304 std::uint16_t unsquelched = 0;
1305 network_.overlay().deletePeer(
1306 it->first, [&](PublicKey const& key, PeerWPtr const& peer) {
1307 unsquelched++;
1308 });
1309 BEAST_EXPECT(unsquelched == 0);
1310 BEAST_EXPECT(checkCounting(network_.validator(0), false));
1311 });
1312 }
1313
1314 void
1315 testConfig(bool log)
1316 {
1317 doTest("Config Test", log, [&](bool log) {
1318 Config c;
1319
1320 std::string toLoad(R"rippleConfig(
1321[reduce_relay]
1322vp_enable=1
1323vp_squelch=1
1324)rippleConfig");
1325
1326 c.loadFromString(toLoad);
1327 BEAST_EXPECT(c.VP_REDUCE_RELAY_ENABLE == true);
1328 BEAST_EXPECT(c.VP_REDUCE_RELAY_SQUELCH == true);
1329
1330 Config c1;
1331
1332 toLoad = (R"rippleConfig(
1333[reduce_relay]
1334vp_enable=0
1335vp_squelch=0
1336)rippleConfig");
1337
1338 c1.loadFromString(toLoad);
1339 BEAST_EXPECT(c1.VP_REDUCE_RELAY_ENABLE == false);
1340 BEAST_EXPECT(c1.VP_REDUCE_RELAY_SQUELCH == false);
1341
1342 Config c2;
1343
1344 toLoad = R"rippleConfig(
1345[reduce_relay]
1346vp_enabled=1
1347vp_squelched=1
1348)rippleConfig";
1349
1350 c2.loadFromString(toLoad);
1351 BEAST_EXPECT(c2.VP_REDUCE_RELAY_ENABLE == false);
1352 BEAST_EXPECT(c2.VP_REDUCE_RELAY_SQUELCH == false);
1353 });
1354 }
1355
1356 void
1358 {
1359 doTest("Duplicate Message", log, [&](bool log) {
1360 network_.reset();
1361 // update message count for the same peer/validator
1362 std::int16_t nMessages = 5;
1363 for (int i = 0; i < nMessages; i++)
1364 {
1365 uint256 key(i);
1366 network_.overlay().updateSlotAndSquelch(
1367 key,
1368 network_.validator(0),
1369 0,
1370 [&](PublicKey const&, PeerWPtr, std::uint32_t) {});
1371 }
1372 auto peers = network_.overlay().getPeers(network_.validator(0));
1373 // first message changes Slot state to Counting and is not counted,
1374 // hence '-1'.
1375 BEAST_EXPECT(std::get<1>(peers[0]) == (nMessages - 1));
1376 // add duplicate
1377 uint256 key(nMessages - 1);
1378 network_.overlay().updateSlotAndSquelch(
1379 key,
1380 network_.validator(0),
1381 0,
1382 [&](PublicKey const&, PeerWPtr, std::uint32_t) {});
1383 // confirm the same number of messages
1384 peers = network_.overlay().getPeers(network_.validator(0));
1385 BEAST_EXPECT(std::get<1>(peers[0]) == (nMessages - 1));
1386 // advance the clock
1387 ManualClock::advance(reduce_relay::IDLED + seconds(1));
1388 network_.overlay().updateSlotAndSquelch(
1389 key,
1390 network_.validator(0),
1391 0,
1392 [&](PublicKey const&, PeerWPtr, std::uint32_t) {});
1393 peers = network_.overlay().getPeers(network_.validator(0));
1394 // confirm message number increased
1395 BEAST_EXPECT(std::get<1>(peers[0]) == nMessages);
1396 });
1397 }
1398
1400 {
1401 Handler() : maxDuration_(0)
1402 {
1403 }
1404 void
1406 const override
1407 {
1408 if (duration > maxDuration_)
1409 maxDuration_ = duration;
1410 }
1411 void
1412 unsquelch(PublicKey const&, Peer::id_t) const override
1413 {
1414 }
1415 mutable int maxDuration_;
1416 };
1417
1418 void
1420 {
1421 doTest("Random Squelch", l, [&](bool l) {
1423 Handler handler;
1424
1425 auto run = [&](int npeers) {
1426 handler.maxDuration_ = 0;
1428 env_.app().logs(), handler);
1429 // 1st message from a new peer switches the slot
1430 // to counting state and resets the counts of all peers +
1431 // MAX_MESSAGE_THRESHOLD + 1 messages to reach the threshold
1432 // and switch the slot's state to peer selection.
1433 for (int m = 1; m <= reduce_relay::MAX_MESSAGE_THRESHOLD + 2;
1434 m++)
1435 {
1436 for (int peer = 0; peer < npeers; peer++)
1437 {
1438 // make unique message hash to make the
1439 // slot's internal hash router accept the message
1440 std::uint64_t mid = m * 1000 + peer;
1441 uint256 const message{mid};
1443 message,
1444 validator,
1445 peer,
1446 protocol::MessageType::mtVALIDATION);
1447 }
1448 }
1449 // make Slot's internal hash router expire all messages
1450 ManualClock::advance(hours(1));
1451 };
1452
1453 using namespace reduce_relay;
1454 // expect max duration less than MAX_UNSQUELCH_EXPIRE_DEFAULT with
1455 // less than or equal to 60 peers
1456 run(20);
1457 BEAST_EXPECT(
1458 handler.maxDuration_ >= MIN_UNSQUELCH_EXPIRE.count() &&
1459 handler.maxDuration_ <= MAX_UNSQUELCH_EXPIRE_DEFAULT.count());
1460 run(60);
1461 BEAST_EXPECT(
1462 handler.maxDuration_ >= MIN_UNSQUELCH_EXPIRE.count() &&
1463 handler.maxDuration_ <= MAX_UNSQUELCH_EXPIRE_DEFAULT.count());
1464 // expect max duration greater than MIN_UNSQUELCH_EXPIRE and less
1465 // than MAX_UNSQUELCH_EXPIRE_PEERS with peers greater than 60
1466 // and less than 360
1467 run(350);
1468 // can't make this condition stronger. squelch
1469 // duration is probabilistic and max condition may still fail.
1470 // log when the value is low
1471 BEAST_EXPECT(
1472 handler.maxDuration_ >= MIN_UNSQUELCH_EXPIRE.count() &&
1473 handler.maxDuration_ <= MAX_UNSQUELCH_EXPIRE_PEERS.count());
1474 using namespace beast::unit_test::detail;
1475 if (handler.maxDuration_ <= MAX_UNSQUELCH_EXPIRE_DEFAULT.count())
1476 log << make_reason(
1477 "warning: squelch duration is low",
1478 __FILE__,
1479 __LINE__)
1480 << std::endl
1481 << std::flush;
1482 // more than 400 is still less than MAX_UNSQUELCH_EXPIRE_PEERS
1483 run(400);
1484 BEAST_EXPECT(
1485 handler.maxDuration_ >= MIN_UNSQUELCH_EXPIRE.count() &&
1486 handler.maxDuration_ <= MAX_UNSQUELCH_EXPIRE_PEERS.count());
1487 if (handler.maxDuration_ <= MAX_UNSQUELCH_EXPIRE_DEFAULT.count())
1488 log << make_reason(
1489 "warning: squelch duration is low",
1490 __FILE__,
1491 __LINE__)
1492 << std::endl
1493 << std::flush;
1494 });
1495 }
1496
1497 void
1499 {
1500 doTest("Handshake", log, [&](bool log) {
1501 auto setEnv = [&](bool enable) {
1502 Config c;
1504 str << "[reduce_relay]\n"
1505 << "vp_enable=" << enable << "\n"
1506 << "vp_squelch=" << enable << "\n"
1507 << "[compression]\n"
1508 << "1\n";
1509 c.loadFromString(str.str());
1510 env_.app().config().VP_REDUCE_RELAY_ENABLE =
1512 env_.app().config().VP_REDUCE_RELAY_SQUELCH =
1514 env_.app().config().COMPRESSION = c.COMPRESSION;
1515 };
1516 auto handshake = [&](int outboundEnable, int inboundEnable) {
1517 beast::IP::Address addr =
1518 boost::asio::ip::address::from_string("172.1.1.100");
1519
1520 setEnv(outboundEnable);
1521 auto request = ripple::makeRequest(
1522 true,
1523 env_.app().config().COMPRESSION,
1524 false,
1525 env_.app().config().TX_REDUCE_RELAY_ENABLE,
1526 env_.app().config().VP_REDUCE_RELAY_ENABLE);
1527 http_request_type http_request;
1528 http_request.version(request.version());
1529 http_request.base() = request.base();
1530 // feature enabled on the peer's connection only if both sides
1531 // are enabled
1532 auto const peerEnabled = inboundEnable && outboundEnable;
1533 // inbound is enabled if the request's header has the feature
1534 // enabled and the peer's configuration is enabled
1535 auto const inboundEnabled = peerFeatureEnabled(
1536 http_request, FEATURE_VPRR, inboundEnable);
1537 BEAST_EXPECT(!(peerEnabled ^ inboundEnabled));
1538
1539 setEnv(inboundEnable);
1540 auto http_resp = ripple::makeResponse(
1541 true,
1542 http_request,
1543 addr,
1544 addr,
1545 uint256{1},
1546 1,
1547 {1, 0},
1548 env_.app());
1549 // outbound is enabled if the response's header has the feature
1550 // enabled and the peer's configuration is enabled
1551 auto const outboundEnabled =
1552 peerFeatureEnabled(http_resp, FEATURE_VPRR, outboundEnable);
1553 BEAST_EXPECT(!(peerEnabled ^ outboundEnabled));
1554 };
1555 handshake(1, 1);
1556 handshake(1, 0);
1557 handshake(0, 1);
1558 handshake(0, 0);
1559 });
1560 }
1561
1564
1565public:
1566 reduce_relay_test() : env_(*this), network_(env_.app())
1567 {
1568 }
1569
1570 void
1571 run() override
1572 {
1573 bool log = false;
1574 testConfig(log);
1575 testInitialRound(log);
1576 testPeerUnsquelchedTooSoon(log);
1577 testPeerUnsquelched(log);
1578 testNewPeer(log);
1579 testSquelchedPeerDisconnects(log);
1580 testSelectedPeerDisconnects(log);
1581 testSelectedPeerStopsRelaying(log);
1582 testInternalHashRouter(log);
1583 testRandomSquelch(log);
1584 testHandshake(log);
1585 }
1586};
1587
1589{
1590 void
1591 testRandom(bool log)
1592 {
1593 doTest("Random Test", log, [&](bool log) { random(log); });
1594 }
1595
1596 void
1597 run() override
1598 {
1599 bool log = false;
1600 testRandom(log);
1601 }
1602};
1603
1604BEAST_DEFINE_TESTSUITE(reduce_relay, ripple_data, ripple);
1605BEAST_DEFINE_TESTSUITE_MANUAL(reduce_relay_simulate, ripple_data, ripple);
1606
1607} // namespace test
1608
1609} // namespace ripple
T back_inserter(T... args)
T begin(T... args)
Represents a JSON value.
Definition: json_value.h:150
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
void removeTxQueue(uint256 const &) override
Remove hash from the transactions' hashes queue.
std::optional< std::size_t > publisherListSequence(PublicKey const &) const override
void send(protocol::TMSquelch const &squelch)
bool txReduceRelayEnabled() const override
void addTxQueue(uint256 const &) 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
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:121
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 testInitialRound(bool log)
Initial counting round: three peers receive message "faster" then others.
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.
void printPeers(std::string const &msg, std::uint16_t validator=0)
void doTest(std::string const &msg, bool log, std::function< void(bool)> f)
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)