mirror of
https://github.com/Xahau/xahaud.git
synced 2025-12-06 17:27:52 +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>
|
</ClCompile>
|
||||||
<ClInclude Include="..\..\src\ripple\app\ledger\TransactionStateSF.h">
|
<ClInclude Include="..\..\src\ripple\app\ledger\TransactionStateSF.h">
|
||||||
</ClInclude>
|
</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">
|
<ClCompile Include="..\..\src\ripple\app\main\Application.cpp">
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
||||||
@@ -1308,10 +1312,6 @@
|
|||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\src\ripple\app\misc\AmendmentTable.h">
|
<ClInclude Include="..\..\src\ripple\app\misc\AmendmentTable.h">
|
||||||
</ClInclude>
|
</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">
|
<ClCompile Include="..\..\src\ripple\app\misc\CanonicalTXSet.cpp">
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
||||||
@@ -1336,6 +1336,10 @@
|
|||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClInclude Include="..\..\src\ripple\app\misc\impl\AccountTxPaging.h">
|
<ClInclude Include="..\..\src\ripple\app\misc\impl\AccountTxPaging.h">
|
||||||
</ClInclude>
|
</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">
|
<ClCompile Include="..\..\src\ripple\app\misc\impl\Transaction.cpp">
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
||||||
|
|||||||
@@ -1857,6 +1857,9 @@
|
|||||||
<ClInclude Include="..\..\src\ripple\app\ledger\TransactionStateSF.h">
|
<ClInclude Include="..\..\src\ripple\app\ledger\TransactionStateSF.h">
|
||||||
<Filter>ripple\app\ledger</Filter>
|
<Filter>ripple\app\ledger</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClCompile Include="..\..\src\ripple\app\main\Amendments.cpp">
|
||||||
|
<Filter>ripple\app\main</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="..\..\src\ripple\app\main\Application.cpp">
|
<ClCompile Include="..\..\src\ripple\app\main\Application.cpp">
|
||||||
<Filter>ripple\app\main</Filter>
|
<Filter>ripple\app\main</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
@@ -1908,9 +1911,6 @@
|
|||||||
<ClInclude Include="..\..\src\ripple\app\misc\AmendmentTable.h">
|
<ClInclude Include="..\..\src\ripple\app\misc\AmendmentTable.h">
|
||||||
<Filter>ripple\app\misc</Filter>
|
<Filter>ripple\app\misc</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClCompile Include="..\..\src\ripple\app\misc\AmendmentTableImpl.cpp">
|
|
||||||
<Filter>ripple\app\misc</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="..\..\src\ripple\app\misc\CanonicalTXSet.cpp">
|
<ClCompile Include="..\..\src\ripple\app\misc\CanonicalTXSet.cpp">
|
||||||
<Filter>ripple\app\misc</Filter>
|
<Filter>ripple\app\misc</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
@@ -1935,6 +1935,9 @@
|
|||||||
<ClInclude Include="..\..\src\ripple\app\misc\impl\AccountTxPaging.h">
|
<ClInclude Include="..\..\src\ripple\app\misc\impl\AccountTxPaging.h">
|
||||||
<Filter>ripple\app\misc\impl</Filter>
|
<Filter>ripple\app\misc\impl</Filter>
|
||||||
</ClInclude>
|
</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">
|
<ClCompile Include="..\..\src\ripple\app\misc\impl\Transaction.cpp">
|
||||||
<Filter>ripple\app\misc\impl</Filter>
|
<Filter>ripple\app\misc\impl</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
|||||||
@@ -1401,11 +1401,28 @@ void LedgerConsensusImp::takeInitialPosition (
|
|||||||
&& ((mPreviousLedger->info().seq % 256) == 0))
|
&& ((mPreviousLedger->info().seq % 256) == 0))
|
||||||
{
|
{
|
||||||
// previous ledger was flag ledger, add pseudo-transactions
|
// previous ledger was flag ledger, add pseudo-transactions
|
||||||
ValidationSet parentSet = app_.getValidations().getValidations (
|
auto const validations =
|
||||||
|
app_.getValidations().getValidations (
|
||||||
mPreviousLedger->info().parentHash);
|
mPreviousLedger->info().parentHash);
|
||||||
m_feeVote.doVoting (mPreviousLedger, parentSet, initialSet);
|
|
||||||
|
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 (
|
app_.getAmendmentTable ().doVoting (
|
||||||
mPreviousLedger, parentSet, initialSet);
|
mPreviousLedger,
|
||||||
|
validations,
|
||||||
|
initialSet);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set should be immutable snapshot
|
// 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
|
} // detail
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
@@ -453,10 +462,6 @@ public:
|
|||||||
, serverHandler_ (make_ServerHandler (*this, *m_networkOPs, get_io_service (),
|
, serverHandler_ (make_ServerHandler (*this, *m_networkOPs, get_io_service (),
|
||||||
*m_jobQueue, *m_networkOPs, *m_resourceManager, *m_collectorManager))
|
*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")))
|
, mFeeTrack (std::make_unique<LoadFeeTrack>(logs_->journal("LoadManager")))
|
||||||
|
|
||||||
, mHashRouter (std::make_unique<HashRouter>(
|
, mHashRouter (std::make_unique<HashRouter>(
|
||||||
@@ -962,8 +967,23 @@ void ApplicationImp::setup()
|
|||||||
if (!config_->RUN_STANDALONE)
|
if (!config_->RUN_STANDALONE)
|
||||||
updateTables ();
|
updateTables ();
|
||||||
|
|
||||||
m_amendmentTable->addInitial (
|
// Configure the amendments the server supports
|
||||||
config_->section (SECTION_AMENDMENTS));
|
{
|
||||||
|
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();
|
Pathfinder::initPathTable();
|
||||||
|
|
||||||
m_ledgerMaster->setMinValidations (
|
m_ledgerMaster->setMinValidations (
|
||||||
|
|||||||
@@ -22,135 +22,11 @@
|
|||||||
|
|
||||||
#include <ripple/app/ledger/Ledger.h>
|
#include <ripple/app/ledger/Ledger.h>
|
||||||
#include <ripple/app/misc/Validations.h>
|
#include <ripple/app/misc/Validations.h>
|
||||||
|
#include <ripple/core/ConfigSections.h>
|
||||||
#include <ripple/protocol/Protocol.h>
|
#include <ripple/protocol/Protocol.h>
|
||||||
|
|
||||||
namespace ripple {
|
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.
|
/** The amendment table stores the list of enabled and potential amendments.
|
||||||
Individuals amendments are voted on by validators during the consensus
|
Individuals amendments are voted on by validators during the consensus
|
||||||
process.
|
process.
|
||||||
@@ -158,30 +34,9 @@ class Section;
|
|||||||
class AmendmentTable
|
class AmendmentTable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/** Create a new AmendmentTable.
|
virtual ~AmendmentTable() = default;
|
||||||
|
|
||||||
@param majorityTime the number of seconds an amendment must hold a majority
|
virtual uint256 find (std::string const& name) = 0;
|
||||||
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 bool veto (uint256 const& amendment) = 0;
|
virtual bool veto (uint256 const& amendment) = 0;
|
||||||
virtual bool unVeto (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 isEnabled (uint256 const& amendment) = 0;
|
||||||
virtual bool isSupported (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;
|
virtual Json::Value getJson (int) = 0;
|
||||||
|
|
||||||
/** Returns a Json::objectValue. */
|
/** Returns a Json::objectValue. */
|
||||||
@@ -221,22 +67,23 @@ public:
|
|||||||
needValidatedLedger (LedgerIndex seq) = 0;
|
needValidatedLedger (LedgerIndex seq) = 0;
|
||||||
|
|
||||||
virtual void
|
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
|
// Called by the consensus code when we need to
|
||||||
// inject pseudo-transactions
|
// inject pseudo-transactions
|
||||||
virtual std::map <uint256, std::uint32_t>
|
virtual std::map <uint256, std::uint32_t>
|
||||||
doVoting (
|
doVoting (
|
||||||
NetClock::time_point closeTime,
|
NetClock::time_point closeTime,
|
||||||
enabledAmendments_t const& enabledAmendments,
|
std::set <uint256> const& enabledAmendments,
|
||||||
majorityAmendments_t const& majorityAmendments,
|
majorityAmendments_t const& majorityAmendments,
|
||||||
ValidationSet const& valSet) = 0;
|
ValidationSet const& valSet) = 0;
|
||||||
|
|
||||||
// Called by the consensus code when we need to
|
// Called by the consensus code when we need to
|
||||||
// add feature entries to a validation
|
// add feature entries to a validation
|
||||||
virtual std::vector <uint256>
|
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
|
// The two function below adapt the API callers expect to the
|
||||||
// internal amendment table API. This allows the amendment
|
// internal amendment table API. This allows the amendment
|
||||||
@@ -255,7 +102,6 @@ public:
|
|||||||
STVector256 (sfAmendments, ourAmendments));
|
STVector256 (sfAmendments, ourAmendments));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
doVoting (
|
doVoting (
|
||||||
std::shared_ptr <ReadView const> const& lastClosedLedger,
|
std::shared_ptr <ReadView const> const& lastClosedLedger,
|
||||||
@@ -269,6 +115,7 @@ public:
|
|||||||
getMajorityAmendments(*lastClosedLedger),
|
getMajorityAmendments(*lastClosedLedger),
|
||||||
parentValidations);
|
parentValidations);
|
||||||
|
|
||||||
|
#if RIPPLE_PROPOSE_AMENDMENTS
|
||||||
// Inject appropriate pseudo-transactions
|
// Inject appropriate pseudo-transactions
|
||||||
for (auto const& it : actions)
|
for (auto const& it : actions)
|
||||||
{
|
{
|
||||||
@@ -286,14 +133,14 @@ public:
|
|||||||
Serializer s;
|
Serializer s;
|
||||||
amendTx.add (s);
|
amendTx.add (s);
|
||||||
|
|
||||||
#if ! RIPPLE_PROPOSE_AMENDMENTS
|
initialPosition->addGiveItem (
|
||||||
return;
|
std::make_shared <SHAMapItem> (
|
||||||
#endif
|
amendTx.getTransactionID(),
|
||||||
|
s.peekData()),
|
||||||
uint256 txID = amendTx.getTransactionID();
|
true,
|
||||||
auto tItem = std::make_shared <SHAMapItem> (txID, s.peekData());
|
false);
|
||||||
initialPosition->addGiveItem (tItem, true, false);
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
@@ -301,6 +148,9 @@ public:
|
|||||||
std::unique_ptr<AmendmentTable> make_AmendmentTable (
|
std::unique_ptr<AmendmentTable> make_AmendmentTable (
|
||||||
std::chrono::seconds majorityTime,
|
std::chrono::seconds majorityTime,
|
||||||
int majorityFraction,
|
int majorityFraction,
|
||||||
|
Section const& supported,
|
||||||
|
Section const& enabled,
|
||||||
|
Section const& vetoed,
|
||||||
beast::Journal journal);
|
beast::Journal journal);
|
||||||
|
|
||||||
} // ripple
|
} // 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;
|
JLOG (j_.trace) << "VC: " << ledger << "f:" << full << " p:" << partial;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int getTrustedValidationCount (uint256 const& ledger) override
|
int getTrustedValidationCount (uint256 const& ledger) override
|
||||||
{
|
{
|
||||||
int trusted = 0;
|
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/app/misc/AmendmentTable.h>
|
||||||
#include <ripple/basics/BasicConfig.h>
|
#include <ripple/basics/BasicConfig.h>
|
||||||
#include <ripple/basics/chrono.h>
|
#include <ripple/basics/chrono.h>
|
||||||
#include <ripple/basics/contract.h>
|
|
||||||
#include <ripple/basics/Log.h>
|
#include <ripple/basics/Log.h>
|
||||||
#include <ripple/core/ConfigSections.h>
|
#include <ripple/core/ConfigSections.h>
|
||||||
#include <ripple/protocol/PublicKey.h>
|
#include <ripple/protocol/PublicKey.h>
|
||||||
#include <ripple/protocol/SecretKey.h>
|
#include <ripple/protocol/SecretKey.h>
|
||||||
|
#include <ripple/protocol/digest.h>
|
||||||
#include <ripple/protocol/TxFlags.h>
|
#include <ripple/protocol/TxFlags.h>
|
||||||
#include <beast/unit_test/suite.h>
|
#include <beast/unit_test/suite.h>
|
||||||
|
|
||||||
@@ -35,349 +35,299 @@ namespace ripple
|
|||||||
|
|
||||||
class AmendmentTable_test final : public beast::unit_test::suite
|
class AmendmentTable_test final : public beast::unit_test::suite
|
||||||
{
|
{
|
||||||
public:
|
|
||||||
using StringPairVec = std::vector<std::pair<std::string, std::string>>;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum class TablePopulationAlgo
|
|
||||||
{
|
|
||||||
addInitial,
|
|
||||||
addKnown
|
|
||||||
};
|
|
||||||
|
|
||||||
// 204/256 about 80% (we round down because the implementation rounds up)
|
// 204/256 about 80% (we round down because the implementation rounds up)
|
||||||
static int const majorityFraction{204};
|
static int const majorityFraction{204};
|
||||||
|
|
||||||
static void populateTable (AmendmentTable& table,
|
static
|
||||||
std::vector<std::string> const& configLines)
|
uint256
|
||||||
|
amendmentId (std::string in)
|
||||||
{
|
{
|
||||||
Section section (SECTION_AMENDMENTS);
|
sha256_hasher h;
|
||||||
section.append (configLines);
|
using beast::hash_append;
|
||||||
table.addInitial (section);
|
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 (
|
static
|
||||||
StringPairVec const& amendmentPairs)
|
std::vector<std::string>
|
||||||
|
createSet (int group, int count)
|
||||||
{
|
{
|
||||||
std::vector<AmendmentName> amendmentNames;
|
std::vector<std::string> amendments;
|
||||||
amendmentNames.reserve (amendmentPairs.size ());
|
for (int i = 0; i < count; i++)
|
||||||
for (auto const& i : amendmentPairs)
|
amendments.push_back (
|
||||||
{
|
"Amendment" + std::to_string ((1000000 * group) + i));
|
||||||
amendmentNames.emplace_back (i.first, i.second);
|
return amendments;
|
||||||
}
|
|
||||||
return amendmentNames;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<AmendmentName> populateTable (
|
static
|
||||||
AmendmentTable& table,
|
Section
|
||||||
StringPairVec const& amendmentPairs,
|
makeSection (std::vector<std::string> const& amendments)
|
||||||
TablePopulationAlgo populationAlgo = TablePopulationAlgo::addKnown)
|
|
||||||
{
|
{
|
||||||
std::vector<AmendmentName> const amendmentNames (
|
Section section ("Test");
|
||||||
getAmendmentNames (amendmentPairs));
|
for (auto const& a : amendments)
|
||||||
switch (populationAlgo)
|
section.append (to_string(amendmentId (a)) + " " + a);
|
||||||
{
|
return section;
|
||||||
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;
|
static
|
||||||
|
Section
|
||||||
|
makeSection (uint256 const& amendment)
|
||||||
|
{
|
||||||
|
Section section ("Test");
|
||||||
|
section.append (to_string (amendment) + " " + to_string(amendment));
|
||||||
|
return section;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::unique_ptr< AmendmentTable >
|
std::vector<std::string> const m_set1;
|
||||||
makeTable (int w)
|
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 (
|
return make_AmendmentTable (
|
||||||
weeks (w),
|
weeks (w),
|
||||||
majorityFraction,
|
majorityFraction,
|
||||||
|
supported,
|
||||||
|
enabled,
|
||||||
|
vetoed,
|
||||||
beast::Journal{});
|
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
|
void testConstruct ()
|
||||||
// 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"}})
|
|
||||||
{
|
{
|
||||||
|
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 ()
|
void testGet ()
|
||||||
{
|
{
|
||||||
testcase ("get");
|
testcase ("Name to ID mapping");
|
||||||
auto table (makeTable (2));
|
|
||||||
std::vector<AmendmentName> const amendmentNames (
|
auto table = makeTable (1);
|
||||||
populateTable (*table, m_knownAmendmentPairs));
|
|
||||||
std::vector<AmendmentName> const unknownAmendmentNames (
|
for (auto const& a : m_set1)
|
||||||
getAmendmentNames (m_unknownAmendmentPairs));
|
expect (table->find (a) == amendmentId (a));
|
||||||
for (auto const& i : amendmentNames)
|
for (auto const& a : m_set2)
|
||||||
{
|
expect (table->find (a) == amendmentId (a));
|
||||||
expect (table->get (i.friendlyName ()) == i.id ());
|
|
||||||
|
for (auto const& a : m_set3)
|
||||||
|
expect (!table->find (a));
|
||||||
|
for (auto const& a : m_set4)
|
||||||
|
expect (!table->find (a));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto const& i : unknownAmendmentNames)
|
void testBadConfig ()
|
||||||
{
|
{
|
||||||
expect (table->get (i.friendlyName ()) == uint256 ());
|
auto const section = makeSection (m_set1);
|
||||||
}
|
auto const id = to_string (amendmentId (m_set2[0]));
|
||||||
}
|
|
||||||
|
|
||||||
void testAddInitialAddKnown ()
|
testcase ("Bad Config");
|
||||||
{
|
|
||||||
testcase ("addInitialAddKnown");
|
|
||||||
|
|
||||||
for (auto tablePopulationAlgo :
|
{ // Two arguments are required - we pass one
|
||||||
{TablePopulationAlgo::addInitial, TablePopulationAlgo::addKnown})
|
Section test = section;
|
||||||
{
|
test.append (id);
|
||||||
{
|
|
||||||
// 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 ()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
// check that we throw an exception on bad hex pairs
|
|
||||||
StringPairVec const badHexPairs (
|
|
||||||
{{"a9f90e7cddbcadfedm8fc89ec4d02011c", "BadHex1"},
|
|
||||||
{"c956ccabf25151a16d77T3171c485423b", "BadHex2"},
|
|
||||||
{"6dcd528f057711c5d2Z6b57be28e23dfa", "BadHex3"}});
|
|
||||||
|
|
||||||
// make sure each element throws
|
|
||||||
for (auto const& i : badHexPairs)
|
|
||||||
{
|
|
||||||
StringPairVec v ({i});
|
|
||||||
auto table (makeTable (2));
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
populateTable (*table, v, tablePopulationAlgo);
|
if (makeTable (2, test, emptySection, emptySection))
|
||||||
// line above should throw
|
fail ("Accepted only amendment ID");
|
||||||
fail ("didn't throw");
|
|
||||||
}
|
}
|
||||||
catch (std::exception const&)
|
catch (...)
|
||||||
{
|
{
|
||||||
pass ();
|
pass();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // Two arguments are required - we pass three
|
||||||
|
Section test = section;
|
||||||
|
test.append (id + " Test Name");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
populateTable (
|
if (makeTable (2, test, emptySection, emptySection))
|
||||||
*table, badHexPairs, tablePopulationAlgo);
|
fail ("Accepted extra arguments");
|
||||||
// line above should throw
|
|
||||||
fail ("didn't throw");
|
|
||||||
}
|
}
|
||||||
catch (std::exception const&)
|
catch (...)
|
||||||
{
|
{
|
||||||
pass ();
|
pass();
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
// check that we thow on bad num tokens
|
auto sid = id;
|
||||||
std::vector<std::string> const badNumTokensConfigLines (
|
sid.resize (sid.length() - 1);
|
||||||
{"19f6d",
|
|
||||||
"19fd6 bad friendly name"
|
Section test = section;
|
||||||
"9876 one two"});
|
test.append (sid + " Name");
|
||||||
|
|
||||||
// make sure each element throws
|
|
||||||
for (auto const& i : badNumTokensConfigLines)
|
|
||||||
{
|
|
||||||
std::vector<std::string> v ({i});
|
|
||||||
auto table (makeTable (2));
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
populateTable (*table, v);
|
if (makeTable (2, test, emptySection, emptySection))
|
||||||
// line above should throw
|
fail ("Accepted short amendment ID");
|
||||||
fail ("didn't throw");
|
|
||||||
}
|
}
|
||||||
catch (std::exception const&)
|
catch (...)
|
||||||
{
|
{
|
||||||
pass ();
|
pass();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto sid = id;
|
||||||
|
sid.resize (sid.length() + 1, '0');
|
||||||
|
|
||||||
|
Section test = section;
|
||||||
|
test.append (sid + " Name");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
populateTable (*table, badNumTokensConfigLines);
|
if (makeTable (2, test, emptySection, emptySection))
|
||||||
// line above should throw
|
fail ("Accepted long amendment ID");
|
||||||
fail ("didn't throw");
|
|
||||||
}
|
}
|
||||||
catch (std::exception const&)
|
catch (...)
|
||||||
{
|
{
|
||||||
pass ();
|
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");
|
std::map<uint256, bool> state;
|
||||||
auto table (makeTable (2));
|
|
||||||
std::vector<AmendmentName> const amendmentNames (
|
auto track = [&state,table](std::vector<std::string> const& v)
|
||||||
populateTable (*table, m_knownAmendmentPairs));
|
|
||||||
{
|
{
|
||||||
// enable/disable tests
|
for (auto const& a : v)
|
||||||
for (auto const& i : amendmentNames)
|
|
||||||
{
|
{
|
||||||
auto id (i.id ());
|
auto const id = amendmentId(a);
|
||||||
table->enable (id);
|
state[id] = table->isEnabled (id);
|
||||||
expect (table->isEnabled (id));
|
}
|
||||||
table->disable (id);
|
};
|
||||||
expect (!table->isEnabled (id));
|
|
||||||
table->enable (id);
|
track (m_set1);
|
||||||
expect (table->isEnabled (id));
|
track (m_set2);
|
||||||
|
track (m_set3);
|
||||||
|
track (m_set4);
|
||||||
|
|
||||||
|
for (auto const& a : exclude)
|
||||||
|
state.erase(a);
|
||||||
|
|
||||||
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<uint256> toEnable;
|
void testEnableDisable ()
|
||||||
for (auto const& i : amendmentNames)
|
|
||||||
{
|
{
|
||||||
auto id (i.id ());
|
testcase ("enable & disable");
|
||||||
toEnable.emplace_back (id);
|
|
||||||
table->disable (id);
|
|
||||||
expect (!table->isEnabled (id));
|
|
||||||
}
|
|
||||||
table->setEnabled (toEnable);
|
|
||||||
for (auto const& i : toEnable)
|
|
||||||
{
|
|
||||||
expect (table->isEnabled (i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
using ATSetter =
|
auto const testAmendment = amendmentId("TestAmendment");
|
||||||
void (AmendmentTable::*)(const std::vector<uint256>& amendments);
|
auto table = makeTable (2);
|
||||||
using ATGetter = bool (AmendmentTable::*)(uint256 const& amendment);
|
|
||||||
void testVectorSetUnset (ATSetter setter, ATGetter getter)
|
|
||||||
{
|
|
||||||
auto table (makeTable (2));
|
|
||||||
// make pointer to ref syntax a little nicer
|
|
||||||
auto& tableRef (*table);
|
|
||||||
std::vector<AmendmentName> const amendmentNames (
|
|
||||||
populateTable (tableRef, m_knownAmendmentPairs));
|
|
||||||
|
|
||||||
// they should all be set
|
// Subset of amendments to enable
|
||||||
for (auto const& i : amendmentNames)
|
std::set<uint256> enabled;
|
||||||
{
|
enabled.insert (testAmendment);
|
||||||
expect ((tableRef.*getter)(i.id ())); // i.e. "isSupported"
|
enabled.insert (amendmentId(m_set1[0]));
|
||||||
}
|
enabled.insert (amendmentId(m_set2[0]));
|
||||||
|
enabled.insert (amendmentId(m_set3[0]));
|
||||||
|
enabled.insert (amendmentId(m_set4[0]));
|
||||||
|
|
||||||
{
|
// Get the state before, excluding the items we'll change:
|
||||||
// only set every other amendment
|
auto const pre_state = getState (table.get(), enabled);
|
||||||
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));
|
|
||||||
|
|
||||||
std::vector<AmendmentName> const amendmentNames (
|
// Enable the subset and verify
|
||||||
populateTable (*table, m_knownAmendmentPairs));
|
for (auto const& a : enabled)
|
||||||
|
table->enable (a);
|
||||||
|
|
||||||
{
|
for (auto const& a : enabled)
|
||||||
// support every even amendment
|
expect (table->isEnabled (a));
|
||||||
// enable every odd amendment
|
|
||||||
std::vector<uint256> toSupport;
|
// Disable the subset and verify
|
||||||
toSupport.reserve (amendmentNames.size ());
|
for (auto const& a : enabled)
|
||||||
std::vector<uint256> toEnable;
|
table->disable (a);
|
||||||
toEnable.reserve (amendmentNames.size ());
|
|
||||||
for (int i = 0; i < amendmentNames.size (); ++i)
|
for (auto const& a : enabled)
|
||||||
{
|
expect (!table->isEnabled (a));
|
||||||
if (i % 2)
|
|
||||||
{
|
// Get the state after, excluding the items we changed:
|
||||||
toSupport.emplace_back (amendmentNames[i].id ());
|
auto const post_state = getState (table.get(), enabled);
|
||||||
}
|
|
||||||
else
|
// Ensure the states are identical
|
||||||
{
|
auto ret = std::mismatch(
|
||||||
toEnable.emplace_back (amendmentNames[i].id ());
|
pre_state.begin(), pre_state.end(),
|
||||||
}
|
post_state.begin(), post_state.end());
|
||||||
}
|
|
||||||
table->setEnabled (toEnable);
|
expect (ret.first == pre_state.end());
|
||||||
table->setSupported (toSupport);
|
expect (ret.second == post_state.end());
|
||||||
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 ())));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector <PublicKey> makeValidators (int num)
|
std::vector <PublicKey> makeValidators (int num)
|
||||||
@@ -398,14 +348,14 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Execute a pretend consensus round for a flag ledger
|
// Execute a pretend consensus round for a flag ledger
|
||||||
void doRound
|
void doRound(
|
||||||
( AmendmentTable& table
|
AmendmentTable& table,
|
||||||
, weeks week
|
weeks week,
|
||||||
, std::vector <PublicKey> const& validators
|
std::vector <PublicKey> const& validators,
|
||||||
, std::vector <std::pair <uint256, int> > const& votes
|
std::vector <std::pair <uint256, int>> const& votes,
|
||||||
, std::vector <uint256>& ourVotes
|
std::vector <uint256>& ourVotes,
|
||||||
, enabledAmendments_t& enabled
|
std::set <uint256>& enabled,
|
||||||
, majorityAmendments_t& majority)
|
majorityAmendments_t& majority)
|
||||||
{
|
{
|
||||||
// Do a round at the specified time
|
// Do a round at the specified time
|
||||||
// Returns the amendments we voted for
|
// Returns the amendments we voted for
|
||||||
@@ -427,9 +377,8 @@ public:
|
|||||||
int i = 0;
|
int i = 0;
|
||||||
for (auto const& val : validators)
|
for (auto const& val : validators)
|
||||||
{
|
{
|
||||||
STValidation::pointer v =
|
auto v = std::make_shared <STValidation> (
|
||||||
std::make_shared <STValidation>
|
uint256(), roundTime, val, true);
|
||||||
(uint256(), roundTime, val, true);
|
|
||||||
|
|
||||||
++i;
|
++i;
|
||||||
STVector256 field (sfAmendments);
|
STVector256 field (sfAmendments);
|
||||||
@@ -451,7 +400,8 @@ public:
|
|||||||
|
|
||||||
ourVotes = table.doValidation (enabled);
|
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)
|
for (auto const& action : actions)
|
||||||
{
|
{
|
||||||
// This code assumes other validators do as we do
|
// This code assumes other validators do as we do
|
||||||
@@ -462,47 +412,47 @@ public:
|
|||||||
case 0:
|
case 0:
|
||||||
// amendment goes from majority to enabled
|
// amendment goes from majority to enabled
|
||||||
if (enabled.find (hash) != enabled.end ())
|
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 ())
|
if (majority.find (hash) == majority.end ())
|
||||||
Throw<std::runtime_error> ("enabling without majority");
|
throw std::runtime_error ("enabling without majority");
|
||||||
enabled.insert (hash);
|
enabled.insert (hash);
|
||||||
majority.erase (hash);
|
majority.erase (hash);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case tfGotMajority:
|
case tfGotMajority:
|
||||||
if (majority.find (hash) != majority.end ())
|
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;
|
majority[hash] = roundTime;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case tfLostMajority:
|
case tfLostMajority:
|
||||||
if (majority.find (hash) == majority.end ())
|
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);
|
majority.erase (hash);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
assert (false);
|
throw std::runtime_error ("unknown action");
|
||||||
Throw<std::runtime_error> ("unknown action");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// No vote on unknown amendment
|
// No vote on unknown amendment
|
||||||
void testNoUnknown ()
|
void testNoOnUnknown ()
|
||||||
{
|
{
|
||||||
testcase ("voteNoUnknown");
|
testcase ("Vote NO on unknown");
|
||||||
|
|
||||||
auto table (makeTable (2));
|
|
||||||
|
|
||||||
|
auto const testAmendment = amendmentId("TestAmendment");
|
||||||
auto const validators = makeValidators (10);
|
auto const validators = makeValidators (10);
|
||||||
|
|
||||||
uint256 testAmendment;
|
auto table = makeTable (2,
|
||||||
testAmendment.SetHex("6dcd528f057711c5d26b57be28e23dfa");
|
emptySection,
|
||||||
|
emptySection,
|
||||||
|
emptySection);
|
||||||
|
|
||||||
std::vector <std::pair <uint256, int>> votes;
|
std::vector <std::pair <uint256, int>> votes;
|
||||||
std::vector <uint256> ourVotes;
|
std::vector <uint256> ourVotes;
|
||||||
enabledAmendments_t enabled;
|
std::set <uint256> enabled;
|
||||||
majorityAmendments_t majority;
|
majorityAmendments_t majority;
|
||||||
|
|
||||||
doRound (*table, weeks{1},
|
doRound (*table, weeks{1},
|
||||||
@@ -541,21 +491,22 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// No vote on vetoed amendment
|
// 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);
|
auto const validators = makeValidators (10);
|
||||||
|
|
||||||
uint256 testAmendment;
|
|
||||||
testAmendment.SetHex("6dcd528f057711c5d26b57be28e23dfa");
|
|
||||||
table->veto(testAmendment);
|
|
||||||
|
|
||||||
std::vector <std::pair <uint256, int>> votes;
|
std::vector <std::pair <uint256, int>> votes;
|
||||||
std::vector <uint256> ourVotes;
|
std::vector <uint256> ourVotes;
|
||||||
enabledAmendments_t enabled;
|
std::set <uint256> enabled;
|
||||||
majorityAmendments_t majority;
|
majorityAmendments_t majority;
|
||||||
|
|
||||||
doRound (*table, weeks{1},
|
doRound (*table, weeks{1},
|
||||||
@@ -596,15 +547,16 @@ public:
|
|||||||
{
|
{
|
||||||
testcase ("voteEnable");
|
testcase ("voteEnable");
|
||||||
|
|
||||||
auto table (makeTable (2));
|
auto table = makeTable (
|
||||||
auto const amendmentNames (
|
2,
|
||||||
populateTable (*table, m_knownAmendmentPairs));
|
makeSection (m_set1),
|
||||||
|
emptySection,
|
||||||
|
emptySection);
|
||||||
|
|
||||||
auto const validators = makeValidators (10);
|
auto const validators = makeValidators (10);
|
||||||
|
|
||||||
std::vector <std::pair <uint256, int>> votes;
|
std::vector <std::pair <uint256, int>> votes;
|
||||||
std::vector <uint256> ourVotes;
|
std::vector <uint256> ourVotes;
|
||||||
enabledAmendments_t enabled;
|
std::set <uint256> enabled;
|
||||||
majorityAmendments_t majority;
|
majorityAmendments_t majority;
|
||||||
|
|
||||||
// Week 1: We should vote for all known amendments not enabled
|
// Week 1: We should vote for all known amendments not enabled
|
||||||
@@ -614,14 +566,15 @@ public:
|
|||||||
ourVotes,
|
ourVotes,
|
||||||
enabled,
|
enabled,
|
||||||
majority);
|
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");
|
expect (enabled.empty(), "Enabled amendment for no reason");
|
||||||
for (auto const& i : amendmentNames)
|
for (auto const& i : m_set1)
|
||||||
expect(majority.find(i.id()) == majority.end(), "majority detected for no reaosn");
|
expect(majority.find(amendmentId (i)) == majority.end(),
|
||||||
|
"majority detected for no reason");
|
||||||
|
|
||||||
// Now, everyone votes for this feature
|
// Now, everyone votes for this feature
|
||||||
for (auto const& i : amendmentNames)
|
for (auto const& i : m_set1)
|
||||||
votes.emplace_back (i.id(), 256);
|
votes.emplace_back (amendmentId(i), 256);
|
||||||
|
|
||||||
// Week 2: We should recognize a majority
|
// Week 2: We should recognize a majority
|
||||||
doRound (*table, weeks{2},
|
doRound (*table, weeks{2},
|
||||||
@@ -630,10 +583,12 @@ public:
|
|||||||
ourVotes,
|
ourVotes,
|
||||||
enabled,
|
enabled,
|
||||||
majority);
|
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");
|
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
|
// Week 5: We should enable the amendment
|
||||||
doRound (*table, weeks{5},
|
doRound (*table, weeks{5},
|
||||||
@@ -642,7 +597,7 @@ public:
|
|||||||
ourVotes,
|
ourVotes,
|
||||||
enabled,
|
enabled,
|
||||||
majority);
|
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
|
// Week 6: We should remove it from our votes and from having a majority
|
||||||
doRound (*table, weeks{6},
|
doRound (*table, weeks{6},
|
||||||
@@ -651,25 +606,28 @@ public:
|
|||||||
ourVotes,
|
ourVotes,
|
||||||
enabled,
|
enabled,
|
||||||
majority);
|
majority);
|
||||||
expect (enabled.size() == amendmentNames.size(), "Disabled");
|
expect (enabled.size() == m_set1.size(), "Disabled");
|
||||||
expect (ourVotes.empty(), "Voted after enabling");
|
expect (ourVotes.empty(), "Voted after enabling");
|
||||||
for (auto const& i : amendmentNames)
|
for (auto const& i : m_set1)
|
||||||
expect(majority.find(i.id()) == majority.end(), "majority not removed");
|
expect(majority.find(amendmentId (i)) == majority.end(),
|
||||||
|
"majority not removed");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Detect majority at 80%, enable later
|
// Detect majority at 80%, enable later
|
||||||
void testDetectMajority ()
|
void testDetectMajority ()
|
||||||
{
|
{
|
||||||
testcase ("detectMajority");
|
testcase ("detectMajority");
|
||||||
auto table (makeTable (2));
|
|
||||||
|
|
||||||
uint256 testAmendment;
|
auto const testAmendment = amendmentId ("detectMajority");
|
||||||
testAmendment.SetHex("6dcd528f057711c5d26b57be28e23dfa");
|
auto table = makeTable (
|
||||||
table->addKnown({testAmendment, "testAmendment"});
|
2,
|
||||||
|
makeSection (testAmendment),
|
||||||
|
emptySection,
|
||||||
|
emptySection);
|
||||||
|
|
||||||
auto const validators = makeValidators (16);
|
auto const validators = makeValidators (16);
|
||||||
|
|
||||||
enabledAmendments_t enabled;
|
std::set <uint256> enabled;
|
||||||
majorityAmendments_t majority;
|
majorityAmendments_t majority;
|
||||||
|
|
||||||
for (int i = 0; i <= 17; ++i)
|
for (int i = 0; i <= 17; ++i)
|
||||||
@@ -683,25 +641,22 @@ public:
|
|||||||
doRound (*table, weeks{i},
|
doRound (*table, weeks{i},
|
||||||
validators, votes, ourVotes, enabled, majority);
|
validators, votes, ourVotes, enabled, majority);
|
||||||
|
|
||||||
if (i < 14)
|
if (i < 13)
|
||||||
{
|
{
|
||||||
// rounds 0-13
|
|
||||||
// We are voting yes, not enabled, no majority
|
// We are voting yes, not enabled, no majority
|
||||||
expect (!ourVotes.empty(), "We aren't voting");
|
expect (!ourVotes.empty(), "We aren't voting");
|
||||||
expect (enabled.empty(), "Enabled too early");
|
expect (enabled.empty(), "Enabled too early");
|
||||||
expect (majority.empty(), "Majority 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
|
// We have a majority, not enabled, keep voting
|
||||||
expect (!ourVotes.empty(), "We stopped voting");
|
expect (!ourVotes.empty(), "We stopped voting");
|
||||||
expect (!majority.empty(), "Failed to detect majority");
|
expect (!majority.empty(), "Failed to detect majority");
|
||||||
expect (enabled.empty(), "Enabled too early");
|
expect (enabled.empty(), "Enabled too early");
|
||||||
}
|
}
|
||||||
else if (i == 16) // round 16
|
else if (i == 15)
|
||||||
{
|
{
|
||||||
// round 16
|
|
||||||
// enable, keep voting, remove from majority
|
// enable, keep voting, remove from majority
|
||||||
expect (!ourVotes.empty(), "We stopped voting");
|
expect (!ourVotes.empty(), "We stopped voting");
|
||||||
expect (majority.empty(), "Failed to remove from majority");
|
expect (majority.empty(), "Failed to remove from majority");
|
||||||
@@ -709,7 +664,6 @@ public:
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// round 17
|
|
||||||
// Done, we should be enabled and not voting
|
// Done, we should be enabled and not voting
|
||||||
expect (ourVotes.empty(), "We did not stop voting");
|
expect (ourVotes.empty(), "We did not stop voting");
|
||||||
expect (majority.empty(), "Failed to revove from majority");
|
expect (majority.empty(), "Failed to revove from majority");
|
||||||
@@ -723,15 +677,16 @@ public:
|
|||||||
{
|
{
|
||||||
testcase ("lostMajority");
|
testcase ("lostMajority");
|
||||||
|
|
||||||
auto table (makeTable (8));
|
auto const testAmendment = amendmentId ("lostMajority");
|
||||||
|
|
||||||
uint256 testAmendment;
|
|
||||||
testAmendment.SetHex("6dcd528f057711c5d26b57be28e23dfa");
|
|
||||||
table->addKnown({testAmendment, "testAmendment"});
|
|
||||||
|
|
||||||
auto const validators = makeValidators (16);
|
auto const validators = makeValidators (16);
|
||||||
|
|
||||||
enabledAmendments_t enabled;
|
auto table = makeTable (
|
||||||
|
8,
|
||||||
|
makeSection (testAmendment),
|
||||||
|
emptySection,
|
||||||
|
emptySection);
|
||||||
|
|
||||||
|
std::set <uint256> enabled;
|
||||||
majorityAmendments_t majority;
|
majorityAmendments_t majority;
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -759,9 +714,8 @@ public:
|
|||||||
doRound (*table, weeks{i + 1},
|
doRound (*table, weeks{i + 1},
|
||||||
validators, votes, ourVotes, enabled, majority);
|
validators, votes, ourVotes, enabled, majority);
|
||||||
|
|
||||||
if (i < 6)
|
if (i < 8)
|
||||||
{
|
{
|
||||||
// rounds 1 to 5
|
|
||||||
// We are voting yes, not enabled, majority
|
// We are voting yes, not enabled, majority
|
||||||
expect (!ourVotes.empty(), "We aren't voting");
|
expect (!ourVotes.empty(), "We aren't voting");
|
||||||
expect (enabled.empty(), "Enabled for no reason");
|
expect (enabled.empty(), "Enabled for no reason");
|
||||||
@@ -769,7 +723,6 @@ public:
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// rounds 6 to 15
|
|
||||||
// No majority, not enabled, keep voting
|
// No majority, not enabled, keep voting
|
||||||
expect (!ourVotes.empty(), "We stopped voting");
|
expect (!ourVotes.empty(), "We stopped voting");
|
||||||
expect (majority.empty(), "Failed to detect loss of majority");
|
expect (majority.empty(), "Failed to detect loss of majority");
|
||||||
@@ -780,13 +733,12 @@ public:
|
|||||||
|
|
||||||
void run ()
|
void run ()
|
||||||
{
|
{
|
||||||
|
testConstruct();
|
||||||
testGet ();
|
testGet ();
|
||||||
testAddInitialAddKnown ();
|
testBadConfig ();
|
||||||
testEnable ();
|
testEnableDisable ();
|
||||||
testSupported ();
|
testNoOnUnknown ();
|
||||||
testSupportedEnabled ();
|
testNoOnVetoed ();
|
||||||
testNoUnknown ();
|
|
||||||
testNoVetoed ();
|
|
||||||
testVoteEnable ();
|
testVoteEnable ();
|
||||||
testDetectMajority ();
|
testDetectMajority ();
|
||||||
testLostMajority ();
|
testLostMajority ();
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ struct ConfigSection
|
|||||||
#define SECTION_VALIDATION_SEED "validation_seed"
|
#define SECTION_VALIDATION_SEED "validation_seed"
|
||||||
#define SECTION_WEBSOCKET_PING_FREQ "websocket_ping_frequency"
|
#define SECTION_WEBSOCKET_PING_FREQ "websocket_ping_frequency"
|
||||||
#define SECTION_VALIDATORS "validators"
|
#define SECTION_VALIDATORS "validators"
|
||||||
|
#define SECTION_VETO_AMENDMENTS "veto_amendments"
|
||||||
|
|
||||||
} // ripple
|
} // ripple
|
||||||
|
|
||||||
|
|||||||
@@ -130,8 +130,7 @@ cdirNext (ReadView const& view,
|
|||||||
beast::Journal j);
|
beast::Journal j);
|
||||||
|
|
||||||
// Return the list of enabled amendments
|
// Return the list of enabled amendments
|
||||||
using enabledAmendments_t = std::set <uint256>;
|
std::set <uint256>
|
||||||
enabledAmendments_t
|
|
||||||
getEnabledAmendments (ReadView const& view);
|
getEnabledAmendments (ReadView const& view);
|
||||||
|
|
||||||
// Return a map of amendments that have achieved majority
|
// Return a map of amendments that have achieved majority
|
||||||
|
|||||||
@@ -469,16 +469,19 @@ cdirNext (ReadView const& view,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
enabledAmendments_t
|
std::set <uint256>
|
||||||
getEnabledAmendments (ReadView const& view)
|
getEnabledAmendments (ReadView const& view)
|
||||||
{
|
{
|
||||||
enabledAmendments_t amendments;
|
std::set<uint256> amendments;
|
||||||
auto const sleAmendments = view.read(keylet::amendments());
|
|
||||||
|
|
||||||
if (sleAmendments)
|
if (auto const sle = view.read(keylet::amendments()))
|
||||||
{
|
{
|
||||||
for (auto const &a : sleAmendments->getFieldV256 (sfAmendments))
|
if (!sle->isFieldPresent (sfAmendments))
|
||||||
amendments.insert (a);
|
LogicError ("No amendments field is present");
|
||||||
|
|
||||||
|
auto const& v = sle->getFieldV256 (sfAmendments);
|
||||||
|
|
||||||
|
amendments.insert (v.begin(), v.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
return amendments;
|
return amendments;
|
||||||
@@ -487,20 +490,24 @@ getEnabledAmendments (ReadView const& view)
|
|||||||
majorityAmendments_t
|
majorityAmendments_t
|
||||||
getMajorityAmendments (ReadView const& view)
|
getMajorityAmendments (ReadView const& view)
|
||||||
{
|
{
|
||||||
|
majorityAmendments_t ret;
|
||||||
|
|
||||||
|
if (auto const sle = view.read(keylet::amendments()))
|
||||||
|
{
|
||||||
|
if (sle->isFieldPresent (sfMajorities))
|
||||||
|
{
|
||||||
using tp = NetClock::time_point;
|
using tp = NetClock::time_point;
|
||||||
using d = tp::duration;
|
using d = tp::duration;
|
||||||
majorityAmendments_t majorities;
|
|
||||||
auto const sleAmendments = view.read(keylet::amendments());
|
|
||||||
|
|
||||||
if (sleAmendments && sleAmendments->isFieldPresent (sfMajorities))
|
auto const majorities = sle->getFieldArray (sfMajorities);
|
||||||
{
|
|
||||||
auto const& majArray = sleAmendments->getFieldArray (sfMajorities);
|
for (auto const& m : majorities)
|
||||||
for (auto const& m : majArray)
|
ret[m.getFieldH256 (sfAmendment)] =
|
||||||
majorities[m.getFieldH256 (sfAmendment)] =
|
|
||||||
tp(d(m.getFieldU32(sfCloseTime)));
|
tp(d(m.getFieldU32(sfCloseTime)));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return majorities;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<uint256>
|
boost::optional<uint256>
|
||||||
|
|||||||
@@ -35,6 +35,7 @@
|
|||||||
#include <ripple/protocol/types.h>
|
#include <ripple/protocol/types.h>
|
||||||
#include <ripple/server/ServerHandler.h>
|
#include <ripple/server/ServerHandler.h>
|
||||||
#include <beast/module/core/text/LexicalCast.h>
|
#include <beast/module/core/text/LexicalCast.h>
|
||||||
|
#include <beast/utility/ci_char_traits.h>
|
||||||
#include <boost/asio/streambuf.hpp>
|
#include <boost/asio/streambuf.hpp>
|
||||||
#include <boost/regex.hpp>
|
#include <boost/regex.hpp>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
@@ -406,7 +407,7 @@ private:
|
|||||||
return rpcError (rpcNO_EVENTS);
|
return rpcError (rpcNO_EVENTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
// feature [<feature>] [true|false]
|
// feature [<feature>] [accept|reject]
|
||||||
Json::Value parseFeature (Json::Value const& jvParams)
|
Json::Value parseFeature (Json::Value const& jvParams)
|
||||||
{
|
{
|
||||||
Json::Value jvRequest (Json::objectValue);
|
Json::Value jvRequest (Json::objectValue);
|
||||||
@@ -415,7 +416,19 @@ private:
|
|||||||
jvRequest[jss::feature] = jvParams[0u].asString ();
|
jvRequest[jss::feature] = jvParams[0u].asString ();
|
||||||
|
|
||||||
if (jvParams.size () > 1)
|
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;
|
return jvRequest;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -236,6 +236,7 @@ JSS ( load_factor_net ); // out: NetworkOPs
|
|||||||
JSS ( load_fee ); // out: LoadFeeTrackImp
|
JSS ( load_fee ); // out: LoadFeeTrackImp
|
||||||
JSS ( local ); // out: resource/Logic.h
|
JSS ( local ); // out: resource/Logic.h
|
||||||
JSS ( local_txs ); // out: GetCounts
|
JSS ( local_txs ); // out: GetCounts
|
||||||
|
JSS ( majority ); // out: RPC feature
|
||||||
JSS ( marker ); // in/out: AccountTx, AccountOffers,
|
JSS ( marker ); // in/out: AccountTx, AccountOffers,
|
||||||
// AccountLines, AccountObjects,
|
// AccountLines, AccountObjects,
|
||||||
// LedgerData
|
// LedgerData
|
||||||
@@ -412,6 +413,7 @@ JSS ( validation_key ); // out: ValidationCreate, ValidationSeed
|
|||||||
JSS ( validation_public_key ); // out: ValidationCreate, ValidationSeed
|
JSS ( validation_public_key ); // out: ValidationCreate, ValidationSeed
|
||||||
JSS ( validation_quorum ); // out: NetworkOPs
|
JSS ( validation_quorum ); // out: NetworkOPs
|
||||||
JSS ( validation_seed ); // out: ValidationCreate, ValidationSeed
|
JSS ( validation_seed ); // out: ValidationCreate, ValidationSeed
|
||||||
|
JSS ( validations ); // out: AmendmentTableImpl
|
||||||
JSS ( value ); // out: STAmount
|
JSS ( value ); // out: STAmount
|
||||||
JSS ( version ); // out: RPCVersion
|
JSS ( version ); // out: RPCVersion
|
||||||
JSS ( vetoed ); // out: AmendmentTableImpl
|
JSS ( vetoed ); // out: AmendmentTableImpl
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
||||||
#include <BeastConfig.h>
|
#include <BeastConfig.h>
|
||||||
|
#include <ripple/app/ledger/LedgerMaster.h>
|
||||||
#include <ripple/app/main/Application.h>
|
#include <ripple/app/main/Application.h>
|
||||||
#include <ripple/app/misc/AmendmentTable.h>
|
#include <ripple/app/misc/AmendmentTable.h>
|
||||||
#include <ripple/protocol/ErrorCodes.h>
|
#include <ripple/protocol/ErrorCodes.h>
|
||||||
@@ -28,32 +29,60 @@
|
|||||||
|
|
||||||
namespace ripple {
|
namespace ripple {
|
||||||
|
|
||||||
|
|
||||||
|
// {
|
||||||
|
// feature : <feature>
|
||||||
|
// vetoed : true/false
|
||||||
|
// }
|
||||||
Json::Value doFeature (RPC::Context& context)
|
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))
|
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;
|
Json::Value jvReply = Json::objectValue;
|
||||||
jvReply[jss::features] = context.app.getAmendmentTable ().getJson(0);
|
jvReply[jss::features] = features;
|
||||||
return jvReply;
|
return jvReply;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint256 uFeature
|
auto feature = table.find (
|
||||||
= context.app.getAmendmentTable ().get(
|
|
||||||
context.params[jss::feature].asString());
|
context.params[jss::feature].asString());
|
||||||
|
|
||||||
if (uFeature.isZero ())
|
if (!feature &&
|
||||||
{
|
!feature.SetHexExact (context.params[jss::feature].asString ()))
|
||||||
uFeature.SetHex (context.params[jss::feature].asString ());
|
|
||||||
|
|
||||||
if (uFeature.isZero ())
|
|
||||||
return rpcError (rpcBAD_FEATURE);
|
return rpcError (rpcBAD_FEATURE);
|
||||||
|
|
||||||
|
if (context.params.isMember (jss::vetoed))
|
||||||
|
{
|
||||||
|
if (context.params[jss::vetoed].asBool ())
|
||||||
|
context.app.getAmendmentTable().veto (feature);
|
||||||
|
else
|
||||||
|
context.app.getAmendmentTable().unVeto(feature);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!context.params.isMember (jss::vote))
|
Json::Value jvReply = table.getJson(feature);
|
||||||
return context.app.getAmendmentTable ().getJson(uFeature);
|
|
||||||
|
|
||||||
// WRITEME
|
auto m = majorities.find (feature);
|
||||||
return rpcError (rpcNOT_SUPPORTED);
|
if (m != majorities.end())
|
||||||
|
jvReply [jss::majority] =
|
||||||
|
m->second.time_since_epoch().count();
|
||||||
|
|
||||||
|
return jvReply;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
#include <BeastConfig.h>
|
#include <BeastConfig.h>
|
||||||
|
|
||||||
#include <ripple/app/main/BasicApp.cpp>
|
#include <ripple/app/main/BasicApp.cpp>
|
||||||
|
#include <ripple/app/main/Amendments.cpp>
|
||||||
#include <ripple/app/main/Application.cpp>
|
#include <ripple/app/main/Application.cpp>
|
||||||
#include <ripple/app/main/CollectorManager.cpp>
|
#include <ripple/app/main/CollectorManager.cpp>
|
||||||
#include <ripple/app/main/Main.cpp>
|
#include <ripple/app/main/Main.cpp>
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
|
|
||||||
#include <BeastConfig.h>
|
#include <BeastConfig.h>
|
||||||
|
|
||||||
#include <ripple/app/misc/AmendmentTableImpl.cpp>
|
|
||||||
#include <ripple/app/misc/CanonicalTXSet.cpp>
|
#include <ripple/app/misc/CanonicalTXSet.cpp>
|
||||||
#include <ripple/app/misc/FeeVoteImpl.cpp>
|
#include <ripple/app/misc/FeeVoteImpl.cpp>
|
||||||
#include <ripple/app/misc/HashRouter.cpp>
|
#include <ripple/app/misc/HashRouter.cpp>
|
||||||
@@ -28,6 +27,7 @@
|
|||||||
#include <ripple/app/misc/Validations.cpp>
|
#include <ripple/app/misc/Validations.cpp>
|
||||||
|
|
||||||
#include <ripple/app/misc/impl/AccountTxPaging.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/Transaction.cpp>
|
||||||
#include <ripple/app/misc/impl/TxQ.cpp>
|
#include <ripple/app/misc/impl/TxQ.cpp>
|
||||||
#include <ripple/app/misc/impl/ValidatorList.cpp>
|
#include <ripple/app/misc/impl/ValidatorList.cpp>
|
||||||
|
|||||||
Reference in New Issue
Block a user