Enable Amendments from config file or static data (RIPD-746):

* The rippled.cfg file has a new section called "amendments"

* Each line in this section contains two white-space separated items
** The first item is the ID of the amendment (a 256-bit hash)
** The second item is the friendly name

* Replaces config section name macros with variables

* Make addKnown arguments safer

* Added lock to addKnown
This commit is contained in:
Scott Determan
2015-01-09 09:49:18 -05:00
committed by Nik Bougalis
parent 312aec79ca
commit 44450bf644
8 changed files with 765 additions and 138 deletions

3
Builds/VisualStudio2013/RippleD.vcxproj Normal file → Executable file
View File

@@ -1990,6 +1990,9 @@
</ClCompile>
<ClInclude Include="..\..\src\ripple\app\misc\SHAMapStoreImp.h">
</ClInclude>
<ClCompile Include="..\..\src\ripple\app\misc\tests\AmendmentTable.test.cpp">
<ExcludedFromBuild>True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\ripple\app\misc\Validations.cpp">
<ExcludedFromBuild>True</ExcludedFromBuild>
</ClCompile>

6
Builds/VisualStudio2013/RippleD.vcxproj.filters Normal file → Executable file
View File

@@ -325,6 +325,9 @@
<Filter Include="ripple\app\misc">
<UniqueIdentifier>{5A1509B2-871B-A7AC-1E60-544D3F398741}</UniqueIdentifier>
</Filter>
<Filter Include="ripple\app\misc\tests">
<UniqueIdentifier>{815DC1A2-E2EF-E6E3-D979-19AD1476A28B}</UniqueIdentifier>
</Filter>
<Filter Include="ripple\app\node">
<UniqueIdentifier>{0FCD3973-E9A6-7172-C8A3-C3401E1A03DD}</UniqueIdentifier>
</Filter>
@@ -2964,6 +2967,9 @@
<ClInclude Include="..\..\src\ripple\app\misc\SHAMapStoreImp.h">
<Filter>ripple\app\misc</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ripple\app\misc\tests\AmendmentTable.test.cpp">
<Filter>ripple\app\misc\tests</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple\app\misc\Validations.cpp">
<Filter>ripple\app\misc</Filter>
</ClCompile>

View File

@@ -50,6 +50,7 @@
#include <ripple/json/json_reader.h>
#include <ripple/json/to_string.h>
#include <ripple/core/LoadFeeTrack.h>
#include <ripple/core/ConfigSections.h>
#include <ripple/net/SNTPClient.h>
#include <ripple/nodestore/Database.h>
#include <ripple/nodestore/DummyScheduler.h>
@@ -317,7 +318,8 @@ public:
, m_validators (Validators::make_Manager(*this, get_io_service(),
getConfig ().getModuleDatabasePath (), m_logs.journal("UVL")))
, m_amendmentTable (make_AmendmentTable (weeks(2), MAJORITY_FRACTION,
, m_amendmentTable (make_AmendmentTable
(weeks(2), MAJORITY_FRACTION,
m_logs.journal("AmendmentTable")))
, mFeeTrack (LoadFeeTrack::New (m_logs.journal("LoadManager")))
@@ -650,7 +652,8 @@ public:
if (!getConfig ().RUN_STANDALONE)
updateTables ();
m_amendmentTable->addInitial();
m_amendmentTable->addInitial (
getConfig ().section (SECTION_AMENDMENTS));
initializePathfinding ();
m_ledgerMaster->setMinValidations (getConfig ().VALIDATION_QUORUM);

View File

@@ -21,6 +21,7 @@
#define RIPPLE_AMENDMENT_TABLE_H
#include <ripple/app/book/Types.h>
#include <ripple/app/misc/Validations.h>
namespace ripple {
@@ -46,6 +47,48 @@ public:
}
};
/** 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.
@@ -53,22 +96,19 @@ public:
class AmendmentState
{
public:
bool mVetoed; // We don't want this amendment enabled
bool mEnabled;
bool mSupported;
bool mDefault; // Include in genesis ledger
bool mVetoed{false}; // We don't want this amendment enabled
bool mEnabled{false};
bool mSupported{false};
bool mDefault{false}; // Include in genesis ledger
core::Clock::time_point m_firstMajority; // First time we saw a majority (close time)
core::Clock::time_point m_lastMajority; // Most recent time we saw a majority (close time)
core::Clock::time_point
m_firstMajority{0}; // First time we saw a majority (close time)
core::Clock::time_point
m_lastMajority{0}; // Most recent time we saw a majority (close time)
std::string mFriendlyName;
AmendmentState ()
: mVetoed (false), mEnabled (false), mSupported (false), mDefault (false),
m_firstMajority (0), m_lastMajority (0)
{
;
}
AmendmentState () = default;
void setVeto ()
{
@@ -104,6 +144,8 @@ public:
}
};
class Section;
/** The amendment table stores the list of enabled and potential amendments.
Individuals amendments are voted on by validators during the consensus
process.
@@ -123,10 +165,17 @@ public:
virtual ~AmendmentTable() { }
virtual void addInitial () = 0;
/**
@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 AmendmentState* addKnown (const char* amendmentID,
const char* friendlyName, bool veto) = 0;
virtual uint256 get (std::string const& name) = 0;
virtual bool veto (uint256 const& amendment) = 0;
@@ -138,9 +187,17 @@ public:
virtual bool isEnabled (uint256 const& amendment) = 0;
virtual bool isSupported (uint256 const& amendment) = 0;
/** Enable only the specified amendments.
Other amendments in the table will be set to disabled.
*/
virtual void setEnabled (const std::vector<uint256>& amendments) = 0;
/** Support only the specified amendments.
Other amendments in the table will be set to unsupported.
*/
virtual void setSupported (const std::vector<uint256>& amendments) = 0;
/** Update the walletDB with the majority times.
*/
virtual void reportValidations (const AmendmentSet&) = 0;
virtual Json::Value getJson (int) = 0;
@@ -154,9 +211,11 @@ public:
doVoting (Ledger::ref lastClosedLedger, SHAMap::ref initialPosition) = 0;
};
std::unique_ptr<AmendmentTable>
make_AmendmentTable (std::chrono::seconds majorityTime, int majorityFraction,
beast::Journal journal);
std::unique_ptr<AmendmentTable> make_AmendmentTable (
std::chrono::seconds majorityTime,
int majorityFraction,
beast::Journal journal,
bool useMockFacade = false);
} // ripple

