Add fee voting configuration and docs (RIPD-564)

This commit is contained in:
Vinnie Falco
2014-09-16 15:09:09 -07:00
parent 3fef916972
commit b6d9f1d4b2
5 changed files with 375 additions and 217 deletions

View File

@@ -22,6 +22,8 @@
#
# 8. Diagnostics
#
# 9. Voting
#
#-------------------------------------------------------------------------------
#
# Purpose
@@ -770,6 +772,57 @@
# prefix=my_validator
#
#-------------------------------------------------------------------------------
#
# 9. Voting
#
#----------
#
# The vote settings configure settings for the entire Ripple network.
# While a single instance of rippled cannot unilaterally enforce network-wide
# settings, these choices become part of the instance's vote during the
# consensus process for each voting ledger.
#
# [voting]
#
# A set of key/value pair parameters used during voting ledgers.
#
# reference_fee = <drops>
#
# The cost of the reference transaction fee, specified in drops.
# The reference transaction is the simplest form of transaction.
# It represents an XRP payment between two parties.
#
# If this parameter is unspecified, rippled will use an internal
# default. Don't change this without understanding the consequences.
#
# Example:
# reference_fee = 10 # 10 drops
#
# account_reserve = <drops>
#
# The account reserve requirement specified in drops. The portion of an
# account's XRP balance that is at or below the reserve may only be
# spent on transaction fees, and not transferred out of the account.
#
# If this parameter is unspecified, rippled will use an internal
# default. Don't change this without understanding the consequences.
#
# Example:
# account_reserve = 20000000 # 20 XRP
#
# owner_reserve = <drops>
#
# The owner reserve is the amount of XRP reserved in the account for
# each ledger item owned by the account. Ledger items an account may
# own include trust lines, open orders, and tickets.
#
# If this parameter is unspecified, rippled will use an internal
# default. Don't change this without understanding the consequences.
#
# Example:
# owner_reserve = 5000000 # 5 XRP
#
#-------------------------------------------------------------------------------
# Allow other peers to connect to this server.
#

View File

