diff --git a/Builds/VisualStudio2013/RippleD.vcxproj b/Builds/VisualStudio2013/RippleD.vcxproj
index dd23303f96..78ce2ff170 100644
--- a/Builds/VisualStudio2013/RippleD.vcxproj
+++ b/Builds/VisualStudio2013/RippleD.vcxproj
@@ -103,6 +103,12 @@
true
+
+ true
+ true
+ true
+ true
+
true
true
@@ -115,6 +121,12 @@
true
true
+
+ true
+ true
+ true
+ true
+
true
true
@@ -133,6 +145,24 @@
true
true
+
+ true
+ true
+ true
+ true
+
+
+ true
+ true
+ true
+ true
+
+
+ true
+ true
+ true
+ true
+
true
true
@@ -2235,16 +2265,17 @@
+
-
+
+
-
-
+
diff --git a/Builds/VisualStudio2013/RippleD.vcxproj.filters b/Builds/VisualStudio2013/RippleD.vcxproj.filters
index 06fa9c29b9..ec16d1dc9e 100644
--- a/Builds/VisualStudio2013/RippleD.vcxproj.filters
+++ b/Builds/VisualStudio2013/RippleD.vcxproj.filters
@@ -1473,6 +1473,21 @@
[2] Old Ripple\ripple_overlay
+
+ [1] Ripple\peerfinder\impl
+
+
+ [1] Ripple\peerfinder\impl
+
+
+ [1] Ripple\peerfinder\impl
+
+
+ [1] Ripple\peerfinder\impl
+
+
+ [1] Ripple\peerfinder\impl
+
@@ -2913,9 +2928,6 @@
[1] Ripple\peerfinder\impl
-
- [1] Ripple\peerfinder\impl
-
[1] Ripple\peerfinder\impl
@@ -2931,12 +2943,6 @@
[1] Ripple\peerfinder\impl
-
- [1] Ripple\peerfinder\impl
-
-
- [1] Ripple\peerfinder\impl
-
[1] Ripple\peerfinder\impl
@@ -2946,9 +2952,6 @@
[1] Ripple\peerfinder\impl
-
- [1] Ripple\peerfinder\impl
-
[1] Ripple\peerfinder\impl
@@ -3036,6 +3039,21 @@
[2] Old Ripple\ripple_overlay
+
+ [1] Ripple\peerfinder\impl
+
+
+ [1] Ripple\peerfinder\impl
+
+
+ [1] Ripple\peerfinder\impl
+
+
+ [1] Ripple\peerfinder\impl
+
+
+ [1] Ripple\peerfinder\impl
+
diff --git a/src/ripple/peerfinder/README.md b/src/ripple/peerfinder/README.md
index 6bda585976..0924256987 100644
--- a/src/ripple/peerfinder/README.md
+++ b/src/ripple/peerfinder/README.md
@@ -260,7 +260,7 @@ Slot properties may be combined and are not mutually exclusive.
configuration file or learned through overlay messages from other trusted
peers. Cluster slots do not count towards connection limits.
-* **Superpeer** (2.0)
+* **Superpeer** (forthcoming)
A superpeer slot is a connection to a peer which can accept incoming
connections, meets certain resource availaibility requirements (such as
diff --git a/src/ripple/peerfinder/api/Endpoint.h b/src/ripple/peerfinder/api/Endpoint.h
index 77edea6ca7..37229b6339 100644
--- a/src/ripple/peerfinder/api/Endpoint.h
+++ b/src/ripple/peerfinder/api/Endpoint.h
@@ -27,11 +27,15 @@ namespace PeerFinder {
struct Endpoint
{
Endpoint ();
-
+
+ Endpoint (IP::Endpoint const& ep, int hops_);
+
int hops;
IP::Endpoint address;
};
+bool operator< (Endpoint const& lhs, Endpoint const& rhs);
+
}
}
diff --git a/src/ripple/peerfinder/impl/Bootcache.cpp b/src/ripple/peerfinder/impl/Bootcache.cpp
new file mode 100644
index 0000000000..06aceb50c5
--- /dev/null
+++ b/src/ripple/peerfinder/impl/Bootcache.cpp
@@ -0,0 +1,267 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of rippled: https://github.com/ripple/rippled
+ Copyright (c) 2012, 2013 Ripple Labs Inc.
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+//==============================================================================
+
+#include "Bootcache.h"
+
+namespace ripple {
+namespace PeerFinder {
+
+Bootcache::Bootcache (
+ Store& store,
+ clock_type& clock,
+ Journal journal)
+ : m_store (store)
+ , m_clock (clock)
+ , m_journal (journal)
+ , m_whenUpdate (m_clock.now ())
+{
+}
+
+Bootcache::~Bootcache ()
+{
+ update();
+}
+
+bool
+Bootcache::empty() const
+{
+ return m_map.empty();
+}
+
+Bootcache::map_type::size_type
+Bootcache::size() const
+{
+ return m_map.size();
+}
+
+Bootcache::const_iterator
+Bootcache::begin() const
+{
+ return const_iterator (m_map.right.begin());
+}
+
+Bootcache::const_iterator
+Bootcache::cbegin() const
+{
+ return const_iterator (m_map.right.begin());
+}
+
+Bootcache::const_iterator
+Bootcache::end() const
+{
+ return const_iterator (m_map.right.end());
+}
+
+Bootcache::const_iterator
+Bootcache::cend() const
+{
+ return const_iterator (m_map.right.end());
+}
+
+void
+Bootcache::clear()
+{
+ m_map.clear();
+ m_needsUpdate = true;
+}
+
+//--------------------------------------------------------------------------
+
+void
+Bootcache::load ()
+{
+ clear();
+ auto const n (m_store.load (
+ [this](IP::Endpoint const& endpoint, int valence)
+ {
+ auto const result (this->m_map.insert (
+ value_type (endpoint, valence)));
+ if (! result.second)
+ {
+ if (this->m_journal.error)
+ this->m_journal.error << leftw (18) <<
+ "Bootcache discard " << endpoint;
+ }
+ }));
+
+ if (n > 0)
+ {
+ if (m_journal.info) m_journal.info << leftw (18) <<
+ "Bootcache loaded " << n <<
+ ((n > 1) ? " addresses" : " address");
+ prune ();
+ }
+}
+
+bool
+Bootcache::insert (IP::Endpoint const& endpoint)
+{
+ auto const result (m_map.insert (
+ value_type (endpoint, 0)));
+ if (result.second)
+ {
+ if (m_journal.trace) m_journal.trace << leftw (18) <<
+ "Bootcache insert " << endpoint;
+ prune ();
+ flagForUpdate();
+ }
+ return result.second;
+}
+
+void
+Bootcache::on_success (IP::Endpoint const& endpoint)
+{
+ auto result (m_map.insert (
+ value_type (endpoint, 1)));
+ if (result.second)
+ {
+ prune ();
+ }
+ else
+ {
+ Entry entry (result.first->right);
+ if (entry.valence() < 0)
+ entry.valence() = 0;
+ ++entry.valence();
+ m_map.erase (result.first);
+ result = m_map.insert (
+ value_type (endpoint, entry));
+ assert (result.second);
+ }
+ Entry const& entry (result.first->right);
+ if (m_journal.info) m_journal.info << leftw (18) <<
+ "Bootcache connect " << endpoint <<
+ " with " << entry.valence() <<
+ ((entry.valence() > 1) ? " successes" : " success");
+ flagForUpdate();
+}
+
+void
+Bootcache::on_failure (IP::Endpoint const& endpoint)
+{
+ auto result (m_map.insert (
+ value_type (endpoint, -1)));
+ if (result.second)
+ {
+ prune();
+ }
+ else
+ {
+ Entry entry (result.first->right);
+ if (entry.valence() > 0)
+ entry.valence() = 0;
+ --entry.valence();
+ m_map.erase (result.first);
+ result = m_map.insert (
+ value_type (endpoint, entry));
+ assert (result.second);
+ }
+ Entry const& entry (result.first->right);
+ auto const n (std::abs (entry.valence()));
+ if (m_journal.debug) m_journal.debug << leftw (18) <<
+ "Bootcache failed " << endpoint <<
+ " with " << n <<
+ ((n > 1) ? " attempts" : " attempt");
+ flagForUpdate();
+}
+
+void
+Bootcache::periodicActivity ()
+{
+ checkUpdate();
+}
+
+//--------------------------------------------------------------------------
+
+void
+Bootcache::onWrite (PropertyStream::Map& map)
+{
+ map ["entries"] = uint32 (m_map.size());
+}
+
+ // Checks the cache size and prunes if its over the limit.
+void
+Bootcache::prune ()
+{
+ if (size() <= Tuning::bootcacheSize)
+ return;
+
+ // Calculate the amount to remove
+ auto count ((size() *
+ Tuning::bootcachePrunePercent) / 100);
+ decltype(count) pruned (0);
+
+ // Work backwards because bimap doesn't handle
+ // erasing using a reverse iterator very well.
+ //
+ for (auto iter (m_map.right.end());
+ count-- > 0 && iter != m_map.right.begin(); ++pruned)
+ {
+ --iter;
+ IP::Endpoint const& endpoint (iter->get_left());
+ Entry const& entry (iter->get_right());
+ if (m_journal.trace) m_journal.trace << leftw (18) <<
+ "Bootcache pruned" << endpoint <<
+ " at valence " << entry.valence();
+ iter = m_map.right.erase (iter);
+ }
+
+ if (m_journal.debug) m_journal.debug << leftw (18) <<
+ "Bootcache pruned " << pruned << " entries total";
+}
+
+// Updates the Store with the current set of entries if needed.
+void
+Bootcache::update ()
+{
+ if (! m_needsUpdate)
+ return;
+ std::vector list;
+ list.reserve (m_map.size());
+ for (auto const& e : m_map)
+ {
+ Store::Entry se;
+ se.endpoint = e.get_left();
+ se.valence = e.get_right().valence();
+ list.push_back (se);
+ }
+ m_store.save (list);
+ // Reset the flag and cooldown timer
+ m_needsUpdate = false;
+ m_whenUpdate = m_clock.now() + Tuning::bootcacheCooldownTime;
+}
+
+// Checks the clock and calls update if we are off the cooldown.
+void
+Bootcache::checkUpdate ()
+{
+ if (m_needsUpdate && m_whenUpdate < m_clock.now())
+ update ();
+}
+
+// Called when changes to an entry will affect the Store.
+void
+Bootcache::flagForUpdate ()
+{
+ m_needsUpdate = true;
+ checkUpdate ();
+}
+
+}
+}
diff --git a/src/ripple/peerfinder/impl/Bootcache.h b/src/ripple/peerfinder/impl/Bootcache.h
index f7a97dc5d0..3e6df6988a 100644
--- a/src/ripple/peerfinder/impl/Bootcache.h
+++ b/src/ripple/peerfinder/impl/Bootcache.h
@@ -20,6 +20,10 @@
#ifndef RIPPLE_PEERFINDER_BOOTCACHE_H_INCLUDED
#define RIPPLE_PEERFINDER_BOOTCACHE_H_INCLUDED
+#include
+#include
+#include
+
namespace ripple {
namespace PeerFinder {
@@ -29,13 +33,7 @@ namespace PeerFinder {
connections are needed. Along with the address, each entry has this
additional metadata:
- Uptime
-
- The number of seconds that the address has maintained an active
- peer connection, cumulative, without a connection attempt failure.
-
Valence
-
A signed integer which represents the number of successful
consecutive connection attempts when positive, and the number of
failed consecutive connection attempts when negative.
@@ -46,33 +44,18 @@ namespace PeerFinder {
*/
class Bootcache
{
-public:
- /** An item used for connecting. */
- class Endpoint
+private:
+ class Entry
{
public:
- Endpoint ()
- : m_uptime (0)
- , m_valence (0)
+ Entry (int valence)
+ : m_valence (valence)
{
}
- Endpoint (IP::Endpoint const& address,
- std::chrono::seconds uptime, int valence)
- : m_address (address)
- , m_uptime (uptime)
- , m_valence (valence)
+ int& valence ()
{
- }
-
- IP::Endpoint const& address () const
- {
- return m_address;
- }
-
- std::chrono::seconds uptime () const
- {
- return m_uptime;
+ return m_valence;
}
int valence () const
@@ -80,106 +63,40 @@ public:
return m_valence;
}
- private:
- IP::Endpoint m_address;
- std::chrono::seconds m_uptime;
- int m_valence;
- };
-
- typedef std::vector Endpoints;
-
- //--------------------------------------------------------------------------
-
- /** An entry in the bootstrap cache. */
- struct Entry
- {
- Entry ()
- : cumulativeUptime (0)
- , sessionUptime (0)
- , connectionValence (0)
- , active (false)
+ friend bool operator< (Entry const& lhs, Entry const& rhs)
{
- }
-
- /** Update the uptime measurement based on the time. */
- void update (clock_type::time_point const& now)
- {
- // Must be active!
- assert (active);
- // Clock must be monotonically increasing
- assert (now >= whenActive);
- // Remove the uptime we added earlier in the
- // session and add back in the new uptime measurement.
- auto const uptime (now - whenActive);
- cumulativeUptime -= sessionUptime;
- cumulativeUptime += uptime;
- sessionUptime = uptime;
- }
-
- /** Our cumulative uptime with this address with no failures. */
- std::chrono::seconds cumulativeUptime;
-
- /** Amount of uptime from the current session (if any). */
- std::chrono::seconds sessionUptime;
-
- /** Number of consecutive connection successes or failures.
- If the number is positive, indicates the number of
- consecutive successful connection attempts, else the
- absolute value indicates the number of consecutive
- connection failures.
- */
- int connectionValence;
-
- /** `true` if the peer has handshaked and is currently connected. */
- bool active;
-
- /** Time when the peer became active. */
- clock_type::time_point whenActive;
- };
-
- //--------------------------------------------------------------------------
-
- /* Comparison function for entries.
-
- 1. Sort descending by cumulative uptime
- 2. For all uptimes == 0,
- Sort descending by connection successes
- 3. For all successes == 0
- Sort ascending by number of failures
- */
- struct Less
- {
- template
- bool operator() (
- Iter const& lhs_iter, Iter const& rhs_iter)
- {
- Entry const& lhs (lhs_iter->second);
- Entry const& rhs (rhs_iter->second);
- // Higher cumulative uptime always wins
- if (lhs.cumulativeUptime > rhs.cumulativeUptime)
- return true;
- else if (lhs.cumulativeUptime <= rhs.cumulativeUptime
- && rhs.cumulativeUptime.count() != 0)
- return false;
- // At this point both uptimes will be zero
- consistency_check (lhs.cumulativeUptime.count() == 0 &&
- rhs.cumulativeUptime.count() == 0);
- if (lhs.connectionValence > rhs.connectionValence)
+ if (lhs.valence() > rhs.valence())
return true;
return false;
}
+
+ private:
+ int m_valence;
};
- //--------------------------------------------------------------------------
+ typedef boost::bimaps::unordered_set_of left_t;
+ typedef boost::bimaps::multiset_of right_t;
+ typedef boost::bimap map_type;
+ typedef map_type::value_type value_type;
- typedef std::unordered_map Entries;
+ struct Transform : std::unary_function <
+ map_type::right_map::const_iterator::value_type const&,
+ IP::Endpoint const&>
+ {
+ IP::Endpoint const& operator() (
+ map_type::right_map::
+ const_iterator::value_type const& v) const
+ {
+ return v.get_left();
+ }
+ };
- typedef std::vector SortedEntries;
+private:
+ map_type m_map;
Store& m_store;
clock_type& m_clock;
Journal m_journal;
- Entries m_entries;
// Time after which we can update the database again
clock_type::time_point m_whenUpdate;
@@ -187,367 +104,57 @@ public:
// Set to true when a database update is needed
bool m_needsUpdate;
+public:
+ typedef boost::transform_iterator iterator;
+
+ typedef iterator const_iterator;
+
Bootcache (
Store& store,
clock_type& clock,
- Journal journal)
- : m_store (store)
- , m_clock (clock)
- , m_journal (journal)
- , m_whenUpdate (m_clock.now ())
- {
- }
+ Journal journal);
- ~Bootcache ()
- {
- update ();
- }
+ ~Bootcache ();
- //--------------------------------------------------------------------------
-
- /** Load the persisted data from the Store into the container. */
- void load ()
- {
- typedef std::vector StoredData;
- StoredData const list (m_store.loadBootstrapCache ());
-
- std::size_t count (0);
-
- for (StoredData::const_iterator iter (list.begin());
- iter != list.end(); ++iter)
- {
- std::pair result (
- m_entries.emplace (std::piecewise_construct,
- std::forward_as_tuple (iter->address),
- std::make_tuple ()));
- if (result.second)
- {
- ++count;
- Entry& entry (result.first->second);
- entry.cumulativeUptime = iter->cumulativeUptime;
- entry.connectionValence = iter->connectionValence;
- }
- else
- {
- if (m_journal.error) m_journal.error << leftw (18) <<
- "Bootcache discard " << iter->address;
- }
- }
-
- if (count > 0)
- {
- if (m_journal.info) m_journal.info << leftw (18) <<
- "Bootcache loaded " << count <<
- ((count > 1) ? " addresses" : " address");
- }
-
- prune ();
- }
+ /** Returns `true` if the cache is empty. */
+ bool empty() const;
/** Returns the number of entries in the cache. */
- std::size_t size () const
- {
- return m_entries.size();
- }
+ map_type::size_type size() const;
- /** Returns up to the specified number of the best addresses. */
- IPAddresses getAddresses (int n)
- {
- SortedEntries const list (sort());
- IPAddresses result;
- int count (0);
- result.reserve (n);
- for (SortedEntries::const_iterator iter (
- list.begin()); ++count <= n && iter != list.end(); ++iter)
- result.push_back ((*iter)->first);
- consistency_check (result.size() <= n);
- return result;
- }
+ /** IP::Endpoint iterators that traverse in decreasing valence. */
+ /** @{ */
+ const_iterator begin() const;
+ const_iterator cbegin() const;
+ const_iterator end() const;
+ const_iterator cend() const;
+ void clear();
+ /** @} */
- /** Returns all entries in the cache. */
- Endpoints fetch () const
- {
- Endpoints result;
- result.reserve (m_entries.size ());
- for (Entries::const_iterator iter (m_entries.begin ());
- iter != m_entries.end (); ++iter)
- result.emplace_back (iter->first,
- iter->second.cumulativeUptime,
- iter->second.connectionValence);
- return result;
- }
+ /** Load the persisted data from the Store into the container. */
+ void load ();
- /** Called periodically to perform time related tasks. */
- void periodicActivity ()
- {
- checkUpdate();
- }
-
- /** Called when an address is learned from a message. */
- bool insert (IP::Endpoint const& address)
- {
- std::pair result (
- m_entries.emplace (std::piecewise_construct,
- std::forward_as_tuple (address),
- std::make_tuple ()));
- if (result.second)
- {
- if (m_journal.trace) m_journal.trace << leftw (18) <<
- "Bootcache insert " << address;
- prune ();
- flagForUpdate();
- }
- return result.second;
- }
-
- /** Called when an outbound connection attempt fails to handshake. */
- void onConnectionFailure (IP::Endpoint const& address)
- {
- Entries::iterator iter (m_entries.find (address));
- // If the entry doesn't already exist don't bother remembering
- // it since the connection failed.
- //
- if (iter == m_entries.end())
- return;
- Entry& entry (iter->second);
- // Reset cumulative uptime to zero. We are aggressive
- // with resetting uptime to prevent the entire network
- // from settling on just a handful of addresses.
- //
- entry.cumulativeUptime = std::chrono::seconds (0);
- entry.sessionUptime = std::chrono::seconds (0);
- // Increment the number of consecutive failures.
- if (entry.connectionValence > 0)
- entry.connectionValence = 0;
- --entry.connectionValence;
- int const count (std::abs (entry.connectionValence));
- if (m_journal.debug) m_journal.debug << leftw (18) <<
- "Bootcache failed " << address <<
- " with " << count <<
- ((count > 1) ? " attempts" : " attempt");
- flagForUpdate();
- }
+ /** Add the address to the cache. */
+ bool insert (IP::Endpoint const& endpoint);
/** Called when an outbound connection handshake completes. */
- void onConnectionHandshake (IP::Endpoint const& address,
- HandshakeAction action)
- {
- std::pair result (
- m_entries.emplace (std::piecewise_construct,
- std::forward_as_tuple (address),
- std::make_tuple ()));
- Entry& entry (result.first->second);
- // Can't already be active!
- consistency_check (! entry.active);
- // Reset session uptime
- entry.sessionUptime = std::chrono::seconds (0);
- // Count this as a connection success
- if (entry.connectionValence < 0)
- entry.connectionValence = 0;
- ++entry.connectionValence;
- // Update active status
- if (action == doActivate)
- {
- entry.active = true;
- entry.whenActive = m_clock.now();
- }
- else
- {
- entry.active = false;
- }
- // Prune if we made the container larger
- if (result.second)
- prune ();
- flagForUpdate();
- if (m_journal.info) m_journal.info << leftw (18) <<
- "Bootcache connect " << address <<
- " with " << entry.connectionValence <<
- ((entry.connectionValence > 1) ? " successes" : " success");
- }
+ void on_success (IP::Endpoint const& endpoint);
- /** Called periodically while the peer is active. */
- //
- // VFALCO TODO Can't we just put the active ones into an intrusive list
- // and update their uptime in periodicActivity() now that
- // we have the m_clock member?
- //
- void onConnectionActive (IP::Endpoint const& address)
- {
- std::pair result (
- m_entries.emplace (std::piecewise_construct,
- std::forward_as_tuple (address),
- std::make_tuple ()));
- // Must exist!
- consistency_check (! result.second);
- Entry& entry (result.first->second);
- entry.update (m_clock.now());
- flagForUpdate();
- }
+ /** Called when an outbound connection attempt fails to handshake. */
+ void on_failure (IP::Endpoint const& endpoint);
- template
- static std::string uptime_phrase (
- std::chrono::duration const& elapsed)
- {
- if (elapsed.count() > 0)
- {
- std::stringstream ss;
- ss << " with " << elapsed << " uptime";
- return ss.str();
- }
- return std::string ();
- }
- /** Called when an active outbound connection closes. */
- void onConnectionClosed (IP::Endpoint const& address)
- {
- Entries::iterator iter (m_entries.find (address));
- // Must exist!
- consistency_check (iter != m_entries.end());
- Entry& entry (iter->second);
- // Must be active!
- consistency_check (entry.active);
- if (m_journal.trace) m_journal.trace << leftw (18) <<
- "Bootcache close " << address <<
- uptime_phrase (entry.cumulativeUptime);
- entry.update (m_clock.now());
- entry.sessionUptime = std::chrono::seconds (0);
- entry.active = false;
- flagForUpdate();
- }
+ /** Stores the cache in the persistent database on a timer. */
+ void periodicActivity ();
- //--------------------------------------------------------------------------
- //
- // Diagnostics
- //
- //--------------------------------------------------------------------------
+ /** Write the cache state to the property stream. */
+ void onWrite (PropertyStream::Map& map);
- void onWrite (PropertyStream::Map& map)
- {
- map ["entries"] = uint32(m_entries.size());
- }
-
- static std::string valenceString (int valence)
- {
- std::stringstream ss;
- if (valence >= 0)
- ss << '+';
- ss << valence;
- return ss.str();
- }
-
- void dump (Journal::ScopedStream const& ss) const
- {
- std::vector const list (csort ());
- ss << std::endl << std::endl <<
- "Bootcache (size " << list.size() << ")";
- for (std::vector ::const_iterator iter (
- list.begin()); iter != list.end(); ++iter)
- {
- ss << std::endl <<
- (*iter)->first << ", " <<
- (*iter)->second.cumulativeUptime << ", "
- << valenceString ((*iter)->second.connectionValence);
- if ((*iter)->second.active)
- ss <<
- ", active";
- }
- }
-
- //--------------------------------------------------------------------------
private:
- // Returns a vector of entry iterators sorted by descending score
- std::vector csort () const
- {
- std::vector result;
- result.reserve (m_entries.size());
- for (Entries::const_iterator iter (m_entries.begin());
- iter != m_entries.end(); ++iter)
- result.push_back (iter);
- std::random_shuffle (result.begin(), result.end());
- // should be std::unstable_sort (c++11)
- std::sort (result.begin(), result.end(), Less());
- return result;
- }
-
- // Returns a vector of entry iterators sorted by descending score
- std::vector sort ()
- {
- std::vector result;
- result.reserve (m_entries.size());
- for (Entries::iterator iter (m_entries.begin());
- iter != m_entries.end(); ++iter)
- result.push_back (iter);
- std::random_shuffle (result.begin(), result.end());
- // should be std::unstable_sort (c++11)
- std::sort (result.begin(), result.end(), Less());
- return result;
- }
-
- // Checks the cache size and prunes if its over the limit.
- void prune ()
- {
- if (m_entries.size() <= Tuning::bootcacheSize)
- return;
- // Calculate the amount to remove
- int count ((m_entries.size() *
- Tuning::bootcachePrunePercent) / 100);
- int pruned (0);
- SortedEntries list (sort ());
- for (SortedEntries::const_reverse_iterator iter (
- list.rbegin()); count > 0 && iter != list.rend(); ++iter)
- {
- Entry& entry ((*iter)->second);
- // skip active entries
- if (entry.active)
- continue;
- if (m_journal.trace) m_journal.trace << leftw (18) <<
- "Bootcache pruned" << (*iter)->first <<
- uptime_phrase (entry.cumulativeUptime) <<
- " and valence " << entry.connectionValence;
- m_entries.erase (*iter);
- --count;
- ++pruned;
- }
-
- if (m_journal.debug) m_journal.debug << leftw (18) <<
- "Bootcache pruned " << pruned << " entries total";
- }
-
- // Updates the Store with the current set of entries if needed.
- void update ()
- {
- if (! m_needsUpdate)
- return;
- typedef std::vector StoredData;
- StoredData list;
- list.reserve (m_entries.size());
- for (Entries::const_iterator iter (m_entries.begin());
- iter != m_entries.end(); ++iter)
- {
- Store::SavedBootstrapAddress entry;
- entry.address = iter->first;
- entry.cumulativeUptime = iter->second.cumulativeUptime;
- entry.connectionValence = iter->second.connectionValence;
- list.push_back (entry);
- }
- m_store.updateBootstrapCache (list);
- // Reset the flag and cooldown timer
- m_needsUpdate = false;
- m_whenUpdate = m_clock.now() + Tuning::bootcacheCooldownTime;
- }
-
- // Checks the clock and calls update if we are off the cooldown.
- void checkUpdate ()
- {
- if (m_needsUpdate && m_whenUpdate < m_clock.now())
- update ();
- }
-
- // Called when changes to an entry will affect the Store.
- void flagForUpdate ()
- {
- m_needsUpdate = true;
- checkUpdate ();
- }
+ void prune ();
+ void update ();
+ void checkUpdate ();
+ void flagForUpdate ();
};
}
diff --git a/src/ripple/peerfinder/impl/ConnectHandouts.cpp b/src/ripple/peerfinder/impl/ConnectHandouts.cpp
new file mode 100644
index 0000000000..52b3bb1506
--- /dev/null
+++ b/src/ripple/peerfinder/impl/ConnectHandouts.cpp
@@ -0,0 +1,64 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of rippled: https://github.com/ripple/rippled
+ Copyright (c) 2012, 2013 Ripple Labs Inc.
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+//==============================================================================
+
+#include "ConnectHandouts.h"
+
+namespace ripple {
+namespace PeerFinder {
+
+ConnectHandouts::ConnectHandouts (
+ std::size_t needed, Squelches& squelches)
+ : m_needed (needed)
+ , m_squelches (squelches)
+{
+ m_list.reserve (needed);
+}
+
+bool
+ConnectHandouts::try_insert (IP::Endpoint const& endpoint)
+{
+ if (full ())
+ return false;
+
+ // Make sure the address isn't already in our list
+ if (std::any_of (m_list.begin(), m_list.end(),
+ [&endpoint](IP::Endpoint const& other)
+ {
+ // Ignore port for security reasons
+ return other.address() ==
+ endpoint.address();
+ }))
+ {
+ return false;
+ }
+
+ // Add to squelch list so we don't try it too often.
+ // If its already there, then make try_insert fail.
+ auto const result (m_squelches.insert (
+ endpoint.address()));
+ if (! result.second)
+ return false;
+
+ m_list.push_back (endpoint);
+
+ return true;
+}
+
+}
+}
diff --git a/src/ripple/peerfinder/impl/ConnectHandouts.h b/src/ripple/peerfinder/impl/ConnectHandouts.h
new file mode 100644
index 0000000000..06d4c60125
--- /dev/null
+++ b/src/ripple/peerfinder/impl/ConnectHandouts.h
@@ -0,0 +1,79 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of rippled: https://github.com/ripple/rippled
+ Copyright (c) 2012, 2013 Ripple Labs Inc.
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+//==============================================================================
+
+#ifndef RIPPLE_PEERFINDER_CONNECTHANDOUTS_H_INCLUDED
+#define RIPPLE_PEERFINDER_CONNECTHANDOUTS_H_INCLUDED
+
+#include "Tuning.h"
+
+#include "../../../beast/beast/container/aged_set.h"
+
+namespace ripple {
+namespace PeerFinder {
+
+/** Receives handouts for making automatic connections. */
+class ConnectHandouts
+{
+public:
+ // Keeps track of addresses we have made outgoing connections
+ // to, for the purposes of not connecting to them too frequently.
+ typedef beast::aged_set Squelches;
+
+ typedef std::vector list_type;
+
+private:
+ std::size_t m_needed;
+ Squelches& m_squelches;
+ list_type m_list;
+
+public:
+ ConnectHandouts (std::size_t needed, Squelches& squelches);
+
+ bool empty() const
+ {
+ return m_list.empty();
+ }
+
+ bool full() const
+ {
+ return m_list.size() >= m_needed;
+ }
+
+ bool try_insert (Endpoint const& endpoint)
+ {
+ return try_insert (endpoint.address);
+ }
+
+ list_type& list()
+ {
+ return m_list;
+ }
+
+ list_type const& list() const
+ {
+ return m_list;
+ }
+
+ bool try_insert (IP::Endpoint const& endpoint);
+};
+
+}
+}
+
+#endif
diff --git a/src/ripple/peerfinder/impl/Endpoint.cpp b/src/ripple/peerfinder/impl/Endpoint.cpp
index 176fe697a6..8756919db6 100644
--- a/src/ripple/peerfinder/impl/Endpoint.cpp
+++ b/src/ripple/peerfinder/impl/Endpoint.cpp
@@ -27,5 +27,16 @@ Endpoint::Endpoint ()
{
}
+Endpoint::Endpoint (IP::Endpoint const& ep, int hops_)
+ : hops (hops_)
+ , address (ep)
+{
+}
+
+bool operator< (Endpoint const& lhs, Endpoint const& rhs)
+{
+ return lhs.address < rhs.address;
+}
+
}
}
diff --git a/src/ripple/peerfinder/impl/Giveaways.h b/src/ripple/peerfinder/impl/Giveaways.h
deleted file mode 100644
index ee0d63d46c..0000000000
--- a/src/ripple/peerfinder/impl/Giveaways.h
+++ /dev/null
@@ -1,150 +0,0 @@
-//------------------------------------------------------------------------------
-/*
- This file is part of rippled: https://github.com/ripple/rippled
- Copyright (c) 2012, 2013 Ripple Labs Inc.
-
- Permission to use, copy, modify, and/or distribute this software for any
- purpose with or without fee is hereby granted, provided that the above
- copyright notice and this permission notice appear in all copies.
-
- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
-//==============================================================================
-
-#ifndef RIPPLE_PEERFINDER_GIVEAWAYS_H_INCLUDED
-#define RIPPLE_PEERFINDER_GIVEAWAYS_H_INCLUDED
-
-namespace ripple {
-namespace PeerFinder {
-
-/** Holds a rotating set of endpoint messages to give away. */
-class Giveaways
-{
-public:
- typedef std::vector Bucket;
- typedef boost::array Buckets;
-
- Endpoints m_endpoints;
- std::size_t m_remain;
- Buckets m_buckets;
-
- void prepare ()
- {
- for (Buckets::iterator iter (m_buckets.begin());
- iter != m_buckets.end(); ++iter)
- iter->reserve (m_endpoints.size ());
- }
-
-public:
- bool is_consistent ()
- {
- // Make sure the counts add up
- std::size_t count (0);
- for (Buckets::const_iterator iter (m_buckets.begin());
- iter != m_buckets.end(); ++iter)
- count += iter->size();
- return count == m_remain;
- }
-
- void refill ()
- {
- // Empty out the buckets
- for (Buckets::iterator iter (m_buckets.begin());
- iter != m_buckets.end(); ++iter)
- iter->clear();
- // Put endpoints back into buckets
- for (Endpoints::const_iterator iter (m_endpoints.begin());
- iter != m_endpoints.end(); ++iter)
- {
- Endpoint const& ep (*iter);
- consistency_check (ep.hops <= Tuning::maxHops);
- m_buckets [ep.hops].push_back (&ep);
- }
- // Shuffle the buckets
- for (Buckets::iterator iter (m_buckets.begin());
- iter != m_buckets.end(); ++iter)
- std::random_shuffle (iter->begin(), iter->end());
- m_remain = m_endpoints.size();
- consistency_check (is_consistent ());
- }
-
-public:
- explicit Giveaways (Endpoints const& endpoints)
- : m_endpoints (endpoints)
- , m_remain (0)
- {
- prepare();
- }
-
-#if BEAST_COMPILER_SUPPORTS_MOVE_SEMANTICS
- Giveaways (Endpoints&& endpoints)
- : m_endpoints (endpoints)
- , m_remain (0)
- {
- prepare();
- }
-#endif
-
- /** Append up to `n` Endpoint to the specified container.
- The entries added to the container will have hops incremented.
- */
- template
- void append (Endpoints::size_type n, EndpointContainer& c)
- {
- n = std::min (n, m_endpoints.size());
- c.reserve (c.size () + n);
- if (m_remain < n)
- refill ();
- for (cyclic_iterator iter (
- m_buckets.begin (), m_buckets.begin (), m_buckets.end()); n;)
- {
- Bucket& bucket (*iter++);
- if (! bucket.empty ())
- {
- c.emplace_back (*bucket.back ());
- bucket.pop_back ();
- ++c.back ().hops;
- --n;
- --m_remain;
- }
- }
- consistency_check (is_consistent ());
- }
-
- /** Retrieve a fresh set of endpoints, preferring high hops.
- The entries added to the container will have hops incremented.
- */
- template
- void reverse_append (Endpoints::size_type n, EndpointContainer& c)
- {
- n = std::min (n, m_endpoints.size());
- c.reserve (c.size () + n);
- if (m_remain < n)
- refill ();
- for (cyclic_iterator iter (
- m_buckets.rbegin (), m_buckets.rbegin (), m_buckets.rend()); n;)
- {
- Bucket& bucket (*iter++);
- if (! bucket.empty ())
- {
- c.emplace_back (*bucket.back ());
- bucket.pop_back ();
- ++c.back ().hops;
- --n;
- --m_remain;
- }
- }
- consistency_check (is_consistent ());
- }
-};
-
-}
-}
-
-#endif
diff --git a/src/ripple/peerfinder/impl/Livecache.cpp b/src/ripple/peerfinder/impl/Livecache.cpp
index ff95d7cb6d..bea513cfa0 100644
--- a/src/ripple/peerfinder/impl/Livecache.cpp
+++ b/src/ripple/peerfinder/impl/Livecache.cpp
@@ -20,13 +20,24 @@
namespace ripple {
namespace PeerFinder {
+//------------------------------------------------------------------------------
+
+namespace detail {
+
+
+
+}
+
+//------------------------------------------------------------------------------
+
class LivecacheTests : public UnitTest
{
public:
manual_clock m_clock;
// Add the address as an endpoint
- void add (uint32 index, uint16 port, Livecache& c)
+ template
+ void add (uint32 index, uint16 port, C& c)
{
Endpoint ep;
ep.hops = 0;
@@ -39,7 +50,7 @@ public:
{
beginTestCase ("fetch");
- Livecache c (m_clock, Journal());
+ Livecache <> c (m_clock, Journal());
add (1, 1, c);
add (2, 1, c);
@@ -52,32 +63,7 @@ public:
add (6, 2, c);
add (7, 1, c);
- Endpoints const eps (c.fetch_unique ());
-
- struct IsAddr
- {
- explicit IsAddr (uint32 index_)
- : index (index_)
- { }
- bool operator() (Endpoint const& ep) const
- { return ep.address.to_v4().value == index; }
- uint32 index;
- };
-
- expect (std::count_if (
- eps.begin(), eps.end(), IsAddr (1)) == 1);
- expect (std::count_if (
- eps.begin(), eps.end(), IsAddr (2)) == 1);
- expect (std::count_if (
- eps.begin(), eps.end(), IsAddr (3)) == 1);
- expect (std::count_if (
- eps.begin(), eps.end(), IsAddr (4)) == 1);
- expect (std::count_if (
- eps.begin(), eps.end(), IsAddr (5)) == 1);
- expect (std::count_if (
- eps.begin(), eps.end(), IsAddr (6)) == 1);
- expect (std::count_if (
- eps.begin(), eps.end(), IsAddr (7)) == 1);
+ // VFALCO TODO!!!
pass();
}
diff --git a/src/ripple/peerfinder/impl/Livecache.h b/src/ripple/peerfinder/impl/Livecache.h
index ff8540aaef..3ecf299cfb 100644
--- a/src/ripple/peerfinder/impl/Livecache.h
+++ b/src/ripple/peerfinder/impl/Livecache.h
@@ -20,11 +20,151 @@
#ifndef RIPPLE_PEERFINDER_LIVECACHE_H_INCLUDED
#define RIPPLE_PEERFINDER_LIVECACHE_H_INCLUDED
-#include
+#include "../../../beast/beast/container/aged_map.h"
+#include "../../../beast/beast/type_traits/maybe_const.h"
+
+#include
+#include
namespace ripple {
namespace PeerFinder {
+template
+class Livecache;
+
+namespace detail {
+
+class LivecacheBase
+{
+protected:
+ struct Element
+ : boost::intrusive::list_base_hook <>
+ {
+ Element (Endpoint const& endpoint_)
+ : endpoint (endpoint_)
+ {
+ }
+
+ Endpoint endpoint;
+ };
+
+ typedef boost::intrusive::make_list
+ >::type list_type;
+
+public:
+ /** A list of Endpoint at the same hops
+ This is a lightweight wrapper around a reference to the underlying
+ container.
+ */
+ template
+ class Hop
+ {
+ public:
+ // Iterator transformation to extract the endpoint from Element
+ struct Transform
+ : public std::unary_function
+ {
+ Endpoint const& operator() (Element const& e) const
+ {
+ return e.endpoint;
+ }
+ };
+
+ public:
+ typedef boost::transform_iterator iterator;
+
+ typedef iterator const_iterator;
+
+ typedef boost::transform_iterator reverse_iterator;
+
+ typedef reverse_iterator const_reverse_iterator;
+
+ iterator begin () const
+ {
+ return iterator (m_list.get().cbegin(),
+ Transform());
+ }
+
+ iterator cbegin () const
+ {
+ return iterator (m_list.get().cbegin(),
+ Transform());
+ }
+
+ iterator end () const
+ {
+ return iterator (m_list.get().cend(),
+ Transform());
+ }
+
+ iterator cend () const
+ {
+ return iterator (m_list.get().cend(),
+ Transform());
+ }
+
+ reverse_iterator rbegin () const
+ {
+ return reverse_iterator (m_list.get().crbegin(),
+ Transform());
+ }
+
+ reverse_iterator crbegin () const
+ {
+ return reverse_iterator (m_list.get().crbegin(),
+ Transform());
+ }
+
+ reverse_iterator rend () const
+ {
+ return reverse_iterator (m_list.get().crend(),
+ Transform());
+ }
+
+ reverse_iterator crend () const
+ {
+ return reverse_iterator (m_list.get().crend(),
+ Transform());
+ }
+
+ // move the element to the end of the container
+ void move_back (const_iterator pos)
+ {
+ auto& e (const_cast (*pos.base()));
+ m_list.get().erase (m_list.get().iterator_to (e));
+ m_list.get().push_back (e);
+ }
+
+ private:
+ explicit Hop (typename maybe_const <
+ IsConst, list_type>::type& list)
+ : m_list (list)
+ {
+ }
+
+ friend class LivecacheBase;
+
+ std::reference_wrapper ::type> m_list;
+ };
+
+protected:
+ // Work-around to call Hop's private constructor from Livecache
+ template
+ static Hop make_hop (typename maybe_const <
+ IsConst, list_type>::type& list)
+ {
+ return Hop (list);
+ }
+};
+
+}
+
+//------------------------------------------------------------------------------
+
/** The Livecache holds the short-lived relayed Endpoint messages.
Since peers only advertise themselves when they have open slots,
@@ -37,234 +177,382 @@ namespace PeerFinder {
launches or for bootstrapping, because they do not have verifiable
and locally observed uptime and connectibility information.
*/
-class Livecache
+template >
+class Livecache : protected detail::LivecacheBase
{
-public:
- struct Entry;
+private:
+ typedef aged_map <
+ IP::Endpoint,
+ Element,
+ std::chrono::seconds,
+ std::less ,
+ Allocator
+ > cache_type;
- typedef List EntryList;
-
- struct Entry : public EntryList::Node
- {
- Entry (Endpoint const& endpoint_,
- clock_type::time_point const& whenExpires_)
- : endpoint (endpoint_)
- , whenExpires (whenExpires_)
- {
- }
-
- Endpoint endpoint;
- clock_type::time_point whenExpires;
- };
-
- typedef std::set SortedTable;
- typedef std::unordered_map AddressTable;
-
- clock_type& m_clock;
Journal m_journal;
- AddressTable m_byAddress;
- SortedTable m_bySorted;
-
- // Tracks all the cached endpoints stored in the endpoint table
- // in oldest-to-newest order. The oldest item is at the head.
- EntryList m_list;
+ cache_type m_cache;
public:
+ typedef Allocator allocator_type;
+
/** Create the cache. */
Livecache (
clock_type& clock,
- Journal journal)
- : m_clock (clock)
- , m_journal (journal)
+ Journal journal,
+ Allocator alloc = Allocator());
+
+ //
+ // Iteration by hops
+ //
+ // The range [begin, end) provides a sequence of list_type
+ // where each list contains endpoints at a given hops.
+ //
+
+ class hops_t
{
- }
+ private:
+ // An endpoint at hops=0 represents the local node.
+ // Endpoints coming in at maxHops are stored at maxHops +1,
+ // but not given out (since they would exceed maxHops). They
+ // are used for automatic connection attempts.
+ //
+ typedef std::array Histogram;
+ typedef std::array lists_type;
+
+ template
+ struct Transform
+ : public std::unary_function <
+ typename lists_type::value_type, Hop >
+ {
+ Hop operator() (typename maybe_const <
+ IsConst, typename lists_type::value_type>::type& list) const
+ {
+ return make_hop (list);
+ }
+ };
+
+ public:
+ typedef boost::transform_iterator ,
+ typename lists_type::iterator> iterator;
+
+ typedef boost::transform_iterator ,
+ typename lists_type::const_iterator> const_iterator;
+
+ typedef boost::transform_iterator ,
+ typename lists_type::reverse_iterator> reverse_iterator;
+
+ typedef boost::transform_iterator ,
+ typename lists_type::const_reverse_iterator> const_reverse_iterator;
+
+ iterator begin ()
+ {
+ return iterator (m_lists.begin(),
+ Transform ());
+ }
+
+ const_iterator begin () const
+ {
+ return const_iterator (m_lists.cbegin(),
+ Transform ());
+ }
+
+ const_iterator cbegin () const
+ {
+ return const_iterator (m_lists.cbegin(),
+ Transform ());
+ }
+
+ iterator end ()
+ {
+ return iterator (m_lists.end(),
+ Transform ());
+ }
+
+ const_iterator end () const
+ {
+ return const_iterator (m_lists.cend(),
+ Transform ());
+ }
+
+ const_iterator cend () const
+ {
+ return const_iterator (m_lists.cend(),
+ Transform ());
+ }
+
+ reverse_iterator rbegin ()
+ {
+ return reverse_iterator (m_lists.rbegin(),
+ Transform ());
+ }
+
+ const_reverse_iterator rbegin () const
+ {
+ return const_reverse_iterator (m_lists.crbegin(),
+ Transform ());
+ }
+
+ const_reverse_iterator crbegin () const
+ {
+ return const_reverse_iterator (m_lists.crbegin(),
+ Transform ());
+ }
+
+ reverse_iterator rend ()
+ {
+ return reverse_iterator (m_lists.rend(),
+ Transform ());
+ }
+
+ const_reverse_iterator rend () const
+ {
+ return const_reverse_iterator (m_lists.crend(),
+ Transform ());
+ }
+
+ const_reverse_iterator crend () const
+ {
+ return const_reverse_iterator (m_lists.crend(),
+ Transform ());
+ }
+
+ /** Shuffle each hop list. */
+ void shuffle ();
+
+ std::string histogram() const;
+
+ private:
+ explicit hops_t (Allocator const& alloc);
+
+ void insert (Element& e);
+
+ // Reinsert e at a new hops
+ void reinsert (Element& e, int hops);
+
+ void remove (Element& e);
+
+ friend class Livecache;
+ lists_type m_lists;
+ Histogram m_hist;
+ } hops;
/** Returns `true` if the cache is empty. */
bool empty () const
{
- return m_byAddress.empty ();
+ return m_cache.empty ();
}
/** Returns the number of entries in the cache. */
- AddressTable::size_type size() const
+ typename cache_type::size_type size() const
{
- return m_byAddress.size();
+ return m_cache.size();
}
/** Erase entries whose time has expired. */
- void sweep ()
- {
- auto const now (m_clock.now ());
- AddressTable::size_type count (0);
- for (EntryList::iterator iter (m_list.begin());
- iter != m_list.end();)
- {
- // Short circuit the loop since the list is sorted
- if (iter->whenExpires > now)
- break;
- Entry& entry (*iter);
- if (m_journal.trace) m_journal.trace << leftw (18) <<
- "Livecache expired " << entry.endpoint.address;
- // Must erase from list before map
- iter = m_list.erase (iter);
- meets_postcondition (m_bySorted.erase (
- entry.endpoint) == 1);
- meets_postcondition (m_byAddress.erase (
- entry.endpoint.address) == 1);
- ++count;
- }
+ void expire ();
- if (count > 0)
- {
- if (m_journal.debug) m_journal.debug << leftw (18) <<
- "Livecache expired " << count <<
- ((count > 1) ? " entries" : " entry");
- }
- }
-
- /** Creates or updates an existing entry based on a new message. */
- void insert (Endpoint endpoint)
- {
- // Caller is responsible for validation
- check_precondition (endpoint.hops <= Tuning::maxHops);
- auto now (m_clock.now ());
- auto const whenExpires (now + Tuning::liveCacheSecondsToLive);
- std::pair result (
- m_byAddress.emplace (std::piecewise_construct,
- std::make_tuple (endpoint.address),
- std::make_tuple (endpoint, whenExpires)));
- Entry& entry (result.first->second);
- // Drop duplicates at higher hops
- if (! result.second && (endpoint.hops > entry.endpoint.hops))
- {
- std::size_t const excess (
- endpoint.hops - entry.endpoint.hops);
- if (m_journal.trace) m_journal.trace << leftw(18) <<
- "Livecache drop " << endpoint.address <<
- " at hops +" << excess;
- return;
- }
- // Update metadata if the address already exists
- if (! result.second)
- {
- meets_postcondition (m_bySorted.erase (
- result.first->second.endpoint) == 1);
- if (endpoint.hops < entry.endpoint.hops)
- {
- if (m_journal.debug) m_journal.debug << leftw (18) <<
- "Livecache update " << endpoint.address <<
- " at hops " << endpoint.hops;
- entry.endpoint.hops = endpoint.hops;
- }
- else
- {
- if (m_journal.trace) m_journal.trace << leftw (18) <<
- "Livecache refresh " << endpoint.address <<
- " at hops " << endpoint.hops;
- }
-
- entry.whenExpires = whenExpires;
-
- m_list.erase (m_list.iterator_to(entry));
- }
- else
- {
- if (m_journal.debug) m_journal.debug << leftw (18) <<
- "Livecache insert " << endpoint.address <<
- " at hops " << endpoint.hops;
- }
- meets_postcondition (m_bySorted.insert (entry.endpoint).second);
- m_list.push_back (entry);
- }
-
- /** Returns the full set of endpoints in a Giveaways class. */
- Giveaways giveaways()
- {
- Endpoints endpoints;
- endpoints.reserve (m_list.size());
- for (EntryList::const_iterator iter (m_list.cbegin());
- iter != m_list.cend(); ++iter)
- {
- endpoints.push_back (iter->endpoint);
- endpoints.back ().hops;
- }
- if (! endpoints.empty())
- return Giveaways (endpoints);
- return Giveaways (endpoints);
- }
-
- /** Returns an ordered list all entries with unique addresses. */
- Endpoints fetch_unique () const
- {
- Endpoints result;
- if (m_bySorted.empty ())
- return result;
- result.reserve (m_bySorted.size ());
- Endpoint const& front (*m_bySorted.begin());
- IP::Address prev (front.address.address());
- result.emplace_back (front);
- for (SortedTable::const_iterator iter (++m_bySorted.begin());
- iter != m_bySorted.end(); ++iter)
- {
- IP::Address const addr (iter->address.address());
- if (addr != prev)
- {
- result.emplace_back (*iter);
- ++result.back().hops;
- prev = addr;
- }
- }
- return result;
- }
+ /** Creates or updates an existing Element based on a new message. */
+ void insert (Endpoint const& ep);
/** Produce diagnostic output. */
- void dump (Journal::ScopedStream& ss) const
- {
- ss << std::endl << std::endl <<
- "Livecache (size " << m_byAddress.size() << ")";
- for (AddressTable::const_iterator iter (m_byAddress.begin());
- iter != m_byAddress.end(); ++iter)
- {
- Entry const& entry (iter->second);
- ss << std::endl <<
- entry.endpoint.address << ", " <<
- entry.endpoint.hops << " hops";
- }
- }
-
- /** Returns a histogram of message counts by hops. */
- typedef boost::array Histogram;
- Histogram histogram () const
- {
- Histogram h;
- for (Histogram::iterator iter (h.begin());
- iter != h.end(); ++iter)
- *iter = 0;
- for (EntryList::const_iterator iter (m_list.begin());
- iter != m_list.end(); ++iter)
- ++h[iter->endpoint.hops];
- return h;
- }
+ void dump (Journal::ScopedStream& ss) const;
/** Output statistics. */
- void onWrite (PropertyStream::Map& map)
- {
- clock_type::time_point const now (m_clock.now ());
- map ["size"] = size ();
- PropertyStream::Set set ("entries", map);
- for (auto entry : m_byAddress)
- {
- PropertyStream::Map item (set);
- Entry const& e (entry.second);
- item ["hops"] = e.endpoint.hops;
- item ["address"] = e.endpoint.address.to_string ();
- std::stringstream ss;
- ss << e.whenExpires - now;
- item ["expires"] = ss.str();
- }
- }
+ void onWrite (PropertyStream::Map& map);
};
+//------------------------------------------------------------------------------
+
+template
+Livecache ::Livecache (
+ clock_type& clock,
+ Journal journal,
+ Allocator alloc)
+ : m_journal (journal)
+ , m_cache (clock, alloc)
+ , hops (alloc)
+{
+}
+
+template
+void
+Livecache ::expire()
+{
+ std::size_t n (0);
+ typename cache_type::time_point const expired (
+ m_cache.clock().now() - Tuning::liveCacheSecondsToLive);
+ for (auto iter (m_cache.chronological.begin());
+ iter != m_cache.chronological.end() && iter.when() <= expired;)
+ {
+ Element& e (iter->second);
+ hops.remove (e);
+ iter = m_cache.erase (iter);
+ ++n;
+ }
+ if (n > 0)
+ {
+ if (m_journal.debug) m_journal.debug << leftw (18) <<
+ "Livecache expired " << n <<
+ ((n > 1) ? " entries" : " entry");
+ }
+}
+
+template
+void Livecache ::insert (Endpoint const& ep)
+{
+ // The caller already incremented hop, so if we got a
+ // message at maxHops we will store it at maxHops + 1.
+ // This means we won't give out the address to other peers
+ // but we will use it to make connections and hand it out
+ // when redirecting.
+ //
+ assert (ep.hops <= (Tuning::maxHops + 1));
+ std::pair result (
+ m_cache.emplace (ep.address, ep));
+ Element& e (result.first->second);
+ if (result.second)
+ {
+ hops.insert (e);
+ if (m_journal.debug) m_journal.debug << leftw (18) <<
+ "Livecache insert " << ep.address <<
+ " at hops " << ep.hops;
+ return;
+ }
+ else if (! result.second && (ep.hops > e.endpoint.hops))
+ {
+ // Drop duplicates at higher hops
+ std::size_t const excess (
+ ep.hops - e.endpoint.hops);
+ if (m_journal.trace) m_journal.trace << leftw(18) <<
+ "Livecache drop " << ep.address <<
+ " at hops +" << excess;
+ return;
+ }
+
+ m_cache.touch (result.first);
+
+ // Address already in the cache so update metadata
+ if (ep.hops < e.endpoint.hops)
+ {
+ hops.reinsert (e, ep.hops);
+ if (m_journal.debug) m_journal.debug << leftw (18) <<
+ "Livecache update " << ep.address <<
+ " at hops " << ep.hops;
+ }
+ else
+ {
+ if (m_journal.trace) m_journal.trace << leftw (18) <<
+ "Livecache refresh " << ep.address <<
+ " at hops " << ep.hops;
+ }
+}
+
+template
+void
+Livecache ::dump (Journal::ScopedStream& ss) const
+{
+ ss << std::endl << std::endl <<
+ "Livecache (size " << m_cache.size() << ")";
+ for (auto const& entry : m_cache)
+ {
+ auto const& e (entry.second);
+ ss << std::endl <<
+ e.endpoint.address << ", " <<
+ e.endpoint.hops << " hops";
+ }
+}
+
+template
+void
+Livecache ::onWrite (PropertyStream::Map& map)
+{
+ typename cache_type::time_point const expired (
+ m_cache.clock().now() - Tuning::liveCacheSecondsToLive);
+ map ["size"] = size ();
+ map ["hist"] = hops.histogram();
+ PropertyStream::Set set ("entries", map);
+ for (auto iter (m_cache.cbegin()); iter != m_cache.cend(); ++iter)
+ {
+ auto const& e (iter->second);
+ PropertyStream::Map item (set);
+ item ["hops"] = e.endpoint.hops;
+ item ["address"] = e.endpoint.address.to_string ();
+ std::stringstream ss;
+ ss << iter.when() - expired;
+ item ["expires"] = ss.str();
+ }
+}
+
+//------------------------------------------------------------------------------
+
+template
+void
+Livecache ::hops_t::shuffle()
+{
+ for (auto& list : m_lists)
+ {
+ std::vector > v;
+ v.reserve (list.size());
+ std::copy (list.begin(), list.end(),
+ std::back_inserter (v));
+ std::random_shuffle (v.begin(), v.end());
+ list.clear();
+ for (auto& e : v)
+ list.push_back (e);
+ }
+}
+
+template
+std::string
+Livecache ::hops_t::histogram() const
+{
+ std::stringstream ss;
+ for (auto i : m_hist)
+ ss <<
+ i <<
+ ((i < Tuning::maxHops + 1) ? ", " : "");
+ return ss.str();
+}
+
+template
+Livecache ::hops_t::hops_t (Allocator const& alloc)
+{
+ std::fill (m_hist.begin(), m_hist.end(), 0);
+}
+
+template
+void
+Livecache ::hops_t::insert (Element& e)
+{
+ assert (e.endpoint.hops >= 0 &&
+ e.endpoint.hops <= Tuning::maxHops + 1);
+ // This has security implications without a shuffle
+ m_lists [e.endpoint.hops].push_front (e);
+ ++m_hist [e.endpoint.hops];
+}
+
+template
+void
+Livecache ::hops_t::reinsert (Element& e, int hops)
+{
+ assert (hops >= 0 && hops <= Tuning::maxHops + 1);
+ list_type& list (m_lists [e.endpoint.hops]);
+ list.erase (list.iterator_to (e));
+ --m_hist [e.endpoint.hops];
+
+ e.endpoint.hops = hops;
+ insert (e);
+}
+
+template
+void
+Livecache ::hops_t::remove (Element& e)
+{
+ --m_hist [e.endpoint.hops];
+ list_type& list (m_lists [e.endpoint.hops]);
+ list.erase (list.iterator_to (e));
+}
+
}
}
diff --git a/src/ripple/peerfinder/impl/Logic.h b/src/ripple/peerfinder/impl/Logic.h
index 8294fc5976..1c0a1a0052 100644
--- a/src/ripple/peerfinder/impl/Logic.h
+++ b/src/ripple/peerfinder/impl/Logic.h
@@ -23,6 +23,14 @@
#include "Fixed.h"
#include "SlotImp.h"
+#include "handout.h"
+#include "ConnectHandouts.h"
+#include "RedirectHandouts.h"
+#include "SlotHandouts.h"
+
+#include "../../../beast/beast/container/aged_container_utility.h"
+
+#include