From d9a3af82078d596b169de80bb4bda4d19ad58bb1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 19 May 2026 16:35:38 +0000 Subject: [PATCH 1/2] ci: [DEPENDABOT] bump actions/upload-artifact from 7.0.0 to 7.0.1 (#7286) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/reusable-package.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/reusable-package.yml b/.github/workflows/reusable-package.yml index ec019b2e7e..5108a55cd2 100644 --- a/.github/workflows/reusable-package.yml +++ b/.github/workflows/reusable-package.yml @@ -89,7 +89,7 @@ jobs: run: ./package/build_pkg.sh - name: Upload package artifact - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: ${{ matrix.artifact_name }}-pkg-${{ needs.generate-version.outputs.version }} path: | From 93ac1aa7aa23f801b0840a87804a6d755c944623 Mon Sep 17 00:00:00 2001 From: Vito Tumas <5780819+Tapanito@users.noreply.github.com> Date: Tue, 19 May 2026 18:38:50 +0200 Subject: [PATCH 2/2] fix: Disable unnecessary sanity-check in VaultDeposit (#7288) --- .../tx/transactors/vault/VaultDeposit.cpp | 32 +++++--- src/test/app/Vault_test.cpp | 80 +++++++++++++++++++ 2 files changed, 100 insertions(+), 12 deletions(-) diff --git a/src/libxrpl/tx/transactors/vault/VaultDeposit.cpp b/src/libxrpl/tx/transactors/vault/VaultDeposit.cpp index b8230b90c8..aaaf3cced8 100644 --- a/src/libxrpl/tx/transactors/vault/VaultDeposit.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultDeposit.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -252,19 +253,26 @@ VaultDeposit::doApply() !isTesSuccess(ter)) return ter; - // Sanity check - if (accountHolds( - view(), - accountID_, - assetsDeposited.asset(), - FreezeHandling::IgnoreFreeze, - AuthHandling::IgnoreAuth, - j_) < beast::kZero) + // This check is wrong. Disable it with fixCleanup3_2_0. + // For XRP and MPT the predicate is structurally unsatisfiable: xrpLiquid clamps at zero, and + // MPT balances are unsigned. For IOUs it only fires when the deposit drove the depositor's + // trust line into debt the exact case preclaim authorizes via SpendableHandling::FullBalance. + // The check thus converts a preclaim- authorized deposit into tefINTERNAL after the asset + // transfer. + if (!view().rules().enabled(fixCleanup3_2_0)) { - // LCOV_EXCL_START - JLOG(j_.error()) << "VaultDeposit: negative balance of account assets."; - return tefINTERNAL; - // LCOV_EXCL_STOP + // Sanity check + if (accountHolds( + view(), + accountID_, + assetsDeposited.asset(), + FreezeHandling::IgnoreFreeze, + AuthHandling::IgnoreAuth, + j_) < beast::kZero) + { + JLOG(j_.error()) << "VaultDeposit: negative balance of account assets."; + return tefINTERNAL; + } } // Transfer shares from vault to depositor. diff --git a/src/test/app/Vault_test.cpp b/src/test/app/Vault_test.cpp index cd4c35f1a5..318a6dc45c 100644 --- a/src/test/app/Vault_test.cpp +++ b/src/test/app/Vault_test.cpp @@ -6140,10 +6140,90 @@ class Vault_test : public beast::unit_test::Suite runTest(amendments); } + // VaultDeposit::preclaim uses accountHolds(..., SpendableHandling:: + // shFULL_BALANCE), which for an IOU asset adds the counterparty's + // LowLimit/HighLimit to the depositor's raw balance (TokenHelpers.cpp: + // getTrustLineBalance with includeOppositeLimit=true). When the + // depositor's raw balance < deposit amount but raw + opposite limit >= + // amount, preclaim is satisfied. doApply then calls + // directSendNoFeeIOU, which unconditionally subtracts saAmount from + // saBalance — driving the trust line negative — and returns tesSUCCESS. + // The post-send sanity check uses the default shSIMPLE_BALANCE (no + // opposite-limit add), sees a negative balance, and returns tefINTERNAL. + void + testVaultDepositNegativeBalanceFromOppositeLimit() + { + auto runTest = [&](FeatureBitset f, TER expected) { + using namespace test::jtx; + using namespace std::literals; + + Env env{*this, f}; + Account const gw{"gateway"}; + Account const owner{"owner"}; + Account const depositor{"depositor"}; + + env.fund(XRP(10000), gw, owner, depositor); + env.close(); + + // Gateway with DefaultRipple so vault creation on its IOU works. + env(fset(gw, asfDefaultRipple)); + env.close(); + + // Depositor opens a trust line to gateway and receives a small + // balance. + PrettyAsset const usd = gw["USD"]; + env.trust(usd(1000), depositor); + env(pay(gw, depositor, usd(100))); // raw trust-line balance: 100 + env.close(); + + // Key precondition: gateway sets a non-zero limit on the same + // RippleState — the "opposite field" from depositor's perspective. + // This is what inflates shFULL_BALANCE in preclaim above the raw + // balance. + env(trust(gw, depositor["USD"](1000))); + env.close(); + + // Create the IOU vault. + Vault const vault{env}; + auto [vaultTx, keylet] = vault.create({.owner = owner, .asset = usd}); + env(vaultTx); + env.close(); + + // Submit a deposit of 500 USD: + // - raw balance: 100 USD + // - opposite limit (gw's side): 1000 USD + // - preclaim sees 100 + 1000 = 1100, passes (>= 500) + // - doApply transfers 500, depositor's trust-line balance + // becomes -400 + // - sanity check at VaultDeposit.cpp:256 fires + // - tx returns tefINTERNAL (BUG — should be tesSUCCESS. + auto depositTx = + vault.deposit({.depositor = depositor, .id = keylet.key, .amount = usd(500)}); + env(depositTx, Ter(expected)); + env.close(); + }; + + { + testcase( + "IOU vault deposit exceeding depositor's balance but " + "within counterparty's trust limit, pre-fixCleanup3_2_0 " + "(tefINTERNAL)"); + runTest(test::jtx::testableAmendments() - fixCleanup3_2_0, tefINTERNAL); + } + { + testcase( + "IOU vault deposit exceeding depositor's balance but " + "within counterparty's trust limit, post-fixCleanup3_2_0 " + "(tesSUCCESS)"); + runTest(test::jtx::testableAmendments(), tesSUCCESS); + } + } + public: void run() override { + testVaultDepositNegativeBalanceFromOppositeLimit(); testSequences(); testPreflight(); testCreateFailXRP();