@@ -17,8 +17,11 @@
*/
//==============================================================================
#ifndef RIPPLE_IFEEVOTE_H
#define RIPPLE_IFEEVOTE_H
#ifndef RIPPLE_APP_FEEVOTE_H_INCLUDED
#define RIPPLE_APP_FEEVOTE_H_INCLUDED
#include <ripple/core/Section.h>
#include <ripple/core/SystemParameters.h>
namespace ripple {
@@ -26,22 +29,33 @@ namespace ripple {
class FeeVote
{
public:
/** Create a new fee vote manager.
@param targetBaseFee
@param targetReserveBase
@param targetReserveIncrement
@param journal
/** Fee schedule to vote for.
During voting ledgers, the FeeVote logic will try to move towards
these values when injecting fee-setting transactions.
A default-constructed Setup contains recommended values.
*/
struct Setup
{
/** The cost of a reference transaction in drops. */
std::uint64_t reference_fee = 10;
virtual ~FeeVote () { }
/** The account reserve requirement in drops. */
std::uint64_t account_reserve = 20 * SYSTEM_CURRENCY_PARTS;
/** The per-owned item reserve requirement in drops. */
std::uint64_t owner_reserve = 5 * SYSTEM_CURRENCY_PARTS;
};
virtual ~FeeVote () = default;
/** Add local fee preference to validation.
@param lastClosedLedger
@param baseValidation
*/
virtual void doValidation (Ledger::ref lastClosedLedger,
virtual
void
doValidation (Ledger::ref lastClosedLedger,
STObject& baseValidation) = 0;
/** Cast our local vote on the fee.
@@ -49,13 +63,22 @@ public:
@param lastClosedLedger
@param initialPosition
*/
virtual void doVoting (Ledger::ref lastClosedLedger,
virtual
void
doVoting (Ledger::ref lastClosedLedger,
SHAMap::ref initialPosition) = 0;
};
/** Build FeeVote::Setup from a config section. */
FeeVote::Setup
setup_FeeVote (Section const& section);
/** Create an instance of the FeeVote logic.
@param setup The fee schedule to vote for.
@param journal Where to log.
*/
std::unique_ptr <FeeVote>
make_FeeVote (std::uint64_t targetBaseFee, std::uint32_t targetReserveBase,
std::uint32_t targetReserveIncrement, beast::Journal journal);
make_FeeVote (FeeVote::Setup const& setup, beast::Journal journal);
} // ripple

View File

@@ -19,15 +19,17 @@
namespace ripple {
class FeaturesImpl;
class FeeVoteImpl : public FeeVote
{
private:
namespace detail {
template <typename Integer>
class VotableInteger
{
private:
typedef std::map <Integer, int> map_type;
Integer mCurrent; // The current setting
Integer mTarget; // The setting we want
map_type mVoteMap;
public:
VotableInteger (Integer current, Integer target)
: mCurrent (current)
@@ -37,13 +39,6 @@ private:
++mVoteMap[mTarget];
}
bool
mayVote () const
{
// If we love the current setting, we will not vote
return mCurrent != mTarget;
}
void
addVote(Integer vote)
{
@@ -57,12 +52,15 @@ private:
}
Integer
getVotes ()
getVotes() const;
};
template <class Integer>
Integer
VotableInteger <Integer>::getVotes() const
{
Integer ourVote = mCurrent;
int weight = 0;
typedef typename std::map<Integer, int>::value_type mapVType;
for (auto const& e : mVoteMap)
{
// Take most voted value between current and target, inclusive
@@ -78,66 +76,86 @@ private:
return ourVote;
}
}
//------------------------------------------------------------------------------
class FeeVoteImpl : public FeeVote
{
private:
Integer mCurrent; // The current setting
Integer mTarget; // The setting we want
std::map<Integer, int> mVoteMap;
};
Setup target_;
beast::Journal journal_;
public:
FeeVoteImpl (std::uint64_t targetBaseFee, std::uint32_t targetReserveBase,
std::uint32_t targetReserveIncrement, beast::Journal journal)
: mTargetBaseFee (targetBaseFee)
, mTargetReserveBase (targetReserveBase)
, mTargetReserveIncrement (targetReserveIncrement)
, m_journal (journal)
{
}
FeeVoteImpl (Setup const& setup, beast::Journal journal);
void
doValidation (Ledger::ref lastClosedLedger,
STObject& baseValidation) override;
void
doVoting (Ledger::ref lastClosedLedger,
SHAMap::ref initialPosition) override;
};
//--------------------------------------------------------------------------
void
doValidation (Ledger::ref lastClosedLedger, STObject& baseValidation) override
FeeVoteImpl::FeeVoteImpl (Setup const& setup, beast::Journal journal)
: target_ (setup)
, journal_ (journal)
{
if (lastClosedLedger->getBaseFee () != mTargetBaseFee)
{
if (m_journal.info) m_journal.info <<
"Voting for base fee of " << mTargetBaseFee;
baseValidation.setFieldU64 (sfBaseFee, mTargetBaseFee);
}
if (lastClosedLedger->getReserve (0) != mTargetReserveBase)
{
if (m_journal.info) m_journal.info <<
"Voting for base resrve of " << mTargetReserveBase;
baseValidation.setFieldU32(sfReserveBase, mTargetReserveBase);
}
if (lastClosedLedger->getReserveInc () != mTargetReserveIncrement)
{
if (m_journal.info) m_journal.info <<
"Voting for reserve increment of " << mTargetReserveIncrement;
baseValidation.setFieldU32 (sfReserveIncrement, mTargetReserveIncrement);
}
}
//--------------------------------------------------------------------------
void
doVoting (Ledger::ref lastClosedLedger, SHAMap::ref initialPosition) override
FeeVoteImpl::doValidation (Ledger::ref lastClosedLedger,
STObject& baseValidation)
{
if (lastClosedLedger->getBaseFee () != target_.reference_fee)
{
if (journal_.info) journal_.info <<
"Voting for base fee of " << target_.reference_fee;
baseValidation.setFieldU64 (sfBaseFee, target_.reference_fee);
}
if (lastClosedLedger->getReserve (0) != target_.account_reserve)
{
if (journal_.info) journal_.info <<
"Voting for base resrve of " << target_.account_reserve;
baseValidation.setFieldU32(sfReserveBase, target_.account_reserve);
}
if (lastClosedLedger->getReserveInc () != target_.owner_reserve)
{
if (journal_.info) journal_.info <<
"Voting for reserve increment of " << target_.owner_reserve;
baseValidation.setFieldU32 (sfReserveIncrement,
target_.owner_reserve);
}
}
void
FeeVoteImpl::doVoting (Ledger::ref lastClosedLedger,
SHAMap::ref initialPosition)
{
// LCL must be flag ledger
assert ((lastClosedLedger->getLedgerSeq () % 256) == 0);
VotableInteger<std::uint64_t> baseFeeVote (lastClosedLedger->getBaseFee (), mTargetBaseFee);
VotableInteger<std::uint32_t> baseReserveVote (lastClosedLedger->getReserve (0), mTargetReserveBase);
VotableInteger<std::uint32_t> incReserveVote (lastClosedLedger->getReserveInc (), mTargetReserveIncrement);
detail::VotableInteger<std::uint64_t> baseFeeVote (
lastClosedLedger->getBaseFee (), target_.reference_fee);
detail::VotableInteger<std::uint32_t> baseReserveVote (
lastClosedLedger->getReserve (0), target_.account_reserve);
detail::VotableInteger<std::uint32_t> incReserveVote (
lastClosedLedger->getReserveInc (), target_.owner_reserve);
// get validations for ledger before flag
ValidationSet set = getApp().getValidations ().getValidations (lastClosedLedger->getParentHash ());
ValidationSet const set =
getApp().getValidations ().getValidations (
lastClosedLedger->getParentHash ());
for (auto const& e : set)
{
SerializedValidation const& val = *e.second;
@@ -174,16 +192,16 @@ public:
}
// choose our positions
std::uint64_t baseFee = baseFeeVote.getVotes ();
std::uint32_t baseReserve = baseReserveVote.getVotes ();
std::uint32_t incReserve = incReserveVote.getVotes ();
std::uint64_t const baseFee = baseFeeVote.getVotes ();
std::uint32_t const baseReserve = baseReserveVote.getVotes ();
std::uint32_t const incReserve = incReserveVote.getVotes ();
// add transactions to our position
if ((baseFee != lastClosedLedger->getBaseFee ()) ||
(baseReserve != lastClosedLedger->getReserve (0)) ||
(incReserve != lastClosedLedger->getReserveInc ()))
{
if (m_journal.warning) m_journal.warning <<
if (journal_.warning) journal_.warning <<
"We are voting for a fee change: " << baseFee <<
"/" << baseReserve <<
"/" << incReserve;
@@ -197,37 +215,39 @@ public:
uint256 txID = trans.getTransactionID ();
if (m_journal.warning)
m_journal.warning << "Vote: " << txID;
if (journal_.warning)
journal_.warning << "Vote: " << txID;
Serializer s;
trans.add (s, true);
SHAMapItem::pointer tItem = std::make_shared<SHAMapItem> (txID, s.peekData ());
SHAMapItem::pointer tItem = std::make_shared<SHAMapItem> (
txID, s.peekData ());
if (!initialPosition->addGiveItem (tItem, true, false))
{
if (m_journal.warning) m_journal.warning <<
if (journal_.warning) journal_.warning <<
"Ledger already had fee change";
}
}
}
private:
std::uint64_t mTargetBaseFee;
std::uint32_t mTargetReserveBase;
std::uint32_t mTargetReserveIncrement;
beast::Journal m_journal;
};
//------------------------------------------------------------------------------
std::unique_ptr<FeeVote>
make_FeeVote (std::uint64_t targetBaseFee, std::uint32_t targetReserveBase,
std::uint32_t targetReserveIncrement, beast::Journal journal)
FeeVote::Setup
setup_FeeVote (Section const& section)
{
return std::make_unique<FeeVoteImpl> (targetBaseFee, targetReserveBase,
targetReserveIncrement, journal);
FeeVote::Setup setup;
set (setup.reference_fee, "reference_fee", section);
set (setup.account_reserve, "account_reserve", section);
set (setup.owner_reserve, "owner_reserve", section);
return setup;
}
std::unique_ptr<FeeVote>
make_FeeVote (FeeVote::Setup const& setup, beast::Journal journal)
{
return std::make_unique<FeeVoteImpl> (setup, journal);
}
} // ripple

View File

@@ -18,6 +18,7 @@
//==============================================================================
#include <ripple/app/book/Quality.h>
#include <ripple/app/misc/FeeVote.h>
#include <ripple/basics/Time.h>
#include <ripple/basics/StringUtilities.h>
#include <ripple/common/jsonrpc_fields.h>
@@ -56,8 +57,8 @@ public:
, m_clock (clock)
, m_journal (journal)
, m_localTX (LocalTxs::New ())
, m_feeVote (make_FeeVote(10, 20 * SYSTEM_CURRENCY_PARTS,
5 * SYSTEM_CURRENCY_PARTS, deprecatedLogs().journal("FeeVote")))
, m_feeVote (make_FeeVote (setup_FeeVote (getConfig().section ("voting")),
deprecatedLogs().journal("FeeVote")))
, mMode (omDISCONNECTED)
, mNeedNetworkLedger (false)
, mProposing (false)

View File

@@ -1,3 +1,64 @@
# Fee Voting
The Ripple payment protocol enforces a fee schedule expressed in units of the
native currency, XRP. Fees for transactions are paid directly from the account
owner. There are also reserve requirements for each item that occupies storage
in the ledger. The reserve fee schedule contains both a per-account reserve,
and a per-owned-item reserve. The items an account may own include active
offers, trust lines, and tickets.
Validators may vote to increase fees if they feel that the network is charging
too little. They may also vote to decrease fees if the fees are too costly
relative to the value the network provides. One common case where a validator
may want to change fees is when the value of the native currency XRP fluctuates
relative to other currencies.
The fee voting mechanism takes place every 256 ledgers ("voting ledgers"). In
a voting ledger, each validator takes a position on what they think the fees
should be. The consensus process converges on the majority position, and in
subsequent ledgers a new fee schedule is enacted.
## Consensus
The Ripple consensus algorithm allows distributed participants to arrive at
the same answer for yes/no questions. The canonical case for consensus is
whether or not a particular transaction is included in the ledger. Fees
present a more difficult challenge, since the decision on the new fee is not
a yes or no question.
To convert validators' positions on fees into a yes or no question that can
be converged in the consensus process, the following algorithm is used:
- In the ledger before a voting ledger, validators send proposals which also
include the values they think the network should use for the new fee schedule.
- In the voting ledger, validators examine the proposals from other validators
and choose a new fee schedule which moves the fees in a direction closer to
the validator's ideal fee schedule and is also likely to be accepted. A fee
amount is likely to be accepted if a majority of validators agree on the
number.
- Each validator injects a "pseudo transaction" into their proposed ledger
which sets the fees to the chosen schedule.
- The consensus process is applied to these fee-setting transactions as normal.
Each transaction is either included in the ledger or not. In most cases, one
fee setting transaction will make it in while the others are rejected. In
some rare cases more than one fee setting transaction will make it in. The
last one to be applied will take effect. This is harmless since a majority
of validators still agreed on it.
- After the voting ledger has been validated, future pseudo transactions
before the next voting ledger are rejected as fee setting transactions may
only appear in voting ledgers.
## Configuration
A validating instance of rippled uses information in the configuration file
to determine how it wants to vote on the fee schedule. It is the responsibility
of the administrator to set these values.
---
# Amendment