View File

@@ -19,24 +19,24 @@
#include <BeastConfig.h>
#include <ripple/app/misc/AmendmentTable.h>
#include <ripple/app/main/Application.h>
#include <ripple/app/misc/Validations.h>
#include <ripple/app/data/DatabaseCon.h>
#include <ripple/core/ConfigSections.h>
#include <boost/format.hpp>
#include <boost/tokenizer.hpp>
namespace ripple {
/** Track the list of "amendments"
An "amendment" is an option that can affect transaction processing
rules that is identified by a 256-bit amendment identifier
and adopted, or rejected, by the network.
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 : public AmendmentTable
template<class AppApiFacade>
class AmendmentTableImpl final : public AmendmentTable
{
protected:
typedef hash_map<uint256, AmendmentState> amendmentMap_t;
typedef std::pair<const uint256, AmendmentState> amendmentIt_t;
typedef hash_set<uint256> amendmentList_t;
typedef RippleMutex LockType;
@@ -49,13 +49,17 @@ protected:
core::Clock::time_point m_firstReport; // close time of first majority report
core::Clock::time_point m_lastReport; // close time of most recent majority report
beast::Journal m_journal;
AppApiFacade m_appApiFacade;
AmendmentState* getCreate (uint256 const& amendment, bool create);
AmendmentState& getCreate (uint256 const& amendment);
AmendmentState* getExisting (uint256 const& amendment);
bool shouldEnable (std::uint32_t closeTime, const AmendmentState& fs);
void setJson (Json::Value& v, const AmendmentState&);
public:
AmendmentTableImpl (std::chrono::seconds majorityTime, int majorityFraction,
AmendmentTableImpl (
std::chrono::seconds majorityTime,
int majorityFraction,
beast::Journal journal)
: m_majorityTime (majorityTime)
, mMajorityFraction (majorityFraction)
@@ -65,10 +69,10 @@ public:
{
}
void addInitial () override;
void addInitial (Section const& section) override;
void addKnown (AmendmentName const& name) override;
AmendmentState* addKnown (const char* amendmentID, const char* friendlyName,
bool veto) override;
uint256 get (std::string const& name) override;
bool veto (uint256 const& amendment) override;
@@ -97,51 +101,114 @@ public:
amendmentList_t getDesired(); // amendments we support, do not veto, are not enabled
};
void
AmendmentTableImpl::addInitial ()
namespace detail
{
// For each amendment this version supports, construct the AmendmentState object by calling
// addKnown. Set any vetoes or defaults. A pointer to the AmendmentState can be stashed
/** 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;
}
AmendmentState*
AmendmentTableImpl::getCreate (uint256 const& amendmentHash, bool create)
template<class AppApiFacade>
void
AmendmentTableImpl<AppApiFacade>::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 ());
}
}
template<class AppApiFacade>
AmendmentState&
AmendmentTableImpl<AppApiFacade>::getCreate (uint256 const& amendmentHash)
{
// call with the mutex held
auto iter (m_amendmentMap.find (amendmentHash));
if (iter == m_amendmentMap.end())
{
if (!create)
return nullptr;
AmendmentState* amendment = & (m_amendmentMap[amendmentHash]);
{
std::string query = "SELECT FirstMajority,LastMajority FROM Features WHERE hash='";
query.append (to_string (amendmentHash));
query.append ("';");
auto sl (getApp().getWalletDB ().lock ());
auto db = getApp().getWalletDB ().getDB ();
if (db->executeSQL (query) && db->startIterRows ())
{
amendment->m_firstMajority = db->getBigInt("FirstMajority");
amendment->m_lastMajority = db->getBigInt("LastMajority");
db->endIterRows ();
}
}
AmendmentState& amendment = m_amendmentMap[amendmentHash];
m_appApiFacade.setMajorityTimesFromDBToState (amendment, amendmentHash);
return amendment;
}
return iter->second;
}
template<class AppApiFacade>
AmendmentState*
AmendmentTableImpl<AppApiFacade>::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);
}
template<class AppApiFacade>
uint256
AmendmentTableImpl::get (std::string const& name)
AmendmentTableImpl<AppApiFacade>::get (std::string const& name)
{
ScopedLockType sl (mLock);
for (auto const& e : m_amendmentMap)
{
if (name == e.second.mFriendlyName)
@@ -151,48 +218,50 @@ AmendmentTableImpl::get (std::string const& name)
return uint256 ();
}
AmendmentState*
AmendmentTableImpl::addKnown (const char* amendmentID, const char* friendlyName,
bool veto)
template<class AppApiFacade>
void
AmendmentTableImpl<AppApiFacade>::addKnown (AmendmentName const& name)
{
uint256 hash;
hash.SetHex (amendmentID);
if (hash.isZero ())
if (!name.valid ())
{
assert (false);
return nullptr;
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);
}
AmendmentState* f = getCreate (hash, true);
ScopedLockType sl (mLock);
AmendmentState& amendment = getCreate (name.id ());
if (friendlyName != nullptr)
f->setFriendlyName (friendlyName);
if (!name.friendlyName ().empty ())
amendment.setFriendlyName (name.friendlyName ());
f->mVetoed = veto;
f->mSupported = true;
return f;
amendment.mVetoed = false;
amendment.mSupported = true;
}
template<class AppApiFacade>
bool
AmendmentTableImpl::veto (uint256 const& amendment)
AmendmentTableImpl<AppApiFacade>::veto (uint256 const& amendment)
{
ScopedLockType sl (mLock);
AmendmentState* s = getCreate (amendment, true);
AmendmentState& s = getCreate (amendment);
if (s->mVetoed)
if (s.mVetoed)
return false;
s->mVetoed = true;
s.mVetoed = true;
return true;
}
template<class AppApiFacade>
bool
AmendmentTableImpl::unVeto (uint256 const& amendment)
AmendmentTableImpl<AppApiFacade>::unVeto (uint256 const& amendment)
{
ScopedLockType sl (mLock);
AmendmentState* s = getCreate (amendment, false);
AmendmentState* s = getExisting (amendment);
if (!s || !s->mVetoed)
return false;
@@ -201,24 +270,26 @@ AmendmentTableImpl::unVeto (uint256 const& amendment)
return true;
}
template<class AppApiFacade>
bool
AmendmentTableImpl::enable (uint256 const& amendment)
AmendmentTableImpl<AppApiFacade>::enable (uint256 const& amendment)
{
ScopedLockType sl (mLock);
AmendmentState* s = getCreate (amendment, true);
AmendmentState& s = getCreate (amendment);
if (s->mEnabled)
if (s.mEnabled)
return false;
s->mEnabled = true;
s.mEnabled = true;
return true;
}
template<class AppApiFacade>
bool
AmendmentTableImpl::disable (uint256 const& amendment)
AmendmentTableImpl<AppApiFacade>::disable (uint256 const& amendment)
{
ScopedLockType sl (mLock);
AmendmentState* s = getCreate (amendment, false);
AmendmentState* s = getExisting (amendment);
if (!s || !s->mEnabled)
return false;
@@ -227,24 +298,27 @@ AmendmentTableImpl::disable (uint256 const& amendment)
return true;
}
template<class AppApiFacade>
bool
AmendmentTableImpl::isEnabled (uint256 const& amendment)
AmendmentTableImpl<AppApiFacade>::isEnabled (uint256 const& amendment)
{
ScopedLockType sl (mLock);
AmendmentState* s = getCreate (amendment, false);
AmendmentState* s = getExisting (amendment);
return s && s->mEnabled;
}
template<class AppApiFacade>
bool
AmendmentTableImpl::isSupported (uint256 const& amendment)
AmendmentTableImpl<AppApiFacade>::isSupported (uint256 const& amendment)
{
ScopedLockType sl (mLock);
AmendmentState* s = getCreate (amendment, false);
AmendmentState* s = getExisting (amendment);
return s && s->mSupported;
}
AmendmentTableImpl::amendmentList_t
AmendmentTableImpl::getVetoed ()
template<class AppApiFacade>
typename AmendmentTableImpl<AppApiFacade>::amendmentList_t
AmendmentTableImpl<AppApiFacade>::getVetoed ()
{
amendmentList_t ret;
ScopedLockType sl (mLock);
@@ -256,8 +330,9 @@ AmendmentTableImpl::getVetoed ()
return ret;
}
AmendmentTableImpl::amendmentList_t
AmendmentTableImpl::getEnabled ()
template<class AppApiFacade>
typename AmendmentTableImpl<AppApiFacade>::amendmentList_t
AmendmentTableImpl<AppApiFacade>::getEnabled ()
{
amendmentList_t ret;
ScopedLockType sl (mLock);
@@ -269,8 +344,9 @@ AmendmentTableImpl::getEnabled ()
return ret;
}
template<class AppApiFacade>
bool
AmendmentTableImpl::shouldEnable (std::uint32_t closeTime,
AmendmentTableImpl<AppApiFacade>::shouldEnable (std::uint32_t closeTime,
const AmendmentState& fs)
{
if (fs.mVetoed || fs.mEnabled || !fs.mSupported || (fs.m_lastMajority != m_lastReport))
@@ -286,8 +362,9 @@ AmendmentTableImpl::shouldEnable (std::uint32_t closeTime,
return (fs.m_lastMajority - fs.m_firstMajority) > m_majorityTime.count();
}
AmendmentTableImpl::amendmentList_t
AmendmentTableImpl::getToEnable (core::Clock::time_point closeTime)
template<class AppApiFacade>
typename AmendmentTableImpl<AppApiFacade>::amendmentList_t
AmendmentTableImpl<AppApiFacade>::getToEnable (core::Clock::time_point closeTime)
{
amendmentList_t ret;
ScopedLockType sl (mLock);
@@ -304,8 +381,9 @@ AmendmentTableImpl::getToEnable (core::Clock::time_point closeTime)
return ret;
}
AmendmentTableImpl::amendmentList_t
AmendmentTableImpl::getDesired ()
template<class AppApiFacade>
typename AmendmentTableImpl<AppApiFacade>::amendmentList_t
AmendmentTableImpl<AppApiFacade>::getDesired ()
{
amendmentList_t ret;
ScopedLockType sl (mLock);
@@ -319,8 +397,9 @@ AmendmentTableImpl::getDesired ()
return ret;
}
template<class AppApiFacade>
void
AmendmentTableImpl::reportValidations (const AmendmentSet& set)
AmendmentTableImpl<AppApiFacade>::reportValidations (const AmendmentSet& set)
{
if (set.mTrustedValidations == 0)
return;
@@ -335,7 +414,7 @@ AmendmentTableImpl::reportValidations (const AmendmentSet& set)
m_firstReport = set.mCloseTime;
std::vector<uint256> changedAmendments;
changedAmendments.resize(set.mVotes.size());
changedAmendments.reserve (set.mVotes.size());
for (auto const& e : set.mVotes)
{
@@ -378,27 +457,15 @@ AmendmentTableImpl::reportValidations (const AmendmentSet& set)
if (!changedAmendments.empty())
{
auto sl (getApp().getWalletDB ().lock ());
auto db = getApp().getWalletDB ().getDB ();
db->executeSQL ("BEGIN TRANSACTION;");
for (auto const& hash : changedAmendments)
{
AmendmentState& fState = m_amendmentMap[hash];
db->executeSQL (boost::str (boost::format (
"UPDATE Features SET FirstMajority = %d WHERE Hash = '%s';") %
fState.m_firstMajority % to_string (hash)));
db->executeSQL (boost::str (boost::format (
"UPDATE Features SET LastMajority = %d WHERE Hash = '%s';") %
fState.m_lastMajority % to_string(hash)));
}
db->executeSQL ("END TRANSACTION;");
m_appApiFacade.setMajorityTimesFromStateToDB (changedAmendments,
m_amendmentMap);
changedAmendments.clear ();
}
}
template<class AppApiFacade>
void
AmendmentTableImpl::setEnabled (const std::vector<uint256>& amendments)
AmendmentTableImpl<AppApiFacade>::setEnabled (const std::vector<uint256>& amendments)
{
ScopedLockType sl (mLock);
for (auto& e : m_amendmentMap)
@@ -411,8 +478,9 @@ AmendmentTableImpl::setEnabled (const std::vector<uint256>& amendments)
}
}
template<class AppApiFacade>
void
AmendmentTableImpl::setSupported (const std::vector<uint256>& amendments)
AmendmentTableImpl<AppApiFacade>::setSupported (const std::vector<uint256>& amendments)
{
ScopedLockType sl (mLock);
for (auto &e : m_amendmentMap)
@@ -425,8 +493,9 @@ AmendmentTableImpl::setSupported (const std::vector<uint256>& amendments)
}
}
template<class AppApiFacade>
void
AmendmentTableImpl::doValidation (Ledger::ref lastClosedLedger,
AmendmentTableImpl<AppApiFacade>::doValidation (Ledger::ref lastClosedLedger,
STObject& baseValidation)
{
amendmentList_t lAmendments = getDesired();
@@ -441,8 +510,9 @@ AmendmentTableImpl::doValidation (Ledger::ref lastClosedLedger,
baseValidation.setFieldV256 (sfAmendments, vAmendments);
}
template<class AppApiFacade>
void
AmendmentTableImpl::doVoting (Ledger::ref lastClosedLedger,
AmendmentTableImpl<AppApiFacade>::doVoting (Ledger::ref lastClosedLedger,
SHAMap::ref initialPosition)
{
@@ -452,7 +522,8 @@ AmendmentTableImpl::doVoting (Ledger::ref lastClosedLedger,
AmendmentSet amendmentSet (lastClosedLedger->getParentCloseTimeNC ());
// get validations for ledger before flag ledger
ValidationSet valSet = getApp().getValidations ().getValidations (lastClosedLedger->getParentHash ());
ValidationSet valSet = m_appApiFacade.getValidations (
lastClosedLedger->getParentHash ());
for (auto const& entry : valSet)
{
auto const& val = *entry.second;
@@ -500,8 +571,9 @@ AmendmentTableImpl::doVoting (Ledger::ref lastClosedLedger,
}
}
template<class AppApiFacade>
Json::Value
AmendmentTableImpl::getJson (int)
AmendmentTableImpl<AppApiFacade>::getJson (int)
{
Json::Value ret(Json::objectValue);
{
@@ -514,8 +586,9 @@ AmendmentTableImpl::getJson (int)
return ret;
}
template<class AppApiFacade>
void
AmendmentTableImpl::setJson (Json::Value& v, const AmendmentState& fs)
AmendmentTableImpl<AppApiFacade>::setJson (Json::Value& v, const AmendmentState& fs)
{
if (!fs.mFriendlyName.empty())
v["name"] = fs.mFriendlyName;
@@ -560,8 +633,9 @@ AmendmentTableImpl::setJson (Json::Value& v, const AmendmentState& fs)
v["veto"] = true;
}
template<class AppApiFacade>
Json::Value
AmendmentTableImpl::getJson (uint256 const& amendmentID)
AmendmentTableImpl<AppApiFacade>::getJson (uint256 const& amendmentID)
{
Json::Value ret = Json::objectValue;
Json::Value& jAmendment = (ret[to_string (amendmentID)] = Json::objectValue);
@@ -569,19 +643,108 @@ AmendmentTableImpl::getJson (uint256 const& amendmentID)
{
ScopedLockType sl(mLock);
AmendmentState *amendmentState = getCreate (amendmentID, true);
setJson (jAmendment, *amendmentState);
AmendmentState& amendmentState = getCreate (amendmentID);
setJson (jAmendment, amendmentState);
}
return ret;
}
std::unique_ptr<AmendmentTable>
make_AmendmentTable (std::chrono::seconds majorityTime, int majorityFraction,
beast::Journal journal)
namespace detail
{
return std::make_unique<AmendmentTableImpl> (majorityTime, majorityFraction,
journal);
class AppApiFacadeImpl final
{
public:
void setMajorityTimesFromDBToState (AmendmentState& toUpdate,
uint256 const& amendmentHash) const;
void setMajorityTimesFromStateToDB (
std::vector<uint256> const& changedAmendments,
hash_map<uint256, AmendmentState>& amendmentMap) const;
ValidationSet getValidations (uint256 const& hash) const;
};
class AppApiFacadeMock final
{
public:
void setMajorityTimesFromDBToState (AmendmentState& toUpdate,
uint256 const& amendmentHash) const {};
void setMajorityTimesFromStateToDB (
std::vector<uint256> const& changedAmendments,
hash_map<uint256, AmendmentState>& amendmentMap) const {};
ValidationSet getValidations (uint256 const& hash) const
{
return ValidationSet ();
};
};
void AppApiFacadeImpl::setMajorityTimesFromDBToState (
AmendmentState& toUpdate,
uint256 const& amendmentHash) const
{
std::string query =
"SELECT FirstMajority,LastMajority FROM Features WHERE hash='";
query.append (to_string (amendmentHash));
query.append ("';");
auto& walletDB (getApp ().getWalletDB ());
auto sl (walletDB.lock ());
auto db (walletDB.getDB ());
if (db->executeSQL (query) && db->startIterRows ())
{
toUpdate.m_firstMajority = db->getBigInt ("FirstMajority");
toUpdate.m_lastMajority = db->getBigInt ("LastMajority");
db->endIterRows ();
}
}
void AppApiFacadeImpl::setMajorityTimesFromStateToDB (
std::vector<uint256> const& changedAmendments,
hash_map<uint256, AmendmentState>& amendmentMap) const
{
if (changedAmendments.empty ())
return;
auto& walletDB (getApp ().getWalletDB ());
auto sl (walletDB.lock ());
auto db (walletDB.getDB ());
db->executeSQL ("BEGIN TRANSACTION;");
for (auto const& hash : changedAmendments)
{
AmendmentState const& fState = amendmentMap[hash];
db->executeSQL (boost::str (boost::format (
"UPDATE Features SET FirstMajority "
"= %d WHERE Hash = '%s';") %
fState.m_firstMajority % to_string (hash)));
db->executeSQL (boost::str (boost::format (
"UPDATE Features SET LastMajority "
"= %d WHERE Hash = '%s';") %
fState.m_lastMajority % to_string (hash)));
}
db->executeSQL ("END TRANSACTION;");
}
ValidationSet AppApiFacadeImpl::getValidations (uint256 const& hash) const
{
return getApp ().getValidations ().getValidations (hash);
}
} // detail
std::unique_ptr<AmendmentTable> make_AmendmentTable (
std::chrono::seconds majorityTime,
int majorityFraction,
beast::Journal journal,
bool useMockFacade)
{
if (useMockFacade)
{
return std::make_unique<AmendmentTableImpl<detail::AppApiFacadeMock>>(
majorityTime, majorityFraction, std::move (journal));
}
return std::make_unique<AmendmentTableImpl<detail::AppApiFacadeImpl>>(
majorityTime, majorityFraction, std::move (journal));
}
} // ripple

View File

@@ -0,0 +1,391 @@
//------------------------------------------------------------------------------
/*
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/misc/AmendmentTable.h>
#include <ripple/core/ConfigSections.h>
#include <beast/unit_test/suite.h>
namespace ripple
{
class AmendmentTable_test final : public beast::unit_test::suite
{
public:
using StringPairVec = std::vector<std::pair<std::string, std::string>>;
private:
enum class TablePopulationAlgo
{
addInitial,
addKnown
};
// 204/256 about 80%
static int const majorityFraction{204};
static void populateTable (AmendmentTable& table,
std::vector<std::string> const& configLines)
{
Section section (SECTION_AMENDMENTS);
section.append (configLines);
table.addInitial (section);
}
static std::vector<AmendmentName> getAmendmentNames (
StringPairVec const& amendmentPairs)
{
std::vector<AmendmentName> amendmentNames;
amendmentNames.reserve (amendmentPairs.size ());
for (auto const& i : amendmentPairs)
{
amendmentNames.emplace_back (i.first, i.second);
}
return amendmentNames;
}
std::vector<AmendmentName> populateTable (
AmendmentTable& table,
StringPairVec const& amendmentPairs,
TablePopulationAlgo populationAlgo = TablePopulationAlgo::addKnown)
{
std::vector<AmendmentName> const amendmentNames (
getAmendmentNames (amendmentPairs));
switch (populationAlgo)
{
case TablePopulationAlgo::addKnown:
for (auto const& i : amendmentNames)
{
table.addKnown (i);
}
break;
case TablePopulationAlgo::addInitial:
{
std::vector<std::string> configLines;
configLines.reserve (amendmentPairs.size ());
for (auto const& i : amendmentPairs)
{
configLines.emplace_back (i.first + " " + i.second);
}
populateTable (table, configLines);
}
break;
default:
fail ("Error in test case logic");
}
return amendmentNames;
}
static std::unique_ptr<AmendmentTable> makeTable ()
{
beast::Journal journal;
return make_AmendmentTable (
weeks (2),
majorityFraction,
journal,
/*useMock*/ true);
};
// Create the amendments by string pairs instead of AmendmentNames
// as this helps test the AmendmentNames class
StringPairVec const m_validAmendmentPairs;
StringPairVec const m_notAddedAmendmentPairs;
public:
AmendmentTable_test ()
: m_validAmendmentPairs (
{{"a49f90e7cddbcadfed8fc89ec4d02011", "Added1"},
{"ca956ccabf25151a16d773171c485423", "Added2"},
{"60dcd528f057711c5d26b57be28e23df", "Added3"},
{"da956ccabf25151a16d773171c485423", "Added4"},
{"70dcd528f057711c5d26b57be28e23df", "Added5"},
{"70dcd528f057711c5d26b57be28e23d0", "Added6"}})
, m_notAddedAmendmentPairs (
{{"a9f90e7cddbcadfed8fc89ec4d02011c", "NotAdded1"},
{"c956ccabf25151a16d773171c485423b", "NotAdded2"},
{"6dcd528f057711c5d26b57be28e23dfa", "NotAdded3"}})
{
}
void testGet ()
{
testcase ("get");
auto table (makeTable ());
std::vector<AmendmentName> const amendmentNames (
populateTable (*table, m_validAmendmentPairs));
std::vector<AmendmentName> const notAddedAmendmentNames (
getAmendmentNames (m_notAddedAmendmentPairs));
for (auto const& i : amendmentNames)
{
expect (table->get (i.friendlyName ()) == i.id ());
}
for (auto const& i : notAddedAmendmentNames)
{
expect (table->get (i.friendlyName ()) == uint256 ());
}
}
void testAddInitialAddKnown ()
{
testcase ("addInitialAddKnown");
for (auto tablePopulationAlgo :
{TablePopulationAlgo::addInitial, TablePopulationAlgo::addKnown})
{
{
// test that the amendments we add are enabled and amendments we
// didn't add are not enabled
auto table (makeTable ());
std::vector<AmendmentName> const amendmentNames (populateTable (
*table, m_validAmendmentPairs, tablePopulationAlgo));
std::vector<AmendmentName> const notAddedAmendmentNames (
getAmendmentNames (m_notAddedAmendmentPairs));
for (auto const& i : amendmentNames)
{
expect (table->isSupported (i.id ()));
if (tablePopulationAlgo == TablePopulationAlgo::addInitial)
expect (table->isEnabled (i.id ()));
}
for (auto const& i : notAddedAmendmentNames)
{
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 ());
try
{
populateTable (*table, v, tablePopulationAlgo);
// line above should throw
fail ("didn't throw");
}
catch (...)
{
pass ();
}
try
{
populateTable (
*table, badHexPairs, tablePopulationAlgo);
// line above should throw
fail ("didn't throw");
}
catch (...)
{
pass ();
}
}
}
}
{
// check that we thow on bad num tokens
std::vector<std::string> const badNumTokensConfigLines (
{"19f6d",
"19fd6 bad friendly name"
"9876 one two"});
// make sure each element throws
for (auto const& i : badNumTokensConfigLines)
{
std::vector<std::string> v ({i});
auto table (makeTable ());
try
{
populateTable (*table, v);
// line above should throw
fail ("didn't throw");
}
catch (...)
{
pass ();
}
try
{
populateTable (*table, badNumTokensConfigLines);
// line above should throw
fail ("didn't throw");
}
catch (...)
{
pass ();
}
}
}
}
void testEnable ()
{
testcase ("enable");
auto table (makeTable ());
std::vector<AmendmentName> const amendmentNames (
populateTable (*table, m_validAmendmentPairs));
{
// enable/disable tests
for (auto const& i : amendmentNames)
{
auto id (i.id ());
table->enable (id);
expect (table->isEnabled (id));
table->disable (id);
expect (!table->isEnabled (id));
table->enable (id);
expect (table->isEnabled (id));
}
std::vector<uint256> toEnable;
for (auto const& i : amendmentNames)
{
auto id (i.id ());
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 =
void (AmendmentTable::*)(const std::vector<uint256>& amendments);
using ATGetter = bool (AmendmentTable::*)(uint256 const& amendment);
void testVectorSetUnset (ATSetter setter, ATGetter getter)
{
auto table (makeTable ());
// make pointer to ref syntax a little nicer
auto& tableRef (*table);
std::vector<AmendmentName> const amendmentNames (
populateTable (tableRef, m_validAmendmentPairs));
// they should all be set
for (auto const& i : amendmentNames)
{
expect ((tableRef.*getter)(i.id ())); // i.e. "isSupported"
}
{
// only set every other amendment
std::vector<uint256> toSet;
toSet.reserve (amendmentNames.size ());
for (int i = 0; i < amendmentNames.size (); ++i)
{
if (i % 2)
{
toSet.emplace_back (amendmentNames[i].id ());
}
}
(tableRef.*setter)(toSet);
for (int i = 0; i < amendmentNames.size (); ++i)
{
bool const shouldBeSet = i % 2;
expect (shouldBeSet ==
(tableRef.*getter)(
amendmentNames[i].id ())); // i.e. "isSupported"
}
}
}
void testSupported ()
{
testcase ("supported");
testVectorSetUnset (&AmendmentTable::setSupported,
&AmendmentTable::isSupported);
}
void testEnabled ()
{
testcase ("enabled");
testVectorSetUnset (&AmendmentTable::setEnabled,
&AmendmentTable::isEnabled);
}
void testSupportedEnabled ()
{
// Check that supported/enabled aren't the same thing
testcase ("supportedEnabled");
auto table (makeTable ());
std::vector<AmendmentName> const amendmentNames (
populateTable (*table, m_validAmendmentPairs));
{
// support every even amendment
// enable every odd amendment
std::vector<uint256> toSupport;
toSupport.reserve (amendmentNames.size ());
std::vector<uint256> toEnable;
toEnable.reserve (amendmentNames.size ());
for (int i = 0; i < amendmentNames.size (); ++i)
{
if (i % 2)
{
toSupport.emplace_back (amendmentNames[i].id ());
}
else
{
toEnable.emplace_back (amendmentNames[i].id ());
}
}
table->setEnabled (toEnable);
table->setSupported (toSupport);
for (int i = 0; i < amendmentNames.size (); ++i)
{
bool const shouldBeSupported = i % 2;
bool const shouldBeEnabled = !(i % 2);
expect (shouldBeEnabled ==
(table->isEnabled (amendmentNames[i].id ())));
expect (shouldBeSupported ==
(table->isSupported (amendmentNames[i].id ())));
}
}
}
// TBD: veto/reportValidations/getJson/doValidation/doVoting
// Threading test?
void run ()
{
testGet ();
testAddInitialAddKnown ();
testEnable ();
testSupported ();
testSupportedEnabled ();
}
};
BEAST_DEFINE_TESTSUITE (AmendmentTable, app, ripple);
} // ripple

View File

@@ -32,6 +32,7 @@ struct ConfigSection
// VFALCO TODO Rename and replace these macros with variables.
#define SECTION_ACCOUNT_PROBE_MAX "account_probe_max"
#define SECTION_AMENDMENTS "amendments"
#define SECTION_CLUSTER_NODES "cluster_nodes"
#define SECTION_DATABASE_PATH "database_path"
#define SECTION_DEBUG_LOGFILE "debug_logfile"

View File

@@ -30,3 +30,4 @@
#include <ripple/app/paths/FindPaths.cpp>
#include <ripple/app/paths/Pathfinder.cpp>
#include <ripple/app/misc/AmendmentTableImpl.cpp>
#include <ripple/app/misc/tests/AmendmentTable.test.cpp>