Amendment RPC enhancements:

* RPC command to veto/unveto
* Store votes
* Add vote information to JSON
* Add ledger majority information to JSON
* Config section for vetos
This commit is contained in:
Nik Bougalis
2015-11-03 17:31:18 -08:00
parent c9486863c3
commit f13668371e
18 changed files with 1120 additions and 1148 deletions

View File

@@ -1258,6 +1258,10 @@
</ClCompile>
<ClInclude Include="..\..\src\ripple\app\ledger\TransactionStateSF.h">
</ClInclude>
<ClCompile Include="..\..\src\ripple\app\main\Amendments.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\ripple\app\main\Application.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
@@ -1308,10 +1312,6 @@
</ClInclude>
<ClInclude Include="..\..\src\ripple\app\misc\AmendmentTable.h">
</ClInclude>
<ClCompile Include="..\..\src\ripple\app\misc\AmendmentTableImpl.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\ripple\app\misc\CanonicalTXSet.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
@@ -1336,6 +1336,10 @@
</ClCompile>
<ClInclude Include="..\..\src\ripple\app\misc\impl\AccountTxPaging.h">
</ClInclude>
<ClCompile Include="..\..\src\ripple\app\misc\impl\AmendmentTable.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\ripple\app\misc\impl\Transaction.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>

View File

@@ -1857,6 +1857,9 @@
<ClInclude Include="..\..\src\ripple\app\ledger\TransactionStateSF.h">
<Filter>ripple\app\ledger</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ripple\app\main\Amendments.cpp">
<Filter>ripple\app\main</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple\app\main\Application.cpp">
<Filter>ripple\app\main</Filter>
</ClCompile>
@@ -1908,9 +1911,6 @@
<ClInclude Include="..\..\src\ripple\app\misc\AmendmentTable.h">
<Filter>ripple\app\misc</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ripple\app\misc\AmendmentTableImpl.cpp">
<Filter>ripple\app\misc</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple\app\misc\CanonicalTXSet.cpp">
<Filter>ripple\app\misc</Filter>
</ClCompile>
@@ -1935,6 +1935,9 @@
<ClInclude Include="..\..\src\ripple\app\misc\impl\AccountTxPaging.h">
<Filter>ripple\app\misc\impl</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ripple\app\misc\impl\AmendmentTable.cpp">
<Filter>ripple\app\misc\impl</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple\app\misc\impl\Transaction.cpp">
<Filter>ripple\app\misc\impl</Filter>
</ClCompile>

View File

@@ -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

View File

@@ -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 <string>
#include <vector>
namespace ripple {
namespace detail {
/** Amendments that this server supports and enables by default */
std::vector<std::string>
preEnabledAmendments ()
{
return
{
};
}
/** Amendments that this server supports, but doesn't enable by default */
std::vector<std::string>
supportedAmendments ()
{
return
{
};
}
}
}

View File

@@ -210,6 +210,15 @@ public:
}
};
/** Amendments that this server supports and enables by default */
std::vector<std::string>
preEnabledAmendments ();
/** Amendments that this server supports, but doesn't enable by default */
std::vector<std::string>
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<LoadFeeTrack>(logs_->journal("LoadManager")))
, mHashRouter (std::make_unique<HashRouter>(
@@ -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 (

View File

@@ -22,135 +22,11 @@
#include <ripple/app/ledger/Ledger.h>
#include <ripple/app/misc/Validations.h>
#include <ripple/core/ConfigSections.h>
#include <ripple/protocol/Protocol.h>
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<uint256, int> 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<uint256>& amendments) = 0;
/** Support only the specified amendments.
Other amendments in the table will be set to unsupported.
*/
virtual void setSupported (const std::vector<uint256>& 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 <uint256> const& enabled) = 0;
// Called by the consensus code when we need to
// inject pseudo-transactions
virtual std::map <uint256, std::uint32_t>
doVoting (
NetClock::time_point closeTime,
enabledAmendments_t const& enabledAmendments,
std::set <uint256> 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 <uint256>
doValidation (enabledAmendments_t const&) = 0;
doValidation (std::set <uint256> 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 <ReadView const> 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 <SHAMapItem> (txID, s.peekData());
initialPosition->addGiveItem (tItem, true, false);
initialPosition->addGiveItem (
std::make_shared <SHAMapItem> (
amendTx.getTransactionID(),
s.peekData()),
true,
false);
}
#endif
}
};
@@ -301,6 +148,9 @@ public:
std::unique_ptr<AmendmentTable> make_AmendmentTable (
std::chrono::seconds majorityTime,
int majorityFraction,
Section const& supported,
Section const& enabled,
Section const& vetoed,
beast::Journal journal);
} // ripple

View File

