diff --git a/src/test/app/LoanBroker_test.cpp b/src/test/app/LoanBroker_test.cpp index 6169ef325c..e8b450e886 100644 --- a/src/test/app/LoanBroker_test.cpp +++ b/src/test/app/LoanBroker_test.cpp @@ -72,7 +72,7 @@ class LoanBroker_test : public beast::unit_test::suite using namespace loanBroker; // Can't create a loan broker regardless of whether the vault exists - env(set(alice, keylet.key), ter(temDISABLED)); + env(set(alice, keylet.key), fee(increment), ter(temDISABLED)); }; failAll(all - featureMPTokensV1); failAll(all - featureSingleAssetVault - featureLendingProtocol); @@ -153,62 +153,80 @@ class LoanBroker_test : public beast::unit_test::suite { auto badKeylet = keylet::vault(alice.id(), env.seq(alice)); // Try some failure cases + // insufficient fee + env(set(evan, vault.vaultID), ter(telINSUF_FEE_P)); // not the vault owner - env(set(evan, vault.vaultID), ter(tecNO_PERMISSION)); + env(set(evan, vault.vaultID), + fee(increment), + ter(tecNO_PERMISSION)); // not a vault - env(set(alice, badKeylet.key), ter(tecNO_ENTRY)); + env(set(alice, badKeylet.key), + fee(increment), + ter(tecNO_ENTRY)); // flags are checked first env(set(evan, vault.vaultID, ~tfUniversal), + fee(increment), ter(temINVALID_FLAG)); // field length validation // sfData: good length, bad account env(set(evan, vault.vaultID), + fee(increment), data(std::string(maxDataPayloadLength, 'X')), ter(tecNO_PERMISSION)); // sfData: too long env(set(evan, vault.vaultID), + fee(increment), data(std::string(maxDataPayloadLength + 1, 'Y')), ter(temINVALID)); // sfManagementFeeRate: good value, bad account env(set(evan, vault.vaultID), managementFeeRate(maxFeeRate), + fee(increment), ter(tecNO_PERMISSION)); // sfManagementFeeRate: too big env(set(evan, vault.vaultID), managementFeeRate(maxFeeRate + 1), + fee(increment), ter(temINVALID)); // sfCoverRateMinimum: good value, bad account env(set(evan, vault.vaultID), coverRateMinimum(maxCoverRate), + fee(increment), ter(tecNO_PERMISSION)); // sfCoverRateMinimum: too big env(set(evan, vault.vaultID), coverRateMinimum(maxCoverRate + 1), + fee(increment), ter(temINVALID)); // sfCoverRateLiquidation: good value, bad account env(set(evan, vault.vaultID), coverRateLiquidation(maxCoverRate), + fee(increment), ter(tecNO_PERMISSION)); // sfCoverRateLiquidation: too big env(set(evan, vault.vaultID), coverRateLiquidation(maxCoverRate + 1), + fee(increment), ter(temINVALID)); // sfDebtMaximum: good value, bad account env(set(evan, vault.vaultID), debtMaximum(Number(0)), + fee(increment), ter(tecNO_PERMISSION)); // sfDebtMaximum: overflow env(set(evan, vault.vaultID), debtMaximum(Number(1, 100)), + fee(increment), ter(temINVALID)); // sfDebtMaximum: negative env(set(evan, vault.vaultID), debtMaximum(Number(-1)), + fee(increment), ter(temINVALID)); auto keylet = keylet::loanbroker(alice.id(), env.seq(alice)); // Successfully create a Loan Broker with all default values. - env(set(alice, vault.vaultID)); + env(set(alice, vault.vaultID), fee(increment)); env.close(); if (auto broker = env.le(keylet); BEAST_EXPECT(broker)) { @@ -339,7 +357,8 @@ class LoanBroker_test : public beast::unit_test::suite managementFeeRate(123), debtMaximum(Number(9)), coverRateMinimum(100), - coverRateLiquidation(200)); + coverRateLiquidation(200), + fee(increment)); env.close(); if (auto broker = env.le(keylet2); BEAST_EXPECT(broker)) { diff --git a/src/test/jtx/fee.h b/src/test/jtx/fee.h index 7d54804f87..3e3740b80d 100644 --- a/src/test/jtx/fee.h +++ b/src/test/jtx/fee.h @@ -37,6 +37,7 @@ class fee { private: bool manual_ = true; + bool increment_ = false; std::optional amount_; public: @@ -44,6 +45,10 @@ public: { } + explicit fee(increment_t) : increment_(true) + { + } + explicit fee(none_t) { } diff --git a/src/test/jtx/impl/fee.cpp b/src/test/jtx/impl/fee.cpp index 71e3dd089a..b887849946 100644 --- a/src/test/jtx/impl/fee.cpp +++ b/src/test/jtx/impl/fee.cpp @@ -26,13 +26,15 @@ namespace test { namespace jtx { void -fee::operator()(Env&, JTx& jt) const +fee::operator()(Env& env, JTx& jt) const { if (!manual_) return; jt.fill_fee = false; + if (increment_) + jt[sfFee] = STAmount(env.current()->fees().increment).getJson(); if (amount_) - jt[jss::Fee] = amount_->getJson(JsonOptions::none); + jt[sfFee] = amount_->getJson(JsonOptions::none); } } // namespace jtx diff --git a/src/test/jtx/tags.h b/src/test/jtx/tags.h index bb64295f05..4d55929d69 100644 --- a/src/test/jtx/tags.h +++ b/src/test/jtx/tags.h @@ -49,6 +49,16 @@ struct disabled_t }; static disabled_t const disabled; +/** Used for fee() calls that use an owner reserve increment */ +struct increment_t +{ + increment_t() + { + } +}; + +static increment_t const increment; + } // namespace jtx } // namespace test diff --git a/src/test/ledger/Invariants_test.cpp b/src/test/ledger/Invariants_test.cpp index d3950cdfa2..b0230e9aee 100644 --- a/src/test/ledger/Invariants_test.cpp +++ b/src/test/ledger/Invariants_test.cpp @@ -1319,21 +1319,16 @@ class Invariants_test : public beast::unit_test::suite auto [tx, vKeylet] = vault.create({.owner = a, .asset = xrpAsset}); env(tx); - env.close(); BEAST_EXPECT(env.le(vKeylet)); vaultID = vKeylet.key; - env(vault.deposit( - {.depositor = a, .id = vaultID, .amount = xrpAsset(50)})); - env.close(); - // Create Loan Broker using namespace loanBroker; loanBrokerKeylet = keylet::loanbroker(a.id(), env.seq(a)); // Create a Loan Broker with all default values. - env(set(a, vaultID)); + env(set(a, vaultID), fee(increment)); return BEAST_EXPECT(env.le(loanBrokerKeylet)); }; diff --git a/src/xrpld/app/tx/detail/AMMCreate.cpp b/src/xrpld/app/tx/detail/AMMCreate.cpp index 56f898871e..4e376e51dd 100644 --- a/src/xrpld/app/tx/detail/AMMCreate.cpp +++ b/src/xrpld/app/tx/detail/AMMCreate.cpp @@ -81,7 +81,7 @@ XRPAmount AMMCreate::calculateBaseFee(ReadView const& view, STTx const& tx) { // The fee required for AMMCreate is one owner reserve. - return view.fees().increment; + return calculateOwnerReserveFee(view, tx); } TER diff --git a/src/xrpld/app/tx/detail/DeleteAccount.cpp b/src/xrpld/app/tx/detail/DeleteAccount.cpp index 3ca9f6d324..b2150a6d55 100644 --- a/src/xrpld/app/tx/detail/DeleteAccount.cpp +++ b/src/xrpld/app/tx/detail/DeleteAccount.cpp @@ -70,7 +70,7 @@ XRPAmount DeleteAccount::calculateBaseFee(ReadView const& view, STTx const& tx) { // The fee required for AccountDelete is one owner reserve. - return view.fees().increment; + return calculateOwnerReserveFee(view, tx); } namespace { diff --git a/src/xrpld/app/tx/detail/LedgerStateFix.cpp b/src/xrpld/app/tx/detail/LedgerStateFix.cpp index a3a7ba90a8..9ea022bb30 100644 --- a/src/xrpld/app/tx/detail/LedgerStateFix.cpp +++ b/src/xrpld/app/tx/detail/LedgerStateFix.cpp @@ -61,7 +61,7 @@ LedgerStateFix::calculateBaseFee(ReadView const& view, STTx const& tx) { // The fee required for LedgerStateFix is one owner reserve, just like // the fee for AccountDelete. - return view.fees().increment; + return calculateOwnerReserveFee(view, tx); } TER diff --git a/src/xrpld/app/tx/detail/LoanBrokerSet.cpp b/src/xrpld/app/tx/detail/LoanBrokerSet.cpp index 7d09383752..7a043e205b 100644 --- a/src/xrpld/app/tx/detail/LoanBrokerSet.cpp +++ b/src/xrpld/app/tx/detail/LoanBrokerSet.cpp @@ -92,6 +92,15 @@ LoanBrokerSet::doPreflight(PreflightContext const& ctx) return tesSUCCESS; } +XRPAmount +LoanBrokerSet::calculateBaseFee(ReadView const& view, STTx const& tx) +{ + // One reserve increment is typically much greater than one base fee. + if (!tx.isFieldPresent(sfLoanBrokerID)) + return calculateOwnerReserveFee(view, tx); + return Transactor::calculateBaseFee(view, tx); +} + TER LoanBrokerSet::preclaim(PreclaimContext const& ctx) { diff --git a/src/xrpld/app/tx/detail/LoanBrokerSet.h b/src/xrpld/app/tx/detail/LoanBrokerSet.h index be118ebe92..23248d1c9d 100644 --- a/src/xrpld/app/tx/detail/LoanBrokerSet.h +++ b/src/xrpld/app/tx/detail/LoanBrokerSet.h @@ -46,6 +46,9 @@ public: static NotTEC doPreflight(PreflightContext const& ctx); + static XRPAmount + calculateBaseFee(ReadView const& view, STTx const& tx); + static TER preclaim(PreclaimContext const& ctx); diff --git a/src/xrpld/app/tx/detail/Transactor.cpp b/src/xrpld/app/tx/detail/Transactor.cpp index a5dbca31a4..54ca58c0e5 100644 --- a/src/xrpld/app/tx/detail/Transactor.cpp +++ b/src/xrpld/app/tx/detail/Transactor.cpp @@ -233,6 +233,18 @@ Transactor::calculateBaseFee(ReadView const& view, STTx const& tx) return baseFee + (signerCount * baseFee); } +// Returns the fee in fee units, not scaled for load. +XRPAmount +Transactor::calculateOwnerReserveFee(ReadView const& view, STTx const& tx) +{ + // One reserve increment is typically much greater than one base fee. + XRPL_ASSERT( + view.fees().increment > view.fees().base * 100, + "ripple::Transactor::calculateOwnerReserveFee : Owner reserve is much " + "greater than base fee"); + return view.fees().increment; +} + XRPAmount Transactor::minimumFee( Application& app, diff --git a/src/xrpld/app/tx/detail/Transactor.h b/src/xrpld/app/tx/detail/Transactor.h index 2f42c6b13e..096474e315 100644 --- a/src/xrpld/app/tx/detail/Transactor.h +++ b/src/xrpld/app/tx/detail/Transactor.h @@ -208,6 +208,10 @@ protected: Fees const& fees, ApplyFlags flags); + // Returns the fee in fee units, not scaled for load. + static XRPAmount + calculateOwnerReserveFee(ReadView const& view, STTx const& tx); + // Base class always returns true static bool isEnabled(PreflightContext const& ctx); diff --git a/src/xrpld/app/tx/detail/VaultCreate.cpp b/src/xrpld/app/tx/detail/VaultCreate.cpp index 05bab34d5c..4e99505761 100644 --- a/src/xrpld/app/tx/detail/VaultCreate.cpp +++ b/src/xrpld/app/tx/detail/VaultCreate.cpp @@ -91,7 +91,7 @@ XRPAmount VaultCreate::calculateBaseFee(ReadView const& view, STTx const& tx) { // One reserve increment is typically much greater than one base fee. - return view.fees().increment; + return calculateOwnerReserveFee(view, tx); } TER