From ef66a1cc0eee294fbe73ae8734672d69c926d501 Mon Sep 17 00:00:00 2001 From: Ed Hennis Date: Wed, 12 Nov 2025 13:46:11 -0500 Subject: [PATCH] Disable inner Batch transactions for Vault and Loan types --- src/test/app/Batch_test.cpp | 21 ++++++++++++++++----- src/test/app/Loan_test.cpp | 9 ++++++++- src/xrpld/app/tx/detail/Batch.cpp | 11 ++++++++++- src/xrpld/app/tx/detail/Batch.h | 18 ++++++++++++++++++ 4 files changed, 52 insertions(+), 7 deletions(-) diff --git a/src/test/app/Batch_test.cpp b/src/test/app/Batch_test.cpp index b74d492cc1..2f3b9337e0 100644 --- a/src/test/app/Batch_test.cpp +++ b/src/test/app/Batch_test.cpp @@ -2543,6 +2543,11 @@ class Batch_test : public beast::unit_test::suite { testcase("loan"); + bool const lendingBatchEnabled = !std::any_of( + Batch::disabledTxTypes.begin(), + Batch::disabledTxTypes.end(), + [](auto const& disabled) { return disabled == ttLOAN_BROKER_SET; }); + using namespace test::jtx; test::jtx::Env env{ @@ -2609,7 +2614,8 @@ class Batch_test : public beast::unit_test::suite { auto const [txIDs, batchID] = submitBatch( env, - temBAD_SIGNATURE, + lendingBatchEnabled ? temBAD_SIGNATURE + : temINVALID_INNER_BATCH, batch::outer(lender, lenderSeq, batchFee, tfAllOrNothing), batch::inner( env.json( @@ -2648,7 +2654,8 @@ class Batch_test : public beast::unit_test::suite { auto const [txIDs, batchID] = submitBatch( env, - temBAD_SIGNER, + lendingBatchEnabled ? temBAD_SIGNER + : temINVALID_INNER_BATCH, batch::outer(lender, lenderSeq, batchFee, tfAllOrNothing), batch::inner( env.json( @@ -2672,7 +2679,8 @@ class Batch_test : public beast::unit_test::suite auto const batchFee = batch::calcBatchFee(env, 1, 2); auto const [txIDs, batchID] = submitBatch( env, - tesSUCCESS, + lendingBatchEnabled ? TER(tesSUCCESS) + : TER(temINVALID_INNER_BATCH), batch::outer(lender, lenderSeq, batchFee, tfAllOrNothing), batch::inner( env.json( @@ -2704,7 +2712,8 @@ class Batch_test : public beast::unit_test::suite auto const batchFee = batch::calcBatchFee(env, 1, 2); auto const [txIDs, batchID] = submitBatch( env, - tesSUCCESS, + lendingBatchEnabled ? TER(tesSUCCESS) + : TER(temINVALID_INNER_BATCH), batch::outer(lender, lenderSeq, batchFee, tfAllOrNothing), batch::inner( env.json( @@ -2721,7 +2730,9 @@ class Batch_test : public beast::unit_test::suite } env.close(); BEAST_EXPECT(env.le(brokerKeylet)); - if (auto const sleLoan = env.le(loanKeylet); BEAST_EXPECT(sleLoan)) + if (auto const sleLoan = env.le(loanKeylet); lendingBatchEnabled + ? BEAST_EXPECT(sleLoan) + : !BEAST_EXPECT(!sleLoan)) { BEAST_EXPECT(sleLoan->isFlag(lsfLoanImpaired)); } diff --git a/src/test/app/Loan_test.cpp b/src/test/app/Loan_test.cpp index 47b76d2a8a..da8b964c94 100644 --- a/src/test/app/Loan_test.cpp +++ b/src/test/app/Loan_test.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -3801,6 +3802,11 @@ protected: // From FIND-001 testcase << "Batch Bypass Counterparty"; + bool const lendingBatchEnabled = !std::any_of( + Batch::disabledTxTypes.begin(), + Batch::disabledTxTypes.end(), + [](auto const& disabled) { return disabled == ttLOAN_BROKER_SET; }); + using namespace jtx; using namespace std::chrono_literals; Env env(*this, all); @@ -3846,7 +3852,8 @@ protected: env(batch::outer(borrower, seq, batchFee, tfAllOrNothing), batch::inner(forgedLoanSet, seq + 1), batch::inner(pay(borrower, lender, XRP(1)), seq + 2), - ter(temBAD_SIGNATURE)); + ter(lendingBatchEnabled ? temBAD_SIGNATURE + : temINVALID_INNER_BATCH)); env.close(); // ? Check that the loan was NOT created diff --git a/src/xrpld/app/tx/detail/Batch.cpp b/src/xrpld/app/tx/detail/Batch.cpp index 052e90e19e..9d2fe4e47d 100644 --- a/src/xrpld/app/tx/detail/Batch.cpp +++ b/src/xrpld/app/tx/detail/Batch.cpp @@ -263,7 +263,8 @@ Batch::preflight(PreflightContext const& ctx) return temREDUNDANT; } - if (stx.getFieldU16(sfTransactionType) == ttBATCH) + auto const txType = stx.getFieldU16(sfTransactionType); + if (txType == ttBATCH) { JLOG(ctx.j.debug()) << "BatchTrace[" << parentBatchId << "]: " << "batch cannot have an inner batch txn. " @@ -271,6 +272,14 @@ Batch::preflight(PreflightContext const& ctx) return temINVALID; } + if (std::any_of( + disabledTxTypes.begin(), + disabledTxTypes.end(), + [txType](auto const& disabled) { return txType == disabled; })) + { + return temINVALID_INNER_BATCH; + } + if (!(stx.getFlags() & tfInnerBatchTxn)) { JLOG(ctx.j.debug()) diff --git a/src/xrpld/app/tx/detail/Batch.h b/src/xrpld/app/tx/detail/Batch.h index 1b1d7614d5..7889e91bdc 100644 --- a/src/xrpld/app/tx/detail/Batch.h +++ b/src/xrpld/app/tx/detail/Batch.h @@ -35,6 +35,24 @@ public: TER doApply() override; + + static constexpr auto disabledTxTypes = std::to_array({ + ttVAULT_CREATE, + ttVAULT_SET, + ttVAULT_DELETE, + ttVAULT_DEPOSIT, + ttVAULT_WITHDRAW, + ttVAULT_CLAWBACK, + ttLOAN_BROKER_SET, + ttLOAN_BROKER_DELETE, + ttLOAN_BROKER_COVER_DEPOSIT, + ttLOAN_BROKER_COVER_WITHDRAW, + ttLOAN_BROKER_COVER_CLAWBACK, + ttLOAN_SET, + ttLOAN_DELETE, + ttLOAN_MANAGE, + ttLOAN_PAY, + }); }; } // namespace ripple