@@ -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 <BeastConfig.h>
#include <ripple/app/main/Application.h>
#include <ripple/app/misc/AmendmentTable.h>
#include <ripple/app/misc/Validations.h>
#include <ripple/basics/contract.h>
#include <ripple/core/DatabaseCon.h>
#include <ripple/core/ConfigSections.h>
#include <ripple/protocol/JsonFields.h>
#include <ripple/protocol/TxFlags.h>
#include <boost/format.hpp>
#include <boost/tokenizer.hpp>
#include <algorithm>
#include <mutex>
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<uint256, AmendmentState>;
using amendmentList_t = hash_set<uint256>;
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<uint256>& amendments) override;
void setSupported (const std::vector<uint256>& 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 <uint256>
doValidation (enabledAmendments_t const& enabledAmendments)
override;
std::map <uint256, std::uint32_t> 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<AmendmentName> 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<std::runtime_error> (errorMsg);
}
}
std::vector<AmendmentName> 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<std::string> 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<std::runtime_error> (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<std::runtime_error> (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 <std::mutex> 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<std::runtime_error> (errorMsg);
}
std::lock_guard <std::mutex> 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 <std::mutex> 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 <std::mutex> 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 <std::mutex> 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 <std::mutex> 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 <std::mutex> sl (mLock);
AmendmentState* s = getExisting (amendment);
return s && s->mEnabled;
}
bool
AmendmentTableImpl::isSupported (uint256 const& amendment)
{
std::lock_guard <std::mutex> sl (mLock);
AmendmentState* s = getExisting (amendment);
return s && s->mSupported;
}
AmendmentTableImpl::amendmentList_t
AmendmentTableImpl::getVetoed ()
{
amendmentList_t ret;
std::lock_guard <std::mutex> 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 <std::mutex> 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 <std::mutex> 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<uint256>& amendments)
{
std::lock_guard <std::mutex> 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<uint256>& amendments)
{
std::lock_guard <std::mutex> sl (mLock);
for (auto &e : m_amendmentMap)
{
e.second.mSupported = false;
}
for (auto const& e : amendments)
{
m_amendmentMap[e].mSupported = true;
}
}
std::vector <uint256>
AmendmentTableImpl::doValidation (
enabledAmendments_t const& enabledAmendments)
{
auto lAmendments = getDesired (enabledAmendments);
if (lAmendments.empty())
return {};
std::vector <uint256> amendments (lAmendments.begin(), lAmendments.end());
std::sort (amendments.begin (), amendments.end ());
return amendments;
}
std::map <uint256, std::uint32_t>
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 <uint256, std::uint32_t> actions;
{
std::lock_guard <std::mutex> 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 <std::mutex> 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 <std::mutex> 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 <std::mutex> 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 <std::mutex> sl(mLock);
AmendmentState& amendmentState = getCreate (amendmentID);
setJson (jAmendment, amendmentState);
}
return ret;
}
std::unique_ptr<AmendmentTable> make_AmendmentTable (
std::chrono::seconds majorityTime,
int majorityFraction,
beast::Journal journal)
{
return std::make_unique<AmendmentTableImpl>(
majorityTime, majorityFraction, journal);
}
} // ripple

View File

