Report escalated ledger fee in load_factor (RIPD-1207):

* Updates both server_info and server_state
* Adds "load_factor_server", which reports the server-only portion of the
  load (if appropriate) so clients can decide an appropriate fee to pay if
  the open ledger fee is higher than they're willing to pay.

=== Release Notes ===
==== Updated Features ====

Both `server_info` and `server_state` report the escalated ledger fee in
the `load_factor` result parameter. If appropriate, `load_factor_server`
reports the server-only portion of the load so clients can submit a fee
between those two values to get into the queue.
This commit is contained in:
Edward Hennis
2016-07-12 20:17:34 -04:00
parent e762d09e7e
commit a252fefede
3 changed files with 267 additions and 20 deletions

View File

@@ -44,6 +44,7 @@
#include <ripple/app/tx/apply.h>
#include <ripple/basics/contract.h>
#include <ripple/basics/Log.h>
#include <ripple/basics/mulDiv.h>
#include <ripple/basics/random.h>
#include <ripple/protocol/digest.h>
#include <ripple/basics/StringUtilities.h>
@@ -2095,57 +2096,74 @@ Json::Value NetworkOPsImp::getServerInfo (bool human, bool admin)
auto const escalationMetrics = app_.getTxQ().getMetrics(
app_.config(), *app_.openLedger().current());
constexpr std::uint64_t max32 =
std::numeric_limits<std::uint32_t>::max();
auto const loadFactorServer = app_.getFeeTrack().getLoadFactor();
auto const loadBaseServer = app_.getFeeTrack().getLoadBase();
auto const loadFactorFeeEscalation = escalationMetrics ?
escalationMetrics->expFeeLevel : 1;
auto const loadBaseFeeEscalation = escalationMetrics ?
escalationMetrics->referenceFeeLevel : 1;
auto const loadFactor = std::max(static_cast<std::uint64_t>(loadFactorServer),
mulDiv(loadFactorFeeEscalation, loadBaseServer, loadBaseFeeEscalation).second);
if (!human)
{
info[jss::load_base] = app_.getFeeTrack ().getLoadBase ();
info[jss::load_factor] = app_.getFeeTrack ().getLoadFactor ();
info[jss::load_base] = loadBaseServer;
info[jss::load_factor] = static_cast<std::uint32_t>(
std::min(max32, loadFactor));
if (escalationMetrics)
{
info[jss::load_factor_server] = loadFactorServer;
/* Json::Value doesn't support uint64, so clamp to max
uint32 value. This is mostly theoretical, since there
probably isn't enough extant XRP to drive the factor
that high.
*/
constexpr std::uint64_t max =
std::numeric_limits<std::uint32_t>::max();
info[jss::load_factor_fee_escalation] =
static_cast<std::uint32_t> (std::min(
max, escalationMetrics->expFeeLevel));
max32, loadFactorFeeEscalation));
info[jss::load_factor_fee_queue] =
static_cast<std::uint32_t> (std::min(
max, escalationMetrics->minFeeLevel));
max32, escalationMetrics->minFeeLevel));
info[jss::load_factor_fee_reference] =
static_cast<std::uint32_t> (std::min(
max, escalationMetrics->referenceFeeLevel));
max32, loadBaseFeeEscalation));
}
}
else
{
info[jss::load_factor] =
static_cast<double> (app_.getFeeTrack ().getLoadFactor ()) /
app_.getFeeTrack ().getLoadBase ();
info[jss::load_factor] = static_cast<double> (loadFactor) / loadBaseServer;
if (loadFactorServer != loadFactor)
info[jss::load_factor_server] =
static_cast<double> (loadFactorServer) / loadBaseServer;
if (admin)
{
std::uint32_t base = app_.getFeeTrack().getLoadBase();
std::uint32_t fee = app_.getFeeTrack().getLocalFee();
if (fee != base)
if (fee != loadBaseServer)
info[jss::load_factor_local] =
static_cast<double> (fee) / base;
static_cast<double> (fee) / loadBaseServer;
fee = app_.getFeeTrack ().getRemoteFee();
if (fee != base)
if (fee != loadBaseServer)
info[jss::load_factor_net] =
static_cast<double> (fee) / base;
static_cast<double> (fee) / loadBaseServer;
fee = app_.getFeeTrack().getClusterFee();
if (fee != base)
if (fee != loadBaseServer)
info[jss::load_factor_cluster] =
static_cast<double> (fee) / base;
static_cast<double> (fee) / loadBaseServer;
}
if (escalationMetrics)
{
if (escalationMetrics->expFeeLevel !=
escalationMetrics->referenceFeeLevel)
if (loadFactorFeeEscalation !=
escalationMetrics->referenceFeeLevel &&
(admin || loadFactorFeeEscalation != loadFactor))
info[jss::load_factor_fee_escalation] =
static_cast<double> (escalationMetrics->expFeeLevel) /
static_cast<double> (loadFactorFeeEscalation) /
escalationMetrics->referenceFeeLevel;
if (escalationMetrics->minFeeLevel !=
escalationMetrics->referenceFeeLevel)

View File

@@ -2171,6 +2171,233 @@ public:
}
}
void testServerInfo()
{
using namespace jtx;
Env env(*this, makeConfig({ { "minimum_txn_in_ledger_standalone", "3" } }),
features(featureFeeEscalation));
Env_ss envs(env);
Account const alice{ "alice" };
env.fund(XRP(1000000), alice);
env.close();
auto submitParams = Json::Value(Json::objectValue);
// Max fee = 100 drops
submitParams[jss::fee_mult_max] = 10;
submitParams["x-queue-okay"] = true;
submitParams["x_queue_okay"] = true;
{
auto const server_info = env.rpc("server_info");
BEAST_EXPECT(server_info.isMember(jss::result) &&
server_info[jss::result].isMember(jss::info));
auto const& info = server_info[jss::result][jss::info];
BEAST_EXPECT(info.isMember(jss::load_factor) &&
info[jss::load_factor] == 1);
BEAST_EXPECT(!info.isMember(jss::load_factor_server));
BEAST_EXPECT(!info.isMember(jss::load_factor_local));
BEAST_EXPECT(!info.isMember(jss::load_factor_net));
BEAST_EXPECT(!info.isMember(jss::load_factor_fee_escalation));
}
{
auto const server_state = env.rpc("server_state");
log << server_state;
auto const& state = server_state[jss::result][jss::state];
BEAST_EXPECT(state.isMember(jss::load_factor) &&
state[jss::load_factor] == 256);
BEAST_EXPECT(state.isMember(jss::load_base) &&
state[jss::load_base] == 256);
BEAST_EXPECT(state.isMember(jss::load_factor_server) &&
state[jss::load_factor_server] == 256);
BEAST_EXPECT(state.isMember(jss::load_factor_fee_escalation) &&
state[jss::load_factor_fee_escalation] == 256);
BEAST_EXPECT(state.isMember(jss::load_factor_fee_queue) &&
state[jss::load_factor_fee_queue] == 256);
BEAST_EXPECT(state.isMember(jss::load_factor_fee_reference) &&
state[jss::load_factor_fee_reference] == 256);
}
checkMetrics(env, 0, 6, 0, 3, 256);
fillQueue(env, alice);
checkMetrics(env, 0, 6, 4, 3, 256);
auto aliceSeq = env.seq(alice);
for (auto i = 0; i < 4; ++i)
envs(noop(alice), fee(none), seq(aliceSeq + i),
ter(terQUEUED))(submitParams);
checkMetrics(env, 4, 6, 4, 3, 256);
{
auto const server_info = env.rpc("server_info");
BEAST_EXPECT(server_info.isMember(jss::result) &&
server_info[jss::result].isMember(jss::info));
auto const& info = server_info[jss::result][jss::info];
// Avoid double rounding issues by comparing to a range.
BEAST_EXPECT(info.isMember(jss::load_factor) &&
info[jss::load_factor] > 888.88 &&
info[jss::load_factor] < 888.89);
BEAST_EXPECT(info.isMember(jss::load_factor_server) &&
info[jss::load_factor_server] == 1);
BEAST_EXPECT(!info.isMember(jss::load_factor_local));
BEAST_EXPECT(!info.isMember(jss::load_factor_net));
BEAST_EXPECT(info.isMember(jss::load_factor_fee_escalation) &&
info[jss::load_factor_fee_escalation] > 888.88 &&
info[jss::load_factor_fee_escalation] < 888.89);
}
{
auto const server_state = env.rpc("server_state");
log << server_state;
auto const& state = server_state[jss::result][jss::state];
BEAST_EXPECT(state.isMember(jss::load_factor) &&
state[jss::load_factor] == 227555);
BEAST_EXPECT(state.isMember(jss::load_base) &&
state[jss::load_base] == 256);
BEAST_EXPECT(state.isMember(jss::load_factor_server) &&
state[jss::load_factor_server] == 256);
BEAST_EXPECT(state.isMember(jss::load_factor_fee_escalation) &&
state[jss::load_factor_fee_escalation] == 227555);
BEAST_EXPECT(state.isMember(jss::load_factor_fee_queue) &&
state[jss::load_factor_fee_queue] == 256);
BEAST_EXPECT(state.isMember(jss::load_factor_fee_reference) &&
state[jss::load_factor_fee_reference] == 256);
}
env.app().getFeeTrack().setRemoteFee(256000);
{
auto const server_info = env.rpc("server_info");
BEAST_EXPECT(server_info.isMember(jss::result) &&
server_info[jss::result].isMember(jss::info));
auto const& info = server_info[jss::result][jss::info];
// Avoid double rounding issues by comparing to a range.
BEAST_EXPECT(info.isMember(jss::load_factor) &&
info[jss::load_factor] == 1000);
BEAST_EXPECT(!info.isMember(jss::load_factor_server));
BEAST_EXPECT(!info.isMember(jss::load_factor_local));
BEAST_EXPECT(info.isMember(jss::load_factor_net) &&
info[jss::load_factor_net] == 1000);
BEAST_EXPECT(info.isMember(jss::load_factor_fee_escalation) &&
info[jss::load_factor_fee_escalation] > 888.88 &&
info[jss::load_factor_fee_escalation] < 888.89);
}
{
auto const server_state = env.rpc("server_state");
log << server_state;
auto const& state = server_state[jss::result][jss::state];
BEAST_EXPECT(state.isMember(jss::load_factor) &&
state[jss::load_factor] == 256000);
BEAST_EXPECT(state.isMember(jss::load_base) &&
state[jss::load_base] == 256);
BEAST_EXPECT(state.isMember(jss::load_factor_server) &&
state[jss::load_factor_server] == 256000);
BEAST_EXPECT(state.isMember(jss::load_factor_fee_escalation) &&
state[jss::load_factor_fee_escalation] == 227555);
BEAST_EXPECT(state.isMember(jss::load_factor_fee_queue) &&
state[jss::load_factor_fee_queue] == 256);
BEAST_EXPECT(state.isMember(jss::load_factor_fee_reference) &&
state[jss::load_factor_fee_reference] == 256);
}
env.app().getFeeTrack().setRemoteFee(256);
// Increase the server load
for (int i = 0; i < 5; ++i)
env.app().getFeeTrack().raiseLocalFee();
BEAST_EXPECT(env.app().getFeeTrack().getLoadFactor() == 625);
{
auto const server_info = env.rpc("server_info");
BEAST_EXPECT(server_info.isMember(jss::result) &&
server_info[jss::result].isMember(jss::info));
auto const& info = server_info[jss::result][jss::info];
// Avoid double rounding issues by comparing to a range.
BEAST_EXPECT(info.isMember(jss::load_factor) &&
info[jss::load_factor] > 888.88 &&
info[jss::load_factor] < 888.89);
// There can be a race between LoadManager lowering the fee,
// and the call to server_info, so check a wide range.
// The important thing is that it's not 1.
BEAST_EXPECT(info.isMember(jss::load_factor_server) &&
info[jss::load_factor_server] > 1.245 &&
info[jss::load_factor_server] < 2.4415);
BEAST_EXPECT(info.isMember(jss::load_factor_local) &&
info[jss::load_factor_local] > 1.245 &&
info[jss::load_factor_local] < 2.4415);
BEAST_EXPECT(!info.isMember(jss::load_factor_net));
BEAST_EXPECT(info.isMember(jss::load_factor_fee_escalation) &&
info[jss::load_factor_fee_escalation] > 888.88 &&
info[jss::load_factor_fee_escalation] < 888.89);
}
{
auto const server_state = env.rpc("server_state");
log << server_state;
auto const& state = server_state[jss::result][jss::state];
BEAST_EXPECT(state.isMember(jss::load_factor) &&
state[jss::load_factor] == 227555);
BEAST_EXPECT(state.isMember(jss::load_base) &&
state[jss::load_base] == 256);
// There can be a race between LoadManager lowering the fee,
// and the call to server_info, so check a wide range.
// The important thing is that it's not 256.
BEAST_EXPECT(state.isMember(jss::load_factor_server) &&
state[jss::load_factor_server] >= 320 &&
state[jss::load_factor_server] <= 625);
BEAST_EXPECT(state.isMember(jss::load_factor_fee_escalation) &&
state[jss::load_factor_fee_escalation] == 227555);
BEAST_EXPECT(state.isMember(jss::load_factor_fee_queue) &&
state[jss::load_factor_fee_queue] == 256);
BEAST_EXPECT(state.isMember(jss::load_factor_fee_reference) &&
state[jss::load_factor_fee_reference] == 256);
}
env.close();
{
auto const server_info = env.rpc("server_info");
BEAST_EXPECT(server_info.isMember(jss::result) &&
server_info[jss::result].isMember(jss::info));
auto const& info = server_info[jss::result][jss::info];
// Avoid double rounding issues by comparing to a range.
// There can be a race between LoadManager lowering the fee,
// and the call to server_info, so check a wide range.
// The important thing is that it's not 1.
BEAST_EXPECT(info.isMember(jss::load_factor) &&
info[jss::load_factor] > 1.245 &&
info[jss::load_factor] < 2.4415);
BEAST_EXPECT(!info.isMember(jss::load_factor_server));
BEAST_EXPECT(info.isMember(jss::load_factor_local) &&
info[jss::load_factor_local] > 1.245 &&
info[jss::load_factor_local] < 2.4415);
BEAST_EXPECT(!info.isMember(jss::load_factor_net));
BEAST_EXPECT(!info.isMember(jss::load_factor_fee_escalation));
}
{
auto const server_state = env.rpc("server_state");
log << server_state;
auto const& state = server_state[jss::result][jss::state];
BEAST_EXPECT(state.isMember(jss::load_factor) &&
state[jss::load_factor] >= 320 &&
state[jss::load_factor] <= 625);
BEAST_EXPECT(state.isMember(jss::load_base) &&
state[jss::load_base] == 256);
// There can be a race between LoadManager lowering the fee,
// and the call to server_info, so check a wide range.
// The important thing is that it's not 256.
BEAST_EXPECT(state.isMember(jss::load_factor_server) &&
state[jss::load_factor_server] >= 320 &&
state[jss::load_factor_server] <= 625);
BEAST_EXPECT(state.isMember(jss::load_factor_fee_escalation) &&
state[jss::load_factor_fee_escalation] == 256);
BEAST_EXPECT(state.isMember(jss::load_factor_fee_queue) &&
state[jss::load_factor_fee_queue] == 256);
BEAST_EXPECT(state.isMember(jss::load_factor_fee_reference) &&
state[jss::load_factor_fee_reference] == 256);
}
}
void run()
{
testQueue();
@@ -2192,6 +2419,7 @@ public:
testExpirationReplacement();
testSignAndSubmitSequence();
testAccountInfo();
testServerInfo();
}
};

View File

@@ -244,6 +244,7 @@ JSS ( load_factor_fee_queue ); // out: NetworkOPs
JSS ( load_factor_fee_reference ); // out: NetworkOPs
JSS ( load_factor_local ); // out: NetworkOPs
JSS ( load_factor_net ); // out: NetworkOPs
JSS ( load_factor_server ); // out: NetworkOPs
JSS ( load_fee ); // out: LoadFeeTrackImp, NetworkOPs
JSS ( local ); // out: resource/Logic.h
JSS ( local_txs ); // out: GetCounts