From 27ef4f87e952f3bf8359b1306d06cf0743c1a36c Mon Sep 17 00:00:00 2001 From: Gregory Tsipenyuk Date: Mon, 1 Jun 2026 10:53:46 -0400 Subject: [PATCH] Strengthen MPT overflow offer-crossing regression --- src/test/app/OfferMPT_test.cpp | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/test/app/OfferMPT_test.cpp b/src/test/app/OfferMPT_test.cpp index 7887392cc7..b4b32eb60a 100644 --- a/src/test/app/OfferMPT_test.cpp +++ b/src/test/app/OfferMPT_test.cpp @@ -3524,6 +3524,13 @@ public: MPTTester const token{ {.env = env, .issuer = issuer, .holders = {taker}, .maxAmt = kMaxMpTokenAmount}}; + // Give the taker exactly one MPT. If the old rounding overflow + // collapsed the required input to the minimum positive amount, the + // taker could afford the bad fill and the balance checks below + // would catch the economic gain. + env(pay(issuer, taker, token(1))); + env.close(); + // Covers BookStep::revImp() output reduction. The issuer's offer // is fully funded and has no transfer fee, so offer preparation // succeeds. The taker asks for slightly less output, forcing @@ -3539,13 +3546,23 @@ public: auto const poisonKeylet = keylet::offer(issuer.id(), poisonSeq); BEAST_EXPECT(env.le(poisonKeylet) != nullptr); + auto const issuerXRPBefore = env.balance(issuer, XRP); + auto const takerXRPBefore = env.balance(taker, XRP); + auto const takerMPTBefore = env.balance(taker, token); + auto const fee = env.current()->fees().base; + auto const takerSeq = env.seq(taker); env(offer(taker, token(funded), XRP(1))); env.close(); + // The former overflow point must not turn into a near-free fill: + // the unusable offer is removed, the taker's offer remains, and no + // value changes hands beyond the taker's transaction fee. BEAST_EXPECT(env.le(poisonKeylet) == nullptr); BEAST_EXPECT(env.le(keylet::offer(taker.id(), takerSeq)) != nullptr); - BEAST_EXPECT(env.balance(taker, token) == token(0)); + BEAST_EXPECT(env.balance(issuer, XRP) == issuerXRPBefore); + BEAST_EXPECT(env.balance(taker, XRP) == takerXRPBefore - fee); + BEAST_EXPECT(env.balance(taker, token) == takerMPTBefore); } {