diff --git a/src/ripple/app/misc/NetworkOPs.cpp b/src/ripple/app/misc/NetworkOPs.cpp index 97c260282..f61f814ea 100644 --- a/src/ripple/app/misc/NetworkOPs.cpp +++ b/src/ripple/app/misc/NetworkOPs.cpp @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include @@ -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::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(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::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::max(); info[jss::load_factor_fee_escalation] = static_cast (std::min( - max, escalationMetrics->expFeeLevel)); + max32, loadFactorFeeEscalation)); info[jss::load_factor_fee_queue] = static_cast (std::min( - max, escalationMetrics->minFeeLevel)); + max32, escalationMetrics->minFeeLevel)); info[jss::load_factor_fee_reference] = static_cast (std::min( - max, escalationMetrics->referenceFeeLevel)); + max32, loadBaseFeeEscalation)); } } else { - info[jss::load_factor] = - static_cast (app_.getFeeTrack ().getLoadFactor ()) / - app_.getFeeTrack ().getLoadBase (); + info[jss::load_factor] = static_cast (loadFactor) / loadBaseServer; + + if (loadFactorServer != loadFactor) + info[jss::load_factor_server] = + static_cast (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 (fee) / base; + static_cast (fee) / loadBaseServer; fee = app_.getFeeTrack ().getRemoteFee(); - if (fee != base) + if (fee != loadBaseServer) info[jss::load_factor_net] = - static_cast (fee) / base; + static_cast (fee) / loadBaseServer; fee = app_.getFeeTrack().getClusterFee(); - if (fee != base) + if (fee != loadBaseServer) info[jss::load_factor_cluster] = - static_cast (fee) / base; + static_cast (fee) / loadBaseServer; } if (escalationMetrics) { - if (escalationMetrics->expFeeLevel != - escalationMetrics->referenceFeeLevel) + if (loadFactorFeeEscalation != + escalationMetrics->referenceFeeLevel && + (admin || loadFactorFeeEscalation != loadFactor)) info[jss::load_factor_fee_escalation] = - static_cast (escalationMetrics->expFeeLevel) / + static_cast (loadFactorFeeEscalation) / escalationMetrics->referenceFeeLevel; if (escalationMetrics->minFeeLevel != escalationMetrics->referenceFeeLevel) diff --git a/src/ripple/app/tests/TxQ_test.cpp b/src/ripple/app/tests/TxQ_test.cpp index 5009da660..d5dfc3ff7 100644 --- a/src/ripple/app/tests/TxQ_test.cpp +++ b/src/ripple/app/tests/TxQ_test.cpp @@ -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(); } }; diff --git a/src/ripple/protocol/JsonFields.h b/src/ripple/protocol/JsonFields.h index ea85973c8..9aa4c5763 100644 --- a/src/ripple/protocol/JsonFields.h +++ b/src/ripple/protocol/JsonFields.h @@ -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