Skip to content

Commit

Permalink
Persist API-configured voting settings:
Browse files Browse the repository at this point in the history
Prior to this commit, the amendments that a server would vote in support
of or against could be configured both via the configuration file and
via the command line "feature" command. Changes made in the configuration
file would only be loaded once at server startup and changes made via the
command line take effect immediately but are not persisted across
restarts.

This commit deprecates management of amendments via the configuration
file and stores the relevant information in the `wallet.db` database
file.

1. On startup, the new code parses the configuration file.
2. If the `[veto_amendments]` or `[amendments]` sections are present,
   we check if the `FeatureVotes` table is present in `wallet.db`.
3. If it is not, we create the `FeatureVotes` table and transfer the
   settings from the config file.
4. Proceed normally but only reference the `FeatureVotes` table instead
   of the config file.
5. Warns if the voting table already exists in `wallet.db` and there
   exists voting sections in the config file. The config file is ignored
   in this case.

This change addresses & closes #3366
  • Loading branch information
HowardHinnant authored and nbougalis committed Dec 17, 2020
1 parent 24b17c6 commit 28ed2b9
Show file tree
Hide file tree
Showing 4 changed files with 225 additions and 56 deletions.
1 change: 1 addition & 0 deletions src/ripple/app/main/Application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1360,6 +1360,7 @@ ApplicationImp::setup()
Section enabledAmendments = config_->section(SECTION_AMENDMENTS);

