rippled
Loading...
Searching...
No Matches
overlay/Slot.h
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2012, 2013 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#ifndef RIPPLE_OVERLAY_SLOT_H_INCLUDED
21#define RIPPLE_OVERLAY_SLOT_H_INCLUDED
22
23#include <xrpld/overlay/Peer.h>
24#include <xrpld/overlay/ReduceRelayCommon.h>
25#include <xrpld/overlay/Squelch.h>
26#include <xrpl/basics/Log.h>
27#include <xrpl/basics/chrono.h>
28#include <xrpl/beast/container/aged_unordered_map.h>
29#include <xrpl/beast/utility/Journal.h>
30#include <xrpl/protocol/PublicKey.h>
31#include <xrpl/protocol/messages.h>
32
33#include <algorithm>
34#include <memory>
35#include <optional>
36#include <set>
37#include <tuple>
38#include <unordered_map>
39#include <unordered_set>
40
41namespace ripple {
42
43namespace reduce_relay {
44
45template <typename clock_type>
46class Slots;
47
49enum class PeerState : uint8_t {
50 Counting, // counting messages
51 Selected, // selected to relay, counting if Slot in Counting
52 Squelched, // squelched, doesn't relay
53};
55enum class SlotState : uint8_t {
56 Counting, // counting messages
57 Selected, // peers selected, stop counting
58};
59
60template <typename Unit, typename TP>
61Unit
62epoch(TP const& t)
63{
64 return std::chrono::duration_cast<Unit>(t.time_since_epoch());
65}
66
72{
73public:
75 {
76 }
82 virtual void
83 squelch(PublicKey const& validator, Peer::id_t id, std::uint32_t duration)
84 const = 0;
89 virtual void
90 unsquelch(PublicKey const& validator, Peer::id_t id) const = 0;
91};
92
103template <typename clock_type>
104class Slot final
105{
106private:
107 friend class Slots<clock_type>;
109 using time_point = typename clock_type::time_point;
110
115 Slot(SquelchHandler const& handler, beast::Journal journal)
117 , lastSelected_(clock_type::now())
119 , handler_(handler)
120 , journal_(journal)
121 {
122 }
123
142 void
143 update(PublicKey const& validator, id_t id, protocol::MessageType type);
144
155 void
156 deletePeer(PublicKey const& validator, id_t id, bool erase);
157
159 const time_point&
161 {
162 return lastSelected_;
163 }
164
167 inState(PeerState state) const;
168
171 notInState(PeerState state) const;
172
175 getState() const
176 {
177 return state_;
178 }
179
182 getSelected() const;
183
187 std::
188 unordered_map<id_t, std::tuple<PeerState, uint16_t, uint32_t, uint32_t>>
189 getPeers() const;
190
197 void
198 deleteIdlePeer(PublicKey const& validator);
199
207
208private:
210 void
212
214 void
216
218 struct PeerInfo
219 {
220 PeerState state; // peer's state
221 std::size_t count; // message count
222 time_point expire; // squelch expiration time
223 time_point lastMessage; // time last message received
224 };
226 // pool of peers considered as the source of messages
227 // from validator - peers that reached MIN_MESSAGE_THRESHOLD
229 // number of peers that reached MAX_MESSAGE_THRESHOLD
231 // last time peers were selected, used to age the slot
232 typename clock_type::time_point lastSelected_;
233 SlotState state_; // slot's state
234 SquelchHandler const& handler_; // squelch/unsquelch handler
235 beast::Journal const journal_; // logging
236};
237
238template <typename clock_type>
239void
241{
242 using namespace std::chrono;
243 auto now = clock_type::now();
244 for (auto it = peers_.begin(); it != peers_.end();)
245 {
246 auto& peer = it->second;
247 auto id = it->first;
248 ++it;
249 if (now - peer.lastMessage > IDLED)
250 {
251 JLOG(journal_.trace())
252 << "deleteIdlePeer: " << Slice(validator) << " " << id
253 << " idled "
254 << duration_cast<seconds>(now - peer.lastMessage).count()
255 << " selected " << (peer.state == PeerState::Selected);
256 deletePeer(validator, id, false);
257 }
258 }
259}
260
261template <typename clock_type>
262void
264 PublicKey const& validator,
265 id_t id,
266 protocol::MessageType type)
267{
268 using namespace std::chrono;
269 auto now = clock_type::now();
270 auto it = peers_.find(id);
271 // First message from this peer
272 if (it == peers_.end())
273 {
274 JLOG(journal_.trace())
275 << "update: adding peer " << Slice(validator) << " " << id;
276 peers_.emplace(
277 std::make_pair(id, PeerInfo{PeerState::Counting, 0, now, now}));
278 initCounting();
279 return;
280 }
281 // Message from a peer with expired squelch
282 if (it->second.state == PeerState::Squelched && now > it->second.expire)
283 {
284 JLOG(journal_.trace())
285 << "update: squelch expired " << Slice(validator) << " " << id;
286 it->second.state = PeerState::Counting;
287 it->second.lastMessage = now;
288 initCounting();
289 return;
290 }
291
292 auto& peer = it->second;
293
294 JLOG(journal_.trace())
295 << "update: existing peer " << Slice(validator) << " " << id
296 << " slot state " << static_cast<int>(state_) << " peer state "
297 << static_cast<int>(peer.state) << " count " << peer.count << " last "
298 << duration_cast<milliseconds>(now - peer.lastMessage).count()
299 << " pool " << considered_.size() << " threshold " << reachedThreshold_
300 << " " << (type == protocol::mtVALIDATION ? "validation" : "proposal");
301
302 peer.lastMessage = now;
303
304 if (state_ != SlotState::Counting || peer.state == PeerState::Squelched)
305 return;
306
307 if (++peer.count > MIN_MESSAGE_THRESHOLD)
308 considered_.insert(id);
309 if (peer.count == (MAX_MESSAGE_THRESHOLD + 1))
310 ++reachedThreshold_;
311
312 if (now - lastSelected_ > 2 * MAX_UNSQUELCH_EXPIRE_DEFAULT)
313 {
314 JLOG(journal_.trace())
315 << "update: resetting due to inactivity " << Slice(validator) << " "
316 << id << " " << duration_cast<seconds>(now - lastSelected_).count();
317 initCounting();
318 return;
319 }
320
321 if (reachedThreshold_ == MAX_SELECTED_PEERS)
322 {
323 // Randomly select MAX_SELECTED_PEERS peers from considered.
324 // Exclude peers that have been idling > IDLED -
325 // it's possible that deleteIdlePeer() has not been called yet.
326 // If number of remaining peers != MAX_SELECTED_PEERS
327 // then reset the Counting state and let deleteIdlePeer() handle
328 // idled peers.
330 auto const consideredPoolSize = considered_.size();
331 while (selected.size() != MAX_SELECTED_PEERS && considered_.size() != 0)
332 {
333 auto i =
334 considered_.size() == 1 ? 0 : rand_int(considered_.size() - 1);
335 auto it = std::next(considered_.begin(), i);
336 auto id = *it;
337 considered_.erase(it);
338 auto const& itpeers = peers_.find(id);
339 if (itpeers == peers_.end())
340 {
341 JLOG(journal_.error()) << "update: peer not found "
342 << Slice(validator) << " " << id;
343 continue;
344 }
345 if (now - itpeers->second.lastMessage < IDLED)
346 selected.insert(id);
347 }
348
349 if (selected.size() != MAX_SELECTED_PEERS)
350 {
351 JLOG(journal_.trace())
352 << "update: selection failed " << Slice(validator) << " " << id;
353 initCounting();
354 return;
355 }
356
357 lastSelected_ = now;
358
359 auto s = selected.begin();
360 JLOG(journal_.trace())
361 << "update: " << Slice(validator) << " " << id << " pool size "
362 << consideredPoolSize << " selected " << *s << " "
363 << *std::next(s, 1) << " " << *std::next(s, 2);
364
365 XRPL_ASSERT(
366 peers_.size() >= MAX_SELECTED_PEERS,
367 "ripple::reduce_relay::Slot::update : minimum peers");
368
369 // squelch peers which are not selected and
370 // not already squelched
372 for (auto& [k, v] : peers_)
373 {
374 v.count = 0;
375
376 if (selected.find(k) != selected.end())
377 v.state = PeerState::Selected;
378 else if (v.state != PeerState::Squelched)
379 {
380 if (journal_.trace())
381 str << k << " ";
382 v.state = PeerState::Squelched;
384 getSquelchDuration(peers_.size() - MAX_SELECTED_PEERS);
385 v.expire = now + duration;
386 handler_.squelch(validator, k, duration.count());
387 }
388 }
389 JLOG(journal_.trace()) << "update: squelching " << Slice(validator)
390 << " " << id << " " << str.str();
391 considered_.clear();
392 reachedThreshold_ = 0;
393 state_ = SlotState::Selected;
394 }
395}
396
397template <typename clock_type>
400{
401 using namespace std::chrono;
402 auto m = std::max(
405 {
407 JLOG(journal_.warn())
408 << "getSquelchDuration: unexpected squelch duration " << npeers;
409 }
410 return seconds{ripple::rand_int(MIN_UNSQUELCH_EXPIRE / 1s, m / 1s)};
411}
412
413template <typename clock_type>
414void
416{
417 auto it = peers_.find(id);
418 if (it != peers_.end())
419 {
420 JLOG(journal_.trace())
421 << "deletePeer: " << Slice(validator) << " " << id << " selected "
422 << (it->second.state == PeerState::Selected) << " considered "
423 << (considered_.find(id) != considered_.end()) << " erase "
424 << erase;
425 auto now = clock_type::now();
426 if (it->second.state == PeerState::Selected)
427 {
428 for (auto& [k, v] : peers_)
429 {
430 if (v.state == PeerState::Squelched)
431 handler_.unsquelch(validator, k);
432 v.state = PeerState::Counting;
433 v.count = 0;
434 v.expire = now;
435 }
436
437 considered_.clear();
438 reachedThreshold_ = 0;
439 state_ = SlotState::Counting;
440 }
441 else if (considered_.find(id) != considered_.end())
442 {
443 if (it->second.count > MAX_MESSAGE_THRESHOLD)
444 --reachedThreshold_;
445 considered_.erase(id);
446 }
447
448 it->second.lastMessage = now;
449 it->second.count = 0;
450
451 if (erase)
452 peers_.erase(it);
453 }
454}
455
456template <typename clock_type>
457void
459{
460 for (auto& [_, peer] : peers_)
461 {
462 (void)_;
463 peer.count = 0;
464 }
465}
466
467template <typename clock_type>
468void
470{
471 state_ = SlotState::Counting;
472 considered_.clear();
473 reachedThreshold_ = 0;
474 resetCounts();
475}
476
477template <typename clock_type>
480{
481 return std::count_if(peers_.begin(), peers_.end(), [&](auto const& it) {
482 return (it.second.state == state);
483 });
484}
485
486template <typename clock_type>
489{
490 return std::count_if(peers_.begin(), peers_.end(), [&](auto const& it) {
491 return (it.second.state != state);
492 });
493}
494
495template <typename clock_type>
498{
500 for (auto const& [id, info] : peers_)
501 if (info.state == PeerState::Selected)
502 r.insert(id);
503 return r;
504}
505
506template <typename clock_type>
508 typename Peer::id_t,
511{
512 using namespace std::chrono;
513 auto r = std::unordered_map<
514 id_t,
516
517 for (auto const& [id, info] : peers_)
518 r.emplace(std::make_pair(
519 id,
520 std::move(std::make_tuple(
521 info.state,
522 info.count,
523 epoch<milliseconds>(info.expire).count(),
524 epoch<milliseconds>(info.lastMessage).count()))));
525
526 return r;
527}
528
533template <typename clock_type>
534class Slots final
535{
536 using time_point = typename clock_type::time_point;
537 using id_t = typename Peer::id_t;
539 uint256,
541 clock_type,
543
544public:
549 Slots(Logs& logs, SquelchHandler const& handler)
550 : handler_(handler), logs_(logs), journal_(logs.journal("Slots"))
551 {
552 }
553 ~Slots() = default;
560 void
562 uint256 const& key,
563 PublicKey const& validator,
564 id_t id,
565 protocol::MessageType type);
566
570 void
572
575 inState(PublicKey const& validator, PeerState state) const
576 {
577 auto const& it = slots_.find(validator);
578 if (it != slots_.end())
579 return it->second.inState(state);
580 return {};
581 }
582
585 notInState(PublicKey const& validator, PeerState state) const
586 {
587 auto const& it = slots_.find(validator);
588 if (it != slots_.end())
589 return it->second.notInState(state);
590 return {};
591 }
592
594 bool
595 inState(PublicKey const& validator, SlotState state) const
596 {
597 auto const& it = slots_.find(validator);
598 if (it != slots_.end())
599 return it->second.state_ == state;
600 return false;
601 }
602
605 getSelected(PublicKey const& validator)
606 {
607 auto const& it = slots_.find(validator);
608 if (it != slots_.end())
609 return it->second.getSelected();
610 return {};
611 }
612
617 typename Peer::id_t,
619 getPeers(PublicKey const& validator)
620 {
621 auto const& it = slots_.find(validator);
622 if (it != slots_.end())
623 return it->second.getPeers();
624 return {};
625 }
626
629 getState(PublicKey const& validator)
630 {
631 auto const& it = slots_.find(validator);
632 if (it != slots_.end())
633 return it->second.getState();
634 return {};
635 }
636
643 void
645
646private:
650 bool
651 addPeerMessage(uint256 const& key, id_t id);
652
654 SquelchHandler const& handler_; // squelch/unsquelch handler
657 // Maintain aged container of message/peers. This is required
658 // to discard duplicate message from the same peer. A message
659 // is aged after IDLED seconds. A message received IDLED seconds
660 // after it was relayed is ignored by PeerImp.
662 beast::get_abstract_clock<clock_type>()};
663};
664
665template <typename clock_type>
666bool
668{
669 beast::expire(peersWithMessage_, reduce_relay::IDLED);
670
671 if (key.isNonZero())
672 {
673 auto it = peersWithMessage_.find(key);
674 if (it == peersWithMessage_.end())
675 {
676 JLOG(journal_.trace())
677 << "addPeerMessage: new " << to_string(key) << " " << id;
678 peersWithMessage_.emplace(key, std::unordered_set<id_t>{id});
679 return true;
680 }
681
682 if (it->second.find(id) != it->second.end())
683 {
684 JLOG(journal_.trace()) << "addPeerMessage: duplicate message "
685 << to_string(key) << " " << id;
686 return false;
687 }
688
689 JLOG(journal_.trace())
690 << "addPeerMessage: added " << to_string(key) << " " << id;
691
692 it->second.insert(id);
693 }
694
695 return true;
696}
697
698template <typename clock_type>
699void
701 uint256 const& key,
702 PublicKey const& validator,
703 id_t id,
704 protocol::MessageType type)
705{
706 if (!addPeerMessage(key, id))
707 return;
708
709 auto it = slots_.find(validator);
710 if (it == slots_.end())
711 {
712 JLOG(journal_.trace())
713 << "updateSlotAndSquelch: new slot " << Slice(validator);
714 auto it = slots_
715 .emplace(std::make_pair(
716 validator,
717 Slot<clock_type>(handler_, logs_.journal("Slot"))))
718 .first;
719 it->second.update(validator, id, type);
720 }
721 else
722 it->second.update(validator, id, type);
723}
724
725template <typename clock_type>
726void
728{
729 for (auto& [validator, slot] : slots_)
730 slot.deletePeer(validator, id, erase);
731}
732
733template <typename clock_type>
734void
736{
737 auto now = clock_type::now();
738
739 for (auto it = slots_.begin(); it != slots_.end();)
740 {
741 it->second.deleteIdlePeer(it->first);
742 if (now - it->second.getLastSelected() > MAX_UNSQUELCH_EXPIRE_DEFAULT)
743 {
744 JLOG(journal_.trace())
745 << "deleteIdlePeers: deleting idle slot " << Slice(it->first);
746 it = slots_.erase(it);
747 }
748 else
749 ++it;
750 }
751}
752
753} // namespace reduce_relay
754
755} // namespace ripple
756
757#endif // RIPPLE_OVERLAY_SLOT_H_INCLUDED
T begin(T... args)
A generic endpoint for log messages.
Definition: Journal.h:59
Associative container where each element is also indexed by time.
Manages partitions for logging.
Definition: Log.h:49
std::uint32_t id_t
Uniquely identifies a peer.
A public key.
Definition: PublicKey.h:62
An immutable linear range of bytes.
Definition: Slice.h:45
std::size_t size() const noexcept
Returns the number of bytes in the storage.
Definition: Slice.h:80
bool isNonZero() const
Definition: base_uint.h:544
Seed functor once per construction.
Definition: hardened_hash.h:97
Slot is associated with a specific validator via validator's public key.
Definition: overlay/Slot.h:105
Slot(SquelchHandler const &handler, beast::Journal journal)
Constructor.
Definition: overlay/Slot.h:115
std::unordered_map< id_t, std::tuple< PeerState, uint16_t, uint32_t, uint32_t > > getPeers() const
Get peers info.
Definition: overlay/Slot.h:510
void deletePeer(PublicKey const &validator, id_t id, bool erase)
Handle peer deletion when a peer disconnects.
Definition: overlay/Slot.h:415
std::uint16_t reachedThreshold_
Definition: overlay/Slot.h:230
std::uint16_t notInState(PeerState state) const
Return number of peers not in state.
Definition: overlay/Slot.h:488
typename clock_type::time_point time_point
Definition: overlay/Slot.h:109
SquelchHandler const & handler_
Definition: overlay/Slot.h:234
void initCounting()
Initialize slot to Counting state.
Definition: overlay/Slot.h:469
void update(PublicKey const &validator, id_t id, protocol::MessageType type)
Update peer info.
Definition: overlay/Slot.h:263
clock_type::time_point lastSelected_
Definition: overlay/Slot.h:232
std::set< id_t > getSelected() const
Return selected peers.
Definition: overlay/Slot.h:497
SlotState getState() const
Return Slot's state.
Definition: overlay/Slot.h:175
std::uint16_t inState(PeerState state) const
Return number of peers in state.
Definition: overlay/Slot.h:479
const time_point & getLastSelected() const
Get the time of the last peer selection round.
Definition: overlay/Slot.h:160
void deleteIdlePeer(PublicKey const &validator)
Check if peers stopped relaying messages.
Definition: overlay/Slot.h:240
std::unordered_set< id_t > considered_
Definition: overlay/Slot.h:228
std::chrono::seconds getSquelchDuration(std::size_t npeers)
Get random squelch duration between MIN_UNSQUELCH_EXPIRE and min(max(MAX_UNSQUELCH_EXPIRE_DEFAULT,...
Definition: overlay/Slot.h:399
std::unordered_map< id_t, PeerInfo > peers_
Definition: overlay/Slot.h:225
void resetCounts()
Reset counts of peers in Selected or Counting state.
Definition: overlay/Slot.h:458
beast::Journal const journal_
Definition: overlay/Slot.h:235
Slots is a container for validator's Slot and handles Slot update when a message is received from a v...
Definition: overlay/Slot.h:535
typename clock_type::time_point time_point
Definition: overlay/Slot.h:536
static messages peersWithMessage_
Definition: overlay/Slot.h:661
void deletePeer(id_t id, bool erase)
Called when a peer is deleted.
Definition: overlay/Slot.h:727
void deleteIdlePeers()
Check if peers stopped relaying messages and if slots stopped receiving messages from the validator.
Definition: overlay/Slot.h:735
bool addPeerMessage(uint256 const &key, id_t id)
Add message/peer if have not seen this message from the peer.
Definition: overlay/Slot.h:667
beast::Journal const journal_
Definition: overlay/Slot.h:656
std::optional< std::uint16_t > notInState(PublicKey const &validator, PeerState state) const
Return number of peers not in state.
Definition: overlay/Slot.h:585
std::set< id_t > getSelected(PublicKey const &validator)
Get selected peers.
Definition: overlay/Slot.h:605
hash_map< PublicKey, Slot< clock_type > > slots_
Definition: overlay/Slot.h:653
std::optional< std::uint16_t > inState(PublicKey const &validator, PeerState state) const
Return number of peers in state.
Definition: overlay/Slot.h:575
bool inState(PublicKey const &validator, SlotState state) const
Return true if Slot is in state.
Definition: overlay/Slot.h:595
std::unordered_map< typename Peer::id_t, std::tuple< PeerState, uint16_t, uint32_t, std::uint32_t > > getPeers(PublicKey const &validator)
Get peers info.
Definition: overlay/Slot.h:619
std::optional< SlotState > getState(PublicKey const &validator)
Get Slot's state.
Definition: overlay/Slot.h:629
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:700
Slots(Logs &logs, SquelchHandler const &handler)
Definition: overlay/Slot.h:549
SquelchHandler const & handler_
Definition: overlay/Slot.h:654
typename Peer::id_t id_t
Definition: overlay/Slot.h:537
virtual void unsquelch(PublicKey const &validator, Peer::id_t id) const =0
Unsquelch handler.
virtual void squelch(PublicKey const &validator, Peer::id_t id, std::uint32_t duration) const =0
Squelch handler.
T end(T... args)
T find(T... args)
T insert(T... args)
T make_pair(T... args)
T make_tuple(T... args)
T max(T... args)
std::enable_if< is_aged_container< AgedContainer >::value, std::size_t >::type expire(AgedContainer &c, std::chrono::duration< Rep, Period > const &age)
Expire aged container items past the specified age.
static constexpr auto SQUELCH_PER_PEER
static constexpr uint16_t MAX_SELECTED_PEERS
Unit epoch(TP const &t)
Definition: overlay/Slot.h:62
static constexpr auto MIN_UNSQUELCH_EXPIRE
SlotState
Slot's State.
Definition: overlay/Slot.h:55
static constexpr auto IDLED
PeerState
Peer's State.
Definition: overlay/Slot.h:49
static constexpr uint16_t MAX_MESSAGE_THRESHOLD
static constexpr auto MAX_UNSQUELCH_EXPIRE_DEFAULT
static constexpr uint16_t MIN_MESSAGE_THRESHOLD
static constexpr auto MAX_UNSQUELCH_EXPIRE_PEERS
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()
base_uint< 256 > uint256
Definition: base_uint.h:557
void erase(STObject &st, TypedField< U > const &f)
Remove a field in an STObject.
Definition: STExchange.h:171
std::string to_string(base_uint< Bits, Tag > const &a)
Definition: base_uint.h:629
T next(T... args)
T size(T... args)
T str(T... args)
Data maintained for each peer.
Definition: overlay/Slot.h:219