mirror of
https://github.com/Xahau/xahaud.git
synced 2025-12-06 17:27:52 +00:00
Add fee voting configuration and docs (RIPD-564)
This commit is contained in:
@@ -22,6 +22,8 @@
|
|||||||
#
|
#
|
||||||
# 8. Diagnostics
|
# 8. Diagnostics
|
||||||
#
|
#
|
||||||
|
# 9. Voting
|
||||||
|
#
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# Purpose
|
# Purpose
|
||||||
@@ -770,6 +772,57 @@
|
|||||||
# prefix=my_validator
|
# 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.
|
# Allow other peers to connect to this server.
|
||||||
#
|
#
|
||||||
@@ -844,11 +897,11 @@ r.ripple.com 51235
|
|||||||
# https://ripple.com/ripple.txt
|
# https://ripple.com/ripple.txt
|
||||||
#
|
#
|
||||||
[validators]
|
[validators]
|
||||||
n949f75evCHwgyP4fPVgaHqNHxUVN15PsJEZ3B3HnXPcPjcZAoy7 RL1
|
n949f75evCHwgyP4fPVgaHqNHxUVN15PsJEZ3B3HnXPcPjcZAoy7 RL1
|
||||||
n9MD5h24qrQqiyBC8aeqqCWvpiBiYQ3jxSr91uiDvmrkyHRdYLUj RL2
|
n9MD5h24qrQqiyBC8aeqqCWvpiBiYQ3jxSr91uiDvmrkyHRdYLUj RL2
|
||||||
n9L81uNCaPgtUJfaHh89gmdvXKAmSt5Gdsw2g1iPWaPkAHW5Nm4C RL3
|
n9L81uNCaPgtUJfaHh89gmdvXKAmSt5Gdsw2g1iPWaPkAHW5Nm4C RL3
|
||||||
n9KiYM9CgngLvtRCQHZwgC2gjpdaZcCcbt3VboxiNFcKuwFVujzS RL4
|
n9KiYM9CgngLvtRCQHZwgC2gjpdaZcCcbt3VboxiNFcKuwFVujzS RL4
|
||||||
n9LdgEtkmGB9E2h3K4Vp7iGUaKuq23Zr32ehxiU8FWY7xoxbWTSA RL5
|
n9LdgEtkmGB9E2h3K4Vp7iGUaKuq23Zr32ehxiU8FWY7xoxbWTSA RL5
|
||||||
|
|
||||||
# Ditto.
|
# Ditto.
|
||||||
[validation_quorum]
|
[validation_quorum]
|
||||||
|
|||||||
@@ -17,8 +17,11 @@
|
|||||||
*/
|
*/
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
||||||
#ifndef RIPPLE_IFEEVOTE_H
|
#ifndef RIPPLE_APP_FEEVOTE_H_INCLUDED
|
||||||
#define RIPPLE_IFEEVOTE_H
|
#define RIPPLE_APP_FEEVOTE_H_INCLUDED
|
||||||
|
|
||||||
|
#include <ripple/core/Section.h>
|
||||||
|
#include <ripple/core/SystemParameters.h>
|
||||||
|
|
||||||
namespace ripple {
|
namespace ripple {
|
||||||
|
|
||||||
@@ -26,36 +29,56 @@ namespace ripple {
|
|||||||
class FeeVote
|
class FeeVote
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/** Create a new fee vote manager.
|
/** Fee schedule to vote for.
|
||||||
|
During voting ledgers, the FeeVote logic will try to move towards
|
||||||
@param targetBaseFee
|
these values when injecting fee-setting transactions.
|
||||||
@param targetReserveBase
|
A default-constructed Setup contains recommended values.
|
||||||
@param targetReserveIncrement
|
|
||||||
@param journal
|
|
||||||
*/
|
*/
|
||||||
|
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.
|
/** Add local fee preference to validation.
|
||||||
|
|
||||||
@param lastClosedLedger
|
@param lastClosedLedger
|
||||||
@param baseValidation
|
@param baseValidation
|
||||||
*/
|
*/
|
||||||
virtual void doValidation (Ledger::ref lastClosedLedger,
|
virtual
|
||||||
STObject& baseValidation) = 0;
|
void
|
||||||
|
doValidation (Ledger::ref lastClosedLedger,
|
||||||
|
STObject& baseValidation) = 0;
|
||||||
|
|
||||||
/** Cast our local vote on the fee.
|
/** Cast our local vote on the fee.
|
||||||
|
|
||||||
@param lastClosedLedger
|
@param lastClosedLedger
|
||||||
@param initialPosition
|
@param initialPosition
|
||||||
*/
|
*/
|
||||||
virtual void doVoting (Ledger::ref lastClosedLedger,
|
virtual
|
||||||
SHAMap::ref initialPosition) = 0;
|
void
|
||||||
|
doVoting (Ledger::ref lastClosedLedger,
|
||||||
|
SHAMap::ref initialPosition) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::unique_ptr<FeeVote>
|
/** Build FeeVote::Setup from a config section. */
|
||||||
make_FeeVote (std::uint64_t targetBaseFee, std::uint32_t targetReserveBase,
|
FeeVote::Setup
|
||||||
std::uint32_t targetReserveIncrement, beast::Journal journal);
|
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 (FeeVote::Setup const& setup, beast::Journal journal);
|
||||||
|
|
||||||
} // ripple
|
} // ripple
|
||||||
|
|
||||||
|
|||||||
@@ -19,215 +19,235 @@
|
|||||||
|
|
||||||
namespace ripple {
|
namespace ripple {
|
||||||
|
|
||||||
class FeaturesImpl;
|
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)
|
||||||
|
, mTarget (target)
|
||||||
|
{
|
||||||
|
// Add our vote
|
||||||
|
++mVoteMap[mTarget];
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
addVote(Integer vote)
|
||||||
|
{
|
||||||
|
++mVoteMap[vote];
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
noVote()
|
||||||
|
{
|
||||||
|
addVote (mCurrent);
|
||||||
|
}
|
||||||
|
|
||||||
|
Integer
|
||||||
|
getVotes() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class Integer>
|
||||||
|
Integer
|
||||||
|
VotableInteger <Integer>::getVotes() const
|
||||||
|
{
|
||||||
|
Integer ourVote = mCurrent;
|
||||||
|
int weight = 0;
|
||||||
|
for (auto const& e : mVoteMap)
|
||||||
|
{
|
||||||
|
// Take most voted value between current and target, inclusive
|
||||||
|
if ((e.first <= std::max (mTarget, mCurrent)) &&
|
||||||
|
(e.first >= std::min (mTarget, mCurrent)) &&
|
||||||
|
(e.second > weight))
|
||||||
|
{
|
||||||
|
ourVote = e.first;
|
||||||
|
weight = e.second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ourVote;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
class FeeVoteImpl : public FeeVote
|
class FeeVoteImpl : public FeeVote
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
Setup target_;
|
||||||
template <typename Integer>
|
beast::Journal journal_;
|
||||||
class VotableInteger
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
VotableInteger (Integer current, Integer target)
|
|
||||||
: mCurrent (current)
|
|
||||||
, mTarget (target)
|
|
||||||
{
|
|
||||||
// Add our vote
|
|
||||||
++mVoteMap[mTarget];
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
mayVote () const
|
|
||||||
{
|
|
||||||
// If we love the current setting, we will not vote
|
|
||||||
return mCurrent != mTarget;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
addVote (Integer vote)
|
|
||||||
{
|
|
||||||
++mVoteMap[vote];
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
noVote ()
|
|
||||||
{
|
|
||||||
addVote (mCurrent);
|
|
||||||
}
|
|
||||||
|
|
||||||
Integer
|
|
||||||
getVotes ()
|
|
||||||
{
|
|
||||||
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
|
|
||||||
if ((e.first <= std::max (mTarget, mCurrent)) &&
|
|
||||||
(e.first >= std::min (mTarget, mCurrent)) &&
|
|
||||||
(e.second > weight))
|
|
||||||
{
|
|
||||||
ourVote = e.first;
|
|
||||||
weight = e.second;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ourVote;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
Integer mCurrent; // The current setting
|
|
||||||
Integer mTarget; // The setting we want
|
|
||||||
std::map<Integer, int> mVoteMap;
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FeeVoteImpl (std::uint64_t targetBaseFee, std::uint32_t targetReserveBase,
|
FeeVoteImpl (Setup const& setup, beast::Journal journal);
|
||||||
std::uint32_t targetReserveIncrement, beast::Journal journal)
|
|
||||||
: mTargetBaseFee (targetBaseFee)
|
|
||||||
, mTargetReserveBase (targetReserveBase)
|
|
||||||
, mTargetReserveIncrement (targetReserveIncrement)
|
|
||||||
, m_journal (journal)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
//--------------------------------------------------------------------------
|
|
||||||
|
|
||||||
void
|
void
|
||||||
doValidation (Ledger::ref lastClosedLedger, STObject& baseValidation) override
|
doValidation (Ledger::ref lastClosedLedger,
|
||||||
{
|
STObject& baseValidation) override;
|
||||||
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
|
void
|
||||||
doVoting (Ledger::ref lastClosedLedger, SHAMap::ref initialPosition) override
|
doVoting (Ledger::ref lastClosedLedger,
|
||||||
{
|
SHAMap::ref initialPosition) override;
|
||||||
// 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);
|
|
||||||
|
|
||||||
// get validations for ledger before flag
|
|
||||||
ValidationSet set = getApp().getValidations ().getValidations (lastClosedLedger->getParentHash ());
|
|
||||||
for (auto const& e : set)
|
|
||||||
{
|
|
||||||
SerializedValidation const& val = *e.second;
|
|
||||||
|
|
||||||
if (val.isTrusted ())
|
|
||||||
{
|
|
||||||
if (val.isFieldPresent (sfBaseFee))
|
|
||||||
{
|
|
||||||
baseFeeVote.addVote (val.getFieldU64 (sfBaseFee));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
baseFeeVote.noVote ();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (val.isFieldPresent (sfReserveBase))
|
|
||||||
{
|
|
||||||
baseReserveVote.addVote (val.getFieldU32 (sfReserveBase));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
baseReserveVote.noVote ();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (val.isFieldPresent (sfReserveIncrement))
|
|
||||||
{
|
|
||||||
incReserveVote.addVote (val.getFieldU32 (sfReserveIncrement));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
incReserveVote.noVote ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// choose our positions
|
|
||||||
std::uint64_t baseFee = baseFeeVote.getVotes ();
|
|
||||||
std::uint32_t baseReserve = baseReserveVote.getVotes ();
|
|
||||||
std::uint32_t 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 <<
|
|
||||||
"We are voting for a fee change: " << baseFee <<
|
|
||||||
"/" << baseReserve <<
|
|
||||||
"/" << incReserve;
|
|
||||||
|
|
||||||
SerializedTransaction trans (ttFEE);
|
|
||||||
trans.setFieldAccount (sfAccount, Account ());
|
|
||||||
trans.setFieldU64 (sfBaseFee, baseFee);
|
|
||||||
trans.setFieldU32 (sfReferenceFeeUnits, 10);
|
|
||||||
trans.setFieldU32 (sfReserveBase, baseReserve);
|
|
||||||
trans.setFieldU32 (sfReserveIncrement, incReserve);
|
|
||||||
|
|
||||||
uint256 txID = trans.getTransactionID ();
|
|
||||||
|
|
||||||
if (m_journal.warning)
|
|
||||||
m_journal.warning << "Vote: " << txID;
|
|
||||||
|
|
||||||
Serializer s;
|
|
||||||
trans.add (s, true);
|
|
||||||
|
|
||||||
SHAMapItem::pointer tItem = std::make_shared<SHAMapItem> (txID, s.peekData ());
|
|
||||||
|
|
||||||
if (!initialPosition->addGiveItem (tItem, true, false))
|
|
||||||
{
|
|
||||||
if (m_journal.warning) m_journal.warning <<
|
|
||||||
"Ledger already had fee change";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::uint64_t mTargetBaseFee;
|
|
||||||
std::uint32_t mTargetReserveBase;
|
|
||||||
std::uint32_t mTargetReserveIncrement;
|
|
||||||
beast::Journal m_journal;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
FeeVoteImpl::FeeVoteImpl (Setup const& setup, beast::Journal journal)
|
||||||
|
: target_ (setup)
|
||||||
|
, journal_ (journal)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
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);
|
||||||
|
|
||||||
|
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 const set =
|
||||||
|
getApp().getValidations ().getValidations (
|
||||||
|
lastClosedLedger->getParentHash ());
|
||||||
|
for (auto const& e : set)
|
||||||
|
{
|
||||||
|
SerializedValidation const& val = *e.second;
|
||||||
|
|
||||||
|
if (val.isTrusted ())
|
||||||
|
{
|
||||||
|
if (val.isFieldPresent (sfBaseFee))
|
||||||
|
{
|
||||||
|
baseFeeVote.addVote (val.getFieldU64 (sfBaseFee));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
baseFeeVote.noVote ();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (val.isFieldPresent (sfReserveBase))
|
||||||
|
{
|
||||||
|
baseReserveVote.addVote (val.getFieldU32 (sfReserveBase));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
baseReserveVote.noVote ();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (val.isFieldPresent (sfReserveIncrement))
|
||||||
|
{
|
||||||
|
incReserveVote.addVote (val.getFieldU32 (sfReserveIncrement));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
incReserveVote.noVote ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// choose our positions
|
||||||
|
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 (journal_.warning) journal_.warning <<
|
||||||
|
"We are voting for a fee change: " << baseFee <<
|
||||||
|
"/" << baseReserve <<
|
||||||
|
"/" << incReserve;
|
||||||
|
|
||||||
|
SerializedTransaction trans (ttFEE);
|
||||||
|
trans.setFieldAccount (sfAccount, Account ());
|
||||||
|
trans.setFieldU64 (sfBaseFee, baseFee);
|
||||||
|
trans.setFieldU32 (sfReferenceFeeUnits, 10);
|
||||||
|
trans.setFieldU32 (sfReserveBase, baseReserve);
|
||||||
|
trans.setFieldU32 (sfReserveIncrement, incReserve);
|
||||||
|
|
||||||
|
uint256 txID = trans.getTransactionID ();
|
||||||
|
|
||||||
|
if (journal_.warning)
|
||||||
|
journal_.warning << "Vote: " << txID;
|
||||||
|
|
||||||
|
Serializer s;
|
||||||
|
trans.add (s, true);
|
||||||
|
|
||||||
|
SHAMapItem::pointer tItem = std::make_shared<SHAMapItem> (
|
||||||
|
txID, s.peekData ());
|
||||||
|
|
||||||
|
if (!initialPosition->addGiveItem (tItem, true, false))
|
||||||
|
{
|
||||||
|
if (journal_.warning) journal_.warning <<
|
||||||
|
"Ledger already had fee change";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
std::unique_ptr<FeeVote>
|
FeeVote::Setup
|
||||||
make_FeeVote (std::uint64_t targetBaseFee, std::uint32_t targetReserveBase,
|
setup_FeeVote (Section const& section)
|
||||||
std::uint32_t targetReserveIncrement, beast::Journal journal)
|
|
||||||
{
|
{
|
||||||
return std::make_unique<FeeVoteImpl> (targetBaseFee, targetReserveBase,
|
FeeVote::Setup setup;
|
||||||
targetReserveIncrement, journal);
|
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
|
} // ripple
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
||||||
#include <ripple/app/book/Quality.h>
|
#include <ripple/app/book/Quality.h>
|
||||||
|
#include <ripple/app/misc/FeeVote.h>
|
||||||
#include <ripple/basics/Time.h>
|
#include <ripple/basics/Time.h>
|
||||||
#include <ripple/basics/StringUtilities.h>
|
#include <ripple/basics/StringUtilities.h>
|
||||||
#include <ripple/common/jsonrpc_fields.h>
|
#include <ripple/common/jsonrpc_fields.h>
|
||||||
@@ -56,8 +57,8 @@ public:
|
|||||||
, m_clock (clock)
|
, m_clock (clock)
|
||||||
, m_journal (journal)
|
, m_journal (journal)
|
||||||
, m_localTX (LocalTxs::New ())
|
, m_localTX (LocalTxs::New ())
|
||||||
, m_feeVote (make_FeeVote(10, 20 * SYSTEM_CURRENCY_PARTS,
|
, m_feeVote (make_FeeVote (setup_FeeVote (getConfig().section ("voting")),
|
||||||
5 * SYSTEM_CURRENCY_PARTS, deprecatedLogs().journal("FeeVote")))
|
deprecatedLogs().journal("FeeVote")))
|
||||||
, mMode (omDISCONNECTED)
|
, mMode (omDISCONNECTED)
|
||||||
, mNeedNetworkLedger (false)
|
, mNeedNetworkLedger (false)
|
||||||
, mProposing (false)
|
, mProposing (false)
|
||||||
|
|||||||
@@ -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
|
# Amendment
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user