diff --git a/Builds/VisualStudio2012/RippleD.vcxproj.filters b/Builds/VisualStudio2012/RippleD.vcxproj.filters
index ef790e4ab7..ede0939f3b 100644
--- a/Builds/VisualStudio2012/RippleD.vcxproj.filters
+++ b/Builds/VisualStudio2012/RippleD.vcxproj.filters
@@ -834,9 +834,6 @@
[1] Ripple\validators\impl
-
- [1] Ripple\validators\impl
-
[1] Ripple\validators\impl
@@ -1059,6 +1056,9 @@
[1] Ripple\types\impl
+
+ [1] Ripple\validators\impl
+
diff --git a/src/ripple/validators/api/Manager.h b/src/ripple/validators/api/Manager.h
index 14e855fda7..c85cd49f7b 100644
--- a/src/ripple/validators/api/Manager.h
+++ b/src/ripple/validators/api/Manager.h
@@ -74,9 +74,11 @@ public:
//--------------------------------------------------------------------------
- /** Called when a validation with a proper signature is received.
- */
+ /** Called when a validation with a proper signature is received. */
virtual void receiveValidation (ReceivedValidation const& rv) = 0;
+
+ /** Called when a ledger is closed. */
+ virtual void ledgerClosed (RippleLedgerHash const& ledgerHash) = 0;
};
}
diff --git a/src/ripple/validators/impl/Logic.h b/src/ripple/validators/impl/Logic.h
index d9b2cf4383..3726f9e17a 100644
--- a/src/ripple/validators/impl/Logic.h
+++ b/src/ripple/validators/impl/Logic.h
@@ -27,26 +27,228 @@ enum
//------------------------------------------------------------------------------
+enum
+{
+ maxSizeBeforeSwap = 100
+};
+
+// Simple container swapping template
+template
+class AgedHistory
+{
+public:
+ typedef Container container_type;
+
+ AgedHistory()
+ : m_p1 (&m_c1)
+ , m_p2 (&m_c2)
+ {
+ }
+
+ AgedHistory (AgedHistory const& other)
+ : m_c1 (other.front())
+ , m_c2 (other.back())
+ , m_p1 (&m_c1)
+ , m_p2 (&m_c2)
+ {
+ }
+
+ AgedHistory& operator= (AgedHistory const& other)
+ {
+ m_c1 = other.front();
+ m_c2 = other.back();
+ m_p1 = &m_c1;
+ m_p2 = &m_c2;
+ return *this;
+ }
+
+ void swap () { std::swap (m_p1, m_p2); }
+
+ Container* operator-> () { return m_p1; }
+ Container const* operator-> () const { return m_p1; }
+
+ Container& front() { return *m_p1; }
+ Container const& front() const { return *m_p1; }
+ Container& back() { return *m_p2; }
+ Container const& back() const { return *m_p2; }
+
+private:
+ Container m_c1;
+ Container m_c2;
+ Container* m_p1;
+ Container* m_p2;
+};
+
+//------------------------------------------------------------------------------
+
+struct Ledger
+{
+ Ledger() : when (Time::getCurrentTime())
+ {
+ }
+
+ Time when;
+};
+
+typedef AgedHistory > Ledgers;
+
+// Information associated with each distinguishable validator
+struct Validator
+{
+ Validator ()
+ : refCount (0)
+ {
+ }
+
+ void receiveValidation (RippleLedgerHash const& ledgerHash)
+ {
+ typedef Ledgers::container_type::iterator iterator;
+
+ ++count->seen;
+
+ // If we already have it in the expected list, close it out
+ //
+ iterator iter (expected->find (ledgerHash));
+ if (iter != expected->end())
+ {
+ expected->erase (iter);
+ expected.back().erase (ledgerHash);
+ return;
+ }
+ else if ((iter = expected.back().find(ledgerHash)) !=
+ expected.back().end())
+ {
+ expected.back().erase (iter);
+ return;
+ }
+
+ // Ledger hasn't closed yet so put it in the received list
+ //
+ std::pair result (
+ received->emplace (ledgerHash, Ledger()));
+ bassert (result.second);
+ if (received->size() >= maxSizeBeforeSwap)
+ swap();
+ }
+
+ void ledgerClosed (RippleLedgerHash const& ledgerHash)
+ {
+ typedef Ledgers::container_type::iterator iterator;
+
+ ++count->closed;
+
+ // If the Validator already gave us the ledger
+ // then count it and remove it from both tables.
+ //
+ iterator iter (received->find (ledgerHash));
+ if (iter != received->end())
+ {
+ received->erase (iter);
+ received.back().erase (ledgerHash);
+ return;
+ }
+ else if ((iter = received.back().find (ledgerHash)) !=
+ received.back().end())
+ {
+ received.back().erase (iter);
+ return;
+ }
+
+ // We haven't seen this ledger hash from the
+ // validator yet so put it on the expected list
+ //
+ std::pair result (
+ expected->emplace (ledgerHash, Ledger ()));
+ bassert (result.second);
+ if (expected->size() >= maxSizeBeforeSwap)
+ swap();
+ }
+
+ void swap()
+ {
+ // Count anything in the old expected list as missing
+ count->missing += expected.back().size();
+
+ // Count anything in the old received list as orphaned
+ count->orphans += received.back().size();
+
+ // Rotate and clear
+ count.swap();
+ expected.swap();
+ received.swap();
+ count->clear();
+ expected->clear();
+ received->clear();
+ }
+
+ struct Count
+ {
+ Count()
+ : closed (0)
+ , seen (0)
+ , missing (0)
+ , orphans (0)
+ {
+ }
+
+ void clear ()
+ {
+ *this = Count();
+ }
+
+ // How many ledgers we've seen
+ std::size_t closed;
+
+ // How many validation's we've seen
+ std::size_t seen;
+
+ // Estimate of validation's that were missed
+ std::size_t missing;
+
+ // Estimate of validations not belonging to any ledger
+ std::size_t orphans;
+ };
+
+ int refCount;
+
+ AgedHistory count;
+ Ledgers received;
+ Ledgers expected;
+};
+
+//------------------------------------------------------------------------------
+
// Encapsulates the logic for creating the chosen validators.
// This is a separate class to facilitate the unit tests.
//
class Logic
{
public:
- // Information associated with each distinguishable validator
- //
- struct ValidatorInfo
- {
- ValidatorInfo ()
- : refCount (0)
- {
- }
-
- int refCount;
- };
+ //--------------------------------------------------------------------------
typedef boost::unordered_map <
- RipplePublicKey, ValidatorInfo, RipplePublicKey::hasher> MapType;
+ RipplePublicKey, Validator,
+ RipplePublicKey::hasher> MapType;
+
+ // The master in-memory database of Validator, indexed by all the
+ // possible things that we need to care about, and even some that we don't.
+ //
+ /*
+ typedef boost::multi_index_container <
+ Validator, boost::multi_index::indexed_by <
+
+ boost::multi_index::hashed_unique <
+ BOOST_MULTI_INDEX_MEMBER(Logic::Validator,UniqueID,uniqueID)>,
+
+ boost::multi_index::hashed_unique <
+ BOOST_MULTI_INDEX_MEMBER(Logic::Validator,IPEndpoint,endpoint),
+ Connectible::HashAddress>
+ >
+ > ValidationsMap;
+ */
+
+ //--------------------------------------------------------------------------
struct State
{
@@ -62,6 +264,18 @@ public:
ChosenList::Ptr m_chosenList;
SharedState m_state;
+ // Used to filter duplicate public keys
+ //
+ typedef AgedHistory > SeenPublicKeys;
+ SeenPublicKeys m_seenPublicKeys;
+
+ // Used to filter duplicate ledger hashes
+ //
+ typedef AgedHistory > SeenLedgerHashes;
+ SeenLedgerHashes m_seenLedgerHashes;
+
//----------------------------------------------------------------------
Logic (Store& store, Journal journal = Journal ())
@@ -120,8 +334,8 @@ public:
{
Source::Info const& info (list.getReference (i));
std::pair result (
- state->map.emplace (info.publicKey, ValidatorInfo ()));
- ValidatorInfo& validatorInfo (result.first->second);
+ state->map.emplace (info.publicKey, Validator ()));
+ Validator& validatorInfo (result.first->second);
++validatorInfo.refCount;
if (result.second)
{
@@ -141,7 +355,7 @@ public:
Source::Info const& info (list.getReference (i));
MapType::iterator iter (state->map.find (info.publicKey));
bassert (iter != state->map.end ());
- ValidatorInfo& validatorInfo (iter->second);
+ Validator& validatorInfo (iter->second);
if (--validatorInfo.refCount == 0)
{
// Last reference removed
@@ -310,6 +524,7 @@ public:
{
Json::Value result (Json::objectValue);
+#if 0
Json::Value entries (Json::arrayValue);
ChosenList::Ptr list (m_chosenList);
if (list != nullptr)
@@ -327,6 +542,57 @@ public:
}
result ["chosen_list"] = entries;
+ {
+ SharedState::ConstAccess state (m_state);
+ std::size_t count (0);
+ result ["validators"] = state->map.size();
+ for (MapType::const_iterator iter (state->map.begin());
+ iter != state->map.end(); ++iter)
+ count += iter->second.map.size();
+ result ["signatures"] = count;
+ }
+#else
+ Json::Value entries (Json::arrayValue);
+ {
+ SharedState::ConstAccess state (m_state);
+ result ["count"] = int(state->map.size());
+ for (MapType::const_iterator iter (state->map.begin());
+ iter != state->map.end(); ++iter)
+ {
+ Validator const& v (iter->second);
+ Json::Value entry (Json::objectValue);
+
+ std::size_t const closed (
+ v.count->closed + v.count.back().closed);
+
+ std::size_t const seen (
+ v.count->seen + v.count.back().seen);
+
+ std::size_t const missing (
+ v.count->missing + v.count.back().missing);
+
+ std::size_t const orphans (
+ v.count->orphans + v.count.back().orphans);
+
+ entry ["public"] = iter->first.to_string();
+ entry ["closed"] = int(closed);
+ entry ["seen"] = int(seen);
+ entry ["missing"] = int(missing);
+ entry ["orphans"] = int(orphans);
+
+ if (closed > 0)
+ {
+ int const percent (
+ ((seen - missing) * 100) / closed);
+ entry ["percent"] = percent;
+ }
+
+ entries.append (entry);
+ }
+ }
+ result ["validators"] = entries;
+
+#endif
return result;
}
@@ -367,25 +633,71 @@ public:
// Ripple interface
//
- // Called when we receive a validation from a peer.
+ // VFALCO NOTE We cannot make any assumptions about the quality of the
+ // information being passed into the logic. Specifically,
+ // we can expect to see duplicate ledgerClose, and duplicate
+ // receiveValidation. Therefore, we must program defensively
+ // to prevent undefined behavior
+
+ // Called when we receive a signed validation
//
void receiveValidation (ReceivedValidation const& rv)
{
+ // Filter duplicates
+ {
+ std::pair result (
+ m_seenPublicKeys->emplace (rv.publicKey));
+ if (m_seenPublicKeys->size() > maxSizeBeforeSwap)
+ {
+ m_seenPublicKeys.swap();
+ m_seenPublicKeys->clear();
+ }
+ if (! result.second)
+ return;
+ }
+
+ SharedState::Access state (m_state);
#if 0
- MapType::iterator iter (state->map.find (rv.signerPublicKeyHash));
+ MapType::iterator iter (state->map.find (rv.publicKey));
if (iter != state->map.end ())
{
- // Exists
- //ValidatorInfo& validatorInfo (iter->value ());
- }
- else
- {
- // New
- //ValidatorInfo& validatorInfo (state->map.insert (rv.signerPublicKeyHash));
+ Validator& v (iter->second);
+ v.receiveValidation (rv.ledgerHash);
}
+#else
+ std::pair result (
+ state->map.emplace (rv.publicKey, Validator()));
+ Validator& v (result.first->second);
+ v.receiveValidation (rv.ledgerHash);
#endif
}
+ // Called when a ledger is closed
+ //
+ void ledgerClosed (RippleLedgerHash const& ledgerHash)
+ {
+ // Filter duplicates
+ {
+ std::pair result (
+ m_seenLedgerHashes->emplace (ledgerHash));
+ if (m_seenLedgerHashes->size() > maxSizeBeforeSwap)
+ {
+ m_seenLedgerHashes.swap();
+ m_seenLedgerHashes->clear();
+ }
+ if (! result.second)
+ return;
+ }
+
+ SharedState::Access state (m_state);
+ for (MapType::iterator iter (state->map.begin());
+ iter != state->map.end(); ++iter)
+ {
+ Validator& v (iter->second);
+ v.ledgerClosed (ledgerHash);
+ }
+ }
+
// Returns `true` if the public key hash is contained in the Chosen List.
//
bool isTrustedPublicKeyHash (RipplePublicKeyHash const& publicKeyHash)
@@ -393,7 +705,6 @@ public:
return m_chosenList->containsPublicKeyHash (publicKeyHash);
}
- //
//
//----------------------------------------------------------------------
};
diff --git a/src/ripple/validators/impl/Manager.cpp b/src/ripple/validators/impl/Manager.cpp
index d74f63eea5..a7390aab1c 100644
--- a/src/ripple/validators/impl/Manager.cpp
+++ b/src/ripple/validators/impl/Manager.cpp
@@ -191,7 +191,7 @@ public:
{
#if RIPPLE_USE_NEW_VALIDATORS
bassert (! isStopping());
- m_thread.call (&Logic::add, &m_logic, source);
+ //m_thread.call (&Logic::add, &m_logic, source);
#else
delete source;
#endif
@@ -201,7 +201,7 @@ public:
{
#if RIPPLE_USE_NEW_VALIDATORS
bassert (! isStopping());
- m_thread.call (&Logic::addStatic, &m_logic, source);
+ //m_thread.call (&Logic::addStatic, &m_logic, source);
#else
delete source;
#endif
@@ -215,6 +215,14 @@ public:
#endif
}
+ void ledgerClosed (RippleLedgerHash const& ledgerHash)
+ {
+#if RIPPLE_USE_NEW_VALIDATORS
+ if (! isStopping())
+ m_thread.call (&Logic::ledgerClosed, &m_logic, ledgerHash);
+#endif
+ }
+
//--------------------------------------------------------------------------
void onDeadlineTimer (DeadlineTimer& timer)
diff --git a/src/ripple/validators/ripple_validators.cpp b/src/ripple/validators/ripple_validators.cpp
index b42f9b3586..de6f2eb539 100644
--- a/src/ripple/validators/ripple_validators.cpp
+++ b/src/ripple/validators/ripple_validators.cpp
@@ -11,6 +11,11 @@
#include "beast/modules/beast_core/system/BeforeBoost.h"
#include
#include
+#include
+#include
+#include
+
+#include
#include "beast/modules/beast_asio/beast_asio.h"
#include "beast/modules/beast_sqdb/beast_sqdb.h"
diff --git a/src/ripple_app/ledger/LedgerMaster.cpp b/src/ripple_app/ledger/LedgerMaster.cpp
index 0c0cacb6e7..fbc5c2f409 100644
--- a/src/ripple_app/ledger/LedgerMaster.cpp
+++ b/src/ripple_app/ledger/LedgerMaster.cpp
@@ -453,32 +453,45 @@ void LedgerMaster::setFullLedger (Ledger::pointer ledger, bool isSynchronous, bo
mLedgerHistory.addLedger(ledger);
ledger->setFull();
- ScopedLockType ml (mLock, __FILE__, __LINE__);
-
- mCompleteLedgers.setValue (ledger->getLedgerSeq ());
-
- ledger->pendSaveValidated (isSynchronous, isCurrent);
-
- if (!mValidLedger || (ledger->getLedgerSeq() > mValidLedger->getLedgerSeq()))
- mValidLedger = ledger;
- if (!mPubLedger)
{
- mPubLedger = ledger;
- getApp().getOrderBookDB().setup(ledger);
- }
+ ScopedLockType ml (mLock, __FILE__, __LINE__);
- if ((ledger->getLedgerSeq () != 0) && mCompleteLedgers.hasValue (ledger->getLedgerSeq () - 1))
- {
- // we think we have the previous ledger, double check
- Ledger::pointer prevLedger = getLedgerBySeq (ledger->getLedgerSeq () - 1);
+ mCompleteLedgers.setValue (ledger->getLedgerSeq ());
- if (!prevLedger || (prevLedger->getHash () != ledger->getParentHash ()))
+ ledger->pendSaveValidated (isSynchronous, isCurrent);
+
+ if (!mValidLedger || (ledger->getLedgerSeq() > mValidLedger->getLedgerSeq()))
+ mValidLedger = ledger;
+ if (!mPubLedger)
{
- WriteLog (lsWARNING, LedgerMaster) << "Acquired ledger invalidates previous ledger: " <<
- (prevLedger ? "hashMismatch" : "missingLedger");
- fixMismatch (ledger);
+ mPubLedger = ledger;
+ getApp().getOrderBookDB().setup(ledger);
+ }
+
+ if ((ledger->getLedgerSeq () != 0) && mCompleteLedgers.hasValue (ledger->getLedgerSeq () - 1))
+ {
+ // we think we have the previous ledger, double check
+ Ledger::pointer prevLedger = getLedgerBySeq (ledger->getLedgerSeq () - 1);
+
+ if (!prevLedger || (prevLedger->getHash () != ledger->getParentHash ()))
+ {
+ WriteLog (lsWARNING, LedgerMaster) << "Acquired ledger invalidates previous ledger: " <<
+ (prevLedger ? "hashMismatch" : "missingLedger");
+ fixMismatch (ledger);
+ }
}
}
+
+ //--------------------------------------------------------------------------
+ //
+#if RIPPLE_USE_NEW_VALIDATORS
+ {
+ if (isCurrent)
+ getApp ().getValidators ().ledgerClosed (ledger->getHash());
+ }
+#endif
+ //
+ //--------------------------------------------------------------------------
}
void LedgerMaster::checkAccept (uint256 const& hash)
diff --git a/src/ripple_app/ripple_app_pt1.cpp b/src/ripple_app/ripple_app_pt1.cpp
index 8eb8dae721..a0ef926fea 100644
--- a/src/ripple_app/ripple_app_pt1.cpp
+++ b/src/ripple_app/ripple_app_pt1.cpp
@@ -13,6 +13,8 @@
#include "ripple_app.h"
+#include "../ripple/validators/ripple_validators.h"
+
namespace ripple
{