diff --git a/src/ripple/app/misc/impl/TxQ.cpp b/src/ripple/app/misc/impl/TxQ.cpp index 4ee348b064..45c63af08e 100644 --- a/src/ripple/app/misc/impl/TxQ.cpp +++ b/src/ripple/app/misc/impl/TxQ.cpp @@ -111,10 +111,12 @@ TxQ::FeeMetrics::update(Application& app, // Ledgers are taking to long to process, // so clamp down on limits. auto const cutPct = 100 - setup.slowConsensusDecreasePercent; + // upperLimit must be >= minimumTxnCount_ or boost::clamp can give + // unexpected results + auto const upperLimit = std::max( + mulDiv(txnsExpected_, cutPct, 100).second, minimumTxnCount_); txnsExpected_ = boost::algorithm::clamp( - mulDiv(size, cutPct, 100).second, - minimumTxnCount_, - mulDiv(txnsExpected_, cutPct, 100).second); + mulDiv(size, cutPct, 100).second, minimumTxnCount_, upperLimit); recentTxnCounts_.clear(); } else if (size > txnsExpected_ || @@ -1534,7 +1536,28 @@ setup_TxQ(Config const& config) set(setup.targetTxnInLedger, "target_txn_in_ledger", section); std::uint32_t max; if (set(max, "maximum_txn_in_ledger", section)) + { + if (max < setup.minimumTxnInLedger) + { + Throw( + "The minimum number of low-fee transactions allowed " + "per ledger (minimum_txn_in_ledger) exceeds " + "the maximum number of low-fee transactions allowed per " + "ledger (maximum_txn_in_ledger)." + ); + } + if (max < setup.minimumTxnInLedgerSA) + { + Throw( + "The minimum number of low-fee transactions allowed " + "per ledger (minimum_txn_in_ledger_standalone) exceeds " + "the maximum number of low-fee transactions allowed per " + "ledger (maximum_txn_in_ledger)." + ); + } + setup.maximumTxnInLedger.emplace(max); + } /* The math works as expected for any value up to and including MAXINT, but put a reasonable limit on this percentage so that diff --git a/src/test/app/TxQ_test.cpp b/src/test/app/TxQ_test.cpp index b39c9df212..0cfd81ad24 100644 --- a/src/test/app/TxQ_test.cpp +++ b/src/test/app/TxQ_test.cpp @@ -1144,28 +1144,88 @@ public: void testMaximum() { using namespace jtx; + using namespace std::string_literals; - Env env(*this, makeConfig( - { {"minimum_txn_in_ledger_standalone", "2"}, - {"target_txn_in_ledger", "4"}, - {"maximum_txn_in_ledger", "5"} })); + { + Env env(*this, makeConfig( + { {"minimum_txn_in_ledger_standalone", "2"}, + {"target_txn_in_ledger", "4"}, + {"maximum_txn_in_ledger", "5"} })); - auto alice = Account("alice"); + auto alice = Account("alice"); - checkMetrics(env, 0, boost::none, 0, 2, 256); + checkMetrics(env, 0, boost::none, 0, 2, 256); - env.fund(XRP(50000), noripple(alice)); - checkMetrics(env, 0, boost::none, 1, 2, 256); + env.fund(XRP(50000), noripple(alice)); + checkMetrics(env, 0, boost::none, 1, 2, 256); - for (int i = 0; i < 10; ++i) - env(noop(alice), openLedgerFee(env)); + for (int i = 0; i < 10; ++i) + env(noop(alice), openLedgerFee(env)); - checkMetrics(env, 0, boost::none, 11, 2, 256); + checkMetrics(env, 0, boost::none, 11, 2, 256); - env.close(); - // If not for the maximum, the per ledger would be 11. - checkMetrics(env, 0, 10, 0, 5, 256, 800025); + env.close(); + // If not for the maximum, the per ledger would be 11. + checkMetrics(env, 0, 10, 0, 5, 256, 800025); + } + try + { + Env env(*this, makeConfig( + { {"minimum_txn_in_ledger", "200"}, + {"minimum_txn_in_ledger_standalone", "200"}, + {"target_txn_in_ledger", "4"}, + {"maximum_txn_in_ledger", "5"} })); + // should throw + fail(); + } + catch (std::runtime_error const& e) + { + BEAST_EXPECT(e.what() == + "The minimum number of low-fee transactions allowed " + "per ledger (minimum_txn_in_ledger) exceeds " + "the maximum number of low-fee transactions allowed per " + "ledger (maximum_txn_in_ledger)."s + ); + } + try + { + Env env(*this, makeConfig( + { {"minimum_txn_in_ledger", "200"}, + {"minimum_txn_in_ledger_standalone", "2"}, + {"target_txn_in_ledger", "4"}, + {"maximum_txn_in_ledger", "5"} })); + // should throw + fail(); + } + catch (std::runtime_error const& e) + { + BEAST_EXPECT(e.what() == + "The minimum number of low-fee transactions allowed " + "per ledger (minimum_txn_in_ledger) exceeds " + "the maximum number of low-fee transactions allowed per " + "ledger (maximum_txn_in_ledger)."s + ); + } + try + { + Env env(*this, makeConfig( + { {"minimum_txn_in_ledger", "2"}, + {"minimum_txn_in_ledger_standalone", "200"}, + {"target_txn_in_ledger", "4"}, + {"maximum_txn_in_ledger", "5"} })); + // should throw + fail(); + } + catch (std::runtime_error const& e) + { + BEAST_EXPECT(e.what() == + "The minimum number of low-fee transactions allowed " + "per ledger (minimum_txn_in_ledger_standalone) exceeds " + "the maximum number of low-fee transactions allowed per " + "ledger (maximum_txn_in_ledger)."s + ); + } } void testUnexpectedBalanceChange()