m_amendmentTable = make_AmendmentTable(
*this,
config().AMENDMENT_MAJORITY_TIME,
supportedAmendments,
enabledAmendments,
Expand Down
1 change: 1 addition & 0 deletions src/ripple/app/misc/AmendmentTable.h
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ class AmendmentTable

std::unique_ptr<AmendmentTable>
make_AmendmentTable(
Application& app,
std::chrono::seconds majorityTime,
Section const& supported,
Section const& enabled,
Expand Down
155 changes: 137 additions & 18 deletions src/ripple/app/misc/impl/AmendmentTable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,9 @@ class AmendmentTableImpl final : public AmendmentTable

beast::Journal const j_;

// Database which persists veto/unveto vote
DatabaseCon& db_;

// Finds or creates state. Must be called with mutex_ locked.
AmendmentState*
add(uint256 const& amendment, std::lock_guard<std::mutex> const& sl);
Expand All @@ -239,8 +242,15 @@ class AmendmentTableImpl final : public AmendmentTable
AmendmentState const& state,
std::lock_guard<std::mutex> const& sl) const;

void
persistVote(
uint256 const& amendment,
std::string const& name,
bool vote_to_veto) const;

public:
AmendmentTableImpl(
Application& app,
std::chrono::seconds majorityTime,
Section const& supported,
Section const& enabled,
Expand Down Expand Up @@ -301,6 +311,7 @@ class AmendmentTableImpl final : public AmendmentTable
//------------------------------------------------------------------------------

AmendmentTableImpl::AmendmentTableImpl(
Application& app,
std::chrono::seconds majorityTime,
Section const& supported,
Section const& enabled,
Expand All @@ -310,9 +321,34 @@ AmendmentTableImpl::AmendmentTableImpl(
, majorityTime_(majorityTime)
, unsupportedEnabled_(false)
, j_(journal)
, db_(app.getWalletDB())
{
std::lock_guard sl(mutex_);

// Find out if the FeatureVotes table exists in WalletDB
bool const featureVotesExist = [this]() {
auto db = db_.checkoutDb();
soci::transaction tr(*db);
std::string sql =
"SELECT count(*) FROM sqlite_master "
"WHERE type='table' AND name='FeatureVotes'";
boost::optional<int> featureVotesCount;
*db << sql, soci::into(featureVotesCount);
bool exists = static_cast<bool>(*featureVotesCount);

// Create FeatureVotes table in WalletDB if it doesn't exist
if (!exists)
{
*db << "CREATE TABLE FeatureVotes ( "
"AmendmentHash CHARACTER(64) NOT NULL, "
"AmendmentName TEXT, "
"Veto INTEGER NOT NULL );";
tr.commit();
}
return exists;
}();

// Parse supported amendments
for (auto const& a : parseSection(supported))
{
if (auto s = add(a.first, sl))
Expand All @@ -326,31 +362,93 @@ AmendmentTableImpl::AmendmentTableImpl(
}
}

hash_set<uint256> detect_conflict;
// Parse enabled amendments from config
for (auto const& a : parseSection(enabled))
{
if (auto s = add(a.first, sl))
{
JLOG(j_.debug()) << "Amendment " << a.first << " is enabled.";

if (!a.second.empty())
s->name = a.second;

s->supported = true;
s->enabled = true;
if (featureVotesExist)
{ // If the table existed, warn about duplicate config info
JLOG(j_.warn()) << "[amendments] section in config file ignored"
" in favor of data in db/wallet.db.";
break;
}
else
{ // Otherwise transfer config data into the table
detect_conflict.insert(a.first);
persistVote(a.first, a.second, false); // un-veto
}
}

// Parse vetoed amendments from config
for (auto const& a : parseSection(vetoed))
{
// Unknown amendments are effectively vetoed already
if (auto s = get(a.first, sl))
{
JLOG(j_.info()) << "Amendment " << a.first << " is vetoed.";

if (!a.second.empty())
s->name = a.second;
if (featureVotesExist)
{ // If the table existed, warn about duplicate config info
JLOG(j_.warn())
<< "[veto_amendments] section in config file ignored"
" in favor of data in db/wallet.db.";
break;
}
else
{ // Otherwise transfer config data into the table
if (detect_conflict.count(a.first) == 0)
{
persistVote(a.first, a.second, true); // veto
}
else
{
JLOG(j_.warn())
<< "[veto_amendments] section in config has amendment "
<< '(' << a.first << ", " << a.second
<< ") both [veto_amendments] and [amendments].";
}
}
}

s->vetoed = true;
// Read amendment votes from wallet.db
auto db = db_.checkoutDb();
soci::transaction tr(*db);
std::string sql =
"SELECT AmendmentHash, AmendmentName, Veto FROM FeatureVotes";
boost::optional<std::string> amendment_hash;
boost::optional<std::string> amendment_name;
boost::optional<int> vote_to_veto;
soci::statement st =
(db->prepare << sql,
soci::into(amendment_hash),
soci::into(amendment_name),
soci::into(vote_to_veto));
st.execute();
while (st.fetch())
{
uint256 amend_hash;
if (!amend_hash.parseHex(*amendment_hash))
{
Throw<std::runtime_error>(
"Invalid amendment ID '" + *amendment_hash + " in wallet.db");
}
if (*vote_to_veto)
{
// Unknown amendments are effectively vetoed already
if (auto s = get(amend_hash, sl))
{
JLOG(j_.info()) << "Amendment {" << *amendment_name << ", "
<< amend_hash << "} is vetoed.";
if (!amendment_name->empty())
s->name = *amendment_name;
s->vetoed = true;
}
}
else // un-veto
{
if (auto s = add(amend_hash, sl))
{
JLOG(j_.debug()) << "Amendment {" << *amendment_name << ", "
<< amend_hash << "} is un-vetoed.";
if (!amendment_name->empty())
s->name = *amendment_name;
s->vetoed = false;
}
}
}
}
Expand Down Expand Up @@ -402,6 +500,24 @@ AmendmentTableImpl::find(std::string const& name) const
return {};
}

void
AmendmentTableImpl::persistVote(
uint256 const& amendment,
std::string const& name,
bool vote_to_veto) const
{
auto db = db_.checkoutDb();
soci::transaction tr(*db);
std::string sql =
"INSERT INTO FeatureVotes (AmendmentHash, AmendmentName, Veto) VALUES "
"('";
sql += to_string(amendment);
sql += "', '" + name;
sql += "', '" + std::to_string(int{vote_to_veto}) + "');";
*db << sql;
tr.commit();
}

bool
AmendmentTableImpl::veto(uint256 const& amendment)
{
Expand All @@ -411,6 +527,7 @@ AmendmentTableImpl::veto(uint256 const& amendment)
if (s->vetoed)
return false;
s->vetoed = true;
persistVote(amendment, s->name, s->vetoed);
return true;
}

Expand All @@ -423,6 +540,7 @@ AmendmentTableImpl::unVeto(uint256 const& amendment)
if (!s || !s->vetoed)
return false;
s->vetoed = false;
persistVote(amendment, s->name, s->vetoed);
return true;
}

Expand Down Expand Up @@ -693,14 +811,15 @@ AmendmentTableImpl::getJson(uint256 const& amendmentID) const

std::unique_ptr<AmendmentTable>
make_AmendmentTable(
Application& app,
std::chrono::seconds majorityTime,
Section const& supported,
Section const& enabled,
Section const& vetoed,
beast::Journal journal)
{
return std::make_unique<AmendmentTableImpl>(
majorityTime, supported, enabled, vetoed, journal);
app, majorityTime, supported, enabled, vetoed, journal);
}

} // namespace ripple
Loading

1 comment on commit 28ed2b9

@intelliot
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Released in rippled version 1.7.0

Please sign in to comment.