mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-18 18:15:50 +00:00
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:
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
48
src/ripple/app/main/Amendments.cpp
Normal file
48
src/ripple/app/main/Amendments.cpp
Normal 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
|
||||
{
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -243,7 +243,6 @@ private:
|
||||
JLOG (j_.trace) << "VC: " << ledger << "f:" << full << " p:" << partial;
|
||||
}
|
||||
|
||||
|
||||
int getTrustedValidationCount (uint256 const& ledger) override
|
||||
{
|
||||
int trusted = 0;
|
||||
|
||||
585
src/ripple/app/misc/impl/AmendmentTable.cpp
Normal file
585
src/ripple/app/misc/impl/AmendmentTable.cpp
Normal 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
|
||||
@@ -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 ();
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user