diff --git a/Builds/VisualStudio2015/RippleD.vcxproj b/Builds/VisualStudio2015/RippleD.vcxproj
index f83e0351ad..764dc947d4 100644
--- a/Builds/VisualStudio2015/RippleD.vcxproj
+++ b/Builds/VisualStudio2015/RippleD.vcxproj
@@ -1258,6 +1258,10 @@
+
+ True
+ True
+
True
True
@@ -1308,10 +1312,6 @@
-
- True
- True
-
True
True
@@ -1336,6 +1336,10 @@
+
+ True
+ True
+
True
True
diff --git a/Builds/VisualStudio2015/RippleD.vcxproj.filters b/Builds/VisualStudio2015/RippleD.vcxproj.filters
index a93564270b..09f6028f03 100644
--- a/Builds/VisualStudio2015/RippleD.vcxproj.filters
+++ b/Builds/VisualStudio2015/RippleD.vcxproj.filters
@@ -1857,6 +1857,9 @@
ripple\app\ledger
+
+ ripple\app\main
+
ripple\app\main
@@ -1908,9 +1911,6 @@
ripple\app\misc
-
- ripple\app\misc
-
ripple\app\misc
@@ -1935,6 +1935,9 @@
ripple\app\misc\impl
+
+ ripple\app\misc\impl
+
ripple\app\misc\impl
diff --git a/src/ripple/app/ledger/impl/LedgerConsensusImp.cpp b/src/ripple/app/ledger/impl/LedgerConsensusImp.cpp
index 1dd4f68b8a..a4202eb7b2 100644
--- a/src/ripple/app/ledger/impl/LedgerConsensusImp.cpp
+++ b/src/ripple/app/ledger/impl/LedgerConsensusImp.cpp
@@ -1401,11 +1401,28 @@ void LedgerConsensusImp::takeInitialPosition (
&& ((mPreviousLedger->info().seq % 256) == 0))
{
// previous ledger was flag ledger, add pseudo-transactions
- ValidationSet parentSet = app_.getValidations().getValidations (
- mPreviousLedger->info().parentHash);
- m_feeVote.doVoting (mPreviousLedger, parentSet, initialSet);
- app_.getAmendmentTable ().doVoting (
- mPreviousLedger, parentSet, initialSet);
+ auto const validations =
+ app_.getValidations().getValidations (
+ mPreviousLedger->info().parentHash);
+
+ auto const count = std::count_if (
+ validations.begin(), validations.end(),
+ [](auto const& v)
+ {
+ return v.second->isTrusted();
+ });
+
+ if (count >= ledgerMaster_.getMinValidations())
+ {
+ m_feeVote.doVoting (
+ mPreviousLedger,
+ validations,
+ initialSet);
+ app_.getAmendmentTable ().doVoting (
+ mPreviousLedger,
+ validations,
+ initialSet);
+ }
}
// Set should be immutable snapshot
diff --git a/src/ripple/app/main/Amendments.cpp b/src/ripple/app/main/Amendments.cpp
new file mode 100644
index 0000000000..daf6de6b87
--- /dev/null
+++ b/src/ripple/app/main/Amendments.cpp
@@ -0,0 +1,48 @@
+//------------------------------------------------------------------------------
+/*
+ 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
+#include
+
+namespace ripple {
+
+namespace detail {
+
+/** Amendments that this server supports and enables by default */
+std::vector
+preEnabledAmendments ()
+{
+ return
+ {
+ };
+}
+
+/** Amendments that this server supports, but doesn't enable by default */
+std::vector
+supportedAmendments ()
+{
+ return
+ {
+ };
+}
+
+}
+
+}
+
diff --git a/src/ripple/app/main/Application.cpp b/src/ripple/app/main/Application.cpp
index aad3416824..387be6fad5 100644
--- a/src/ripple/app/main/Application.cpp
+++ b/src/ripple/app/main/Application.cpp
@@ -210,6 +210,15 @@ public:
}
};
+
+/** Amendments that this server supports and enables by default */
+std::vector
+preEnabledAmendments ();
+
+/** Amendments that this server supports, but doesn't enable by default */
+std::vector
+supportedAmendments ();
+
} // detail
//------------------------------------------------------------------------------
@@ -453,10 +462,6 @@ public:
, serverHandler_ (make_ServerHandler (*this, *m_networkOPs, get_io_service (),
*m_jobQueue, *m_networkOPs, *m_resourceManager, *m_collectorManager))
- , m_amendmentTable (make_AmendmentTable
- (weeks(2), MAJORITY_FRACTION,
- logs_->journal("AmendmentTable")))
-
, mFeeTrack (std::make_unique(logs_->journal("LoadManager")))
, mHashRouter (std::make_unique(
@@ -962,8 +967,23 @@ void ApplicationImp::setup()
if (!config_->RUN_STANDALONE)
updateTables ();
- m_amendmentTable->addInitial (
- config_->section (SECTION_AMENDMENTS));
+ // Configure the amendments the server supports
+ {
+ Section supportedAmendments ("Supported Amendments");
+ supportedAmendments.append (detail::supportedAmendments ());
+
+ Section enabledAmendments = config_->section (SECTION_AMENDMENTS);
+ enabledAmendments.append (detail::preEnabledAmendments ());
+
+ m_amendmentTable = make_AmendmentTable (
+ weeks(2),
+ MAJORITY_FRACTION,
+ supportedAmendments,
+ enabledAmendments,
+ config_->section (SECTION_VETO_AMENDMENTS),
+ logs_->journal("Amendments"));
+ }
+
Pathfinder::initPathTable();
m_ledgerMaster->setMinValidations (
diff --git a/src/ripple/app/misc/AmendmentTable.h b/src/ripple/app/misc/AmendmentTable.h
index c83f5277f7..c3ba969fdf 100644
--- a/src/ripple/app/misc/AmendmentTable.h
+++ b/src/ripple/app/misc/AmendmentTable.h
@@ -22,135 +22,11 @@
#include
#include
+#include
#include
namespace ripple {
-/** The status of all amendments requested in a given window. */
-class AmendmentSet
-{
-public:
- NetClock::time_point mCloseTime;
- int mTrustedValidations; // number of trusted validations
- hash_map mVotes; // yes votes by amendment
-
- AmendmentSet (NetClock::time_point ct) : mCloseTime (ct), mTrustedValidations (0)
- {
- ;
- }
-
- void addVoter ()
- {
- ++mTrustedValidations;
- }
-
- void addVote (uint256 const& amendment)
- {
- ++mVotes[amendment];
- }
-
- int count (uint256 const& amendment)
- {
- auto const& it = mVotes.find (amendment);
- return (it == mVotes.end()) ? 0 : it->second;
- }
-};
-
-/** 256-bit Id and human friendly name of an amendment.
-*/
-class AmendmentName final
-{
-private:
- uint256 mId;
- // Keep the hex string around for error reporting
- std::string mHexString;
- std::string mFriendlyName;
- bool mValid{false};
-
-public:
- AmendmentName () = default;
- AmendmentName (AmendmentName const& rhs) = default;
- // AmendmentName (AmendmentName&& rhs) = default; // MSVS not supported
- AmendmentName (uint256 const& id, std::string friendlyName)
- : mId (id), mFriendlyName (std::move (friendlyName)), mValid (true)
- {
- }
- AmendmentName (std::string id, std::string friendlyName)
- : mHexString (std::move (id)), mFriendlyName (std::move (friendlyName))
- {
- mValid = mId.SetHex (mHexString);
- }
- bool valid () const
- {
- return mValid;
- }
- uint256 const& id () const
- {
- return mId;
- }
- std::string const& hexString () const
- {
- return mHexString;
- }
- std::string const& friendlyName () const
- {
- return mFriendlyName;
- }
-};
-
-/** Current state of an amendment.
- Tells if a amendment is supported, enabled or vetoed. A vetoed amendment
- means the node will never announce its support.
-*/
-class AmendmentState
-{
-public:
- bool mVetoed{false}; // We don't want this amendment enabled
- bool mEnabled{false};
- bool mSupported{false};
- bool mDefault{false}; // Include in genesis ledger
-
- std::string mFriendlyName;
-
- AmendmentState () = default;
-
- void setVeto ()
- {
- mVetoed = true;
- }
- void setDefault ()
- {
- mDefault = true;
- }
- bool isDefault ()
- {
- return mDefault;
- }
- bool isSupported ()
- {
- return mSupported;
- }
- bool isVetoed ()
- {
- return mVetoed;
- }
- bool isEnabled ()
- {
- return mEnabled;
- }
- std::string const& getFiendlyName ()
- {
- return mFriendlyName;
- }
- void setFriendlyName (std::string const& n)
- {
- mFriendlyName = n;
- }
-};
-
-class Section;
-
-
/** The amendment table stores the list of enabled and potential amendments.
Individuals amendments are voted on by validators during the consensus
process.
@@ -158,30 +34,9 @@ class Section;
class AmendmentTable
{
public:
- /** Create a new AmendmentTable.
+ virtual ~AmendmentTable() = default;
- @param majorityTime the number of seconds an amendment must hold a majority
- before we're willing to vote yes on it.
- @param majorityFraction ratio, out of 256, of servers that must say
- they want an amendment before we consider it to
- have a majority.
- @param journal
- */
-
- virtual ~AmendmentTable() { }
-
- /**
- @param section the config section of initial amendments
- */
- virtual void addInitial (Section const& section) = 0;
-
- /** Add an amendment to the AmendmentTable
-
- @throw will throw if the name parameter is not valid
- */
- virtual void addKnown (AmendmentName const& name) = 0;
-
- virtual uint256 get (std::string const& name) = 0;
+ virtual uint256 find (std::string const& name) = 0;
virtual bool veto (uint256 const& amendment) = 0;
virtual bool unVeto (uint256 const& amendment) = 0;
@@ -192,15 +47,6 @@ public:
virtual bool isEnabled (uint256 const& amendment) = 0;
virtual bool isSupported (uint256 const& amendment) = 0;
- /** Enable only the specified amendments.
- Other amendments in the table will be set to disabled.
- */
- virtual void setEnabled (const std::vector& amendments) = 0;
- /** Support only the specified amendments.
- Other amendments in the table will be set to unsupported.
- */
- virtual void setSupported (const std::vector& amendments) = 0;
-
virtual Json::Value getJson (int) = 0;
/** Returns a Json::objectValue. */
@@ -221,22 +67,23 @@ public:
needValidatedLedger (LedgerIndex seq) = 0;
virtual void
- doValidatedLedger (LedgerIndex ledgerSeq, enabledAmendments_t enabled) = 0;
+ doValidatedLedger (
+ LedgerIndex ledgerSeq,
+ std::set const& enabled) = 0;
// Called by the consensus code when we need to
// inject pseudo-transactions
virtual std::map
doVoting (
NetClock::time_point closeTime,
- enabledAmendments_t const& enabledAmendments,
+ std::set const& enabledAmendments,
majorityAmendments_t const& majorityAmendments,
ValidationSet const& valSet) = 0;
// Called by the consensus code when we need to
// add feature entries to a validation
virtual std::vector
- doValidation (enabledAmendments_t const&) = 0;
-
+ doValidation (std::set const& enabled) = 0;
// The two function below adapt the API callers expect to the
// internal amendment table API. This allows the amendment
@@ -255,7 +102,6 @@ public:
STVector256 (sfAmendments, ourAmendments));
}
-
void
doVoting (
std::shared_ptr const& lastClosedLedger,
@@ -269,6 +115,7 @@ public:
getMajorityAmendments(*lastClosedLedger),
parentValidations);
+#if RIPPLE_PROPOSE_AMENDMENTS
// Inject appropriate pseudo-transactions
for (auto const& it : actions)
{
@@ -286,14 +133,14 @@ public:
Serializer s;
amendTx.add (s);
- #if ! RIPPLE_PROPOSE_AMENDMENTS
- return;
- #endif
-
- uint256 txID = amendTx.getTransactionID();
- auto tItem = std::make_shared (txID, s.peekData());
- initialPosition->addGiveItem (tItem, true, false);
+ initialPosition->addGiveItem (
+ std::make_shared (
+ amendTx.getTransactionID(),
+ s.peekData()),
+ true,
+ false);
}
+#endif
}
};
@@ -301,6 +148,9 @@ public:
std::unique_ptr make_AmendmentTable (
std::chrono::seconds majorityTime,
int majorityFraction,
+ Section const& supported,
+ Section const& enabled,
+ Section const& vetoed,
beast::Journal journal);
} // ripple
diff --git a/src/ripple/app/misc/AmendmentTableImpl.cpp b/src/ripple/app/misc/AmendmentTableImpl.cpp
deleted file mode 100644
index 72f079d2c3..0000000000
--- a/src/ripple/app/misc/AmendmentTableImpl.cpp
+++ /dev/null
@@ -1,558 +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.
-*/
-//==============================================================================
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-namespace ripple {
-/** Track the list of "amendments"
-
- An "amendment" is an option that can affect transaction processing rules.
- Amendments are proposed and then adopted or rejected by the network. An
- Amendment is uniquely identified by its AmendmentID, a 256-bit key.
-*/
-
-class AmendmentTableImpl final : public AmendmentTable
-{
-protected:
- using amendmentMap_t = hash_map;
- using amendmentList_t = hash_set;
-
- std::mutex mLock;
-
- amendmentMap_t m_amendmentMap;
- std::uint32_t m_lastUpdateSeq;
-
- std::chrono::seconds m_majorityTime; // Seconds an amendment must hold a majority
- int mMajorityFraction; // 256 = 100%
- beast::Journal m_journal;
-
- AmendmentState& getCreate (uint256 const& amendment);
- AmendmentState* getExisting (uint256 const& amendment);
- void setJson (Json::Value& v, const AmendmentState&);
-
-public:
- AmendmentTableImpl (
- std::chrono::seconds majorityTime,
- int majorityFraction,
- beast::Journal journal)
- : m_lastUpdateSeq (0)
- , m_majorityTime (majorityTime)
- , mMajorityFraction (majorityFraction)
- , m_journal (journal)
- {
- }
-
- void addInitial (Section const& section) override;
-
- void addKnown (AmendmentName const& name) override;
-
- uint256 get (std::string const& name) override;
-
- bool veto (uint256 const& amendment) override;
- bool unVeto (uint256 const& amendment) override;
-
- bool enable (uint256 const& amendment) override;
- bool disable (uint256 const& amendment) override;
-
- bool isEnabled (uint256 const& amendment) override;
- bool isSupported (uint256 const& amendment) override;
-
- void setEnabled (const std::vector& amendments) override;
- void setSupported (const std::vector& amendments) override;
-
- Json::Value getJson (int) override;
- Json::Value getJson (uint256 const&) override;
-
- bool needValidatedLedger (LedgerIndex seq) override;
-
- void doValidatedLedger (LedgerIndex seq,
- enabledAmendments_t enabled) override;
-
- std::vector
- doValidation (enabledAmendments_t const& enabledAmendments)
- override;
-
- std::map doVoting (NetClock::time_point closeTime,
- enabledAmendments_t const& enabledAmendments,
- majorityAmendments_t const& majorityAmendments,
- ValidationSet const& validations) override;
-
- amendmentList_t getVetoed();
- amendmentList_t getEnabled();
-
- // Amendments we support, do not veto, and are not enabled
- amendmentList_t getDesired (enabledAmendments_t const&);
-};
-
-namespace detail
-{
-/** preEnabledAmendments is a static collection of amendments that are are
- enabled at build time.
-
- Add amendments to this collection at build time to enable them on this
- server.
-*/
-
-std::vector const preEnabledAmendments;
-}
-
-void
-AmendmentTableImpl::addInitial (Section const& section)
-{
- for (auto const& a : detail::preEnabledAmendments)
- {
- if (!a.valid ())
- {
- std::string const errorMsg =
- (boost::format (
- "preEnabledAmendments contains an invalid hash (expected "
- "a hex number). Value was: %1%") %
- a.hexString ()).str ();
- Throw (errorMsg);
- }
- }
-
- std::vector toAdd (detail::preEnabledAmendments);
-
- {
- // add the amendments from the config file
- int const numExpectedToks = 2;
- for (auto const& line : section.lines ())
- {
- boost::tokenizer<> tokenizer (line);
- std::vector tokens (tokenizer.begin (),
- tokenizer.end ());
- if (tokens.size () != numExpectedToks)
- {
- std::string const errorMsg =
- (boost::format (
- "The %1% section in the config file expects %2% "
- "items. Found %3%. Line was: %4%") %
- SECTION_AMENDMENTS % numExpectedToks % tokens.size () %
- line).str ();
- Throw (errorMsg);
- }
-
- toAdd.emplace_back (std::move (tokens[0]), std::move (tokens[1]));
- if (!toAdd.back ().valid ())
- {
- std::string const errorMsg =
- (boost::format (
- "%1% is not a valid hash. Expected a hex "
- "number. In config setcion: %2%. Line was: "
- "%3%") %
- toAdd.back ().hexString () % SECTION_AMENDMENTS %
- line).str ();
- Throw (errorMsg);
- }
- }
- }
-
- for (auto const& a : toAdd)
- {
- addKnown (a);
- enable (a.id ());
- }
-}
-
-AmendmentState&
-AmendmentTableImpl::getCreate (uint256 const& amendmentHash)
-{
- // call with the mutex held
- auto iter (m_amendmentMap.find (amendmentHash));
-
- if (iter == m_amendmentMap.end())
- {
- AmendmentState& amendment = m_amendmentMap[amendmentHash];
- return amendment;
- }
-
- return iter->second;
-}
-
-AmendmentState*
-AmendmentTableImpl::getExisting (uint256 const& amendmentHash)
-{
- // call with the mutex held
- auto iter (m_amendmentMap.find (amendmentHash));
-
- if (iter == m_amendmentMap.end())
- return nullptr;
-
- return & (iter->second);
-}
-
-uint256
-AmendmentTableImpl::get (std::string const& name)
-{
- std::lock_guard sl (mLock);
-
- for (auto const& e : m_amendmentMap)
- {
- if (name == e.second.mFriendlyName)
- return e.first;
- }
-
- return uint256 ();
-}
-
-void
-AmendmentTableImpl::addKnown (AmendmentName const& name)
-{
- if (!name.valid ())
- {
- std::string const errorMsg =
- (boost::format (
- "addKnown was given an invalid hash (expected a hex number). "
- "Value was: %1%") %
- name.hexString ()).str ();
- Throw (errorMsg);
- }
-
- std::lock_guard sl (mLock);
- AmendmentState& amendment = getCreate (name.id ());
-
- if (!name.friendlyName ().empty ())
- amendment.setFriendlyName (name.friendlyName ());
-
- amendment.mVetoed = false;
- amendment.mSupported = true;
-}
-
-bool
-AmendmentTableImpl::veto (uint256 const& amendment)
-{
- std::lock_guard sl (mLock);
- AmendmentState& s = getCreate (amendment);
-
- if (s.mVetoed)
- return false;
-
- s.mVetoed = true;
- return true;
-}
-
-bool
-AmendmentTableImpl::unVeto (uint256 const& amendment)
-{
- std::lock_guard sl (mLock);
- AmendmentState* s = getExisting (amendment);
-
- if (!s || !s->mVetoed)
- return false;
-
- s->mVetoed = false;
- return true;
-}
-
-bool
-AmendmentTableImpl::enable (uint256 const& amendment)
-{
- std::lock_guard sl (mLock);
- AmendmentState& s = getCreate (amendment);
-
- if (s.mEnabled)
- return false;
-
- s.mEnabled = true;
- return true;
-}
-
-bool
-AmendmentTableImpl::disable (uint256 const& amendment)
-{
- std::lock_guard sl (mLock);
- AmendmentState* s = getExisting (amendment);
-
- if (!s || !s->mEnabled)
- return false;
-
- s->mEnabled = false;
- return true;
-}
-
-bool
-AmendmentTableImpl::isEnabled (uint256 const& amendment)
-{
- std::lock_guard sl (mLock);
- AmendmentState* s = getExisting (amendment);
- return s && s->mEnabled;
-}
-
-bool
-AmendmentTableImpl::isSupported (uint256 const& amendment)
-{
- std::lock_guard sl (mLock);
- AmendmentState* s = getExisting (amendment);
- return s && s->mSupported;
-}
-
-AmendmentTableImpl::amendmentList_t
-AmendmentTableImpl::getVetoed ()
-{
- amendmentList_t ret;
- std::lock_guard sl (mLock);
- for (auto const& e : m_amendmentMap)
- {
- if (e.second.mVetoed)
- ret.insert (e.first);
- }
- return ret;
-}
-
-AmendmentTableImpl::amendmentList_t
-AmendmentTableImpl::getEnabled ()
-{
- amendmentList_t ret;
- std::lock_guard sl (mLock);
- for (auto const& e : m_amendmentMap)
- {
- if (e.second.mEnabled)
- ret.insert (e.first);
- }
- return ret;
-}
-
-AmendmentTableImpl::amendmentList_t
-AmendmentTableImpl::getDesired (enabledAmendments_t const& enabled)
-{
- amendmentList_t ret;
- std::lock_guard sl (mLock);
-
- for (auto const& e : m_amendmentMap)
- {
- if (e.second.mSupported && ! e.second.mVetoed &&
- (enabled.count (e.first) == 0))
- ret.insert (e.first);
- }
-
- return ret;
-}
-
-void
-AmendmentTableImpl::setEnabled (const std::vector& amendments)
-{
- std::lock_guard sl (mLock);
- for (auto& e : m_amendmentMap)
- {
- e.second.mEnabled = false;
- }
- for (auto const& e : amendments)
- {
- m_amendmentMap[e].mEnabled = true;
- }
-}
-
-void
-AmendmentTableImpl::setSupported (const std::vector& amendments)
-{
- std::lock_guard sl (mLock);
- for (auto &e : m_amendmentMap)
- {
- e.second.mSupported = false;
- }
- for (auto const& e : amendments)
- {
- m_amendmentMap[e].mSupported = true;
- }
-}
-
-std::vector
-AmendmentTableImpl::doValidation (
- enabledAmendments_t const& enabledAmendments)
-{
- auto lAmendments = getDesired (enabledAmendments);
-
- if (lAmendments.empty())
- return {};
-
- std::vector amendments (lAmendments.begin(), lAmendments.end());
- std::sort (amendments.begin (), amendments.end ());
-
- return amendments;
-}
-
-std::map
-AmendmentTableImpl::doVoting (
- NetClock::time_point closeTime,
- enabledAmendments_t const& enabledAmendments,
- majorityAmendments_t const& majorityAmendments,
- ValidationSet const& valSet)
-{
- // LCL must be flag ledger
- //assert((lastClosedLedger->info().seq % 256) == 0);
-
- AmendmentSet amendmentSet (closeTime);
-
- // process validations for ledger before flag ledger
- for (auto const& entry : valSet)
- {
- auto const& val = *entry.second;
-
- if (val.isTrusted ())
- {
- amendmentSet.addVoter ();
- if (val.isFieldPresent (sfAmendments))
- {
- for (auto const& amendment : val.getFieldV256 (sfAmendments))
- {
- amendmentSet.addVote (amendment);
- }
- }
- }
- }
- int threshold =
- (amendmentSet.mTrustedValidations * mMajorityFraction + 255) / 256;
-
- if (m_journal.trace)
- m_journal.trace <<
- amendmentSet.mTrustedValidations << " trusted validations, threshold is "
- << threshold;
-
- // Map of amendments to the action to be taken
- // for each one. The action is the value of the
- // flags in the pseudo-transaction
- std::map actions;
-
- {
- std::lock_guard sl (mLock);
-
- // process all amendments we know of
- for (auto const& entry : m_amendmentMap)
- {
- bool const hasValMajority = amendmentSet.count (entry.first) >= threshold;
-
- NetClock::time_point majorityTime = {};
- auto const it = majorityAmendments.find (entry.first);
- if (it != majorityAmendments.end ())
- majorityTime = it->second;
-
- // FIXME: Add logging here
- if (enabledAmendments.count (entry.first) != 0)
- {
- // Already enabled, nothing to do
- }
- else if (hasValMajority && (majorityTime == NetClock::time_point{})
- && (! entry.second.mVetoed))
- {
- // Ledger says no majority, validators say yes
- actions[entry.first] = tfGotMajority;
- }
- else if (! hasValMajority && (majorityTime != NetClock::time_point{}))
- {
- // Ledger says majority, validators say no
- actions[entry.first] = tfLostMajority;
- }
- else if ((majorityTime != NetClock::time_point{}) &&
- ((majorityTime + m_majorityTime) <= closeTime) &&
- ! entry.second.mVetoed)
- {
- // Ledger says majority held
- actions[entry.first] = 0;
- }
- }
- }
-
- return actions;
-}
-
-bool
-AmendmentTableImpl::needValidatedLedger (LedgerIndex ledgerSeq)
-{
- std::lock_guard sl (mLock);
-
- // Is there a ledger in which an amendment could have been enabled
- // between these two ledger sequences?
-
- return ((ledgerSeq - 1) / 256) != ((m_lastUpdateSeq - 1) / 256);
-}
-
-void
-AmendmentTableImpl::doValidatedLedger (LedgerIndex ledgerSeq,
- enabledAmendments_t enabled)
-{
- std::lock_guard sl (mLock);
-
- for (auto& e : m_amendmentMap)
- e.second.mEnabled = (enabled.count (e.first) != 0);
-}
-
-Json::Value
-AmendmentTableImpl::getJson (int)
-{
- Json::Value ret(Json::objectValue);
- {
- std::lock_guard sl(mLock);
- for (auto const& e : m_amendmentMap)
- {
- setJson (ret[to_string (e.first)] = Json::objectValue, e.second);
- }
- }
- return ret;
-}
-
-void
-AmendmentTableImpl::setJson (Json::Value& v, const AmendmentState& fs)
-{
- if (!fs.mFriendlyName.empty())
- v[jss::name] = fs.mFriendlyName;
-
- v[jss::supported] = fs.mSupported;
- v[jss::vetoed] = fs.mVetoed;
- v[jss::enabled] = fs.mEnabled;
-}
-
-Json::Value
-AmendmentTableImpl::getJson (uint256 const& amendmentID)
-{
- Json::Value ret = Json::objectValue;
- Json::Value& jAmendment = (ret[to_string (amendmentID)] = Json::objectValue);
-
- {
- std::lock_guard sl(mLock);
-
- AmendmentState& amendmentState = getCreate (amendmentID);
- setJson (jAmendment, amendmentState);
- }
-
- return ret;
-}
-
-std::unique_ptr make_AmendmentTable (
- std::chrono::seconds majorityTime,
- int majorityFraction,
- beast::Journal journal)
-{
- return std::make_unique(
- majorityTime, majorityFraction, journal);
-}
-
-} // ripple
diff --git a/src/ripple/app/misc/Validations.cpp b/src/ripple/app/misc/Validations.cpp
index 63d40ad0f5..73b70a007e 100644
--- a/src/ripple/app/misc/Validations.cpp
+++ b/src/ripple/app/misc/Validations.cpp
@@ -243,7 +243,6 @@ private:
JLOG (j_.trace) << "VC: " << ledger << "f:" << full << " p:" << partial;
}
-
int getTrustedValidationCount (uint256 const& ledger) override
{
int trusted = 0;
diff --git a/src/ripple/app/misc/impl/AmendmentTable.cpp b/src/ripple/app/misc/impl/AmendmentTable.cpp
new file mode 100644
index 0000000000..7fb2dd32a0
--- /dev/null
+++ b/src/ripple/app/misc/impl/AmendmentTable.cpp
@@ -0,0 +1,585 @@
+//------------------------------------------------------------------------------
+/*
+ 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
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace ripple {
+
+static
+std::vector>
+parseSection (Section const& section)
+{
+ static boost::regex const re1 (
+ "^" // start of line
+ "(?:\\s*)" // whitespace (optional)
+ "([abcdefABCDEF0-9]{64})" //
+ "(?:\\s+)" // whitespace
+ "(\\S+)" //
+ , boost::regex_constants::optimize
+ );
+
+ std::vector> names;
+
+ for (auto const& line : section.lines ())
+ {
+ boost::smatch match;
+
+ if (!boost::regex_match (line, match, re1))
+ Throw (
+ "Invalid entry '" + line +
+ "' in [" + section.name () + "]");
+
+ uint256 id;
+
+ if (!id.SetHexExact (match[1]))
+ Throw (
+ "Invalid amendment ID '" + match[1] +
+ "' in [" + section.name () + "]");
+
+ names.push_back (std::make_pair (id, match[2]));
+ }
+
+ return names;
+}
+
+/** Current state of an amendment.
+ Tells if a amendment is supported, enabled or vetoed. A vetoed amendment
+ means the node will never announce its support.
+*/
+struct AmendmentState
+{
+ /** If an amendment is vetoed, a server will not support it */
+ bool vetoed = false;
+
+ /** Indicates that the amendment has been enabled.
+ This is a one-way switch: once an amendment is enabled
+ it can never be disabled, but it can be superseded by
+ a subsequent amendment.
+ */
+ bool enabled = false;
+
+ /** Indicates an amendment that this server has code support for. */
+ bool supported = false;
+
+ /** The name of this amendment, possibly empty. */
+ std::string name;
+
+ AmendmentState () = default;
+};
+
+/** The status of all amendments requested in a given window. */
+struct AmendmentSet
+{
+private:
+ // How many yes votes each amendment received
+ hash_map votes_;
+
+public:
+ // number of trusted validations
+ int mTrustedValidations = 0;
+
+ // number of votes needed
+ int mThreshold = 0;
+
+ AmendmentSet () = default;
+
+ void tally (std::set const& amendments)
+ {
+ ++mTrustedValidations;
+
+ for (auto const& amendment : amendments)
+ ++votes_[amendment];
+ }
+
+ int votes (uint256 const& amendment) const
+ {
+ auto const& it = votes_.find (amendment);
+
+ if (it == votes_.end())
+ return 0;
+
+ return it->second;
+ }
+};
+
+//------------------------------------------------------------------------------
+
+/** Track the list of "amendments"
+
+ An "amendment" is an option that can affect transaction processing rules.
+ Amendments are proposed and then adopted or rejected by the network. An
+ Amendment is uniquely identified by its AmendmentID, a 256-bit key.
+*/
+class AmendmentTableImpl final
+ : public AmendmentTable
+{
+protected:
+ std::mutex mutex_;
+
+ hash_map amendmentMap_;
+ std::uint32_t lastUpdateSeq_;
+
+ // Time that an amendment must hold a majority for
+ std::chrono::seconds const majorityTime_;
+
+ // The amount of support that an amendment must receive
+ // 0 = 0% and 256 = 100%
+ int const majorityFraction_;
+
+ // The results of the last voting round - may be empty if
+ // we haven't participated in one yet.
+ std::unique_ptr lastVote_;
+
+ beast::Journal j_;
+
+ // Finds or creates state
+ AmendmentState* add (uint256 const& amendment);
+
+ // Finds existing state
+ AmendmentState* get (uint256 const& amendment);
+
+ void setJson (Json::Value& v, uint256 const& amendment, const AmendmentState&);
+
+public:
+ AmendmentTableImpl (
+ std::chrono::seconds majorityTime,
+ int majorityFraction,
+ Section const& supported,
+ Section const& enabled,
+ Section const& vetoed,
+ beast::Journal journal);
+
+ uint256 find (std::string const& name) override;
+
+ bool veto (uint256 const& amendment) override;
+ bool unVeto (uint256 const& amendment) override;
+
+ bool enable (uint256 const& amendment) override;
+ bool disable (uint256 const& amendment) override;
+
+ bool isEnabled (uint256 const& amendment) override;
+ bool isSupported (uint256 const& amendment) override;
+
+ Json::Value getJson (int) override;
+ Json::Value getJson (uint256 const&) override;
+
+ bool needValidatedLedger (LedgerIndex seq) override;
+
+ void doValidatedLedger (
+ LedgerIndex seq,
+ std::set const& enabled) override;
+
+ std::vector
+ doValidation (std::set const& enabledAmendments) override;
+
+ std::map
+ doVoting (
+ NetClock::time_point closeTime,
+ std::set const& enabledAmendments,
+ majorityAmendments_t const& majorityAmendments,
+ ValidationSet const& validations) override;
+};
+
+//------------------------------------------------------------------------------
+
+AmendmentTableImpl::AmendmentTableImpl (
+ std::chrono::seconds majorityTime,
+ int majorityFraction,
+ Section const& supported,
+ Section const& enabled,
+ Section const& vetoed,
+ beast::Journal journal)
+ : lastUpdateSeq_ (0)
+ , majorityTime_ (majorityTime)
+ , majorityFraction_ (majorityFraction)
+ , j_ (journal)
+{
+ assert (majorityFraction_ != 0);
+
+ std::lock_guard sl (mutex_);
+
+ for (auto const& a : parseSection(supported))
+ {
+ auto s = add (a.first);
+
+ if (!a.second.empty ())
+ s->name = a.second;
+
+ s->supported = true;
+ }
+
+ for (auto const& a : parseSection (enabled))
+ {
+ auto s = add (a.first);
+
+ if (!a.second.empty ())
+ s->name = a.second;
+ s->supported = true;
+ s->enabled = true;
+ }
+
+ for (auto const& a : parseSection (vetoed))
+ {
+ // Unknown amendments are effectively vetoed already
+ auto s = get (a.first);
+
+ if (s)
+ {
+ if (!a.second.empty ())
+ s->name = a.second;
+ s->vetoed = true;
+ }
+ }
+}
+
+AmendmentState*
+AmendmentTableImpl::add (uint256 const& amendmentHash)
+{
+ // call with the mutex held
+ return &amendmentMap_[amendmentHash];
+}
+
+AmendmentState*
+AmendmentTableImpl::get (uint256 const& amendmentHash)
+{
+ // call with the mutex held
+ auto ret = amendmentMap_.find (amendmentHash);
+
+ if (ret == amendmentMap_.end())
+ return nullptr;
+
+ return &ret->second;
+}
+
+uint256
+AmendmentTableImpl::find (std::string const& name)
+{
+ std::lock_guard sl (mutex_);
+
+ for (auto const& e : amendmentMap_)
+ {
+ if (name == e.second.name)
+ return e.first;
+ }
+
+ return {};
+}
+
+bool
+AmendmentTableImpl::veto (uint256 const& amendment)
+{
+ std::lock_guard sl (mutex_);
+ auto s = add (amendment);
+
+ if (s->vetoed)
+ return false;
+ s->vetoed = true;
+ return true;
+}
+
+bool
+AmendmentTableImpl::unVeto (uint256 const& amendment)
+{
+ std::lock_guard sl (mutex_);
+ auto s = get (amendment);
+
+ if (!s || !s->vetoed)
+ return false;
+ s->vetoed = false;
+ return true;
+}
+
+bool
+AmendmentTableImpl::enable (uint256 const& amendment)
+{
+ std::lock_guard sl (mutex_);
+ auto s = add (amendment);
+
+ if (s->enabled)
+ return false;
+
+ s->enabled = true;
+ return true;
+}
+
+bool
+AmendmentTableImpl::disable (uint256 const& amendment)
+{
+ std::lock_guard sl (mutex_);
+ auto s = get (amendment);
+
+ if (!s || !s->enabled)
+ return false;
+
+ s->enabled = false;
+ return true;
+}
+
+bool
+AmendmentTableImpl::isEnabled (uint256 const& amendment)
+{
+ std::lock_guard sl (mutex_);
+ auto s = get (amendment);
+ return s && s->enabled;
+}
+
+bool
+AmendmentTableImpl::isSupported (uint256 const& amendment)
+{
+ std::lock_guard sl (mutex_);
+ auto s = get (amendment);
+ return s && s->supported;
+}
+
+std::vector
+AmendmentTableImpl::doValidation (
+ std::set const& enabled)
+{
+ // Get the list of amendments we support and do not
+ // veto, but that are not already enabled
+ std::vector amendments;
+ amendments.reserve (amendmentMap_.size());
+
+ {
+ std::lock_guard sl (mutex_);
+ for (auto const& e : amendmentMap_)
+ {
+ if (e.second.supported && ! e.second.vetoed &&
+ (enabled.count (e.first) == 0))
+ {
+ amendments.push_back (e.first);
+ }
+ }
+ }
+
+ if (!amendments.empty())
+ std::sort (amendments.begin (), amendments.end ());
+
+ return amendments;
+}
+
+std::map
+AmendmentTableImpl::doVoting (
+ NetClock::time_point closeTime,
+ std::set const& enabledAmendments,
+ majorityAmendments_t const& majorityAmendments,
+ ValidationSet const& valSet)
+{
+ JLOG (j_.trace) <<
+ "voting at " << closeTime.time_since_epoch().count() <<
+ ": " << enabledAmendments.size() <<
+ ", " << majorityAmendments.size() <<
+ ", " << valSet.size();
+
+ auto vote = std::make_unique ();
+
+ // process validations for ledger before flag ledger
+ for (auto const& entry : valSet)
+ {
+ if (entry.second->isTrusted ())
+ {
+ std::set ballot;
+
+ if (entry.second->isFieldPresent (sfAmendments))
+ {
+ auto const choices =
+ entry.second->getFieldV256 (sfAmendments);
+ ballot.insert (choices.begin (), choices.end ());
+ }
+
+ vote->tally (ballot);
+ }
+ }
+
+ vote->mThreshold = std::max(1,
+ (vote->mTrustedValidations * majorityFraction_) / 256);
+
+ JLOG (j_.debug) <<
+ "Received " << vote->mTrustedValidations <<
+ " trusted validations, threshold is: " << vote->mThreshold;
+
+ // Map of amendments to the action to be taken for each one. The action is
+ // the value of the flags in the pseudo-transaction
+ std::map actions;
+
+ {
+ std::lock_guard sl (mutex_);
+
+ // process all amendments we know of
+ for (auto const& entry : amendmentMap_)
+ {
+ NetClock::time_point majorityTime = {};
+
+ bool const hasValMajority =
+ (vote->votes (entry.first) >= vote->mThreshold);
+
+ {
+ auto const it = majorityAmendments.find (entry.first);
+ if (it != majorityAmendments.end ())
+ majorityTime = it->second;
+ }
+
+ if (enabledAmendments.count (entry.first) != 0)
+ {
+ JLOG (j_.debug) <<
+ entry.first << ": amendment already enabled";
+ }
+ else if (hasValMajority &&
+ (majorityTime == NetClock::time_point{}) &&
+ ! entry.second.vetoed)
+ {
+ // Ledger says no majority, validators say yes
+ JLOG (j_.debug) <<
+ entry.first << ": amendment got majority";
+ actions[entry.first] = tfGotMajority;
+ }
+ else if (! hasValMajority &&
+ (majorityTime != NetClock::time_point{}))
+ {
+ // Ledger says majority, validators say no
+ JLOG (j_.debug) <<
+ entry.first << ": amendment lost majority";
+ actions[entry.first] = tfLostMajority;
+ }
+ else if ((majorityTime != NetClock::time_point{}) &&
+ ((majorityTime + majorityTime_) <= closeTime) &&
+ ! entry.second.vetoed)
+ {
+ // Ledger says majority held
+ JLOG (j_.debug) <<
+ entry.first << ": amendment majority held";
+ actions[entry.first] = 0;
+ }
+ }
+
+ // Stash for reporting
+ lastVote_ = std::move(vote);
+ }
+
+ return actions;
+}
+
+bool
+AmendmentTableImpl::needValidatedLedger (LedgerIndex ledgerSeq)
+{
+ std::lock_guard sl (mutex_);
+
+ // Is there a ledger in which an amendment could have been enabled
+ // between these two ledger sequences?
+
+ return ((ledgerSeq - 1) / 256) != ((lastUpdateSeq_ - 1) / 256);
+}
+
+void
+AmendmentTableImpl::doValidatedLedger (
+ LedgerIndex ledgerSeq,
+ std::set const& enabled)
+{
+ std::lock_guard sl (mutex_);
+
+ for (auto& e : amendmentMap_)
+ e.second.enabled = (enabled.count (e.first) != 0);
+}
+
+void
+AmendmentTableImpl::setJson (Json::Value& v, const uint256& id, const AmendmentState& fs)
+{
+ if (!fs.name.empty())
+ v[jss::name] = fs.name;
+
+ v[jss::supported] = fs.supported;
+ v[jss::vetoed] = fs.vetoed;
+ v[jss::enabled] = fs.enabled;
+
+ if (!fs.enabled && lastVote_)
+ {
+ auto const votesTotal = lastVote_->mTrustedValidations;
+ auto const votesNeeded = lastVote_->mThreshold;
+ auto const votesFor = lastVote_->votes (id);
+
+ v[jss::count] = votesFor;
+ v[jss::validations] = votesTotal;
+
+ if (votesNeeded)
+ {
+ v[jss::vote] = votesFor * 256 / votesNeeded;
+ v[jss::threshold] = votesNeeded;
+ }
+ }
+}
+
+Json::Value
+AmendmentTableImpl::getJson (int)
+{
+ Json::Value ret(Json::objectValue);
+ {
+ std::lock_guard sl(mutex_);
+ for (auto const& e : amendmentMap_)
+ {
+ setJson (ret[to_string (e.first)] = Json::objectValue,
+ e.first, e.second);
+ }
+ }
+ return ret;
+}
+
+Json::Value
+AmendmentTableImpl::getJson (uint256 const& amendmentID)
+{
+ Json::Value ret = Json::objectValue;
+ Json::Value& jAmendment = (ret[to_string (amendmentID)] = Json::objectValue);
+
+ {
+ std::lock_guard sl(mutex_);
+ auto a = add (amendmentID);
+ setJson (jAmendment, amendmentID, *a);
+ }
+
+ return ret;
+}
+
+std::unique_ptr make_AmendmentTable (
+ std::chrono::seconds majorityTime,
+ int majorityFraction,
+ Section const& supported,
+ Section const& enabled,
+ Section const& vetoed,
+ beast::Journal journal)
+{
+ return std::make_unique (
+ majorityTime,
+ majorityFraction,
+ supported,
+ enabled,
+ vetoed,
+ journal);
+}
+
+} // ripple
diff --git a/src/ripple/app/tests/AmendmentTable.test.cpp b/src/ripple/app/tests/AmendmentTable.test.cpp
index 6f032425d5..e531e83e6f 100644
--- a/src/ripple/app/tests/AmendmentTable.test.cpp
+++ b/src/ripple/app/tests/AmendmentTable.test.cpp
@@ -22,11 +22,11 @@
#include
#include
#include
-#include
#include
#include
#include
#include
+#include
#include
#include
@@ -35,349 +35,299 @@ namespace ripple
class AmendmentTable_test final : public beast::unit_test::suite
{
-public:
- using StringPairVec = std::vector>;
-
private:
- enum class TablePopulationAlgo
- {
- addInitial,
- addKnown
- };
-
// 204/256 about 80% (we round down because the implementation rounds up)
static int const majorityFraction{204};
- static void populateTable (AmendmentTable& table,
- std::vector const& configLines)
+ static
+ uint256
+ amendmentId (std::string in)
{
- Section section (SECTION_AMENDMENTS);
- section.append (configLines);
- table.addInitial (section);
+ sha256_hasher h;
+ using beast::hash_append;
+ hash_append(h, in);
+ auto const d = static_cast(h);
+ uint256 result;
+ std::memcpy(result.data(), d.data(), d.size());
+ return result;
}
- static std::vector getAmendmentNames (
- StringPairVec const& amendmentPairs)
+ static
+ std::vector
+ createSet (int group, int count)
{
- std::vector amendmentNames;
- amendmentNames.reserve (amendmentPairs.size ());
- for (auto const& i : amendmentPairs)
- {
- amendmentNames.emplace_back (i.first, i.second);
- }
- return amendmentNames;
+ std::vector amendments;
+ for (int i = 0; i < count; i++)
+ amendments.push_back (
+ "Amendment" + std::to_string ((1000000 * group) + i));
+ return amendments;
}
- std::vector populateTable (
- AmendmentTable& table,
- StringPairVec const& amendmentPairs,
- TablePopulationAlgo populationAlgo = TablePopulationAlgo::addKnown)
+ static
+ Section
+ makeSection (std::vector const& amendments)
{
- std::vector const amendmentNames (
- getAmendmentNames (amendmentPairs));
- switch (populationAlgo)
- {
- case TablePopulationAlgo::addKnown:
- for (auto const& i : amendmentNames)
- {
- table.addKnown (i);
- }
- break;
- case TablePopulationAlgo::addInitial:
- {
- std::vector configLines;
- configLines.reserve (amendmentPairs.size ());
- for (auto const& i : amendmentPairs)
- {
- configLines.emplace_back (i.first + " " + i.second);
- }
- populateTable (table, configLines);
- }
- break;
- default:
- fail ("Error in test case logic");
- }
-
- return amendmentNames;
+ Section section ("Test");
+ for (auto const& a : amendments)
+ section.append (to_string(amendmentId (a)) + " " + a);
+ return section;
}
- static std::unique_ptr< AmendmentTable >
- makeTable (int w)
+ static
+ Section
+ makeSection (uint256 const& amendment)
+ {
+ Section section ("Test");
+ section.append (to_string (amendment) + " " + to_string(amendment));
+ return section;
+ }
+
+ std::vector const m_set1;
+ std::vector const m_set2;
+ std::vector const m_set3;
+ std::vector const m_set4;
+
+ Section const emptySection;
+
+public:
+ AmendmentTable_test ()
+ : m_set1 (createSet (1, 12))
+ , m_set2 (createSet (2, 12))
+ , m_set3 (createSet (3, 12))
+ , m_set4 (createSet (4, 12))
+ {
+ }
+
+ std::unique_ptr
+ makeTable(
+ int w,
+ Section const supported,
+ Section const enabled,
+ Section const vetoed)
{
return make_AmendmentTable (
weeks (w),
majorityFraction,
+ supported,
+ enabled,
+ vetoed,
beast::Journal{});
+ }
+
+ std::unique_ptr
+ makeTable (int w)
+ {
+ return makeTable (
+ w,
+ makeSection (m_set1),
+ makeSection (m_set2),
+ makeSection (m_set3));
};
- // Create the amendments by string pairs instead of AmendmentNames
- // as this helps test the AmendmentNames class
- StringPairVec const m_knownAmendmentPairs;
- StringPairVec const m_unknownAmendmentPairs;
-
-public:
- AmendmentTable_test ()
- : m_knownAmendmentPairs (
- {{"a49f90e7cddbcadfed8fc89ec4d02011", "Known1"},
- {"ca956ccabf25151a16d773171c485423", "Known2"},
- {"60dcd528f057711c5d26b57be28e23df", "Known3"},
- {"da956ccabf25151a16d773171c485423", "Known4"},
- {"70dcd528f057711c5d26b57be28e23df", "Known5"},
- {"70dcd528f057711c5d26b57be28e23d0", "Known6"}})
- , m_unknownAmendmentPairs (
- {{"a9f90e7cddbcadfed8fc89ec4d02011c", "Unknown1"},
- {"c956ccabf25151a16d773171c485423b", "Unknown2"},
- {"6dcd528f057711c5d26b57be28e23dfa", "Unknown3"}})
+ void testConstruct ()
{
+ testcase ("Construction");
+
+ auto table = makeTable(1);
+
+ for (auto const& a : m_set1)
+ {
+ expect (table->isSupported (amendmentId (a)));
+ expect (!table->isEnabled (amendmentId (a)));
+ }
+
+ for (auto const& a : m_set2)
+ {
+ expect (table->isSupported (amendmentId (a)));
+ expect (table->isEnabled (amendmentId (a)));
+ }
+
+ for (auto const& a : m_set3)
+ {
+ expect (!table->isSupported (amendmentId (a)));
+ expect (!table->isEnabled (amendmentId (a)));
+ }
}
void testGet ()
{
- testcase ("get");
- auto table (makeTable (2));
- std::vector const amendmentNames (
- populateTable (*table, m_knownAmendmentPairs));
- std::vector const unknownAmendmentNames (
- getAmendmentNames (m_unknownAmendmentPairs));
- for (auto const& i : amendmentNames)
- {
- expect (table->get (i.friendlyName ()) == i.id ());
- }
+ testcase ("Name to ID mapping");
- for (auto const& i : unknownAmendmentNames)
- {
- expect (table->get (i.friendlyName ()) == uint256 ());
- }
+ auto table = makeTable (1);
+
+ for (auto const& a : m_set1)
+ expect (table->find (a) == amendmentId (a));
+ for (auto const& a : m_set2)
+ expect (table->find (a) == amendmentId (a));
+
+ for (auto const& a : m_set3)
+ expect (!table->find (a));
+ for (auto const& a : m_set4)
+ expect (!table->find (a));
}
- void testAddInitialAddKnown ()
+ void testBadConfig ()
{
- testcase ("addInitialAddKnown");
+ auto const section = makeSection (m_set1);
+ auto const id = to_string (amendmentId (m_set2[0]));
- for (auto tablePopulationAlgo :
- {TablePopulationAlgo::addInitial, TablePopulationAlgo::addKnown})
- {
+ testcase ("Bad Config");
+
+ { // Two arguments are required - we pass one
+ Section test = section;
+ test.append (id);
+
+ try
{
- // test that the amendments we add are enabled and amendments we
- // didn't add are not enabled
-
- auto table (makeTable (2));
- std::vector const amendmentNames (populateTable (
- *table, m_knownAmendmentPairs, tablePopulationAlgo));
- std::vector const unknownAmendmentNames (
- getAmendmentNames (m_unknownAmendmentPairs));
-
- for (auto const& i : amendmentNames)
- {
- expect (table->isSupported (i.id ()));
- if (tablePopulationAlgo == TablePopulationAlgo::addInitial)
- expect (table->isEnabled (i.id ()));
- }
-
- for (auto const& i : unknownAmendmentNames)
- {
- expect (!table->isSupported (i.id ()));
- expect (!table->isEnabled (i.id ()));
- }
+ if (makeTable (2, test, emptySection, emptySection))
+ fail ("Accepted only amendment ID");
}
-
+ catch (...)
{
- // check that we throw an exception on bad hex pairs
- StringPairVec const badHexPairs (
- {{"a9f90e7cddbcadfedm8fc89ec4d02011c", "BadHex1"},
- {"c956ccabf25151a16d77T3171c485423b", "BadHex2"},
- {"6dcd528f057711c5d2Z6b57be28e23dfa", "BadHex3"}});
+ pass();
+ }
+ }
- // make sure each element throws
- for (auto const& i : badHexPairs)
- {
- StringPairVec v ({i});
- auto table (makeTable (2));
- try
- {
- populateTable (*table, v, tablePopulationAlgo);
- // line above should throw
- fail ("didn't throw");
- }
- catch (std::exception const&)
- {
- pass ();
- }
- try
- {
- populateTable (
- *table, badHexPairs, tablePopulationAlgo);
- // line above should throw
- fail ("didn't throw");
- }
- catch (std::exception const&)
- {
- pass ();
- }
- }
+ { // Two arguments are required - we pass three
+ Section test = section;
+ test.append (id + " Test Name");
+
+ try
+ {
+ if (makeTable (2, test, emptySection, emptySection))
+ fail ("Accepted extra arguments");
+ }
+ catch (...)
+ {
+ pass();
}
}
{
- // check that we thow on bad num tokens
- std::vector const badNumTokensConfigLines (
- {"19f6d",
- "19fd6 bad friendly name"
- "9876 one two"});
+ auto sid = id;
+ sid.resize (sid.length() - 1);
- // make sure each element throws
- for (auto const& i : badNumTokensConfigLines)
+ Section test = section;
+ test.append (sid + " Name");
+
+ try
{
- std::vector v ({i});
- auto table (makeTable (2));
- try
- {
- populateTable (*table, v);
- // line above should throw
- fail ("didn't throw");
- }
- catch (std::exception const&)
- {
- pass ();
- }
- try
- {
- populateTable (*table, badNumTokensConfigLines);
- // line above should throw
- fail ("didn't throw");
- }
- catch (std::exception const&)
- {
- pass ();
- }
+ if (makeTable (2, test, emptySection, emptySection))
+ fail ("Accepted short amendment ID");
+ }
+ catch (...)
+ {
+ pass();
+ }
+ }
+
+ {
+ auto sid = id;
+ sid.resize (sid.length() + 1, '0');
+
+ Section test = section;
+ test.append (sid + " Name");
+
+ try
+ {
+ if (makeTable (2, test, emptySection, emptySection))
+ fail ("Accepted long amendment ID");
+ }
+ catch (...)
+ {
+ pass();
+ }
+ }
+
+ {
+ auto sid = id;
+ sid.resize (sid.length() - 1);
+ sid.push_back ('Q');
+
+ Section test = section;
+ test.append (sid + " Name");
+
+ try
+ {
+ if (makeTable (2, test, emptySection, emptySection))
+ fail ("Accepted non-hex amendment ID");
+ }
+ catch (...)
+ {
+ pass();
}
}
}
- void testEnable ()
+ std::map
+ getState (
+ AmendmentTable *table,
+ std::set const& exclude)
{
- testcase ("enable");
- auto table (makeTable (2));
- std::vector const amendmentNames (
- populateTable (*table, m_knownAmendmentPairs));
- {
- // enable/disable tests
- for (auto const& i : amendmentNames)
- {
- auto id (i.id ());
- table->enable (id);
- expect (table->isEnabled (id));
- table->disable (id);
- expect (!table->isEnabled (id));
- table->enable (id);
- expect (table->isEnabled (id));
- }
+ std::map state;
- std::vector toEnable;
- for (auto const& i : amendmentNames)
+ auto track = [&state,table](std::vector const& v)
+ {
+ for (auto const& a : v)
{
- auto id (i.id ());
- toEnable.emplace_back (id);
- table->disable (id);
- expect (!table->isEnabled (id));
+ auto const id = amendmentId(a);
+ state[id] = table->isEnabled (id);
}
- table->setEnabled (toEnable);
- for (auto const& i : toEnable)
- {
- expect (table->isEnabled (i));
- }
- }
+ };
+
+ track (m_set1);
+ track (m_set2);
+ track (m_set3);
+ track (m_set4);
+
+ for (auto const& a : exclude)
+ state.erase(a);
+
+ return state;
}
- using ATSetter =
- void (AmendmentTable::*)(const std::vector& amendments);
- using ATGetter = bool (AmendmentTable::*)(uint256 const& amendment);
- void testVectorSetUnset (ATSetter setter, ATGetter getter)
+ void testEnableDisable ()
{
- auto table (makeTable (2));
- // make pointer to ref syntax a little nicer
- auto& tableRef (*table);
- std::vector const amendmentNames (
- populateTable (tableRef, m_knownAmendmentPairs));
+ testcase ("enable & disable");
- // they should all be set
- for (auto const& i : amendmentNames)
- {
- expect ((tableRef.*getter)(i.id ())); // i.e. "isSupported"
- }
+ auto const testAmendment = amendmentId("TestAmendment");
+ auto table = makeTable (2);
- {
- // only set every other amendment
- std::vector toSet;
- toSet.reserve (amendmentNames.size ());
- for (int i = 0; i < amendmentNames.size (); ++i)
- {
- if (i % 2)
- {
- toSet.emplace_back (amendmentNames[i].id ());
- }
- }
- (tableRef.*setter)(toSet);
- for (int i = 0; i < amendmentNames.size (); ++i)
- {
- bool const shouldBeSet = i % 2;
- expect (shouldBeSet ==
- (tableRef.*getter)(
- amendmentNames[i].id ())); // i.e. "isSupported"
- }
- }
- }
- void testSupported ()
- {
- testcase ("supported");
- testVectorSetUnset (&AmendmentTable::setSupported,
- &AmendmentTable::isSupported);
- }
- void testEnabled ()
- {
- testcase ("enabled");
- testVectorSetUnset (&AmendmentTable::setEnabled,
- &AmendmentTable::isEnabled);
- }
- void testSupportedEnabled ()
- {
- // Check that supported/enabled aren't the same thing
- testcase ("supportedEnabled");
- auto table (makeTable (2));
+ // Subset of amendments to enable
+ std::set enabled;
+ enabled.insert (testAmendment);
+ enabled.insert (amendmentId(m_set1[0]));
+ enabled.insert (amendmentId(m_set2[0]));
+ enabled.insert (amendmentId(m_set3[0]));
+ enabled.insert (amendmentId(m_set4[0]));
- std::vector const amendmentNames (
- populateTable (*table, m_knownAmendmentPairs));
+ // Get the state before, excluding the items we'll change:
+ auto const pre_state = getState (table.get(), enabled);
- {
- // support every even amendment
- // enable every odd amendment
- std::vector toSupport;
- toSupport.reserve (amendmentNames.size ());
- std::vector toEnable;
- toEnable.reserve (amendmentNames.size ());
- for (int i = 0; i < amendmentNames.size (); ++i)
- {
- if (i % 2)
- {
- toSupport.emplace_back (amendmentNames[i].id ());
- }
- else
- {
- toEnable.emplace_back (amendmentNames[i].id ());
- }
- }
- table->setEnabled (toEnable);
- table->setSupported (toSupport);
- for (int i = 0; i < amendmentNames.size (); ++i)
- {
- bool const shouldBeSupported = i % 2;
- bool const shouldBeEnabled = !(i % 2);
- expect (shouldBeEnabled ==
- (table->isEnabled (amendmentNames[i].id ())));
- expect (shouldBeSupported ==
- (table->isSupported (amendmentNames[i].id ())));
- }
- }
+ // Enable the subset and verify
+ for (auto const& a : enabled)
+ table->enable (a);
+
+ for (auto const& a : enabled)
+ expect (table->isEnabled (a));
+
+ // Disable the subset and verify
+ for (auto const& a : enabled)
+ table->disable (a);
+
+ for (auto const& a : enabled)
+ expect (!table->isEnabled (a));
+
+ // Get the state after, excluding the items we changed:
+ auto const post_state = getState (table.get(), enabled);
+
+ // Ensure the states are identical
+ auto ret = std::mismatch(
+ pre_state.begin(), pre_state.end(),
+ post_state.begin(), post_state.end());
+
+ expect (ret.first == pre_state.end());
+ expect (ret.second == post_state.end());
}
std::vector makeValidators (int num)
@@ -398,14 +348,14 @@ public:
}
// Execute a pretend consensus round for a flag ledger
- void doRound
- ( AmendmentTable& table
- , weeks week
- , std::vector const& validators
- , std::vector > const& votes
- , std::vector & ourVotes
- , enabledAmendments_t& enabled
- , majorityAmendments_t& majority)
+ void doRound(
+ AmendmentTable& table,
+ weeks week,
+ std::vector const& validators,
+ std::vector > const& votes,
+ std::vector & ourVotes,
+ std::set & enabled,
+ majorityAmendments_t& majority)
{
// Do a round at the specified time
// Returns the amendments we voted for
@@ -427,9 +377,8 @@ public:
int i = 0;
for (auto const& val : validators)
{
- STValidation::pointer v =
- std::make_shared
- (uint256(), roundTime, val, true);
+ auto v = std::make_shared (
+ uint256(), roundTime, val, true);
++i;
STVector256 field (sfAmendments);
@@ -451,7 +400,8 @@ public:
ourVotes = table.doValidation (enabled);
- auto actions = table.doVoting (roundTime, enabled, majority, validations);
+ auto actions = table.doVoting (
+ roundTime, enabled, majority, validations);
for (auto const& action : actions)
{
// This code assumes other validators do as we do
@@ -462,47 +412,47 @@ public:
case 0:
// amendment goes from majority to enabled
if (enabled.find (hash) != enabled.end ())
- Throw ("enabling already enabled");
+ throw std::runtime_error ("enabling already enabled");
if (majority.find (hash) == majority.end ())
- Throw ("enabling without majority");
+ throw std::runtime_error ("enabling without majority");
enabled.insert (hash);
majority.erase (hash);
break;
case tfGotMajority:
if (majority.find (hash) != majority.end ())
- Throw ("got majority while having majority");
+ throw std::runtime_error ("got majority while having majority");
majority[hash] = roundTime;
break;
case tfLostMajority:
if (majority.find (hash) == majority.end ())
- Throw ("lost majority without majority");
+ throw std::runtime_error ("lost majority without majority");
majority.erase (hash);
break;
default:
- assert (false);
- Throw ("unknown action");
+ throw std::runtime_error ("unknown action");
}
}
}
// No vote on unknown amendment
- void testNoUnknown ()
+ void testNoOnUnknown ()
{
- testcase ("voteNoUnknown");
-
- auto table (makeTable (2));
+ testcase ("Vote NO on unknown");
+ auto const testAmendment = amendmentId("TestAmendment");
auto const validators = makeValidators (10);
- uint256 testAmendment;
- testAmendment.SetHex("6dcd528f057711c5d26b57be28e23dfa");
+ auto table = makeTable (2,
+ emptySection,
+ emptySection,
+ emptySection);
std::vector > votes;
std::vector ourVotes;
- enabledAmendments_t enabled;
+ std::set enabled;
majorityAmendments_t majority;
doRound (*table, weeks{1},
@@ -541,21 +491,22 @@ public:
}
// No vote on vetoed amendment
- void testNoVetoed ()
+ void testNoOnVetoed ()
{
- testcase ("voteNoVetoed");
+ testcase ("Vote NO on vetoed");
- auto table (makeTable (2));
+ auto const testAmendment = amendmentId ("vetoedAmendment");
+
+ auto table = makeTable (2,
+ emptySection,
+ emptySection,
+ makeSection (testAmendment));
auto const validators = makeValidators (10);
- uint256 testAmendment;
- testAmendment.SetHex("6dcd528f057711c5d26b57be28e23dfa");
- table->veto(testAmendment);
-
std::vector > votes;
std::vector ourVotes;
- enabledAmendments_t enabled;
+ std::set enabled;
majorityAmendments_t majority;
doRound (*table, weeks{1},
@@ -596,15 +547,16 @@ public:
{
testcase ("voteEnable");
- auto table (makeTable (2));
- auto const amendmentNames (
- populateTable (*table, m_knownAmendmentPairs));
+ auto table = makeTable (
+ 2,
+ makeSection (m_set1),
+ emptySection,
+ emptySection);
auto const validators = makeValidators (10);
-
std::vector > votes;
std::vector ourVotes;
- enabledAmendments_t enabled;
+ std::set enabled;
majorityAmendments_t majority;
// Week 1: We should vote for all known amendments not enabled
@@ -614,14 +566,15 @@ public:
ourVotes,
enabled,
majority);
- expect (ourVotes.size() == amendmentNames.size(), "Did not vote");
+ expect (ourVotes.size() == m_set1.size(), "Did not vote");
expect (enabled.empty(), "Enabled amendment for no reason");
- for (auto const& i : amendmentNames)
- expect(majority.find(i.id()) == majority.end(), "majority detected for no reaosn");
+ for (auto const& i : m_set1)
+ expect(majority.find(amendmentId (i)) == majority.end(),
+ "majority detected for no reason");
// Now, everyone votes for this feature
- for (auto const& i : amendmentNames)
- votes.emplace_back (i.id(), 256);
+ for (auto const& i : m_set1)
+ votes.emplace_back (amendmentId(i), 256);
// Week 2: We should recognize a majority
doRound (*table, weeks{2},
@@ -630,10 +583,12 @@ public:
ourVotes,
enabled,
majority);
- expect (ourVotes.size() == amendmentNames.size(), "Did not vote");
+ expect (ourVotes.size() == m_set1.size(), "Did not vote");
expect (enabled.empty(), "Enabled amendment for no reason");
- for (auto const& i : amendmentNames)
- expect (majority[i.id()] == weekTime(weeks{2}), "majority not detected");
+
+ for (auto const& i : m_set1)
+ expect (majority[amendmentId (i)] == weekTime(weeks{2}),
+ "majority not detected");
// Week 5: We should enable the amendment
doRound (*table, weeks{5},
@@ -642,7 +597,7 @@ public:
ourVotes,
enabled,
majority);
- expect (enabled.size() == amendmentNames.size(), "Did not enable");
+ expect (enabled.size() == m_set1.size(), "Did not enable");
// Week 6: We should remove it from our votes and from having a majority
doRound (*table, weeks{6},
@@ -651,25 +606,28 @@ public:
ourVotes,
enabled,
majority);
- expect (enabled.size() == amendmentNames.size(), "Disabled");
+ expect (enabled.size() == m_set1.size(), "Disabled");
expect (ourVotes.empty(), "Voted after enabling");
- for (auto const& i : amendmentNames)
- expect(majority.find(i.id()) == majority.end(), "majority not removed");
+ for (auto const& i : m_set1)
+ expect(majority.find(amendmentId (i)) == majority.end(),
+ "majority not removed");
}
// Detect majority at 80%, enable later
void testDetectMajority ()
{
testcase ("detectMajority");
- auto table (makeTable (2));
- uint256 testAmendment;
- testAmendment.SetHex("6dcd528f057711c5d26b57be28e23dfa");
- table->addKnown({testAmendment, "testAmendment"});
+ auto const testAmendment = amendmentId ("detectMajority");
+ auto table = makeTable (
+ 2,
+ makeSection (testAmendment),
+ emptySection,
+ emptySection);
auto const validators = makeValidators (16);
- enabledAmendments_t enabled;
+ std::set enabled;
majorityAmendments_t majority;
for (int i = 0; i <= 17; ++i)
@@ -683,25 +641,22 @@ public:
doRound (*table, weeks{i},
validators, votes, ourVotes, enabled, majority);
- if (i < 14)
+ if (i < 13)
{
- // rounds 0-13
// We are voting yes, not enabled, no majority
expect (!ourVotes.empty(), "We aren't voting");
expect (enabled.empty(), "Enabled too early");
expect (majority.empty(), "Majority too early");
}
- else if (i < 16)
+ else if (i < 15)
{
- // rounds 14 and 15
// We have a majority, not enabled, keep voting
expect (!ourVotes.empty(), "We stopped voting");
expect (!majority.empty(), "Failed to detect majority");
expect (enabled.empty(), "Enabled too early");
}
- else if (i == 16) // round 16
+ else if (i == 15)
{
- // round 16
// enable, keep voting, remove from majority
expect (!ourVotes.empty(), "We stopped voting");
expect (majority.empty(), "Failed to remove from majority");
@@ -709,7 +664,6 @@ public:
}
else
{
- // round 17
// Done, we should be enabled and not voting
expect (ourVotes.empty(), "We did not stop voting");
expect (majority.empty(), "Failed to revove from majority");
@@ -723,15 +677,16 @@ public:
{
testcase ("lostMajority");
- auto table (makeTable (8));
-
- uint256 testAmendment;
- testAmendment.SetHex("6dcd528f057711c5d26b57be28e23dfa");
- table->addKnown({testAmendment, "testAmendment"});
-
+ auto const testAmendment = amendmentId ("lostMajority");
auto const validators = makeValidators (16);
- enabledAmendments_t enabled;
+ auto table = makeTable (
+ 8,
+ makeSection (testAmendment),
+ emptySection,
+ emptySection);
+
+ std::set enabled;
majorityAmendments_t majority;
{
@@ -759,9 +714,8 @@ public:
doRound (*table, weeks{i + 1},
validators, votes, ourVotes, enabled, majority);
- if (i < 6)
+ if (i < 8)
{
- // rounds 1 to 5
// We are voting yes, not enabled, majority
expect (!ourVotes.empty(), "We aren't voting");
expect (enabled.empty(), "Enabled for no reason");
@@ -769,7 +723,6 @@ public:
}
else
{
- // rounds 6 to 15
// No majority, not enabled, keep voting
expect (!ourVotes.empty(), "We stopped voting");
expect (majority.empty(), "Failed to detect loss of majority");
@@ -780,13 +733,12 @@ public:
void run ()
{
+ testConstruct();
testGet ();
- testAddInitialAddKnown ();
- testEnable ();
- testSupported ();
- testSupportedEnabled ();
- testNoUnknown ();
- testNoVetoed ();
+ testBadConfig ();
+ testEnableDisable ();
+ testNoOnUnknown ();
+ testNoOnVetoed ();
testVoteEnable ();
testDetectMajority ();
testLostMajority ();
diff --git a/src/ripple/core/ConfigSections.h b/src/ripple/core/ConfigSections.h
index b6b53107d4..0207972da4 100644
--- a/src/ripple/core/ConfigSections.h
+++ b/src/ripple/core/ConfigSections.h
@@ -64,6 +64,7 @@ struct ConfigSection
#define SECTION_VALIDATION_SEED "validation_seed"
#define SECTION_WEBSOCKET_PING_FREQ "websocket_ping_frequency"
#define SECTION_VALIDATORS "validators"
+#define SECTION_VETO_AMENDMENTS "veto_amendments"
} // ripple
diff --git a/src/ripple/ledger/View.h b/src/ripple/ledger/View.h
index e7502f8c8b..0a15c38138 100644
--- a/src/ripple/ledger/View.h
+++ b/src/ripple/ledger/View.h
@@ -130,8 +130,7 @@ cdirNext (ReadView const& view,
beast::Journal j);
// Return the list of enabled amendments
-using enabledAmendments_t = std::set ;
-enabledAmendments_t
+std::set
getEnabledAmendments (ReadView const& view);
// Return a map of amendments that have achieved majority
diff --git a/src/ripple/ledger/impl/View.cpp b/src/ripple/ledger/impl/View.cpp
index ac61387536..c244585ae5 100644
--- a/src/ripple/ledger/impl/View.cpp
+++ b/src/ripple/ledger/impl/View.cpp
@@ -469,16 +469,19 @@ cdirNext (ReadView const& view,
return true;
}
-enabledAmendments_t
+std::set
getEnabledAmendments (ReadView const& view)
{
- enabledAmendments_t amendments;
- auto const sleAmendments = view.read(keylet::amendments());
+ std::set amendments;
- if (sleAmendments)
+ if (auto const sle = view.read(keylet::amendments()))
{
- for (auto const &a : sleAmendments->getFieldV256 (sfAmendments))
- amendments.insert (a);
+ if (!sle->isFieldPresent (sfAmendments))
+ LogicError ("No amendments field is present");
+
+ auto const& v = sle->getFieldV256 (sfAmendments);
+
+ amendments.insert (v.begin(), v.end());
}
return amendments;
@@ -487,20 +490,24 @@ getEnabledAmendments (ReadView const& view)
majorityAmendments_t
getMajorityAmendments (ReadView const& view)
{
- using tp = NetClock::time_point;
- using d = tp::duration;
- majorityAmendments_t majorities;
- auto const sleAmendments = view.read(keylet::amendments());
+ majorityAmendments_t ret;
- if (sleAmendments && sleAmendments->isFieldPresent (sfMajorities))
+ if (auto const sle = view.read(keylet::amendments()))
{
- auto const& majArray = sleAmendments->getFieldArray (sfMajorities);
- for (auto const& m : majArray)
- majorities[m.getFieldH256 (sfAmendment)] =
- tp(d(m.getFieldU32(sfCloseTime)));
+ if (sle->isFieldPresent (sfMajorities))
+ {
+ using tp = NetClock::time_point;
+ using d = tp::duration;
+
+ auto const majorities = sle->getFieldArray (sfMajorities);
+
+ for (auto const& m : majorities)
+ ret[m.getFieldH256 (sfAmendment)] =
+ tp(d(m.getFieldU32(sfCloseTime)));
+ }
}
- return majorities;
+ return ret;
}
boost::optional
diff --git a/src/ripple/net/impl/RPCCall.cpp b/src/ripple/net/impl/RPCCall.cpp
index 26e38c425e..2153490052 100644
--- a/src/ripple/net/impl/RPCCall.cpp
+++ b/src/ripple/net/impl/RPCCall.cpp
@@ -35,6 +35,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -406,16 +407,28 @@ private:
return rpcError (rpcNO_EVENTS);
}
- // feature [] [true|false]
+ // feature [] [accept|reject]
Json::Value parseFeature (Json::Value const& jvParams)
{
Json::Value jvRequest (Json::objectValue);
if (jvParams.size () > 0)
- jvRequest[jss::feature] = jvParams[0u].asString ();
+ jvRequest[jss::feature] = jvParams[0u].asString ();
if (jvParams.size () > 1)
- jvRequest[jss::vote] = beast::lexicalCastThrow (jvParams[1u].asString ());
+ {
+ auto const action = jvParams[1u].asString ();
+
+ // This may look reversed, but it's intentional: jss::vetoed
+ // determines whether an amendment is vetoed - so "reject" means
+ // that jss::vetoed is true.
+ if (beast::ci_equal(action, "reject"))
+ jvRequest[jss::vetoed] = Json::Value (true);
+ else if (beast::ci_equal(action, "accept"))
+ jvRequest[jss::vetoed] = Json::Value (false);
+ else
+ return rpcError (rpcINVALID_PARAMS);
+ }
return jvRequest;
}
diff --git a/src/ripple/protocol/JsonFields.h b/src/ripple/protocol/JsonFields.h
index d090de833e..337586b6ec 100644
--- a/src/ripple/protocol/JsonFields.h
+++ b/src/ripple/protocol/JsonFields.h
@@ -236,6 +236,7 @@ JSS ( load_factor_net ); // out: NetworkOPs
JSS ( load_fee ); // out: LoadFeeTrackImp
JSS ( local ); // out: resource/Logic.h
JSS ( local_txs ); // out: GetCounts
+JSS ( majority ); // out: RPC feature
JSS ( marker ); // in/out: AccountTx, AccountOffers,
// AccountLines, AccountObjects,
// LedgerData
@@ -412,6 +413,7 @@ JSS ( validation_key ); // out: ValidationCreate, ValidationSeed
JSS ( validation_public_key ); // out: ValidationCreate, ValidationSeed
JSS ( validation_quorum ); // out: NetworkOPs
JSS ( validation_seed ); // out: ValidationCreate, ValidationSeed
+JSS ( validations ); // out: AmendmentTableImpl
JSS ( value ); // out: STAmount
JSS ( version ); // out: RPCVersion
JSS ( vetoed ); // out: AmendmentTableImpl
diff --git a/src/ripple/rpc/handlers/Feature1.cpp b/src/ripple/rpc/handlers/Feature1.cpp
index 3c16f0496c..b1f45b8329 100644
--- a/src/ripple/rpc/handlers/Feature1.cpp
+++ b/src/ripple/rpc/handlers/Feature1.cpp
@@ -18,6 +18,7 @@
//==============================================================================
#include
+#include
#include
#include
#include
@@ -28,32 +29,60 @@
namespace ripple {
+
+// {
+// feature :
+// vetoed : true/false
+// }
Json::Value doFeature (RPC::Context& context)
{
+
+ // Get majority amendment status
+ majorityAmendments_t majorities;
+
+ if (auto const valLedger = context.ledgerMaster.getValidatedLedger())
+ majorities = getMajorityAmendments (*valLedger);
+
+ auto& table = context.app.getAmendmentTable ();
+
if (!context.params.isMember (jss::feature))
{
+ auto features = table.getJson(0);
+
+ for (auto const& m : majorities)
+ {
+ features[to_string(m.first)][jss::majority] =
+ m.second.time_since_epoch().count();
+ }
+
Json::Value jvReply = Json::objectValue;
- jvReply[jss::features] = context.app.getAmendmentTable ().getJson(0);
+ jvReply[jss::features] = features;
return jvReply;
}
- uint256 uFeature
- = context.app.getAmendmentTable ().get(
- context.params[jss::feature].asString());
+ auto feature = table.find (
+ context.params[jss::feature].asString());
- if (uFeature.isZero ())
+ if (!feature &&
+ !feature.SetHexExact (context.params[jss::feature].asString ()))
+ return rpcError (rpcBAD_FEATURE);
+
+ if (context.params.isMember (jss::vetoed))
{
- uFeature.SetHex (context.params[jss::feature].asString ());
-
- if (uFeature.isZero ())
- return rpcError (rpcBAD_FEATURE);
+ if (context.params[jss::vetoed].asBool ())
+ context.app.getAmendmentTable().veto (feature);
+ else
+ context.app.getAmendmentTable().unVeto(feature);
}
- if (!context.params.isMember (jss::vote))
- return context.app.getAmendmentTable ().getJson(uFeature);
+ Json::Value jvReply = table.getJson(feature);
- // WRITEME
- return rpcError (rpcNOT_SUPPORTED);
+ auto m = majorities.find (feature);
+ if (m != majorities.end())
+ jvReply [jss::majority] =
+ m->second.time_since_epoch().count();
+
+ return jvReply;
}
diff --git a/src/ripple/unity/app_main.cpp b/src/ripple/unity/app_main.cpp
index ebc5c5fc24..be3c746d32 100644
--- a/src/ripple/unity/app_main.cpp
+++ b/src/ripple/unity/app_main.cpp
@@ -20,6 +20,7 @@
#include
#include
+#include
#include
#include
#include
diff --git a/src/ripple/unity/app_misc.cpp b/src/ripple/unity/app_misc.cpp
index 75745f9f61..421636bbfe 100644
--- a/src/ripple/unity/app_misc.cpp
+++ b/src/ripple/unity/app_misc.cpp
@@ -19,7 +19,6 @@
#include
-#include
#include
#include
#include
@@ -28,6 +27,7 @@
#include
#include
+#include
#include
#include
#include