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