From 0f0ff0d6999fd6055a052d8ef8e77c01b1ef90bc Mon Sep 17 00:00:00 2001 From: Bart <11445373+bthomee@users.noreply.github.com> Date: Sat, 22 Aug 2026 22:47:24 -0400 Subject: [PATCH] fix: Report no ledger from a failed acquisition InboundLedger::getLedger() now reports nothing once the acquisition has failed, since a failed acquisition can still hold a partially built ledger that must never be used. The guard sits at the one accessor rather than relying on every caller to check isFailed() first; the pointer itself is kept, since getJson() still reports on the partial maps of a failed acquire. --- src/test/app/InboundLedger_test.cpp | 6 +++++- src/xrpld/app/ledger/InboundLedger.h | 12 +++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/test/app/InboundLedger_test.cpp b/src/test/app/InboundLedger_test.cpp index 8b0801a939..8f1b37d378 100644 --- a/src/test/app/InboundLedger_test.cpp +++ b/src/test/app/InboundLedger_test.cpp @@ -542,6 +542,9 @@ struct InboundLedger_test : public beast::unit_test::Suite BEAST_EXPECT(acquire->isFailed()); BEAST_EXPECT(!acquire->isComplete()); + + // A failed acquisition must not hand back the partial ledger it built. + BEAST_EXPECT(acquire->getLedger() == nullptr); } /** @@ -705,7 +708,8 @@ struct InboundLedger_test : public beast::unit_test::Suite BEAST_EXPECT(!acquire->isComplete()); BEAST_EXPECT(peerSetPtr->requests() > requestsFromInit); - // done() remembered the hash, which is what stops the next round asking again. + // A failed acquisition holds no ledger to hand back, and done() remembered the hash. + BEAST_EXPECT(acquire->getLedger() == nullptr); BEAST_EXPECT( waitFor([&] { return env.app().getInboundLedgers().isFailure(kUnknownLedger); })); } diff --git a/src/xrpld/app/ledger/InboundLedger.h b/src/xrpld/app/ledger/InboundLedger.h index f88d88c99f..b0d6696e85 100644 --- a/src/xrpld/app/ledger/InboundLedger.h +++ b/src/xrpld/app/ledger/InboundLedger.h @@ -102,10 +102,20 @@ public: return failed_; } + /** + * The acquired ledger, or nullptr if it is not yet available. + * + * Nullptr both before a header has been obtained, since there is + * nothing built yet to hand out, and once the acquisition has failed. + * A failed acquisition may still hold a partially built ledger, which + * must never be used, so failure reports nothing rather than rely on + * every caller to check isFailed() first. The pointer itself is kept, + * since getJson() reports on the partial maps of a failed acquire. + */ std::shared_ptr getLedger() const { - return ledger_; + return failed_ ? nullptr : ledger_; } std::uint32_t