@@ -243,7 +243,6 @@ private:
JLOG (j_.trace) << "VC: " << ledger << "f:" << full << " p:" << partial;
}
int getTrustedValidationCount (uint256 const& ledger) override
{
int trusted = 0;

View File

@@ -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 <BeastConfig.h>
#include <ripple/app/main/Application.h>
#include <ripple/app/misc/AmendmentTable.h>
#include <ripple/app/misc/Validations.h>
#include <ripple/core/DatabaseCon.h>
#include <ripple/core/ConfigSections.h>
#include <ripple/protocol/JsonFields.h>
#include <ripple/protocol/TxFlags.h>
#include <boost/format.hpp>
#include <boost/regex.hpp>
#include <algorithm>
#include <mutex>
namespace ripple {
static
std::vector<std::pair<uint256, std::string>>
parseSection (Section const& section)
{
static boost::regex const re1 (
"^" // start of line
"(?:\\s*)" // whitespace (optional)
"([abcdefABCDEF0-9]{64})" // <hexadecimal amendment ID>
"(?:\\s+)" // whitespace
"(\\S+)" // <description>
, boost::regex_constants::optimize
);
std::vector<std::pair<uint256, std::string>> names;
for (auto const& line : section.lines ())
{
boost::smatch match;
if (!boost::regex_match (line, match, re1))
Throw<std::runtime_error> (
"Invalid entry '" + line +
"' in [" + section.name () + "]");
uint256 id;
if (!id.SetHexExact (match[1]))
Throw<std::runtime_error> (
"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<uint256, int> votes_;
public:
// number of trusted validations
int mTrustedValidations = 0;
// number of votes needed
int mThreshold = 0;
AmendmentSet () = default;
void tally (std::set<uint256> 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<uint256, AmendmentState> 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 <AmendmentSet> 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<uint256> const& enabled) override;
std::vector <uint256>
doValidation (std::set<uint256> const& enabledAmendments) override;
std::map <uint256, std::uint32_t>
doVoting (
NetClock::time_point closeTime,
std::set<uint256> 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 <std::mutex> 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 <std::mutex> 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 <std::mutex> 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 <std::mutex> 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 <std::mutex> 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 <std::mutex> 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 <std::mutex> sl (mutex_);
auto s = get (amendment);
return s && s->enabled;
}
bool
AmendmentTableImpl::isSupported (uint256 const& amendment)
{
std::lock_guard <std::mutex> sl (mutex_);
auto s = get (amendment);
return s && s->supported;
}
std::vector <uint256>
AmendmentTableImpl::doValidation (
std::set<uint256> const& enabled)
{
// Get the list of amendments we support and do not
// veto, but that are not already enabled
std::vector <uint256> amendments;
amendments.reserve (amendmentMap_.size());
{
std::lock_guard <std::mutex> 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 <uint256, std::uint32_t>
AmendmentTableImpl::doVoting (
NetClock::time_point closeTime,
std::set<uint256> 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 <AmendmentSet> ();
// process validations for ledger before flag ledger
for (auto const& entry : valSet)
{
if (entry.second->isTrusted ())
{
std::set<uint256> 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 <uint256, std::uint32_t> actions;
{
std::lock_guard <std::mutex> 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 <std::mutex> 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<uint256> const& enabled)
{
std::lock_guard <std::mutex> 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 <std::mutex> 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 <std::mutex> sl(mutex_);
auto a = add (amendmentID);
setJson (jAmendment, amendmentID, *a);
}
return ret;
}
std::unique_ptr<AmendmentTable> make_AmendmentTable (
std::chrono::seconds majorityTime,
int majorityFraction,
Section const& supported,
Section const& enabled,
Section const& vetoed,
beast::Journal journal)
{
return std::make_unique<AmendmentTableImpl> (
majorityTime,
majorityFraction,
supported,
enabled,
vetoed,
journal);
}
} // ripple

View File

@@ -22,11 +22,11 @@
#include <ripple/app/misc/AmendmentTable.h>
#include <ripple/basics/BasicConfig.h>
#include <ripple/basics/chrono.h>
#include <ripple/basics/contract.h>
#include <ripple/basics/Log.h>
#include <ripple/core/ConfigSections.h>
#include <ripple/protocol/PublicKey.h>
#include <ripple/protocol/SecretKey.h>
#include <ripple/protocol/digest.h>
#include <ripple/protocol/TxFlags.h>
#include <beast/unit_test/suite.h>
@@ -35,349 +35,299 @@ namespace ripple
class AmendmentTable_test final : public beast::unit_test::suite
{
public:
using StringPairVec = std::vector<std::pair<std::string, std::string>>;
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<std::string> 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<sha256_hasher::result_type>(h);
uint256 result;
std::memcpy(result.data(), d.data(), d.size());
return result;
}
static std::vector<AmendmentName> getAmendmentNames (
StringPairVec const& amendmentPairs)
static
std::vector<std::string>
createSet (int group, int count)
{
std::vector<AmendmentName> amendmentNames;
amendmentNames.reserve (amendmentPairs.size ());
for (auto const& i : amendmentPairs)
{
amendmentNames.emplace_back (i.first, i.second);
}
return amendmentNames;
std::vector<std::string> amendments;
for (int i = 0; i < count; i++)
amendments.push_back (
"Amendment" + std::to_string ((1000000 * group) + i));
return amendments;
}
std::vector<AmendmentName> populateTable (
AmendmentTable& table,
StringPairVec const& amendmentPairs,
TablePopulationAlgo populationAlgo = TablePopulationAlgo::addKnown)
static
Section
makeSection (std::vector<std::string> const& amendments)
{
std::vector<AmendmentName> const amendmentNames (
getAmendmentNames (amendmentPairs));
switch (populationAlgo)
{
case TablePopulationAlgo::addKnown:
for (auto const& i : amendmentNames)
{
table.addKnown (i);
}
break;
case TablePopulationAlgo::addInitial:
{
std::vector<std::string> 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<std::string> const m_set1;
std::vector<std::string> const m_set2;
std::vector<std::string> const m_set3;
std::vector<std::string> 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<AmendmentTable>
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<AmendmentTable>
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<AmendmentName> const amendmentNames (
populateTable (*table, m_knownAmendmentPairs));
std::vector<AmendmentName> 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<AmendmentName> const amendmentNames (populateTable (
*table, m_knownAmendmentPairs, tablePopulationAlgo));
std::vector<AmendmentName> 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<std::string> 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<std::string> 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<uint256, bool>
getState (
AmendmentTable *table,
std::set<uint256> const& exclude)
{
testcase ("enable");
auto table (makeTable (2));
std::vector<AmendmentName> 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<uint256, bool> state;
std::vector<uint256> toEnable;
for (auto const& i : amendmentNames)
auto track = [&state,table](std::vector<std::string> 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<uint256>& 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<AmendmentName> 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<uint256> 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<uint256> 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<AmendmentName> 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<uint256> toSupport;
toSupport.reserve (amendmentNames.size ());
std::vector<uint256> 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 <PublicKey> 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 <PublicKey> const& validators
, std::vector <std::pair <uint256, int> > const& votes
, std::vector <uint256>& ourVotes
, enabledAmendments_t& enabled
, majorityAmendments_t& majority)
void doRound(
AmendmentTable& table,
weeks week,
std::vector <PublicKey> const& validators,
std::vector <std::pair <uint256, int>> const& votes,
std::vector <uint256>& ourVotes,
std::set <uint256>& 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 <STValidation>
(uint256(), roundTime, val, true);
auto v = std::make_shared <STValidation> (
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<std::runtime_error> ("enabling already enabled");
throw std::runtime_error ("enabling already enabled");
if (majority.find (hash) == majority.end ())
Throw<std::runtime_error> ("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<std::runtime_error> ("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<std::runtime_error> ("lost majority without majority");
throw std::runtime_error ("lost majority without majority");
majority.erase (hash);
break;
default:
assert (false);
Throw<std::runtime_error> ("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 <std::pair <uint256, int>> votes;
std::vector <uint256> ourVotes;
enabledAmendments_t enabled;
std::set <uint256> 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 <std::pair <uint256, int>> votes;
std::vector <uint256> ourVotes;
enabledAmendments_t enabled;
std::set <uint256> 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 <std::pair <uint256, int>> votes;
std::vector <uint256> ourVotes;
enabledAmendments_t enabled;
std::set <uint256> 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 <uint256> 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 <uint256> 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 ();

View File

@@ -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

View File

@@ -130,8 +130,7 @@ cdirNext (ReadView const& view,
beast::Journal j);
// Return the list of enabled amendments
using enabledAmendments_t = std::set <uint256>;
enabledAmendments_t
std::set <uint256>
getEnabledAmendments (ReadView const& view);
// Return a map of amendments that have achieved majority

View File

@@ -469,16 +469,19 @@ cdirNext (ReadView const& view,
return true;
}
enabledAmendments_t
std::set <uint256>
getEnabledAmendments (ReadView const& view)
{
enabledAmendments_t amendments;
auto const sleAmendments = view.read(keylet::amendments());
std::set<uint256> 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<uint256>

View File

@@ -35,6 +35,7 @@
#include <ripple/protocol/types.h>
#include <ripple/server/ServerHandler.h>
#include <beast/module/core/text/LexicalCast.h>
#include <beast/utility/ci_char_traits.h>
#include <boost/asio/streambuf.hpp>
#include <boost/regex.hpp>
#include <iostream>
@@ -406,16 +407,28 @@ private:
return rpcError (rpcNO_EVENTS);
}
// feature [<feature>] [true|false]
// feature [<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 <bool> (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;
}

View File

@@ -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

View File

@@ -18,6 +18,7 @@
//==============================================================================
#include <BeastConfig.h>
#include <ripple/app/ledger/LedgerMaster.h>
#include <ripple/app/main/Application.h>
#include <ripple/app/misc/AmendmentTable.h>
#include <ripple/protocol/ErrorCodes.h>
@@ -28,32 +29,60 @@
namespace ripple {
// {
// feature : <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;
}

View File

@@ -20,6 +20,7 @@
#include <BeastConfig.h>
#include <ripple/app/main/BasicApp.cpp>
#include <ripple/app/main/Amendments.cpp>
#include <ripple/app/main/Application.cpp>
#include <ripple/app/main/CollectorManager.cpp>
#include <ripple/app/main/Main.cpp>

View File

@@ -19,7 +19,6 @@
#include <BeastConfig.h>
#include <ripple/app/misc/AmendmentTableImpl.cpp>
#include <ripple/app/misc/CanonicalTXSet.cpp>
#include <ripple/app/misc/FeeVoteImpl.cpp>
#include <ripple/app/misc/HashRouter.cpp>
@@ -28,6 +27,7 @@
#include <ripple/app/misc/Validations.cpp>
#include <ripple/app/misc/impl/AccountTxPaging.cpp>
#include <ripple/app/misc/impl/AmendmentTable.cpp>
#include <ripple/app/misc/impl/Transaction.cpp>
#include <ripple/app/misc/impl/TxQ.cpp>
#include <ripple/app/misc/impl/ValidatorList.cpp>