Transaction queue and fee escalation (RIPD-598):

The first few transactions are added to the open ledger at
the base fee (ie. 10 drops).  Once enough transactions are
added, the required fee will jump dramatically. If additional
transactions are added, the fee will grow exponentially.

Transactions that don't have a high enough fee to be applied to
the ledger are added to the queue in order from highest fee to
lowest. Whenever a new ledger is accepted as validated, transactions
are first applied from the queue to the open ledger in fee order
until either all transactions are applied or the fee again jumps
too high for the remaining transactions.

Current implementation is restricted to one transaction in the
queue per account. Some groundwork has been laid to expand in
the future.

Note that this fee logic escalates independently of the load-based
fee logic (ie. LoadFeeTrack). Submitted transactions must meet
the load fee to be considered for the queue, and must meet both
fees to be put into open ledger.
This commit is contained in:
Edward Hennis
2015-07-10 20:00:21 -04:00
parent dc1276efa3
commit 9329aafe53
39 changed files with 2454 additions and 421 deletions

View File

@@ -37,6 +37,7 @@ feature (const char* name);
extern uint256 const featureMultiSign;
extern uint256 const featureSusPay;
extern uint256 const featureTrustSetAuth;
extern uint256 const featureFeeEscalation;
} // ripple

View File

@@ -119,6 +119,8 @@ JSS ( count ); // in: AccountTx*
JSS ( currency ); // in: paths/PathRequest, STAmount
// out: paths/Node, STPathSet, STAmount
JSS ( current ); // out: OwnerInfo
JSS ( current_ledger_size ); // out: TxQ
JSS ( current_queue_size ); // out: TxQ
JSS ( data ); // out: LedgerData
JSS ( date ); // out: tx/Transaction, NetworkOPs
JSS ( dbKBLedger ); // out: getCounts
@@ -136,6 +138,7 @@ JSS ( dir_entry ); // out: DirectoryEntryIterator
JSS ( dir_index ); // out: DirectoryEntryIterator
JSS ( dir_root ); // out: DirectoryEntryIterator
JSS ( directory ); // in: LedgerEntry
JSS ( drops ); // out: TxQ
JSS ( enabled ); // out: AmendmentTable
JSS ( engine_result ); // out: NetworkOPs, TransactionSign, Submit
JSS ( engine_result_code ); // out: NetworkOPs, TransactionSign, Submit
@@ -145,6 +148,7 @@ JSS ( error_code ); // out: error
JSS ( error_exception ); // out: Submit
JSS ( error_message ); // out: error
JSS ( expand ); // in: handler/Ledger
JSS ( expected_ledger_size ); // out: TxQ
JSS ( fail_hard ); // in: Sign, Submit
JSS ( failed ); // out: InboundLedger
JSS ( feature ); // in: Feature
@@ -232,6 +236,9 @@ JSS ( master_key ); // out: WalletPropose
JSS ( master_seed ); // out: WalletPropose
JSS ( master_seed_hex ); // out: WalletPropose
JSS ( max_ledger ); // in/out: LedgerCleaner
JSS ( max_queue_size ); // out: TxQ
JSS ( median_fee ); // out: TxQ
JSS ( median_level ); // out: TxQ
JSS ( message ); // error.
JSS ( meta ); // out: NetworkOPs, AccountTx*, Tx
JSS ( metaData );
@@ -239,6 +246,8 @@ JSS ( metadata ); // out: TransactionEntry
JSS ( method ); // RPC
JSS ( min_count ); // in: GetCounts
JSS ( min_ledger ); // in: LedgerCleaner
JSS ( minimum_fee ); // out: TxQ
JSS ( minimum_level ); // out: TxQ
JSS ( missingCommand ); // error
JSS ( name ); // out: AmendmentTableImpl, PeerImp
JSS ( needed_state_hashes ); // out: InboundLedger
@@ -261,6 +270,8 @@ JSS ( offers ); // out: NetworkOPs, AccountOffers, Subscribe
JSS ( offline ); // in: TransactionSign
JSS ( offset ); // in/out: AccountTxOld
JSS ( open ); // out: handlers/Ledger
JSS ( open_ledger_fee ); // out: TxQ
JSS ( open_ledger_level ); // out: TxQ
JSS ( owner ); // in: LedgerEntry, out: NetworkOPs
JSS ( owner_funds ); // out: NetworkOPs, AcceptedLedgerTx
JSS ( params ); // RPC
@@ -293,6 +304,7 @@ JSS ( quality_out ); // out: AccountLines
JSS ( random ); // out: Random
JSS ( raw_meta ); // out: AcceptedLedgerTx
JSS ( receive_currencies ); // out: AccountCurrencies
JSS ( reference_level ); // out: TxQ
JSS ( regular_seed ); // in/out: LedgerEntry
JSS ( remote ); // out: Logic.h
JSS ( request ); // RPC

View File

@@ -407,6 +407,14 @@ inline bool isXRP(STAmount const& amount)
std::uint64_t
mulDiv(std::uint64_t value, std::uint64_t mul, std::uint64_t div);
/**
A utility function to compute (value)*(mul)/(div) while avoiding
overflow but keeping precision. Will return the max uint64_t
value if mulDiv would overflow anyway.
*/
std::uint64_t
mulDivNoThrow(std::uint64_t value, std::uint64_t mul, std::uint64_t div);
template <class T1, class T2>
void lowestTerms(T1& a, T2& b)
{

View File

@@ -144,6 +144,7 @@ enum TER
// burden network.
terLAST, // Process after all other transactions
terNO_RIPPLE, // Rippling not allowed
terQUEUED, // Transaction is being held in TxQ until fee drops
// 0: S Success (success)
// Causes:

View File

@@ -48,5 +48,6 @@ feature (const char* name)
uint256 const featureMultiSign = feature("MultiSign");
uint256 const featureSusPay = feature("SusPay");
uint256 const featureTrustSetAuth = feature("TrustSetAuth");
uint256 const featureFeeEscalation = feature("FeeEscalation");
} // ripple

View File

@@ -1277,4 +1277,18 @@ mulDiv(std::uint64_t value, std::uint64_t mul, std::uint64_t div)
return value * mul / div;
}
// compute (value)*(mul)/(div) - avoid overflow but keep precision
std::uint64_t
mulDivNoThrow(std::uint64_t value, std::uint64_t mul, std::uint64_t div)
{
try
{
return mulDiv(value, mul, div);
}
catch (std::overflow_error)
{
return std::numeric_limits<std::uint64_t>::max();
}
}
} // ripple

View File

@@ -138,6 +138,7 @@ bool transResultInfo (TER code, std::string& token, std::string& text)
{ terNO_LINE, "terNO_LINE", "No such line." },
{ terPRE_SEQ, "terPRE_SEQ", "Missing/inapplicable prior transaction." },
{ terOWNERS, "terOWNERS", "Non-zero owner count." },
{ terQUEUED, "terQUEUED", "Held until fee drops." },
{ tesSUCCESS, "tesSUCCESS", "The transaction was applied. Only final in a validated ledger." },
};